The Battle for Wesnoth  1.19.7+dev
function_gamestate.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2024
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 
17 
18 #include "formula/callable_objects.hpp"
19 #include <utility>
20 
21 #include "resources.hpp"
22 #include "game_board.hpp"
23 #include "map/map.hpp"
24 #include "pathutils.hpp"
25 #include "units/types.hpp"
26 #include "units/unit.hpp"
27 #include "play_controller.hpp"
28 #include "tod_manager.hpp"
29 
30 namespace wfl {
31 
32 namespace gamestate {
33 
34 DEFINE_WFL_FUNCTION(adjacent_locs, 1, 1)
35 {
36  const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "adjacent_locs:location")).convert_to<location_callable>()->loc();
37 
38  std::vector<variant> v;
39  for(const map_location& adj : get_adjacent_tiles(loc)) {
40  if(resources::gameboard->map().on_board(adj)) {
41  v.emplace_back(std::make_shared<location_callable>(adj));
42  }
43  }
44 
45  return variant(v);
46 }
47 
48 DEFINE_WFL_FUNCTION(locations_in_radius, 2, 2)
49 {
50  const map_location loc = args()[0]->evaluate(variables, fdb).convert_to<location_callable>()->loc();
51 
52  int range = args()[1]->evaluate(variables, fdb).as_int();
53 
54  if(range < 0) {
55  return variant();
56  }
57 
58  if(!range) {
59  return variant(std::make_shared<location_callable>(loc));
60  }
61 
62  std::vector<map_location> res;
63 
64  get_tiles_in_radius(loc, range, res);
65 
66  std::vector<variant> v;
67  v.reserve(res.size() + 1);
68  v.emplace_back(std::make_shared<location_callable>(loc));
69 
70  for(std::size_t n = 0; n != res.size(); ++n) {
71  if(resources::gameboard->map().on_board(res[n])) {
72  v.emplace_back(std::make_shared<location_callable>(res[n]));
73  }
74  }
75 
76  return variant(v);
77 }
78 
80 {
81  const std::string type = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "get_unit_type:name")).as_string();
82 
83  const unit_type *ut = unit_types.find(type);
84  if(ut) {
85  return variant(std::make_shared<unit_type_callable>(*ut));
86  }
87 
88  return variant();
89 }
90 
91 DEFINE_WFL_FUNCTION(unit_at, 1, 1)
92 {
93  variant loc_var = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "unit_at:location"));
94  if(loc_var.is_null()) {
95  return variant();
96  }
97  auto loc = loc_var.convert_to<location_callable>();
99  if(i != resources::gameboard->units().end()) {
100  return variant(std::make_shared<unit_callable>(*i));
101  } else {
102  return variant();
103  }
104 }
105 
106 DEFINE_WFL_FUNCTION(defense_on, 2, 2)
107 {
108  variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "defense_on:unit"));
109  variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "defense_on:location"));
110  if(u.is_null() || loc_var.is_null()) {
111  return variant();
112  }
113 
114  auto u_call = u.try_convert<unit_callable>();
115  auto u_type = u.try_convert<unit_type_callable>();
116 
117  const auto& tdata = resources::gameboard->map().tdata();
119 
120  if(loc_var.is_string()) {
122  } else if(auto tc = loc_var.try_convert<terrain_callable>()) {
123  const std::string id = tc->get_value("id").as_string();
124  auto iter = std::find_if(tdata->map().begin(), tdata->map().end(), [id](const std::pair<t_translation::terrain_code, terrain_type>& p) {
125  return id == p.second.id();
126  });
127  if(iter == tdata->map().end()) {
128  return variant();
129  }
130  ter = iter->first;
131  } else if(auto loc = loc_var.try_convert<location_callable>()) {
132  if(!resources::gameboard->map().on_board(loc->loc())) {
133  return variant();
134  }
135  ter = resources::gameboard->map().get_terrain(loc->loc());
136  } else {
137  return variant();
138  }
139 
140  if(u_call) {
141  const unit& un = u_call->get_unit();
142 
143  return variant(100 - un.defense_modifier(ter));
144  }
145 
146  if(u_type) {
147  const unit_type& un = u_type->get_unit_type();
148 
149  return variant(100 - un.movement_type().defense_modifier(ter));
150  }
151 
152  return variant();
153 }
154 
155 DEFINE_WFL_FUNCTION(chance_to_hit, 2, 2)
156 {
157  variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "chance_to_hit:unit"));
158  variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "chance_to_hit:location"));
159  if(u.is_null() || loc_var.is_null()) {
160  return variant();
161  }
162 
163  auto u_call = u.try_convert<unit_callable>();
164  auto u_type = u.try_convert<unit_type_callable>();
165 
166  const auto& tdata = resources::gameboard->map().tdata();
168 
169  if(loc_var.is_string()) {
171  } else if(auto tc = loc_var.try_convert<terrain_callable>()) {
172  const std::string id = tc->get_value("id").as_string();
173  auto iter = std::find_if(tdata->map().begin(), tdata->map().end(), [id](const std::pair<t_translation::terrain_code, terrain_type>& p) {
174  return id == p.second.id();
175  });
176  if(iter == tdata->map().end()) {
177  return variant();
178  }
179  ter = iter->first;
180  } else if(auto loc = loc_var.try_convert<location_callable>()) {
181  if(!resources::gameboard->map().on_board(loc->loc())) {
182  return variant();
183  }
184  ter = resources::gameboard->map().get_terrain(loc->loc());
185  } else {
186  return variant();
187  }
188 
189  if(u_call) {
190  const unit& un = u_call->get_unit();
191 
192  return variant(un.defense_modifier((ter)));
193  }
194 
195  if(u_type) {
196  const unit_type& un = u_type->get_unit_type();
197 
198  return variant(un.movement_type().defense_modifier((ter)));
199  }
200 
201  return variant();
202 }
203 
204 DEFINE_WFL_FUNCTION(movement_cost, 2, 2)
205 {
206  variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "movement_cost:unit"));
207  variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 0, "movement_cost:location"));
208  if(u.is_null() || loc_var.is_null()) {
209  return variant();
210  }
211  //we can pass to this function either unit_callable or unit_type callable
212  auto u_call = u.try_convert<unit_callable>();
213  auto u_type = u.try_convert<unit_type_callable>();
214 
215  const auto& tdata = resources::gameboard->map().tdata();
217 
218  if(loc_var.is_string()) {
220  } else if(auto tc = loc_var.try_convert<terrain_callable>()) {
221  const std::string id = tc->get_value("id").as_string();
222  auto iter = std::find_if(tdata->map().begin(), tdata->map().end(), [id](const std::pair<t_translation::terrain_code, terrain_type>& p) {
223  return id == p.second.id();
224  });
225  if(iter == tdata->map().end()) {
226  return variant();
227  }
228  ter = iter->first;
229  } else if(auto loc = loc_var.try_convert<location_callable>()) {
230  if(!resources::gameboard->map().on_board(loc->loc())) {
231  return variant();
232  }
233  ter = resources::gameboard->map().get_terrain(loc->loc());
234  } else {
235  return variant();
236  }
237 
238  if(u_call) {
239  const unit& un = u_call->get_unit();
240 
241  return variant(un.movement_cost(ter));
242  }
243 
244  if(u_type) {
245  const unit_type& un = u_type->get_unit_type();
246 
247  return variant(un.movement_type().movement_cost(ter));
248  }
249 
250  return variant();
251 }
252 
253 DEFINE_WFL_FUNCTION(vision_cost, 2, 2)
254 {
255  variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "vision_cost:unit"));
256  variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 0, "vision_cost:location"));
257  if(u.is_null() || loc_var.is_null()) {
258  return variant();
259  }
260  //we can pass to this function either unit_callable or unit_type callable
261  auto u_call = u.try_convert<unit_callable>();
262  auto u_type = u.try_convert<unit_type_callable>();
263 
264  const auto& tdata = resources::gameboard->map().tdata();
266 
267  if(loc_var.is_string()) {
269  } else if(auto tc = loc_var.try_convert<terrain_callable>()) {
270  const std::string id = tc->get_value("id").as_string();
271  auto iter = std::find_if(tdata->map().begin(), tdata->map().end(), [id](const std::pair<t_translation::terrain_code, terrain_type>& p) {
272  return id == p.second.id();
273  });
274  if(iter == tdata->map().end()) {
275  return variant();
276  }
277  ter = iter->first;
278  } else if(auto loc = loc_var.try_convert<location_callable>()) {
279  if(!resources::gameboard->map().on_board(loc->loc())) {
280  return variant();
281  }
282  ter = resources::gameboard->map().get_terrain(loc->loc());
283  } else {
284  return variant();
285  }
286 
287  if(u_call) {
288  const unit& un = u_call->get_unit();
289 
290  return variant(un.vision_cost(ter));
291  }
292 
293  if(u_type) {
294  const unit_type& un = u_type->get_unit_type();
295 
296  return variant(un.movement_type().vision_cost(ter));
297  }
298 
299  return variant();
300 }
301 
302 DEFINE_WFL_FUNCTION(jamming_cost, 2, 2)
303 {
304  variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "jamming_cost:unit"));
305  variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 0, "jamming_cost:location"));
306  if(u.is_null() || loc_var.is_null()) {
307  return variant();
308  }
309  //we can pass to this function either unit_callable or unit_type callable
310  auto u_call = u.try_convert<unit_callable>();
311  auto u_type = u.try_convert<unit_type_callable>();
312 
313  const auto& tdata = resources::gameboard->map().tdata();
315 
316  if(loc_var.is_string()) {
318  } else if(auto tc = loc_var.try_convert<terrain_callable>()) {
319  const std::string id = tc->get_value("id").as_string();
320  auto iter = std::find_if(tdata->map().begin(), tdata->map().end(), [id](const std::pair<t_translation::terrain_code, terrain_type>& p) {
321  return id == p.second.id();
322  });
323  if(iter == tdata->map().end()) {
324  return variant();
325  }
326  ter = iter->first;
327  } else if(auto loc = loc_var.try_convert<location_callable>()) {
328  if(!resources::gameboard->map().on_board(loc->loc())) {
329  return variant();
330  }
331  ter = resources::gameboard->map().get_terrain(loc->loc());
332  } else {
333  return variant();
334  }
335 
336  if(u_call) {
337  const unit& un = u_call->get_unit();
338 
339  return variant(un.jamming_cost(ter));
340  }
341 
342  if(u_type) {
343  const unit_type& un = u_type->get_unit_type();
344 
345  return variant(un.movement_type().jamming_cost(ter));
346  }
347 
348  return variant();
349 }
350 
351 DEFINE_WFL_FUNCTION(enemy_of, 2, 2)
352 {
353  variant self_v = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "enemy_of:self"));
354  variant other_v = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "enemy_of:other"));
355  int self, other;
356 
357  if(auto uc = self_v.try_convert<unit_callable>()) {
358  self = uc->get_value("side_number").as_int();
359  } else if(auto tc = self_v.try_convert<team_callable>()) {
360  self = tc->get_value("side_number").as_int();
361  } else {
362  self = self_v.as_int();
363  }
364 
365  if(auto uc = other_v.try_convert<unit_callable>()) {
366  other = uc->get_value("side_number").as_int();
367  } else if(auto tc = other_v.try_convert<team_callable>()) {
368  other = tc->get_value("side_number").as_int();
369  } else {
370  other = other_v.as_int();
371  }
372 
373  int num_teams = resources::gameboard->teams().size();
374  if(self < 1 || self > num_teams || other < 1 || other > num_teams) {
375  return variant(0);
376  }
377  return variant(resources::gameboard->get_team(self).is_enemy(other) ? 1 : 0);
378 }
379 
380 DEFINE_WFL_FUNCTION(resistance_on, 3, 4)
381 {
382  variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "resistance_on:unit"));
383  variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "resistance_on:location"));
384  if(u.is_null() || loc_var.is_null()) {
385  return variant();
386  }
387  std::string type = args()[2]->evaluate(variables, add_debug_info(fdb, 2, "resistance_on:type")).as_string();
388  bool attacker = args().size() > 3 ? args()[3]->evaluate(variables, add_debug_info(fdb, 3, "resistance_on:attacker")).as_bool() : false;
389  const map_location& loc = loc_var.convert_to<location_callable>()->loc();
390 
391  if(auto u_call = u.try_convert<unit_callable>()) {
392  const unit& un = u_call->get_unit();
393 
394  return variant(100 - un.resistance_against(type, attacker, loc));
395  }
396 
397  return variant();
398 }
399 
400 DEFINE_WFL_FUNCTION(tod_bonus, 0, 2)
401 {
403  int turn = resources::controller->turn();
404  if(args().size() > 0) {
405  variant loc_arg = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "tod_bonus:loc"));
406  if(auto p = loc_arg.try_convert<location_callable>()) {
407  loc = p->loc();
408  } else return variant();
409 
410  if(args().size() > 1) {
411  variant turn_arg = args()[1]->evaluate(variables, add_debug_info(fdb, 0, "tod_bonus:turn"));
412  if(turn_arg.is_int()) {
413  turn = turn_arg.as_int();
414  } else if(!turn_arg.is_null()) {
415  return variant();
416  }
417  }
418  }
420  return variant(bonus);
421 }
422 
423 DEFINE_WFL_FUNCTION(base_tod_bonus, 0, 2)
424 {
426  int turn = resources::controller->turn();
427  if(args().size() > 0) {
428  variant loc_arg = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "tod_bonus:loc"));
429  if(auto p = loc_arg.try_convert<location_callable>()) {
430  loc = p->loc();
431  } else return variant();
432 
433  if(args().size() > 1) {
434  variant turn_arg = args()[1]->evaluate(variables, add_debug_info(fdb, 0, "tod_bonus:turn"));
435  if(turn_arg.is_int()) {
436  turn = turn_arg.as_int();
437  } else if(!turn_arg.is_null()) {
438  return variant();
439  }
440  }
441  }
443  return variant(bonus);
444 }
445 
446 } // namespace gamestate
447 
448 gamestate_function_symbol_table::gamestate_function_symbol_table(const std::shared_ptr<function_symbol_table>& parent) : function_symbol_table(parent) {
449  using namespace gamestate;
450  function_symbol_table& functions_table = *this;
452  DECLARE_WFL_FUNCTION(unit_at);
453  DECLARE_WFL_FUNCTION(resistance_on);
454  DECLARE_WFL_FUNCTION(defense_on);
455  DECLARE_WFL_FUNCTION(chance_to_hit);
456  DECLARE_WFL_FUNCTION(movement_cost);
457  DECLARE_WFL_FUNCTION(vision_cost);
458  DECLARE_WFL_FUNCTION(jamming_cost);
459  DECLARE_WFL_FUNCTION(adjacent_locs); // This is deliberately duplicated here; this form excludes off-map locations, while the core form does not
460  DECLARE_WFL_FUNCTION(locations_in_radius);
461  DECLARE_WFL_FUNCTION(enemy_of);
462  DECLARE_WFL_FUNCTION(tod_bonus);
463  DECLARE_WFL_FUNCTION(base_tod_bonus);
464 }
465 
466 }
map_location loc
Definition: move.cpp:172
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:80
virtual const unit_map & units() const override
Definition: game_board.hpp:107
virtual const gamemap & map() const override
Definition: game_board.hpp:97
terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:302
const std::shared_ptr< terrain_type_data > & tdata() const
Definition: map.hpp:204
int defense_modifier(const t_translation::terrain_code &terrain) const
Returns the defensive value of the indicated terrain.
Definition: movetype.hpp:288
int jamming_cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost to "jam" through the indicated terrain.
Definition: movetype.hpp:284
int vision_cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost to see through the indicated terrain.
Definition: movetype.hpp:281
int movement_cost(const t_translation::terrain_code &terrain, bool slowed=false) const
Returns the cost to move through the indicated terrain.
Definition: movetype.hpp:278
std::size_t turn() const
const 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.
const time_of_day & get_time_of_day(int for_turn=0) const
Returns global time of day for the passed turn.
Definition: tod_manager.hpp:56
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:1265
A single unit type that the player may recruit.
Definition: types.hpp:43
const movetype & movement_type() const
Definition: types.hpp:189
This class represents a single unit of a specific type.
Definition: unit.hpp:133
gamestate_function_symbol_table(const std::shared_ptr< function_symbol_table > &parent=nullptr)
int as_int() const
Definition: variant.cpp:291
std::shared_ptr< T > try_convert() const
Definition: variant.hpp:90
std::shared_ptr< T > convert_to() const
Definition: variant.hpp:100
const std::string & as_string() const
Definition: variant.cpp:318
bool is_string() const
Definition: variant.hpp:67
bool is_null() const
Functions to test the type of the internal value.
Definition: variant.hpp:62
bool is_int() const
Definition: variant.hpp:63
std::size_t i
Definition: function.cpp:1029
#define DECLARE_WFL_FUNCTION(name)
Declares a function name in the local function table functions_table.
Definition: function.hpp:47
int defense_modifier(const t_translation::terrain_code &terrain) const
The unit's defense on a given terrain.
Definition: unit.cpp:1717
int resistance_against(const std::string &damage_name, bool attacker, const map_location &loc, const_attack_ptr weapon=nullptr, const const_attack_ptr &opp_weapon=nullptr) const
The unit's resistance against a given damage type.
Definition: unit.cpp:1781
int movement_cost(const t_translation::terrain_code &terrain) const
Get the unit's movement cost on a particular terrain.
Definition: unit.hpp:1487
int vision_cost(const t_translation::terrain_code &terrain) const
Get the unit's vision cost on a particular terrain.
Definition: unit.hpp:1497
int jamming_cost(const t_translation::terrain_code &terrain) const
Get the unit's jamming cost on a particular terrain.
Definition: unit.hpp:1507
void get_adjacent_tiles(const map_location &a, map_location *res)
Function which, given a location, will place all adjacent locations in res.
Definition: location.cpp:479
::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.
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
DEFINE_WFL_FUNCTION(adjacent_locs, 1, 1)
Definition: contexts.hpp:43
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:45
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:49
int lawful_bonus
The % bonus lawful units receive.
Definition: time_of_day.hpp:83
mock_party p
static map_location::direction n
unit_type_data unit_types
Definition: types.cpp:1500
static const unit_type & get_unit_type(const std::string &type_id)
Converts a string ID to a unit_type.
Definition: unit.cpp:214