void trapfunc::shotgun(int x, int y) { g->add_msg(_("You trigger a shotgun trap!")); g->u.add_memorial_log(_("Triggered a shotgun trap.")); int shots = (one_in(8) || one_in(20 - g->u.str_max) ? 2 : 1); if (g->m.tr_at(x, y) == tr_shotgun_1) shots = 1; if (rng(5, 50) > g->u.dodge(g)) { body_part hit = num_bp; switch (rng(1, 10)) { case 1: hit = bp_feet; break; case 2: case 3: case 4: hit = bp_legs; break; case 5: case 6: case 7: case 8: case 9: hit = bp_torso; break; case 10: hit = bp_head; break; } int side = random_side(hit); g->add_msg(_("Your %s is hit!"), body_part_name(hit, side).c_str()); g->u.hit(g, hit, side, 0, rng(40 * shots, 60 * shots)); } else g->add_msg(_("You dodge the shot!")); if (shots == 2 || g->m.tr_at(x, y) == tr_shotgun_1) { g->m.spawn_item(x, y, "shotgun_sawn"); g->m.spawn_item(x, y, "string_6"); g->m.remove_trap(x, y); } else g->m.add_trap(x, y, tr_shotgun_1); }
void trapfunc::crossbow(int x, int y) { bool add_bolt = true; g->add_msg(_("You trigger a crossbow trap!")); g->u.add_memorial_log(_("Triggered a crossbow trap.")); if (!one_in(4) && rng(8, 20) > g->u.dodge(g)) { body_part hit = num_bp; switch (rng(1, 10)) { case 1: hit = bp_feet; break; case 2: case 3: case 4: hit = bp_legs; break; case 5: case 6: case 7: case 8: case 9: hit = bp_torso; break; case 10: hit = bp_head; break; } int side = random_side(hit); g->add_msg(_("Your %s is hit!"), body_part_name(hit, side).c_str()); g->u.hit(g, hit, side, 0, rng(20, 30)); add_bolt = !one_in(10); } else g->add_msg(_("You dodge the shot!")); g->m.remove_trap(x, y); g->m.spawn_item(x, y, "crossbow"); g->m.spawn_item(x, y, "string_6"); if (add_bolt) g->m.spawn_item(x, y, "bolt_steel", 1, 1); }
void trapfunc::beartrap(int x, int y) { g->add_msg(_("A bear trap closes on your foot!")); g->u.add_memorial_log(_("Caught by a beartrap.")); g->sound(x, y, 8, _("SNAP!")); g->u.hit(g, bp_legs, random_side(bp_legs), 10, 16); g->u.add_disease("beartrap", 1, true); g->m.remove_trap(x, y); g->m.spawn_item(x, y, "beartrap"); }
void trapfunc::pit_spikes(int x, int y) { g->add_msg(_("You fall in a pit!")); g->u.add_memorial_log(pgettext("memorial_male", "Fell into a spiked pit."), pgettext("memorial_female", "Fell into a spiked pit.")); int dodge = g->u.get_dodge(); int damage = pit_effectiveness(x, y) * rng(20, 50); if (g->u.has_trait("WINGS_BIRD")) { g->add_msg(_("You flap your wings and flutter down gracefully.")); } else if (0 == damage || rng(5, 30) < dodge) { g->add_msg(_("You avoid the spikes within.")); } else { body_part hit = num_bp; switch (rng(1, 10)) { case 1: case 2: hit = bp_legs; break; case 3: case 4: hit = bp_arms; break; case 5: case 6: case 7: case 8: case 9: case 10: hit = bp_torso; break; } int side = random_side(hit); g->add_msg(_("The spikes impale your %s!"), body_part_name(hit, side).c_str()); g->u.hit(NULL, hit, side, 0, damage); if (one_in(4)) { g->add_msg(_("The spears break!")); g->m.ter_set(x, y, t_pit); g->m.add_trap(x, y, tr_pit); for (int i = 0; i < 4; i++) { // 4 spears to a pit if (one_in(3)) { g->m.spawn_item(x, y, "spear_wood"); } } } } g->u.add_disease("in_pit", 1, true); }
void monster::melee_attack(Creature &target, bool, matec_id) { mod_moves(-100); if (type->melee_dice == 0) { // We don't attack, so just return return; } add_effect("hit_by_player", 3); // Make us a valid target for a few turns if (has_flag(MF_HIT_AND_RUN)) { add_effect("run", 4); } bool u_see_me = g->u_see(this); body_part bp_hit; //int highest_hit = 0; int hitstat = type->melee_skill; int hitroll = dice(hitstat,10); damage_instance damage; if(!is_hallucination()) { if (type->melee_dice > 0) { damage.add_damage(DT_BASH, dice(type->melee_dice,type->melee_sides)); } if (type->melee_cut > 0) { damage.add_damage(DT_CUT, type->melee_cut); } } /* TODO: height-related bodypart selection //If the player is knocked down or the monster can fly, any body part is a valid target if(target.is_on_ground() || has_flag(MF_FLIES)){ highest_hit = 20; } else { switch (type->size) { case MS_TINY: highest_hit = 3; break; case MS_SMALL: highest_hit = 12; break; case MS_MEDIUM: highest_hit = 20; break; case MS_LARGE: highest_hit = 28; break; case MS_HUGE: highest_hit = 35; break; } if (digging()){ highest_hit -= 8; } if (highest_hit <= 1){ highest_hit = 2; } } if (highest_hit > 20){ highest_hit = 20; } int bp_rand = rng(0, highest_hit - 1); if (bp_rand <= 2){ bp_hit = bp_legs; } else if (bp_rand <= 10){ bp_hit = bp_torso; } else if (bp_rand <= 14){ bp_hit = bp_arms; } else if (bp_rand <= 16){ bp_hit = bp_mouth; } else if (bp_rand == 18){ bp_hit = bp_eyes; } else{ bp_hit = bp_head; } */ dealt_damage_instance dealt_dam; int hitspread = target.deal_melee_attack(this, hitroll); if (hitspread >= 0) { target.deal_melee_hit(this, hitspread, false, damage, dealt_dam); } bp_hit = dealt_dam.bp_hit; if (hitspread < 0) { // a miss // TODO: characters practice dodge when a hit misses 'em if (target.is_player()) { if (u_see_me) { add_msg(_("You dodge %1$s."), disp_name().c_str()); } else { add_msg(_("You dodge an attack from an unseen source.")); } } else { if (u_see_me) { add_msg(_("The %1$s dodges %2$s attack."), name().c_str(), target.disp_name(true).c_str()); } } //Hallucinations always produce messages but never actually deal damage } else if (is_hallucination() || dealt_dam.total_damage() > 0) { if (target.is_player()) { if (u_see_me) { add_msg(m_bad, _("The %1$s hits your %2$s."), name().c_str(), body_part_name(bp_hit, random_side(bp_hit)).c_str()); } else { add_msg(m_bad, _("Something hits your %s."), body_part_name(bp_hit, random_side(bp_hit)).c_str()); } } else { if (u_see_me) { add_msg(_("The %1$s hits %2$s %3$s."), name().c_str(), target.disp_name(true).c_str(), body_part_name(bp_hit, random_side(bp_hit)).c_str()); } } } else { if (target.is_player()) { if (u_see_me) { add_msg(_("The %1$s hits your %2$s, but your %3$s protects you."), name().c_str(), body_part_name(bp_hit, random_side(bp_hit)).c_str(), target.skin_name().c_str()); } else { add_msg(_("Something hits your %1$s, but your %2$s protects you."), body_part_name(bp_hit, random_side(bp_hit)).c_str(), target.skin_name().c_str()); } } else { if (u_see_me) { add_msg(_("The %1$s hits %2$s %3$s but is stopped by %2$s %4$s."), name().c_str(), target.disp_name(true).c_str(), body_part_name(bp_hit, random_side(bp_hit)).c_str(), target.skin_name().c_str()); } } } if (is_hallucination()) { if(one_in(7)) { dead = true; } return; } // Adjust anger/morale of same-species monsters, if appropriate int anger_adjust = 0, morale_adjust = 0; if (type->has_anger_trigger(MTRIG_FRIEND_ATTACKED)){ anger_adjust += 15; } if (type->has_fear_trigger(MTRIG_FRIEND_ATTACKED)){ morale_adjust -= 15; } if (type->has_placate_trigger(MTRIG_FRIEND_ATTACKED)){ anger_adjust -= 15; } if (anger_adjust != 0 && morale_adjust != 0) { for (int i = 0; i < g->num_zombies(); i++) { g->zombie(i).morale += morale_adjust; g->zombie(i).anger += anger_adjust; } } }
void monster::hit_player(game *g, player &p, bool can_grab) { moves -= 100; if (type->melee_dice == 0) // We don't attack, so just return { return; } add_effect(ME_HIT_BY_PLAYER, 3); // Make us a valid target for a few turns if (has_flag(MF_HIT_AND_RUN)) { add_effect(ME_RUN, 4); } bool is_npc = p.is_npc(); bool u_see = (!is_npc || g->u_see(p.posx, p.posy)); std::string you = (is_npc ? p.name : "you"); std::string You = (is_npc ? p.name : "You"); std::string your = (is_npc ? p.name + "'s" : "your"); std::string Your = (is_npc ? p.name + "'s" : "Your"); body_part bphit; int dam = hit(g, p, bphit), cut = type->melee_cut, stab = 0; int side = random_side(bphit); //110*e^(-.3*[melee skill of monster]) = % chance to miss. *100 to track .01%'s //Returns ~80% at 1, drops quickly to 33% at 4, then slowly to 5% at 10 and 1% at 16 if (rng(0, 10000) < 11000 * exp(-.3 * type->melee_skill)) { if (u_see) { g->add_msg(_("The %s misses."), name().c_str()); } } else { if (!g->u.uncanny_dodge()) { //Reduce player's ability to dodge by monster's ability to hit int dodge_ii = p.dodge(g) - rng(0, type->melee_skill); if (dodge_ii < 0) { dodge_ii = 0; } // 100/(1+99*e^(-.6*[dodge() return modified by monster's skill])) = % chance to dodge // *100 to track .01%'s // 1% minimum, scales slowly to 16% at 5, then rapidly to 80% at 10, // then returns less with each additional point, reaching 99% at 16 if (rng(0, 10000) < 10000/(1 + 99 * exp(-.6 * dodge_ii))) { if (is_npc) { if(u_see) { g->add_msg(_("%1$s dodges the %2$s."), p.name.c_str(), name().c_str()); } } else { g->add_msg(_("You dodge the %s."), name().c_str()); } p.practice(g->turn, "dodge", type->melee_skill * 2); //Better monster = more skill gained } //Successful hit with damage else if (dam > 0) { p.practice(g->turn, "dodge", type->melee_skill); if(!p.block_hit(g, this, NULL, bphit, side, dam, cut, stab) && u_see) { if (is_npc) { if( u_see ) { g->add_msg(_("The %1$s hits %2$s's %3$s."), name().c_str(), p.name.c_str(), body_part_name(bphit, side).c_str()); } } else { g->add_msg(_("The %1$s hits your %2$s."), name().c_str(), body_part_name(bphit, side).c_str()); } } // Attempt defensive moves if (!is_npc) { if (g->u.activity.type == ACT_RELOAD) { g->add_msg(_("You stop reloading.")); } else if (g->u.activity.type == ACT_READ) { g->add_msg(_("You stop reading.")); } else if (g->u.activity.type == ACT_CRAFT || g->u.activity.type == ACT_LONGCRAFT) { g->add_msg(_("You stop crafting.")); g->u.activity.type = ACT_NULL; } } if (p.has_active_bionic("bio_ods")) { if (!is_npc) { g->add_msg(_("Your offensive defense system shocks it!"), p.name.c_str()); } else if (u_see) { g->add_msg(_("%s's offensive defense system shocks it!"), p.name.c_str()); } hurt(rng(10, 40)); } if (p.encumb(bphit) == 0 &&(p.has_trait("SPINES") || p.has_trait("QUILLS"))) { int spine = rng(1, (p.has_trait("QUILLS") ? 20 : 8)); if (is_npc) { if( u_see ) { g->add_msg(_("%1$s's %2$s puncture it!"), p.name.c_str(), (g->u.has_trait("QUILLS") ? _("quills") : _("spines"))); } } else { g->add_msg(_("Your %s puncture it!"), (g->u.has_trait("QUILLS") ? _("quills") : _("spines"))); } hurt(spine); } if (dam + cut <= 0) { return; // Defensive technique canceled damage. } //Hallucinations don't actually hurt the player, but do produce the message if(is_hallucination()) { //~14% chance of vanishing after hitting the player if(one_in(7)) { die(g); return; } } else { //Hurt the player dam = p.hit(g, bphit, side, dam, cut); //Monster effects if (dam > 0 && has_flag(MF_VENOM)) { g->add_msg_if_player(&p, _("You're poisoned!")); p.add_disease("poison", 30); } else if (dam > 0 && has_flag(MF_BADVENOM)) { g->add_msg_if_player(&p, _("You feel poison flood your body, wracking you with pain...")); p.add_disease("badpoison", 40); } else if (dam > 0 && has_flag(MF_PARALYZE)) { g->add_msg_if_player(&p, _("You feel poison enter your body!")); p.add_disease("paralyzepoison", 100, false, 1, 20, 100); } if (has_flag(MF_BLEED) && dam > 6 && cut > 0) { g->add_msg_if_player(&p, _("You're Bleeding!")); p.add_disease("bleed", 60, false, 1, 3, 120, 1, bphit, side, true); } //Same as monster's chance to not miss if (can_grab && has_flag(MF_GRABS) && (rng(0, 10000) > 11000 * exp(-.3 * type->melee_skill))) { g->add_msg(_("The %s grabs you!"), name().c_str()); if (p.has_grab_break_tec() && dice(p.dex_cur + p.skillLevel("melee"), 12) > dice(type->melee_dice, 10)) { g->add_msg_if_player(&p, _("You break the grab!")); } else { hit_player(g, p, false); //We grabed, so hit them again } } } // TODO: readd with counter mechanic } } } // if dam > 0 if (is_npc) { if (p.hp_cur[hp_head] <= 0 || p.hp_cur[hp_torso] <= 0) { npc* tmp = dynamic_cast<npc*>(&p); tmp->die(g); int index = g->npc_at(p.posx, p.posy); if (index != -1 && index < g->active_npc.size()) { g->active_npc.erase(g->active_npc.begin() + index); } plans.clear(); } } // Adjust anger/morale of same-species monsters, if appropriate int anger_adjust = 0, morale_adjust = 0; if (type->has_anger_trigger(MTRIG_FRIEND_ATTACKED)){ anger_adjust += 15; } if (type->has_fear_trigger(MTRIG_FRIEND_ATTACKED)){ morale_adjust -= 15; } if (type->has_placate_trigger(MTRIG_FRIEND_ATTACKED)){ anger_adjust -= 15; } if (anger_adjust != 0 && morale_adjust != 0) { for (int i = 0; i < g->num_zombies(); i++) { g->zombie(i).morale += morale_adjust; g->zombie(i).anger += anger_adjust; } } }
void shoot_player(player &p, player *h, int &dam, double goodhit) { int npcdex = g->npc_at(h->posx, h->posy); // Gunmods don't have a type, so use the player gun type. it_gun* firing = dynamic_cast<it_gun*>(p.weapon.type); body_part hit = bp_torso; if (goodhit < .003) { hit = bp_eyes; dam = rng(3 * dam, 5 * dam); p.practice(g->turn, firing->skill_used, 5); } else if (goodhit < .066) { if (one_in(25)) { hit = bp_eyes; } else if (one_in(15)) { hit = bp_mouth; } else { hit = bp_head; } dam = rng(2 * dam, 5 * dam); p.practice(g->turn, firing->skill_used, 5); } else if (goodhit < .2) { hit = bp_torso; dam = rng(dam, 2 * dam); p.practice(g->turn, firing->skill_used, 2); } else if (goodhit < .4) { if (one_in(3)) { hit = bp_torso; } else if (one_in(2)) { hit = bp_arms; } else { hit = bp_legs; } dam = rng(int(dam * .9), int(dam * 1.5)); p.practice(g->turn, firing->skill_used, rng(0, 1)); } else if (goodhit < .5) { if (one_in(2)) { hit = bp_arms; } else { hit = bp_legs; } dam = rng(dam / 2, dam); } else { dam = 0; } if (dam > 0) { int side = random_side(hit); h->moves -= rng(0, dam); if (h == &(g->u)) { g->add_msg(_("%s shoots your %s for %d damage!"), p.name.c_str(), body_part_name(hit, side).c_str(), dam); } else { if (&p == &(g->u)) { g->add_msg(_("You shoot %s's %s."), h->name.c_str(), body_part_name(hit, side).c_str()); g->active_npc[npcdex]->make_angry(); } else if (g->u_see(h->posx, h->posy)) { g->add_msg(_("%s shoots %s's %s."), (g->u_see(p.posx, p.posy) ? p.name.c_str() : _("Someone")), h->name.c_str(), body_part_name(hit, side).c_str()); } } h->hit(&p, hit, side, 0, dam); } }