The Battle for Wesnoth  1.15.0-dev
play_controller.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2018 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
3  wesnoth playlevel Copyright (C) 2003 by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  * Handle input via mouse & keyboard, events, schedule commands.
19  */
20 
21 #include "play_controller.hpp"
22 
23 #include "actions/create.hpp"
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 "formula/string_utils.hpp"
32 #include "game_events/pump.hpp"
33 #include "game_state.hpp"
34 #include "gettext.hpp"
35 #include "gui/dialogs/game_ui.hpp"
38 #include "help/help.hpp"
41 #include "hotkey/hotkey_item.hpp"
42 #include "log.hpp"
43 #include "map/label.hpp"
44 #include "pathfind/teleport.hpp"
46 #include "preferences/display.hpp"
47 #include "preferences/game.hpp"
48 #include "random.hpp"
49 #include "replay.hpp"
50 #include "reports.hpp"
51 #include "resources.hpp"
52 #include "save_blocker.hpp"
53 #include "saved_game.hpp"
54 #include "savegame.hpp"
57 #include "sound.hpp"
58 #include "soundsource.hpp"
59 #include "statistics.hpp"
60 #include "synced_context.hpp"
61 #include "terrain/type_data.hpp"
62 #include "tooltips.hpp"
63 #include "units/id.hpp"
64 #include "units/unit.hpp"
65 #include "utils/functional.hpp"
66 #include "whiteboard/manager.hpp"
67 
68 static lg::log_domain log_aitesting("aitesting");
69 #define LOG_AIT LOG_STREAM(info, log_aitesting)
70 // If necessary, this define can be replaced with `#define LOG_AIT std::cout` to restore previous behavior
71 
72 static lg::log_domain log_engine("engine");
73 #define LOG_NG LOG_STREAM(info, log_engine)
74 #define DBG_NG LOG_STREAM(debug, 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  "victory_when_enemies_defeated",
94  "remove_from_carryover_on_defeat",
95  "disallow_recall",
96  "experience_modifier",
97  "require_scenario"
98  };
99 
100  static const std::set<std::string> tags{"terrain_graphics", "lua"};
101 
102  for(const std::string& attr : attrs) {
103  dst[attr] = src[attr];
104  }
105 
106  for(const std::string& tag : tags) {
107  dst.append_children(src, tag);
108  }
109 }
110 
111 static void clear_resources()
112 {
113  resources::controller = nullptr;
114  resources::filter_con = nullptr;
115  resources::gameboard = nullptr;
116  resources::gamedata = nullptr;
117  resources::game_events = nullptr;
118  resources::lua_kernel = nullptr;
119  resources::persist = nullptr;
120  resources::soundsources = nullptr;
121  resources::tod_manager = nullptr;
122  resources::tunnels = nullptr;
123  resources::undo_stack = nullptr;
124  resources::recorder = nullptr;
125  resources::whiteboard.reset();
126  resources::classification = nullptr;
127 }
128 
130  saved_game& state_of_game,
131  const ter_data_cache& tdata,
132  bool skip_replay)
133  : controller_base()
134  , observer()
136  , ticks_(SDL_GetTicks())
137  , tdata_(tdata)
138  , gamestate_()
139  , level_()
140  , saved_game_(state_of_game)
141  , tooltips_manager_()
142  , whiteboard_manager_()
143  , plugins_context_()
144  , labels_manager_()
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  , ui_()
152  , xp_mod_(new unit_experience_accelerator(level["experience_modifier"].to_int(100)))
153  , statistics_context_(new statistics::scenario_context(level["name"]))
154  , replay_(new replay(state_of_game.get_replay()))
155  , skip_replay_(skip_replay)
156  , linger_(false)
157  , init_side_done_now_(false)
158  , map_start_()
159  , victory_when_enemies_defeated_(level["victory_when_enemies_defeated"].to_bool(true))
160  , remove_from_carryover_on_defeat_(level["remove_from_carryover_on_defeat"].to_bool(true))
161  , victory_music_()
162  , defeat_music_()
163  , scope_()
164  , ignore_replay_errors_(false)
165  , player_type_changed_(false)
166 {
167  copy_persistent(level, level_);
168 
169  resources::controller = this;
173 
175 
176  join();
177 
179 
182 
183  try {
184  init(level);
185  } catch(...) {
186  clear_resources();
187  throw;
188  }
189 }
190 
192 {
193  help::reset();
195  clear_resources();
196 }
197 
199 {
200  void operator()(const config&) const
201  {
203  }
204 };
205 
207 {
208  gui2::dialogs::loading_screen::display([this, &level]() {
210 
211  LOG_NG << "initializing game_state..." << (SDL_GetTicks() - ticks()) << std::endl;
212  gamestate_.reset(new game_state(level, *this, tdata_));
213 
221 
222  gamestate_->ai_manager_.add_observer(this);
223  gamestate_->init(level, *this);
224 
226 
227  LOG_NG << "initializing whiteboard..." << (SDL_GetTicks() - ticks()) << std::endl;
229  whiteboard_manager_.reset(new wb::manager());
231 
232  LOG_NG << "loading units..." << (SDL_GetTicks() - ticks()) << std::endl;
235 
236  LOG_NG << "initializing theme... " << (SDL_GetTicks() - ticks()) << std::endl;
238  const config& theme_cfg = controller_base::get_theme(game_config_, theme());
239 
240  LOG_NG << "building terrain rules... " << (SDL_GetTicks() - ticks()) << std::endl;
242 
243  gui_.reset(new game_display(gamestate().board_, whiteboard_manager_, theme_cfg, level));
244 
245  map_start_ = map_location(level.child_or_empty("display").child_or_empty("location"));
246 
247  if(!gui_->video().faked()) {
249  gui_->get_theme().modify_label("time-icon", _("time left for current turn"));
250  } else {
251  gui_->get_theme().modify_label("time-icon", _("current local time"));
252  }
253  }
254 
256 
257  mouse_handler_.set_gui(gui_.get());
258  menu_handler_.set_gui(gui_.get());
259 
260  LOG_NG << "done initializing display... " << (SDL_GetTicks() - ticks()) << std::endl;
261  LOG_NG << "building gamestate to gui and whiteboard... " << (SDL_GetTicks() - ticks()) << std::endl;
262 
263  // This *needs* to be created before the show_intro and show_map_scene
264  // as that functions use the manager state_of_game
265  // Has to be done before registering any events!
268 
269  if(gamestate().first_human_team_ != -1) {
270  gui_->set_team(gamestate().first_human_team_);
271  } else if(is_observer()) {
272  // Find first team that is allowed to be observed.
273  // If not set here observer would be without fog until
274  // the first turn of observable side
275  for(const team& t : gamestate().board_.teams()) {
276  if(!t.get_disallow_observers()) {
277  gui_->set_team(t.side() - 1);
278  }
279  }
280  }
281 
282  init_managers();
284 
285  // loadscreen_manager->reset();
287  gamestate().lua_kernel_->load_game(level);
288 
289  plugins_context_.reset(new plugins_context("Game"));
290  plugins_context_->set_callback(
291  "save_game", [this](const config& cfg) { save_game_auto(cfg["filename"]); }, true);
292  plugins_context_->set_callback(
293  "save_replay", [this](const config& cfg) { save_replay_auto(cfg["filename"]); }, true);
294  plugins_context_->set_callback("quit", throw_end_level(), false);
295  plugins_context_->set_accessor_string("scenario_name", [this](config) { return get_scenario_name(); });
296  });
297 
298  // Do this after the loadingscreen, so that ita happens in the main thread.
299  gui_->join();
300 
302 }
303 
305 {
306  // Set member in the base class
307  ui_.reset(new gui2::dialogs::game_ui());
308  assert(ui_);
309 
310  // TODO: reenable once we get the HUD/map event separation problems sorted out...
311  //ui_->show(true);
312 }
313 
314 void play_controller::reset_gamestate(const config& level, int replay_pos)
315 {
316  resources::gameboard = nullptr;
317  resources::gamedata = nullptr;
318  resources::tod_manager = nullptr;
319  resources::filter_con = nullptr;
320  resources::lua_kernel = nullptr;
321  resources::game_events = nullptr;
322  resources::tunnels = nullptr;
323  resources::undo_stack = nullptr;
324 
325  gui_->labels().set_team(nullptr);
326 
327  // First destroy the old game state, then create the new one.
328  // This is necessary to ensure that while the old AI manager is being destroyed,
329  // all its member objects access the old manager instead of the new.
330  gamestate_.reset();
331  gamestate_.reset(new game_state(level, *this, tdata_));
339 
340  gamestate_->ai_manager_.add_observer(this);
341  gamestate_->init(level, *this);
344 
345  //gui_->reset_reports(*gamestate().reports_);
346  gui_->change_display_context(&gamestate().board_);
347 
348  saved_game_.get_replay().set_pos(replay_pos);
349 
351  gamestate().lua_kernel_->load_game(level);
352 }
353 
355 {
356  LOG_NG << "initializing managers... " << (SDL_GetTicks() - ticks()) << std::endl;
360 
362  LOG_NG << "done initializing managers... " << (SDL_GetTicks() - ticks()) << std::endl;
363 }
364 
366 {
367  // Run initialization scripts, even if loading from a snapshot.
368  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
369  pump().fire("preload");
370 }
371 
373 {
374  // pre-start events must be executed before any GUI operation,
375  // as those may cause the display to be refreshed.
376  update_locker lock_display(gui_->video());
378 
379  // Fire these right before prestart events, to catch only the units sides
380  // have started with.
381  for(const unit& u : gamestate().board_.units()) {
382  pump().fire("unit_placed", map_location(u.get_location()));
383  }
384 
385  pump().fire("prestart");
386 
387  // prestart event may modify start turn with WML, reflect any changes.
388  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
389 }
390 
392 {
394  pump().fire("start");
395 
396  // start event may modify start turn with WML, reflect any changes.
397  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
398 
400 
401  // prestart and start events may modify the initial gold amount, reflect any changes.
402  for(team& tm : gamestate().board_.teams_) {
403  tm.set_start_gold(tm.gold());
404  }
405 
406  gamestate_->init_side_done() = false;
408 }
409 
411 {
412  gui_->begin_game();
413  gui_->update_tod();
414 }
415 
417 {
419 
420  // If we are observers we move to watch next team if it is allowed
421  if((is_observer() && !current_team().get_disallow_observers())
422  || (current_team().is_local_human() && !this->is_replay())) {
424  }
425 
426  gui_->set_playing_team(std::size_t(current_side() - 1));
427 
429 }
430 
432 {
433  //
434  // We do side init only if not done yet for a local side when we are not replaying.
435  // For all other sides it is recorded in replay and replay handler has to handle
436  // calling do_init_side() functions.
437  //
438  if(gamestate_->init_side_done()) {
439  // We already executed do_init_side this can for example happen if we reload a game,
440  // but also if we changed control of a side during it's turn
441  return;
442  }
443 
444  if(!current_team().is_local()) {
445  // We are in a mp game and execute do_init_side as soon as we receive [init_side] from the current player
446  // (see replay.cpp)
447  return;
448  }
449 
450  if(is_replay()) {
451  // We are in a replay and execute do_init_side as soon as we reach the next [init_side] in the replay data
452  // (see replay.cpp)
453  return;
454  }
455 
457  do_init_side();
458 }
459 
461 {
462  set_scontext_synced sync;
463  log_scope("player turn");
464 
465  // In case we might end up calling sync:network during the side turn events,
466  // and we don't want do_init_side to be called when a player drops.
467  gamestate_->init_side_done() = true;
468  init_side_done_now_ = true;
469 
470  const std::string turn_num = std::to_string(turn());
471  const std::string side_num = std::to_string(current_side());
472 
473  gamestate().gamedata_.get_variable("side_number") = current_side();
474 
475  // We might have skipped some sides because they were empty so it is not enough to check for side_num==1
476  if(!gamestate().tod_manager_.has_turn_event_fired()) {
477  pump().fire("turn_" + turn_num);
478  pump().fire("new_turn");
480  }
481 
482  pump().fire("side_turn");
483  pump().fire("side_" + side_num + "_turn");
484  pump().fire("side_turn_" + turn_num);
485  pump().fire("side_" + side_num + "_turn_" + turn_num);
486 
487  // We want to work out if units for this player should get healed,
488  // and the player should get income now.
489  // Healing/income happen if it's not the first turn of processing,
490  // or if we are loading a game.
491  if(turn() > 1) {
494 
495  // If the expense is less than the number of villages owned
496  // times the village support capacity,
497  // then we don't have to pay anything at all
498  int expense = gamestate().board_.side_upkeep(current_side()) - current_team().support();
499  if(expense > 0) {
500  current_team().spend_gold(expense);
501  }
502 
504  }
505 
506  // Prepare the undo stack.
508 
509  pump().fire("turn_refresh");
510  pump().fire("side_" + side_num + "_turn_refresh");
511  pump().fire("turn_" + turn_num + "_refresh");
512  pump().fire("side_" + side_num + "_turn_" + turn_num + "_refresh");
513 
514  // Make sure vision is accurate.
516 
517  init_side_end();
518  check_victory();
519  sync.do_final_checkup();
520 }
521 
523 {
525 
526  if(current_side() == 1 || !init_side_done_now_) {
528  }
529 
530  if(!is_skipping_replay() && current_team().get_scroll_to_leader() && !map_start_.valid()) {
531  gui_->scroll_to_leader(current_side(), game_display::ONSCREEN, false);
532  }
533 
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  return cfg;
554 }
555 
557 {
558  whiteboard_manager_->on_finish_side_turn(current_side());
559 
560  { // Block for set_scontext_synced
561  set_scontext_synced sync(1);
562  // Ending the turn commits all moves.
563  undo_stack().clear();
565 
566  const std::string turn_num = std::to_string(turn());
567  const std::string side_num = std::to_string(current_side());
568 
569  // Clear shroud, in case units had been slowed for the turn.
571 
572  pump().fire("side_turn_end");
573  pump().fire("side_" + side_num + "_turn_end");
574  pump().fire("side_turn_" + turn_num + "_end");
575  pump().fire("side_" + side_num + "_turn_" + turn_num + "_end");
576 
577  // This is where we refog, after all of a side's events are done.
579  check_victory();
580  sync.do_final_checkup();
581  }
582 
584  gamestate_->init_side_done() = false;
585 }
586 
588 {
589  set_scontext_synced sync(2);
590  const std::string turn_num = std::to_string(turn());
591 
592  pump().fire("turn_end");
593  pump().fire("turn_" + turn_num + "_end");
594 
595  sync.do_final_checkup();
596 }
597 
599 {
600  // If we aren't using fog/shroud, this is easy :)
601  if(current_team().uses_fog() == false && current_team().uses_shroud() == false) {
602  return true;
603  }
604 
605  // See if any enemies are visible
606  for(const unit& u : gamestate().board_.units()) {
607  if(current_team().is_enemy(u.side()) && !gui_->fogged(u.get_location())) {
608  return true;
609  }
610  }
611 
612  return false;
613 }
614 
615 // TODO: implement in GUI2 command console dialog
616 #if 0
617 void play_controller::tab()
618 {
619  gui::TEXTBOX_MODE mode = menu_handler_.get_textbox().mode();
620 
621  std::set<std::string> dictionary;
622  switch(mode) {
623  case gui::TEXTBOX_SEARCH:
624  {
625  for (const unit& u : gamestate().board_.units()){
626  const map_location& loc = u.get_location();
627  if(!gui_->fogged(loc) &&
628  !(gamestate().board_.teams()[gui_->viewing_team()].is_enemy(u.side()) && u.invisible(loc)))
629  dictionary.insert(u.name());
630  }
631  //TODO List map labels
632  break;
633  }
634  case gui::TEXTBOX_COMMAND:
635  {
636  std::vector<std::string> commands = menu_handler_.get_commands_list();
637  dictionary.insert(commands.begin(), commands.end());
638  FALLTHROUGH; // we also want player names from the next case
639  }
640  case gui::TEXTBOX_MESSAGE:
641  {
642  for (const team& t : gamestate().board_.teams()) {
643  if(!t.is_empty())
644  dictionary.insert(t.current_player());
645  }
646 
647  // Add observers
648  for (const std::string& o : gui_->observers()){
649  dictionary.insert(o);
650  }
651 
652  // Add nicks who whispered you
653  for (const std::string& w : gui_->get_chat_manager().whisperers()){
654  dictionary.insert(w);
655  }
656 
657  // Add nicks from friendlist
658  const std::map<std::string, std::string> friends = preferences::get_acquaintances_nice("friend");
659 
660  for(std::map<std::string, std::string>::const_iterator iter = friends.begin(); iter != friends.end(); ++iter){
661  dictionary.insert((*iter).first);
662  }
663 
664  //Exclude own nick from tab-completion.
665  //NOTE why ?
666  dictionary.erase(preferences::login());
667  break;
668  }
669 
670  default:
671  ERR_DP << "unknown textbox mode" << std::endl;
672  } //switch(mode)
673 
674  menu_handler_.get_textbox().tab(dictionary);
675 }
676 #endif
677 
679 {
680  assert(gamestate().board_.has_team(current_side()));
682 }
683 
685 {
686  assert(gamestate().board_.has_team(current_side()));
688 }
689 
690 /// @returns: the number n in [min, min+mod ) so that (n - num) is a multiple of mod.
691 static int modulo(int num, int mod, int min)
692 {
693  assert(mod > 0);
694  int n = (num - min) % mod;
695  if(n < 0)
696  n += mod;
697  // n is now in [0, mod)
698  n = n + min;
699  return n;
700  // the following properties are easy to verify:
701  // 1) For all m: modulo(num, mod, min) == modulo(num + mod*m, mod, min)
702  // 2) For all 0 <= m < mod: modulo(min + m, mod, min) == min + m
703 }
704 
705 bool play_controller::is_team_visible(int team_num, bool observer) const
706 {
707  const team& t = gamestate().board_.get_team(team_num);
708  if(observer) {
709  return !t.get_disallow_observers() && !t.is_empty();
710  } else {
711  return t.is_local_human() && !t.is_idle();
712  }
713 }
714 
716 {
717  assert(current_side() <= static_cast<int>(gamestate().board_.teams().size()));
718  const int num_teams = gamestate().board_.teams().size();
719  const bool is_observer = this->is_observer();
720 
721  for(int i = 0; i < num_teams; i++) {
722  const int team_num = modulo(current_side() - i, num_teams, 1);
723  if(is_team_visible(team_num, is_observer)) {
724  return team_num;
725  }
726  }
727  return 0;
728 }
729 
731 {
732  return mouse_handler_;
733 }
734 
735 std::shared_ptr<wb::manager> play_controller::get_whiteboard() const
736 {
737  return whiteboard_manager_;
738 }
739 
741 {
742  return saved_game_.mp_settings();
743 }
744 
746 {
747  return saved_game_.classification();
748 }
749 
751 {
752  return *gui_;
753 }
754 
755 void play_controller::process_keydown_event(const SDL_Event& event)
756 {
757  if(event.key.keysym.sym == SDLK_TAB) {
758  whiteboard_manager_->set_invert_behavior(true);
759  }
760 }
761 
762 void play_controller::process_keyup_event(const SDL_Event& event)
763 {
764  // If the user has pressed 1 through 9, we want to show
765  // how far the unit can move in that many turns
766  if(event.key.keysym.sym >= '1' && event.key.keysym.sym <= '9') {
767  const int new_path_turns = (event.type == SDL_KEYDOWN) ? event.key.keysym.sym - '1' : 0;
768 
769  if(new_path_turns != mouse_handler_.get_path_turns()) {
770  mouse_handler_.set_path_turns(new_path_turns);
771 
773 
774  if(u.valid()) {
775  // if it's not the unit's turn, we reset its moves
776  unit_movement_resetter move_reset(*u, u->side() != current_side());
777 
779  gamestate().board_.teams_[gui_->viewing_team()], mouse_handler_.get_path_turns()));
780 
781  gui_->highlight_reach(mouse_handler_.current_paths());
782  } else {
784  }
785  }
786  } else if(event.key.keysym.sym == SDLK_TAB) {
787  CKey keys;
788  if(!keys[SDLK_TAB]) {
789  whiteboard_manager_->set_invert_behavior(false);
790  }
791  }
792 }
793 
795 {
796  assert(replay_);
797  return *replay_.get();
798 }
799 
801 {
803  // Saving while an event is running isn't supported
804  // because it may lead to expired event handlers being saved.
805  assert(!gamestate().events_manager_->is_event_running());
806 
808  scoped_savegame_snapshot snapshot(*this);
811  } else {
813  }
814 }
815 
816 void play_controller::save_game_auto(const std::string& filename)
817 {
820 
821  scoped_savegame_snapshot snapshot(*this);
823  save.save_game_automatic(false, filename);
824  }
825 }
826 
828 {
833  } else {
835  }
836 }
837 
838 void play_controller::save_replay_auto(const std::string& filename)
839 {
843  save.save_game_automatic(false, filename);
844  }
845 }
846 
848 {
852  } else {
854  }
855 }
856 
858 {
860  load.load_game_ingame();
861 }
862 
864 {
866  undo_stack().undo();
867 }
868 
870 {
872  undo_stack().redo();
873 }
874 
876 {
878 }
879 
881 {
883 }
884 
885 const std::string& play_controller::select_music(bool victory) const
886 {
887  const std::vector<std::string>& music_list = victory
888  ? (gamestate_->get_game_data()->get_victory_music().empty()
890  : gamestate_->get_game_data()->get_victory_music())
891  : (gamestate_->get_game_data()->get_defeat_music().empty()
893  : gamestate_->get_game_data()->get_defeat_music());
894 
895  if(music_list.empty()) {
896  // Since this function returns a reference, we can't return a temporary empty string.
897  static const std::string empty_str = "";
898  return empty_str;
899  }
900 
901  return music_list[randomness::rng::default_instance().get_random_int(0, music_list.size() - 1)];
902 }
903 
905 {
906  if(linger_) {
907  return;
908  }
909 
910  if(is_regular_game_end()) {
911  return;
912  }
913 
914  bool continue_level, found_player, found_network_player, invalidate_all;
915  std::set<unsigned> not_defeated;
916 
918  continue_level,
919  found_player,
920  found_network_player,
921  invalidate_all,
922  not_defeated,
924  );
925 
926  if(continue_level) {
927  return;
928  }
929 
930  if(found_player || found_network_player) {
931  pump().fire("enemies_defeated");
932 
933  if(is_regular_game_end()) {
934  return;
935  }
936  }
937 
938  DBG_EE << "victory_when_enemies_defeated: " << victory_when_enemies_defeated_ << std::endl;
939  DBG_EE << "found_player: " << found_player << std::endl;
940  DBG_EE << "found_network_player: " << found_network_player << std::endl;
941 
942  if(!victory_when_enemies_defeated_ && (found_player || found_network_player)) {
943  // This level has asked not to be ended by this condition.
944  return;
945  }
946 
947  if(gui_->video().non_interactive()) {
948  LOG_AIT << "winner: ";
949 
950  for(unsigned l : not_defeated) {
952  if(ai.empty()) {
953  ai = "default ai";
954  }
955 
956  LOG_AIT << l << " (using " << ai << ") ";
957  }
958 
959  LOG_AIT << std::endl;
960  ai_testing::log_victory(not_defeated);
961  }
962 
963  DBG_EE << "throwing end level exception..." << std::endl;
964 
965  // Also proceed to the next scenario when another player survived.
966  end_level_data el_data;
967  el_data.proceed_to_next_level = found_player || found_network_player;
968  el_data.is_victory = found_player;
969  set_end_level_data(el_data);
970 }
971 
972 void play_controller::process_oos(const std::string& msg) const
973 {
974  if(gui_->video().non_interactive()) {
975  throw game::game_error(msg);
976  }
977 
979  return;
980  }
981 
982  std::stringstream message;
983  message << _("The game is out of sync. It might not make much sense to continue. Do you want to save your game?");
984  message << "\n\n" << _("Error details:") << "\n\n" << msg;
985 
986  scoped_savegame_snapshot snapshot(*this);
988  save.save_game_interactive(message.str(), savegame::savegame::YES_NO); // can throw quit_game_exception
989 }
990 
991 void play_controller::update_gui_to_player(const int team_index, const bool observe)
992 {
993  gui_->set_team(team_index, observe);
994 }
995 
997 {
998  scoped_savegame_snapshot snapshot(*this);
1001 }
1002 
1003 void play_controller::do_consolesave(const std::string& filename)
1004 {
1005  scoped_savegame_snapshot snapshot(*this);
1007  save.save_game_automatic(true, filename);
1008 }
1009 
1011 {
1012  // note: this writes to level_ if this is not a replay.
1014 }
1015 
1017 {
1018  return gamestate().events_manager_->pump();
1019 }
1020 
1022 {
1023  return ticks_;
1024 }
1025 
1027 {
1028  return soundsources_manager_.get();
1029 }
1030 
1032 {
1033  return plugins_context_.get();
1034 }
1035 
1037 {
1038  return hotkey_handler_.get();
1039 }
1040 
1042 {
1043  if(linger_ || !gamestate_->init_side_done() || gamestate().gamedata_.phase() != game_data::PLAY) {
1044  return true;
1045  }
1046 
1047  const team& t = current_team();
1048  return !t.is_local_human() || !t.is_proxy_human();
1049 }
1050 
1052 {
1054  return;
1055  }
1056 
1057  try {
1058  play_slice();
1059  } catch(const return_to_play_side_exception&) {
1060  assert(should_return_to_play_side());
1061  }
1062 }
1063 
1065 {
1066  fire_preload();
1067 
1068  if(!gamestate().start_event_fired_) {
1069  gamestate().start_event_fired_ = true;
1073 
1074  set_scontext_synced sync;
1075 
1076  fire_prestart();
1077 
1078  if(is_regular_game_end()) {
1079  return;
1080  }
1081 
1082  for(const team& t : gamestate().board_.teams()) {
1083  actions::clear_shroud(t.side(), false, false);
1084  }
1085 
1086  init_gui();
1087  LOG_NG << "first_time..." << (is_skipping_replay() ? "skipping" : "no skip") << "\n";
1088 
1089  fire_start();
1090 
1091  if(is_regular_game_end()) {
1092  return;
1093  }
1094 
1095  sync.do_final_checkup();
1096 
1097  // Initialize countdown clock.
1098  for(const team& t : gamestate().board_.teams()) {
1100  t.set_countdown_time(1000 * saved_game_.mp_settings().mp_countdown_init_time);
1101  }
1102  }
1103  } else {
1104  init_gui();
1106  }
1107 }
1108 
1110 {
1111  const team& viewing_team = get_teams_const()[gui_->viewing_team()];
1112  return gui_->viewing_team() == gui_->playing_team() && !events::commands_disabled && viewing_team.is_local_human()
1113  && !is_lingering() && !is_browsing();
1114 }
1115 
1116 std::set<std::string> play_controller::all_players() const
1117 {
1118  std::set<std::string> res = gui_->observers();
1119  for(const team& t : get_teams_const()) {
1120  if(t.is_human()) {
1121  res.insert(t.current_player());
1122  }
1123  }
1124 
1125  return res;
1126 }
1127 
1129 {
1130  do {
1132  {
1133  save_blocker blocker;
1135  if(is_regular_game_end()) {
1136  return;
1137  }
1138  }
1139 
1140  // This flag can be set by derived classes (in overridden functions).
1141  player_type_changed_ = false;
1142 
1143  statistics::reset_turn_stats(gamestate().board_.get_team(current_side()).save_id_or_number());
1144 
1145  play_side_impl();
1146 
1147  if(is_regular_game_end()) {
1148  return;
1149  }
1150 
1151  } while(player_type_changed_);
1152 
1153  // Keep looping if the type of a team (human/ai/networked) has changed mid-turn
1154  sync_end_turn();
1155 }
1156 
1158 {
1159  whiteboard_manager_->on_gamestate_change();
1160  gui_->new_turn();
1161 
1162  LOG_NG << "turn: " << turn() << "\n";
1163 
1164  if(gui_->video().non_interactive()) {
1165  LOG_AIT << "Turn " << turn() << ":" << std::endl;
1166  }
1167 
1168  int last_player_number = gamestate_->player_number_;
1169  int next_player_number = gamestate_->next_player_number_;
1170 
1171  while(gamestate_->player_number_ <= static_cast<int>(gamestate().board_.teams().size())) {
1172  gamestate_->next_player_number_ = gamestate_->player_number_ + 1;
1173  next_player_number = gamestate_->next_player_number_;
1174  last_player_number = gamestate_->player_number_;
1175 
1176  // If a side is empty skip over it.
1177  if(!current_team().is_empty()) {
1178  init_side_begin();
1179 
1180  if(gamestate_->init_side_done()) {
1181  // This is the case in a reloaded game where the side was initialized before saving the game.
1182  init_side_end();
1183  }
1184 
1186  play_side();
1187  // ignore any changes to next_player_number_ that happen after the [end_turn] is sent to the server,
1188  // otherwise we will get OOS.
1189  next_player_number = gamestate_->next_player_number_;
1190  assert(next_player_number <= 2 * static_cast<int>(gamestate().board_.teams().size()));
1191  if(is_regular_game_end()) {
1192  return;
1193  }
1194  // note: play_side() send the [end_turn] to the sever and finish_side_turn() calls the side turn end
1195  // events.
1196  // this means that during the side turn end events the clients think it is still the last sides turn
1197  // while the server thinks that it is already the next plyers turn. i don'T think this is a problem
1198  // though.
1199  finish_side_turn();
1200 
1201  if(is_regular_game_end()) {
1202  return;
1203  }
1204 
1205  if(gui_->video().non_interactive()) {
1206  LOG_AIT << " Player " << current_side() << ": " << current_team().villages().size() << " Villages"
1207  << std::endl;
1209  }
1210  }
1211 
1212  gamestate_->player_number_ = next_player_number;
1213  }
1214 
1215  // If the loop exits due to the last team having been processed.
1216  gamestate_->player_number_ = last_player_number;
1217 
1218  finish_turn();
1219 
1220  // Time has run out
1221  check_time_over();
1222 
1223  if(!is_regular_game_end()) {
1224  gamestate_->player_number_ = modulo(next_player_number, gamestate().board_.teams().size(), 1);
1225  }
1226 }
1227 
1229 {
1230  const bool time_left = gamestate().tod_manager_.next_turn(&gamestate().gamedata_);
1231 
1232  if(!time_left) {
1233  LOG_NG << "firing time over event...\n";
1234 
1236  pump().fire("time_over");
1237 
1238  LOG_NG << "done firing time over event...\n";
1239 
1240  // If turns are added while handling 'time over' event.
1241  if(gamestate().tod_manager_.is_time_left()) {
1242  return;
1243  }
1244 
1245  if(gui_->video().non_interactive()) {
1246  LOG_AIT << "time over (draw)\n";
1248  }
1249 
1250  check_victory();
1251 
1252  if(is_regular_game_end()) {
1253  return;
1254  }
1255 
1256  end_level_data e;
1257  e.proceed_to_next_level = false;
1258  e.is_victory = false;
1259  set_end_level_data(e);
1260  }
1261 }
1262 
1264  : controller_(controller)
1265 {
1267 }
1268 
1270 {
1272 }
1273 
1275 {
1276  const team& t = gamestate().board_.teams()[gui_->viewing_team()];
1277  static const std::string no_objectives(_("No objectives available"));
1278  std::string objectives = utils::interpolate_variables_into_string(t.objectives(), *gamestate_->get_game_data());
1279  gui2::show_transient_message(get_scenario_name(), (objectives.empty() ? no_objectives : objectives), "", true);
1281 }
1282 
1284 {
1286 #if 0
1287  const std::shared_ptr<gui::button> skip_animation_button = get_display().find_action_button("skip-animation");
1288  if(skip_animation_button) {
1289  skip_animation_button->set_check(skip_replay_);
1290  }
1291 #endif
1292 }
static const config & get_theme(const config &game_config, std::string theme_name)
void clear()
Clears the stack of undoable (and redoable) actions.
Definition: undo.cpp:219
play_controller * controller
Definition: resources.cpp:21
std::vector< std::string > default_victory_music
void set_current_paths(const pathfind::paths &new_paths)
int autosavemax()
Definition: game.cpp:806
virtual bool should_return_to_play_side() const
std::unique_ptr< soundsource::manager > soundsources_manager_
void operator()(const config &) const
static void log_turn_start(unsigned int side)
Definition: testing.cpp:35
static void log_turn_end(unsigned int side)
Definition: testing.cpp:40
void set_preference_display_settings()
Definition: display.cpp:44
::tod_manager * tod_manager
Definition: resources.cpp:29
static lg::log_domain log_engine_enemies("engine/enemies")
bool is_empty() const
Definition: team.hpp:258
hotkey::command_executor * get_hotkey_command_executor() override
Optionally get a command executor to handle context menu events.
void reset_objectives_changed() const
Definition: team.hpp:238
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with &#39;$&#39; in the string &#39;str&#39; with the equivalent ...
map_location last_selected
the last location where a select event fired.
Definition: game_data.hpp:83
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:92
The class for loading a savefile.
Definition: savegame.hpp:96
std::unique_ptr< game_lua_kernel > lua_kernel_
Definition: game_state.hpp:50
void save_replay_auto(const std::string &filename)
int find_last_visible_team() const
returns 0 if no such team was found.
const ter_data_cache & tdata_
This class represents a single unit of a specific type.
Definition: unit.hpp:99
std::unique_ptr< game_display > gui_
bool victory_when_enemies_defeated_
While any instance of this class exists, attempts to save the game via any call to play_controller wi...
game_classification * classification
Definition: resources.cpp:34
Various functions implementing vision (through fog of war and shroud).
const mp_game_settings & get_mp_settings()
void do_final_checkup(bool dont_throw=false)
events::mouse_handler mouse_handler_
static manager & get_singleton()
Definition: manager.hpp:152
static void copy_persistent(const config &src, config &dst)
Copies [scenario] attributes/tags that are not otherwise stored in C++ structs/clases.
std::unique_ptr< plugins_context > plugins_context_
bool is_skipping_replay() const
std::vector< game_tip > load(const config &cfg)
Loads the tips from a config.
Definition: tips.cpp:35
std::unique_ptr< replay > replay_
void reset()
Flags the help manager&#39;s contents for regeneration.
Definition: help.cpp:41
game_display & get_display() override
Get a reference to a display member a derived class uses.
replay_recorder_base & get_replay()
Definition: saved_game.hpp:121
Class for autosaves.
Definition: savegame.hpp:269
game_events::wml_event_pump & pump()
void set_scope_active(scope s, bool set)
static void log_victory(std::set< unsigned int > teams)
Definition: testing.cpp:81
static void on_unblock(play_controller *controller, void(play_controller::*callback)())
std::string sounds
List of "ambient" sounds associated with this time_of_day, Played at the beginning of turn...
std::unique_ptr< hotkey_handler > hotkey_handler_
const int INFINITE_AUTO_SAVES
Definition: game.hpp:189
bool enemies_visible() const
void process_keydown_event(const SDL_Event &event) override
Process keydown (always).
bool is_idle() const
Definition: team.hpp:284
const pathfind::paths & current_paths() const
static void progress(loading_stage stage=loading_stage::none)
virtual void update_viewing_player()=0
void new_turn()
Definition: team.hpp:204
void set_gui(game_display *gui)
Definition: menu_events.hpp:51
virtual soundsource::manager * get_soundsource_man() override
Get (optionally) a soundsources manager a derived class uses.
void undo()
Undoes the top action on the undo stack.
Definition: undo.cpp:347
static lg::log_domain log_aitesting("aitesting")
Gather statistics important for AI testing and output them.
virtual void process_oos(const std::string &msg) const
Asks the user whether to continue on an OOS error.
virtual void play_slice(bool is_delay_enabled=true)
#define LOG_AIT
void new_side_turn(int side)
Performs some initializations and error checks when starting a new side-turn.
Definition: undo.cpp:261
static lg::log_domain log_engine("engine")
Class for "normal" midgame saves.
Definition: savegame.hpp:242
Replay control code.
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:108
persist_manager * persist
Definition: resources.cpp:26
void check_victory()
Checks to see if a side has won.
config & set_snapshot(config snapshot)
Definition: saved_game.cpp:503
void append_children(const config &cfg)
Adds children from cfg.
Definition: config.cpp:245
static void log_draw()
Definition: testing.cpp:75
bool save_game_interactive(const std::string &message, DIALOG_TYPE dialog_type)
Save a game interactively through the savegame dialog.
Definition: savegame.cpp:352
static lg::log_domain log_display("display")
void write_music_play_list(config &snapshot)
Definition: sound.cpp:813
#define DBG_EE
void process_keyup_event(const SDL_Event &event) override
Process keyup (always).
static void clear_resources()
This file implements all the hotkey handling and menu details for play controller.
void select_hex(const map_location &hex, const bool browse, const bool highlight=true, const bool fire_event=true)
persist_manager persist_
void reset_turn_stats(const std::string &save_id)
Definition: statistics.cpp:546
static void add_color_info(const config &v, bool build_defaults)
#define ERR_DP
std::unique_ptr< tooltips::manager > tooltips_manager_
std::vector< std::string > default_defeat_music
const std::unique_ptr< game_events::manager > events_manager_
Definition: game_state.hpp:52
game_data * gamedata
Definition: resources.cpp:22
config::attribute_value & get_variable(const std::string &varname)
throws invalid_variablename_exception if varname is no valid variable name.
Definition: game_data.cpp:62
events::menu_handler menu_handler_
void play_sound(const std::string &files, channel_group group, unsigned int repeats)
Definition: sound.cpp:980
bool can_undo() const
void set_phase(PHASE phase)
Definition: game_data.hpp:77
std::unique_ptr< game_state > gamestate_
An exception-safe means of making sure that unblock() gets called after try_block().
void do_consolesave(const std::string &filename)
bool is_lingering() const
map_location get_selected_hex() const
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:57
saved_game & saved_game_
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
A small explanation about what&#39;s going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:58
scoped_savegame_snapshot(const play_controller &controller)
void init_side()
Definition: replay.cpp:215
static UNUSEDNOWARN std::string _(const char *str)
Definition: gettext.hpp:89
static int modulo(int num, int mod, int min)
team & get_team(int i)
Definition: game_board.hpp:104
void throw_quit_game_exception()
void save_game_auto(const std::string &filename)
bool add_start_if_not_there_yet()
Definition: replay.cpp:657
void set_path_turns(const int path_turns)
virtual void check_time_over()
bool can_redo() const
True if there are actions that can be redone.
Definition: undo.hpp:95
Implements a quit confirmation dialog.
config to_config() const
Builds the snapshot config from members and their respective configs.
filter_context * filter_con
Definition: resources.cpp:23
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.
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:54
bool valid() const
Definition: location.hpp:93
bool ignore_replay_errors
int side_upkeep(int side_num) const
game_board * gameboard
Definition: resources.cpp:20
This class is the frontend of the whiteboard framework for the rest of the Wesnoth code...
Definition: manager.hpp:42
bool is_regular_game_end() const
void init(const config &level)
void recalculate_fog(int side)
Function that recalculates the fog of war.
Definition: vision.cpp:692
virtual void join()
Definition: events.cpp:253
virtual void init_gui()
void maybe_do_init_side()
Called by turn_info::process_network_data() or init_side() to call do_init_side() if necessary...
bool is_enemy(int n) const
Definition: team.hpp:243
tod_manager tod_manager_
Definition: game_state.hpp:47
Object which temporarily resets a unit&#39;s movement.
Definition: unit.hpp:1756
bool load_game_ingame()
Load a game without providing any information.
Definition: savegame.cpp:118
std::unique_ptr< pathfind::manager > pathfind_manager_
Definition: game_state.hpp:48
Managing the AIs lifecycle - headers.
std::string theme() const
replay * recorder
Definition: resources.cpp:28
void check_victory(bool &, bool &, bool &, bool &, std::set< unsigned > &, bool)
Definition: game_board.cpp:96
void set_gui(game_display *gui)
void spend_gold(const int amount)
Definition: team.hpp:208
std::shared_ptr< wb::manager > get_whiteboard() const
An object which will lock the display for the duration of its lifetime.
Definition: video.hpp:256
Error used for any general game error, e.g.
Definition: game_errors.hpp:46
game_events::manager * game_events
Definition: resources.cpp:24
bool is_browsing() const override
void deactivate_all_scopes()
virtual void initialize_and_show_ui() override
Creates and displays the HUD UI that accompanies this controller.
bool is_observer() 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.
void do_init_side()
Called by replay handler or init_side() to do actual work for turn change.
Encapsulates the map of the game.
Definition: location.hpp:42
void calculate_healing(int side, bool update_display)
Calculates healing for all units for the given side.
Definition: heal.cpp:292
std::string login()
std::shared_ptr< wb::manager > whiteboard
Definition: resources.cpp:33
virtual ~play_controller()
bool is_proxy_human() const
Definition: team.hpp:282
Various functions related to the creation of units (recruits, recalls, and placed units)...
int support() const
Calculate total support capacity, based on support_per_village.
Definition: team.hpp:203
void set_end_level_data(const end_level_data &data)
void end_turn(int pnum)
Definition: game_board.cpp:72
int get_path_turns() const
std::size_t i
Definition: function.cpp:933
soundsource::manager * soundsources
Definition: resources.cpp:27
const std::string & select_music(bool victory) const
void redo()
Redoes the top action on the redo stack.
Definition: undo.cpp:396
map_location map_start_
unit_map::iterator selected_unit()
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:712
const config & game_config_
void reset_gamestate(const config &level, int replay_pos)
const t_string & objectives() const
Definition: team.hpp:240
std::shared_ptr< wb::manager > whiteboard_manager_
bool is_team_visible(int team_num, bool observer) const
Define the game&#39;s event mechanism.
void encounter_all_content(const game_board &gameboard_)
Definition: game.cpp:1009
#define log_scope(description)
Definition: log.hpp:186
bool proceed_to_next_level
whether to proceed to the next scenario, equals is_victory in sp.
bool can_use_synced_wml_menu() const
void turn_event_fired()
t_string get_scenario_name() const
game_data gamedata_
Definition: game_state.hpp:45
int w
Additional information on the game outcome which can be provided by WML.
int get_random_int(int min, int max)
This helper method provides a random int from the underlying generator, using results of next_random...
Definition: random.hpp:51
actions::undo_list & undo_stack()
std::map< std::string, std::string > get_acquaintances_nice(const std::string &filter)
Definition: game.cpp:232
static void save(LexState *ls, int c)
Definition: llex.cpp:57
bool can_redo() const
static lg::log_domain log_enginerefac("enginerefac")
std::unique_ptr< gui2::dialogs::game_ui > ui_
std::string observer
bool is_local() const
Definition: team.hpp:260
Class for replay saves (either manually or automatically).
Definition: savegame.hpp:256
config & add_child(config_key_type key)
Definition: config.cpp:479
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:486
compression::format save_compression_format()
Definition: game.cpp:858
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:738
static bool try_block()
bool get_disallow_observers() const
Definition: team.hpp:338
static void display(std::function< void()> f)
void set_game_display(game_display *)
Definition: game_state.cpp:232
bool is_local_human() const
Definition: team.hpp:266
events::mouse_handler & get_mouse_handler_base() override
Get a reference to a mouse handler member a derived class uses.
game_state & gamestate()
double t
Definition: astarsearch.cpp:63
Various functions that implement healing of units (when a side turn starts).
game_classification & classification()
Definition: saved_game.hpp:55
game_board board_
Definition: game_state.hpp:46
Various functions that implement the undoing (and redoing) of in-game commands.
virtual bool is_replay()
Standard logging facilities (interface).
void autosave(const bool disable_autosave, const int autosave_max, const int infinite_autosaves)
Definition: savegame.cpp:580
int current_side() const
Returns the number of the side whose turn it is.
Object which contains all the possible locations a unit can move to, with associated best routes to t...
Definition: pathfind.hpp:70
static const map_location & null_location()
Definition: location.hpp:85
game_lua_kernel * lua_kernel
Definition: resources.cpp:25
config * get_next_action()
Definition: replay.cpp:614
std::set< std::string > all_players() const
void fire_preload()
preload events cannot be synced
virtual plugins_context * get_plugins_context() override
Get (optionally) a plugins context a derived class uses.
void new_turn(int pnum)
Definition: game_board.cpp:64
#define e
actions::undo_list * undo_stack
Definition: resources.cpp:32
std::size_t turn() const
void update_savegame_snapshot() const
void remove_snapshot()
Definition: saved_game.cpp:521
const config & child_or_empty(config_key_type key) const
Returns the first child with the given key, or an empty config if there is none.
Definition: config.cpp:456
const std::vector< team > & get_teams_const() const
void set_side(int side_number)
bool next_turn(game_data *vars)
Function to move to the next turn.
PHASE phase() const
Definition: game_data.hpp:76
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:68
game_classification & get_classification()
bool valid() const
Definition: map.hpp:276
Class that keeps track of all the keys on the keyboard.
Definition: key.hpp:27
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
Definition: saved_game.hpp:59
void delete_all_wml_hotkeys()
deletes all wml hotkeys, should be called after a game has ended
std::vector< std::string > get_commands_list()
void write(config &cfg) const
Definition: game_state.cpp:237
static map_location::DIRECTION n
const std::set< map_location > & villages() const
Definition: team.hpp:184
pathfind::manager * tunnels
Definition: resources.cpp:31
bool can_undo() const
True if there are actions that can be undone.
Definition: undo.hpp:93
static rng & default_instance()
Definition: random.cpp:73
virtual void check_objectives()=0
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:336
virtual void play_side_impl()
bool start_event_fired_
Definition: game_state.hpp:62
void show_objectives() const
bool init_side_done_now_
Whether we did init sides in this session (false = we did init sides before we reloaded the game)...
bool remove_from_carryover_on_defeat_
play_controller(const config &level, saved_game &state_of_game, const ter_data_cache &tdata, bool skip_replay)
#define LOG_NG
std::shared_ptr< terrain_type_data > ter_data_cache
virtual void sync_end_turn()