The Battle for Wesnoth  1.17.23+dev
playsingle_controller.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2023
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 "events.hpp"
30 #include "formula/string_utils.hpp"
31 #include "game_end_exceptions.hpp"
32 #include "game_events/pump.hpp"
33 #include "gettext.hpp"
37 #include "hotkey/hotkey_item.hpp"
38 #include "log.hpp"
39 #include "map/label.hpp"
40 #include "map/map.hpp"
41 #include "playturn.hpp"
42 #include "preferences/game.hpp"
43 #include "random_deterministic.hpp"
44 #include "replay_controller.hpp"
45 #include "replay_helper.hpp"
46 #include "resources.hpp"
47 #include "saved_game.hpp"
48 #include "savegame.hpp"
50 #include "sound.hpp"
51 #include "soundsource.hpp"
52 #include "statistics.hpp"
53 #include "synced_context.hpp"
54 #include "units/unit.hpp"
55 #include "video.hpp"
57 #include "whiteboard/manager.hpp"
58 
59 #include <boost/dynamic_bitset.hpp>
60 
61 static lg::log_domain log_aitesting("ai/testing");
62 #define LOG_AIT LOG_STREAM(info, log_aitesting)
63 // If necessary, this define can be replaced with `#define LOG_AIT std::cout` to restore previous behavior
64 
65 static lg::log_domain log_engine("engine");
66 #define ERR_NG LOG_STREAM(err, log_engine)
67 #define LOG_NG LOG_STREAM(info, log_engine)
68 #define DBG_NG LOG_STREAM(debug, log_engine)
69 
70 static lg::log_domain log_enginerefac("enginerefac");
71 #define LOG_RG LOG_STREAM(info, log_enginerefac)
72 
73 playsingle_controller::playsingle_controller(const config& level, saved_game& state_of_game, bool skip_replay)
74  : play_controller(level, state_of_game, skip_replay, true) // start faded
75  , cursor_setter_(cursor::NORMAL)
76  , replay_sender_(*resources::recorder)
77  , network_reader_([this](config& cfg) { return receive_from_wesnothd(cfg); })
78  , turn_data_(replay_sender_, network_reader_)
79  , end_turn_requested_(false)
80  , ai_fallback_(false)
81  , replay_controller_()
82 {
83  // upgrade hotkey handler to the sp (whiteboard enabled) version
84  hotkey_handler_ = std::make_unique<hotkey_handler>(*this, saved_game_);
85 
86 
87  plugins_context_->set_accessor_string("level_result", std::bind(&playsingle_controller::describe_result, this));
88  plugins_context_->set_accessor_int("turn", std::bind(&play_controller::turn, this));
89 }
90 
91 ///Defined here to reduce file includes.
93 
94 
96 {
97  if(!is_regular_game_end()) {
98  return "NONE";
99  } else if(get_end_level_data().is_victory) {
100  return "VICTORY";
101  } else {
102  return "DEFEAT";
103  }
104 }
105 
107 {
108  LOG_NG << "Initializing GUI... " << (SDL_GetTicks() - ticks());
109  // If we are retarting replay from linger mode.
112 
113  // Scroll to the starting position of the first team. If there is a
114  // human team, use that team; otherwise use team 1. If the map defines
115  // a starting position for the selected team, scroll to that tile. Note
116  // this often does not matter since many scenario start with messages,
117  // which will usually scroll to the speaker. Also note that the map
118  // does not necessarily define the starting positions. While usually
119  // best to use the map, the scenarion may explicitly set the positions,
120  // overriding those found in the map (if any).
121  if(map_start_.valid()) {
122  gui_->scroll_to_tile(map_start_, game_display::WARP, false);
123  LOG_NG << "Found good stored ui location " << map_start_;
124  } else {
125 
126  int scroll_team = find_viewing_side();
127  if(scroll_team == 0) {
128  scroll_team = 1;
129  }
130 
131  map_location loc(get_map().starting_position(scroll_team));
132  if((loc.x >= 0) && (loc.y >= 0)) {
133  gui_->scroll_to_tile(loc, game_display::WARP);
134  LOG_NG << "Found bad stored ui location " << map_start_ << " using side starting location " << loc;
135  } else {
136  LOG_NG << "Found bad stored ui location";
137  }
138  }
139 
140  // Fade in
141  gui_->set_prevent_draw(false);
142  gui_->queue_repaint();
143  if(!video::headless() && !video::testing()) {
144  gui_->fade_to({0,0,0,0}, 500);
145  } else {
146  gui_->set_fade({0,0,0,0});
147  }
148 
150 }
151 
153 {
154  gui_->labels().read(level);
156 
157  // Read sound sources
158  assert(soundsources_manager_ != nullptr);
159  for(const config& s : level.child_range("sound_source")) {
160  try {
162  soundsources_manager_->add(spec);
163  } catch(const bad_lexical_cast&) {
164  ERR_NG << "Error when parsing sound_source config: bad lexical cast.";
165  ERR_NG << "sound_source config was: " << s.debug();
166  ERR_NG << "Skipping this sound source...";
167  }
168  }
169 
170  // At the beginning of the scenario, save a snapshot as replay_start
171  if(saved_game_.replay_start().empty()) {
173  }
174 
175  fire_preload();
177 
178  start_game();
179  gamestate_->player_number_ = skip_empty_sides(gamestate_->player_number_).side_num;
180 
181  if(!get_teams().empty()) {
182  init_side_begin();
183  if(gamestate().in_phase(game_data::TURN_PLAYING)) {
184  init_side_end();
185  }
186  }
187 
189  // This won't cause errors later but we should notify the user about it in case he didn't knew it.
191  // TODO: find a better title
192  _("Game Error"),
193  _("This multiplayer game uses an alternative random mode, if you don't know what this message means, then "
194  "most likely someone is cheating or someone reloaded a corrupt game."));
195  }
196 }
197 
199 {
200  const int sides = static_cast<int>(get_teams().size());
201  const int max = side_num + sides;
202 
203  for (; side_num != max; ++side_num) {
204  int side_num_mod = modulo(side_num, sides, 1);
205  if(!gamestate().board_.get_team(side_num_mod).is_empty()) {
206  return { side_num_mod, side_num_mod != side_num };
207  }
208  }
209  return { side_num, true };
210 }
211 
213 {
214  //TODO: Its still unclear to me when end_turn_requested_ should be reset, i guess the idea is
215  // in particular that in rare cases when the player looses control at the same time
216  // as he presses "end turn" and then regains control back, the "end turn" should be discarded?
217  //One of the main reasonsy why this is here is probably also that play_controller has no access to it.
219 
221 
223  play_side();
224  assert(is_regular_game_end() || gamestate().in_phase(game_data::TURN_ENDED));
225  }
226 
227  if (!is_regular_game_end() && gamestate().in_phase(game_data::TURN_ENDED)) {
229  }
230 
231  if (is_regular_game_end() && !gamestate().in_phase(game_data::GAME_ENDED)) {
233  do_end_level();
235  }
236 
237  if (gamestate().in_phase(game_data::GAME_ENDED)) {
238  if(!get_end_level_data().transient.linger_mode || get_teams().empty() || video::headless()) {
239  end_turn_requested_ = true;
240  }
241  maybe_linger();
242  }
243 }
244 
246 {
247  if(is_regular_game_end()) {
248  return;
249  }
250 
251  /// Make a copy, since the [end_turn] was already sent to to server any changes to
252  // next_player_number by wml would cause OOS otherwise.
253  int next_player_number_temp = gamestate_->next_player_number_;
254  whiteboard_manager_->on_finish_side_turn(current_side());
255 
257  if(is_regular_game_end()) {
258  return;
259  }
260 
261  auto [next_player_number, new_turn] = skip_empty_sides(next_player_number_temp);
262 
263  if(new_turn) {
264  finish_turn();
265  if(is_regular_game_end()) {
266  return;
267  }
268  // Time has run out
269  check_time_over();
270  if(is_regular_game_end()) {
271  return;
272  }
273  did_tod_sound_this_turn_ = false;
274  }
275 
276  gamestate_->player_number_ = next_player_number;
277  if(current_team().is_empty()) {
278  // We don't support this case (turn end events emptying the next sides controller) since the server cannot handle it.
279  throw game::game_error("Empty side after new turn events");
280  }
281 
282  if(new_turn) {
283  whiteboard_manager_->on_gamestate_change();
284  gui_->new_turn();
285  gui_->invalidate_game_status();
286  }
289  did_autosave_this_turn_ = false;
290  end_turn_requested_ = false;
291  init_side_begin();
292 }
293 
295 {
296  LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks());
297 
299  while(!(gamestate().in_phase(game_data::GAME_ENDED) && end_turn_requested_ )) {
300  try {
301  play_some();
302  } catch(const reset_gamestate_exception& ex) {
303  boost::dynamic_bitset<> local_players;
304  local_players.resize(get_teams().size(), true);
305  // Preserve side controllers, because we won't get the side controoller updates again when replaying.
306  for(std::size_t i = 0; i < local_players.size(); ++i) {
307  local_players[i] = get_teams()[i].is_local();
308  }
309 
310  if(ex.stats_) {
311  // "Back to turn"
313  } else {
314  // "Reset Replay To start"
316  }
317 
318  reset_gamestate(*ex.level, (*ex.level)["replay_pos"]);
319 
320  for(std::size_t i = 0; i < local_players.size(); ++i) {
321  resources::gameboard->teams()[i].set_local(local_players[i]);
322  }
323 
324  // TODO: we currently don't set the music to the initial playlist, should we?
325 
327 
328  if(replay_controller_ == nullptr) {
329  replay_controller_ = std::make_unique<replay_controller>(*this, false, ex.level, [this]() { on_replay_end(false); });
330  }
331 
332  if(ex.start_replay) {
333  replay_controller_->play_replay();
334  }
335  }
336  } // end for loop
337 }
338 
340 {
342  exit(0);
343  }
344  const bool is_victory = get_end_level_data().is_victory;
345 
347 
348  const end_level_data& end_level = get_end_level_data();
349 
350  if(get_teams().empty()) {
351  // this is probably only a story scenario, i.e. has its endlevel in the prestart event
352  return;
353  }
354 
355 
356  pump().fire(is_victory ? "local_victory" : "local_defeat");
357 
358  { // Block for set_scontext_synced_base
360  pump().fire(end_level.proceed_to_next_level ? level_result::victory : level_result::defeat);
361  pump().fire("scenario_end");
362  }
363 
364  if(end_level.proceed_to_next_level) {
366  }
367 
368  if(is_observer()) {
369  gui2::show_transient_message(_("Game Over"), _("The game is over."));
370  }
371 
372  // If we're a player, and the result is victory/defeat, then send
373  // a message to notify the server of the reason for the game ending.
375  "info", config {
376  "type", "termination",
377  "condition", "game over",
378  "result", is_victory ? level_result::victory : level_result::defeat,
379  },
380  });
381 
382  // Play victory music once all victory events
383  // are finished, if we aren't observers and the
384  // carryover dialog isn't disabled.
385  //
386  // Some scenario authors may use 'continue'
387  // result for something that is not story-wise
388  // a victory, so let them use [music] tags
389  // instead should they want special music.
390  const std::string& end_music = select_music(is_victory);
391  if((!is_victory || end_level.transient.carryover_report) && !end_music.empty()) {
393  sound::play_music_once(end_music);
394  }
395 
398 
399 }
400 
402 {
403  LOG_NG << "in playsingle_controller::play_scenario()...";
404 
405  // Start music.
406  for(const config& m : level.child_range("music")) {
407  sound::play_music_config(m, true);
408  }
409 
411 
413  // Combine all the [story] tags into a single config. Handle this here since
414  // storyscreen::controller doesn't have a default constructor.
415  config cfg;
416  for(const auto& iter : level.child_range("story")) {
417  cfg.append_children(iter);
418  }
419 
420  if(!cfg.empty()) {
422  }
423  }
424 
425 
426  try {
428  // clears level config (the intention was probably just to save some ram),
429  // Note: this might clear 'level', so don't use level after this.
431 
433 
434  if(is_observer()) {
435  return level_result::type::observer_end;
436  }
437  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);
438  } catch(const savegame::load_game_exception&) {
439  // Loading a new game is effectively a quit.
440  saved_game_.clear();
441  throw;
442  } catch(const wesnothd_error& e) {
443  scoped_savegame_snapshot snapshot(*this);
445  if(e.message == "") {
447  _("A network disconnection has occurred, and the game cannot continue. Do you want to save the game?"),
449  } else {
451  _("This game has been ended.\nReason: ") + e.message + _("\nDo you want to save the game?"),
453  }
454 
455  if(dynamic_cast<const ingame_wesnothd_error*>(&e)) {
456  return level_result::type::quit;
457  } else {
458  throw;
459  }
460  }
461 }
462 
464 {
465  while(!should_return_to_play_side()) {
467  SDL_Delay(10);
468  }
469 }
470 
472 {
473  if(replay_controller_.get() != nullptr) {
474  replay_controller_->play_side_impl();
475 
477  replay_controller_.reset();
478  }
479  } else if((current_team().is_local_human() && current_team().is_proxy_human())) {
480  LOG_NG << "is human...";
481  // If a side is dead end the turn, but play at least side=1's
482  // turn in case all sides are dead
483  if(gamestate().board_.side_units(current_side()) == 0 && !(get_units().empty() && current_side() == 1)) {
485  }
486 
488 
489  if(!end_turn_requested_) {
490  play_human_turn();
491  }
492 
495  }
496 
497  LOG_NG << "human finished turn...";
498  } else if(current_team().is_local_ai() || (current_team().is_local_human() && current_team().is_droid())) {
499  play_ai_turn();
500  } else if(current_team().is_network()) {
502  } else if(current_team().is_local_human() && current_team().is_idle()) {
503  end_turn_enable(false);
506 
508  play_idle_loop();
509  }
510  } else {
511  // we should have skipped over empty controllers before so this shouldn't be possible
512  ERR_NG << "Found invalid side controller " << side_controller::get_string(current_team().controller()) << " ("
513  << side_proxy_controller::get_string(current_team().proxy_controller()) << ") for side " << current_team().side();
514  }
515 }
516 
518 {
519  log_scope("player turn");
520  assert(!is_linger_mode());
522  return;
523  }
524 
527  scoped_savegame_snapshot snapshot(*this);
530  }
531 
532  if(preferences::turn_bell()) {
534  }
535 }
536 
538 {
540  blindfold b(*gui_, true); // apply a blindfold for the duration of this dialog
541  gui_->queue_rerender();
542  std::string message = _("It is now $name|’s turn");
543  utils::string_map symbols;
544  symbols["name"] = gamestate().board_.get_team(current_side()).side_name();
545  message = utils::interpolate_variables_into_string(message, &symbols);
546  gui2::show_transient_message("", message);
547  }
548 }
549 
551 {
553  return;
554  }
555 
556  try {
558  } catch(const return_to_play_side_exception&) {
559  }
560 }
561 
563 {
565 
567  execute_gotos();
568  }
569 
570  end_turn_enable(true);
571 
575  }
576 }
577 
579 {
580  if(is_linger_mode()) {
581  // If we need to set the status depending on the completion state
582  // the key to it is here.
583  gui_->set_game_mode(game_display::LINGER);
584  // change the end-turn button text from "End Turn" to "End Scenario"
585  gui_->get_theme().refresh_title2("button-endturn", "title2");
586  } else {
587  gui_->set_game_mode(game_display::RUNNING);
588  // change the end-turn button text from "End Scenario" to "End Turn"
589  gui_->get_theme().refresh_title2("button-endturn", "title");
590  }
591  // Also checcks whether the button can be pressed.
592  gui_->queue_rerender();
593 }
594 
596 {
597  LOG_NG << "beginning end-of-scenario linger";
598 
599  // Make all of the able-to-move units' orbs consistently red
601 
603 
604  try {
605  if(replay_controller_.get() != nullptr) {
606  replay_controller_->play_side_impl();
608  replay_controller_.reset();
609  }
610  }
611  while(!end_turn_requested_) {
612  play_slice();
613  }
614  } catch(const savegame::load_game_exception&) {
615  // Loading a new game is effectively a quit.
616  saved_game_.clear();
617  throw;
618  }
619 
620  LOG_NG << "ending end-of-scenario linger";
621 }
622 
624 {
625  // TODO: this is really only needed to refresh the visual state of the buttons on each turn.
626  // It looks like we can put it in play_side_impl, but it definitely should be removed from
627  // here since other code already takes care of actually enabling/disabling the end turn button.
629 }
630 
632 {
633  // Clear moves from the GUI.
634  gui_->set_route(nullptr);
635  gui_->unhighlight_reach();
636 }
637 
639 {
640  LOG_NG << "is ai...";
641 
642  end_turn_enable(false);
643  gui_->recalculate_minimap();
644 
645  const cursor::setter cursor_setter(cursor::WAIT);
646 
647  // Correct an oddball case where a human could have left delayed shroud
648  // updates on before giving control to the AI. (The AI does not bother
649  // with the undo stack, so it cannot delay shroud updates.)
650  team& cur_team = current_team();
651  if(!cur_team.auto_shroud_updates()) {
652  // We just took control, so the undo stack is empty. We still need
653  // to record this change for the replay though.
655  }
656 
657  undo_stack().clear();
659 
660  try {
661  try {
664  }
665  } catch(const return_to_play_side_exception&) {
666  } catch(const fallback_ai_to_human_exception&) {
668  player_type_changed_ = true;
669  ai_fallback_ = true;
670  }
671  } catch(...) {
672  DBG_NG << "Caught exception playing ai turn: " << utils::get_unknown_exception_type();
674  throw;
675  }
676 
679  }
680 
682  gui_->recalculate_minimap();
683  gui_->invalidate_unit();
684  gui_->invalidate_game_status();
685  gui_->invalidate_all();
686 }
687 
688 /**
689  * Will handle sending a networked notification in descendent classes.
690  */
692 {
693  gui_->get_chat_manager().add_chat_message(std::time(nullptr), "Wesnoth", 0,
694  "This side is in an idle state. To proceed with the game, the host must assign it to another controller.",
696 }
697 
698 /**
699  * Will handle networked turns in descendent classes.
700  */
702 {
703  // There should be no networked sides in single-player.
704  ERR_NG << "Networked team encountered by playsingle_controller.";
705 }
706 
707 void playsingle_controller::handle_generic_event(const std::string& name)
708 {
709  if(name == "ai_user_interact") {
710  play_slice(false);
711  }
712 }
713 
715 {
716  if(is_linger_mode()) {
717  end_turn_requested_ = true;
718  } else if(!is_browsing() && menu_handler_.end_turn(current_side())) {
720  }
721 }
722 
724 {
726  end_turn_requested_ = true;
727 }
728 
730 {
731  end_turn_requested_ = true;
732 }
733 
735 {
736  if(!get_teams().empty()) {
737  const team& t = get_teams()[gui_->viewing_team()];
738 
739  if(!is_regular_game_end() && !is_browsing() && t.objectives_changed()) {
740  show_objectives();
741  }
742  }
743 }
744 
746 {
747  // mouse_handler expects at least one team for linger mode to work.
748  assert(is_regular_game_end());
749  linger();
750  end_turn_requested_ = true;
751 }
752 
754 {
755  // We cannot add [end_turn] to the recorder while executing another action.
757 
758  if(!gamestate().in_phase(game_data::TURN_ENDED)) {
759  assert(end_turn_requested_);
760  assert(current_team().is_local());
761  assert(gamestate().in_phase(game_data::TURN_PLAYING));
762  // TODO: we should also send this immediately.
763  resources::recorder->end_turn(gamestate_->next_player_number_);
765 
766  }
767 
768 
769  assert(gamestate().in_phase(game_data::TURN_ENDED));
770 
771  if(ai_fallback_) {
772  current_team().make_ai();
773  ai_fallback_ = false;
774  }
775 }
776 
778 {
779  if(replay_controller_ && replay_controller_->is_controlling_view()) {
780  replay_controller_->update_viewing_player();
781  } else if(int side_num = play_controller::find_viewing_side()) {
782  if(side_num != gui_->viewing_side() || gui_->show_everything()) {
783  update_gui_to_player(side_num - 1);
784  }
785  }
786 }
787 
789 {
790  if(replay_controller_ && replay_controller_->allow_reset_replay()) {
791  replay_controller_->stop_replay();
792  throw reset_gamestate_exception(replay_controller_->get_reset_state(), {}, false);
793  } else {
794  ERR_NG << "received invalid reset replay";
795  }
796 }
797 
799 {
800  replay_controller_ = std::make_unique<replay_controller>(
801  *this,
802  true,
803  std::make_shared<config>(saved_game_.get_replay_starting_point()),
804  std::bind(&playsingle_controller::on_replay_end, this, is_unit_test)
805  );
806 
807  if(is_unit_test) {
808  replay_controller_->play_replay();
809  }
810 }
811 
813 {
815  return true;
816  } 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()) {
817  // When we are a locally controlled side and havent done init_side yet also return to play_side
818  return true;
819  } else {
820  return false;
821  }
822 }
823 
825 {
826  if(is_networked_mp()) {
827  // we are using the "Back to turn (replay)" feature
828  // And have reached the current gamestate: end the replay and continue normally.
830  } else if(is_unit_test) {
831  replay_controller_->return_to_play_side();
832  if(!is_regular_game_end()) {
834  e.proceed_to_next_level = false;
835  e.is_victory = false;
837  }
838  }
839 }
840 
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands.
double t
Definition: astarsearch.cpp:65
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:212
static manager & get_singleton()
Definition: manager.hpp:145
void play_turn(side_number side)
Plays a turn for the specified side using its active AI.
Definition: manager.cpp:726
static void log_game_end()
Definition: testing.cpp:103
static void log_game_start()
Definition: testing.cpp:91
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:161
void append_children(const config &cfg)
Adds children from cfg.
Definition: config.cpp:169
bool empty() const
Definition: config.cpp:856
virtual void play_slice(bool is_delay_enabled=true)
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:86
void heal_all_survivors()
Definition: game_board.cpp:94
team & get_team(int i)
Definition: game_board.hpp:98
void set_all_units_user_end_turn()
Definition: game_board.cpp:87
void set_end_turn_forced(bool v)
Definition: game_data.hpp:146
void set_phase(PHASE phase)
Definition: game_data.hpp:107
@ 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:103
@ TURN_PLAYING
The User is controlling the game and invoking actions The game can be saved here.
Definition: game_data.hpp:94
@ TURN_ENDED
The turn_end, side_turn_end etc [events] are fired next phase: TURN_STARTING_WAITING (default),...
Definition: game_data.hpp:97
@ TURN_STARTING_WAITING
we are waiting for the turn to start.
Definition: game_data.hpp:87
@ GAME_ENDING
The victory etc.
Definition: game_data.hpp:100
static PHASE read_phase(const config &cfg)
Definition: game_data.cpp:177
bool end_turn_forced() const
Definition: game_data.hpp:147
@ 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:402
game_board board_
Definition: game_state.hpp:47
game_data gamedata_
Definition: game_state.hpp:46
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.
virtual bool receive_from_wesnothd(config &) const
std::vector< team > & get_teams()
virtual void init_gui()
std::unique_ptr< game_state > gamestate_
void show_objectives() const
events::menu_handler menu_handler_
int find_viewing_side() const
returns 0 if no such team was found.
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_
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_
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)
playsingle_controller(const config &level, saved_game &state_of_game, bool skip_replay)
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.
level_result::type play_scenario(const config &level)
virtual bool should_return_to_play_side() const override
virtual void play_side_impl() override
~playsingle_controller()
Defined here to reduce file includes.
turn_info turn_data_
Helper to read and execute (in particular replay data/ user actions ) messsages from the server.
void update_viewing_player() override
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.
void play_scenario_init(const config &level)
void enable_replay(bool is_unit_test=false)
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:305
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:813
config & replay_start()
Definition: saved_game.hpp:128
void remove_snapshot()
Definition: saved_game.cpp:605
const config & get_replay_starting_point()
Definition: saved_game.cpp:616
statistics_record::campaign_stats_t & statistics()
Definition: saved_game.hpp:143
Class for autosaves.
Definition: savegame.hpp:280
void autosave(const bool disable_autosave, const int autosave_max, const int infinite_autosaves)
Definition: savegame.cpp:605
Class for "normal" midgame saves.
Definition: savegame.hpp:253
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:383
Sound source info class.
static bool run_and_store(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:76
const std::string & side_name() const
Definition: team.hpp:295
int side() const
Definition: team.hpp:176
bool auto_shroud_updates() const
Definition: team.hpp:326
void make_ai()
Definition: team.hpp:262
void make_human()
Definition: team.hpp:261
void send_data()
Definition: playturn.cpp:80
PROCESS_DATA_RESULT sync_network()
Definition: playturn.cpp:61
std::size_t i
Definition: function.cpp:968
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:241
constexpr T modulo(T num, int mod, T min=0)
Definition: math.hpp:62
@ WAIT
Definition: cursor.hpp:29
@ NORMAL
Definition: cursor.hpp:29
std::string turn_bell
bool disable_autosave
Definition: game_config.cpp:88
bool exit_at_end
Definition: game_config.cpp:86
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.
int autosavemax()
Definition: game.cpp:791
compression::format save_compression_format()
Definition: game.cpp:843
bool turn_dialog()
Definition: game.cpp:416
bool disable_auto_moves()
Definition: general.cpp:979
const int INFINITE_AUTO_SAVES
Definition: game.hpp:208
bool turn_bell()
Definition: general.cpp:675
game_board * gameboard
Definition: resources.cpp:21
replay * recorder
Definition: resources.cpp:29
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:713
void play_music_once(const std::string &file)
Definition: sound.cpp:603
void commit_music_changes()
Definition: sound.cpp:837
void play_bell(const std::string &files)
Definition: sound.cpp:1050
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
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:145
bool testing()
The game is running unit tests.
Definition: video.cpp:150
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:38
bool valid() const
Definition: location.hpp:89
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 std::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?
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