battle_context_unit_stats::battle_context_unit_stats(const unit &u, const map_location& u_loc, int u_attack_num, bool attacking, const unit &opp, const map_location& opp_loc, const attack_type *opp_weapon, const unit_map& units) : weapon(NULL), attack_num(u_attack_num), is_attacker(attacking), is_poisoned(u.get_state(unit::STATE_POISONED)), is_slowed(u.get_state(unit::STATE_SLOWED)), slows(false), drains(false), petrifies(false), plagues(false), poisons(false), backstab_pos(false), swarm(false), firststrike(false), experience(u.experience()), max_experience(u.max_experience()), level(u.level()), rounds(1), hp(0), max_hp(u.max_hitpoints()), chance_to_hit(0), damage(0), slow_damage(0), drain_percent(0), drain_constant(0), num_blows(0), swarm_min(0), swarm_max(0), plague_type() { // Get the current state of the unit. if (attack_num >= 0) { weapon = &u.attacks()[attack_num]; } if(u.hitpoints() < 0) { LOG_CF << "Unit with " << u.hitpoints() << " hitpoints found, set to 0 for damage calculations\n"; hp = 0; } else if(u.hitpoints() > u.max_hitpoints()) { // If a unit has more hp than its maximum, the engine will fail // with an assertion failure due to accessing the prob_matrix // out of bounds. hp = u.max_hitpoints(); } else { hp = u.hitpoints(); } // Get the weapon characteristics, if any. if (weapon) { weapon->set_specials_context(u_loc, opp_loc, attacking, opp_weapon); if (opp_weapon) opp_weapon->set_specials_context(opp_loc, u_loc, !attacking, weapon); slows = weapon->get_special_bool("slow"); drains = !opp.get_state("undrainable") && weapon->get_special_bool("drains"); petrifies = weapon->get_special_bool("petrifies"); poisons = !opp.get_state("unpoisonable") && weapon->get_special_bool("poison") && !opp.get_state(unit::STATE_POISONED); backstab_pos = is_attacker && backstab_check(u_loc, opp_loc, units, *resources::teams); rounds = weapon->get_specials("berserk").highest("value", 1).first; firststrike = weapon->get_special_bool("firststrike"); // Handle plague. unit_ability_list plague_specials = weapon->get_specials("plague"); plagues = !opp.get_state("unplagueable") && !plague_specials.empty() && strcmp(opp.undead_variation().c_str(), "null") && !resources::game_map->is_village(opp_loc); if (plagues) { plague_type = (*plague_specials.front().first)["type"].str(); if (plague_type.empty()) plague_type = u.type().base_id(); } // Compute chance to hit. chance_to_hit = opp.defense_modifier( resources::game_map->get_terrain(opp_loc)) + weapon->accuracy() - (opp_weapon ? opp_weapon->parry() : 0); if(chance_to_hit > 100) { chance_to_hit = 100; } unit_ability_list cth_specials = weapon->get_specials("chance_to_hit"); unit_abilities::effect cth_effects(cth_specials, chance_to_hit, backstab_pos); chance_to_hit = cth_effects.get_composite_value(); // Compute base damage done with the weapon. int base_damage = weapon->modified_damage(backstab_pos); // Get the damage multiplier applied to the base damage of the weapon. int damage_multiplier = 100; // Time of day bonus. damage_multiplier += combat_modifier(u_loc, u.alignment(), u.is_fearless()); // Leadership bonus. int leader_bonus = 0; if (under_leadership(units, u_loc, &leader_bonus).valid()) damage_multiplier += leader_bonus; // Resistance modifier. damage_multiplier *= opp.damage_from(*weapon, !attacking, opp_loc); // Compute both the normal and slowed damage. damage = round_damage(base_damage, damage_multiplier, 10000); slow_damage = round_damage(base_damage, damage_multiplier, 20000); if (is_slowed) damage = slow_damage; // Compute drain amounts only if draining is possible. if(drains) { unit_ability_list drain_specials = weapon->get_specials("drains"); // Compute the drain percent (with 50% as the base for backward compatibility) unit_abilities::effect drain_percent_effects(drain_specials, 50, backstab_pos); drain_percent = drain_percent_effects.get_composite_value(); } // Add heal_on_hit (the drain constant) unit_ability_list heal_on_hit_specials = weapon->get_specials("heal_on_hit"); unit_abilities::effect heal_on_hit_effects(heal_on_hit_specials, 0, backstab_pos); drain_constant += heal_on_hit_effects.get_composite_value(); drains = drain_constant || drain_percent; // Compute the number of blows and handle swarm. weapon->modified_attacks(backstab_pos, swarm_min, swarm_max); swarm = swarm_min != swarm_max; num_blows = calc_blows(hp); } }
void battle_prediction_pane::get_unit_strings(const battle_context_unit_stats& stats, const unit& u, const map_location& u_loc, float u_unscathed, const unit& opp, const map_location& opp_loc, const attack_type *opp_weapon, std::vector<std::string>& left_strings, std::vector<std::string>& right_strings, int& left_strings_width, int& right_strings_width, int& strings_width) { std::stringstream str; char str_buf[10]; // With a weapon. if(stats.weapon != NULL) { // Set specials context (for safety, it should not have changed normally). const attack_type *weapon = stats.weapon; weapon->set_specials_context(u_loc, opp_loc, stats.is_attacker, opp_weapon); // Get damage modifiers. unit_ability_list dmg_specials = weapon->get_specials("damage"); unit_abilities::effect dmg_effect(dmg_specials, weapon->damage(), stats.backstab_pos); // Get the SET damage modifier, if any. const unit_abilities::individual_effect *set_dmg_effect = NULL; unit_abilities::effect::const_iterator i; for(i = dmg_effect.begin(); i != dmg_effect.end(); ++i) { if(i->type == unit_abilities::SET) { set_dmg_effect = &*i; break; } } // Either user the SET modifier or the base weapon damage. if(set_dmg_effect == NULL) { left_strings.push_back(weapon->name()); str.str(""); str << weapon->damage(); right_strings.push_back(str.str()); } else { assert(set_dmg_effect->ability); left_strings.push_back((*set_dmg_effect->ability)["name"]); str.str(""); str << set_dmg_effect->value; right_strings.push_back(str.str()); } // Process the ADD damage modifiers. for(i = dmg_effect.begin(); i != dmg_effect.end(); ++i) { if(i->type == unit_abilities::ADD) { left_strings.push_back((*i->ability)["name"]); str.str(""); if(i->value >= 0) str << "+" << i->value; else str << i->value; right_strings.push_back(str.str()); } } // Process the MUL damage modifiers. for(i = dmg_effect.begin(); i != dmg_effect.end(); ++i) { if(i->type == unit_abilities::MUL) { left_strings.push_back((*i->ability)["name"]); str.str(""); str << "* " << (i->value / 100); if(i->value % 100) { str << "." << ((i->value % 100) / 10); if(i->value % 10) str << (i->value % 10); } right_strings.push_back(str.str()); } } // Time of day modifier. int tod_modifier = combat_modifier(resources::gameboard->units(), resources::gameboard->map(), u_loc, u.alignment(), u.is_fearless()); if(tod_modifier != 0) { left_strings.push_back(_("Time of day")); str.str(""); str << utils::signed_percent(tod_modifier); right_strings.push_back(str.str()); } // Leadership bonus. int leadership_bonus = 0; under_leadership(*resources::units, u_loc, &leadership_bonus); if(leadership_bonus != 0) { left_strings.push_back(_("Leadership")); str.str(""); str << utils::signed_percent(leadership_bonus); right_strings.push_back(str.str()); } // Resistance modifier. int resistance_modifier = opp.damage_from(*weapon, !stats.is_attacker, opp_loc); if(resistance_modifier != 100) { str.str(""); if(stats.is_attacker) str << _("Defender"); else str << _("Attacker"); if(resistance_modifier < 100) str << _(" resistance vs "); else str << _(" vulnerability vs "); str << string_table["type_" + weapon->type()]; left_strings.push_back(str.str()); str.str(""); str << "* " << (resistance_modifier / 100) << "." << ((resistance_modifier % 100) / 10); right_strings.push_back(str.str()); } // Slowed penalty. if(stats.is_slowed) { left_strings.push_back(_("Slowed")); right_strings.push_back("/ 2"); } // Total damage. left_strings.push_back(_("Total damage")); str.str(""); str << stats.damage << utils::unicode_en_dash << stats.num_blows << " (" << stats.chance_to_hit << "%)"; right_strings.push_back(str.str()); // Without a weapon. } else { left_strings.push_back(_("No usable weapon")); right_strings.push_back(""); } // Unscathed probability. left_strings.push_back(_("Chance of being unscathed")); format_prob(str_buf, u_unscathed); right_strings.push_back(str_buf); #if 0 // might not be in English! // Fix capitalization of left strings. for(int i = 0; i < (int) left_strings.size(); i++) if(left_strings[i].size() > 0) left_strings[i][0] = toupper(left_strings[i][0]); #endif // Compute the width of the strings. left_strings_width = get_strings_max_length(left_strings); right_strings_width = get_strings_max_length(right_strings); strings_width = left_strings_width + inter_column_gap_ + right_strings_width; }