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