The Battle for Wesnoth  1.19.9+dev
callable_objects.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2025
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 
16 #include "formula/callable_objects.hpp"
17 
18 #include "config.hpp"
19 #include "formula/function.hpp"
20 #include "map/map.hpp"
21 #include "display_context.hpp"
22 #include "team.hpp"
23 #include "units/attack_type.hpp"
25 #include "units/unit.hpp"
26 #include "units/types.hpp"
27 #include "log.hpp"
28 #include "recall_list_manager.hpp"
29 #include "deprecation.hpp"
30 #include "game_board.hpp"
31 #include "game_version.hpp"
32 #include "resources.hpp"
33 #include "tod_manager.hpp"
34 #include "play_controller.hpp"
35 #include "game_events/pump.hpp"
36 
37 static lg::log_domain log_scripting_formula("scripting/formula");
38 #define LOG_SF LOG_STREAM(info, log_scripting_formula)
39 #define ERR_SF LOG_STREAM(err, log_scripting_formula)
40 
41 namespace wfl
42 {
43 
44 variant location_callable::get_value(const std::string& key) const
45 {
46  if(key == "x") {
47  return variant(loc_.wml_x());
48  } else if(key == "y") {
49  return variant(loc_.wml_y());
50  }
51 
52  return variant();
53 }
54 
56 {
57  add_input(inputs, "x");
58  add_input(inputs, "y");
59 }
60 
62 {
63  const location_callable* loc_callable = dynamic_cast<const location_callable*>(callable);
64  if(loc_callable == nullptr) {
65  return formula_callable::do_compare(callable);
66  }
67 
68  const map_location& other_loc = loc_callable->loc();
69  return loc_.do_compare(other_loc);
70 }
71 
72 void location_callable::serialize_to_string(std::string& str) const
73 {
74  std::ostringstream s;
75  s << "loc(" << (loc_.wml_x()) << "," << (loc_.wml_y()) << ")";
76  str += s.str();
77 }
78 
79 attack_type_callable::attack_type_callable(const attack_type& attack) : att_(attack.shared_from_this())
80 {
82 }
83 
84 variant attack_type_callable::get_value(const std::string& key) const
85 {
86  if(key == "id" || key == "name") {
87  return variant(att_->id());
88  } else if(key == "description") {
89  return variant(att_->name());
90  } else if(key == "base_type") {
91  return variant(att_->type());
92  } else if(key == "type") {
93  return variant(att_->effective_damage_type().first);
94  } else if(key == "icon") {
95  return variant(att_->icon());
96  } else if(key == "range") {
97  return variant(att_->range());
98  } else if(key == "alignment") {
99  return variant(att_->alignment_str());
100  } else if(key == "damage") {
101  return variant(att_->damage());
102  } else if(key == "number_of_attacks" || key == "number" || key == "num_attacks" || key == "attacks") {
103  return variant(att_->num_attacks());
104  } else if(key == "attack_weight") {
105  return variant(att_->attack_weight(), variant::DECIMAL_VARIANT);
106  } else if(key == "defense_weight") {
107  return variant(att_->defense_weight(), variant::DECIMAL_VARIANT);
108  } else if(key == "accuracy") {
109  return variant(att_->accuracy());
110  } else if(key == "parry") {
111  return variant(att_->parry());
112  } else if(key == "movement_used") {
113  return variant(att_->movement_used());
114  } else if(key == "attacks_used") {
115  return variant(att_->attacks_used());
116  } else if(key == "min_range") {
117  return variant(att_->min_range());
118  } else if(key == "max_range") {
119  return variant(att_->max_range());
120  } else if(key == "specials" || key == "special") {
121  std::vector<variant> res;
122 
123  for(const auto [_, special_cfg] : att_->specials().all_children_view()) {
124  if(!special_cfg["id"].empty()) {
125  res.emplace_back(special_cfg["id"].str());
126  }
127  }
128  return variant(res);
129  }
130 
131  return variant();
132 }
133 
135 {
136  add_input(inputs, "name");
137  add_input(inputs, "type");
138  add_input(inputs, "base_type");
139  add_input(inputs, "description");
140  add_input(inputs, "icon");
141  add_input(inputs, "range");
142  add_input(inputs, "alignment");
143  add_input(inputs, "damage");
144  add_input(inputs, "number");
145  add_input(inputs, "accuracy");
146  add_input(inputs, "parry");
147  add_input(inputs, "movement_used");
148  add_input(inputs, "attacks_used");
149  add_input(inputs, "attack_weight");
150  add_input(inputs, "defense_weight");
151  add_input(inputs, "min_range");
152  add_input(inputs, "max_range");
153  add_input(inputs, "specials");
154 }
155 
157 {
158  const attack_type_callable* att_callable = dynamic_cast<const attack_type_callable*>(callable);
159  if(att_callable == nullptr) {
160  return formula_callable::do_compare(callable);
161  }
162 
163  if(att_->damage() != att_callable->att_->damage()) {
164  return att_->damage() - att_callable->att_->damage();
165  }
166 
167  if(att_->num_attacks() != att_callable->att_->num_attacks()) {
168  return att_->num_attacks() - att_callable->att_->num_attacks();
169  }
170 
171  if(att_->id() != att_callable->att_->id()) {
172  return att_->id().compare(att_callable->att_->id());
173  }
174 
175  if(att_->type() != att_callable->att_->type()) {
176  return att_->type().compare(att_callable->att_->type());
177  }
178 
179  if(att_->range() != att_callable->att_->range()) {
180  return att_->range().compare(att_callable->att_->range());
181  }
182 
183  if(att_->alignment_str() != att_callable->att_->alignment_str()) {
184  return att_->alignment_str().compare(att_callable->att_->alignment_str());
185  }
186 
187  const auto self_specials = att_->specials().all_children_range();
188  const auto other_specials = att_callable->att_->specials().all_children_range();
189  if(self_specials.size() != other_specials.size()) {
190  return self_specials.size() < other_specials.size() ? -1 : 1;
191  }
192  for(std::size_t i = 0; i < self_specials.size(); ++i) {
193  const auto& s = self_specials[i].cfg["id"];
194  const auto& o = other_specials[i].cfg["id"];
195  if(s != o) {
196  return s.str().compare(o.str());
197  }
198  }
199 
200  return 0;
201 }
202 
204 {
205  type_ = UNIT_C;
206 }
207 
208 variant unit_callable::get_value(const std::string& key) const
209 {
210 
211  if(key == "x") {
213  return variant();
214  }
215 
216  return variant(loc_.wml_x());
217  } else if(key == "y") {
219  return variant();
220  }
221 
222  return variant(loc_.wml_y());
223  } else if(key == "loc") {
225  return variant();
226  }
227 
228  return variant(std::make_shared<location_callable>(loc_));
229  } else if(key == "terrain") {
231  return variant();
232  }
233  return variant(std::make_shared<terrain_callable>(*resources::gameboard, loc_));
234  } else if(key == "id") {
235  return variant(u_.id());
236  } else if(key == "type") {
237  return variant(u_.type_id());
238  } else if(key == "name") {
239  return variant(u_.name());
240  } else if(key == "usage") {
241  return variant(u_.usage());
242  } else if(key == "leader" || key == "canrecruit") {
243  return variant(u_.can_recruit());
244  } else if(key == "undead") {
245  return variant(u_.get_state("not_living") ? 1 : 0);
246  } else if(key == "attacks") {
247  std::vector<variant> res;
248  for(const attack_type& att : u_.attacks()) {
249  res.emplace_back(std::make_shared<attack_type_callable>(att));
250  }
251 
252  return variant(res);
253  } else if(key == "abilities") {
255  } else if(key == "hitpoints") {
256  return variant(u_.hitpoints());
257  } else if(key == "max_hitpoints") {
258  return variant(u_.max_hitpoints());
259  } else if(key == "experience") {
260  return variant(u_.experience());
261  } else if(key == "max_experience") {
262  return variant(u_.max_experience());
263  } else if(key == "level" || key == "full") {
264  // This allows writing "upkeep == full"
265  return variant(u_.level());
266  } else if(key == "total_movement" || key == "max_moves") {
267  return variant(u_.total_movement());
268  } else if(key == "movement_left" || key == "moves") {
269  return variant(u_.movement_left());
270  } else if(key == "attacks_left") {
271  return variant(u_.attacks_left());
272  } else if(key == "max_attacks") {
273  return variant(u_.max_attacks());
274  } else if(key == "traits") {
276  } else if(key == "advancements_taken") {
278  } else if(key == "objects") {
280  } else if(key == "traits_count") {
281  return variant(u_.traits_count());
282  } else if(key == "advancements_taken_count") {
283  return variant(u_.advancements_count());
284  } else if(key == "objects_count") {
285  return variant(u_.objects_count());
286  } else if(key == "extra_recruit") {
288  } else if(key == "advances_to") {
290  } else if(key == "states" || key == "status") {
292  } else if(key == "side_number") {
293  return variant(u_.side());
294  } else if(key == "cost") {
295  return variant(u_.cost());
296  } else if(key == "upkeep") {
297  return variant(u_.upkeep());
298  } else if(key == "loyal") {
299  // So we can write "upkeep == loyal"
300  return variant(0);
301  } else if(key == "hidden") {
302  return variant(u_.get_hidden());
303  } else if(key == "petrified") {
304  return variant(u_.incapacitated());
305  } else if(key == "resting") {
306  return variant(u_.resting());
307  } else if(key == "role") {
308  return variant(u_.get_role());
309  } else if(key == "race") {
310  return variant(u_.race()->id());
311  } else if(key == "gender") {
312  return variant(gender_string(u_.gender()));
313  } else if(key == "variation") {
314  return variant(u_.variation());
315  } else if(key == "zoc") {
316  return variant(u_.get_emit_zoc());
317  } else if(key == "alignment") {
319  } else if(key == "facing") {
321  } else if(key == "resistance" || key == "movement_cost" || key == "vision_cost" || key == "jamming_cost" || key == "defense") {
322  const auto& mt = u_.movement_type();
323  config cfg;
324  bool needs_flip = false;
325  if(key == "resistance") {
326  mt.get_resistances().write(cfg);
327  needs_flip = true;
328  } else if(key == "movement_cost") {
329  mt.get_movement().write(cfg);
330  } else if(key == "vision_cost") {
331  mt.get_vision().write(cfg);
332  } else if(key == "jamming_cost") {
333  mt.get_jamming().write(cfg);
334  } else if(key == "defense") {
335  mt.get_defense().write(cfg);
336  needs_flip = true;
337  }
338  std::map<variant, variant> res;
339  for(const auto& [key, value] : cfg.attribute_range()) {
340  int val = value.to_int();
341  if(needs_flip) {
342  val = 100 - val;
343  }
344  res.emplace(variant(key), variant(val));
345  }
346 
347  return variant(res);
348  } else if(key == "flying") {
349  return variant(u_.is_flying());
350  } else if(key == "fearless") {
351  return variant(u_.is_fearless());
352  } else if(key == "healthy") {
353  return variant(u_.is_healthy());
354  } else if(key == "vars") {
357  }
358 
359  return variant();
360  } else if(key == "wml_vars") {
361  return variant(std::make_shared<config_callable>(u_.variables()));
362  } else if(key == "n" || key == "s" || key == "ne" || key == "se" || key == "nw" || key == "sw" ||
363  key == "lawful" || key == "neutral" || key == "chaotic" || key == "liminal" ||
364  key == "male" || key == "female")
365  {
366  return variant(key);
367  }
368 
369  return variant();
370 }
371 
373 {
374  add_input(inputs, "x");
375  add_input(inputs, "y");
376  add_input(inputs, "loc");
377  add_input(inputs, "terrain");
378  add_input(inputs, "id");
379  add_input(inputs, "type");
380  add_input(inputs, "name");
381  add_input(inputs, "canrecruit");
382  add_input(inputs, "undead");
383  add_input(inputs, "traits");
384  add_input(inputs, "advancements_taken");
385  add_input(inputs, "objects");
386  add_input(inputs, "traits_count");
387  add_input(inputs, "advancements_taken_count");
388  add_input(inputs, "objects_count");
389  add_input(inputs, "attacks");
390  add_input(inputs, "abilities");
391  add_input(inputs, "hitpoints");
392  add_input(inputs, "max_hitpoints");
393  add_input(inputs, "experience");
394  add_input(inputs, "max_experience");
395  add_input(inputs, "level");
396  add_input(inputs, "moves");
397  add_input(inputs, "max_moves");
398  add_input(inputs, "attacks_left");
399  add_input(inputs, "max_attacks");
400  add_input(inputs, "side_number");
401  add_input(inputs, "extra_recruit");
402  add_input(inputs, "advances_to");
403  add_input(inputs, "status");
404  add_input(inputs, "cost");
405  add_input(inputs, "usage");
406  add_input(inputs, "upkeep");
407  add_input(inputs, "hidden");
408  add_input(inputs, "petrified");
409  add_input(inputs, "resting");
410  add_input(inputs, "role");
411  add_input(inputs, "race");
412  add_input(inputs, "gender");
413  add_input(inputs, "variation");
414  add_input(inputs, "zoc");
415  add_input(inputs, "alignment");
416  add_input(inputs, "facing");
417  add_input(inputs, "resistance");
418  add_input(inputs, "movement_cost");
419  add_input(inputs, "vision_cost");
420  add_input(inputs, "jamming_cost");
421  add_input(inputs, "defense");
422  add_input(inputs, "flying");
423  add_input(inputs, "fearless");
424  add_input(inputs, "healthy");
425  add_input(inputs, "vars");
426  add_input(inputs, "wml_vars");
427 }
428 
429 int unit_callable::do_compare(const formula_callable* callable) const
430 {
431  const unit_callable* u_callable = dynamic_cast<const unit_callable*>(callable);
432  if(u_callable == nullptr) {
433  return formula_callable::do_compare(callable);
434  }
435 
436  return u_.underlying_id() - u_callable->u_.underlying_id();
437 }
438 
439 variant unit_type_callable::get_value(const std::string& key) const
440 {
441  if(key == "id") {
442  return variant(u_.id());
443  } else if(key == "type") {
444  return variant(u_.type_name());
445  } else if(key == "alignment") {
447  } else if(key == "race") {
448  return variant(u_.race_id());
449  } else if(key == "abilities") {
451  } else if(key == "traits") {
452  std::vector<variant> res;
453  for(const auto& config : u_.possible_traits()) {
454  res.emplace_back(config["id"].str());
455  }
456 
457  return variant(res);
458  } else if(key == "attacks") {
459  std::vector<variant> res;
460  for(const attack_type& att : u_.attacks()) {
461  res.emplace_back(std::make_shared<attack_type_callable>(att));
462  }
463 
464  return variant(res);
465  } else if(key == "hitpoints" || key == "max_hitpoints") {
466  return variant(u_.hitpoints());
467  } else if(key == "experience" || key == "max_experience") {
468  return variant(u_.experience_needed(true));
469  } else if(key == "level") {
470  return variant(u_.level());
471  } else if(key == "total_movement" || key == "max_moves" || key == "moves") {
472  return variant(u_.movement());
473  } else if(key == "undead") {
474  return variant(u_.musthave_status("unpoisonable") && u_.musthave_status("undrainable") && u_.musthave_status("unplagueable"));
475  } else if(key == "unpoisonable") {
476  return variant(u_.musthave_status("unpoisonable"));
477  } else if(key == "unslowable") {
478  return variant(u_.musthave_status("unslowable"));
479  } else if(key == "unpetrifiable") {
480  return variant(u_.musthave_status("unpetrifiable"));
481  } else if(key == "undrainable") {
482  return variant(u_.musthave_status("undrainable"));
483  } else if(key == "unplagueable") {
484  return variant(u_.musthave_status("unplagueable"));
485  } else if(key == "cost") {
486  return variant(u_.cost());
487  } else if(key == "recall_cost") {
488  return variant(u_.recall_cost());
489  } else if(key == "usage") {
490  return variant(u_.usage());
491  }
492 
493  return variant();
494 }
495 
497 {
498  add_input(inputs, "id");
499  add_input(inputs, "type");
500  add_input(inputs, "race");
501  add_input(inputs, "alignment");
502  add_input(inputs, "abilities");
503  add_input(inputs, "traits");
504  add_input(inputs, "attacks");
505  add_input(inputs, "hitpoints");
506  add_input(inputs, "experience");
507  add_input(inputs, "level");
508  add_input(inputs, "total_movement");
509  add_input(inputs, "undead");
510  add_input(inputs, "cost");
511  add_input(inputs, "recall_cost");
512  add_input(inputs, "usage");
513 }
514 
516 {
517  const unit_type_callable* u_callable = dynamic_cast<const unit_type_callable*>(callable);
518  if(u_callable == nullptr) {
519  return formula_callable::do_compare(callable);
520  }
521 
522  return u_.id().compare(u_callable->u_.id());
523 }
524 
526 #ifdef USING_BOOST_VARIANT
527  : public boost::static_visitor<variant>
528 #endif
529 {
530  variant operator()(bool b) const { return variant(b ? 1 : 0); }
531  variant operator()(int i) const { return variant(i); }
532  variant operator()(unsigned long long i) const { return variant(i); }
533  variant operator()(double i) const { return variant(i * 1000, variant::DECIMAL_VARIANT); }
534  // TODO: Should comma-separated lists of stuff be returned as a list?
535  // The challenge is to distinguish them from ordinary strings that happen to contain a comma
536  // (or should we assume that such strings will be translatable?).
537  variant operator()(const std::string& s) const { return variant(s); }
538  variant operator()(const t_string& s) const { return variant(s.str()); }
539  variant operator()(utils::monostate) const { return variant(); }
540 };
541 
542 variant config_callable::get_value(const std::string& key) const
543 {
544  if(cfg_.has_attribute(key)) {
545  return cfg_[key].apply_visitor(fai_variant_visitor());
546  } else if(cfg_.has_child(key)) {
547  std::vector<variant> result;
548  for(const auto& child : cfg_.child_range(key)) {
549  result.emplace_back(std::make_shared<config_callable>(child));
550  }
551 
552  return variant(result);
553  } else if(key == "__all_children") {
554  std::vector<variant> result;
555  for(const auto [child_key, child_cfg] : cfg_.all_children_view()) {
556  const variant cfg_child(std::make_shared<config_callable>(child_cfg));
557  const variant kv(std::make_shared<key_value_pair>(variant(child_key), cfg_child));
558  result.push_back(kv);
559  }
560 
561  return variant(result);
562  } else if(key == "__children") {
563  std::map<std::string, std::vector<variant>> build;
564  for(const auto [child_key, child_cfg] : cfg_.all_children_view()) {
565  const variant cfg_child(std::make_shared<config_callable>(child_cfg));
566  build[child_key].push_back(cfg_child);
567  }
568 
569  std::map<variant,variant> result;
570  for(auto& p : build) {
571  result[variant(p.first)] = variant(p.second);
572  }
573 
574  return variant(result);
575  } else if(key == "__attributes") {
576  std::map<variant,variant> result;
577  for(const auto& [key, value] : cfg_.attribute_range()) {
578  result[variant(key)] = value.apply_visitor(fai_variant_visitor());
579  }
580 
581  return variant(result);
582  }
583 
584  return variant();
585 }
586 
588 {
589  add_input(inputs, "__all_children");
590  add_input(inputs, "__children");
591  add_input(inputs, "__attributes");
592 
593  for(const auto& [key, _] : cfg_.attribute_range()) {
594  if(key.find_first_not_of(formula::id_chars) != std::string::npos) {
595  add_input(inputs, key);
596  }
597  }
598 }
599 
601 {
602  const config_callable* cfg_callable = dynamic_cast<const config_callable*>(callable);
603  if(cfg_callable == nullptr) {
604  return formula_callable::do_compare(callable);
605  }
606 
607  if(cfg_ == cfg_callable->get_config()) {
608  return 0;
609  }
610 
611  return cfg_.hash().compare(cfg_callable->get_config().hash());
612 }
613 
614 terrain_callable::terrain_callable(const display_context& dc, const map_location& loc) : loc_(loc), t_(dc.map().get_terrain_info(loc)), owner_(dc.village_owner(loc))
615 {
616  type_ = TERRAIN_C;
617 }
618 
619 variant terrain_callable::get_value(const std::string& key) const
620 {
621  if(key == "x") {
622  return variant(loc_.wml_x());
623  } else if(key == "y") {
624  return variant(loc_.wml_y());
625  } else if(key == "loc") {
626  return variant(std::make_shared<location_callable>(loc_));
627  } else if(key == "id") {
628  return variant(std::string(t_.id()));
629  } else if(key == "name") {
630  return variant(t_.name());
631  } else if(key == "editor_name") {
632  return variant(t_.editor_name());
633  } else if(key == "description") {
634  return variant(t_.description());
635  } else if(key == "icon") {
636  return variant(t_.icon_image());
637  } else if(key == "light") {
638  return variant(t_.light_bonus(0));
639  } else if(key == "village") {
640  return variant(t_.is_village());
641  } else if(key == "castle") {
642  return variant(t_.is_castle());
643  } else if(key == "keep") {
644  return variant(t_.is_keep());
645  } else if(key == "healing") {
646  return variant(t_.gives_healing());
647  } else if(key == "owner_side") {
648  return variant(owner_);
649  }
650 
651  return variant();
652 }
653 
655 {
656  add_input(inputs, "x");
657  add_input(inputs, "y");
658  add_input(inputs, "loc");
659  add_input(inputs, "id");
660  add_input(inputs, "name");
661  add_input(inputs, "editor_name");
662  add_input(inputs, "description");
663  add_input(inputs, "icon");
664  add_input(inputs, "light");
665  add_input(inputs, "village");
666  add_input(inputs, "castle");
667  add_input(inputs, "keep");
668  add_input(inputs, "healing");
669  add_input(inputs, "owner_side");
670 }
671 
673 {
674  const terrain_callable* terr_callable = dynamic_cast<const terrain_callable*>(callable);
675  if(terr_callable == nullptr) {
676  return formula_callable::do_compare(callable);
677  }
678 
679  const map_location& other_loc = terr_callable->loc_;
680  return loc_.do_compare(other_loc);
681 }
682 
684  return board_.map();
685 }
686 
688 {
689  add_input(inputs, "w");
690  add_input(inputs, "h");
691 }
692 
693 variant gamemap_callable::get_value(const std::string& key) const
694 {
695  if(key == "terrain") {
696  int w = get_gamemap().w();
697  int h = get_gamemap().h();
698 
699  std::vector<variant> vars;
700  for(int i = 0; i < w; i++) {
701  for(int j = 0; j < h; j++) {
702  const map_location loc(i, j);
703  vars.emplace_back(std::make_shared<terrain_callable>(board_, loc));
704  }
705  }
706 
707  return variant(vars);
708  } else if(key == "gamemap") {
709  int w = get_gamemap().w();
710  int h = get_gamemap().h();
711 
712  std::map<variant, variant> vars;
713  for(int i = 0; i < w; i++) {
714  for(int j = 0; j < h; j++) {
715  const map_location loc(i, j);
716  vars.emplace(std::make_shared<location_callable>(loc), std::make_shared<terrain_callable>(board_, loc));
717  }
718  }
719 
720  return variant(vars);
721  } else if(key == "w") {
722  return variant(get_gamemap().w());
723  } else if(key == "h") {
724  return variant(get_gamemap().h());
725  } else {
726  return variant();
727  }
728 }
729 
731 {
732  add_input(inputs, "side_number");
733  add_input(inputs, "id");
734  add_input(inputs, "gold");
735  add_input(inputs, "start_gold");
736  add_input(inputs, "base_income");
737  add_input(inputs, "total_income");
738  add_input(inputs, "village_gold");
739  add_input(inputs, "village_support");
740  add_input(inputs, "recall_cost");
741  add_input(inputs, "is_human");
742  add_input(inputs, "is_ai");
743  add_input(inputs, "is_network");
744  add_input(inputs, "fog");
745  add_input(inputs, "shroud");
746  add_input(inputs, "hidden");
747  add_input(inputs, "flag");
748  add_input(inputs, "flag_icon");
749  add_input(inputs, "team_name");
750  add_input(inputs, "faction");
751  add_input(inputs, "faction_name");
752  add_input(inputs, "color");
753  add_input(inputs, "share_vision");
754  add_input(inputs, "carryover_bonus");
755  add_input(inputs, "carryover_percentage");
756  add_input(inputs, "carryover_add");
757  add_input(inputs, "recruit");
758  add_input(inputs, "wml_vars");
759 }
760 
761 variant team_callable::get_value(const std::string& key) const
762 {
763  if(key == "side") {
764  deprecated_message("team.side", DEP_LEVEL::INDEFINITE, version_info("1.17"), "Use side_number instead.");
765  return variant(team_.side());
766  } else if(key == "side_number") {
767  return variant(team_.side());
768  } else if(key == "id") {
769  return variant(team_.save_id());
770  } else if(key == "save_id") {
771  return variant(team_.save_id());
772  } else if(key == "gold") {
773  return variant(team_.gold());
774  } else if(key == "start_gold") {
775  return variant(team_.start_gold());
776  } else if(key == "base_income") {
777  return variant(team_.base_income());
778  } else if(key == "total_income") {
779  return variant(team_.total_income());
780  } else if(key == "village_gold") {
781  return variant(team_.village_gold());
782  } else if(key == "village_support") {
783  return variant(team_.village_support());
784  } else if(key == "recall_cost") {
785  return variant(team_.recall_cost());
786  } else if(key == "is_human") {
787  return variant(team_.is_local_human());
788  } else if(key == "is_ai") {
789  return variant(team_.is_local_ai());
790  } else if(key == "is_network") {
791  return variant(team_.is_network());
792  } else if(key == "fog") {
793  return variant(team_.uses_fog());
794  } else if(key == "shroud") {
795  return variant(team_.uses_shroud());
796  } else if(key == "hidden") {
797  return variant(team_.hidden());
798  } else if(key == "flag") {
799  return variant(team_.flag());
800  } else if(key == "flag_icon") {
801  return variant(team_.flag_icon());
802  } else if(key == "team_name") {
803  return variant(team_.team_name());
804  } else if(key == "faction") {
805  return variant(team_.faction());
806  } else if(key == "faction_name") {
807  return variant(team_.faction_name());
808  } else if(key == "color") {
809  return variant(team_.color());
810  } else if(key == "share_vision") {
812  } else if(key == "carryover_bonus") {
814  } else if(key == "carryover_percentage") {
816  } else if(key == "carryover_add") {
817  return variant(team_.carryover_add());
818  } else if(key == "recruit") {
819  std::vector<variant> result;
820  for(const auto& recruit : team_.recruits()) {
821  result.emplace_back(recruit);
822  }
823  return variant(result);
824  } else if(key == "recall") {
825  std::vector<variant> result;
826  for(const auto& u : team_.recall_list()) {
827  result.push_back(std::make_shared<unit_callable>(*u));
828  }
829  return variant(result);
830  } else if(key == "wml_vars") {
831  return variant(std::make_shared<config_callable>(team_.variables()));
832  }
833 
834  return variant();
835 }
836 
837 variant set_var_callable::get_value(const std::string& key) const
838 {
839  if(key == "key") {
840  return variant(key_);
841  } else if(key == "value") {
842  return value_;
843  }
844 
845  return variant();
846 }
847 
849 {
850  add_input(inputs, "key");
851  add_input(inputs, "value");
852 }
853 
855 {
856  //if(infinite_loop_guardian_.set_var_check()) {
857  if(auto obj = ctxt.try_convert<formula_callable>()) {
858  LOG_SF << "Setting variable: " << key_ << " -> " << value_.to_debug_string();
859  obj->mutate_value(key_, value_);
860  return variant(true);
861  }
862  //}
863  //too many calls in a row - possible infinite loop
864  ERR_SF << "ERROR #" << 5001 << " while executing 'set_var' formula function";
865 
866  return variant(std::make_shared<safe_call_result>(fake_ptr(), 5001));
867 }
868 
869 variant safe_call_callable::get_value(const std::string& key) const
870 {
871  if(key == "main") {
872  return variant(main_);
873  } else if(key == "backup") {
874  return variant(backup_);
875  }
876 
877  return variant();
878 }
879 
881 {
882  add_input(inputs, "main");
883  add_input(inputs, "backup");
884 }
885 
887 {
888  variant res;
889  if(auto action = main_.try_convert<action_callable>()) {
890  res = action->execute_self(ctxt);
891  }
892 
893  if(res.try_convert<safe_call_result>()) {
894  /* If we have safe_call formula and either an error occurred, or the current action
895  * was not recognized, then evaluate backup formula from safe_call and execute it
896  * during the next loop
897  */
898 
899  map_formula_callable callable(ctxt.as_callable());
900  callable.add("error", res);
901 
902  /* Store the result in safe_call_callable in case we would like to display it to the user,
903  * for example if this formula was executed from the commandline.
904  */
905  backup_ = get_backup()->evaluate(callable);
906  ctxt.execute_variant(backup_);
907  }
908  return variant(true);
909 }
910 
911 variant safe_call_result::get_value(const std::string& key) const
912 {
913  if(key == "status") {
914  return variant(status_);
915  } else if(key == "object") {
916  if(failed_callable_) {
917  return variant(failed_callable_);
918  }
919 
920  return variant();
921  } else if(key == "current_loc" && current_unit_location_ != map_location()) {
922  return variant(std::make_shared<location_callable>(current_unit_location_));
923  }
924 
925  return variant();
926 }
927 
929 {
930  add_input(inputs, "status");
931  add_input(inputs, "object");
932 
934  add_input(inputs, "current_loc");
935  }
936 }
937 
939 {
940  add_input(inputs, "turn_number");
941  add_input(inputs, "time_of_day");
942  add_input(inputs, "side_number");
943  add_input(inputs, "sides");
944  add_input(inputs, "units");
945  add_input(inputs, "map");
946 }
947 
948 variant gamestate_callable::get_value(const std::string &key) const
949 {
950  if(key == "turn_number") {
951  return variant(resources::tod_manager->turn());
952  } else if(key == "time_of_day") {
953  return variant(resources::tod_manager->get_time_of_day().id);
954  } else if(key == "side_number") {
955  return variant(resources::controller->current_side());
956  } else if(key == "sides") {
957  std::vector<variant> vars;
958  for(const auto& team : resources::gameboard->teams()) {
959  vars.emplace_back(std::make_shared<team_callable>(team));
960  }
961  return variant(vars);
962  } else if(key == "units") {
963  std::vector<variant> vars;
964  for(const auto& unit : resources::gameboard->units()) {
965  vars.emplace_back(std::make_shared<unit_callable>(unit));
966  }
967  return variant(vars);
968  } else if(key == "map") {
969  return variant(std::make_shared<gamemap_callable>(*resources::gameboard));
970  }
971 
972  return variant();
973 }
974 
976 {
977  add_input(inputs, "event");
978  add_input(inputs, "event_id");
979  add_input(inputs, "event_data");
980  add_input(inputs, "loc");
981  add_input(inputs, "unit");
982  add_input(inputs, "weapon");
983  add_input(inputs, "second_loc");
984  add_input(inputs, "second_unit");
985  add_input(inputs, "second_weapon");
986 }
987 
988 variant event_callable::get_value(const std::string &key) const
989 {
990  if(key == "event") {
991  return variant(event_info.name);
992  } else if(key == "event_id") {
993  return variant(event_info.id);
994  } else if(key == "loc") {
995  return variant(std::make_shared<location_callable>(event_info.loc1));
996  } else if(key == "second_loc") {
997  return variant(std::make_shared<location_callable>(event_info.loc2));
998  } else if(key == "event_data") {
999  return variant(std::make_shared<config_callable>(event_info.data));
1000  } else if(key == "unit") {
1001  if(auto u1 = event_info.loc1.get_unit()) {
1002  return variant(std::make_shared<unit_callable>(*u1));
1003  }
1004  } else if(key == "second_unit") {
1005  if(auto u2 = event_info.loc2.get_unit()) {
1006  return variant(std::make_shared<unit_callable>(*u2));
1007  }
1008  } else if(key == "weapon") {
1009  if(event_info.data.has_child("first")) {
1010  first_weapon = std::make_shared<attack_type>(event_info.data.mandatory_child("first"));
1011  return variant(std::make_shared<attack_type_callable>(*first_weapon));
1012  }
1013  } else if(key == "second_weapon") {
1014  if(event_info.data.has_child("second")) {
1015  second_weapon = std::make_shared<attack_type>(event_info.data.mandatory_child("second"));
1016  return variant(std::make_shared<attack_type_callable>(*second_weapon));
1017  }
1018  }
1019 
1020  return variant();
1021 }
1022 
1024 {
1025  add_input(inputs, "red");
1026  add_input(inputs, "green");
1027  add_input(inputs, "blue");
1028  add_input(inputs, "alpha");
1029 }
1030 
1031 variant color_callable::get_value(const std::string& key) const
1032 {
1033  if(key == "red") {
1034  return variant(clr_.r);
1035  } else if(key == "green") {
1036  return variant(clr_.g);
1037  } else if(key == "blue") {
1038  return variant(clr_.b);
1039  } else if(key == "alpha") {
1040  return variant(clr_.a);
1041  }
1042 
1043  return variant();
1044 }
1045 
1046 } // namespace wfl
map_location loc
Definition: move.cpp:172
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
config & mandatory_child(config_key_type key, int n=0)
Returns the nth child with the given key, or throws an error if there is none.
Definition: config.cpp:362
const_attr_itors attribute_range() const
Definition: config.cpp:756
auto all_children_view() const
In-order iteration over all children.
Definition: config.hpp:796
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:312
bool has_attribute(config_key_type key) const
Definition: config.cpp:157
child_itors child_range(config_key_type key)
Definition: config.cpp:268
std::string hash() const
Definition: config.cpp:1279
Abstract class for exposing game data that doesn't depend on the GUI, however which for historical re...
virtual const gamemap & map() const =0
int w() const
Effective map width.
Definition: map.hpp:50
int h() const
Effective map height.
Definition: map.hpp:53
Encapsulates the map of the game.
Definition: map.hpp:172
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:75
bool uses_shroud() const
Definition: team.hpp:308
const std::string & color() const
Definition: team.hpp:247
config & variables()
Definition: team.hpp:353
int side() const
Definition: team.hpp:180
const std::string & faction() const
Definition: team.hpp:301
int village_support() const
Definition: team.hpp:191
int recall_cost() const
Definition: team.hpp:185
const std::string & team_name() const
Definition: team.hpp:287
bool is_local_human() const
Definition: team.hpp:258
int village_gold() const
Definition: team.hpp:184
team_shared_vision::type share_vision() const
Definition: team.hpp:382
int gold() const
Definition: team.hpp:181
const t_string & faction_name() const
Definition: team.hpp:302
bool carryover_add() const
Definition: team.hpp:348
int carryover_percentage() const
Definition: team.hpp:346
bool is_network() const
Definition: team.hpp:253
int total_income() const
Definition: team.hpp:188
const std::string & save_id() const
Definition: team.hpp:222
bool is_local_ai() const
Definition: team.hpp:259
double carryover_bonus() const
Definition: team.hpp:350
int start_gold() const
Definition: team.hpp:182
const std::string & flag_icon() const
Definition: team.hpp:292
int base_income() const
Definition: team.hpp:183
bool uses_fog() const
Definition: team.hpp:309
const std::string & flag() const
Definition: team.hpp:291
bool hidden() const
Definition: team.hpp:338
recall_list_manager & recall_list()
Definition: team.hpp:206
const std::set< std::string > & recruits() const
Definition: team.hpp:214
const std::string & icon_image() const
Definition: terrain.hpp:44
const t_string & name() const
Definition: terrain.hpp:48
bool is_keep() const
Definition: terrain.hpp:143
bool is_castle() const
Definition: terrain.hpp:142
const std::string & id() const
Definition: terrain.hpp:52
const t_string & description() const
Definition: terrain.hpp:50
bool is_village() const
Definition: terrain.hpp:141
int light_bonus(int base) const
Returns the light (lawful) bonus for this terrain when the time of day gives a base bonus.
Definition: terrain.hpp:132
const t_string & editor_name() const
Definition: terrain.hpp:49
int gives_healing() const
Definition: terrain.hpp:140
const wfl::map_formula_callable_ptr & formula_vars() const
const std::string & id() const
Definition: race.hpp:35
std::string race_id() const
Returns the ID of this type's race without the need to build the type.
Definition: types.hpp:272
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:141
int hitpoints() const
Definition: types.hpp:161
const_attack_itors attacks() const
Definition: types.cpp:545
const std::string & usage() const
Definition: types.hpp:175
int movement() const
Definition: types.hpp:166
int cost() const
Definition: types.hpp:172
int experience_needed(bool with_acceleration=true) const
Definition: types.cpp:579
bool musthave_status(const std::string &status) const
Definition: types.cpp:674
std::vector< std::string > get_ability_list() const
Definition: types.cpp:606
const t_string & type_name() const
The name of the unit in the current language setting.
Definition: types.hpp:138
config::const_child_itors possible_traits() const
Definition: types.hpp:231
int level() const
Definition: types.hpp:164
unit_alignments::type alignment() const
Definition: types.hpp:193
int recall_cost() const
Definition: types.hpp:165
This class represents a single unit of a specific type.
Definition: unit.hpp:133
Represents version numbers.
int do_compare(const formula_callable *callable) const override
variant get_value(const std::string &key) const override
attack_type_callable(const attack_type &attack)
void get_inputs(formula_input_vector &inputs) const override
variant get_value(const std::string &key) const override
void get_inputs(formula_input_vector &inputs) const override
void get_inputs(formula_input_vector &inputs) const override
variant get_value(const std::string &key) const override
int do_compare(const formula_callable *callable) const override
const config & get_config() const
void get_inputs(formula_input_vector &inputs) const override
variant get_value(const std::string &key) const override
std::shared_ptr< attack_type > second_weapon
const game_events::queued_event & event_info
std::shared_ptr< attack_type > first_weapon
formula_callable_ptr fake_ptr()
Definition: callable.hpp:42
formula_input_vector inputs() const
Definition: callable.hpp:63
static void add_input(formula_input_vector &inputs, const std::string &key, formula_access access_type=formula_access::read_only)
Definition: callable.hpp:136
virtual int do_compare(const formula_callable *callable) const
Definition: callable.hpp:146
static variant convert_vector(const std::vector< T > &input_vector)
Definition: callable.hpp:126
static variant convert_set(const std::set< T > &input_set)
Definition: callable.hpp:115
static const char *const id_chars
Definition: formula.hpp:75
const display_context & board_
const gamemap & get_gamemap() const
void get_inputs(formula_input_vector &inputs) const override
variant get_value(const std::string &key) const override
variant get_value(const std::string &key) const override
void get_inputs(formula_input_vector &inputs) const override
const map_location & loc() const
void serialize_to_string(std::string &str) const override
void get_inputs(formula_input_vector &inputs) const override
int do_compare(const formula_callable *callable) const override
variant get_value(const std::string &key) const override
map_formula_callable & add(const std::string &key, const variant &value)
Definition: callable.hpp:253
const expression_ptr & get_backup() const
variant execute_self(variant ctxt) override
variant get_value(const std::string &key) const override
void get_inputs(formula_input_vector &inputs) const override
const map_location current_unit_location_
void get_inputs(formula_input_vector &inputs) const override
const_formula_callable_ptr failed_callable_
variant get_value(const std::string &key) const override
void get_inputs(formula_input_vector &inputs) const override
variant get_value(const std::string &key) const override
const std::string & key() const
variant execute_self(variant ctxt) override
void get_inputs(formula_input_vector &inputs) const override
variant get_value(const std::string &key) const override
variant get_value(const std::string &key) const override
const terrain_type & t_
terrain_callable(const display_context &m, const map_location &loc)
void get_inputs(formula_input_vector &inputs) const override
const map_location loc_
int do_compare(const formula_callable *callable) const override
variant get_value(const std::string &key) const override
const map_location & loc_
void get_inputs(formula_input_vector &inputs) const override
unit_callable(const map_location &loc, const unit &u)
int do_compare(const formula_callable *callable) const override
variant get_value(const std::string &key) const override
int do_compare(const formula_callable *callable) const override
void get_inputs(formula_input_vector &inputs) const override
variant execute_variant(const variant &to_exec)
Definition: variant.cpp:653
std::shared_ptr< T > try_convert() const
Definition: variant.hpp:90
const_formula_callable_ptr as_callable() const
Definition: variant.hpp:83
@ DECIMAL_VARIANT
Definition: variant.hpp:31
std::string to_debug_string(bool verbose=false, formula_seen_stack *seen=nullptr) const
Definition: variant.cpp:643
Definitions for the interface to Wesnoth Markup Language (WML).
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
Definition: deprecation.cpp:29
#define LOG_SF
static lg::log_domain log_scripting_formula("scripting/formula")
#define ERR_SF
std::size_t i
Definition: function.cpp:1029
map_location loc_
int w
Interfaces for manipulating version numbers of engine, add-ons, etc.
static std::string _(const char *str)
Definition: gettext.hpp:93
std::vector< std::string > get_ability_list() const
Get a list of all abilities by ID.
Definition: abilities.cpp:264
unit_formula_manager & formula_manager() const
Get the unit formula manager.
Definition: unit.hpp:1950
int max_hitpoints() const
The max number of hitpoints this unit can have.
Definition: unit.hpp:505
unit_alignments::type alignment() const
The alignment of this unit.
Definition: unit.hpp:475
bool incapacitated() const
Check if the unit has been petrified.
Definition: unit.hpp:904
int level() const
The current level of this unit.
Definition: unit.hpp:559
std::string usage() const
Gets this unit's usage.
Definition: unit.hpp:686
const std::string & get_role() const
Gets this unit's role.
Definition: unit.hpp:669
const std::vector< std::string > & recruits() const
The type IDs of the other units this unit may recruit, if possible.
Definition: unit.hpp:624
const std::string & variation() const
The ID of the variation of this unit's type.
Definition: unit.hpp:572
int hitpoints() const
The current number of hitpoints this unit has.
Definition: unit.hpp:499
int cost() const
How much gold is required to recruit this unit.
Definition: unit.hpp:633
bool get_state(const std::string &state) const
Check if the unit is affected by a status effect.
Definition: unit.cpp:1379
const std::string & type_id() const
The id of this unit's type.
Definition: unit.cpp:1913
bool get_hidden() const
Gets whether this unit is currently hidden on the map.
Definition: unit.hpp:720
const std::set< std::string > get_states() const
Get the status effects currently affecting the unit.
Definition: unit.cpp:1362
const unit_race * race() const
Gets this unit's race.
Definition: unit.hpp:493
int experience() const
The current number of experience points this unit has.
Definition: unit.hpp:523
bool can_recruit() const
Whether this unit can recruit other units - ie, are they a leader unit.
Definition: unit.hpp:612
const std::string & id() const
Gets this unit's id.
Definition: unit.hpp:380
int side() const
The side this unit belongs to.
Definition: unit.hpp:343
config & variables()
Gets any user-defined variables this unit 'owns'.
Definition: unit.hpp:703
std::size_t underlying_id() const
This unit's unique internal ID.
Definition: unit.hpp:392
int max_experience() const
The max number of experience points this unit can have.
Definition: unit.hpp:529
unit_race::GENDER gender() const
The gender of this unit.
Definition: unit.hpp:465
const t_string & name() const
Gets this unit's translatable display name.
Definition: unit.hpp:403
const advances_to_t & advances_to() const
Gets the possible types this unit can advance to on level-up.
Definition: unit.hpp:244
attack_itors attacks()
Gets an iterator over this unit's attacks.
Definition: unit.hpp:926
int max_attacks() const
The maximum number of attacks this unit may perform per turn, usually 1.
Definition: unit.hpp:977
int attacks_left() const
Gets the remaining number of attacks this unit can perform this turn.
Definition: unit.hpp:993
std::size_t advancements_count() const
Definition: unit.hpp:1557
std::size_t traits_count() const
Definition: unit.hpp:1547
std::size_t objects_count() const
Definition: unit.hpp:1552
bool get_emit_zoc() const
Gets the raw zone-of-control flag, disregarding incapacitated.
Definition: unit.hpp:1384
const movetype & movement_type() const
Get the unit's movement type.
Definition: unit.hpp:1470
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
Definition: unit.hpp:1322
int total_movement() const
The maximum moves this unit has.
Definition: unit.hpp:1306
map_location::direction facing() const
The current direction this unit is facing within its hex.
Definition: unit.hpp:1413
bool resting() const
Checks whether this unit is 'resting'.
Definition: unit.hpp:1366
bool is_flying() const
Check if the unit is a flying unit.
Definition: unit.hpp:1506
std::vector< std::string > get_advancements_list() const
Definition: unit.hpp:1138
std::vector< std::string > get_objects_list() const
Definition: unit.hpp:1133
int upkeep() const
Gets the amount of gold this unit costs a side per turn.
Definition: unit.cpp:1683
bool is_healthy() const
Gets whether this unit is healthy - ie, always rest heals.
Definition: unit.hpp:1293
bool is_fearless() const
Gets whether this unit is fearless - ie, unaffected by time of day.
Definition: unit.hpp:1287
std::vector< std::string > get_traits_list() const
Gets a list of the traits this unit currently has, including hidden traits.
Definition: unit.hpp:1128
Standard logging facilities (interface).
::tod_manager * tod_manager
Definition: resources.cpp:29
game_board * gameboard
Definition: resources.cpp:20
play_controller * controller
Definition: resources.cpp:21
Definition: contexts.hpp:43
std::vector< formula_input > formula_input_vector
static std::string get_location(const std::string &loc)
Define the game's event mechanism.
const std::string & gender_string(unit_race::GENDER gender)
Definition: race.cpp:138
unit_const_ptr get_unit() const
entity_location loc1
Definition: pump.hpp:65
entity_location loc2
Definition: pump.hpp:66
std::string name
Definition: pump.hpp:63
Encapsulates the map of the game.
Definition: location.hpp:45
static std::string write_direction(direction dir)
Definition: location.cpp:154
int wml_y() const
Definition: location.hpp:184
static const map_location & null_location()
Definition: location.hpp:102
int wml_x() const
Definition: location.hpp:183
int do_compare(const map_location &a) const
three-way comparator
Definition: location.hpp:126
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition: enum_base.hpp:46
variant operator()(utils::monostate) const
variant operator()(bool b) const
variant operator()(const std::string &s) const
variant operator()(int i) const
variant operator()(unsigned long long i) const
variant operator()(const t_string &s) const
variant operator()(double i) const
mock_party p
static map_location::direction s
#define h
#define b