The Battle for Wesnoth  1.15.9+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();
32 
33  std::vector<variant> v;
34  for(const map_location& adj : get_adjacent_tiles(loc)) {
35  if(resources::gameboard->map().on_board(adj)) {
36  v.emplace_back(std::make_shared<location_callable>(adj));
37  }
38  }
39 
40  return variant(v);
41 }
42 
43 DEFINE_WFL_FUNCTION(locations_in_radius, 2, 2)
44 {
45  const map_location loc = args()[0]->evaluate(variables, fdb).convert_to<location_callable>()->loc();
46 
47  int range = args()[1]->evaluate(variables, fdb).as_int();
48 
49  if(range < 0) {
50  return variant();
51  }
52 
53  if(!range) {
54  return variant(std::make_shared<location_callable>(loc));
55  }
56 
57  std::vector<map_location> res;
58 
59  get_tiles_in_radius(loc, range, res);
60 
61  std::vector<variant> v;
62  v.reserve(res.size() + 1);
63  v.emplace_back(std::make_shared<location_callable>(loc));
64 
65  for(std::size_t n = 0; n != res.size(); ++n) {
66  if(resources::gameboard->map().on_board(res[n])) {
67  v.emplace_back(std::make_shared<location_callable>(res[n]));
68  }
69  }
70 
71  return variant(v);
72 }
73 
75 {
76  const std::string type = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "get_unit_type:name")).as_string();
77 
78  const unit_type *ut = unit_types.find(type);
79  if(ut) {
80  return variant(std::make_shared<unit_type_callable>(*ut));
81  }
82 
83  return variant();
84 }
85 
86 DEFINE_WFL_FUNCTION(unit_at, 1, 1)
87 {
88  variant loc_var = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "unit_at:location"));
89  if(loc_var.is_null()) {
90  return variant();
91  }
92  auto loc = loc_var.convert_to<location_callable>();
94  if(i != resources::gameboard->units().end()) {
95  return variant(std::make_shared<unit_callable>(*i));
96  } else {
97  return variant();
98  }
99 }
100 
101 DEFINE_WFL_FUNCTION(defense_on, 2, 2)
102 {
103  variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "defense_on:unit"));
104  variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "defense_on:location"));
105  if(u.is_null() || loc_var.is_null()) {
106  return variant();
107  }
108 
109  auto u_call = u.try_convert<unit_callable>();
110  auto u_type = u.try_convert<unit_type_callable>();
111 
112  const auto& tdata = resources::gameboard->map().tdata();
114 
115  if(loc_var.is_string()) {
117  } else if(auto tc = loc_var.try_convert<terrain_callable>()) {
118  const std::string id = tc->get_value("id").as_string();
119  auto iter = std::find_if(tdata->map().begin(), tdata->map().end(), [id](const std::pair<t_translation::terrain_code, terrain_type>& p) {
120  return id == p.second.id();
121  });
122  if(iter == tdata->map().end()) {
123  return variant();
124  }
125  ter = iter->first;
126  } else if(auto loc = loc_var.try_convert<location_callable>()) {
127  if(!resources::gameboard->map().on_board(loc->loc())) {
128  return variant();
129  }
130  ter = resources::gameboard->map().get_terrain(loc->loc());
131  } else {
132  return variant();
133  }
134 
135  if(u_call) {
136  const unit& un = u_call->get_unit();
137 
138  return variant(100 - un.defense_modifier(ter));
139  }
140 
141  if(u_type) {
142  const unit_type& un = u_type->get_unit_type();
143 
144  return variant(100 - un.movement_type().defense_modifier(ter));
145  }
146 
147  return variant();
148 }
149 
150 DEFINE_WFL_FUNCTION(chance_to_hit, 2, 2)
151 {
152  variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "chance_to_hit:unit"));
153  variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "chance_to_hit:location"));
154  if(u.is_null() || loc_var.is_null()) {
155  return variant();
156  }
157 
158  auto u_call = u.try_convert<unit_callable>();
159  auto u_type = u.try_convert<unit_type_callable>();
160 
161  const auto& tdata = resources::gameboard->map().tdata();
163 
164  if(loc_var.is_string()) {
166  } else if(auto tc = loc_var.try_convert<terrain_callable>()) {
167  const std::string id = tc->get_value("id").as_string();
168  auto iter = std::find_if(tdata->map().begin(), tdata->map().end(), [id](const std::pair<t_translation::terrain_code, terrain_type>& p) {
169  return id == p.second.id();
170  });
171  if(iter == tdata->map().end()) {
172  return variant();
173  }
174  ter = iter->first;
175  } else if(auto loc = loc_var.try_convert<location_callable>()) {
176  if(!resources::gameboard->map().on_board(loc->loc())) {
177  return variant();
178  }
179  ter = resources::gameboard->map().get_terrain(loc->loc());
180  } else {
181  return variant();
182  }
183 
184  if(u_call) {
185  const unit& un = u_call->get_unit();
186 
187  return variant(un.defense_modifier((ter)));
188  }
189 
190  if(u_type) {
191  const unit_type& un = u_type->get_unit_type();
192 
193  return variant(un.movement_type().defense_modifier((ter)));
194  }
195 
196  return variant();
197 }
198 
199 DEFINE_WFL_FUNCTION(movement_cost, 2, 2)
200 {
201  variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "movement_cost:unit"));
202  variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 0, "movement_cost:location"));
203  if(u.is_null() || loc_var.is_null()) {
204  return variant();
205  }
206  //we can pass to this function either unit_callable or unit_type callable
207  auto u_call = u.try_convert<unit_callable>();
208  auto u_type = u.try_convert<unit_type_callable>();
209 
210  const auto& tdata = resources::gameboard->map().tdata();
212 
213  if(loc_var.is_string()) {
215  } else if(auto tc = loc_var.try_convert<terrain_callable>()) {
216  const std::string id = tc->get_value("id").as_string();
217  auto iter = std::find_if(tdata->map().begin(), tdata->map().end(), [id](const std::pair<t_translation::terrain_code, terrain_type>& p) {
218  return id == p.second.id();
219  });
220  if(iter == tdata->map().end()) {
221  return variant();
222  }
223  ter = iter->first;
224  } else if(auto loc = loc_var.try_convert<location_callable>()) {
225  if(!resources::gameboard->map().on_board(loc->loc())) {
226  return variant();
227  }
228  ter = resources::gameboard->map().get_terrain(loc->loc());
229  } else {
230  return variant();
231  }
232 
233  if(u_call) {
234  const unit& un = u_call->get_unit();
235 
236  return variant(un.movement_cost(ter));
237  }
238 
239  if(u_type) {
240  const unit_type& un = u_type->get_unit_type();
241 
242  return variant(un.movement_type().movement_cost(ter));
243  }
244 
245  return variant();
246 }
247 
248 DEFINE_WFL_FUNCTION(vision_cost, 2, 2)
249 {
250  variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "vision_cost:unit"));
251  variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 0, "vision_cost:location"));
252  if(u.is_null() || loc_var.is_null()) {
253  return variant();
254  }
255  //we can pass to this function either unit_callable or unit_type callable
256  auto u_call = u.try_convert<unit_callable>();
257  auto u_type = u.try_convert<unit_type_callable>();
258 
259  const auto& tdata = resources::gameboard->map().tdata();
261 
262  if(loc_var.is_string()) {
264  } else if(auto tc = loc_var.try_convert<terrain_callable>()) {
265  const std::string id = tc->get_value("id").as_string();
266  auto iter = std::find_if(tdata->map().begin(), tdata->map().end(), [id](const std::pair<t_translation::terrain_code, terrain_type>& p) {
267  return id == p.second.id();
268  });
269  if(iter == tdata->map().end()) {
270  return variant();
271  }
272  ter = iter->first;
273  } else if(auto loc = loc_var.try_convert<location_callable>()) {
274  if(!resources::gameboard->map().on_board(loc->loc())) {
275  return variant();
276  }
277  ter = resources::gameboard->map().get_terrain(loc->loc());
278  } else {
279  return variant();
280  }
281 
282  if(u_call) {
283  const unit& un = u_call->get_unit();
284 
285  return variant(un.vision_cost(ter));
286  }
287 
288  if(u_type) {
289  const unit_type& un = u_type->get_unit_type();
290 
291  return variant(un.movement_type().vision_cost(ter));
292  }
293 
294  return variant();
295 }
296 
297 DEFINE_WFL_FUNCTION(jamming_cost, 2, 2)
298 {
299  variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "jamming_cost:unit"));
300  variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 0, "jamming_cost:location"));
301  if(u.is_null() || loc_var.is_null()) {
302  return variant();
303  }
304  //we can pass to this function either unit_callable or unit_type callable
305  auto u_call = u.try_convert<unit_callable>();
306  auto u_type = u.try_convert<unit_type_callable>();
307 
308  const auto& tdata = resources::gameboard->map().tdata();
310 
311  if(loc_var.is_string()) {
313  } else if(auto tc = loc_var.try_convert<terrain_callable>()) {
314  const std::string id = tc->get_value("id").as_string();
315  auto iter = std::find_if(tdata->map().begin(), tdata->map().end(), [id](const std::pair<t_translation::terrain_code, terrain_type>& p) {
316  return id == p.second.id();
317  });
318  if(iter == tdata->map().end()) {
319  return variant();
320  }
321  ter = iter->first;
322  } else if(auto loc = loc_var.try_convert<location_callable>()) {
323  if(!resources::gameboard->map().on_board(loc->loc())) {
324  return variant();
325  }
326  ter = resources::gameboard->map().get_terrain(loc->loc());
327  } else {
328  return variant();
329  }
330 
331  if(u_call) {
332  const unit& un = u_call->get_unit();
333 
334  return variant(un.jamming_cost(ter));
335  }
336 
337  if(u_type) {
338  const unit_type& un = u_type->get_unit_type();
339 
340  return variant(un.movement_type().jamming_cost(ter));
341  }
342 
343  return variant();
344 }
345 
346 DEFINE_WFL_FUNCTION(enemy_of, 2, 2)
347 {
348  variant self_v = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "enemy_of:self"));
349  variant other_v = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "enemy_of:other"));
350  int self, other;
351 
352  if(auto uc = self_v.try_convert<unit_callable>()) {
353  // For some obscure, bizarre reason, the unit callable returns a 0-indexed side. :|
354  self = uc->get_value("side").as_int() + 1;
355  } else if(auto tc = self_v.try_convert<team_callable>()) {
356  self = tc->get_value("side").as_int();
357  } else {
358  self = self_v.as_int();
359  }
360 
361  if(auto uc = other_v.try_convert<unit_callable>()) {
362  // For some obscure, bizarre reason, the unit callable returns a 0-indexed side. :|
363  other = uc->get_value("side").as_int() + 1;
364  } else if(auto tc = other_v.try_convert<team_callable>()) {
365  other = tc->get_value("side").as_int();
366  } else {
367  other = other_v.as_int();
368  }
369 
370  int num_teams = resources::gameboard->teams().size();
371  if(self < 1 || self > num_teams || other < 1 || other > num_teams) {
372  return variant(0);
373  }
374  return variant(resources::gameboard->get_team(self).is_enemy(other) ? 1 : 0);
375 }
376 
377 DEFINE_WFL_FUNCTION(resistance_on, 3, 4)
378 {
379  variant u = args()[0]->evaluate(variables, add_debug_info(fdb, 0, "resistance_on:unit"));
380  variant loc_var = args()[1]->evaluate(variables, add_debug_info(fdb, 1, "resistance_on:location"));
381  if(u.is_null() || loc_var.is_null()) {
382  return variant();
383  }
384  std::string type = args()[2]->evaluate(variables, add_debug_info(fdb, 2, "resistance_on:type")).as_string();
385  bool attacker = args().size() > 3 ? args()[3]->evaluate(variables, add_debug_info(fdb, 3, "resistance_on:attacker")).as_bool() : false;
386  const map_location& loc = loc_var.convert_to<location_callable>()->loc();
387 
388  if(auto u_call = u.try_convert<unit_callable>()) {
389  const unit& un = u_call->get_unit();
390 
391  return variant(100 - un.resistance_against(type, attacker, loc));
392  }
393 
394  return variant();
395 }
396 
397 } // namespace gamestate
398 
399 gamestate_function_symbol_table::gamestate_function_symbol_table(std::shared_ptr<function_symbol_table> parent) : function_symbol_table(parent) {
400  using namespace gamestate;
401  function_symbol_table& functions_table = *this;
403  DECLARE_WFL_FUNCTION(unit_at);
404  DECLARE_WFL_FUNCTION(resistance_on);
405  DECLARE_WFL_FUNCTION(defense_on);
406  DECLARE_WFL_FUNCTION(chance_to_hit);
407  DECLARE_WFL_FUNCTION(movement_cost);
408  DECLARE_WFL_FUNCTION(vision_cost);
409  DECLARE_WFL_FUNCTION(jamming_cost);
410  DECLARE_WFL_FUNCTION(adjacent_locs); // This is deliberately duplicated here; this form excludes off-map locations, while the core form does not
411  DECLARE_WFL_FUNCTION(locations_in_radius);
412  DECLARE_WFL_FUNCTION(enemy_of);
413 }
414 
415 }
int defense_modifier(const t_translation::terrain_code &terrain) const
Returns the defensive value of the indicated terrain.
Definition: movetype.hpp:291
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
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:1222
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:82
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:109
This class represents a single unit of a specific type.
Definition: unit.hpp:120
int movement_cost(const t_translation::terrain_code &terrain) const
Get the unit&#39;s movement cost on a particular terrain.
Definition: unit.hpp:1431
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:99
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
unit_type_data unit_types
Definition: types.cpp:1441
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
A single unit type that the player may recruit.
Definition: types.hpp:44
terrain_code read_terrain_code(std::string_view str, const ter_layer filler)
Reads a single terrain from a string.
t_translation::terrain_code get_terrain(const map_location &loc) const
Looks up terrain at a particular location.
Definition: map.cpp:314
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:1441
int defense_modifier(const t_translation::terrain_code &terrain) const
The unit&#39;s defense on a given terrain.
Definition: unit.cpp:1596
const movetype & movement_type() const
Definition: types.hpp:182
game_board * gameboard
Definition: resources.cpp:20
const std::shared_ptr< terrain_type_data > & tdata() const
Definition: map.hpp:66
Encapsulates the map of the game.
Definition: location.hpp:37
unit_iterator find(std::size_t id)
Definition: map.cpp:309
int jamming_cost(const t_translation::terrain_code &terrain) const
Get the unit&#39;s jamming cost on a particular terrain.
Definition: unit.hpp:1451
std::size_t i
Definition: function.cpp:934
DEFINE_WFL_FUNCTION(adjacent_locs, 1, 1)
mock_party p
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
#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:208
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:43
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:1639
static map_location::DIRECTION n
std::shared_ptr< T > convert_to() const
Definition: variant.hpp:99