void Entity::attack(Entity* target) { if (!target) { debugmsg("'%s' attempted attack() on a null target."); return; } Attack att = std_attack(); action_points -= att.speed; bool you_see = GAME.player->can_sense(GAME.map, posx, posy); bool attacker_is_you = is_you(); std::string miss_verb = (attacker_is_you ? "miss" : "misses"); if (hit_roll(att.to_hit) < target->dodge_roll()) { if (you_see) { std::stringstream msg; msg << get_name_to_player() << " " << miss_verb << " " << target->get_name_to_player() << "!"; GAME.add_msg( msg.str().c_str() ); } // TODO: action_point penalty for missing? return; } Body_part bp_hit = (target->is_player() ? random_body_part_to_hit() : BODYPART_NULL); // TODO: Should total_damage be reduced by damage absorbed by armor? Damage_set damage = att.roll_damage(); for (int i = 0; i < DAMAGE_MAX; i++) { int dam = damage.get_damage(i); target->take_damage(Damage_type(i), dam, get_name_to_player(), bp_hit); } if (you_see) { std::stringstream damage_ss; damage_ss << get_name_to_player() << " "; if (attacker_is_you) { damage_ss << att.verb_first; } else { damage_ss << att.verb_third; } damage_ss << " "; if (bp_hit == BODYPART_NULL) { damage_ss << target->get_name_to_player(); } else { damage_ss << target->get_possessive() << " " << body_part_name(bp_hit); } if (target->is_you()) { damage_ss << " for " << damage.total_damage() << " damage"; } damage_ss << "!"; GAME.add_msg( damage_ss.str().c_str() ); } }
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; 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); } } dealt_damage_instance dealt_dam; int hitspread = target.deal_melee_attack(this, hit_roll()); 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 %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) { //~ 1$s is attacker name, 2$s is bodypart name in accusative. add_msg(m_bad, _("The %1$s hits your %2$s."), name().c_str(), body_part_name_accusative(bp_hit).c_str()); } else { //~ %s is bodypart name in accusative. add_msg(m_bad, _("Something hits your %s."), body_part_name_accusative(bp_hit).c_str()); } } else { if (u_see_me) { //~ 1$s is attacker name, 2$s is target name, 3$s is bodypart name in accusative. add_msg(_("The %1$s hits %2$s %3$s."), name().c_str(), target.disp_name(true).c_str(), body_part_name_accusative(bp_hit).c_str()); } } } else { if (target.is_player()) { if (u_see_me) { //~ 1$s is attacker name, 2$s is bodypart name in accusative, 3$s is armor name add_msg(_("The %1$s hits your %2$s, but your %3$s protects you."), name().c_str(), body_part_name_accusative(bp_hit).c_str(), target.skin_name().c_str()); } else { //~ 1$s is bodypart name in accusative, 2$s is armor name. add_msg(_("Something hits your %1$s, but your %2$s protects you."), body_part_name_accusative(bp_hit).c_str(), target.skin_name().c_str()); } } else { if (u_see_me) { //~ $1s is monster name, %2$s is that monster target name, //~ $3s is target bodypart name in accusative, 4$s is target armor name. 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_accusative(bp_hit).c_str(), target.skin_name().c_str()); } } } if (is_hallucination()) { if(one_in(7)) { die( nullptr ); } 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 (size_t i = 0; i < g->num_zombies(); i++) { g->zombie(i).morale += morale_adjust; g->zombie(i).anger += anger_adjust; } } }
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_weapon_flag(WF_SPEAR) || weapon.has_weapon_flag(WF_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()) {// A miss! stumble(g); return 0; } if (z->has_flag(MF_SHOCK) && !wearing_something_on(bp_hands) && (unarmed || weapon.conductive())) { if (is_u) g->add_msg("The %s's electric body shocks you!", z->name().c_str()); hurtall(rng(1, 3)); } // For very high hit rolls, we crit! bool critical_hit = (hit_roll() >= 50 + 10 * 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 if (unarmed) dam += disease_level(DI_DRUNK) / 250; else dam += disease_level(DI_DRUNK) / 400; } 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; } } else if (rng(1, 45 - dex_cur) < 2 * sklevel[sk_unarmed] && rng(1, 65 - dex_cur) < 2 * sklevel[sk_unarmed] ) { // If we're not unarmed, there's still a possibility of getting in a 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; } } dam += rng(1, sklevel[sk_unarmed]); practice(sk_unarmed, 2); } // Melee skill bonus dam += rng(0, sklevel[sk_melee]); // Bashing damage bonus int bash_dam = weapon.type->melee_dam, 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(str_cur); int bash_min = bash_dam / 4; if (bash_min < sklevel[sk_bashing] * 2) bash_min = sklevel[sk_bashing] * 2; dam += rng(bash_dam / 4, 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_weapon_flag(WF_SPEAR) && weapon.type->melee_cut > z->type->armor - int(sklevel[sk_stabbing])) { int z_armor = z->type->armor - int(sklevel[sk_stabbing]); dam += int(weapon.type->melee_cut / 5); int minstab = sklevel[sk_stabbing] * 8 + weapon.type->melee_cut * 2, maxstab = sklevel[sk_stabbing] * 20 + weapon.type->melee_cut * 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 >= 80) g->add_msg("The %s is skewered and flinches!", z->name().c_str()); z->moves -= monster_penalty; cutting_penalty = weapon.type->melee_cut * 4 + z_armor * 8 - dice(sklevel[sk_stabbing], 10); practice(sk_stabbing, 2); // Cutting damage bonus } else if (weapon.type->melee_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.type->melee_cut - z_armor; cutting_penalty = weapon.type->melee_cut * 3 + z_armor * 8 - dice(sklevel[sk_cutting], 10); } if (weapon.has_weapon_flag(WF_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 = (!z->has_flag(MF_SHOCK) && 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 || (!z->has_flag(MF_SHOCK) && 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_weapon_flag(WF_SPEAR) || weapon.has_weapon_flag(WF_STAB)) { dam += weapon.type->melee_cut; dam += weapon.type->melee_cut * double(sklevel[sk_stabbing] / 10); practice(sk_stabbing, 5); } if (unarmed) { dam += rng(2, 6) * sklevel[sk_unarmed]; if (sklevel[sk_unarmed] > 5) dam += 4 * (sklevel[sk_unarmed - 3]); 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) { double cut_multiplier = double(sklevel[sk_cutting] / 12); if (cut_multiplier > 1.5) cut_multiplier = 1.5; dam += cut_multiplier * weapon.type->melee_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 { // Not cutting, probably 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_weapon_flag(WF_SPEAR) || weapon.has_weapon_flag(WF_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_weapon_flag(WF_SPEAR) || weapon.has_weapon_flag(WF_STAB)) z->speed *= .9; } return dam; }
bool player::hit_player(player &p, body_part &bp, int &hitdam, int &hitcut) { // TODO: Add bionics and other bonus (e.g. heat drain, shock, etc) if (!is_npc() && p.is_npc()) { npc *foe = dynamic_cast<npc*>(&p); foe->make_angry(); } bool unarmed = unarmed_attack(), bashing = weapon.is_bashing_weapon(), cutting = weapon.is_cutting_weapon(); int hitit = hit_roll() - p.dodge_roll(); if (hitit < 0) { // They dodged practice(sk_melee, rng(2, 4)); if (unarmed) practice(sk_unarmed, 3); if (bashing) practice(sk_bashing, 1); if (cutting) practice(sk_cutting, 2); return false; } if (hitit >= 15) bp = bp_eyes; else if (hitit >= 12) bp = bp_mouth; else if (hitit >= 10) bp = bp_head; else if (hitit >= 6) bp = bp_torso; else if (hitit >= 2) bp = bp_arms; else bp = bp_legs; hitdam = base_damage(); if (unarmed) {// Unarmed bonuses hitdam += rng(0, sklevel[sk_unarmed]); if (sklevel[sk_unarmed] >= 5) hitdam += rng(sklevel[sk_unarmed], 3 * sklevel[sk_unarmed]); if (has_trait(PF_TALONS)) hitcut += 10; if (sklevel[sk_unarmed] >= 8 && (one_in(3) || rng(5, 20) < sklevel[sk_unarmed])) hitdam *= rng(2, 3); } // Weapon adds (melee_dam / 4) to (melee_dam) hitdam += rng(weapon.type->melee_dam / 4, weapon.type->melee_dam); if (bashing) hitdam += rng(0, sklevel[sk_bashing]) * sqrt(str_cur); hitdam += int(pow(1.5, sklevel[sk_melee])); hitcut = weapon.type->melee_cut; if (hitcut > 0) hitcut += int(sklevel[sk_cutting] / 3); if (hitdam < 0) hitdam = 0; if (hitdam > 0 || hitcut > 0) { // Practicing 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)); } else { // Less practice if we missed practice(sk_melee, rng(2, 5)); if (unarmed) practice(sk_unarmed, 2); if (bashing) practice(sk_bashing, 2); if (cutting) practice(sk_cutting, 3); } return true; }
technique_id player::pick_defensive_technique(game *g, monster *z, player *p) { if (blocks_left == 0) return TEC_NULL; int foe_melee_skill = 0; if (z != NULL) foe_melee_skill = z->type->melee_skill; else if (p != NULL) foe_melee_skill = p->dex_cur + p->skillLevel("melee"); int foe_dodge = 0; if (z != NULL) foe_dodge = z->dodge_roll(); else if (p != NULL) foe_dodge = p->dodge_roll(g); int foe_size = 0; if (z) foe_size = 4 + z->type->size * 4; else if (p) { foe_size = 12; if (p->str_max <= 5) foe_size -= 3; if (p->str_max >= 12) foe_size += 3; } blocks_left--; if (weapon.has_technique(TEC_WBLOCK_3) && dice(dex_cur + skillLevel("melee"), 12) > dice(foe_melee_skill, 10)) return TEC_WBLOCK_3; if (weapon.has_technique(TEC_WBLOCK_2) && dice(dex_cur + skillLevel("melee"), 6) > dice(foe_melee_skill, 10)) return TEC_WBLOCK_2; if (weapon.has_technique(TEC_WBLOCK_1) && dice(dex_cur + skillLevel("melee"), 3) > dice(foe_melee_skill, 10)) return TEC_WBLOCK_1; if (weapon.has_technique(TEC_DEF_DISARM, this) && z == NULL && p->weapon.typeId() != "null" && !p->weapon.has_flag(IF_UNARMED_WEAPON) && dice( dex_cur + skillLevel("unarmed"), 8) > dice(p->dex_cur + p->skillLevel("melee"), 10)) return TEC_DEF_DISARM; if (weapon.has_technique(TEC_DEF_THROW, this) && str_cur + skillLevel("melee") >= foe_size + rng(-4, 4) && hit_roll() > rng(1, 5) + foe_dodge && !one_in(3)) return TEC_DEF_THROW; if (weapon.has_technique(TEC_COUNTER, this) && hit_roll() > rng(1, 10) + foe_dodge && !one_in(3)) return TEC_COUNTER; if (weapon.has_technique(TEC_BLOCK_LEGS, this) && (hp_cur[hp_leg_l] >= 20 || hp_cur[hp_leg_r] >= 20) && dice(dex_cur + skillLevel("unarmed") + skillLevel("melee"), 13) > dice(8 + foe_melee_skill, 10)) return TEC_BLOCK_LEGS; if (weapon.has_technique(TEC_BLOCK, this) && (hp_cur[hp_arm_l] >= 20 || hp_cur[hp_arm_r] >= 20) && dice(dex_cur + skillLevel("unarmed") + skillLevel("melee"), 16) > dice(6 + foe_melee_skill, 10)) return TEC_BLOCK; blocks_left++; // We didn't use any blocks, so give it back! return TEC_NULL; }
void player::perform_technique(technique_id technique, game *g, monster *z, player *p, int &bash_dam, int &cut_dam, int &stab_dam, int &pain) { bool mon = (z != NULL); std::string You = (is_npc() ? name : "You"); std::string target = (mon ? "the " + z->name() : (p->is_npc() ? p->name : "you")); std::string s = (is_npc() ? "s" : ""); int tarx = (mon ? z->posx : p->posx), tary = (mon ? z->posy : p->posy); int junk; bool u_see = (!is_npc() || g->u_see(posx, posy, junk)); if (technique == TEC_RAPID) { moves += int( attack_speed(*this, false) / 2); return; } if (technique == TEC_BLOCK) { bash_dam *= .7; return; } // The rest affect our target, and thus depend on z vs. p switch (technique) { case TEC_SWEEP: if (z != NULL && !z->has_flag(MF_FLIES)) { z->add_effect(ME_DOWNED, rng(1, 2)); bash_dam += z->fall_damage(); } else if (p != NULL && p->weapon.typeId() != "style_judo") { p->add_disease(DI_DOWNED, rng(1, 2), g); bash_dam += 3; } break; case TEC_PRECISE: if (z != NULL) z->add_effect(ME_STUNNED, rng(1, 4)); else if (p != NULL) p->add_disease(DI_STUNNED, rng(1, 2), g); pain += rng(5, 8); break; case TEC_BRUTAL: if (z != NULL) { z->add_effect(ME_STUNNED, 1); z->knock_back_from(g, posx, posy); } else if (p != NULL) { p->add_disease(DI_STUNNED, 1, g); p->knock_back_from(g, posy, posy); } break; case TEC_THROW: // Throws are less predictable than brutal strikes. // We knock them back from a tile adjacent to us! if (z != NULL) { z->add_effect(ME_DOWNED, rng(1, 2)); z->knock_back_from(g, posx + rng(-1, 1), posy + rng(-1, 1)); } else if (p != NULL) { p->knock_back_from(g, posx + rng(-1, 1), posy + rng(-1, 1)); if (p->weapon.typeId() != "style_judo") p->add_disease(DI_DOWNED, rng(1, 2), g); } break; case TEC_WIDE: { int count_hit = 0; for (int x = posx - 1; x <= posx + 1; x++) { for (int y = posy - 1; y <= posy + 1; y++) { if (x != tarx || y != tary) { // Don't double-hit our target int mondex = g->mon_at(x, y); if (mondex != -1 && hit_roll() >= rng(0, 5) + g->z[mondex].dodge_roll()) { count_hit++; int dam = roll_bash_damage(&(g->z[mondex]), false) + roll_cut_damage (&(g->z[mondex]), false); g->z[mondex].hurt(dam); if (u_see) g->add_msg("%s hit%s %s for %d damage!", You.c_str(), s.c_str(), target.c_str(), dam); } int npcdex = g->npc_at(x, y); if (npcdex != -1 && hit_roll() >= rng(0, 5) + g->active_npc[npcdex].dodge_roll(g)) { count_hit++; int dam = roll_bash_damage(NULL, false); int cut = roll_cut_damage (NULL, false); g->active_npc[npcdex].hit(g, bp_legs, 3, dam, cut); if (u_see) g->add_msg("%s hit%s %s for %d damage!", You.c_str(), s.c_str(), g->active_npc[npcdex].name.c_str(), dam + cut); } } } } if (!is_npc()) g->add_msg("%d enemies hit!", count_hit); } break; case TEC_DISARM: g->m.add_item(p->posx, p->posy, p->remove_weapon()); if (u_see) g->add_msg("%s disarm%s %s!", You.c_str(), s.c_str(), target.c_str()); break; } // switch (tech) }
bool player::scored_crit(int target_dodge) { int num_crits = 0; // Weapon to-hit roll int chance = 25; if (unarmed_attack()) { // Unarmed attack: 1/2 of unarmed skill is to-hit for (int i = 1; i <= int(skillLevel("unarmed") * .5); i++) chance += (50 / (2 + i)); } if (weapon.type->m_to_hit > 0) { for (int i = 1; i <= weapon.type->m_to_hit; i++) chance += (50 / (2 + i)); } else if (chance < 0) { for (int i = 0; i > weapon.type->m_to_hit; i--) chance /= 2; } if (rng(0, 99) < chance + 4 * disease_intensity(DI_ATTACK_BOOST)) num_crits++; // Dexterity to-hit roll // ... except sometimes we don't use dexteiry! int stat = dex_cur; // Some martial arts use something else to determine hits! if(weapon.typeId() == "style_tiger"){ stat = (str_cur * 2 + dex_cur) / 3; } else if(weapon.typeId() == "style_leopard"){ stat = (per_cur + int_cur + dex_cur * 2) / 4; } else if(weapon.typeId() == "style_snake"){ stat = (per_cur + dex_cur) / 2; } chance = 25; if (stat > 8) { for (int i = 9; i <= stat; i++) chance += (21 - i); // 12, 11, 10... } else { int decrease = 5; for (int i = 7; i >= stat; i--) { chance -= decrease; if (i % 2 == 0) decrease--; } } if (rng(0, 99) < chance) num_crits++; // Skill level roll int best_skill = 0; if (weapon.is_bashing_weapon() && skillLevel("bashing") > best_skill) best_skill = skillLevel("bashing"); if (weapon.is_cutting_weapon() && skillLevel("cutting") > best_skill) best_skill = skillLevel("cutting"); if ((weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB)) && skillLevel("stabbing") > best_skill) best_skill = skillLevel("stabbing"); if (unarmed_attack() && skillLevel("unarmed") > best_skill) best_skill = skillLevel("unarmed"); best_skill += int(skillLevel("melee") / 2.5); chance = 25; if (best_skill > 3) { for (int i = 3; i < best_skill; i++) chance += (50 / (2 + i)); } else if (chance < 3) { for (int i = 3; i > best_skill; i--) chance /= 2; } if (rng(0, 99) < chance + 4 * disease_intensity(DI_ATTACK_BOOST)) num_crits++; if (num_crits == 3) return true; else if (num_crits == 2) return (hit_roll() >= target_dodge * 1.5 && !one_in(4)); return false; }
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 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; }
bool player::scored_crit(int target_dodge) { bool to_hit_crit = false, dex_crit = false, skill_crit = false; int num_crits = 0; int chance = 25; if (weapon.type->m_to_hit > 0) { for (int i = 1; i <= weapon.type->m_to_hit; i++) chance += (50 / (2 + i)); } else if (chance < 0) { for (int i = 0; i > weapon.type->m_to_hit; i--) chance /= 2; } if (rng(0, 99) < chance) num_crits++; chance = 25; if (dex_cur > 8) { for (int i = 9; i <= dex_cur; i++) chance += (21 - i); // 12, 11, 10... } else { int decrease = 5; for (int i = 7; i >= dex_cur; i--) { chance -= decrease; if (i % 2 == 0) decrease--; } } if (rng(0, 99) < chance) num_crits++; int best_skill = 0; if (weapon.is_bashing_weapon() && sklevel[sk_bashing] > best_skill) best_skill = sklevel[sk_bashing]; if (weapon.is_cutting_weapon() && sklevel[sk_cutting] > best_skill) best_skill = sklevel[sk_cutting]; if ((weapon.has_flag(IF_SPEAR) || weapon.has_flag(IF_STAB)) && sklevel[sk_stabbing] > best_skill) best_skill = sklevel[sk_stabbing]; if (unarmed_attack() && sklevel[sk_unarmed] > best_skill) best_skill = sklevel[sk_unarmed]; best_skill += int(sklevel[sk_melee] / 2.5); chance = 25; if (best_skill > 3) { for (int i = 3; i < best_skill; i++) chance += (50 / (2 + i)); } else if (chance < 3) { for (int i = 3; i > best_skill; i--) chance /= 2; } if (rng(0, 99) < chance) num_crits++; if (num_crits == 3) return true; else if (num_crits == 2) return (hit_roll() >= target_dodge * 1.5 && !one_in(4)); return false; }
void prise_silmaril(void) { object_type *o_ptr; object_type *w_ptr; artefact_type *a_ptr; object_type object_type_body; cptr freed_msg = NULL; // default to soothe compiler warnings bool freed = FALSE; int slot = 0; int dam = 0; int prt = 0; int net_dam = 0; int prt_percent = 0; int hit_result = 0; int crit_bonus_dice = 0; int pd = 0; int noise = 0; u32b dummy_noticed_flag; int mds = p_ptr->mds; int attack_mod = p_ptr->skill_use[S_MEL]; char o_name[80]; // the Crown is on the ground o_ptr = &o_list[cave_o_idx[p_ptr->py][p_ptr->px]]; switch (o_ptr->name1) { case ART_MORGOTH_3: { pd = 15; noise = 5; freed_msg = "You have freed a Silmaril!"; break; } case ART_MORGOTH_2: { pd = 25; noise = 10; if (p_ptr->crown_shatter) freed_msg = "The fates be damned! You free a second Silmaril."; else freed_msg = "You free a second Silmaril."; break; } case ART_MORGOTH_1: { pd = 30; noise = 15; freed_msg = "You free the final Silmaril. You have a very bad feeling about this."; msg_print("Looking into the hallowed light of the final Silmaril, you are filled with a strange dread."); if (!get_check("Are you sure you wish to proceed? ")) return; break; } } /* Get the weapon */ w_ptr = &inventory[INVEN_WIELD]; // undo rapid attack penalties if (p_ptr->active_ability[S_MEL][MEL_RAPID_ATTACK]) { // undo strength adjustment to the attack mds = total_mds(w_ptr, 0); // undo the dexterity adjustment to the attack attack_mod += 3; } /* Test for hit */ hit_result = hit_roll(attack_mod, 0, PLAYER, NULL, TRUE); /* Make some noise */ stealth_score -= noise; // Determine damage if (hit_result > 0) { crit_bonus_dice = crit_bonus(hit_result, w_ptr->weight, &r_info[R_IDX_MORGOTH], S_MEL, FALSE); dam = damroll(p_ptr->mdd + crit_bonus_dice, mds); prt = damroll(pd, 4); prt_percent = prt_after_sharpness(w_ptr, &dummy_noticed_flag); prt = (prt * prt_percent) / 100; net_dam = dam - prt; /* No negative damage */ if (net_dam < 0) net_dam = 0; //update_combat_rolls1b(PLAYER, TRUE); update_combat_rolls2(p_ptr->mdd + crit_bonus_dice, mds, dam, pd, 4, prt, prt_percent, GF_HURT, TRUE); } // if you succeed in prising out a Silmaril... if (net_dam > 0) { freed = TRUE; switch (o_ptr->name1) { case ART_MORGOTH_3: { break; } case ART_MORGOTH_2: { if (!p_ptr->crown_shatter && one_in_(2)) { shatter_weapon(2); freed = FALSE; } break; } case ART_MORGOTH_1: { if (!p_ptr->crown_shatter) { shatter_weapon(3); freed = FALSE; } else { p_ptr->cursed = TRUE; } break; } } if (freed) { // change its type to that of the crown with one less silmaril o_ptr->name1--; // get the details of this new crown a_ptr = &a_info[o_ptr->name1]; // modify the existing crown object_into_artefact(o_ptr, a_ptr); // report success msg_print(freed_msg); // Get new local object o_ptr = &object_type_body; // Make Silmaril object_prep(o_ptr, lookup_kind(TV_LIGHT, SV_LIGHT_SILMARIL)); // Get it slot = inven_carry(o_ptr); /* Get the object again */ o_ptr = &inventory[slot]; /* Describe the object */ object_desc(o_name, sizeof(o_name), o_ptr, TRUE, 3); /* Message */ msg_format("You have %s (%c).", o_name, index_to_label(slot)); // Break the truce (always) break_truce(TRUE); // add a note to the notes file do_cmd_note("Cut a Silmaril from Morgoth's crown", p_ptr->depth); } } // if you fail to prise out a Silmaril... else { msg_print("Try though you might, you were unable to free a Silmaril."); msg_print("Perhaps you should try again or use a different weapon."); if (pd == 15) msg_print("(The combat rolls window shows what is happening.)"); // Break the truce if creatures see break_truce(FALSE); } // check for taking of final Silmaril if ((pd == 30) && freed) { msg_print("Until you escape you must now roll twice for every skill check, taking the worse result each time."); msg_print("You hear a cry of veangance echo through the iron hells."); wake_all_monsters(0); } }
int player::hit_mon(game *g, monster *z) { 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 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) ); bool can_poison = false; // Recoil penalty if (recoil <= 30) recoil += 6; // Movement cost int move_cost = weapon.attack_time() + 20 * encumb(bp_torso); if (has_trait(PF_LIGHT_BONES)) move_cost *= .9; if (has_trait(PF_HOLLOW_BONES)) move_cost *= .8; moves -= move_cost; // 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_NAILS) && z->armor_cut() == 0 && !wearing_something_on(bp_hands)) { dam++; if (one_in(2)) can_poison = true; } if (has_trait(PF_CLAWS) && z->armor_cut() < 6 && !wearing_something_on(bp_hands)) { dam += 6; if (one_in(2)) can_poison = true; } if (has_trait(PF_TALONS) && z->armor_cut() - sklevel[sk_unarmed] < 10) { int z_armor = (z->armor_cut() - sklevel[sk_unarmed]); if (z_armor < 0) z_armor = 0; dam += 10 - z_armor; if (one_in(2)) can_poison = true; } if (has_trait(PF_THORNS) && z->armor_cut() < 4 && !wearing_something_on(bp_hands)) { dam += 4 - z->armor_cut(); if (one_in(2)) can_poison = true; } if (has_trait(PF_SLIME_HANDS) && !z->has_flag(MF_ACIDPROOF) && !wearing_something_on(bp_hands)) { dam += rng(4, 6); can_poison = true; } } 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, 2)) { case 1: g->add_msg("%s elbow%s the %s!", You.c_str(), (is_u ? "" : "s"), z->name().c_str()); break; case 2: 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() - z->armor_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->armor_cut() - int(sklevel[sk_stabbing])) { int z_armor = z->armor_cut() - 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->armor_cut() - int(sklevel[sk_cutting] / 2)) { int z_armor = z->armor_cut() - 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); } } } } // Critical hit effects if (critical_hit) { bool headshot = (!z->has_flag(MF_NOHEAD) && !one_in(3)); 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]; 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 += (str_cur / 2); int turns_stunned = int(dam / 20) + rng(0, 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; if (cutting) cut_multiplier = double(sklevel[sk_cutting] / 12); else cut_multiplier = double(sklevel[sk_stabbing] / 5); 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 // 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)); if (drain_them) power_level--; drain_them &= one_in(2); // Only works half the time std::vector<special_attack> special_attacks = mutation_attacks(z); 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); } for (int i = 0; i < special_attacks.size(); i++) { int spec_dam = 0; spec_dam += special_attacks[i].bash; if (special_attacks[i].cut > z->armor_cut()) spec_dam += special_attacks[i].cut - z->armor_cut(); if (special_attacks[i].stab > z->armor_cut() * .8) spec_dam += special_attacks[i].stab - z->armor_cut() * .8; if (!can_poison && one_in(2) && (special_attacks[i].cut > z->armor_cut() || special_attacks[i].stab > z->armor_cut() * .8)) can_poison = true; if (spec_dam > 0) { g->add_msg( special_attacks[i].text.c_str() ); dam += spec_dam; } } if (can_poison && has_trait(PF_POISONOUS)) { if (is_u) g->add_msg("You poison the %s!", z->name().c_str()); z->add_effect(ME_POISONED, 6); } // 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; }