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
* Ability to patch movetypes to account for custom terrains or damage types
* 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:
* Added Category field and color sliders to the Edit Label panel.
* 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"
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
backstab=yes
active_on=offense
[filter_opponent]
[filter_adjacent]
adjacent=$other_unit.facing
is_enemy=yes
[not]
status=petrified
[/not]
[/filter_adjacent]
[/filter_opponent]
[/damage]
#enddef

View file

@ -1225,6 +1225,9 @@ namespace {
a_.get_unit().set_movement(-1, true);
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);
VALIDATE(a_.weapon_ < static_cast<int>(a_.get_unit().attacks().size()),

View file

@ -394,7 +394,7 @@ private:
* cfg: an ability WML structure
*/
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 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");
#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)) {
if (affects_side(j, *resources::teams, side(), it->side()) &&
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;
}
}
@ -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)) {
if (affects_side(j, *resources::teams, side(), it->side()) &&
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]));
}
}
@ -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]);
if (unit == units.end())
return false;
if (!ufilt( *unit ))
if (!ufilt(*unit, *this))
return false;
}
}
@ -348,23 +369,24 @@ bool unit::ability_active(const std::string& ability,const config& cfg,const map
* 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";
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"))
{
if (i.has_attribute("adjacent")) { //key adjacent defined
std::vector<std::string> dirs = utils::split(i["adjacent"]);
if (std::find(dirs.begin(),dirs.end(),adjacent_names[dir]) == dirs.end())
std::vector<map_location::DIRECTION> dirs = map_location::parse_directions(i["adjacent"]);
if (std::find(dirs.begin(), dirs.end(), direction) == dirs.end()) {
continue;
}
}
const config &filter = i.child("filter");
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;
}
}
@ -767,12 +789,13 @@ namespace { // Helpers for attack_type::special_active()
* @param[in] child_tag The tag of the child filter to use.
*/
static bool special_unit_matches(const unit_map::const_iterator & un_it,
const unit_map::const_iterator & u2,
const map_location & loc,
const attack_type * weapon,
const config & filter,
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.
// (This is used by reports.cpp to show active specials when the
// opponent is not known. From a player's perspective, the special
@ -786,7 +809,7 @@ namespace { // Helpers for attack_type::special_active()
return true;
// 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;
// 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;
unit_map::const_iterator self = units.find(self_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".
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;
// 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;
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;
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;
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;
map_location adjacent[6];
@ -876,7 +903,7 @@ bool attack_type::special_active(const config& special, AFFECTS whom,
continue;
unit_map::const_iterator unit = units.find(adjacent[index]);
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;
}
}

View file

@ -53,6 +53,14 @@ bool unit_filter::matches(const unit & u) const {
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 {
// assert(false && "called match against a pure abstract unit_filter! this indicates a programmer error, this function must be overrided");
// 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 {
public:
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;
}
virtual std::vector<const unit *> all_matches_on_map() const {
@ -145,7 +153,7 @@ public:
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 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
*/
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;
if(loc.valid()) {
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 {
// If loc is invalid, then this is a recall list unit (already been scoped)
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();
for (j = dirs.begin(); j != j_end; ++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;
}
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 *> ret;
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);
}
}
@ -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 {
const unit_map & units = fc_.get_disp_context().units();
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();
}
}

View file

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