The Battle for Wesnoth  1.19.7+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"
54 #include "sound.hpp"
55 #include "soundsource.hpp"
56 #include "statistics.hpp"
57 #include "synced_context.hpp"
58 #include "units/types.hpp"
59 #include "units/unit.hpp"
60 #include "utils/general.hpp"
61 #include "video.hpp"
62 #include "whiteboard/manager.hpp"
63 
64 #include <functional>
65 
66 static lg::log_domain log_aitesting("ai/testing");
67 #define LOG_AIT LOG_STREAM(info, log_aitesting)
68 // If necessary, this define can be replaced with `#define LOG_AIT std::cout` to restore previous behavior
69 
70 static lg::log_domain log_engine("engine");
71 #define LOG_NG LOG_STREAM(info, log_engine)
72 #define DBG_NG LOG_STREAM(debug, log_engine)
73 #define ERR_NG LOG_STREAM(err, log_engine)
74 
75 static lg::log_domain log_display("display");
76 #define ERR_DP LOG_STREAM(err, log_display)
77 
78 static lg::log_domain log_enginerefac("enginerefac");
79 #define LOG_RG LOG_STREAM(info, log_enginerefac)
80 
81 static lg::log_domain log_engine_enemies("engine/enemies");
82 #define DBG_EE LOG_STREAM(debug, log_engine_enemies)
83 
84 /**
85  * Copies [scenario] attributes/tags that are not otherwise stored in C++ structs/clases.
86  */
87 static void copy_persistent(const config& src, config& dst)
88 {
89  static const std::set<std::string> attrs {
90  "description",
91  "name",
92  "disallow_recall",
93  "experience_modifier",
94  "require_scenario",
95  "loaded_resources"
96  };
97 
98  static const std::set<std::string> tags {
99  "terrain_graphics",
100  "modify_unit_type",
101  "lua"
102  };
103 
104  for(const std::string& attr : attrs) {
105  dst[attr] = src[attr];
106  }
107 
108  for(const std::string& tag : tags) {
109  dst.append_children(src, tag);
110  }
111 }
112 
113 static void clear_resources()
114 {
115  resources::controller = nullptr;
116  resources::filter_con = nullptr;
117  resources::gameboard = nullptr;
118  resources::gamedata = nullptr;
119  resources::lua_kernel = nullptr;
120  resources::game_events = nullptr;
121  resources::persist = nullptr;
122  resources::soundsources = nullptr;
123  resources::tod_manager = nullptr;
124  resources::tunnels = nullptr;
125  resources::undo_stack = nullptr;
126  resources::recorder = nullptr;
127  resources::whiteboard.reset();
128  resources::classification = nullptr;
129 }
130 
132  : controller_base()
133  , observer()
135  , timer_()
136  , gamestate_()
137  , level_()
138  , saved_game_(state_of_game)
139  , tooltips_manager_()
140  , whiteboard_manager_()
141  , plugins_context_()
142  , labels_manager_(new font::floating_label_context())
143  , help_manager_(&game_config_)
144  , mouse_handler_(nullptr, *this)
145  , menu_handler_(nullptr, *this)
146  , hotkey_handler_(new hotkey_handler(*this, saved_game_))
147  , soundsources_manager_()
148  , persist_()
149  , gui_()
150  , xp_mod_(new unit_experience_accelerator(level["experience_modifier"].to_int(100)))
151  , statistics_context_(new statistics_t(state_of_game.statistics()))
152  , replay_(new replay(state_of_game.get_replay()))
153  , skip_replay_(false)
154  , skip_story_(state_of_game.skip_story())
155  , did_autosave_this_turn_(true)
156  , did_tod_sound_this_turn_(false)
157  , map_start_()
158  , start_faded_(true)
159  , victory_music_()
160  , defeat_music_()
161  , scope_(hotkey::scope_game)
162  , ignore_replay_errors_(false)
163  , player_type_changed_(false)
164 {
166 
167  for(const config& modify_unit_type : level_.child_range("modify_unit_type")) {
168  unit_types.apply_scenario_fix(modify_unit_type);
169  }
170  resources::controller = this;
173 
175 
177 
179 
180  try {
181  init(level);
182  } catch(...) {
183  DBG_NG << "Caught exception initializing level: " << utils::get_unknown_exception_type();
184  clear_resources();
185  throw;
186  }
187 }
188 
190 {
192  clear_resources();
193 }
194 
196 {
197  /*
198  * Initilisation currently happens on the following order:
199  * 1) This code, which is executed with the loadingscreen
200  * From inside the constructor.
201  * 2) The Music is changed.
202  * 3) The Storyscreen is shown.
203  * 4) The Labels are added
204  * 5) The preload event is fired
205  * 6) The prestart event is fired
206  * 7) The gui is activated
207  * 8) The start event is fired
208  */
211 
212  LOG_NG << "initializing game_state..." << timer();
213  gamestate_.reset(new game_state(level, *this));
214 
222 
223  gamestate_->ai_manager_.add_observer(this);
224  gamestate_->init(level, *this);
226 
227  LOG_NG << "initializing whiteboard..." << timer();
229  whiteboard_manager_.reset(new wb::manager());
231 
232  LOG_NG << "loading units..." << timer();
235 
236  LOG_NG << "initializing theme... " << timer();
238 
239  LOG_NG << "building terrain rules... " << timer();
241 
242  gui_.reset(new game_display(gamestate().board_, whiteboard_manager_, *gamestate().reports_, theme(), level));
243  map_start_ = map_location(level.child_or_empty("display").child_or_empty("location"));
244  if(start_faded_) {
245  gui_->set_fade({0,0,0,255});
246  gui_->set_prevent_draw(true);
247  }
248 
249  // Ensure the loading screen doesn't end up underneath the game display
251 
252  if(!video::headless()) {
254  gui_->get_theme().modify_label("time-icon", _("time left for current turn"));
255  } else {
256  gui_->get_theme().modify_label("time-icon", _("current local time"));
257  }
258  }
259 
261  mouse_handler_.set_gui(gui_.get());
262  menu_handler_.set_gui(gui_.get());
263 
264  LOG_NG << "done initializing display... " << timer();
265 
266  LOG_NG << "building gamestate to gui and whiteboard... " << timer();
267  // This *needs* to be created before the show_intro and show_map_scene
268  // as that functions use the manager state_of_game
269  // Has to be done before registering any events!
272 
273  init_managers();
275  // loadscreen_manager->reset();
277  gamestate().lua_kernel_->load_game(level);
278 
279  plugins_context_.reset(new plugins_context("Game"));
280  plugins_context_->set_callback("save_game", [this](const config& cfg) { save_game_auto(cfg["filename"]); }, true);
281  plugins_context_->set_callback("save_replay", [this](const config& cfg) { save_replay_auto(cfg["filename"]); }, true);
282  plugins_context_->set_callback("quit", [](const config&) { throw_quit_game_exception(); }, false);
283  plugins_context_->set_accessor_string("scenario_name", [this](const config&) { return get_scenario_name(); });
284  });
285 }
286 
287 void play_controller::reset_gamestate(const config& level, int replay_pos)
288 {
289  // TODO: should we update we update this->level_ with level ?
290 
291  resources::gameboard = nullptr;
292  resources::gamedata = nullptr;
293  resources::tod_manager = nullptr;
294  resources::filter_con = nullptr;
295  resources::lua_kernel = nullptr;
296  resources::game_events = nullptr;
297  resources::tunnels = nullptr;
298  resources::undo_stack = nullptr;
299 
300  gui_->labels().set_team(nullptr);
301 
302  /* First destroy the old game state, then create the new one.
303  This is necessary to ensure that while the old AI manager is being destroyed,
304  all its member objects access the old manager instead of the new. */
305  gamestate_.reset();
306  gamestate_.reset(new game_state(level, *this));
307 
315 
316  gamestate_->ai_manager_.add_observer(this);
317  gamestate_->init(level, *this);
320 
321  gui_->reset_reports(*gamestate().reports_);
322  gui_->change_display_context(&gamestate().board_);
323  saved_game_.get_replay().set_pos(replay_pos);
324 
326  gamestate().lua_kernel_->load_game(level);
327 }
328 
330 {
331  LOG_NG << "initializing managers... " << timer();
333 
335  LOG_NG << "done initializing managers... " << timer();
336 }
337 
339 {
340  // Run initialization scripts, even if loading from a snapshot.
341  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
342  pump().fire("preload");
343  gamestate().lua_kernel_->preload_finished();
344 }
345 
347 {
348  // pre-start events must be executed before any GUI operation,
349  // as those may cause the display to be refreshed.
351 
352  // Fire these right before prestart events, to catch only the units sides
353  // have started with.
354  for(const unit& u : get_units()) {
355  pump().fire("unit_placed", map_location(u.get_location()));
356  }
357 
358  pump().fire("prestart");
359 
360  // prestart event may modify start turn with WML, reflect any changes.
361  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
362 }
363 
365 {
366  if(!get_teams().empty()) {
367  const config cfg("side", gui_->viewing_team().side());
368  gamestate().lua_kernel_->run_wml_action("show_objectives", vconfig(cfg),
369  game_events::queued_event("_from_interface", "", map_location(), map_location(), config()));
370  }
371 }
372 
374 {
376  pump().fire("start");
377 
378  skip_story_ = false; // Show [message]s from now on even with --campaign-skip-story
379 
380  // start event may modify start turn with WML, reflect any changes.
381  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
382 
385 
386  // prestart and start events may modify the initial gold amount,
387  // reflect any changes.
388  for(team& tm : get_teams()) {
389  tm.set_start_gold(tm.gold());
390  }
391 
393 }
394 
396 {
397  gui_->begin_game();
398  gui_->update_tod();
399 }
400 
402 {
404  gui_->set_playing_team_index(std::size_t(current_side() - 1));
405 
407 
409 }
410 
412 {
413  //
414  // We do side init only if not done yet for a local side when we are not replaying.
415  // For all other sides it is recorded in replay and replay handler has to handle
416  // calling do_init_side() functions.
417  //
418  if(is_during_turn()) {
419  // We already executed do_init_side this can for example happe if we reload a game,
420  // but also if we changed control of a side during it's turn
421  return;
422  }
423 
424  if(!current_team().is_local()) {
425  // We are in a mp game and execute do_init_side as soon as we receive [init_side] from the current player
426  // (see replay.cpp)
427  return;
428  }
429 
430  if(is_replay()) {
431  // We are in a replay and execute do_init_side as soon as we reach the next [init_side] in the replay data
432  // (see replay.cpp)
433  return;
434  }
435 
436  if(current_team().is_idle()) {
437  // In this case it can happen that we just gave control of this side to another player so doing init_side
438  // could lead to errors since we no longer own this side from the servers perspective.
439  // (see playturn.cpp)
440  return;
441  }
442 
444  do_init_side();
445 }
446 
448 {
449  { // Block for set_scontext_synced
450  set_scontext_synced sync;
451 
453 
454  log_scope("player turn");
455  // In case we might end up calling sync:network during the side turn events,
456  // and we don't want do_init_side to be called when a player drops.
458  gamestate_->next_player_number_ = gamestate_->player_number_ + 1;
459 
460  const std::string turn_num = std::to_string(turn());
461  const std::string side_num = std::to_string(current_side());
462 
463  gamestate().gamedata_.get_variable("side_number") = current_side();
464 
465  // We might have skipped some sides because they were empty so it is not enough to check for side_num==1
466  if(!gamestate().tod_manager_.has_turn_event_fired()) {
467  pump().fire("turn_" + turn_num);
468  pump().fire("new_turn");
470  }
471 
472  pump().fire("side_turn");
473  pump().fire("side_" + side_num + "_turn");
474  pump().fire("side_turn_" + turn_num);
475  pump().fire("side_" + side_num + "_turn_" + turn_num);
476 
477  // We want to work out if units for this player should get healed,
478  // and the player should get income now.
479  // Healing/income happen if it's not the first turn of processing,
480  // or if we are loading a game.
481  if(turn() > 1) {
484 
485  // If the expense is less than the number of villages owned
486  // times the village support capacity,
487  // then we don't have to pay anything at all
488  int expense = gamestate().board_.side_upkeep(current_side()) - current_team().support();
489  if(expense > 0) {
490  current_team().spend_gold(expense);
491  }
492  }
493 
494  if(do_healing()) {
496  }
497 
498  // Do healing on every side turn except the very first side turn.
499  // (1.14 and earlier did healing whenever turn >= 2.)
500  set_do_healing(true);
501 
502  // Set resting now after the healing has been done.
503  for(unit& patient : resources::gameboard->units()) {
504  if(patient.side() == current_side()) {
505  patient.set_resting(true);
506  }
507  }
508 
509  // Prepare the undo stack.
511 
512  pump().fire("turn_refresh");
513  pump().fire("side_" + side_num + "_turn_refresh");
514  pump().fire("turn_" + turn_num + "_refresh");
515  pump().fire("side_" + side_num + "_turn_" + turn_num + "_refresh");
516 
517  // Make sure vision is accurate.
519 
520  check_victory();
521  sync.do_final_checkup();
523  }
524 
525  statistics().reset_turn_stats(gamestate().board_.get_team(current_side()).save_id_or_number());
526 
527  init_side_end();
528 
529  if(!is_skipping_replay() && current_team().get_scroll_to_leader()) {
530  gui_->scroll_to_leader(current_side(), game_display::ONSCREEN, false);
531  }
532 }
533 
535 {
540  }
541  whiteboard_manager_->on_init_side();
542 }
543 
545 {
546  config cfg = level_;
547 
548  cfg["replay_pos"] = saved_game_.get_replay().get_pos();
549  gamestate().write(cfg);
550 
551  gui_->write(cfg.add_child("display"));
552 
553  // Write the soundsources.
554  soundsources_manager_->write_sourcespecs(cfg);
555 
556  gui_->labels().write(cfg);
558 
559  if(cfg["replay_pos"].to_int(0) > 0 && cfg["playing_team"].empty()) {
560  gui2::show_error_message(_("Trying to create a corrupt file, please report this bug"));
561  }
562 
563  return cfg;
564 }
565 
567 {
568 
569  { // Block for set_scontext_synced
570  set_scontext_synced sync(1);
571  // Also clears the undo stack.
573 
575  const std::string turn_num = std::to_string(turn());
576  const std::string side_num = std::to_string(current_side());
577 
578  // Clear shroud, in case units had been slowed for the turn.
580 
581  pump().fire("side_turn_end");
582  pump().fire("side_" + side_num + "_turn_end");
583  pump().fire("side_turn_" + turn_num + "_end");
584  pump().fire("side_" + side_num + "_turn_" + turn_num + "_end");
585  // This is where we refog, after all of a side's events are done.
587  check_victory();
588  sync.do_final_checkup();
589  }
591 }
592 
594 {
595  set_scontext_synced sync(2);
596  const std::string turn_num = std::to_string(turn());
597  pump().fire("turn_end");
598  pump().fire("turn_" + turn_num + "_end");
599  sync.do_final_checkup();
600 }
601 
603 {
604  // If we aren't using fog/shroud, this is easy :)
605  if(current_team().uses_fog() == false && current_team().uses_shroud() == false) {
606  return true;
607  }
608 
609  // See if any enemies are visible
610  for(const unit& u : get_units()) {
611  if(current_team().is_enemy(u.side()) && !gui_->fogged(u.get_location())) {
612  return true;
613  }
614  }
615 
616  return false;
617 }
618 
620 {
621  if(menu_handler_.get_textbox().active() == false) {
622  return;
623  }
624 
625  const std::string str = menu_handler_.get_textbox().box()->text();
626  const unsigned int team_num = current_side();
627  events::mouse_handler& mousehandler = mouse_handler_;
628 
629  switch(menu_handler_.get_textbox().mode()) {
630  case gui::TEXTBOX_SEARCH:
634  break;
637  menu_handler_.get_textbox().close(); // need to close that one after executing do_speak() !
638  break;
643  break;
644  case gui::TEXTBOX_AI:
647  menu_handler_.do_ai_formula(str, team_num, mousehandler);
648  break;
649  default:
651  ERR_DP << "unknown textbox mode";
652  }
653 }
654 
656 {
657  if(menu_handler_.get_textbox().active() == false) {
658  return;
659  }
660 
663  // Not handling messages to avoid spam
664  return;
665  }
666 
667  const std::string str = menu_handler_.get_textbox().box()->text();
668  const std::vector<std::string>& command_history = menu_handler_.get_textbox().command_history();
669 
670  auto prev = std::find(command_history.begin(), command_history.end(), str);
671 
672  if (prev != command_history.end())
673  {
674  if(up) {
675  if(prev != command_history.begin()) {
676  menu_handler_.get_textbox().box()->set_text(*--prev);
677  }
678  } else {
679  if(++prev != command_history.end()) {
680  menu_handler_.get_textbox().box()->set_text(*prev);
681  } else {
682  menu_handler_.get_textbox().box()->set_text("");
683  }
684  }
685  } else if (up) {
686  if(command_history.size() > 0) {
687  menu_handler_.get_textbox().box()->set_text(*--prev);
688  }
689  if(!str.empty()) {
691  }
692  }
693 }
694 
696 {
698 
699  std::set<std::string> dictionary;
700  switch(mode) {
701  case gui::TEXTBOX_SEARCH: {
702  for(const unit& u : get_units()) {
703  const map_location& loc = u.get_location();
704  if(!gui_->fogged(loc) && !(gui_->viewing_team().is_enemy(u.side()) && u.invisible(loc)))
705  dictionary.insert(u.name());
706  }
707  // TODO List map labels
708  break;
709  }
710  case gui::TEXTBOX_COMMAND: {
711  std::vector<std::string> commands = menu_handler_.get_commands_list();
712  dictionary.insert(commands.begin(), commands.end());
713  [[fallthrough]]; // we also want player names from the next case
714  }
715  case gui::TEXTBOX_MESSAGE: {
716  for(const team& t : get_teams()) {
717  if(!t.is_empty())
718  dictionary.insert(t.current_player());
719  }
720 
721  // Add observers
722  for(const std::string& o : gui_->observers()) {
723  dictionary.insert(o);
724  }
725 
726  // Add nicks who whispered you
727  for(const std::string& w : gui_->get_chat_manager().whisperers()) {
728  dictionary.insert(w);
729  }
730 
731  // Add nicks from friendlist
732  const std::map<std::string, std::string> friends = prefs::get().get_acquaintances_nice("friend");
733 
734  for(std::map<std::string, std::string>::const_iterator iter = friends.begin(); iter != friends.end(); ++iter) {
735  dictionary.insert((*iter).first);
736  }
737 
738  // Exclude own nick from tab-completion.
739  // NOTE why ?
740  dictionary.erase(prefs::get().login());
741  break;
742  }
743 
744  default:
745  ERR_DP << "unknown textbox mode";
746  } // switch(mode)
747 
748  menu_handler_.get_textbox().tab(dictionary);
749 }
750 
752 {
753  if(get_teams().size() == 0) {
754  throw game::game_error("The scenario has no sides defined");
755  }
756 
757  assert(gamestate().board_.has_team(current_side()));
759 }
760 
762 {
763  if(get_teams().size() == 0) {
764  throw game::game_error("The scenario has no sides defined");
765  }
766 
767  assert(gamestate().board_.has_team(current_side()));
769 }
770 
771 
773 {
774  return mouse_handler_;
775 }
776 
777 std::shared_ptr<wb::manager> play_controller::get_whiteboard() const
778 {
779  return whiteboard_manager_;
780 }
781 
783 {
784  return saved_game_.mp_settings();
785 }
786 
788 {
789  return saved_game_.classification();
790 }
791 
793 {
794  return *gui_;
795 }
796 
798 {
799  return !menu_handler_.get_textbox().active();
800 }
801 
803 {
804  if(event.key.keysym.sym == SDLK_ESCAPE) {
806  } else if(event.key.keysym.sym == SDLK_TAB) {
807  tab();
808  } else if(event.key.keysym.sym == SDLK_UP) {
810  } else if(event.key.keysym.sym == SDLK_DOWN) {
812  } else if(event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_KP_ENTER) {
813  enter_textbox();
814  }
815 }
816 
817 void play_controller::process_keydown_event(const SDL_Event& event)
818 {
819  if(event.key.keysym.sym == SDLK_TAB) {
820  whiteboard_manager_->set_invert_behavior(true);
821  }
822 }
823 
824 void play_controller::process_keyup_event(const SDL_Event& event)
825 {
826  // If the user has pressed 1 through 9, we want to show
827  // how far the unit can move in that many turns
828  if(event.key.keysym.sym >= '1' && event.key.keysym.sym <= '9') {
829  const int new_path_turns = (event.type == SDL_KEYDOWN) ? event.key.keysym.sym - '1' : 0;
830 
831  if(new_path_turns != mouse_handler_.get_path_turns()) {
832  mouse_handler_.set_path_turns(new_path_turns);
833 
835 
836  if(u.valid()) {
837  // if it's not the unit's turn, we reset its moves
838  unit_movement_resetter move_reset(*u, u->side() != current_side());
839 
841  *u, false, true, gui_->viewing_team(), mouse_handler_.get_path_turns()));
842 
843  gui_->highlight_reach(mouse_handler_.current_paths());
844  } else {
846  }
847  }
848  } else if(event.key.keysym.sym == SDLK_TAB) {
849  CKey keys;
850  if(!keys[SDLK_TAB]) {
851  whiteboard_manager_->set_invert_behavior(false);
852  }
853  }
854 }
855 
857 {
858  assert(replay_);
859  return *replay_.get();
860 }
861 
863 {
864  // Saving while an event is running isn't supported
865  // because it may lead to expired event handlers being saved.
866  assert(!gamestate().events_manager_->is_event_running());
867 
868  scoped_savegame_snapshot snapshot(*this);
869  savegame::ingame_savegame save(saved_game_, prefs::get().save_compression_format());
871 }
872 
874 {
875  scoped_savegame_snapshot snapshot(*this);
876  savegame::ingame_savegame save(saved_game_, prefs::get().save_compression_format());
877  save.save_game_automatic(false, filename);
878 }
879 
881 {
882  savegame::replay_savegame save(saved_game_, prefs::get().save_compression_format());
884 }
885 
887 {
888  savegame::replay_savegame save(saved_game_, prefs::get().save_compression_format());
889  save.save_game_automatic(false, filename);
890 }
891 
893 {
895 }
896 
898 {
900  load.load_game_ingame();
901 }
902 
904 {
906  undo_stack().undo();
907 }
908 
910 {
912  undo_stack().redo();
913 }
914 
916 {
918 }
919 
921 {
923 }
924 
925 const std::string& play_controller::select_music(bool victory) const
926 {
927  const std::vector<std::string>& music_list = victory
928  ? (gamestate_->get_game_data()->get_victory_music().empty()
930  : gamestate_->get_game_data()->get_victory_music())
931  : (gamestate_->get_game_data()->get_defeat_music().empty()
933  : gamestate_->get_game_data()->get_defeat_music());
934 
935  if(music_list.empty()) {
936  // Since this function returns a reference, we can't return a temporary empty string.
937  static const std::string empty_str = "";
938  return empty_str;
939  }
940 
941  return music_list[randomness::rng::default_instance().get_random_int(0, music_list.size() - 1)];
942 }
943 
945 {
946  if(is_linger_mode()) {
947  return;
948  }
949 
950  if(is_regular_game_end()) {
951  return;
952  }
953 
954  bool continue_level, found_player, found_network_player, invalidate_all;
955  std::set<unsigned> not_defeated;
956 
958  continue_level,
959  found_player,
960  found_network_player,
962  not_defeated,
963  gamestate().remove_from_carryover_on_defeat_
964  );
965 
966  if(invalidate_all) {
967  gui_->invalidate_all();
968  }
969 
970  if(continue_level) {
971  return;
972  }
973 
974  if(found_player || found_network_player) {
975  pump().fire("enemies_defeated");
976  if(is_regular_game_end()) {
977  return;
978  }
979  }
980 
981  DBG_EE << "victory_when_enemies_defeated: " << gamestate().victory_when_enemies_defeated_;
982  DBG_EE << "found_player: " << found_player;
983  DBG_EE << "found_network_player: " << found_network_player;
984 
985  if(!gamestate().victory_when_enemies_defeated_ && (found_player || found_network_player)) {
986  // This level has asked not to be ended by this condition.
987  return;
988  }
989 
990  if(video::headless()) {
991  LOG_AIT << "winner: ";
992  for(unsigned l : not_defeated) {
994  if(ai.empty())
995  ai = "default ai";
996  LOG_AIT << l << " (using " << ai << ") ";
997  }
998 
999  LOG_AIT;
1000  ai_testing::log_victory(not_defeated);
1001  }
1002 
1003  DBG_EE << "throwing end level exception...";
1004  // Also proceed to the next scenario when another player survived.
1005  end_level_data el_data;
1007  el_data.proceed_to_next_level = found_player || found_network_player;
1008  el_data.is_victory = found_player;
1009  set_end_level_data(el_data);
1010 }
1011 
1012 void play_controller::process_oos(const std::string& msg) const
1013 {
1014  if(video::headless()) {
1015  throw game::game_error(msg);
1016  }
1017 
1019  return;
1020  }
1021 
1022  std::stringstream message;
1023  message << _("The game is out of sync. It might not make much sense to continue. Do you want to save your game?");
1024  message << "\n\n" << _("Error details:") << "\n\n" << msg;
1025 
1026  scoped_savegame_snapshot snapshot(*this);
1028  save.save_game_interactive(message.str(), savegame::savegame::YES_NO); // can throw quit_game_exception
1029 }
1030 
1032 {
1033  return saved_game_.classification().get_tagname() == "multiplayer";
1034 }
1035 
1036 void play_controller::update_gui_to_player(const int team_index, const bool observe)
1037 {
1038  gui_->set_viewing_team_index(team_index, observe);
1039  gui_->recalculate_minimap();
1040  gui_->invalidate_all();
1041 }
1042 
1044 {
1045  scoped_savegame_snapshot snapshot(*this);
1046  savegame::autosave_savegame save(saved_game_, prefs::get().save_compression_format());
1047  save.autosave(false, prefs::get().auto_save_max(), pref_constants::INFINITE_AUTO_SAVES);
1048 }
1049 
1051 {
1052  scoped_savegame_snapshot snapshot(*this);
1053  savegame::ingame_savegame save(saved_game_, prefs::get().save_compression_format());
1054  save.save_game_automatic(true, filename);
1055 }
1056 
1058 {
1059  // note: this writes to level_ if this is not a replay.
1061 }
1062 
1064 {
1065  return gamestate().events_manager_->pump();
1066 }
1067 
1069 {
1070  return soundsources_manager_.get();
1071 }
1072 
1074 {
1075  return plugins_context_.get();
1076 }
1077 
1079 {
1080  return hotkey_handler_.get();
1081 }
1082 
1084 {
1085  if(!gamestate().in_phase(game_data::TURN_PLAYING)) {
1086  return true;
1087  }
1088 
1089  const team& t = current_team();
1090  return !t.is_local_human() || !t.is_proxy_human();
1091 }
1092 
1094 {
1096  return;
1097  }
1098 
1099  try {
1100  play_slice();
1101  } catch(const return_to_play_side_exception&) {
1102  assert(should_return_to_play_side());
1103  }
1104 }
1105 
1107 {
1108  if(gamestate().in_phase(game_data::PRELOAD)) {
1111 
1112  set_scontext_synced sync;
1113 
1114  // So that the code knows it can send choices immidiateley
1115  // todo: im not sure whetrh this is actually needed.
1117  fire_prestart();
1118  if(is_regular_game_end()) {
1119  return;
1120  }
1121 
1122  for(const team& t : get_teams()) {
1123  actions::clear_shroud(t.side(), false, false);
1124  }
1125 
1126  init_gui();
1127  LOG_NG << "first_time..." << (is_skipping_replay() ? "skipping" : "no skip");
1128 
1129  fire_start();
1130  if(is_regular_game_end()) {
1131  return;
1132  }
1133 
1134  sync.do_final_checkup();
1135  gui_->recalculate_minimap();
1136 
1137  // Initialize countdown clock.
1138  for(const team& t : get_teams()) {
1140  t.set_countdown_time(saved_game_.mp_settings().mp_countdown_init_time);
1141  }
1142  }
1143  did_autosave_this_turn_ = false;
1144  } else {
1145  init_gui();
1146  gui_->recalculate_minimap();
1147  }
1148 
1150 }
1151 
1152 /**
1153  * Find all [endlevel]next_scenario= attributes, and add them to @a result.
1154  */
1155 static void find_next_scenarios(const config& parent, std::set<std::string>& result) {
1156  for(const auto& endlevel : parent.child_range("endlevel")) {
1157  if(endlevel.has_attribute("next_scenario")) {
1158  result.insert(endlevel["next_scenario"]);
1159  }
1160  }
1161  for(const auto [key, cfg] : parent.all_children_view()) {
1162  find_next_scenarios(cfg, result);
1163  }
1164 };
1165 
1167  // Which scenarios are reachable from the current one?
1168  std::set<std::string> possible_next_scenarios;
1169  possible_next_scenarios.insert(gamestate().gamedata_.next_scenario());
1170 
1171  // Find all "endlevel" tags that could be triggered in events
1172  config events;
1173  gamestate().events_manager_->write_events(events);
1174  find_next_scenarios(events, possible_next_scenarios);
1175 
1176  // Are we looking for [scenario]id=, [multiplayer]id= or [test]id=?
1177  const auto tagname = saved_game_.classification().get_tagname();
1178 
1179  // Of the possible routes, work out which exist.
1180  bool possible_this_is_the_last_scenario = false;
1181  std::vector<std::string> known;
1182  std::vector<std::string> unknown;
1183  for(const auto& x : possible_next_scenarios) {
1184  if(x.empty() || x == "null") {
1185  possible_this_is_the_last_scenario = true;
1186  LOG_NG << "This can be the last scenario";
1187  } else if(utils::contains(x, '$')) {
1188  // Assume a WML variable will be set to a correct value before the end of the scenario
1189  known.push_back(x);
1190  LOG_NG << "Variable value for next scenario '" << x << "'";
1191  } else if(game_config_.find_child(tagname, "id", x)) {
1192  known.push_back(x);
1193  LOG_NG << "Known next scenario '" << x << "'";
1194  } else {
1195  unknown.push_back(x);
1196  ERR_NG << "Unknown next scenario '" << x << "'";
1197  }
1198  }
1199 
1200  if(unknown.empty()) {
1201  // everything's good
1202  return;
1203  }
1204 
1205  std::string title = _("Warning: broken campaign branches");
1206  std::stringstream message;
1207 
1208  message << _n(
1209  // TRANSLATORS: This is an error that will hopefully only be seen by UMC authors and by players who have already
1210  // said "okay" to a "loading saves from an old version might not work" dialog.
1211  "The next scenario is missing, you will not be able to finish this campaign.",
1212  // TRANSLATORS: This is an error that will hopefully only be seen by UMC authors and by players who have already
1213  // said "okay" to a "loading saves from an old version might not work" dialog.
1214  "Some of the possible next scenarios are missing, you might not be able to finish this campaign.",
1215  unknown.size() + known.size() + (possible_this_is_the_last_scenario ? 1 : 0));
1216  message << "\n\n";
1217  message << _n(
1218  "Please report the following missing scenario to the campaign’s author:\n$unknown_list|",
1219  "Please report the following missing scenarios to the campaign’s author:\n$unknown_list|",
1220  unknown.size());
1221  message << "\n";
1222  message << _("Once this is fixed, you will need to restart this scenario.");
1223 
1224  std::stringstream unknown_list;
1225  for(const auto& x : unknown) {
1226  unknown_list << font::unicode_bullet << " " << x << "\n";
1227  }
1228  utils::string_map symbols;
1229  symbols["unknown_list"] = unknown_list.str();
1230  auto message_str = utils::interpolate_variables_into_string(message.str(), &symbols);
1231  ERR_NG << message_str;
1233 }
1234 
1236 {
1237  const team& viewing_team = gui_->viewing_team();
1238  return gui_->viewing_team_is_playing() && !events::commands_disabled && viewing_team.is_local_human()
1239  && !is_browsing();
1240 }
1241 
1242 std::set<std::string> play_controller::all_players() const
1243 {
1244  std::set<std::string> res = gui_->observers();
1245  for(const team& t : get_teams()) {
1246  if(t.is_human()) {
1247  res.insert(t.current_player());
1248  }
1249  }
1250 
1251  return res;
1252 }
1253 
1255 {
1256  const bool time_left = gamestate().tod_manager_.next_turn(&gamestate().gamedata_);
1257 
1258  if(!time_left) {
1259  LOG_NG << "firing time over event...";
1261  pump().fire("time_over");
1262  LOG_NG << "done firing time over event...";
1263 
1264  // If turns are added while handling 'time over' event.
1265  if(gamestate().tod_manager_.is_time_left()) {
1266  return;
1267  }
1268 
1269  if(video::headless()) {
1270  LOG_AIT << "time over (draw)";
1272  }
1273 
1274  check_victory();
1275  if(is_regular_game_end()) {
1276  return;
1277  }
1278 
1279  end_level_data e;
1280  e.transient.reveal_map = reveal_map_default();
1281  e.proceed_to_next_level = false;
1282  e.is_victory = false;
1284  }
1285 }
1286 
1288  : controller_(controller)
1289 {
1291 }
1292 
1294 {
1295  controller_.saved_game_.remove_snapshot();
1296 }
1297 
1299 {
1300  const team& t = gui_->viewing_team();
1301  static const std::string no_objectives(_("No objectives available"));
1302  std::string objectives = utils::interpolate_variables_into_string(t.objectives(), *gamestate_->get_game_data());
1303  gui2::show_transient_message(get_scenario_name(), (objectives.empty() ? no_objectives : objectives), "", true);
1304  t.reset_objectives_changed();
1305 }
1306 
1308 {
1310  const std::shared_ptr<gui::button> skip_animation_button = get_display().find_action_button("skip-animation");
1311  if(skip_animation_button) {
1312  skip_animation_button->set_check(skip_replay_);
1313  }
1314 }
1315 
1317 {
1318  return is_skipping_replay() || (prefs::get().skip_ai_moves() && current_team().is_ai() && !is_replay());
1319 }
1320 
1322 {
1324 }
1325 
1327 {
1329 }
1330 
1332 {
1335  }
1336 }
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
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:172
auto all_children_view() const
In-order iteration over all children.
Definition: config.hpp:810
child_itors child_range(config_key_type key)
Definition: config.cpp:272
config & add_child(config_key_type key)
Definition: config.cpp:440
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:770
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
@ 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
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
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 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:588
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 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:251
int support() const
Calculate total support capacity, based on support_per_village.
Definition: team.hpp:190
bool is_local_human() const
Definition: team.hpp:253
void spend_gold(const int amount)
Definition: team.hpp:195
void new_turn()
Definition: team.hpp:191
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:88
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(const std::string &tag_name, Args &&... contents)
Definition: markup.hpp:45
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
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:2152
Gather statistics important for AI testing and output them.
unit_type_data unit_types
Definition: types.cpp:1500
Various functions that implement the undoing (and redoing) of in-game commands.
Various functions implementing vision (through fog of war and shroud).
#define e