The Battle for Wesnoth  1.15.12+dev
play_controller.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2018 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
3  wesnoth playlevel Copyright (C) 2003 by David White <dave@whitevine.net>
4  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
5 
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10  This program is distributed in the hope that it will be useful,
11  but WITHOUT ANY WARRANTY.
12 
13  See the COPYING file for more details.
14 */
15 
16 /**
17  * @file
18  * Handle input via mouse & keyboard, events, schedule commands.
19  */
20 
21 #include "play_controller.hpp"
22 
23 #include "actions/advancement.hpp"
24 #include "actions/create.hpp"
25 #include "actions/heal.hpp"
26 #include "actions/undo.hpp"
27 #include "actions/vision.hpp"
28 #include "ai/manager.hpp"
29 #include "ai/testing.hpp"
30 #include "display_chat_manager.hpp"
31 #include "floating_label.hpp"
32 #include "formula/string_utils.hpp"
33 #include "game_errors.hpp"
35 #include "game_events/pump.hpp"
36 #include "game_state.hpp"
37 #include "gettext.hpp"
42 #include "hotkey/hotkey_item.hpp"
43 #include "log.hpp"
44 #include "map/label.hpp"
45 #include "pathfind/teleport.hpp"
47 #include "preferences/display.hpp"
48 #include "preferences/game.hpp"
49 #include "random.hpp"
50 #include "replay.hpp"
51 #include "reports.hpp"
52 #include "resources.hpp"
53 #include "save_blocker.hpp"
54 #include "save_index.hpp"
55 #include "saved_game.hpp"
56 #include "savegame.hpp"
59 #include "sound.hpp"
60 #include "soundsource.hpp"
61 #include "statistics.hpp"
62 #include "synced_context.hpp"
63 #include "tooltips.hpp"
64 #include "units/id.hpp"
65 #include "units/types.hpp"
66 #include "units/unit.hpp"
67 #include "whiteboard/manager.hpp"
68 
69 #include <functional>
70 
71 static lg::log_domain log_aitesting("ai/testing");
72 #define LOG_AIT LOG_STREAM(info, log_aitesting)
73 // If necessary, this define can be replaced with `#define LOG_AIT std::cout` to restore previous behavior
74 
75 static lg::log_domain log_engine("engine");
76 #define LOG_NG LOG_STREAM(info, log_engine)
77 #define DBG_NG LOG_STREAM(debug, log_engine)
78 #define ERR_NG LOG_STREAM(err, log_engine)
79 
80 static lg::log_domain log_display("display");
81 #define ERR_DP LOG_STREAM(err, log_display)
82 
83 static lg::log_domain log_enginerefac("enginerefac");
84 #define LOG_RG LOG_STREAM(info, log_enginerefac)
85 
86 static lg::log_domain log_engine_enemies("engine/enemies");
87 #define DBG_EE LOG_STREAM(debug, log_engine_enemies)
88 
89 /**
90  * Copies [scenario] attributes/tags that are not otherwise stored in C++ structs/clases.
91  */
92 static void copy_persistent(const config& src, config& dst)
93 {
94  static const std::set<std::string> attrs {
95  "description",
96  "name",
97  "victory_when_enemies_defeated",
98  "remove_from_carryover_on_defeat",
99  "disallow_recall",
100  "experience_modifier",
101  "require_scenario",
102  "loaded_resources"
103  };
104 
105  static const std::set<std::string> tags {
106  "terrain_graphics",
107  "modify_unit_type",
108  "lua"
109  };
110 
111  for(const std::string& attr : attrs) {
112  dst[attr] = src[attr];
113  }
114 
115  for(const std::string& tag : tags) {
116  dst.append_children(src, tag);
117  }
118 }
119 
120 static void clear_resources()
121 {
122  resources::controller = nullptr;
123  resources::filter_con = nullptr;
124  resources::gameboard = nullptr;
125  resources::gamedata = nullptr;
126  resources::game_events = nullptr;
127  resources::lua_kernel = nullptr;
128  resources::persist = nullptr;
129  resources::soundsources = nullptr;
130  resources::tod_manager = nullptr;
131  resources::tunnels = nullptr;
132  resources::undo_stack = nullptr;
133  resources::recorder = nullptr;
134  resources::whiteboard.reset();
135  resources::classification = nullptr;
136 }
137 
138 play_controller::play_controller(const config& level, saved_game& state_of_game, bool skip_replay)
139  : controller_base()
140  , observer()
142  , ticks_(SDL_GetTicks())
143  , gamestate_()
144  , level_()
145  , saved_game_(state_of_game)
146  , tooltips_manager_()
147  , whiteboard_manager_()
148  , plugins_context_()
149  , labels_manager_(new font::floating_label_context())
150  , help_manager_(&game_config_)
151  , mouse_handler_(nullptr, *this)
152  , menu_handler_(nullptr, *this)
153  , hotkey_handler_(new hotkey_handler(*this, saved_game_))
154  , soundsources_manager_()
155  , persist_()
156  , gui_()
157  , xp_mod_(new unit_experience_accelerator(level["experience_modifier"].to_int(100)))
158  , statistics_context_(new statistics::scenario_context(level["name"]))
159  , replay_(new replay(state_of_game.get_replay()))
160  , skip_replay_(skip_replay)
161  , skip_story_(state_of_game.skip_story())
162  , linger_(false)
163  , init_side_done_now_(false)
164  , map_start_()
165  , victory_when_enemies_defeated_(level["victory_when_enemies_defeated"].to_bool(true))
166  , remove_from_carryover_on_defeat_(level["remove_from_carryover_on_defeat"].to_bool(true))
167  , victory_music_()
168  , defeat_music_()
169  , scope_()
170  , ignore_replay_errors_(false)
171  , player_type_changed_(false)
172 {
173  copy_persistent(level, level_);
174 
175  for(const config& modify_unit_type : level_.child_range("modify_unit_type")) {
176  unit_types.apply_scenario_fix(modify_unit_type);
177  }
178  resources::controller = this;
181 
183 
185 
189 
190  try {
191  init(level);
192  } catch(...) {
193  clear_resources();
194  throw;
195  }
196 }
197 
199 {
202  clear_resources();
203 }
204 
206 {
207  gui2::dialogs::loading_screen::display([this, &level]() {
209 
210  LOG_NG << "initializing game_state..." << (SDL_GetTicks() - ticks()) << std::endl;
211  gamestate_.reset(new game_state(level, *this));
212 
220 
221  gamestate_->ai_manager_.add_observer(this);
222  gamestate_->init(level, *this);
224 
225  LOG_NG << "initializing whiteboard..." << (SDL_GetTicks() - ticks()) << std::endl;
227  whiteboard_manager_.reset(new wb::manager());
229 
230  LOG_NG << "loading units..." << (SDL_GetTicks() - ticks()) << std::endl;
233 
234  LOG_NG << "initializing theme... " << (SDL_GetTicks() - ticks()) << std::endl;
236  const config& theme_cfg = controller_base::get_theme(game_config_, theme());
237 
238  LOG_NG << "building terrain rules... " << (SDL_GetTicks() - ticks()) << std::endl;
240  gui_.reset(new game_display(gamestate().board_, whiteboard_manager_, *gamestate().reports_, theme_cfg, level));
241  map_start_ = map_location(level.child_or_empty("display").child_or_empty("location"));
242 
243  if(!gui_->video().faked()) {
245  gui_->get_theme().modify_label("time-icon", _("time left for current turn"));
246  } else {
247  gui_->get_theme().modify_label("time-icon", _("current local time"));
248  }
249  }
250 
252  mouse_handler_.set_gui(gui_.get());
253  menu_handler_.set_gui(gui_.get());
254 
255  LOG_NG << "done initializing display... " << (SDL_GetTicks() - ticks()) << std::endl;
256 
257  LOG_NG << "building gamestate to gui and whiteboard... " << (SDL_GetTicks() - ticks()) << std::endl;
258  // This *needs* to be created before the show_intro and show_map_scene
259  // as that functions use the manager state_of_game
260  // Has to be done before registering any events!
263 
264  if(gamestate().first_human_team_ != -1) {
265  gui_->set_team(gamestate().first_human_team_);
266  } else if(is_observer()) {
267  // Find first team that is allowed to be observed.
268  // If not set here observer would be without fog until
269  // the first turn of observable side
270  for(const team& t : get_teams()) {
271  if(!t.get_disallow_observers()) {
272  gui_->set_team(t.side() - 1);
273  }
274  }
275  }
276 
277  init_managers();
279  // loadscreen_manager->reset();
281  gamestate().lua_kernel_->load_game(level);
282 
283  plugins_context_.reset(new plugins_context("Game"));
284  plugins_context_->set_callback("save_game", [this](const config& cfg) { save_game_auto(cfg["filename"]); }, true);
285  plugins_context_->set_callback("save_replay", [this](const config& cfg) { save_replay_auto(cfg["filename"]); }, true);
286  plugins_context_->set_callback("quit", [](const config&) { throw_quit_game_exception(); }, false);
287  plugins_context_->set_accessor_string("scenario_name", [this](config) { return get_scenario_name(); });
288  });
289 
290  // Do this after the loadingscreen, so that ita happens in the main thread.
291  gui_->join();
292 }
293 
294 void play_controller::reset_gamestate(const config& level, int replay_pos)
295 {
296  resources::gameboard = nullptr;
297  resources::gamedata = nullptr;
298  resources::tod_manager = nullptr;
299  resources::filter_con = nullptr;
300  resources::lua_kernel = nullptr;
301  resources::game_events = nullptr;
302  resources::tunnels = nullptr;
303  resources::undo_stack = nullptr;
304 
305  gui_->labels().set_team(nullptr);
306 
307  /* First destroy the old game state, then create the new one.
308  This is necessary to ensure that while the old AI manager is being destroyed,
309  all its member objects access the old manager instead of the new. */
310  gamestate_.reset();
311  gamestate_.reset(new game_state(level, *this));
312 
320 
321  gamestate_->ai_manager_.add_observer(this);
322  gamestate_->init(level, *this);
325 
326  gui_->reset_reports(*gamestate().reports_);
327  gui_->change_display_context(&gamestate().board_);
328  saved_game_.get_replay().set_pos(replay_pos);
330  gamestate().lua_kernel_->load_game(level);
331 }
332 
334 {
335  LOG_NG << "initializing managers... " << (SDL_GetTicks() - ticks()) << std::endl;
339 
341  LOG_NG << "done initializing managers... " << (SDL_GetTicks() - ticks()) << std::endl;
342 }
343 
345 {
346  // Run initialization scripts, even if loading from a snapshot.
347  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
348  pump().fire("preload");
349 }
350 
352 {
353  // pre-start events must be executed before any GUI operation,
354  // as those may cause the display to be refreshed.
355  update_locker lock_display(gui_->video());
357 
358  // Fire these right before prestart events, to catch only the units sides
359  // have started with.
360  for(const unit& u : get_units()) {
361  pump().fire("unit_placed", map_location(u.get_location()));
362  }
363 
364  pump().fire("prestart");
365 
366  // prestart event may modify start turn with WML, reflect any changes.
367  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
368 }
369 
371 {
372  const config cfg("side", gui_->viewing_side());
373  gamestate().lua_kernel_->run_wml_action("show_objectives", vconfig(cfg),
374  game_events::queued_event("_from_interface", "", map_location(), map_location(), config()));
375 }
376 
378 {
380  pump().fire("start");
381 
382  skip_story_ = false; // Show [message]s from now on even with --campaign-skip-story
383 
384  // start event may modify start turn with WML, reflect any changes.
385  gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
386 
389 
390  // prestart and start events may modify the initial gold amount,
391  // reflect any changes.
392  for(team& tm : get_teams()) {
393  tm.set_start_gold(tm.gold());
394  }
395 
396  gamestate_->init_side_done() = false;
398 }
399 
401 {
402  gui_->begin_game();
403  gui_->update_tod();
404 }
405 
407 {
409 
410  // If we are observers we move to watch next team if it is allowed
411  if((is_observer() && !current_team().get_disallow_observers()) || (current_team().is_local_human() && !is_replay())) {
413  }
414 
415  gui_->set_playing_team(std::size_t(current_side() - 1));
416 
418 }
419 
421 {
422  //
423  // We do side init only if not done yet for a local side when we are not replaying.
424  // For all other sides it is recorded in replay and replay handler has to handle
425  // calling do_init_side() functions.
426  //
427  if(gamestate_->init_side_done()) {
428  // We already executed do_init_side this can for example happe if we reload a game,
429  // but also if we changed control of a side during it's turn
430  return;
431  }
432 
433  if(!current_team().is_local()) {
434  // We are in a mp game and execute do_init_side as soon as we receive [init_side] from the current player
435  // (see replay.cpp)
436  return;
437  }
438 
439  if(is_replay()) {
440  // We are in a replay and execute do_init_side as soon as we reach the next [init_side] in the replay data
441  // (see replay.cpp)
442  return;
443  }
444 
445  if(current_team().is_idle()) {
446  // In this case it can happen that we just gave control of this side to another player so doing init_side
447  // could lead to errors since we no longer own this side from the servers perspective.
448  // (see playturn.cpp)
449  return;
450  }
451 
453  do_init_side();
454 }
455 
457 {
458  set_scontext_synced sync;
459  log_scope("player turn");
460  // In case we might end up calling sync:network during the side turn events,
461  // and we don't want do_init_side to be called when a player drops.
462  gamestate_->init_side_done() = true;
463  init_side_done_now_ = true;
464 
465  const std::string turn_num = std::to_string(turn());
466  const std::string side_num = std::to_string(current_side());
467 
468  gamestate().gamedata_.get_variable("side_number") = current_side();
469 
470  // We might have skipped some sides because they were empty so it is not enough to check for side_num==1
471  if(!gamestate().tod_manager_.has_turn_event_fired()) {
472  pump().fire("turn_" + turn_num);
473  pump().fire("new_turn");
475  }
476 
477  pump().fire("side_turn");
478  pump().fire("side_" + side_num + "_turn");
479  pump().fire("side_turn_" + turn_num);
480  pump().fire("side_" + side_num + "_turn_" + turn_num);
481 
482  // We want to work out if units for this player should get healed,
483  // and the player should get income now.
484  // Healing/income happen if it's not the first turn of processing,
485  // or if we are loading a game.
486  if(turn() > 1) {
489 
490  // If the expense is less than the number of villages owned
491  // times the village support capacity,
492  // then we don't have to pay anything at all
493  int expense = gamestate().board_.side_upkeep(current_side()) - current_team().support();
494  if(expense > 0) {
495  current_team().spend_gold(expense);
496  }
497  }
498 
499  if(do_healing()) {
501  }
502 
503  // Do healing on every side turn except the very first side turn.
504  // (1.14 and earlier did healing whenever turn >= 2.)
505  set_do_healing(true);
506 
507  // Set resting now after the healing has been done.
508  for(unit& patient : resources::gameboard->units()) {
509  if(patient.side() == current_side()) {
510  patient.set_resting(true);
511  }
512  }
513 
514  // Prepare the undo stack.
516 
517  pump().fire("turn_refresh");
518  pump().fire("side_" + side_num + "_turn_refresh");
519  pump().fire("turn_" + turn_num + "_refresh");
520  pump().fire("side_" + side_num + "_turn_" + turn_num + "_refresh");
521 
522  // Make sure vision is accurate.
524 
525  init_side_end();
526  check_victory();
527  sync.do_final_checkup();
528 }
529 
531 {
533 
534  if(current_side() == 1 || !init_side_done_now_) {
536  }
537 
538  if(!is_skipping_replay()) {
539  gui_->invalidate_all();
540  }
541 
542  if(!is_skipping_replay() && current_team().get_scroll_to_leader() && !map_start_.valid()) {
543  gui_->scroll_to_leader(current_side(), game_display::ONSCREEN, false);
544  }
545 
547  whiteboard_manager_->on_init_side();
548 }
549 
551 {
552  config cfg = level_;
553 
554  cfg["replay_pos"] = saved_game_.get_replay().get_pos();
555  gamestate().write(cfg);
556 
557  gui_->write(cfg.add_child("display"));
558 
559  // Write the soundsources.
560  soundsources_manager_->write_sourcespecs(cfg);
561 
562  gui_->labels().write(cfg);
564 
565  return cfg;
566 }
567 
569 {
570  whiteboard_manager_->on_finish_side_turn(current_side());
571 
572  { // Block for set_scontext_synced
573  set_scontext_synced sync(1);
574  // Ending the turn commits all moves.
575  undo_stack().clear();
577  const std::string turn_num = std::to_string(turn());
578  const std::string side_num = std::to_string(current_side());
579 
580  // Clear shroud, in case units had been slowed for the turn.
582 
583  pump().fire("side_turn_end");
584  pump().fire("side_" + side_num + "_turn_end");
585  pump().fire("side_turn_" + turn_num + "_end");
586  pump().fire("side_" + side_num + "_turn_" + turn_num + "_end");
587  // This is where we refog, after all of a side's events are done.
589  check_victory();
590  sync.do_final_checkup();
591  }
592 
594  gamestate_->init_side_done() = false;
595 }
596 
598 {
599  set_scontext_synced sync(2);
600  const std::string turn_num = std::to_string(turn());
601  pump().fire("turn_end");
602  pump().fire("turn_" + turn_num + "_end");
603  sync.do_final_checkup();
604 }
605 
607 {
608  // If we aren't using fog/shroud, this is easy :)
609  if(current_team().uses_fog() == false && current_team().uses_shroud() == false) {
610  return true;
611  }
612 
613  // See if any enemies are visible
614  for(const unit& u : get_units()) {
615  if(current_team().is_enemy(u.side()) && !gui_->fogged(u.get_location())) {
616  return true;
617  }
618  }
619 
620  return false;
621 }
622 
624 {
625  if(menu_handler_.get_textbox().active() == false) {
626  return;
627  }
628 
629  const std::string str = menu_handler_.get_textbox().box()->text();
630  const unsigned int team_num = current_side();
631  events::mouse_handler& mousehandler = mouse_handler_;
632 
633  switch(menu_handler_.get_textbox().mode()) {
634  case gui::TEXTBOX_SEARCH:
638  break;
641  menu_handler_.get_textbox().close(*gui_); // need to close that one after executing do_speak() !
642  break;
647  break;
648  case gui::TEXTBOX_AI:
651  menu_handler_.do_ai_formula(str, team_num, mousehandler);
652  break;
653  default:
655  ERR_DP << "unknown textbox mode" << std::endl;
656  }
657 }
658 
660 {
661  if(menu_handler_.get_textbox().active() == false) {
662  return;
663  }
664 
667  // Not handling messages to avoid spam
668  return;
669  }
670 
671  const std::string str = menu_handler_.get_textbox().box()->text();
672  const std::vector<std::string>& command_history = menu_handler_.get_textbox().command_history();
673 
674  auto prev = std::find(command_history.begin(), command_history.end(), str);
675 
676  if (prev != command_history.end())
677  {
678  if(up) {
679  if(prev != command_history.begin()) {
680  menu_handler_.get_textbox().box()->set_text(*--prev);
681  }
682  } else {
683  if(++prev != command_history.end()) {
684  menu_handler_.get_textbox().box()->set_text(*prev);
685  } else {
686  menu_handler_.get_textbox().box()->set_text("");
687  }
688  }
689  } else if (up) {
690  if(command_history.size() > 0) {
691  menu_handler_.get_textbox().box()->set_text(*--prev);
692  }
693  if(!str.empty()) {
695  }
696  }
697 }
698 
700 {
702 
703  std::set<std::string> dictionary;
704  switch(mode) {
705  case gui::TEXTBOX_SEARCH: {
706  for(const unit& u : get_units()) {
707  const map_location& loc = u.get_location();
708  if(!gui_->fogged(loc) && !(get_teams()[gui_->viewing_team()].is_enemy(u.side()) && u.invisible(loc)))
709  dictionary.insert(u.name());
710  }
711  // TODO List map labels
712  break;
713  }
714  case gui::TEXTBOX_COMMAND: {
715  std::vector<std::string> commands = menu_handler_.get_commands_list();
716  dictionary.insert(commands.begin(), commands.end());
717  [[fallthrough]]; // we also want player names from the next case
718  }
719  case gui::TEXTBOX_MESSAGE: {
720  for(const team& t : get_teams()) {
721  if(!t.is_empty())
722  dictionary.insert(t.current_player());
723  }
724 
725  // Add observers
726  for(const std::string& o : gui_->observers()) {
727  dictionary.insert(o);
728  }
729 
730  // Add nicks who whispered you
731  for(const std::string& w : gui_->get_chat_manager().whisperers()) {
732  dictionary.insert(w);
733  }
734 
735  // Add nicks from friendlist
736  const std::map<std::string, std::string> friends = preferences::get_acquaintances_nice("friend");
737 
738  for(std::map<std::string, std::string>::const_iterator iter = friends.begin(); iter != friends.end(); ++iter) {
739  dictionary.insert((*iter).first);
740  }
741 
742  // Exclude own nick from tab-completion.
743  // NOTE why ?
744  dictionary.erase(preferences::login());
745  break;
746  }
747 
748  default:
749  ERR_DP << "unknown textbox mode" << std::endl;
750  } // switch(mode)
751 
752  menu_handler_.get_textbox().tab(dictionary);
753 }
754 
756 {
757  if(get_teams().size() == 0) {
758  throw game::game_error("The scenario has no sides defined");
759  }
760 
761  assert(gamestate().board_.has_team(current_side()));
763 }
764 
766 {
767  if(get_teams().size() == 0) {
768  throw game::game_error("The scenario has no sides defined");
769  }
770 
771  assert(gamestate().board_.has_team(current_side()));
773 }
774 
775 bool play_controller::is_team_visible(int team_num, bool observer) const
776 {
777  const team& t = gamestate().board_.get_team(team_num);
778  if(observer) {
779  return !t.get_disallow_observers() && !t.is_empty();
780  } else {
781  return t.is_local_human() && !t.is_idle();
782  }
783 }
784 
786 {
787  assert(current_side() <= static_cast<int>(get_teams().size()));
788  const int num_teams = get_teams().size();
789  const bool observer = is_observer();
790 
791  for(int i = 0; i < num_teams; i++) {
792  const int team_num = modulo(current_side() - i, num_teams, 1);
793  if(is_team_visible(team_num, observer)) {
794  return team_num;
795  }
796  }
797 
798  return 0;
799 }
800 
802 {
803  return mouse_handler_;
804 }
805 
806 std::shared_ptr<wb::manager> play_controller::get_whiteboard() const
807 {
808  return whiteboard_manager_;
809 }
810 
812 {
813  return saved_game_.mp_settings();
814 }
815 
817 {
818  return saved_game_.classification();
819 }
820 
822 {
823  return *gui_;
824 }
825 
827 {
828  return !menu_handler_.get_textbox().active();
829 }
830 
832 {
833  if(event.key.keysym.sym == SDLK_ESCAPE) {
835  } else if(event.key.keysym.sym == SDLK_TAB) {
836  tab();
837  } else if(event.key.keysym.sym == SDLK_UP) {
839  } else if(event.key.keysym.sym == SDLK_DOWN) {
841  } else if(event.key.keysym.sym == SDLK_RETURN || event.key.keysym.sym == SDLK_KP_ENTER) {
842  enter_textbox();
843  }
844 }
845 
846 void play_controller::process_keydown_event(const SDL_Event& event)
847 {
848  if(event.key.keysym.sym == SDLK_TAB) {
849  whiteboard_manager_->set_invert_behavior(true);
850  }
851 }
852 
853 void play_controller::process_keyup_event(const SDL_Event& event)
854 {
855  // If the user has pressed 1 through 9, we want to show
856  // how far the unit can move in that many turns
857  if(event.key.keysym.sym >= '1' && event.key.keysym.sym <= '9') {
858  const int new_path_turns = (event.type == SDL_KEYDOWN) ? event.key.keysym.sym - '1' : 0;
859 
860  if(new_path_turns != mouse_handler_.get_path_turns()) {
861  mouse_handler_.set_path_turns(new_path_turns);
862 
864 
865  if(u.valid()) {
866  // if it's not the unit's turn, we reset its moves
867  unit_movement_resetter move_reset(*u, u->side() != current_side());
868 
870  *u, false, true, get_teams()[gui_->viewing_team()], mouse_handler_.get_path_turns()));
871 
872  gui_->highlight_reach(mouse_handler_.current_paths());
873  } else {
875  }
876  }
877  } else if(event.key.keysym.sym == SDLK_TAB) {
878  CKey keys;
879  if(!keys[SDLK_TAB]) {
880  whiteboard_manager_->set_invert_behavior(false);
881  }
882  }
883 }
884 
886 {
887  assert(replay_);
888  return *replay_.get();
889 }
890 
892 {
894  // Saving while an event is running isn't supported
895  // because it may lead to expired event handlers being saved.
896  assert(!gamestate().events_manager_->is_event_running());
897 
899  scoped_savegame_snapshot snapshot(*this);
902  } else {
904  }
905 }
906 
907 void play_controller::save_game_auto(const std::string& filename)
908 {
911 
912  scoped_savegame_snapshot snapshot(*this);
914  save.save_game_automatic(false, filename);
915  }
916 }
917 
919 {
924  } else {
926  }
927 }
928 
929 void play_controller::save_replay_auto(const std::string& filename)
930 {
934  save.save_game_automatic(false, filename);
935  }
936 }
937 
939 {
943  } else {
945  }
946 }
947 
949 {
951  load.load_game_ingame();
952 }
953 
955 {
957  undo_stack().undo();
958 }
959 
961 {
963  undo_stack().redo();
964 }
965 
967 {
969 }
970 
972 {
974 }
975 
976 const std::string& play_controller::select_music(bool victory) const
977 {
978  const std::vector<std::string>& music_list = victory
979  ? (gamestate_->get_game_data()->get_victory_music().empty()
981  : gamestate_->get_game_data()->get_victory_music())
982  : (gamestate_->get_game_data()->get_defeat_music().empty()
984  : gamestate_->get_game_data()->get_defeat_music());
985 
986  if(music_list.empty()) {
987  // Since this function returns a reference, we can't return a temporary empty string.
988  static const std::string empty_str = "";
989  return empty_str;
990  }
991 
992  return music_list[randomness::rng::default_instance().get_random_int(0, music_list.size() - 1)];
993 }
994 
996 {
997  if(linger_) {
998  return;
999  }
1000 
1001  if(is_regular_game_end()) {
1002  return;
1003  }
1004 
1005  bool continue_level, found_player, found_network_player, invalidate_all;
1006  std::set<unsigned> not_defeated;
1007 
1009  continue_level,
1010  found_player,
1011  found_network_player,
1012  invalidate_all,
1013  not_defeated,
1015  );
1016 
1017  if(invalidate_all) {
1018  gui_->invalidate_all();
1019  }
1020 
1021  if(continue_level) {
1022  return;
1023  }
1024 
1025  if(found_player || found_network_player) {
1026  pump().fire("enemies_defeated");
1027  if(is_regular_game_end()) {
1028  return;
1029  }
1030  }
1031 
1032  DBG_EE << "victory_when_enemies_defeated: " << victory_when_enemies_defeated_ << std::endl;
1033  DBG_EE << "found_player: " << found_player << std::endl;
1034  DBG_EE << "found_network_player: " << found_network_player << std::endl;
1035 
1036  if(!victory_when_enemies_defeated_ && (found_player || found_network_player)) {
1037  // This level has asked not to be ended by this condition.
1038  return;
1039  }
1040 
1041  if(gui_->video().non_interactive()) {
1042  LOG_AIT << "winner: ";
1043  for(unsigned l : not_defeated) {
1045  if(ai.empty())
1046  ai = "default ai";
1047  LOG_AIT << l << " (using " << ai << ") ";
1048  }
1049 
1050  LOG_AIT << std::endl;
1051  ai_testing::log_victory(not_defeated);
1052  }
1053 
1054  DBG_EE << "throwing end level exception..." << std::endl;
1055  // Also proceed to the next scenario when another player survived.
1056  end_level_data el_data;
1057  el_data.proceed_to_next_level = found_player || found_network_player;
1058  el_data.is_victory = found_player;
1059  set_end_level_data(el_data);
1060 }
1061 
1062 void play_controller::process_oos(const std::string& msg) const
1063 {
1064  if(gui_->video().non_interactive()) {
1065  throw game::game_error(msg);
1066  }
1067 
1069  return;
1070  }
1071 
1072  std::stringstream message;
1073  message << _("The game is out of sync. It might not make much sense to continue. Do you want to save your game?");
1074  message << "\n\n" << _("Error details:") << "\n\n" << msg;
1075 
1076  scoped_savegame_snapshot snapshot(*this);
1078  save.save_game_interactive(message.str(), savegame::savegame::YES_NO); // can throw quit_game_exception
1079 }
1080 
1081 void play_controller::update_gui_to_player(const int team_index, const bool observe)
1082 {
1083  gui_->set_team(team_index, observe);
1084  gui_->recalculate_minimap();
1085  gui_->invalidate_all();
1086 }
1087 
1089 {
1090  scoped_savegame_snapshot snapshot(*this);
1093 }
1094 
1095 void play_controller::do_consolesave(const std::string& filename)
1096 {
1097  scoped_savegame_snapshot snapshot(*this);
1099  save.save_game_automatic(true, filename);
1100 }
1101 
1103 {
1104  // note: this writes to level_ if this is not a replay.
1106 }
1107 
1109 {
1110  return gamestate().events_manager_->pump();
1111 }
1112 
1114 {
1115  return ticks_;
1116 }
1117 
1119 {
1120  return soundsources_manager_.get();
1121 }
1122 
1124 {
1125  return plugins_context_.get();
1126 }
1127 
1129 {
1130  return hotkey_handler_.get();
1131 }
1132 
1134 {
1135  if(linger_ || !gamestate_->init_side_done() || gamestate().gamedata_.phase() != game_data::PLAY) {
1136  return true;
1137  }
1138 
1139  const team& t = current_team();
1140  return !t.is_local_human() || !t.is_proxy_human();
1141 }
1142 
1144 {
1146  return;
1147  }
1148 
1149  try {
1150  play_slice();
1151  } catch(const return_to_play_side_exception&) {
1152  assert(should_return_to_play_side());
1153  }
1154 }
1155 
1157 {
1158  fire_preload();
1159 
1160  if(!gamestate().start_event_fired_) {
1161  gamestate().start_event_fired_ = true;
1165 
1166  set_scontext_synced sync;
1167 
1168  fire_prestart();
1169  if(is_regular_game_end()) {
1170  return;
1171  }
1172 
1173  for(const team& t : get_teams()) {
1174  actions::clear_shroud(t.side(), false, false);
1175  }
1176 
1177  init_gui();
1178  LOG_NG << "first_time..." << (is_skipping_replay() ? "skipping" : "no skip") << "\n";
1179 
1180  fire_start();
1181  if(is_regular_game_end()) {
1182  return;
1183  }
1184 
1185  sync.do_final_checkup();
1186  gui_->recalculate_minimap();
1187 
1188  // Initialize countdown clock.
1189  for(const team& t : get_teams()) {
1191  t.set_countdown_time(1000 * saved_game_.mp_settings().mp_countdown_init_time);
1192  }
1193  }
1194  } else {
1195  init_gui();
1197  gui_->recalculate_minimap();
1198  }
1199 }
1200 
1202 {
1203  const team& viewing_team = get_teams()[gui_->viewing_team()];
1204  return gui_->viewing_team() == gui_->playing_team() && !events::commands_disabled && viewing_team.is_local_human()
1205  && !is_lingering() && !is_browsing();
1206 }
1207 
1208 std::set<std::string> play_controller::all_players() const
1209 {
1210  std::set<std::string> res = gui_->observers();
1211  for(const team& t : get_teams()) {
1212  if(t.is_human()) {
1213  res.insert(t.current_player());
1214  }
1215  }
1216 
1217  return res;
1218 }
1219 
1221 {
1222  // check for team-specific items in the scenario
1223  gui_->parse_team_overlays();
1224 
1225  do {
1227  {
1228  save_blocker blocker;
1230  if(is_regular_game_end()) {
1231  return;
1232  }
1233  }
1234  // This flag can be set by derived classes (in overridden functions).
1235  player_type_changed_ = false;
1236 
1237  statistics::reset_turn_stats(gamestate().board_.get_team(current_side()).save_id_or_number());
1238 
1239  play_side_impl();
1240 
1241  if(is_regular_game_end()) {
1242  return;
1243  }
1244  } while(player_type_changed_);
1245 
1246  // Keep looping if the type of a team (human/ai/networked) has changed mid-turn
1247  sync_end_turn();
1248 }
1249 
1251 {
1252  whiteboard_manager_->on_gamestate_change();
1253  gui_->new_turn();
1254  gui_->invalidate_game_status();
1255 
1256  LOG_NG << "turn: " << turn() << "\n";
1257 
1258  if(gui_->video().non_interactive()) {
1259  LOG_AIT << "Turn " << turn() << ":" << std::endl;
1260  }
1261 
1262  int last_player_number = gamestate_->player_number_;
1263  int next_player_number = gamestate_->next_player_number_;
1264 
1265  while(gamestate_->player_number_ <= static_cast<int>(get_teams().size())) {
1266  gamestate_->next_player_number_ = gamestate_->player_number_ + 1;
1267  next_player_number = gamestate_->next_player_number_;
1268  last_player_number = gamestate_->player_number_;
1269 
1270  // If a side is empty skip over it.
1271  if(!current_team().is_empty()) {
1272  init_side_begin();
1273  if(gamestate_->init_side_done()) {
1274  // This is the case in a reloaded game where the side was initialized before saving the game.
1275  init_side_end();
1276  }
1277 
1279  play_side();
1280 
1281  // ignore any changes to next_player_number_ that happen after the [end_turn] is sended to the server,
1282  // otherwise we will get OOS.
1283  next_player_number = gamestate_->next_player_number_;
1284  assert(next_player_number <= 2 * static_cast<int>(get_teams().size()));
1285 
1286  if(is_regular_game_end()) {
1287  return;
1288  }
1289 
1290  // note: play_side() send the [end_turn] to the sever and finish_side_turn() callsie the side turn end
1291  // events.
1292  // this means that during the side turn end events the clients think it is still the last sides turn
1293  // while the server thinks that it is already the next plyers turn. i don'T think this is a problem
1294  // though.
1295  finish_side_turn();
1296  if(is_regular_game_end()) {
1297  return;
1298  }
1299 
1300  if(gui_->video().non_interactive()) {
1301  LOG_AIT << " Player " << current_side() << ": " << current_team().villages().size() << " Villages" << std::endl;
1303  }
1304  }
1305 
1306  gamestate_->player_number_ = next_player_number;
1307  }
1308 
1309  // If the loop exits due to the last team having been processed.
1310  gamestate_->player_number_ = last_player_number;
1311 
1312  finish_turn();
1313 
1314  // Time has run out
1315  check_time_over();
1316 
1317  if(!is_regular_game_end()) {
1318  gamestate_->player_number_ = modulo(next_player_number, get_teams().size(), 1);
1319  }
1320 }
1321 
1323 {
1324  const bool time_left = gamestate().tod_manager_.next_turn(&gamestate().gamedata_);
1325 
1326  if(!time_left) {
1327  LOG_NG << "firing time over event...\n";
1329  pump().fire("time_over");
1330  LOG_NG << "done firing time over event...\n";
1331 
1332  // If turns are added while handling 'time over' event.
1333  if(gamestate().tod_manager_.is_time_left()) {
1334  return;
1335  }
1336 
1337  if(gui_->video().non_interactive()) {
1338  LOG_AIT << "time over (draw)\n";
1340  }
1341 
1342  check_victory();
1343  if(is_regular_game_end()) {
1344  return;
1345  }
1346 
1347  end_level_data e;
1348  e.proceed_to_next_level = false;
1349  e.is_victory = false;
1350  set_end_level_data(e);
1351  }
1352 }
1353 
1355  : controller_(controller)
1356 {
1358 }
1359 
1361 {
1363 }
1364 
1366 {
1367  const team& t = get_teams()[gui_->viewing_team()];
1368  static const std::string no_objectives(_("No objectives available"));
1369  std::string objectives = utils::interpolate_variables_into_string(t.objectives(), *gamestate_->get_game_data());
1370  gui2::show_transient_message(get_scenario_name(), (objectives.empty() ? no_objectives : objectives), "", true);
1372 }
1373 
1375 {
1377  const std::shared_ptr<gui::button> skip_animation_button = get_display().find_action_button("skip-animation");
1378  if(skip_animation_button) {
1379  skip_animation_button->set_check(skip_replay_);
1380  }
1381 }
std::shared_ptr< gui::button > find_action_button(const std::string &id)
Retrieves a pointer to a theme UI button.
Definition: display.cpp:831
void clear()
Clears the stack of undoable (and redoable) actions.
Definition: undo.cpp:220
play_controller * controller
Definition: resources.cpp:21
std::vector< std::string > default_victory_music
void do_search(const std::string &new_search)
void set_current_paths(const pathfind::paths &new_paths)
int autosavemax()
Definition: game.cpp:799
virtual bool should_return_to_play_side() const
std::unique_ptr< soundsource::manager > soundsources_manager_
static void log_turn_start(unsigned int side)
Definition: testing.cpp:35
static void log_turn_end(unsigned int side)
Definition: testing.cpp:40
void set_preference_display_settings()
Definition: display.cpp:44
::tod_manager * tod_manager
Definition: resources.cpp:29
static lg::log_domain log_engine_enemies("engine/enemies")
bool is_empty() const
Definition: team.hpp:267
hotkey::command_executor * get_hotkey_command_executor() override
Optionally get a command executor to handle context menu events.
void reset_objectives_changed() const
Definition: team.hpp:246
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with &#39;$&#39; in the string &#39;str&#39; with the equivalent ...
map_location last_selected
the last location where a select event fired.
Definition: game_data.hpp:91
The class for loading a savefile.
Definition: savegame.hpp:99
std::unique_ptr< game_lua_kernel > lua_kernel_
Definition: game_state.hpp:50
void refresh_objectives() const
Reevaluate [show_if] conditions and build a new objectives string.
void save_replay_auto(const std::string &filename)
int find_last_visible_team() const
returns 0 if no such team was found.
This class represents a single unit of a specific type.
Definition: unit.hpp:120
std::unique_ptr< game_display > gui_
bool victory_when_enemies_defeated_
While any instance of this class exists, attempts to save the game via any call to play_controller wi...
game_classification * classification
Definition: resources.cpp:34
const unit_map & get_units() const
Various functions implementing vision (through fog of war and shroud).
const mp_game_settings & get_mp_settings()
void do_final_checkup(bool dont_throw=false)
Collection of helper functions relating to Pango formatting.
events::mouse_handler mouse_handler_
static manager & get_singleton()
Definition: manager.hpp:143
static void copy_persistent(const config &src, config &dst)
Copies [scenario] attributes/tags that are not otherwise stored in C++ structs/clases.
std::unique_ptr< plugins_context > plugins_context_
bool is_skipping_replay() const
std::vector< game_tip > load(const config &cfg)
Loads the tips from a config.
Definition: tips.cpp:35
std::unique_ptr< replay > replay_
static game_config_view wrap(const config &cfg)
Exception used to escape form the ai or ui code to playsingle_controller::play_side.
game_display & get_display() override
Get a reference to a display member a derived class uses.
static std::shared_ptr< save_index_class > default_saves_dir()
Returns an instance for managing saves in filesystem::get_saves_dir()
Definition: save_index.cpp:210
replay_recorder_base & get_replay()
Definition: saved_game.hpp:134
Class for autosaves.
Definition: savegame.hpp:279
game_events::wml_event_pump & pump()
void set_scope_active(scope s, bool set)
static void log_victory(std::set< unsigned int > teams)
Definition: testing.cpp:81
static void on_unblock(play_controller *controller, void(play_controller::*callback)())
std::string sounds
List of "ambient" sounds associated with this time_of_day, Played at the beginning of turn...
std::unique_ptr< hotkey_handler > hotkey_handler_
const int INFINITE_AUTO_SAVES
Definition: game.hpp:206
bool enemies_visible() const
child_itors child_range(config_key_type key)
Definition: config.cpp:356
void process_keydown_event(const SDL_Event &event) override
Process keydown (always).
bool is_idle() const
Definition: team.hpp:293
const pathfind::paths & current_paths() const
void show_transient_message(const std::string &title, const std::string &message, const std::string &image, const bool message_use_markup, const bool title_use_markup, const bool restore_background)
Shows a transient message to the user.
void do_command(const std::string &str)
void set_do_healing(bool do_healing)
static void progress(loading_stage stage=loading_stage::none)
virtual void update_viewing_player()=0
gui::floating_textbox & get_textbox()
void new_turn()
Definition: team.hpp:212
void set_gui(game_display *gui)
Definition: menu_events.hpp:54
static const config & get_theme(const game_config_view &game_config, std::string theme_name)
virtual soundsource::manager * get_soundsource_man() override
Get (optionally) a soundsources manager a derived class uses.
void undo()
Undoes the top action on the undo stack.
Definition: undo.cpp:348
Gather statistics important for AI testing and output them.
unit_type_data unit_types
Definition: types.cpp:1447
virtual void process_oos(const std::string &msg) const
Asks the user whether to continue on an OOS error.
A RAII object to enter the synced context, cannot be called if we are already in a synced context...
virtual void play_slice(bool is_delay_enabled=true)
#define LOG_AIT
void new_side_turn(int side)
Performs some initializations and error checks when starting a new side-turn.
Definition: undo.cpp:262
static lg::log_domain log_engine("engine")
Class for "normal" midgame saves.
Definition: savegame.hpp:252
Replay control code.
static void msg(const char *act, debug_info &i, const char *to="", const char *result="")
Definition: debugger.cpp:109
persist_manager * persist
Definition: resources.cpp:26
bool do_healing() const
void check_victory()
Checks to see if a side has won.
void apply_scenario_fix(const config &cfg)
Definition: types.cpp:1380
config & set_snapshot(config snapshot)
Definition: saved_game.cpp:557
void append_children(const config &cfg)
Adds children from cfg.
Definition: config.cpp:235
static void log_draw()
Definition: testing.cpp:75
bool save_game_interactive(const std::string &message, DIALOG_TYPE dialog_type)
Save a game interactively through the savegame dialog.
Definition: savegame.cpp:384
static lg::log_domain log_display("display")
void write_music_play_list(config &snapshot)
Definition: sound.cpp:851
static std::string _(const char *str)
Definition: gettext.hpp:92
#define DBG_EE
void process_keyup_event(const SDL_Event &event) override
Process keyup (always).
static void clear_resources()
This file implements all the hotkey handling and menu details for play controller.
void select_hex(const map_location &hex, const bool browse, const bool highlight=true, const bool fire_event=true)
persist_manager persist_
void reset_turn_stats(const std::string &save_id)
Definition: statistics.cpp:686
void process_focus_keydown_event(const SDL_Event &event) override
Process keydown (only when the general map display does not have focus).
#define ERR_DP
void memorize_command(const std::string &command)
std::unique_ptr< tooltips::manager > tooltips_manager_
std::vector< std::string > default_defeat_music
const std::unique_ptr< game_events::manager > events_manager_
Definition: game_state.hpp:52
game_data * gamedata
Definition: resources.cpp:22
config::attribute_value & get_variable(const std::string &varname)
throws invalid_variablename_exception if varname is no valid variable name.
Definition: game_data.cpp:61
bool have_keyboard_focus() override
Derived classes should override this to return false when arrow keys should not scroll the map...
events::menu_handler menu_handler_
void play_sound(const std::string &files, channel_group group, unsigned int repeats)
Definition: sound.cpp:1020
bool can_undo() const
void set_phase(PHASE phase)
Definition: game_data.hpp:79
std::unique_ptr< game_state > gamestate_
An exception-safe means of making sure that unblock() gets called after try_block().
void do_consolesave(const std::string &filename)
bool is_lingering() const
map_location get_selected_hex() const
Object which defines a time of day with associated bonuses, image, sounds etc.
Definition: time_of_day.hpp:55
saved_game & saved_game_
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:44
A small explanation about what&#39;s going on here: Each action has access to two game_info objects First...
Definition: actions.cpp:59
scoped_savegame_snapshot(const play_controller &controller)
void init_side()
Definition: replay.cpp:215
team & get_team(int i)
Definition: game_board.hpp:96
void throw_quit_game_exception()
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:86
void save_game_auto(const std::string &filename)
Various functions that implement advancements of units.
bool add_start_if_not_there_yet()
Definition: replay.cpp:659
void textbox_move_vertically(bool up)
void set_path_turns(const int path_turns)
virtual void check_time_over()
bool can_redo() const
True if there are actions that can be redone.
Definition: undo.hpp:97
Implements a quit confirmation dialog.
config to_config() const
Builds the snapshot config from members and their respective configs.
filter_context * filter_con
Definition: resources.cpp:23
const time_of_day & get_time_of_day(int for_turn=0) const
Returns global time of day for the passed turn.
Definition: tod_manager.hpp:54
bool valid() const
Definition: location.hpp:88
bool ignore_replay_errors
int side_upkeep(int side_num) const
TEXTBOX_MODE mode() const
game_board * gameboard
Definition: resources.cpp:20
This class is the frontend of the whiteboard framework for the rest of the Wesnoth code...
Definition: manager.hpp:42
bool is_regular_game_end() const
void init(const config &level)
std::vector< team > & get_teams()
void recalculate_fog(int side)
Function that recalculates the fog of war.
Definition: vision.cpp:711
virtual void init_gui()
void maybe_do_init_side()
Called by turn_info::process_network_data() or init_side() to call do_init_side() if necessary...
tod_manager tod_manager_
Definition: game_state.hpp:47
Object which temporarily resets a unit&#39;s movement.
Definition: unit.hpp:1994
bool load_game_ingame()
Load a game without providing any information.
Definition: savegame.cpp:122
std::unique_ptr< pathfind::manager > pathfind_manager_
Definition: game_state.hpp:48
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands...
std::string theme() const
replay * recorder
Definition: resources.cpp:28
void check_victory(bool &, bool &, bool &, bool &, std::set< unsigned > &, bool)
Definition: game_board.cpp:111
void set_gui(game_display *gui)
void spend_gold(const int amount)
Definition: team.hpp:216
std::shared_ptr< wb::manager > get_whiteboard() const
static void add_color_info(const game_config_view &v, bool build_defaults)
An object which will lock the display for the duration of its lifetime.
Definition: video.hpp:296
Error used for any general game error, e.g.
Definition: game_errors.hpp:46
game_events::manager * game_events
Definition: resources.cpp:24
bool is_browsing() const override
void deactivate_all_scopes()
bool is_observer() const
void update_gui_to_player(const int team_index, const bool observe=false)
Changes the UI for this client to the passed side index.
void do_init_side()
Called by replay handler or init_side() to do actual work for turn change.
Encapsulates the map of the game.
Definition: location.hpp:37
void calculate_healing(int side, bool update_display)
Calculates healing for all units for the given side.
Definition: heal.cpp:292
std::string login()
std::shared_ptr< wb::manager > whiteboard
Definition: resources.cpp:33
virtual ~play_controller()
bool is_proxy_human() const
Definition: team.hpp:291
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:211
void set_end_level_data(const end_level_data &data)
void end_turn(int pnum)
Definition: game_board.cpp:76
int get_path_turns() const
std::size_t i
Definition: function.cpp:940
soundsource::manager * soundsources
Definition: resources.cpp:27
const std::string & select_music(bool victory) const
void redo()
Redoes the top action on the redo stack.
Definition: undo.cpp:404
map_location map_start_
const std::vector< std::string > & command_history() const
unit_map::iterator selected_unit()
std::string get_active_ai_identifier_for_side(side_number side)
Gets AI algorithm identifier for active AI of the given side.
Definition: manager.cpp:654
void reset_gamestate(const config &level, int replay_pos)
const t_string & objectives() const
Definition: team.hpp:248
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:1008
#define log_scope(description)
Definition: log.hpp:206
void tab(const std::set< std::string > &dictionary)
bool proceed_to_next_level
whether to proceed to the next scenario, equals is_victory in sp.
bool can_use_synced_wml_menu() const
void turn_event_fired()
t_string get_scenario_name() const
game_data gamedata_
Definition: game_state.hpp:45
int w
Additional information on the game outcome which can be provided by WML.
int get_random_int(int min, int max)
This helper method provides a random int from the underlying generator, using results of next_random...
Definition: random.hpp:51
actions::undo_list & undo_stack()
std::map< std::string, std::string > get_acquaintances_nice(const std::string &filter)
Definition: game.cpp:216
static void save(LexState *ls, int c)
Definition: llex.cpp:57
bool can_redo() const
static lg::log_domain log_enginerefac("enginerefac")
std::string observer
bool is_local() const
Definition: team.hpp:269
Class for replay saves (either manually or automatically).
Definition: savegame.hpp:266
config & add_child(config_key_type key)
Definition: config.cpp:500
pump_result_t fire(const std::string &event, const entity_location &loc1=entity_location::null_entity, const entity_location &loc2=entity_location::null_entity, const config &data=config())
Function to fire an event.
Definition: pump.cpp:486
compression::format save_compression_format()
Definition: game.cpp:851
bool is_replay() const
bool clear_shroud(int side, bool reset_fog, bool fire_events)
Function that will clear shroud (and fog) based on current unit positions.
Definition: vision.cpp:760
static bool try_block()
bool get_disallow_observers() const
Definition: team.hpp:348
static void display(std::function< void()> f)
void set_game_display(game_display *)
Definition: game_state.cpp:245
bool is_local_human() const
Definition: team.hpp:275
events::mouse_handler & get_mouse_handler_base() override
Get a reference to a mouse handler member a derived class uses.
const std::unique_ptr< gui::textbox > & box() const
game_state & gamestate()
double t
Definition: astarsearch.cpp:64
Various functions that implement healing of units (when a side turn starts).
const game_config_view & game_config_
map_location prev
Definition: astarsearch.cpp:65
game_classification & classification()
Definition: saved_game.hpp:54
game_board board_
Definition: game_state.hpp:46
A variable-expanding proxy for the config class.
Definition: variable.hpp:44
Various functions that implement the undoing (and redoing) of in-game commands.
Standard logging facilities (interface).
void autosave(const bool disable_autosave, const int autosave_max, const int infinite_autosaves)
Definition: savegame.cpp:609
int current_side() const
Returns the number of the side whose turn it is.
Object which contains all the possible locations a unit can move to, with associated best routes to t...
Definition: pathfind.hpp:71
void remove_scenario_fixes()
Definition: types.cpp:1414
static const map_location & null_location()
Definition: location.hpp:80
game_lua_kernel * lua_kernel
Definition: resources.cpp:25
config * get_next_action()
Definition: replay.cpp:616
std::set< std::string > all_players() const
static lg::log_domain log_aitesting("ai/testing")
void fire_preload()
preload events cannot be synced
virtual plugins_context * get_plugins_context() override
Get (optionally) a plugins context a derived class uses.
void new_turn(int pnum)
Definition: game_board.cpp:67
#define e
actions::undo_list * undo_stack
Definition: resources.cpp:32
std::size_t turn() const
play_controller(const config &level, saved_game &state_of_game, bool skip_replay)
void update_savegame_snapshot() const
void remove_snapshot()
Definition: saved_game.cpp:575
const config & child_or_empty(config_key_type key) const
Returns the first child with the given key, or an empty config if there is none.
Definition: config.cpp:477
void set_side(int side_number)
bool next_turn(game_data *vars)
Function to move to the next turn.
PHASE phase() const
Definition: game_data.hpp:78
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:59
game_classification & get_classification()
bool valid() const
Definition: map.hpp:273
Class that keeps track of all the keys on the keyboard.
Definition: key.hpp:27
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
Definition: saved_game.hpp:58
void delete_all_wml_hotkeys()
deletes all wml hotkeys, should be called after a game has ended
std::vector< std::string > get_commands_list()
void write(config &cfg) const
Definition: game_state.cpp:250
const std::set< map_location > & villages() const
Definition: team.hpp:192
void close(game_display &gui)
pathfind::manager * tunnels
Definition: resources.cpp:31
bool can_undo() const
True if there are actions that can be undone.
Definition: undo.hpp:95
static rng & default_instance()
Definition: random.cpp:73
virtual void check_objectives()=0
bool save_game_automatic(bool ask_for_overwrite=false, const std::string &filename="")
Saves a game without user interaction, unless the file exists and it should be asked to overwrite it...
Definition: savegame.cpp:367
virtual void play_side_impl()
bool start_event_fired_
Definition: game_state.hpp:66
T modulo(T num, int mod, T min=0)
Definition: math.hpp:61
void show_objectives() const
bool init_side_done_now_
Whether we did init sides in this session (false = we did init sides before we reloaded the game)...
bool remove_from_carryover_on_defeat_
void do_ai_formula(const std::string &str, int side_num, mouse_handler &mousehandler)
#define LOG_NG
virtual void sync_end_turn()