The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
function_gamestate.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2003 - 2025
3 by David White <dave@whitevine.net>
4 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY.
12
13 See the COPYING file for more details.
14*/
15
18
19#include "actions/attack.hpp"
20#include "filesystem.hpp"
21#include "game_board.hpp"
22#include "map/label.hpp"
23#include "map/map.hpp"
24#include "pathutils.hpp"
25#include "play_controller.hpp"
26#include "resources.hpp"
27#include "tod_manager.hpp"
28#include "units/types.hpp"
29#include "units/unit.hpp"
30
32#include <utility>
33
34namespace wfl {
35
36namespace gamestate {
37
38namespace
39{
40/**
41 * Extracts a terrain_code from the given variant.
42 *
43 * @param loc_var Variant containing either terrain_callable or location_callable.
44 * A plain terrain code string is also accepted.
45 *
46 * @returns The given location's terrain_code for the given terrain, or nullopt
47 * if an invalid variant was given.
48 */
49utils::optional<t_translation::terrain_code> as_terrain_code(const variant& loc_var)
50{
51 if(loc_var.is_string()) {
53 }
54
55 else if(auto tc = callable_cast<terrain_callable*>(loc_var)) {
56 return tc->get_terrain_type().number();
57 }
58
59 else if(auto loc = callable_cast<location_callable*>(loc_var)) {
60 const gamemap& map = resources::gameboard->map();
61 if(map.on_board(loc->loc())) {
62 return map.get_terrain(loc->loc());
63 }
64 }
65
66 return utils::nullopt;
67}
68
69/**
70 * Gets arbitrary movement type info for a given terrain from either a unit or unit type.
71 *
72 * @param loc_var Variant containing either terrain_callable or location_callable.
73 * A plain terrain code string is also accepted.
74 * @param unit_var Variant containing either unit_callable or unit_type_callable.
75 * @param getter Functor of the signature `[](const terrain_code&, const auto&) -> variant`
76 * If @a unit_var resolves to a unit_callable, the second argument will be a
77 * const @ref unit reference. For unit_type_callable, the argument will be a
78 * const @ref movetype reference for that unit type.
79 */
80template<typename F>
81variant get_movement_property(const variant& loc_var, const variant& unit_var, const F& getter)
82{
83 const utils::optional terrain_code = as_terrain_code(loc_var);
84 if(!terrain_code) {
85 return variant();
86 }
87
88 if(auto u_call = callable_cast<unit_callable*>(unit_var)) {
89 return std::invoke(getter, terrain_code.value(), u_call->get_unit());
90 }
91
92 if(auto u_type = callable_cast<unit_type_callable*>(unit_var)) {
93 return std::invoke(getter, terrain_code.value(), u_type->get_unit_type().movement_type());
94 }
95
96 return variant();
97}
98
99} // namespace
100
101DEFINE_WFL_FUNCTION(run_file, 1, 1)
102{
103 variant var = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "run_file:file"));
104
105 // NOTE: get_wml_location also filters file path to ensure it doesn't contain things like "../../top/secret"
106 auto path = filesystem::get_wml_location(var.as_string());
107 if(!path) {
108 return variant();
109 }
110
111 std::string formula_string = filesystem::read_file(path.value());
113
114 auto parsed_formula = formula::create_optional_formula(formula_string, &symbols);
115 return formula::evaluate(parsed_formula, variables, add_debug_info(fdb, -1, "run_file:formula_from_file"));
116}
117
118DEFINE_WFL_FUNCTION(debug_label, 2, 2)
119{
120 variant var0 = args()[0]->evaluate(variables, fdb);
121 variant var1 = args()[1]->evaluate(variables, fdb);
122
125 const map_location loc = callable_cast<location_callable&>(var0).loc();
126 const std::string text = var1.is_string() ? var1.as_string() : var1.to_debug_string();
127
130 }
131
132 return variant(std::vector{var0, var1});
133}
134
135DEFINE_WFL_FUNCTION(adjacent_locs, 1, 1)
136{
137 const map_location loc = callable_cast<location_callable&>(args()[0]->evaluate(variables, add_debug_info(fdb, 0, "adjacent_locs:location"))).loc();
138
139 std::vector<variant> v;
140 for(const map_location& adj : get_adjacent_tiles(loc)) {
141 if(resources::gameboard->map().on_board(adj)) {
142 v.emplace_back(std::make_shared<location_callable>(adj));
143 }
144 }
145
146 return variant(std::move(v));
147}
148
149DEFINE_WFL_FUNCTION(locations_in_radius, 2, 2)
150{
151 const map_location loc = callable_cast<location_callable&>(args()[0]->evaluate(variables, fdb)).loc();
152
153 int range = args()[1]->evaluate(variables, fdb).as_int();
154
155 if(range < 0) {
156 return variant();
157 }
158
159 if(!range) {
160 return make_callable<location_callable>(loc);
161 }
162
163 std::vector<map_location> res;
164
165 get_tiles_in_radius(loc, range, res);
166
167 std::vector<variant> v;
168 v.reserve(res.size() + 1);
169 v.emplace_back(std::make_shared<location_callable>(loc));
170
171 for(std::size_t n = 0; n != res.size(); ++n) {
172 if(resources::gameboard->map().on_board(res[n])) {
173 v.emplace_back(std::make_shared<location_callable>(res[n]));
174 }
175 }
176
177 return variant(std::move(v));
178}
179
181{
182 const std::string type = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "get_unit_type:name")).as_string();
183
184 const unit_type *ut = unit_types.find(type);
185 if(ut) {
186 return make_callable<unit_type_callable>(*ut);
187 }
188
189 return variant();
190}
191
193{
194 variant loc_var = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "unit_at:location"));
195 if(loc_var.is_null()) {
196 return variant();
197 }
198 const auto& loc = callable_cast<location_callable&>(loc_var);
200 if(i != resources::gameboard->units().end()) {
201 return make_callable<unit_callable>(*i);
202 } else {
203 return variant();
204 }
205}
206
207DEFINE_WFL_FUNCTION(defense_on, 2, 2)
208{
209 variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "defense_on:unit"));
210 variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "defense_on:location"));
211 if(u.is_null() || loc_var.is_null()) {
212 return variant();
213 }
214
215 return get_movement_property(loc_var, u,
216 [](const t_translation::terrain_code& terrain, const auto& datum) {
217 return variant(100 - datum.defense_modifier(terrain));
218 });
219}
220
221DEFINE_WFL_FUNCTION(chance_to_hit, 2, 2)
222{
223 variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "chance_to_hit:unit"));
224 variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "chance_to_hit:location"));
225 if(u.is_null() || loc_var.is_null()) {
226 return variant();
227 }
228
229 return get_movement_property(loc_var, u,
230 [](const t_translation::terrain_code& terrain, const auto& datum) {
231 return variant(datum.defense_modifier(terrain));
232 });
233}
234
235DEFINE_WFL_FUNCTION(movement_cost, 2, 2)
236{
237 variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "movement_cost:unit"));
238 variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 0, "movement_cost:location"));
239 if(u.is_null() || loc_var.is_null()) {
240 return variant();
241 }
242
243 return get_movement_property(loc_var, u,
244 [](const t_translation::terrain_code& terrain, const auto& datum) {
245 return variant(datum.movement_cost(terrain));
246 });
247}
248
249DEFINE_WFL_FUNCTION(vision_cost, 2, 2)
250{
251 variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "vision_cost:unit"));
252 variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 0, "vision_cost:location"));
253 if(u.is_null() || loc_var.is_null()) {
254 return variant();
255 }
256
257 return get_movement_property(loc_var, u,
258 [](const t_translation::terrain_code& terrain, const auto& datum) {
259 return variant(datum.vision_cost(terrain));
260 });
261}
262
263DEFINE_WFL_FUNCTION(jamming_cost, 2, 2)
264{
265 variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "jamming_cost:unit"));
266 variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 0, "jamming_cost:location"));
267 if(u.is_null() || loc_var.is_null()) {
268 return variant();
269 }
270
271 return get_movement_property(loc_var, u,
272 [](const t_translation::terrain_code& terrain, const auto& datum) {
273 return variant(datum.jamming_cost(terrain));
274 });
275}
276
277DEFINE_WFL_FUNCTION(enemy_of, 2, 2)
278{
279 variant self_v = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "enemy_of:self"));
280 variant other_v = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "enemy_of:other"));
281 int self, other;
282
283 if(auto uc = callable_cast<unit_callable*>(self_v)) {
284 self = uc->get_value("side_number").as_int();
285 } else if(auto tc = callable_cast<team_callable*>(self_v)) {
286 self = tc->get_value("side_number").as_int();
287 } else {
288 self = self_v.as_int();
289 }
290
291 if(auto uc = callable_cast<unit_callable*>(other_v)) {
292 other = uc->get_value("side_number").as_int();
293 } else if(auto tc = callable_cast<team_callable*>(other_v)) {
294 other = tc->get_value("side_number").as_int();
295 } else {
296 other = other_v.as_int();
297 }
298
299 int num_teams = resources::gameboard->teams().size();
300 if(self < 1 || self > num_teams || other < 1 || other > num_teams) {
301 return variant(0);
302 }
303 return variant(resources::gameboard->get_team(self).is_enemy(other) ? 1 : 0);
304}
305
306DEFINE_WFL_FUNCTION(resistance_on, 3, 4)
307{
308 variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "resistance_on:unit"));
309 variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "resistance_on:location"));
310 if(u.is_null() || loc_var.is_null()) {
311 return variant();
312 }
313 std::string type = args()[2]->evaluate(variables, add_debug_info(fdb, 2, "resistance_on:type")).as_string();
314 bool attacker = args().size() > 3 ? args()[3]->evaluate(variables, add_debug_info(fdb, 3, "resistance_on:attacker")).as_bool() : false;
315 const map_location& loc = callable_cast<location_callable&>(loc_var).loc();
316
317 if(auto u_call = callable_cast<unit_callable*>(u)) {
318 const unit& un = u_call->get_unit();
319
320 return variant(100 - un.resistance_against(type, attacker, loc));
321 }
322
323 return variant();
324}
325
326DEFINE_WFL_FUNCTION(tod_bonus, 0, 2)
327{
329 int turn = resources::controller->turn();
330 if(args().size() > 0) {
331 variant loc_arg = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "tod_bonus:loc"));
332 if(auto p = callable_cast<location_callable*>(loc_arg)) {
333 loc = p->loc();
334 } else return variant();
335
336 if(args().size() > 1) {
337 variant turn_arg = args()[1]->evaluate(variables, add_debug_info(fdb, 0, "tod_bonus:turn"));
338 if(turn_arg.is_int()) {
339 turn = turn_arg.as_int();
340 } else if(!turn_arg.is_null()) {
341 return variant();
342 }
343 }
344 }
346 return variant(bonus);
347}
348
349DEFINE_WFL_FUNCTION(base_tod_bonus, 0, 2)
350{
352 int turn = resources::controller->turn();
353 if(args().size() > 0) {
354 variant loc_arg = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "tod_bonus:loc"));
355 if(auto p = callable_cast<location_callable*>(loc_arg)) {
356 loc = p->loc();
357 } else return variant();
358
359 if(args().size() > 1) {
360 variant turn_arg = args()[1]->evaluate(variables, add_debug_info(fdb, 0, "tod_bonus:turn"));
361 if(turn_arg.is_int()) {
362 turn = turn_arg.as_int();
363 } else if(!turn_arg.is_null()) {
364 return variant();
365 }
366 }
367 }
369 return variant(bonus);
370}
371
372DEFINE_WFL_FUNCTION(unit_tod_modifier, 1, 2)
373{
374 const unit& unit = callable_cast<unit_callable&>(
375 args()[0]->evaluate(variables, add_debug_info(fdb, 0, "unit_tod_modifier:unit"))).get_unit();
376
377 const map_location loc = args().size() == 2
378 ? callable_cast<location_callable&>(
379 args()[1]->evaluate(variables, add_debug_info(fdb, 1, "unit_tod_modifier:location"))).loc()
380 : unit.get_location();
381
384}
385
387{
388 variant var0 = args()[0]->evaluate(variables, fdb);
389 variant var1 = args()[1]->evaluate(variables, fdb);
390
391 try {
392 const map_location loc = callable_cast<location_callable&>(var0).loc();
393 return variant(resources::gameboard->get_team(var1.as_int()).shrouded(loc));
394
395 } catch(const std::out_of_range&) {
396 return variant();
397 }
398}
399
401{
402 variant var0 = args()[0]->evaluate(variables, fdb);
403 variant var1 = args()[1]->evaluate(variables, fdb);
404
405 try {
406 const map_location loc = callable_cast<location_callable&>(var0).loc();
407 return variant(resources::gameboard->get_team(var1.as_int()).fogged(loc));
408
409 } catch(const std::out_of_range&) {
410 return variant();
411 }
412}
413
414} // namespace gamestate
415
416gamestate_function_symbol_table::gamestate_function_symbol_table(const std::shared_ptr<function_symbol_table>& parent) : function_symbol_table(parent) {
417 using namespace gamestate;
418 function_symbol_table& functions_table = *this;
419 DECLARE_WFL_FUNCTION(run_file);
420 DECLARE_WFL_FUNCTION(debug_label);
422 DECLARE_WFL_FUNCTION(unit_at);
423 DECLARE_WFL_FUNCTION(resistance_on);
424 DECLARE_WFL_FUNCTION(defense_on);
425 DECLARE_WFL_FUNCTION(chance_to_hit);
426 DECLARE_WFL_FUNCTION(movement_cost);
427 DECLARE_WFL_FUNCTION(vision_cost);
428 DECLARE_WFL_FUNCTION(jamming_cost);
429 DECLARE_WFL_FUNCTION(adjacent_locs); // This is deliberately duplicated here; this form excludes off-map locations, while the core form does not
430 DECLARE_WFL_FUNCTION(locations_in_radius);
431 DECLARE_WFL_FUNCTION(enemy_of);
432 DECLARE_WFL_FUNCTION(tod_bonus);
433 DECLARE_WFL_FUNCTION(base_tod_bonus);
434 DECLARE_WFL_FUNCTION(unit_tod_modifier);
437}
438
439} // namespace wfl
static bool is_enemy(std::size_t side, std::size_t other_side)
int combat_modifier(const unit_map &units, const gamemap &map, const map_location &loc, unit_alignments::type alignment, bool is_fearless)
Returns the amount that a unit's damage should be multiplied by due to the current time of day.
Definition attack.cpp:1518
Various functions that implement attacks and attack calculations.
map_location loc
Definition move.cpp:172
map_labels & labels()
Definition display.cpp:2392
static display * get_singleton()
Returns the display object if a display object exists.
Definition display.hpp:102
virtual const gamemap & map() const override
virtual const std::vector< team > & teams() const override
virtual const unit_map & units() const override
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition map.cpp:265
bool on_board(const map_location &loc) const
Tell if a location is on the map.
Definition map.cpp:347
Encapsulates the map of the game.
Definition map.hpp:176
const terrain_label * set_label(const map_location &loc, const t_string &text, const int creator=-1, const std::string &team="", const color_t color=font::NORMAL_COLOR, const bool visible_in_fog=true, const bool visible_in_shroud=false, const bool immutable=false, const std::string &category="", const t_string &tooltip="")
Definition label.cpp:148
std::size_t turn() const
This class stores all the data for a single 'side' (in game nomenclature).
Definition team.hpp:74
int side() const
Definition team.hpp:182
const std::string & team_name() const
Definition team.hpp:323
static color_t get_side_color(int side)
Definition team.cpp:943
const time_of_day & get_time_of_day(int for_turn=0) const
Returns global time of day for the passed turn.
time_of_day get_illuminated_time_of_day(const unit_map &units, const gamemap &map, const map_location &loc, int for_turn=0) const
Returns time of day object for the passed turn at a location.
unit_iterator find(std::size_t id)
Definition map.cpp:302
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition types.cpp:1253
A single unit type that the player may recruit.
Definition types.hpp:43
This class represents a single unit of a specific type.
Definition unit.hpp:39
static variant evaluate(const const_formula_ptr &f, const formula_callable &variables, formula_debugger *fdb=nullptr, variant default_res=variant(0))
Definition formula.hpp:48
static formula_ptr create_optional_formula(const std::string &str, function_symbol_table *symbols=nullptr)
Definition formula.cpp:231
gamestate_function_symbol_table(const std::shared_ptr< function_symbol_table > &parent=nullptr)
int as_int(int fallback=0) const
Returns the variant's value as an integer.
Definition variant.cpp:336
const std::string & as_string() const
Definition variant.cpp:362
bool is_string() const
Definition variant.hpp:74
std::string to_debug_string(bool verbose=false, formula_seen_stack *seen=nullptr) const
Definition variant.cpp:686
bool is_null() const
Functions to test the type of the internal value.
Definition variant.hpp:69
bool is_int() const
Definition variant.hpp:70
Declarations for File-IO.
#define DEFINE_WFL_FUNCTION(name, min_args, max_args)
Helper macro to declare an associated class for a WFL function.
Definition function.hpp:27
#define DECLARE_WFL_FUNCTION(name)
Declares a function name in the local function table functions_table.
Definition function.hpp:47
unit_alignments::type alignment() const
The alignment of this unit.
Definition unit.hpp:397
int resistance_against(const std::string &damage_name, bool attacker, const map_location &loc) const
The unit's resistance against a given damage type.
Definition unit.cpp:1827
const map_location & get_location() const
The current map location this unit is at.
Definition unit.hpp:1327
bool is_fearless() const
Gets whether this unit is fearless - ie, unaffected by time of day.
Definition unit.hpp:1186
void get_adjacent_tiles(const map_location &a, utils::span< map_location, 6 > res)
Function which, given a location, will place all adjacent locations in res.
Definition location.cpp:513
bool is_shrouded(const display *disp, const map_location &loc)
Our definition of map labels being obscured is if the tile is obscured, or the tile below is obscured...
Definition label.cpp:33
bool is_fogged(const display *disp, const map_location &loc)
Rather simple test for a hex being fogged.
Definition label.cpp:43
utils::optional< std::string > get_wml_location(const std::string &path, const utils::optional< std::string > &current_dir=utils::nullopt)
Returns a translated path to the actual file or directory, if it exists.
std::string read_file(const std::string &fname)
Basic disk I/O - read file.
const bool & debug
::tod_manager * tod_manager
Definition resources.cpp:29
game_board * gameboard
Definition resources.cpp:20
play_controller * controller
Definition resources.cpp:21
terrain_code read_terrain_code(std::string_view str, const ter_layer filler)
Reads a single terrain from a string.
formula_debugger * add_debug_info(formula_debugger *fdb, int arg_number, const std::string &f_name)
void get_tiles_in_radius(const map_location &center, const int radius, std::vector< map_location > &result)
Function that will add to result all locations within radius tiles of center (excluding center itself...
Definition pathutils.cpp:56
Encapsulates the map of the game.
Definition location.hpp:46
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
int lawful_bonus
The % bonus lawful units receive.
mock_party p
static map_location::direction n
unit_type_data unit_types
Definition types.cpp:1499
static const unit_type & get_unit_type(const std::string &type_id)
Converts a string ID to a unit_type.
Definition unit.cpp:216
#define i