The Battle for Wesnoth  1.17.10+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  DBG_NG << "Caught exception initializing level: " << utils::get_unknown_exception_type();
197  clear_resources();
198  throw;
199  }
200 }
201 
203 {
205  clear_resources();
206 }
207 
209 {
210  gui2::dialogs::loading_screen::display([this, &level]() {
212 
213  LOG_NG << "initializing game_state..." << (SDL_GetTicks() - ticks());
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());
230  whiteboard_manager_.reset(new wb::manager());
232 
233  LOG_NG << "loading units..." << (SDL_GetTicks() - ticks());
236 
237  LOG_NG << "initializing theme... " << (SDL_GetTicks() - ticks());
239 
240  LOG_NG << "building terrain rules... " << (SDL_GetTicks() - ticks());
242 
243  gui_.reset(new game_display(gamestate().board_, whiteboard_manager_, *gamestate().reports_, theme(), level));
244  map_start_ = map_location(level.child_or_empty("display").child_or_empty("location"));
245  if(start_faded_) {
246  gui_->set_fade({0,0,0,255});
247  gui_->set_prevent_draw(true);
248  }
249 
250  // Ensure the loading screen doesn't end up underneath the game display
252 
253  if(!video::headless()) {
255  gui_->get_theme().modify_label("time-icon", _("time left for current turn"));
256  } else {
257  gui_->get_theme().modify_label("time-icon", _("current local time"));
258  }
259  }
260 
262  mouse_handler_.set_gui(gui_.get());
263  menu_handler_.set_gui(gui_.get());
264 
265  LOG_NG << "done initializing display... " << (SDL_GetTicks() - ticks());
266 
267  LOG_NG << "building gamestate to gui and whiteboard... " << (SDL_GetTicks() - ticks());
268  // This *needs* to be created before the show_intro and show_map_scene
269  // as that functions use the manager state_of_game
270  // Has to be done before registering any events!
273 
274  if(gamestate().first_human_team_ != -1) {
275  gui_->set_team(gamestate().first_human_team_);
276  } else if(is_observer()) {
277  // Find first team that is allowed to be observed.
278  // If not set here observer would be without fog until
279  // the first turn of observable side
280  for(const team& t : get_teams()) {
281  if(!t.get_disallow_observers()) {
282  gui_->set_team(t.side() - 1);
283  }
284  }
285  }
286 
287  init_managers();
289  // loadscreen_manager->reset();
291  gamestate().lua_kernel_->load_game(level);
292 
293  plugins_context_.reset(new plugins_context("Game"));
294  plugins_context_->set_callback("save_game", [this](const config& cfg) { save_game_auto(cfg["filename"]); }, true);
295  plugins_context_->set_callback("save_replay", [this](const config& cfg) { save_replay_auto(cfg["filename"]); }, true);
296  plugins_context_->set_callback("quit", [](const config&) { throw_quit_game_exception(); }, false);
297  plugins_context_->set_accessor_string("scenario_name", [this](config) { return get_scenario_name(); });
298  });
299 }
300 
301 void play_controller::reset_gamestate(const config& level, int replay_pos)
302 {
303  resources::gameboard = nullptr;
304  resources::gamedata = nullptr;
305  resources::tod_manager = nullptr;
306  resources::filter_con = nullptr;
307  resources::lua_kernel = nullptr;
308  resources::game_events = nullptr;
309  resources::tunnels = nullptr;
310  resources::undo_stack = nullptr;
311 
312  gui_->labels().set_team(nullptr);
313 
314  /* First destroy the old game state, then create the new one.
315  This is necessary to ensure that while the old AI manager is being destroyed,
316  all its member objects access the old manager instead of the new. */
317  gamestate_.reset();
318  gamestate_.reset(new game_state(level, *this));
319 
327 
328  gamestate_->ai_manager_.add_observer(this);
329  gamestate_->init(level, *this);
332 
333  gui_->reset_reports(*gamestate().reports_);
334  gui_->change_display_context(&gamestate().board_);
335  saved_game_.get_replay().set_pos(replay_pos);
337  gamestate().lua_kernel_->load_game(level);
338 }
339 
341 {
342  LOG_NG << "initializing managers... " << (SDL_GetTicks() - ticks());
344 
346  LOG_NG << "done initializing managers... " << (SDL_GetTicks() - ticks());
347 }
348 
350 {
351  // Run initialization scripts, even if loading from a snapshot.
352  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
353  pump().fire("preload");
354  gamestate().lua_kernel_->preload_finished();
355 }
356 
358 {
359  // pre-start events must be executed before any GUI operation,
360  // as those may cause the display to be refreshed.
362 
363  // Fire these right before prestart events, to catch only the units sides
364  // have started with.
365  for(const unit& u : get_units()) {
366  pump().fire("unit_placed", map_location(u.get_location()));
367  }
368 
369  pump().fire("prestart");
370 
371  // prestart event may modify start turn with WML, reflect any changes.
372  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
373 }
374 
376 {
377  const config cfg("side", gui_->viewing_side());
378  gamestate().lua_kernel_->run_wml_action("show_objectives", vconfig(cfg),
379  game_events::queued_event("_from_interface", "", map_location(), map_location(), config()));
380 }
381 
383 {
385  pump().fire("start");
386 
387  skip_story_ = false; // Show [message]s from now on even with --campaign-skip-story
388 
389  // start event may modify start turn with WML, reflect any changes.
390  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
391 
394 
395  // prestart and start events may modify the initial gold amount,
396  // reflect any changes.
397  for(team& tm : get_teams()) {
398  tm.set_start_gold(tm.gold());
399  }
400 
401  gamestate_->init_side_done() = false;
403 }
404 
406 {
407  gui_->begin_game();
408  gui_->update_tod();
409 }
410 
412 {
414 
415  // If we are observers we move to watch next team if it is allowed
416  if((is_observer() && !current_team().get_disallow_observers()) || (current_team().is_local_human() && !is_replay())) {
418  }
419 
420  gui_->set_playing_team(std::size_t(current_side() - 1));
421 
423 }
424 
426 {
427  //
428  // We do side init only if not done yet for a local side when we are not replaying.
429  // For all other sides it is recorded in replay and replay handler has to handle
430  // calling do_init_side() functions.
431  //
432  if(gamestate_->init_side_done()) {
433  // We already executed do_init_side this can for example happe if we reload a game,
434  // but also if we changed control of a side during it's turn
435  return;
436  }
437 
438  if(!current_team().is_local()) {
439  // We are in a mp game and execute do_init_side as soon as we receive [init_side] from the current player
440  // (see replay.cpp)
441  return;
442  }
443 
444  if(is_replay()) {
445  // We are in a replay and execute do_init_side as soon as we reach the next [init_side] in the replay data
446  // (see replay.cpp)
447  return;
448  }
449 
450  if(current_team().is_idle()) {
451  // In this case it can happen that we just gave control of this side to another player so doing init_side
452  // could lead to errors since we no longer own this side from the servers perspective.
453  // (see playturn.cpp)
454  return;
455  }
456 
458  do_init_side();
459 }
460 
462 {
463  set_scontext_synced sync;
464  log_scope("player turn");
465  // In case we might end up calling sync:network during the side turn events,
466  // and we don't want do_init_side to be called when a player drops.
467  gamestate_->init_side_done() = true;
468  init_side_done_now_ = true;
469 
470  const std::string turn_num = std::to_string(turn());
471  const std::string side_num = std::to_string(current_side());
472 
473  gamestate().gamedata_.get_variable("side_number") = current_side();
474 
475  // We might have skipped some sides because they were empty so it is not enough to check for side_num==1
476  if(!gamestate().tod_manager_.has_turn_event_fired()) {
477  pump().fire("turn_" + turn_num);
478  pump().fire("new_turn");
480  }
481 
482  pump().fire("side_turn");
483  pump().fire("side_" + side_num + "_turn");
484  pump().fire("side_turn_" + turn_num);
485  pump().fire("side_" + side_num + "_turn_" + turn_num);
486 
487  // We want to work out if units for this player should get healed,
488  // and the player should get income now.
489  // Healing/income happen if it's not the first turn of processing,
490  // or if we are loading a game.
491  if(turn() > 1) {
494 
495  // If the expense is less than the number of villages owned
496  // times the village support capacity,
497  // then we don't have to pay anything at all
498  int expense = gamestate().board_.side_upkeep(current_side()) - current_team().support();
499  if(expense > 0) {
500  current_team().spend_gold(expense);
501  }
502  }
503 
504  if(do_healing()) {
506  }
507 
508  // Do healing on every side turn except the very first side turn.
509  // (1.14 and earlier did healing whenever turn >= 2.)
510  set_do_healing(true);
511 
512  // Set resting now after the healing has been done.
513  for(unit& patient : resources::gameboard->units()) {
514  if(patient.side() == current_side()) {
515  patient.set_resting(true);
516  }
517  }
518 
519  // Prepare the undo stack.
521 
522  pump().fire("turn_refresh");
523  pump().fire("side_" + side_num + "_turn_refresh");
524  pump().fire("turn_" + turn_num + "_refresh");
525  pump().fire("side_" + side_num + "_turn_" + turn_num + "_refresh");
526 
527  // Make sure vision is accurate.
529 
530  init_side_end();
531  check_victory();
532  sync.do_final_checkup();
533 }
534 
536 {
538 
539  if(current_side() == 1 || !init_side_done_now_) {
541  }
542 
543  if(!is_skipping_replay()) {
544  gui_->invalidate_all();
545  }
546 
547  if(!is_skipping_replay() && current_team().get_scroll_to_leader() && !map_start_.valid()) {
548  gui_->scroll_to_leader(current_side(), game_display::ONSCREEN, false);
549  }
550 
552  whiteboard_manager_->on_init_side();
553 }
554 
556 {
557  config cfg = level_;
558 
559  cfg["replay_pos"] = saved_game_.get_replay().get_pos();
560  gamestate().write(cfg);
561 
562  gui_->write(cfg.add_child("display"));
563 
564  // Write the soundsources.
565  soundsources_manager_->write_sourcespecs(cfg);
566 
567  gui_->labels().write(cfg);
569 
570  return cfg;
571 }
572 
574 {
575  whiteboard_manager_->on_finish_side_turn(current_side());
576 
577  { // Block for set_scontext_synced
578  set_scontext_synced sync(1);
579  // Ending the turn commits all moves.
580  undo_stack().clear();
582  const std::string turn_num = std::to_string(turn());
583  const std::string side_num = std::to_string(current_side());
584 
585  // Clear shroud, in case units had been slowed for the turn.
587 
588  pump().fire("side_turn_end");
589  pump().fire("side_" + side_num + "_turn_end");
590  pump().fire("side_turn_" + turn_num + "_end");
591  pump().fire("side_" + side_num + "_turn_" + turn_num + "_end");
592  // This is where we refog, after all of a side's events are done.
594  check_victory();
595  sync.do_final_checkup();
596  }
597 
599  gamestate_->init_side_done() = false;
600 }
601 
603 {
604  set_scontext_synced sync(2);
605  const std::string turn_num = std::to_string(turn());
606  pump().fire("turn_end");
607  pump().fire("turn_" + turn_num + "_end");
608  sync.do_final_checkup();
609 }
610 
612 {
613  // If we aren't using fog/shroud, this is easy :)
614  if(current_team().uses_fog() == false && current_team().uses_shroud() == false) {
615  return true;
616  }
617 
618  // See if any enemies are visible
619  for(const unit& u : get_units()) {
620  if(current_team().is_enemy(u.side()) && !gui_->fogged(u.get_location())) {
621  return true;
622  }
623  }
624 
625  return false;
626 }
627 
629 {
630  if(menu_handler_.get_textbox().active() == false) {
631  return;
632  }
633 
634  const std::string str = menu_handler_.get_textbox().box()->text();
635  const unsigned int team_num = current_side();
636  events::mouse_handler& mousehandler = mouse_handler_;
637 
638  switch(menu_handler_.get_textbox().mode()) {
639  case gui::TEXTBOX_SEARCH:
643  break;
646  menu_handler_.get_textbox().close(); // need to close that one after executing do_speak() !
647  break;
652  break;
653  case gui::TEXTBOX_AI:
656  menu_handler_.do_ai_formula(str, team_num, mousehandler);
657  break;
658  default:
660  ERR_DP << "unknown textbox mode";
661  }
662 }
663 
665 {
666  if(menu_handler_.get_textbox().active() == false) {
667  return;
668  }
669 
672  // Not handling messages to avoid spam
673  return;
674  }
675 
676  const std::string str = menu_handler_.get_textbox().box()->text();
677  const std::vector<std::string>& command_history = menu_handler_.get_textbox().command_history();
678 
679  auto prev = std::find(command_history.begin(), command_history.end(), str);
680 
681  if (prev != command_history.end())
682  {
683  if(up) {
684  if(prev != command_history.begin()) {
685  menu_handler_.get_textbox().box()->set_text(*--prev);
686  }
687  } else {
688  if(++prev != command_history.end()) {
689  menu_handler_.get_textbox().box()->set_text(*prev);
690  } else {
691  menu_handler_.get_textbox().box()->set_text("");
692  }
693  }
694  } else if (up) {
695  if(command_history.size() > 0) {
696  menu_handler_.get_textbox().box()->set_text(*--prev);
697  }
698  if(!str.empty()) {
700  }
701  }
702 }
703 
705 {
707 
708  std::set<std::string> dictionary;
709  switch(mode) {
710  case gui::TEXTBOX_SEARCH: {
711  for(const unit& u : get_units()) {
712  const map_location& loc = u.get_location();
713  if(!gui_->fogged(loc) && !(get_teams()[gui_->viewing_team()].is_enemy(u.side()) && u.invisible(loc)))
714  dictionary.insert(u.name());
715  }
716  // TODO List map labels
717  break;
718  }
719  case gui::TEXTBOX_COMMAND: {
720  std::vector<std::string> commands = menu_handler_.get_commands_list();
721  dictionary.insert(commands.begin(), commands.end());
722  [[fallthrough]]; // we also want player names from the next case
723  }
724  case gui::TEXTBOX_MESSAGE: {
725  for(const team& t : get_teams()) {
726  if(!t.is_empty())
727  dictionary.insert(t.current_player());
728  }
729 
730  // Add observers
731  for(const std::string& o : gui_->observers()) {
732  dictionary.insert(o);
733  }
734 
735  // Add nicks who whispered you
736  for(const std::string& w : gui_->get_chat_manager().whisperers()) {
737  dictionary.insert(w);
738  }
739 
740  // Add nicks from friendlist
741  const std::map<std::string, std::string> friends = preferences::get_acquaintances_nice("friend");
742 
743  for(std::map<std::string, std::string>::const_iterator iter = friends.begin(); iter != friends.end(); ++iter) {
744  dictionary.insert((*iter).first);
745  }
746 
747  // Exclude own nick from tab-completion.
748  // NOTE why ?
749  dictionary.erase(preferences::login());
750  break;
751  }
752 
753  default:
754  ERR_DP << "unknown textbox mode";
755  } // switch(mode)
756 
757  menu_handler_.get_textbox().tab(dictionary);
758 }
759 
761 {
762  if(get_teams().size() == 0) {
763  throw game::game_error("The scenario has no sides defined");
764  }
765 
766  assert(gamestate().board_.has_team(current_side()));
768 }
769 
771 {
772  if(get_teams().size() == 0) {
773  throw game::game_error("The scenario has no sides defined");
774  }
775 
776  assert(gamestate().board_.has_team(current_side()));
778 }
779 
780 bool play_controller::is_team_visible(int team_num, bool observer) const
781 {
782  const team& t = gamestate().board_.get_team(team_num);
783  if(observer) {
784  return !t.get_disallow_observers() && !t.is_empty();
785  } else {
786  return t.is_local_human() && !t.is_idle();
787  }
788 }
789 
791 {
792  assert(current_side() <= static_cast<int>(get_teams().size()));
793  const int num_teams = get_teams().size();
794  const bool observer = is_observer();
795 
796  for(int i = 0; i < num_teams; i++) {
797  const int team_num = modulo(current_side() - i, num_teams, 1);
798  if(is_team_visible(team_num, observer)) {
799  return team_num;
800  }
801  }
802 
803  return 0;
804 }
805 
807 {
808  return mouse_handler_;
809 }
810 
811 std::shared_ptr<wb::manager> play_controller::get_whiteboard() const
812 {
813  return whiteboard_manager_;
814 }
815 
817 {
818  return saved_game_.mp_settings();
819 }
820 
822 {
823  return saved_game_.classification();
824 }
825 
827 {
828  return *gui_;
829 }
830 
832 {
833  return !menu_handler_.get_textbox().active();
834 }
835 
837 {
838  if(event.key.keysym.sym == SDLK_ESCAPE) {
840  } else if(event.key.keysym.sym == SDLK_TAB) {
841  tab();
842  } else if(event.key.keysym.sym == SDLK_UP) {
844  } else if(event.key.keysym.sym == SDLK_DOWN) {
846  } else if(event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_KP_ENTER) {
847  enter_textbox();
848  }
849 }
850 
851 void play_controller::process_keydown_event(const SDL_Event& event)
852 {
853  if(event.key.keysym.sym == SDLK_TAB) {
854  whiteboard_manager_->set_invert_behavior(true);
855  }
856 }
857 
858 void play_controller::process_keyup_event(const SDL_Event& event)
859 {
860  // If the user has pressed 1 through 9, we want to show
861  // how far the unit can move in that many turns
862  if(event.key.keysym.sym >= '1' && event.key.keysym.sym <= '9') {
863  const int new_path_turns = (event.type == SDL_KEYDOWN) ? event.key.keysym.sym - '1' : 0;
864 
865  if(new_path_turns != mouse_handler_.get_path_turns()) {
866  mouse_handler_.set_path_turns(new_path_turns);
867 
869 
870  if(u.valid()) {
871  // if it's not the unit's turn, we reset its moves
872  unit_movement_resetter move_reset(*u, u->side() != current_side());
873 
875  *u, false, true, get_teams()[gui_->viewing_team()], mouse_handler_.get_path_turns()));
876 
877  gui_->highlight_reach(mouse_handler_.current_paths());
878  } else {
880  }
881  }
882  } else if(event.key.keysym.sym == SDLK_TAB) {
883  CKey keys;
884  if(!keys[SDLK_TAB]) {
885  whiteboard_manager_->set_invert_behavior(false);
886  }
887  }
888 }
889 
891 {
892  assert(replay_);
893  return *replay_.get();
894 }
895 
897 {
899  // Saving while an event is running isn't supported
900  // because it may lead to expired event handlers being saved.
901  assert(!gamestate().events_manager_->is_event_running());
902 
904  scoped_savegame_snapshot snapshot(*this);
907  } else {
909  }
910 }
911 
912 void play_controller::save_game_auto(const std::string& filename)
913 {
916 
917  scoped_savegame_snapshot snapshot(*this);
919  save.save_game_automatic(false, filename);
920  }
921 }
922 
924 {
929  } else {
931  }
932 }
933 
934 void play_controller::save_replay_auto(const std::string& filename)
935 {
939  save.save_game_automatic(false, filename);
940  }
941 }
942 
944 {
948  } else {
950  }
951 }
952 
954 {
956  load.load_game_ingame();
957 }
958 
960 {
962  undo_stack().undo();
963 }
964 
966 {
968  undo_stack().redo();
969 }
970 
972 {
974 }
975 
977 {
979 }
980 
981 const std::string& play_controller::select_music(bool victory) const
982 {
983  const std::vector<std::string>& music_list = victory
984  ? (gamestate_->get_game_data()->get_victory_music().empty()
986  : gamestate_->get_game_data()->get_victory_music())
987  : (gamestate_->get_game_data()->get_defeat_music().empty()
989  : gamestate_->get_game_data()->get_defeat_music());
990 
991  if(music_list.empty()) {
992  // Since this function returns a reference, we can't return a temporary empty string.
993  static const std::string empty_str = "";
994  return empty_str;
995  }
996 
997  return music_list[randomness::rng::default_instance().get_random_int(0, music_list.size() - 1)];
998 }
999 
1001 {
1002  if(linger_) {
1003  return;
1004  }
1005 
1006  if(is_regular_game_end()) {
1007  return;
1008  }
1009 
1010  bool continue_level, found_player, found_network_player, invalidate_all;
1011  std::set<unsigned> not_defeated;
1012 
1014  continue_level,
1015  found_player,
1016  found_network_player,
1017  invalidate_all,
1018  not_defeated,
1020  );
1021 
1022  if(invalidate_all) {
1023  gui_->invalidate_all();
1024  }
1025 
1026  if(continue_level) {
1027  return;
1028  }
1029 
1030  if(found_player || found_network_player) {
1031  pump().fire("enemies_defeated");
1032  if(is_regular_game_end()) {
1033  return;
1034  }
1035  }
1036 
1037  DBG_EE << "victory_when_enemies_defeated: " << victory_when_enemies_defeated_;
1038  DBG_EE << "found_player: " << found_player;
1039  DBG_EE << "found_network_player: " << found_network_player;
1040 
1041  if(!victory_when_enemies_defeated_ && (found_player || found_network_player)) {
1042  // This level has asked not to be ended by this condition.
1043  return;
1044  }
1045 
1046  if(video::headless()) {
1047  LOG_AIT << "winner: ";
1048  for(unsigned l : not_defeated) {
1050  if(ai.empty())
1051  ai = "default ai";
1052  LOG_AIT << l << " (using " << ai << ") ";
1053  }
1054 
1055  LOG_AIT;
1056  ai_testing::log_victory(not_defeated);
1057  }
1058 
1059  DBG_EE << "throwing end level exception...";
1060  // Also proceed to the next scenario when another player survived.
1061  end_level_data el_data;
1062  el_data.proceed_to_next_level = found_player || found_network_player;
1063  el_data.is_victory = found_player;
1064  set_end_level_data(el_data);
1065 }
1066 
1067 void play_controller::process_oos(const std::string& msg) const
1068 {
1069  if(video::headless()) {
1070  throw game::game_error(msg);
1071  }
1072 
1074  return;
1075  }
1076 
1077  std::stringstream message;
1078  message << _("The game is out of sync. It might not make much sense to continue. Do you want to save your game?");
1079  message << "\n\n" << _("Error details:") << "\n\n" << msg;
1080 
1081  scoped_savegame_snapshot snapshot(*this);
1083  save.save_game_interactive(message.str(), savegame::savegame::YES_NO); // can throw quit_game_exception
1084 }
1085 
1086 void play_controller::update_gui_to_player(const int team_index, const bool observe)
1087 {
1088  gui_->set_team(team_index, observe);
1089  gui_->recalculate_minimap();
1090  gui_->invalidate_all();
1091 }
1092 
1094 {
1095  scoped_savegame_snapshot snapshot(*this);
1098 }
1099 
1100 void play_controller::do_consolesave(const std::string& filename)
1101 {
1102  scoped_savegame_snapshot snapshot(*this);
1104  save.save_game_automatic(true, filename);
1105 }
1106 
1108 {
1109  // note: this writes to level_ if this is not a replay.
1111 }
1112 
1114 {
1115  return gamestate().events_manager_->pump();
1116 }
1117 
1119 {
1120  return ticks_;
1121 }
1122 
1124 {
1125  return soundsources_manager_.get();
1126 }
1127 
1129 {
1130  return plugins_context_.get();
1131 }
1132 
1134 {
1135  return hotkey_handler_.get();
1136 }
1137 
1139 {
1140  if(linger_ || !gamestate_->init_side_done() || gamestate().gamedata_.phase() != game_data::PLAY) {
1141  return true;
1142  }
1143 
1144  const team& t = current_team();
1145  return !t.is_local_human() || !t.is_proxy_human();
1146 }
1147 
1149 {
1151  return;
1152  }
1153 
1154  try {
1155  play_slice();
1156  } catch(const return_to_play_side_exception&) {
1157  assert(should_return_to_play_side());
1158  }
1159 }
1160 
1162 {
1163  fire_preload();
1164 
1165  if(!gamestate().start_event_fired_) {
1166  gamestate().start_event_fired_ = true;
1170 
1171  set_scontext_synced sync;
1172 
1173  fire_prestart();
1174  if(is_regular_game_end()) {
1175  return;
1176  }
1177 
1178  for(const team& t : get_teams()) {
1179  actions::clear_shroud(t.side(), false, false);
1180  }
1181 
1182  init_gui();
1183  LOG_NG << "first_time..." << (is_skipping_replay() ? "skipping" : "no skip");
1184 
1185  fire_start();
1186  if(is_regular_game_end()) {
1187  return;
1188  }
1189 
1190  sync.do_final_checkup();
1191  gui_->recalculate_minimap();
1192 
1193  // Initialize countdown clock.
1194  for(const team& t : get_teams()) {
1196  t.set_countdown_time(1000 * saved_game_.mp_settings().mp_countdown_init_time);
1197  }
1198  }
1199  } else {
1200  init_gui();
1202  gui_->recalculate_minimap();
1203  }
1204 
1206 }
1207 
1208 /**
1209  * Find all [endlevel]next_scenario= attributes, and add them to @a result.
1210  */
1211 static void find_next_scenarios(const config& parent, std::set<std::string>& result) {
1212  for(const auto& endlevel : parent.child_range("endlevel")) {
1213  if(endlevel.has_attribute("next_scenario")) {
1214  result.insert(endlevel["next_scenario"]);
1215  }
1216  }
1217  for(const auto cfg : parent.all_children_range()) {
1218  find_next_scenarios(cfg.cfg, result);
1219  }
1220 };
1221 
1223  // Which scenarios are reachable from the current one?
1224  std::set<std::string> possible_next_scenarios;
1225  possible_next_scenarios.insert(gamestate().gamedata_.next_scenario());
1226 
1227  // Find all "endlevel" tags that could be triggered in events
1228  config events;
1229  gamestate().events_manager_->write_events(events);
1230  find_next_scenarios(events, possible_next_scenarios);
1231 
1232  // Are we looking for [scenario]id=, [multiplayer]id= or [test]id=?
1233  const auto tagname = saved_game_.classification().get_tagname();
1234 
1235  // Of the possible routes, work out which exist.
1236  bool possible_this_is_the_last_scenario = false;
1237  std::vector<std::string> known;
1238  std::vector<std::string> unknown;
1239  for(const auto& x : possible_next_scenarios) {
1240  if(x.empty() || x == "null") {
1241  possible_this_is_the_last_scenario = true;
1242  LOG_NG << "This can be the last scenario";
1243  } else if(utils::contains(x, '$')) {
1244  // Assume a WML variable will be set to a correct value before the end of the scenario
1245  known.push_back(x);
1246  LOG_NG << "Variable value for next scenario '" << x << "'";
1247  } else if(game_config_.find_child(tagname, "id", x)) {
1248  known.push_back(x);
1249  LOG_NG << "Known next scenario '" << x << "'";
1250  } else {
1251  unknown.push_back(x);
1252  ERR_NG << "Unknown next scenario '" << x << "'";
1253  }
1254  }
1255 
1256  if(unknown.empty()) {
1257  // everything's good
1258  return;
1259  }
1260 
1261  std::string title = _("Warning: broken campaign branches");
1262  std::stringstream message;
1263 
1264  message << _n(
1265  // TRANSLATORS: This is an error that will hopefully only be seen by UMC authors and by players who have already
1266  // said "okay" to a "loading saves from an old version might not work" dialog.
1267  "The next scenario is missing, you will not be able to finish this campaign.",
1268  // TRANSLATORS: This is an error that will hopefully only be seen by UMC authors and by players who have already
1269  // said "okay" to a "loading saves from an old version might not work" dialog.
1270  "Some of the possible next scenarios are missing, you might not be able to finish this campaign.",
1271  unknown.size() + known.size() + (possible_this_is_the_last_scenario ? 1 : 0));
1272  message << "\n\n";
1273  message << _n(
1274  "Please report the following missing scenario to the campaign’s author:\n$unknown_list|",
1275  "Please report the following missing scenarios to the campaign’s author:\n$unknown_list|",
1276  unknown.size());
1277  message << "\n";
1278  message << _("Once this is fixed, you will need to restart this scenario.");
1279 
1280  std::stringstream unknown_list;
1281  for(const auto& x : unknown) {
1282  unknown_list << font::unicode_bullet << " " << x << "\n";
1283  }
1284  utils::string_map symbols;
1285  symbols["unknown_list"] = unknown_list.str();
1286  auto message_str = utils::interpolate_variables_into_string(message.str(), &symbols);
1287  ERR_NG << message_str;
1289 }
1290 
1292 {
1293  const team& viewing_team = get_teams()[gui_->viewing_team()];
1294  return gui_->viewing_team() == gui_->playing_team() && !events::commands_disabled && viewing_team.is_local_human()
1295  && !is_lingering() && !is_browsing();
1296 }
1297 
1298 std::set<std::string> play_controller::all_players() const
1299 {
1300  std::set<std::string> res = gui_->observers();
1301  for(const team& t : get_teams()) {
1302  if(t.is_human()) {
1303  res.insert(t.current_player());
1304  }
1305  }
1306 
1307  return res;
1308 }
1309 
1311 {
1312  // check for team-specific items in the scenario
1313  gui_->parse_team_overlays();
1314 
1315  do {
1317  {
1318  save_blocker blocker;
1320  if(is_regular_game_end()) {
1321  return;
1322  }
1323  }
1324  // This flag can be set by derived classes (in overridden functions).
1325  player_type_changed_ = false;
1326 
1327  statistics::reset_turn_stats(gamestate().board_.get_team(current_side()).save_id_or_number());
1328 
1329  play_side_impl();
1330 
1331  if(is_regular_game_end()) {
1332  return;
1333  }
1334  } while(player_type_changed_);
1335 
1336  // Keep looping if the type of a team (human/ai/networked) has changed mid-turn
1337  sync_end_turn();
1338 }
1339 
1341 {
1342  whiteboard_manager_->on_gamestate_change();
1343  gui_->new_turn();
1344  gui_->invalidate_game_status();
1345 
1346  LOG_NG << "turn: " << turn();
1347 
1348  if(video::headless()) {
1349  LOG_AIT << "Turn " << turn() << ":";
1350  }
1351 
1352  int last_player_number = gamestate_->player_number_;
1353  int next_player_number = gamestate_->next_player_number_;
1354 
1355  while(gamestate_->player_number_ <= static_cast<int>(get_teams().size())) {
1356  gamestate_->next_player_number_ = gamestate_->player_number_ + 1;
1357  next_player_number = gamestate_->next_player_number_;
1358  last_player_number = gamestate_->player_number_;
1359 
1360  // If a side is empty skip over it.
1361  if(!current_team().is_empty()) {
1362  init_side_begin();
1363  if(gamestate_->init_side_done()) {
1364  // This is the case in a reloaded game where the side was initialized before saving the game.
1365  init_side_end();
1366  }
1367 
1369  play_side();
1370 
1371  // ignore any changes to next_player_number_ that happen after the [end_turn] is sended to the server,
1372  // otherwise we will get OOS.
1373  next_player_number = gamestate_->next_player_number_;
1374  assert(next_player_number <= 2 * static_cast<int>(get_teams().size()));
1375 
1376  if(is_regular_game_end()) {
1377  return;
1378  }
1379 
1380  // note: play_side() send the [end_turn] to the sever and finish_side_turn() callsie the side turn end
1381  // events.
1382  // this means that during the side turn end events the clients think it is still the last sides turn
1383  // while the server thinks that it is already the next plyers turn. i don'T think this is a problem
1384  // though.
1385  finish_side_turn();
1386  if(is_regular_game_end()) {
1387  return;
1388  }
1389 
1390  if(video::headless()) {
1391  LOG_AIT << " Player " << current_side() << ": " << current_team().villages().size() << " Villages";
1393  }
1394  }
1395 
1396  gamestate_->player_number_ = next_player_number;
1397  }
1398 
1399  // If the loop exits due to the last team having been processed.
1400  gamestate_->player_number_ = last_player_number;
1401 
1402  finish_turn();
1403 
1404  // Time has run out
1405  check_time_over();
1406 
1407  if(!is_regular_game_end()) {
1408  gamestate_->player_number_ = modulo(next_player_number, get_teams().size(), 1);
1409  }
1410 }
1411 
1413 {
1414  const bool time_left = gamestate().tod_manager_.next_turn(&gamestate().gamedata_);
1415 
1416  if(!time_left) {
1417  LOG_NG << "firing time over event...";
1419  pump().fire("time_over");
1420  LOG_NG << "done firing time over event...";
1421 
1422  // If turns are added while handling 'time over' event.
1423  if(gamestate().tod_manager_.is_time_left()) {
1424  return;
1425  }
1426 
1427  if(video::headless()) {
1428  LOG_AIT << "time over (draw)";
1430  }
1431 
1432  check_victory();
1433  if(is_regular_game_end()) {
1434  return;
1435  }
1436 
1437  end_level_data e;
1438  e.proceed_to_next_level = false;
1439  e.is_victory = false;
1440  set_end_level_data(e);
1441  }
1442 }
1443 
1445  : controller_(controller)
1446 {
1448 }
1449 
1451 {
1453 }
1454 
1456 {
1457  const team& t = get_teams()[gui_->viewing_team()];
1458  static const std::string no_objectives(_("No objectives available"));
1459  std::string objectives = utils::interpolate_variables_into_string(t.objectives(), *gamestate_->get_game_data());
1460  gui2::show_transient_message(get_scenario_name(), (objectives.empty() ? no_objectives : objectives), "", true);
1462 }
1463 
1465 {
1467  const std::shared_ptr<gui::button> skip_animation_button = get_display().find_action_button("skip-animation");
1468  if(skip_animation_button) {
1469  skip_animation_button->set_check(skip_replay_);
1470  }
1471 }
std::shared_ptr< gui::button > find_action_button(const std::string &id)
Retrieves a pointer to a theme UI button.
Definition: display.cpp:825
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:133
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
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
Definition: general.cpp:23
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
#define DBG_NG
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:143
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:2004
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:113
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:290
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:78
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:237
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:69
#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()