Merge pull request #1806 from wesnoth/wfl_filters
Use WFL formulas instead of Lua for leadership, backstab, and teleport abilities
This commit is contained in:
commit
9015d9b57d
26 changed files with 876 additions and 641 deletions
17
changelog
17
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:
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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" />
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<gamemap_callable>(resources::gameboard->map()));
|
||||
return variant(std::make_shared<gamemap_callable>(*resources::gameboard));
|
||||
} else if(key == "villages")
|
||||
{
|
||||
return villages_from_set(resources::gameboard->map().villages());
|
||||
|
|
|
@ -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(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<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
|
||||
|
||||
}
|
||||
|
|
|
@ -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<variant> 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<terrain_callable>(gamemap_.get_terrain_info(loc), loc));
|
||||
vars.emplace_back(std::make_shared<terrain_callable>(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();
|
||||
}
|
||||
|
|
|
@ -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 @@ public:
|
|||
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
|
||||
|
|
|
@ -1463,6 +1463,80 @@ 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) {
|
||||
v.emplace_back(std::make_shared<location_callable>(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<location_callable>()->loc();
|
||||
const map_location loc2 = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "are_adjacent:location_B")).convert_to<location_callable>()->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<location_callable>()->loc();
|
||||
const map_location loc2 = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "relative_dir:location_B")).convert_to<location_callable>()->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<location_callable>()->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<location_callable>(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<location_callable>()->loc();
|
||||
const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 1, "direction_from:location")).convert_to<location_callable>()->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<location_callable>(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<function_symbol_table> 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<std::string> function_symbol_table::get_function_names() const
|
|||
std::set<std::string> 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<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)))
|
||||
|
||||
function_symbol_table* function_symbol_table::get_builtins() {
|
||||
static function_symbol_table functions_table;
|
||||
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(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 &functions_table;
|
||||
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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -139,23 +139,29 @@ typedef std::shared_ptr<formula_function> formula_function_ptr;
|
|||
typedef std::map<std::string, formula_function_ptr> functions_map;
|
||||
|
||||
class function_symbol_table {
|
||||
function_symbol_table* parent = nullptr;
|
||||
std::shared_ptr<function_symbol_table> 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<function_symbol_table> parent = nullptr);
|
||||
void add_function(const std::string& name, formula_function_ptr fcn);
|
||||
expression_ptr create_function(const std::string& fn, const std::vector<expression_ptr>& args) const;
|
||||
std::set<std::string> 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<function_symbol_table> get_builtins();
|
||||
};
|
||||
|
||||
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()
|
||||
|
|
316
src/formula/function_gamestate.cpp
Normal file
316
src/formula/function_gamestate.cpp
Normal file
|
@ -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<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();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
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<unit_callable>()) {
|
||||
// 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<team_callable>()) {
|
||||
self = tc->get_value("side").as_int();
|
||||
} else {
|
||||
self = self_v.as_int();
|
||||
}
|
||||
|
||||
if(auto uc = other_v.try_convert<unit_callable>()) {
|
||||
// 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<team_callable>()) {
|
||||
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<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); // 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);
|
||||
}
|
||||
|
||||
}
|
11
src/formula/function_gamestate.hpp
Normal file
11
src/formula/function_gamestate.hpp
Normal 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);
|
||||
};
|
||||
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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<const unit*> 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<map_location> 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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
@ -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<wfl::unit_callable> 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<map_location> hexes;
|
||||
std::vector<map_location> loc_vec(1, loc);
|
||||
|
||||
std::unique_ptr<scoped_wml_variable> 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<map_location>::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<map_location>& locs, bool with_border) const
|
||||
void terrain_filter::get_locs_impl(std::set<map_location>& locs, const unit* ref_unit, bool with_border) const
|
||||
{
|
||||
std::unique_ptr<scoped_wml_variable> 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<map_location> match_set;
|
||||
|
||||
// See if the caller provided an override to with_border
|
||||
|
@ -543,10 +579,10 @@ void terrain_filter::get_locations(std::set<map_location>& locs, bool with_borde
|
|||
}
|
||||
std::set<map_location>::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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -39,13 +39,37 @@ public:
|
|||
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<map_location>& 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<map_location>& 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<map_location>& 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 @@ public:
|
|||
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<map_location>& 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_;
|
||||
|
|
|
@ -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 <boost/dynamic_bitset.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
|
||||
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,8 +408,67 @@ bool unit::has_ability_type(const std::string& ability) const
|
|||
return !abilities_.child_range(ability).empty();
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
|
||||
template<typename T, typename TFuncFormula>
|
||||
class get_ability_value_visitor : public boost::static_visitor<T>
|
||||
{
|
||||
public:
|
||||
// Constructor stores the default value.
|
||||
get_ability_value_visitor(T def, const TFuncFormula& formula_handler) : def_(def), formula_handler_(formula_handler) {}
|
||||
|
||||
std::pair<int,map_location> unit_ability_list::highest(const std::string& key, int def) const
|
||||
T operator()(boost::blank const &) const { return def_; }
|
||||
T operator()(bool) const { return def_; }
|
||||
T operator()(int i) const { return static_cast<T>(i); }
|
||||
T operator()(unsigned long long u) const { return static_cast<T>(u); }
|
||||
T operator()(double d) const { return static_cast<T>(d); }
|
||||
T operator()(t_string const &) const { return def_; }
|
||||
T operator()(const std::string& s) const
|
||||
{
|
||||
if(s.size() >= 2 && s[0] == '(') {
|
||||
return formula_handler_(s);
|
||||
}
|
||||
return lexical_cast_default<T>(s, def_);
|
||||
}
|
||||
|
||||
private:
|
||||
const T def_;
|
||||
const TFuncFormula& formula_handler_;
|
||||
};
|
||||
template<typename T, typename TFuncFormula>
|
||||
get_ability_value_visitor<T, TFuncFormula> make_get_ability_value_visitor(T def, const TFuncFormula& formula_handler)
|
||||
{
|
||||
return get_ability_value_visitor<T, TFuncFormula>(def, formula_handler);
|
||||
}
|
||||
template<typename T, typename TFuncFormula>
|
||||
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<wfl::unit_callable>(*u_itor));
|
||||
u_itor = resources::units->find(receiver_loc);
|
||||
if(u_itor != resources::units->end()) {
|
||||
callable.add("other", wfl::variant(std::make_shared<wfl::unit_callable>(*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;
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename TComp>
|
||||
std::pair<int,map_location> 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());
|
||||
|
@ -417,15 +482,18 @@ std::pair<int,map_location> unit_ability_list::highest(const std::string& key, i
|
|||
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;
|
||||
|
@ -434,36 +502,8 @@ std::pair<int,map_location> unit_ability_list::highest(const std::string& key, i
|
|||
return std::make_pair(flat + stack, best_loc);
|
||||
}
|
||||
|
||||
std::pair<int,map_location> unit_ability_list::lowest(const std::string& key, int def) const
|
||||
{
|
||||
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_)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
return std::make_pair(flat + stack, best_loc);
|
||||
}
|
||||
template std::pair<int, map_location> unit_ability_list::get_extremum<std::less<int>>(const std::string& key, int def, const std::less<int>& comp) const;
|
||||
template std::pair<int, map_location> unit_ability_list::get_extremum<std::greater<int>>(const std::string& key, int def, const std::greater<int>& comp) const;
|
||||
|
||||
/*
|
||||
*
|
||||
|
@ -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<std::string,individual_effect>::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<std::string,individual_effect>::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<int>(get_single_ability_value(*v, static_cast<double>(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<std::string,individual_effect>::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<int>(get_single_ability_value(*v, static_cast<double>(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<std::string,individual_effect>::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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
@ -79,7 +80,7 @@ public:
|
|||
virtual bool matches(const unit & /*u*/, const map_location & /*loc*/, const unit *) const {
|
||||
return true;
|
||||
}
|
||||
virtual std::vector<const unit *> all_matches_on_map(unsigned max_matches) const {
|
||||
virtual std::vector<const unit *> all_matches_on_map(unsigned max_matches, const map_location* = nullptr, const unit* = nullptr) const {
|
||||
std::vector<const unit *> ret;
|
||||
for(const unit & u : fc_.get_disp_context().units()) {
|
||||
--max_matches;
|
||||
|
@ -167,7 +168,7 @@ public:
|
|||
}
|
||||
|
||||
virtual bool matches(const unit & u, const map_location & loc, const unit * u2) const;
|
||||
virtual std::vector<const unit *> all_matches_on_map(unsigned max_matches) const;
|
||||
virtual std::vector<const unit *> 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<const unit *> basic_unit_filter_impl::all_matches_on_map(unsigned max_matches) const {
|
||||
std::vector<const unit *> basic_unit_filter_impl::all_matches_on_map(unsigned max_matches, const map_location* loc, const unit* u2) const {
|
||||
std::vector<const unit *> 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;
|
||||
}
|
||||
|
|
|
@ -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<const unit*> all_matches_on_map(unsigned max_matches) const = 0;
|
||||
virtual std::vector<const unit*> 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 @@ public:
|
|||
return impl_->all_matches_on_map(max_matches_);
|
||||
}
|
||||
|
||||
std::vector<const unit*> all_matches_at(const map_location& loc) const {
|
||||
return impl_->all_matches_on_map(max_matches_, &loc);
|
||||
}
|
||||
|
||||
std::vector<const unit*> all_matches_with_unit(const unit& u) const {
|
||||
return impl_->all_matches_on_map(max_matches_, nullptr, &u);
|
||||
}
|
||||
|
||||
std::vector<const unit*> 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();
|
||||
}
|
||||
|
|
|
@ -54,11 +54,20 @@ using unit_ability = std::pair<const config*, map_location>;
|
|||
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<int, map_location> highest(const std::string& key, int def=0) const;
|
||||
std::pair<int, map_location> lowest(const std::string& key, int def=0) const;
|
||||
std::pair<int, map_location> highest(const std::string& key, int def=0) const
|
||||
{
|
||||
return get_extremum(key, def, std::less<int>());
|
||||
}
|
||||
std::pair<int, map_location> lowest(const std::string& key, int def=0) const
|
||||
{
|
||||
return get_extremum(key, def, std::greater<int>());
|
||||
}
|
||||
|
||||
template<typename TComp>
|
||||
std::pair<int, map_location> 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<unit_ability>::iterator iterator;
|
||||
|
@ -79,9 +88,11 @@ public:
|
|||
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<unit_ability> cfgs_;
|
||||
map_location loc_;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
Loading…
Add table
Reference in a new issue