Implement [filter_weapon] in leadership and resistance abilities

Closes #2718

(cherry-picked from commit 76c2cbac46)
This commit is contained in:
newfrenchy83 2018-03-25 11:10:53 -04:00 committed by Celtic Minstrel
parent 122976bef0
commit ebb980a45e
8 changed files with 52 additions and 23 deletions

View file

@ -2,6 +2,9 @@
### WML engine
* Support formula= key in [variable] ConditionalWML
* Support to_location in [move_unit], taking a location ID
* Support [filter_weapon] in leadership and resistance abilities,
which activates the ability only when the affected unit is using
a matching weapon.
### Language and i18n
* Fixed many cases of interpolated strings in the engine possibly having
their translations retrieved from the wrong textdomain and falling back

View file

@ -183,13 +183,13 @@ battle_context_unit_stats::battle_context_unit_stats(const unit& u,
resources::gameboard->units(), resources::gameboard->map(), u_loc, u.alignment(), u.is_fearless());
// Leadership bonus.
int leader_bonus = under_leadership(units, u_loc).first;
int leader_bonus = under_leadership(units, u_loc, weapon).first;
if(leader_bonus != 0) {
damage_multiplier += leader_bonus;
}
// Resistance modifier.
damage_multiplier *= opp.damage_from(*weapon, !attacking, opp_loc);
damage_multiplier *= opp.damage_from(*weapon, !attacking, opp_loc, opp_weapon);
// Compute both the normal and slowed damage.
damage = round_damage(base_damage, damage_multiplier, 10000);
@ -1589,14 +1589,14 @@ void attack_unit_and_advance(const map_location& attacker,
}
}
std::pair<int, map_location> under_leadership(const unit_map& units, const map_location& loc)
std::pair<int, map_location> under_leadership(const unit_map& units, const map_location& loc, const_attack_ptr weapon)
{
const unit_map::const_iterator un = units.find(loc);
if(un == units.end()) {
return {0, map_location::null_location()};
}
unit_ability_list abil = un->get_abilities("leadership");
unit_ability_list abil = un->get_abilities("leadership", weapon);
return abil.highest("value");
}

View file

@ -266,7 +266,7 @@ void attack_unit_and_advance(const map_location& attacker,
* Returns a pair of bonus percentage and the leader's location if the unit is affected,
* or 0 and map_location::null_location() otherwise.
*/
std::pair<int, map_location> under_leadership(const unit_map& units, const map_location& loc);
std::pair<int, map_location> under_leadership(const unit_map& units, const map_location& loc, const_attack_ptr weapon);
/**
* Returns the amount that a unit's damage should be multiplied by

View file

@ -126,8 +126,13 @@ void attack_predictions::set_data(window& window, const combatant_data& attacker
ss.str("");
// Set specials context (for safety, it should not have changed normally).
const_attack_ptr weapon = attacker.stats_.weapon;
auto ctx = weapon->specials_context(&attacker.unit_, &defender.unit_, attacker.unit_.get_location(), defender.unit_.get_location(), attacker.stats_.is_attacker, defender.stats_.weapon);
const_attack_ptr weapon = attacker.stats_.weapon, opp_weapon = defender.stats_.weapon;
auto ctx = weapon->specials_context(&attacker.unit_, &defender.unit_, attacker.unit_.get_location(), defender.unit_.get_location(), attacker.stats_.is_attacker, opp_weapon);
boost::optional<decltype(ctx)> opp_ctx;
if(opp_weapon) {
opp_ctx = opp_weapon->specials_context(&defender.unit_, &attacker.unit_, defender.unit_.get_location(), attacker.unit_.get_location(), defender.stats_.is_attacker, weapon);
}
// Get damage modifiers.
unit_ability_list dmg_specials = weapon->get_specials("damage");
@ -182,7 +187,7 @@ void attack_predictions::set_data(window& window, const combatant_data& attacker
ss.str("");
// Resistance modifier.
const int resistance_modifier = defender.unit_.damage_from(*weapon, !attacker.stats_.is_attacker, defender.unit_.get_location());
const int resistance_modifier = defender.unit_.damage_from(*weapon, !attacker.stats_.is_attacker, defender.unit_.get_location(), opp_weapon);
if(resistance_modifier != 100) {
if(attacker.stats_.is_attacker) {
ss << _("Defender resistance vs") << " ";
@ -217,7 +222,7 @@ void attack_predictions::set_data(window& window, const combatant_data& attacker
}
// Leadership bonus.
const int leadership_bonus = under_leadership(resources::gameboard->units(), attacker.unit_.get_location()).first;
const int leadership_bonus = under_leadership(resources::gameboard->units(), attacker.unit_.get_location(), weapon).first;
if(leadership_bonus != 0) {
set_label_helper("leadership_modifier", utils::signed_percent(leadership_bonus));

View file

@ -661,9 +661,10 @@ static int attack_info(reports::context & rc, const attack_type &at, config &res
int base_damage = at.damage();
int specials_damage = at.modified_damage(false);
int damage_multiplier = 100;
const_attack_ptr weapon = at.shared_from_this();
int tod_bonus = combat_modifier(rc.units(), rc.map(), displayed_unit_hex, u.alignment(), u.is_fearless());
damage_multiplier += tod_bonus;
int leader_bonus = under_leadership(rc.units(), displayed_unit_hex).first;
int leader_bonus = under_leadership(rc.units(), displayed_unit_hex, weapon).first;
if (leader_bonus != 0)
damage_multiplier += leader_bonus;

View file

@ -176,13 +176,15 @@ bool unit::get_ability_bool(const std::string& tag_name, const map_location& loc
return false;
}
unit_ability_list unit::get_abilities(const std::string& tag_name, const map_location& loc) const
unit_ability_list unit::get_abilities(const std::string& tag_name, const map_location& loc, const_attack_ptr weapon) const
{
unit_ability_list res(loc_);
for (const config &i : this->abilities_.child_range(tag_name)) {
if (ability_active(tag_name, i, loc) &&
ability_affects_self(tag_name, i, loc))
ability_affects_self(tag_name, i, loc) &&
ability_affects_weapon(tag_name, i, loc, weapon))
{
res.push_back(unit_ability(&i, loc));
}
@ -207,7 +209,8 @@ unit_ability_list unit::get_abilities(const std::string& tag_name, const map_loc
for (const config &j : it->abilities_.child_range(tag_name)) {
if (affects_side(j, resources::gameboard->teams(), side(), it->side()) &&
it->ability_active(tag_name, j, adjacent[i]) &&
ability_affects_adjacent(tag_name, j, i, loc, *it))
ability_affects_adjacent(tag_name, j, i, loc, *it) &&
ability_affects_weapon(tag_name, j, adjacent[i], weapon))
{
res.push_back(unit_ability(&j, adjacent[i]));
}
@ -409,6 +412,21 @@ bool unit::ability_affects_self(const std::string& ability,const config& cfg,con
return unit_filter(vconfig(filter)).set_use_flat_tod(ability == "illuminates").matches(*this, loc);
}
bool unit::ability_affects_weapon(const std::string&, const config& cfg, const map_location&, const_attack_ptr weapon) const
{
if(!cfg.has_child("filter_weapon")) {
return true;
}
const config& filter = cfg.child("filter_weapon");
if(!weapon) {
// Not sure if this is the correct behaviour here
return false;
}
return weapon->matches_filter(filter);
}
bool unit::has_ability_type(const std::string& ability) const
{
return !abilities_.child_range(ability).empty();

View file

@ -1589,11 +1589,11 @@ bool unit::resistance_filter_matches(const config& cfg, bool attacker, const std
return true;
}
int unit::resistance_against(const std::string& damage_name,bool attacker,const map_location& loc) const
int unit::resistance_against(const std::string& damage_name,bool attacker,const map_location& loc, const_attack_ptr weapon) const
{
int res = movement_type_.resistance_against(damage_name);
unit_ability_list resistance_abilities = get_abilities("resistance",loc);
unit_ability_list resistance_abilities = get_abilities("resistance",loc, weapon);
for(unit_ability_list::iterator i = resistance_abilities.begin(); i != resistance_abilities.end();) {
if(!resistance_filter_matches(*i->first, attacker, damage_name, 100-res)) {
i = resistance_abilities.erase(i);

View file

@ -845,9 +845,9 @@ public:
*
* @returns The expected damage.
*/
int damage_from(const attack_type& attack, bool attacker, const map_location& loc) const
int damage_from(const attack_type& attack, bool attacker, const map_location& loc, const_attack_ptr weapon = nullptr) const
{
return resistance_against(attack, attacker, loc);
return resistance_against(attack, attacker, loc, weapon);
}
/** The maximum number of attacks this unit may perform per turn, usually 1. */
@ -887,7 +887,7 @@ public:
* @param attacker True if this unit is on the offensive (to resolve [resistance] abilities)
* @param loc The unit's location (to resolve [resistance] abilities)
*/
int resistance_against(const std::string& damage_name, bool attacker, const map_location& loc) const;
int resistance_against(const std::string& damage_name, bool attacker, const map_location& loc, const_attack_ptr weapon = nullptr) const;
/**
* The unit's resistance against a given attack
@ -895,9 +895,9 @@ public:
* @param attacker True if this unit is on the offensive (to resolve [resistance] abilities)
* @param loc The unit's location (to resolve [resistance] abilities)
*/
int resistance_against(const attack_type& atk, bool attacker, const map_location& loc) const
int resistance_against(const attack_type& atk, bool attacker, const map_location& loc, const_attack_ptr weapon = nullptr) const
{
return resistance_against(atk.type(), attacker, loc);
return resistance_against(atk.type(), attacker, loc , weapon);
}
/** Gets resistances without any abilities applied. */
@ -1468,16 +1468,16 @@ public:
* @param loc The location to use for resolving abilities
* @return A list of active abilities, paired with the location they are active on
*/
unit_ability_list get_abilities(const std::string& tag_name, const map_location& loc) const;
unit_ability_list get_abilities(const std::string& tag_name, const map_location& loc, const_attack_ptr weapon = nullptr) const;
/**
* Gets the unit's active abilities of a particular type.
* @param tag_name The type of ability to check for
* @return A list of active abilities, paired with the location they are active on
*/
unit_ability_list get_abilities(const std::string& tag_name) const
unit_ability_list get_abilities(const std::string& tag_name, const_attack_ptr weapon = nullptr) const
{
return get_abilities(tag_name, loc_);
return get_abilities(tag_name, loc_, weapon);
}
/**
@ -1543,6 +1543,8 @@ private:
*/
bool ability_affects_self(const std::string& ability, const config& cfg, const map_location& loc) const;
bool ability_affects_weapon(const std::string& ability,const config& cfg,const map_location& loc, const_attack_ptr weapon) const;
public:
/** Get the unit formula manager. */
unit_formula_manager& formula_manager() const