The Battle for Wesnoth 1.21.0-dev
 
Loading...
Searching...
No Matches
play_controller.cpp
Go to the documentation of this file.
1/*
2 Copyright (C) 2006 - 2025
3 by Joerg Hinrichs <joerg.hinrichs@alice-dsl.de>
4 Copyright (C) 2003 by David White <dave@whitevine.net>
5 Part of the Battle for Wesnoth Project https://www.wesnoth.org/
6
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY.
13
14 See the COPYING file for more details.
15*/
16
17/**
18 * @file
19 * Handle input via mouse & keyboard, events, schedule commands.
20 */
21
22#include "play_controller.hpp"
23
24#include "actions/heal.hpp"
25#include "actions/undo.hpp"
26#include "actions/vision.hpp"
27#include "ai/manager.hpp"
28#include "ai/testing.hpp"
30#include "floating_label.hpp"
32#include "game_errors.hpp"
34#include "game_events/pump.hpp"
35#include "game_state.hpp"
36#include "gettext.hpp"
38#include "gui/dialogs/message.hpp" // for show_error_message
42#include "log.hpp"
43#include "map/label.hpp"
44#include "pathfind/teleport.hpp"
46#include "random.hpp"
47#include "replay.hpp"
48#include "resources.hpp"
49#include "save_index.hpp"
50#include "saved_game.hpp"
51#include "savegame.hpp"
55#include "sound.hpp"
56#include "soundsource.hpp"
57#include "statistics.hpp"
58#include "synced_context.hpp"
59#include "units/types.hpp"
60#include "units/unit.hpp"
61#include "utils/general.hpp"
62#include "video.hpp"
64
65#include <functional>
66
67static lg::log_domain log_aitesting("ai/testing");
68#define LOG_AIT LOG_STREAM(info, log_aitesting)
69// If necessary, this define can be replaced with `#define LOG_AIT std::cout` to restore previous behavior
70
71static lg::log_domain log_engine("engine");
72#define LOG_NG LOG_STREAM(info, log_engine)
73#define DBG_NG LOG_STREAM(debug, log_engine)
74#define ERR_NG LOG_STREAM(err, log_engine)
75
76static lg::log_domain log_display("display");
77#define ERR_DP LOG_STREAM(err, log_display)
78
79static lg::log_domain log_enginerefac("enginerefac");
80#define LOG_RG LOG_STREAM(info, log_enginerefac)
81
82static lg::log_domain log_engine_enemies("engine/enemies");
83#define DBG_EE LOG_STREAM(debug, log_engine_enemies)
84
85/**
86 * Copies [scenario] attributes/tags that are not otherwise stored in C++ structs/clases.
87 */
88static void copy_persistent(const config& src, config& dst)
89{
90 static const std::set<std::string> attrs {
91 "description",
92 "name",
93 "disallow_recall",
94 "experience_modifier",
95 "require_scenario",
96 "loaded_resources"
97 };
98
99 static const std::set<std::string> tags {
100 "terrain_graphics",
101 "modify_unit_type",
102 "lua"
103 };
104
105 for(const std::string& attr : attrs) {
106 dst[attr] = src[attr];
107 }
108
109 for(const std::string& tag : tags) {
110 dst.append_children(src, tag);
111 }
112}
113
114static void clear_resources()
115{
116 resources::controller = nullptr;
117 resources::filter_con = nullptr;
118 resources::gameboard = nullptr;
119 resources::gamedata = nullptr;
120 resources::lua_kernel = nullptr;
121 resources::game_events = nullptr;
122 resources::persist = nullptr;
123 resources::soundsources = nullptr;
124 resources::tod_manager = nullptr;
125 resources::tunnels = nullptr;
126 resources::undo_stack = nullptr;
127 resources::recorder = nullptr;
128 resources::whiteboard.reset();
130}
131
134 , observer()
136 , timer_()
137 , gamestate_()
138 , level_()
139 , saved_game_(state_of_game)
140 , tooltips_manager_()
141 , whiteboard_manager_()
142 , plugins_context_()
143 , labels_manager_(new font::floating_label_context())
144 , help_manager_(help::help_manager::get_instance())
145 , mouse_handler_(*this)
146 , menu_handler_(*this)
147 , hotkey_handler_(new hotkey_handler(*this, saved_game_))
148 , soundsources_manager_()
149 , persist_()
150 , gui_()
151 , xp_mod_(new unit_experience_accelerator(level["experience_modifier"].to_int(100)))
152 , statistics_context_(new statistics_t(state_of_game.statistics()))
153 , replay_(new replay(state_of_game.get_replay()))
154 , skip_replay_(false)
155 , skip_story_(state_of_game.skip_story())
156 , did_autosave_this_turn_(true)
157 , did_tod_sound_this_turn_(false)
158 , map_start_()
159 , start_faded_(true)
160 , victory_music_()
161 , defeat_music_()
162 , scope_(hotkey::scope_game)
163 , ignore_replay_errors_(false)
164 , player_type_changed_(false)
165{
166 copy_persistent(level, level_);
167
168 for(const config& modify_unit_type : level_.child_range("modify_unit_type")) {
169 unit_types.apply_scenario_fix(modify_unit_type);
170 }
174
176
178
180
181 try {
182 init(level);
183 } catch(...) {
184 DBG_NG << "Caught exception initializing level: " << utils::get_unknown_exception_type();
186 throw;
187 }
188}
189
195
197{
198 /*
199 * Initilisation currently happens on the following order:
200 * 1) This code, which is executed with the loadingscreen
201 * From inside the constructor.
202 * 2) The Music is changed.
203 * 3) The Storyscreen is shown.
204 * 4) The Labels are added
205 * 5) The preload event is fired
206 * 6) The prestart event is fired
207 * 7) The gui is activated
208 * 8) The start event is fired
209 */
212
213 LOG_NG << "initializing game_state..." << timer();
214 gamestate_.reset(new game_state(level, *this));
215
223
224 gamestate_->ai_manager_.add_observer(this);
225 gamestate_->init(level, *this);
227
228 LOG_NG << "initializing whiteboard..." << timer();
230 whiteboard_manager_.reset(new wb::manager());
232
233 LOG_NG << "loading units..." << timer();
236
237 LOG_NG << "initializing display (includes theme init and building terrain rules)... " << timer();
239 gui_.reset(new game_display(gamestate().board_, whiteboard_manager_, *gamestate().reports_, theme(), level));
240 map_start_ = map_location(level.child_or_empty("display").child_or_empty("location"));
241 if(start_faded_) {
242 gui_->set_fade({0,0,0,255});
243 gui_->set_prevent_draw(true);
244 }
245
246 // Ensure the loading screen doesn't end up underneath the game display
248
249 if(!video::headless()) {
251 gui_->get_theme().modify_label("time-icon", _("time left for current turn"));
252 } else {
253 gui_->get_theme().modify_label("time-icon", _("current local time"));
254 }
255 }
256
259
260 LOG_NG << "done initializing display... " << timer();
261
262 LOG_NG << "building gamestate to gui and whiteboard... " << timer();
263 // This *needs* to be created before the show_intro and show_map_scene
264 // as that functions use the manager state_of_game
265 // Has to be done before registering any events!
268
271 // loadscreen_manager->reset();
273 gamestate().lua_kernel_->load_game(level);
274
275 plugins_context_.reset(new plugins_context("Game"));
276 plugins_context_->set_callback("save_game", [this](const config& cfg) { save_game_auto(cfg["filename"]); }, true);
277 plugins_context_->set_callback("save_replay", [this](const config& cfg) { save_replay_auto(cfg["filename"]); }, true);
278 plugins_context_->set_callback("quit", [](const config&) { throw_quit_game_exception(); }, false);
279 plugins_context_->set_callback_execute(*resources::lua_kernel);
280 plugins_context_->set_accessor_string("scenario_name", [this](const config&) { return get_scenario_name(); });
281 plugins_context_->set_accessor_int("current_side", [this](const config&) { return current_side(); });
282 plugins_context_->set_accessor_int("current_turn", [this](const config&) { return turn(); });
283 plugins_context_->set_accessor_bool("can_move", [this](const config&) { return !events::commands_disabled && gamestate().gamedata_.phase() == game_data::TURN_PLAYING; });
284 plugins_context_->set_callback("end_turn", [this](const config&) { require_end_turn(); }, false);
285 plugins_context_->set_callback("synced_command", [this](const config& cmd) {
286 auto& pm = *plugins_manager::get();
287 if(resources::whiteboard->has_planned_unit_map())
288 {
289 ERR_NG << "plugin called synced command while whiteboard is applied, ignoring";
290 pm.notify_event("synced_command_error", config{"error", "whiteboard"});
291 return;
292 }
293
294 auto& gamedata = gamestate().gamedata_;
295 const bool is_too_early = gamedata.phase() == game_data::INITIAL || resources::gamedata->phase() == game_data::PRELOAD;
296 const bool is_during_turn = gamedata.phase() == game_data::TURN_PLAYING;
298 if(is_too_early) {
299 ERR_NG << "synced command called too early, only allowed at START or later";
300 pm.notify_event("synced_command_error", config{"error", "too-early"});
301 return;
302 }
303 if(is_unsynced && !is_during_turn) {
304 ERR_NG << "synced command can only be used during a turn when a user would also be able to invoke commands";
305 pm.notify_event("synced_command_error", config{"error", "not-your-turn"});
306 return;
307 }
308 if(is_unsynced && events::commands_disabled) {
309 ERR_NG << "synced command cannot be invoked while commands are blocked";
310 pm.notify_event("synced_command_error", config{"error", "disabled"});
311 return;
312 }
313 if(is_unsynced && !resources::controller->current_team().is_local()) {
314 ERR_NG << "synced command can only be used from clients that control the currently playing side";
315 pm.notify_event("synced_command_error", config{"error", "not-your-turn"});
316 return;
317 }
318 action_spectator spectator([&pm](const std::string& message) {
319 ERR_NG << "synced command from plugin raised an error: " << message;
320 pm.notify_event("synced_command_error", config{"error", "error", "message", message});
321 });
322 for(const auto [key, child] : cmd.all_children_range()) {
325 }
326 }, false);
327 });
328}
329
330void play_controller::reset_gamestate(const config& level, int replay_pos)
331{
332 // TODO: should we update we update this->level_ with level ?
333
334 resources::gameboard = nullptr;
335 resources::gamedata = nullptr;
336 resources::tod_manager = nullptr;
337 resources::filter_con = nullptr;
338 resources::lua_kernel = nullptr;
339 resources::game_events = nullptr;
340 resources::tunnels = nullptr;
341 resources::undo_stack = nullptr;
342
343 gui_->labels().set_team(nullptr);
344
345 // Item overlays (including their halos) are stored on the display, not the
346 // game state, so they need to be cleared here on reset. Refer GitHub #10866.
347 gui_->remove_overlays();
348
349 /* First destroy the old game state, then create the new one.
350 This is necessary to ensure that while the old AI manager is being destroyed,
351 all its member objects access the old manager instead of the new. */
352 gamestate_.reset();
353 gamestate_.reset(new game_state(level, *this));
354
362
363 gamestate_->ai_manager_.add_observer(this);
364 gamestate_->init(level, *this);
367
368 gui_->reset_reports(*gamestate().reports_);
369 gui_->change_display_context(&gamestate().board_);
370 saved_game_.get_replay().set_pos(replay_pos);
371
373 gamestate().lua_kernel_->load_game(level);
374}
375
377{
378 LOG_NG << "initializing managers... " << timer();
380
382 LOG_NG << "done initializing managers... " << timer();
383}
384
386{
387 // Run initialization scripts, even if loading from a snapshot.
388 gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
389 pump().fire("preload");
390 gamestate().lua_kernel_->preload_finished();
391}
392
394{
395 // pre-start events must be executed before any GUI operation,
396 // as those may cause the display to be refreshed.
398
399 // Fire these right before prestart events, to catch only the units sides
400 // have started with.
401 for(const unit& u : get_units()) {
402 pump().fire("unit_placed", map_location(u.get_location()));
403 }
404
405 pump().fire("prestart");
406
407 // prestart event may modify start turn with WML, reflect any changes.
408 gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
409}
410
412{
413 if(!get_teams().empty()) {
414 const config cfg("side", gui_->viewing_team().side());
415 gamestate().lua_kernel_->run_wml_action("show_objectives", vconfig(cfg),
416 game_events::queued_event("_from_interface", "", map_location(), map_location(), config()));
417 }
418}
419
421{
423 pump().fire("start");
424
425 skip_story_ = false; // Show [message]s from now on even with --campaign-skip-story
426
427 // start event may modify start turn with WML, reflect any changes.
428 gamestate().gamedata_.get_variable("turn_number") = static_cast<int>(turn());
429
432
433 // prestart and start events may modify the initial gold amount,
434 // reflect any changes.
435 for(team& tm : get_teams()) {
436 tm.set_start_gold(tm.gold());
437 }
438
440}
441
443{
444 gui_->begin_game();
445 gui_->update_tod();
446}
447
449{
451 gui_->set_playing_team_index(std::size_t(current_side() - 1));
452
454
456}
457
459{
460 //
461 // We do side init only if not done yet for a local side when we are not replaying.
462 // For all other sides it is recorded in replay and replay handler has to handle
463 // calling do_init_side() functions.
464 //
465 if(is_during_turn()) {
466 // We already executed do_init_side this can for example happe if we reload a game,
467 // but also if we changed control of a side during it's turn
468 return;
469 }
470
471 if(!current_team().is_local()) {
472 // We are in a mp game and execute do_init_side as soon as we receive [init_side] from the current player
473 // (see replay.cpp)
474 return;
475 }
476
477 if(is_replay()) {
478 // We are in a replay and execute do_init_side as soon as we reach the next [init_side] in the replay data
479 // (see replay.cpp)
480 return;
481 }
482
483 if(current_team().is_idle()) {
484 // In this case it can happen that we just gave control of this side to another player so doing init_side
485 // could lead to errors since we no longer own this side from the servers perspective.
486 // (see playturn.cpp)
487 return;
488 }
489
491 do_init_side();
492}
493
495{
496 { // Block for set_scontext_synced
498
500
501 log_scope("player turn");
502 // In case we might end up calling sync:network during the side turn events,
503 // and we don't want do_init_side to be called when a player drops.
505 gamestate_->next_player_number_ = gamestate_->player_number_ + 1;
506
507 const std::string turn_num = std::to_string(turn());
508 const std::string side_num = std::to_string(current_side());
509
510 gamestate().gamedata_.get_variable("side_number") = current_side();
511
512 // We might have skipped some sides because they were empty so it is not enough to check for side_num==1
513 if(!gamestate().tod_manager_.has_turn_event_fired()) {
514 pump().fire("turn_" + turn_num);
515 pump().fire("new_turn");
517 }
518
519 pump().fire("side_turn");
520 pump().fire("side_" + side_num + "_turn");
521 pump().fire("side_turn_" + turn_num);
522 pump().fire("side_" + side_num + "_turn_" + turn_num);
523
524 // We want to work out if units for this player should get healed,
525 // and the player should get income now.
526 // Healing/income happen if it's not the first turn of processing,
527 // or if we are loading a game.
528 if(turn() > 1) {
531
532 // If the expense is less than the number of villages owned
533 // times the village support capacity,
534 // then we don't have to pay anything at all
536 if(expense > 0) {
537 current_team().spend_gold(expense);
538 }
539 }
540
541 if(do_healing()) {
543 }
544
545 // Do healing on every side turn except the very first side turn.
546 // (1.14 and earlier did healing whenever turn >= 2.)
547 set_do_healing(true);
548
549 // Set resting now after the healing has been done.
550 for(unit& patient : resources::gameboard->units()) {
551 if(patient.side() == current_side()) {
552 patient.set_resting(true);
553 }
554 }
555
556 // Prepare the undo stack.
558
559 pump().fire("turn_refresh");
560 pump().fire("side_" + side_num + "_turn_refresh");
561 pump().fire("turn_" + turn_num + "_refresh");
562 pump().fire("side_" + side_num + "_turn_" + turn_num + "_refresh");
563
564 // Make sure vision is accurate.
566
568 sync.do_final_checkup();
570 }
571
572 statistics().reset_turn_stats(gamestate().board_.get_team(current_side()).save_id_or_number());
573
575
576 if(!is_skipping_replay() && current_team().get_scroll_to_leader()) {
577 gui_->scroll_to_leader(current_side(), game_display::ONSCREEN, false);
578 }
579}
580
582{
586 LOG_NG << "playing ToD sound: " << tod.sounds;
587 sound::play_sound(tod.sounds, sound_tracks::type::sound_source);
588 }
589 whiteboard_manager_->on_init_side();
590}
591
593{
594 config cfg = level_;
595
596 cfg["replay_pos"] = saved_game_.get_replay().get_pos();
598
599 gui_->write(cfg.add_child("display"));
600
601 // Write the soundsources.
602 soundsources_manager_->write_sourcespecs(cfg);
603
604 gui_->labels().write(cfg);
606
607 if(cfg["replay_pos"].to_int(0) > 0 && cfg["playing_team"].empty()) {
608 gui2::show_error_message(_("Trying to create a corrupt file, please report this bug"));
609 }
610
611 return cfg;
612}
613
615{
616
617 { // Block for set_scontext_synced
618 set_scontext_synced sync(1);
619 // Also clears the undo stack.
621
623 const std::string turn_num = std::to_string(turn());
624 const std::string side_num = std::to_string(current_side());
625
626 // Clear shroud, in case units had been slowed for the turn.
628
629 pump().fire("side_turn_end");
630 pump().fire("side_" + side_num + "_turn_end");
631 pump().fire("side_turn_" + turn_num + "_end");
632 pump().fire("side_" + side_num + "_turn_" + turn_num + "_end");
633 // This is where we refog, after all of a side's events are done.
636 sync.do_final_checkup();
637 }
639}
640
642{
643 set_scontext_synced sync(2);
644 const std::string turn_num = std::to_string(turn());
645 pump().fire("turn_end");
646 pump().fire("turn_" + turn_num + "_end");
647 sync.do_final_checkup();
648}
649
651{
652 // If we aren't using fog/shroud, this is easy :)
653 if(current_team().uses_fog() == false && current_team().uses_shroud() == false) {
654 return true;
655 }
656
657 // See if any enemies are visible
658 for(const unit& u : get_units()) {
659 if(current_team().is_enemy(u.side()) && !gui_->fogged(u.get_location())) {
660 return true;
661 }
662 }
663
664 return false;
665}
666
668{
669 if(menu_handler_.get_textbox().active() == false) {
670 return;
671 }
672
673 const std::string str = menu_handler_.get_textbox().box()->text();
674 const unsigned int team_num = current_side();
676
677 switch(menu_handler_.get_textbox().mode()) {
682 break;
684 if (menu_handler_.do_speak()) {
687 }
688 break;
693 break;
694 case gui::TEXTBOX_AI:
697 menu_handler_.do_ai_formula(str, team_num, mousehandler);
698 break;
699 default:
701 ERR_DP << "unknown textbox mode";
702 }
703}
704
706{
707 if(menu_handler_.get_textbox().active() == false) {
708 return;
709 }
710
711 const std::string str = menu_handler_.get_textbox().box()->text();
712 const std::vector<std::string>& command_history = menu_handler_.get_textbox().command_history();
713
714 auto prev = std::find(command_history.begin(), command_history.end(), str);
715
716 if (prev != command_history.end())
717 {
718 if(up) {
719 if(prev != command_history.begin()) {
720 menu_handler_.get_textbox().box()->set_text(*--prev);
721 }
722 } else {
723 if(++prev != command_history.end()) {
724 menu_handler_.get_textbox().box()->set_text(*prev);
725 } else {
726 menu_handler_.get_textbox().box()->set_text("");
727 }
728 }
729 } else if (up) {
730 if(command_history.size() > 0) {
731 menu_handler_.get_textbox().box()->set_text(*--prev);
732 }
733 if(!str.empty()) {
735 }
736 }
737}
738
740{
742
743 std::set<std::string> dictionary;
744 switch(mode) {
745 case gui::TEXTBOX_SEARCH: {
746 for(const unit& u : get_units()) {
747 const map_location& loc = u.get_location();
748 if(!gui_->fogged(loc) && !(gui_->viewing_team().is_enemy(u.side()) && u.invisible(loc)))
749 dictionary.insert(u.name());
750 }
751 // TODO List map labels
752 break;
753 }
755 std::vector<std::string> commands = menu_handler_.get_commands_list();
756 dictionary.insert(commands.begin(), commands.end());
757 [[fallthrough]]; // we also want player names from the next case
758 }
760 for(const team& t : get_teams()) {
761 if(!t.is_empty())
762 dictionary.insert(t.current_player());
763 }
764
765 // Add observers
766 for(const std::string& o : gui_->observers()) {
767 dictionary.insert(o);
768 }
769
770 // Add nicks who whispered you
771 for(const std::string& w : gui_->get_chat_manager().whisperers()) {
772 dictionary.insert(w);
773 }
774
775 // Add nicks from friendlist
776 const std::map<std::string, std::string> friends = prefs::get().get_acquaintances_nice("friend");
777
778 for(std::map<std::string, std::string>::const_iterator iter = friends.begin(); iter != friends.end(); ++iter) {
779 dictionary.insert((*iter).first);
780 }
781
782 // Exclude own nick from tab-completion.
783 // NOTE why ?
784 dictionary.erase(prefs::get().login());
785 break;
786 }
787
788 default:
789 ERR_DP << "unknown textbox mode";
790 } // switch(mode)
791
792 menu_handler_.get_textbox().tab(dictionary);
793}
794
796{
797 if(get_teams().size() == 0) {
798 throw game::game_error("The scenario has no sides defined");
799 }
800
801 assert(gamestate().board_.has_team(current_side()));
803}
804
806{
807 if(get_teams().size() == 0) {
808 throw game::game_error("The scenario has no sides defined");
809 }
810
811 assert(gamestate().board_.has_team(current_side()));
813}
814
815
820
821std::shared_ptr<wb::manager> play_controller::get_whiteboard() const
822{
823 return whiteboard_manager_;
824}
825
830
835
840
845
847{
848 if(event.key.key == SDLK_ESCAPE || event.key.key == SDLK_AC_BACK) {
850 } else if(event.key.key == SDLK_TAB) {
851 tab();
852 } else if(event.key.key == SDLK_UP) {
854 } else if(event.key.key == SDLK_DOWN) {
856 } else if(event.key.key == SDLK_RETURN || event.key.key == SDLK_KP_ENTER) {
858 }
859}
860
861void play_controller::process_keydown_event(const SDL_Event& event)
862{
863 if(event.key.key == SDLK_TAB) {
864 whiteboard_manager_->set_invert_behavior(true);
865 }
866}
867
868void play_controller::process_keyup_event(const SDL_Event& event)
869{
870 // If the user has pressed 1 through 9, we want to show
871 // how far the unit can move in that many turns
872 if(event.key.key >= '1' && event.key.key <= '9') {
873 const int new_path_turns = (event.type == SDL_EVENT_KEY_DOWN) ? event.key.key - '1' : 0;
874
875 if(new_path_turns != mouse_handler_.get_path_turns()) {
876 mouse_handler_.set_path_turns(new_path_turns);
877
879
880 if(u.valid()) {
881 // if it's not the unit's turn, we reset its moves
882 unit_movement_resetter move_reset(*u, u->side() != current_side());
883
885 *u, false, true, gui_->viewing_team(), mouse_handler_.get_path_turns()));
886
887 gui_->highlight_reach(mouse_handler_.current_paths());
888 } else {
890 }
891 }
892 } else if(event.key.key == SDLK_TAB) {
893 CKey keys;
894 if(!keys[SDLK_TAB]) {
895 whiteboard_manager_->set_invert_behavior(false);
896 }
897 }
898}
899
901{
902 assert(replay_);
903 return *replay_.get();
904}
905
907{
908 // Saving while an event is running isn't supported
909 // because it may lead to expired event handlers being saved.
910 assert(!gamestate().events_manager_->is_event_running());
911
912 scoped_savegame_snapshot snapshot(*this);
913 savegame::ingame_savegame save(saved_game_, prefs::get().save_compression_format());
915}
916
918{
919 scoped_savegame_snapshot snapshot(*this);
920 savegame::ingame_savegame save(saved_game_, prefs::get().save_compression_format());
921 save.save_game_automatic(false, filename);
922}
923
929
931{
932 savegame::replay_savegame save(saved_game_, prefs::get().save_compression_format());
933 save.save_game_automatic(false, filename);
934}
935
940
945
951
957
962
967
968const std::string& play_controller::select_music(bool victory) const
969{
970 const std::vector<std::string>& music_list = victory
971 ? (gamestate_->get_game_data()->get_victory_music().empty()
973 : gamestate_->get_game_data()->get_victory_music())
974 : (gamestate_->get_game_data()->get_defeat_music().empty()
976 : gamestate_->get_game_data()->get_defeat_music());
977
978 if(music_list.empty()) {
979 // Since this function returns a reference, we can't return a temporary empty string.
980 static const std::string empty_str = "";
981 return empty_str;
982 }
983
984 return music_list[randomness::rng::default_instance().get_random_int(0, music_list.size() - 1)];
985}
986
988{
989 if(is_linger_mode()) {
990 return;
991 }
992
993 if(is_regular_game_end()) {
994 return;
995 }
996
997 bool continue_level, found_player, found_network_player, invalidate_all;
998 std::set<unsigned> not_defeated;
999
1001 continue_level,
1002 found_player,
1003 found_network_player,
1004 invalidate_all,
1005 not_defeated,
1006 gamestate().remove_from_carryover_on_defeat_
1007 );
1008
1009 if(invalidate_all) {
1010 gui_->invalidate_all();
1011 }
1012
1013 if(continue_level) {
1014 return;
1015 }
1016
1017 if(found_player || found_network_player) {
1018 pump().fire("enemies_defeated");
1019 if(is_regular_game_end()) {
1020 return;
1021 }
1022 }
1023
1024 DBG_EE << "victory_when_enemies_defeated: " << gamestate().victory_when_enemies_defeated_;
1025 DBG_EE << "found_player: " << found_player;
1026 DBG_EE << "found_network_player: " << found_network_player;
1027
1028 if(!gamestate().victory_when_enemies_defeated_ && (found_player || found_network_player)) {
1029 // This level has asked not to be ended by this condition.
1030 return;
1031 }
1032
1033 if(video::headless()) {
1034 LOG_AIT << "winner: ";
1035 for(unsigned l : not_defeated) {
1037 if(ai.empty())
1038 ai = "default ai";
1039 LOG_AIT << l << " (using " << ai << ") ";
1040 }
1041
1042 LOG_AIT;
1043 ai_testing::log_victory(not_defeated);
1044 }
1045
1046 DBG_EE << "throwing end level exception...";
1047 // Also proceed to the next scenario when another player survived.
1048 end_level_data el_data;
1050 el_data.proceed_to_next_level = found_player || found_network_player;
1051 el_data.is_victory = found_player;
1052 set_end_level_data(el_data);
1053}
1054
1055void play_controller::process_oos(const std::string& msg) const
1056{
1057 if(video::headless()) {
1058 throw game::game_error(msg);
1059 }
1060
1062 return;
1063 }
1064
1065 std::stringstream message;
1066 message << _("The game is out of sync. It might not make much sense to continue. Do you want to save your game?");
1067 message << "\n\n" << _("Error details:") << "\n\n" << msg;
1068
1069 scoped_savegame_snapshot snapshot(*this);
1071 save.save_game_interactive(message.str(), savegame::savegame::YES_NO); // can throw quit_game_exception
1072}
1073
1075{
1076 return saved_game_.classification().get_tagname() == "multiplayer";
1077}
1078
1079void play_controller::update_gui_to_player(const int team_index, const bool observe)
1080{
1081 gui_->set_viewing_team_index(team_index, observe);
1082 gui_->recalculate_minimap();
1083 gui_->invalidate_all();
1084}
1085
1087{
1088 scoped_savegame_snapshot snapshot(*this);
1089 savegame::autosave_savegame save(saved_game_, prefs::get().save_compression_format());
1090 save.autosave(false, prefs::get().auto_save_max(), pref_constants::INFINITE_AUTO_SAVES);
1091}
1092
1094{
1095 scoped_savegame_snapshot snapshot(*this);
1096 savegame::ingame_savegame save(saved_game_, prefs::get().save_compression_format());
1097 save.save_game_automatic(true, filename);
1098}
1099
1101{
1102 // note: this writes to level_ if this is not a replay.
1104}
1105
1110
1115
1120
1125
1127{
1128 if(!gamestate().in_phase(game_data::TURN_PLAYING)) {
1129 return true;
1130 }
1131
1132 const team& t = current_team();
1133 return !t.is_local_human() || !t.is_proxy_human();
1134}
1135
1137{
1139 return;
1140 }
1141
1142 try {
1143 play_slice();
1144 } catch(const return_to_play_side_exception&) {
1146 }
1147}
1148
1150{
1151 if(gamestate().in_phase(game_data::PRELOAD)) {
1154
1156
1157 // So that the code knows it can send choices immidiateley
1159 fire_prestart();
1160 if(is_regular_game_end()) {
1161 return;
1162 }
1163
1164 for(const team& t : get_teams()) {
1165 actions::clear_shroud(t.side(), false, false);
1166 }
1167
1168 init_gui();
1169 LOG_NG << "first_time..." << (is_skipping_replay() ? "skipping" : "no skip");
1170
1171 fire_start();
1172 if(is_regular_game_end()) {
1173 return;
1174 }
1175
1176 sync.do_final_checkup();
1177 gui_->recalculate_minimap();
1178
1179 // Initialize countdown clock.
1180 for(const team& t : get_teams()) {
1182 t.set_countdown_time(saved_game_.mp_settings().mp_countdown_init_time);
1183 }
1184 }
1186 } else {
1187 init_gui();
1188 gui_->recalculate_minimap();
1189 }
1190
1192}
1193
1194/**
1195 * Find all [endlevel]next_scenario= attributes, and add them to @a result.
1196 */
1197static void find_next_scenarios(const config& parent, std::set<std::string>& result) {
1198 for(const auto& endlevel : parent.child_range("endlevel")) {
1199 if(endlevel.has_attribute("next_scenario")) {
1200 result.insert(endlevel["next_scenario"]);
1201 }
1202 }
1203 for(const auto [key, cfg] : parent.all_children_view()) {
1204 find_next_scenarios(cfg, result);
1205 }
1206};
1207
1209 // Which scenarios are reachable from the current one?
1210 std::set<std::string> possible_next_scenarios;
1211 possible_next_scenarios.insert(gamestate().gamedata_.next_scenario());
1212
1213 // Find all "endlevel" tags that could be triggered in events
1214 config events;
1215 gamestate().events_manager_->write_events(events);
1216 find_next_scenarios(events, possible_next_scenarios);
1217
1218 // Are we looking for [scenario]id=, [multiplayer]id= or [test]id=?
1219 const auto tagname = saved_game_.classification().get_tagname();
1220
1221 // Of the possible routes, work out which exist.
1222 bool possible_this_is_the_last_scenario = false;
1223 std::vector<std::string> known;
1224 std::vector<std::string> unknown;
1225 for(const auto& x : possible_next_scenarios) {
1226 if(x.empty() || x == "null") {
1227 possible_this_is_the_last_scenario = true;
1228 LOG_NG << "This can be the last scenario";
1229 } else if(utils::contains(x, '$')) {
1230 // Assume a WML variable will be set to a correct value before the end of the scenario
1231 known.push_back(x);
1232 LOG_NG << "Variable value for next scenario '" << x << "'";
1233 } else if(game_config_.find_child(tagname, "id", x)) {
1234 known.push_back(x);
1235 LOG_NG << "Known next scenario '" << x << "'";
1236 } else {
1237 unknown.push_back(x);
1238 ERR_NG << "Unknown next scenario '" << x << "'";
1239 }
1240 }
1241
1242 if(unknown.empty()) {
1243 // everything's good
1244 return;
1245 }
1246
1247 std::string title = _("Warning: broken campaign branches");
1248 std::stringstream message;
1249
1250 message << _n(
1251 // TRANSLATORS: This is an error that will hopefully only be seen by UMC authors and by players who have already
1252 // said "okay" to a "loading saves from an old version might not work" dialog.
1253 "The next scenario is missing, you will not be able to finish this campaign.",
1254 // TRANSLATORS: This is an error that will hopefully only be seen by UMC authors and by players who have already
1255 // said "okay" to a "loading saves from an old version might not work" dialog.
1256 "Some of the possible next scenarios are missing, you might not be able to finish this campaign.",
1257 unknown.size() + known.size() + (possible_this_is_the_last_scenario ? 1 : 0));
1258 message << "\n\n";
1259 message << _n(
1260 "Please report the following missing scenario to the campaign’s author:\n$unknown_list|",
1261 "Please report the following missing scenarios to the campaign’s author:\n$unknown_list|",
1262 unknown.size());
1263 message << "\n";
1264 message << _("Once this is fixed, you will need to restart this scenario.");
1265
1266 std::stringstream unknown_list;
1267 for(const auto& x : unknown) {
1268 unknown_list << font::unicode_bullet << " " << x << "\n";
1269 }
1270 utils::string_map symbols;
1271 symbols["unknown_list"] = unknown_list.str();
1272 auto message_str = utils::interpolate_variables_into_string(message.str(), &symbols);
1273 ERR_NG << message_str;
1275}
1276
1278{
1279 const team& viewing_team = gui_->viewing_team();
1280 return gui_->viewing_team_is_playing() && !events::commands_disabled && viewing_team.is_local_human()
1281 && !is_browsing();
1282}
1283
1284std::set<std::string> play_controller::all_players() const
1285{
1286 std::set<std::string> res = gui_->observers();
1287 for(const team& t : get_teams()) {
1288 if(t.is_human()) {
1289 res.insert(t.current_player());
1290 }
1291 }
1292
1293 return res;
1294}
1295
1297{
1298 const bool time_left = gamestate().tod_manager_.next_turn(&gamestate().gamedata_);
1299
1300 if(!time_left) {
1301 LOG_NG << "firing time over event...";
1303 pump().fire("time_over");
1304 LOG_NG << "done firing time over event...";
1305
1306 // If turns are added while handling 'time over' event.
1307 if(gamestate().tod_manager_.is_time_left()) {
1308 return;
1309 }
1310
1311 if(video::headless()) {
1312 LOG_AIT << "time over (draw)";
1314 }
1315
1316 check_victory();
1317 if(is_regular_game_end()) {
1318 return;
1319 }
1320
1323 e.proceed_to_next_level = false;
1324 e.is_victory = false;
1326 }
1327}
1328
1334
1336{
1337 controller_.saved_game_.remove_snapshot();
1338}
1339
1341{
1342 const team& t = gui_->viewing_team();
1343 static const std::string no_objectives(_("No objectives available"));
1344 std::string objectives = utils::interpolate_variables_into_string(t.objectives(), *gamestate_->get_game_data());
1345 gui2::show_transient_message(get_scenario_name(), (objectives.empty() ? no_objectives : objectives), "", true);
1346 t.reset_objectives_changed();
1347}
1348
1350{
1352 const std::shared_ptr<gui::button> skip_animation_button = get_display().find_action_button("skip-animation");
1353 if(skip_animation_button) {
1354 skip_animation_button->set_check(skip_replay_);
1355 }
1356}
1357
1359{
1360 return is_skipping_replay() || (prefs::get().skip_ai_moves() && current_team().is_ai() && !is_replay());
1361}
1362
1367
1372
map_location loc
Definition move.cpp:172
#define ERR_NG
#define DBG_NG
#define LOG_NG
Managing the AIs lifecycle - headers TODO: Refactor history handling and internal commands.
double t
map_location prev
Class that keeps track of all the keys on the keyboard.
Definition key.hpp:29
void new_side_turn(int side)
Performs some initializations and error checks when starting a new side-turn.
Definition undo.cpp:173
bool can_undo() const
True if there are actions that can be undone.
Definition undo.hpp:95
void redo()
Redoes the top action on the redo stack.
Definition undo.cpp:314
bool can_redo() const
True if there are actions that can be redone.
Definition undo.hpp:97
void undo()
Undoes the top action on the undo stack.
Definition undo.cpp:281
static manager & get_singleton()
Definition manager.hpp:140
std::string get_active_ai_identifier_for_side(side_number side)
Gets AI algorithm identifier for active AI of the given side.
Definition manager.cpp:542
void raise_gamestate_changed()
Notifies all observers of 'ai_gamestate_changed' event.
Definition manager.cpp:448
static void log_victory(const std::set< unsigned int > &teams)
Definition testing.cpp:81
static void log_draw()
Definition testing.cpp:75
A config object defines a single node in a WML file, with access to child nodes.
Definition config.hpp:157
config & add_child(std::string_view key)
Definition config.cpp:436
auto all_children_view() const
In-order iteration over all children.
Definition config.hpp:795
child_itors child_range(std::string_view key)
Definition config.cpp:268
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition config.cpp:858
const game_config_view & game_config_
virtual void play_slice()
int side_upkeep(int side_num) const
std::shared_ptr< gui::button > find_action_button(const std::string &id)
Retrieves a pointer to a theme UI button.
Definition display.cpp:768
void do_ai_formula(const std::string &str, int side_num, mouse_handler &mousehandler)
gui::floating_textbox & get_textbox()
void do_command(const std::string &str)
void do_search(const std::string &new_search)
void set_gui(game_display *gui)
std::vector< std::string > get_commands_list()
void set_gui(game_display *gui)
void set_current_paths(const pathfind::paths &new_paths)
unit_map::iterator selected_unit()
map_location get_selected_hex() const
void select_hex(const map_location &hex, const bool browse, const bool highlight=true, const bool fire_event=true, const bool force_unhighlight=false)
const pathfind::paths & current_paths() const
void set_side(int side_number)
void set_path_turns(const int path_turns)
void check_victory(bool &, bool &, bool &, bool &, std::set< unsigned > &, bool)
void end_turn(int pnum)
team & get_team(int i)
virtual const unit_map & units() const override
void new_turn(int pnum)
std::string get_tagname() const
static game_config_view wrap(const config &cfg)
optional_const_config find_child(std::string_view key, const std::string &name, const std::string &value) const
void set_phase(PHASE phase)
@ PRELOAD
the preload [event] is fired next phase: PRESTART (normal game), TURN_STARTING_WAITING (reloaded game...
Definition game_data.hpp:76
@ INITIAL
creating intitial [unit]s, executing toplevel [lua] etc.
Definition game_data.hpp:73
@ GAME_ENDED
The game has ended and the user is observing the final state "lingering" The game can be saved here.
@ PRESTART
the prestart [event] is fired next phase: START (default), GAME_ENDING
Definition game_data.hpp:79
@ TURN_PLAYING
The User is controlling the game and invoking actions The game can be saved here.
Definition game_data.hpp:93
@ TURN_STARTING_WAITING
we are waiting for the turn to start.
Definition game_data.hpp:86
@ START
the start [event] is fired next phase: TURN_STARTING_WAITING (default), GAME_ENDING
Definition game_data.hpp:82
@ TURN_STARTING
the turn, side turn etc.
Definition game_data.hpp:89
PHASE phase() const
map_location last_selected
the last location where a select event fired.
config::attribute_value & get_variable(const std::string &varname)
throws invalid_variablename_exception if varname is no valid variable name.
Definition game_data.cpp:66
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
void write(config &cfg) const
bool in_phase(game_data::PHASE phase) const
void set_game_display(game_display *)
std::unique_ptr< game_lua_kernel > lua_kernel_
std::unique_ptr< pathfind::manager > pathfind_manager_
game_board board_
const std::unique_ptr< game_events::manager > events_manager_
bool victory_when_enemies_defeated_
tod_manager tod_manager_
game_data gamedata_
static void progress(loading_stage stage=loading_stage::none)
Report what is being loaded to the loading screen.
static void raise()
Raise the loading screen to the top of the draw stack.
static void display(const std::function< void()> &f)
@ close_button
Shows a close button.
Definition message.hpp:75
const std::unique_ptr< gui::textbox > & box() const
void memorize_command(const std::string &command)
TEXTBOX_MODE mode() const
void tab(const std::set< std::string > &dictionary)
const std::vector< std::string > & command_history() const
actions::undo_list & undo_stack()
game_classification & get_classification()
const std::string & select_music(bool victory) const
void process_keydown_event(const SDL_Event &event) override
Process keydown (always).
void maybe_throw_return_to_play_side() const
config to_config() const
Builds the snapshot config from members and their respective configs.
void init(const config &level)
std::unique_ptr< hotkey_handler > hotkey_handler_
virtual void init_gui()
bool have_keyboard_focus() override
Derived classes should override this to return false when arrow keys should not scroll the map,...
const unit_map & get_units() const
std::unique_ptr< game_state > gamestate_
void show_objectives() const
events::menu_handler menu_handler_
void fire_preload()
preload events cannot be synced
void check_victory()
Checks to see if a side has won.
bool is_linger_mode() const
virtual soundsource::manager * get_soundsource_man() override
Get (optionally) a soundsources manager a derived class uses.
void check_next_scenario_is_known()
This shows a warning dialog if either [scenario]next_scenario or any [endlevel]next_scenario would le...
bool reveal_map_default() const
play_controller(const config &level, saved_game &state_of_game)
bool do_healing() const
void set_end_level_data(const end_level_data &data)
events::mouse_handler & get_mouse_handler_base() override
Get a reference to a mouse handler member a derived class uses.
void update_savegame_snapshot() const
void set_do_healing(bool do_healing)
bool is_skipping_actions() const
bool is_during_turn() const
virtual void require_end_turn()=0
bool can_use_synced_wml_menu() const
void reset_gamestate(const config &level, int replay_pos)
void do_consolesave(const std::string &filename)
void save_game_auto(const std::string &filename)
void textbox_move_vertically(bool up)
bool is_regular_game_end() const
virtual void check_objectives()=0
void save_replay_auto(const std::string &filename)
saved_game & saved_game_
hotkey::command_executor * get_hotkey_command_executor() override
Optionally get a command executor to handle context menu events.
bool enemies_visible() const
std::unique_ptr< replay > replay_
const mp_game_settings & get_mp_settings()
game_display & get_display() override
Get a reference to a display member a derived class uses.
virtual void update_viewing_player()=0
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.
virtual void process_oos(const std::string &msg) const
Asks the user whether to continue on an OOS error.
bool is_browsing() const override
std::vector< team > & get_teams()
std::unique_ptr< soundsource::manager > soundsources_manager_
void do_init_side()
Called by replay handler or init_side() to do actual work for turn change.
std::string theme() const
int current_side() const
Returns the number of the side whose turn it is.
bool is_skipping_replay() const
std::shared_ptr< wb::manager > get_whiteboard() 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
std::size_t turn() const
map_location map_start_
virtual bool should_return_to_play_side() const
std::set< std::string > all_players() 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.
events::mouse_handler mouse_handler_
std::unique_ptr< plugins_context > plugins_context_
virtual plugins_context * get_plugins_context() override
Get (optionally) a plugins context a derived class uses.
bool can_undo() const
void process_focus_keydown_event(const SDL_Event &event) override
Process keydown (only when the general map display does not have focus).
void refresh_objectives() const
Reevaluate [show_if] conditions and build a new objectives string.
bool can_redo() const
game_events::wml_event_pump & pump()
const auto & timer() const
statistics_t & statistics()
void process_keyup_event(const SDL_Event &event) override
Process keyup (always).
persist_manager persist_
std::shared_ptr< wb::manager > whiteboard_manager_
virtual void check_time_over()
t_string get_scenario_name() const
game_state & gamestate()
static plugins_manager * get()
Definition manager.cpp:58
static prefs & get()
std::string login()
void encounter_all_content(const game_board &gb)
std::map< std::string, std::string > get_acquaintances_nice(const std::string &filter)
Implements a quit confirmation dialog.
static rng & default_instance()
Definition random.cpp:73
int get_random_int(int min, int max)
Definition random.hpp:51
bool add_start_if_not_there_yet()
Definition replay.cpp:661
void init_side()
Definition replay.cpp:203
config * get_next_action()
Definition replay.cpp:607
Exception used to escape form the ai or ui code to playsingle_controller::play_side.
replay_recorder_base & get_replay()
game_classification & classification()
mp_game_settings & mp_settings()
Multiplayer parameters for this game.
config & set_snapshot(config snapshot)
Class for autosaves.
Definition savegame.hpp:257
void autosave(const bool disable_autosave, const int autosave_max, const int infinite_autosaves)
Definition savegame.cpp:511
Class for "normal" midgame saves.
Definition savegame.hpp:230
Class for replay saves (either manually or automatically).
Definition savegame.hpp:244
bool save_game_automatic(bool ask_for_overwrite=false, const std::string &filename="")
Saves a game without user interaction, unless the file exists and it should be asked to overwrite it.
Definition savegame.cpp:274
bool save_game_interactive(const std::string &message, DIALOG_TYPE dialog_type)
Save a game interactively through the savegame dialog.
Definition savegame.cpp:291
A RAII object to enter the synced context, cannot be called if we are already in a synced context.
void do_final_checkup(bool dont_throw=false)
sourcespec get(const std::string &id)
void reset_turn_stats(const std::string &save_id)
static bool run_in_synced_context_if_not_already(const std::string &commandname, const config &data, action_spectator &spectator=get_default_spectator())
Checks whether we are currently running in a synced context, and if not we enters it.
static synced_state get_synced_state()
static void block_undo(bool do_block=true, bool clear_undo=true)
set this to false to prevent clearing the undo stack, this is important when we cannot change the gam...
This class stores all the data for a single 'side' (in game nomenclature).
Definition team.hpp:74
bool is_ai() const
Definition team.hpp:292
int support() const
Definition team.hpp:217
bool is_local_human() const
Definition team.hpp:294
bool is_enemy(int n) const
Definition team.hpp:270
void spend_gold(const int amount)
Definition team.hpp:221
void new_turn()
Definition team.hpp:218
void turn_event_fired()
const time_of_day & get_time_of_day(int for_turn=0) const
Returns global time of day for the passed turn.
bool next_turn(game_data *vars)
Function to move to the next turn.
void remove_scenario_fixes()
Definition types.cpp:1462
void apply_scenario_fix(const config &cfg)
Definition types.cpp:1427
This class represents a single unit of a specific type.
Definition unit.hpp:39
A variable-expanding proxy for the config class.
Definition variable.hpp:45
This class is the frontend of the whiteboard framework for the rest of the Wesnoth code.
Definition manager.hpp:44
#define ERR_DP
const config * cfg
#define DBG_EE
void throw_quit_game_exception()
static std::string _n(const char *str1, const char *str2, int n)
Definition gettext.hpp:104
static std::string _(const char *str)
Definition gettext.hpp:100
void calculate_healing(int side, bool update_display)
Calculates healing for all units for the given side.
Definition heal.cpp:290
Various functions that implement healing of units (when a side turn starts).
This file implements all the hotkey handling and menu details for play controller.
Standard logging facilities (interface).
#define log_scope(description)
Definition log.hpp:275
Declarations for a class that implements WML-defined (right-click) menu items.
bool clear_shroud(int side, bool reset_fog, bool fire_events)
Function that will clear shroud (and fog) based on current unit positions.
Definition vision.cpp:746
void recalculate_fog(int side)
Function that recalculates the fog of war.
Definition vision.cpp:697
A small explanation about what's going on here: Each action has access to two game_info objects First...
Definition actions.cpp:59
Handling of system events.
Graphical text output.
const std::string unicode_bullet
Definition constants.cpp:47
std::vector< std::string > default_defeat_music
bool ignore_replay_errors
static void add_color_info(const game_config_view &v, bool build_defaults)
std::vector< std::string > default_victory_music
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.
void show_error_message(const std::string &msg, bool message_use_markup)
Shows an error message to the user.
Definition message.cpp:201
void show_message(const std::string &title, const std::string &msg, const std::string &button_caption, const bool auto_close, const bool message_use_markup, const bool title_use_markup)
Shows a message to the user.
Definition message.cpp:148
Keyboard shortcuts for game actions.
const int INFINITE_AUTO_SAVES
::tod_manager * tod_manager
Definition resources.cpp:29
game_board * gameboard
Definition resources.cpp:20
persist_manager * persist
Definition resources.cpp:26
game_data * gamedata
Definition resources.cpp:22
game_events::manager * game_events
Definition resources.cpp:24
replay * recorder
Definition resources.cpp:28
actions::undo_list * undo_stack
Definition resources.cpp:32
soundsource::manager * soundsources
Definition resources.cpp:27
game_lua_kernel * lua_kernel
Definition resources.cpp:25
game_classification * classification
Definition resources.cpp:34
pathfind::manager * tunnels
Definition resources.cpp:31
play_controller * controller
Definition resources.cpp:21
filter_context * filter_con
Definition resources.cpp:23
std::shared_ptr< wb::manager > whiteboard
Definition resources.cpp:33
void load_interactive_by_exception()
load_interactive wrapper for in-game save loading.
Definition savegame.cpp:174
void write_music_play_list(config &snapshot)
Definition sound.cpp:823
void play_sound(const std::string &files, sound_tracks::type group, unsigned int repeats)
Definition sound.cpp:978
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 ...
bool contains(const Container &container, const Value &value)
Returns true iff value is found in container.
Definition general.hpp:88
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:146
int w
Definition pathfind.cpp:188
static void clear_resources()
static lg::log_domain log_engine("engine")
static lg::log_domain log_aitesting("ai/testing")
static void find_next_scenarios(const config &parent, std::set< std::string > &result)
Find all [endlevel]next_scenario= attributes, and add them to result.
static void copy_persistent(const config &src, config &dst)
Copies [scenario] attributes/tags that are not otherwise stored in C++ structs/clases.
static lg::log_domain log_enginerefac("enginerefac")
#define LOG_AIT
static lg::log_domain log_display("display")
static lg::log_domain log_engine_enemies("engine/enemies")
Define the game's event mechanism.
Replay control code.
rect dst
Location on the final composed sheet.
rect src
Non-transparent portion of the surface to compose.
std::string filename
Filename.
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.
Encapsulates the map of the game.
Definition location.hpp:46
static const map_location & null_location()
Definition location.hpp:103
std::chrono::seconds mp_countdown_init_time
Object which contains all the possible locations a unit can move to, with associated best routes to t...
Definition pathfind.hpp:73
scoped_savegame_snapshot(const play_controller &controller)
Object which defines a time of day with associated bonuses, image, sounds etc.
std::string sounds
List of "ambient" sounds associated with this time_of_day, Played at the beginning of turn.
bool reveal_map
Should we reveal map when game is ended? (Multiplayer only)
bool valid() const
Definition map.hpp:273
Object which temporarily resets a unit's movement.
Definition unit.hpp:2017
const std::string & gamedata
Gather statistics important for AI testing and output them.
unit_type_data unit_types
Definition types.cpp:1499
Various functions that implement the undoing (and redoing) of in-game commands.
Various functions implementing vision (through fog of war and shroud).
#define e