diff --git a/changelog b/changelog index 6435b822f592..9768131238b5 100644 --- a/changelog +++ b/changelog @@ -36,6 +36,10 @@ Version 1.13.8+dev: * 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 * 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. 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/function_table.cpp b/src/ai/formula/function_table.cpp index 5f26041e8aa3..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(std::make_shared()) { - 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/function.cpp b/src/formula/function.cpp index ecc9744b7a62..63edfb84255f 100644 --- a/src/formula/function.cpp +++ b/src/formula/function.cpp @@ -1606,85 +1606,82 @@ 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))) - 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); + 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(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 ecdb3212d352..e70daecb7f0a 100644 --- a/src/formula/function.hpp +++ b/src/formula/function.hpp @@ -154,9 +154,14 @@ class function_symbol_table { 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..833c7e81c5e8 --- /dev/null +++ b/src/formula/function_gamestate.cpp @@ -0,0 +1,275 @@ + +#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(); + } +}; + +} // 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); + DECLARE_WFL_FUNCTION(locations_in_radius); +} + +} 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/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 be06c93c8e06..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 @@ -333,7 +334,7 @@ bool terrain_filter::match_internal(const map_location& loc, const unit* ref_uni 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"]); + const wfl::formula form(cfg_["formula"], new wfl::gamestate_function_symbol_table); if(!form.evaluate(callable).as_bool()) { return false; } 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 2dfff89bd8f4..93470fb5484c 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 @@ -699,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; }