The Battle for Wesnoth  1.17.10+dev
playsingle_controller.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2006 - 2022
3  by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
4  Copyright (C) 2003 by David White <dave@whitevine.net>
5  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
6 
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 2 of the License, or
10  (at your option) any later version.
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY.
13 
14  See the COPYING file for more details.
15 */
16 
17 /**
18  * @file
19  * 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 "events.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 "hotkey/hotkey_item.hpp"
37 #include "log.hpp"
38 #include "map/label.hpp"
39 #include "map/map.hpp"
40 #include "playturn.hpp"
41 #include "preferences/game.hpp"
42 #include "random_deterministic.hpp"
43 #include "replay_helper.hpp"
44 #include "resources.hpp"
45 #include "savegame.hpp"
47 #include "sound.hpp"
48 #include "soundsource.hpp"
49 #include "statistics.hpp"
50 #include "synced_context.hpp"
51 #include "units/unit.hpp"
52 #include "video.hpp"
54 #include "whiteboard/manager.hpp"
55 
56 #include <boost/dynamic_bitset.hpp>
57 
58 static lg::log_domain log_aitesting("ai/testing");
59 #define LOG_AIT LOG_STREAM(info, log_aitesting)
60 // If necessary, this define can be replaced with `#define LOG_AIT std::cout` to restore previous behavior
61 
62 static lg::log_domain log_engine("engine");
63 #define ERR_NG LOG_STREAM(err, log_engine)
64 #define LOG_NG LOG_STREAM(info, log_engine)
65 #define DBG_NG LOG_STREAM(debug, log_engine)
66 
67 static lg::log_domain log_enginerefac("enginerefac");
68 #define LOG_RG LOG_STREAM(info, log_enginerefac)
69 
70 playsingle_controller::playsingle_controller(const config& level, saved_game& state_of_game, bool skip_replay)
71  : play_controller(level, state_of_game, skip_replay, true) // start faded
72  , cursor_setter_(cursor::NORMAL)
73  , textbox_info_()
74  , replay_sender_(*resources::recorder)
75  , network_reader_([this](config& cfg) { return receive_from_wesnothd(cfg); })
78  , skip_next_turn_(false)
79  , ai_fallback_(false)
81 {
82  // upgrade hotkey handler to the sp (whiteboard enabled) version
83  hotkey_handler_ = std::make_unique<hotkey_handler>(*this, saved_game_);
84 
85  // game may need to start in linger mode
87 
88  plugins_context_->set_accessor_string("level_result", std::bind(&playsingle_controller::describe_result, this));
89  plugins_context_->set_accessor_int("turn", std::bind(&play_controller::turn, this));
90 }
91 
93 {
94  if(!is_regular_game_end()) {
95  return "NONE";
96  } else if(get_end_level_data().is_victory) {
97  return "VICTORY";
98  } else {
99  return "DEFEAT";
100  }
101 }
102 
104 {
105  LOG_NG << "Initializing GUI... " << (SDL_GetTicks() - ticks());
107 
108  // Scroll to the starting position of the first team. If there is a
109  // human team, use that team; otherwise use team 1. If the map defines
110  // a starting position for the selected team, scroll to that tile. Note
111  // this often does not matter since many scenario start with messages,
112  // which will usually scroll to the speaker. Also note that the map
113  // does not necessarily define the starting positions. While usually
114  // best to use the map, the scenarion may explicitly set the positions,
115  // overriding those found in the map (if any).
116  if(map_start_.valid()) {
117  gui_->scroll_to_tile(map_start_, game_display::WARP, false);
118  LOG_NG << "Found good stored ui location " << map_start_;
119  } else {
120  int scroll_team = gamestate().first_human_team_ + 1;
121  if(scroll_team == 0) {
122  scroll_team = 1;
123  }
124 
125  map_location loc(get_map().starting_position(scroll_team));
126  if((loc.x >= 0) && (loc.y >= 0)) {
127  gui_->scroll_to_tile(loc, game_display::WARP);
128  LOG_NG << "Found bad stored ui location " << map_start_ << " using side starting location " << loc;
129  } else {
130  LOG_NG << "Found bad stored ui location";
131  }
132  }
133 
134  // Fade in
135  gui_->set_prevent_draw(false);
136  gui_->queue_repaint();
137  if(!video::headless() && !video::testing()) {
138  gui_->fade_to({0,0,0,0}, 500);
139  } else {
140  gui_->set_fade({0,0,0,0});
141  }
142 
144 }
145 
147 {
148  // At the beginning of the scenario, save a snapshot as replay_start
149  if(saved_game_.replay_start().empty()) {
151  }
152 
153  start_game();
154 
156  // This won't cause errors later but we should notify the user about it in case he didn't knew it.
158  // TODO: find a better title
159  _("Game Error"),
160  _("This multiplayer game uses an alternative random mode, if you don't know what this message means, then "
161  "most likely someone is cheating or someone reloaded a corrupt game."));
162  }
163 
164  return;
165 }
166 
168 {
169  LOG_NG << "starting main loop\n" << (SDL_GetTicks() - ticks());
170 
172  if(get_teams().empty()) {
173  ERR_NG << "Playing game with 0 teams.";
174  }
175 
176  while(true) {
177  try {
178  play_turn();
179  if(is_regular_game_end()) {
181  return;
182  }
183  } catch(const reset_gamestate_exception& ex) {
184  //
185  // TODO:
186  //
187  // The MP replay feature still doesn't work properly (causes OOS)
188  // because:
189  //
190  // 1) The undo stack is not reset along with the gamestate (fixed).
191  // 2) The server_request_number_ is not reset along with the
192  // gamestate (fixed).
193  // 3) chat and other unsynced actions are inserted in the middle of
194  // the replay bringing the replay_pos in unorder (fixed).
195  // 4) untracked changes in side controllers are lost when resetting
196  // gamestate (fixed).
197  // 5) The game should have a stricter check for whether the loaded
198  // game is actually a parent of this game.
199  // 6) If an action was undone after a game was saved it can cause
200  // OOS if the undone action is in the snapshot of the saved
201  // game (luckily this is never the case for autosaves).
202  //
203  boost::dynamic_bitset<> local_players;
204  local_players.resize(get_teams().size(), true);
205  // Preserve side controllers, because we won't get the side controoller updates again when replaying.
206  for(std::size_t i = 0; i < local_players.size(); ++i) {
207  local_players[i] = get_teams()[i].is_local();
208  }
209 
210  if(ex.start_replay) {
211  // MP "Back to turn"
213  } else {
214  // SP replay
216  }
217 
218  reset_gamestate(*ex.level, (*ex.level)["replay_pos"]);
219 
220  for(std::size_t i = 0; i < local_players.size(); ++i) {
221  resources::gameboard->teams()[i].set_local(local_players[i]);
222  }
223 
225 
226  if(replay_controller_ == nullptr) {
227  replay_controller_ = std::make_unique<replay_controller>(*this, false, ex.level, [this]() { on_replay_end(false); });
228  }
229 
230  if(ex.start_replay) {
231  replay_controller_->play_replay();
232  }
233  }
234  } // end for loop
235 }
236 
238 {
239  LOG_NG << "in playsingle_controller::play_scenario()...";
240 
241  // Start music.
242  for(const config& m : level.child_range("music")) {
243  sound::play_music_config(m, true);
244  }
245 
247 
249  // Combine all the [story] tags into a single config. Handle this here since
250  // storyscreen::controller doesn't have a default constructor.
251  config cfg;
252  for(const auto& iter : level.child_range("story")) {
253  cfg.append_children(iter);
254  }
255 
256  if(!cfg.empty()) {
258  }
259  }
260 
261  gui_->labels().read(level);
262 
263  // Read sound sources
264  assert(soundsources_manager_ != nullptr);
265  for(const config& s : level.child_range("sound_source")) {
266  try {
268  soundsources_manager_->add(spec);
269  } catch(const bad_lexical_cast&) {
270  ERR_NG << "Error when parsing sound_source config: bad lexical cast.";
271  ERR_NG << "sound_source config was: " << s.debug();
272  ERR_NG << "Skipping this sound source...";
273  }
274  }
275 
276  LOG_NG << "entering try... " << (SDL_GetTicks() - ticks());
277 
278  try {
280  // clears level config;
282 
283  if(!is_regular_game_end() && !linger_) {
285  }
286 
288  exit(0);
289  }
290  const bool is_victory = get_end_level_data().is_victory;
291 
292  //if(gamestate().gamedata_.phase() <= game_data::PRESTART) {
293  // video::clear_screen();
294  //}
295 
297 
298  const end_level_data& end_level = get_end_level_data();
299 
300  if(get_teams().empty()) {
301  // store persistent teams
303 
304  // this is probably only a story scenario, i.e. has its endlevel in the prestart event
305  return level_result::type::victory;
306  }
307 
308  if(linger_) {
309  LOG_NG << "resuming from loaded linger state...";
310  // as carryover information is stored in the snapshot, we have to re-store it after loading a linger state
312  if(!is_observer()) {
314  }
315 
316  return level_result::type::victory;
317  }
318 
319  pump().fire(is_victory ? "local_victory" : "local_defeat");
320 
321  { // Block for set_scontext_synced_base
323  pump().fire(end_level.proceed_to_next_level ? level_result::victory : level_result::defeat);
324  pump().fire("scenario_end");
325  }
326 
327  if(end_level.proceed_to_next_level) {
329  }
330 
331  if(is_observer()) {
332  gui2::show_transient_message(_("Game Over"), _("The game is over."));
333  return level_result::type::observer_end;
334  }
335 
336  // If we're a player, and the result is victory/defeat, then send
337  // a message to notify the server of the reason for the game ending.
339  "info", config {
340  "type", "termination",
341  "condition", "game over",
342  "result", is_victory ? level_result::victory : level_result::defeat,
343  },
344  });
345 
346  // Play victory music once all victory events
347  // are finished, if we aren't observers and the
348  // carryover dialog isn't disabled.
349  //
350  // Some scenario authors may use 'continue'
351  // result for something that is not story-wise
352  // a victory, so let them use [music] tags
353  // instead should they want special music.
354  const std::string& end_music = select_music(is_victory);
355  if((!is_victory || end_level.transient.carryover_report) && !end_music.empty()) {
357  sound::play_music_once(end_music);
358  }
359 
361 
362  level_result::type res = level_result::get_enum(end_level.test_result).value_or(level_result::type::test_invalid);
363  if(res == level_result::type::result_not_set) {
364  return is_victory ? level_result::type::victory : level_result::type::defeat;
365  } else {
366  return res;
367  }
368  } catch(const savegame::load_game_exception&) {
369  // Loading a new game is effectively a quit.
370  saved_game_.clear();
371  throw;
372  } catch(const wesnothd_error& e) {
373  scoped_savegame_snapshot snapshot(*this);
375  if(e.message == "") {
377  _("A network disconnection has occurred, and the game cannot continue. Do you want to save the game?"),
379  } else {
381  _("This game has been ended.\nReason: ") + e.message + _("\nDo you want to save the game?"),
383  }
384 
385  if(dynamic_cast<const ingame_wesnothd_error*>(&e)) {
386  return level_result::type::quit;
387  } else {
388  throw;
389  }
390  }
391 }
392 
394 {
395  while(!should_return_to_play_side()) {
397  SDL_Delay(10);
398  }
399 }
400 
402 {
403  if(!skip_next_turn_) {
405  }
406 
407  if(replay_controller_.get() != nullptr) {
408  init_side_done_now_ = false;
409 
410  REPLAY_RETURN res = replay_controller_->play_side_impl();
411  if(res == REPLAY_FOUND_END_TURN) {
413  }
414 
416  replay_controller_.reset();
417  }
418  } else if((current_team().is_local_human() && current_team().is_proxy_human())) {
419  LOG_NG << "is human...";
420  // If a side is dead end the turn, but play at least side=1's
421  // turn in case all sides are dead
422  if(gamestate().board_.side_units(current_side()) == 0 && !(get_units().empty() && current_side() == 1)) {
424  }
425 
427 
428  if(end_turn_ == END_TURN_NONE) {
429  play_human_turn();
430  }
431 
434  }
435 
436  LOG_NG << "human finished turn...";
437  } else if(current_team().is_local_ai() || (current_team().is_local_human() && current_team().is_droid())) {
438  play_ai_turn();
439  } else if(current_team().is_network()) {
441  } else if(current_team().is_local_human() && current_team().is_idle()) {
442  end_turn_enable(false);
445 
446  if(end_turn_ == END_TURN_NONE) {
447  play_idle_loop();
448  }
449  } else {
450  // we should have skipped over empty controllers before so this shouldn't be possible
451  ERR_NG << "Found invalid side controller " << side_controller::get_string(current_team().controller()) << " ("
452  << side_proxy_controller::get_string(current_team().proxy_controller()) << ") for side " << current_team().side();
453  }
454 }
455 
457 {
458  log_scope("player turn");
459  assert(!linger_);
461  return;
462  }
463 
465  scoped_savegame_snapshot snapshot(*this);
468  }
469 
470  if(preferences::turn_bell()) {
472  }
473 }
474 
476 {
478  blindfold b(*gui_, true); // apply a blindfold for the duration of this dialog
479  gui_->queue_rerender();
480  std::string message = _("It is now $name|’s turn");
481  utils::string_map symbols;
482  symbols["name"] = gamestate().board_.get_team(current_side()).side_name();
483  message = utils::interpolate_variables_into_string(message, &symbols);
484  gui2::show_transient_message("", message);
485  }
486 }
487 
489 {
491  return;
492  }
493 
494  try {
496  } catch(const return_to_play_side_exception&) {
497  }
498 }
499 
501 {
503 
505  execute_gotos();
506  }
507 
508  end_turn_enable(true);
509 
510  while(!should_return_to_play_side()) {
513  }
514 }
515 
517 {
518  LOG_NG << "beginning end-of-scenario linger";
519  linger_ = true;
520 
521  // If we need to set the status depending on the completion state
522  // the key to it is here.
523  gui_->set_game_mode(game_display::LINGER);
524 
525  // Make all of the able-to-move units' orbs consistently red
527 
528  // change the end-turn button text to its alternate label
529  gui_->get_theme().refresh_title2("button-endturn", "title2");
530  gui_->queue_rerender();
531 
532  try {
533  // Same logic as single-player human turn, but
534  // *not* the same as multiplayer human turn.
535  end_turn_enable(true);
537  while(end_turn_ == END_TURN_NONE) {
538  play_slice();
539  }
540  } catch(const savegame::load_game_exception&) {
541  // Loading a new game is effectively a quit.
542  saved_game_.clear();
543  throw;
544  }
545 
546  // revert the end-turn button text to its normal label
547  gui_->get_theme().refresh_title2("button-endturn", "title");
548  gui_->queue_rerender();
549  gui_->set_game_mode(game_display::RUNNING);
550 
551  LOG_NG << "ending end-of-scenario linger";
552 }
553 
555 {
556  // TODO: this is really only needed to refresh the visual state of the buttons on each turn.
557  // It looks like we can put it in play_side_impl, but it definitely should be removed from
558  // here since other code already takes care of actually enabling/disabling the end turn button.
560 }
561 
563 {
564  // Clear moves from the GUI.
565  gui_->set_route(nullptr);
566  gui_->unhighlight_reach();
567 }
568 
570 {
571  LOG_NG << "is ai...";
572 
573  end_turn_enable(false);
574  gui_->recalculate_minimap();
575 
576  const cursor::setter cursor_setter(cursor::WAIT);
577 
578  // Correct an oddball case where a human could have left delayed shroud
579  // updates on before giving control to the AI. (The AI does not bother
580  // with the undo stack, so it cannot delay shroud updates.)
581  team& cur_team = current_team();
582  if(!cur_team.auto_shroud_updates()) {
583  // We just took control, so the undo stack is empty. We still need
584  // to record this change for the replay though.
586  }
587 
588  undo_stack().clear();
590 
591  try {
592  try {
595  }
596  } catch(const return_to_play_side_exception&) {
597  } catch(const fallback_ai_to_human_exception&) {
599  player_type_changed_ = true;
600  ai_fallback_ = true;
601  }
602  } catch(...) {
603  DBG_NG << "Caught exception playing ai turn: " << utils::get_unknown_exception_type();
605  throw;
606  }
607 
610  }
611 
613  gui_->recalculate_minimap();
614  gui_->invalidate_unit();
615  gui_->invalidate_game_status();
616  gui_->invalidate_all();
617 }
618 
619 /**
620  * Will handle sending a networked notification in descendent classes.
621  */
623 {
624  gui_->get_chat_manager().add_chat_message(std::time(nullptr), "Wesnoth", 0,
625  "This side is in an idle state. To proceed with the game, the host must assign it to another controller.",
627 }
628 
629 /**
630  * Will handle networked turns in descendent classes.
631  */
633 {
634  // There should be no networked sides in single-player.
635  ERR_NG << "Networked team encountered by playsingle_controller.";
636 }
637 
638 void playsingle_controller::handle_generic_event(const std::string& name)
639 {
640  if(name == "ai_user_interact") {
641  play_slice(false);
642  }
643 }
644 
646 {
647  if(linger_) {
649  } else if(!is_browsing() && menu_handler_.end_turn(current_side())) {
651  }
652 }
653 
655 {
656  skip_next_turn_ = true;
658 }
659 
661 {
662  if(!get_teams().empty()) {
663  const team& t = get_teams()[gui_->viewing_team()];
664 
666  show_objectives();
667  }
668  }
669 }
670 
672 {
673  // mouse_handler expects at least one team for linger mode to work.
674  assert(is_regular_game_end());
675  if(get_end_level_data().transient.linger_mode && !get_teams().empty()) {
676  linger();
677  }
678 }
679 
681 {
682  // We cannot add [end_turn] to the recorder while executing another action.
684 
685  if(end_turn_ == END_TURN_REQUIRED && current_team().is_local()) {
686  // TODO: we should also send this immediately.
687  resources::recorder->end_turn(gamestate_->next_player_number_);
689  }
690 
691  assert(end_turn_ == END_TURN_SYNCED);
692  skip_next_turn_ = false;
693 
694  if(ai_fallback_) {
695  current_team().make_ai();
696  ai_fallback_ = false;
697  }
698 }
699 
701 {
702  if(replay_controller_ && replay_controller_->is_controlling_view()) {
703  replay_controller_->update_viewing_player();
704  } else if(int side_num = play_controller::find_last_visible_team()) {
705  // Update viewing team in case it has changed during the loop.
706  if(side_num != gui_->viewing_side()) {
707  update_gui_to_player(side_num - 1);
708  }
709  }
710 }
711 
713 {
714  if(replay_controller_ && replay_controller_->allow_reset_replay()) {
715  replay_controller_->stop_replay();
716  throw reset_gamestate_exception(replay_controller_->get_reset_state(), {}, false);
717  } else {
718  ERR_NG << "received invalid reset replay";
719  }
720 }
721 
723 {
724  replay_controller_ = std::make_unique<replay_controller>(
725  *this,
727  std::make_shared<config>(saved_game_.get_replay_starting_point()),
728  std::bind(&playsingle_controller::on_replay_end, this, is_unit_test)
729  );
730 
731  if(is_unit_test) {
732  replay_controller_->play_replay();
733  }
734 }
735 
737 {
739  return true;
740  } else if(end_turn_ == END_TURN_NONE || replay_controller_.get() != 0 || current_team().is_network()) {
741  return false;
742  } else {
743  return true;
744  }
745 }
746 
748 {
749  if(is_networked_mp()) {
751  } else if(is_unit_test) {
752  replay_controller_->return_to_play_side();
753  if(!is_regular_game_end()) {
755  e.proceed_to_next_level = false;
756  e.is_victory = false;
758  }
759  }
760 }
bool disable_auto_moves()
Definition: general.cpp:982
void empty_playlist()
Definition: sound.cpp:612
void end_turn(int next_player_number)
Definition: replay.cpp:305
void clear()
Clears the stack of undoable (and redoable) actions.
Definition: undo.cpp:221
void set_all_units_user_end_turn()
Definition: game_board.cpp:87
An error occurred during when trying to communicate with the wesnothd server.
static lg::log_domain log_aitesting("ai/testing")
int autosavemax()
Definition: game.cpp:792
std::unique_ptr< soundsource::manager > soundsources_manager_
bool turn_bell()
Definition: general.cpp:678
static void display(const std::string &scenario_name, const config &story)
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)
hotkey::command_executor * get_hotkey_command_executor() override
Optionally get a command executor to handle context menu events.
std::string interpolate_variables_into_string(const std::string &str, const string_map *const symbols)
virtual const std::vector< team > & teams() const override
Definition: game_board.hpp:86
void heal_all_survivors()
Definition: game_board.cpp:94
std::map< std::string, t_string > string_map
int find_last_visible_team() const
returns 0 if no such team was found.
std::unique_ptr< game_display > gui_
const unit_map & get_units() const
events::mouse_handler mouse_handler_
static manager & get_singleton()
Definition: manager.hpp:145
std::unique_ptr< plugins_context > plugins_context_
bool is_network() const
Definition: team.hpp:250
config & replay_start()
Definition: saved_game.hpp:123
bool is_skipping_replay() const
const std::string & side_name() const
Definition: team.hpp:295
level_result::type play_scenario(const config &level)
Exception used to escape form the ai or ui code to playsingle_controller::play_side.
virtual void play_side_impl() override
Class for autosaves.
Definition: savegame.hpp:279
game_events::wml_event_pump & pump()
const int INFINITE_AUTO_SAVES
Definition: game.hpp:208
std::unique_ptr< hotkey_handler > hotkey_handler_
int first_human_team_
Definition: game_state.hpp:75
child_itors child_range(config_key_type key)
Definition: config.cpp:344
virtual void init_gui() override
bool objectives_changed() const
Definition: team.hpp:229
PROCESS_DATA_RESULT sync_network()
Definition: playturn.cpp:61
static config get_auto_shroud(bool turned_on)
Records that the player has toggled automatic shroud updates.
Gather statistics important for AI testing and output them.
And endturn was required eigher by the player, by the ai or by [end_turn].
void clear()
Definition: saved_game.cpp:785
void enable_replay(bool is_unit_test=false)
virtual void play_slice(bool is_delay_enabled=true)
const gamemap & get_map() const
Class for "normal" midgame saves.
Definition: savegame.hpp:252
std::string get_unknown_exception_type()
Utility function for finding the type of thing caught with catch(...).
Definition: general.cpp:23
bool testing()
The game is running unit tests.
Definition: video.cpp:148
config & set_snapshot(config snapshot)
Definition: saved_game.cpp:559
void play_music_once(const std::string &file)
Definition: sound.cpp:603
void append_children(const config &cfg)
Adds children from cfg.
Definition: config.cpp:223
static void log_game_end()
Definition: testing.cpp:103
Contains the exception interfaces used to signal completion of a scenario, campaign or turn...
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 std::string _(const char *str)
Definition: gettext.hpp:93
persist_manager persist_
void reset_current_scenario()
Reset the stats of the current scenario to the beginning.
Definition: statistics.cpp:797
void send_data()
Definition: playturn.cpp:80
An extension of play_controller::hotkey_handler, which has support for SP wesnoth features like white...
events::menu_handler menu_handler_
virtual void do_idle_notification()
Will handle sending a networked notification in descendent classes.
playsingle_controller(const config &level, saved_game &state_of_game, bool skip_replay)
#define b
void make_human()
Definition: team.hpp:261
std::unique_ptr< game_state > gamestate_
REPLAY_RETURN
Definition: replay.hpp:156
no linger overlay, show fog and shroud.
std::string turn_bell
saved_game & saved_game_
void end_turn_enable(bool enable)
This class stores all the data for a single &#39;side&#39; (in game nomenclature).
Definition: team.hpp:75
bool end_turn(int side_num)
virtual void check_objectives() override
virtual bool should_return_to_play_side() const override
team & get_team(int i)
Definition: game_board.hpp:98
std::size_t size(const std::string &str)
Length in characters of a UTF-8 string.
Definition: unicode.cpp:87
std::string test_result
result to use if this is a unit test
bool disable_autosave
bool has_human_sides() const
Definition: game_state.hpp:76
config to_config() const
Builds the snapshot config from members and their respective configs.
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.
bool valid() const
Definition: location.hpp:89
game_board * gameboard
Definition: resources.cpp:21
bool is_regular_game_end() const
std::vector< team > & get_teams()
bool headless()
The game is running headless.
Definition: video.cpp:143
std::shared_ptr< config > stats_
virtual void init_gui()
void read_stats(const config &cfg)
Definition: statistics.cpp:773
std::string describe_result() const
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands...
#define LOG_NG
replay * recorder
Definition: resources.cpp:29
void play_turn(side_number side)
Plays a turn for the specified side using its active AI.
Definition: manager.cpp:726
static lg::log_domain log_enginerefac("enginerefac")
bool carryover_report
Should a summary of the scenario outcome be displayed?
virtual void play_network_turn()
Will handle networked turns in descendent classes.
static void log_game_start()
Definition: testing.cpp:91
bool is_browsing() const override
bool is_observer() const
void update_gui_to_player(const int team_index, const bool observe=false)
Changes the UI for this client to the passed side index.
Encapsulates the map of the game.
Definition: location.hpp:38
void on_replay_end(bool is_unit_test)
bool auto_shroud_updates() const
Definition: team.hpp:326
void make_ai()
Definition: team.hpp:262
Sound source info class.
void set_end_level_data(const end_level_data &data)
Exception used to signal that the user has decided to abortt a game, and to load another game instead...
Definition: savegame.hpp:83
transient_end_level transient
virtual bool is_networked_mp() const
const config & get_replay_starting_point()
Definition: saved_game.cpp:588
std::size_t i
Definition: function.cpp:967
const std::string & select_music(bool victory) const
map_location map_start_
void execute_gotos(mouse_handler &mousehandler, int side_num)
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
#define ERR_NG
static map_location::DIRECTION s
std::unique_ptr< replay_controller > replay_controller_
void reset_gamestate(const config &level, int replay_pos)
Define the game&#39;s event mechanism.
#define log_scope(description)
Definition: log.hpp:237
bool empty() const
Definition: map.hpp:446
bool proceed_to_next_level
whether to proceed to the next scenario, equals is_victory in sp.
t_string get_scenario_name() const
Additional information on the game outcome which can be provided by WML.
actions::undo_list & undo_stack()
const end_level_data & get_end_level_data() const
pump_result_t fire(const std::string &event, const entity_location &loc1=entity_location::null_entity, const entity_location &loc2=entity_location::null_entity, const config &data=config())
Function to fire an event.
Definition: pump.cpp:402
virtual bool receive_from_wesnothd(config &) const
void update_viewing_player() override
compression::format save_compression_format()
Definition: game.cpp:844
void play_bell(const std::string &files)
Definition: sound.cpp:1050
game_state & gamestate()
double t
Definition: astarsearch.cpp:65
playturn_network_adapter network_reader_
void play_music_config(const config &music_node, bool allow_interrupt_current_track, int i)
Definition: sound.cpp:713
game_classification & classification()
Definition: saved_game.hpp:55
game_board board_
Definition: game_state.hpp:47
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.
bool is_skipping_story() const
replay_network_sender replay_sender_
std::string message
Definition: exceptions.hpp:30
#define DBG_NG
std::shared_ptr< config > level
#define e
std::size_t turn() const
void commit_music_changes()
Definition: sound.cpp:837
void remove_snapshot()
Definition: saved_game.cpp:577
int side() const
Definition: team.hpp:176
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:60
static lg::log_domain log_engine("engine")
bool turn_dialog()
Definition: game.cpp:417
Thrown when a lexical_cast fails.
bool empty() const
Definition: config.cpp:941
virtual void send_to_wesnothd(const config &, const std::string &="unknown") const
virtual void handle_generic_event(const std::string &name) override
An [end_turn] was added to the replay.
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)...
static std::string get_string(enum_type key)
Converts a enum to its string equivalent.
Definition: enum_base.hpp:46