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