Implement [filter_weapon] in leadership and resistance abilities
Closes #2718
(cherry-picked from commit 76c2cbac46
)
This commit is contained in:
parent
122976bef0
commit
ebb980a45e
8 changed files with 52 additions and 23 deletions
|
@ -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
|
||||
|
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Add table
Reference in a new issue