The Battle for Wesnoth  1.17.6+dev
play_controller.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2022
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 "video.hpp"
71 #include "whiteboard/manager.hpp"
72 
73 #include <functional>
74 
75 static lg::log_domain log_aitesting("ai/testing");
76 #define LOG_AIT LOG_STREAM(info, log_aitesting)
77 // If necessary, this define can be replaced with `#define LOG_AIT std::cout` to restore previous behavior
78 
79 static lg::log_domain log_engine("engine");
80 #define LOG_NG LOG_STREAM(info, log_engine)
81 #define DBG_NG LOG_STREAM(debug, log_engine)
82 #define ERR_NG LOG_STREAM(err, log_engine)
83 
84 static lg::log_domain log_display("display");
85 #define ERR_DP LOG_STREAM(err, log_display)
86 
87 static lg::log_domain log_enginerefac("enginerefac");
88 #define LOG_RG LOG_STREAM(info, log_enginerefac)
89 
90 static lg::log_domain log_engine_enemies("engine/enemies");
91 #define DBG_EE LOG_STREAM(debug, log_engine_enemies)
92 
93 /**
94  * Copies [scenario] attributes/tags that are not otherwise stored in C++ structs/clases.
95  */
96 static void copy_persistent(const config& src, config& dst)
97 {
98  static const std::set<std::string> attrs {
99  "description",
100  "name",
101  "victory_when_enemies_defeated",
102  "remove_from_carryover_on_defeat",
103  "disallow_recall",
104  "experience_modifier",
105  "require_scenario",
106  "loaded_resources"
107  };
108 
109  static const std::set<std::string> tags {
110  "terrain_graphics",
111  "modify_unit_type",
112  "lua"
113  };
114 
115  for(const std::string& attr : attrs) {
116  dst[attr] = src[attr];
117  }
118 
119  for(const std::string& tag : tags) {
120  dst.append_children(src, tag);
121  }
122 }
123 
124 static void clear_resources()
125 {
126  resources::controller = nullptr;
127  resources::filter_con = nullptr;
128  resources::gameboard = nullptr;
129  resources::gamedata = nullptr;
130  resources::lua_kernel = nullptr;
131  resources::game_events = nullptr;
132  resources::persist = nullptr;
133  resources::soundsources = nullptr;
134  resources::tod_manager = nullptr;
135  resources::tunnels = nullptr;
136  resources::undo_stack = nullptr;
137  resources::recorder = nullptr;
138  resources::whiteboard.reset();
139  resources::classification = nullptr;
140 }
141 
142 play_controller::play_controller(const config& level, saved_game& state_of_game, bool skip_replay, bool start_faded)
143  : controller_base()
144  , observer()
146  , ticks_(SDL_GetTicks())
147  , gamestate_()
148  , level_()
149  , saved_game_(state_of_game)
150  , tooltips_manager_()
151  , whiteboard_manager_()
152  , plugins_context_()
153  , labels_manager_(new font::floating_label_context())
154  , help_manager_(&game_config_)
155  , mouse_handler_(nullptr, *this)
156  , menu_handler_(nullptr, *this)
157  , hotkey_handler_(new hotkey_handler(*this, saved_game_))
158  , soundsources_manager_()
159  , persist_()
160  , gui_()
161  , xp_mod_(new unit_experience_accelerator(level["experience_modifier"].to_int(100)))
162  , statistics_context_(new statistics::scenario_context(level["name"]))
163  , replay_(new replay(state_of_game.get_replay()))
164  , skip_replay_(skip_replay)
165  , skip_story_(state_of_game.skip_story())
166  , linger_(false)
167  , init_side_done_now_(false)
168  , map_start_()
169  , start_faded_(start_faded)
170  , victory_when_enemies_defeated_(level["victory_when_enemies_defeated"].to_bool(true))
171  , remove_from_carryover_on_defeat_(level["remove_from_carryover_on_defeat"].to_bool(true))
172  , victory_music_()
173  , defeat_music_()
174  , scope_(hotkey::scope_game)
175  , ignore_replay_errors_(false)
176  , player_type_changed_(false)
177 {
178  copy_persistent(level, level_);
179 
180  for(const config& modify_unit_type : level_.child_range("modify_unit_type")) {
181  unit_types.apply_scenario_fix(modify_unit_type);
182  }
183  resources::controller = this;
186 
188 
190 
192 
193  try {
194  init(level);
195  } catch(...) {
196  clear_resources();
197  throw;
198  }
199 }
200 
202 {
204  clear_resources();
205 }
206 
208 {
209  gui2::dialogs::loading_screen::display([this, &level]() {
211 
212  LOG_NG << "initializing game_state..." << (SDL_GetTicks() - ticks());
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());
229  whiteboard_manager_.reset(new wb::manager());
231 
232  LOG_NG << "loading units..." << (SDL_GetTicks() - ticks());
235 
236  LOG_NG << "initializing theme... " << (SDL_GetTicks() - ticks());
238 
239  LOG_NG << "building terrain rules... " << (SDL_GetTicks() - ticks());
241 
242  gui_.reset(new game_display(gamestate().board_, whiteboard_manager_, *gamestate().reports_, theme(), level));
243  map_start_ = map_location(level.child_or_empty("display").child_or_empty("location"));
244  if(start_faded_) {
245  gui_->set_fade({0,0,0,255});
246  gui_->set_prevent_draw(true);
247  }
248 
249  // Ensure the loading screen doesn't end up underneath the game display
251 
252  if(!video::headless()) {
254  gui_->get_theme().modify_label("time-icon", _("time left for current turn"));
255  } else {
256  gui_->get_theme().modify_label("time-icon", _("current local time"));
257  }
258  }
259 
261  mouse_handler_.set_gui(gui_.get());
262  menu_handler_.set_gui(gui_.get());
263 
264  LOG_NG << "done initializing display... " << (SDL_GetTicks() - ticks());
265 
266  LOG_NG << "building gamestate to gui and whiteboard... " << (SDL_GetTicks() - ticks());
267  // This *needs* to be created before the show_intro and show_map_scene
268  // as that functions use the manager state_of_game
269  // Has to be done before registering any events!
272 
273  if(gamestate().first_human_team_ != -1) {
274  gui_->set_team(gamestate().first_human_team_);
275  } else if(is_observer()) {
276  // Find first team that is allowed to be observed.
277  // If not set here observer would be without fog until
278  // the first turn of observable side
279  for(const team& t : get_teams()) {
280  if(!t.get_disallow_observers()) {
281  gui_->set_team(t.side() - 1);
282  }
283  }
284  }
285 
286  init_managers();
288  // loadscreen_manager->reset();
290  gamestate().lua_kernel_->load_game(level);
291 
292  plugins_context_.reset(new plugins_context("Game"));
293  plugins_context_->set_callback("save_game", [this](const config& cfg) { save_game_auto(cfg["filename"]); }, true);
294  plugins_context_->set_callback("save_replay", [this](const config& cfg) { save_replay_auto(cfg["filename"]); }, true);
295  plugins_context_->set_callback("quit", [](const config&) { throw_quit_game_exception(); }, false);
296  plugins_context_->set_accessor_string("scenario_name", [this](config) { return get_scenario_name(); });
297  });
298 }
299 
300 void play_controller::reset_gamestate(const config& level, int replay_pos)
301 {
302  resources::gameboard = nullptr;
303  resources::gamedata = nullptr;
304  resources::tod_manager = nullptr;
305  resources::filter_con = nullptr;
306  resources::lua_kernel = nullptr;
307  resources::game_events = nullptr;
308  resources::tunnels = nullptr;
309  resources::undo_stack = nullptr;
310 
311  gui_->labels().set_team(nullptr);
312 
313  /* First destroy the old game state, then create the new one.
314  This is necessary to ensure that while the old AI manager is being destroyed,
315  all its member objects access the old manager instead of the new. */
316  gamestate_.reset();
317  gamestate_.reset(new game_state(level, *this));
318 
326 
327  gamestate_->ai_manager_.add_observer(this);
328  gamestate_->init(level, *this);
331 
332  gui_->reset_reports(*gamestate().reports_);
333  gui_->change_display_context(&gamestate().board_);
334  saved_game_.get_replay().set_pos(replay_pos);
336  gamestate().lua_kernel_->load_game(level);
337 }
338 
340 {
341  LOG_NG << "initializing managers... " << (SDL_GetTicks() - ticks());
343 
345  LOG_NG << "done initializing managers... " << (SDL_GetTicks() - ticks());
346 }
347 
349 {
350  // Run initialization scripts, even if loading from a snapshot.
351  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
352  pump().fire("preload");
353  gamestate().lua_kernel_->preload_finished();
354 }
355 
357 {
358  // pre-start events must be executed before any GUI operation,
359  // as those may cause the display to be refreshed.
361 
362  // Fire these right before prestart events, to catch only the units sides
363  // have started with.
364  for(const unit& u : get_units()) {
365  pump().fire("unit_placed", map_location(u.get_location()));
366  }
367 
368  pump().fire("prestart");
369 
370  // prestart event may modify start turn with WML, reflect any changes.
371  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
372 }
373 
375 {
376  const config cfg("side", gui_->viewing_side());
377  gamestate().lua_kernel_->run_wml_action("show_objectives", vconfig(cfg),
378  game_events::queued_event("_from_interface", "", map_location(), map_location(), config()));
379 }
380 
382 {
384  pump().fire("start");
385 
386  skip_story_ = false; // Show [message]s from now on even with --campaign-skip-story
387 
388  // start event may modify start turn with WML, reflect any changes.
389  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
390 
393 
394  // prestart and start events may modify the initial gold amount,
395  // reflect any changes.
396  for(team& tm : get_teams()) {
397  tm.set_start_gold(tm.gold());
398  }
399 
400  gamestate_->init_side_done() = false;
402 }
403 
405 {
406  gui_->begin_game();
407  gui_->update_tod();
408 }
409 
411 {
413 
414  // If we are observers we move to watch next team if it is allowed
415  if((is_observer() && !current_team().get_disallow_observers()) || (current_team().is_local_human() && !is_replay())) {
417  }
418 
419  gui_->set_playing_team(std::size_t(current_side() - 1));
420 
422 }
423 
425 {
426  //
427  // We do side init only if not done yet for a local side when we are not replaying.
428  // For all other sides it is recorded in replay and replay handler has to handle
429  // calling do_init_side() functions.
430  //
431  if(gamestate_->init_side_done()) {
432  // We already executed do_init_side this can for example happe if we reload a game,
433  // but also if we changed control of a side during it's turn
434  return;
435  }
436 
437  if(!current_team().is_local()) {
438  // We are in a mp game and execute do_init_side as soon as we receive [init_side] from the current player
439  // (see replay.cpp)
440  return;
441  }
442 
443  if(is_replay()) {
444  // We are in a replay and execute do_init_side as soon as we reach the next [init_side] in the replay data
445  // (see replay.cpp)
446  return;
447  }
448 
449  if(current_team().is_idle()) {
450  // In this case it can happen that we just gave control of this side to another player so doing init_side
451  // could lead to errors since we no longer own this side from the servers perspective.
452  // (see playturn.cpp)
453  return;
454  }
455 
457  do_init_side();
458 }
459 
461 {
462  set_scontext_synced sync;
463  log_scope("player turn");
464  // In case we might end up calling sync:network during the side turn events,
465  // and we don't want do_init_side to be called when a player drops.
466  gamestate_->init_side_done() = true;
467  init_side_done_now_ = true;
468 
469  const std::string turn_num = std::to_string(turn());
470  const std::string side_num = std::to_string(current_side());
471 
472  gamestate().gamedata_.get_variable("side_number") = current_side();
473 
474  // We might have skipped some sides because they were empty so it is not enough to check for side_num==1
475  if(!gamestate().tod_manager_.has_turn_event_fired()) {
476  pump().fire("turn_" + turn_num);
477  pump().fire("new_turn");
479  }
480 
481  pump().fire("side_turn");
482  pump().fire("side_" + side_num + "_turn");
483  pump().fire("side_turn_" + turn_num);
484  pump().fire("side_" + side_num + "_turn_" + turn_num);
485 
486  // We want to work out if units for this player should get healed,
487  // and the player should get income now.
488  // Healing/income happen if it's not the first turn of processing,
489  // or if we are loading a game.
490  if(turn() > 1) {
493 
494  // If the expense is less than the number of villages owned
495  // times the village support capacity,
496  // then we don't have to pay anything at all
497  int expense = gamestate().board_.side_upkeep(current_side()) - current_team().support();
498  if(expense > 0) {
499  current_team().spend_gold(expense);
500  }
501  }
502 
503  if(do_healing()) {
505  }
506 
507  // Do healing on every side turn except the very first side turn.
508  // (1.14 and earlier did healing whenever turn >= 2.)
509  set_do_healing(true);
510 
511  // Set resting now after the healing has been done.
512  for(unit& patient : resources::gameboard->units()) {
513  if(patient.side() == current_side()) {
514  patient.set_resting(true);
515  }
516  }
517 
518  // Prepare the undo stack.
520 
521  pump().fire("turn_refresh");
522  pump().fire("side_" + side_num + "_turn_refresh");
523  pump().fire("turn_" + turn_num + "_refresh");
524  pump().fire("side_" + side_num + "_turn_" + turn_num + "_refresh");
525 
526  // Make sure vision is accurate.
528 
529  init_side_end();
530  check_victory();
531  sync.do_final_checkup();
532 }
533 
535 {
537 
538  if(current_side() == 1 || !init_side_done_now_) {
540  }
541 
542  if(!is_skipping_replay()) {
543  gui_->invalidate_all();
544  }
545 
546  if(!is_skipping_replay() && current_team().get_scroll_to_leader() && !map_start_.valid()) {
547  gui_->scroll_to_leader(current_side(), game_display::ONSCREEN, false);
548  }
549 
551  whiteboard_manager_->on_init_side();
552 }
553 
555 {
556  config cfg = level_;
557 
558  cfg["replay_pos"] = saved_game_.get_replay().get_pos();
559  gamestate().write(cfg);
560 
561  gui_->write(cfg.add_child("display"));
562 
563  // Write the soundsources.
564  soundsources_manager_->write_sourcespecs(cfg);
565 
566  gui_->labels().write(cfg);
568 
569  return cfg;
570 }
571 
573 {
574  whiteboard_manager_->on_finish_side_turn(current_side());
575 
576  { // Block for set_scontext_synced
577  set_scontext_synced sync(1);
578  // Ending the turn commits all moves.
579  undo_stack().clear();
581  const std::string turn_num = std::to_string(turn());
582  const std::string side_num = std::to_string(current_side());
583 
584  // Clear shroud, in case units had been slowed for the turn.
586 
587  pump().fire("side_turn_end");
588  pump().fire("side_" + side_num + "_turn_end");
589  pump().fire("side_turn_" + turn_num + "_end");
590  pump().fire("side_" + side_num + "_turn_" + turn_num + "_end");
591  // This is where we refog, after all of a side's events are done.
593  check_victory();
594  sync.do_final_checkup();
595  }
596 
598  gamestate_->init_side_done() = false;
599 }
600 
602 {
603  set_scontext_synced sync(2);
604  const std::string turn_num = std::to_string(turn());
605  pump().fire("turn_end");
606  pump().fire("turn_" + turn_num + "_end");
607  sync.do_final_checkup();
608 }
609 
611 {
612  // If we aren't using fog/shroud, this is easy :)
613  if(current_team().uses_fog() == false && current_team().uses_shroud() == false) {
614  return true;
615  }
616 
617  // See if any enemies are visible
618  for(const unit& u : get_units()) {
619  if(current_team().is_enemy(u.side()) && !gui_->fogged(u.get_location())) {
620  return true;
621  }
622  }
623 
624  return false;
625 }
626 
628 {
629  if(menu_handler_.get_textbox().active() == false) {
630  return;
631  }
632 
633  const std::string str = menu_handler_.get_textbox().box()->text();
634  const unsigned int team_num = current_side();
635  events::mouse_handler& mousehandler = mouse_handler_;
636 
637  switch(menu_handler_.get_textbox().mode()) {
638  case gui::TEXTBOX_SEARCH:
642  break;
645  menu_handler_.get_textbox().close(); // need to close that one after executing do_speak() !
646  break;
651  break;
652  case gui::TEXTBOX_AI:
655  menu_handler_.do_ai_formula(str, team_num, mousehandler);
656  break;
657  default:
659  ERR_DP << "unknown textbox mode";
660  }
661 }
662 
664 {
665  if(menu_handler_.get_textbox().active() == false) {
666  return;
667  }
668 
671  // Not handling messages to avoid spam
672  return;
673  }
674 
675  const std::string str = menu_handler_.get_textbox().box()->text();
676  const std::vector<std::string>& command_history = menu_handler_.get_textbox().command_history();
677 
678  auto prev = std::find(command_history.begin(), command_history.end(), str);
679 
680  if (prev != command_history.end())
681  {
682  if(up) {
683  if(prev != command_history.begin()) {
684  menu_handler_.get_textbox().box()->set_text(*--prev);
685  }
686  } else {
687  if(++prev != command_history.end()) {
688  menu_handler_.get_textbox().box()->set_text(*prev);
689  } else {
690  menu_handler_.get_textbox().box()->set_text("");
691  }
692  }
693  } else if (up) {
694  if(command_history.size() > 0) {
695  menu_handler_.get_textbox().box()->set_text(*--prev);
696  }
697  if(!str.empty()) {
699  }
700  }
701 }
702 
704 {
706 
707  std::set<std::string> dictionary;
708  switch(mode) {
709  case gui::TEXTBOX_SEARCH: {
710  for(const unit& u : get_units()) {
711  const map_location& loc = u.get_location();
712  if(!gui_->fogged(loc) && !(get_teams()[gui_->viewing_team()].is_enemy(u.side()) && u.invisible(loc)))
713  dictionary.insert(u.name());
714  }
715  // TODO List map labels
716  break;
717  }
718  case gui::TEXTBOX_COMMAND: {
719  std::vector<std::string> commands = menu_handler_.get_commands_list();
720  dictionary.insert(commands.begin(), commands.end());
721  [[fallthrough]]; // we also want player names from the next case
722  }
723  case gui::TEXTBOX_MESSAGE: {
724  for(const team& t : get_teams()) {
725  if(!t.is_empty())
726  dictionary.insert(t.current_player());
727  }
728 
729  // Add observers
730  for(const std::string& o : gui_->observers()) {
731  dictionary.insert(o);
732  }
733 
734  // Add nicks who whispered you
735  for(const std::string& w : gui_->get_chat_manager().whisperers()) {
736  dictionary.insert(w);
737  }
738 
739  // Add nicks from friendlist
740  const std::map<std::string, std::string> friends = preferences::get_acquaintances_nice("friend");
741 
742  for(std::map<std::string, std::string>::const_iterator iter = friends.begin(); iter != friends.end(); ++iter) {
743  dictionary.insert((*iter).first);
744  }
745 
746  // Exclude own nick from tab-completion.
747  // NOTE why ?
748  dictionary.erase(preferences::login());
749  break;
750  }
751 
752  default:
753  ERR_DP << "unknown textbox mode";
754  } // switch(mode)
755 
756  menu_handler_.get_textbox().tab(dictionary);
757 }
758 
760 {
761  if(get_teams().size() == 0) {
762  throw game::game_error("The scenario has no sides defined");
763  }
764 
765  assert(gamestate().board_.has_team(current_side()));
767 }
768 
770 {
771  if(get_teams().size() == 0) {
772  throw game::game_error("The scenario has no sides defined");
773  }
774 
775  assert(gamestate().board_.has_team(current_side()));
777 }
778 
779 bool play_controller::is_team_visible(int team_num, bool observer) const
780 {
781  const team& t = gamestate().board_.get_team(team_num);
782  if(observer) {
783  return !t.get_disallow_observers() && !t.is_empty();
784  } else {
785  return t.is_local_human() && !t.is_idle();
786  }
787 }
788 
790 {
791  assert(current_side() <= static_cast<int>(get_teams().size()));
792  const int num_teams = get_teams().size();
793  const bool observer = is_observer();
794 
795  for(int i = 0; i < num_teams; i++) {
796  const int team_num = modulo(current_side() - i, num_teams, 1);
797  if(is_team_visible(team_num, observer)) {
798  return team_num;
799  }
800  }
801 
802  return 0;
803 }
804 
806 {
807  return mouse_handler_;
808 }
809 
810 std::shared_ptr<wb::manager> play_controller::get_whiteboard() const
811 {
812  return whiteboard_manager_;
813 }
814 
816 {
817  return saved_game_.mp_settings();
818 }
819 
821 {
822  return saved_game_.classification();
823 }
824 
826 {
827  return *gui_;
828 }
829 
831 {
832  return !menu_handler_.get_textbox().active();
833 }
834 
836 {
837  if(event.key.keysym.sym == SDLK_ESCAPE) {
839  } else if(event.key.keysym.sym == SDLK_TAB) {
840  tab();
841  } else if(event.key.keysym.sym == SDLK_UP) {
843  } else if(event.key.keysym.sym == SDLK_DOWN) {
845  } else if(event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_KP_ENTER) {
846  enter_textbox();
847  }
848 }
849 
850 void play_controller::process_keydown_event(const SDL_Event& event)
851 {
852  if(event.key.keysym.sym == SDLK_TAB) {
853  whiteboard_manager_->set_invert_behavior(true);
854  }
855 }
856 
857 void play_controller::process_keyup_event(const SDL_Event& event)
858 {
859  // If the user has pressed 1 through 9, we want to show
860  // how far the unit can move in that many turns
861  if(event.key.keysym.sym >= '1' && event.key.keysym.sym <= '9') {
862  const int new_path_turns = (event.type == SDL_KEYDOWN) ? event.key.keysym.sym - '1' : 0;
863 
864  if(new_path_turns != mouse_handler_.get_path_turns()) {
865  mouse_handler_.set_path_turns(new_path_turns);
866 
868 
869  if(u.valid()) {
870  // if it's not the unit's turn, we reset its moves
871  unit_movement_resetter move_reset(*u, u->side() != current_side());
872 
874  *u, false, true, get_teams()[gui_->viewing_team()], mouse_handler_.get_path_turns()));
875 
876  gui_->highlight_reach(mouse_handler_.current_paths());
877  } else {
879  }
880  }
881  } else if(event.key.keysym.sym == SDLK_TAB) {
882  CKey keys;
883  if(!keys[SDLK_TAB]) {
884  whiteboard_manager_->set_invert_behavior(false);
885  }
886  }
887 }
888 
890 {
891  assert(replay_);
892  return *replay_.get();
893 }
894 
896 {
898  // Saving while an event is running isn't supported
899  // because it may lead to expired event handlers being saved.
900  assert(!gamestate().events_manager_->is_event_running());
901 
903  scoped_savegame_snapshot snapshot(*this);
906  } else {
908  }
909 }
910 
911 void play_controller::save_game_auto(const std::string& filename)
912 {
915 
916  scoped_savegame_snapshot snapshot(*this);
918  save.save_game_automatic(false, filename);
919  }
920 }
921 
923 {
928  } else {
930  }
931 }
932 
933 void play_controller::save_replay_auto(const std::string& filename)
934 {
938  save.save_game_automatic(false, filename);
939  }
940 }
941 
943 {
947  } else {
949  }
950 }
951 
953 {
955  load.load_game_ingame();
956 }
957 
959 {
961  undo_stack().undo();
962 }
963 
965 {
967  undo_stack().redo();
968 }
969 
971 {
973 }
974 
976 {
978 }
979 
980 const std::string& play_controller::select_music(bool victory) const
981 {
982  const std::vector<std::string>& music_list = victory
983  ? (gamestate_->get_game_data()->get_victory_music().empty()
985  : gamestate_->get_game_data()->get_victory_music())
986  : (gamestate_->get_game_data()->get_defeat_music().empty()
988  : gamestate_->get_game_data()->get_defeat_music());
989 
990  if(music_list.empty()) {
991  // Since this function returns a reference, we can't return a temporary empty string.
992  static const std::string empty_str = "";
993  return empty_str;
994  }
995 
996  return music_list[randomness::rng::default_instance().get_random_int(0, music_list.size() - 1)];
997 }
998 
1000 {
1001  if(linger_) {
1002  return;
1003  }
1004 
1005  if(is_regular_game_end()) {
1006  return;
1007  }
1008 
1009  bool continue_level, found_player, found_network_player, invalidate_all;
1010  std::set<unsigned> not_defeated;
1011 
1013  continue_level,
1014  found_player,
1015  found_network_player,
1016  invalidate_all,
1017  not_defeated,
1019  );
1020 
1021  if(invalidate_all) {
1022  gui_->invalidate_all();
1023  }
1024 
1025  if(continue_level) {
1026  return;
1027  }
1028 
1029  if(found_player || found_network_player) {
1030  pump().fire("enemies_defeated");
1031  if(is_regular_game_end()) {
1032  return;
1033  }
1034  }
1035 
1036  DBG_EE << "victory_when_enemies_defeated: " << victory_when_enemies_defeated_;
1037  DBG_EE << "found_player: " << found_player;
1038  DBG_EE << "found_network_player: " << found_network_player;
1039 
1040  if(!victory_when_enemies_defeated_ && (found_player || found_network_player)) {
1041  // This level has asked not to be ended by this condition.
1042  return;
1043  }
1044 
1045  if(video::headless()) {
1046  LOG_AIT << "winner: ";
1047  for(unsigned l : not_defeated) {
1049  if(ai.empty())
1050  ai = "default ai";
1051  LOG_AIT << l << " (using " << ai << ") ";
1052  }
1053 
1054  LOG_AIT;
1055  ai_testing::log_victory(not_defeated);
1056  }
1057 
1058  DBG_EE << "throwing end level exception...";
1059  // Also proceed to the next scenario when another player survived.
1060  end_level_data el_data;
1061  el_data.proceed_to_next_level = found_player || found_network_player;
1062  el_data.is_victory = found_player;
1063  set_end_level_data(el_data);
1064 }
1065 
1066 void play_controller::process_oos(const std::string& msg) const
1067 {
1068  if(video::headless()) {
1069  throw game::game_error(msg);
1070  }
1071 
1073  return;
1074  }
1075 
1076  std::stringstream message;
1077  message << _("The game is out of sync. It might not make much sense to continue. Do you want to save your game?");
1078  message << "\n\n" << _("Error details:") << "\n\n" << msg;
1079 
1080  scoped_savegame_snapshot snapshot(*this);
1082  save.save_game_interactive(message.str(), savegame::savegame::YES_NO); // can throw quit_game_exception
1083 }
1084 
1085 void play_controller::update_gui_to_player(const int team_index, const bool observe)
1086 {
1087  gui_->set_team(team_index, observe);
1088  gui_->recalculate_minimap();
1089  gui_->invalidate_all();
1090 }
1091 
1093 {
1094  scoped_savegame_snapshot snapshot(*this);
1097 }
1098 
1099 void play_controller::do_consolesave(const std::string& filename)
1100 {
1101  scoped_savegame_snapshot snapshot(*this);
1103  save.save_game_automatic(true, filename);
1104 }
1105 
1107 {
1108  // note: this writes to level_ if this is not a replay.
1110 }
1111 
1113 {
1114  return gamestate().events_manager_->pump();
1115 }
1116 
1118 {
1119  return ticks_;
1120 }
1121 
1123 {
1124  return soundsources_manager_.get();
1125 }
1126 
1128 {
1129  return plugins_context_.get();
1130 }
1131 
1133 {
1134  return hotkey_handler_.get();
1135 }
1136 
1138 {
1139  if(linger_ || !gamestate_->init_side_done() || gamestate().gamedata_.phase() != game_data::PLAY) {
1140  return true;
1141  }
1142 
1143  const team& t = current_team();
1144  return !t.is_local_human() || !t.is_proxy_human();
1145 }
1146 
1148 {
1150  return;
1151  }
1152 
1153  try {
1154  play_slice();
1155  } catch(const return_to_play_side_exception&) {
1156  assert(should_return_to_play_side());
1157  }
1158 }
1159 
1161 {
1162  fire_preload();
1163 
1164  if(!gamestate().start_event_fired_) {
1165  gamestate().start_event_fired_ = true;
1169 
1170  set_scontext_synced sync;
1171 
1172  fire_prestart();
1173  if(is_regular_game_end()) {
1174  return;
1175  }
1176 
1177  for(const team& t : get_teams()) {
1178  actions::clear_shroud(t.side(), false, false);
1179  }
1180 
1181  init_gui();
1182  LOG_NG << "first_time..." << (is_skipping_replay() ? "skipping" : "no skip");
1183 
1184  fire_start();
1185  if(is_regular_game_end()) {
1186  return;
1187  }
1188 
1189  sync.do_final_checkup();
1190  gui_->recalculate_minimap();
1191 
1192  // Initialize countdown clock.
1193  for(const team& t : get_teams()) {
1195  t.set_countdown_time(1000 * saved_game_.mp_settings().mp_countdown_init_time);
1196  }
1197  }
1198  } else {
1199  init_gui();
1201  gui_->recalculate_minimap();
1202  }
1203 
1205 }
1206 
1207 /**
1208  * Find all [endlevel]next_scenario= attributes, and add them to @a result.
1209  */
1210 static void find_next_scenarios(const config& parent, std::set<std::string>& result) {
1211  for(const auto& endlevel : parent.child_range("endlevel")) {
1212  if(endlevel.has_attribute("next_scenario")) {
1213  result.insert(endlevel["next_scenario"]);
1214  }
1215  }
1216  for(const auto cfg : parent.all_children_range()) {
1217  find_next_scenarios(cfg.cfg, result);
1218  }
1219 };
1220 
1222  // Which scenarios are reachable from the current one?
1223  std::set<std::string> possible_next_scenarios;
1224  possible_next_scenarios.insert(gamestate().gamedata_.next_scenario());
1225 
1226  // Find all "endlevel" tags that could be triggered in events
1227  config events;
1228  gamestate().events_manager_->write_events(events);
1229  find_next_scenarios(events, possible_next_scenarios);
1230 
1231  // Are we looking for [scenario]id=, [multiplayer]id= or [test]id=?
1232  const auto tagname = saved_game_.classification().get_tagname();
1233 
1234  // Of the possible routes, work out which exist.
1235  bool possible_this_is_the_last_scenario = false;
1236  std::vector<std::string> known;
1237  std::vector<std::string> unknown;
1238  for(const auto& x : possible_next_scenarios) {
1239  if(x.empty() || x == "null") {
1240  possible_this_is_the_last_scenario = true;
1241  LOG_NG << "This can be the last scenario";
1242  } else if(utils::contains(x, '$')) {
1243  // Assume a WML variable will be set to a correct value before the end of the scenario
1244  known.push_back(x);
1245  LOG_NG << "Variable value for next scenario '" << x << "'";
1246  } else if(game_config_.find_child(tagname, "id", x)) {
1247  known.push_back(x);
1248  LOG_NG << "Known next scenario '" << x << "'";
1249  } else {
1250  unknown.push_back(x);
1251  ERR_NG << "Unknown next scenario '" << x << "'";
1252  }
1253  }
1254 
1255  if(unknown.empty()) {
1256  // everything's good
1257  return;
1258  }
1259 
1260  std::string title = _("Warning: broken campaign branches");
1261  std::stringstream message;
1262 
1263  message << _n(
1264  // TRANSLATORS: This is an error that will hopefully only be seen by UMC authors and by players who have already
1265  // said "okay" to a "loading saves from an old version might not work" dialog.
1266  "The next scenario is missing, you will not be able to finish this campaign.",
1267  // TRANSLATORS: This is an error that will hopefully only be seen by UMC authors and by players who have already
1268  // said "okay" to a "loading saves from an old version might not work" dialog.
1269  "Some of the possible next scenarios are missing, you might not be able to finish this campaign.",
1270  unknown.size() + known.size() + (possible_this_is_the_last_scenario ? 1 : 0));
1271  message << "\n\n";
1272  message << _n(
1273  "Please report the following missing scenario to the campaign’s author:\n$unknown_list|",
1274  "Please report the following missing scenarios to the campaign’s author:\n$unknown_list|",
1275  unknown.size());
1276  message << "\n";
1277  message << _("Once this is fixed, you will need to restart this scenario.");
1278 
1279  std::stringstream unknown_list;
1280  for(const auto& x : unknown) {
1281  unknown_list << font::unicode_bullet << " " << x << "\n";
1282  }
1283  utils::string_map symbols;
1284  symbols["unknown_list"] = unknown_list.str();
1285  auto message_str = utils::interpolate_variables_into_string(message.str(), &symbols);
1286  ERR_NG << message_str;
1288 }
1289 
1291 {
1292  const team& viewing_team = get_teams()[gui_->viewing_team()];
1293  return gui_->viewing_team() == gui_->playing_team() && !events::commands_disabled && viewing_team.is_local_human()
1294  && !is_lingering() && !is_browsing();
1295 }
1296 
1297 std::set<std::string> play_controller::all_players() const
1298 {
1299  std::set<std::string> res = gui_->observers();
1300  for(const team& t : get_teams()) {
1301  if(t.is_human()) {
1302  res.insert(t.current_player());
1303  }
1304  }
1305 
1306  return res;
1307 }
1308 
1310 {
1311  // check for team-specific items in the scenario
1312  gui_->parse_team_overlays();
1313 
1314  do {
1316  {
1317  save_blocker blocker;
1319  if(is_regular_game_end()) {
1320  return;
1321  }
1322  }
1323  // This flag can be set by derived classes (in overridden functions).
1324  player_type_changed_ = false;
1325 
1326  statistics::reset_turn_stats(gamestate().board_.get_team(current_side()).save_id_or_number());
1327 
1328  play_side_impl();
1329 
1330  if(is_regular_game_end()) {
1331  return;
1332  }
1333  } while(player_type_changed_);
1334 
1335  // Keep looping if the type of a team (human/ai/networked) has changed mid-turn
1336  sync_end_turn();
1337 }
1338 
1340 {
1341  whiteboard_manager_->on_gamestate_change();
1342  gui_->new_turn();
1343  gui_->invalidate_game_status();
1344 
1345  LOG_NG << "turn: " << turn();
1346 
1347  if(video::headless()) {
1348  LOG_AIT << "Turn " << turn() << ":";
1349  }
1350 
1351  int last_player_number = gamestate_->player_number_;
1352  int next_player_number = gamestate_->next_player_number_;
1353 
1354  while(gamestate_->player_number_ <= static_cast<int>(get_teams().size())) {
1355  gamestate_->next_player_number_ = gamestate_->player_number_ + 1;
1356  next_player_number = gamestate_->next_player_number_;
1357  last_player_number = gamestate_->player_number_;
1358 
1359  // If a side is empty skip over it.
1360  if(!current_team().is_empty()) {
1361  init_side_begin();
1362  if(gamestate_->init_side_done()) {
1363  // This is the case in a reloaded game where the side was initialized before saving the game.
1364  init_side_end();
1365  }
1366 
1368  play_side();
1369 
1370  // ignore any changes to next_player_number_ that happen after the [end_turn] is sended to the server,
1371  // otherwise we will get OOS.
1372  next_player_number = gamestate_->next_player_number_;
1373  assert(next_player_number <= 2 * static_cast<int>(get_teams().size()));
1374 
1375  if(is_regular_game_end()) {
1376  return;
1377  }
1378 
1379  // note: play_side() send the [end_turn] to the sever and finish_side_turn() callsie the side turn end
1380  // events.
1381  // this means that during the side turn end events the clients think it is still the last sides turn
1382  // while the server thinks that it is already the next plyers turn. i don'T think this is a problem
1383  // though.
1384  finish_side_turn();
1385  if(is_regular_game_end()) {
1386  return;
1387  }
1388 
1389  if(video::headless()) {
1390  LOG_AIT << " Player " << current_side() << ": " << current_team().villages().size() << " Villages";
1392  }
1393  }
1394 
1395  gamestate_->player_number_ = next_player_number;
1396  }
1397 
1398  // If the loop exits due to the last team having been processed.
1399  gamestate_->player_number_ = last_player_number;
1400 
1401  finish_turn();
1402 
1403  // Time has run out
1404  check_time_over();
1405 
1406  if(!is_regular_game_end()) {
1407  gamestate_->player_number_ = modulo(next_player_number, get_teams().size(), 1);
1408  }
1409 }
1410 
1412 {
1413  const bool time_left = gamestate().tod_manager_.next_turn(&gamestate().gamedata_);
1414 
1415  if(!time_left) {
1416  LOG_NG << "firing time over event...";
1418  pump().fire("time_over");
1419  LOG_NG << "done firing time over event...";
1420 
1421  // If turns are added while handling 'time over' event.
1422  if(gamestate().tod_manager_.is_time_left()) {
1423  return;
1424  }
1425 
1426  if(video::headless()) {
1427  LOG_AIT << "time over (draw)";
1429  }
1430 
1431  check_victory();
1432  if(is_regular_game_end()) {
1433  return;
1434  }
1435 
1436  end_level_data e;
1437  e.proceed_to_next_level = false;
1438  e.is_victory = false;
1439  set_end_level_data(e);
1440  }
1441 }
1442 
1444  : controller_(controller)
1445 {
1447 }
1448 
1450 {
1452 }
1453 
1455 {
1456  const team& t = get_teams()[gui_->viewing_team()];
1457  static const std::string no_objectives(_("No objectives available"));
1458  std::string objectives = utils::interpolate_variables_into_string(t.objectives(), *gamestate_->get_game_data());
1459  gui2::show_transient_message(get_scenario_name(), (objectives.empty() ? no_objectives : objectives), "", true);
1461 }
1462 
1464 {
1466  const std::shared_ptr<gui::button> skip_animation_button = get_display().find_action_button("skip-animation");
1467  if(skip_animation_button) {
1468  skip_animation_button->set_check(skip_replay_);
1469  }
1470 }
std::shared_ptr< gui::button > find_action_button(const std::string &id)
Retrieves a pointer to a theme UI button.
Definition: display.cpp:826
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:792
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:151
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
::tod_manager * tod_manager
Definition: resources.cpp:30
static lg::log_domain log_engine_enemies("engine/enemies")
bool is_empty() const
Definition: team.hpp:247
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:226
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
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: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: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()
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...
play_controller(const config &level, saved_game &state_of_game, bool skip_replay, bool start_faded=false)
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:272
const pathfind::paths & current_paths() const
void do_command(const std::string &str)
void set_do_healing(bool do_healing)
static void progress(loading_stage stage=loading_stage::none)
Report what is being loaded to the loading screen.
virtual void update_viewing_player()=0
gui::floating_textbox & get_textbox()
void new_turn()
Definition: team.hpp:192
void set_gui(game_display *gui)
Definition: menu_events.hpp:55
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:1465
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:1398
config & set_snapshot(config snapshot)
Definition: saved_game.cpp:559
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:866
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
Keyboard shortcuts for game actions.
void memorize_command(const std::string &command)
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:75
events::menu_handler menu_handler_
void invalidate_all()
Mark the entire screen as requiring redraw.
void play_sound(const std::string &files, channel_group group, unsigned int repeats)
Definition: sound.cpp:1035
bool can_undo() const
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition: general.hpp:84
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:75
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:219
team & get_team(int i)
Definition: game_board.hpp:98
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:666
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
void show_transient_message(const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup)
Shows a transient message to the user.
const time_of_day & get_time_of_day(int for_turn=0) const
Returns global time of day for the passed turn.
Definition: tod_manager.hpp: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:702
bool headless()
The game is running headless.
Definition: video.cpp:107
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:1991
constexpr T modulo(T num, int mod, T min=0)
Definition: math.hpp:62
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:196
constexpr uint32_t scope_game
std::shared_ptr< wb::manager > get_whiteboard() const
static void add_color_info(const game_config_view &v, bool build_defaults)
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
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:270
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:191
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:687
void reset_gamestate(const config &level, int replay_pos)
const t_string & objectives() const
Definition: team.hpp:228
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:1001
#define log_scope(description)
Definition: log.hpp:235
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:214
const std::string unicode_bullet
Definition: constants.cpp:47
bool can_redo() const
static lg::log_domain log_enginerefac("enginerefac")
std::string observer
bool is_local() const
Definition: team.hpp:249
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:402
Handling of system events.
Definition: manager.hpp:43
compression::format save_compression_format()
Definition: game.cpp:844
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:751
static bool try_block()
bool get_disallow_observers() const
Definition: team.hpp:328
static void display(std::function< void()> f)
void set_game_display(game_display *)
Definition: game_state.cpp:247
bool is_local_human() const
Definition: team.hpp:255
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:1432
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:623
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
void update_savegame_snapshot() const
void remove_snapshot()
Definition: saved_game.cpp:577
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:60
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
std::vector< std::string > get_commands_list()
void write(config &cfg) const
Definition: game_state.cpp:252
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:172
static void raise()
Raise the loading screen to the top of the draw stack.
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
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()