New autostored WML variable $other_unit for [filter_adjacent]

Also accessible in:
- Weapon specials ([filter_self], [filter_opponent], [filter_attacker], [filter_defender])
- Abilities ([affect_adjacent][filter])

The backstab weapon special is now implemented using this.
(In a weapon special filter, units are guaranteed to be facing each other.)
This commit is contained in:
Celtic Minstrel 2015-09-18 19:15:21 -04:00
parent a5184181df
commit 12c96a94d2
7 changed files with 96 additions and 26 deletions

View file

@ -85,6 +85,13 @@ Version 1.13.1+dev:
number of movement points the attack consumes number of movement points the attack consumes
* Ability to patch movetypes to account for custom terrains or damage types * Ability to patch movetypes to account for custom terrains or damage types
* Removed y offset by -1 from [message]'s scroll-to-unit logic. * Removed y offset by -1 from [message]'s scroll-to-unit logic.
* New auto-stored WML variable usable in the following contexts:
* [filter_adjacent] - refers to the $this_unit of the enclosing filter
(In weapon specials and unit abilities, the unit owning the ability.)
* [filter_self/opponent/attacker/defender] (weapon specials)
Refers to the other unit in the attack (eg in [filter_self], it's the opponent)
* [affect_adjacent][filter] (unit abilities)
Refers to the unit owning the ability
* Editor: * Editor:
* Added Category field and color sliders to the Edit Label panel. * Added Category field and color sliders to the Edit Label panel.
* Miscellaneous and bug fixes: * Miscellaneous and bug fixes:

View file

@ -551,8 +551,16 @@ Enemy units cannot see this unit while it is in deep water, except if they have
name= _ "backstab" name= _ "backstab"
description= _ "When used offensively, this attack deals double damage if there is an enemy of the target on the opposite side of the target, and that unit is not incapacitated (turned to stone or otherwise paralyzed)." description= _ "When used offensively, this attack deals double damage if there is an enemy of the target on the opposite side of the target, and that unit is not incapacitated (turned to stone or otherwise paralyzed)."
multiply=2 multiply=2
backstab=yes
active_on=offense active_on=offense
[filter_opponent]
[filter_adjacent]
adjacent=$other_unit.facing
is_enemy=yes
[not]
status=petrified
[/not]
[/filter_adjacent]
[/filter_opponent]
[/damage] [/damage]
#enddef #enddef

View file

@ -1225,6 +1225,9 @@ namespace {
a_.get_unit().set_movement(-1, true); a_.get_unit().set_movement(-1, true);
return; return;
} }
a_.get_unit().set_facing(a_.loc_.get_relative_dir(d_.loc_));
d_.get_unit().set_facing(d_.loc_.get_relative_dir(a_.loc_));
a_.get_unit().set_attacks(a_.get_unit().attacks_left()-1); a_.get_unit().set_attacks(a_.get_unit().attacks_left()-1);
VALIDATE(a_.weapon_ < static_cast<int>(a_.get_unit().attacks().size()), VALIDATE(a_.weapon_ < static_cast<int>(a_.get_unit().attacks().size()),

View file

@ -394,7 +394,7 @@ private:
* cfg: an ability WML structure * cfg: an ability WML structure
*/ */
bool ability_active(const std::string& ability,const config& cfg,const map_location& loc) const; bool ability_active(const std::string& ability,const config& cfg,const map_location& loc) const;
bool ability_affects_adjacent(const std::string& ability,const config& cfg,int dir,const map_location& loc) const; bool ability_affects_adjacent(const std::string& ability,const config& cfg,int dir,const map_location& loc,const unit& from) const;
bool ability_affects_self(const std::string& ability,const config& cfg,const map_location& loc) const; bool ability_affects_self(const std::string& ability,const config& cfg,const map_location& loc) const;
bool resistance_filter_matches(const config& cfg,bool attacker,const std::string& damage_name, int res) const; bool resistance_filter_matches(const config& cfg,bool attacker,const std::string& damage_name, int res) const;

View file

@ -32,7 +32,28 @@
static lg::log_domain log_engine("engine"); static lg::log_domain log_engine("engine");
#define ERR_NG LOG_STREAM(err, log_engine) #define ERR_NG LOG_STREAM(err, log_engine)
namespace {
class temporary_facing
{
map_location::DIRECTION save_dir_;
unit_map::const_iterator u_;
public:
temporary_facing(unit_map::const_iterator u, map_location::DIRECTION new_dir)
: save_dir_(u.valid() ? u->facing() : map_location::NDIRECTIONS)
, u_(u)
{
if (u_.valid()) {
u_->set_facing(new_dir);
}
}
~temporary_facing()
{
if (u_.valid()) {
u_->set_facing(save_dir_);
}
}
};
}
/* /*
* *
@ -139,7 +160,7 @@ bool unit::get_ability_bool(const std::string& tag_name, const map_location& loc
BOOST_FOREACH(const config &j, adj_abilities.child_range(tag_name)) { BOOST_FOREACH(const config &j, adj_abilities.child_range(tag_name)) {
if (affects_side(j, *resources::teams, side(), it->side()) && if (affects_side(j, *resources::teams, side(), it->side()) &&
it->ability_active(tag_name, j, adjacent[i]) && it->ability_active(tag_name, j, adjacent[i]) &&
ability_affects_adjacent(tag_name, j, i, loc)) ability_affects_adjacent(tag_name, j, i, loc, *it))
return true; return true;
} }
} }
@ -182,7 +203,7 @@ unit_ability_list unit::get_abilities(const std::string& tag_name, const map_loc
BOOST_FOREACH(const config &j, adj_abilities.child_range(tag_name)) { BOOST_FOREACH(const config &j, adj_abilities.child_range(tag_name)) {
if (affects_side(j, *resources::teams, side(), it->side()) && if (affects_side(j, *resources::teams, side(), it->side()) &&
it->ability_active(tag_name, j, adjacent[i]) && it->ability_active(tag_name, j, adjacent[i]) &&
ability_affects_adjacent(tag_name, j, i, loc)) ability_affects_adjacent(tag_name, j, i, loc, *it))
res.push_back(unit_ability(&j, adjacent[i])); res.push_back(unit_ability(&j, adjacent[i]));
} }
} }
@ -320,7 +341,7 @@ bool unit::ability_active(const std::string& ability,const config& cfg,const map
unit_map::const_iterator unit = units.find(adjacent[index]); unit_map::const_iterator unit = units.find(adjacent[index]);
if (unit == units.end()) if (unit == units.end())
return false; return false;
if (!ufilt( *unit )) if (!ufilt(*unit, *this))
return false; return false;
} }
} }
@ -348,23 +369,24 @@ bool unit::ability_active(const std::string& ability,const config& cfg,const map
* cfg: an ability WML structure * cfg: an ability WML structure
* *
*/ */
bool unit::ability_affects_adjacent(const std::string& ability, const config& cfg,int dir,const map_location& loc) const bool unit::ability_affects_adjacent(const std::string& ability, const config& cfg,int dir,const map_location& loc,const unit& from) const
{ {
bool illuminates = ability == "illuminates"; bool illuminates = ability == "illuminates";
assert(dir >=0 && dir <= 5); assert(dir >=0 && dir <= 5);
static const std::string adjacent_names[6] = {"n","ne","se","s","sw","nw"}; map_location::DIRECTION direction = static_cast<map_location::DIRECTION>(dir);
BOOST_FOREACH(const config &i, cfg.child_range("affect_adjacent")) BOOST_FOREACH(const config &i, cfg.child_range("affect_adjacent"))
{ {
if (i.has_attribute("adjacent")) { //key adjacent defined if (i.has_attribute("adjacent")) { //key adjacent defined
std::vector<std::string> dirs = utils::split(i["adjacent"]); std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
if (std::find(dirs.begin(),dirs.end(),adjacent_names[dir]) == dirs.end()) if (std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
continue; continue;
}
} }
const config &filter = i.child("filter"); const config &filter = i.child("filter");
if (!filter || //filter tag given if (!filter || //filter tag given
unit_filter(vconfig(filter), resources::filter_con, illuminates).matches(*this, loc) ) { unit_filter(vconfig(filter), resources::filter_con, illuminates).matches(*this, loc, from) ) {
return true; return true;
} }
} }
@ -767,12 +789,13 @@ namespace { // Helpers for attack_type::special_active()
* @param[in] child_tag The tag of the child filter to use. * @param[in] child_tag The tag of the child filter to use.
*/ */
static bool special_unit_matches(const unit_map::const_iterator & un_it, static bool special_unit_matches(const unit_map::const_iterator & un_it,
const unit_map::const_iterator & u2,
const map_location & loc, const map_location & loc,
const attack_type * weapon, const attack_type * weapon,
const config & filter, const config & filter,
const std::string & child_tag) const std::string & child_tag)
{ {
if ( !loc.valid() ) if (!loc.valid() || !u2.valid())
// The special's context was set to ignore this unit, so assume we pass. // The special's context was set to ignore this unit, so assume we pass.
// (This is used by reports.cpp to show active specials when the // (This is used by reports.cpp to show active specials when the
// opponent is not known. From a player's perspective, the special // opponent is not known. From a player's perspective, the special
@ -786,7 +809,7 @@ namespace { // Helpers for attack_type::special_active()
return true; return true;
// Check for a unit match. // Check for a unit match.
if ( !un_it.valid() || !unit_filter(vconfig(filter_child), resources::filter_con).matches(*un_it, loc) ) if ( !un_it.valid() || !unit_filter(vconfig(filter_child), resources::filter_con).matches(*un_it, loc, *u2))
return false; return false;
// Check for a weapon match. // Check for a weapon match.
@ -843,6 +866,10 @@ bool attack_type::special_active(const config& special, AFFECTS whom,
const unit_map & units = *resources::units; const unit_map & units = *resources::units;
unit_map::const_iterator self = units.find(self_loc_); unit_map::const_iterator self = units.find(self_loc_);
unit_map::const_iterator other = units.find(other_loc_); unit_map::const_iterator other = units.find(other_loc_);
// Make sure they're facing each other.
temporary_facing self_facing(self, self_loc_.get_relative_dir(other_loc_));
temporary_facing other_facing(other, other_loc_.get_relative_dir(self_loc_));
// Translate our context into terms of "attacker" and "defender". // Translate our context into terms of "attacker" and "defender".
unit_map::const_iterator & att = is_attacker_ ? self : other; unit_map::const_iterator & att = is_attacker_ ? self : other;
@ -853,13 +880,13 @@ bool attack_type::special_active(const config& special, AFFECTS whom,
const attack_type * def_weapon = is_attacker_ ? other_attack_ : this; const attack_type * def_weapon = is_attacker_ ? other_attack_ : this;
// Filter the units involved. // Filter the units involved.
if ( !special_unit_matches(self, self_loc_, this, special, "filter_self") ) if (!special_unit_matches(self, other, self_loc_, this, special, "filter_self"))
return false; return false;
if ( !special_unit_matches(other, other_loc_, other_attack_, special, "filter_opponent") ) if (!special_unit_matches(other, self, other_loc_, other_attack_, special, "filter_opponent"))
return false; return false;
if ( !special_unit_matches(att, att_loc, att_weapon, special, "filter_attacker") ) if (!special_unit_matches(att, def, att_loc, att_weapon, special, "filter_attacker"))
return false; return false;
if ( !special_unit_matches(def, def_loc, def_weapon, special, "filter_defender") ) if (!special_unit_matches(def, att, def_loc, def_weapon, special, "filter_defender"))
return false; return false;
map_location adjacent[6]; map_location adjacent[6];
@ -876,7 +903,7 @@ bool attack_type::special_active(const config& special, AFFECTS whom,
continue; continue;
unit_map::const_iterator unit = units.find(adjacent[index]); unit_map::const_iterator unit = units.find(adjacent[index]);
if ( unit == units.end() || if ( unit == units.end() ||
!unit_filter(vconfig(i), resources::filter_con).matches(*unit, adjacent[index]) ) //TODO: Should this filter get precomputed? !unit_filter(vconfig(i), resources::filter_con).matches(*unit, adjacent[index], *self)) //TODO: Should this filter get precomputed?
return false; return false;
} }
} }

View file

@ -53,6 +53,14 @@ bool unit_filter::matches(const unit & u) const {
return matches (u, u.get_location()); return matches (u, u.get_location());
} }
bool unit_filter::matches(const unit & u, const map_location & loc, const unit & u2) const {
return impl_->matches(u,loc,&u2);
}
bool unit_filter::matches(const unit & u, const unit & u2) const {
return matches(u, u.get_location(), u2);
}
//bool unit_filter::matches(const unit & /*u*/, const map_location & /*loc*/) const { //bool unit_filter::matches(const unit & /*u*/, const map_location & /*loc*/) const {
// assert(false && "called match against a pure abstract unit_filter! this indicates a programmer error, this function must be overrided"); // assert(false && "called match against a pure abstract unit_filter! this indicates a programmer error, this function must be overrided");
// return false; // return false;
@ -66,7 +74,7 @@ static boost::shared_ptr<unit_filter_abstract_impl> construct(const vconfig & vc
class null_unit_filter_impl : public unit_filter_abstract_impl { class null_unit_filter_impl : public unit_filter_abstract_impl {
public: public:
null_unit_filter_impl(const filter_context & fc) : fc_(fc) {} null_unit_filter_impl(const filter_context & fc) : fc_(fc) {}
virtual bool matches(const unit & /*u*/, const map_location & /*loc*/) const { virtual bool matches(const unit & /*u*/, const map_location & /*loc*/, const unit *) const {
return true; return true;
} }
virtual std::vector<const unit *> all_matches_on_map() const { virtual std::vector<const unit *> all_matches_on_map() const {
@ -145,7 +153,7 @@ public:
this->vcfg.make_safe(); this->vcfg.make_safe();
} }
virtual bool matches(const unit & u, const map_location & loc) const; virtual bool matches(const unit & u, const map_location & loc, const unit * u2) const;
virtual std::vector<const unit *> all_matches_on_map() const; virtual std::vector<const unit *> all_matches_on_map() const;
virtual unit_const_ptr first_match_on_map() const; virtual unit_const_ptr first_match_on_map() const;
@ -188,13 +196,19 @@ unit_filter::unit_filter(const vconfig & vcfg, const filter_context * fc, bool f
/** Begin implementations of filter impl's /** Begin implementations of filter impl's
*/ */
bool basic_unit_filter_impl::matches(const unit & u, const map_location& loc) const bool basic_unit_filter_impl::matches(const unit & u, const map_location& loc, const unit * u2) const
{ {
bool matches = true; bool matches = true;
if(loc.valid()) { if(loc.valid()) {
scoped_xy_unit auto_store("this_unit", loc.x, loc.y, fc_.get_disp_context().units()); scoped_xy_unit auto_store("this_unit", loc.x, loc.y, fc_.get_disp_context().units());
matches = internal_matches_filter(u, loc); if (u2) {
const map_location& loc2 = u2->get_location();
scoped_xy_unit auto_store("other_unit", loc2.x, loc2.y, fc_.get_disp_context().units());
matches = internal_matches_filter(u, loc);
} else {
matches = internal_matches_filter(u, loc);
}
} else { } else {
// If loc is invalid, then this is a recall list unit (already been scoped) // If loc is invalid, then this is a recall list unit (already been scoped)
matches = internal_matches_filter(u, loc); matches = internal_matches_filter(u, loc);
@ -466,7 +480,7 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l
std::vector<map_location::DIRECTION>::const_iterator j, j_end = dirs.end(); std::vector<map_location::DIRECTION>::const_iterator j, j_end = dirs.end();
for (j = dirs.begin(); j != j_end; ++j) { for (j = dirs.begin(); j != j_end; ++j) {
unit_map::const_iterator unit_itor = units.find(adjacent[*j]); unit_map::const_iterator unit_itor = units.find(adjacent[*j]);
if (unit_itor == units.end() || !filt(*unit_itor)) { if (unit_itor == units.end() || !filt(*unit_itor, u)) {
continue; continue;
} }
boost::optional<bool> is_enemy; boost::optional<bool> is_enemy;
@ -529,7 +543,7 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l
std::vector<const unit *> basic_unit_filter_impl::all_matches_on_map() const { std::vector<const unit *> basic_unit_filter_impl::all_matches_on_map() const {
std::vector<const unit *> ret; std::vector<const unit *> ret;
BOOST_FOREACH(const unit & u, fc_.get_disp_context().units()) { BOOST_FOREACH(const unit & u, fc_.get_disp_context().units()) {
if (matches(u, u.get_location())) { if (matches(u, u.get_location(), NULL)) {
ret.push_back(&u); ret.push_back(&u);
} }
} }
@ -539,7 +553,7 @@ std::vector<const unit *> basic_unit_filter_impl::all_matches_on_map() const {
unit_const_ptr basic_unit_filter_impl::first_match_on_map() const { unit_const_ptr basic_unit_filter_impl::first_match_on_map() const {
const unit_map & units = fc_.get_disp_context().units(); const unit_map & units = fc_.get_disp_context().units();
for(unit_map::const_iterator u = units.begin(); u != units.end(); u++) { for(unit_map::const_iterator u = units.begin(); u != units.end(); u++) {
if (matches(*u,u->get_location())) { if (matches(*u,u->get_location(),NULL)) {
return u.get_shared_ptr(); return u.get_shared_ptr();
} }
} }

View file

@ -38,7 +38,7 @@ struct map_location;
class unit_filter_abstract_impl { class unit_filter_abstract_impl {
public: public:
virtual bool matches(const unit & u, const map_location & loc) const = 0; virtual bool matches(const unit & u, const map_location & loc, const unit * u2 = NULL) const = 0;
virtual std::vector<const unit*> all_matches_on_map() const = 0; virtual std::vector<const unit*> all_matches_on_map() const = 0;
virtual unit_const_ptr first_match_on_map() const = 0; virtual unit_const_ptr first_match_on_map() const = 0;
virtual ~unit_filter_abstract_impl() {} virtual ~unit_filter_abstract_impl() {}
@ -68,6 +68,9 @@ public:
/// (Only use for units currently on the map; otherwise use the overload /// (Only use for units currently on the map; otherwise use the overload
/// that takes a location, possibly with a null location.) /// that takes a location, possibly with a null location.)
bool matches(const unit & u) const; bool matches(const unit & u) const;
bool matches(const unit & u, const map_location & loc, const unit & u2) const;
bool matches(const unit & u, const unit & u2) const;
bool operator()(const unit & u, const map_location & loc) const { bool operator()(const unit & u, const map_location & loc) const {
return matches(u,loc); return matches(u,loc);
@ -77,6 +80,14 @@ public:
return matches(u); return matches(u);
} }
bool operator()(const unit & u, const map_location & loc, const unit & u2) const {
return matches(u,loc,u2);
}
bool operator()(const unit & u, const unit & u2) const {
return matches(u,u2);
}
std::vector<const unit *> all_matches_on_map() const { std::vector<const unit *> all_matches_on_map() const {
return impl_->all_matches_on_map(); return impl_->all_matches_on_map();
} }