The Battle for Wesnoth  1.17.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/advancement.hpp"
24 #include "actions/create.hpp"
25 #include "actions/heal.hpp"
26 #include "actions/undo.hpp"
27 #include "actions/vision.hpp"
28 #include "ai/manager.hpp"
29 #include "ai/testing.hpp"
30 #include "display_chat_manager.hpp"
31 #include "floating_label.hpp"
32 #include "formula/string_utils.hpp"
33 #include "game_errors.hpp"
35 #include "game_events/pump.hpp"
36 #include "game_state.hpp"
37 #include "gettext.hpp"
39 #include "gui/dialogs/message.hpp" // for show_error_message
43 #include "hotkey/hotkey_item.hpp"
44 #include "log.hpp"
45 #include "map/label.hpp"
46 #include "pathfind/teleport.hpp"
48 #include "preferences/display.hpp"
49 #include "preferences/game.hpp"
50 #include "random.hpp"
51 #include "replay.hpp"
52 #include "reports.hpp"
53 #include "resources.hpp"
54 #include "save_blocker.hpp"
55 #include "save_index.hpp"
56 #include "saved_game.hpp"
57 #include "savegame.hpp"
60 #include "sound.hpp"
61 #include "soundsource.hpp"
62 #include "statistics.hpp"
63 #include "synced_context.hpp"
64 #include "tooltips.hpp"
65 #include "units/id.hpp"
66 #include "units/types.hpp"
67 #include "units/unit.hpp"
68 #include "utils/general.hpp"
69 #include "whiteboard/manager.hpp"
70 
71 #include <functional>
72 
73 static lg::log_domain log_aitesting("ai/testing");
74 #define LOG_AIT LOG_STREAM(info, log_aitesting)
75 // If necessary, this define can be replaced with `#define LOG_AIT std::cout` to restore previous behavior
76 
77 static lg::log_domain log_engine("engine");
78 #define LOG_NG LOG_STREAM(info, log_engine)
79 #define DBG_NG LOG_STREAM(debug, log_engine)
80 #define ERR_NG LOG_STREAM(err, log_engine)
81 
82 static lg::log_domain log_display("display");
83 #define ERR_DP LOG_STREAM(err, log_display)
84 
85 static lg::log_domain log_enginerefac("enginerefac");
86 #define LOG_RG LOG_STREAM(info, log_enginerefac)
87 
88 static lg::log_domain log_engine_enemies("engine/enemies");
89 #define DBG_EE LOG_STREAM(debug, log_engine_enemies)
90 
91 /**
92  * Copies [scenario] attributes/tags that are not otherwise stored in C++ structs/clases.
93  */
94 static void copy_persistent(const config& src, config& dst)
95 {
96  static const std::set<std::string> attrs {
97  "description",
98  "name",
99  "victory_when_enemies_defeated",
100  "remove_from_carryover_on_defeat",
101  "disallow_recall",
102  "experience_modifier",
103  "require_scenario",
104  "loaded_resources"
105  };
106 
107  static const std::set<std::string> tags {
108  "terrain_graphics",
109  "modify_unit_type",
110  "lua"
111  };
112 
113  for(const std::string& attr : attrs) {
114  dst[attr] = src[attr];
115  }
116 
117  for(const std::string& tag : tags) {
118  dst.append_children(src, tag);
119  }
120 }
121 
122 static void clear_resources()
123 {
124  resources::controller = nullptr;
125  resources::filter_con = nullptr;
126  resources::gameboard = nullptr;
127  resources::gamedata = nullptr;
128  resources::game_events = nullptr;
129  resources::lua_kernel = nullptr;
130  resources::persist = nullptr;
131  resources::soundsources = nullptr;
132  resources::tod_manager = nullptr;
133  resources::tunnels = nullptr;
134  resources::undo_stack = nullptr;
135  resources::recorder = nullptr;
136  resources::whiteboard.reset();
137  resources::classification = nullptr;
138 }
139 
140 play_controller::play_controller(const config& level, saved_game& state_of_game, bool skip_replay)
141  : controller_base()
142  , observer()
144  , ticks_(SDL_GetTicks())
145  , gamestate_()
146  , level_()
147  , saved_game_(state_of_game)
148  , tooltips_manager_()
149  , whiteboard_manager_()
150  , plugins_context_()
151  , labels_manager_(new font::floating_label_context())
152  , help_manager_(&game_config_)
153  , mouse_handler_(nullptr, *this)
154  , menu_handler_(nullptr, *this)
155  , hotkey_handler_(new hotkey_handler(*this, saved_game_))
156  , soundsources_manager_()
157  , persist_()
158  , gui_()
159  , xp_mod_(new unit_experience_accelerator(level["experience_modifier"].to_int(100)))
160  , statistics_context_(new statistics::scenario_context(level["name"]))
161  , replay_(new replay(state_of_game.get_replay()))
162  , skip_replay_(skip_replay)
163  , skip_story_(state_of_game.skip_story())
164  , linger_(false)
165  , init_side_done_now_(false)
166  , map_start_()
167  , victory_when_enemies_defeated_(level["victory_when_enemies_defeated"].to_bool(true))
168  , remove_from_carryover_on_defeat_(level["remove_from_carryover_on_defeat"].to_bool(true))
169  , victory_music_()
170  , defeat_music_()
171  , scope_()
172  , ignore_replay_errors_(false)
173  , player_type_changed_(false)
174 {
175  copy_persistent(level, level_);
176 
177  for(const config& modify_unit_type : level_.child_range("modify_unit_type")) {
178  unit_types.apply_scenario_fix(modify_unit_type);
179  }
180  resources::controller = this;
183 
185 
187 
191 
192  try {
193  init(level);
194  } catch(...) {
195  clear_resources();
196  throw;
197  }
198 }
199 
201 {
204  clear_resources();
205 }
206 
208 {
209  gui2::dialogs::loading_screen::display([this, &level]() {
211 
212  LOG_NG << "initializing game_state..." << (SDL_GetTicks() - ticks()) << std::endl;
213  gamestate_.reset(new game_state(level, *this));
214 
222 
223  gamestate_->ai_manager_.add_observer(this);
224  gamestate_->init(level, *this);
226 
227  LOG_NG << "initializing whiteboard..." << (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  gui_.reset(new game_display(gamestate().board_, whiteboard_manager_, *gamestate().reports_, theme_cfg, level));
243  map_start_ = map_location(level.child_or_empty("display").child_or_empty("location"));
244 
245  if(!gui_->video().faked()) {
247  gui_->get_theme().modify_label("time-icon", _("time left for current turn"));
248  } else {
249  gui_->get_theme().modify_label("time-icon", _("current local time"));
250  }
251  }
252 
254  mouse_handler_.set_gui(gui_.get());
255  menu_handler_.set_gui(gui_.get());
256 
257  LOG_NG << "done initializing display... " << (SDL_GetTicks() - ticks()) << std::endl;
258 
259  LOG_NG << "building gamestate to gui and whiteboard... " << (SDL_GetTicks() - ticks()) << std::endl;
260  // This *needs* to be created before the show_intro and show_map_scene
261  // as that functions use the manager state_of_game
262  // Has to be done before registering any events!
265 
266  if(gamestate().first_human_team_ != -1) {
267  gui_->set_team(gamestate().first_human_team_);
268  } else if(is_observer()) {
269  // Find first team that is allowed to be observed.
270  // If not set here observer would be without fog until
271  // the first turn of observable side
272  for(const team& t : get_teams()) {
273  if(!t.get_disallow_observers()) {
274  gui_->set_team(t.side() - 1);
275  }
276  }
277  }
278 
279  init_managers();
281  // loadscreen_manager->reset();
283  gamestate().lua_kernel_->load_game(level);
284 
285  plugins_context_.reset(new plugins_context("Game"));
286  plugins_context_->set_callback("save_game", [this](const config& cfg) { save_game_auto(cfg["filename"]); }, true);
287  plugins_context_->set_callback("save_replay", [this](const config& cfg) { save_replay_auto(cfg["filename"]); }, true);
288  plugins_context_->set_callback("quit", [](const config&) { throw_quit_game_exception(); }, false);
289  plugins_context_->set_accessor_string("scenario_name", [this](config) { return get_scenario_name(); });
290  });
291 
292  // Do this after the loadingscreen, so that ita happens in the main thread.
293  gui_->join();
294 }
295 
296 void play_controller::reset_gamestate(const config& level, int replay_pos)
297 {
298  resources::gameboard = nullptr;
299  resources::gamedata = nullptr;
300  resources::tod_manager = nullptr;
301  resources::filter_con = nullptr;
302  resources::lua_kernel = nullptr;
303  resources::game_events = nullptr;
304  resources::tunnels = nullptr;
305  resources::undo_stack = nullptr;
306 
307  gui_->labels().set_team(nullptr);
308 
309  /* First destroy the old game state, then create the new one.
310  This is necessary to ensure that while the old AI manager is being destroyed,
311  all its member objects access the old manager instead of the new. */
312  gamestate_.reset();
313  gamestate_.reset(new game_state(level, *this));
314 
322 
323  gamestate_->ai_manager_.add_observer(this);
324  gamestate_->init(level, *this);
327 
328  gui_->reset_reports(*gamestate().reports_);
329  gui_->change_display_context(&gamestate().board_);
330  saved_game_.get_replay().set_pos(replay_pos);
332  gamestate().lua_kernel_->load_game(level);
333 }
334 
336 {
337  LOG_NG << "initializing managers... " << (SDL_GetTicks() - ticks()) << std::endl;
341 
343  LOG_NG << "done initializing managers... " << (SDL_GetTicks() - ticks()) << std::endl;
344 }
345 
347 {
348  // Run initialization scripts, even if loading from a snapshot.
349  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
350  pump().fire("preload");
351 }
352 
354 {
355  // pre-start events must be executed before any GUI operation,
356  // as those may cause the display to be refreshed.
357  update_locker lock_display(gui_->video());
359 
360  // Fire these right before prestart events, to catch only the units sides
361  // have started with.
362  for(const unit& u : get_units()) {
363  pump().fire("unit_placed", map_location(u.get_location()));
364  }
365 
366  pump().fire("prestart");
367 
368  // prestart event may modify start turn with WML, reflect any changes.
369  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
370 }
371 
373 {
374  const config cfg("side", gui_->viewing_side());
375  gamestate().lua_kernel_->run_wml_action("show_objectives", vconfig(cfg),
376  game_events::queued_event("_from_interface", "", map_location(), map_location(), config()));
377 }
378 
380 {
382  pump().fire("start");
383 
384  skip_story_ = false; // Show [message]s from now on even with --campaign-skip-story
385 
386  // start event may modify start turn with WML, reflect any changes.
387  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
388 
391 
392  // prestart and start events may modify the initial gold amount,
393  // reflect any changes.
394  for(team& tm : get_teams()) {
395  tm.set_start_gold(tm.gold());
396  }
397 
398  gamestate_->init_side_done() = false;
400 }
401 
403 {
404  gui_->begin_game();
405  gui_->update_tod();
406 }
407 
409 {
411 
412  // If we are observers we move to watch next team if it is allowed
413  if((is_observer() && !current_team().get_disallow_observers()) || (current_team().is_local_human() && !is_replay())) {
415  }
416 
417  gui_->set_playing_team(std::size_t(current_side() - 1));
418 
420 }
421 
423 {
424  //
425  // We do side init only if not done yet for a local side when we are not replaying.
426  // For all other sides it is recorded in replay and replay handler has to handle
427  // calling do_init_side() functions.
428  //
429  if(gamestate_->init_side_done()) {
430  // We already executed do_init_side this can for example happe if we reload a game,
431  // but also if we changed control of a side during it's turn
432  return;
433  }
434 
435  if(!current_team().is_local()) {
436  // We are in a mp game and execute do_init_side as soon as we receive [init_side] from the current player
437  // (see replay.cpp)
438  return;
439  }
440 
441  if(is_replay()) {
442  // We are in a replay and execute do_init_side as soon as we reach the next [init_side] in the replay data
443  // (see replay.cpp)
444  return;
445  }
446 
447  if(current_team().is_idle()) {
448  // In this case it can happen that we just gave control of this side to another player so doing init_side
449  // could lead to errors since we no longer own this side from the servers perspective.
450  // (see playturn.cpp)
451  return;
452  }
453 
455  do_init_side();
456 }
457 
459 {
460  set_scontext_synced sync;
461  log_scope("player turn");
462  // In case we might end up calling sync:network during the side turn events,
463  // and we don't want do_init_side to be called when a player drops.
464  gamestate_->init_side_done() = true;
465  init_side_done_now_ = true;
466 
467  const std::string turn_num = std::to_string(turn());
468  const std::string side_num = std::to_string(current_side());
469 
470  gamestate().gamedata_.get_variable("side_number") = current_side();
471 
472  // We might have skipped some sides because they were empty so it is not enough to check for side_num==1
473  if(!gamestate().tod_manager_.has_turn_event_fired()) {
474  pump().fire("turn_" + turn_num);
475  pump().fire("new_turn");
477  }
478 
479  pump().fire("side_turn");
480  pump().fire("side_" + side_num + "_turn");
481  pump().fire("side_turn_" + turn_num);
482  pump().fire("side_" + side_num + "_turn_" + turn_num);
483 
484  // We want to work out if units for this player should get healed,
485  // and the player should get income now.
486  // Healing/income happen if it's not the first turn of processing,
487  // or if we are loading a game.
488  if(turn() > 1) {
491 
492  // If the expense is less than the number of villages owned
493  // times the village support capacity,
494  // then we don't have to pay anything at all
495  int expense = gamestate().board_.side_upkeep(current_side()) - current_team().support();
496  if(expense > 0) {
497  current_team().spend_gold(expense);
498  }
499  }
500 
501  if(do_healing()) {
503  }
504 
505  // Do healing on every side turn except the very first side turn.
506  // (1.14 and earlier did healing whenever turn >= 2.)
507  set_do_healing(true);
508 
509  // Set resting now after the healing has been done.
510  for(unit& patient : resources::gameboard->units()) {
511  if(patient.side() == current_side()) {
512  patient.set_resting(true);
513  }
514  }
515 
516  // Prepare the undo stack.
518 
519  pump().fire("turn_refresh");
520  pump().fire("side_" + side_num + "_turn_refresh");
521  pump().fire("turn_" + turn_num + "_refresh");
522  pump().fire("side_" + side_num + "_turn_" + turn_num + "_refresh");
523 
524  // Make sure vision is accurate.
526 
527  init_side_end();
528  check_victory();
529  sync.do_final_checkup();
530 }
531 
533 {
535 
536  if(current_side() == 1 || !init_side_done_now_) {
538  }
539 
540  if(!is_skipping_replay()) {
541  gui_->invalidate_all();
542  }
543 
544  if(!is_skipping_replay() && current_team().get_scroll_to_leader() && !map_start_.valid()) {
545  gui_->scroll_to_leader(current_side(), game_display::ONSCREEN, false);
546  }
547 
549  whiteboard_manager_->on_init_side();
550 }
551 
553 {
554  config cfg = level_;
555 
556  cfg["replay_pos"] = saved_game_.get_replay().get_pos();
557  gamestate().write(cfg);
558 
559  gui_->write(cfg.add_child("display"));
560 
561  // Write the soundsources.
562  soundsources_manager_->write_sourcespecs(cfg);
563 
564  gui_->labels().write(cfg);
566 
567  return cfg;
568 }
569 
571 {
572  whiteboard_manager_->on_finish_side_turn(current_side());
573 
574  { // Block for set_scontext_synced
575  set_scontext_synced sync(1);
576  // Ending the turn commits all moves.
577  undo_stack().clear();
579  const std::string turn_num = std::to_string(turn());
580  const std::string side_num = std::to_string(current_side());
581 
582  // Clear shroud, in case units had been slowed for the turn.
584 
585  pump().fire("side_turn_end");
586  pump().fire("side_" + side_num + "_turn_end");
587  pump().fire("side_turn_" + turn_num + "_end");
588  pump().fire("side_" + side_num + "_turn_" + turn_num + "_end");
589  // This is where we refog, after all of a side's events are done.
591  check_victory();
592  sync.do_final_checkup();
593  }
594 
596  gamestate_->init_side_done() = false;
597 }
598 
600 {
601  set_scontext_synced sync(2);
602  const std::string turn_num = std::to_string(turn());
603  pump().fire("turn_end");
604  pump().fire("turn_" + turn_num + "_end");
605  sync.do_final_checkup();
606 }
607 
609 {
610  // If we aren't using fog/shroud, this is easy :)
611  if(current_team().uses_fog() == false && current_team().uses_shroud() == false) {
612  return true;
613  }
614 
615  // See if any enemies are visible
616  for(const unit& u : get_units()) {
617  if(current_team().is_enemy(u.side()) && !gui_->fogged(u.get_location())) {
618  return true;
619  }
620  }
621 
622  return false;
623 }
624 
626 {
627  if(menu_handler_.get_textbox().active() == false) {
628  return;
629  }
630 
631  const std::string str = menu_handler_.get_textbox().box()->text();
632  const unsigned int team_num = current_side();
633  events::mouse_handler& mousehandler = mouse_handler_;
634 
635  switch(menu_handler_.get_textbox().mode()) {
636  case gui::TEXTBOX_SEARCH:
640  break;
643  menu_handler_.get_textbox().close(*gui_); // need to close that one after executing do_speak() !
644  break;
649  break;
650  case gui::TEXTBOX_AI:
653  menu_handler_.do_ai_formula(str, team_num, mousehandler);
654  break;
655  default:
657  ERR_DP << "unknown textbox mode" << std::endl;
658  }
659 }
660 
662 {
663  if(menu_handler_.get_textbox().active() == false) {
664  return;
665  }
666 
669  // Not handling messages to avoid spam
670  return;
671  }
672 
673  const std::string str = menu_handler_.get_textbox().box()->text();
674  const std::vector<std::string>& command_history = menu_handler_.get_textbox().command_history();
675 
676  auto prev = std::find(command_history.begin(), command_history.end(), str);
677 
678  if (prev != command_history.end())
679  {
680  if(up) {
681  if(prev != command_history.begin()) {
682  menu_handler_.get_textbox().box()->set_text(*--prev);
683  }
684  } else {
685  if(++prev != command_history.end()) {
686  menu_handler_.get_textbox().box()->set_text(*prev);
687  } else {
688  menu_handler_.get_textbox().box()->set_text("");
689  }
690  }
691  } else if (up) {
692  if(command_history.size() > 0) {
693  menu_handler_.get_textbox().box()->set_text(*--prev);
694  }
695  if(!str.empty()) {
697  }
698  }
699 }
700 
702 {
704 
705  std::set<std::string> dictionary;
706  switch(mode) {
707  case gui::TEXTBOX_SEARCH: {
708  for(const unit& u : get_units()) {
709  const map_location& loc = u.get_location();
710  if(!gui_->fogged(loc) && !(get_teams()[gui_->viewing_team()].is_enemy(u.side()) && u.invisible(loc)))
711  dictionary.insert(u.name());
712  }
713  // TODO List map labels
714  break;
715  }
716  case gui::TEXTBOX_COMMAND: {
717  std::vector<std::string> commands = menu_handler_.get_commands_list();
718  dictionary.insert(commands.begin(), commands.end());
719  [[fallthrough]]; // we also want player names from the next case
720  }
721  case gui::TEXTBOX_MESSAGE: {
722  for(const team& t : get_teams()) {
723  if(!t.is_empty())
724  dictionary.insert(t.current_player());
725  }
726 
727  // Add observers
728  for(const std::string& o : gui_->observers()) {
729  dictionary.insert(o);
730  }
731 
732  // Add nicks who whispered you
733  for(const std::string& w : gui_->get_chat_manager().whisperers()) {
734  dictionary.insert(w);
735  }
736 
737  // Add nicks from friendlist
738  const std::map<std::string, std::string> friends = preferences::get_acquaintances_nice("friend");
739 
740  for(std::map<std::string, std::string>::const_iterator iter = friends.begin(); iter != friends.end(); ++iter) {
741  dictionary.insert((*iter).first);
742  }
743 
744  // Exclude own nick from tab-completion.
745  // NOTE why ?
746  dictionary.erase(preferences::login());
747  break;
748  }
749 
750  default:
751  ERR_DP << "unknown textbox mode" << std::endl;
752  } // switch(mode)
753 
754  menu_handler_.get_textbox().tab(dictionary);
755 }
756 
758 {
759  if(get_teams().size() == 0) {
760  throw game::game_error("The scenario has no sides defined");
761  }
762 
763  assert(gamestate().board_.has_team(current_side()));
765 }
766 
768 {
769  if(get_teams().size() == 0) {
770  throw game::game_error("The scenario has no sides defined");
771  }
772 
773  assert(gamestate().board_.has_team(current_side()));
775 }
776 
777 bool play_controller::is_team_visible(int team_num, bool observer) const
778 {
779  const team& t = gamestate().board_.get_team(team_num);
780  if(observer) {
781  return !t.get_disallow_observers() && !t.is_empty();
782  } else {
783  return t.is_local_human() && !t.is_idle();
784  }
785 }
786 
788 {
789  assert(current_side() <= static_cast<int>(get_teams().size()));
790  const int num_teams = get_teams().size();
791  const bool observer = is_observer();
792 
793  for(int i = 0; i < num_teams; i++) {
794  const int team_num = modulo(current_side() - i, num_teams, 1);
795  if(is_team_visible(team_num, observer)) {
796  return team_num;
797  }
798  }
799 
800  return 0;
801 }
802 
804 {
805  return mouse_handler_;
806 }
807 
808 std::shared_ptr<wb::manager> play_controller::get_whiteboard() const
809 {
810  return whiteboard_manager_;
811 }
812 
814 {
815  return saved_game_.mp_settings();
816 }
817 
819 {
820  return saved_game_.classification();
821 }
822 
824 {
825  return *gui_;
826 }
827 
829 {
830  return !menu_handler_.get_textbox().active();
831 }
832 
834 {
835  if(event.key.keysym.sym == SDLK_ESCAPE) {
837  } else if(event.key.keysym.sym == SDLK_TAB) {
838  tab();
839  } else if(event.key.keysym.sym == SDLK_UP) {
841  } else if(event.key.keysym.sym == SDLK_DOWN) {
843  } else if(event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_KP_ENTER) {
844  enter_textbox();
845  }
846 }
847 
848 void play_controller::process_keydown_event(const SDL_Event& event)
849 {
850  if(event.key.keysym.sym == SDLK_TAB) {
851  whiteboard_manager_->set_invert_behavior(true);
852  }
853 }
854 
855 void play_controller::process_keyup_event(const SDL_Event& event)
856 {
857  // If the user has pressed 1 through 9, we want to show
858  // how far the unit can move in that many turns
859  if(event.key.keysym.sym >= '1' && event.key.keysym.sym <= '9') {
860  const int new_path_turns = (event.type == SDL_KEYDOWN) ? event.key.keysym.sym - '1' : 0;
861 
862  if(new_path_turns != mouse_handler_.get_path_turns()) {
863  mouse_handler_.set_path_turns(new_path_turns);
864 
866 
867  if(u.valid()) {
868  // if it's not the unit's turn, we reset its moves
869  unit_movement_resetter move_reset(*u, u->side() != current_side());
870 
872  *u, false, true, get_teams()[gui_->viewing_team()], mouse_handler_.get_path_turns()));
873 
874  gui_->highlight_reach(mouse_handler_.current_paths());
875  } else {
877  }
878  }
879  } else if(event.key.keysym.sym == SDLK_TAB) {
880  CKey keys;
881  if(!keys[SDLK_TAB]) {
882  whiteboard_manager_->set_invert_behavior(false);
883  }
884  }
885 }
886 
888 {
889  assert(replay_);
890  return *replay_.get();
891 }
892 
894 {
896  // Saving while an event is running isn't supported
897  // because it may lead to expired event handlers being saved.
898  assert(!gamestate().events_manager_->is_event_running());
899 
901  scoped_savegame_snapshot snapshot(*this);
904  } else {
906  }
907 }
908 
909 void play_controller::save_game_auto(const std::string& filename)
910 {
913 
914  scoped_savegame_snapshot snapshot(*this);
916  save.save_game_automatic(false, filename);
917  }
918 }
919 
921 {
926  } else {
928  }
929 }
930 
931 void play_controller::save_replay_auto(const std::string& filename)
932 {
936  save.save_game_automatic(false, filename);
937  }
938 }
939 
941 {
945  } else {
947  }
948 }
949 
951 {
953  load.load_game_ingame();
954 }
955 
957 {
959  undo_stack().undo();
960 }
961 
963 {
965  undo_stack().redo();
966 }
967 
969 {
971 }
972 
974 {
976 }
977 
978 const std::string& play_controller::select_music(bool victory) const
979 {
980  const std::vector<std::string>& music_list = victory
981  ? (gamestate_->get_game_data()->get_victory_music().empty()
983  : gamestate_->get_game_data()->get_victory_music())
984  : (gamestate_->get_game_data()->get_defeat_music().empty()
986  : gamestate_->get_game_data()->get_defeat_music());
987 
988  if(music_list.empty()) {
989  // Since this function returns a reference, we can't return a temporary empty string.
990  static const std::string empty_str = "";
991  return empty_str;
992  }
993 
994  return music_list[randomness::rng::default_instance().get_random_int(0, music_list.size() - 1)];
995 }
996 
998 {
999  if(linger_) {
1000  return;
1001  }
1002 
1003  if(is_regular_game_end()) {
1004  return;
1005  }
1006 
1007  bool continue_level, found_player, found_network_player, invalidate_all;
1008  std::set<unsigned> not_defeated;
1009 
1011  continue_level,
1012  found_player,
1013  found_network_player,
1014  invalidate_all,
1015  not_defeated,
1017  );
1018 
1019  if(invalidate_all) {
1020  gui_->invalidate_all();
1021  }
1022 
1023  if(continue_level) {
1024  return;
1025  }
1026 
1027  if(found_player || found_network_player) {
1028  pump().fire("enemies_defeated");
1029  if(is_regular_game_end()) {
1030  return;
1031  }
1032  }
1033 
1034  DBG_EE << "victory_when_enemies_defeated: " << victory_when_enemies_defeated_ << std::endl;
1035  DBG_EE << "found_player: " << found_player << std::endl;
1036  DBG_EE << "found_network_player: " << found_network_player << std::endl;
1037 
1038  if(!victory_when_enemies_defeated_ && (found_player || found_network_player)) {
1039  // This level has asked not to be ended by this condition.
1040  return;
1041  }
1042 
1043  if(gui_->video().non_interactive()) {
1044  LOG_AIT << "winner: ";
1045  for(unsigned l : not_defeated) {
1047  if(ai.empty())
1048  ai = "default ai";
1049  LOG_AIT << l << " (using " << ai << ") ";
1050  }
1051 
1052  LOG_AIT << std::endl;
1053  ai_testing::log_victory(not_defeated);
1054  }
1055 
1056  DBG_EE << "throwing end level exception..." << std::endl;
1057  // Also proceed to the next scenario when another player survived.
1058  end_level_data el_data;
1059  el_data.proceed_to_next_level = found_player || found_network_player;
1060  el_data.is_victory = found_player;
1061  set_end_level_data(el_data);
1062 }
1063 
1064 void play_controller::process_oos(const std::string& msg) const
1065 {
1066  if(gui_->video().non_interactive()) {
1067  throw game::game_error(msg);
1068  }
1069 
1071  return;
1072  }
1073 
1074  std::stringstream message;
1075  message << _("The game is out of sync. It might not make much sense to continue. Do you want to save your game?");
1076  message << "\n\n" << _("Error details:") << "\n\n" << msg;
1077 
1078  scoped_savegame_snapshot snapshot(*this);
1080  save.save_game_interactive(message.str(), savegame::savegame::YES_NO); // can throw quit_game_exception
1081 }
1082 
1083 void play_controller::update_gui_to_player(const int team_index, const bool observe)
1084 {
1085  gui_->set_team(team_index, observe);
1086  gui_->recalculate_minimap();
1087  gui_->invalidate_all();
1088 }
1089 
1091 {
1092  scoped_savegame_snapshot snapshot(*this);
1095 }
1096 
1097 void play_controller::do_consolesave(const std::string& filename)
1098 {
1099  scoped_savegame_snapshot snapshot(*this);
1101  save.save_game_automatic(true, filename);
1102 }
1103 
1105 {
1106  // note: this writes to level_ if this is not a replay.
1108 }
1109 
1111 {
1112  return gamestate().events_manager_->pump();
1113 }
1114 
1116 {
1117  return ticks_;
1118 }
1119 
1121 {
1122  return soundsources_manager_.get();
1123 }
1124 
1126 {
1127  return plugins_context_.get();
1128 }
1129 
1131 {
1132  return hotkey_handler_.get();
1133 }
1134 
1136 {
1137  if(linger_ || !gamestate_->init_side_done() || gamestate().gamedata_.phase() != game_data::PLAY) {
1138  return true;
1139  }
1140 
1141  const team& t = current_team();
1142  return !t.is_local_human() || !t.is_proxy_human();
1143 }
1144 
1146 {
1148  return;
1149  }
1150 
1151  try {
1152  play_slice();
1153  } catch(const return_to_play_side_exception&) {
1154  assert(should_return_to_play_side());
1155  }
1156 }
1157 
1159 {
1160  fire_preload();
1161 
1162  if(!gamestate().start_event_fired_) {
1163  gamestate().start_event_fired_ = true;
1167 
1168  set_scontext_synced sync;
1169 
1170  fire_prestart();
1171  if(is_regular_game_end()) {
1172  return;
1173  }
1174 
1175  for(const team& t : get_teams()) {
1176  actions::clear_shroud(t.side(), false, false);
1177  }
1178 
1179  init_gui();
1180  LOG_NG << "first_time..." << (is_skipping_replay() ? "skipping" : "no skip") << "\n";
1181 
1182  fire_start();
1183  if(is_regular_game_end()) {
1184  return;
1185  }
1186 
1187  sync.do_final_checkup();
1188  gui_->recalculate_minimap();
1189 
1190  // Initialize countdown clock.
1191  for(const team& t : get_teams()) {
1193  t.set_countdown_time(1000 * saved_game_.mp_settings().mp_countdown_init_time);
1194  }
1195  }
1196  } else {
1197  init_gui();
1199  gui_->recalculate_minimap();
1200  }
1201 
1203 }
1204 
1205 /**
1206  * Find all [endlevel]next_scenario= attributes, and add them to @a result.
1207  */
1208 static void find_next_scenarios(const config& parent, std::set<std::string>& result) {
1209  for(const auto& endlevel : parent.child_range("endlevel")) {
1210  if(endlevel.has_attribute("next_scenario")) {
1211  result.insert(endlevel["next_scenario"]);
1212  }
1213  }
1214  for(const auto cfg : parent.all_children_range()) {
1215  find_next_scenarios(cfg.cfg, result);
1216  }
1217 };
1218 
1220  // Which scenarios are reachable from the current one?
1221  std::set<std::string> possible_next_scenarios;
1222  possible_next_scenarios.insert(gamestate().gamedata_.next_scenario());
1223 
1224  // Find all "endlevel" tags that could be triggered in events
1225  config events;
1226  gamestate().events_manager_->write_events(events);
1227  find_next_scenarios(events, possible_next_scenarios);
1228 
1229  // Are we looking for [scenario]id=, [multiplayer]id= or [test]id=?
1230  const auto tagname = saved_game_.classification().get_tagname();
1231 
1232  // Of the possible routes, work out which exist.
1233  bool possible_this_is_the_last_scenario = false;
1234  std::vector<std::string> known;
1235  std::vector<std::string> unknown;
1236  for(const auto& x : possible_next_scenarios) {
1237  if(x.empty() || x == "null") {
1238  possible_this_is_the_last_scenario = true;
1239  LOG_NG << "This can be the last scenario\n";
1240  } else if(utils::contains(x, '$')) {
1241  // Assume a WML variable will be set to a correct value before the end of the scenario
1242  known.push_back(x);
1243  LOG_NG << "Variable value for next scenario '" << x << "'\n";
1244  } else if(game_config_.find_child(tagname, "id", x)) {
1245  known.push_back(x);
1246  LOG_NG << "Known next scenario '" << x << "'\n";
1247  } else {
1248  unknown.push_back(x);
1249  ERR_NG << "Unknown next scenario '" << x << "'\n";
1250  }
1251  }
1252 
1253  if(unknown.empty()) {
1254  // everything's good
1255  return;
1256  }
1257 
1258  std::string title = _("Warning: broken campaign branches");
1259  std::stringstream message;
1260 
1261  message << _n(
1262  // TRANSLATORS: This is an error that will hopefully only be seen by UMC authors and by players who have already
1263  // said "okay" to a "loading saves from an old version might not work" dialog.
1264  "The next scenario is missing, you will not be able to finish this campaign.",
1265  // TRANSLATORS: This is an error that will hopefully only be seen by UMC authors and by players who have already
1266  // said "okay" to a "loading saves from an old version might not work" dialog.
1267  "Some of the possible next scenarios are missing, you might not be able to finish this campaign.",
1268  unknown.size() + known.size() + (possible_this_is_the_last_scenario ? 1 : 0));
1269  message << "\n\n";
1270  message << _n(
1271  "Please report the following missing scenario to the campaign’s author:\n$unknown_list|",
1272  "Please report the following missing scenarios to the campaign’s author:\n$unknown_list|",
1273  unknown.size());
1274  message << "\n";
1275  message << _("Once this is fixed, you will need to restart this scenario.");
1276 
1277  std::stringstream unknown_list;
1278  for(const auto& x : unknown) {
1279  unknown_list << font::unicode_bullet << " " << x << "\n";
1280  }
1281  utils::string_map symbols;
1282  symbols["unknown_list"] = unknown_list.str();
1283  auto message_str = utils::interpolate_variables_into_string(message.str(), &symbols);
1284  ERR_NG << message_str << "\n";
1286 }
1287 
1289 {
1290  const team& viewing_team = get_teams()[gui_->viewing_team()];
1291  return gui_->viewing_team() == gui_->playing_team() && !events::commands_disabled && viewing_team.is_local_human()
1292  && !is_lingering() && !is_browsing();
1293 }
1294 
1295 std::set<std::string> play_controller::all_players() const
1296 {
1297  std::set<std::string> res = gui_->observers();
1298  for(const team& t : get_teams()) {
1299  if(t.is_human()) {
1300  res.insert(t.current_player());
1301  }
1302  }
1303 
1304  return res;
1305 }
1306 
1308 {
1309  // check for team-specific items in the scenario
1310  gui_->parse_team_overlays();
1311 
1312  do {
1314  {
1315  save_blocker blocker;
1317  if(is_regular_game_end()) {
1318  return;
1319  }
1320  }
1321  // This flag can be set by derived classes (in overridden functions).
1322  player_type_changed_ = false;
1323 
1324  statistics::reset_turn_stats(gamestate().board_.get_team(current_side()).save_id_or_number());
1325 
1326  play_side_impl();
1327 
1328  if(is_regular_game_end()) {
1329  return;
1330  }
1331  } while(player_type_changed_);
1332 
1333  // Keep looping if the type of a team (human/ai/networked) has changed mid-turn
1334  sync_end_turn();
1335 }
1336 
1338 {
1339  whiteboard_manager_->on_gamestate_change();
1340  gui_->new_turn();
1341  gui_->invalidate_game_status();
1342 
1343  LOG_NG << "turn: " << turn() << "\n";
1344 
1345  if(gui_->video().non_interactive()) {
1346  LOG_AIT << "Turn " << turn() << ":" << std::endl;
1347  }
1348 
1349  int last_player_number = gamestate_->player_number_;
1350  int next_player_number = gamestate_->next_player_number_;
1351 
1352  while(gamestate_->player_number_ <= static_cast<int>(get_teams().size())) {
1353  gamestate_->next_player_number_ = gamestate_->player_number_ + 1;
1354  next_player_number = gamestate_->next_player_number_;
1355  last_player_number = gamestate_->player_number_;
1356 
1357  // If a side is empty skip over it.
1358  if(!current_team().is_empty()) {
1359  init_side_begin();
1360  if(gamestate_->init_side_done()) {
1361  // This is the case in a reloaded game where the side was initialized before saving the game.
1362  init_side_end();
1363  }
1364 
1366  play_side();
1367 
1368  // ignore any changes to next_player_number_ that happen after the [end_turn] is sended to the server,
1369  // otherwise we will get OOS.
1370  next_player_number = gamestate_->next_player_number_;
1371  assert(next_player_number <= 2 * static_cast<int>(get_teams().size()));
1372 
1373  if(is_regular_game_end()) {
1374  return;
1375  }
1376 
1377  // note: play_side() send the [end_turn] to the sever and finish_side_turn() callsie the side turn end
1378  // events.
1379  // this means that during the side turn end events the clients think it is still the last sides turn
1380  // while the server thinks that it is already the next plyers turn. i don'T think this is a problem
1381  // though.
1382  finish_side_turn();
1383  if(is_regular_game_end()) {
1384  return;
1385  }
1386 
1387  if(gui_->video().non_interactive()) {
1388  LOG_AIT << " Player " << current_side() << ": " << current_team().villages().size() << " Villages" << std::endl;
1390  }
1391  }
1392 
1393  gamestate_->player_number_ = next_player_number;
1394  }
1395 
1396  // If the loop exits due to the last team having been processed.
1397  gamestate_->player_number_ = last_player_number;
1398 
1399  finish_turn();
1400 
1401  // Time has run out
1402  check_time_over();
1403 
1404  if(!is_regular_game_end()) {
1405  gamestate_->player_number_ = modulo(next_player_number, get_teams().size(), 1);
1406  }
1407 }
1408 
1410 {
1411  const bool time_left = gamestate().tod_manager_.next_turn(&gamestate().gamedata_);
1412 
1413  if(!time_left) {
1414  LOG_NG << "firing time over event...\n";
1416  pump().fire("time_over");
1417  LOG_NG << "done firing time over event...\n";
1418 
1419  // If turns are added while handling 'time over' event.
1420  if(gamestate().tod_manager_.is_time_left()) {
1421  return;
1422  }
1423 
1424  if(gui_->video().non_interactive()) {
1425  LOG_AIT << "time over (draw)\n";
1427  }
1428 
1429  check_victory();
1430  if(is_regular_game_end()) {
1431  return;
1432  }
1433 
1434  end_level_data e;
1435  e.proceed_to_next_level = false;
1436  e.is_victory = false;
1437  set_end_level_data(e);
1438  }
1439 }
1440 
1442  : controller_(controller)
1443 {
1445 }
1446 
1448 {
1450 }
1451 
1453 {
1454  const team& t = get_teams()[gui_->viewing_team()];
1455  static const std::string no_objectives(_("No objectives available"));
1456  std::string objectives = utils::interpolate_variables_into_string(t.objectives(), *gamestate_->get_game_data());
1457  gui2::show_transient_message(get_scenario_name(), (objectives.empty() ? no_objectives : objectives), "", true);
1459 }
1460 
1462 {
1464  const std::shared_ptr<gui::button> skip_animation_button = get_display().find_action_button("skip-animation");
1465  if(skip_animation_button) {
1466  skip_animation_button->set_check(skip_replay_);
1467  }
1468 }
std::shared_ptr< gui::button > find_action_button(const std::string &id)
Retrieves a pointer to a theme UI button.
Definition: display.cpp:831
void clear()
Clears the stack of undoable (and redoable) actions.
Definition: undo.cpp:220
play_controller * controller
Definition: resources.cpp:21
std::vector< std::string > default_victory_music
void do_search(const std::string &new_search)
void set_current_paths(const pathfind::paths &new_paths)
int autosavemax()
Definition: game.cpp:804
virtual bool should_return_to_play_side() const
std::unique_ptr< soundsource::manager > soundsources_manager_
void show_message(const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition: message.cpp:152
static std::string _n(const char *str1, const char *str2, int n)
Definition: gettext.hpp:96
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:270
hotkey::command_executor * get_hotkey_command_executor() override
Optionally get a command executor to handle context menu events.
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:953
void reset_objectives_changed() const
Definition: team.hpp:249
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:91
The class for loading a savefile.
Definition: savegame.hpp:99
std::map< std::string, t_string > string_map
std::unique_ptr< game_lua_kernel > lua_kernel_
Definition: game_state.hpp:50
void refresh_objectives() const
Reevaluate [show_if] conditions and build a new objectives string.
void save_replay_auto(const std::string &filename)
int find_last_visible_team() const
returns 0 if no such team was found.
This class represents a single unit of a specific type.
Definition: unit.hpp:120
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
const unit_map & get_units() 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)
Collection of helper functions relating to Pango formatting.
events::mouse_handler mouse_handler_
static manager & get_singleton()
Definition: manager.hpp:143
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_
static game_config_view wrap(const config &cfg)
Exception used to escape form the ai or ui code to playsingle_controller::play_side.
game_display & get_display() override
Get a reference to a display member a derived class uses.
static std::shared_ptr< save_index_class > default_saves_dir()
Returns an instance for managing saves in filesystem::get_saves_dir()
Definition: save_index.cpp:210
replay_recorder_base & get_replay()
Definition: saved_game.hpp:134
Class for autosaves.
Definition: savegame.hpp:279
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:207
bool enemies_visible() const
child_itors child_range(config_key_type key)
Definition: config.cpp:356
void process_keydown_event(const SDL_Event &event) override
Process keydown (always).
bool is_idle() const
Definition: team.hpp:296
const pathfind::paths & current_paths() const
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)
void set_do_healing(bool do_healing)
static void progress(loading_stage stage=loading_stage::none)
virtual void update_viewing_player()=0
gui::floating_textbox & get_textbox()
void new_turn()
Definition: team.hpp:215
void set_gui(game_display *gui)
Definition: menu_events.hpp:54
static const config & get_theme(const game_config_view &game_config, std::string theme_name)
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:348
Gather statistics important for AI testing and output them.
unit_type_data unit_types
Definition: types.cpp:1480
virtual void process_oos(const std::string &msg) const
Asks the user whether to continue on an OOS error.
A RAII object to enter the synced context, cannot be called if we are already in a synced context...
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:262
static lg::log_domain log_engine("engine")
Class for "normal" midgame saves.
Definition: savegame.hpp:252
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
bool do_healing() const
void check_victory()
Checks to see if a side has won.
void apply_scenario_fix(const config &cfg)
Definition: types.cpp:1413
config & set_snapshot(config snapshot)
Definition: saved_game.cpp:557
void append_children(const config &cfg)
Adds children from cfg.
Definition: config.cpp:235
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:384
static lg::log_domain log_display("display")
void write_music_play_list(config &snapshot)
Definition: sound.cpp:851
static std::string _(const char *str)
Definition: gettext.hpp:92
#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_
#define ERR_NG
void reset_turn_stats(const std::string &save_id)
Definition: statistics.cpp:686
void process_focus_keydown_event(const SDL_Event &event) override
Process keydown (only when the general map display does not have focus).
#define ERR_DP
void memorize_command(const std::string &command)
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...
Shows a close button.
Definition: message.hpp:73
events::menu_handler menu_handler_
void play_sound(const std::string &files, channel_group group, unsigned int repeats)
Definition: sound.cpp:1020
bool can_undo() const
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:75
void set_phase(PHASE phase)
Definition: game_data.hpp:79
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:55
saved_game & saved_game_
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:71
A small explanation about what&#39;s going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:60
scoped_savegame_snapshot(const play_controller &controller)
void init_side()
Definition: replay.cpp:215
team & get_team(int i)
Definition: game_board.hpp:96
void throw_quit_game_exception()
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
void save_game_auto(const std::string &filename)
Various functions that implement advancements of units.
bool add_start_if_not_there_yet()
Definition: replay.cpp:662
void textbox_move_vertically(bool up)
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:97
Implements a quit confirmation dialog.
config to_config() const
Builds the snapshot config from members and their respective configs.
static void find_next_scenarios(const config &parent, std::set< std::string > &result)
Find all [endlevel]next_scenario= attributes, and add them to result.
filter_context * filter_con
Definition: resources.cpp:23
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:55
bool valid() const
Definition: location.hpp:88
bool ignore_replay_errors
int side_upkeep(int side_num) const
TEXTBOX_MODE mode() 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)
std::vector< team > & get_teams()
void recalculate_fog(int side)
Function that recalculates the fog of war.
Definition: vision.cpp:711
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
Object which temporarily resets a unit&#39;s movement.
Definition: unit.hpp:1991
bool load_game_ingame()
Load a game without providing any information.
Definition: savegame.cpp:122
std::unique_ptr< pathfind::manager > pathfind_manager_
Definition: game_state.hpp:48
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands...
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:111
void set_gui(game_display *gui)
void spend_gold(const int amount)
Definition: team.hpp:219
std::shared_ptr< wb::manager > get_whiteboard() const
static void add_color_info(const game_config_view &v, bool build_defaults)
An object which will lock the display for the duration of its lifetime.
Definition: video.hpp:296
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()
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:37
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:294
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:214
void set_end_level_data(const end_level_data &data)
void end_turn(int pnum)
Definition: game_board.cpp:76
int get_path_turns() const
std::size_t i
Definition: function.cpp:940
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:404
map_location map_start_
const std::vector< std::string > & command_history() const
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:654
void reset_gamestate(const config &level, int replay_pos)
const t_string & objectives() const
Definition: team.hpp:251
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:1013
#define log_scope(description)
Definition: log.hpp:206
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.
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:221
const std::string unicode_bullet
Definition: constants.cpp:46
static void save(LexState *ls, int c)
Definition: llex.cpp:57
bool can_redo() const
static lg::log_domain log_enginerefac("enginerefac")
std::string observer
bool is_local() const
Definition: team.hpp:272
Class for replay saves (either manually or automatically).
Definition: savegame.hpp:266
config & add_child(config_key_type key)
Definition: config.cpp:500
const config & find_child(config_key_type key, const std::string &name, const std::string &value) const
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
Handling of system events.
Definition: manager.hpp:42
compression::format save_compression_format()
Definition: game.cpp:856
bool is_replay() const
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:760
static bool try_block()
bool get_disallow_observers() const
Definition: team.hpp:352
static void display(std::function< void()> f)
void set_game_display(game_display *)
Definition: game_state.cpp:245
bool is_local_human() const
Definition: team.hpp:278
events::mouse_handler & get_mouse_handler_base() override
Get a reference to a mouse handler member a derived class uses.
const std::unique_ptr< gui::textbox > & box() const
game_state & gamestate()
double t
Definition: astarsearch.cpp:64
Various functions that implement healing of units (when a side turn starts).
const game_config_view & game_config_
map_location prev
Definition: astarsearch.cpp:65
game_classification & classification()
Definition: saved_game.hpp:54
game_board board_
Definition: game_state.hpp:46
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
Various functions that implement the undoing (and redoing) of in-game commands.
Standard logging facilities (interface).
void autosave(const bool disable_autosave, const int autosave_max, const int infinite_autosaves)
Definition: savegame.cpp:609
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:71
void remove_scenario_fixes()
Definition: types.cpp:1447
static const map_location & null_location()
Definition: location.hpp:80
game_lua_kernel * lua_kernel
Definition: resources.cpp:25
config * get_next_action()
Definition: replay.cpp:619
std::set< std::string > all_players() const
static lg::log_domain log_aitesting("ai/testing")
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:67
#define e
actions::undo_list * undo_stack
Definition: resources.cpp:32
std::size_t turn() const
play_controller(const config &level, saved_game &state_of_game, bool skip_replay)
void update_savegame_snapshot() const
void remove_snapshot()
Definition: saved_game.cpp:575
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:477
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:78
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:59
game_classification & get_classification()
bool valid() const
Definition: map.hpp:273
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:58
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:250
void check_next_scenario_is_known()
This shows a warning dialog if either [scenario]next_scenario or any [endlevel]next_scenario would le...
const std::set< map_location > & villages() const
Definition: team.hpp:195
void close(game_display &gui)
pathfind::manager * tunnels
Definition: resources.cpp:31
bool can_undo() const
True if there are actions that can be undone.
Definition: undo.hpp:95
static rng & default_instance()
Definition: random.cpp:73
virtual void check_objectives()=0
std::string get_tagname() 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:367
virtual void play_side_impl()
bool start_event_fired_
Definition: game_state.hpp:66
T modulo(T num, int mod, T min=0)
Definition: math.hpp:61
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_
void do_ai_formula(const std::string &str, int side_num, mouse_handler &mousehandler)
#define LOG_NG
virtual void sync_end_turn()