The Battle for Wesnoth  1.19.1+dev
callable_objects.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2024
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 == "type") {
91  return variant(att_->type());
92  } else if(key == "icon") {
93  return variant(att_->icon());
94  } else if(key == "range") {
95  return variant(att_->range());
96  } else if(key == "damage") {
97  return variant(att_->damage());
98  } else if(key == "number_of_attacks" || key == "number" || key == "num_attacks" || key == "attacks") {
99  return variant(att_->num_attacks());
100  } else if(key == "attack_weight") {
101  return variant(att_->attack_weight(), variant::DECIMAL_VARIANT);
102  } else if(key == "defense_weight") {
103  return variant(att_->defense_weight(), variant::DECIMAL_VARIANT);
104  } else if(key == "accuracy") {
105  return variant(att_->accuracy());
106  } else if(key == "parry") {
107  return variant(att_->parry());
108  } else if(key == "movement_used") {
109  return variant(att_->movement_used());
110  } else if(key == "attacks_used") {
111  return variant(att_->attacks_used());
112  } else if(key == "specials" || key == "special") {
113  std::vector<variant> res;
114 
115  for(const auto special : att_->specials().all_children_range()) {
116  if(!special.cfg["id"].empty()) {
117  res.emplace_back(special.cfg["id"].str());
118  }
119  }
120  return variant(res);
121  }
122 
123  return variant();
124 }
125 
127 {
128  add_input(inputs, "name");
129  add_input(inputs, "type");
130  add_input(inputs, "description");
131  add_input(inputs, "icon");
132  add_input(inputs, "range");
133  add_input(inputs, "damage");
134  add_input(inputs, "number");
135  add_input(inputs, "accuracy");
136  add_input(inputs, "parry");
137  add_input(inputs, "movement_used");
138  add_input(inputs, "attacks_used");
139  add_input(inputs, "attack_weight");
140  add_input(inputs, "defense_weight");
141  add_input(inputs, "specials");
142 }
143 
145 {
146  const attack_type_callable* att_callable = dynamic_cast<const attack_type_callable*>(callable);
147  if(att_callable == nullptr) {
148  return formula_callable::do_compare(callable);
149  }
150 
151  if(att_->damage() != att_callable->att_->damage()) {
152  return att_->damage() - att_callable->att_->damage();
153  }
154 
155  if(att_->num_attacks() != att_callable->att_->num_attacks()) {
156  return att_->num_attacks() - att_callable->att_->num_attacks();
157  }
158 
159  if(att_->id() != att_callable->att_->id()) {
160  return att_->id().compare(att_callable->att_->id());
161  }
162 
163  if(att_->type() != att_callable->att_->type()) {
164  return att_->type().compare(att_callable->att_->type());
165  }
166 
167  if(att_->range() != att_callable->att_->range()) {
168  return att_->range().compare(att_callable->att_->range());
169  }
170 
171  const auto self_specials = att_->specials().all_children_range();
172  const auto other_specials = att_callable->att_->specials().all_children_range();
173  if(self_specials.size() != other_specials.size()) {
174  return self_specials.size() < other_specials.size() ? -1 : 1;
175  }
176  for(std::size_t i = 0; i < self_specials.size(); ++i) {
177  const auto& s = self_specials[i].cfg["id"];
178  const auto& o = other_specials[i].cfg["id"];
179  if(s != o) {
180  return s.str().compare(o.str());
181  }
182  }
183 
184  return 0;
185 }
186 
188 {
189  type_ = UNIT_C;
190 }
191 
192 variant unit_callable::get_value(const std::string& key) const
193 {
194 
195  if(key == "x") {
197  return variant();
198  }
199 
200  return variant(loc_.wml_x());
201  } else if(key == "y") {
203  return variant();
204  }
205 
206  return variant(loc_.wml_y());
207  } else if(key == "loc") {
209  return variant();
210  }
211 
212  return variant(std::make_shared<location_callable>(loc_));
213  } else if(key == "terrain") {
215  return variant();
216  }
217  return variant(std::make_shared<terrain_callable>(*resources::gameboard, loc_));
218  } else if(key == "id") {
219  return variant(u_.id());
220  } else if(key == "type") {
221  return variant(u_.type_id());
222  } else if(key == "name") {
223  return variant(u_.name());
224  } else if(key == "usage") {
225  return variant(u_.usage());
226  } else if(key == "leader" || key == "canrecruit") {
227  return variant(u_.can_recruit());
228  } else if(key == "undead") {
229  return variant(u_.get_state("not_living") ? 1 : 0);
230  } else if(key == "attacks") {
231  std::vector<variant> res;
232  for(const attack_type& att : u_.attacks()) {
233  res.emplace_back(std::make_shared<attack_type_callable>(att));
234  }
235 
236  return variant(res);
237  } else if(key == "abilities") {
239  } else if(key == "hitpoints") {
240  return variant(u_.hitpoints());
241  } else if(key == "max_hitpoints") {
242  return variant(u_.max_hitpoints());
243  } else if(key == "experience") {
244  return variant(u_.experience());
245  } else if(key == "max_experience") {
246  return variant(u_.max_experience());
247  } else if(key == "level" || key == "full") {
248  // This allows writing "upkeep == full"
249  return variant(u_.level());
250  } else if(key == "total_movement" || key == "max_moves") {
251  return variant(u_.total_movement());
252  } else if(key == "movement_left" || key == "moves") {
253  return variant(u_.movement_left());
254  } else if(key == "attacks_left") {
255  return variant(u_.attacks_left());
256  } else if(key == "max_attacks") {
257  return variant(u_.max_attacks());
258  } else if(key == "traits") {
260  } else if(key == "advancements_taken") {
262  } else if(key == "objects") {
264  } else if(key == "traits_count") {
265  return variant(u_.traits_count());
266  } else if(key == "advancements_taken_count") {
267  return variant(u_.advancements_count());
268  } else if(key == "objects_count") {
269  return variant(u_.objects_count());
270  } else if(key == "extra_recruit") {
272  } else if(key == "advances_to") {
274  } else if(key == "states" || key == "status") {
276  } else if(key == "side") {
277  deprecated_message("unit.side", DEP_LEVEL::FOR_REMOVAL, version_info("1.17"), "This returns 0 for side 1 etc and should not be used. Use side_number instead.");
278  return variant(u_.side()-1);
279  } else if(key == "side_number") {
280  return variant(u_.side());
281  } else if(key == "cost") {
282  return variant(u_.cost());
283  } else if(key == "upkeep") {
284  return variant(u_.upkeep());
285  } else if(key == "loyal") {
286  // So we can write "upkeep == loyal"
287  return variant(0);
288  } else if(key == "hidden") {
289  return variant(u_.get_hidden());
290  } else if(key == "petrified") {
291  return variant(u_.incapacitated());
292  } else if(key == "resting") {
293  return variant(u_.resting());
294  } else if(key == "role") {
295  return variant(u_.get_role());
296  } else if(key == "race") {
297  return variant(u_.race()->id());
298  } else if(key == "gender") {
299  return variant(gender_string(u_.gender()));
300  } else if(key == "variation") {
301  return variant(u_.variation());
302  } else if(key == "zoc") {
303  return variant(u_.get_emit_zoc());
304  } else if(key == "alignment") {
306  } else if(key == "facing") {
308  } else if(key == "resistance" || key == "movement_cost" || key == "vision_cost" || key == "jamming_cost" || key == "defense") {
309  const auto& mt = u_.movement_type();
310  config cfg;
311  bool needs_flip = false;
312  if(key == "resistance") {
313  mt.get_resistances().write(cfg);
314  needs_flip = true;
315  } else if(key == "movement_cost") {
316  mt.get_movement().write(cfg);
317  } else if(key == "vision_cost") {
318  mt.get_vision().write(cfg);
319  } else if(key == "jamming_cost") {
320  mt.get_jamming().write(cfg);
321  } else if(key == "defense") {
322  mt.get_defense().write(cfg);
323  needs_flip = true;
324  }
325  std::map<variant, variant> res;
326  for(const auto& p : cfg.attribute_range()) {
327  int val = p.second;
328  if(needs_flip) {
329  val = 100 - val;
330  }
331  res.emplace(variant(p.first), variant(val));
332  }
333 
334  return variant(res);
335  } else if(key == "flying") {
336  return variant(u_.is_flying());
337  } else if(key == "vars") {
340  }
341 
342  return variant();
343  } else if(key == "wml_vars") {
344  return variant(std::make_shared<config_callable>(u_.variables()));
345  } else if(key == "n" || key == "s" || key == "ne" || key == "se" || key == "nw" || key == "sw" ||
346  key == "lawful" || key == "neutral" || key == "chaotic" || key == "liminal" ||
347  key == "male" || key == "female")
348  {
349  return variant(key);
350  }
351 
352  return variant();
353 }
354 
356 {
357  add_input(inputs, "x");
358  add_input(inputs, "y");
359  add_input(inputs, "loc");
360  add_input(inputs, "terrain");
361  add_input(inputs, "id");
362  add_input(inputs, "type");
363  add_input(inputs, "name");
364  add_input(inputs, "canrecruit");
365  add_input(inputs, "undead");
366  add_input(inputs, "traits");
367  add_input(inputs, "advancements_taken");
368  add_input(inputs, "objects");
369  add_input(inputs, "traits_count");
370  add_input(inputs, "advancements_taken_count");
371  add_input(inputs, "objects_count");
372  add_input(inputs, "attacks");
373  add_input(inputs, "abilities");
374  add_input(inputs, "hitpoints");
375  add_input(inputs, "max_hitpoints");
376  add_input(inputs, "experience");
377  add_input(inputs, "max_experience");
378  add_input(inputs, "level");
379  add_input(inputs, "moves");
380  add_input(inputs, "max_moves");
381  add_input(inputs, "attacks_left");
382  add_input(inputs, "max_attacks");
383  add_input(inputs, "side_number");
384  add_input(inputs, "extra_recruit");
385  add_input(inputs, "advances_to");
386  add_input(inputs, "status");
387  add_input(inputs, "cost");
388  add_input(inputs, "usage");
389  add_input(inputs, "upkeep");
390  add_input(inputs, "hidden");
391  add_input(inputs, "petrified");
392  add_input(inputs, "resting");
393  add_input(inputs, "role");
394  add_input(inputs, "race");
395  add_input(inputs, "gender");
396  add_input(inputs, "variation");
397  add_input(inputs, "zoc");
398  add_input(inputs, "alignment");
399  add_input(inputs, "facing");
400  add_input(inputs, "resistance");
401  add_input(inputs, "movement_cost");
402  add_input(inputs, "vision_cost");
403  add_input(inputs, "jamming_cost");
404  add_input(inputs, "defense");
405  add_input(inputs, "flying");
406  add_input(inputs, "vars");
407  add_input(inputs, "wml_vars");
408 }
409 
410 int unit_callable::do_compare(const formula_callable* callable) const
411 {
412  const unit_callable* u_callable = dynamic_cast<const unit_callable*>(callable);
413  if(u_callable == nullptr) {
414  return formula_callable::do_compare(callable);
415  }
416 
417  return u_.underlying_id() - u_callable->u_.underlying_id();
418 }
419 
420 variant unit_type_callable::get_value(const std::string& key) const
421 {
422  if(key == "id") {
423  return variant(u_.id());
424  } else if(key == "type") {
425  return variant(u_.type_name());
426  } else if(key == "alignment") {
428  } else if(key == "race") {
429  return variant(u_.race_id());
430  } else if(key == "abilities") {
432  } else if(key == "traits") {
433  std::vector<variant> res;
434  for(const auto& config : u_.possible_traits()) {
435  res.emplace_back(config["id"].str());
436  }
437 
438  return variant(res);
439  } else if(key == "attacks") {
440  std::vector<variant> res;
441  for(const attack_type& att : u_.attacks()) {
442  res.emplace_back(std::make_shared<attack_type_callable>(att));
443  }
444 
445  return variant(res);
446  } else if(key == "hitpoints" || key == "max_hitpoints") {
447  return variant(u_.hitpoints());
448  } else if(key == "experience" || key == "max_experience") {
449  return variant(u_.experience_needed(true));
450  } else if(key == "level") {
451  return variant(u_.level());
452  } else if(key == "total_movement" || key == "max_moves" || key == "moves") {
453  return variant(u_.movement());
454  } else if(key == "undead") {
455  return variant(u_.musthave_status("unpoisonable") && u_.musthave_status("undrainable") && u_.musthave_status("unplagueable"));
456  } else if(key == "unpoisonable") {
457  return variant(u_.musthave_status("unpoisonable"));
458  } else if(key == "unslowable") {
459  return variant(u_.musthave_status("unslowable"));
460  } else if(key == "unpetrifiable") {
461  return variant(u_.musthave_status("unpetrifiable"));
462  } else if(key == "undrainable") {
463  return variant(u_.musthave_status("undrainable"));
464  } else if(key == "unplagueable") {
465  return variant(u_.musthave_status("unplagueable"));
466  } else if(key == "cost") {
467  return variant(u_.cost());
468  } else if(key == "recall_cost") {
469  return variant(u_.recall_cost());
470  } else if(key == "usage") {
471  return variant(u_.usage());
472  }
473 
474  return variant();
475 }
476 
478 {
479  add_input(inputs, "id");
480  add_input(inputs, "type");
481  add_input(inputs, "race");
482  add_input(inputs, "alignment");
483  add_input(inputs, "abilities");
484  add_input(inputs, "traits");
485  add_input(inputs, "attacks");
486  add_input(inputs, "hitpoints");
487  add_input(inputs, "experience");
488  add_input(inputs, "level");
489  add_input(inputs, "total_movement");
490  add_input(inputs, "undead");
491  add_input(inputs, "cost");
492  add_input(inputs, "recall_cost");
493  add_input(inputs, "usage");
494 }
495 
497 {
498  const unit_type_callable* u_callable = dynamic_cast<const unit_type_callable*>(callable);
499  if(u_callable == nullptr) {
500  return formula_callable::do_compare(callable);
501  }
502 
503  return u_.id().compare(u_callable->u_.id());
504 }
505 
507 {
508  variant operator()(bool b) const { return variant(b ? 1 : 0); }
509  variant operator()(int i) const { return variant(i); }
510  variant operator()(unsigned long long i) const { return variant(i); }
511  variant operator()(double i) const { return variant(i * 1000, variant::DECIMAL_VARIANT); }
512  // TODO: Should comma-separated lists of stuff be returned as a list?
513  // The challenge is to distinguish them from ordinary strings that happen to contain a comma
514  // (or should we assume that such strings will be translatable?).
515  variant operator()(const std::string& s) const { return variant(s); }
516  variant operator()(const t_string& s) const { return variant(s.str()); }
517  variant operator()(std::monostate) const { return variant(); }
518 };
519 
520 variant config_callable::get_value(const std::string& key) const
521 {
522  if(cfg_.has_attribute(key)) {
523  return cfg_[key].apply_visitor(fai_variant_visitor());
524  } else if(cfg_.has_child(key)) {
525  std::vector<variant> result;
526  for(const auto& child : cfg_.child_range(key)) {
527  result.emplace_back(std::make_shared<config_callable>(child));
528  }
529 
530  return variant(result);
531  } else if(key == "__all_children") {
532  std::vector<variant> result;
533  for(const auto child : cfg_.all_children_range()) {
534  const variant cfg_child(std::make_shared<config_callable>(child.cfg));
535  const variant kv(std::make_shared<key_value_pair>(variant(child.key), cfg_child));
536  result.push_back(kv);
537  }
538 
539  return variant(result);
540  } else if(key == "__children") {
541  std::map<std::string, std::vector<variant>> build;
542  for(const auto child : cfg_.all_children_range()) {
543  const variant cfg_child(std::make_shared<config_callable>(child.cfg));
544  build[child.key].push_back(cfg_child);
545  }
546 
547  std::map<variant,variant> result;
548  for(auto& p : build) {
549  result[variant(p.first)] = variant(p.second);
550  }
551 
552  return variant(result);
553  } else if(key == "__attributes") {
554  std::map<variant,variant> result;
555  for(const auto& val : cfg_.attribute_range()) {
556  result[variant(val.first)] = val.second.apply_visitor(fai_variant_visitor());
557  }
558 
559  return variant(result);
560  }
561 
562  return variant();
563 }
564 
566 {
567  add_input(inputs, "__all_children");
568  add_input(inputs, "__children");
569  add_input(inputs, "__attributes");
570 
571  for(const auto& val : cfg_.attribute_range()) {
572  if(val.first.find_first_not_of(formula::id_chars) != std::string::npos) {
573  add_input(inputs, val.first);
574  }
575  }
576 }
577 
579 {
580  const config_callable* cfg_callable = dynamic_cast<const config_callable*>(callable);
581  if(cfg_callable == nullptr) {
582  return formula_callable::do_compare(callable);
583  }
584 
585  if(cfg_ == cfg_callable->get_config()) {
586  return 0;
587  }
588 
589  return cfg_.hash().compare(cfg_callable->get_config().hash());
590 }
591 
592 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))
593 {
594  type_ = TERRAIN_C;
595 }
596 
597 variant terrain_callable::get_value(const std::string& key) const
598 {
599  if(key == "x") {
600  return variant(loc_.wml_x());
601  } else if(key == "y") {
602  return variant(loc_.wml_y());
603  } else if(key == "loc") {
604  return variant(std::make_shared<location_callable>(loc_));
605  } else if(key == "id") {
606  return variant(std::string(t_.id()));
607  } else if(key == "name") {
608  return variant(t_.name());
609  } else if(key == "editor_name") {
610  return variant(t_.editor_name());
611  } else if(key == "description") {
612  return variant(t_.description());
613  } else if(key == "icon") {
614  return variant(t_.icon_image());
615  } else if(key == "light") {
616  return variant(t_.light_bonus(0));
617  } else if(key == "village") {
618  return variant(t_.is_village());
619  } else if(key == "castle") {
620  return variant(t_.is_castle());
621  } else if(key == "keep") {
622  return variant(t_.is_keep());
623  } else if(key == "healing") {
624  return variant(t_.gives_healing());
625  } else if(key == "owner") {
626  deprecated_message("terrain.owner", DEP_LEVEL::FOR_REMOVAL, version_info("1.17"), "This returns 0 for side 1 etc and should not be used. Use owner_side instead.");
627  return variant(owner_ - 1);
628  } else if(key == "owner_side") {
629  return variant(owner_);
630  }
631 
632  return variant();
633 }
634 
636 {
637  add_input(inputs, "x");
638  add_input(inputs, "y");
639  add_input(inputs, "loc");
640  add_input(inputs, "id");
641  add_input(inputs, "name");
642  add_input(inputs, "editor_name");
643  add_input(inputs, "description");
644  add_input(inputs, "icon");
645  add_input(inputs, "light");
646  add_input(inputs, "village");
647  add_input(inputs, "castle");
648  add_input(inputs, "keep");
649  add_input(inputs, "healing");
650  add_input(inputs, "owner_side");
651 }
652 
654 {
655  const terrain_callable* terr_callable = dynamic_cast<const terrain_callable*>(callable);
656  if(terr_callable == nullptr) {
657  return formula_callable::do_compare(callable);
658  }
659 
660  const map_location& other_loc = terr_callable->loc_;
661  return loc_.do_compare(other_loc);
662 }
663 
665  return board_.map();
666 }
667 
669 {
670  add_input(inputs, "w");
671  add_input(inputs, "h");
672 }
673 
674 variant gamemap_callable::get_value(const std::string& key) const
675 {
676  if(key == "terrain") {
677  int w = get_gamemap().w();
678  int h = get_gamemap().h();
679 
680  std::vector<variant> vars;
681  for(int i = 0; i < w; i++) {
682  for(int j = 0; j < h; j++) {
683  const map_location loc(i, j);
684  vars.emplace_back(std::make_shared<terrain_callable>(board_, loc));
685  }
686  }
687 
688  return variant(vars);
689  } else if(key == "gamemap") {
690  int w = get_gamemap().w();
691  int h = get_gamemap().h();
692 
693  std::map<variant, variant> vars;
694  for(int i = 0; i < w; i++) {
695  for(int j = 0; j < h; j++) {
696  const map_location loc(i, j);
697  vars.emplace(std::make_shared<location_callable>(loc), std::make_shared<terrain_callable>(board_, loc));
698  }
699  }
700 
701  return variant(vars);
702  } else if(key == "w") {
703  return variant(get_gamemap().w());
704  } else if(key == "h") {
705  return variant(get_gamemap().h());
706  } else {
707  return variant();
708  }
709 }
710 
712 {
713  add_input(inputs, "side_number");
714  add_input(inputs, "id");
715  add_input(inputs, "gold");
716  add_input(inputs, "start_gold");
717  add_input(inputs, "base_income");
718  add_input(inputs, "total_income");
719  add_input(inputs, "village_gold");
720  add_input(inputs, "village_support");
721  add_input(inputs, "recall_cost");
722  add_input(inputs, "is_human");
723  add_input(inputs, "is_ai");
724  add_input(inputs, "is_network");
725  add_input(inputs, "fog");
726  add_input(inputs, "shroud");
727  add_input(inputs, "hidden");
728  add_input(inputs, "flag");
729  add_input(inputs, "flag_icon");
730  add_input(inputs, "team_name");
731  add_input(inputs, "faction");
732  add_input(inputs, "faction_name");
733  add_input(inputs, "color");
734  add_input(inputs, "share_vision");
735  add_input(inputs, "carryover_bonus");
736  add_input(inputs, "carryover_percentage");
737  add_input(inputs, "carryover_add");
738  add_input(inputs, "recruit");
739  add_input(inputs, "wml_vars");
740 }
741 
742 variant team_callable::get_value(const std::string& key) const
743 {
744  if(key == "side") {
745  deprecated_message("team.side", DEP_LEVEL::INDEFINITE, version_info("1.17"), "Use side_number instead.");
746  return variant(team_.side());
747  } else if(key == "side_number") {
748  return variant(team_.side());
749  } else if(key == "id") {
750  return variant(team_.save_id());
751  } else if(key == "save_id") {
752  return variant(team_.save_id());
753  } else if(key == "gold") {
754  return variant(team_.gold());
755  } else if(key == "start_gold") {
756  return variant(team_.start_gold());
757  } else if(key == "base_income") {
758  return variant(team_.base_income());
759  } else if(key == "total_income") {
760  return variant(team_.total_income());
761  } else if(key == "village_gold") {
762  return variant(team_.village_gold());
763  } else if(key == "village_support") {
764  return variant(team_.village_support());
765  } else if(key == "recall_cost") {
766  return variant(team_.recall_cost());
767  } else if(key == "is_human") {
768  return variant(team_.is_local_human());
769  } else if(key == "is_ai") {
770  return variant(team_.is_local_ai());
771  } else if(key == "is_network") {
772  return variant(team_.is_network());
773  } else if(key == "fog") {
774  return variant(team_.uses_fog());
775  } else if(key == "shroud") {
776  return variant(team_.uses_shroud());
777  } else if(key == "hidden") {
778  return variant(team_.hidden());
779  } else if(key == "flag") {
780  return variant(team_.flag());
781  } else if(key == "flag_icon") {
782  return variant(team_.flag_icon());
783  } else if(key == "team_name") {
784  return variant(team_.team_name());
785  } else if(key == "faction") {
786  return variant(team_.faction());
787  } else if(key == "faction_name") {
788  return variant(team_.faction_name());
789  } else if(key == "color") {
790  return variant(team_.color());
791  } else if(key == "share_vision") {
793  } else if(key == "carryover_bonus") {
795  } else if(key == "carryover_percentage") {
797  } else if(key == "carryover_add") {
798  return variant(team_.carryover_add());
799  } else if(key == "recruit") {
800  std::vector<variant> result;
801  for(const auto& recruit : team_.recruits()) {
802  result.emplace_back(recruit);
803  }
804  return variant(result);
805  } else if(key == "recall") {
806  std::vector<variant> result;
807  for(const auto& u : team_.recall_list()) {
808  result.push_back(std::make_shared<unit_callable>(*u));
809  }
810  return variant(result);
811  } else if(key == "wml_vars") {
812  return variant(std::make_shared<config_callable>(team_.variables()));
813  }
814 
815  return variant();
816 }
817 
818 variant set_var_callable::get_value(const std::string& key) const
819 {
820  if(key == "key") {
821  return variant(key_);
822  } else if(key == "value") {
823  return value_;
824  }
825 
826  return variant();
827 }
828 
830 {
831  add_input(inputs, "key");
832  add_input(inputs, "value");
833 }
834 
836 {
837  //if(infinite_loop_guardian_.set_var_check()) {
838  if(auto obj = ctxt.try_convert<formula_callable>()) {
839  LOG_SF << "Setting variable: " << key_ << " -> " << value_.to_debug_string();
840  obj->mutate_value(key_, value_);
841  return variant(true);
842  }
843  //}
844  //too many calls in a row - possible infinite loop
845  ERR_SF << "ERROR #" << 5001 << " while executing 'set_var' formula function";
846 
847  return variant(std::make_shared<safe_call_result>(fake_ptr(), 5001));
848 }
849 
850 variant safe_call_callable::get_value(const std::string& key) const
851 {
852  if(key == "main") {
853  return variant(main_);
854  } else if(key == "backup") {
855  return variant(backup_);
856  }
857 
858  return variant();
859 }
860 
862 {
863  add_input(inputs, "main");
864  add_input(inputs, "backup");
865 }
866 
868 {
869  variant res;
870  if(auto action = main_.try_convert<action_callable>()) {
871  res = action->execute_self(ctxt);
872  }
873 
874  if(res.try_convert<safe_call_result>()) {
875  /* If we have safe_call formula and either an error occurred, or the current action
876  * was not recognized, then evaluate backup formula from safe_call and execute it
877  * during the next loop
878  */
879 
880  map_formula_callable callable(ctxt.as_callable());
881  callable.add("error", res);
882 
883  /* Store the result in safe_call_callable in case we would like to display it to the user,
884  * for example if this formula was executed from the commandline.
885  */
886  backup_ = get_backup()->evaluate(callable);
887  ctxt.execute_variant(backup_);
888  }
889  return variant(true);
890 }
891 
892 variant safe_call_result::get_value(const std::string& key) const
893 {
894  if(key == "status") {
895  return variant(status_);
896  } else if(key == "object") {
897  if(failed_callable_) {
898  return variant(failed_callable_);
899  }
900 
901  return variant();
902  } else if(key == "current_loc" && current_unit_location_ != map_location()) {
903  return variant(std::make_shared<location_callable>(current_unit_location_));
904  }
905 
906  return variant();
907 }
908 
910 {
911  add_input(inputs, "status");
912  add_input(inputs, "object");
913 
915  add_input(inputs, "current_loc");
916  }
917 }
918 
920 {
921  add_input(inputs, "turn_number");
922  add_input(inputs, "time_of_day");
923  add_input(inputs, "side_number");
924  add_input(inputs, "sides");
925  add_input(inputs, "units");
926  add_input(inputs, "map");
927 }
928 
929 variant gamestate_callable::get_value(const std::string &key) const
930 {
931  if(key == "turn_number") {
932  return variant(resources::tod_manager->turn());
933  } else if(key == "time_of_day") {
934  return variant(resources::tod_manager->get_time_of_day().id);
935  } else if(key == "side_number") {
936  return variant(resources::controller->current_side());
937  } else if(key == "sides") {
938  std::vector<variant> vars;
939  for(const auto& team : resources::gameboard->teams()) {
940  vars.emplace_back(std::make_shared<team_callable>(team));
941  }
942  return variant(vars);
943  } else if(key == "units") {
944  std::vector<variant> vars;
945  for(const auto& unit : resources::gameboard->units()) {
946  vars.emplace_back(std::make_shared<unit_callable>(unit));
947  }
948  return variant(vars);
949  } else if(key == "map") {
950  return variant(std::make_shared<gamemap_callable>(*resources::gameboard));
951  }
952 
953  return variant();
954 }
955 
957 {
958  add_input(inputs, "event");
959  add_input(inputs, "event_id");
960  add_input(inputs, "event_data");
961  add_input(inputs, "loc");
962  add_input(inputs, "unit");
963  add_input(inputs, "weapon");
964  add_input(inputs, "second_loc");
965  add_input(inputs, "second_unit");
966  add_input(inputs, "second_weapon");
967 }
968 
969 variant event_callable::get_value(const std::string &key) const
970 {
971  if(key == "event") {
972  return variant(event_info.name);
973  } else if(key == "event_id") {
974  return variant(event_info.id);
975  } else if(key == "loc") {
976  return variant(std::make_shared<location_callable>(event_info.loc1));
977  } else if(key == "second_loc") {
978  return variant(std::make_shared<location_callable>(event_info.loc2));
979  } else if(key == "event_data") {
980  return variant(std::make_shared<config_callable>(event_info.data));
981  } else if(key == "unit") {
982  if(auto u1 = event_info.loc1.get_unit()) {
983  return variant(std::make_shared<unit_callable>(*u1));
984  }
985  } else if(key == "second_unit") {
986  if(auto u2 = event_info.loc2.get_unit()) {
987  return variant(std::make_shared<unit_callable>(*u2));
988  }
989  } else if(key == "weapon") {
990  if(event_info.data.has_child("first")) {
991  first_weapon = std::make_shared<attack_type>(event_info.data.mandatory_child("first"));
992  return variant(std::make_shared<attack_type_callable>(*first_weapon));
993  }
994  } else if(key == "second_weapon") {
995  if(event_info.data.has_child("second")) {
996  second_weapon = std::make_shared<attack_type>(event_info.data.mandatory_child("second"));
997  return variant(std::make_shared<attack_type_callable>(*second_weapon));
998  }
999  }
1000 
1001  return variant();
1002 }
1003 
1004 } // namespace wfl
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:159
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:367
const_attr_itors attribute_range() const
Definition: config.cpp:763
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:317
bool has_attribute(config_key_type key) const
Definition: config.cpp:155
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:887
child_itors child_range(config_key_type key)
Definition: config.cpp:273
std::string hash() const
Definition: config.cpp:1287
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:74
bool uses_shroud() const
Definition: team.hpp:303
const std::string & color() const
Definition: team.hpp:242
config & variables()
Definition: team.hpp:348
int side() const
Definition: team.hpp:174
const std::string & faction() const
Definition: team.hpp:296
int village_support() const
Definition: team.hpp:185
int recall_cost() const
Definition: team.hpp:179
const std::string & team_name() const
Definition: team.hpp:282
bool is_local_human() const
Definition: team.hpp:253
int village_gold() const
Definition: team.hpp:178
team_shared_vision::type share_vision() const
Definition: team.hpp:377
int gold() const
Definition: team.hpp:175
const t_string & faction_name() const
Definition: team.hpp:297
bool carryover_add() const
Definition: team.hpp:343
int carryover_percentage() const
Definition: team.hpp:341
bool is_network() const
Definition: team.hpp:248
int total_income() const
Definition: team.hpp:182
const std::string & save_id() const
Definition: team.hpp:217
bool is_local_ai() const
Definition: team.hpp:254
double carryover_bonus() const
Definition: team.hpp:345
int start_gold() const
Definition: team.hpp:176
const std::string & flag_icon() const
Definition: team.hpp:287
int base_income() const
Definition: team.hpp:177
bool uses_fog() const
Definition: team.hpp:304
const std::string & flag() const
Definition: team.hpp:286
bool hidden() const
Definition: team.hpp:333
recall_list_manager & recall_list()
Definition: team.hpp:201
const std::set< std::string > & recruits() const
Definition: team.hpp:209
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:36
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:543
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:577
bool musthave_status(const std::string &status) const
Definition: types.cpp:672
std::vector< std::string > get_ability_list() const
Definition: types.cpp:604
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
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
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:968
map_location loc_
int w
Interfaces for manipulating version numbers of engine, add-ons, etc.
std::vector< std::string > get_ability_list() const
Get a list of all abilities by ID.
Definition: abilities.cpp:267
unit_formula_manager & formula_manager() const
Get the unit formula manager.
Definition: unit.hpp:1895
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:905
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:1349
const std::string & type_id() const
The id of this unit's type.
Definition: unit.cpp:2012
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:1332
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:927
int max_attacks() const
The maximum number of attacks this unit may perform per turn, usually 1.
Definition: unit.hpp:978
int attacks_left() const
Gets the remaining number of attacks this unit can perform this turn.
Definition: unit.hpp:994
std::size_t advancements_count() const
Definition: unit.hpp:1549
std::size_t traits_count() const
Definition: unit.hpp:1539
std::size_t objects_count() const
Definition: unit.hpp:1544
bool get_emit_zoc() const
Gets the raw zone-of-control flag, disregarding incapacitated.
Definition: unit.hpp:1376
const movetype & movement_type() const
Get the unit's movement type.
Definition: unit.hpp:1462
map_location::DIRECTION facing() const
The current direction this unit is facing within its hex.
Definition: unit.hpp:1405
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
Definition: unit.hpp:1314
int total_movement() const
The maximum moves this unit has.
Definition: unit.hpp:1298
bool resting() const
Checks whether this unit is 'resting'.
Definition: unit.hpp:1358
bool is_flying() const
Check if the unit is a flying unit.
Definition: unit.hpp:1498
std::vector< std::string > get_advancements_list() const
Definition: unit.hpp:1139
std::vector< std::string > get_objects_list() const
Definition: unit.hpp:1134
int upkeep() const
Gets the amount of gold this unit costs a side per turn.
Definition: unit.cpp:1770
std::vector< std::string > get_traits_list() const
Gets a list of the traits this unit currently has, including hidden traits.
Definition: unit.hpp:1129
Standard logging facilities (interface).
std::unique_ptr< window > build(const builder_window::window_resolution &definition)
Builds a window.
::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:140
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:38
int wml_y() const
Definition: location.hpp:154
static const map_location & null_location()
Definition: location.hpp:81
int wml_x() const
Definition: location.hpp:153
int do_compare(const map_location &a) const
three-way comparator
Definition: location.hpp:105
static std::string write_direction(DIRECTION dir)
Definition: location.cpp:140
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition: enum_base.hpp:46
variant operator()(bool b) const
variant operator()(const std::string &s) const
variant operator()(std::monostate) 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