The Battle for Wesnoth  1.17.0-dev
play_controller.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2021
3  by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
4  Copyright (C) 2003 by David White <dave@whitevine.net>
5  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY.
13 
14  See the COPYING file for more details.
15 */
16 
17 /**
18  * @file
19  * Handle input via mouse & keyboard, events, schedule commands.
20  */
21 
22 #include "play_controller.hpp"
23 
24 #include "actions/advancement.hpp"
25 #include "actions/create.hpp"
26 #include "actions/heal.hpp"
27 #include "actions/undo.hpp"
28 #include "actions/vision.hpp"
29 #include "ai/manager.hpp"
30 #include "ai/testing.hpp"
31 #include "display_chat_manager.hpp"
32 #include "floating_label.hpp"
33 #include "formula/string_utils.hpp"
34 #include "game_errors.hpp"
36 #include "game_events/pump.hpp"
37 #include "game_state.hpp"
38 #include "gettext.hpp"
40 #include "gui/dialogs/message.hpp" // for show_error_message
44 #include "hotkey/hotkey_item.hpp"
45 #include "log.hpp"
46 #include "map/label.hpp"
47 #include "pathfind/teleport.hpp"
49 #include "preferences/display.hpp"
50 #include "preferences/game.hpp"
51 #include "random.hpp"
52 #include "replay.hpp"
53 #include "reports.hpp"
54 #include "resources.hpp"
55 #include "save_blocker.hpp"
56 #include "save_index.hpp"
57 #include "saved_game.hpp"
58 #include "savegame.hpp"
61 #include "sound.hpp"
62 #include "soundsource.hpp"
63 #include "statistics.hpp"
64 #include "synced_context.hpp"
65 #include "tooltips.hpp"
66 #include "units/id.hpp"
67 #include "units/types.hpp"
68 #include "units/unit.hpp"
69 #include "utils/general.hpp"
70 #include "whiteboard/manager.hpp"
71 
72 #include <functional>
73 
74 static lg::log_domain log_aitesting("ai/testing");
75 #define LOG_AIT LOG_STREAM(info, log_aitesting)
76 // If necessary, this define can be replaced with `#define LOG_AIT std::cout` to restore previous behavior
77 
78 static lg::log_domain log_engine("engine");
79 #define LOG_NG LOG_STREAM(info, log_engine)
80 #define DBG_NG LOG_STREAM(debug, log_engine)
81 #define ERR_NG LOG_STREAM(err, log_engine)
82 
83 static lg::log_domain log_display("display");
84 #define ERR_DP LOG_STREAM(err, log_display)
85 
86 static lg::log_domain log_enginerefac("enginerefac");
87 #define LOG_RG LOG_STREAM(info, log_enginerefac)
88 
89 static lg::log_domain log_engine_enemies("engine/enemies");
90 #define DBG_EE LOG_STREAM(debug, log_engine_enemies)
91 
92 /**
93  * Copies [scenario] attributes/tags that are not otherwise stored in C++ structs/clases.
94  */
95 static void copy_persistent(const config& src, config& dst)
96 {
97  static const std::set<std::string> attrs {
98  "description",
99  "name",
100  "victory_when_enemies_defeated",
101  "remove_from_carryover_on_defeat",
102  "disallow_recall",
103  "experience_modifier",
104  "require_scenario",
105  "loaded_resources"
106  };
107 
108  static const std::set<std::string> tags {
109  "terrain_graphics",
110  "modify_unit_type",
111  "lua"
112  };
113 
114  for(const std::string& attr : attrs) {
115  dst[attr] = src[attr];
116  }
117 
118  for(const std::string& tag : tags) {
119  dst.append_children(src, tag);
120  }
121 }
122 
123 static void clear_resources()
124 {
125  resources::controller = nullptr;
126  resources::filter_con = nullptr;
127  resources::gameboard = nullptr;
128  resources::gamedata = nullptr;
129  resources::game_events = nullptr;
130  resources::lua_kernel = nullptr;
131  resources::persist = nullptr;
132  resources::soundsources = nullptr;
133  resources::tod_manager = nullptr;
134  resources::tunnels = nullptr;
135  resources::undo_stack = nullptr;
136  resources::recorder = nullptr;
137  resources::whiteboard.reset();
138  resources::classification = nullptr;
139 }
140 
141 play_controller::play_controller(const config& level, saved_game& state_of_game, bool skip_replay)
142  : controller_base()
143  , observer()
145  , ticks_(SDL_GetTicks())
146  , gamestate_()
147  , level_()
148  , saved_game_(state_of_game)
149  , tooltips_manager_()
150  , whiteboard_manager_()
151  , plugins_context_()
152  , labels_manager_(new font::floating_label_context())
153  , help_manager_(&game_config_)
154  , mouse_handler_(nullptr, *this)
155  , menu_handler_(nullptr, *this)
156  , hotkey_handler_(new hotkey_handler(*this, saved_game_))
157  , soundsources_manager_()
158  , persist_()
159  , gui_()
160  , xp_mod_(new unit_experience_accelerator(level["experience_modifier"].to_int(100)))
161  , statistics_context_(new statistics::scenario_context(level["name"]))
162  , replay_(new replay(state_of_game.get_replay()))
163  , skip_replay_(skip_replay)
164  , skip_story_(state_of_game.skip_story())
165  , linger_(false)
166  , init_side_done_now_(false)
167  , map_start_()
168  , victory_when_enemies_defeated_(level["victory_when_enemies_defeated"].to_bool(true))
169  , remove_from_carryover_on_defeat_(level["remove_from_carryover_on_defeat"].to_bool(true))
170  , victory_music_()
171  , defeat_music_()
172  , scope_()
173  , ignore_replay_errors_(false)
174  , player_type_changed_(false)
175 {
176  copy_persistent(level, level_);
177 
178  for(const config& modify_unit_type : level_.child_range("modify_unit_type")) {
179  unit_types.apply_scenario_fix(modify_unit_type);
180  }
181  resources::controller = this;
184 
186 
188 
192 
193  try {
194  init(level);
195  } catch(...) {
196  clear_resources();
197  throw;
198  }
199 }
200 
202 {
205  clear_resources();
206 }
207 
209 {
210  gui2::dialogs::loading_screen::display([this, &level]() {
212 
213  LOG_NG << "initializing game_state..." << (SDL_GetTicks() - ticks()) << std::endl;
214  gamestate_.reset(new game_state(level, *this));
215 
223 
224  gamestate_->ai_manager_.add_observer(this);
225  gamestate_->init(level, *this);
227 
228  LOG_NG << "initializing whiteboard..." << (SDL_GetTicks() - ticks()) << std::endl;
230  whiteboard_manager_.reset(new wb::manager());
232 
233  LOG_NG << "loading units..." << (SDL_GetTicks() - ticks()) << std::endl;
236 
237  LOG_NG << "initializing theme... " << (SDL_GetTicks() - ticks()) << std::endl;
239  const config& theme_cfg = controller_base::get_theme(game_config_, theme());
240 
241  LOG_NG << "building terrain rules... " << (SDL_GetTicks() - ticks()) << std::endl;
243  gui_.reset(new game_display(gamestate().board_, whiteboard_manager_, *gamestate().reports_, theme_cfg, level));
244  map_start_ = map_location(level.child_or_empty("display").child_or_empty("location"));
245 
246  if(!gui_->video().faked()) {
248  gui_->get_theme().modify_label("time-icon", _("time left for current turn"));
249  } else {
250  gui_->get_theme().modify_label("time-icon", _("current local time"));
251  }
252  }
253 
255  mouse_handler_.set_gui(gui_.get());
256  menu_handler_.set_gui(gui_.get());
257 
258  LOG_NG << "done initializing display... " << (SDL_GetTicks() - ticks()) << std::endl;
259 
260  LOG_NG << "building gamestate to gui and whiteboard... " << (SDL_GetTicks() - ticks()) << std::endl;
261  // This *needs* to be created before the show_intro and show_map_scene
262  // as that functions use the manager state_of_game
263  // Has to be done before registering any events!
266 
267  if(gamestate().first_human_team_ != -1) {
268  gui_->set_team(gamestate().first_human_team_);
269  } else if(is_observer()) {
270  // Find first team that is allowed to be observed.
271  // If not set here observer would be without fog until
272  // the first turn of observable side
273  for(const team& t : get_teams()) {
274  if(!t.get_disallow_observers()) {
275  gui_->set_team(t.side() - 1);
276  }
277  }
278  }
279 
280  init_managers();
282  // loadscreen_manager->reset();
284  gamestate().lua_kernel_->load_game(level);
285 
286  plugins_context_.reset(new plugins_context("Game"));
287  plugins_context_->set_callback("save_game", [this](const config& cfg) { save_game_auto(cfg["filename"]); }, true);
288  plugins_context_->set_callback("save_replay", [this](const config& cfg) { save_replay_auto(cfg["filename"]); }, true);
289  plugins_context_->set_callback("quit", [](const config&) { throw_quit_game_exception(); }, false);
290  plugins_context_->set_accessor_string("scenario_name", [this](config) { return get_scenario_name(); });
291  });
292 
293  // Do this after the loadingscreen, so that ita happens in the main thread.
294  gui_->join();
295 }
296 
297 void play_controller::reset_gamestate(const config& level, int replay_pos)
298 {
299  resources::gameboard = nullptr;
300  resources::gamedata = nullptr;
301  resources::tod_manager = nullptr;
302  resources::filter_con = nullptr;
303  resources::lua_kernel = nullptr;
304  resources::game_events = nullptr;
305  resources::tunnels = nullptr;
306  resources::undo_stack = nullptr;
307 
308  gui_->labels().set_team(nullptr);
309 
310  /* First destroy the old game state, then create the new one.
311  This is necessary to ensure that while the old AI manager is being destroyed,
312  all its member objects access the old manager instead of the new. */
313  gamestate_.reset();
314  gamestate_.reset(new game_state(level, *this));
315 
323 
324  gamestate_->ai_manager_.add_observer(this);
325  gamestate_->init(level, *this);
328 
329  gui_->reset_reports(*gamestate().reports_);
330  gui_->change_display_context(&gamestate().board_);
331  saved_game_.get_replay().set_pos(replay_pos);
333  gamestate().lua_kernel_->load_game(level);
334 }
335 
337 {
338  LOG_NG << "initializing managers... " << (SDL_GetTicks() - ticks()) << std::endl;
342 
344  LOG_NG << "done initializing managers... " << (SDL_GetTicks() - ticks()) << std::endl;
345 }
346 
348 {
349  // Run initialization scripts, even if loading from a snapshot.
350  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
351  pump().fire("preload");
352 }
353 
355 {
356  // pre-start events must be executed before any GUI operation,
357  // as those may cause the display to be refreshed.
358  update_locker lock_display(gui_->video());
360 
361  // Fire these right before prestart events, to catch only the units sides
362  // have started with.
363  for(const unit& u : get_units()) {
364  pump().fire("unit_placed", map_location(u.get_location()));
365  }
366 
367  pump().fire("prestart");
368 
369  // prestart event may modify start turn with WML, reflect any changes.
370  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
371 }
372 
374 {
375  const config cfg("side", gui_->viewing_side());
376  gamestate().lua_kernel_->run_wml_action("show_objectives", vconfig(cfg),
377  game_events::queued_event("_from_interface", "", map_location(), map_location(), config()));
378 }
379 
381 {
383  pump().fire("start");
384 
385  skip_story_ = false; // Show [message]s from now on even with --campaign-skip-story
386 
387  // start event may modify start turn with WML, reflect any changes.
388  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
389 
392 
393  // prestart and start events may modify the initial gold amount,
394  // reflect any changes.
395  for(team& tm : get_teams()) {
396  tm.set_start_gold(tm.gold());
397  }
398 
399  gamestate_->init_side_done() = false;
401 }
402 
404 {
405  gui_->begin_game();
406  gui_->update_tod();
407 }
408 
410 {
412 
413  // If we are observers we move to watch next team if it is allowed
414  if((is_observer() && !current_team().get_disallow_observers()) || (current_team().is_local_human() && !is_replay())) {
416  }
417 
418  gui_->set_playing_team(std::size_t(current_side() - 1));
419 
421 }
422 
424 {
425  //
426  // We do side init only if not done yet for a local side when we are not replaying.
427  // For all other sides it is recorded in replay and replay handler has to handle
428  // calling do_init_side() functions.
429  //
430  if(gamestate_->init_side_done()) {
431  // We already executed do_init_side this can for example happe if we reload a game,
432  // but also if we changed control of a side during it's turn
433  return;
434  }
435 
436  if(!current_team().is_local()) {
437  // We are in a mp game and execute do_init_side as soon as we receive [init_side] from the current player
438  // (see replay.cpp)
439  return;
440  }
441 
442  if(is_replay()) {
443  // We are in a replay and execute do_init_side as soon as we reach the next [init_side] in the replay data
444  // (see replay.cpp)
445  return;
446  }
447 
448  if(current_team().is_idle()) {
449  // In this case it can happen that we just gave control of this side to another player so doing init_side
450  // could lead to errors since we no longer own this side from the servers perspective.
451  // (see playturn.cpp)
452  return;
453  }
454 
456  do_init_side();
457 }
458 
460 {
461  set_scontext_synced sync;
462  log_scope("player turn");
463  // In case we might end up calling sync:network during the side turn events,
464  // and we don't want do_init_side to be called when a player drops.
465  gamestate_->init_side_done() = true;
466  init_side_done_now_ = true;
467 
468  const std::string turn_num = std::to_string(turn());
469  const std::string side_num = std::to_string(current_side());
470 
471  gamestate().gamedata_.get_variable("side_number") = current_side();
472 
473  // We might have skipped some sides because they were empty so it is not enough to check for side_num==1
474  if(!gamestate().tod_manager_.has_turn_event_fired()) {
475  pump().fire("turn_" + turn_num);
476  pump().fire("new_turn");
478  }
479 
480  pump().fire("side_turn");
481  pump().fire("side_" + side_num + "_turn");
482  pump().fire("side_turn_" + turn_num);
483  pump().fire("side_" + side_num + "_turn_" + turn_num);
484 
485  // We want to work out if units for this player should get healed,
486  // and the player should get income now.
487  // Healing/income happen if it's not the first turn of processing,
488  // or if we are loading a game.
489  if(turn() > 1) {
492 
493  // If the expense is less than the number of villages owned
494  // times the village support capacity,
495  // then we don't have to pay anything at all
496  int expense = gamestate().board_.side_upkeep(current_side()) - current_team().support();
497  if(expense > 0) {
498  current_team().spend_gold(expense);
499  }
500  }
501 
502  if(do_healing()) {
504  }
505 
506  // Do healing on every side turn except the very first side turn.
507  // (1.14 and earlier did healing whenever turn >= 2.)
508  set_do_healing(true);
509 
510  // Set resting now after the healing has been done.
511  for(unit& patient : resources::gameboard->units()) {
512  if(patient.side() == current_side()) {
513  patient.set_resting(true);
514  }
515  }
516 
517  // Prepare the undo stack.
519 
520  pump().fire("turn_refresh");
521  pump().fire("side_" + side_num + "_turn_refresh");
522  pump().fire("turn_" + turn_num + "_refresh");
523  pump().fire("side_" + side_num + "_turn_" + turn_num + "_refresh");
524 
525  // Make sure vision is accurate.
527 
528  init_side_end();
529  check_victory();
530  sync.do_final_checkup();
531 }
532 
534 {
536 
537  if(current_side() == 1 || !init_side_done_now_) {
539  }
540 
541  if(!is_skipping_replay()) {
542  gui_->invalidate_all();
543  }
544 
545  if(!is_skipping_replay() && current_team().get_scroll_to_leader() && !map_start_.valid()) {
546  gui_->scroll_to_leader(current_side(), game_display::ONSCREEN, false);
547  }
548 
550  whiteboard_manager_->on_init_side();
551 }
552 
554 {
555  config cfg = level_;
556 
557  cfg["replay_pos"] = saved_game_.get_replay().get_pos();
558  gamestate().write(cfg);
559 
560  gui_->write(cfg.add_child("display"));
561 
562  // Write the soundsources.
563  soundsources_manager_->write_sourcespecs(cfg);
564 
565  gui_->labels().write(cfg);
567 
568  return cfg;
569 }
570 
572 {
573  whiteboard_manager_->on_finish_side_turn(current_side());
574 
575  { // Block for set_scontext_synced
576  set_scontext_synced sync(1);
577  // Ending the turn commits all moves.
578  undo_stack().clear();
580  const std::string turn_num = std::to_string(turn());
581  const std::string side_num = std::to_string(current_side());
582 
583  // Clear shroud, in case units had been slowed for the turn.
585 
586  pump().fire("side_turn_end");
587  pump().fire("side_" + side_num + "_turn_end");
588  pump().fire("side_turn_" + turn_num + "_end");
589  pump().fire("side_" + side_num + "_turn_" + turn_num + "_end");
590  // This is where we refog, after all of a side's events are done.
592  check_victory();
593  sync.do_final_checkup();
594  }
595 
597  gamestate_->init_side_done() = false;
598 }
599 
601 {
602  set_scontext_synced sync(2);
603  const std::string turn_num = std::to_string(turn());
604  pump().fire("turn_end");
605  pump().fire("turn_" + turn_num + "_end");
606  sync.do_final_checkup();
607 }
608 
610 {
611  // If we aren't using fog/shroud, this is easy :)
612  if(current_team().uses_fog() == false && current_team().uses_shroud() == false) {
613  return true;
614  }
615 
616  // See if any enemies are visible
617  for(const unit& u : get_units()) {
618  if(current_team().is_enemy(u.side()) && !gui_->fogged(u.get_location())) {
619  return true;
620  }
621  }
622 
623  return false;
624 }
625 
627 {
628  if(menu_handler_.get_textbox().active() == false) {
629  return;
630  }
631 
632  const std::string str = menu_handler_.get_textbox().box()->text();
633  const unsigned int team_num = current_side();
634  events::mouse_handler& mousehandler = mouse_handler_;
635 
636  switch(menu_handler_.get_textbox().mode()) {
637  case gui::TEXTBOX_SEARCH:
641  break;
644  menu_handler_.get_textbox().close(*gui_); // need to close that one after executing do_speak() !
645  break;
650  break;
651  case gui::TEXTBOX_AI:
654  menu_handler_.do_ai_formula(str, team_num, mousehandler);
655  break;
656  default:
658  ERR_DP << "unknown textbox mode" << std::endl;
659  }
660 }
661 
663 {
664  if(menu_handler_.get_textbox().active() == false) {
665  return;
666  }
667 
670  // Not handling messages to avoid spam
671  return;
672  }
673 
674  const std::string str = menu_handler_.get_textbox().box()->text();
675  const std::vector<std::string>& command_history = menu_handler_.get_textbox().command_history();
676 
677  auto prev = std::find(command_history.begin(), command_history.end(), str);
678 
679  if (prev != command_history.end())
680  {
681  if(up) {
682  if(prev != command_history.begin()) {
683  menu_handler_.get_textbox().box()->set_text(*--prev);
684  }
685  } else {
686  if(++prev != command_history.end()) {
687  menu_handler_.get_textbox().box()->set_text(*prev);
688  } else {
689  menu_handler_.get_textbox().box()->set_text("");
690  }
691  }
692  } else if (up) {
693  if(command_history.size() > 0) {
694  menu_handler_.get_textbox().box()->set_text(*--prev);
695  }
696  if(!str.empty()) {
698  }
699  }
700 }
701 
703 {
705 
706  std::set<std::string> dictionary;
707  switch(mode) {
708  case gui::TEXTBOX_SEARCH: {
709  for(const unit& u : get_units()) {
710  const map_location& loc = u.get_location();
711  if(!gui_->fogged(loc) && !(get_teams()[gui_->viewing_team()].is_enemy(u.side()) && u.invisible(loc)))
712  dictionary.insert(u.name());
713  }
714  // TODO List map labels
715  break;
716  }
717  case gui::TEXTBOX_COMMAND: {
718  std::vector<std::string> commands = menu_handler_.get_commands_list();
719  dictionary.insert(commands.begin(), commands.end());
720  [[fallthrough]]; // we also want player names from the next case
721  }
722  case gui::TEXTBOX_MESSAGE: {
723  for(const team& t : get_teams()) {
724  if(!t.is_empty())
725  dictionary.insert(t.current_player());
726  }
727 
728  // Add observers
729  for(const std::string& o : gui_->observers()) {
730  dictionary.insert(o);
731  }
732 
733  // Add nicks who whispered you
734  for(const std::string& w : gui_->get_chat_manager().whisperers()) {
735  dictionary.insert(w);
736  }
737 
738  // Add nicks from friendlist
739  const std::map<std::string, std::string> friends = preferences::get_acquaintances_nice("friend");
740 
741  for(std::map<std::string, std::string>::const_iterator iter = friends.begin(); iter != friends.end(); ++iter) {
742  dictionary.insert((*iter).first);
743  }
744 
745  // Exclude own nick from tab-completion.
746  // NOTE why ?
747  dictionary.erase(preferences::login());
748  break;
749  }
750 
751  default:
752  ERR_DP << "unknown textbox mode" << std::endl;
753  } // switch(mode)
754 
755  menu_handler_.get_textbox().tab(dictionary);
756 }
757 
759 {
760  if(get_teams().size() == 0) {
761  throw game::game_error("The scenario has no sides defined");
762  }
763 
764  assert(gamestate().board_.has_team(current_side()));
766 }
767 
769 {
770  if(get_teams().size() == 0) {
771  throw game::game_error("The scenario has no sides defined");
772  }
773 
774  assert(gamestate().board_.has_team(current_side()));
776 }
777 
778 bool play_controller::is_team_visible(int team_num, bool observer) const
779 {
780  const team& t = gamestate().board_.get_team(team_num);
781  if(observer) {
782  return !t.get_disallow_observers() && !t.is_empty();
783  } else {
784  return t.is_local_human() && !t.is_idle();
785  }
786 }
787 
789 {
790  assert(current_side() <= static_cast<int>(get_teams().size()));
791  const int num_teams = get_teams().size();
792  const bool observer = is_observer();
793 
794  for(int i = 0; i < num_teams; i++) {
795  const int team_num = modulo(current_side() - i, num_teams, 1);
796  if(is_team_visible(team_num, observer)) {
797  return team_num;
798  }
799  }
800 
801  return 0;
802 }
803 
805 {
806  return mouse_handler_;
807 }
808 
809 std::shared_ptr<wb::manager> play_controller::get_whiteboard() const
810 {
811  return whiteboard_manager_;
812 }
813 
815 {
816  return saved_game_.mp_settings();
817 }
818 
820 {
821  return saved_game_.classification();
822 }
823 
825 {
826  return *gui_;
827 }
828 
830 {
831  return !menu_handler_.get_textbox().active();
832 }
833 
835 {
836  if(event.key.keysym.sym == SDLK_ESCAPE) {
838  } else if(event.key.keysym.sym == SDLK_TAB) {
839  tab();
840  } else if(event.key.keysym.sym == SDLK_UP) {
842  } else if(event.key.keysym.sym == SDLK_DOWN) {
844  } else if(event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_KP_ENTER) {
845  enter_textbox();
846  }
847 }
848 
849 void play_controller::process_keydown_event(const SDL_Event& event)
850 {
851  if(event.key.keysym.sym == SDLK_TAB) {
852  whiteboard_manager_->set_invert_behavior(true);
853  }
854 }
855 
856 void play_controller::process_keyup_event(const SDL_Event& event)
857 {
858  // If the user has pressed 1 through 9, we want to show
859  // how far the unit can move in that many turns
860  if(event.key.keysym.sym >= '1' && event.key.keysym.sym <= '9') {
861  const int new_path_turns = (event.type == SDL_KEYDOWN) ? event.key.keysym.sym - '1' : 0;
862 
863  if(new_path_turns != mouse_handler_.get_path_turns()) {
864  mouse_handler_.set_path_turns(new_path_turns);
865 
867 
868  if(u.valid()) {
869  // if it's not the unit's turn, we reset its moves
870  unit_movement_resetter move_reset(*u, u->side() != current_side());
871 
873  *u, false, true, get_teams()[gui_->viewing_team()], mouse_handler_.get_path_turns()));
874 
875  gui_->highlight_reach(mouse_handler_.current_paths());
876  } else {
878  }
879  }
880  } else if(event.key.keysym.sym == SDLK_TAB) {
881  CKey keys;
882  if(!keys[SDLK_TAB]) {
883  whiteboard_manager_->set_invert_behavior(false);
884  }
885  }
886 }
887 
889 {
890  assert(replay_);
891  return *replay_.get();
892 }
893 
895 {
897  // Saving while an event is running isn't supported
898  // because it may lead to expired event handlers being saved.
899  assert(!gamestate().events_manager_->is_event_running());
900 
902  scoped_savegame_snapshot snapshot(*this);
905  } else {
907  }
908 }
909 
910 void play_controller::save_game_auto(const std::string& filename)
911 {
914 
915  scoped_savegame_snapshot snapshot(*this);
917  save.save_game_automatic(false, filename);
918  }
919 }
920 
922 {
927  } else {
929  }
930 }
931 
932 void play_controller::save_replay_auto(const std::string& filename)
933 {
937  save.save_game_automatic(false, filename);
938  }
939 }
940 
942 {
946  } else {
948  }
949 }
950 
952 {
954  load.load_game_ingame();
955 }
956 
958 {
960  undo_stack().undo();
961 }
962 
964 {
966  undo_stack().redo();
967 }
968 
970 {
972 }
973 
975 {
977 }
978 
979 const std::string& play_controller::select_music(bool victory) const
980 {
981  const std::vector<std::string>& music_list = victory
982  ? (gamestate_->get_game_data()->get_victory_music().empty()
984  : gamestate_->get_game_data()->get_victory_music())
985  : (gamestate_->get_game_data()->get_defeat_music().empty()
987  : gamestate_->get_game_data()->get_defeat_music());
988 
989  if(music_list.empty()) {
990  // Since this function returns a reference, we can't return a temporary empty string.
991  static const std::string empty_str = "";
992  return empty_str;
993  }
994 
995  return music_list[randomness::rng::default_instance().get_random_int(0, music_list.size() - 1)];
996 }
997 
999 {
1000  if(linger_) {
1001  return;
1002  }
1003 
1004  if(is_regular_game_end()) {
1005  return;
1006  }
1007 
1008  bool continue_level, found_player, found_network_player, invalidate_all;
1009  std::set<unsigned> not_defeated;
1010 
1012  continue_level,
1013  found_player,
1014  found_network_player,
1015  invalidate_all,
1016  not_defeated,
1018  );
1019 
1020  if(invalidate_all) {
1021  gui_->invalidate_all();
1022  }
1023 
1024  if(continue_level) {
1025  return;
1026  }
1027 
1028  if(found_player || found_network_player) {
1029  pump().fire("enemies_defeated");
1030  if(is_regular_game_end()) {
1031  return;
1032  }
1033  }
1034 
1035  DBG_EE << "victory_when_enemies_defeated: " << victory_when_enemies_defeated_ << std::endl;
1036  DBG_EE << "found_player: " << found_player << std::endl;
1037  DBG_EE << "found_network_player: " << found_network_player << std::endl;
1038 
1039  if(!victory_when_enemies_defeated_ && (found_player || found_network_player)) {
1040  // This level has asked not to be ended by this condition.
1041  return;
1042  }
1043 
1044  if(gui_->video().non_interactive()) {
1045  LOG_AIT << "winner: ";
1046  for(unsigned l : not_defeated) {
1048  if(ai.empty())
1049  ai = "default ai";
1050  LOG_AIT << l << " (using " << ai << ") ";
1051  }
1052 
1053  LOG_AIT << std::endl;
1054  ai_testing::log_victory(not_defeated);
1055  }
1056 
1057  DBG_EE << "throwing end level exception..." << std::endl;
1058  // Also proceed to the next scenario when another player survived.
1059  end_level_data el_data;
1060  el_data.proceed_to_next_level = found_player || found_network_player;
1061  el_data.is_victory = found_player;
1062  set_end_level_data(el_data);
1063 }
1064 
1065 void play_controller::process_oos(const std::string& msg) const
1066 {
1067  if(gui_->video().non_interactive()) {
1068  throw game::game_error(msg);
1069  }
1070 
1072  return;
1073  }
1074 
1075  std::stringstream message;
1076  message << _("The game is out of sync. It might not make much sense to continue. Do you want to save your game?");
1077  message << "\n\n" << _("Error details:") << "\n\n" << msg;
1078 
1079  scoped_savegame_snapshot snapshot(*this);
1081  save.save_game_interactive(message.str(), savegame::savegame::YES_NO); // can throw quit_game_exception
1082 }
1083 
1084 void play_controller::update_gui_to_player(const int team_index, const bool observe)
1085 {
1086  gui_->set_team(team_index, observe);
1087  gui_->recalculate_minimap();
1088  gui_->invalidate_all();
1089 }
1090 
1092 {
1093  scoped_savegame_snapshot snapshot(*this);
1096 }
1097 
1098 void play_controller::do_consolesave(const std::string& filename)
1099 {
1100  scoped_savegame_snapshot snapshot(*this);
1102  save.save_game_automatic(true, filename);
1103 }
1104 
1106 {
1107  // note: this writes to level_ if this is not a replay.
1109 }
1110 
1112 {
1113  return gamestate().events_manager_->pump();
1114 }
1115 
1117 {
1118  return ticks_;
1119 }
1120 
1122 {
1123  return soundsources_manager_.get();
1124 }
1125 
1127 {
1128  return plugins_context_.get();
1129 }
1130 
1132 {
1133  return hotkey_handler_.get();
1134 }
1135 
1137 {
1138  if(linger_ || !gamestate_->init_side_done() || gamestate().gamedata_.phase() != game_data::PLAY) {
1139  return true;
1140  }
1141 
1142  const team& t = current_team();
1143  return !t.is_local_human() || !t.is_proxy_human();
1144 }
1145 
1147 {
1149  return;
1150  }
1151 
1152  try {
1153  play_slice();
1154  } catch(const return_to_play_side_exception&) {
1155  assert(should_return_to_play_side());
1156  }
1157 }
1158 
1160 {
1161  fire_preload();
1162 
1163  if(!gamestate().start_event_fired_) {
1164  gamestate().start_event_fired_ = true;
1168 
1169  set_scontext_synced sync;
1170 
1171  fire_prestart();
1172  if(is_regular_game_end()) {
1173  return;
1174  }
1175 
1176  for(const team& t : get_teams()) {
1177  actions::clear_shroud(t.side(), false, false);
1178  }
1179 
1180  init_gui();
1181  LOG_NG << "first_time..." << (is_skipping_replay() ? "skipping" : "no skip") << "\n";
1182 
1183  fire_start();
1184  if(is_regular_game_end()) {
1185  return;
1186  }
1187 
1188  sync.do_final_checkup();
1189  gui_->recalculate_minimap();
1190 
1191  // Initialize countdown clock.
1192  for(const team& t : get_teams()) {
1194  t.set_countdown_time(1000 * saved_game_.mp_settings().mp_countdown_init_time);
1195  }
1196  }
1197  } else {
1198  init_gui();
1200  gui_->recalculate_minimap();
1201  }
1202 
1204 }
1205 
1206 /**
1207  * Find all [endlevel]next_scenario= attributes, and add them to @a result.
1208  */
1209 static void find_next_scenarios(const config& parent, std::set<std::string>& result) {
1210  for(const auto& endlevel : parent.child_range("endlevel")) {
1211  if(endlevel.has_attribute("next_scenario")) {
1212  result.insert(endlevel["next_scenario"]);
1213  }
1214  }
1215  for(const auto cfg : parent.all_children_range()) {
1216  find_next_scenarios(cfg.cfg, result);
1217  }
1218 };
1219 
1221  // Which scenarios are reachable from the current one?
1222  std::set<std::string> possible_next_scenarios;
1223  possible_next_scenarios.insert(gamestate().gamedata_.next_scenario());
1224 
1225  // Find all "endlevel" tags that could be triggered in events
1226  config events;
1227  gamestate().events_manager_->write_events(events);
1228  find_next_scenarios(events, possible_next_scenarios);
1229 
1230  // Are we looking for [scenario]id=, [multiplayer]id= or [test]id=?
1231  const auto tagname = saved_game_.classification().get_tagname();
1232 
1233  // Of the possible routes, work out which exist.
1234  bool possible_this_is_the_last_scenario = false;
1235  std::vector<std::string> known;
1236  std::vector<std::string> unknown;
1237  for(const auto& x : possible_next_scenarios) {
1238  if(x.empty() || x == "null") {
1239  possible_this_is_the_last_scenario = true;
1240  LOG_NG << "This can be the last scenario\n";
1241  } else if(utils::contains(x, '$')) {
1242  // Assume a WML variable will be set to a correct value before the end of the scenario
1243  known.push_back(x);
1244  LOG_NG << "Variable value for next scenario '" << x << "'\n";
1245  } else if(game_config_.find_child(tagname, "id", x)) {
1246  known.push_back(x);
1247  LOG_NG << "Known next scenario '" << x << "'\n";
1248  } else {
1249  unknown.push_back(x);
1250  ERR_NG << "Unknown next scenario '" << x << "'\n";
1251  }
1252  }
1253 
1254  if(unknown.empty()) {
1255  // everything's good
1256  return;
1257  }
1258 
1259  std::string title = _("Warning: broken campaign branches");
1260  std::stringstream message;
1261 
1262  message << _n(
1263  // TRANSLATORS: This is an error that will hopefully only be seen by UMC authors and by players who have already
1264  // said "okay" to a "loading saves from an old version might not work" dialog.
1265  "The next scenario is missing, you will not be able to finish this campaign.",
1266  // TRANSLATORS: This is an error that will hopefully only be seen by UMC authors and by players who have already
1267  // said "okay" to a "loading saves from an old version might not work" dialog.
1268  "Some of the possible next scenarios are missing, you might not be able to finish this campaign.",
1269  unknown.size() + known.size() + (possible_this_is_the_last_scenario ? 1 : 0));
1270  message << "\n\n";
1271  message << _n(
1272  "Please report the following missing scenario to the campaign’s author:\n$unknown_list|",
1273  "Please report the following missing scenarios to the campaign’s author:\n$unknown_list|",
1274  unknown.size());
1275  message << "\n";
1276  message << _("Once this is fixed, you will need to restart this scenario.");
1277 
1278  std::stringstream unknown_list;
1279  for(const auto& x : unknown) {
1280  unknown_list << font::unicode_bullet << " " << x << "\n";
1281  }
1282  utils::string_map symbols;
1283  symbols["unknown_list"] = unknown_list.str();
1284  auto message_str = utils::interpolate_variables_into_string(message.str(), &symbols);
1285  ERR_NG << message_str << "\n";
1287 }
1288 
1290 {
1291  const team& viewing_team = get_teams()[gui_->viewing_team()];
1292  return gui_->viewing_team() == gui_->playing_team() && !events::commands_disabled && viewing_team.is_local_human()
1293  && !is_lingering() && !is_browsing();
1294 }
1295 
1296 std::set<std::string> play_controller::all_players() const
1297 {
1298  std::set<std::string> res = gui_->observers();
1299  for(const team& t : get_teams()) {
1300  if(t.is_human()) {
1301  res.insert(t.current_player());
1302  }
1303  }
1304 
1305  return res;
1306 }
1307 
1309 {
1310  // check for team-specific items in the scenario
1311  gui_->parse_team_overlays();
1312 
1313  do {
1315  {
1316  save_blocker blocker;
1318  if(is_regular_game_end()) {
1319  return;
1320  }
1321  }
1322  // This flag can be set by derived classes (in overridden functions).
1323  player_type_changed_ = false;
1324 
1325  statistics::reset_turn_stats(gamestate().board_.get_team(current_side()).save_id_or_number());
1326 
1327  play_side_impl();
1328 
1329  if(is_regular_game_end()) {
1330  return;
1331  }
1332  } while(player_type_changed_);
1333 
1334  // Keep looping if the type of a team (human/ai/networked) has changed mid-turn
1335  sync_end_turn();
1336 }
1337 
1339 {
1340  whiteboard_manager_->on_gamestate_change();
1341  gui_->new_turn();
1342  gui_->invalidate_game_status();
1343 
1344  LOG_NG << "turn: " << turn() << "\n";
1345 
1346  if(gui_->video().non_interactive()) {
1347  LOG_AIT << "Turn " << turn() << ":" << std::endl;
1348  }
1349 
1350  int last_player_number = gamestate_->player_number_;
1351  int next_player_number = gamestate_->next_player_number_;
1352 
1353  while(gamestate_->player_number_ <= static_cast<int>(get_teams().size())) {
1354  gamestate_->next_player_number_ = gamestate_->player_number_ + 1;
1355  next_player_number = gamestate_->next_player_number_;
1356  last_player_number = gamestate_->player_number_;
1357 
1358  // If a side is empty skip over it.
1359  if(!current_team().is_empty()) {
1360  init_side_begin();
1361  if(gamestate_->init_side_done()) {
1362  // This is the case in a reloaded game where the side was initialized before saving the game.
1363  init_side_end();
1364  }
1365 
1367  play_side();
1368 
1369  // ignore any changes to next_player_number_ that happen after the [end_turn] is sended to the server,
1370  // otherwise we will get OOS.
1371  next_player_number = gamestate_->next_player_number_;
1372  assert(next_player_number <= 2 * static_cast<int>(get_teams().size()));
1373 
1374  if(is_regular_game_end()) {
1375  return;
1376  }
1377 
1378  // note: play_side() send the [end_turn] to the sever and finish_side_turn() callsie the side turn end
1379  // events.
1380  // this means that during the side turn end events the clients think it is still the last sides turn
1381  // while the server thinks that it is already the next plyers turn. i don'T think this is a problem
1382  // though.
1383  finish_side_turn();
1384  if(is_regular_game_end()) {
1385  return;
1386  }
1387 
1388  if(gui_->video().non_interactive()) {
1389  LOG_AIT << " Player " << current_side() << ": " << current_team().villages().size() << " Villages" << std::endl;
1391  }
1392  }
1393 
1394  gamestate_->player_number_ = next_player_number;
1395  }
1396 
1397  // If the loop exits due to the last team having been processed.
1398  gamestate_->player_number_ = last_player_number;
1399 
1400  finish_turn();
1401 
1402  // Time has run out
1403  check_time_over();
1404 
1405  if(!is_regular_game_end()) {
1406  gamestate_->player_number_ = modulo(next_player_number, get_teams().size(), 1);
1407  }
1408 }
1409 
1411 {
1412  const bool time_left = gamestate().tod_manager_.next_turn(&gamestate().gamedata_);
1413 
1414  if(!time_left) {
1415  LOG_NG << "firing time over event...\n";
1417  pump().fire("time_over");
1418  LOG_NG << "done firing time over event...\n";
1419 
1420  // If turns are added while handling 'time over' event.
1421  if(gamestate().tod_manager_.is_time_left()) {
1422  return;
1423  }
1424 
1425  if(gui_->video().non_interactive()) {
1426  LOG_AIT << "time over (draw)\n";
1428  }
1429 
1430  check_victory();
1431  if(is_regular_game_end()) {
1432  return;
1433  }
1434 
1435  end_level_data e;
1436  e.proceed_to_next_level = false;
1437  e.is_victory = false;
1438  set_end_level_data(e);
1439  }
1440 }
1441 
1443  : controller_(controller)
1444 {
1446 }
1447 
1449 {
1451 }
1452 
1454 {
1455  const team& t = get_teams()[gui_->viewing_team()];
1456  static const std::string no_objectives(_("No objectives available"));
1457  std::string objectives = utils::interpolate_variables_into_string(t.objectives(), *gamestate_->get_game_data());
1458  gui2::show_transient_message(get_scenario_name(), (objectives.empty() ? no_objectives : objectives), "", true);
1460 }
1461 
1463 {
1465  const std::shared_ptr<gui::button> skip_animation_button = get_display().find_action_button("skip-animation");
1466  if(skip_animation_button) {
1467  skip_animation_button->set_check(skip_replay_);
1468  }
1469 }
std::shared_ptr< gui::button > find_action_button(const std::string &id)
Retrieves a pointer to a theme UI button.
Definition: display.cpp:832
void clear()
Clears the stack of undoable (and redoable) actions.
Definition: undo.cpp:221
play_controller * controller
Definition: resources.cpp:22
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:805
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:153
static std::string _n(const char *str1, const char *str2, int n)
Definition: gettext.hpp:97
static void log_turn_start(unsigned int side)
Definition: testing.cpp:36
static void log_turn_end(unsigned int side)
Definition: testing.cpp:41
void set_preference_display_settings()
Definition: display.cpp:45
::tod_manager * tod_manager
Definition: resources.cpp:30
static lg::log_domain log_engine_enemies("engine/enemies")
bool is_empty() const
Definition: team.hpp:271
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:978
void reset_objectives_changed() const
Definition: team.hpp:250
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:92
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:51
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:121
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:35
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:145
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:36
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:135
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:82
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:208
bool enemies_visible() const
child_itors child_range(config_key_type key)
Definition: config.cpp:344
void process_keydown_event(const SDL_Event &event) override
Process keydown (always).
bool is_idle() const
Definition: team.hpp:297
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:216
void set_gui(game_display *gui)
Definition: menu_events.hpp:55
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:349
Gather statistics important for AI testing and output them.
unit_type_data unit_types
Definition: types.cpp:1481
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:263
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:110
persist_manager * persist
Definition: resources.cpp:27
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:1414
config & set_snapshot(config snapshot)
Definition: saved_game.cpp:558
void append_children(const config &cfg)
Adds children from cfg.
Definition: config.cpp:223
static void log_draw()
Definition: testing.cpp:76
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:852
static std::string _(const char *str)
Definition: gettext.hpp:93
#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:687
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:53
game_data * gamedata
Definition: resources.cpp:23
config::attribute_value & get_variable(const std::string &varname)
throws invalid_variablename_exception if varname is no valid variable name.
Definition: game_data.cpp:63
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:74
events::menu_handler menu_handler_
void play_sound(const std::string &files, channel_group group, unsigned int repeats)
Definition: sound.cpp:1021
bool can_undo() const
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:76
void set_phase(PHASE phase)
Definition: game_data.hpp:80
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:56
saved_game & saved_game_
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:72
A small explanation about what&#39;s going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:61
scoped_savegame_snapshot(const play_controller &controller)
void init_side()
Definition: replay.cpp:216
team & get_team(int i)
Definition: game_board.hpp:97
void throw_quit_game_exception()
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
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:663
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:98
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:24
const time_of_day & get_time_of_day(int for_turn=0) const
Returns global time of day for the passed turn.
Definition: tod_manager.hpp:56
bool valid() const
Definition: location.hpp:89
bool ignore_replay_errors
int side_upkeep(int side_num) const
TEXTBOX_MODE mode() const
game_board * gameboard
Definition: resources.cpp:21
This class is the frontend of the whiteboard framework for the rest of the Wesnoth code...
Definition: manager.hpp:43
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:712
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:48
Object which temporarily resets a unit&#39;s movement.
Definition: unit.hpp:1992
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:49
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands...
std::string theme() const
replay * recorder
Definition: resources.cpp:29
void check_victory(bool &, bool &, bool &, bool &, std::set< unsigned > &, bool)
Definition: game_board.cpp:112
void set_gui(game_display *gui)
void spend_gold(const int amount)
Definition: team.hpp:220
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:299
Error used for any general game error, e.g.
Definition: game_errors.hpp:47
game_events::manager * game_events
Definition: resources.cpp:25
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:38
void calculate_healing(int side, bool update_display)
Calculates healing for all units for the given side.
Definition: heal.cpp:293
std::string login()
std::shared_ptr< wb::manager > whiteboard
Definition: resources.cpp:34
virtual ~play_controller()
bool is_proxy_human() const
Definition: team.hpp:295
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:215
void set_end_level_data(const end_level_data &data)
void end_turn(int pnum)
Definition: game_board.cpp:77
int get_path_turns() const
std::size_t i
Definition: function.cpp:967
soundsource::manager * soundsources
Definition: resources.cpp:28
const std::string & select_music(bool victory) const
void redo()
Redoes the top action on the redo stack.
Definition: undo.cpp:405
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:685
void reset_gamestate(const config &level, int replay_pos)
const t_string & objectives() const
Definition: team.hpp:252
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:1014
#define log_scope(description)
Definition: log.hpp:218
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:46
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:52
actions::undo_list & undo_stack()
std::map< std::string, std::string > get_acquaintances_nice(const std::string &filter)
Definition: game.cpp:222
const std::string unicode_bullet
Definition: constants.cpp:47
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:273
Class for replay saves (either manually or automatically).
Definition: savegame.hpp:266
config & add_child(config_key_type key)
Definition: config.cpp:514
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:481
Handling of system events.
Definition: manager.hpp:43
compression::format save_compression_format()
Definition: game.cpp:857
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:761
static bool try_block()
bool get_disallow_observers() const
Definition: team.hpp:353
static void display(std::function< void()> f)
void set_game_display(game_display *)
Definition: game_state.cpp:246
bool is_local_human() const
Definition: team.hpp:279
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:65
Various functions that implement healing of units (when a side turn starts).
const game_config_view & game_config_
map_location prev
Definition: astarsearch.cpp:66
game_classification & classification()
Definition: saved_game.hpp:55
game_board board_
Definition: game_state.hpp:47
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:72
void remove_scenario_fixes()
Definition: types.cpp:1448
static const map_location & null_location()
Definition: location.hpp:81
game_lua_kernel * lua_kernel
Definition: resources.cpp:26
config * get_next_action()
Definition: replay.cpp:620
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:68
#define e
actions::undo_list * undo_stack
Definition: resources.cpp:33
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:576
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:465
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:79
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:61
game_classification & get_classification()
bool valid() const
Definition: map.hpp:274
Class that keeps track of all the keys on the keyboard.
Definition: key.hpp:28
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
Definition: saved_game.hpp:59
void delete_all_wml_hotkeys()
deletes all wml hotkeys, should be called after a game has ended
std::vector< std::string > get_commands_list()
void write(config &cfg) const
Definition: game_state.cpp:251
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:196
void close(game_display &gui)
pathfind::manager * tunnels
Definition: resources.cpp:32
bool can_undo() const
True if there are actions that can be undone.
Definition: undo.hpp:96
static rng & default_instance()
Definition: random.cpp:74
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:67
T modulo(T num, int mod, T min=0)
Definition: math.hpp:62
void show_objectives() const
bool init_side_done_now_
Whether we did init sides in this session (false = we did init sides before we reloaded the game)...
bool remove_from_carryover_on_defeat_
void do_ai_formula(const std::string &str, int side_num, mouse_handler &mousehandler)
#define LOG_NG
virtual void sync_end_turn()