The Battle for Wesnoth  1.19.8+dev
play_controller.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2024
3  by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
4  Copyright (C) 2003 by David White <dave@whitevine.net>
5  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY.
13 
14  See the COPYING file for more details.
15 */
16 
17 /**
18  * @file
19  * Handle input via mouse & keyboard, events, schedule commands.
20  */
21 
22 #include "play_controller.hpp"
23 
24 #include "actions/heal.hpp"
25 #include "actions/undo.hpp"
26 #include "actions/vision.hpp"
27 #include "ai/manager.hpp"
28 #include "ai/testing.hpp"
29 #include "display_chat_manager.hpp"
30 #include "floating_label.hpp"
31 #include "formula/string_utils.hpp"
32 #include "game_errors.hpp"
34 #include "game_events/pump.hpp"
35 #include "game_state.hpp"
36 #include "gettext.hpp"
38 #include "gui/dialogs/message.hpp" // for show_error_message
42 #include "log.hpp"
43 #include "map/label.hpp"
44 #include "pathfind/teleport.hpp"
46 #include "random.hpp"
47 #include "replay.hpp"
48 #include "resources.hpp"
49 #include "save_index.hpp"
50 #include "saved_game.hpp"
51 #include "savegame.hpp"
55 #include "sound.hpp"
56 #include "soundsource.hpp"
57 #include "statistics.hpp"
58 #include "synced_context.hpp"
59 #include "units/types.hpp"
60 #include "units/unit.hpp"
61 #include "utils/general.hpp"
62 #include "video.hpp"
63 #include "whiteboard/manager.hpp"
64 
65 #include <functional>
66 
67 static lg::log_domain log_aitesting("ai/testing");
68 #define LOG_AIT LOG_STREAM(info, log_aitesting)
69 // If necessary, this define can be replaced with `#define LOG_AIT std::cout` to restore previous behavior
70 
71 static lg::log_domain log_engine("engine");
72 #define LOG_NG LOG_STREAM(info, log_engine)
73 #define DBG_NG LOG_STREAM(debug, log_engine)
74 #define ERR_NG LOG_STREAM(err, log_engine)
75 
76 static lg::log_domain log_display("display");
77 #define ERR_DP LOG_STREAM(err, log_display)
78 
79 static lg::log_domain log_enginerefac("enginerefac");
80 #define LOG_RG LOG_STREAM(info, log_enginerefac)
81 
82 static lg::log_domain log_engine_enemies("engine/enemies");
83 #define DBG_EE LOG_STREAM(debug, log_engine_enemies)
84 
85 /**
86  * Copies [scenario] attributes/tags that are not otherwise stored in C++ structs/clases.
87  */
88 static void copy_persistent(const config& src, config& dst)
89 {
90  static const std::set<std::string> attrs {
91  "description",
92  "name",
93  "disallow_recall",
94  "experience_modifier",
95  "require_scenario",
96  "loaded_resources"
97  };
98 
99  static const std::set<std::string> tags {
100  "terrain_graphics",
101  "modify_unit_type",
102  "lua"
103  };
104 
105  for(const std::string& attr : attrs) {
106  dst[attr] = src[attr];
107  }
108 
109  for(const std::string& tag : tags) {
110  dst.append_children(src, tag);
111  }
112 }
113 
114 static void clear_resources()
115 {
116  resources::controller = nullptr;
117  resources::filter_con = nullptr;
118  resources::gameboard = nullptr;
119  resources::gamedata = nullptr;
120  resources::lua_kernel = nullptr;
121  resources::game_events = nullptr;
122  resources::persist = nullptr;
123  resources::soundsources = nullptr;
124  resources::tod_manager = nullptr;
125  resources::tunnels = nullptr;
126  resources::undo_stack = nullptr;
127  resources::recorder = nullptr;
128  resources::whiteboard.reset();
129  resources::classification = nullptr;
130 }
131 
133  : controller_base()
134  , observer()
136  , timer_()
137  , gamestate_()
138  , level_()
139  , saved_game_(state_of_game)
140  , tooltips_manager_()
141  , whiteboard_manager_()
142  , plugins_context_()
143  , labels_manager_(new font::floating_label_context())
144  , help_manager_(&game_config_)
145  , mouse_handler_(nullptr, *this)
146  , menu_handler_(nullptr, *this)
147  , hotkey_handler_(new hotkey_handler(*this, saved_game_))
148  , soundsources_manager_()
149  , persist_()
150  , gui_()
151  , xp_mod_(new unit_experience_accelerator(level["experience_modifier"].to_int(100)))
152  , statistics_context_(new statistics_t(state_of_game.statistics()))
153  , replay_(new replay(state_of_game.get_replay()))
154  , skip_replay_(false)
155  , skip_story_(state_of_game.skip_story())
156  , did_autosave_this_turn_(true)
157  , did_tod_sound_this_turn_(false)
158  , map_start_()
159  , start_faded_(true)
160  , victory_music_()
161  , defeat_music_()
162  , scope_(hotkey::scope_game)
163  , ignore_replay_errors_(false)
164  , player_type_changed_(false)
165 {
167 
168  for(const config& modify_unit_type : level_.child_range("modify_unit_type")) {
169  unit_types.apply_scenario_fix(modify_unit_type);
170  }
171  resources::controller = this;
174 
176 
178 
180 
181  try {
182  init(level);
183  } catch(...) {
184  DBG_NG << "Caught exception initializing level: " << utils::get_unknown_exception_type();
185  clear_resources();
186  throw;
187  }
188 }
189 
191 {
193  clear_resources();
194 }
195 
197 {
198  /*
199  * Initilisation currently happens on the following order:
200  * 1) This code, which is executed with the loadingscreen
201  * From inside the constructor.
202  * 2) The Music is changed.
203  * 3) The Storyscreen is shown.
204  * 4) The Labels are added
205  * 5) The preload event is fired
206  * 6) The prestart event is fired
207  * 7) The gui is activated
208  * 8) The start event is fired
209  */
212 
213  LOG_NG << "initializing game_state..." << timer();
214  gamestate_.reset(new game_state(level, *this));
215 
223 
224  gamestate_->ai_manager_.add_observer(this);
225  gamestate_->init(level, *this);
227 
228  LOG_NG << "initializing whiteboard..." << timer();
230  whiteboard_manager_.reset(new wb::manager());
232 
233  LOG_NG << "loading units..." << timer();
236 
237  LOG_NG << "initializing theme... " << timer();
239 
240  LOG_NG << "building terrain rules... " << timer();
242 
243  gui_.reset(new game_display(gamestate().board_, whiteboard_manager_, *gamestate().reports_, theme(), level));
244  map_start_ = map_location(level.child_or_empty("display").child_or_empty("location"));
245  if(start_faded_) {
246  gui_->set_fade({0,0,0,255});
247  gui_->set_prevent_draw(true);
248  }
249 
250  // Ensure the loading screen doesn't end up underneath the game display
252 
253  if(!video::headless()) {
255  gui_->get_theme().modify_label("time-icon", _("time left for current turn"));
256  } else {
257  gui_->get_theme().modify_label("time-icon", _("current local time"));
258  }
259  }
260 
262  mouse_handler_.set_gui(gui_.get());
263  menu_handler_.set_gui(gui_.get());
264 
265  LOG_NG << "done initializing display... " << timer();
266 
267  LOG_NG << "building gamestate to gui and whiteboard... " << timer();
268  // This *needs* to be created before the show_intro and show_map_scene
269  // as that functions use the manager state_of_game
270  // Has to be done before registering any events!
273 
274  init_managers();
276  // loadscreen_manager->reset();
278  gamestate().lua_kernel_->load_game(level);
279 
280  plugins_context_.reset(new plugins_context("Game"));
281  plugins_context_->set_callback("save_game", [this](const config& cfg) { save_game_auto(cfg["filename"]); }, true);
282  plugins_context_->set_callback("save_replay", [this](const config& cfg) { save_replay_auto(cfg["filename"]); }, true);
283  plugins_context_->set_callback("quit", [](const config&) { throw_quit_game_exception(); }, false);
284  plugins_context_->set_callback_execute(*resources::lua_kernel);
285  plugins_context_->set_accessor_string("scenario_name", [this](const config&) { return get_scenario_name(); });
286  plugins_context_->set_accessor_int("current_side", [this](const config&) { return current_side(); });
287  plugins_context_->set_accessor_int("current_turn", [this](const config&) { return turn(); });
288  plugins_context_->set_accessor_bool("can_move", [this](const config&) { return !events::commands_disabled && gamestate().gamedata_.phase() == game_data::TURN_PLAYING; });
289  plugins_context_->set_callback("end_turn", [this](const config&) { require_end_turn(); }, false);
290  plugins_context_->set_callback("synced_command", [this](const config& cmd) {
291  auto& pm = *plugins_manager::get();
292  if(resources::whiteboard->has_planned_unit_map())
293  {
294  ERR_NG << "plugin called synced command while whiteboard is applied, ignoring";
295  pm.notify_event("synced_command_error", config{"error", "whiteboard"});
296  return;
297  }
298 
299  auto& gamedata = gamestate().gamedata_;
300  const bool is_too_early = gamedata.phase() == game_data::INITIAL || resources::gamedata->phase() == game_data::PRELOAD;
301  const bool is_during_turn = gamedata.phase() == game_data::TURN_PLAYING;
302  const bool is_unsynced = synced_context::get_synced_state() == synced_context::UNSYNCED;
303  if(is_too_early) {
304  ERR_NG << "synced command called too early, only allowed at START or later";
305  pm.notify_event("synced_command_error", config{"error", "too-early"});
306  return;
307  }
308  if(is_unsynced && !is_during_turn) {
309  ERR_NG << "synced command can only be used during a turn when a user would also be able to invoke commands";
310  pm.notify_event("synced_command_error", config{"error", "not-your-turn"});
311  return;
312  }
313  if(is_unsynced && events::commands_disabled) {
314  ERR_NG << "synced command cannot be invoked while commands are blocked";
315  pm.notify_event("synced_command_error", config{"error", "disabled"});
316  return;
317  }
318  if(is_unsynced && !resources::controller->current_team().is_local()) {
319  ERR_NG << "synced command can only be used from clients that control the currently playing side";
320  pm.notify_event("synced_command_error", config{"error", "not-your-turn"});
321  return;
322  }
323  action_spectator spectator([&pm](const std::string& message) {
324  ERR_NG << "synced command from plugin raised an error: " << message;
325  pm.notify_event("synced_command_error", config{"error", "error", "message", message});
326  });
327  for(const auto [key, child] : cmd.all_children_range()) {
330  }
331  }, false);
332  });
333 }
334 
335 void play_controller::reset_gamestate(const config& level, int replay_pos)
336 {
337  // TODO: should we update we update this->level_ with level ?
338 
339  resources::gameboard = nullptr;
340  resources::gamedata = nullptr;
341  resources::tod_manager = nullptr;
342  resources::filter_con = nullptr;
343  resources::lua_kernel = nullptr;
344  resources::game_events = nullptr;
345  resources::tunnels = nullptr;
346  resources::undo_stack = nullptr;
347 
348  gui_->labels().set_team(nullptr);
349 
350  /* First destroy the old game state, then create the new one.
351  This is necessary to ensure that while the old AI manager is being destroyed,
352  all its member objects access the old manager instead of the new. */
353  gamestate_.reset();
354  gamestate_.reset(new game_state(level, *this));
355 
363 
364  gamestate_->ai_manager_.add_observer(this);
365  gamestate_->init(level, *this);
368 
369  gui_->reset_reports(*gamestate().reports_);
370  gui_->change_display_context(&gamestate().board_);
371  saved_game_.get_replay().set_pos(replay_pos);
372 
374  gamestate().lua_kernel_->load_game(level);
375 }
376 
378 {
379  LOG_NG << "initializing managers... " << timer();
381 
383  LOG_NG << "done initializing managers... " << timer();
384 }
385 
387 {
388  // Run initialization scripts, even if loading from a snapshot.
389  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
390  pump().fire("preload");
391  gamestate().lua_kernel_->preload_finished();
392 }
393 
395 {
396  // pre-start events must be executed before any GUI operation,
397  // as those may cause the display to be refreshed.
399 
400  // Fire these right before prestart events, to catch only the units sides
401  // have started with.
402  for(const unit& u : get_units()) {
403  pump().fire("unit_placed", map_location(u.get_location()));
404  }
405 
406  pump().fire("prestart");
407 
408  // prestart event may modify start turn with WML, reflect any changes.
409  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
410 }
411 
413 {
414  if(!get_teams().empty()) {
415  const config cfg("side", gui_->viewing_team().side());
416  gamestate().lua_kernel_->run_wml_action("show_objectives", vconfig(cfg),
417  game_events::queued_event("_from_interface", "", map_location(), map_location(), config()));
418  }
419 }
420 
422 {
424  pump().fire("start");
425 
426  skip_story_ = false; // Show [message]s from now on even with --campaign-skip-story
427 
428  // start event may modify start turn with WML, reflect any changes.
429  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
430 
433 
434  // prestart and start events may modify the initial gold amount,
435  // reflect any changes.
436  for(team& tm : get_teams()) {
437  tm.set_start_gold(tm.gold());
438  }
439 
441 }
442 
444 {
445  gui_->begin_game();
446  gui_->update_tod();
447 }
448 
450 {
452  gui_->set_playing_team_index(std::size_t(current_side() - 1));
453 
455 
457 }
458 
460 {
461  //
462  // We do side init only if not done yet for a local side when we are not replaying.
463  // For all other sides it is recorded in replay and replay handler has to handle
464  // calling do_init_side() functions.
465  //
466  if(is_during_turn()) {
467  // We already executed do_init_side this can for example happe if we reload a game,
468  // but also if we changed control of a side during it's turn
469  return;
470  }
471 
472  if(!current_team().is_local()) {
473  // We are in a mp game and execute do_init_side as soon as we receive [init_side] from the current player
474  // (see replay.cpp)
475  return;
476  }
477 
478  if(is_replay()) {
479  // We are in a replay and execute do_init_side as soon as we reach the next [init_side] in the replay data
480  // (see replay.cpp)
481  return;
482  }
483 
484  if(current_team().is_idle()) {
485  // In this case it can happen that we just gave control of this side to another player so doing init_side
486  // could lead to errors since we no longer own this side from the servers perspective.
487  // (see playturn.cpp)
488  return;
489  }
490 
492  do_init_side();
493 }
494 
496 {
497  { // Block for set_scontext_synced
498  set_scontext_synced sync;
499 
501 
502  log_scope("player turn");
503  // In case we might end up calling sync:network during the side turn events,
504  // and we don't want do_init_side to be called when a player drops.
506  gamestate_->next_player_number_ = gamestate_->player_number_ + 1;
507 
508  const std::string turn_num = std::to_string(turn());
509  const std::string side_num = std::to_string(current_side());
510 
511  gamestate().gamedata_.get_variable("side_number") = current_side();
512 
513  // We might have skipped some sides because they were empty so it is not enough to check for side_num==1
514  if(!gamestate().tod_manager_.has_turn_event_fired()) {
515  pump().fire("turn_" + turn_num);
516  pump().fire("new_turn");
518  }
519 
520  pump().fire("side_turn");
521  pump().fire("side_" + side_num + "_turn");
522  pump().fire("side_turn_" + turn_num);
523  pump().fire("side_" + side_num + "_turn_" + turn_num);
524 
525  // We want to work out if units for this player should get healed,
526  // and the player should get income now.
527  // Healing/income happen if it's not the first turn of processing,
528  // or if we are loading a game.
529  if(turn() > 1) {
532 
533  // If the expense is less than the number of villages owned
534  // times the village support capacity,
535  // then we don't have to pay anything at all
536  int expense = gamestate().board_.side_upkeep(current_side()) - current_team().support();
537  if(expense > 0) {
538  current_team().spend_gold(expense);
539  }
540  }
541 
542  if(do_healing()) {
544  }
545 
546  // Do healing on every side turn except the very first side turn.
547  // (1.14 and earlier did healing whenever turn >= 2.)
548  set_do_healing(true);
549 
550  // Set resting now after the healing has been done.
551  for(unit& patient : resources::gameboard->units()) {
552  if(patient.side() == current_side()) {
553  patient.set_resting(true);
554  }
555  }
556 
557  // Prepare the undo stack.
559 
560  pump().fire("turn_refresh");
561  pump().fire("side_" + side_num + "_turn_refresh");
562  pump().fire("turn_" + turn_num + "_refresh");
563  pump().fire("side_" + side_num + "_turn_" + turn_num + "_refresh");
564 
565  // Make sure vision is accurate.
567 
568  check_victory();
569  sync.do_final_checkup();
571  }
572 
573  statistics().reset_turn_stats(gamestate().board_.get_team(current_side()).save_id_or_number());
574 
575  init_side_end();
576 
577  if(!is_skipping_replay() && current_team().get_scroll_to_leader()) {
578  gui_->scroll_to_leader(current_side(), game_display::ONSCREEN, false);
579  }
580 }
581 
583 {
588  }
589  whiteboard_manager_->on_init_side();
590 }
591 
593 {
594  config cfg = level_;
595 
596  cfg["replay_pos"] = saved_game_.get_replay().get_pos();
597  gamestate().write(cfg);
598 
599  gui_->write(cfg.add_child("display"));
600 
601  // Write the soundsources.
602  soundsources_manager_->write_sourcespecs(cfg);
603 
604  gui_->labels().write(cfg);
606 
607  if(cfg["replay_pos"].to_int(0) > 0 && cfg["playing_team"].empty()) {
608  gui2::show_error_message(_("Trying to create a corrupt file, please report this bug"));
609  }
610 
611  return cfg;
612 }
613 
615 {
616 
617  { // Block for set_scontext_synced
618  set_scontext_synced sync(1);
619  // Also clears the undo stack.
621 
623  const std::string turn_num = std::to_string(turn());
624  const std::string side_num = std::to_string(current_side());
625 
626  // Clear shroud, in case units had been slowed for the turn.
628 
629  pump().fire("side_turn_end");
630  pump().fire("side_" + side_num + "_turn_end");
631  pump().fire("side_turn_" + turn_num + "_end");
632  pump().fire("side_" + side_num + "_turn_" + turn_num + "_end");
633  // This is where we refog, after all of a side's events are done.
635  check_victory();
636  sync.do_final_checkup();
637  }
639 }
640 
642 {
643  set_scontext_synced sync(2);
644  const std::string turn_num = std::to_string(turn());
645  pump().fire("turn_end");
646  pump().fire("turn_" + turn_num + "_end");
647  sync.do_final_checkup();
648 }
649 
651 {
652  // If we aren't using fog/shroud, this is easy :)
653  if(current_team().uses_fog() == false && current_team().uses_shroud() == false) {
654  return true;
655  }
656 
657  // See if any enemies are visible
658  for(const unit& u : get_units()) {
659  if(current_team().is_enemy(u.side()) && !gui_->fogged(u.get_location())) {
660  return true;
661  }
662  }
663 
664  return false;
665 }
666 
668 {
669  if(menu_handler_.get_textbox().active() == false) {
670  return;
671  }
672 
673  const std::string str = menu_handler_.get_textbox().box()->text();
674  const unsigned int team_num = current_side();
675  events::mouse_handler& mousehandler = mouse_handler_;
676 
677  switch(menu_handler_.get_textbox().mode()) {
678  case gui::TEXTBOX_SEARCH:
682  break;
685  menu_handler_.get_textbox().close(); // need to close that one after executing do_speak() !
686  break;
691  break;
692  case gui::TEXTBOX_AI:
695  menu_handler_.do_ai_formula(str, team_num, mousehandler);
696  break;
697  default:
699  ERR_DP << "unknown textbox mode";
700  }
701 }
702 
704 {
705  if(menu_handler_.get_textbox().active() == false) {
706  return;
707  }
708 
711  // Not handling messages to avoid spam
712  return;
713  }
714 
715  const std::string str = menu_handler_.get_textbox().box()->text();
716  const std::vector<std::string>& command_history = menu_handler_.get_textbox().command_history();
717 
718  auto prev = std::find(command_history.begin(), command_history.end(), str);
719 
720  if (prev != command_history.end())
721  {
722  if(up) {
723  if(prev != command_history.begin()) {
724  menu_handler_.get_textbox().box()->set_text(*--prev);
725  }
726  } else {
727  if(++prev != command_history.end()) {
728  menu_handler_.get_textbox().box()->set_text(*prev);
729  } else {
730  menu_handler_.get_textbox().box()->set_text("");
731  }
732  }
733  } else if (up) {
734  if(command_history.size() > 0) {
735  menu_handler_.get_textbox().box()->set_text(*--prev);
736  }
737  if(!str.empty()) {
739  }
740  }
741 }
742 
744 {
746 
747  std::set<std::string> dictionary;
748  switch(mode) {
749  case gui::TEXTBOX_SEARCH: {
750  for(const unit& u : get_units()) {
751  const map_location& loc = u.get_location();
752  if(!gui_->fogged(loc) && !(gui_->viewing_team().is_enemy(u.side()) && u.invisible(loc)))
753  dictionary.insert(u.name());
754  }
755  // TODO List map labels
756  break;
757  }
758  case gui::TEXTBOX_COMMAND: {
759  std::vector<std::string> commands = menu_handler_.get_commands_list();
760  dictionary.insert(commands.begin(), commands.end());
761  [[fallthrough]]; // we also want player names from the next case
762  }
763  case gui::TEXTBOX_MESSAGE: {
764  for(const team& t : get_teams()) {
765  if(!t.is_empty())
766  dictionary.insert(t.current_player());
767  }
768 
769  // Add observers
770  for(const std::string& o : gui_->observers()) {
771  dictionary.insert(o);
772  }
773 
774  // Add nicks who whispered you
775  for(const std::string& w : gui_->get_chat_manager().whisperers()) {
776  dictionary.insert(w);
777  }
778 
779  // Add nicks from friendlist
780  const std::map<std::string, std::string> friends = prefs::get().get_acquaintances_nice("friend");
781 
782  for(std::map<std::string, std::string>::const_iterator iter = friends.begin(); iter != friends.end(); ++iter) {
783  dictionary.insert((*iter).first);
784  }
785 
786  // Exclude own nick from tab-completion.
787  // NOTE why ?
788  dictionary.erase(prefs::get().login());
789  break;
790  }
791 
792  default:
793  ERR_DP << "unknown textbox mode";
794  } // switch(mode)
795 
796  menu_handler_.get_textbox().tab(dictionary);
797 }
798 
800 {
801  if(get_teams().size() == 0) {
802  throw game::game_error("The scenario has no sides defined");
803  }
804 
805  assert(gamestate().board_.has_team(current_side()));
807 }
808 
810 {
811  if(get_teams().size() == 0) {
812  throw game::game_error("The scenario has no sides defined");
813  }
814 
815  assert(gamestate().board_.has_team(current_side()));
817 }
818 
819 
821 {
822  return mouse_handler_;
823 }
824 
825 std::shared_ptr<wb::manager> play_controller::get_whiteboard() const
826 {
827  return whiteboard_manager_;
828 }
829 
831 {
832  return saved_game_.mp_settings();
833 }
834 
836 {
837  return saved_game_.classification();
838 }
839 
841 {
842  return *gui_;
843 }
844 
846 {
847  return !menu_handler_.get_textbox().active();
848 }
849 
851 {
852  if(event.key.keysym.sym == SDLK_ESCAPE) {
854  } else if(event.key.keysym.sym == SDLK_TAB) {
855  tab();
856  } else if(event.key.keysym.sym == SDLK_UP) {
858  } else if(event.key.keysym.sym == SDLK_DOWN) {
860  } else if(event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_KP_ENTER) {
861  enter_textbox();
862  }
863 }
864 
865 void play_controller::process_keydown_event(const SDL_Event& event)
866 {
867  if(event.key.keysym.sym == SDLK_TAB) {
868  whiteboard_manager_->set_invert_behavior(true);
869  }
870 }
871 
872 void play_controller::process_keyup_event(const SDL_Event& event)
873 {
874  // If the user has pressed 1 through 9, we want to show
875  // how far the unit can move in that many turns
876  if(event.key.keysym.sym >= '1' && event.key.keysym.sym <= '9') {
877  const int new_path_turns = (event.type == SDL_KEYDOWN) ? event.key.keysym.sym - '1' : 0;
878 
879  if(new_path_turns != mouse_handler_.get_path_turns()) {
880  mouse_handler_.set_path_turns(new_path_turns);
881 
883 
884  if(u.valid()) {
885  // if it's not the unit's turn, we reset its moves
886  unit_movement_resetter move_reset(*u, u->side() != current_side());
887 
889  *u, false, true, gui_->viewing_team(), mouse_handler_.get_path_turns()));
890 
891  gui_->highlight_reach(mouse_handler_.current_paths());
892  } else {
894  }
895  }
896  } else if(event.key.keysym.sym == SDLK_TAB) {
897  CKey keys;
898  if(!keys[SDLK_TAB]) {
899  whiteboard_manager_->set_invert_behavior(false);
900  }
901  }
902 }
903 
905 {
906  assert(replay_);
907  return *replay_.get();
908 }
909 
911 {
912  // Saving while an event is running isn't supported
913  // because it may lead to expired event handlers being saved.
914  assert(!gamestate().events_manager_->is_event_running());
915 
916  scoped_savegame_snapshot snapshot(*this);
917  savegame::ingame_savegame save(saved_game_, prefs::get().save_compression_format());
919 }
920 
922 {
923  scoped_savegame_snapshot snapshot(*this);
924  savegame::ingame_savegame save(saved_game_, prefs::get().save_compression_format());
925  save.save_game_automatic(false, filename);
926 }
927 
929 {
930  savegame::replay_savegame save(saved_game_, prefs::get().save_compression_format());
932 }
933 
935 {
936  savegame::replay_savegame save(saved_game_, prefs::get().save_compression_format());
937  save.save_game_automatic(false, filename);
938 }
939 
941 {
943 }
944 
946 {
948  load.load_game_ingame();
949 }
950 
952 {
954  undo_stack().undo();
955 }
956 
958 {
960  undo_stack().redo();
961 }
962 
964 {
966 }
967 
969 {
971 }
972 
973 const std::string& play_controller::select_music(bool victory) const
974 {
975  const std::vector<std::string>& music_list = victory
976  ? (gamestate_->get_game_data()->get_victory_music().empty()
978  : gamestate_->get_game_data()->get_victory_music())
979  : (gamestate_->get_game_data()->get_defeat_music().empty()
981  : gamestate_->get_game_data()->get_defeat_music());
982 
983  if(music_list.empty()) {
984  // Since this function returns a reference, we can't return a temporary empty string.
985  static const std::string empty_str = "";
986  return empty_str;
987  }
988 
989  return music_list[randomness::rng::default_instance().get_random_int(0, music_list.size() - 1)];
990 }
991 
993 {
994  if(is_linger_mode()) {
995  return;
996  }
997 
998  if(is_regular_game_end()) {
999  return;
1000  }
1001 
1002  bool continue_level, found_player, found_network_player, invalidate_all;
1003  std::set<unsigned> not_defeated;
1004 
1006  continue_level,
1007  found_player,
1008  found_network_player,
1010  not_defeated,
1011  gamestate().remove_from_carryover_on_defeat_
1012  );
1013 
1014  if(invalidate_all) {
1015  gui_->invalidate_all();
1016  }
1017 
1018  if(continue_level) {
1019  return;
1020  }
1021 
1022  if(found_player || found_network_player) {
1023  pump().fire("enemies_defeated");
1024  if(is_regular_game_end()) {
1025  return;
1026  }
1027  }
1028 
1029  DBG_EE << "victory_when_enemies_defeated: " << gamestate().victory_when_enemies_defeated_;
1030  DBG_EE << "found_player: " << found_player;
1031  DBG_EE << "found_network_player: " << found_network_player;
1032 
1033  if(!gamestate().victory_when_enemies_defeated_ && (found_player || found_network_player)) {
1034  // This level has asked not to be ended by this condition.
1035  return;
1036  }
1037 
1038  if(video::headless()) {
1039  LOG_AIT << "winner: ";
1040  for(unsigned l : not_defeated) {
1042  if(ai.empty())
1043  ai = "default ai";
1044  LOG_AIT << l << " (using " << ai << ") ";
1045  }
1046 
1047  LOG_AIT;
1048  ai_testing::log_victory(not_defeated);
1049  }
1050 
1051  DBG_EE << "throwing end level exception...";
1052  // Also proceed to the next scenario when another player survived.
1053  end_level_data el_data;
1055  el_data.proceed_to_next_level = found_player || found_network_player;
1056  el_data.is_victory = found_player;
1057  set_end_level_data(el_data);
1058 }
1059 
1060 void play_controller::process_oos(const std::string& msg) const
1061 {
1062  if(video::headless()) {
1063  throw game::game_error(msg);
1064  }
1065 
1067  return;
1068  }
1069 
1070  std::stringstream message;
1071  message << _("The game is out of sync. It might not make much sense to continue. Do you want to save your game?");
1072  message << "\n\n" << _("Error details:") << "\n\n" << msg;
1073 
1074  scoped_savegame_snapshot snapshot(*this);
1076  save.save_game_interactive(message.str(), savegame::savegame::YES_NO); // can throw quit_game_exception
1077 }
1078 
1080 {
1081  return saved_game_.classification().get_tagname() == "multiplayer";
1082 }
1083 
1084 void play_controller::update_gui_to_player(const int team_index, const bool observe)
1085 {
1086  gui_->set_viewing_team_index(team_index, observe);
1087  gui_->recalculate_minimap();
1088  gui_->invalidate_all();
1089 }
1090 
1092 {
1093  scoped_savegame_snapshot snapshot(*this);
1094  savegame::autosave_savegame save(saved_game_, prefs::get().save_compression_format());
1095  save.autosave(false, prefs::get().auto_save_max(), pref_constants::INFINITE_AUTO_SAVES);
1096 }
1097 
1099 {
1100  scoped_savegame_snapshot snapshot(*this);
1101  savegame::ingame_savegame save(saved_game_, prefs::get().save_compression_format());
1102  save.save_game_automatic(true, filename);
1103 }
1104 
1106 {
1107  // note: this writes to level_ if this is not a replay.
1109 }
1110 
1112 {
1113  return gamestate().events_manager_->pump();
1114 }
1115 
1117 {
1118  return soundsources_manager_.get();
1119 }
1120 
1122 {
1123  return plugins_context_.get();
1124 }
1125 
1127 {
1128  return hotkey_handler_.get();
1129 }
1130 
1132 {
1133  if(!gamestate().in_phase(game_data::TURN_PLAYING)) {
1134  return true;
1135  }
1136 
1137  const team& t = current_team();
1138  return !t.is_local_human() || !t.is_proxy_human();
1139 }
1140 
1142 {
1144  return;
1145  }
1146 
1147  try {
1148  play_slice();
1149  } catch(const return_to_play_side_exception&) {
1150  assert(should_return_to_play_side());
1151  }
1152 }
1153 
1155 {
1156  if(gamestate().in_phase(game_data::PRELOAD)) {
1159 
1160  set_scontext_synced sync;
1161 
1162  // So that the code knows it can send choices immidiateley
1163  // todo: im not sure whetrh this is actually needed.
1165  fire_prestart();
1166  if(is_regular_game_end()) {
1167  return;
1168  }
1169 
1170  for(const team& t : get_teams()) {
1171  actions::clear_shroud(t.side(), false, false);
1172  }
1173 
1174  init_gui();
1175  LOG_NG << "first_time..." << (is_skipping_replay() ? "skipping" : "no skip");
1176 
1177  fire_start();
1178  if(is_regular_game_end()) {
1179  return;
1180  }
1181 
1182  sync.do_final_checkup();
1183  gui_->recalculate_minimap();
1184 
1185  // Initialize countdown clock.
1186  for(const team& t : get_teams()) {
1188  t.set_countdown_time(saved_game_.mp_settings().mp_countdown_init_time);
1189  }
1190  }
1191  did_autosave_this_turn_ = false;
1192  } else {
1193  init_gui();
1194  gui_->recalculate_minimap();
1195  }
1196 
1198 }
1199 
1200 /**
1201  * Find all [endlevel]next_scenario= attributes, and add them to @a result.
1202  */
1203 static void find_next_scenarios(const config& parent, std::set<std::string>& result) {
1204  for(const auto& endlevel : parent.child_range("endlevel")) {
1205  if(endlevel.has_attribute("next_scenario")) {
1206  result.insert(endlevel["next_scenario"]);
1207  }
1208  }
1209  for(const auto [key, cfg] : parent.all_children_view()) {
1210  find_next_scenarios(cfg, result);
1211  }
1212 };
1213 
1215  // Which scenarios are reachable from the current one?
1216  std::set<std::string> possible_next_scenarios;
1217  possible_next_scenarios.insert(gamestate().gamedata_.next_scenario());
1218 
1219  // Find all "endlevel" tags that could be triggered in events
1220  config events;
1221  gamestate().events_manager_->write_events(events);
1222  find_next_scenarios(events, possible_next_scenarios);
1223 
1224  // Are we looking for [scenario]id=, [multiplayer]id= or [test]id=?
1225  const auto tagname = saved_game_.classification().get_tagname();
1226 
1227  // Of the possible routes, work out which exist.
1228  bool possible_this_is_the_last_scenario = false;
1229  std::vector<std::string> known;
1230  std::vector<std::string> unknown;
1231  for(const auto& x : possible_next_scenarios) {
1232  if(x.empty() || x == "null") {
1233  possible_this_is_the_last_scenario = true;
1234  LOG_NG << "This can be the last scenario";
1235  } else if(utils::contains(x, '$')) {
1236  // Assume a WML variable will be set to a correct value before the end of the scenario
1237  known.push_back(x);
1238  LOG_NG << "Variable value for next scenario '" << x << "'";
1239  } else if(game_config_.find_child(tagname, "id", x)) {
1240  known.push_back(x);
1241  LOG_NG << "Known next scenario '" << x << "'";
1242  } else {
1243  unknown.push_back(x);
1244  ERR_NG << "Unknown next scenario '" << x << "'";
1245  }
1246  }
1247 
1248  if(unknown.empty()) {
1249  // everything's good
1250  return;
1251  }
1252 
1253  std::string title = _("Warning: broken campaign branches");
1254  std::stringstream message;
1255 
1256  message << _n(
1257  // TRANSLATORS: This is an error that will hopefully only be seen by UMC authors and by players who have already
1258  // said "okay" to a "loading saves from an old version might not work" dialog.
1259  "The next scenario is missing, you will not be able to finish this campaign.",
1260  // TRANSLATORS: This is an error that will hopefully only be seen by UMC authors and by players who have already
1261  // said "okay" to a "loading saves from an old version might not work" dialog.
1262  "Some of the possible next scenarios are missing, you might not be able to finish this campaign.",
1263  unknown.size() + known.size() + (possible_this_is_the_last_scenario ? 1 : 0));
1264  message << "\n\n";
1265  message << _n(
1266  "Please report the following missing scenario to the campaign’s author:\n$unknown_list|",
1267  "Please report the following missing scenarios to the campaign’s author:\n$unknown_list|",
1268  unknown.size());
1269  message << "\n";
1270  message << _("Once this is fixed, you will need to restart this scenario.");
1271 
1272  std::stringstream unknown_list;
1273  for(const auto& x : unknown) {
1274  unknown_list << font::unicode_bullet << " " << x << "\n";
1275  }
1276  utils::string_map symbols;
1277  symbols["unknown_list"] = unknown_list.str();
1278  auto message_str = utils::interpolate_variables_into_string(message.str(), &symbols);
1279  ERR_NG << message_str;
1281 }
1282 
1284 {
1285  const team& viewing_team = gui_->viewing_team();
1286  return gui_->viewing_team_is_playing() && !events::commands_disabled && viewing_team.is_local_human()
1287  && !is_browsing();
1288 }
1289 
1290 std::set<std::string> play_controller::all_players() const
1291 {
1292  std::set<std::string> res = gui_->observers();
1293  for(const team& t : get_teams()) {
1294  if(t.is_human()) {
1295  res.insert(t.current_player());
1296  }
1297  }
1298 
1299  return res;
1300 }
1301 
1303 {
1304  const bool time_left = gamestate().tod_manager_.next_turn(&gamestate().gamedata_);
1305 
1306  if(!time_left) {
1307  LOG_NG << "firing time over event...";
1309  pump().fire("time_over");
1310  LOG_NG << "done firing time over event...";
1311 
1312  // If turns are added while handling 'time over' event.
1313  if(gamestate().tod_manager_.is_time_left()) {
1314  return;
1315  }
1316 
1317  if(video::headless()) {
1318  LOG_AIT << "time over (draw)";
1320  }
1321 
1322  check_victory();
1323  if(is_regular_game_end()) {
1324  return;
1325  }
1326 
1327  end_level_data e;
1328  e.transient.reveal_map = reveal_map_default();
1329  e.proceed_to_next_level = false;
1330  e.is_victory = false;
1332  }
1333 }
1334 
1336  : controller_(controller)
1337 {
1339 }
1340 
1342 {
1343  controller_.saved_game_.remove_snapshot();
1344 }
1345 
1347 {
1348  const team& t = gui_->viewing_team();
1349  static const std::string no_objectives(_("No objectives available"));
1350  std::string objectives = utils::interpolate_variables_into_string(t.objectives(), *gamestate_->get_game_data());
1351  gui2::show_transient_message(get_scenario_name(), (objectives.empty() ? no_objectives : objectives), "", true);
1352  t.reset_objectives_changed();
1353 }
1354 
1356 {
1358  const std::shared_ptr<gui::button> skip_animation_button = get_display().find_action_button("skip-animation");
1359  if(skip_animation_button) {
1360  skip_animation_button->set_check(skip_replay_);
1361  }
1362 }
1363 
1365 {
1366  return is_skipping_replay() || (prefs::get().skip_ai_moves() && current_team().is_ai() && !is_replay());
1367 }
1368 
1370 {
1372 }
1373 
1375 {
1377 }
1378 
1380 {
1383  }
1384 }
map_location loc
Definition: move.cpp:172
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands.
double t
Definition: astarsearch.cpp:63
map_location prev
Definition: astarsearch.cpp:64
Class that keeps track of all the keys on the keyboard.
Definition: key.hpp:29
void new_side_turn(int side)
Performs some initializations and error checks when starting a new side-turn.
Definition: undo.cpp:173
bool can_undo() const
True if there are actions that can be undone.
Definition: undo.hpp:95
void redo()
Redoes the top action on the redo stack.
Definition: undo.cpp:313
bool can_redo() const
True if there are actions that can be redone.
Definition: undo.hpp:97
void undo()
Undoes the top action on the undo stack.
Definition: undo.cpp:280
std::string get_active_ai_identifier_for_side(side_number side)
Gets AI algorithm identifier for active AI of the given side.
Definition: manager.cpp:692
void raise_gamestate_changed()
Notifies all observers of 'ai_gamestate_changed' event.
Definition: manager.cpp:448
static manager & get_singleton()
Definition: manager.hpp:140
static void log_victory(const std::set< unsigned int > &teams)
Definition: testing.cpp:81
static void log_draw()
Definition: testing.cpp:75
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:158
auto all_children_view() const
In-order iteration over all children.
Definition: config.hpp:796
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:880
child_itors child_range(config_key_type key)
Definition: config.cpp:268
config & add_child(config_key_type key)
Definition: config.cpp:436
const game_config_view & game_config_
virtual void play_slice()
int side_upkeep(int side_num) const
@ ONSCREEN
Definition: display.hpp:509
std::shared_ptr< gui::button > find_action_button(const std::string &id)
Retrieves a pointer to a theme UI button.
Definition: display.cpp:771
void do_ai_formula(const std::string &str, int side_num, mouse_handler &mousehandler)
gui::floating_textbox & get_textbox()
void do_command(const std::string &str)
void do_search(const std::string &new_search)
void set_gui(game_display *gui)
Definition: menu_events.hpp:57
std::vector< std::string > get_commands_list()
void set_gui(game_display *gui)
const pathfind::paths & current_paths() const
void set_current_paths(const pathfind::paths &new_paths)
unit_map::iterator selected_unit()
map_location get_selected_hex() const
void select_hex(const map_location &hex, const bool browse, const bool highlight=true, const bool fire_event=true, const bool force_unhighlight=false)
int get_path_turns() const
void set_side(int side_number)
void set_path_turns(const int path_turns)
void check_victory(bool &, bool &, bool &, bool &, std::set< unsigned > &, bool)
Definition: game_board.cpp:114
team & get_team(int i)
Definition: game_board.hpp:92
void end_turn(int pnum)
Definition: game_board.cpp:79
void new_turn(int pnum)
Definition: game_board.cpp:70
std::string get_tagname() const
optional_const_config find_child(config_key_type key, const std::string &name, const std::string &value) const
static game_config_view wrap(const config &cfg)
void set_phase(PHASE phase)
Definition: game_data.hpp:106
@ PRELOAD
the preload [event] is fired next phase: PRESTART (normal game), TURN_STARTING_WAITING (reloaded game...
Definition: game_data.hpp:76
@ INITIAL
creating intitial [unit]s, executing toplevel [lua] etc.
Definition: game_data.hpp:73
@ GAME_ENDED
The game has ended and the user is observing the final state "lingering" The game can be saved here.
Definition: game_data.hpp:102
@ PRESTART
the prestart [event] is fired next phase: START (default), GAME_ENDING
Definition: game_data.hpp:79
@ TURN_PLAYING
The User is controlling the game and invoking actions The game can be saved here.
Definition: game_data.hpp:93
@ TURN_STARTING_WAITING
we are waiting for the turn to start.
Definition: game_data.hpp:86
@ START
the start [event] is fired next phase: TURN_STARTING_WAITING (default), GAME_ENDING
Definition: game_data.hpp:82
@ TURN_STARTING
the turn, side turn etc.
Definition: game_data.hpp:89
PHASE phase() const
Definition: game_data.hpp:105
map_location last_selected
the last location where a select event fired.
Definition: game_data.hpp:126
config::attribute_value & get_variable(const std::string &varname)
throws invalid_variablename_exception if varname is no valid variable name.
Definition: game_data.cpp:66
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 write(config &cfg) const
Definition: game_state.cpp:216
bool in_phase(game_data::PHASE phase) const
Definition: game_state.hpp:109
void set_game_display(game_display *)
Definition: game_state.cpp:211
std::unique_ptr< game_lua_kernel > lua_kernel_
Definition: game_state.hpp:48
std::unique_ptr< pathfind::manager > pathfind_manager_
Definition: game_state.hpp:46
game_board board_
Definition: game_state.hpp:44
const std::unique_ptr< game_events::manager > events_manager_
Definition: game_state.hpp:50
bool victory_when_enemies_defeated_
Definition: game_state.hpp:61
tod_manager tod_manager_
Definition: game_state.hpp:45
game_data gamedata_
Definition: game_state.hpp:43
static void progress(loading_stage stage=loading_stage::none)
Report what is being loaded to the loading screen.
static void raise()
Raise the loading screen to the top of the draw stack.
static void display(const std::function< void()> &f)
@ close_button
Shows a close button.
Definition: message.hpp:75
void memorize_command(const std::string &command)
const std::unique_ptr< gui::textbox > & box() const
TEXTBOX_MODE mode() const
void tab(const std::set< std::string > &dictionary)
const std::vector< std::string > & command_history() const
game_classification & get_classification()
const std::string & select_music(bool victory) const
void process_keydown_event(const SDL_Event &event) override
Process keydown (always).
void maybe_throw_return_to_play_side() const
config to_config() const
Builds the snapshot config from members and their respective configs.
void init(const config &level)
std::vector< team > & get_teams()
std::unique_ptr< hotkey_handler > hotkey_handler_
virtual void init_gui()
bool have_keyboard_focus() override
Derived classes should override this to return false when arrow keys should not scroll the map,...
std::unique_ptr< game_state > gamestate_
void show_objectives() const
events::menu_handler menu_handler_
void fire_preload()
preload events cannot be synced
void check_victory()
Checks to see if a side has won.
bool is_linger_mode() const
actions::undo_list & undo_stack()
statistics_t & statistics()
virtual soundsource::manager * get_soundsource_man() override
Get (optionally) a soundsources manager a derived class uses.
void check_next_scenario_is_known()
This shows a warning dialog if either [scenario]next_scenario or any [endlevel]next_scenario would le...
bool reveal_map_default() const
const unit_map & get_units() const
play_controller(const config &level, saved_game &state_of_game)
bool do_healing() const
void set_end_level_data(const end_level_data &data)
events::mouse_handler & get_mouse_handler_base() override
Get a reference to a mouse handler member a derived class uses.
void update_savegame_snapshot() const
void set_do_healing(bool do_healing)
bool is_skipping_actions() const
bool is_during_turn() const
virtual void require_end_turn()=0
bool can_use_synced_wml_menu() const
void reset_gamestate(const config &level, int replay_pos)
void do_consolesave(const std::string &filename)
void save_game_auto(const std::string &filename)
void textbox_move_vertically(bool up)
bool is_regular_game_end() const
virtual void check_objectives()=0
void save_replay_auto(const std::string &filename)
saved_game & saved_game_
game_state & gamestate()
hotkey::command_executor * get_hotkey_command_executor() override
Optionally get a command executor to handle context menu events.
bool enemies_visible() const
std::unique_ptr< replay > replay_
const mp_game_settings & get_mp_settings()
game_display & get_display() override
Get a reference to a display member a derived class uses.
virtual void update_viewing_player()=0
std::unique_ptr< game_display > gui_
void maybe_do_init_side()
Called by turn_info::process_network_data() or init_side() to call do_init_side() if necessary.
virtual void process_oos(const std::string &msg) const
Asks the user whether to continue on an OOS error.
bool is_browsing() const override
std::unique_ptr< soundsource::manager > soundsources_manager_
void do_init_side()
Called by replay handler or init_side() to do actual work for turn change.
std::string theme() const
int current_side() const
Returns the number of the side whose turn it is.
bool is_skipping_replay() const
std::shared_ptr< wb::manager > get_whiteboard() const
bool did_autosave_this_turn_
Whether we did init sides in this session (false = we did init sides before we reloaded the game).
bool is_replay() const
std::size_t turn() const
map_location map_start_
virtual bool should_return_to_play_side() const
std::set< std::string > all_players() const
void update_gui_to_player(const int team_index, const bool observe=false)
Changes the UI for this client to the passed side index.
events::mouse_handler mouse_handler_
const auto & timer() const
std::unique_ptr< plugins_context > plugins_context_
virtual plugins_context * get_plugins_context() override
Get (optionally) a plugins context a derived class uses.
virtual ~play_controller()
bool can_undo() const
void process_focus_keydown_event(const SDL_Event &event) override
Process keydown (only when the general map display does not have focus).
void refresh_objectives() const
Reevaluate [show_if] conditions and build a new objectives string.
bool can_redo() const
game_events::wml_event_pump & pump()
void process_keyup_event(const SDL_Event &event) override
Process keyup (always).
void finish_side_turn_events()
persist_manager persist_
std::shared_ptr< wb::manager > whiteboard_manager_
virtual void check_time_over()
t_string get_scenario_name() const
static plugins_manager * get()
Definition: manager.cpp:58
static prefs & get()
void encounter_all_content(const game_board &gb)
std::map< std::string, std::string > get_acquaintances_nice(const std::string &filter)
Implements a quit confirmation dialog.
static rng & default_instance()
Definition: random.cpp:73
int get_random_int(int min, int max)
Definition: random.hpp:51
bool add_start_if_not_there_yet()
Definition: replay.cpp:660
void init_side()
Definition: replay.cpp:204
config * get_next_action()
Definition: replay.cpp:606
Exception used to escape form the ai or ui code to playsingle_controller::play_side.
game_classification & classification()
Definition: saved_game.hpp:56
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
Definition: saved_game.hpp:60
replay_recorder_base & get_replay()
Definition: saved_game.hpp:140
config & set_snapshot(config snapshot)
Definition: saved_game.cpp:610
Class for autosaves.
Definition: savegame.hpp:281
void autosave(const bool disable_autosave, const int autosave_max, const int infinite_autosaves)
Definition: savegame.cpp:601
Class for "normal" midgame saves.
Definition: savegame.hpp:254
The class for loading a savefile.
Definition: savegame.hpp:101
Class for replay saves (either manually or automatically).
Definition: savegame.hpp:268
static std::shared_ptr< save_index_class > default_saves_dir()
Returns an instance for managing saves in filesystem::get_saves_dir()
Definition: save_index.cpp:209
bool save_game_automatic(bool ask_for_overwrite=false, const std::string &filename="")
Saves a game without user interaction, unless the file exists and it should be asked to overwrite it.
Definition: savegame.cpp:361
bool save_game_interactive(const std::string &message, DIALOG_TYPE dialog_type)
Save a game interactively through the savegame dialog.
Definition: savegame.cpp:378
A RAII object to enter the synced context, cannot be called if we are already in a synced context.
void do_final_checkup(bool dont_throw=false)
void reset_turn_stats(const std::string &save_id)
Definition: statistics.cpp:207
static bool run_in_synced_context_if_not_already(const std::string &commandname, const config &data, action_spectator &spectator=get_default_spectator())
Checks whether we are currently running in a synced context, and if not we enters it.
static synced_state get_synced_state()
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:75
bool is_ai() const
Definition: team.hpp:256
int support() const
Calculate total support capacity, based on support_per_village.
Definition: team.hpp:195
bool is_local_human() const
Definition: team.hpp:258
void spend_gold(const int amount)
Definition: team.hpp:200
void new_turn()
Definition: team.hpp:196
void turn_event_fired()
const time_of_day & get_time_of_day(int for_turn=0) const
Returns global time of day for the passed turn.
Definition: tod_manager.hpp:56
bool next_turn(game_data *vars)
Function to move to the next turn.
void remove_scenario_fixes()
Definition: types.cpp:1467
void apply_scenario_fix(const config &cfg)
Definition: types.cpp:1432
This class represents a single unit of a specific type.
Definition: unit.hpp:133
A variable-expanding proxy for the config class.
Definition: variable.hpp:45
This class is the frontend of the whiteboard framework for the rest of the Wesnoth code.
Definition: manager.hpp:44
int w
void throw_quit_game_exception()
static std::string _n(const char *str1, const char *str2, int n)
Definition: gettext.hpp:97
static std::string _(const char *str)
Definition: gettext.hpp:93
void calculate_healing(int side, bool update_display)
Calculates healing for all units for the given side.
Definition: heal.cpp:290
Various functions that implement healing of units (when a side turn starts).
This file implements all the hotkey handling and menu details for play controller.
Standard logging facilities (interface).
#define log_scope(description)
Definition: log.hpp:276
Declarations for a class that implements WML-defined (right-click) menu items.
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
void recalculate_fog(int side)
Function that recalculates the fog of war.
Definition: vision.cpp:697
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:59
void invalidate_all()
Mark the entire screen as requiring redraw.
Handling of system events.
Graphical text output.
const std::string unicode_bullet
Definition: constants.cpp:47
std::string observer
std::vector< std::string > default_defeat_music
bool ignore_replay_errors
Definition: game_config.cpp:89
static void add_color_info(const game_config_view &v, bool build_defaults)
std::vector< std::string > default_victory_music
std::vector< game_tip > load(const config &cfg)
Loads the tips from a config.
Definition: tips.cpp:37
void show_transient_message(const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup)
Shows a transient message to the user.
void show_error_message(const std::string &msg, bool message_use_markup)
Shows an error message to the user.
Definition: message.cpp:201
void show_message(const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition: message.cpp:148
@ TEXTBOX_MESSAGE
@ TEXTBOX_SEARCH
@ TEXTBOX_COMMAND
Keyboard shortcuts for game actions.
constexpr uint32_t scope_game
std::string tag(std::string_view tag, Args &&... data)
Wraps the given data in the specified formatting tag.
Definition: markup.hpp:50
const int INFINITE_AUTO_SAVES
Definition: preferences.hpp:59
::tod_manager * tod_manager
Definition: resources.cpp:29
game_board * gameboard
Definition: resources.cpp:20
persist_manager * persist
Definition: resources.cpp:26
game_data * gamedata
Definition: resources.cpp:22
game_events::manager * game_events
Definition: resources.cpp:24
replay * recorder
Definition: resources.cpp:28
actions::undo_list * undo_stack
Definition: resources.cpp:32
soundsource::manager * soundsources
Definition: resources.cpp:27
game_lua_kernel * lua_kernel
Definition: resources.cpp:25
game_classification * classification
Definition: resources.cpp:34
pathfind::manager * tunnels
Definition: resources.cpp:31
play_controller * controller
Definition: resources.cpp:21
filter_context * filter_con
Definition: resources.cpp:23
std::shared_ptr< wb::manager > whiteboard
Definition: resources.cpp:33
void write_music_play_list(config &snapshot)
Definition: sound.cpp:872
void play_sound(const std::string &files, channel_group group, unsigned int repeats)
Definition: sound.cpp:1047
@ SOUND_SOURCES
Definition: sound.hpp:30
std::size_t size(std::string_view str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
constexpr auto keys
Definition: ranges.hpp:39
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with '$' in the string 'str' with the equivalent ...
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:86
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
Definition: general.cpp:23
std::map< std::string, t_string > string_map
auto * find(Container &container, const Value &value)
Convenience wrapper for using find on a container without needing to comare to end()
Definition: general.hpp:140
bool headless()
The game is running headless.
Definition: video.cpp:139
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
static void clear_resources()
static lg::log_domain log_engine("engine")
#define ERR_NG
static lg::log_domain log_aitesting("ai/testing")
static void find_next_scenarios(const config &parent, std::set< std::string > &result)
Find all [endlevel]next_scenario= attributes, and add them to result.
static void copy_persistent(const config &src, config &dst)
Copies [scenario] attributes/tags that are not otherwise stored in C++ structs/clases.
static lg::log_domain log_enginerefac("enginerefac")
#define LOG_AIT
static lg::log_domain log_display("display")
static lg::log_domain log_engine_enemies("engine/enemies")
#define DBG_NG
#define ERR_DP
#define DBG_EE
#define LOG_NG
Define the game's event mechanism.
Replay control code.
rect dst
Location on the final composed sheet.
rect src
Non-transparent portion of the surface to compose.
std::string filename
Filename.
Additional information on the game outcome which can be provided by WML.
bool proceed_to_next_level
whether to proceed to the next scenario, equals is_victory in sp.
transient_end_level transient
Error used for any general game error, e.g.
Definition: game_errors.hpp:47
Encapsulates the map of the game.
Definition: location.hpp:45
static const map_location & null_location()
Definition: location.hpp:102
std::chrono::seconds mp_countdown_init_time
Object which contains all the possible locations a unit can move to, with associated best routes to t...
Definition: pathfind.hpp:73
scoped_savegame_snapshot(const play_controller &controller)
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:57
std::string sounds
List of "ambient" sounds associated with this time_of_day, Played at the beginning of turn.
bool reveal_map
Should we reveal map when game is ended? (Multiplayer only)
bool valid() const
Definition: map.hpp:273
Object which temporarily resets a unit's movement.
Definition: unit.hpp:2144
const std::string & gamedata
Gather statistics important for AI testing and output them.
unit_type_data unit_types
Definition: types.cpp:1504
Various functions that implement the undoing (and redoing) of in-game commands.
Various functions implementing vision (through fog of war and shroud).
#define e