Allow filter formulas to use a number of previously FormulaAI-exclusive functions

This commit is contained in:
Celtic Minstrel 2017-06-20 00:24:47 -04:00
parent 3f3a176f57
commit 4bd1fa7157
13 changed files with 422 additions and 356 deletions

View file

@ -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.

View file

@ -1088,6 +1088,12 @@
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Debug|Win32'">$(IntDir)Formula\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Release|Win32'">$(IntDir)Formula\</ObjectFileName>
</ClCompile>
<ClCompile Include="..\..\src\formula\function_gamestate.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)Formula\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='ReleaseDEBUG|Win32'">$(IntDir)Formula\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Debug|Win32'">$(IntDir)Formula\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Test_Release|Win32'">$(IntDir)Formula\</ObjectFileName>
</ClCompile>
<ClCompile Include="..\..\src\formula\string_utils.cpp">
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(IntDir)Formula\</ObjectFileName>
<ObjectFileName Condition="'$(Configuration)|$(Platform)'=='ReleaseDEBUG|Win32'">$(IntDir)Formula\</ObjectFileName>
@ -3596,6 +3602,7 @@
<ClInclude Include="..\..\src\formula\debugger_fwd.hpp" />
<ClInclude Include="..\..\src\formula\formula.hpp" />
<ClInclude Include="..\..\src\formula\formula_fwd.hpp" />
<ClInclude Include="..\..\src\formula\function_gamestate.hpp" />
<ClInclude Include="..\..\src\formula\function.hpp" />
<ClInclude Include="..\..\src\formula\string_utils.hpp" />
<ClInclude Include="..\..\src\formula\tokenizer.hpp" />

View file

@ -1538,6 +1538,9 @@
<ClCompile Include="..\..\src\preferences\lobby.cpp">
<Filter>Preferences</Filter>
</ClCompile>
<ClCompile Include="..\..\src\formula\function_gamestate.cpp">
<Filter>Formula</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\..\src\addon\client.hpp">
@ -2982,6 +2985,9 @@
<ClInclude Include="..\..\src\preferences\lobby.hpp">
<Filter>Preferences</Filter>
</ClInclude>
<ClInclude Include="..\..\src\formula\function_gamestate.hpp">
<Filter>Formula</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<CustomBuild Include="..\..\src\tests\test_sdl_utils.hpp">

View file

@ -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

View file

@ -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 @@ private:
};
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<location_callable>()->loc();
map_location adj[6];
get_adjacent_tiles(loc, adj);
std::vector<variant> v;
for(int n = 0; n != 6; ++n) {
if (resources::gameboard->map().on_board(adj[n]) )
v.emplace_back(std::make_shared<location_callable>(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<location_callable>()->loc();
int range = args()[1]->evaluate(variables,fdb).as_int();
if( range < 0 )
return variant();
if(!range)
return variant(std::make_shared<location_callable>(loc));
std::vector<map_location> res;
get_tiles_in_radius( loc, range, res);
std::vector<variant> v;
v.reserve(res.size()+1);
v.emplace_back(std::make_shared<location_callable>(loc));
for(size_t n = 0; n != res.size(); ++n) {
if (resources::gameboard->map().on_board(res[n]) )
v.emplace_back(std::make_shared<location_callable>(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 @@ private:
// 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<unit_type_callable>(*ut));
}
return variant();
}
};
class rate_action_function : public function_expression {
@ -1273,28 +1195,6 @@ private:
};
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<location_callable>();
const unit_map::const_iterator i = resources::gameboard->units().find(loc->loc());
if(i != resources::gameboard->units().end()) {
return variant(std::make_shared<unit_callable>(*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 @@ private:
}
};
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<unit_callable>();
auto u_type = u.try_convert<unit_type_callable>();
const map_location& loc = loc_var.convert_to<location_callable>()->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<unit_callable>();
auto u_type = u.try_convert<unit_type_callable>();
const map_location& loc = loc_var.convert_to<location_callable>()->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<unit_callable>();
auto u_type = u.try_convert<unit_type_callable>();
const map_location& loc = loc_var.convert_to<location_callable>()->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 @@ public:
}
// 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##_function>(#name, ai)))
#define FUNCTION(name) add_function(#name, formula_function_ptr( \
new builtin_formula_function<name##_function>(#name)))
ai_function_symbol_table::ai_function_symbol_table(ai::formula_ai& ai) : function_symbol_table(std::make_shared<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<gamestate_function_symbol_table>(std::make_shared<action_function_symbol_table>()))
{
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
}

View file

@ -1606,85 +1606,82 @@ std::set<std::string> 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>(#name)))
std::shared_ptr<function_symbol_table> 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<function_symbol_table>(&functions_table, [](function_symbol_table*){});
}
action_function_symbol_table::action_function_symbol_table() {
action_function_symbol_table::action_function_symbol_table(std::shared_ptr<function_symbol_table> 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);
}
}

View file

@ -154,9 +154,14 @@ public:
class action_function_symbol_table : public function_symbol_table {
public:
action_function_symbol_table();
action_function_symbol_table(std::shared_ptr<function_symbol_table> 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##_function>(#name)))
class wrapper_formula : public formula_expression {
public:
wrapper_formula()

View file

@ -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<location_callable>()->loc();
map_location adj[6];
get_adjacent_tiles(loc, adj);
std::vector<variant> v;
for(int n = 0; n != 6; ++n) {
if(resources::gameboard->map().on_board(adj[n])) {
v.emplace_back(std::make_shared<location_callable>(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<location_callable>()->loc();
int range = args()[1]->evaluate(variables, fdb).as_int();
if(range < 0) {
return variant();
}
if(!range) {
return variant(std::make_shared<location_callable>(loc));
}
std::vector<map_location> res;
get_tiles_in_radius(loc, range, res);
std::vector<variant> v;
v.reserve(res.size() + 1);
v.emplace_back(std::make_shared<location_callable>(loc));
for(size_t n = 0; n != res.size(); ++n) {
if(resources::gameboard->map().on_board(res[n])) {
v.emplace_back(std::make_shared<location_callable>(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<unit_type_callable>(*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<location_callable>();
const unit_map::const_iterator i = resources::gameboard->units().find(loc->loc());
if(i != resources::gameboard->units().end()) {
return variant(std::make_shared<unit_callable>(*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<unit_callable>();
auto u_type = u.try_convert<unit_type_callable>();
const map_location& loc = loc_var.convert_to<location_callable>()->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<unit_callable>();
auto u_type = u.try_convert<unit_type_callable>();
const map_location& loc = loc_var.convert_to<location_callable>()->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<unit_callable>();
auto u_type = u.try_convert<unit_type_callable>();
const map_location& loc = loc_var.convert_to<location_callable>()->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<function_symbol_table> 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);
}
}

View file

@ -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<function_symbol_table> parent = nullptr);
};
}

View file

@ -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;
}

View file

@ -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 <boost/range/adaptor/transformed.hpp>
@ -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;
}

View file

@ -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;
}

View file

@ -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 <boost/optional.hpp>
@ -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;
}