diff --git a/src/actions/attack.cpp b/src/actions/attack.cpp index 01b7a12e7252..db85fefb766e 100644 --- a/src/actions/attack.cpp +++ b/src/actions/attack.cpp @@ -163,7 +163,7 @@ battle_context_unit_stats::battle_context_unit_stats(const unit& u, opp.undead_variation() != "null" && !resources::gameboard->map().is_village(opp_loc); if(plagues) { - plague_type = (*plague_specials.front().first)["type"].str(); + plague_type = (*plague_specials.front().ability_cfg)["type"].str(); if(plague_type.empty()) { plague_type = u.type().base_id(); @@ -313,7 +313,7 @@ battle_context_unit_stats::battle_context_unit_stats(const unit_type* u_type, opp_type->undead_variation() != "null"; if(plagues) { - plague_type = (*plague_specials.front().first)["type"].str(); + plague_type = (*plague_specials.front().ability_cfg)["type"].str(); if(plague_type.empty()) { plague_type = u_type->base_id(); } diff --git a/src/actions/heal.cpp b/src/actions/heal.cpp index a7fa1d6e8b42..9f9e13c857b5 100644 --- a/src/actions/heal.cpp +++ b/src/actions/heal.cpp @@ -97,7 +97,7 @@ namespace { // Regeneration? for (const unit_ability & regen : patient.get_abilities("regenerate")) { - curing = std::max(curing, poison_status((*regen.first)["poison"])); + curing = std::max(curing, poison_status((*regen.ability_cfg)["poison"])); if ( curing == POISON_CURE ) // This is as good as it gets. return POISON_CURE; @@ -109,14 +109,14 @@ namespace { // Assumed: curing is not POISON_CURE at the start of any iteration. for (const unit_ability & heal : patient.get_abilities("heals")) { - POISON_STATUS this_cure = poison_status((*heal.first)["poison"]); + POISON_STATUS this_cure = poison_status((*heal.ability_cfg)["poison"]); if ( this_cure <= curing ) // We already recorded this level of curing. continue; // NOTE: At this point, this_cure will be *_SLOW or *_CURE. - unit_map::iterator cure_it = units.find(heal.second); + unit_map::iterator cure_it = units.find(heal.teacher_loc); assert(cure_it != units.end()); const int cure_side = cure_it->side(); @@ -201,7 +201,7 @@ namespace { // Remove all healers not on this side (since they do not heal now). unit_ability_list::iterator heal_it = heal_list.begin(); while ( heal_it != heal_list.end() ) { - unit_map::iterator healer = units.find(heal_it->second); + unit_map::iterator healer = units.find(heal_it->teacher_loc); assert(healer != units.end()); if ( healer->side() != side ) diff --git a/src/actions/move.cpp b/src/actions/move.cpp index c470c3318e20..4a1c70dcc143 100644 --- a/src/actions/move.cpp +++ b/src/actions/move.cpp @@ -850,7 +850,7 @@ namespace { // Private helpers for move_unit() { for(const unit_ability &hide : ambusher.get_abilities("hides")) { - const std::string & ambush_string = (*hide.first)["alert"].str(); + const std::string & ambush_string = (*hide.ability_cfg)["alert"].str(); if (!ambush_string.empty()) { return ambush_string; } diff --git a/src/pathfind/teleport.cpp b/src/pathfind/teleport.cpp index 64ce500012cd..670b710abb94 100644 --- a/src/pathfind/teleport.cpp +++ b/src/pathfind/teleport.cpp @@ -264,9 +264,9 @@ const teleport_map get_teleport_locations(const unit &u, std::vector groups; for (const unit_ability & teleport : u.get_abilities("teleport")) { - const int tunnel_count = (teleport.first)->child_count("tunnel"); + const int tunnel_count = (teleport.ability_cfg)->child_count("tunnel"); for(int i = 0; i < tunnel_count; ++i) { - config teleport_group_cfg = (teleport.first)->child("tunnel", i); + config teleport_group_cfg = (teleport.ability_cfg)->child("tunnel", i); groups.emplace_back(vconfig(teleport_group_cfg, true), false); } } diff --git a/src/units/abilities.cpp b/src/units/abilities.cpp index 44ce0f56bcac..0225f5a7da25 100644 --- a/src/units/abilities.cpp +++ b/src/units/abilities.cpp @@ -189,7 +189,7 @@ unit_ability_list unit::get_abilities(const std::string& tag_name, const map_loc if(ability_active(tag_name, i, loc) && ability_affects_self(tag_name, i, loc)) { - res.emplace_back(&i, loc); + res.emplace_back(&i, loc, loc); } } @@ -214,7 +214,7 @@ unit_ability_list unit::get_abilities(const std::string& tag_name, const map_loc && it->ability_active(tag_name, j, adjacent[i]) && ability_affects_adjacent(tag_name, j, i, loc, *it)) { - res.emplace_back(&j, adjacent[i]); + res.emplace_back(&j, loc, adjacent[i]); } } } @@ -227,7 +227,7 @@ unit_ability_list unit::get_abilities_weapons(const std::string& tag_name, const { unit_ability_list res = get_abilities(tag_name, loc); for(unit_ability_list::iterator i = res.begin(); i != res.end();) { - if((!ability_affects_weapon(*i->first, weapon, false) || !ability_affects_weapon(*i->first, opp_weapon, true))) { + if((!ability_affects_weapon(*i->ability_cfg, weapon, false) || !ability_affects_weapon(*i->ability_cfg, opp_weapon, true))) { i = res.erase(i); } else { ++i; @@ -504,7 +504,7 @@ get_ability_value_visitor make_get_ability_value_visitor(T def, return get_ability_value_visitor(def, formula_handler); } template -T get_single_ability_value(const config::attribute_value& v, T def, const map_location& sender_loc, const map_location& receiver_loc, const TFuncFormula& formula_handler) +T get_single_ability_value(const config::attribute_value& v, T def, const unit_ability& ability_info, const map_location& receiver_loc, const TFuncFormula& formula_handler) { return v.apply_visitor(make_get_ability_value_visitor(def, [&](const std::string& s) { @@ -512,15 +512,17 @@ T get_single_ability_value(const config::attribute_value& v, T def, const map_lo assert(display::get_singleton()); const unit_map& units = display::get_singleton()->get_units(); - auto u_itor = units.find(sender_loc); + auto u_itor = units.find(ability_info.teacher_loc); if(u_itor == units.end()) { return def; } wfl::map_formula_callable callable(std::make_shared(*u_itor)); - u_itor = units.find(receiver_loc); - if(u_itor != units.end()) { - callable.add("other", wfl::variant(std::make_shared(*u_itor))); + if (auto uptr = units.find_unit_ptr(ability_info.student_loc)) { + callable.add("student", wfl::variant(std::make_shared(*uptr))); + } + if (auto uptr = units.find_unit_ptr(receiver_loc)) { + callable.add("other", wfl::variant(std::make_shared(*uptr))); } return formula_handler(wfl::formula(s, new wfl::gamestate_function_symbol_table), callable); } catch(const wfl::formula_error& e) { @@ -546,21 +548,21 @@ std::pair unit_ability_list::get_extremum(const std::string& k int stack = 0; for (const unit_ability& p : cfgs_) { - int value = get_single_ability_value((*p.first)[key], def, p.second, loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) { + int value = get_single_ability_value((*p.ability_cfg)[key], def, p, loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) { return formula.evaluate(callable).as_int(); }); - if ((*p.first)["cumulative"].to_bool()) { + if ((*p.ability_cfg)["cumulative"].to_bool()) { stack += value; if (value < 0) value = -value; if (only_cumulative && !comp(value, abs_max)) { abs_max = value; - best_loc = p.second; + best_loc = p.teacher_loc; } } else if (only_cumulative || comp(flat, value)) { only_cumulative = false; flat = value; - best_loc = p.second; + best_loc = p.teacher_loc; } } return std::make_pair(flat + stack, best_loc); @@ -761,7 +763,7 @@ unit_ability_list attack_type::get_specials(const std::string& special) const for(const config& i : specials_.child_range(special)) { if(special_active(i, AFFECT_SELF, special)) { - res.emplace_back(&i, loc); + res.emplace_back(&i, loc, loc); } } @@ -771,7 +773,7 @@ unit_ability_list attack_type::get_specials(const std::string& special) const for(const config& i : other_attack_->specials_.child_range(special)) { if(other_attack_->special_active(i, AFFECT_OTHER, special)) { - res.emplace_back(&i, other_loc_); + res.emplace_back(&i, other_loc_, other_loc_); } } return res; @@ -1099,7 +1101,7 @@ unit_ability_list attack_type::list_ability(const std::string& ability) const if(self_) { abil_list.append((*self_).get_abilities(ability, self_loc_)); for(unit_ability_list::iterator i = abil_list.begin(); i != abil_list.end();) { - if(!special_active(*i->first, AFFECT_SELF, ability, true, "filter_student")) { + if(!special_active(*i->ability_cfg, AFFECT_SELF, ability, true, "filter_student")) { i = abil_list.erase(i); } else { ++i; @@ -1110,7 +1112,7 @@ unit_ability_list attack_type::list_ability(const std::string& ability) const if(other_) { abil_other_list.append((*other_).get_abilities(ability, other_loc_)); for(unit_ability_list::iterator i = abil_other_list.begin(); i != abil_other_list.end();) { - if(!other_attack_->special_active(*i->first, AFFECT_OTHER, ability, true, "filter_student")) { + if(!other_attack_->special_active(*i->ability_cfg, AFFECT_OTHER, ability, true, "filter_student")) { i = abil_other_list.erase(i); } else { ++i; @@ -1343,7 +1345,7 @@ effect::effect(const unit_ability_list& list, int def, bool backstab) : individual_effect set_effect_min; for (const unit_ability & ability : list) { - const config& cfg = *ability.first; + const config& cfg = *ability.ability_cfg; const std::string& effect_id = cfg[cfg["id"].empty() ? "name" : "id"]; if (!cfg["backstab"].blank()) { @@ -1356,7 +1358,7 @@ effect::effect(const unit_ability_list& list, int def, bool backstab) : continue; if (const config::attribute_value *v = cfg.get("value")) { - int value = get_single_ability_value(*v, def, ability.second, list.loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) { + int value = get_single_ability_value(*v, def, ability, list.loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) { callable.add("base_value", wfl::variant(def)); return formula.evaluate(callable).as_int(); }); @@ -1364,51 +1366,51 @@ effect::effect(const unit_ability_list& list, int def, bool backstab) : int value_cum = cfg["cumulative"].to_bool() ? std::max(def, value) : value; assert((set_effect_min.type != NOT_USED) == (set_effect_max.type != NOT_USED)); if(set_effect_min.type == NOT_USED) { - set_effect_min.set(SET, value_cum, ability.first, ability.second); - set_effect_max.set(SET, value_cum, ability.first, ability.second); + set_effect_min.set(SET, value_cum, ability.ability_cfg, ability.teacher_loc); + set_effect_max.set(SET, value_cum, ability.ability_cfg, ability.teacher_loc); } else { if(value_cum > set_effect_max.value) { - set_effect_max.set(SET, value_cum, ability.first, ability.second); + set_effect_max.set(SET, value_cum, ability.ability_cfg, ability.teacher_loc); } if(value_cum < set_effect_min.value) { - set_effect_min.set(SET, value_cum, ability.first, ability.second); + set_effect_min.set(SET, value_cum, ability.ability_cfg, ability.teacher_loc); } } } if (const config::attribute_value *v = cfg.get("add")) { - int add = get_single_ability_value(*v, def, ability.second, list.loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) { + int add = get_single_ability_value(*v, def, ability, list.loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) { callable.add("base_value", wfl::variant(def)); return formula.evaluate(callable).as_int(); }); std::map::iterator add_effect = values_add.find(effect_id); if(add_effect == values_add.end() || add > add_effect->second.value) { - values_add[effect_id].set(ADD, add, ability.first, ability.second); + values_add[effect_id].set(ADD, add, ability.ability_cfg, ability.teacher_loc); } } if (const config::attribute_value *v = cfg.get("sub")) { - int sub = - get_single_ability_value(*v, def, ability.second, list.loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) { + int sub = - get_single_ability_value(*v, def, ability, list.loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) { callable.add("base_value", wfl::variant(def)); return formula.evaluate(callable).as_int(); }); std::map::iterator sub_effect = values_add.find(effect_id); if(sub_effect == values_add.end() || sub < sub_effect->second.value) { - values_add[effect_id].set(ADD, sub, ability.first, ability.second); + values_add[effect_id].set(ADD, sub, ability.ability_cfg, ability.teacher_loc); } } if (const config::attribute_value *v = cfg.get("multiply")) { - int multiply = static_cast(get_single_ability_value(*v, static_cast(def), ability.second, list.loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) { + int multiply = static_cast(get_single_ability_value(*v, static_cast(def), ability, list.loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) { callable.add("base_value", wfl::variant(def)); return formula.evaluate(callable).as_decimal() / 1000.0 ; }) * 100); std::map::iterator mul_effect = values_mul.find(effect_id); if(mul_effect == values_mul.end() || multiply > mul_effect->second.value) { - values_mul[effect_id].set(MUL, multiply, ability.first, ability.second); + values_mul[effect_id].set(MUL, multiply, ability.ability_cfg, ability.teacher_loc); } } if (const config::attribute_value *v = cfg.get("divide")) { - int divide = static_cast(get_single_ability_value(*v, static_cast(def), ability.second, list.loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) { + int divide = static_cast(get_single_ability_value(*v, static_cast(def), ability, list.loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) { callable.add("base_value", wfl::variant(def)); return formula.evaluate(callable).as_decimal() / 1000.0 ; }) * 100); @@ -1419,7 +1421,7 @@ effect::effect(const unit_ability_list& list, int def, bool backstab) : else { std::map::iterator div_effect = values_div.find(effect_id); if(div_effect == values_div.end() || divide > div_effect->second.value) { - values_div[effect_id].set(DIV, divide, ability.first, ability.second); + values_div[effect_id].set(DIV, divide, ability.ability_cfg, ability.teacher_loc); } } } diff --git a/src/units/map.hpp b/src/units/map.hpp index 043a9b43d7c2..efd06fabe747 100644 --- a/src/units/map.hpp +++ b/src/units/map.hpp @@ -384,6 +384,20 @@ class unit_map return const_cast(this)->find(id); } + template + unit_ptr find_unit_ptr(const T& val) + { + auto res = find(val); + return res != end() ? res.get_shared_ptr() : unit_ptr(); + } + + template + unit_const_ptr find_unit_ptr(const T& val) const + { + auto res = find(val); + return res != end() ? res.get_shared_ptr() : unit_ptr(); + } + unit_iterator find_leader(int side); const_unit_iterator find_leader(int side) const diff --git a/src/units/types.cpp b/src/units/types.cpp index 92ce2d50aca0..bcee02e850f4 100644 --- a/src/units/types.cpp +++ b/src/units/types.cpp @@ -796,7 +796,7 @@ int unit_type::resistance_against(const std::string& damage_name, bool attacker) continue; } - resistance_abilities.emplace_back(&cfg, map_location::null_location()); + resistance_abilities.emplace_back(&cfg, map_location::null_location(), map_location::null_location()); } } diff --git a/src/units/udisplay.cpp b/src/units/udisplay.cpp index 3be2bff4bbf2..d7bc8d550c57 100644 --- a/src/units/udisplay.cpp +++ b/src/units/udisplay.cpp @@ -647,35 +647,35 @@ void unit_attack(display * disp, game_board & board, animator.add_animation(&defender, defender_anim, def->get_location(), true, text, {255, 0, 0}); for(const unit_ability& ability : attacker.get_abilities_weapons("leadership", attack.shared_from_this(), secondary_attack)) { - if(ability.second == a) { + if(ability.teacher_loc == a) { continue; } - if(ability.second == b) { + if(ability.teacher_loc == b) { continue; } - unit_map::const_iterator leader = board.units().find(ability.second); + unit_map::const_iterator leader = board.units().find(ability.teacher_loc); assert(leader.valid()); - leader->set_facing(ability.second.get_relative_dir(a)); - animator.add_animation(&*leader, "leading", ability.second, + leader->set_facing(ability.teacher_loc.get_relative_dir(a)); + animator.add_animation(&*leader, "leading", ability.teacher_loc, att->get_location(), damage, true, "", {0,0,0}, hit_type, attack.shared_from_this(), secondary_attack, swing); } for(const unit_ability& ability : defender.get_abilities_weapons("resistance", attack.shared_from_this(), secondary_attack)) { - if(ability.second == a) { + if(ability.teacher_loc == a) { continue; } - if(ability.second == b) { + if(ability.teacher_loc == b) { continue; } - unit_map::const_iterator helper = board.units().find(ability.second); + unit_map::const_iterator helper = board.units().find(ability.teacher_loc); assert(helper.valid()); - helper->set_facing(ability.second.get_relative_dir(b)); - animator.add_animation(&*helper, "resistance", ability.second, + helper->set_facing(ability.teacher_loc.get_relative_dir(b)); + animator.add_animation(&*helper, "resistance", ability.teacher_loc, def->get_location(), damage, true, "", {0,0,0}, hit_type, attack.shared_from_this(), secondary_attack, swing); } @@ -715,7 +715,7 @@ void reset_helpers(const unit *attacker,const unit *defender) const unit_map& units = disp->get_units(); if(attacker) { for(const unit_ability& ability : attacker->get_abilities("leadership")) { - unit_map::const_iterator leader = units.find(ability.second); + unit_map::const_iterator leader = units.find(ability.teacher_loc); assert(leader != units.end()); leader->anim_comp().set_standing(); } @@ -723,7 +723,7 @@ void reset_helpers(const unit *attacker,const unit *defender) if(defender) { for(const unit_ability& ability : defender->get_abilities("resistance")) { - unit_map::const_iterator helper = units.find(ability.second); + unit_map::const_iterator helper = units.find(ability.teacher_loc); assert(helper != units.end()); helper->anim_comp().set_standing(); } diff --git a/src/units/unit.cpp b/src/units/unit.cpp index b44ad71c6405..552b0adb25f3 100644 --- a/src/units/unit.cpp +++ b/src/units/unit.cpp @@ -1695,7 +1695,7 @@ int unit::resistance_against(const std::string& damage_name,bool attacker,const unit_ability_list resistance_abilities = get_abilities_weapons("resistance",loc, weapon, opp_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)) { + if(!resistance_filter_matches(*i->ability_cfg, attacker, damage_name, 100-res)) { i = resistance_abilities.erase(i); } else { ++i; diff --git a/src/units/unit.hpp b/src/units/unit.hpp index 22a216da948d..d24aed412e44 100644 --- a/src/units/unit.hpp +++ b/src/units/unit.hpp @@ -44,8 +44,27 @@ namespace unit_detail } } -// Data typedef for unit_ability_list. -using unit_ability = std::pair; +/// Data typedef for unit_ability_list. +struct unit_ability +{ + unit_ability(const config* ability_cfg, map_location student_loc, map_location teacher_loc) + : student_loc(student_loc) + , teacher_loc(teacher_loc) + , ability_cfg(ability_cfg) + { + + } + /// Used by the formula in the ability. + /// The REAL location of the student (not the 'we are assiming the student is at this position' location) + /// once unit_ability_list can contain abilities from different 'students', as it contains abilities from + /// a unit aswell from its opponents (abilities with apply_to= opponent) + map_location student_loc; + /// The location of the teacher, that is the unit who owns the ability tags + /// (differnt from student because of [afect_adjacent]) + map_location teacher_loc; + /// The contents of the ability tag, never nullptr. + const config* ability_cfg; +}; class unit_ability_list {