diff --git a/changelog b/changelog index 6c89f471d0ad..0f030521387e 100644 --- a/changelog +++ b/changelog @@ -15,6 +15,9 @@ Version 1.13.8+dev: * Warnings for using deprecated Lua functions now only appear in debug mode. * wesnoth.game_config is now accessible in application and mapgen kernels, though some of its contents are missing. + * New extra argument to wesnoth.match_location and wesnoth.get_locations + allows specifying the teleport_unit. + * Support the extra wesnoth.match_unit arguments also in wesnoth.get_units * Multiplayer: * When set to remember your password, Wesnoth now encrypts it. It is still possible to obtain the password from preferences, but it's no longer as @@ -32,6 +35,18 @@ Version 1.13.8+dev: * Miscellaneous and Bug Fixes: * Add --report/-R command line switch for printing the same report from the Game Version dialog's clipboard function to stdout. + * WFL Engine + * Add owner key to terrain space callable, for villages + * Location formulas in [tunnel] now have a teleport_unit variable + * Fix a crash when attempting to call a non-existent function + * The following previously FormulaAI-exclusive functions are now also available + in filter formulas (SUF, SLF, SSF, SWF): + adjacent_locs, location_in_radius, get_unit_type, unit_at, defense_on, + chance_to_hit, movement_cost + * New builtin functions for manipulating locations (available to all formulas): + adjacent_locs, are_adjacent, relative_dir, direction_from, rotate_loc_around + * New enemy_of function checks if its second argument is an enemy of the first + Arguments can be side or unit objects, or integer side indices (1..n) * WML Engine * If ai_algorithm is used in [modify_side][ai], it now replaces the whole AI with the contents of [modify_side][ai], instead of appending these parameters. @@ -46,6 +61,8 @@ Version 1.13.8+dev: * Fix some hotkeys not working (issues #1737 and #1769) * New vision_cost and jamming_cost keys in SUF * Integer SUF keys (eg level) now accept a list of ranges + * Fix $other_unit variable in SUF not being available in nested [and] [or] [not] + * Unit ability values can now be specified with WFL Version 1.13.8: * Campaigns: diff --git a/data/campaigns/The_Hammer_of_Thursagan/utils/abilities.cfg b/data/campaigns/The_Hammer_of_Thursagan/utils/abilities.cfg index 0822c15996d3..e00a9d97a02a 100644 --- a/data/campaigns/The_Hammer_of_Thursagan/utils/abilities.cfg +++ b/data/campaigns/The_Hammer_of_Thursagan/utils/abilities.cfg @@ -6,10 +6,9 @@ _" The presence of this unit inspires own units next to it to deal more damage i #define ABILITY_INSPIRE # Canned definition of the Inspire ability to be included in an # [abilities] clause. - # Note: Works only on units of level 0-5 [leadership] id=inspire - value=150 + value="(25 * (level - other.level + 1))" cumulative=no name= _ "inspire" female_name= _ "female^inspire" @@ -17,62 +16,7 @@ _" The presence of this unit inspires own units next to it to deal more damage i affect_self=no [affect_adjacent] [filter] - level=$($other_unit.level - 5) - [/filter] - [/affect_adjacent] - [/leadership] - [leadership] - id=inspire - value=125 - cumulative=no - affect_self=no - [affect_adjacent] - [filter] - level=$($other_unit.level - 4) - [/filter] - [/affect_adjacent] - [/leadership] - [leadership] - id=inspire - value=100 - cumulative=no - affect_self=no - [affect_adjacent] - [filter] - level=$($other_unit.level - 3) - [/filter] - [/affect_adjacent] - [/leadership] - [leadership] - id=inspire - value=75 - cumulative=no - affect_self=no - [affect_adjacent] - [filter] - level=$($other_unit.level - 2) - [/filter] - [/affect_adjacent] - [/leadership] - [leadership] - id=inspire - value=50 - cumulative=no - affect_self=no - [affect_adjacent] - [filter] - level=$($other_unit.level - 1) - [/filter] - [/affect_adjacent] - [/leadership] - [leadership] - id=inspire - value=25 - cumulative=no - affect_self=no - [affect_adjacent] - [filter] - level=$other_unit.level + formula="level <= other.level" [/filter] [/affect_adjacent] [/leadership] diff --git a/data/core/_main.cfg b/data/core/_main.cfg index 0abbeba562c8..e95fbe9d68eb 100644 --- a/data/core/_main.cfg +++ b/data/core/_main.cfg @@ -8,7 +8,6 @@ wesnoth.dofile 'lua/backwards-compatibility.lua' wesnoth.dofile 'lua/wml-tags.lua' wesnoth.dofile 'lua/feeding.lua' -wesnoth.dofile 'lua/teleport_filter.lua' >> [/lua] diff --git a/data/core/macros/abilities.cfg b/data/core/macros/abilities.cfg index 54274ada27d4..6b66697d52bc 100644 --- a/data/core/macros/abilities.cfg +++ b/data/core/macros/abilities.cfg @@ -99,10 +99,9 @@ This ability will not cure an affected unit of poison, however, only delay its e #define ABILITY_LEADERSHIP # Canned definition of the Leadership ability to be included in an # [abilities] clause. - # Note: Works only on units of level 1-5 [leadership] id=leadership - value=125 + value="(25 * (level - other.level))" cumulative=no name= _ "leadership" female_name= _ "female^leadership" @@ -112,51 +111,7 @@ Adjacent own units of lower level will do more damage in battle. When a unit adj affect_self=no [affect_adjacent] [filter] - lua_function="leadership_receiver_filter_5" - [/filter] - [/affect_adjacent] - [/leadership] - [leadership] - id=leadership - value=100 - cumulative=no - affect_self=no - [affect_adjacent] - [filter] - lua_function="leadership_receiver_filter_4" - [/filter] - [/affect_adjacent] - [/leadership] - [leadership] - id=leadership - value=75 - cumulative=no - affect_self=no - [affect_adjacent] - [filter] - lua_function="leadership_receiver_filter_3" - [/filter] - [/affect_adjacent] - [/leadership] - [leadership] - id=leadership - value=50 - cumulative=no - affect_self=no - [affect_adjacent] - [filter] - lua_function="leadership_receiver_filter_2" - [/filter] - [/affect_adjacent] - [/leadership] - [leadership] - id=leadership - value=25 - cumulative=no - affect_self=no - [affect_adjacent] - [filter] - lua_function="leadership_receiver_filter_1" + formula="level < other.level" [/filter] [/affect_adjacent] [/leadership] @@ -201,15 +156,18 @@ Any units adjacent to this unit will fight as if it were dusk when it is night, description= _ "This unit may teleport between any two empty villages owned by its side using one of its moves." [tunnel] id=village_teleport - [source] terrain=*^V* - lua_function="teleport_source_filter" + formula=" + owner = teleport_unit.side and (unit = teleport_unit or not unit) + where + unit = unit_at(loc) + " [/source] [target] terrain=*^V* - lua_function="teleport_target_filter" + formula="owner = teleport_unit.side and not unit_at(loc)" [/target] [filter] @@ -349,7 +307,11 @@ Enemy units cannot see this unit while it is in deep water, except if they have multiply=2 active_on=offense [filter_opponent] - lua_function="backstab_defender_filter" + formula=" + enemy_of(self, flanker) and not flanker.petrified + where + flanker = unit_at(direction_from(loc, other.facing)) + " [/filter_opponent] [/damage] #enddef diff --git a/data/lua/teleport_filter.lua b/data/lua/teleport_filter.lua deleted file mode 100644 index 93006a44325c..000000000000 --- a/data/lua/teleport_filter.lua +++ /dev/null @@ -1,58 +0,0 @@ -function teleport_source_filter(x, y) - if wesnoth.get_village_owner(x, y) ~= wesnoth.get_variable("teleport_unit.side") then - return false - end - local blocking_unit = wesnoth.get_unit(x, y) - return (not blocking_unit) or blocking_unit.id == wesnoth.get_variable("teleport_unit.id") -end - -function teleport_target_filter(x, y) - if wesnoth.get_village_owner(x, y) ~= wesnoth.get_variable("teleport_unit.side") then - return false - end - local blocking_unit = wesnoth.get_unit(x, y) - return not blocking_unit -end - -function backstab_defender_filter(defender) - - local attacker_x = wesnoth.get_variable("other_unit.x") - local attacker_y = wesnoth.get_variable("other_unit.y") - - local defender_x = defender.x - local defender_y = defender.y - - local adjacent = {wesnoth.map.get_adjacent_tiles(defender_x, defender_y)} - - local attacker_pos_index = nil - for i,v in ipairs(adjacent) do - if v[1] == attacker_x and v[2] == attacker_y then - attacker_pos_index = i - end - end - - if attacker_pos_index == nil then - -- Attack not from adjacent location - return false - end - - local opposite_pos_index = (((attacker_pos_index + 3) - 1) % 6) + 1 - local opposite_unit = wesnoth.get_unit(adjacent[opposite_pos_index][1], adjacent[opposite_pos_index][2]) - - if not opposite_unit then - -- No opposite unit - return false - end - - if opposite_unit.status.petrified then - return false - end - - return wesnoth.is_enemy(opposite_unit.side, defender.side) -end - -for i = 1, 5 do - _G["leadership_receiver_filter_" .. i] = function(receiver) - return receiver.level == wesnoth.get_variable("other_unit.level") - i - end -end diff --git a/projectfiles/VC12/wesnoth.vcxproj b/projectfiles/VC12/wesnoth.vcxproj index 946156bd9fb1..ec7dab057ac3 100644 --- a/projectfiles/VC12/wesnoth.vcxproj +++ b/projectfiles/VC12/wesnoth.vcxproj @@ -1088,6 +1088,12 @@ $(IntDir)Formula\ $(IntDir)Formula\ + + $(IntDir)Formula\ + $(IntDir)Formula\ + $(IntDir)Formula\ + $(IntDir)Formula\ + $(IntDir)Formula\ $(IntDir)Formula\ @@ -3596,6 +3602,7 @@ + diff --git a/projectfiles/VC12/wesnoth.vcxproj.filters b/projectfiles/VC12/wesnoth.vcxproj.filters index 9a9d41452167..f3bff16d2805 100644 --- a/projectfiles/VC12/wesnoth.vcxproj.filters +++ b/projectfiles/VC12/wesnoth.vcxproj.filters @@ -1538,6 +1538,9 @@ Preferences + + Formula + @@ -2982,6 +2985,9 @@ Preferences + + Formula + diff --git a/source_lists/wesnoth b/source_lists/wesnoth index 87d0dbd04ceb..6ce15a3a2fa6 100644 --- a/source_lists/wesnoth +++ b/source_lists/wesnoth @@ -108,6 +108,7 @@ formula/debugger.cpp formula/debugger_fwd.cpp formula/formula.cpp formula/function.cpp +formula/function_gamestate.cpp formula/string_utils.cpp formula/tokenizer.cpp formula/variant.cpp diff --git a/src/ai/formula/ai.cpp b/src/ai/formula/ai.cpp index a98f551a7a30..c155ba600bc9 100644 --- a/src/ai/formula/ai.cpp +++ b/src/ai/formula/ai.cpp @@ -534,7 +534,7 @@ variant formula_ai::get_value(const std::string& key) const return get_keeps(); } else if(key == "map") { - return variant(std::make_shared(resources::gameboard->map())); + return variant(std::make_shared(*resources::gameboard)); } else if(key == "villages") { return villages_from_set(resources::gameboard->map().villages()); diff --git a/src/ai/formula/function_table.cpp b/src/ai/formula/function_table.cpp index 844dfc51922f..a10caddcce51 100644 --- a/src/ai/formula/function_table.cpp +++ b/src/ai/formula/function_table.cpp @@ -24,6 +24,7 @@ #include "ai/default/contexts.hpp" +#include "formula/function_gamestate.hpp" #include "attack_prediction.hpp" #include "filesystem.hpp" #include "game_board.hpp" @@ -407,67 +408,6 @@ class nearest_loc_function : public function_expression { }; -class adjacent_locs_function : public function_expression { -public: - adjacent_locs_function(const args_list& args) - : function_expression("adjacent_locs", args, 1, 1) - { - } - -private: - variant execute(const formula_callable& variables, formula_debugger *fdb) const { - const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "adjacent_locs:location")).convert_to()->loc(); - map_location adj[6]; - get_adjacent_tiles(loc, adj); - - std::vector v; - for(int n = 0; n != 6; ++n) { - if (resources::gameboard->map().on_board(adj[n]) ) - v.emplace_back(std::make_shared(adj[n])); - } - - return variant(v); - } -}; - - -class locations_in_radius_function : public function_expression { -public: - locations_in_radius_function(const args_list& args) - : function_expression("locations_in_radius", args, 2, 2) - { - } - -private: - variant execute(const formula_callable& variables, formula_debugger *fdb) const { - const map_location loc = args()[0]->evaluate(variables,fdb).convert_to()->loc(); - - int range = args()[1]->evaluate(variables,fdb).as_int(); - - if( range < 0 ) - return variant(); - - if(!range) - return variant(std::make_shared(loc)); - - std::vector res; - - get_tiles_in_radius( loc, range, res); - - std::vector v; - v.reserve(res.size()+1); - v.emplace_back(std::make_shared(loc)); - - for(size_t n = 0; n != res.size(); ++n) { - if (resources::gameboard->map().on_board(res[n]) ) - v.emplace_back(std::make_shared(res[n])); - } - - return variant(v); - } -}; - - /** FormulaAI function to run fai script from file. Usable from in-game console. * arguments[0] - required file name, follows the usual wml convention */ @@ -861,24 +801,6 @@ class outcomes_function : public function_expression { // formula_ai& ai_; //}; -class get_unit_type_function : public function_expression { -public: - explicit get_unit_type_function(const args_list& args) - : function_expression("get_unit_type", args, 1, 1) - {} -private: - variant execute(const formula_callable& variables, formula_debugger *fdb) const { - const std::string type = args()[0]->evaluate(variables,add_debug_info(fdb,0,"get_unit_type:name")).as_string(); - - const unit_type *ut = unit_types.find(type); - if(ut) { - return variant(std::make_shared(*ut)); - } - - return variant(); - } -}; - class rate_action_function : public function_expression { @@ -1273,28 +1195,6 @@ class is_unowned_village_function : public function_expression { }; -class unit_at_function : public function_expression { -public: - unit_at_function(const args_list& args) - : function_expression("unit_at", args, 1, 1) - {} -private: - variant execute(const formula_callable& variables, formula_debugger *fdb) const { - variant loc_var = args()[0]->evaluate(variables,add_debug_info(fdb,0,"unit_at:location")); - if (loc_var.is_null()) { - return variant(); - } - auto loc = loc_var.convert_to(); - const unit_map::const_iterator i = resources::gameboard->units().find(loc->loc()); - if(i != resources::gameboard->units().end()) { - return variant(std::make_shared(*i)); - } else { - return variant(); - } - } -}; - - class unit_moves_function : public function_expression { public: unit_moves_function(const args_list& args, const formula_ai& ai_object) @@ -1347,144 +1247,6 @@ class units_can_reach_function : public function_expression { } }; - -class defense_on_function : public function_expression { -public: - defense_on_function(const args_list& args) - : function_expression("defense_on", args, 2, 2) - {} -private: - variant execute(const formula_callable& variables, formula_debugger *fdb) const { - variant u = args()[0]->evaluate(variables,add_debug_info(fdb,0,"defense_on:unit")); - variant loc_var = args()[1]->evaluate(variables,add_debug_info(fdb,1,"defense_on:location")); - if(u.is_null() || loc_var.is_null()) { - return variant(); - } - - auto u_call = u.try_convert(); - auto u_type = u.try_convert(); - const map_location& loc = loc_var.convert_to()->loc(); - - if (u_call) - { - const unit& un = u_call->get_unit(); - - if( un.total_movement() < un.movement_cost( (resources::gameboard->map())[loc]) ) - return variant(); - - if(!resources::gameboard->map().on_board(loc)) { - return variant(); - } - - return variant(100 - un.defense_modifier((resources::gameboard->map())[loc])); - } - - if (u_type) - { - const unit_type& un = u_type->get_unit_type(); - - if( un.movement() < un.movement_type().movement_cost((resources::gameboard->map())[loc]) ) - return variant(); - - if(!resources::gameboard->map().on_board(loc)) { - return variant(); - } - - return variant(100 - un.movement_type().defense_modifier((resources::gameboard->map())[loc])); - } - - return variant(); - } -}; - - -class chance_to_hit_function : public function_expression { -public: - chance_to_hit_function(const args_list& args) - : function_expression("chance_to_hit", args, 2, 2) - {} -private: - variant execute(const formula_callable& variables, formula_debugger *fdb) const { - variant u = args()[0]->evaluate(variables,add_debug_info(fdb,0,"chance_to_hit:unit")); - variant loc_var = args()[1]->evaluate(variables,add_debug_info(fdb,1,"chance_to_hit:location")); - if(u.is_null() || loc_var.is_null()) { - return variant(); - } - - auto u_call = u.try_convert(); - auto u_type = u.try_convert(); - const map_location& loc = loc_var.convert_to()->loc(); - - if (u_call) - { - const unit& un = u_call->get_unit(); - - if(!resources::gameboard->map().on_board(loc)) { - return variant(); - } - - return variant(un.defense_modifier((resources::gameboard->map())[loc])); - } - - if (u_type) - { - const unit_type& un = u_type->get_unit_type(); - - if(!resources::gameboard->map().on_board(loc)) { - return variant(); - } - - return variant(un.movement_type().defense_modifier((resources::gameboard->map())[loc])); - } - - return variant(); - } -}; - - -class movement_cost_function : public function_expression { -public: - movement_cost_function(const args_list& args) - : function_expression("movement_cost", args, 2, 2) - {} -private: - variant execute(const formula_callable& variables, formula_debugger *fdb) const { - variant u = args()[0]->evaluate(variables,add_debug_info(fdb,0,"movement_cost:unit")); - variant loc_var = args()[1]->evaluate(variables,add_debug_info(fdb,0,"movement_cost:location")); - if(u.is_null() || loc_var.is_null()) { - return variant(); - } - //we can pass to this function either unit_callable or unit_type callable - auto u_call = u.try_convert(); - auto u_type = u.try_convert(); - const map_location& loc = loc_var.convert_to()->loc(); - - if (u_call) - { - const unit& un = u_call->get_unit(); - - if(!resources::gameboard->map().on_board(loc)) { - return variant(); - } - - return variant(un.movement_cost((resources::gameboard->map())[loc])); - } - - if (u_type) - { - const unit_type& un = u_type->get_unit_type(); - - if(!resources::gameboard->map().on_board(loc)) { - return variant(); - } - - return variant(un.movement_type().movement_cost((resources::gameboard->map())[loc])); - } - - return variant(); - } -}; - class is_avoided_location_function : public function_expression { public: is_avoided_location_function(const args_list& args, const formula_ai& ai_object) @@ -1589,54 +1351,48 @@ class ai_formula_function : public formula_function { } -// First macro is for functions taking an additional formula_ai argument. -// Functions using the second macro could potentially be made core. -#define AI_FUNCTION(name) add_function(#name, formula_function_ptr( \ +// This macro is for functions taking an additional formula_ai argument. +// Functions using the other macro could potentially be made core. +#define DECLARE_FAI_FUNCTION(name) add_function(#name, formula_function_ptr( \ new ai_formula_function(#name, ai))) -#define FUNCTION(name) add_function(#name, formula_function_ptr( \ - new builtin_formula_function(#name))) - -ai_function_symbol_table::ai_function_symbol_table(ai::formula_ai& ai) : function_symbol_table(new action_function_symbol_table) { - FUNCTION(outcomes); - //AI_FUNCTION(evaluate_for_position); - FUNCTION(move); - FUNCTION(move_partial); - FUNCTION(attack); - AI_FUNCTION(rate_action); - FUNCTION(recall); - FUNCTION(recruit); - FUNCTION(get_unit_type); - AI_FUNCTION(is_avoided_location); - FUNCTION(is_village); - AI_FUNCTION(is_unowned_village); - FUNCTION(unit_at); - AI_FUNCTION(unit_moves); - FUNCTION(set_unit_var); - FUNCTION(fallback); - FUNCTION(units_can_reach); - AI_FUNCTION(debug_label); - FUNCTION(defense_on); - FUNCTION(chance_to_hit); - FUNCTION(movement_cost); - FUNCTION(max_possible_damage); - FUNCTION(max_possible_damage_with_retaliation); - AI_FUNCTION(next_hop); - FUNCTION(adjacent_locs); - FUNCTION(locations_in_radius); - FUNCTION(castle_locs); - FUNCTION(timeofday_modifier); - AI_FUNCTION(distance_to_nearest_unowned_village); - AI_FUNCTION(shortest_path); - AI_FUNCTION(simplest_path); - AI_FUNCTION(nearest_keep); - AI_FUNCTION(suitable_keep); - FUNCTION(nearest_loc); - AI_FUNCTION(find_shroud); - AI_FUNCTION(close_enemies); - FUNCTION(calculate_outcome); - AI_FUNCTION(run_file); - AI_FUNCTION(calculate_map_ownership); + +ai_function_symbol_table::ai_function_symbol_table(ai::formula_ai& ai) + : function_symbol_table(std::make_shared(std::make_shared())) +{ + function_symbol_table& functions_table = *this; + DECLARE_WFL_FUNCTION(outcomes); + //DECLARE_FAI_FUNCTION(evaluate_for_position); + DECLARE_WFL_FUNCTION(move); + DECLARE_WFL_FUNCTION(move_partial); + DECLARE_WFL_FUNCTION(attack); + DECLARE_FAI_FUNCTION(rate_action); + DECLARE_WFL_FUNCTION(recall); + DECLARE_WFL_FUNCTION(recruit); + DECLARE_FAI_FUNCTION(is_avoided_location); + DECLARE_WFL_FUNCTION(is_village); + DECLARE_FAI_FUNCTION(is_unowned_village); + DECLARE_FAI_FUNCTION(unit_moves); + DECLARE_WFL_FUNCTION(set_unit_var); + DECLARE_WFL_FUNCTION(fallback); + DECLARE_WFL_FUNCTION(units_can_reach); + DECLARE_FAI_FUNCTION(debug_label); + DECLARE_WFL_FUNCTION(max_possible_damage); + DECLARE_WFL_FUNCTION(max_possible_damage_with_retaliation); + DECLARE_FAI_FUNCTION(next_hop); + DECLARE_WFL_FUNCTION(castle_locs); + DECLARE_WFL_FUNCTION(timeofday_modifier); + DECLARE_FAI_FUNCTION(distance_to_nearest_unowned_village); + DECLARE_FAI_FUNCTION(shortest_path); + DECLARE_FAI_FUNCTION(simplest_path); + DECLARE_FAI_FUNCTION(nearest_keep); + DECLARE_FAI_FUNCTION(suitable_keep); + DECLARE_WFL_FUNCTION(nearest_loc); + DECLARE_FAI_FUNCTION(find_shroud); + DECLARE_FAI_FUNCTION(close_enemies); + DECLARE_WFL_FUNCTION(calculate_outcome); + DECLARE_FAI_FUNCTION(run_file); + DECLARE_FAI_FUNCTION(calculate_map_ownership); } -#undef FUNCTION +#undef DECLARE_WFL_FUNCTION } diff --git a/src/formula/callable_objects.cpp b/src/formula/callable_objects.cpp index fe733f93f217..25b44ab02f4d 100644 --- a/src/formula/callable_objects.cpp +++ b/src/formula/callable_objects.cpp @@ -17,6 +17,7 @@ #include "config.hpp" #include "formula/function.hpp" #include "map/map.hpp" +#include "display_context.hpp" #include "team.hpp" #include "units/formula_manager.hpp" #include "log.hpp" @@ -484,6 +485,11 @@ int config_callable::do_compare(const formula_callable* callable) const return cfg_.hash().compare(cfg_callable->get_config().hash()); } +terrain_callable::terrain_callable(const display_context& dc, const map_location& loc) : loc_(loc), t_(dc.map().get_terrain_info(loc)), owner_(dc.village_owner(loc)) +{ + type_ = TERRAIN_C; +} + variant terrain_callable::get_value(const std::string& key) const { if(key == "x") { @@ -512,6 +518,8 @@ variant terrain_callable::get_value(const std::string& key) const return variant(t_.is_keep()); } else if(key == "healing") { return variant(t_.gives_healing()); + } else if(key == "owner") { + return variant(owner_); } return variant(); @@ -532,6 +540,7 @@ void terrain_callable::get_inputs(formula_input_vector& inputs) const add_input(inputs, "castle"); add_input(inputs, "keep"); add_input(inputs, "healing"); + add_input(inputs, "owner"); } int terrain_callable::do_compare(const formula_callable* callable) const @@ -545,6 +554,10 @@ int terrain_callable::do_compare(const formula_callable* callable) const return loc_.do_compare(other_loc); } +const gamemap& gamemap_callable::get_gamemap() const { + return board_.map(); +} + void gamemap_callable::get_inputs(formula_input_vector& inputs) const { add_input(inputs, "gamemap"); @@ -556,22 +569,22 @@ void gamemap_callable::get_inputs(formula_input_vector& inputs) const variant gamemap_callable::get_value(const std::string& key) const { if(key == "terrain") { - int w = gamemap_.w(); - int h = gamemap_.h(); + int w = get_gamemap().w(); + int h = get_gamemap().h(); std::vector vars; for(int i = 0; i < w; i++) { for(int j = 0; j < h; j++) { const map_location loc(i, j); - vars.emplace_back(std::make_shared(gamemap_.get_terrain_info(loc), loc)); + vars.emplace_back(std::make_shared(board_, loc)); } } return variant(vars); } else if(key == "w") { - return variant(gamemap_.w()); + return variant(get_gamemap().w()); } else if(key == "h") { - return variant(gamemap_.h()); + return variant(get_gamemap().h()); } else { return variant(); } diff --git a/src/formula/callable_objects.hpp b/src/formula/callable_objects.hpp index b3702b179b6d..5a1667b26b02 100644 --- a/src/formula/callable_objects.hpp +++ b/src/formula/callable_objects.hpp @@ -21,6 +21,7 @@ class team; class terrain_type; +class display_context; namespace wfl { @@ -28,10 +29,7 @@ namespace wfl class terrain_callable : public formula_callable { public: - terrain_callable(const terrain_type& t, const map_location& loc) : loc_(loc), t_(t) - { - type_ = TERRAIN_C; - } + terrain_callable(const display_context& m, const map_location& loc); variant get_value(const std::string& key) const override; void get_inputs(formula_input_vector& inputs) const override; @@ -41,21 +39,22 @@ class terrain_callable : public formula_callable private: const map_location loc_; const terrain_type& t_; + const int owner_; }; class gamemap_callable : public formula_callable { public: - explicit gamemap_callable(const gamemap& g) : gamemap_(g) + explicit gamemap_callable(const display_context& g) : board_(g) {} void get_inputs(formula_input_vector& inputs) const override; variant get_value(const std::string& key) const override; - const gamemap& get_gamemap() const { return gamemap_; } + const gamemap& get_gamemap() const; private: - const gamemap& gamemap_; + const display_context& board_; }; class location_callable : public formula_callable diff --git a/src/formula/function.cpp b/src/formula/function.cpp index 1d75316ea2a4..8d7c52510d28 100644 --- a/src/formula/function.cpp +++ b/src/formula/function.cpp @@ -1463,6 +1463,80 @@ class distance_between_function : public function_expression { } }; +class adjacent_locs_function : public function_expression { +public: + adjacent_locs_function(const args_list& args) + : function_expression("adjacent_locs", args, 1, 1) {} + +private: + variant execute(const formula_callable& variables, formula_debugger *fdb) const { + const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "adjacent_locs:location")).convert_to()->loc(); + map_location adj[6]; + get_adjacent_tiles(loc, adj); + + std::vector v; + for(int n = 0; n != 6; ++n) { + v.emplace_back(std::make_shared(adj[n])); + } + + return variant(v); + } +}; + +class are_adjacent_function : public function_expression { +public: + are_adjacent_function(const args_list& args) + : function_expression("are_adjacent", args, 2, 2) {} + +private: + variant execute(const formula_callable& variables, formula_debugger* fdb) const { + const map_location loc1 = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "are_adjacent:location_A")).convert_to()->loc(); + const map_location loc2 = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "are_adjacent:location_B")).convert_to()->loc(); + return variant(tiles_adjacent(loc1, loc2) ? 1 : 0); + } +}; + +class relative_dir_function : public function_expression { +public: + relative_dir_function(const args_list& args) + : function_expression("relative_dir", args, 2, 2) {} + +private: + variant execute(const formula_callable& variables, formula_debugger* fdb) const { + const map_location loc1 = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "relative_dir:location_A")).convert_to()->loc(); + const map_location loc2 = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "relative_dir:location_B")).convert_to()->loc(); + return variant(map_location::write_direction(loc1.get_relative_dir(loc2))); + } +}; + +class direction_from_function : public function_expression { +public: + direction_from_function(const args_list& args) + : function_expression("direction_from", args, 2, 3) {} + +private: + variant execute(const formula_callable& variables, formula_debugger* fdb) const { + const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "direction_from:location")).convert_to()->loc(); + const std::string dir_str = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "direction_from:dir")).as_string(); + int n = args().size() == 3 ? args()[2]->evaluate(variables, add_debug_info(fdb, 2, "direction_from:count")).as_int() : 1; + return variant(std::make_shared(loc.get_direction(map_location::parse_direction(dir_str), n))); + } +}; + +class rotate_loc_around_function : public function_expression { +public: + rotate_loc_around_function(const args_list& args) + : function_expression("rotate_loc_around", args, 2, 3) {} + +private: + variant execute(const formula_callable& variables, formula_debugger* fdb) const { + const map_location center = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "direction_from:center")).convert_to()->loc(); + const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 1, "direction_from:location")).convert_to()->loc(); + int n = args().size() == 3 ? args()[2]->evaluate(variables, add_debug_info(fdb, 2, "direction_from:count")).as_int() : 1; + return variant(std::make_shared(loc.rotate_right_around_center(center, n))); + } +}; + class type_function : public function_expression { public: @@ -1582,11 +1656,7 @@ function_expression_ptr user_formula_function::generate_function_expression(cons return function_expression_ptr(new formula_function_expression(name_, args, formula_, precondition_, args_)); } -function_symbol_table::function_symbol_table(function_symbol_table* parent) : parent(parent) {} - -function_symbol_table::~function_symbol_table() { - if(parent) delete parent; -} +function_symbol_table::function_symbol_table(std::shared_ptr parent) : parent(parent ? parent : get_builtins()) {} void function_symbol_table::add_function(const std::string& name, formula_function_ptr fcn) { @@ -1600,9 +1670,11 @@ expression_ptr function_symbol_table::create_function(const std::string& fn, con return i->second->generate_function_expression(args); } - expression_ptr res((parent ? parent : get_builtins())->create_function(fn, args)); - if(res) { - return res; + if(parent) { + expression_ptr res(parent->create_function(fn, args)); + if(res) { + return res; + } } throw formula_error("Unknown function: " + fn, "", "", 0); @@ -1613,8 +1685,6 @@ std::set function_symbol_table::get_function_names() const std::set res; if(parent) { res = parent->get_function_names(); - } else if(this != get_builtins()) { - res = get_builtins()->get_function_names(); } for(functions_map::const_iterator iter = custom_formulas_.begin(); iter != custom_formulas_.end(); ++iter ) { res.insert((*iter).first); @@ -1622,84 +1692,87 @@ std::set function_symbol_table::get_function_names() const return res; } -#define FUNCTION(name) functions_table.add_function(#name, \ - formula_function_ptr(new builtin_formula_function(#name))) - -function_symbol_table* function_symbol_table::get_builtins() { - static function_symbol_table functions_table; +std::shared_ptr function_symbol_table::get_builtins() { + static function_symbol_table functions_table(builtins_tag); if(functions_table.empty()) { + functions_table.parent = nullptr; using namespace builtins; - FUNCTION(debug); - FUNCTION(dir); - FUNCTION(if); - FUNCTION(switch); - FUNCTION(abs); - FUNCTION(min); - FUNCTION(max); - FUNCTION(choose); - FUNCTION(debug_float); - FUNCTION(debug_print); - FUNCTION(debug_profile); - FUNCTION(wave); - FUNCTION(sort); - FUNCTION(contains_string); - FUNCTION(find_string); - FUNCTION(reverse); - FUNCTION(filter); - FUNCTION(find); - FUNCTION(map); - FUNCTION(zip); - FUNCTION(take_while); - FUNCTION(reduce); - FUNCTION(sum); - FUNCTION(head); - FUNCTION(tail); - FUNCTION(size); - FUNCTION(null); - FUNCTION(ceil); - FUNCTION(floor); - FUNCTION(trunc); - FUNCTION(frac); - FUNCTION(sgn); - FUNCTION(round); - FUNCTION(as_decimal); - FUNCTION(pair); - FUNCTION(loc); - FUNCTION(distance_between); - FUNCTION(index_of); - FUNCTION(keys); - FUNCTION(values); - FUNCTION(tolist); - FUNCTION(tomap); - FUNCTION(substring); - FUNCTION(replace); - FUNCTION(length); - FUNCTION(concatenate); - FUNCTION(sin); - FUNCTION(cos); - FUNCTION(tan); - FUNCTION(asin); - FUNCTION(acos); - FUNCTION(atan); - FUNCTION(sqrt); - FUNCTION(cbrt); - FUNCTION(root); - FUNCTION(log); - FUNCTION(exp); - FUNCTION(pi); - FUNCTION(hypot); - FUNCTION(type); - } - - return &functions_table; + DECLARE_WFL_FUNCTION(debug); + DECLARE_WFL_FUNCTION(dir); + DECLARE_WFL_FUNCTION(if); + DECLARE_WFL_FUNCTION(switch); + DECLARE_WFL_FUNCTION(abs); + DECLARE_WFL_FUNCTION(min); + DECLARE_WFL_FUNCTION(max); + DECLARE_WFL_FUNCTION(choose); + DECLARE_WFL_FUNCTION(debug_float); + DECLARE_WFL_FUNCTION(debug_print); + DECLARE_WFL_FUNCTION(debug_profile); + DECLARE_WFL_FUNCTION(wave); + DECLARE_WFL_FUNCTION(sort); + DECLARE_WFL_FUNCTION(contains_string); + DECLARE_WFL_FUNCTION(find_string); + DECLARE_WFL_FUNCTION(reverse); + DECLARE_WFL_FUNCTION(filter); + DECLARE_WFL_FUNCTION(find); + DECLARE_WFL_FUNCTION(map); + DECLARE_WFL_FUNCTION(zip); + DECLARE_WFL_FUNCTION(take_while); + DECLARE_WFL_FUNCTION(reduce); + DECLARE_WFL_FUNCTION(sum); + DECLARE_WFL_FUNCTION(head); + DECLARE_WFL_FUNCTION(tail); + DECLARE_WFL_FUNCTION(size); + DECLARE_WFL_FUNCTION(null); + DECLARE_WFL_FUNCTION(ceil); + DECLARE_WFL_FUNCTION(floor); + DECLARE_WFL_FUNCTION(trunc); + DECLARE_WFL_FUNCTION(frac); + DECLARE_WFL_FUNCTION(sgn); + DECLARE_WFL_FUNCTION(round); + DECLARE_WFL_FUNCTION(as_decimal); + DECLARE_WFL_FUNCTION(pair); + DECLARE_WFL_FUNCTION(loc); + DECLARE_WFL_FUNCTION(distance_between); + DECLARE_WFL_FUNCTION(adjacent_locs); + DECLARE_WFL_FUNCTION(are_adjacent); + DECLARE_WFL_FUNCTION(relative_dir); + DECLARE_WFL_FUNCTION(direction_from); + DECLARE_WFL_FUNCTION(rotate_loc_around); + DECLARE_WFL_FUNCTION(index_of); + DECLARE_WFL_FUNCTION(keys); + DECLARE_WFL_FUNCTION(values); + DECLARE_WFL_FUNCTION(tolist); + DECLARE_WFL_FUNCTION(tomap); + DECLARE_WFL_FUNCTION(substring); + DECLARE_WFL_FUNCTION(replace); + DECLARE_WFL_FUNCTION(length); + DECLARE_WFL_FUNCTION(concatenate); + DECLARE_WFL_FUNCTION(sin); + DECLARE_WFL_FUNCTION(cos); + DECLARE_WFL_FUNCTION(tan); + DECLARE_WFL_FUNCTION(asin); + DECLARE_WFL_FUNCTION(acos); + DECLARE_WFL_FUNCTION(atan); + DECLARE_WFL_FUNCTION(sqrt); + DECLARE_WFL_FUNCTION(cbrt); + DECLARE_WFL_FUNCTION(root); + DECLARE_WFL_FUNCTION(log); + DECLARE_WFL_FUNCTION(exp); + DECLARE_WFL_FUNCTION(pi); + DECLARE_WFL_FUNCTION(hypot); + DECLARE_WFL_FUNCTION(type); + } + + return std::shared_ptr(&functions_table, [](function_symbol_table*){}); } -action_function_symbol_table::action_function_symbol_table() { +action_function_symbol_table::action_function_symbol_table(std::shared_ptr parent) : function_symbol_table(parent) { using namespace actions; function_symbol_table& functions_table = *this; - FUNCTION(safe_call); - FUNCTION(set_var); + DECLARE_WFL_FUNCTION(safe_call); + DECLARE_WFL_FUNCTION(set_var); } } diff --git a/src/formula/function.hpp b/src/formula/function.hpp index af5898fa735e..e70daecb7f0a 100644 --- a/src/formula/function.hpp +++ b/src/formula/function.hpp @@ -139,23 +139,29 @@ typedef std::shared_ptr formula_function_ptr; typedef std::map functions_map; class function_symbol_table { - function_symbol_table* parent = nullptr; + std::shared_ptr parent; functions_map custom_formulas_; + enum builtins_tag_t {builtins_tag}; + function_symbol_table(builtins_tag_t) {} public: - explicit function_symbol_table(function_symbol_table* parent = nullptr); - ~function_symbol_table(); + explicit function_symbol_table(std::shared_ptr parent = nullptr); void add_function(const std::string& name, formula_function_ptr fcn); expression_ptr create_function(const std::string& fn, const std::vector& args) const; std::set get_function_names() const; - bool empty() {return custom_formulas_.empty();} - static function_symbol_table* get_builtins(); + bool empty() {return custom_formulas_.empty() && (parent == nullptr || parent->empty());} + static std::shared_ptr get_builtins(); }; class action_function_symbol_table : public function_symbol_table { public: - action_function_symbol_table(); + action_function_symbol_table(std::shared_ptr parent = nullptr); }; +/// Declares a function `name` in the local function table `functions_table`. +/// The function must be defined by a `name_function` class which is accessible in the current scope. +#define DECLARE_WFL_FUNCTION(name) functions_table.add_function(#name, \ + formula_function_ptr(new builtin_formula_function(#name))) + class wrapper_formula : public formula_expression { public: wrapper_formula() diff --git a/src/formula/function_gamestate.cpp b/src/formula/function_gamestate.cpp new file mode 100644 index 000000000000..12e644619c0f --- /dev/null +++ b/src/formula/function_gamestate.cpp @@ -0,0 +1,316 @@ + +#include "formula/function_gamestate.hpp" +#include "formula/callable_objects.hpp" + +#include "resources.hpp" +#include "game_board.hpp" +#include "map/map.hpp" +#include "pathutils.hpp" + +namespace wfl { + +namespace gamestate { + +class adjacent_locs_function : public function_expression +{ +public: + adjacent_locs_function(const args_list& args) + : function_expression("adjacent_locs", args, 1, 1) + {} + +private: + variant execute(const formula_callable& variables, formula_debugger *fdb) const + { + const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "adjacent_locs:location")).convert_to()->loc(); + map_location adj[6]; + get_adjacent_tiles(loc, adj); + + std::vector v; + for(int n = 0; n != 6; ++n) { + if(resources::gameboard->map().on_board(adj[n])) { + v.emplace_back(std::make_shared(adj[n])); + } + } + + return variant(v); + } +}; + +class locations_in_radius_function : public function_expression +{ +public: + locations_in_radius_function(const args_list& args) + : function_expression("locations_in_radius", args, 2, 2) + {} + +private: + variant execute(const formula_callable& variables, formula_debugger *fdb) const + { + const map_location loc = args()[0]->evaluate(variables, fdb).convert_to()->loc(); + + int range = args()[1]->evaluate(variables, fdb).as_int(); + + if(range < 0) { + return variant(); + } + + if(!range) { + return variant(std::make_shared(loc)); + } + + std::vector res; + + get_tiles_in_radius(loc, range, res); + + std::vector v; + v.reserve(res.size() + 1); + v.emplace_back(std::make_shared(loc)); + + for(size_t n = 0; n != res.size(); ++n) { + if(resources::gameboard->map().on_board(res[n])) { + v.emplace_back(std::make_shared(res[n])); + } + } + + return variant(v); + } +}; + +class get_unit_type_function : public function_expression +{ +public: + explicit get_unit_type_function(const args_list& args) + : function_expression("get_unit_type", args, 1, 1) + {} +private: + variant execute(const formula_callable& variables, formula_debugger *fdb) const + { + const std::string type = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "get_unit_type:name")).as_string(); + + const unit_type *ut = unit_types.find(type); + if(ut) { + return variant(std::make_shared(*ut)); + } + + return variant(); + } +}; + + +class unit_at_function : public function_expression +{ +public: + unit_at_function(const args_list& args) + : function_expression("unit_at", args, 1, 1) + {} +private: + variant execute(const formula_callable& variables, formula_debugger *fdb) const + { + variant loc_var = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "unit_at:location")); + if(loc_var.is_null()) { + return variant(); + } + auto loc = loc_var.convert_to(); + const unit_map::const_iterator i = resources::gameboard->units().find(loc->loc()); + if(i != resources::gameboard->units().end()) { + return variant(std::make_shared(*i)); + } else { + return variant(); + } + } +}; + + +class defense_on_function : public function_expression +{ +public: + defense_on_function(const args_list& args) + : function_expression("defense_on", args, 2, 2) + {} +private: + variant execute(const formula_callable& variables, formula_debugger *fdb) const + { + variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "defense_on:unit")); + variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "defense_on:location")); + if(u.is_null() || loc_var.is_null()) { + return variant(); + } + + auto u_call = u.try_convert(); + auto u_type = u.try_convert(); + const map_location& loc = loc_var.convert_to()->loc(); + + if(u_call) { + const unit& un = u_call->get_unit(); + + if(un.total_movement() < un.movement_cost((resources::gameboard->map())[loc])) + return variant(); + + if(!resources::gameboard->map().on_board(loc)) { + return variant(); + } + + return variant(100 - un.defense_modifier((resources::gameboard->map())[loc])); + } + + if(u_type) { + const unit_type& un = u_type->get_unit_type(); + + if(un.movement() < un.movement_type().movement_cost((resources::gameboard->map())[loc])) + return variant(); + + if(!resources::gameboard->map().on_board(loc)) { + return variant(); + } + + return variant(100 - un.movement_type().defense_modifier((resources::gameboard->map())[loc])); + } + + return variant(); + } +}; + + +class chance_to_hit_function : public function_expression +{ +public: + chance_to_hit_function(const args_list& args) + : function_expression("chance_to_hit", args, 2, 2) + {} +private: + variant execute(const formula_callable& variables, formula_debugger *fdb) const + { + variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "chance_to_hit:unit")); + variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "chance_to_hit:location")); + if(u.is_null() || loc_var.is_null()) { + return variant(); + } + + auto u_call = u.try_convert(); + auto u_type = u.try_convert(); + const map_location& loc = loc_var.convert_to()->loc(); + + if(u_call) { + const unit& un = u_call->get_unit(); + + if(!resources::gameboard->map().on_board(loc)) { + return variant(); + } + + return variant(un.defense_modifier((resources::gameboard->map())[loc])); + } + + if(u_type) { + const unit_type& un = u_type->get_unit_type(); + + if(!resources::gameboard->map().on_board(loc)) { + return variant(); + } + + return variant(un.movement_type().defense_modifier((resources::gameboard->map())[loc])); + } + + return variant(); + } +}; + + +class movement_cost_function : public function_expression +{ +public: + movement_cost_function(const args_list& args) + : function_expression("movement_cost", args, 2, 2) + {} +private: + variant execute(const formula_callable& variables, formula_debugger *fdb) const + { + variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "movement_cost:unit")); + variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 0, "movement_cost:location")); + if(u.is_null() || loc_var.is_null()) { + return variant(); + } + //we can pass to this function either unit_callable or unit_type callable + auto u_call = u.try_convert(); + auto u_type = u.try_convert(); + const map_location& loc = loc_var.convert_to()->loc(); + + if(u_call) { + const unit& un = u_call->get_unit(); + + if(!resources::gameboard->map().on_board(loc)) { + return variant(); + } + + return variant(un.movement_cost((resources::gameboard->map())[loc])); + } + + if(u_type) { + const unit_type& un = u_type->get_unit_type(); + + if(!resources::gameboard->map().on_board(loc)) { + return variant(); + } + + return variant(un.movement_type().movement_cost((resources::gameboard->map())[loc])); + } + + return variant(); + } +}; + + +class enemy_of_function : public function_expression +{ +public: + enemy_of_function(const args_list& args) + : function_expression("enemy_of", args, 2, 2) + {} +private: + variant execute(const formula_callable& variables, formula_debugger *fdb) const + { + variant self_v = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "enemy_of:self")); + variant other_v = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "enemy_of:other")); + int self, other; + + if(auto uc = self_v.try_convert()) { + // For some obscure, bizarre reason, the unit callable returns a 0-indexed side. :| + self = uc->get_value("side").as_int() + 1; + } else if(auto tc = self_v.try_convert()) { + self = tc->get_value("side").as_int(); + } else { + self = self_v.as_int(); + } + + if(auto uc = other_v.try_convert()) { + // For some obscure, bizarre reason, the unit callable returns a 0-indexed side. :| + other = uc->get_value("side").as_int() + 1; + } else if(auto tc = other_v.try_convert()) { + other = tc->get_value("side").as_int(); + } else { + other = other_v.as_int(); + } + + int num_teams = resources::gameboard->teams().size(); + if(self < 1 || self > num_teams || other < 1 || other > num_teams) { + return variant(0); + } + return variant(resources::gameboard->get_team(self).is_enemy(other) ? 1 : 0); + } +}; + +} // namespace gamestate + +gamestate_function_symbol_table::gamestate_function_symbol_table(std::shared_ptr parent) : function_symbol_table(parent) { + using namespace gamestate; + function_symbol_table& functions_table = *this; + DECLARE_WFL_FUNCTION(get_unit_type); + DECLARE_WFL_FUNCTION(unit_at); + DECLARE_WFL_FUNCTION(defense_on); + DECLARE_WFL_FUNCTION(chance_to_hit); + DECLARE_WFL_FUNCTION(movement_cost); + DECLARE_WFL_FUNCTION(adjacent_locs); // This is deliberately duplicated here; this form excludes off-map locations, while the core form does not + DECLARE_WFL_FUNCTION(locations_in_radius); + DECLARE_WFL_FUNCTION(enemy_of); +} + +} diff --git a/src/formula/function_gamestate.hpp b/src/formula/function_gamestate.hpp new file mode 100644 index 000000000000..04a78a900a71 --- /dev/null +++ b/src/formula/function_gamestate.hpp @@ -0,0 +1,11 @@ + +#include "formula/function.hpp" + +namespace wfl { + +class gamestate_function_symbol_table : public function_symbol_table { +public: + gamestate_function_symbol_table(std::shared_ptr parent = nullptr); +}; + +} diff --git a/src/pathfind/teleport.cpp b/src/pathfind/teleport.cpp index 087f8d451c1a..004447f20ee1 100644 --- a/src/pathfind/teleport.cpp +++ b/src/pathfind/teleport.cpp @@ -109,8 +109,6 @@ void teleport_group::get_teleport_pair( , const unit& u , const bool ignore_units) const { - const map_location &loc = u.get_location(); - const filter_context * fc = resources::filter_con; assert(fc); @@ -122,15 +120,12 @@ void teleport_group::get_teleport_pair( vconfig source(cfg_.child_or_empty("source"), true); vconfig target(cfg_.child_or_empty("target"), true); const unit_filter ufilt(filter, resources::filter_con); //Note: Don't use the ignore units filter context here, only for the terrain filters. (That's how it worked before the filter contexts were introduced) - if (ufilt.matches(u, loc)) { - - scoped_xy_unit teleport_unit("teleport_unit", loc, resources::gameboard->units()); - + if (ufilt.matches(u)) { terrain_filter source_filter(source, fc); - source_filter.get_locations(reversed_ ? loc_pair.second : loc_pair.first); + source_filter.get_locations(reversed_ ? loc_pair.second : loc_pair.first, u); terrain_filter target_filter(target, fc); - target_filter.get_locations(reversed_ ? loc_pair.first : loc_pair.second); + target_filter.get_locations(reversed_ ? loc_pair.first : loc_pair.second, u); } if (ignore_units) { diff --git a/src/scripting/game_lua_kernel.cpp b/src/scripting/game_lua_kernel.cpp index b95279a74637..26709015a0c8 100644 --- a/src/scripting/game_lua_kernel.cpp +++ b/src/scripting/game_lua_kernel.cpp @@ -547,6 +547,8 @@ int game_lua_kernel::intf_get_displayed_unit(lua_State *L) /** * Gets all the units matching a given filter. * - Arg 1: optional table containing a filter + * - Arg 2: optional location (to find all units that would match on that location) + OR unit (to find all units that would match adjacent to that unit) * - Ret 1: table containing full userdata with __index pointing to * impl_unit_get and __newindex pointing to impl_unit_set. */ @@ -554,15 +556,34 @@ int game_lua_kernel::intf_get_units(lua_State *L) { vconfig filter = luaW_checkvconfig(L, 1, true); + // note that if filter is null, this yields a null filter matching everything (and doing no work) + filter_context & fc = game_state_; + unit_filter filt(filter, &fc); + std::vector units; + + if(unit* u_adj = luaW_tounit(L, 2)) { + if(!u_adj) { + return luaL_argerror(L, 2, "unit not found"); + } + units = filt.all_matches_with_unit(*u_adj); + } else if(!lua_isnoneornil(L, 2)) { + map_location loc; + luaW_tolocation(L, 2, loc); + if(!loc.valid()) { + return luaL_argerror(L, 2, "invalid location"); + } + units = filt.all_matches_at(loc); + } else { + units = filt.all_matches_on_map(); + } + // Go through all the units while keeping the following stack: // 1: return table, 2: userdata lua_settop(L, 0); lua_newtable(L); int i = 1; - // note that if filter is null, this yields a null filter matching everything (and doing no work) - filter_context & fc = game_state_; - for (const unit * ui : unit_filter(filter, &fc).all_matches_on_map()) { + for (const unit * ui : units) { luaW_pushunit(L, ui->underlying_id()); lua_rawseti(L, 1, i); ++i; @@ -2808,6 +2829,7 @@ static int intf_do_unsynced(lua_State *L) /** * Gets all the locations matching a given filter. * - Arg 1: WML table. + * - Arg 2: Optional reference unit (teleport_unit) * - Ret 1: array of integer pairs. */ int game_lua_kernel::intf_get_locations(lua_State *L) @@ -2817,7 +2839,11 @@ int game_lua_kernel::intf_get_locations(lua_State *L) std::set res; filter_context & fc = game_state_; const terrain_filter t_filter(filter, &fc); - t_filter.get_locations(res, true); + if(luaW_isunit(L, 2)) { + t_filter.get_locations(res, *luaW_tounit(L, 2), true); + } else { + t_filter.get_locations(res, true); + } lua_createtable(L, res.size(), 0); int i = 1; @@ -2867,6 +2893,7 @@ int game_lua_kernel::intf_get_villages(lua_State *L) * Matches a location against the given filter. * - Arg 1: location. * - Arg 2: WML table. + * - Arg 3: Optional reference unit (teleport_unit) * - Ret 1: boolean. */ int game_lua_kernel::intf_match_location(lua_State *L) @@ -2881,7 +2908,11 @@ int game_lua_kernel::intf_match_location(lua_State *L) filter_context & fc = game_state_; const terrain_filter t_filter(filter, &fc); - lua_pushboolean(L, t_filter.match(loc)); + if(luaW_isunit(L, 3)) { + lua_pushboolean(L, t_filter.match(loc, *luaW_tounit(L, 3))); + } else { + lua_pushboolean(L, t_filter.match(loc)); + } return 1; } diff --git a/src/side_filter.cpp b/src/side_filter.cpp index 81ae5cd0877f..5955b70ef747 100644 --- a/src/side_filter.cpp +++ b/src/side_filter.cpp @@ -32,6 +32,7 @@ #include "variable.hpp" #include "formula/callable_objects.hpp" #include "formula/formula.hpp" +#include "formula/function_gamestate.hpp" static lg::log_domain log_engine_sf("engine/side_filter"); #define ERR_NG LOG_STREAM(err, log_engine_sf) @@ -228,7 +229,7 @@ bool side_filter::match_internal(const team &t) const if (cfg_.has_attribute("formula")) { try { const wfl::team_callable callable(t); - const wfl::formula form(cfg_["formula"]); + const wfl::formula form(cfg_["formula"], new wfl::gamestate_function_symbol_table); if(!form.evaluate(callable).as_bool()) { return false; } diff --git a/src/terrain/filter.cpp b/src/terrain/filter.cpp index 253c60472397..46397a953562 100644 --- a/src/terrain/filter.cpp +++ b/src/terrain/filter.cpp @@ -30,6 +30,7 @@ #include "variable.hpp" #include "formula/callable_objects.hpp" #include "formula/formula.hpp" +#include "formula/function_gamestate.hpp" #include "scripting/game_lua_kernel.hpp" #include @@ -97,7 +98,7 @@ namespace { }; } //end anonymous namespace -bool terrain_filter::match_internal(const map_location& loc, const bool ignore_xy) const +bool terrain_filter::match_internal(const map_location& loc, const unit* ref_unit, const bool ignore_xy) const { if (!this->fc_->get_disp_context().map().on_board_with_border(loc)) { return false; @@ -326,11 +327,14 @@ bool terrain_filter::match_internal(const map_location& loc, const bool ignore_x if(cfg_.has_attribute("formula")) { try { - const gamemap& map = fc_->get_disp_context().map(); - t_translation::terrain_code t = map.get_terrain(loc); - const terrain_type& ter = map.tdata()->get_terrain_info(t); - const wfl::terrain_callable callable(ter,loc); - const wfl::formula form(cfg_["formula"]); + const wfl::terrain_callable main(fc_->get_disp_context(), loc); + wfl::map_formula_callable callable(main.fake_ptr()); + if(ref_unit) { + std::shared_ptr ref(new wfl::unit_callable(*ref_unit)); + callable.add("teleport_unit", wfl::variant(ref)); + // It's not destroyed upon scope exit because the variant holds a reference + } + const wfl::formula form(cfg_["formula"], new wfl::gamestate_function_symbol_table); if(!form.evaluate(callable).as_bool()) { return false; } @@ -345,7 +349,17 @@ bool terrain_filter::match_internal(const map_location& loc, const bool ignore_x return true; } -bool terrain_filter::match(const map_location& loc) const +class filter_with_unit : public xy_pred { + const terrain_filter& filt_; + const unit& ref_; +public: + filter_with_unit(const terrain_filter& filt, const unit& ref) : filt_(filt), ref_(ref) {} + bool operator()(const map_location& loc) const override { + return filt_.match(loc, ref_); + } +}; + +bool terrain_filter::match_impl(const map_location& loc, const unit* ref_unit) const { if(cfg_["x"] == "recall" && cfg_["y"] == "recall") { return !fc_->get_disp_context().map().on_board(loc); @@ -353,6 +367,15 @@ bool terrain_filter::match(const map_location& loc) const std::set hexes; std::vector loc_vec(1, loc); + std::unique_ptr ref_unit_var; + if(ref_unit) { + if(fc_->get_disp_context().map().on_board(ref_unit->get_location())) { + ref_unit_var.reset(new scoped_xy_unit("teleport_unit", ref_unit->get_location(), fc_->get_disp_context().units())); + } else { + // Possible TODO: Support recall list units? + } + } + //handle radius size_t radius = cfg_["radius"].to_size_t(0); if(radius > max_loop_) { @@ -364,7 +387,11 @@ bool terrain_filter::match(const map_location& loc) const hexes.insert(loc_vec.begin(), loc_vec.end()); else if ( cfg_.has_child("filter_radius") ) { terrain_filter r_filter(cfg_.child("filter_radius"), *this); - get_tiles_radius(fc_->get_disp_context().map(), loc_vec, radius, hexes, false, r_filter); + if(ref_unit) { + get_tiles_radius(fc_->get_disp_context().map(), loc_vec, radius, hexes, false, filter_with_unit(r_filter, *ref_unit)); + } else { + get_tiles_radius(fc_->get_disp_context().map(), loc_vec, radius, hexes, false, r_filter); + } } else { get_tiles_radius(fc_->get_disp_context().map(), loc_vec, radius, hexes); } @@ -372,7 +399,7 @@ bool terrain_filter::match(const map_location& loc) const size_t loop_count = 0; std::set::const_iterator i; for(i = hexes.begin(); i != hexes.end(); ++i) { - bool matches = match_internal(*i, false); + bool matches = match_internal(*i, ref_unit, false); //handle [and], [or], and [not] with in-order precedence vconfig::all_children_iterator cond = cfg_.ordered_begin(); @@ -385,17 +412,17 @@ bool terrain_filter::match(const map_location& loc) const //handle [and] if(cond_name == "and") { - matches = matches && terrain_filter(cond_cfg, *this)(*i); + matches = matches && terrain_filter(cond_cfg, *this).match_impl(*i, ref_unit); } //handle [or] else if(cond_name == "or") { - matches = matches || terrain_filter(cond_cfg, *this)(*i); + matches = matches || terrain_filter(cond_cfg, *this).match_impl(*i, ref_unit); } //handle [not] else if(cond_name == "not") { - matches = matches && !terrain_filter(cond_cfg, *this)(*i); + matches = matches && !terrain_filter(cond_cfg, *this).match_impl(*i, ref_unit); } ++cond; } @@ -476,8 +503,17 @@ struct cfg_to_loc map_location operator()(const config& cfg) const { return map_location(cfg, nullptr); } typedef map_location result_type; }; -void terrain_filter::get_locations(std::set& locs, bool with_border) const +void terrain_filter::get_locs_impl(std::set& locs, const unit* ref_unit, bool with_border) const { + std::unique_ptr ref_unit_var; + if(ref_unit) { + if(fc_->get_disp_context().map().on_board(ref_unit->get_location())) { + ref_unit_var.reset(new scoped_xy_unit("teleport_unit", ref_unit->get_location(), fc_->get_disp_context().units())); + } else { + // Possible TODO: Support recall list units? + } + } + std::set match_set; // See if the caller provided an override to with_border @@ -543,10 +579,10 @@ void terrain_filter::get_locations(std::set& locs, bool with_borde } std::set::iterator loc_itor = match_set.begin(); while(loc_itor != match_set.end()) { - if(match_internal(*loc_itor, true)) { + if(match_internal(*loc_itor, ref_unit, true)) { ++loc_itor; } else { - match_set.erase(loc_itor++); + loc_itor = match_set.erase(loc_itor); } } diff --git a/src/terrain/filter.hpp b/src/terrain/filter.hpp index 11c6ff8251dc..19faa17b1c93 100644 --- a/src/terrain/filter.hpp +++ b/src/terrain/filter.hpp @@ -39,13 +39,37 @@ class terrain_filter : public xy_pred { terrain_filter(const terrain_filter &other); terrain_filter& operator=(const terrain_filter &other); - //match: returns true if and only if the given location matches this filter - bool match(const map_location& loc) const; + /// @param loc The location to test + /// @returns true if and only if the given location matches this filter + bool match(const map_location& loc) const { + return match_impl(loc, nullptr); + } + + /// @param loc The location to test + /// @param ref_unit A reference unit for the $teleport_unit auto-stored variable + /// @param unit_loc The reference unit's apparent location for this filter + /// @returns true if and only if the given location matches this filter + bool match(const map_location& loc, const unit& ref_unit) const { + return match_impl(loc, &ref_unit); + } + virtual bool operator()(const map_location& loc) const { return this->match(loc); } - //get_locations: gets all locations on the map that match this filter - // @param locs - out parameter containing the results - void get_locations(std::set& locs, bool with_border=false) const; + /// gets all locations on the map that match this filter + /// @param[out] locs set to store the results in + /// @param[in] with_border whether to include the borders + void get_locations(std::set& locs, bool with_border=false) const { + return get_locs_impl(locs, nullptr, with_border); + } + + /// gets all locations on the map that match this filter + /// @param[out] locs set to store the results in + /// @param[in] with_border whether to include the borders + /// @param[in] ref_unit A reference unit for the $teleport_unit auto-stored variable + /// @param[in] unit_loc The reference unit's apparent location for this filter + void get_locations(std::set& locs, const unit& ref_unit, bool with_border=false) const { + return get_locs_impl(locs, &ref_unit, with_border); + } //restrict: limits the allowed radius size and also limits nesting // The purpose to limit the time spent for WML handling @@ -58,7 +82,9 @@ class terrain_filter : public xy_pred { config to_config() const; friend class terrain_filterimpl; private: - bool match_internal(const map_location& loc, const bool ignore_xy) const; + bool match_impl(const map_location& loc, const unit* ref_unit) const; + void get_locs_impl(std::set& locs, const unit* ref_unit, bool with_border) const; + bool match_internal(const map_location& loc, const unit* ref_unit, const bool ignore_xy) const; const vconfig cfg_; //config contains WML for a Standard Location Filter const filter_context * fc_; diff --git a/src/units/abilities.cpp b/src/units/abilities.cpp index 78ef049ed1f7..7c7c3a247c20 100644 --- a/src/units/abilities.cpp +++ b/src/units/abilities.cpp @@ -19,6 +19,7 @@ #include "display_context.hpp" #include "game_board.hpp" +#include "lexical_cast.hpp" #include "log.hpp" #include "resources.hpp" #include "team.hpp" @@ -28,7 +29,12 @@ #include "units/filter.hpp" #include "units/map.hpp" #include "filter_context.hpp" +#include "formula/callable_objects.hpp" +#include "formula/formula.hpp" +#include "serialization/string_view.hpp" + #include +#include static lg::log_domain log_engine("engine"); #define ERR_NG LOG_STREAM(err, log_engine) @@ -169,7 +175,7 @@ bool unit::get_ability_bool(const std::string& tag_name, const map_location& loc } unit_ability_list unit::get_abilities(const std::string& tag_name, const map_location& loc) const { - unit_ability_list res; + unit_ability_list res(loc_); for (const config &i : this->abilities_.child_range(tag_name)) { if (ability_active(tag_name, i, loc) && @@ -402,39 +408,67 @@ bool unit::has_ability_type(const std::string& ability) const return !abilities_.child_range(ability).empty(); } - -std::pair unit_ability_list::highest(const std::string& key, int def) const +namespace { + + +template +class get_ability_value_visitor : public boost::static_visitor { - if ( cfgs_.empty() ) { - return std::make_pair(def, map_location()); - } - // The returned location is the best non-cumulative one, if any, - // the best absolute cumulative one otherwise. - map_location best_loc; - bool only_cumulative = true; - int abs_max = 0; - int flat = 0; - int stack = 0; - for (unit_ability const &p : cfgs_) +public: + // Constructor stores the default value. + get_ability_value_visitor(T def, const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {} + + T operator()(boost::blank const &) const { return def_; } + T operator()(bool) const { return def_; } + T operator()(int i) const { return static_cast(i); } + T operator()(unsigned long long u) const { return static_cast(u); } + T operator()(double d) const { return static_cast(d); } + T operator()(t_string const &) const { return def_; } + T operator()(const std::string& s) const { - int value = (*p.first)[key].to_int(def); - if ((*p.first)["cumulative"].to_bool()) { - stack += value; - if (value < 0) value = -value; - if (only_cumulative && value >= abs_max) { - abs_max = value; - best_loc = p.second; - } - } else if (only_cumulative || value > flat) { - only_cumulative = false; - flat = value; - best_loc = p.second; + if(s.size() >= 2 && s[0] == '(') { + return formula_handler_(s); } + return lexical_cast_default(s, def_); } - return std::make_pair(flat + stack, best_loc); + +private: + const T def_; + const TFuncFormula& formula_handler_; +}; +template +get_ability_value_visitor make_get_ability_value_visitor(T def, const TFuncFormula& formula_handler) +{ + 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) +{ + return v.apply_visitor(make_get_ability_value_visitor(def, [&](const std::string& s) { + + try { + assert(resources::units); + auto u_itor = resources::units->find(sender_loc); + + if(u_itor == resources::units->end()) { + return def; + } + wfl::map_formula_callable callable(std::make_shared(*u_itor)); + u_itor = resources::units->find(receiver_loc); + if(u_itor != resources::units->end()) { + callable.add("other", wfl::variant(std::make_shared(*u_itor))); + } + return formula_handler(wfl::formula(s), callable); + } catch(wfl::formula_error& e) { + lg::wml_error() << "Formula error in ability or weapon special: " << e.type << " at " << e.filename << ':' << e.line << ")\n"; + return def; + } + })); +} } -std::pair unit_ability_list::lowest(const std::string& key, int def) const +template +std::pair unit_ability_list::get_extremum(const std::string& key, int def, const TComp& comp) const { if ( cfgs_.empty() ) { return std::make_pair(def, map_location()); @@ -448,15 +482,18 @@ std::pair unit_ability_list::lowest(const std::string& key, in int stack = 0; for (unit_ability const &p : cfgs_) { - int value = (*p.first)[key].to_int(def); + int value = get_single_ability_value((*p.first)[key], def, p.second, loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) { + return formula.evaluate(callable).as_int(); + }); + if ((*p.first)["cumulative"].to_bool()) { stack += value; if (value < 0) value = -value; - if (only_cumulative && value <= abs_max) { + if (only_cumulative && !comp(value, abs_max)) { abs_max = value; best_loc = p.second; } - } else if (only_cumulative || value < flat) { + } else if (only_cumulative || comp(flat, value)) { only_cumulative = false; flat = value; best_loc = p.second; @@ -465,6 +502,9 @@ std::pair unit_ability_list::lowest(const std::string& key, in return std::make_pair(flat + stack, best_loc); } +template std::pair unit_ability_list::get_extremum>(const std::string& key, int def, const std::less& comp) const; +template std::pair unit_ability_list::get_extremum>(const std::string& key, int def, const std::greater& comp) const; + /* * * [special] @@ -567,7 +607,7 @@ bool attack_type::get_special_bool(const std::string& special, bool simple_check unit_ability_list attack_type::get_specials(const std::string& special) const { //log_scope("get_specials"); - unit_ability_list res; + unit_ability_list res(self_loc_); for (const config &i : specials_.child_range(special)) { if ( special_active(i, AFFECT_SELF) ) res.push_back(unit_ability(&i, self_loc_)); @@ -1016,7 +1056,11 @@ effect::effect(const unit_ability_list& list, int def, bool backstab) : continue; if (const config::attribute_value *v = cfg.get("value")) { - int value = *v; + int value = get_single_ability_value(*v, def, ability.second, list.loc(),[&](const wfl::formula& formula, wfl::map_formula_callable& callable) { + callable.add("base_value", wfl::variant(def)); + return formula.evaluate(callable).as_int(); + }); + bool cumulative = cfg["cumulative"].to_bool(); if (!value_is_set && !cumulative) { value_set = value; @@ -1032,32 +1076,45 @@ effect::effect(const unit_ability_list& list, int def, bool backstab) : } if (const config::attribute_value *v = cfg.get("add")) { - int add = *v; + int add = get_single_ability_value(*v, def, ability.second, 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); } } if (const config::attribute_value *v = cfg.get("sub")) { - int sub = - *v; + int sub = - get_single_ability_value(*v, def, ability.second, 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); } } if (const config::attribute_value *v = cfg.get("multiply")) { - int multiply = int(v->to_double() * 100); + 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) { + 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); } } if (const config::attribute_value *v = cfg.get("divide")) { - if (*v == 0) { + 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) { + callable.add("base_value", wfl::variant(def)); + return formula.evaluate(callable).as_decimal() / 1000.0 ; + }) * 100); + + if (divide == 0) { ERR_NG << "division by zero with divide= in ability/weapon special " << effect_id << std::endl; } else { - int divide = int(v->to_double() * 100); 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); diff --git a/src/units/attack_type.cpp b/src/units/attack_type.cpp index 74b61762cbaa..1e6e47a386a4 100644 --- a/src/units/attack_type.cpp +++ b/src/units/attack_type.cpp @@ -21,6 +21,7 @@ #include "formula/callable_objects.hpp" #include "formula/formula.hpp" #include "formula/string_utils.hpp" +#include "formula/function_gamestate.hpp" #include "lexical_cast.hpp" #include "log.hpp" @@ -138,7 +139,7 @@ static bool matches_simple_filter(const attack_type & attack, const config & fil if (!filter_formula.empty()) { try { const wfl::attack_type_callable callable(attack); - const wfl::formula form(filter_formula); + const wfl::formula form(filter_formula, new wfl::gamestate_function_symbol_table); if(!form.evaluate(callable).as_bool()) { return false; } diff --git a/src/units/filter.cpp b/src/units/filter.cpp index ee7cf096b728..51dd651f0aff 100644 --- a/src/units/filter.cpp +++ b/src/units/filter.cpp @@ -36,6 +36,7 @@ #include "wml_exception.hpp" // needed for FAIL #include "formula/callable_objects.hpp" #include "formula/formula.hpp" +#include "formula/function_gamestate.hpp" #include @@ -79,7 +80,7 @@ class null_unit_filter_impl : public unit_filter_abstract_impl { virtual bool matches(const unit & /*u*/, const map_location & /*loc*/, const unit *) const { return true; } - virtual std::vector all_matches_on_map(unsigned max_matches) const { + virtual std::vector all_matches_on_map(unsigned max_matches, const map_location* = nullptr, const unit* = nullptr) const { std::vector ret; for(const unit & u : fc_.get_disp_context().units()) { --max_matches; @@ -167,7 +168,7 @@ class basic_unit_filter_impl : public unit_filter_abstract_impl { } virtual bool matches(const unit & u, const map_location & loc, const unit * u2) const; - virtual std::vector all_matches_on_map(unsigned max_matches) const; + virtual std::vector all_matches_on_map(unsigned max_matches, const map_location* loc = nullptr, const unit* u2 = nullptr) const; virtual unit_const_ptr first_match_on_map() const; config to_config() const { return vcfg.get_config(); @@ -242,13 +243,25 @@ bool basic_unit_filter_impl::matches(const unit & u, const map_location& loc, co for (size_t i = 0; i < cond_children_.size(); i++) { switch (cond_child_types_[i].v) { case conditional::TYPE::AND: - matches = matches && cond_children_[i].matches(u,loc); + if(u2) { + matches = matches && cond_children_[i].matches(u,loc,*u2); + } else { + matches = matches && cond_children_[i].matches(u,loc); + } break; case conditional::TYPE::OR: - matches = matches || cond_children_[i].matches(u,loc); + if(u2) { + matches = matches || cond_children_[i].matches(u,loc,*u2); + } else { + matches = matches || cond_children_[i].matches(u,loc); + } break; case conditional::TYPE::NOT: - matches = matches && !cond_children_[i].matches(u,loc); + if(u2) { + matches = matches && !cond_children_[i].matches(u,loc,*u2); + } else { + matches = matches && !cond_children_[i].matches(u,loc); + } } } return matches; @@ -687,7 +700,7 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l callable.add("other", wfl::variant(secondary)); // It's not destroyed upon scope exit because the variant holds a reference } - const wfl::formula form(vcfg["formula"]); + const wfl::formula form(vcfg["formula"], new wfl::gamestate_function_symbol_table); if(!form.evaluate(callable).as_bool()) { return false; } @@ -709,10 +722,10 @@ bool basic_unit_filter_impl::internal_matches_filter(const unit & u, const map_l return true; } -std::vector basic_unit_filter_impl::all_matches_on_map(unsigned max_matches) const { +std::vector basic_unit_filter_impl::all_matches_on_map(unsigned max_matches, const map_location* loc, const unit* u2) const { std::vector ret; for (const unit & u : fc_.get_disp_context().units()) { - if (matches(u, u.get_location(), nullptr)) { + if (matches(u, loc ? *loc : u.get_location(), u2)) { if(max_matches == 0) { return ret; } diff --git a/src/units/filter.hpp b/src/units/filter.hpp index 06c3246af363..63644090e222 100644 --- a/src/units/filter.hpp +++ b/src/units/filter.hpp @@ -39,7 +39,7 @@ struct map_location; class unit_filter_abstract_impl { public: virtual bool matches(const unit & u, const map_location & loc, const unit * u2 = nullptr) const = 0; - virtual std::vector all_matches_on_map(unsigned max_matches) const = 0; + virtual std::vector all_matches_on_map(unsigned max_matches, const map_location* loc = nullptr, const unit* u2 = nullptr) const = 0; virtual unit_const_ptr first_match_on_map() const = 0; virtual config to_config() const = 0; virtual bool empty() const {return false;} @@ -95,6 +95,18 @@ class unit_filter { return impl_->all_matches_on_map(max_matches_); } + std::vector all_matches_at(const map_location& loc) const { + return impl_->all_matches_on_map(max_matches_, &loc); + } + + std::vector all_matches_with_unit(const unit& u) const { + return impl_->all_matches_on_map(max_matches_, nullptr, &u); + } + + std::vector all_matches_with_unit_at(const unit& u, const map_location& loc) const { + return impl_->all_matches_on_map(max_matches_, &loc, &u); + } + unit_const_ptr first_match_on_map() const { return impl_->first_match_on_map(); } diff --git a/src/units/unit.hpp b/src/units/unit.hpp index 59f7e3a5f8b7..7b7b698742cb 100644 --- a/src/units/unit.hpp +++ b/src/units/unit.hpp @@ -54,11 +54,20 @@ using unit_ability = std::pair; class unit_ability_list { public: - unit_ability_list() : cfgs_() {} + unit_ability_list(const map_location& loc = map_location()) : cfgs_() , loc_(loc) {} // Implemented in unit_abilities.cpp - std::pair highest(const std::string& key, int def=0) const; - std::pair lowest(const std::string& key, int def=0) const; + std::pair highest(const std::string& key, int def=0) const + { + return get_extremum(key, def, std::less()); + } + std::pair lowest(const std::string& key, int def=0) const + { + return get_extremum(key, def, std::greater()); + } + + template + std::pair get_extremum(const std::string& key, int def, const TComp& comp) const; // The following make this class usable with standard library algorithms and such typedef std::vector::iterator iterator; @@ -79,9 +88,11 @@ class unit_ability_list iterator erase(const iterator& erase_it) { return cfgs_.erase(erase_it); } void push_back(const unit_ability& ability) { cfgs_.push_back(ability); } + const map_location& loc() const { return loc_; } private: // Data std::vector cfgs_; + map_location loc_; }; /**