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