The Battle for Wesnoth  1.17.23+dev
play_controller.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2023
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/advancement.hpp"
25 #include "actions/create.hpp"
26 #include "actions/heal.hpp"
27 #include "actions/undo.hpp"
28 #include "actions/vision.hpp"
29 #include "ai/manager.hpp"
30 #include "ai/testing.hpp"
31 #include "display_chat_manager.hpp"
32 #include "floating_label.hpp"
33 #include "formula/string_utils.hpp"
34 #include "game_errors.hpp"
36 #include "game_events/pump.hpp"
37 #include "game_state.hpp"
38 #include "gettext.hpp"
40 #include "gui/dialogs/message.hpp" // for show_error_message
44 #include "hotkey/hotkey_item.hpp"
45 #include "log.hpp"
46 #include "map/label.hpp"
47 #include "pathfind/teleport.hpp"
49 #include "preferences/display.hpp"
50 #include "preferences/game.hpp"
51 #include "random.hpp"
52 #include "replay.hpp"
53 #include "reports.hpp"
54 #include "resources.hpp"
55 #include "save_index.hpp"
56 #include "saved_game.hpp"
57 #include "savegame.hpp"
60 #include "sound.hpp"
61 #include "soundsource.hpp"
62 #include "statistics.hpp"
63 #include "synced_context.hpp"
64 #include "tooltips.hpp"
65 #include "units/id.hpp"
66 #include "units/types.hpp"
67 #include "units/unit.hpp"
68 #include "utils/general.hpp"
69 #include "video.hpp"
70 #include "whiteboard/manager.hpp"
71 
72 #include <functional>
73 
74 static lg::log_domain log_aitesting("ai/testing");
75 #define LOG_AIT LOG_STREAM(info, log_aitesting)
76 // If necessary, this define can be replaced with `#define LOG_AIT std::cout` to restore previous behavior
77 
78 static lg::log_domain log_engine("engine");
79 #define LOG_NG LOG_STREAM(info, log_engine)
80 #define DBG_NG LOG_STREAM(debug, log_engine)
81 #define ERR_NG LOG_STREAM(err, log_engine)
82 
83 static lg::log_domain log_display("display");
84 #define ERR_DP LOG_STREAM(err, log_display)
85 
86 static lg::log_domain log_enginerefac("enginerefac");
87 #define LOG_RG LOG_STREAM(info, log_enginerefac)
88 
89 static lg::log_domain log_engine_enemies("engine/enemies");
90 #define DBG_EE LOG_STREAM(debug, log_engine_enemies)
91 
92 /**
93  * Copies [scenario] attributes/tags that are not otherwise stored in C++ structs/clases.
94  */
95 static void copy_persistent(const config& src, config& dst)
96 {
97  static const std::set<std::string> attrs {
98  "description",
99  "name",
100  "victory_when_enemies_defeated",
101  "remove_from_carryover_on_defeat",
102  "disallow_recall",
103  "experience_modifier",
104  "require_scenario",
105  "loaded_resources"
106  };
107 
108  static const std::set<std::string> tags {
109  "terrain_graphics",
110  "modify_unit_type",
111  "lua"
112  };
113 
114  for(const std::string& attr : attrs) {
115  dst[attr] = src[attr];
116  }
117 
118  for(const std::string& tag : tags) {
119  dst.append_children(src, tag);
120  }
121 }
122 
123 static void clear_resources()
124 {
125  resources::controller = nullptr;
126  resources::filter_con = nullptr;
127  resources::gameboard = nullptr;
128  resources::gamedata = nullptr;
129  resources::lua_kernel = nullptr;
130  resources::game_events = nullptr;
131  resources::persist = nullptr;
132  resources::soundsources = nullptr;
133  resources::tod_manager = nullptr;
134  resources::tunnels = nullptr;
135  resources::undo_stack = nullptr;
136  resources::recorder = nullptr;
137  resources::whiteboard.reset();
138  resources::classification = nullptr;
139 }
140 
141 play_controller::play_controller(const config& level, saved_game& state_of_game, bool skip_replay, bool start_faded)
142  : controller_base()
143  , observer()
145  , ticks_(SDL_GetTicks())
146  , gamestate_()
147  , level_()
148  , saved_game_(state_of_game)
149  , tooltips_manager_()
150  , whiteboard_manager_()
151  , plugins_context_()
152  , labels_manager_(new font::floating_label_context())
153  , help_manager_(&game_config_)
154  , mouse_handler_(nullptr, *this)
155  , menu_handler_(nullptr, *this)
156  , hotkey_handler_(new hotkey_handler(*this, saved_game_))
157  , soundsources_manager_()
158  , persist_()
159  , gui_()
160  , xp_mod_(new unit_experience_accelerator(level["experience_modifier"].to_int(100)))
161  , statistics_context_(new statistics_t(state_of_game.statistics()))
162  , replay_(new replay(state_of_game.get_replay()))
163  , skip_replay_(skip_replay)
164  , skip_story_(state_of_game.skip_story())
165  , did_autosave_this_turn_(true)
166  , did_tod_sound_this_turn_(false)
167  , map_start_()
168  , start_faded_(start_faded)
169  , victory_when_enemies_defeated_(level["victory_when_enemies_defeated"].to_bool(true))
170  , remove_from_carryover_on_defeat_(level["remove_from_carryover_on_defeat"].to_bool(true))
171  , victory_music_()
172  , defeat_music_()
173  , scope_(hotkey::scope_game)
174  , ignore_replay_errors_(false)
175  , player_type_changed_(false)
176 {
178 
179  for(const config& modify_unit_type : level_.child_range("modify_unit_type")) {
180  unit_types.apply_scenario_fix(modify_unit_type);
181  }
182  resources::controller = this;
185 
187 
189 
191 
192  try {
193  init(level);
194  } catch(...) {
195  DBG_NG << "Caught exception initializing level: " << utils::get_unknown_exception_type();
196  clear_resources();
197  throw;
198  }
199 }
200 
202 {
204  clear_resources();
205 }
206 
208 {
209  /*
210  * Initilisation currently happens on the following order:
211  * 1) This code, which is executed with the loadingscreen
212  * From inside the constructor.
213  * 2) The Music is changed.
214  * 3) The Storyscreen is shown.
215  * 4) The Labels are added
216  * 5) The preload event is fired
217  * 6) The prestart event is fired
218  * 7) The gui is activated
219  * 8) The start event is fired
220  */
223 
224  LOG_NG << "initializing game_state..." << (SDL_GetTicks() - ticks());
225  gamestate_.reset(new game_state(level, *this));
226 
234 
235  gamestate_->ai_manager_.add_observer(this);
236  gamestate_->init(level, *this);
238 
239  LOG_NG << "initializing whiteboard..." << (SDL_GetTicks() - ticks());
241  whiteboard_manager_.reset(new wb::manager());
243 
244  LOG_NG << "loading units..." << (SDL_GetTicks() - ticks());
247 
248  LOG_NG << "initializing theme... " << (SDL_GetTicks() - ticks());
250 
251  LOG_NG << "building terrain rules... " << (SDL_GetTicks() - ticks());
253 
254  gui_.reset(new game_display(gamestate().board_, whiteboard_manager_, *gamestate().reports_, theme(), level));
255  map_start_ = map_location(level.child_or_empty("display").child_or_empty("location"));
256  if(start_faded_) {
257  gui_->set_fade({0,0,0,255});
258  gui_->set_prevent_draw(true);
259  }
260 
261  // Ensure the loading screen doesn't end up underneath the game display
263 
264  if(!video::headless()) {
266  gui_->get_theme().modify_label("time-icon", _("time left for current turn"));
267  } else {
268  gui_->get_theme().modify_label("time-icon", _("current local time"));
269  }
270  }
271 
273  mouse_handler_.set_gui(gui_.get());
274  menu_handler_.set_gui(gui_.get());
275 
276  LOG_NG << "done initializing display... " << (SDL_GetTicks() - ticks());
277 
278  LOG_NG << "building gamestate to gui and whiteboard... " << (SDL_GetTicks() - ticks());
279  // This *needs* to be created before the show_intro and show_map_scene
280  // as that functions use the manager state_of_game
281  // Has to be done before registering any events!
284 
285  init_managers();
287  // loadscreen_manager->reset();
289  gamestate().lua_kernel_->load_game(level);
290 
291  plugins_context_.reset(new plugins_context("Game"));
292  plugins_context_->set_callback("save_game", [this](const config& cfg) { save_game_auto(cfg["filename"]); }, true);
293  plugins_context_->set_callback("save_replay", [this](const config& cfg) { save_replay_auto(cfg["filename"]); }, true);
294  plugins_context_->set_callback("quit", [](const config&) { throw_quit_game_exception(); }, false);
295  plugins_context_->set_accessor_string("scenario_name", [this](config) { return get_scenario_name(); });
296  });
297 }
298 
299 void play_controller::reset_gamestate(const config& level, int replay_pos)
300 {
301  // TODO: should we update we update this->level_ with level ?
302 
303  resources::gameboard = nullptr;
304  resources::gamedata = nullptr;
305  resources::tod_manager = nullptr;
306  resources::filter_con = nullptr;
307  resources::lua_kernel = nullptr;
308  resources::game_events = nullptr;
309  resources::tunnels = nullptr;
310  resources::undo_stack = nullptr;
311 
312  gui_->labels().set_team(nullptr);
313 
314  /* First destroy the old game state, then create the new one.
315  This is necessary to ensure that while the old AI manager is being destroyed,
316  all its member objects access the old manager instead of the new. */
317  gamestate_.reset();
318  gamestate_.reset(new game_state(level, *this));
319 
327 
328  gamestate_->ai_manager_.add_observer(this);
329  gamestate_->init(level, *this);
332 
333  gui_->reset_reports(*gamestate().reports_);
334  gui_->change_display_context(&gamestate().board_);
335  saved_game_.get_replay().set_pos(replay_pos);
336 
338  gamestate().lua_kernel_->load_game(level);
339 }
340 
342 {
343  LOG_NG << "initializing managers... " << (SDL_GetTicks() - ticks());
345 
347  LOG_NG << "done initializing managers... " << (SDL_GetTicks() - ticks());
348 }
349 
351 {
352  // Run initialization scripts, even if loading from a snapshot.
353  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
354  pump().fire("preload");
355  gamestate().lua_kernel_->preload_finished();
356 }
357 
359 {
360  // pre-start events must be executed before any GUI operation,
361  // as those may cause the display to be refreshed.
363 
364  // Fire these right before prestart events, to catch only the units sides
365  // have started with.
366  for(const unit& u : get_units()) {
367  pump().fire("unit_placed", map_location(u.get_location()));
368  }
369 
370  pump().fire("prestart");
371 
372  // prestart event may modify start turn with WML, reflect any changes.
373  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
374 }
375 
377 {
378  const config cfg("side", gui_->viewing_side());
379  gamestate().lua_kernel_->run_wml_action("show_objectives", vconfig(cfg),
380  game_events::queued_event("_from_interface", "", map_location(), map_location(), config()));
381 }
382 
384 {
386  pump().fire("start");
387 
388  skip_story_ = false; // Show [message]s from now on even with --campaign-skip-story
389 
390  // start event may modify start turn with WML, reflect any changes.
391  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
392 
395 
396  // prestart and start events may modify the initial gold amount,
397  // reflect any changes.
398  for(team& tm : get_teams()) {
399  tm.set_start_gold(tm.gold());
400  }
401 
403 }
404 
406 {
407  gui_->begin_game();
408  gui_->update_tod();
409 }
410 
412 {
414  gui_->set_playing_team(std::size_t(current_side() - 1));
415 
417 
419 }
420 
422 {
423  //
424  // We do side init only if not done yet for a local side when we are not replaying.
425  // For all other sides it is recorded in replay and replay handler has to handle
426  // calling do_init_side() functions.
427  //
428  if(is_during_turn()) {
429  // We already executed do_init_side this can for example happe if we reload a game,
430  // but also if we changed control of a side during it's turn
431  return;
432  }
433 
434  if(!current_team().is_local()) {
435  // We are in a mp game and execute do_init_side as soon as we receive [init_side] from the current player
436  // (see replay.cpp)
437  return;
438  }
439 
440  if(is_replay()) {
441  // We are in a replay and execute do_init_side as soon as we reach the next [init_side] in the replay data
442  // (see replay.cpp)
443  return;
444  }
445 
446  if(current_team().is_idle()) {
447  // In this case it can happen that we just gave control of this side to another player so doing init_side
448  // could lead to errors since we no longer own this side from the servers perspective.
449  // (see playturn.cpp)
450  return;
451  }
452 
454  do_init_side();
455 }
456 
458 {
459  { // Block for set_scontext_synced
460  set_scontext_synced sync;
461  log_scope("player turn");
462  // In case we might end up calling sync:network during the side turn events,
463  // and we don't want do_init_side to be called when a player drops.
465  gamestate_->next_player_number_ = gamestate_->player_number_ + 1;
466 
467  const std::string turn_num = std::to_string(turn());
468  const std::string side_num = std::to_string(current_side());
469 
470  gamestate().gamedata_.get_variable("side_number") = current_side();
471 
472  // We might have skipped some sides because they were empty so it is not enough to check for side_num==1
473  if(!gamestate().tod_manager_.has_turn_event_fired()) {
474  pump().fire("turn_" + turn_num);
475  pump().fire("new_turn");
477  }
478 
479  pump().fire("side_turn");
480  pump().fire("side_" + side_num + "_turn");
481  pump().fire("side_turn_" + turn_num);
482  pump().fire("side_" + side_num + "_turn_" + turn_num);
483 
484  // We want to work out if units for this player should get healed,
485  // and the player should get income now.
486  // Healing/income happen if it's not the first turn of processing,
487  // or if we are loading a game.
488  if(turn() > 1) {
491 
492  // If the expense is less than the number of villages owned
493  // times the village support capacity,
494  // then we don't have to pay anything at all
495  int expense = gamestate().board_.side_upkeep(current_side()) - current_team().support();
496  if(expense > 0) {
497  current_team().spend_gold(expense);
498  }
499  }
500 
501  if(do_healing()) {
503  }
504 
505  // Do healing on every side turn except the very first side turn.
506  // (1.14 and earlier did healing whenever turn >= 2.)
507  set_do_healing(true);
508 
509  // Set resting now after the healing has been done.
510  for(unit& patient : resources::gameboard->units()) {
511  if(patient.side() == current_side()) {
512  patient.set_resting(true);
513  }
514  }
515 
516  // Prepare the undo stack.
518 
519  pump().fire("turn_refresh");
520  pump().fire("side_" + side_num + "_turn_refresh");
521  pump().fire("turn_" + turn_num + "_refresh");
522  pump().fire("side_" + side_num + "_turn_" + turn_num + "_refresh");
523 
524  // Make sure vision is accurate.
526 
527  check_victory();
528  sync.do_final_checkup();
530  }
531 
532  init_side_end();
533 
534  if(!is_skipping_replay() && current_team().get_scroll_to_leader()) {
535  gui_->scroll_to_leader(current_side(), game_display::ONSCREEN, false);
536  }
537 }
538 
540 {
545  }
546  whiteboard_manager_->on_init_side();
547 }
548 
550 {
551  config cfg = level_;
552 
553  cfg["replay_pos"] = saved_game_.get_replay().get_pos();
554  gamestate().write(cfg);
555 
556  gui_->write(cfg.add_child("display"));
557 
558  // Write the soundsources.
559  soundsources_manager_->write_sourcespecs(cfg);
560 
561  gui_->labels().write(cfg);
563 
564  if(cfg["replay_pos"].to_int(0) > 0 && cfg["playing_team"].empty()) {
565  gui2::show_error_message(_("Trying to create a corrupt file, please report this bug"));
566  }
567 
568  return cfg;
569 }
570 
572 {
573 
574  { // Block for set_scontext_synced
575  set_scontext_synced sync(1);
576  // Ending the turn commits all moves.
577  undo_stack().clear();
579  const std::string turn_num = std::to_string(turn());
580  const std::string side_num = std::to_string(current_side());
581 
582  // Clear shroud, in case units had been slowed for the turn.
584 
585  pump().fire("side_turn_end");
586  pump().fire("side_" + side_num + "_turn_end");
587  pump().fire("side_turn_" + turn_num + "_end");
588  pump().fire("side_" + side_num + "_turn_" + turn_num + "_end");
589  // This is where we refog, after all of a side's events are done.
591  check_victory();
592  sync.do_final_checkup();
593  }
595 }
596 
598 {
599  set_scontext_synced sync(2);
600  const std::string turn_num = std::to_string(turn());
601  pump().fire("turn_end");
602  pump().fire("turn_" + turn_num + "_end");
603  sync.do_final_checkup();
604 }
605 
607 {
608  // If we aren't using fog/shroud, this is easy :)
609  if(current_team().uses_fog() == false && current_team().uses_shroud() == false) {
610  return true;
611  }
612 
613  // See if any enemies are visible
614  for(const unit& u : get_units()) {
615  if(current_team().is_enemy(u.side()) && !gui_->fogged(u.get_location())) {
616  return true;
617  }
618  }
619 
620  return false;
621 }
622 
624 {
625  if(menu_handler_.get_textbox().active() == false) {
626  return;
627  }
628 
629  const std::string str = menu_handler_.get_textbox().box()->text();
630  const unsigned int team_num = current_side();
631  events::mouse_handler& mousehandler = mouse_handler_;
632 
633  switch(menu_handler_.get_textbox().mode()) {
634  case gui::TEXTBOX_SEARCH:
638  break;
641  menu_handler_.get_textbox().close(); // need to close that one after executing do_speak() !
642  break;
647  break;
648  case gui::TEXTBOX_AI:
651  menu_handler_.do_ai_formula(str, team_num, mousehandler);
652  break;
653  default:
655  ERR_DP << "unknown textbox mode";
656  }
657 }
658 
660 {
661  if(menu_handler_.get_textbox().active() == false) {
662  return;
663  }
664 
667  // Not handling messages to avoid spam
668  return;
669  }
670 
671  const std::string str = menu_handler_.get_textbox().box()->text();
672  const std::vector<std::string>& command_history = menu_handler_.get_textbox().command_history();
673 
674  auto prev = std::find(command_history.begin(), command_history.end(), str);
675 
676  if (prev != command_history.end())
677  {
678  if(up) {
679  if(prev != command_history.begin()) {
680  menu_handler_.get_textbox().box()->set_text(*--prev);
681  }
682  } else {
683  if(++prev != command_history.end()) {
684  menu_handler_.get_textbox().box()->set_text(*prev);
685  } else {
686  menu_handler_.get_textbox().box()->set_text("");
687  }
688  }
689  } else if (up) {
690  if(command_history.size() > 0) {
691  menu_handler_.get_textbox().box()->set_text(*--prev);
692  }
693  if(!str.empty()) {
695  }
696  }
697 }
698 
700 {
702 
703  std::set<std::string> dictionary;
704  switch(mode) {
705  case gui::TEXTBOX_SEARCH: {
706  for(const unit& u : get_units()) {
707  const map_location& loc = u.get_location();
708  if(!gui_->fogged(loc) && !(get_teams()[gui_->viewing_team()].is_enemy(u.side()) && u.invisible(loc)))
709  dictionary.insert(u.name());
710  }
711  // TODO List map labels
712  break;
713  }
714  case gui::TEXTBOX_COMMAND: {
715  std::vector<std::string> commands = menu_handler_.get_commands_list();
716  dictionary.insert(commands.begin(), commands.end());
717  [[fallthrough]]; // we also want player names from the next case
718  }
719  case gui::TEXTBOX_MESSAGE: {
720  for(const team& t : get_teams()) {
721  if(!t.is_empty())
722  dictionary.insert(t.current_player());
723  }
724 
725  // Add observers
726  for(const std::string& o : gui_->observers()) {
727  dictionary.insert(o);
728  }
729 
730  // Add nicks who whispered you
731  for(const std::string& w : gui_->get_chat_manager().whisperers()) {
732  dictionary.insert(w);
733  }
734 
735  // Add nicks from friendlist
736  const std::map<std::string, std::string> friends = preferences::get_acquaintances_nice("friend");
737 
738  for(std::map<std::string, std::string>::const_iterator iter = friends.begin(); iter != friends.end(); ++iter) {
739  dictionary.insert((*iter).first);
740  }
741 
742  // Exclude own nick from tab-completion.
743  // NOTE why ?
744  dictionary.erase(preferences::login());
745  break;
746  }
747 
748  default:
749  ERR_DP << "unknown textbox mode";
750  } // switch(mode)
751 
752  menu_handler_.get_textbox().tab(dictionary);
753 }
754 
756 {
757  if(get_teams().size() == 0) {
758  throw game::game_error("The scenario has no sides defined");
759  }
760 
761  assert(gamestate().board_.has_team(current_side()));
763 }
764 
766 {
767  if(get_teams().size() == 0) {
768  throw game::game_error("The scenario has no sides defined");
769  }
770 
771  assert(gamestate().board_.has_team(current_side()));
773 }
774 
775 bool play_controller::is_team_visible(int team_num, bool observer) const
776 {
777  const team& t = gamestate().board_.get_team(team_num);
778  if(observer) {
779  return !t.get_disallow_observers() && !t.is_empty();
780  } else {
781  return t.is_local_human() && !t.is_idle();
782  }
783 }
784 
786 {
787  const int num_teams = get_teams().size();
788  const bool observer = is_observer();
789 
790  for(int i = 0; i < num_teams; i++) {
791  const int team_num = modulo(current_side() + i, num_teams, 1);
792  if(is_team_visible(team_num, observer)) {
793  return team_num;
794  }
795  }
796 
797  return 0;
798 }
799 
801 {
802  return mouse_handler_;
803 }
804 
805 std::shared_ptr<wb::manager> play_controller::get_whiteboard() const
806 {
807  return whiteboard_manager_;
808 }
809 
811 {
812  return saved_game_.mp_settings();
813 }
814 
816 {
817  return saved_game_.classification();
818 }
819 
821 {
822  return *gui_;
823 }
824 
826 {
827  return !menu_handler_.get_textbox().active();
828 }
829 
831 {
832  if(event.key.keysym.sym == SDLK_ESCAPE) {
834  } else if(event.key.keysym.sym == SDLK_TAB) {
835  tab();
836  } else if(event.key.keysym.sym == SDLK_UP) {
838  } else if(event.key.keysym.sym == SDLK_DOWN) {
840  } else if(event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_KP_ENTER) {
841  enter_textbox();
842  }
843 }
844 
845 void play_controller::process_keydown_event(const SDL_Event& event)
846 {
847  if(event.key.keysym.sym == SDLK_TAB) {
848  whiteboard_manager_->set_invert_behavior(true);
849  }
850 }
851 
852 void play_controller::process_keyup_event(const SDL_Event& event)
853 {
854  // If the user has pressed 1 through 9, we want to show
855  // how far the unit can move in that many turns
856  if(event.key.keysym.sym >= '1' && event.key.keysym.sym <= '9') {
857  const int new_path_turns = (event.type == SDL_KEYDOWN) ? event.key.keysym.sym - '1' : 0;
858 
859  if(new_path_turns != mouse_handler_.get_path_turns()) {
860  mouse_handler_.set_path_turns(new_path_turns);
861 
863 
864  if(u.valid()) {
865  // if it's not the unit's turn, we reset its moves
866  unit_movement_resetter move_reset(*u, u->side() != current_side());
867 
869  *u, false, true, get_teams()[gui_->viewing_team()], mouse_handler_.get_path_turns()));
870 
871  gui_->highlight_reach(mouse_handler_.current_paths());
872  } else {
874  }
875  }
876  } else if(event.key.keysym.sym == SDLK_TAB) {
877  CKey keys;
878  if(!keys[SDLK_TAB]) {
879  whiteboard_manager_->set_invert_behavior(false);
880  }
881  }
882 }
883 
885 {
886  assert(replay_);
887  return *replay_.get();
888 }
889 
891 {
892  // Saving while an event is running isn't supported
893  // because it may lead to expired event handlers being saved.
894  assert(!gamestate().events_manager_->is_event_running());
895 
896  scoped_savegame_snapshot snapshot(*this);
899 }
900 
901 void play_controller::save_game_auto(const std::string& filename)
902 {
903  scoped_savegame_snapshot snapshot(*this);
905  save.save_game_automatic(false, filename);
906 }
907 
909 {
912 }
913 
914 void play_controller::save_replay_auto(const std::string& filename)
915 {
917  save.save_game_automatic(false, filename);
918 }
919 
921 {
923 }
924 
926 {
928  load.load_game_ingame();
929 }
930 
932 {
934  undo_stack().undo();
935 }
936 
938 {
940  undo_stack().redo();
941 }
942 
944 {
946 }
947 
949 {
951 }
952 
953 const std::string& play_controller::select_music(bool victory) const
954 {
955  const std::vector<std::string>& music_list = victory
956  ? (gamestate_->get_game_data()->get_victory_music().empty()
958  : gamestate_->get_game_data()->get_victory_music())
959  : (gamestate_->get_game_data()->get_defeat_music().empty()
961  : gamestate_->get_game_data()->get_defeat_music());
962 
963  if(music_list.empty()) {
964  // Since this function returns a reference, we can't return a temporary empty string.
965  static const std::string empty_str = "";
966  return empty_str;
967  }
968 
969  return music_list[randomness::rng::default_instance().get_random_int(0, music_list.size() - 1)];
970 }
971 
973 {
974  if(is_linger_mode()) {
975  return;
976  }
977 
978  if(is_regular_game_end()) {
979  return;
980  }
981 
982  bool continue_level, found_player, found_network_player, invalidate_all;
983  std::set<unsigned> not_defeated;
984 
986  continue_level,
987  found_player,
988  found_network_player,
990  not_defeated,
992  );
993 
994  if(invalidate_all) {
995  gui_->invalidate_all();
996  }
997 
998  if(continue_level) {
999  return;
1000  }
1001 
1002  if(found_player || found_network_player) {
1003  pump().fire("enemies_defeated");
1004  if(is_regular_game_end()) {
1005  return;
1006  }
1007  }
1008 
1009  DBG_EE << "victory_when_enemies_defeated: " << victory_when_enemies_defeated_;
1010  DBG_EE << "found_player: " << found_player;
1011  DBG_EE << "found_network_player: " << found_network_player;
1012 
1013  if(!victory_when_enemies_defeated_ && (found_player || found_network_player)) {
1014  // This level has asked not to be ended by this condition.
1015  return;
1016  }
1017 
1018  if(video::headless()) {
1019  LOG_AIT << "winner: ";
1020  for(unsigned l : not_defeated) {
1022  if(ai.empty())
1023  ai = "default ai";
1024  LOG_AIT << l << " (using " << ai << ") ";
1025  }
1026 
1027  LOG_AIT;
1028  ai_testing::log_victory(not_defeated);
1029  }
1030 
1031  DBG_EE << "throwing end level exception...";
1032  // Also proceed to the next scenario when another player survived.
1033  end_level_data el_data;
1034  el_data.proceed_to_next_level = found_player || found_network_player;
1035  el_data.is_victory = found_player;
1036  set_end_level_data(el_data);
1037 }
1038 
1039 void play_controller::process_oos(const std::string& msg) const
1040 {
1041  if(video::headless()) {
1042  throw game::game_error(msg);
1043  }
1044 
1046  return;
1047  }
1048 
1049  std::stringstream message;
1050  message << _("The game is out of sync. It might not make much sense to continue. Do you want to save your game?");
1051  message << "\n\n" << _("Error details:") << "\n\n" << msg;
1052 
1053  scoped_savegame_snapshot snapshot(*this);
1055  save.save_game_interactive(message.str(), savegame::savegame::YES_NO); // can throw quit_game_exception
1056 }
1057 
1058 void play_controller::update_gui_to_player(const int team_index, const bool observe)
1059 {
1060  gui_->set_team(team_index, observe);
1061  gui_->recalculate_minimap();
1062  gui_->invalidate_all();
1063 }
1064 
1066 {
1067  scoped_savegame_snapshot snapshot(*this);
1070 }
1071 
1072 void play_controller::do_consolesave(const std::string& filename)
1073 {
1074  scoped_savegame_snapshot snapshot(*this);
1076  save.save_game_automatic(true, filename);
1077 }
1078 
1080 {
1081  // note: this writes to level_ if this is not a replay.
1083 }
1084 
1086 {
1087  return gamestate().events_manager_->pump();
1088 }
1089 
1091 {
1092  return ticks_;
1093 }
1094 
1096 {
1097  return soundsources_manager_.get();
1098 }
1099 
1101 {
1102  return plugins_context_.get();
1103 }
1104 
1106 {
1107  return hotkey_handler_.get();
1108 }
1109 
1111 {
1112  if(!gamestate().in_phase(game_data::TURN_PLAYING)) {
1113  return true;
1114  }
1115 
1116  const team& t = current_team();
1117  return !t.is_local_human() || !t.is_proxy_human();
1118 }
1119 
1121 {
1123  return;
1124  }
1125 
1126  try {
1127  play_slice();
1128  } catch(const return_to_play_side_exception&) {
1129  assert(should_return_to_play_side());
1130  }
1131 }
1132 
1134 {
1135  if(gamestate().in_phase(game_data::PRELOAD)) {
1139 
1140  set_scontext_synced sync;
1141 
1142  fire_prestart();
1143  if(is_regular_game_end()) {
1144  return;
1145  }
1146 
1147  for(const team& t : get_teams()) {
1148  actions::clear_shroud(t.side(), false, false);
1149  }
1150 
1151  init_gui();
1152  LOG_NG << "first_time..." << (is_skipping_replay() ? "skipping" : "no skip");
1153 
1154  fire_start();
1155  if(is_regular_game_end()) {
1156  return;
1157  }
1158 
1159  sync.do_final_checkup();
1160  gui_->recalculate_minimap();
1161 
1162  // Initialize countdown clock.
1163  for(const team& t : get_teams()) {
1165  t.set_countdown_time(1000 * saved_game_.mp_settings().mp_countdown_init_time);
1166  }
1167  }
1168  did_autosave_this_turn_ = false;
1169  } else {
1170  init_gui();
1171  gui_->recalculate_minimap();
1172  }
1173 
1175 }
1176 
1177 /**
1178  * Find all [endlevel]next_scenario= attributes, and add them to @a result.
1179  */
1180 static void find_next_scenarios(const config& parent, std::set<std::string>& result) {
1181  for(const auto& endlevel : parent.child_range("endlevel")) {
1182  if(endlevel.has_attribute("next_scenario")) {
1183  result.insert(endlevel["next_scenario"]);
1184  }
1185  }
1186  for(const auto cfg : parent.all_children_range()) {
1187  find_next_scenarios(cfg.cfg, result);
1188  }
1189 };
1190 
1192  // Which scenarios are reachable from the current one?
1193  std::set<std::string> possible_next_scenarios;
1194  possible_next_scenarios.insert(gamestate().gamedata_.next_scenario());
1195 
1196  // Find all "endlevel" tags that could be triggered in events
1197  config events;
1198  gamestate().events_manager_->write_events(events);
1199  find_next_scenarios(events, possible_next_scenarios);
1200 
1201  // Are we looking for [scenario]id=, [multiplayer]id= or [test]id=?
1202  const auto tagname = saved_game_.classification().get_tagname();
1203 
1204  // Of the possible routes, work out which exist.
1205  bool possible_this_is_the_last_scenario = false;
1206  std::vector<std::string> known;
1207  std::vector<std::string> unknown;
1208  for(const auto& x : possible_next_scenarios) {
1209  if(x.empty() || x == "null") {
1210  possible_this_is_the_last_scenario = true;
1211  LOG_NG << "This can be the last scenario";
1212  } else if(utils::contains(x, '$')) {
1213  // Assume a WML variable will be set to a correct value before the end of the scenario
1214  known.push_back(x);
1215  LOG_NG << "Variable value for next scenario '" << x << "'";
1216  } else if(game_config_.find_child(tagname, "id", x)) {
1217  known.push_back(x);
1218  LOG_NG << "Known next scenario '" << x << "'";
1219  } else {
1220  unknown.push_back(x);
1221  ERR_NG << "Unknown next scenario '" << x << "'";
1222  }
1223  }
1224 
1225  if(unknown.empty()) {
1226  // everything's good
1227  return;
1228  }
1229 
1230  std::string title = _("Warning: broken campaign branches");
1231  std::stringstream message;
1232 
1233  message << _n(
1234  // TRANSLATORS: This is an error that will hopefully only be seen by UMC authors and by players who have already
1235  // said "okay" to a "loading saves from an old version might not work" dialog.
1236  "The next scenario is missing, you will not be able to finish this campaign.",
1237  // TRANSLATORS: This is an error that will hopefully only be seen by UMC authors and by players who have already
1238  // said "okay" to a "loading saves from an old version might not work" dialog.
1239  "Some of the possible next scenarios are missing, you might not be able to finish this campaign.",
1240  unknown.size() + known.size() + (possible_this_is_the_last_scenario ? 1 : 0));
1241  message << "\n\n";
1242  message << _n(
1243  "Please report the following missing scenario to the campaign’s author:\n$unknown_list|",
1244  "Please report the following missing scenarios to the campaign’s author:\n$unknown_list|",
1245  unknown.size());
1246  message << "\n";
1247  message << _("Once this is fixed, you will need to restart this scenario.");
1248 
1249  std::stringstream unknown_list;
1250  for(const auto& x : unknown) {
1251  unknown_list << font::unicode_bullet << " " << x << "\n";
1252  }
1253  utils::string_map symbols;
1254  symbols["unknown_list"] = unknown_list.str();
1255  auto message_str = utils::interpolate_variables_into_string(message.str(), &symbols);
1256  ERR_NG << message_str;
1258 }
1259 
1261 {
1262  const team& viewing_team = get_teams()[gui_->viewing_team()];
1263  return gui_->viewing_team() == gui_->playing_team() && !events::commands_disabled && viewing_team.is_local_human()
1264  && !is_browsing();
1265 }
1266 
1267 std::set<std::string> play_controller::all_players() const
1268 {
1269  std::set<std::string> res = gui_->observers();
1270  for(const team& t : get_teams()) {
1271  if(t.is_human()) {
1272  res.insert(t.current_player());
1273  }
1274  }
1275 
1276  return res;
1277 }
1278 
1280 {
1281  do {
1282  if(std::find_if(get_teams().begin(), get_teams().end(), [](const team& t) { return !t.is_empty(); }) == get_teams().end()){
1283  throw game::game_error("The scenario has no (non-empty) sides defined");
1284  }
1286 
1288  if(is_regular_game_end()) {
1289  return;
1290  }
1291  // This flag can be set by derived classes (in overridden functions).
1292  player_type_changed_ = false;
1293 
1294  //TODO: this resets the "current turn" statistics whenever the controller changes,
1295  // in particular whenever a game is reloaded, wouldn't it be better if this was
1296  // only done, when a sides turn actually ends?
1297  statistics().reset_turn_stats(gamestate().board_.get_team(current_side()).save_id_or_number());
1298 
1299  play_side_impl();
1300 
1301  if(is_regular_game_end()) {
1302  return;
1303  }
1304  } while(player_type_changed_);
1305 
1306  // Keep looping if the type of a team (human/ai/networked) has changed mid-turn
1307  sync_end_turn();
1308 }
1309 
1311 {
1312  const bool time_left = gamestate().tod_manager_.next_turn(&gamestate().gamedata_);
1313 
1314  if(!time_left) {
1315  LOG_NG << "firing time over event...";
1317  pump().fire("time_over");
1318  LOG_NG << "done firing time over event...";
1319 
1320  // If turns are added while handling 'time over' event.
1321  if(gamestate().tod_manager_.is_time_left()) {
1322  return;
1323  }
1324 
1325  if(video::headless()) {
1326  LOG_AIT << "time over (draw)";
1328  }
1329 
1330  check_victory();
1331  if(is_regular_game_end()) {
1332  return;
1333  }
1334 
1335  end_level_data e;
1336  e.proceed_to_next_level = false;
1337  e.is_victory = false;
1339  }
1340 }
1341 
1343  : controller_(controller)
1344 {
1346 }
1347 
1349 {
1350  controller_.saved_game_.remove_snapshot();
1351 }
1352 
1354 {
1355  const team& t = get_teams()[gui_->viewing_team()];
1356  static const std::string no_objectives(_("No objectives available"));
1357  std::string objectives = utils::interpolate_variables_into_string(t.objectives(), *gamestate_->get_game_data());
1358  gui2::show_transient_message(get_scenario_name(), (objectives.empty() ? no_objectives : objectives), "", true);
1359  t.reset_objectives_changed();
1360 }
1361 
1363 {
1365  const std::shared_ptr<gui::button> skip_animation_button = get_display().find_action_button("skip-animation");
1366  if(skip_animation_button) {
1367  skip_animation_button->set_check(skip_replay_);
1368  }
1369 }
1370 
1372 {
1374 }
1375 
1377 {
1379 }
1380 
1382 {
1385  }
1386 }
Various functions that implement advancements of units.
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands.
double t
Definition: astarsearch.cpp:65
map_location prev
Definition: astarsearch.cpp:66
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:255
bool can_undo() const
True if there are actions that can be undone.
Definition: undo.hpp:99
void redo()
Redoes the top action on the redo stack.
Definition: undo.cpp:397
void clear()
Clears the stack of undoable (and redoable) actions.
Definition: undo.cpp:212
bool can_redo() const
True if there are actions that can be redone.
Definition: undo.hpp:101
void undo()
Undoes the top action on the undo stack.
Definition: undo.cpp:341
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:687
static manager & get_singleton()
Definition: manager.hpp:145
static void log_victory(std::set< unsigned int > teams)
Definition: testing.cpp:82
static void log_draw()
Definition: testing.cpp:76
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:161
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
void append_children(const config &cfg)
Adds children from cfg.
Definition: config.cpp:169
config & add_child(config_key_type key)
Definition: config.cpp:445
virtual void play_slice(bool is_delay_enabled=true)
const game_config_view & game_config_
int side_upkeep(int side_num) const
@ ONSCREEN
Definition: display.hpp:505
std::shared_ptr< gui::button > find_action_button(const std::string &id)
Retrieves a pointer to a theme UI button.
Definition: display.cpp:810
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:55
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
int get_path_turns() const
void select_hex(const map_location &hex, const bool browse, const bool highlight=true, const bool fire_event=true)
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:113
team & get_team(int i)
Definition: game_board.hpp:98
void end_turn(int pnum)
Definition: game_board.cpp:78
void new_turn(int pnum)
Definition: game_board.cpp:69
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:107
@ PRELOAD
the preload [event] is fired next phase: PRESTART (normal game), TURN_STARTING_WAITING (reloaded game...
Definition: game_data.hpp:77
@ 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:103
@ PRESTART
the prestart [event] is fired next phase: START (default), GAME_ENDING
Definition: game_data.hpp:80
@ TURN_PLAYING
The User is controlling the game and invoking actions The game can be saved here.
Definition: game_data.hpp:94
@ TURN_STARTING_WAITING
we are waiting for the turn to start.
Definition: game_data.hpp:87
@ START
the start [event] is fired next phase: TURN_STARTING_WAITING (default), GAME_ENDING
Definition: game_data.hpp:83
@ TURN_STARTING
the turn, side turn etc.
Definition: game_data.hpp:90
map_location last_selected
the last location where a select event fired.
Definition: game_data.hpp:127
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:402
void write(config &cfg) const
Definition: game_state.cpp:218
bool in_phase(game_data::PHASE phase) const
Definition: game_state.hpp:110
void set_game_display(game_display *)
Definition: game_state.cpp:213
std::unique_ptr< game_lua_kernel > lua_kernel_
Definition: game_state.hpp:51
std::unique_ptr< pathfind::manager > pathfind_manager_
Definition: game_state.hpp:49
game_board board_
Definition: game_state.hpp:47
const std::unique_ptr< game_events::manager > events_manager_
Definition: game_state.hpp:53
tod_manager tod_manager_
Definition: game_state.hpp:48
game_data gamedata_
Definition: game_state.hpp:46
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(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_
int find_viewing_side() const
returns 0 if no such team was found.
void fire_preload()
preload events cannot be synced
void check_victory()
Checks to see if a side has won.
bool is_linger_mode() const
bool is_team_visible(int team_num, bool observer) 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...
const unit_map & get_units() const
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
bool is_observer() const
void set_do_healing(bool do_healing)
bool is_during_turn() const
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
play_controller(const config &level, saved_game &state_of_game, bool skip_replay, bool start_faded=false)
virtual void check_objectives()=0
bool remove_from_carryover_on_defeat_
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
virtual void sync_end_turn()
bool victory_when_enemies_defeated_
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
virtual void play_side_impl()
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
bool player_type_changed_
true when the controller of the currently playing side has changed.
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_
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
Implements a quit confirmation dialog.
static rng & default_instance()
Definition: random.cpp:74
int get_random_int(int min, int max)
Definition: random.hpp:52
bool add_start_if_not_there_yet()
Definition: replay.cpp:663
void init_side()
Definition: replay.cpp:219
config * get_next_action()
Definition: replay.cpp:620
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:587
Class for autosaves.
Definition: savegame.hpp:280
void autosave(const bool disable_autosave, const int autosave_max, const int infinite_autosaves)
Definition: savegame.cpp:605
Class for "normal" midgame saves.
Definition: savegame.hpp:253
The class for loading a savefile.
Definition: savegame.hpp:100
Class for replay saves (either manually or automatically).
Definition: savegame.hpp:267
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:210
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:366
bool save_game_interactive(const std::string &message, DIALOG_TYPE dialog_type)
Save a game interactively through the savegame dialog.
Definition: savegame.cpp:383
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
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:76
int support() const
Calculate total support capacity, based on support_per_village.
Definition: team.hpp:191
bool is_local_human() const
Definition: team.hpp:255
void spend_gold(const int amount)
Definition: team.hpp:196
void new_turn()
Definition: team.hpp:192
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:1432
void apply_scenario_fix(const config &cfg)
Definition: types.cpp:1398
This class represents a single unit of a specific type.
Definition: unit.hpp:135
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
Various functions related to the creation of units (recruits, recalls, and placed units).
std::size_t i
Definition: function.cpp:968
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:241
constexpr T modulo(T num, int mod, T min=0)
Definition: math.hpp:62
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:751
void recalculate_fog(int side)
Function that recalculates the fog of war.
Definition: vision.cpp:702
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:61
void invalidate_all()
Mark the entire screen as requiring redraw.
Handling of system events.
Definition: manager.hpp:43
Collection of helper functions relating to Pango formatting.
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:84
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:36
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:204
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:151
@ TEXTBOX_MESSAGE
@ TEXTBOX_SEARCH
@ TEXTBOX_COMMAND
Keyboard shortcuts for game actions.
constexpr uint32_t scope_game
int autosavemax()
Definition: game.cpp:791
compression::format save_compression_format()
Definition: game.cpp:843
void encounter_all_content(const game_board &gameboard_)
Definition: game.cpp:1000
std::map< std::string, std::string > get_acquaintances_nice(const std::string &filter)
Definition: game.cpp:213
std::string login()
const int INFINITE_AUTO_SAVES
Definition: game.hpp:208
::tod_manager * tod_manager
Definition: resources.cpp:30
game_board * gameboard
Definition: resources.cpp:21
persist_manager * persist
Definition: resources.cpp:27
game_data * gamedata
Definition: resources.cpp:23
game_events::manager * game_events
Definition: resources.cpp:25
replay * recorder
Definition: resources.cpp:29
actions::undo_list * undo_stack
Definition: resources.cpp:33
soundsource::manager * soundsources
Definition: resources.cpp:28
game_lua_kernel * lua_kernel
Definition: resources.cpp:26
game_classification * classification
Definition: resources.cpp:35
pathfind::manager * tunnels
Definition: resources.cpp:32
play_controller * controller
Definition: resources.cpp:22
filter_context * filter_con
Definition: resources.cpp:24
std::shared_ptr< wb::manager > whiteboard
Definition: resources.cpp:34
void write_music_play_list(config &snapshot)
Definition: sound.cpp:866
void play_sound(const std::string &files, channel_group group, unsigned int repeats)
Definition: sound.cpp:1035
@ SOUND_SOURCES
Definition: sound.hpp:30
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
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:84
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
bool headless()
The game is running headless.
Definition: video.cpp:145
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:110
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.
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.
Error used for any general game error, e.g.
Definition: game_errors.hpp:47
Encapsulates the map of the game.
Definition: location.hpp:38
static const map_location & null_location()
Definition: location.hpp:81
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 valid() const
Definition: map.hpp:274
Object which temporarily resets a unit's movement.
Definition: unit.hpp:2030
Gather statistics important for AI testing and output them.
unit_type_data unit_types
Definition: types.cpp:1465
Various functions that implement the undoing (and redoing) of in-game commands.
Various functions implementing vision (through fog of war and shroud).
#define e