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 trapfunc::shotgun(game *g, int x, int y) { g->add_msg("You trigger 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()) { body_part hit; 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 = rng(0, 1); 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.add_item(x, y, g->itypes[itm_shotgun_sawn], 0); g->m.add_item(x, y, g->itypes[itm_string_6], 0); g->m.tr_at(x, y) = tr_null; } else g->m.tr_at(x, y) = tr_shotgun_1; }
void trapfunc::pit_spikes(game *g, int x, int y) { g->add_msg("You fall in a pit!"); int dodge = g->u.dodge(g); int damage = rng(20, 50); if (g->u.has_trait(PF_WINGS_BIRD)) g->add_msg("You flap your wings and flutter down gracefully."); else if (rng(5, 30) < dodge) g->add_msg("You avoid the spikes within."); else { body_part hit; 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 = rng(0, 1); g->add_msg("The spikes impale your %s!", body_part_name(hit, side).c_str()); g->u.hit(g, hit, side, 0, damage); if (one_in(4)) { g->add_msg("The spears break!"); g->m.ter_set(x, y, t_pit); g->m.tr_at(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, g->itypes["spear_wood"], g->turn); g->m.spawn_item(x, y, g->itypes["spear_wood"], g->turn); } } } g->u.add_disease(DI_IN_PIT, -1, g); }
void trapfunc::crossbow(game *g, int x, int y) { bool add_bolt = true; g->add_msg("You trigger a crossbow trap!"); if (!one_in(4) && rng(8, 20) > g->u.dodge()) { body_part hit; 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 = rng(0, 1); 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.tr_at(x, y) = tr_null; g->m.add_item(x, y, g->itypes[itm_crossbow], 0); g->m.add_item(x, y, g->itypes[itm_string_6], 0); if (add_bolt) g->m.add_item(x, y, g->itypes[itm_bolt_steel], 0); }
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(game *g, 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; int side = rng(0, 1); 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) { 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(g, hit, side, 0, dam); /* if (h != &(g->u)) { int npcdex = g->npc_at(h->posx, h->posy); if (g->active_npc[npcdex].hp_cur[hp_head] <= 0 || g->active_npc[npcdex].hp_cur[hp_torso] <= 0 ) { g->active_npc[npcdex].die(g, !p.is_npc()); g->active_npc.erase(g->active_npc.begin() + npcdex); } } */ } }
int Creature::deal_projectile_attack(Creature *source, double missed_by, const projectile &proj, dealt_damage_instance &dealt_dam) { bool u_see_this = g->u_see(this); body_part bp_hit; int side = rng(0, 1); // do 10,speed because speed could potentially be > 10000 if (dodge_roll() >= dice(10, proj.speed)) { if (is_player()) g->add_msg(_("You dodge %s's projectile!"), skin_name().c_str()); else if (u_see_this) g->add_msg(_("%s dodges %s's projectile."), disp_name().c_str(), source->disp_name().c_str()); return 0; } double hit_value = missed_by + rng_float(-0.5, 0.5); // headshots considered elsewhere if (hit_value <= 0.4) { bp_hit = bp_torso; } else if (one_in(4)) { bp_hit = bp_legs; } else { bp_hit = bp_arms; } double monster_speed_penalty = std::max(double(get_speed()) / 80., 1.0); double goodhit = missed_by / monster_speed_penalty; double damage_mult = 1.0; if (goodhit <= .1) { g->add_msg_if_player(source, _("Headshot!")); damage_mult *= rng_float(2.45, 3.35); bp_hit = bp_head; // headshot hits the head, of course } else if (goodhit <= .2) { g->add_msg_if_player(source, _("Critical!")); damage_mult *= rng_float(1.75, 2.3); } else if (goodhit <= .4) { g->add_msg_if_player(source, _("Good hit!")); damage_mult *= rng_float(1, 1.5); } else if (goodhit <= .6) { damage_mult *= rng_float(0.5, 1); } else if (goodhit <= .8) { g->add_msg_if_player(source, _("Grazing hit.")); damage_mult *= rng_float(0, .25); } else { damage_mult *= 0; } // copy it, since we're mutating damage_instance impact = proj.impact; impact.mult_damage(damage_mult); dealt_dam = deal_damage(source, bp_hit, side, impact); dealt_dam.bp_hit = bp_hit; if(u_see_this) { if (damage_mult == 0) { if(source != NULL) { g->add_msg(source->is_player() ? _("You miss!") : _("The shot misses!")); } } else if (dealt_dam.total_damage() == 0) { g->add_msg(_("The shot reflects off the %s!"), skin_name().c_str()); } else if (source != NULL) { if (source->is_player()) { g->add_msg(_("You hit the %s for %d damage."), disp_name().c_str(), dealt_dam.total_damage()); } else if( this->is_player() && g->u.has_trait("SELFAWARE")) { g->add_msg_if_player( this, _( "You were hit in the %s for %d damage." ), body_part_name( bp_hit, side ).c_str( ), dealt_dam.total_damage( ) ); } else if( u_see_this ) { g->add_msg(_("%s shoots %s."), source->disp_name().c_str(), disp_name().c_str()); } } } return 0; }
int Creature::deal_projectile_attack(Creature *source, double missed_by, const projectile &proj, dealt_damage_instance &dealt_dam) { bool u_see_this = g->u_see(this); body_part bp_hit; int side = rng(0, 1); // do 10,speed because speed could potentially be > 10000 if (dodge_roll() >= dice(10, proj.speed)) { if (is_player()) add_msg(_("You dodge %s projectile!"), source->disp_name(true).c_str()); else if (u_see_this) add_msg(_("%s dodges %s projectile."), disp_name().c_str(), source->disp_name(true).c_str()); return 0; } // Bounce applies whether it does damage or not. if (proj.proj_effects.count("BOUNCE")) { add_effect("bounced", 1); } double hit_value = missed_by + rng_float(-0.5, 0.5); // headshots considered elsewhere if (hit_value <= 0.4) { bp_hit = bp_torso; } else if (one_in(4)) { bp_hit = bp_legs; } else { bp_hit = bp_arms; } double monster_speed_penalty = std::max(double(get_speed()) / 80., 1.0); double goodhit = missed_by / monster_speed_penalty; double damage_mult = 1.0; std::string message = ""; game_message_type gmtSCTcolor = m_neutral; if (goodhit <= .1) { message = _("Headshot!"); source->add_msg_if_player(m_good, message.c_str()); gmtSCTcolor = m_headshot; damage_mult *= rng_float(2.45, 3.35); bp_hit = bp_head; // headshot hits the head, of course } else if (goodhit <= .2) { message = _("Critical!"); source->add_msg_if_player(m_good, message.c_str()); gmtSCTcolor = m_critical; damage_mult *= rng_float(1.75, 2.3); } else if (goodhit <= .4) { message = _("Good hit!"); source->add_msg_if_player(m_good, message.c_str()); gmtSCTcolor = m_good; damage_mult *= rng_float(1, 1.5); } else if (goodhit <= .6) { damage_mult *= rng_float(0.5, 1); } else if (goodhit <= .8) { message = _("Grazing hit."); source->add_msg_if_player(m_good, message.c_str()); gmtSCTcolor = m_grazing; damage_mult *= rng_float(0, .25); } else { damage_mult *= 0; } // copy it, since we're mutating damage_instance impact = proj.impact; if( item(proj.ammo->id, 0).has_flag("NOGIB") ) { impact.add_effect("NOGIB"); } impact.mult_damage(damage_mult); dealt_dam = deal_damage(source, bp_hit, side, impact); dealt_dam.bp_hit = bp_hit; // Apply ammo effects to target. const std::string target_material = get_material(); if (proj.proj_effects.count("FLAME")) { if (0 == target_material.compare("veggy") || 0 == target_material.compare("cotton") || 0 == target_material.compare("wool") || 0 == target_material.compare("paper") || 0 == target_material.compare("wood" ) ) { add_effect("onfire", rng(8, 20)); } else if (0 == target_material.compare("flesh") || 0 == target_material.compare("iflesh") ) { add_effect("onfire", rng(5, 10)); } } else if (proj.proj_effects.count("INCENDIARY") ) { if (0 == target_material.compare("veggy") || 0 == target_material.compare("cotton") || 0 == target_material.compare("wool") || 0 == target_material.compare("paper") || 0 == target_material.compare("wood") ) { add_effect("onfire", rng(2, 6)); } else if ( (0 == target_material.compare("flesh") || 0 == target_material.compare("iflesh") ) && one_in(4) ) { add_effect("onfire", rng(1, 4)); } } else if (proj.proj_effects.count("IGNITE")) { if (0 == target_material.compare("veggy") || 0 == target_material.compare("cotton") || 0 == target_material.compare("wool") || 0 == target_material.compare("paper") || 0 == target_material.compare("wood") ) { add_effect("onfire", rng(6, 6)); } else if (0 == target_material.compare("flesh") || 0 == target_material.compare("iflesh") ) { add_effect("onfire", rng(10, 10)); } } int stun_strength = 0; if (proj.proj_effects.count("BEANBAG")) { stun_strength = 4; } if(proj.proj_effects.count("WHIP")) { stun_strength = rng(4, 10); } if (proj.proj_effects.count("LARGE_BEANBAG")) { stun_strength = 16; } if( stun_strength > 0 ) { switch( get_size() ) { case MS_TINY: stun_strength *= 4; break; case MS_SMALL: stun_strength *= 2; break; case MS_MEDIUM: default: break; case MS_LARGE: stun_strength /= 2; break; case MS_HUGE: stun_strength /= 4; break; } add_effect( "stunned", rng(stun_strength / 2, stun_strength) ); } if(u_see_this) { if (damage_mult == 0) { if(source != NULL) { add_msg(source->is_player() ? _("You miss!") : _("The shot misses!")); } } else if (dealt_dam.total_damage() == 0) { add_msg(_("The shot reflects off %s %s!"), disp_name(true).c_str(), skin_name().c_str()); } else if (source != NULL) { if (source->is_player()) { //player hits monster ranged nc_color color; std::string health_bar = ""; get_HP_Bar(dealt_dam.total_damage(), this->get_hp_max(), color, health_bar, true); SCT.add(this->xpos(), this->ypos(), direction_from(0, 0, this->xpos() - source->xpos(), this->ypos() - source->ypos()), health_bar, m_good, message, gmtSCTcolor); if (this->get_hp() > 0) { get_HP_Bar(this->get_hp(), this->get_hp_max(), color, health_bar, true); SCT.add(this->xpos(), this->ypos(), direction_from(0, 0, this->xpos() - source->xpos(), this->ypos() - source->ypos()), health_bar, m_good, "hp", m_neutral, "hp"); } else { SCT.removeCreatureHP(); } add_msg(m_good, _("You hit the %s for %d damage."), disp_name().c_str(), dealt_dam.total_damage()); } else if(this->is_player()) { //monster hits player ranged add_msg_if_player( m_bad, _( "You were hit in the %s for %d damage." ), body_part_name( bp_hit, side ).c_str( ), dealt_dam.total_damage( ) ); } else if( u_see_this ) { add_msg(_("%s shoots %s."), source->disp_name().c_str(), disp_name().c_str()); } } } return 0; }
void player::perform_defensive_technique( technique_id technique, game *g, monster *z, player *p, body_part &bp_hit, int &side, int &bash_dam, int &cut_dam, int &stab_dam) { int junk; bool mon = (z != NULL); std::string You = (is_npc() ? name : "You"); std::string your = (is_npc() ? (male ? "his" : "her") : "your"); std::string target = (mon ? "the " + z->name() : p->name); bool u_see = (!is_npc() || g->u_see(posx, posy, junk)); switch (technique) { case TEC_BLOCK: case TEC_BLOCK_LEGS: { if (technique == TEC_BLOCK) { bp_hit = bp_arms; if (hp_cur[hp_arm_l] >= hp_cur[hp_arm_r]) side = 0; else side = 1; } else { // Blocking with our legs bp_hit = bp_legs; if (hp_cur[hp_leg_l] >= hp_cur[hp_leg_r]) side = 0; else side = 1; } if (u_see) g->add_msg("%s block%s with %s %s.", You.c_str(), (is_npc() ? "s" : ""), your.c_str(), body_part_name(bp_hit, side).c_str()); bash_dam *= .5; double reduction = 1.0; // Special reductions for certain styles if (weapon.typeId() == "style_tai_chi") reduction -= double(0.08 * double(per_cur - 6)); if (weapon.typeId() == "style_taekwondo") reduction -= double(0.08 * double(str_cur - 6)); if (reduction > 1.0) reduction = 1.0; if (reduction < 0.3) reduction = 0.3; bash_dam *= reduction; } break; case TEC_WBLOCK_1: case TEC_WBLOCK_2: case TEC_WBLOCK_3: // TODO: Cause weapon damage bash_dam = 0; cut_dam = 0; stab_dam = 0; if (u_see) g->add_msg("%s block%s with %s %s.", You.c_str(), (is_npc() ? "s" : ""), your.c_str(), weapon.tname().c_str()); case TEC_COUNTER: break; // Handled elsewhere case TEC_DEF_THROW: if (u_see) g->add_msg("%s throw%s %s!", You.c_str(), (is_npc() ? "s" : ""), target.c_str()); bash_dam = 0; cut_dam = 0; stab_dam = 0; if (mon) { z->add_effect(ME_DOWNED, rng(1, 2)); z->knock_back_from(g, posx + rng(-1, 1), posy + rng(-1, 1)); } else { p->add_disease(DI_DOWNED, rng(1, 2), g); p->knock_back_from(g, posx + rng(-1, 1), posy + rng(-1, 1)); } break; case TEC_DEF_DISARM: g->m.add_item(p->posx, p->posy, p->remove_weapon()); // Re-roll damage, without our weapon bash_dam = p->roll_bash_damage(NULL, false); cut_dam = p->roll_cut_damage(NULL, false); stab_dam = p->roll_stab_damage(NULL, false); if (u_see) g->add_msg("%s disarm%s %s!", You.c_str(), (is_npc() ? "s" : ""), target.c_str()); break; } // switch (technique) }
void trapfunc::crossbow( Creature *c, const tripoint &p ) { bool add_bolt = true; if( c != nullptr ) { c->add_msg_player_or_npc( m_neutral, _( "You trigger a crossbow trap!" ), _( "<npcname> triggers a crossbow trap!" ) ); c->add_memorial_log( pgettext( "memorial_male", "Triggered a crossbow trap." ), pgettext( "memorial_female", "Triggered a crossbow trap." ) ); monster *z = dynamic_cast<monster *>( c ); player *n = dynamic_cast<player *>( c ); if( n != nullptr ) { if( !one_in( 4 ) && rng( 8, 20 ) > n->get_dodge() ) { body_part hit = num_bp; switch( rng( 1, 10 ) ) { case 1: if( one_in( 2 ) ) { hit = bp_foot_l; } else { hit = bp_foot_r; } break; case 2: case 3: case 4: if( one_in( 2 ) ) { hit = bp_leg_l; } else { hit = bp_leg_r; } break; case 5: case 6: case 7: case 8: case 9: hit = bp_torso; break; case 10: hit = bp_head; break; } //~ %s is bodypart n->add_msg_if_player( m_bad, _( "Your %s is hit!" ), body_part_name( hit ).c_str() ); c->deal_damage( nullptr, hit, damage_instance( DT_CUT, rng( 20, 30 ) ) ); add_bolt = !one_in( 10 ); } else { n->add_msg_player_or_npc( m_neutral, _( "You dodge the shot!" ), _( "<npcname> dodges the shot!" ) ); } } else if( z != nullptr ) { bool seen = g->u.sees( *z ); int chance = 0; // adapted from shotgun code - chance of getting hit depends on size switch( z->type->size ) { case MS_TINY: chance = 50; break; case MS_SMALL: chance = 8; break; case MS_MEDIUM: chance = 6; break; case MS_LARGE: chance = 4; break; case MS_HUGE: chance = 1; break; } if( one_in( chance ) ) { if( seen ) { add_msg( m_bad, _( "A bolt shoots out and hits the %s!" ), z->name().c_str() ); } z->apply_damage( nullptr, bp_torso, rng( 20, 30 ) ); add_bolt = !one_in( 10 ); } else if( seen ) { add_msg( m_neutral, _( "A bolt shoots out, but misses the %s." ), z->name().c_str() ); } } c->check_dead_state(); } g->m.remove_trap( p ); g->m.spawn_item( p, "crossbow" ); g->m.spawn_item( p, "string_6" ); if( add_bolt ) { g->m.spawn_item( p, "bolt_steel", 1, 1 ); } }
void monster::hit_player(game *g, player &p, bool can_grab) { 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(); int junk; bool u_see = (!is_npc || g->u_see(p.posx, p.posy, junk)); 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 side = rng(0, 1); int dam = hit(g, p, bphit), cut = type->melee_cut, stab = 0; technique_id tech = p.pick_defensive_technique(g, this, NULL); p.perform_defensive_technique(tech, g, this, NULL, bphit, side, dam, cut, stab); if (dam == 0 && u_see) g->add_msg("The %s misses %s.", name().c_str(), you.c_str()); else if (dam > 0) { if (u_see && tech != TEC_BLOCK) g->add_msg("The %s hits %s %s.", name().c_str(), your.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->add_msg("You stop crafting."); g->u.activity.type = ACT_NULL; } if (p.has_active_bionic(bio_ods)) { if (u_see) g->add_msg("%s offensive defense system shocks it!", Your.c_str()); hurt(rng(10, 40)); } if (p.encumb(bphit) == 0 && (p.has_trait(PF_SPINES) || p.has_trait(PF_QUILLS))) { int spine = rng(1, (p.has_trait(PF_QUILLS) ? 20 : 8)); g->add_msg("%s %s puncture it!", Your.c_str(), (g->u.has_trait(PF_QUILLS) ? "quills" : "spines")); hurt(spine); } if (dam + cut <= 0) return; // Defensive technique canceled damage. p.hit(g, bphit, side, dam, cut); if (has_flag(MF_VENOM)) { if (!is_npc) g->add_msg("You're poisoned!"); p.add_disease(DI_POISON, 30, g); } else if (has_flag(MF_BADVENOM)) { if (!is_npc) g->add_msg("You feel poison flood your body, wracking you with pain..."); p.add_disease(DI_BADPOISON, 40, g); } if (has_flag(MF_BLEED) && dam > 6 && cut > 0) { if (!is_npc) g->add_msg("You're Bleeding!"); p.add_disease(DI_BLEED, 30, g); } if (can_grab && has_flag(MF_GRABS) && dice(type->melee_dice, 10) > dice(p.dodge(g), 10)) { if (!is_npc) g->add_msg("The %s grabs you!", name().c_str()); if (p.weapon.has_technique(TEC_BREAK, &p) && dice(p.dex_cur + p.skillLevel("melee").level(), 12) > dice(type->melee_dice, 10)){ if (!is_npc) g->add_msg("You break the grab!"); } else hit_player(g, p, false); } if (tech == TEC_COUNTER && !is_npc) { g->add_msg("Counter-attack!"); hurt( p.hit_mon(g, this) ); } } // 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; for (unsigned int i = 0; i < type->anger.size(); i++) { if (type->anger[i] == MTRIG_FRIEND_ATTACKED) anger_adjust += 15; } for (unsigned int i = 0; i < type->placate.size(); i++) { if (type->placate[i] == MTRIG_FRIEND_ATTACKED) anger_adjust -= 15; } for (unsigned int i = 0; i < type->fear.size(); i++) { if (type->fear[i] == MTRIG_FRIEND_ATTACKED) morale_adjust -= 15; } if (anger_adjust != 0 && morale_adjust != 0) { for (unsigned int i = 0; i < g->z.size(); i++) { g->z[i].morale += morale_adjust; g->z[i].anger += anger_adjust; } } }
void monster::hit_player(game *g, player &p) { if (type->melee_dice == 0) // We don't attack, so just return return; bool is_npc = p.is_npc(); int junk; bool u_see = (!is_npc || g->u_see(p.posx, p.posy, junk)); 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 side = rng(0, 1); int dam = hit(p, bphit); if (dam == 0 && u_see) g->add_msg("The %s misses %s.", name().c_str(), you.c_str()); else if (dam > 0) { if (u_see) g->add_msg("The %s hits %s %s.", name().c_str(), your.c_str(), body_part_name(bphit, side).c_str()); 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->add_msg("You stop crafting."); g->u.activity.type = ACT_NULL; } if (p.has_active_bionic(bio_ods)) { if (u_see) g->add_msg("%s offensive defense system shocks it!", Your.c_str()); hurt(rng(10, 40)); } if (p.encumb(bphit) == 0 && (p.has_trait(PF_SPINES) || p.has_trait(PF_QUILLS))) { int spine = rng(1, (p.has_trait(PF_QUILLS) ? 20 : 8)); g->add_msg("%s %s puncture it!", Your.c_str(), (g->u.has_trait(PF_QUILLS) ? "quills" : "spines")); hurt(spine); } p.hit(g, bphit, side, dam, type->melee_cut); if (has_flag(MF_VENOM)) { if (!is_npc) g->add_msg("You're poisoned!"); p.add_disease(DI_POISON, 30, g); } else if (has_flag(MF_BADVENOM)) { if (!is_npc) g->add_msg("You feel poison flood your body, wracking you with pain..."); p.add_disease(DI_BADPOISON, 40, g); } } 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); g->active_npc.erase(g->active_npc.begin() + index); plans.clear(); } } }
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); } }
body_part Creature::select_body_part(Creature *source, int hit_roll) const { // Get size difference (-1,0,1); int szdif = source->get_size() - get_size(); if(szdif < -1) { szdif = -1; } else if (szdif > 1) { szdif = 1; } add_msg( m_debug, "hit roll = %d", hit_roll); add_msg( m_debug, "source size = %d", source->get_size() ); add_msg( m_debug, "target size = %d", get_size() ); add_msg( m_debug, "difference = %d", szdif ); std::map<body_part, double> hit_weights = default_hit_weights[szdif]; // If the target is on the ground, even small/tiny creatures may target eyes/head. Also increases chances of larger creatures. // Any hit modifiers to locations should go here. (Tags, attack style, etc) if(is_on_ground()) { hit_weights[bp_eyes] += 1; hit_weights[bp_head] += 5; } //Adjust based on hit roll: Eyes, Head & Torso get higher, while Arms and Legs get lower. //This should eventually be replaced with targeted attacks and this being miss chances. // pow() is unstable at 0, so don't apply any changes. if( hit_roll != 0 ) { hit_weights[bp_eyes] *= std::pow(hit_roll, 1.15); hit_weights[bp_head] *= std::pow(hit_roll, 1.35); hit_weights[bp_torso] *= std::pow(hit_roll, 1); hit_weights[bp_arm_l] *= std::pow(hit_roll, 0.95); hit_weights[bp_arm_r] *= std::pow(hit_roll, 0.95); hit_weights[bp_leg_l] *= std::pow(hit_roll, 0.975); hit_weights[bp_leg_r] *= std::pow(hit_roll, 0.975); } // Debug for seeing weights. add_msg( m_debug, "eyes = %f", hit_weights.at( bp_eyes ) ); add_msg( m_debug, "head = %f", hit_weights.at( bp_head ) ); add_msg( m_debug, "torso = %f", hit_weights.at( bp_torso ) ); add_msg( m_debug, "arm_l = %f", hit_weights.at( bp_arm_l ) ); add_msg( m_debug, "arm_r = %f", hit_weights.at( bp_arm_r ) ); add_msg( m_debug, "leg_l = %f", hit_weights.at( bp_leg_l ) ); add_msg( m_debug, "leg_r = %f", hit_weights.at( bp_leg_r ) ); double totalWeight = 0; for( const auto &hit_weight : hit_weights ) { totalWeight += hit_weight.second; } double roll = rng_float(0, totalWeight); body_part selected_part = bp_torso; for( const auto &hit_candidate : hit_weights) { roll -= hit_candidate.second; if(roll <= 0) { selected_part = hit_candidate.first; break; } } add_msg( m_debug, "selected part: %s", body_part_name(selected_part).c_str() ); return selected_part; }
void trapfunc::shotgun( Creature *c, const tripoint &p ) { sounds::sound( p, 60, _( "Kerblam!" ) ); int shots = 1; if( c != nullptr ) { c->add_msg_player_or_npc( m_neutral, _( "You trigger a shotgun trap!" ), _( "<npcname> triggers a shotgun trap!" ) ); c->add_memorial_log( pgettext( "memorial_male", "Triggered a shotgun trap." ), pgettext( "memorial_female", "Triggered a shotgun trap." ) ); monster *z = dynamic_cast<monster *>( c ); player *n = dynamic_cast<player *>( c ); if( n != nullptr ) { shots = ( one_in( 8 ) || one_in( 20 - n->str_max ) ? 2 : 1 ); if( g->m.tr_at( p ).loadid == tr_shotgun_1 ) { shots = 1; } if( rng( 5, 50 ) > n->get_dodge() ) { body_part hit = num_bp; switch( rng( 1, 10 ) ) { case 1: if( one_in( 2 ) ) { hit = bp_foot_l; } else { hit = bp_foot_r; } break; case 2: case 3: case 4: if( one_in( 2 ) ) { hit = bp_leg_l; } else { hit = bp_leg_r; } break; case 5: case 6: case 7: case 8: case 9: hit = bp_torso; break; case 10: hit = bp_head; break; } //~ %s is bodypart n->add_msg_if_player( m_bad, _( "Your %s is hit!" ), body_part_name( hit ).c_str() ); c->deal_damage( nullptr, hit, damage_instance( DT_CUT, rng( 40 * shots, 60 * shots ) ) ); } else { n->add_msg_player_or_npc( m_neutral, _( "You dodge the shot!" ), _( "<npcname> dodges the shot!" ) ); } } else if( z != nullptr ) { bool seen = g->u.sees( *z ); int chance = 0; switch( z->type->size ) { case MS_TINY: chance = 100; break; case MS_SMALL: chance = 16; break; case MS_MEDIUM: chance = 12; break; case MS_LARGE: chance = 8; break; case MS_HUGE: chance = 2; break; } shots = ( one_in( 8 ) || one_in( chance ) ? 2 : 1 ); if( g->m.tr_at( p ).loadid == tr_shotgun_1 ) { shots = 1; } if( seen ) { add_msg( m_bad, _( "A shotgun fires and hits the %s!" ), z->name().c_str() ); } z->apply_damage( nullptr, bp_torso, rng( 40 * shots, 60 * shots ) ); } c->check_dead_state(); } if( shots == 2 || g->m.tr_at( p ).loadid == tr_shotgun_1 ) { g->m.remove_trap( p ); g->m.spawn_item( p, "shotgun_sawn" ); g->m.spawn_item( p, "string_6" ); } else { g->m.add_trap( p, tr_shotgun_1 ); } }
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 side = rng(0, 1); int dam = hit(g, p, bphit), cut = type->melee_cut, stab = 0; technique_id tech = p.pick_defensive_technique(g, this, NULL); p.perform_defensive_technique(tech, g, this, NULL, bphit, side, dam, cut, stab); //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)) { g->add_msg("The %s misses.", name().c_str()); } else { //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))) { g->add_msg("%s dodge the %s.", You.c_str(), 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 (u_see && tech != TEC_BLOCK) { g->add_msg("The %s hits %s %s.", name().c_str(), your.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 (u_see) { g->add_msg("%s offensive defense system shocks it!", Your.c_str()); } if (hurt(rng(10, 40))) die(g); } if (p.encumb(bphit) == 0 &&(p.has_trait(PF_SPINES) || p.has_trait(PF_QUILLS))) { int spine = rng(1, (p.has_trait(PF_QUILLS) ? 20 : 8)); g->add_msg("%s %s puncture it!", Your.c_str(), (g->u.has_trait(PF_QUILLS) ? "quills" : "spines")); if (hurt(spine)) die(g); } if (dam + cut <= 0) { return; // Defensive technique canceled damage. } //Hurt the player dam = p.hit(g, bphit, side, dam, cut); //Monster effects if (dam > 0 && has_flag(MF_VENOM)) { if (!is_npc) { g->add_msg("You're poisoned!"); } p.add_disease("poison", 30); } else if (dam > 0 && has_flag(MF_BADVENOM)) { if (!is_npc) { g->add_msg("You feel poison flood your body, wracking you with pain..."); } p.add_disease("badpoison", 40); } if (has_flag(MF_BLEED) && dam > 6 && cut > 0) { if (!is_npc) { g->add_msg("You're Bleeding!"); } p.add_disease("bleed", 60); } //Same as monster's chance to not miss if (can_grab && has_flag(MF_GRABS) && (rng(0, 10000) > 11000 * exp(-.3 * type->melee_skill))) { if (!is_npc) { g->add_msg("The %s grabs you!", name().c_str()); } if (p.weapon.has_technique(TEC_BREAK, &p) && dice(p.dex_cur + p.skillLevel("melee"), 12) > dice(type->melee_dice, 10)) { if (!is_npc) { g->add_msg("You break the grab!"); } } else hit_player(g, p, false); //We grabed, so hit them again } //Counter-attack? if (tech == TEC_COUNTER && !is_npc) { g->add_msg("Counter-attack!"); // A counterattack is a free action to avoid stunlocking the player. int player_moves = p.moves; hurt( p.hit_mon(g, this) ); p.moves = player_moves; } } } // 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; for (int i = 0; i < type->anger.size(); i++) { if (type->anger[i] == MTRIG_FRIEND_ATTACKED) { anger_adjust += 15; } } for (int i = 0; i < type->placate.size(); i++) { if (type->placate[i] == MTRIG_FRIEND_ATTACKED) { anger_adjust -= 15; } } for (int i = 0; i < type->fear.size(); i++) { if (type->fear[i] == MTRIG_FRIEND_ATTACKED) { morale_adjust -= 15; } } if (anger_adjust != 0 && morale_adjust != 0) { for (int i = 0; i < g->z.size(); i++) { g->z[i].morale += morale_adjust; g->z[i].anger += anger_adjust; } } }
void player::hit_player(game *g, player &p, bool allow_grab) { bool is_u = (this == &(g->u)); // Affects how we'll display messages if (is_u && p.is_npc()) { npc* npcPtr = dynamic_cast<npc*>(&p); npcPtr->make_angry(); } std::string You = (is_u ? "You" : name); std::string Your = (is_u ? "Your" : name + "'s"); std::string your = (is_u ? "your" : (male ? "his" : "her")); std::string verb = "hit"; // Divide their dodge roll by 2 if this is a grab int target_dodge = (allow_grab ? p.dodge_roll(g) : p.dodge_roll(g) / 2); int hit_value = hit_roll() - target_dodge; bool missed = (hit_roll() <= 0); int move_cost = attack_speed(*this, missed); if (missed) { int stumble_pen = stumble(*this); if (is_u) { // Only display messages if this is the player if (weapon.has_technique(TEC_FEINT, this)) g->add_msg("You feint."); else if (stumble_pen >= 60) g->add_msg("You miss and stumble with the momentum."); else if (stumble_pen >= 10) g->add_msg("You swing wildly and miss."); else g->add_msg("You miss."); } melee_practice(*this, false, unarmed_attack(), weapon.is_bashing_weapon(), weapon.is_cutting_weapon(), (weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB))); move_cost += stumble_pen; if (weapon.has_technique(TEC_FEINT, this)) move_cost = rng(move_cost / 3, move_cost); moves -= move_cost; return; } moves -= move_cost; body_part bp_hit; int side = rng(0, 1); hit_value += rng(-10, 10); if (hit_value >= 30) bp_hit = bp_eyes; else if (hit_value >= 20) bp_hit = bp_head; else if (hit_value >= 10) bp_hit = bp_torso; else if (one_in(4)) bp_hit = bp_legs; else bp_hit = bp_arms; std::string target = (p.is_npc() ? p.name + "'s " : "your "); target += body_part_name(bp_hit, side); bool critical_hit = scored_crit(target_dodge); int bash_dam = roll_bash_damage(NULL, critical_hit); int cut_dam = roll_cut_damage(NULL, critical_hit); int stab_dam = roll_stab_damage(NULL, critical_hit); technique_id tech_def = p.pick_defensive_technique(g, NULL, this); p.perform_defensive_technique(tech_def, g, NULL, this, bp_hit, side, bash_dam, cut_dam, stab_dam); if (bash_dam + cut_dam + stab_dam <= 0) return; // Defensive technique canceled our attack! if (critical_hit) // Crits cancel out Toad Style's armor boost p.rem_disease(DI_ARMOR_BOOST); int pain = 0; // Boost to pain; required for perform_technique // Moves lost to getting your weapon stuck int stuck_penalty = roll_stuck_penalty(NULL, (stab_dam >= cut_dam)); if (weapon.is_style()) stuck_penalty = 0; // Pick one or more special attacks technique_id technique = pick_technique(g, NULL, &p, critical_hit, allow_grab); // Handles effects as well; not done in melee_affect_* perform_technique(technique, g, NULL, &p, bash_dam, cut_dam, stab_dam, pain); p.pain += pain; // Mutation-based attacks perform_special_attacks(g, NULL, &p, bash_dam, cut_dam, stab_dam); // Handles speed penalties to monster & us, etc melee_special_effects(g, NULL, &p, critical_hit, bash_dam, cut_dam, stab_dam); // Make a rather quiet sound, to alert any nearby monsters if (weapon.typeId() != "style_ninjutsu") // Ninjutsu is silent! g->sound(posx, posy, 8, ""); p.hit(g, bp_hit, side, bash_dam, (cut_dam > stab_dam ? cut_dam : stab_dam)); verb = melee_verb(technique, your, *this, bash_dam, cut_dam, stab_dam); int dam = bash_dam + (cut_dam > stab_dam ? cut_dam : stab_dam); hit_message(g, You.c_str(), verb.c_str(), target.c_str(), dam, critical_hit); bool bashing = (bash_dam >= 10 && !unarmed_attack()); bool cutting = (cut_dam >= 10 && cut_dam >= stab_dam); bool stabbing = (stab_dam >= 10 && stab_dam >= cut_dam); melee_practice(*this, true, unarmed_attack(), bashing, cutting, stabbing); if (dam >= 5 && has_artifact_with(AEP_SAP_LIFE)) healall( rng(dam / 10, dam / 5) ); if (allow_grab && technique == TEC_GRAB) { // Move our weapon to a temp slot, if it's not unarmed if (p.weapon.has_technique(TEC_BREAK, &p) && dice(p.dex_cur + p.skillLevel("melee"), 12) > dice(dex_cur + skillLevel("melee"), 10)) { if (is_u) g->add_msg("%s break%s the grab!", target.c_str(), (p.is_npc() ? "s" : "")); } else if (!unarmed_attack()) { item tmpweap = remove_weapon(); hit_player(g, p, false); // False means a second grab isn't allowed weapon = tmpweap; } else hit_player(g, p, false); // False means a second grab isn't allowed } if (tech_def == TEC_COUNTER) { if (!p.is_npc()) g->add_msg("Counter-attack!"); p.hit_player(g, *this); } }
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; } } }
body_part Creature::select_body_part(Creature *source, int hit_roll) { // Get size difference (-1,0,1); int szdif = source->get_size() - get_size(); if(szdif < -1) { szdif = -1; } else if (szdif > 1) { szdif = 1; } add_msg( m_debug, "source size = %d", source->get_size() ); add_msg( m_debug, "target size = %d", get_size() ); add_msg( m_debug, "difference = %d", szdif ); std::map<body_part, double> hit_weights = default_hit_weights[szdif]; std::map<body_part, double>::iterator iter; // If the target is on the ground, even small/tiny creatures may target eyes/head. Also increases chances of larger creatures. // Any hit modifiers to locations should go here. (Tags, attack style, etc) if(is_on_ground()) { hit_weights[bp_eyes] += 10; hit_weights[bp_head] += 20; } //Adjust based on hit roll: Eyes, Head & Torso get higher, while Arms and Legs get lower. //This should eventually be replaced with targeted attacks and this being miss chances. hit_weights[bp_eyes] = floor(hit_weights[bp_eyes] * std::pow(hit_roll, 1.15) * 10); hit_weights[bp_head] = floor(hit_weights[bp_head] * std::pow(hit_roll, 1.15) * 10); hit_weights[bp_torso] = floor(hit_weights[bp_torso] * std::pow(hit_roll, 1) * 10); hit_weights[bp_arm_l] = floor(hit_weights[bp_arm_l] * std::pow(hit_roll, 0.95) * 10); hit_weights[bp_arm_r] = floor(hit_weights[bp_arm_r] * std::pow(hit_roll, 0.95) * 10); hit_weights[bp_leg_l] = floor(hit_weights[bp_leg_l] * std::pow(hit_roll, 0.975) * 10); hit_weights[bp_leg_r] = floor(hit_weights[bp_leg_r] * std::pow(hit_roll, 0.975) * 10); // Debug for seeing weights. add_msg( m_debug, "eyes = %f", hit_weights.at( bp_eyes ) ); add_msg( m_debug, "head = %f", hit_weights.at( bp_head ) ); add_msg( m_debug, "torso = %f", hit_weights.at( bp_torso ) ); add_msg( m_debug, "arm_l = %f", hit_weights.at( bp_arm_l ) ); add_msg( m_debug, "arm_r = %f", hit_weights.at( bp_arm_r ) ); add_msg( m_debug, "leg_l = %f", hit_weights.at( bp_leg_l ) ); add_msg( m_debug, "leg_r = %f", hit_weights.at( bp_leg_r ) ); double totalWeight = 0; std::set<std::pair<body_part, double>, weight_compare> adjusted_weights; for(iter = hit_weights.begin(); iter != hit_weights.end(); ++iter) { totalWeight += iter->second; adjusted_weights.insert(*iter); } body_part selected_part = bp_torso; // Blood thirsty monsters can discard body part and go to more damaged int part_rolls = 1; int repick_chance = 50; if (source->has_flag(MF_BLOODTHIRSTY)) { part_rolls += 2; if (is_player() && g->u.has_trait("ANIMALEMPATH")) { part_rolls -= 1; repick_chance -= 10; } if (is_player() && g->u.has_trait("ANIMALDISCORD")) { part_rolls += 1; repick_chance += 10; } } body_part last_part = selected_part; for(int r = 0; r < part_rolls; ++r) { double roll = rng_float(1, totalWeight); std::set<std::pair<body_part, double>, weight_compare>::iterator adj_iter; for(adj_iter = adjusted_weights.begin(); adj_iter != adjusted_weights.end(); ++adj_iter) { roll -= adj_iter->second; if(roll <= 0) { selected_part = adj_iter->first; break; } } if (r != 0) { hp_part hpart_cur = bodypart_to_hp_part(selected_part); hp_part hpart_lst = bodypart_to_hp_part(last_part); double ratio_cur = get_hp(hpart_cur) / float(get_hp_max(hpart_cur)); double ratio_lst = get_hp(hpart_lst) / float(get_hp_max(hpart_lst)); body_part cur_pick_part = selected_part; if(ratio_cur > ratio_lst && repick_chance >= rng(1,100)) selected_part = last_part; add_msg( m_debug, "picked %s from %s(%.2f)/%s(%.2f)", body_part_name(selected_part).c_str(), body_part_name(cur_pick_part).c_str(), ratio_cur, body_part_name(last_part).c_str(), ratio_lst); } last_part = selected_part; } return selected_part; }