Several expansions to filters
- Side, location, and weapon filters now support formulas - Additional keys in weapon filters; now covers everything except weights - [has_attack] in unit filters - Formula view of sides, terrain, and weapons expanded to mostly match Lua view
This commit is contained in:
parent
d8ee9dca29
commit
0f072f34f2
7 changed files with 195 additions and 12 deletions
|
@ -17,6 +17,10 @@ Version 1.13.4+dev:
|
|||
nearest neighbor scaling.
|
||||
* Support delayed_variable_substitution= in [on_undo], [on_redo]
|
||||
* formula= in SUF can now reference $other_unit via the formula variable "other"
|
||||
* formula= now supported in location, side, and weapon filters
|
||||
* Weapon filters now support number, parry, accuracy, and movement_used
|
||||
* New [has_attack] in standard unit filters; supercedes has_weapon= and
|
||||
uses full weapon filter.
|
||||
* AiWML:
|
||||
* Simplified aspect syntax which works for all aspects, present and future:
|
||||
* All aspects with simple values can be specified as key=value
|
||||
|
@ -156,6 +160,9 @@ Version 1.13.4+dev:
|
|||
parameter and returns true or false.
|
||||
* Wesnoth formula engine:
|
||||
* Formulas in unit filters can now access nearly all unit attributes
|
||||
* Nearly All side, weapon, and terrain attributes available to Lua code
|
||||
are now also exposed to WFL. The exceptions are mainly translatable
|
||||
strings.
|
||||
* New syntax features:
|
||||
* String interpolation syntax. Within a formula string (enclosed in
|
||||
'single quotes'), the syntax [some_formula] interpolates the result
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
#include "formula/callable_objects.hpp"
|
||||
#include "units/unit.hpp"
|
||||
#include "units/formula_manager.hpp"
|
||||
#include "utils/foreach.hpp"
|
||||
|
||||
template <typename T, typename K>
|
||||
variant convert_map( const std::map<T, K>& input_map ) {
|
||||
|
@ -80,22 +81,38 @@ void location_callable::serialize_to_string(std::string& str) const
|
|||
|
||||
variant attack_type_callable::get_value(const std::string& key) const
|
||||
{
|
||||
if(key == "id") {
|
||||
if(key == "id" || key == "name") {
|
||||
return variant(att_.id());
|
||||
} else if(key == "description") {
|
||||
return variant(att_.name());
|
||||
} else if(key == "type") {
|
||||
return variant(att_.type());
|
||||
} else if(key == "icon") {
|
||||
return variant(att_.icon());
|
||||
} else if(key == "range") {
|
||||
return variant(att_.range());
|
||||
} else if(key == "damage") {
|
||||
return variant(att_.damage());
|
||||
} else if(key == "number_of_attacks") {
|
||||
} else if(key == "number_of_attacks" || key == "number" || key == "num_attacks" || key == "attacks") {
|
||||
return variant(att_.num_attacks());
|
||||
} else if(key == "special") {
|
||||
std::vector<std::pair<t_string, t_string> > specials = att_.special_tooltips();
|
||||
} else if(key == "attack_weight") {
|
||||
return variant(att_.attack_weight(), variant::DECIMAL_VARIANT);
|
||||
} else if(key == "defense_weight") {
|
||||
return variant(att_.defense_weight(), variant::DECIMAL_VARIANT);
|
||||
} else if(key == "accuracy") {
|
||||
return variant(att_.accuracy());
|
||||
} else if(key == "parry") {
|
||||
return variant(att_.parry());
|
||||
} else if(key == "movement_used") {
|
||||
return variant(att_.movement_used());
|
||||
} else if(key == "specials" || key == "special") {
|
||||
const config specials = att_.specials();
|
||||
std::vector<variant> res;
|
||||
|
||||
for( size_t i = 0; i != specials.size(); ++i ) {
|
||||
res.push_back( variant(specials[i].first.base_str()) );
|
||||
FOREACH(const AUTO& special , specials.all_children_range()) {
|
||||
if(!special.cfg["id"].empty()) {
|
||||
res.push_back(variant(special.cfg["id"].str()));
|
||||
}
|
||||
}
|
||||
return variant(&res);
|
||||
}
|
||||
|
@ -106,12 +123,19 @@ variant attack_type_callable::get_value(const std::string& key) const
|
|||
void attack_type_callable::get_inputs(std::vector<game_logic::formula_input>* inputs) const
|
||||
{
|
||||
using game_logic::FORMULA_READ_ONLY;
|
||||
inputs->push_back(game_logic::formula_input("id", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("name", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("type", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("description", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("icon", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("range", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("damage", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("number_of_attacks", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("special", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("number", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("accuracy", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("parry", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("movement_used", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("attack_weight", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("defense_weight", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("specials", FORMULA_READ_ONLY));
|
||||
}
|
||||
|
||||
int attack_type_callable::do_compare(const formula_callable* callable) const
|
||||
|
@ -431,6 +455,24 @@ variant terrain_callable::get_value(const std::string& key) const
|
|||
return variant(new location_callable(loc_));
|
||||
} else if(key == "id") {
|
||||
return variant(std::string(t_.id()));
|
||||
} else if(key == "name") {
|
||||
return variant(t_.name());
|
||||
} else if(key == "editor_name") {
|
||||
return variant(t_.editor_name());
|
||||
} else if(key == "description") {
|
||||
return variant(t_.description());
|
||||
} else if(key == "icon") {
|
||||
return variant(t_.icon_image());
|
||||
} else if(key == "light") {
|
||||
return variant(t_.light_bonus(0));
|
||||
} else if(key == "village") {
|
||||
return variant(t_.is_village());
|
||||
} else if(key == "castle") {
|
||||
return variant(t_.is_castle());
|
||||
} else if(key == "keep") {
|
||||
return variant(t_.is_keep());
|
||||
} else if(key == "healing") {
|
||||
return variant(t_.gives_healing());
|
||||
} else
|
||||
return variant();
|
||||
}
|
||||
|
@ -442,6 +484,15 @@ void terrain_callable::get_inputs(std::vector<game_logic::formula_input>* inputs
|
|||
inputs->push_back(game_logic::formula_input("y", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("loc", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("id", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("name", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("editor_name", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("description", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("icon", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("light", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("village", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("castle", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("keep", FORMULA_READ_ONLY));
|
||||
inputs->push_back(game_logic::formula_input("healing", FORMULA_READ_ONLY));
|
||||
}
|
||||
|
||||
int terrain_callable::do_compare(const formula_callable* callable) const
|
||||
|
|
|
@ -188,26 +188,66 @@ private:
|
|||
|
||||
|
||||
CALLABLE_WRAPPER_START(team)
|
||||
CALLABLE_WRAPPER_INPUT(side)
|
||||
CALLABLE_WRAPPER_INPUT(id)
|
||||
CALLABLE_WRAPPER_INPUT(gold)
|
||||
CALLABLE_WRAPPER_INPUT(start_gold)
|
||||
CALLABLE_WRAPPER_INPUT(base_income)
|
||||
CALLABLE_WRAPPER_INPUT(total_income)
|
||||
CALLABLE_WRAPPER_INPUT(village_gold)
|
||||
CALLABLE_WRAPPER_INPUT(village_support)
|
||||
CALLABLE_WRAPPER_INPUT(recall_cost)
|
||||
CALLABLE_WRAPPER_INPUT(name)
|
||||
CALLABLE_WRAPPER_INPUT(is_human)
|
||||
CALLABLE_WRAPPER_INPUT(is_ai)
|
||||
CALLABLE_WRAPPER_INPUT(is_network)
|
||||
CALLABLE_WRAPPER_INPUT(fog)
|
||||
CALLABLE_WRAPPER_INPUT(shroud)
|
||||
CALLABLE_WRAPPER_INPUT(hidden)
|
||||
CALLABLE_WRAPPER_INPUT(flag)
|
||||
CALLABLE_WRAPPER_INPUT(flag_icon)
|
||||
CALLABLE_WRAPPER_INPUT(team_name)
|
||||
CALLABLE_WRAPPER_INPUT(color)
|
||||
CALLABLE_WRAPPER_INPUT(share_vision)
|
||||
CALLABLE_WRAPPER_INPUT(carryover_bonus)
|
||||
CALLABLE_WRAPPER_INPUT(carryover_percentage)
|
||||
CALLABLE_WRAPPER_INPUT(carryover_add)
|
||||
CALLABLE_WRAPPER_INPUT(recruit)
|
||||
CALLABLE_WRAPPER_INPUT_END
|
||||
CALLABLE_WRAPPER_FN(side)
|
||||
CALLABLE_WRAPPER_FN2(id, save_id)
|
||||
CALLABLE_WRAPPER_FN(save_id)
|
||||
CALLABLE_WRAPPER_FN(gold)
|
||||
if(key == "start_gold") { \
|
||||
return variant(lexical_cast<int>(object_.start_gold())); \
|
||||
if(key == "start_gold") {
|
||||
return variant(lexical_cast<int>(object_.start_gold()));
|
||||
} else
|
||||
CALLABLE_WRAPPER_FN(base_income)
|
||||
CALLABLE_WRAPPER_FN(total_income)
|
||||
CALLABLE_WRAPPER_FN(village_gold)
|
||||
CALLABLE_WRAPPER_FN(village_support)
|
||||
CALLABLE_WRAPPER_FN(recall_cost)
|
||||
CALLABLE_WRAPPER_FN2(is_human, is_local_human)
|
||||
CALLABLE_WRAPPER_FN2(is_ai, is_local_ai)
|
||||
CALLABLE_WRAPPER_FN(is_network)
|
||||
CALLABLE_WRAPPER_FN2(fog, uses_fog)
|
||||
CALLABLE_WRAPPER_FN2(shroud, uses_shroud)
|
||||
CALLABLE_WRAPPER_FN(hidden)
|
||||
CALLABLE_WRAPPER_FN(flag)
|
||||
CALLABLE_WRAPPER_FN(flag_icon)
|
||||
CALLABLE_WRAPPER_FN(team_name)
|
||||
CALLABLE_WRAPPER_FN(color)
|
||||
CALLABLE_WRAPPER_FN2(share_vision, share_vision().to_string)
|
||||
CALLABLE_WRAPPER_FN(carryover_bonus)
|
||||
CALLABLE_WRAPPER_FN(carryover_percentage)
|
||||
CALLABLE_WRAPPER_FN(carryover_add)
|
||||
if(key == "recruit") {
|
||||
const std::set<std::string>& recruits = object_.recruits();
|
||||
std::vector<variant> result;
|
||||
for(std::set<std::string>::const_iterator it = recruits.begin(); it != recruits.end(); ++it) {
|
||||
result.push_back(variant(*it));
|
||||
}
|
||||
return variant(&result);
|
||||
} else
|
||||
CALLABLE_WRAPPER_END
|
||||
|
||||
#endif
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include "units/filter.hpp"
|
||||
#include "units/map.hpp"
|
||||
#include "variable.hpp"
|
||||
#include "formula/callable_objects.hpp"
|
||||
#include "formula/formula.hpp"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
|
@ -241,6 +243,21 @@ bool side_filter::match_internal(const team &t) const
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cfg_.has_attribute("formula")) {
|
||||
try {
|
||||
const team_callable callable(t);
|
||||
const game_logic::formula form(cfg_["formula"]);
|
||||
if(!form.evaluate(callable).as_bool()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch(game_logic::formula_error& e) {
|
||||
lg::wml_error() << "Formula error in side filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
|
||||
// Formulae with syntax errors match nothing
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -30,6 +30,8 @@
|
|||
#include "units/unit.hpp"
|
||||
#include "units/filter.hpp"
|
||||
#include "variable.hpp"
|
||||
#include "formula/callable_objects.hpp"
|
||||
#include "formula/formula.hpp"
|
||||
|
||||
#include <boost/foreach.hpp>
|
||||
|
||||
|
@ -325,6 +327,24 @@ bool terrain_filter::match_internal(const map_location& loc, const bool ignore_x
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if(cfg_.has_attribute("formula")) {
|
||||
try {
|
||||
const gamemap& map = fc_->get_disp_context().map();
|
||||
t_translation::t_terrain t = map.get_terrain(loc);
|
||||
const terrain_type& ter = map.tdata()->get_terrain_info(t);
|
||||
const terrain_callable callable(ter,loc);
|
||||
const game_logic::formula form(cfg_["formula"]);
|
||||
if(!form.evaluate(callable).as_bool()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch(game_logic::formula_error& e) {
|
||||
lg::wml_error() << "Formula error in location filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
|
||||
// Formulae with syntax errors match nothing
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -20,6 +20,8 @@
|
|||
#include "global.hpp"
|
||||
|
||||
#include "units/attack_type.hpp"
|
||||
#include "formula/callable_objects.hpp"
|
||||
#include "formula/formula.hpp"
|
||||
|
||||
#include "log.hpp"
|
||||
#include "serialization/string_utils.hpp"
|
||||
|
@ -96,9 +98,14 @@ static bool matches_simple_filter(const attack_type & attack, const config & fil
|
|||
{
|
||||
const std::vector<std::string>& filter_range = utils::split(filter["range"]);
|
||||
const std::string& filter_damage = filter["damage"];
|
||||
const std::string& filter_attacks = filter["number"];
|
||||
const std::string& filter_accuracy = filter["accuracy"];
|
||||
const std::string& filter_parry = filter["parry"];
|
||||
const std::string& filter_movement = filter["movement_used"];
|
||||
const std::vector<std::string> filter_name = utils::split(filter["name"]);
|
||||
const std::vector<std::string> filter_type = utils::split(filter["type"]);
|
||||
const std::string filter_special = filter["special"];
|
||||
const std::string filter_formula = filter["formula"];
|
||||
|
||||
if ( !filter_range.empty() && std::find(filter_range.begin(), filter_range.end(), attack.range()) == filter_range.end() )
|
||||
return false;
|
||||
|
@ -106,6 +113,18 @@ static bool matches_simple_filter(const attack_type & attack, const config & fil
|
|||
if ( !filter_damage.empty() && !in_ranges(attack.damage(), utils::parse_ranges(filter_damage)) )
|
||||
return false;
|
||||
|
||||
if (!filter_attacks.empty() && !in_ranges(attack.num_attacks(), utils::parse_ranges(filter_attacks)))
|
||||
return false;
|
||||
|
||||
if (!filter_accuracy.empty() && !in_ranges(attack.accuracy(), utils::parse_ranges(filter_accuracy)))
|
||||
return false;
|
||||
|
||||
if (!filter_parry.empty() && !in_ranges(attack.parry(), utils::parse_ranges(filter_parry)))
|
||||
return false;
|
||||
|
||||
if (!filter_movement.empty() && !in_ranges(attack.movement_used(), utils::parse_ranges(filter_movement)))
|
||||
return false;
|
||||
|
||||
if ( !filter_name.empty() && std::find(filter_name.begin(), filter_name.end(), attack.id()) == filter_name.end() )
|
||||
return false;
|
||||
|
||||
|
@ -114,6 +133,21 @@ static bool matches_simple_filter(const attack_type & attack, const config & fil
|
|||
|
||||
if ( !filter_special.empty() && !attack.get_special_bool(filter_special, true) )
|
||||
return false;
|
||||
|
||||
if (!filter_formula.empty()) {
|
||||
try {
|
||||
const attack_type_callable callable(attack);
|
||||
const game_logic::formula form(filter_formula);
|
||||
if(!form.evaluate(callable).as_bool()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
} catch(game_logic::formula_error& e) {
|
||||
lg::wml_error() << "Formula error in weapon filter: " << e.type << " at " << e.filename << ':' << e.line << ")\n";
|
||||
// Formulae with syntax errors match nothing
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Passed all tests.
|
||||
return true;
|
||||
|
|
|
@ -399,7 +399,21 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l
|
|||
}
|
||||
}
|
||||
|
||||
if (!vcfg["has_weapon"].blank()) {
|
||||
if (vcfg.has_child("has_attack")) {
|
||||
const vconfig& weap_filter = vcfg.child("has_attack");
|
||||
bool has_weapon = false;
|
||||
const std::vector<attack_type>& attacks = u.attacks();
|
||||
for(std::vector<attack_type>::const_iterator i = attacks.begin();
|
||||
i != attacks.end(); ++i) {
|
||||
if(i->matches_filter(weap_filter.get_parsed_config())) {
|
||||
has_weapon = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!has_weapon) {
|
||||
return false;
|
||||
}
|
||||
} else if (!vcfg["has_weapon"].blank()) {
|
||||
std::string weapon = vcfg["has_weapon"];
|
||||
bool has_weapon = false;
|
||||
const std::vector<attack_type>& attacks = u.attacks();
|
||||
|
|
Loading…
Add table
Reference in a new issue