The Battle for Wesnoth  1.19.28+dev
synced_commands.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2014 - 2025
3  by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 #include "synced_commands.hpp"
17 #include <cassert>
18 #include <stdexcept>
19 
20 #include "log.hpp"
21 #include "map/location.hpp"
22 #include "game_data.hpp"
23 #include "units/unit.hpp"
24 #include "team.hpp"
25 #include "play_controller.hpp"
26 #include "actions/create.hpp"
27 #include "actions/advancement.hpp"
28 #include "actions/attack.hpp"
29 #include "actions/move.hpp"
30 #include "actions/undo.hpp"
32 #include "game_events/pump.hpp"
33 #include "map/map.hpp"
34 #include "recall_list_manager.hpp"
35 #include "resources.hpp"
36 #include "savegame.hpp"
38 #include "formula/string_utils.hpp"
39 #include "units/types.hpp"
40 #include "units/udisplay.hpp"
41 #include "whiteboard/manager.hpp"
42 #include "font/standard_colors.hpp"
43 
44 static lg::log_domain log_replay("replay");
45 #define DBG_REPLAY LOG_STREAM(debug, log_replay)
46 #define LOG_REPLAY LOG_STREAM(info, log_replay)
47 #define WRN_REPLAY LOG_STREAM(warn, log_replay)
48 #define ERR_REPLAY LOG_STREAM(err, log_replay)
49 
50 
51 /**
52  * @param[in] tag The replay tag for this action.
53  * @param[in] function The callback for this action.
54  */
55 synced_command::synced_command(const std::string & tag, handler function)
56 {
57  assert(registry().find( tag ) == registry().end());
58  registry()[tag] = function;
59 }
60 
62 {
63  //Use a pointer to ensure that this object is not destructed when the program ends.
64  static map* instance = new map();
65  return *instance;
66 }
67 
68 
69 SYNCED_COMMAND_HANDLER_FUNCTION(recruit, child, spectator)
70 {
71  int current_team_num = resources::controller->current_side();
72  team &current_team = resources::gameboard->get_team(current_team_num);
73 
75  map_location from(child.child_or_empty("from"), resources::gamedata);
76  // Validate "from".
77  if ( !from.valid() ) {
78  // This will be the case for AI recruits in replays saved
79  // before 1.11.2, so it is not more severe than a warning.
80  // EDIT: we broke compatibility with 1.11.2 anyway so we should give an error.
81  spectator.error("Missing leader location for recruitment.\n");
82  }
83  else if ( resources::gameboard->units().find(from) == resources::gameboard->units().end() ) {
84  // Sync problem?
85  std::stringstream errbuf;
86  errbuf << "Recruiting leader not found at " << from << ".\n";
87  spectator.error(errbuf.str());
88  }
89 
90  // Get the unit_type ID.
91  std::string type_id = child["type"];
92  if ( type_id.empty() ) {
93  spectator.error("Recruitment is missing a unit type.");
94  return false;
95  }
96 
97  const unit_type *u_type = unit_types.find(type_id);
98  if (!u_type) {
99  std::stringstream errbuf;
100  errbuf << "Recruiting illegal unit: '" << type_id << "'.\n";
101  spectator.error(errbuf.str());
102  return false;
103  }
104 
105  const std::string res = actions::find_recruit_location(current_team_num, loc, from, type_id);
106  if(!res.empty())
107  {
108  std::stringstream errbuf;
109  errbuf << "cannot recruit unit: " << res << "\n";
110  spectator.error(errbuf.str());
111  return false;
112  //we are already oos because the unit wasn't created, no need to keep the bookkeeping right...
113  }
114  const int beginning_gold = current_team.gold();
115 
116 
117 
118  if ( u_type->cost() > beginning_gold ) {
119  std::stringstream errbuf;
120  errbuf << "unit '" << type_id << "' is too expensive to recruit: "
121  << u_type->cost() << "/" << beginning_gold << "\n";
122  spectator.error(errbuf.str());
123  }
124 
125  actions::recruit_unit(*u_type, current_team_num, loc, from);
126 
127  LOG_REPLAY << "recruit: team=" << current_team_num << " '" << type_id << "' at (" << loc
128  << ") cost=" << u_type->cost() << " from gold=" << beginning_gold << ' '
129  << "-> " << current_team.gold();
130  return true;
131 }
132 
133 SYNCED_COMMAND_HANDLER_FUNCTION(recall, child, spectator)
134 {
135  int current_team_num = resources::controller->current_side();
136  team &current_team = resources::gameboard->get_team(current_team_num);
137 
138  const std::string& unit_id = child["value"];
140  map_location from(child.child_or_empty("from"), resources::gamedata);
141 
142  if(!actions::recall_unit(unit_id, current_team, loc, from, map_location::direction::indeterminate)) {
143  spectator.error("illegal recall: unit_id '" + unit_id + "' could not be found within the recall list.\n");
144  //when recall_unit returned false nothing happened so we can safety return false;
145  return false;
146  }
147  return true;
148 }
149 
150 SYNCED_COMMAND_HANDLER_FUNCTION(attack, child, spectator)
151 {
152  const auto destination = child.optional_child("destination");
153  const auto source = child.optional_child("source");
154  //check_checksums(*cfg);
155 
156  if (!destination) {
157  spectator.error("no destination found in attack\n");
158  return false;
159  }
160 
161  if (!source) {
162  spectator.error("no source found in attack \n");
163  return false;
164  }
165 
166  //we must get locations by value instead of by references, because the iterators
167  //may become invalidated later
168  const map_location src(source.value(), resources::gamedata);
169  const map_location dst(destination.value(), resources::gamedata);
170 
171  int weapon_num = child["weapon"].to_int();
172  // having defender_weapon in the replay fixes a bug (OOS) where one player (or observer) chooses a different defensive weapon.
173  // Xan pointed out this was a possibility: we calculate defense weapon
174  // now based on attack_prediction code, but this uses floating point
175  // calculations, which means that in the case where results are close,
176  // rounding differences can mean that both ends choose different weapons.
177  int def_weapon_num = child["defender_weapon"].to_int(-2);
178  if (def_weapon_num == -2) {
179  // Let's not gratuitously destroy backwards compatibility.
180  LOG_REPLAY << "Old data, having to guess weapon";
181  def_weapon_num = -1;
182  }
183 
185  if (!u.valid()) {
186  spectator.error("unfound location for source of attack\n");
187  return false;
188  }
189 
190  if (child.has_attribute("attacker_type")) {
191  const std::string &att_type_id = child["attacker_type"];
192  if (u->type_id() != att_type_id) {
193  WRN_REPLAY << "unexpected attacker type: " << att_type_id << "(game state gives: " << u->type_id() << ")";
194  }
195  }
196 
197  if (static_cast<unsigned>(weapon_num) >= u->attacks().size()) {
198  spectator.error("illegal weapon type in attack\n");
199  return false;
200  }
201 
203 
204  if (!tgt.valid()) {
205  std::stringstream errbuf;
206  errbuf << "unfound defender for attack: " << src << " -> " << dst << '\n';
207  spectator.error(errbuf.str());
208  return false;
209  }
210 
211  if (child.has_attribute("defender_type")) {
212  const std::string &def_type_id = child["defender_type"];
213  if (tgt->type_id() != def_type_id) {
214  WRN_REPLAY << "unexpected defender type: " << def_type_id << "(game state gives: " << tgt->type_id() << ")";
215  }
216  }
217 
218  if (def_weapon_num >= static_cast<int>(tgt->attacks().size())) {
219 
220  spectator.error("illegal defender weapon type in attack\n");
221  return false;
222  }
223 
224  DBG_REPLAY << "Attacker XP (before attack): " << u->experience();
225 
228  attack_unit_and_advance(src, dst, weapon_num, def_weapon_num, show);
229  return true;
230 }
231 
232 SYNCED_COMMAND_HANDLER_FUNCTION(disband, child, spectator)
233 {
234 
235  team& current_team = resources::controller->current_team();
236 
237  const std::string& unit_id = child["value"];
238  std::size_t old_size = current_team.recall_list().size();
239 
240  // Find the unit in the recall list.
241  unit_ptr dismissed_unit = current_team.recall_list().find_if_matches_id(unit_id);
242  if (!dismissed_unit) {
243  spectator.error("illegal disband\n");
244  return false;
245  }
246  //add dismissal to the undo stack
247  resources::undo_stack->add_dismissal(dismissed_unit);
248 
249  current_team.recall_list().erase_if_matches_id(unit_id);
250 
251  assert(old_size != current_team.recall_list().size());
252  return true;
253 }
254 
255 SYNCED_COMMAND_HANDLER_FUNCTION(move, child, spectator)
256 {
257  team& current_team = resources::controller->current_team();
258 
259  std::vector<map_location> steps;
260 
261  try {
262  steps = read_locations(child);
263  } catch (const std::invalid_argument&) {
264  WRN_REPLAY << "Warning: Path data contained something which could not be parsed to a sequence of locations:" << "\n config = " << child.debug();
265  return false;
266  } catch (const std::out_of_range&) {
267  WRN_REPLAY << "Warning: Path data contained something which could not be parsed to a sequence of locations:" << "\n config = " << child.debug();
268  return false;
269  }
270 
271  if(steps.empty())
272  {
273  WRN_REPLAY << "Warning: Missing path data found in [move]";
274  return false;
275  }
276 
277  for(const map_location& step : steps) {
278  if(!resources::gameboard->map().on_board(step)) {
279  WRN_REPLAY << "Warning: Movement path contained an off-map location";
280  return false;
281  }
282  }
283 
284  const map_location& src = steps.front();
285  const map_location& dst = steps.back();
286 
287  if (src == dst) {
288  WRN_REPLAY << "Warning: Move with identical source and destination. Skipping...";
289  return false;
290  }
291 
292  // The nominal destination should appear to be unoccupied.
294  if ( u.valid() ) {
295  WRN_REPLAY << "Warning: Move destination " << dst << " appears occupied.";
296  // We'll still proceed with this movement, though, since
297  // an event might intervene.
298  // 'event' doesn't mean wml event but rather it means 'hidden' units form the movers point of view.
299  }
300 
302  if (!u.valid()) {
303  std::stringstream errbuf;
304  errbuf << "unfound location for source of movement: "
305  << src << " -> " << dst << '\n';
306  spectator.error(errbuf.str());
307  return false;
308  }
309 
310  bool skip_sighted = child["skip_sighted"] == "all";
311  bool skip_ally_sighted = child["skip_sighted"] == "only_ally";
312 
313  actions::execute_move_unit(steps, skip_sighted, skip_ally_sighted, dynamic_cast<actions::move_unit_spectator*>(&spectator));
314 
315  return true;
316 }
317 
319 {
320  if(const auto last_select = child.optional_child("last_select"))
321  {
322  //the select event cannot clear the undo stack.
323  resources::game_events->pump().fire("select", map_location(last_select.value(), resources::gamedata));
324  }
325  const std::string &event_name = child["raise"];
326  if (const auto source = child.optional_child("source")) {
327  synced_context::block_undo(std::get<0>(resources::game_events->pump().fire(event_name, map_location(source.value(), resources::gamedata))));
328  } else {
329  synced_context::block_undo(std::get<0>(resources::game_events->pump().fire(event_name)));
330  }
331 
332  return true;
333 }
334 
335 SYNCED_COMMAND_HANDLER_FUNCTION(custom_command, child, /*spectator*/)
336 {
337  assert(resources::lua_kernel);
338  resources::lua_kernel->custom_command(child["name"], child.child_or_empty("data"));
339 
340  return true;
341 }
342 
343 SYNCED_COMMAND_HANDLER_FUNCTION(auto_shroud, child, /*spectator*/)
344 {
345  team &current_team = resources::controller->current_team();
346 
347  bool active = child["active"].to_bool();
348  bool block_undo = child["block_undo"].to_bool();
349  if(active && !current_team.auto_shroud_updates()) {
350  block_undo |= resources::undo_stack->commit_vision();
351  }
352  current_team.set_auto_shroud_updates(active);
354  if(block_undo) {
356  }
357  return true;
358 }
359 
360 SYNCED_COMMAND_HANDLER_FUNCTION(update_shroud, /*child*/, spectator)
361 {
362  // When "updating shroud now" is used.
363  // Updates fog/shroud based on the undo stack, then updates stack as needed.
364  // This may fire events and change the game state.
365 
366  team &current_team = resources::controller->current_team();
367  if(current_team.auto_shroud_updates()) {
368  spectator.error("Team has DSU disabled but we found an explicit shroud update");
369  }
370  bool res = resources::undo_stack->commit_vision();
371  if(res) {
373  }
374  return true;
375 }
376 
377 namespace
378 {
379  void debug_notification(const std::string& text, bool message_is_command = false)
380  {
382  auto& current_team = controller.current_team();
383  static bool ignore = false;
384  bool show_long_message = controller.is_replay() || !current_team.is_local();
385 
386  std::string message;
387  utils::string_map i18n_vars = {{ "player", current_team.current_player() }};
388 
389  if(i18n_vars["player"].empty()) {
390  i18n_vars["player"] = _("(unknown player)");
391  }
392 
393  if(message_is_command) {
394  i18n_vars["command"] = text;
395  message = VGETTEXT("The :$command debug command was used during $player’s turn", i18n_vars);
396  } else {
397  message = VGETTEXT(text.c_str(), i18n_vars);
398  }
399 
400  if(show_long_message && !ignore) {
402  std::stringstream sbuilder;
403  sbuilder << _("A player used a debug command during the game. If this is unexpected, it is possible the player in question is cheating.")
404  << "\n\n"
405  << _("Details:") << "\n"
406  << message
407  << "\n\n"
408  << _("Do you wish to save the game before continuing?");
409  savegame::oos_savegame save(controller.get_saved_game(), ignore);
410  save.set_title(_("Debug Command Used"));
411  save.save_game_interactive(sbuilder.str(), savegame::savegame::YES_NO); // can throw quit_game_exception
412  }
413  else {
414  display::announce_options announce_options;
415  display::get_singleton()->announce(message, font::NORMAL_COLOR, announce_options);
416  }
417  }
418 
419  void debug_cmd_notification(const std::string& command)
420  {
421  debug_notification(command, true);
422  }
423 }
424 
425 SYNCED_COMMAND_HANDLER_FUNCTION(debug_terrain, child, /*spectator*/)
426 {
428  debug_cmd_notification("terrain");
429 
430  map_location loc(child);
431  const std::string& terrain_type = child["terrain_type"];
432  const std::string& mode_str = child["mode_str"];
433 
434  bool result = resources::gameboard->change_terrain(loc, terrain_type, mode_str, false);
435  if(result) {
439  }
440  return true;
441 }
442 
443 SYNCED_COMMAND_HANDLER_FUNCTION(debug_unit, child, /*spectator*/)
444 {
446  debug_cmd_notification("unit");
447  map_location loc(child);
448  const std::string name = child["name"];
449  const std::string value = child["value"];
450 
452  if (i == resources::gameboard->units().end()) {
453  return false;
454  }
455  if (name == "advances" ) {
456  int int_value = 0;
457  try {
458  int_value = std::stoi(value);
459  } catch (const std::invalid_argument&) {
460  WRN_REPLAY << "Warning: Invalid unit advancement argument: " << value;
461  return false;
462  }
463  for (int levels=0; levels<int_value; levels++) {
464  i->set_experience(i->max_experience());
465 
466  advance_unit_at(advance_unit_params(loc).force_dialog(true));
468  if (!i.valid()) {
469  break;
470  }
471  }
472  } else if (name == "status" ) {
473  for (std::string status : utils::split(value)) {
474  bool add = true;
475  if (status.length() >= 1 && status[0] == '-') {
476  add = false;
477  status = status.substr(1);
478  }
479  if (status.empty()) {
480  continue;
481  }
482  i->set_state(status, add);
483  }
484  } else {
485  config cfg;
486  i->write(cfg);
487  cfg[name] = value;
488 
489  // Attempt to create a new unit. If there are error (such an invalid type key), exit.
490  try{
491  unit_ptr new_u = unit::create(cfg, true);
492  new_u->set_location(loc);
493  // Don't remove the unit until after we've verified there are no errors in creating the new one,
494  // or else the unit would simply be removed from the map with no replacement.
496  resources::whiteboard->on_kill_unit();
498  } catch(const unit_type::error& e) {
499  ERR_REPLAY << e.what(); // TODO: more appropriate error message log
500  return false;
501  }
502  }
503  if (name == "fail") { //testcase for bug #18488
504  assert(i.valid());
505  }
508 
509  return true;
510 }
511 
512 SYNCED_COMMAND_HANDLER_FUNCTION(debug_create_unit, child, spectator)
513 {
515 
516  debug_notification(N_("A unit was created using debug mode during $player’s turn"));
517  map_location loc(child);
518  resources::whiteboard->on_kill_unit();
519  const std::string& variation = child["variation"].str();
520  const unit_race::GENDER gender = string_gender(child["gender"], unit_race::NUM_GENDERS);
521  const unit_type *u_type = unit_types.find(child["type"]);
522  if (!u_type) {
523  spectator.error("Invalid unit type");
524  return false;
525  }
526 
527  const int side_num = resources::controller
529 
530  // Create the unit.
531  unit_ptr created = unit::create(*u_type, side_num, true, gender, variation);
532  created->new_turn();
533 
534  unit_map::unit_iterator unit_it;
535 
536  // Add the unit to the board.
537  std::tie(unit_it, std::ignore) = resources::gameboard->units().replace(loc, created);
538 
540  resources::game_events->pump().fire("unit_placed", loc);
542 
543  // Village capture?
544  if ( resources::gameboard->map().is_village(loc) )
545  actions::get_village(loc, created->side());
546 
547  // Update fog/shroud.
548  // Not checking auto_shroud_updates() here since :create is not undoable. (#2196)
549  actions::shroud_clearer clearer;
550  clearer.clear_unit(loc, *created);
551  clearer.fire_events();
552  if (unit_it.valid() ) // In case sighted events messed with the unit.
553  actions::actor_sighted(*unit_it);
554 
555  return true;
556 }
557 
559 {
561  debug_cmd_notification("lua");
562  resources::lua_kernel->run(child["code"].str().c_str(), "debug command");
564 
565  return true;
566 }
567 
568 SYNCED_COMMAND_HANDLER_FUNCTION(debug_teleport, child, /*spectator*/)
569 {
571  debug_cmd_notification("teleport");
572 
573  const map_location teleport_from(child["teleport_from_x"].to_int(), child["teleport_from_y"].to_int(), wml_loc());
574  const map_location teleport_to(child["teleport_to_x"].to_int(), child["teleport_to_y"].to_int(), wml_loc());
575 
576  const unit_map::iterator unit_iter = resources::gameboard->units().find(teleport_from);
577  if(unit_iter != resources::gameboard->units().end()) {
578  if(unit_iter.valid()) {
579  actions::teleport_unit_from_replay({teleport_from, teleport_to}, false, false, false);
580  }
582  }
583 
584  return true;
585 }
586 
587 SYNCED_COMMAND_HANDLER_FUNCTION(debug_kill, child, /*spectator*/)
588 {
590  debug_cmd_notification("kill");
591 
592  const map_location loc(child["x"].to_int(), child["y"].to_int(), wml_loc());
594  if (i != resources::gameboard->units().end()) {
595  const int dying_side = i->side();
596  resources::controller->pump().fire("last_breath", loc, loc);
597  if (i.valid()) {
599  }
601  if (i.valid()) {
602  i->set_hitpoints(0);
603  }
604  resources::controller->pump().fire("die", loc, loc);
605  if (i.valid()) {
607  }
608  resources::whiteboard->on_kill_unit();
609  actions::recalculate_fog(dying_side);
610  }
611  return true;
612 }
613 
614 SYNCED_COMMAND_HANDLER_FUNCTION(debug_next_level, child, /*spectator*/)
615 {
617 
618  debug_cmd_notification("next_level");
619 
620  std::string next_level = child["next_level"];
621  if (!next_level.empty())
624  e.transient.carryover_report = false;
625  e.prescenario_save = true;
626  e.transient.linger_mode = false;
627  e.proceed_to_next_level = true;
628  e.is_victory = true;
629 
632 
633  return true;
634 }
635 
636 SYNCED_COMMAND_HANDLER_FUNCTION(debug_turn_limit, child, /*spectator*/)
637 {
639 
640  debug_cmd_notification("turn_limit");
641 
642  resources::tod_manager->set_number_of_turns(child["turn_limit"].to_int(-1));
644  return true;
645 }
646 
647 SYNCED_COMMAND_HANDLER_FUNCTION(debug_turn, child, /*spectator*/)
648 {
650 
651  debug_cmd_notification("turn");
652 
653  resources::tod_manager->set_turn(child["turn"].to_int(1), resources::gamedata);
654 
657 
658  return true;
659 }
660 
661 SYNCED_COMMAND_HANDLER_FUNCTION(debug_set_var, child, /*spectator*/)
662 {
664 
665  debug_cmd_notification("set_var");
666 
667  try {
668  resources::gamedata->set_variable(child["name"],child["value"]);
669  }
670  catch(const invalid_variablename_exception&) {
671  // command_failed(_("Variable not found"));
672  return false;
673  }
674  return true;
675 }
676 
677 SYNCED_COMMAND_HANDLER_FUNCTION(debug_gold, child, /*spectator*/)
678 {
680 
681  debug_cmd_notification("gold");
682 
683  resources::controller->current_team().spend_gold(-child["gold"].to_int(0));
685  return true;
686 }
687 
688 
689 SYNCED_COMMAND_HANDLER_FUNCTION(debug_event, child, /*spectator*/)
690 {
692 
693  debug_cmd_notification("throw");
694 
695  resources::controller->pump().fire(child["eventname"]);
697 
698  return true;
699 }
700 
701 
702 SYNCED_COMMAND_HANDLER_FUNCTION(debug_fog, /*child*/, /*spectator*/)
703 {
705 
706  debug_cmd_notification("fog");
707 
708  team& current_team = resources::controller->current_team();
709  current_team.set_fog(!current_team.uses_fog());
710  actions::recalculate_fog(current_team.side());
711 
714 
715  return true;
716 }
717 
718 
719 SYNCED_COMMAND_HANDLER_FUNCTION(debug_shroud, /*child*/, /*spectator*/)
720 {
722 
723  debug_cmd_notification("shroud");
724 
725  team& current_team = resources::controller->current_team();
726  current_team.set_shroud(!current_team.uses_shroud());
727  actions::clear_shroud(current_team.side());
728 
731 
732  return true;
733 }
void attack_unit_and_advance(const map_location &attacker, const map_location &defender, int attack_with, int defend_with, bool update_display)
Performs an attack, and advanced the units afterwards.
Definition: attack.cpp:1453
Various functions that implement attacks and attack calculations.
map_location loc
Definition: move.cpp:172
Various functions related to moving units.
void advance_unit_at(const advance_unit_params &params)
Various functions that implement advancements of units.
Class to encapsulate fog/shroud clearing and the resultant sighted events.
Definition: vision.hpp:72
game_events::pump_result_t fire_events()
Fires the sighted events that were earlier recorded by fog/shroud clearing.
Definition: vision.cpp:541
bool clear_unit(const map_location &view_loc, team &view_team, std::size_t viewer_id, int sight_range, bool slowed, const movetype::terrain_costs &costs, const map_location &real_loc, const std::set< map_location > *known_units=nullptr, std::size_t *enemy_count=nullptr, std::size_t *friend_count=nullptr, move_unit_spectator *spectator=nullptr, bool instant=true)
Clears shroud (and fog) around the provided location for view_team based on sight_range,...
Definition: vision.cpp:330
void add_dismissal(const unit_const_ptr &u)
Adds a dismissal to the undo stack.
Definition: undo.cpp:88
bool commit_vision()
Updates fog/shroud based on the undo stack, then updates stack as needed.
Definition: undo.cpp:153
void add_auto_shroud(bool turned_on)
Adds an auto-shroud toggle to the undo stack.
Definition: undo.cpp:79
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:157
void recalculate_minimap()
Schedule the minimap for recalculation.
Definition: display.cpp:1465
void redraw_minimap()
Schedule the minimap to be redrawn.
Definition: display.cpp:1486
bool invalidate(const map_location &loc)
Function to invalidate a specific tile for redrawing.
Definition: display.cpp:2927
void announce(const std::string &msg, const color_t &color=font::GOOD_COLOR, const announce_options &options=announce_options())
Announce a message prominently.
Definition: display.cpp:1449
void queue_rerender()
Marks everything for rendering including all tiles and sidebar.
Definition: display.cpp:2105
static display * get_singleton()
Returns the display object if a display object exists.
Definition: display.hpp:102
bool change_terrain(const map_location &loc, const std::string &t, const std::string &mode, bool replace_if_failed)
Definition: game_board.cpp:304
team & get_team(int i)
Definition: game_board.hpp:91
unit_map::iterator find_visible_unit(const map_location &loc, const team &current_team, bool see_all=false)
Definition: game_board.cpp:172
virtual const unit_map & units() const override
Definition: game_board.hpp:106
void set_next_scenario(const std::string &next_scenario)
Definition: game_data.hpp:131
void set_variable(const std::string &varname, const config::attribute_value &value)
does nothing if varname is no valid variable name.
Definition: game_data.cpp:88
void invalidate_unit()
Function to invalidate that unit status displayed on the sidebar.
static game_display * get_singleton()
void new_turn()
Update lighting settings.
void needs_rebuild(bool b)
Sets whether the screen (map visuals) needs to be rebuilt.
bool maybe_rebuild()
Rebuilds the screen if needs_rebuild(true) was previously called, and resets the flag.
game_events::wml_event_pump & pump()
Definition: manager.cpp:257
void flush_messages()
Flushes WML messages and errors.
Definition: pump.cpp:521
pump_result_t fire(const std::string &event, const entity_location &loc1=entity_location::null_entity, const entity_location &loc2=entity_location::null_entity, const config &data=config())
Function to fire an event.
Definition: pump.cpp:399
void custom_command(const std::string &, const config &)
void run(char const *prog, const std::string &name, int nArgs=0)
Runs a plain script.
void set_end_level_data(const end_level_data &data)
int current_side() const
Returns the number of the side whose turn it is.
bool is_skipping_replay() const
virtual void force_end_turn()=0
game_events::wml_event_pump & pump()
std::size_t size() const
Get the number of units on the list.
unit_ptr find_if_matches_id(const std::string &unit_id)
Find a unit by id.
void erase_if_matches_id(const std::string &unit_id)
Erase any unit with this id.
synced_command(const std::string &tag, handler function)
static map & registry()
using static function variable instead of static member variable to prevent static initialization fia...
std::map< std::string, handler > map
static void block_undo(bool do_block=true, bool clear_undo=true)
set this to false to prevent clearing the undo stack, this is important when we cannot change the gam...
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:74
bool uses_shroud() const
Definition: team.hpp:344
int side() const
Definition: team.hpp:182
void set_shroud(bool shroud)
Definition: team.hpp:352
bool auto_shroud_updates() const
Definition: team.hpp:366
void set_auto_shroud_updates(bool value)
Definition: team.hpp:367
int gold() const
Definition: team.hpp:183
void set_fog(bool fog)
Definition: team.hpp:353
void spend_gold(const int amount)
Definition: team.hpp:221
bool uses_fog() const
Definition: team.hpp:345
recall_list_manager & recall_list()
Definition: team.hpp:242
void set_turn(const int num, game_data *vars=nullptr, const bool increase_limit_if_needed=true)
Dynamically change the current turn number.
void set_number_of_turns(int num)
unit_iterator find(std::size_t id)
Definition: map.cpp:302
umap_retval_pair_t replace(const map_location &l, const unit_ptr &p)
Works like unit_map::add; but l is emptied first, if needed.
Definition: map.cpp:216
std::size_t erase(const map_location &l)
Erases the unit at location l, if any.
Definition: map.cpp:289
umap_retval_pair_t insert(const unit_ptr &p)
Inserts the unit pointed to by p into the map.
Definition: map.cpp:135
@ NUM_GENDERS
Definition: race.hpp:28
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition: types.cpp:1253
A single unit type that the player may recruit.
Definition: types.hpp:43
unit_type_error error
Definition: types.hpp:49
int cost() const
Definition: types.hpp:176
static unit_ptr create(const config &cfg, bool use_traits=false, const vconfig *vcfg=nullptr)
Initializes a unit from a config.
Definition: unit.hpp:107
Various functions related to the creation of units (recruits, recalls, and placed units).
const config * cfg
#define VGETTEXT(msgid,...)
Handy wrappers around interpolate_variables_into_string and gettext.
std::size_t i
Definition: function.cpp:1031
#define N_(String)
Definition: gettext.hpp:108
static std::string _(const char *str)
Definition: gettext.hpp:100
std::vector< map_location > read_locations(const config &cfg)
Parse x,y keys of a config into a vector of locations.
Definition: location.cpp:475
Standard logging facilities (interface).
std::string find_recruit_location(const int side, map_location &recruit_location, map_location &recruited_from, const std::string &unit_type)
Finds a location on which to place a unit.
Definition: create.cpp:466
void teleport_unit_from_replay(const std::vector< map_location > &steps, bool continued_move, bool skip_ally_sighted, bool show_move)
Teleports a unit across the board.
Definition: move.cpp:1412
bool clear_shroud(int side, bool reset_fog, bool fire_events)
Function that will clear shroud (and fog) based on current unit positions.
Definition: vision.cpp:746
bool recall_unit(const std::string &id, team &current_team, const map_location &loc, const map_location &from, map_location::direction facing)
Recalls the unit with the indicated ID for the provided team.
Definition: create.cpp:740
game_events::pump_result_t actor_sighted(const unit &target, const std::vector< int > *cache)
Fires sighted events for the sides that can see target.
Definition: vision.cpp:614
void recruit_unit(const unit_type &u_type, int side_num, const map_location &loc, const map_location &from)
Recruits a unit of the given type for the given side.
Definition: create.cpp:717
game_events::pump_result_t get_village(const map_location &loc, int side, bool *action_timebonus, bool fire_event)
Makes it so the village at the given location is owned by the given side.
Definition: move.cpp:221
void recalculate_fog(int side)
Function that recalculates the fog of war.
Definition: vision.cpp:697
void execute_move_unit(const std::vector< map_location > &steps, bool continued_move, bool skip_ally_sighted, move_unit_spectator *move_spectator)
Moves a unit across the board.
Definition: move.cpp:1388
void pump()
Process all events currently in the queue.
Definition: events.cpp:480
const color_t NORMAL_COLOR
bool fire_event(const ui_event event, const std::vector< std::pair< widget *, ui_event >> &event_chain, widget *dispatcher, widget *w, F &&... params)
Helper function for fire_event.
std::string tag(std::string_view tag, Args &&... data)
Wraps the given data in the specified tag.
Definition: markup.hpp:45
::tod_manager * tod_manager
Definition: resources.cpp:29
game_board * gameboard
Definition: resources.cpp:20
game_data * gamedata
Definition: resources.cpp:22
game_events::manager * game_events
Definition: resources.cpp:24
actions::undo_list * undo_stack
Definition: resources.cpp:32
game_lua_kernel * lua_kernel
Definition: resources.cpp:25
play_controller * controller
Definition: resources.cpp:21
std::shared_ptr< wb::manager > whiteboard
Definition: resources.cpp:33
void unit_die(const map_location &loc, unit &loser, const const_attack_ptr &attack, const const_attack_ptr &secondary_attack, const map_location &winner_loc, const unit_ptr &winner)
Show a unit fading out.
Definition: udisplay.cpp:571
void unit_recruited(const map_location &loc, const map_location &leader_loc)
Definition: udisplay.cpp:798
int stoi(std::string_view str)
Same interface as std::stoi and meant as a drop in replacement, except:
Definition: charconv.hpp:156
std::map< std::string, t_string > string_map
std::vector< std::string > split(const config_attribute_value &val)
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
Definition: general.hpp:142
bool show(std::string title, std::string message)
Displays a tray notification.
std::shared_ptr< unit > unit_ptr
Definition: ptr.hpp:26
Define the game's event mechanism.
unit_race::GENDER string_gender(const std::string &str, unit_race::GENDER def)
Definition: race.cpp:149
std::unique_ptr< MIX_Audio, decltype(&MIX_DestroyAudio)> value
Definition: sound.cpp:139
rect dst
Location on the final composed sheet.
rect src
Non-transparent portion of the surface to compose.
advances the unit at loc if it has enough experience, maximum 20 times.
Definition: advancement.hpp:39
Holds options for calls to function 'announce' (announce).
Definition: display.hpp:602
Additional information on the game outcome which can be provided by WML.
Encapsulates the map of the game.
Definition: location.hpp:46
bool valid() const
Definition: location.hpp:111
bool valid() const
Definition: map.hpp:273
#define WRN_REPLAY
#define DBG_REPLAY
static lg::log_domain log_replay("replay")
#define LOG_REPLAY
#define ERR_REPLAY
SYNCED_COMMAND_HANDLER_FUNCTION(recruit, child, spectator)
unit_type_data unit_types
Definition: types.cpp:1499
Display units performing various actions: moving, attacking, and dying.
Various functions that implement the undoing (and redoing) of in-game commands.
#define e