int player::hit_mon(game *g, monster *z, bool allow_grab) // defaults to true { bool is_u = (this == &(g->u)); // Affects how we'll display messages if (is_u) z->add_effect(ME_HIT_BY_PLAYER, 100); // Flag as attacked by us 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"; std::string target = "the " + z->name(); // If !allow_grab, then we already grabbed them--meaning their dodge is hampered int mondodge = (allow_grab ? z->dodge_roll() : z->dodge_roll() / 3); bool missed = (hit_roll() < mondodge || one_in(4 + dex_cur + weapon.type->m_to_hit)); 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 0; } moves -= move_cost; bool critical_hit = scored_crit(mondodge); int bash_dam = roll_bash_damage(z, critical_hit); int cut_dam = roll_cut_damage(z, critical_hit); int stab_dam = roll_stab_damage(z, critical_hit); int pain = 0; // Boost to pain; required for perform_technique // Moves lost to getting your weapon stuck int stuck_penalty = roll_stuck_penalty(z, (stab_dam >= cut_dam)); if (weapon.is_style()) stuck_penalty = 0; // Pick one or more special attacks technique_id technique = pick_technique(g, z, NULL, critical_hit, allow_grab); // Handles effects as well; not done in melee_affect_* perform_technique(technique, g, z, NULL, bash_dam, cut_dam, stab_dam, pain); z->speed -= int(pain / 2); // Mutation-based attacks perform_special_attacks(g, z, NULL, bash_dam, cut_dam, stab_dam); // Handles speed penalties to monster & us, etc melee_special_effects(g, z, NULL, 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, ""); 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 (allow_grab && technique == TEC_GRAB) { // Move our weapon to a temp slot, if it's not unarmed if (!unarmed_attack()) { item tmpweap = remove_weapon(); dam += hit_mon(g, z, false); // False means a second grab isn't allowed weapon = tmpweap; } else dam += hit_mon(g, z, false); // False means a second grab isn't allowed } if (dam >= 5 && has_artifact_with(AEP_SAP_LIFE)) healall( rng(dam / 10, dam / 5) ); return dam; }
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); } }
int player::hit_mon(game *g, monster *z) { bool is_u = (this == &(g->u)); // Affects how we'll display messages int j; bool can_see = (is_u || g->u_see(posx, posy, j)); std::string You = (is_u ? "You" : name); std::string Your = (is_u ? "Your" : name + "'s"); std::string your = (is_u ? "your" : (male ? "his" : "her")); // Types of combat (may overlap!) bool unarmed = unarmed_attack(), bashing = weapon.is_bashing_weapon(), cutting = weapon.is_cutting_weapon(), stabbing = (weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB) ); // Recoil penalty if (recoil <= 30) recoil += 6; // Movement cost moves -= weapon.attack_time() + 20 * encumb(bp_torso); // Different sizes affect your chance to hit if (hit_roll() < z->dodge_roll() || one_in(4 + dex_cur + weapon.type->m_to_hit)) {// A miss! stumble(g); return 0; } // For very high hit rolls, we crit! bool critical_hit = scored_crit(z->dodge_roll()); int dam = base_damage(true); int cutting_penalty = 0; // Moves lost from getting a cutting weapon stuck // Drunken Master damage bonuses if (has_trait(PF_DRUNKEN) && has_disease(DI_DRUNK)) { // Remember, a single drink gives 600 levels of DI_DRUNK int mindrunk, maxdrunk; if (unarmed) { mindrunk = disease_level(DI_DRUNK) / 600; maxdrunk = disease_level(DI_DRUNK) / 250; } else { mindrunk = disease_level(DI_DRUNK) / 900; maxdrunk = disease_level(DI_DRUNK) / 400; } dam += rng(mindrunk, maxdrunk); } if (unarmed) { // Unarmed bonuses dam += rng(0, sklevel[sk_unarmed]); if (has_trait(PF_TALONS) && z->type->armor - sklevel[sk_unarmed] < 10) { int z_armor = (z->type->armor - sklevel[sk_unarmed]); if (z_armor < 0) z_armor = 0; dam += 10 - z_armor; } } if (rng(1, 45 - dex_cur) < 2 * sklevel[sk_unarmed] && rng(1, 65 - dex_cur) < 2 * sklevel[sk_unarmed] ) { // Bonus unarmed attack! if (is_u || can_see) { switch (rng(1, 4)) { case 1: g->add_msg("%s kick%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); break; case 2: g->add_msg("%s headbutt%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); break; case 3: g->add_msg("%s elbow%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); break; case 4: g->add_msg("%s knee%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); break; } } if (sklevel[sk_unarmed] >= 4) dam += rng(1, sklevel[sk_unarmed] / 2); else dam++; practice(sk_unarmed, 2); } // Melee skill bonus dam += rng(0, sklevel[sk_melee]); // Bashing damage bonus int bash_dam = weapon.damage_bash(), bash_cap = 5 + str_cur + sklevel[sk_bashing]; if (bash_dam > bash_cap)// Cap for weak characters bash_dam = (bash_cap * 3 + bash_dam) / 4; if (bashing) bash_dam += rng(0, sklevel[sk_bashing] + sqrt(double(str_cur))); if (z->has_flag(MF_PLASTIC)) bash_dam /= rng(2, 4); int bash_min = bash_dam / 4; if (bash_min < sklevel[sk_bashing] ) bash_min = sklevel[sk_bashing]; dam += rng(bash_min, bash_dam); // Take some moves away from the target; at this point it's skill & bash damage z->moves -= rng(0, dam * 2); // Spears treat cutting damage specially. if (weapon.has_flag(IF_SPEAR) && weapon.damage_cut() > z->type->armor - int(sklevel[sk_stabbing])) { int z_armor = z->type->armor - int(sklevel[sk_stabbing]); dam += int(weapon.damage_cut() / 5); int minstab = sklevel[sk_stabbing] * 5 + weapon.volume() * 2, maxstab = sklevel[sk_stabbing] * 15 + weapon.volume() * 4; int monster_penalty = rng(minstab, maxstab); if (monster_penalty >= 150) g->add_msg("You force the %s to the ground!", z->name().c_str()); else if (monster_penalty >= 50) g->add_msg("The %s is skewered and flinches!", z->name().c_str()); z->moves -= monster_penalty; cutting_penalty = weapon.damage_cut() * 4 + z_armor * 8 - dice(sklevel[sk_stabbing], 10); practice(sk_stabbing, 2); // Cutting damage bonus } else if (weapon.damage_cut() > z->type->armor - int(sklevel[sk_cutting] / 2)) { int z_armor = z->type->armor - int(sklevel[sk_cutting] / 2); if (z_armor < 0) z_armor = 0; dam += weapon.damage_cut() - z_armor; cutting_penalty = weapon.damage_cut() * 3 + z_armor * 8 - dice(sklevel[sk_cutting], 10); } if (weapon.has_flag(IF_MESSY)) { // e.g. chainsaws cutting_penalty /= 6; // Harder to get stuck for (int x = z->posx - 1; x <= z->posx + 1; x++) { for (int y = z->posy - 1; y <= z->posy + 1; y++) { if (!one_in(3)) { if (g->m.field_at(x, y).type == fd_blood && g->m.field_at(x, y).density < 3) g->m.field_at(x, y).density++; else g->m.add_field(g, x, y, fd_blood, 1); } } } } // Bonus attacks! bool shock_them = (has_bionic(bio_shock) && power_level >= 2 && unarmed && one_in(3)); bool drain_them = (has_bionic(bio_heat_absorb) && power_level >= 1 && !is_armed() && z->has_flag(MF_WARM)); bool bite_them = (has_trait(PF_FANGS) && z->armor() < 18 && one_in(20 - dex_cur - sklevel[sk_unarmed])); bool peck_them = (has_trait(PF_BEAK) && z->armor() < 16 && one_in(15 - dex_cur - sklevel[sk_unarmed])); if (drain_them) power_level--; drain_them &= one_in(2); // Only works half the time // Critical hit effects if (critical_hit) { bool headshot = (!z->has_flag(MF_NOHEAD) && !one_in(3)); // Second chance for shock_them, drain_them, bite_them and peck_them shock_them = (shock_them || (has_bionic(bio_shock)&& power_level >= 2 && unarmed && !one_in(3))); drain_them = (drain_them || (has_bionic(bio_heat_absorb) && !is_armed() && power_level >= 1 && z->has_flag(MF_WARM) && !one_in(3))); bite_them = ( bite_them || (has_trait(PF_FANGS) && z->armor() < 18 && one_in(5))); peck_them = ( peck_them || (has_trait(PF_BEAK) && z->armor() < 16 && one_in(4))); if (weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB)) { dam += weapon.damage_cut(); dam += weapon.damage_cut() * double(sklevel[sk_stabbing] / 10); practice(sk_stabbing, 5); } if (unarmed) { dam += rng(1, 4) * sklevel[sk_unarmed]; if (sklevel[sk_unarmed] > 5) dam += 4 * (sklevel[sk_unarmed] - 5); z->moves -= dam; // Stunning blow if (weapon.type->id == itm_bio_claws) { if (sklevel[sk_cutting] >= 3) dam += 5; headshot &= z->hp < dam && one_in(2); if (headshot && can_see) g->add_msg("%s claws pierce the %s's skull!", Your.c_str(), z->name().c_str()); else if (can_see) g->add_msg("%s claws stab straight through the %s!", Your.c_str(), z->name().c_str()); } else if (has_trait(PF_TALONS)) { dam += 2; headshot &= z->hp < dam && one_in(2); if (headshot && can_see) g->add_msg("%s talons tear the %s's head open!", Your.c_str(), z->name().c_str()); else if (can_see) g->add_msg("%s bur%s %s talons into the %s!", You.c_str(),(is_u?"y":"ies"), your.c_str(), z->name().c_str()); } else { headshot &= z->hp < dam && one_in(2); if (headshot && can_see) g->add_msg("%s crush%s the %s's skull in a single blow!", You.c_str(), (is_u ? "" : "es"), z->name().c_str()); else if (can_see) g->add_msg("%s deliver%s a crushing punch!",You.c_str(),(is_u ? "" : "s")); } if (z->hp > 0 && rng(1, 5) < sklevel[sk_unarmed]) z->add_effect(ME_STUNNED, 1 + sklevel[sk_unarmed]); } else { // Not unarmed if (bashing) { dam += 8 + (str_cur / 2); int turns_stunned = int(dam / 20) + int(sklevel[sk_bashing] / 2); if (turns_stunned > 6) turns_stunned = 6; z->add_effect(ME_STUNNED, turns_stunned); } if (cutting || stabbing) { double cut_multiplier = double(sklevel[sk_cutting] / 12); if (cut_multiplier > 1.5) cut_multiplier = 1.5; dam += cut_multiplier * weapon.damage_cut(); headshot &= z->hp < dam; if (stabbing) { if (headshot && can_see) g->add_msg("%s %s stabs through the %s's skull!", Your.c_str(), weapon.tname(g).c_str(), z->name().c_str()); else if (can_see) g->add_msg("%s stab %s %s through the %s!", You.c_str(), your.c_str(), weapon.tname(g).c_str(), z->name().c_str()); } else { if (headshot && can_see) g->add_msg("%s %s slices the %s's head off!", Your.c_str(), weapon.tname(g).c_str(), z->name().c_str()); else g->add_msg("%s %s cuts the %s deeply!", Your.c_str(), weapon.tname(g).c_str(), z->name().c_str()); } } else if (bashing) { headshot &= z->hp < dam; if (headshot && can_see) g->add_msg("%s crush%s the %s's skull!", You.c_str(), (is_u ? "" : "es"), z->name().c_str()); else if (can_see) g->add_msg("%s crush%s the %s's body!", You.c_str(), (is_u ? "" : "es"), z->name().c_str()); } } // End of not-unarmed } // End of critical hit if (shock_them) { power_level -= 2; if (can_see) g->add_msg("%s shock%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); int shock = rng(2, 5); dam += shock * rng(1, 3); z->moves -= shock * 180; } if (drain_them) { charge_power(rng(0, 4)); if (can_see) g->add_msg("%s drain%s the %s's body heat!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); dam += rng(4, 10); z->moves -= rng(80, 120); } if (bite_them) { if (can_see) g->add_msg("%s sink %s fangs into the %s!", You.c_str(), your.c_str(), z->name().c_str()); dam += 18 - z->armor(); } if (peck_them) { if (can_see) g->add_msg("%s peck%s the %s viciously!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); dam += 16 - z->armor(); } // Make a rather quiet sound, to alert any nearby monsters g->sound(posx, posy, 8, ""); // Glass weapons shatter sometimes if (weapon.made_of(GLASS) && rng(0, weapon.volume() + 8) < weapon.volume() + str_cur) { if (can_see) g->add_msg("%s %s shatters!", Your.c_str(), weapon.tname(g).c_str()); g->sound(posx, posy, 16, ""); // Dump its contents on the ground for (int i = 0; i < weapon.contents.size(); i++) g->m.add_item(posx, posy, weapon.contents[i]); hit(g, bp_arms, 1, 0, rng(0, weapon.volume() * 2));// Take damage if (weapon.is_two_handed(this))// Hurt left arm too, if it was big hit(g, bp_arms, 0, 0, rng(0, weapon.volume())); dam += rng(0, 5 + int(weapon.volume() * 1.5));// Hurt the monster extra remove_weapon(); } if (dam <= 0) { if (is_u) g->add_msg("You hit the %s, but do no damage.", z->name().c_str()); else if (can_see) g->add_msg("%s's %s hits the %s, but does no damage.", You.c_str(), weapon.tname(g).c_str(), z->name().c_str()); practice(sk_melee, rng(2, 5)); if (unarmed) practice(sk_unarmed, 2); if (bashing) practice(sk_bashing, 2); if (cutting) practice(sk_cutting, 2); if (stabbing) practice(sk_stabbing, 2); return 0; } if (is_u) g->add_msg("You hit the %s for %d damage.", z->name().c_str(), dam); else if (can_see) g->add_msg("%s hits the %s with %s %s.", You.c_str(), z->name().c_str(), (male ? "his" : "her"), (weapon.type->id == 0 ? "fists" : weapon.tname(g).c_str())); practice(sk_melee, rng(5, 10)); if (unarmed) practice(sk_unarmed, rng(5, 10)); if (bashing) practice(sk_bashing, rng(5, 10)); if (cutting) practice(sk_cutting, rng(5, 10)); if (stabbing) practice(sk_stabbing, rng(5, 10)); // Penalize the player if their cutting weapon got stuck if (!unarmed && dam < z->hp && cutting_penalty > dice(str_cur * 2, 20)) { if (is_u) g->add_msg("Your %s gets stuck in the %s, pulling it out of your hands!", weapon.tname().c_str(), z->type->name.c_str()); z->add_item(remove_weapon()); if (weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB)) z->speed *= .7; else z->speed *= .85; } else { if (dam >= z->hp) { cutting_penalty /= 2; cutting_penalty -= rng(sklevel[sk_cutting], sklevel[sk_cutting] * 2 + 2); } if (cutting_penalty > 0) moves -= cutting_penalty; if (cutting_penalty >= 50 && is_u) g->add_msg("Your %s gets stuck in the %s, but you yank it free.", weapon.tname().c_str(), z->type->name.c_str()); if (weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB)) z->speed *= .9; } return dam; }