The Battle for Wesnoth  1.17.23+dev
callable_objects.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2023
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  if(key == "x") {
196  return variant();
197  }
198 
199  return variant(loc_.wml_x());
200  } else if(key == "y") {
202  return variant();
203  }
204 
205  return variant(loc_.wml_y());
206  } else if(key == "loc") {
208  return variant();
209  }
210 
211  return variant(std::make_shared<location_callable>(loc_));
212  } else if(key == "terrain") {
214  return variant();
215  }
216  return variant(std::make_shared<terrain_callable>(*resources::gameboard, loc_));
217  } else if(key == "id") {
218  return variant(u_.id());
219  } else if(key == "type") {
220  return variant(u_.type_id());
221  } else if(key == "name") {
222  return variant(u_.name());
223  } else if(key == "usage") {
224  return variant(u_.usage());
225  } else if(key == "leader" || key == "canrecruit") {
226  return variant(u_.can_recruit());
227  } else if(key == "undead") {
228  return variant(u_.get_state("not_living") ? 1 : 0);
229  } else if(key == "attacks") {
230  std::vector<variant> res;
231  for(const attack_type& att : u_.attacks()) {
232  res.emplace_back(std::make_shared<attack_type_callable>(att));
233  }
234 
235  return variant(res);
236  } else if(key == "abilities") {
238  } else if(key == "hitpoints") {
239  return variant(u_.hitpoints());
240  } else if(key == "max_hitpoints") {
241  return variant(u_.max_hitpoints());
242  } else if(key == "experience") {
243  return variant(u_.experience());
244  } else if(key == "max_experience") {
245  return variant(u_.max_experience());
246  } else if(key == "level" || key == "full") {
247  // This allows writing "upkeep == full"
248  return variant(u_.level());
249  } else if(key == "total_movement" || key == "max_moves") {
250  return variant(u_.total_movement());
251  } else if(key == "movement_left" || key == "moves") {
252  return variant(u_.movement_left());
253  } else if(key == "attacks_left") {
254  return variant(u_.attacks_left());
255  } else if(key == "max_attacks") {
256  return variant(u_.max_attacks());
257  } else if(key == "traits") {
259  } else if(key == "extra_recruit") {
261  } else if(key == "advances_to") {
263  } else if(key == "states" || key == "status") {
265  } else if(key == "side") {
266  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.");
267  return variant(u_.side()-1);
268  } else if(key == "side_number") {
269  return variant(u_.side());
270  } else if(key == "cost") {
271  return variant(u_.cost());
272  } else if(key == "upkeep") {
273  return variant(u_.upkeep());
274  } else if(key == "loyal") {
275  // So we can write "upkeep == loyal"
276  return variant(0);
277  } else if(key == "hidden") {
278  return variant(u_.get_hidden());
279  } else if(key == "petrified") {
280  return variant(u_.incapacitated());
281  } else if(key == "resting") {
282  return variant(u_.resting());
283  } else if(key == "role") {
284  return variant(u_.get_role());
285  } else if(key == "race") {
286  return variant(u_.race()->id());
287  } else if(key == "gender") {
288  return variant(gender_string(u_.gender()));
289  } else if(key == "variation") {
290  return variant(u_.variation());
291  } else if(key == "zoc") {
292  return variant(u_.get_emit_zoc());
293  } else if(key == "alignment") {
295  } else if(key == "facing") {
297  } else if(key == "resistance" || key == "movement_cost" || key == "vision_cost" || key == "jamming_cost" || key == "defense") {
298  const auto& mt = u_.movement_type();
299  config cfg;
300  bool needs_flip = false;
301  if(key == "resistance") {
302  mt.get_resistances().write(cfg);
303  needs_flip = true;
304  } else if(key == "movement_cost") {
305  mt.get_movement().write(cfg);
306  } else if(key == "vision_cost") {
307  mt.get_vision().write(cfg);
308  } else if(key == "jamming_cost") {
309  mt.get_jamming().write(cfg);
310  } else if(key == "defense") {
311  mt.get_defense().write(cfg);
312  needs_flip = true;
313  }
314  std::map<variant, variant> res;
315  for(const auto& p : cfg.attribute_range()) {
316  int val = p.second;
317  if(needs_flip) {
318  val = 100 - val;
319  }
320  res.emplace(variant(p.first), variant(val));
321  }
322 
323  return variant(res);
324  } else if(key == "flying") {
325  return variant(u_.is_flying());
326  } else if(key == "vars") {
329  }
330 
331  return variant();
332  } else if(key == "wml_vars") {
333  return variant(std::make_shared<config_callable>(u_.variables()));
334  } else if(key == "n" || key == "s" || key == "ne" || key == "se" || key == "nw" || key == "sw" ||
335  key == "lawful" || key == "neutral" || key == "chaotic" || key == "liminal" ||
336  key == "male" || key == "female")
337  {
338  return variant(key);
339  }
340 
341  return variant();
342 }
343 
345 {
346  add_input(inputs, "x");
347  add_input(inputs, "y");
348  add_input(inputs, "loc");
349  add_input(inputs, "terrain");
350  add_input(inputs, "id");
351  add_input(inputs, "type");
352  add_input(inputs, "name");
353  add_input(inputs, "canrecruit");
354  add_input(inputs, "undead");
355  add_input(inputs, "traits");
356  add_input(inputs, "attacks");
357  add_input(inputs, "abilities");
358  add_input(inputs, "hitpoints");
359  add_input(inputs, "max_hitpoints");
360  add_input(inputs, "experience");
361  add_input(inputs, "max_experience");
362  add_input(inputs, "level");
363  add_input(inputs, "moves");
364  add_input(inputs, "max_moves");
365  add_input(inputs, "attacks_left");
366  add_input(inputs, "max_attacks");
367  add_input(inputs, "side_number");
368  add_input(inputs, "extra_recruit");
369  add_input(inputs, "advances_to");
370  add_input(inputs, "status");
371  add_input(inputs, "cost");
372  add_input(inputs, "usage");
373  add_input(inputs, "upkeep");
374  add_input(inputs, "hidden");
375  add_input(inputs, "petrified");
376  add_input(inputs, "resting");
377  add_input(inputs, "role");
378  add_input(inputs, "race");
379  add_input(inputs, "gender");
380  add_input(inputs, "variation");
381  add_input(inputs, "zoc");
382  add_input(inputs, "alignment");
383  add_input(inputs, "facing");
384  add_input(inputs, "resistance");
385  add_input(inputs, "movement_cost");
386  add_input(inputs, "vision_cost");
387  add_input(inputs, "jamming_cost");
388  add_input(inputs, "defense");
389  add_input(inputs, "flying");
390  add_input(inputs, "vars");
391  add_input(inputs, "wml_vars");
392 }
393 
394 int unit_callable::do_compare(const formula_callable* callable) const
395 {
396  const unit_callable* u_callable = dynamic_cast<const unit_callable*>(callable);
397  if(u_callable == nullptr) {
398  return formula_callable::do_compare(callable);
399  }
400 
401  return u_.underlying_id() - u_callable->u_.underlying_id();
402 }
403 
404 variant unit_type_callable::get_value(const std::string& key) const
405 {
406  if(key == "id") {
407  return variant(u_.id());
408  } else if(key == "type") {
409  return variant(u_.type_name());
410  } else if(key == "alignment") {
412  } else if(key == "race") {
413  return variant(u_.race_id());
414  } else if(key == "abilities") {
416  } else if(key == "traits") {
417  std::vector<variant> res;
418  for(const auto& config : u_.possible_traits()) {
419  res.emplace_back(config["id"].str());
420  }
421 
422  return variant(res);
423  } else if(key == "attacks") {
424  std::vector<variant> res;
425  for(const attack_type& att : u_.attacks()) {
426  res.emplace_back(std::make_shared<attack_type_callable>(att));
427  }
428 
429  return variant(res);
430  } else if(key == "hitpoints" || key == "max_hitpoints") {
431  return variant(u_.hitpoints());
432  } else if(key == "experience" || key == "max_experience") {
433  return variant(u_.experience_needed(true));
434  } else if(key == "level") {
435  return variant(u_.level());
436  } else if(key == "total_movement" || key == "max_moves" || key == "moves") {
437  return variant(u_.movement());
438  } else if(key == "unpoisonable") {
439  return variant(u_.musthave_status("unpoisonable"));
440  } else if(key == "unslowable") {
441  return variant(u_.musthave_status("unslowable"));
442  } else if(key == "unpetrifiable") {
443  return variant(u_.musthave_status("unpetrifiable"));
444  } else if(key == "undrainable") {
445  return variant(u_.musthave_status("undrainable"));
446  } else if(key == "unplagueable") {
447  return variant(u_.musthave_status("unplagueable"));
448  } else if(key == "cost") {
449  return variant(u_.cost());
450  } else if(key == "recall_cost") {
451  return variant(u_.recall_cost());
452  } else if(key == "usage") {
453  return variant(u_.usage());
454  }
455 
456  return variant();
457 }
458 
460 {
461  add_input(inputs, "id");
462  add_input(inputs, "type");
463  add_input(inputs, "race");
464  add_input(inputs, "alignment");
465  add_input(inputs, "abilities");
466  add_input(inputs, "traits");
467  add_input(inputs, "attacks");
468  add_input(inputs, "hitpoints");
469  add_input(inputs, "experience");
470  add_input(inputs, "level");
471  add_input(inputs, "total_movement");
472  add_input(inputs, "undead");
473  add_input(inputs, "cost");
474  add_input(inputs, "recall_cost");
475  add_input(inputs, "usage");
476 }
477 
479 {
480  const unit_type_callable* u_callable = dynamic_cast<const unit_type_callable*>(callable);
481  if(u_callable == nullptr) {
482  return formula_callable::do_compare(callable);
483  }
484 
485  return u_.id().compare(u_callable->u_.id());
486 }
487 
489 #ifdef USING_BOOST_VARIANT
490  : public boost::static_visitor<variant>
491 #endif
492 {
493  variant operator()(bool b) const { return variant(b ? 1 : 0); }
494  variant operator()(int i) const { return variant(i); }
495  variant operator()(unsigned long long i) const { return variant(i); }
496  variant operator()(double i) const { return variant(i * 1000, variant::DECIMAL_VARIANT); }
497  // TODO: Should comma-separated lists of stuff be returned as a list?
498  // The challenge is to distinguish them from ordinary strings that happen to contain a comma
499  // (or should we assume that such strings will be translatable?).
500  variant operator()(const std::string& s) const { return variant(s); }
501  variant operator()(const t_string& s) const { return variant(s.str()); }
502  variant operator()(utils::monostate) const { return variant(); }
503 };
504 
505 variant config_callable::get_value(const std::string& key) const
506 {
507  if(cfg_.has_attribute(key)) {
508  return cfg_[key].apply_visitor(fai_variant_visitor());
509  } else if(cfg_.has_child(key)) {
510  std::vector<variant> result;
511  for(const auto& child : cfg_.child_range(key)) {
512  result.emplace_back(std::make_shared<config_callable>(child));
513  }
514 
515  return variant(result);
516  } else if(key == "__all_children") {
517  std::vector<variant> result;
518  for(const auto child : cfg_.all_children_range()) {
519  const variant cfg_child(std::make_shared<config_callable>(child.cfg));
520  const variant kv(std::make_shared<key_value_pair>(variant(child.key), cfg_child));
521  result.push_back(kv);
522  }
523 
524  return variant(result);
525  } else if(key == "__children") {
526  std::map<std::string, std::vector<variant>> build;
527  for(const auto child : cfg_.all_children_range()) {
528  const variant cfg_child(std::make_shared<config_callable>(child.cfg));
529  build[child.key].push_back(cfg_child);
530  }
531 
532  std::map<variant,variant> result;
533  for(auto& p : build) {
534  result[variant(p.first)] = variant(p.second);
535  }
536 
537  return variant(result);
538  } else if(key == "__attributes") {
539  std::map<variant,variant> result;
540  for(const auto& val : cfg_.attribute_range()) {
541  result[variant(val.first)] = val.second.apply_visitor(fai_variant_visitor());
542  }
543 
544  return variant(result);
545  }
546 
547  return variant();
548 }
549 
551 {
552  add_input(inputs, "__all_children");
553  add_input(inputs, "__children");
554  add_input(inputs, "__attributes");
555 
556  for(const auto& val : cfg_.attribute_range()) {
557  if(val.first.find_first_not_of(formula::id_chars) != std::string::npos) {
558  add_input(inputs, val.first);
559  }
560  }
561 }
562 
564 {
565  const config_callable* cfg_callable = dynamic_cast<const config_callable*>(callable);
566  if(cfg_callable == nullptr) {
567  return formula_callable::do_compare(callable);
568  }
569 
570  if(cfg_ == cfg_callable->get_config()) {
571  return 0;
572  }
573 
574  return cfg_.hash().compare(cfg_callable->get_config().hash());
575 }
576 
577 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))
578 {
579  type_ = TERRAIN_C;
580 }
581 
582 variant terrain_callable::get_value(const std::string& key) const
583 {
584  if(key == "x") {
585  return variant(loc_.wml_x());
586  } else if(key == "y") {
587  return variant(loc_.wml_y());
588  } else if(key == "loc") {
589  return variant(std::make_shared<location_callable>(loc_));
590  } else if(key == "id") {
591  return variant(std::string(t_.id()));
592  } else if(key == "name") {
593  return variant(t_.name());
594  } else if(key == "editor_name") {
595  return variant(t_.editor_name());
596  } else if(key == "description") {
597  return variant(t_.description());
598  } else if(key == "icon") {
599  return variant(t_.icon_image());
600  } else if(key == "light") {
601  return variant(t_.light_bonus(0));
602  } else if(key == "village") {
603  return variant(t_.is_village());
604  } else if(key == "castle") {
605  return variant(t_.is_castle());
606  } else if(key == "keep") {
607  return variant(t_.is_keep());
608  } else if(key == "healing") {
609  return variant(t_.gives_healing());
610  } else if(key == "owner") {
611  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.");
612  return variant(owner_ - 1);
613  } else if(key == "owner_side") {
614  return variant(owner_);
615  }
616 
617  return variant();
618 }
619 
621 {
622  add_input(inputs, "x");
623  add_input(inputs, "y");
624  add_input(inputs, "loc");
625  add_input(inputs, "id");
626  add_input(inputs, "name");
627  add_input(inputs, "editor_name");
628  add_input(inputs, "description");
629  add_input(inputs, "icon");
630  add_input(inputs, "light");
631  add_input(inputs, "village");
632  add_input(inputs, "castle");
633  add_input(inputs, "keep");
634  add_input(inputs, "healing");
635  add_input(inputs, "owner_side");
636 }
637 
639 {
640  const terrain_callable* terr_callable = dynamic_cast<const terrain_callable*>(callable);
641  if(terr_callable == nullptr) {
642  return formula_callable::do_compare(callable);
643  }
644 
645  const map_location& other_loc = terr_callable->loc_;
646  return loc_.do_compare(other_loc);
647 }
648 
650  return board_.map();
651 }
652 
654 {
655  add_input(inputs, "w");
656  add_input(inputs, "h");
657 }
658 
659 variant gamemap_callable::get_value(const std::string& key) const
660 {
661  if(key == "terrain") {
662  int w = get_gamemap().w();
663  int h = get_gamemap().h();
664 
665  std::vector<variant> vars;
666  for(int i = 0; i < w; i++) {
667  for(int j = 0; j < h; j++) {
668  const map_location loc(i, j);
669  vars.emplace_back(std::make_shared<terrain_callable>(board_, loc));
670  }
671  }
672 
673  return variant(vars);
674  } else if(key == "gamemap") {
675  int w = get_gamemap().w();
676  int h = get_gamemap().h();
677 
678  std::map<variant, variant> vars;
679  for(int i = 0; i < w; i++) {
680  for(int j = 0; j < h; j++) {
681  const map_location loc(i, j);
682  vars.emplace(std::make_shared<location_callable>(loc), std::make_shared<terrain_callable>(board_, loc));
683  }
684  }
685 
686  return variant(vars);
687  } else if(key == "w") {
688  return variant(get_gamemap().w());
689  } else if(key == "h") {
690  return variant(get_gamemap().h());
691  } else {
692  return variant();
693  }
694 }
695 
697 {
698  add_input(inputs, "side_number");
699  add_input(inputs, "id");
700  add_input(inputs, "gold");
701  add_input(inputs, "start_gold");
702  add_input(inputs, "base_income");
703  add_input(inputs, "total_income");
704  add_input(inputs, "village_gold");
705  add_input(inputs, "village_support");
706  add_input(inputs, "recall_cost");
707  add_input(inputs, "name");
708  add_input(inputs, "is_human");
709  add_input(inputs, "is_ai");
710  add_input(inputs, "is_network");
711  add_input(inputs, "fog");
712  add_input(inputs, "shroud");
713  add_input(inputs, "hidden");
714  add_input(inputs, "flag");
715  add_input(inputs, "flag_icon");
716  add_input(inputs, "team_name");
717  add_input(inputs, "faction");
718  add_input(inputs, "faction_name");
719  add_input(inputs, "color");
720  add_input(inputs, "share_vision");
721  add_input(inputs, "carryover_bonus");
722  add_input(inputs, "carryover_percentage");
723  add_input(inputs, "carryover_add");
724  add_input(inputs, "recruit");
725  add_input(inputs, "wml_vars");
726 }
727 
728 variant team_callable::get_value(const std::string& key) const
729 {
730  if(key == "side") {
731  deprecated_message("team.side", DEP_LEVEL::INDEFINITE, version_info("1.17"), "Use side_number instead.");
732  return variant(team_.side());
733  } else if(key == "side_number") {
734  return variant(team_.side());
735  } else if(key == "id") {
736  return variant(team_.save_id());
737  } else if(key == "save_id") {
738  return variant(team_.save_id());
739  } else if(key == "gold") {
740  return variant(team_.gold());
741  } else if(key == "start_gold") {
742  return variant(team_.start_gold());
743  } else if(key == "base_income") {
744  return variant(team_.base_income());
745  } else if(key == "total_income") {
746  return variant(team_.total_income());
747  } else if(key == "village_gold") {
748  return variant(team_.village_gold());
749  } else if(key == "village_support") {
750  return variant(team_.village_support());
751  } else if(key == "recall_cost") {
752  return variant(team_.recall_cost());
753  } else if(key == "is_human") {
754  return variant(team_.is_local_human());
755  } else if(key == "is_ai") {
756  return variant(team_.is_local_ai());
757  } else if(key == "is_network") {
758  return variant(team_.is_network());
759  } else if(key == "fog") {
760  return variant(team_.uses_fog());
761  } else if(key == "shroud") {
762  return variant(team_.uses_shroud());
763  } else if(key == "hidden") {
764  return variant(team_.hidden());
765  } else if(key == "flag") {
766  return variant(team_.flag());
767  } else if(key == "flag_icon") {
768  return variant(team_.flag_icon());
769  } else if(key == "team_name") {
770  return variant(team_.team_name());
771  } else if(key == "color") {
772  return variant(team_.color());
773  } else if(key == "share_vision") {
775  } else if(key == "carryover_bonus") {
777  } else if(key == "carryover_percentage") {
779  } else if(key == "carryover_add") {
780  return variant(team_.carryover_add());
781  } else if(key == "recruit") {
782  std::vector<variant> result;
783  for(const auto& recruit : team_.recruits()) {
784  result.emplace_back(recruit);
785  }
786  return variant(result);
787  } else if(key == "recall") {
788  std::vector<variant> result;
789  for(const auto& u : team_.recall_list()) {
790  result.push_back(std::make_shared<unit_callable>(*u));
791  }
792  return variant(result);
793  } else if(key == "wml_vars") {
794  return variant(std::make_shared<config_callable>(team_.variables()));
795  }
796 
797  return variant();
798 }
799 
800 variant set_var_callable::get_value(const std::string& key) const
801 {
802  if(key == "key") {
803  return variant(key_);
804  } else if(key == "value") {
805  return value_;
806  }
807 
808  return variant();
809 }
810 
812 {
813  add_input(inputs, "key");
814  add_input(inputs, "value");
815 }
816 
818 {
819  //if(infinite_loop_guardian_.set_var_check()) {
820  if(auto obj = ctxt.try_convert<formula_callable>()) {
821  LOG_SF << "Setting variable: " << key_ << " -> " << value_.to_debug_string();
822  obj->mutate_value(key_, value_);
823  return variant(true);
824  }
825  //}
826  //too many calls in a row - possible infinite loop
827  ERR_SF << "ERROR #" << 5001 << " while executing 'set_var' formula function";
828 
829  return variant(std::make_shared<safe_call_result>(fake_ptr(), 5001));
830 }
831 
832 variant safe_call_callable::get_value(const std::string& key) const
833 {
834  if(key == "main") {
835  return variant(main_);
836  } else if(key == "backup") {
837  return variant(backup_);
838  }
839 
840  return variant();
841 }
842 
844 {
845  add_input(inputs, "main");
846  add_input(inputs, "backup");
847 }
848 
850 {
851  variant res;
852  if(auto action = main_.try_convert<action_callable>()) {
853  res = action->execute_self(ctxt);
854  }
855 
856  if(res.try_convert<safe_call_result>()) {
857  /* If we have safe_call formula and either an error occurred, or the current action
858  * was not recognized, then evaluate backup formula from safe_call and execute it
859  * during the next loop
860  */
861 
862  map_formula_callable callable(ctxt.as_callable());
863  callable.add("error", res);
864 
865  /* Store the result in safe_call_callable in case we would like to display it to the user,
866  * for example if this formula was executed from the commandline.
867  */
868  backup_ = get_backup()->evaluate(callable);
869  ctxt.execute_variant(backup_);
870  }
871  return variant(true);
872 }
873 
874 variant safe_call_result::get_value(const std::string& key) const
875 {
876  if(key == "status") {
877  return variant(status_);
878  } else if(key == "object") {
879  if(failed_callable_) {
880  return variant(failed_callable_);
881  }
882 
883  return variant();
884  } else if(key == "current_loc" && current_unit_location_ != map_location()) {
885  return variant(std::make_shared<location_callable>(current_unit_location_));
886  }
887 
888  return variant();
889 }
890 
892 {
893  add_input(inputs, "status");
894  add_input(inputs, "object");
895 
897  add_input(inputs, "current_loc");
898  }
899 }
900 
902 {
903  add_input(inputs, "turn_number");
904  add_input(inputs, "time_of_day");
905  add_input(inputs, "side_number");
906  add_input(inputs, "sides");
907  add_input(inputs, "units");
908  add_input(inputs, "map");
909 }
910 
911 variant gamestate_callable::get_value(const std::string &key) const
912 {
913  if(key == "turn_number") {
914  return variant(resources::tod_manager->turn());
915  } else if(key == "time_of_day") {
916  return variant(resources::tod_manager->get_time_of_day().id);
917  } else if(key == "side_number") {
918  return variant(resources::controller->current_side());
919  } else if(key == "sides") {
920  std::vector<variant> vars;
921  for(const auto& team : resources::gameboard->teams()) {
922  vars.emplace_back(std::make_shared<team_callable>(team));
923  }
924  return variant(vars);
925  } else if(key == "units") {
926  std::vector<variant> vars;
927  for(const auto& unit : resources::gameboard->units()) {
928  vars.emplace_back(std::make_shared<unit_callable>(unit));
929  }
930  return variant(vars);
931  } else if(key == "map") {
932  return variant(std::make_shared<gamemap_callable>(*resources::gameboard));
933  }
934 
935  return variant();
936 }
937 
939 {
940  add_input(inputs, "event");
941  add_input(inputs, "event_id");
942  add_input(inputs, "event_data");
943  add_input(inputs, "loc");
944  add_input(inputs, "unit");
945  add_input(inputs, "weapon");
946  add_input(inputs, "second_loc");
947  add_input(inputs, "second_unit");
948  add_input(inputs, "second_weapon");
949 }
950 
951 variant event_callable::get_value(const std::string &key) const
952 {
953  if(key == "event") {
954  return variant(event_info.name);
955  } else if(key == "event_id") {
956  return variant(event_info.id);
957  } else if(key == "loc") {
958  return variant(std::make_shared<location_callable>(event_info.loc1));
959  } else if(key == "second_loc") {
960  return variant(std::make_shared<location_callable>(event_info.loc2));
961  } else if(key == "event_data") {
962  return variant(std::make_shared<config_callable>(event_info.data));
963  } else if(key == "unit") {
964  if(auto u1 = event_info.loc1.get_unit()) {
965  return variant(std::make_shared<unit_callable>(*u1));
966  }
967  } else if(key == "second_unit") {
968  if(auto u2 = event_info.loc2.get_unit()) {
969  return variant(std::make_shared<unit_callable>(*u2));
970  }
971  } else if(key == "weapon") {
972  if(event_info.data.has_child("first")) {
973  first_weapon = std::make_shared<attack_type>(event_info.data.mandatory_child("first"));
974  return variant(std::make_shared<attack_type_callable>(*first_weapon));
975  }
976  } else if(key == "second_weapon") {
977  if(event_info.data.has_child("second")) {
978  second_weapon = std::make_shared<attack_type>(event_info.data.mandatory_child("second"));
979  return variant(std::make_shared<attack_type_callable>(*second_weapon));
980  }
981  }
982 
983  return variant();
984 }
985 
986 } // namespace wfl
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:161
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:371
const_attr_itors attribute_range() const
Definition: config.cpp:767
bool has_child(config_key_type key) const
Determine whether a config has a child or not.
Definition: config.cpp:321
bool has_attribute(config_key_type key) const
Definition: config.cpp:159
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:891
child_itors child_range(config_key_type key)
Definition: config.cpp:277
std::string hash() const
Definition: config.cpp:1291
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:76
bool uses_shroud() const
Definition: team.hpp:305
const std::string & color() const
Definition: team.hpp:244
config & variables()
Definition: team.hpp:350
int side() const
Definition: team.hpp:176
int village_support() const
Definition: team.hpp:187
int recall_cost() const
Definition: team.hpp:181
const std::string & team_name() const
Definition: team.hpp:284
bool is_local_human() const
Definition: team.hpp:255
int village_gold() const
Definition: team.hpp:180
team_shared_vision::type share_vision() const
Definition: team.hpp:379
int gold() const
Definition: team.hpp:177
bool carryover_add() const
Definition: team.hpp:345
int carryover_percentage() const
Definition: team.hpp:343
bool is_network() const
Definition: team.hpp:250
int total_income() const
Definition: team.hpp:184
const std::string & save_id() const
Definition: team.hpp:219
bool is_local_ai() const
Definition: team.hpp:256
double carryover_bonus() const
Definition: team.hpp:347
int start_gold() const
Definition: team.hpp:178
const std::string & flag_icon() const
Definition: team.hpp:289
int base_income() const
Definition: team.hpp:179
bool uses_fog() const
Definition: team.hpp:306
const std::string & flag() const
Definition: team.hpp:288
bool hidden() const
Definition: team.hpp:335
recall_list_manager & recall_list()
Definition: team.hpp:203
const std::set< std::string > & recruits() const
Definition: team.hpp:211
const std::string & icon_image() const
Definition: terrain.hpp:44
const t_string & name() const
Definition: terrain.hpp:48
bool is_keep() const
Definition: terrain.hpp:143
bool is_castle() const
Definition: terrain.hpp:142
const std::string & id() const
Definition: terrain.hpp:52
const t_string & description() const
Definition: terrain.hpp:50
bool is_village() const
Definition: terrain.hpp:141
int light_bonus(int base) const
Returns the light (lawful) bonus for this terrain when the time of day gives a base bonus.
Definition: terrain.hpp:132
const t_string & editor_name() const
Definition: terrain.hpp:49
int gives_healing() const
Definition: terrain.hpp:140
const wfl::map_formula_callable_ptr & formula_vars() const
const std::string & id() const
Definition: race.hpp:35
std::string race_id() const
Returns the ID of this type's race without the need to build the type.
Definition: types.hpp:274
const std::string & id() const
The id for this unit_type.
Definition: types.hpp:144
int hitpoints() const
Definition: types.hpp:164
const_attack_itors attacks() const
Definition: types.cpp:543
const std::string & usage() const
Definition: types.hpp:178
int movement() const
Definition: types.hpp:169
int cost() const
Definition: types.hpp:175
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:141
config::const_child_itors possible_traits() const
Definition: types.hpp:233
int level() const
Definition: types.hpp:167
unit_alignments::type alignment() const
Definition: types.hpp:195
int recall_cost() const
Definition: types.hpp:168
This class represents a single unit of a specific type.
Definition: unit.hpp:135
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:656
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:646
std::string deprecated_message(const std::string &elem_name, DEP_LEVEL level, const version_info &version, const std::string &detail)
Definition: deprecation.cpp:30
#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:269
unit_formula_manager & formula_manager() const
Get the unit formula manager.
Definition: unit.hpp:1842
int max_hitpoints() const
The max number of hitpoints this unit can have.
Definition: unit.hpp:507
unit_alignments::type alignment() const
The alignment of this unit.
Definition: unit.hpp:477
bool incapacitated() const
Check if the unit has been petrified.
Definition: unit.hpp:907
int level() const
The current level of this unit.
Definition: unit.hpp:561
std::string usage() const
Gets this unit's usage.
Definition: unit.hpp:688
const std::string & get_role() const
Gets this unit's role.
Definition: unit.hpp:671
const std::vector< std::string > & recruits() const
The type IDs of the other units this unit may recruit, if possible.
Definition: unit.hpp:626
const std::string & variation() const
The ID of the variation of this unit's type.
Definition: unit.hpp:574
int hitpoints() const
The current number of hitpoints this unit has.
Definition: unit.hpp:501
int cost() const
How much gold is required to recruit this unit.
Definition: unit.hpp:635
bool get_state(const std::string &state) const
Check if the unit is affected by a status effect.
Definition: unit.cpp:1328
const std::string & type_id() const
The id of this unit's type.
Definition: unit.cpp:1938
bool get_hidden() const
Gets whether this unit is currently hidden on the map.
Definition: unit.hpp:722
const std::set< std::string > get_states() const
Get the status effects currently affecting the unit.
Definition: unit.cpp:1311
const unit_race * race() const
Gets this unit's race.
Definition: unit.hpp:495
int experience() const
The current number of experience points this unit has.
Definition: unit.hpp:525
bool can_recruit() const
Whether this unit can recruit other units - ie, are they a leader unit.
Definition: unit.hpp:614
const std::string & id() const
Gets this unit's id.
Definition: unit.hpp:382
int side() const
The side this unit belongs to.
Definition: unit.hpp:345
config & variables()
Gets any user-defined variables this unit 'owns'.
Definition: unit.hpp:705
std::size_t underlying_id() const
This unit's unique internal ID.
Definition: unit.hpp:394
int max_experience() const
The max number of experience points this unit can have.
Definition: unit.hpp:531
unit_race::GENDER gender() const
The gender of this unit.
Definition: unit.hpp:467
const t_string & name() const
Gets this unit's translatable display name.
Definition: unit.hpp:405
const advances_to_t & advances_to() const
Gets the possible types this unit can advance to on level-up.
Definition: unit.hpp:246
attack_itors attacks()
Gets an iterator over this unit's attacks.
Definition: unit.hpp:929
int max_attacks() const
The maximum number of attacks this unit may perform per turn, usually 1.
Definition: unit.hpp:980
int attacks_left() const
Gets the remaining number of attacks this unit can perform this turn.
Definition: unit.hpp:996
bool get_emit_zoc() const
Gets the raw zone-of-control flag, disregarding incapacitated.
Definition: unit.hpp:1346
const movetype & movement_type() const
Get the unit's movement type.
Definition: unit.hpp:1432
map_location::DIRECTION facing() const
The current direction this unit is facing within its hex.
Definition: unit.hpp:1375
int movement_left() const
Gets how far a unit can move, considering the incapacitated flag.
Definition: unit.hpp:1284
int total_movement() const
The maximum moves this unit has.
Definition: unit.hpp:1268
bool resting() const
Checks whether this unit is 'resting'.
Definition: unit.hpp:1328
bool is_flying() const
Check if the unit is a flying unit.
Definition: unit.hpp:1468
int upkeep() const
Gets the amount of gold this unit costs a side per turn.
Definition: unit.cpp:1731
std::vector< std::string > get_traits_list() const
Gets a list of the traits this unit currently has.
Definition: unit.cpp:873
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:30
game_board * gameboard
Definition: resources.cpp:21
play_controller * controller
Definition: resources.cpp:22
Definition: contexts.hpp:44
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:142
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:141
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition: enum_base.hpp:46
variant operator()(utils::monostate) const
variant operator()(bool b) const
variant operator()(const std::string &s) const
variant operator()(int i) const
variant operator()(unsigned long long i) const
variant operator()(const t_string &s) const
variant operator()(double i) const
mock_party p
static map_location::DIRECTION s
#define h
#define b