void Creature::deal_melee_hit( Creature *source, int hit_spread, bool critical_hit, const damage_instance &dam, dealt_damage_instance &dealt_dam ) { damage_instance d = dam; // copy, since we will mutate in block_hit body_part bp_hit = select_body_part(source, hit_spread); block_hit(source, bp_hit, d); // Bashing critical if( critical_hit && !is_immune_effect( effect_stunned ) ) { if( d.type_damage(DT_BASH) * hit_spread > get_hp_max() ) { add_effect( effect_stunned, 1_turns ); // 1 turn is enough } } // Stabbing effects int stab_moves = rng( d.type_damage(DT_STAB) / 2, d.type_damage(DT_STAB) * 1.5 ); if (critical_hit) { stab_moves *= 1.5; } if( stab_moves >= 150 && !is_immune_effect( effect_downed ) ) { if( is_player() ) { source->add_msg_if_npc( m_bad, _("<npcname> forces you to the ground!")); } else { source->add_msg_player_or_npc( m_good, _("You force %s to the ground!"), _("<npcname> forces %s to the ground!"), disp_name().c_str() ); } add_effect( effect_downed, 1_turns ); mod_moves(-stab_moves / 2); } else { mod_moves(-stab_moves); } on_hit( source, bp_hit ); // trigger on-gethit events dealt_dam = deal_damage(source, bp_hit, d); dealt_dam.bp_hit = bp_hit; }
void Creature::deal_damage_handle_type(const damage_unit &du, body_part, int &damage, int &pain) { switch (du.type) { case DT_BASH: damage += du.amount; pain += du.amount / 4; // add up pain before using mod_pain since certain traits modify that mod_moves(-rng(0, damage * 2)); // bashing damage reduces moves break; case DT_CUT: damage += du.amount; pain += (du.amount + sqrt(double(du.amount))) / 4; break; case DT_STAB: // stab differs from cut in that it ignores some armor damage += du.amount; pain += (du.amount + sqrt(double(du.amount))) / 4; break; case DT_HEAT: // heat damage sets us on fire sometimes damage += du.amount; pain += du.amount / 4; if (rng(0, 100) > (100 - 400 / (du.amount + 3))) { add_effect("onfire", rng(1, 3)); } break; case DT_ELECTRIC: // electrical damage slows us a lot damage += du.amount; pain += du.amount / 4; mod_moves(-du.amount * 100); break; case DT_COLD: // cold damage slows us a bit and hurts less damage += du.amount; pain += du.amount / 6; mod_moves(-du.amount * 80); break; default: damage += du.amount; pain += du.amount / 4; } }
void Creature::mod_stat( const std::string &stat, float modifier ) { if( stat == "speed" ) { mod_speed_bonus( modifier ); } else if( stat == "dodge" ) { mod_dodge_bonus( modifier ); } else if( stat == "block" ) { mod_block_bonus( modifier ); } else if( stat == "hit" ) { mod_hit_bonus( modifier ); } else if( stat == "bash" ) { mod_bash_bonus( modifier ); } else if( stat == "cut" ) { mod_cut_bonus( modifier ); } else if( stat == "pain" ) { mod_pain( modifier ); } else if( stat == "moves" ) { mod_moves( modifier ); } else { add_msg( "Tried to modify a nonexistent stat %s.", stat.c_str() ); } }
bool player::feed_furnace_with( item &it ) { if( !can_feed_furnace_with( it ) ) { return false; } const int energy = get_acquirable_energy( it, rechargeable_cbm::furnace ); if( energy == 0 ) { add_msg_player_or_npc( m_info, _( "You digest your %s, but fail to acquire energy from it." ), _( "<npcname> digests their %s for energy, but fails to acquire energy from it." ), it.tname().c_str() ); } else if( power_level >= max_power_level ) { add_msg_player_or_npc( _( "You digest your %s, but you're fully powered already, so the energy is wasted." ), _( "<npcname> digests a %s for energy, they're fully powered already, so the energy is wasted." ), it.tname().c_str() ); } else { const int profitable_energy = std::min( energy, max_power_level - power_level ); add_msg_player_or_npc( m_info, ngettext( "You digest your %s and recharge %d point of energy.", "You digest your %s and recharge %d points of energy.", profitable_energy ), ngettext( "<npcname> digests a %s and recharges %d point of energy.", "<npcname> digests a %s and recharges %d points of energy.", profitable_energy ), it.tname().c_str(), profitable_energy ); charge_power( profitable_energy ); } it.charges = 0; mod_moves( -250 ); return true; }
bool player::feed_reactor_with( item &it ) { if( !can_feed_reactor_with( it ) ) { return false; } const auto iter = plut_charges.find( it.typeId() ); const int max_amount = iter != plut_charges.end() ? iter->second : 0; const int amount = std::min( get_acquirable_energy( it, rechargeable_cbm::reactor ), max_amount ); if( amount >= PLUTONIUM_CHARGES * 10 && !query_yn( _( "Thats a LOT of plutonium. Are you sure you want that much?" ) ) ) { return false; } add_msg_player_or_npc( _( "You add your %s to your reactor's tank." ), _( "<npcname> pours %s into their reactor's tank." ), it.tname().c_str() ); tank_plut += amount; // @todo Encapsulate it.charges -= 1; mod_moves( -250 ); return true; }
void Character::mod_stat( const std::string &stat, int modifier ) { if( stat == "str" ) { mod_str_bonus( modifier ); } else if( stat == "dex" ) { mod_dex_bonus( modifier ); } else if( stat == "per" ) { mod_per_bonus( modifier ); } else if( stat == "int" ) { mod_int_bonus( modifier ); } else if( stat == "healthy" ) { mod_healthy( modifier ); } else if( stat == "healthy_mod" ) { mod_healthy_mod( modifier ); } else if( stat == "hunger" ) { mod_hunger( modifier ); } else if( stat == "speed" ) { mod_speed_bonus( modifier ); } else if( stat == "dodge" ) { mod_dodge_bonus( modifier ); } else if( stat == "block" ) { mod_block_bonus( modifier ); } else if( stat == "hit" ) { mod_hit_bonus( modifier ); } else if( stat == "bash" ) { mod_bash_bonus( modifier ); } else if( stat == "cut" ) { mod_cut_bonus( modifier ); } else if( stat == "pain" ) { mod_pain( modifier ); } else if( stat == "moves" ) { mod_moves( modifier ); } else { Creature::mod_stat( stat, modifier ); } }
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; } } }
int Creature::deal_melee_attack(Creature *source, int hitroll, bool critical_hit, const damage_instance &dam, dealt_damage_instance &dealt_dam) { int dodgeroll = dodge_roll(); int hit_spread = hitroll - dodgeroll; bool missed = hit_spread <= 0; damage_instance d = dam; // copy, since we will mutate in block_hit if (missed) { return hit_spread; } //bool critical_hit = hit_spread > 30; //scored_crit(dodgeroll); body_part bp_hit; int side = rng(0, 1); int hit_value = hit_spread + dice(10, 6) - 35; if (hit_value >= 40) { bp_hit = bp_eyes; } else if (hit_value >= 30) { bp_hit = bp_head; } else if (hit_value >= 5) { bp_hit = bp_torso; } else if (one_in(4)) { bp_hit = bp_legs; } else { bp_hit = bp_arms; } // Bashing crit if (critical_hit) { int turns_stunned = (d.type_damage(DT_BASH) + hit_spread) / 20; if (turns_stunned > 6) { turns_stunned = 6; } if (turns_stunned > 0) { add_effect("stunned", turns_stunned); } } // Stabbing effects int stab_moves = rng(d.type_damage(DT_STAB) / 2, d.type_damage(DT_STAB) * 1.5); if (critical_hit) { stab_moves *= 1.5; } if (stab_moves >= 150) { if (is_player()) { // can the player force their self to the ground? probably not. g->add_msg_if_npc(source, _("<npcname> forces you to the ground!")); } else { g->add_msg_player_or_npc(source, _("You force %s to the ground!"), _("<npcname> forces %s to the ground!"), disp_name().c_str() ); } add_effect("downed", 1); mod_moves(-stab_moves / 2); } else { mod_moves(-stab_moves); } block_hit(bp_hit, side, d); on_gethit(source, bp_hit, d); // trigger on-gethit events dealt_dam = deal_damage(source, bp_hit, side, d); dealt_dam.bp_hit = bp_hit; /* TODO: add grabs n shit back in if (allow_special && technique.grabs) { // TODO: make this depend on skill (through grab_resist stat) again if (t.get_grab_resist() > 0 && dice(t.get_dex() , 12) > dice(get_dex(), 10)) { g->add_msg_player_or_npc(&t, _("You break the grab!"), _("<npcname> breaks the grab!")); } else if (!unarmed_attack()) { // Move our weapon to a temp slot, if it's not unarmed item tmpweap = remove_weapon(); melee_attack(t, false); // False means a second grab isn't allowed weapon = tmpweap; } else melee_attack(t, false); // False means a second grab isn't allowed } */ return hit_spread; }
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; } } }