The Battle for Wesnoth  1.19.5+dev
playsingle_controller.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2024
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  * Logic for single-player game.
20  */
21 
23 
24 #include "actions/undo.hpp"
25 #include "ai/manager.hpp"
26 #include "ai/testing.hpp"
27 #include "display_chat_manager.hpp"
28 #include "carryover_show_gold.hpp"
29 #include "formula/string_utils.hpp"
30 #include "game_end_exceptions.hpp"
31 #include "game_events/pump.hpp"
32 #include "gettext.hpp"
36 #include "log.hpp"
37 #include "map/label.hpp"
38 #include "map/map.hpp"
40 #include "replay_controller.hpp"
41 #include "replay_helper.hpp"
42 #include "resources.hpp"
43 #include "saved_game.hpp"
44 #include "savegame.hpp"
46 #include "sound.hpp"
47 #include "soundsource.hpp"
48 #include "synced_context.hpp"
49 #include "video.hpp"
51 #include "whiteboard/manager.hpp"
52 
53 #include <thread>
54 
55 static lg::log_domain log_aitesting("ai/testing");
56 #define LOG_AIT LOG_STREAM(info, log_aitesting)
57 // If necessary, this define can be replaced with `#define LOG_AIT std::cout` to restore previous behavior
58 
59 static lg::log_domain log_engine("engine");
60 #define ERR_NG LOG_STREAM(err, log_engine)
61 #define LOG_NG LOG_STREAM(info, log_engine)
62 #define DBG_NG LOG_STREAM(debug, log_engine)
63 
64 static lg::log_domain log_enginerefac("enginerefac");
65 #define LOG_RG LOG_STREAM(info, log_enginerefac)
66 
68  : play_controller(level, state_of_game)
69  , cursor_setter_(cursor::NORMAL)
70  , end_turn_requested_(false)
71  , ai_fallback_(false)
72  , replay_controller_()
73 {
74  // upgrade hotkey handler to the sp (whiteboard enabled) version
75  hotkey_handler_ = std::make_unique<hotkey_handler>(*this, saved_game_);
76 
77 
78  plugins_context_->set_accessor_string("level_result", std::bind(&playsingle_controller::describe_result, this));
79  plugins_context_->set_accessor_int("turn", std::bind(&play_controller::turn, this));
80 }
81 
82 ///Defined here to reduce file includes.
84 
85 
87 {
88  if(!is_regular_game_end()) {
89  return "NONE";
90  } else if(get_end_level_data().is_victory) {
91  return "VICTORY";
92  } else {
93  return "DEFEAT";
94  }
95 }
96 
98 {
99  LOG_NG << "Initializing GUI... " << timer();
100  // If we are retarting replay from linger mode.
103 
104  // Scroll to the starting position of the first team. If there is a
105  // human team, use that team; otherwise use team 1. If the map defines
106  // a starting position for the selected team, scroll to that tile. Note
107  // this often does not matter since many scenario start with messages,
108  // which will usually scroll to the speaker. Also note that the map
109  // does not necessarily define the starting positions. While usually
110  // best to use the map, the scenarion may explicitly set the positions,
111  // overriding those found in the map (if any).
112  if(map_start_.valid()) {
113  gui_->scroll_to_tile(map_start_, game_display::WARP, false);
114  LOG_NG << "Found good stored ui location " << map_start_;
115  } else {
116 
117  int scroll_team = find_viewing_side();
118  if(scroll_team == 0) {
119  scroll_team = 1;
120  }
121 
122  map_location loc(get_map().starting_position(scroll_team));
123  if((loc.x >= 0) && (loc.y >= 0)) {
124  gui_->scroll_to_tile(loc, game_display::WARP);
125  LOG_NG << "Found bad stored ui location " << map_start_ << " using side starting location " << loc;
126  } else {
127  LOG_NG << "Found bad stored ui location";
128  }
129  }
130 
131  // Fade in
132  gui_->set_prevent_draw(false);
133  gui_->queue_repaint();
134  if(!video::headless() && !video::testing()) {
135  gui_->fade_to({0,0,0,0}, std::chrono::milliseconds{500});
136  } else {
137  gui_->set_fade({0,0,0,0});
138  }
139 
141 }
142 
144 {
145  gui_->labels().read(level);
147 
148  // Read sound sources
149  assert(soundsources_manager_ != nullptr);
150  for(const config& s : level.child_range("sound_source")) {
151  try {
153  soundsources_manager_->add(spec);
154  } catch(const bad_lexical_cast&) {
155  ERR_NG << "Error when parsing sound_source config: bad lexical cast.";
156  ERR_NG << "sound_source config was: " << s.debug();
157  ERR_NG << "Skipping this sound source...";
158  }
159  }
160 
161  // At the beginning of the scenario, save a snapshot as replay_start
162  if(saved_game_.replay_start().empty()) {
164  }
165 
166  fire_preload();
168 
169  start_game();
170  gamestate_->player_number_ = skip_empty_sides(gamestate_->player_number_).side_num;
171 
172  if(!get_teams().empty()) {
173  init_side_begin();
174  if(gamestate().in_phase(game_data::TURN_PLAYING)) {
175  init_side_end();
176  }
177  }
178 
180  // This won't cause errors later but we should notify the user about it in case he didn't knew it.
182  // TODO: find a better title
183  _("Game Error"),
184  _("This multiplayer game uses an alternative random mode, if you don’t know what this message means, then "
185  "most likely someone is cheating or someone reloaded a corrupt game."));
186  }
187 }
188 
190 {
191  const int sides = static_cast<int>(get_teams().size());
192  const int max = side_num + sides;
193 
194  for (; side_num != max; ++side_num) {
195  int side_num_mod = modulo(side_num, sides, 1);
196  if(!gamestate().board_.get_team(side_num_mod).is_empty()) {
197  return { side_num_mod, side_num_mod != side_num };
198  }
199  }
200  return { side_num, true };
201 }
202 
204 {
205  //TODO: Its still unclear to me when end_turn_requested_ should be reset, i guess the idea is
206  // in particular that in rare cases when the player looses control at the same time
207  // as he presses "end turn" and then regains control back, the "end turn" should be discarded?
208  //One of the main reasonsy why this is here is probably also that play_controller has no access to it.
210 
212 
214  play_side();
215  assert(is_regular_game_end() || gamestate().in_phase(game_data::TURN_ENDED));
216  }
217 
218  if (!is_regular_game_end() && gamestate().in_phase(game_data::TURN_ENDED)) {
220  }
221 
222  if (is_regular_game_end() && !gamestate().in_phase(game_data::GAME_ENDED)) {
224  do_end_level();
226  }
227 
228  if (gamestate().in_phase(game_data::GAME_ENDED)) {
230  maybe_linger();
231  }
232 }
233 
235 {
236  do {
237  if(std::find_if(get_teams().begin(), get_teams().end(), [](const team& t) { return !t.is_empty(); }) == get_teams().end()){
238  throw game::game_error("The scenario has no (non-empty) sides defined");
239  }
241 
243  if(is_regular_game_end()) {
244  return;
245  }
246  // This flag can be set by derived classes (in overridden functions).
247  player_type_changed_ = false;
248 
249 
250  play_side_impl();
251 
252  if(is_regular_game_end()) {
253  return;
254  }
255  } while(player_type_changed_);
256 
257  // Keep looping if the type of a team (human/ai/networked) has changed mid-turn
258  sync_end_turn();
259 }
260 
262 {
263  if(is_regular_game_end()) {
264  return;
265  }
266 
267  /// Make a copy, since the [end_turn] was already sent to to server any changes to
268  // next_player_number by wml would cause OOS otherwise.
269  int next_player_number_temp = gamestate_->next_player_number_;
270  whiteboard_manager_->on_finish_side_turn(current_side());
271 
273  if(is_regular_game_end()) {
274  return;
275  }
276 
277  auto [next_player_number, new_turn] = skip_empty_sides(next_player_number_temp);
278 
279  if(new_turn) {
280  finish_turn();
281  if(is_regular_game_end()) {
282  return;
283  }
284  // Time has run out
285  check_time_over();
286  if(is_regular_game_end()) {
287  return;
288  }
289  did_tod_sound_this_turn_ = false;
290  }
291 
292  gamestate_->player_number_ = next_player_number;
293  if(current_team().is_empty()) {
294  // We don't support this case (turn end events emptying the next sides controller) since the server cannot handle it.
295  throw game::game_error("Empty side after new turn events");
296  }
297 
298  if(new_turn) {
299  whiteboard_manager_->on_gamestate_change();
300  gui_->new_turn();
301  gui_->invalidate_game_status();
302  }
305  did_autosave_this_turn_ = false;
306  end_turn_requested_ = false;
307  init_side_begin();
308 }
309 
311 {
312  LOG_NG << "starting main loop\n" << timer();
313 
315  while(!(gamestate().in_phase(game_data::GAME_ENDED) && end_turn_requested_ )) {
316  try {
317  play_some();
318  } catch(const reset_gamestate_exception& ex) {
319  boost::dynamic_bitset<> local_players;
320  local_players.resize(get_teams().size(), true);
321  // Preserve side controllers, because we won't get the side controoller updates again when replaying.
322  for(std::size_t i = 0; i < local_players.size(); ++i) {
323  local_players[i] = get_teams()[i].is_local();
324  }
325 
326  if(ex.stats_) {
327  // "Back to turn"
329  } else {
330  // "Reset Replay To start"
332  }
333 
334  reset_gamestate(*ex.level, (*ex.level)["replay_pos"].to_int());
335 
336  for(std::size_t i = 0; i < local_players.size(); ++i) {
337  resources::gameboard->teams()[i].set_local(local_players[i]);
338  }
339 
340  // TODO: we currently don't set the music to the initial playlist, should we?
341 
343 
344  if(replay_controller_ == nullptr) {
345  replay_controller_ = std::make_unique<replay_controller>(*this, false, ex.level, [this]() { on_replay_end(false); });
346  }
347 
348  if(ex.start_replay) {
349  replay_controller_->play_replay();
350  }
351  }
352  } // end for loop
353 }
354 
356 {
358  exit(0);
359  }
360  const bool is_victory = get_end_level_data().is_victory;
361 
363 
364  const end_level_data& end_level = get_end_level_data();
365 
366  if(get_teams().empty()) {
367  // this is probably only a story scenario, i.e. has its endlevel in the prestart event
368  return;
369  }
370 
371 
372  pump().fire(is_victory ? "local_victory" : "local_defeat");
373 
374  { // Block for set_scontext_synced_base
376  pump().fire(end_level.proceed_to_next_level ? level_result::victory : level_result::defeat);
377  pump().fire("scenario_end");
378  }
379 
380  if(end_level.proceed_to_next_level) {
382  }
383 
384  if(is_observer()) {
385  gui2::show_transient_message(_("Game Over"), _("The game is over."));
386  }
387 
388  // If we're a player, and the result is victory/defeat, then send
389  // a message to notify the server of the reason for the game ending.
391  "info", config {
392  "type", "termination",
393  "condition", "game over",
394  "result", is_victory ? level_result::victory : level_result::defeat,
395  },
396  });
397 
398  // Play victory music once all victory events
399  // are finished, if we aren't observers and the
400  // carryover dialog isn't disabled.
401  //
402  // Some scenario authors may use 'continue'
403  // result for something that is not story-wise
404  // a victory, so let them use [music] tags
405  // instead should they want special music.
406  const std::string& end_music = select_music(is_victory);
407  if((!is_victory || end_level.transient.carryover_report) && !end_music.empty()) {
409  sound::play_music_once(end_music);
410  }
411 
414 
415 }
416 
418 {
419  LOG_NG << "in playsingle_controller::play_scenario()...";
420 
421  // Start music.
422  for(const config& m : level.child_range("music")) {
423  sound::play_music_config(m, true);
424  }
425 
427 
429  // Combine all the [story] tags into a single config. Handle this here since
430  // storyscreen::controller doesn't have a default constructor.
431  config cfg;
432  for(const auto& iter : level.child_range("story")) {
433  cfg.append_children(iter);
434  }
435 
436  if(!cfg.empty()) {
438  }
439  }
440 
441 
442  try {
444  // clears level config (the intention was probably just to save some ram),
445  // Note: this might clear 'level', so don't use level after this.
447 
449 
450  // TODO: would it be better if the is_networked_mp() check was done in is_observer() ?
451  if(is_networked_mp() && is_observer()) {
452  return level_result::type::observer_end;
453  }
454  return level_result::get_enum(get_end_level_data().test_result).value_or(get_end_level_data().is_victory ? level_result::type::victory : level_result::type::defeat);
455  } catch(const savegame::load_game_exception&) {
456  // Loading a new game is effectively a quit.
457  saved_game_.clear();
458  throw;
459  } catch(const wesnothd_error& e) {
460  scoped_savegame_snapshot snapshot(*this);
461  savegame::ingame_savegame save(saved_game_, prefs::get().save_compression_format());
462  if(e.message == "") {
464  _("A network disconnection has occurred, and the game cannot continue. Do you want to save the game?"),
466  } else {
468  _("This game has been ended.\nReason: ") + e.message + _("\nDo you want to save the game?"),
470  }
471 
472  if(dynamic_cast<const ingame_wesnothd_error*>(&e) || dynamic_cast<const leavegame_wesnothd_error*>(&e)) {
473  return level_result::type::quit;
474  } else {
475  throw;
476  }
477  }
478 }
479 
481 {
482  while(!should_return_to_play_side()) {
484  using namespace std::chrono_literals;
485  std::this_thread::sleep_for(10ms);
486  }
487 }
488 
490 {
491  if(replay_controller_.get() != nullptr) {
492  replay_controller_->play_side_impl();
493 
495  replay_controller_.reset();
496  }
497  } else if((current_team().is_local_human() && current_team().is_proxy_human())) {
498  LOG_NG << "is human...";
499  // If a side is dead end the turn, but play at least side=1's
500  // turn in case all sides are dead
501  if(gamestate().board_.side_units(current_side()) == 0 && !(get_units().empty() && current_side() == 1)) {
503  }
504 
505 
506  if(!end_turn_requested_) {
508  play_human_turn();
509  }
510 
513  }
514 
515  LOG_NG << "human finished turn...";
516  } else if(current_team().is_local_ai() || (current_team().is_local_human() && current_team().is_droid())) {
517  play_ai_turn();
518  } else if(current_team().is_network()) {
520  } else if(current_team().is_local_human() && current_team().is_idle()) {
521  end_turn_enable(false);
524 
526  play_idle_loop();
527  }
528  } else {
529  // we should have skipped over empty controllers before so this shouldn't be possible
530  ERR_NG << "Found invalid side controller " << side_controller::get_string(current_team().controller()) << " ("
531  << side_proxy_controller::get_string(current_team().proxy_controller()) << ") for side " << current_team().side();
532  }
533 }
534 
536 {
537  log_scope("player turn");
538  assert(!is_linger_mode());
540  return;
541  }
542 
543  if(!did_autosave_this_turn_ && !game_config::disable_autosave && prefs::get().auto_save_max() > 0) {
545  scoped_savegame_snapshot snapshot(*this);
546  savegame::autosave_savegame save(saved_game_, prefs::get().save_compression_format());
548  }
549 
550  if(prefs::get().turn_bell()) {
552  }
553 }
554 
556 {
557  if(prefs::get().turn_dialog() && !is_regular_game_end()) {
558  blindfold b(*gui_, true); // apply a blindfold for the duration of this dialog
559  gui_->queue_rerender();
560  std::string message = _("It is now $name|’s turn");
561  utils::string_map symbols;
562  symbols["name"] = gamestate().board_.get_team(current_side()).side_name();
563  message = utils::interpolate_variables_into_string(message, &symbols);
564  gui2::show_transient_message("", message);
565  }
566 }
567 
569 {
571  return;
572  }
573 
574  try {
576  } catch(const return_to_play_side_exception&) {
577  }
578 }
579 
581 {
583 
584  if(!prefs::get().disable_auto_moves()) {
585  execute_gotos();
586  }
587 
588  end_turn_enable(true);
589 
593  }
594 }
595 
597 {
598  if(is_linger_mode()) {
599  // If we need to set the status depending on the completion state
600  // the key to it is here.
601  gui_->set_game_mode(game_display::LINGER);
602  // change the end-turn button text from "End Turn" to "End Scenario"
603  gui_->get_theme().refresh_title2("button-endturn", "title2");
604 
605  if(get_end_level_data().transient.reveal_map) {
606  // Change the view of all players and observers
607  // to see the whole map regardless of shroud and fog.
608  update_gui_to_player(gui_->viewing_team_index(), true);
609  }
610  } else {
611  gui_->set_game_mode(game_display::RUNNING);
612  // change the end-turn button text from "End Scenario" to "End Turn"
613  gui_->get_theme().refresh_title2("button-endturn", "title");
614  }
615  // Also checcks whether the button can be pressed.
616  gui_->queue_rerender();
617 }
618 
620 {
621  LOG_NG << "beginning end-of-scenario linger";
622 
623  // Make all of the able-to-move units' orbs consistently red
625 
627 
628  try {
629  if(replay_controller_.get() != nullptr) {
630  replay_controller_->play_side_impl();
632  replay_controller_.reset();
633  }
634  }
635  while(!end_turn_requested_) {
636  play_slice();
637  }
638  } catch(const savegame::load_game_exception&) {
639  // Loading a new game is effectively a quit.
640  saved_game_.clear();
641  throw;
642  }
643 
644  LOG_NG << "ending end-of-scenario linger";
645 }
646 
648 {
649  // TODO: this is really only needed to refresh the visual state of the buttons on each turn.
650  // It looks like we can put it in play_side_impl, but it definitely should be removed from
651  // here since other code already takes care of actually enabling/disabling the end turn button.
653 }
654 
656 {
657  // Clear moves from the GUI.
658  gui_->set_route(nullptr);
659  gui_->unhighlight_reach();
660 }
661 
663 {
664  LOG_NG << "is ai...";
665 
666  end_turn_enable(false);
667 
668  const cursor::setter cursor_setter(cursor::WAIT);
669 
670  // Correct an oddball case where a human could have left delayed shroud
671  // updates on before giving control to the AI. (The AI does not bother
672  // with the undo stack, so it cannot delay shroud updates.)
673  team& cur_team = current_team();
674  if(!cur_team.auto_shroud_updates()) {
675  // We just took control, so the undo stack is empty. We still need
676  // to record this change for the replay though.
678  }
679 
680  undo_stack().clear();
681 
682  try {
683  try {
686  }
687  } catch(const return_to_play_side_exception&) {
688  } catch(const fallback_ai_to_human_exception&) {
690  player_type_changed_ = true;
691  ai_fallback_ = true;
692  }
693  } catch(...) {
694  DBG_NG << "Caught exception playing ai turn: " << utils::get_unknown_exception_type();
695  throw;
696  }
697 
700  }
701 }
702 
703 /**
704  * Will handle sending a networked notification in descendent classes.
705  */
707 {
708  gui_->get_chat_manager().add_chat_message(std::time(nullptr), "Wesnoth", 0,
709  "This side is in an idle state. To proceed with the game, the host must assign it to another controller.",
711 }
712 
713 /**
714  * Will handle networked turns in descendent classes.
715  */
717 {
718  // There should be no networked sides in single-player.
719  ERR_NG << "Networked team encountered by playsingle_controller.";
720 }
721 
722 void playsingle_controller::handle_generic_event(const std::string& name)
723 {
724  if(name == "ai_user_interact") {
725  play_slice();
726  }
727 }
728 
730 {
731  if(is_linger_mode()) {
732  end_turn_requested_ = true;
733  } else if(!is_browsing() && menu_handler_.end_turn(current_side())) {
735  }
736 }
737 
739 {
741  end_turn_requested_ = true;
742 }
743 
745 {
746  end_turn_requested_ = true;
747 }
748 
750 {
751  if(!get_teams().empty()) {
752  const team& t = gui_->viewing_team();
753 
754  if(!is_regular_game_end() && !is_browsing() && t.objectives_changed()) {
755  show_objectives();
756  }
757  }
758 }
759 
761 {
762  // mouse_handler expects at least one team for linger mode to work.
763  assert(is_regular_game_end());
764  linger();
765  end_turn_requested_ = true;
766 }
767 
769 {
770  // We cannot add [end_turn] to the recorder while executing another action.
772 
773  if(!gamestate().in_phase(game_data::TURN_ENDED)) {
774  assert(end_turn_requested_);
775  assert(current_team().is_local());
776  assert(gamestate().in_phase(game_data::TURN_PLAYING));
777  // TODO: we should also send this immediately.
778  resources::recorder->end_turn(gamestate_->next_player_number_);
780 
781  }
782 
783 
784  assert(gamestate().in_phase(game_data::TURN_ENDED));
785 
786  if(ai_fallback_) {
787  current_team().make_ai();
788  ai_fallback_ = false;
789  }
790 }
791 
792 bool playsingle_controller::is_team_visible(int team_num, bool observer) const
793 {
794  const team& t = gamestate().board_.get_team(team_num);
795  if(observer) {
796  return !t.get_disallow_observers() && !t.is_empty();
797  } else {
798  return t.is_local_human() && !t.is_idle();
799  }
800 }
801 
803 {
804  const int num_teams = get_teams().size();
805  const bool observer = is_observer();
806 
807  for(int i = 0; i < num_teams; i++) {
808  const int team_num = modulo(current_side() + i, num_teams, 1);
809  if(is_team_visible(team_num, observer)) {
810  return team_num;
811  }
812  }
813 
814  return 0;
815 }
816 
818 {
819  if(replay_controller_ && replay_controller_->is_controlling_view()) {
820  replay_controller_->update_viewing_player();
821  } else if(int side_num = find_viewing_side()) {
822  if(side_num != gui_->viewing_team().side() || gui_->show_everything()) {
823  update_gui_to_player(side_num - 1);
824  }
825  }
826 }
827 
829 {
830  if(replay_controller_ && replay_controller_->allow_reset_replay()) {
831  replay_controller_->stop_replay();
832  throw reset_gamestate_exception(replay_controller_->get_reset_state(), {}, false);
833  } else {
834  ERR_NG << "received invalid reset replay";
835  }
836 }
837 
839 {
840  replay_controller_ = std::make_unique<replay_controller>(
841  *this,
842  true,
843  std::make_shared<config>(saved_game_.get_replay_starting_point()),
844  std::bind(&playsingle_controller::on_replay_end, this, is_unit_test)
845  );
846 
847  if(is_unit_test) {
848  replay_controller_->play_replay();
849  }
850 }
851 
853 {
855  return true;
856  } else if(gamestate().in_phase(game_data::TURN_ENDED)) {
857  return true;
858  } else if((gamestate().in_phase(game_data::TURN_STARTING_WAITING) || end_turn_requested_) && replay_controller_.get() == 0 && current_team().is_local() && !current_team().is_idle()) {
859  // When we are a locally controlled side and havent done init_side yet also return to play_side
860  return true;
861  } else {
862  return false;
863  }
864 }
865 
867 {
868  if(is_networked_mp()) {
869  // we are using the "Back to turn (replay)" feature
870  // And have reached the current gamestate: end the replay and continue normally.
872  } else if(is_unit_test) {
873  replay_controller_->return_to_play_side();
874  if(!is_regular_game_end()) {
876  e.proceed_to_next_level = false;
877  e.is_victory = false;
879  }
880  } else {
881  replay_controller_->stop_replay();
882  }
883 }
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands.
double t
Definition: astarsearch.cpp:63
void carryover_show_gold(game_state &state, bool hidden, bool is_observer, bool is_test)
calculates the amount of gold carried over for each team, stores the data in the team object and show...
void clear()
Clears the stack of undoable (and redoable) actions.
Definition: undo.cpp:199
static manager & get_singleton()
Definition: manager.hpp:142
void play_turn(side_number side)
Plays a turn for the specified side using its active AI.
Definition: manager.cpp:724
static void log_game_end()
Definition: testing.cpp:101
static void log_game_start()
Definition: testing.cpp:90
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:172
void append_children(const config &cfg)
Adds children from cfg.
Definition: config.cpp:167
bool empty() const
Definition: config.cpp:849
virtual void play_slice()
void execute_gotos(mouse_handler &mousehandler, int side_num)
bool end_turn(int side_num)
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:80
void heal_all_survivors()
Definition: game_board.cpp:95
team & get_team(int i)
Definition: game_board.hpp:92
void set_all_units_user_end_turn()
Definition: game_board.cpp:88
void set_end_turn_forced(bool v)
Definition: game_data.hpp:145
void set_phase(PHASE phase)
Definition: game_data.hpp:106
@ GAME_ENDED
The game has ended and the user is observing the final state "lingering" The game can be saved here.
Definition: game_data.hpp:102
@ TURN_PLAYING
The User is controlling the game and invoking actions The game can be saved here.
Definition: game_data.hpp:93
@ TURN_ENDED
The turn_end, side_turn_end etc [events] are fired next phase: TURN_STARTING_WAITING (default),...
Definition: game_data.hpp:96
@ TURN_STARTING_WAITING
we are waiting for the turn to start.
Definition: game_data.hpp:86
@ GAME_ENDING
The victory etc.
Definition: game_data.hpp:99
static PHASE read_phase(const config &cfg)
Definition: game_data.cpp:177
bool end_turn_forced() const
Definition: game_data.hpp:146
@ RUNNING
no linger overlay, show fog and shroud.
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:399
game_board board_
Definition: game_state.hpp:44
game_data gamedata_
Definition: game_state.hpp:43
static void display(const std::string &scenario_name, const config &story)
const std::string & select_music(bool victory) const
config to_config() const
Builds the snapshot config from members and their respective configs.
std::vector< team > & get_teams()
std::unique_ptr< hotkey_handler > hotkey_handler_
virtual void init_gui()
std::unique_ptr< game_state > gamestate_
void show_objectives() const
events::menu_handler menu_handler_
void fire_preload()
preload events cannot be synced
bool is_linger_mode() const
actions::undo_list & undo_stack()
const unit_map & get_units() const
void set_end_level_data(const end_level_data &data)
bool is_observer() const
void reset_gamestate(const config &level, int replay_pos)
bool is_skipping_story() const
bool is_regular_game_end() const
saved_game & get_saved_game()
saved_game & saved_game_
game_state & gamestate()
hotkey::command_executor * get_hotkey_command_executor() override
Optionally get a command executor to handle context menu events.
std::unique_ptr< game_display > gui_
void maybe_do_init_side()
Called by turn_info::process_network_data() or init_side() to call do_init_side() if necessary.
bool is_browsing() const override
virtual void send_to_wesnothd(const config &, const std::string &="unknown") const
std::unique_ptr< soundsource::manager > soundsources_manager_
const end_level_data & get_end_level_data() const
int current_side() const
Returns the number of the side whose turn it is.
virtual bool is_networked_mp() const
bool is_skipping_replay() const
const gamemap & get_map() const
bool did_autosave_this_turn_
Whether we did init sides in this session (false = we did init sides before we reloaded the game).
bool is_replay() const
bool player_type_changed_
true when the controller of the currently playing side has changed.
std::size_t turn() const
map_location map_start_
void update_gui_to_player(const int team_index, const bool observe=false)
Changes the UI for this client to the passed side index.
events::mouse_handler mouse_handler_
const auto & timer() const
std::unique_ptr< plugins_context > plugins_context_
game_events::wml_event_pump & pump()
void finish_side_turn_events()
persist_manager persist_
std::shared_ptr< wb::manager > whiteboard_manager_
virtual void check_time_over()
t_string get_scenario_name() const
void end_turn_enable(bool enable)
virtual void play_network_turn()
Will handle networked turns in descendent classes.
virtual void init_gui() override
void on_replay_end(bool is_unit_test)
std::string describe_result() const
std::unique_ptr< replay_controller > replay_controller_
non-null when replay mode in active, is used in singleplayer and for the "back to turn" feature in mu...
bool ai_fallback_
true when the current side is actually an ai side but was taken over by a human (usually for debuggin...
virtual void check_objectives() override
ses_result skip_empty_sides(int side_num)
Calculates the current side, starting at side_num that is non-empty.
bool is_team_visible(int team_num, bool observer) const
level_result::type play_scenario(const config &level)
virtual bool should_return_to_play_side() const override
~playsingle_controller()
Defined here to reduce file includes.
void update_viewing_player() override
playsingle_controller(const config &level, saved_game &state_of_game)
virtual void handle_generic_event(const std::string &name) override
virtual void do_idle_notification()
Will handle sending a networked notification in descendent classes.
bool end_turn_requested_
true iff the user has pressed the end turn button this turn.
int find_viewing_side() const override
returns 0 if no such team was found.
void play_scenario_init(const config &level)
void enable_replay(bool is_unit_test=false)
static prefs & get()
static config get_auto_shroud(bool turned_on)
Records that the player has toggled automatic shroud updates.
void end_turn(int next_player_number)
Definition: replay.cpp:306
Exception used to escape form the ai or ui code to playsingle_controller::play_side.
game_classification & classification()
Definition: saved_game.hpp:56
void clear()
Definition: saved_game.cpp:814
config & replay_start()
Definition: saved_game.hpp:128
void remove_snapshot()
Definition: saved_game.cpp:606
const config & get_replay_starting_point()
Definition: saved_game.cpp:617
statistics_record::campaign_stats_t & statistics()
Definition: saved_game.hpp:143
Class for autosaves.
Definition: savegame.hpp:281
void autosave(const bool disable_autosave, const int autosave_max, const int infinite_autosaves)
Definition: savegame.cpp:601
Class for "normal" midgame saves.
Definition: savegame.hpp:254
Exception used to signal that the user has decided to abortt a game, and to load another game instead...
Definition: savegame.hpp:85
bool save_game_interactive(const std::string &message, DIALOG_TYPE dialog_type)
Save a game interactively through the savegame dialog.
Definition: savegame.cpp:378
Sound source info class.
static bool run_and_store(const std::string &commandname, const config &data, action_spectator &spectator=get_default_spectator())
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:75
const std::string & side_name() const
Definition: team.hpp:293
int side() const
Definition: team.hpp:175
bool auto_shroud_updates() const
Definition: team.hpp:324
void make_ai()
Definition: team.hpp:260
void make_human()
Definition: team.hpp:259
std::size_t i
Definition: function.cpp:1028
Contains the exception interfaces used to signal completion of a scenario, campaign or turn.
static std::string _(const char *str)
Definition: gettext.hpp:93
An extension of play_controller::hotkey_handler, which has support for SP wesnoth features like white...
Standard logging facilities (interface).
#define log_scope(description)
Definition: log.hpp:278
constexpr T modulo(T num, int mod, T min=0)
Definition: math.hpp:62
@ WAIT
Definition: cursor.hpp:28
@ NORMAL
Definition: cursor.hpp:28
std::string observer
std::string turn_bell
bool disable_autosave
Definition: game_config.cpp:91
bool exit_at_end
Definition: game_config.cpp:90
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 int INFINITE_AUTO_SAVES
Definition: preferences.hpp:59
game_board * gameboard
Definition: resources.cpp:20
replay * recorder
Definition: resources.cpp:28
void empty_playlist()
Definition: sound.cpp:612
void play_music_config(const config &music_node, bool allow_interrupt_current_track, int i)
Definition: sound.cpp:715
void play_music_once(const std::string &file)
Definition: sound.cpp:603
void commit_music_changes()
Definition: sound.cpp:842
void play_bell(const std::string &files)
Definition: sound.cpp:1061
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:85
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
Function which will interpolate variables, starting with '$' in the string 'str' with the equivalent ...
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
Definition: general.cpp:23
std::map< std::string, t_string > string_map
bool headless()
The game is running headless.
Definition: video.cpp:139
bool testing()
The game is running unit tests.
Definition: video.cpp:144
static lg::log_domain log_engine("engine")
#define ERR_NG
static lg::log_domain log_aitesting("ai/testing")
static lg::log_domain log_enginerefac("enginerefac")
#define DBG_NG
#define LOG_NG
Define the game's event mechanism.
Thrown when a lexical_cast fails.
Additional information on the game outcome which can be provided by WML.
bool proceed_to_next_level
whether to proceed to the next scenario, equals is_victory in sp.
transient_end_level transient
Error used for any general game error, e.g.
Definition: game_errors.hpp:47
We received invalid data from wesnothd during a game This means we cannot continue with the game but ...
Encapsulates the map of the game.
Definition: location.hpp:45
bool valid() const
Definition: location.hpp:110
std::shared_ptr< config > stats_
std::shared_ptr< config > level
void read(const config &cfg, bool append=false)
void clear_current_scenario()
Delete the current scenario from the stats.
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition: enum_base.hpp:46
static constexpr utils::optional< enum_type > get_enum(const std::string_view value)
Converts a string into its enum equivalent.
Definition: enum_base.hpp:57
bool carryover_report
Should a summary of the scenario outcome be displayed?
bool linger_mode
Should linger mode be invoked?
An error occurred during when trying to communicate with the wesnothd server.
static map_location::direction s
Gather statistics important for AI testing and output them.
Various functions that implement the undoing (and redoing) of in-game commands.
#define e
#define b