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