The Battle for Wesnoth  1.15.2+dev
function_gamestate.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2018 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
16 #include "formula/callable_objects.hpp"
17 
18 #include "resources.hpp"
19 #include "game_board.hpp"
20 #include "map/map.hpp"
21 #include "pathutils.hpp"
22 #include "units/types.hpp"
23 #include "units/unit.hpp"
24 
25 namespace wfl {
26 
27 namespace gamestate {
28 
29 DEFINE_WFL_FUNCTION(adjacent_locs, 1, 1)
30 {
31  const map_location loc = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "adjacent_locs:location")).convert_to<location_callable>()->loc();
33  get_adjacent_tiles(loc, adj.data());
34 
35  std::vector<variant> v;
36  for(unsigned n = 0; n < adj.size(); ++n) {
37  if(resources::gameboard->map().on_board(adj[n])) {
38  v.emplace_back(std::make_shared<location_callable>(adj[n]));
39  }
40  }
41 
42  return variant(v);
43 }
44 
45 DEFINE_WFL_FUNCTION(locations_in_radius, 2, 2)
46 {
47  const map_location loc = args()[0]->evaluate(variables, fdb).convert_to<location_callable>()->loc();
48 
49  int range = args()[1]->evaluate(variables, fdb).as_int();
50 
51  if(range < 0) {
52  return variant();
53  }
54 
55  if(!range) {
56  return variant(std::make_shared<location_callable>(loc));
57  }
58 
59  std::vector<map_location> res;
60 
61  get_tiles_in_radius(loc, range, res);
62 
63  std::vector<variant> v;
64  v.reserve(res.size() + 1);
65  v.emplace_back(std::make_shared<location_callable>(loc));
66 
67  for(std::size_t n = 0; n != res.size(); ++n) {
68  if(resources::gameboard->map().on_board(res[n])) {
69  v.emplace_back(std::make_shared<location_callable>(res[n]));
70  }
71  }
72 
73  return variant(v);
74 }
75 
77 {
78  const std::string type = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "get_unit_type:name")).as_string();
79 
80  const unit_type *ut = unit_types.find(type);
81  if(ut) {
82  return variant(std::make_shared<unit_type_callable>(*ut));
83  }
84 
85  return variant();
86 }
87 
88 DEFINE_WFL_FUNCTION(unit_at, 1, 1)
89 {
90  variant loc_var = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "unit_at:location"));
91  if(loc_var.is_null()) {
92  return variant();
93  }
94  auto loc = loc_var.convert_to<location_callable>();
96  if(i != resources::gameboard->units().end()) {
97  return variant(std::make_shared<unit_callable>(*i));
98  } else {
99  return variant();
100  }
101 }
102 
103 DEFINE_WFL_FUNCTION(defense_on, 2, 2)
104 {
105  variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "defense_on:unit"));
106  variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "defense_on:location"));
107  if(u.is_null() || loc_var.is_null()) {
108  return variant();
109  }
110 
111  auto u_call = u.try_convert<unit_callable>();
112  auto u_type = u.try_convert<unit_type_callable>();
113 
114  const auto& tdata = resources::gameboard->map().tdata();
116 
117  if(loc_var.is_string()) {
119  } else if(auto tc = loc_var.try_convert<terrain_callable>()) {
120  const std::string id = tc->get_value("id").as_string();
121  auto iter = std::find_if(tdata->map().begin(), tdata->map().end(), [id](const std::pair<t_translation::terrain_code, terrain_type>& p) {
122  return id == p.second.id();
123  });
124  if(iter == tdata->map().end()) {
125  return variant();
126  }
127  ter = iter->first;
128  } else if(auto loc = loc_var.try_convert<location_callable>()) {
129  if(!resources::gameboard->map().on_board(loc->loc())) {
130  return variant();
131  }
132  ter = resources::gameboard->map().get_terrain(loc->loc());
133  } else {
134  return variant();
135  }
136 
137  if(u_call) {
138  const unit& un = u_call->get_unit();
139 
140  return variant(100 - un.defense_modifier(ter));
141  }
142 
143  if(u_type) {
144  const unit_type& un = u_type->get_unit_type();
145 
146  return variant(100 - un.movement_type().defense_modifier(ter));
147  }
148 
149  return variant();
150 }
151 
152 DEFINE_WFL_FUNCTION(chance_to_hit, 2, 2)
153 {
154  variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "chance_to_hit:unit"));
155  variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "chance_to_hit:location"));
156  if(u.is_null() || loc_var.is_null()) {
157  return variant();
158  }
159 
160  auto u_call = u.try_convert<unit_callable>();
161  auto u_type = u.try_convert<unit_type_callable>();
162 
163  const auto& tdata = resources::gameboard->map().tdata();
165 
166  if(loc_var.is_string()) {
168  } else if(auto tc = loc_var.try_convert<terrain_callable>()) {
169  const std::string id = tc->get_value("id").as_string();
170  auto iter = std::find_if(tdata->map().begin(), tdata->map().end(), [id](const std::pair<t_translation::terrain_code, terrain_type>& p) {
171  return id == p.second.id();
172  });
173  if(iter == tdata->map().end()) {
174  return variant();
175  }
176  ter = iter->first;
177  } else if(auto loc = loc_var.try_convert<location_callable>()) {
178  if(!resources::gameboard->map().on_board(loc->loc())) {
179  return variant();
180  }
181  ter = resources::gameboard->map().get_terrain(loc->loc());
182  } else {
183  return variant();
184  }
185 
186  if(u_call) {
187  const unit& un = u_call->get_unit();
188 
189  return variant(un.defense_modifier((ter)));
190  }
191 
192  if(u_type) {
193  const unit_type& un = u_type->get_unit_type();
194 
195  return variant(un.movement_type().defense_modifier((ter)));
196  }
197 
198  return variant();
199 }
200 
201 DEFINE_WFL_FUNCTION(movement_cost, 2, 2)
202 {
203  variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "movement_cost:unit"));
204  variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 0, "movement_cost:location"));
205  if(u.is_null() || loc_var.is_null()) {
206  return variant();
207  }
208  //we can pass to this function either unit_callable or unit_type callable
209  auto u_call = u.try_convert<unit_callable>();
210  auto u_type = u.try_convert<unit_type_callable>();
211 
212  const auto& tdata = resources::gameboard->map().tdata();
214 
215  if(loc_var.is_string()) {
217  } else if(auto tc = loc_var.try_convert<terrain_callable>()) {
218  const std::string id = tc->get_value("id").as_string();
219  auto iter = std::find_if(tdata->map().begin(), tdata->map().end(), [id](const std::pair<t_translation::terrain_code, terrain_type>& p) {
220  return id == p.second.id();
221  });
222  if(iter == tdata->map().end()) {
223  return variant();
224  }
225  ter = iter->first;
226  } else if(auto loc = loc_var.try_convert<location_callable>()) {
227  if(!resources::gameboard->map().on_board(loc->loc())) {
228  return variant();
229  }
230  ter = resources::gameboard->map().get_terrain(loc->loc());
231  } else {
232  return variant();
233  }
234 
235  if(u_call) {
236  const unit& un = u_call->get_unit();
237 
238  return variant(un.movement_cost(ter));
239  }
240 
241  if(u_type) {
242  const unit_type& un = u_type->get_unit_type();
243 
244  return variant(un.movement_type().movement_cost(ter));
245  }
246 
247  return variant();
248 }
249 
250 DEFINE_WFL_FUNCTION(vision_cost, 2, 2)
251 {
252  variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "vision_cost:unit"));
253  variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 0, "vision_cost:location"));
254  if(u.is_null() || loc_var.is_null()) {
255  return variant();
256  }
257  //we can pass to this function either unit_callable or unit_type callable
258  auto u_call = u.try_convert<unit_callable>();
259  auto u_type = u.try_convert<unit_type_callable>();
260 
261  const auto& tdata = resources::gameboard->map().tdata();
263 
264  if(loc_var.is_string()) {
266  } else if(auto tc = loc_var.try_convert<terrain_callable>()) {
267  const std::string id = tc->get_value("id").as_string();
268  auto iter = std::find_if(tdata->map().begin(), tdata->map().end(), [id](const std::pair<t_translation::terrain_code, terrain_type>& p) {
269  return id == p.second.id();
270  });
271  if(iter == tdata->map().end()) {
272  return variant();
273  }
274  ter = iter->first;
275  } else if(auto loc = loc_var.try_convert<location_callable>()) {
276  if(!resources::gameboard->map().on_board(loc->loc())) {
277  return variant();
278  }
279  ter = resources::gameboard->map().get_terrain(loc->loc());
280  } else {
281  return variant();
282  }
283 
284  if(u_call) {
285  const unit& un = u_call->get_unit();
286 
287  return variant(un.vision_cost(ter));
288  }
289 
290  if(u_type) {
291  const unit_type& un = u_type->get_unit_type();
292 
293  return variant(un.movement_type().vision_cost(ter));
294  }
295 
296  return variant();
297 }
298 
299 DEFINE_WFL_FUNCTION(jamming_cost, 2, 2)
300 {
301  variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "jamming_cost:unit"));
302  variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 0, "jamming_cost:location"));
303  if(u.is_null() || loc_var.is_null()) {
304  return variant();
305  }
306  //we can pass to this function either unit_callable or unit_type callable
307  auto u_call = u.try_convert<unit_callable>();
308  auto u_type = u.try_convert<unit_type_callable>();
309 
310  const auto& tdata = resources::gameboard->map().tdata();
312 
313  if(loc_var.is_string()) {
315  } else if(auto tc = loc_var.try_convert<terrain_callable>()) {
316  const std::string id = tc->get_value("id").as_string();
317  auto iter = std::find_if(tdata->map().begin(), tdata->map().end(), [id](const std::pair<t_translation::terrain_code, terrain_type>& p) {
318  return id == p.second.id();
319  });
320  if(iter == tdata->map().end()) {
321  return variant();
322  }
323  ter = iter->first;
324  } else if(auto loc = loc_var.try_convert<location_callable>()) {
325  if(!resources::gameboard->map().on_board(loc->loc())) {
326  return variant();
327  }
328  ter = resources::gameboard->map().get_terrain(loc->loc());
329  } else {
330  return variant();
331  }
332 
333  if(u_call) {
334  const unit& un = u_call->get_unit();
335 
336  return variant(un.jamming_cost(ter));
337  }
338 
339  if(u_type) {
340  const unit_type& un = u_type->get_unit_type();
341 
342  return variant(un.movement_type().jamming_cost(ter));
343  }
344 
345  return variant();
346 }
347 
348 DEFINE_WFL_FUNCTION(enemy_of, 2, 2)
349 {
350  variant self_v = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "enemy_of:self"));
351  variant other_v = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "enemy_of:other"));
352  int self, other;
353 
354  if(auto uc = self_v.try_convert<unit_callable>()) {
355  // For some obscure, bizarre reason, the unit callable returns a 0-indexed side. :|
356  self = uc->get_value("side").as_int() + 1;
357  } else if(auto tc = self_v.try_convert<team_callable>()) {
358  self = tc->get_value("side").as_int();
359  } else {
360  self = self_v.as_int();
361  }
362 
363  if(auto uc = other_v.try_convert<unit_callable>()) {
364  // For some obscure, bizarre reason, the unit callable returns a 0-indexed side. :|
365  other = uc->get_value("side").as_int() + 1;
366  } else if(auto tc = other_v.try_convert<team_callable>()) {
367  other = tc->get_value("side").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 } // namespace gamestate
400 
401 gamestate_function_symbol_table::gamestate_function_symbol_table(std::shared_ptr<function_symbol_table> parent) : function_symbol_table(parent) {
402  using namespace gamestate;
403  function_symbol_table& functions_table = *this;
405  DECLARE_WFL_FUNCTION(unit_at);
406  DECLARE_WFL_FUNCTION(resistance_on);
407  DECLARE_WFL_FUNCTION(defense_on);
408  DECLARE_WFL_FUNCTION(chance_to_hit);
409  DECLARE_WFL_FUNCTION(movement_cost);
410  DECLARE_WFL_FUNCTION(vision_cost);
411  DECLARE_WFL_FUNCTION(jamming_cost);
412  DECLARE_WFL_FUNCTION(adjacent_locs); // This is deliberately duplicated here; this form excludes off-map locations, while the core form does not
413  DECLARE_WFL_FUNCTION(locations_in_radius);
414  DECLARE_WFL_FUNCTION(enemy_of);
415 }
416 
417 }
int defense_modifier(const t_translation::terrain_code &terrain) const
Returns the defensive value of the indicated terrain.
Definition: movetype.hpp:267
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:260
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:1271
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:92
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
virtual const unit_map & units() const override
Definition: game_board.hpp:114
This class represents a single unit of a specific type.
Definition: unit.hpp:99
int movement_cost(const t_translation::terrain_code &terrain) const
Get the unit&#39;s movement cost on a particular terrain.
Definition: unit.hpp:1396
int as_int() const
Definition: variant.cpp:292
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:55
bool is_string() const
Definition: variant.hpp:66
virtual const gamemap & map() const override
Definition: game_board.hpp:109
A terrain string which is converted to a terrain is a string with 1 or 2 layers the layers are separa...
Definition: translation.hpp:50
unit_type_data unit_types
Definition: types.cpp:1529
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:263
A single unit type that the player may recruit.
Definition: types.hpp:42
t_translation::terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:309
const std::string & as_string() const
Definition: variant.cpp:319
int vision_cost(const t_translation::terrain_code &terrain) const
Get the unit&#39;s vision cost on a particular terrain.
Definition: unit.hpp:1406
int defense_modifier(const t_translation::terrain_code &terrain) const
The unit&#39;s defense on a given terrain.
Definition: unit.cpp:1637
const movetype & movement_type() const
Definition: types.hpp:177
game_board * gameboard
Definition: resources.cpp:20
std::array< map_location, 6 > adjacent_loc_array_t
Definition: location.hpp:170
Encapsulates the map of the game.
Definition: location.hpp:42
unit_iterator find(std::size_t id)
Definition: map.cpp:311
int jamming_cost(const t_translation::terrain_code &terrain) const
Get the unit&#39;s jamming cost on a particular terrain.
Definition: unit.hpp:1416
std::size_t i
Definition: function.cpp:933
const ter_data_cache & tdata() const
Definition: map.hpp:69
DEFINE_WFL_FUNCTION(adjacent_locs, 1, 1)
mock_party p
terrain_code read_terrain_code(utils::string_view str, const ter_layer filler)
Reads a single terrain from a string.
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:257
#define DECLARE_WFL_FUNCTION(name)
Declares a function name in the local function table functions_table.
Definition: function.hpp:47
static const unit_type & get_unit_type(const std::string &type_id)
Converts a string ID to a unit_type.
Definition: unit.cpp:245
bool is_null() const
Functions to test the type of the internal value.
Definition: variant.hpp:61
std::shared_ptr< T > try_convert() const
Definition: variant.hpp:89
Definition: contexts.hpp:42
gamestate_function_symbol_table(std::shared_ptr< function_symbol_table > parent=nullptr)
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&#39;s resistance against a given damage type.
Definition: unit.cpp:1680
static map_location::DIRECTION n
std::shared_ptr< T > convert_to() const
Definition: variant.hpp:99