The Battle for Wesnoth  1.13.11+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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 http://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"
30 #include "display_chat_manager.hpp"
31 #include "formula/string_utils.hpp"
33 #include "game_events/pump.hpp"
34 #include "preferences/game.hpp"
35 #include "game_state.hpp"
36 #include "hotkey/hotkey_item.hpp"
38 #include "map/label.hpp"
39 #include "gettext.hpp"
43 #include "log.hpp"
44 #include "pathfind/teleport.hpp"
45 #include "preferences/display.hpp"
46 #include "random.hpp"
47 #include "replay.hpp"
48 #include "reports.hpp"
49 #include "resources.hpp"
50 #include "savegame.hpp"
51 #include "saved_game.hpp"
52 #include "save_blocker.hpp"
55 #include "sound.hpp"
56 #include "soundsource.hpp"
57 #include "statistics.hpp"
58 #include "synced_context.hpp"
59 #include "terrain/type_data.hpp"
60 #include "tooltips.hpp"
61 #include "units/unit.hpp"
62 #include "units/id.hpp"
63 #include "whiteboard/manager.hpp"
64 #include "wml_exception.hpp"
65 
66 #include "utils/functional.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  static const std::set<std::string> tags {
100  "terrain_graphics",
101  "lua"};
102 
103  for (const std::string& attr : attrs)
104  {
105  dst[attr] = src[attr];
106  }
107 
108  for (const std::string& tag : tags)
109  {
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::game_events = nullptr;
121  resources::lua_kernel = nullptr;
122  resources::persist = nullptr;
123  resources::soundsources = nullptr;
124  resources::tod_manager = nullptr;
125  resources::tunnels = nullptr;
126  resources::undo_stack = nullptr;
127  resources::recorder = nullptr;
128  resources::whiteboard.reset();
129  resources::classification = nullptr;
130 }
131 
133  const config& game_config, const ter_data_cache& tdata, bool skip_replay)
134  : controller_base(game_config)
135  , observer()
137  , ticks_(SDL_GetTicks())
138  , tdata_(tdata)
139  , gamestate_()
140  , level_()
141  , saved_game_(state_of_game)
142  , tooltips_manager_()
143  , whiteboard_manager_()
144  , plugins_context_()
145  , labels_manager_()
146  , help_manager_(&game_config)
147  , mouse_handler_(nullptr, *this)
148  , menu_handler_(nullptr, *this, game_config)
149  , hotkey_handler_(new hotkey_handler(*this, saved_game_))
150  , soundsources_manager_()
151  , persist_()
152  , gui_()
153  , xp_mod_(new unit_experience_accelerator(level["experience_modifier"].to_int(100)))
154  , statistics_context_(new statistics::scenario_context(level["name"]))
155  , replay_(new replay(state_of_game.get_replay()))
156  , skip_replay_(skip_replay)
157  , linger_(false)
158  , init_side_done_now_(false)
159  , map_start_()
160  , victory_when_enemies_defeated_(level["victory_when_enemies_defeated"].to_bool(true))
161  , remove_from_carryover_on_defeat_(level["remove_from_carryover_on_defeat"].to_bool(true))
162  , victory_music_()
163  , defeat_music_()
164  , scope_()
165  , ignore_replay_errors_(false)
166  , player_type_changed_(false)
167 {
168  copy_persistent(level, level_);
169 
170  resources::controller = this;
173 
175 
177 
181  try {
182  init(level);
183  } catch (...) {
184  clear_resources();
185  throw;
186  }
187 }
188 
190 {
192  clear_resources();
193 }
194 
196 {
197  void operator()(const config&) const
198  {
200  }
201 };
202 
204 {
205  gui2::dialogs::loading_screen::display([this, &level]() {
207 
208  LOG_NG << "initializing game_state..." << (SDL_GetTicks() - ticks()) << std::endl;
209  gamestate_.reset(new game_state(level, *this, tdata_));
210 
218 
219  gamestate_->init(level, *this);
221 
222  LOG_NG << "initializing whiteboard..." << (SDL_GetTicks() - ticks()) << std::endl;
224  whiteboard_manager_.reset(new wb::manager());
226 
227  LOG_NG << "loading units..." << (SDL_GetTicks() - ticks()) << std::endl;
230 
231  LOG_NG << "initializing theme... " << (SDL_GetTicks() - ticks()) << std::endl;
233  const config& theme_cfg = controller_base::get_theme(game_config_, theme());
234 
235  LOG_NG << "building terrain rules... " << (SDL_GetTicks() - ticks()) << std::endl;
237  gui_.reset(new game_display(gamestate().board_, whiteboard_manager_, *gamestate().reports_, theme_cfg, level));
238  map_start_ = map_location(level.child_or_empty("display").child_or_empty("location"));
239  if (!gui_->video().faked()) {
241  gui_->get_theme().modify_label("time-icon", _ ("time left for current turn"));
242  else
243  gui_->get_theme().modify_label("time-icon", _ ("current local time"));
244  }
245 
247  mouse_handler_.set_gui(gui_.get());
248  menu_handler_.set_gui(gui_.get());
249 
250  LOG_NG << "done initializing display... " << (SDL_GetTicks() - ticks()) << std::endl;
251 
252  LOG_NG << "building gamestate to gui and whiteboard... " << (SDL_GetTicks() - ticks()) << std::endl;
253  // This *needs* to be created before the show_intro and show_map_scene
254  // as that functions use the manager state_of_game
255  // Has to be done before registering any events!
258 
259  if(gamestate().first_human_team_ != -1) {
260  gui_->set_team(gamestate().first_human_team_);
261  }
262  else if(is_observer()) {
263  // Find first team that is allowed to be observed.
264  // If not set here observer would be without fog until
265  // the first turn of observable side
266  for (const team& t : gamestate().board_.teams())
267  {
268  if (!t.get_disallow_observers())
269  {
270  gui_->set_team(t.side() - 1);
271  }
272  }
273  }
274 
275  init_managers();
277  //loadscreen_manager->reset();
279  gamestate().lua_kernel_->load_game(level);
280 
281  plugins_context_.reset(new plugins_context("Game"));
282  plugins_context_->set_callback("save_game", [this](const config& cfg) { save_game_auto(cfg["filename"]); }, true);
283  plugins_context_->set_callback("save_replay", [this](const config& cfg) { save_replay_auto(cfg["filename"]); }, true);
284  plugins_context_->set_callback("quit", throw_end_level(), false);
285  plugins_context_->set_accessor_string("scenario_name", [this](config) { return get_scenario_name(); });
286  });
287  //Do this after the loadingscreen, so that ita happens in the main thread.
288  gui_->join();
289 }
290 
291 void play_controller::reset_gamestate(const config& level, int replay_pos)
292 {
293  resources::gameboard = nullptr;
294  resources::gamedata = nullptr;
295  resources::tod_manager = nullptr;
296  resources::filter_con = nullptr;
297  resources::lua_kernel = nullptr;
298  resources::game_events = nullptr;
299  resources::tunnels = nullptr;
300  resources::undo_stack = nullptr;
301 
302  gui_->labels().set_team(nullptr);
303 
304  /* First destroy the old game state, then create the new one.
305  This is necessary to ensure that while the old AI manager is being destroyed,
306  all its member objects access the old manager instead of the new. */
307  gamestate_.reset();
308  gamestate_.reset(new game_state(level, *this, tdata_));
316 
317  gamestate_->init(level, *this);
320 
321  gui_->reset_reports(*gamestate().reports_);
322  gui_->change_display_context(&gamestate().board_);
323  saved_game_.get_replay().set_pos(replay_pos);
325  gamestate().lua_kernel_->load_game(level);
326 }
327 
329 {
330  LOG_NG << "initializing managers... " << (SDL_GetTicks() - ticks()) << std::endl;
334 
336  LOG_NG << "done initializing managers... " << (SDL_GetTicks() - ticks()) << std::endl;
337 }
338 
340 {
341  // Run initialization scripts, even if loading from a snapshot.
342  gamestate().gamedata_.get_variable("turn_number") = int(turn());
343  pump().fire("preload");
344 }
345 
347 {
348  // pre-start events must be executed before any GUI operation,
349  // as those may cause the display to be refreshed.
350  update_locker lock_display(gui_->video());
352 
353  // Fire these right before prestart events, to catch only the units sides
354  // have started with.
355  for (const unit& u : gamestate().board_.units()) {
356  pump().fire("unit_placed", map_location(u.get_location()));
357  }
358 
359  pump().fire("prestart");
360  // prestart event may modify start turn with WML, reflect any changes.
361  gamestate().gamedata_.get_variable("turn_number") = int(turn());
362 }
363 
365 {
367  pump().fire("start");
368  // start event may modify start turn with WML, reflect any changes.
369  gamestate().gamedata_.get_variable("turn_number") = int(turn());
371  // prestart and start events may modify the initial gold amount,
372  // reflect any changes.
373  for (team& tm : gamestate().board_.teams_)
374  {
375  tm.set_start_gold(tm.gold());
376  }
377  gamestate_->init_side_done() = false;
379 }
380 
382 {
383  gui_->begin_game();
384  gui_->update_tod();
385 }
386 
388 {
390 
391  // If we are observers we move to watch next team if it is allowed
392  if ((is_observer() && !current_team().get_disallow_observers())
393  || (current_team().is_local_human() && !this->is_replay()))
394  {
396  }
397 
398  gui_->set_playing_team(size_t(current_side() - 1));
399 
401 }
402 
404 {
405  //
406  // We do side init only if not done yet for a local side when we are not replaying.
407  // For all other sides it is recorded in replay and replay handler has to handle
408  // calling do_init_side() functions.
409  //
410  if (gamestate_->init_side_done()) {
411  // We already executed do_init_side this can for example happe if we reload a game,
412  // but also if we changed control of a side during it's turn
413  return;
414  }
415  if (!current_team().is_local()) {
416  // We are in a mp game and execute do_init_side as soon as we receive [init_side] from the current player
417  // (see replay.cpp)
418  return;
419  }
420 
421  if (is_replay()) {
422  // We are in a replay and execute do_init_side as soon as we reach the next [init_side] in the replay data
423  // (see replay.cpp)
424  return;
425  }
426 
428  do_init_side();
429 }
430 
432 {
433  set_scontext_synced sync;
434  log_scope("player turn");
435  // In case we might end up calling sync:network during the side turn events,
436  // and we don't want do_init_side to be called when a player drops.
437  gamestate_->init_side_done() = true;
438  init_side_done_now_ = true;
439 
440  const std::string turn_num = std::to_string(turn());
441  const std::string side_num = std::to_string(current_side());
442 
443  gamestate().gamedata_.get_variable("side_number") = current_side();
444 
445  // We might have skipped some sides because they were empty so it is not enough to check for side_num==1
446  if(!gamestate().tod_manager_.has_turn_event_fired())
447  {
448  pump().fire("turn_" + turn_num);
449  pump().fire("new_turn");
451  }
452 
453  pump().fire("side_turn");
454  pump().fire("side_" + side_num + "_turn");
455  pump().fire("side_turn_" + turn_num);
456  pump().fire("side_" + side_num + "_turn_" + turn_num);
457 
458  // We want to work out if units for this player should get healed,
459  // and the player should get income now.
460  // Healing/income happen if it's not the first turn of processing,
461  // or if we are loading a game.
462  if (turn() > 1) {
465 
466  // If the expense is less than the number of villages owned
467  // times the village support capacity,
468  // then we don't have to pay anything at all
469  int expense = gamestate().board_.side_upkeep(current_side()) -
470  current_team().support();
471  if(expense > 0) {
472  current_team().spend_gold(expense);
473  }
474 
476  }
477 
478  // Prepare the undo stack.
480 
481  pump().fire("turn_refresh");
482  pump().fire("side_" + side_num + "_turn_refresh");
483  pump().fire("turn_" + turn_num + "_refresh");
484  pump().fire("side_" + side_num + "_turn_" + turn_num + "_refresh");
485 
486  // Make sure vision is accurate.
488  init_side_end();
489  check_victory();
490  sync.do_final_checkup();
491 }
492 
494 {
496 
497  if (current_side() == 1 || !init_side_done_now_)
499 
500  if (!is_skipping_replay()){
501  gui_->invalidate_all();
502  }
503 
504  if (!is_skipping_replay() && current_team().get_scroll_to_leader() && !map_start_.valid()){
505  gui_->scroll_to_leader(current_side(), game_display::ONSCREEN,false);
506  }
508  whiteboard_manager_->on_init_side();
509 }
510 
512 {
513  config cfg = level_;
514 
515  cfg["replay_pos"] = saved_game_.get_replay().get_pos();
516  gamestate().write(cfg);
517 
518  gui_->write(cfg.add_child("display"));
519 
520  //Write the soundsources.
521  soundsources_manager_->write_sourcespecs(cfg);
522 
523  gui_->labels().write(cfg);
525 
526  return cfg;
527 }
528 
530 {
531  whiteboard_manager_->on_finish_side_turn(current_side());
532 
533  { //Block for set_scontext_synced
534  set_scontext_synced sync(1);
535  // Ending the turn commits all moves.
536  undo_stack().clear();
538  const std::string turn_num = std::to_string(turn());
539  const std::string side_num = std::to_string(current_side());
540 
541  // Clear shroud, in case units had been slowed for the turn.
543 
544  pump().fire("side_turn_end");
545  pump().fire("side_"+ side_num + "_turn_end");
546  pump().fire("side_turn_" + turn_num + "_end");
547  pump().fire("side_" + side_num + "_turn_" + turn_num + "_end");
548  // This is where we refog, after all of a side's events are done.
550  check_victory();
551  sync.do_final_checkup();
552  }
553 
556  gamestate_->init_side_done() = false;
557 }
558 
560 {
561  set_scontext_synced sync(2);
562  const std::string turn_num = std::to_string(turn());
563  pump().fire("turn_end");
564  pump().fire("turn_" + turn_num + "_end");
565  sync.do_final_checkup();
566 }
567 
569 {
570  // If we aren't using fog/shroud, this is easy :)
571  if(current_team().uses_fog() == false && current_team().uses_shroud() == false)
572  return true;
573 
574  // See if any enemies are visible
575  for (const unit& u : gamestate().board_.units()) {
576  if (current_team().is_enemy(u.side()) && !gui_->fogged(u.get_location())) {
577  return true;
578  }
579  }
580 
581  return false;
582 }
583 
585 {
586  if(menu_handler_.get_textbox().active() == false) {
587  return;
588  }
589 
590  const std::string str = menu_handler_.get_textbox().box()->text();
591  const unsigned int team_num = current_side();
592  events::mouse_handler& mousehandler = mouse_handler_;
593 
594  switch(menu_handler_.get_textbox().mode()) {
595  case gui::TEXTBOX_SEARCH:
598  break;
601  menu_handler_.get_textbox().close(*gui_); //need to close that one after executing do_speak() !
602  break;
606  break;
607  case gui::TEXTBOX_AI:
609  menu_handler_.do_ai_formula(str, team_num, mousehandler);
610  break;
611  default:
613  ERR_DP << "unknown textbox mode" << std::endl;
614  }
615 }
616 
618 {
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, gui_->get_disp_context())))
629  dictionary.insert(u.name());
630  }
631  //TODO List map labels
632  break;
633  }
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  }
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 
678 {
679  assert(gamestate().board_.has_team(current_side()));
681 }
682 
684 {
685  assert(gamestate().board_.has_team(current_side()));
687 }
688 
689 /// @returns: the number n in [min, min+mod ) so that (n - num) is a multiple of mod.
690 static int modulo(int num, int mod, int min)
691 {
692  assert(mod > 0);
693  int n = (num - min) % mod;
694  if (n < 0)
695  n += mod;
696  //n is now in [0, mod)
697  n = n + min;
698  return n;
699  // the following properties are easy to verify:
700  // 1) For all m: modulo(num, mod, min) == modulo(num + mod*m, mod, min)
701  // 2) For all 0 <= m < mod: modulo(min + m, mod, min) == min + m
702 }
703 
704 bool play_controller::is_team_visible(int team_num, bool observer) const
705 {
706  const team& t = gamestate().board_.get_team(team_num);
707  if(observer) {
708  return !t.get_disallow_observers() && !t.is_empty();
709  }
710  else {
711  return t.is_local_human() && !t.is_idle();
712  }
713 }
714 
716 {
717  assert(current_side() <= 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 
756 {
757  return !menu_handler_.get_textbox().active();
758 }
759 
761 {
762  if(event.key.keysym.sym == SDLK_ESCAPE) {
764  } else if(event.key.keysym.sym == SDLK_TAB) {
765  tab();
766  } else if(event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_KP_ENTER) {
767  enter_textbox();
768  }
769 }
770 
771 void play_controller::process_keydown_event(const SDL_Event& event)
772 {
773  if (event.key.keysym.sym == SDLK_TAB) {
774  whiteboard_manager_->set_invert_behavior(true);
775  }
776 }
777 
778 void play_controller::process_keyup_event(const SDL_Event& event)
779 {
780  // If the user has pressed 1 through 9, we want to show
781  // how far the unit can move in that many turns
782  if(event.key.keysym.sym >= '1' && event.key.keysym.sym <= '9') {
783  const int new_path_turns = (event.type == SDL_KEYDOWN) ?
784  event.key.keysym.sym - '1' : 0;
785 
786  if(new_path_turns != mouse_handler_.get_path_turns()) {
787  mouse_handler_.set_path_turns(new_path_turns);
788 
790 
791  if(u.valid()) {
792  // if it's not the unit's turn, we reset its moves
793  unit_movement_resetter move_reset(*u, u->side() != current_side());
794 
796  true, gamestate().board_.teams_[gui_->viewing_team()],
798 
799  gui_->highlight_reach(mouse_handler_.current_paths());
800  } else {
802  }
803 
804  }
805  } else if (event.key.keysym.sym == SDLK_TAB) {
806  CKey keys;
807  if (!keys[SDLK_TAB]) {
808  whiteboard_manager_->set_invert_behavior(false);
809  }
810  }
811 }
812 
814  assert(replay_);
815  return *replay_.get();
816 }
817 
819 {
822  scoped_savegame_snapshot snapshot(*this);
825  } else {
827  }
828 }
829 
831 {
834 
835  scoped_savegame_snapshot snapshot(*this);
837  save.save_game_automatic(false, filename);
838  }
839 }
840 
842 {
847  } else {
849  }
850 }
851 
853 {
857  save.save_game_automatic(false, filename);
858  }
859 }
860 
862 {
866  } else {
868  }
869 }
870 
872 {
874  load.load_game_ingame();
875 }
876 
878 {
880  undo_stack().undo();
881 }
882 
884 {
886  undo_stack().redo();
887 }
888 
890 {
892 }
893 
895 {
897 }
898 
899 const std::string& play_controller::select_music(bool victory) const
900 {
901  const std::vector<std::string>& music_list = victory
902  ? (gamestate_->get_game_data()->get_victory_music().empty() ? game_config::default_victory_music : gamestate_->get_game_data()->get_victory_music())
903  : (gamestate_->get_game_data()->get_defeat_music().empty() ? game_config::default_defeat_music : gamestate_->get_game_data()->get_defeat_music());
904 
905  if(music_list.empty()) {
906  // Since this function returns a reference, we can't return a temporary empty string.
907  static const std::string empty_str = "";
908  return empty_str;
909  }
910 
911  return music_list[randomness::rng::default_instance().get_random_int(0, music_list.size()-1)];
912 }
913 
915 {
916  if(linger_)
917  {
918  return;
919  }
920 
921  if (is_regular_game_end()) {
922  return;
923  }
924  bool continue_level, found_player, found_network_player, invalidate_all;
925  std::set<unsigned> not_defeated;
926 
927  gamestate().board_.check_victory(continue_level, found_player, found_network_player, invalidate_all, not_defeated, remove_from_carryover_on_defeat_);
928 
929  if (invalidate_all) {
930  gui_->invalidate_all();
931  }
932 
933  if (continue_level) {
934  return ;
935  }
936 
937  if (found_player || found_network_player) {
938  pump().fire("enemies_defeated");
939  if (is_regular_game_end()) {
940  return;
941  }
942  }
943 
944  DBG_EE << "victory_when_enemies_defeated: " << victory_when_enemies_defeated_ << std::endl;
945  DBG_EE << "found_player: " << found_player << std::endl;
946  DBG_EE << "found_network_player: " << found_network_player << std::endl;
947 
948  if (!victory_when_enemies_defeated_ && (found_player || found_network_player)) {
949  // This level has asked not to be ended by this condition.
950  return;
951  }
952 
953  if (gui_->video().non_interactive()) {
954  LOG_AIT << "winner: ";
955  for (unsigned l : not_defeated) {
957  if (ai.empty()) ai = "default ai";
958  LOG_AIT << l << " (using " << ai << ") ";
959  }
960  LOG_AIT << std::endl;
961  ai_testing::log_victory(not_defeated);
962  }
963 
964  DBG_EE << "throwing end level exception..." << std::endl;
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 
973 {
974  if (gui_->video().non_interactive()) {
975  throw game::game_error(msg);
976  }
978 
979  std::stringstream message;
980  message << _("The game is out of sync. It might not make much sense to continue. Do you want to save your game?");
981  message << "\n\n" << _("Error details:") << "\n\n" << msg;
982 
983  scoped_savegame_snapshot snapshot(*this);
985  save.save_game_interactive(message.str(), savegame::savegame::YES_NO); // can throw quit_game_exception
986 }
987 
988 void play_controller::update_gui_to_player(const int team_index, const bool observe)
989 {
990  gui_->set_team(team_index, observe);
991  gui_->recalculate_minimap();
992  gui_->invalidate_all();
993 }
994 
996 {
997  scoped_savegame_snapshot snapshot(*this);
1000 }
1001 
1003 {
1004  scoped_savegame_snapshot snapshot(*this);
1006  save.save_game_automatic(true, filename);
1007 }
1008 
1010 {
1011  //note: this writes to level_ if this is not a replay.
1013 }
1014 
1016 {
1017  return gamestate().events_manager_->pump();
1018 }
1019 
1021 {
1022  return ticks_;
1023 }
1024 
1026 {
1027  return soundsources_manager_.get();
1028 }
1029 
1031 {
1032  return plugins_context_.get();
1033 }
1034 
1036 {
1037  return hotkey_handler_.get();
1038 }
1039 
1041 {
1042  if(linger_ || !gamestate_->init_side_done() || gamestate().gamedata_.phase() != game_data::PLAY) {
1043  return true;
1044  }
1045  const team& t = current_team();
1046  return !t.is_local_human() || !t.is_proxy_human();
1047 }
1048 
1050 {
1052  return;
1053  }
1054  try
1055  {
1056  play_slice();
1057  }
1058  catch(const return_to_play_side_exception&)
1059  {
1060  assert(should_return_to_play_side());
1061  }
1062 }
1063 
1065 {
1066  fire_preload();
1067 
1068  if(!gamestate().start_event_fired_)
1069  {
1070  gamestate().start_event_fired_ = true;
1074 
1075  set_scontext_synced sync;
1076 
1077  fire_prestart();
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  if (is_regular_game_end()) {
1091  return;
1092  }
1093  sync.do_final_checkup();
1094  gui_->recalculate_minimap();
1095  // Initialize countdown clock.
1096  for (const team& t : gamestate().board_.teams())
1097  {
1099  t.set_countdown_time(1000 * saved_game_.mp_settings().mp_countdown_init_time);
1100  }
1101  }
1102  }
1103  else
1104  {
1105  init_gui();
1107  gui_->recalculate_minimap();
1108  }
1109 }
1110 
1112 {
1113  const team& viewing_team = get_teams_const()[gui_->viewing_team()];
1114  return gui_->viewing_team() == gui_->playing_team() && !events::commands_disabled && viewing_team.is_local_human() && !is_lingering() && !is_browsing();
1115 }
1116 
1117 std::set<std::string> play_controller::all_players() const
1118 {
1119  std::set<std::string> res = gui_->observers();
1120  for (const team& t : get_teams_const())
1121  {
1122  if (t.is_human()) {
1123  res.insert(t.current_player());
1124  }
1125  }
1126  return res;
1127 }
1128 
1130 {
1131  //check for team-specific items in the scenario
1132  gui_->parse_team_overlays();
1133  do {
1135  {
1136  save_blocker blocker;
1138  if(is_regular_game_end()) {
1139  return;
1140  }
1141  }
1142  // This flag can be set by derived classes (in overridden functions).
1143  player_type_changed_ = false;
1144 
1145  statistics::reset_turn_stats(gamestate().board_.get_team(current_side()).save_id());
1146 
1147  play_side_impl();
1148 
1149  if(is_regular_game_end()) {
1150  return;
1151  }
1152 
1153  } while (player_type_changed_);
1154  // Keep looping if the type of a team (human/ai/networked)
1155  // has changed mid-turn
1156  sync_end_turn();
1157 }
1158 
1160 {
1161  whiteboard_manager_->on_gamestate_change();
1162  gui_->new_turn();
1163  gui_->invalidate_game_status();
1164 
1165  LOG_NG << "turn: " << turn() << "\n";
1166 
1167  if(gui_->video().non_interactive()) {
1168  LOG_AIT << "Turn " << turn() << ":" << std::endl;
1169  }
1170 
1171  for (; gamestate_->player_number_ <= int(gamestate().board_.teams().size()); ++gamestate_->player_number_)
1172  {
1173  // If a side is empty skip over it.
1174  if (current_team().is_empty()) {
1175  continue;
1176  }
1177  init_side_begin();
1178  if(gamestate_->init_side_done()) {
1179  // This is the case in a reloaded game where the side was initialized before saving the game.
1180  init_side_end();
1181  }
1182 
1184  play_side();
1185  if(is_regular_game_end()) {
1186  return;
1187  }
1188  finish_side_turn();
1189  if(is_regular_game_end()) {
1190  return;
1191  }
1192  if(gui_->video().non_interactive()) {
1193  LOG_AIT << " Player " << current_side() << ": " <<
1194  current_team().villages().size() << " Villages" <<
1195  std::endl;
1197  }
1198  }
1199  // If the loop exits due to the last team having been processed.
1200  gamestate_->player_number_ = gamestate().board_.teams().size();
1201 
1202  finish_turn();
1203 
1204  // Time has run out
1205  check_time_over();
1206 }
1207 
1209 {
1210  const bool time_left = gamestate().tod_manager_.next_turn(&gamestate().gamedata_);
1211 
1212  if(!time_left) {
1213  LOG_NG << "firing time over event...\n";
1215  pump().fire("time_over");
1216  LOG_NG << "done firing time over event...\n";
1217  // If turns are added while handling 'time over' event.
1218  if (gamestate().tod_manager_.is_time_left()) {
1219  return;
1220  }
1221 
1222  if(gui_->video().non_interactive()) {
1223  LOG_AIT << "time over (draw)\n";
1225  }
1226 
1227  check_victory();
1228  if (is_regular_game_end()) {
1229  return;
1230  }
1231  end_level_data e;
1232  e.proceed_to_next_level = false;
1233  e.is_victory = false;
1234  set_end_level_data(e);
1235  }
1236 }
1237 
1239  : controller_(controller)
1240 {
1242 }
1243 
1245 {
1246  controller_.saved_game_.remove_snapshot();
1247 }
1248 
1250 {
1251  const team& t = gamestate().board_.teams()[gui_->viewing_team()];
1252  static const std::string no_objectives(_("No objectives available"));
1253  std::string objectives = utils::interpolate_variables_into_string(t.objectives(), *gamestate_->get_game_data());
1254  gui2::show_transient_message(get_scenario_name(), (objectives.empty() ? no_objectives : objectives), "", true);
1256 }
1257 
1259 {
1261  const std::shared_ptr<gui::button> skip_animation_button = get_display().find_action_button("skip-animation");
1262  if (skip_animation_button) {
1263  skip_animation_button->set_check(skip_replay_);
1264  }
1265 }
static const config & get_theme(const config &game_config, std::string theme_name)
std::shared_ptr< gui::button > find_action_button(const std::string &id)
Retrieves a pointer to a theme UI button.
Definition: display.cpp:844
void clear()
Clears the stack of undoable (and redoable) actions.
Definition: undo.cpp:218
void operator()(const config &) const
play_controller * controller
Definition: resources.cpp:21
std::vector< std::string > default_victory_music
void do_search(const std::string &new_search)
bool is_local() const
Definition: team.hpp:258
void set_current_paths(const pathfind::paths &new_paths)
int autosavemax()
Definition: game.cpp:810
std::unique_ptr< soundsource::manager > soundsources_manager_
static void log_turn_start(unsigned int side)
Definition: testing.cpp:34
static void log_turn_end(unsigned int side)
Definition: testing.cpp:39
void set_preference_display_settings()
Definition: display.cpp:46
::tod_manager * tod_manager
Definition: resources.cpp:29
PHASE phase() const
Definition: game_data.hpp:76
bool is_team_visible(int team_num, bool observer) const
static lg::log_domain log_engine_enemies("engine/enemies")
std::vector< char_t > string
int ticks() const
hotkey::command_executor * get_hotkey_command_executor() override
Optionally get a command executor to handle context menu events.
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 ...
map_location last_selected
the last location where a select event fired.
Definition: game_data.hpp:83
int get_path_turns() const
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)
const ter_data_cache & tdata_
This class represents a single unit of a specific type.
Definition: unit.hpp:100
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
bool can_redo() const
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)
const std::string & select_music(bool victory) const
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_
virtual void process_oos(const std::string &msg) const
Asks the user whether to continue on an OOS error.
std::vector< game_tip > load(const config &cfg)
Loads the tips from a config.
Definition: tips.cpp:35
Add a special kind of assert to validate whether the input from WML doesn't contain any problems that...
std::unique_ptr< replay > replay_
bool get_disallow_observers() const
Definition: team.hpp:336
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
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:117
Class for autosaves.
Definition: savegame.hpp:268
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:80
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:191
bool is_enemy(int n) const
Definition: team.hpp:241
void process_keydown_event(const SDL_Event &event) override
Process keydown (always).
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, const bool restore_background)
Shows a transient message to the user.
void do_command(const std::string &str)
static void progress(loading_stage stage=loading_stage::none)
virtual void update_viewing_player()=0
const t_string & objectives() const
Definition: team.hpp:238
gui::floating_textbox & get_textbox()
void new_turn()
Definition: team.hpp:203
void set_gui(game_display *gui)
Definition: menu_events.hpp:52
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:346
bool can_use_synced_wml_menu() const
static lg::log_domain log_aitesting("aitesting")
Gather statistics important for AI testing and output them.
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:260
static lg::log_domain log_engine("engine")
Class for "normal" midgame saves.
Definition: savegame.hpp:241
Replay control code.
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
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:474
bool is_skipping_replay() const
void append_children(const config &cfg)
Adds children from cfg.
Definition: config.cpp:241
static void log_draw()
Definition: testing.cpp:74
bool save_game_interactive(const std::string &message, DIALOG_TYPE dialog_type)
Save a game interactively through the savegame dialog.
Definition: savegame.cpp:351
static lg::log_domain log_display("display")
void write_music_play_list(config &snapshot)
Definition: sound.cpp:812
std::shared_ptr< wb::manager > get_whiteboard() const
#define DBG_EE
bool is_proxy_human() const
Definition: team.hpp:280
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_
bool can_undo() const
True if there are actions that can be undone.
Definition: undo.hpp:92
void reset_turn_stats(const std::string &save_id)
Definition: statistics.cpp:546
void process_focus_keydown_event(const SDL_Event &event) override
Process keydown (only when the general map display does not have focus).
map_location get_selected_hex() const
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
bool have_keyboard_focus() override
Derived classes should override this to return false when arrow keys should not scroll the map...
events::menu_handler menu_handler_
void play_sound(const std::string &files, channel_group group, unsigned int repeats)
Definition: sound.cpp:989
bool enemies_visible() 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().
std::string theme() const
int side_upkeep(int side_num) const
void do_consolesave(const std::string &filename)
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 'side' (in game nomenclature).
Definition: team.hpp:44
A small explanation about what'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:214
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)
int current_side() const
Returns the number of the side whose turn it is.
bool add_start_if_not_there_yet()
Definition: replay.cpp:651
bool valid() const
Definition: location.hpp:74
bool is_regular_game_end() const
void update_savegame_snapshot() const
void set_path_turns(const int path_turns)
virtual void check_time_over()
Implements a quit confirmation dialog.
filter_context * filter_con
Definition: resources.cpp:23
size_t turn() const
bool ignore_replay_errors
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
void init(const config &level)
void recalculate_fog(int side)
Function that recalculates the fog of war.
Definition: vision.cpp:705
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...
tod_manager tod_manager_
Definition: game_state.hpp:47
bool is_local_human() const
Definition: team.hpp:264
Object which temporarily resets a unit's movement.
Definition: unit.hpp:1723
bool load_game_ingame()
Load a game without providing any information.
Definition: savegame.cpp:117
std::unique_ptr< pathfind::manager > pathfind_manager_
Definition: game_state.hpp:48
Managing the AIs lifecycle - headers.
replay * recorder
Definition: resources.cpp:28
bool is_lingering() const
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:207
bool is_observer() const
void reset_objectives_changed() const
Definition: team.hpp:236
std::set< std::string > all_players() const
static const map_location & null_location()
Definition: location.hpp:224
bool can_redo() const
True if there are actions that can be redone.
Definition: undo.hpp:94
An object which will lock the display for the duration of its lifetime.
Definition: video.hpp:279
Error used for any general game error, e.g.
Definition: game_errors.hpp:46
const std::set< map_location > & villages() const
Definition: team.hpp:183
game_events::manager * game_events
Definition: resources.cpp:24
bool is_browsing() const override
void deactivate_all_scopes()
void update_gui_to_player(const int team_index, const bool observe=false)
Changes the UI for this client to the passed side index.
int get_ticks() const
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()
Various functions related to the creation of units (recruits, recalls, and placed units)...
void set_end_level_data(const end_level_data &data)
void end_turn(int pnum)
Definition: game_board.cpp:72
void write(config &cfg) const
Definition: game_state.cpp:235
soundsource::manager * soundsources
Definition: resources.cpp:27
void redo()
Redoes the top action on the redo stack.
Definition: undo.cpp:396
map_location map_start_
Game configuration data as global variables.
Definition: build_info.cpp:53
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:450
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:722
const config & game_config_
void reset_gamestate(const config &level, int replay_pos)
std::shared_ptr< wb::manager > whiteboard_manager_
Define the game's event mechanism.
void encounter_all_content(const game_board &gameboard_)
Definition: game.cpp:1024
#define log_scope(description)
Definition: log.hpp:186
size_t i
Definition: function.cpp:933
void tab(const std::set< std::string > &dictionary)
bool proceed_to_next_level
whether to proceed to the next scenario, equals is_victory in sp.
const std::vector< team > & get_teams_const() const
void turn_event_fired()
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
const std::unique_ptr< gui::textbox > & box() const
int support() const
Calculate total support capacity, based on support_per_village.
Definition: team.hpp:202
actions::undo_list & undo_stack()
std::map< std::string, std::string > get_acquaintances_nice(const std::string &filter)
Definition: game.cpp:231
static void save(LexState *ls, int c)
Definition: llex.cpp:57
static lg::log_domain log_enginerefac("enginerefac")
std::string observer
Class for replay saves (either manually or automatically).
Definition: savegame.hpp:255
config & add_child(config_key_type key)
Definition: config.cpp:473
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:491
compression::format save_compression_format()
Definition: game.cpp:873
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:754
static bool try_block()
static void display(std::function< void()> f)
void set_game_display(game_display *)
Definition: game_state.cpp:230
events::mouse_handler & get_mouse_handler_base() override
Get a reference to a mouse handler member a derived class uses.
n_unit::id_manager & unit_id_manager()
Definition: game_board.hpp:86
game_state & gamestate()
double t
Definition: astarsearch.cpp:64
bool is_empty() const
Definition: team.hpp:256
Various functions that implement healing of units (when a side turn starts).
void show_objectives() const
game_classification & classification()
Definition: saved_game.hpp:55
game_board board_
Definition: game_state.hpp:46
config to_config() const
Builds the snapshot config from members and their respective configs.
Various functions that implement the undoing (and redoing) of in-game commands.
bool is_idle() const
Definition: team.hpp:282
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:579
Object which contains all the possible locations a unit can move to, with associated best routes to t...
Definition: pathfind.hpp:70
game_lua_kernel * lua_kernel
Definition: resources.cpp:25
config * get_next_action()
Definition: replay.cpp:611
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
void reset_fake()
Definition: id.cpp:53
t_string get_scenario_name() const
#define e
actions::undo_list * undo_stack
Definition: resources.cpp:32
bool valid() const
Definition: map.hpp:276
void set_side(int side_number)
bool next_turn(game_data *vars)
Function to move to the next turn.
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
game_classification & get_classification()
Class that keeps track of all the keys on the keyboard.
Definition: key.hpp:27
const pathfind::paths & current_paths() const
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()
static map_location::DIRECTION n
void close(game_display &gui)
pathfind::manager * tunnels
Definition: resources.cpp:31
static rng & default_instance()
Definition: random.cpp:70
virtual void check_objectives()=0
bool can_undo() const
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:335
int find_last_visible_team() const
returns 0 if no such team was found.
virtual void play_side_impl()
bool start_event_fired_
Definition: game_state.hpp:61
bool init_side_done_now_
Whether we did init sides in this session (false = we did init sides before we reloaded the game)...
virtual bool should_return_to_play_side() const
bool remove_from_carryover_on_defeat_
TEXTBOX_MODE mode() const
void do_ai_formula(const std::string &str, int side_num, mouse_handler &mousehandler)
#define LOG_NG
play_controller(const config &level, saved_game &state_of_game, const config &game_config, const ter_data_cache &tdata, bool skip_replay)
std::shared_ptr< terrain_type_data > ter_data_cache
virtual void sync_end_turn()