The Battle for Wesnoth  1.17.12+dev
replay_controller.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2015 - 2022
3  Part of the Battle for Wesnoth Project https://www.wesnoth.org/
4 
5  This program is free software; you can redistribute it and/or modify
6  it under the terms of the GNU General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or
8  (at your option) any later version.
9  This program is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY.
11 
12  See the COPYING file for more details.
13 */
14 
15 #include "replay_controller.hpp"
16 
17 #include "game_config_manager.hpp"
18 #include "gettext.hpp"
19 #include "log.hpp"
20 #include "replay.hpp"
21 #include "resources.hpp"
23 
24 static lg::log_domain log_engine("engine");
25 #define DBG_NG LOG_STREAM(debug, log_engine)
26 
27 static lg::log_domain log_replay("replay");
28 #define DBG_REPLAY LOG_STREAM(debug, log_replay)
29 #define LOG_REPLAY LOG_STREAM(info, log_replay)
30 #define ERR_REPLAY LOG_STREAM(err, log_replay)
31 
32 namespace
33 {
34 struct replay_play_nostop : public replay_controller::replay_stop_condition
35 {
36  replay_play_nostop() {}
37  virtual bool should_stop() { return false; }
38 };
39 
40 struct replay_play_moves : public replay_controller::replay_stop_condition
41 {
42  int moves_todo_;
43  replay_play_moves(int moves_todo) : moves_todo_(moves_todo) {}
44  virtual void move_done() { --moves_todo_; }
45  virtual bool should_stop() { return moves_todo_ == 0; }
46 };
47 
48 struct replay_play_turn : public replay_controller::replay_stop_condition
49 {
50  int turn_begin_;
51  int turn_current_;
52  replay_play_turn(int turn_begin) : turn_begin_(turn_begin), turn_current_(turn_begin) {}
53  virtual void new_side_turn(int , int turn) { turn_current_ = turn; }
54  virtual bool should_stop() { return turn_begin_ != turn_current_; }
55 };
56 
57 struct replay_play_side : public replay_controller::replay_stop_condition
58 {
59  bool next_side_;
60  replay_play_side() : next_side_(false) {}
61  virtual void new_side_turn(int , int) { next_side_ = true; }
62  virtual bool should_stop() { return next_side_; }
63 };
64 }
65 
66 replay_controller::replay_controller(play_controller& controller, bool control_view, const std::shared_ptr<config>& reset_state, const std::function<void()>& on_end_replay)
67  : controller_(controller)
68  , stop_condition_(new replay_stop_condition())
69  , disabler_()
70  , vision_()
71  , reset_state_(reset_state)
72  , on_end_replay_(on_end_replay)
73  , return_to_play_side_(false)
74 {
75  if(control_view) {
77  }
80 }
82 {
85  }
88 }
90 {
91  const config& theme_cfg = theme::get_theme_config(controller_.theme());
92  if (const auto res = theme_cfg.optional_child("resolution"))
93  {
94  if (const auto replay_theme_cfg = res->optional_child("replay")) {
95  controller_.get_display().get_theme().modify(replay_theme_cfg.value());
96  }
97  }
98 }
99 
101 {
104 }
105 
107 {
108  stop_condition_.reset(new replay_play_turn(controller_.gamestate().tod_manager_.turn()));
110 }
111 
113 {
114  stop_condition_.reset(new replay_play_side());
116 }
117 
119 {
120  stop_condition_.reset(new replay_play_moves(1));
122 }
123 
124 //move all sides till stop/end
126 {
127  stop_condition_.reset(new replay_play_nostop());
129 }
130 
132 {
134 }
135 
137 {
139 }
140 
141 void replay_controller::handle_generic_event(const std::string& name)
142 {
143  // this is only attached to one event - the theme_reset_event
144  if(name == "theme_reset") {
146  }
147  if(std::shared_ptr<gui::button> skip_animation_button = controller_.get_display().find_action_button("skip-animation")) {
148  skip_animation_button->set_check(controller_.is_skipping_replay());
149  }
150 }
151 
153 {
154  return resources::recorder->at_end();
155 }
156 
158 {
160  while(!return_to_play_side_ && !static_cast<playsingle_controller&>(controller_).get_player_type_changed())
161  {
162  if(!stop_condition_->should_stop())
163  {
164  if(resources::recorder->at_end()) {
165  //Gather more replay data
166  on_end_replay_();
167  }
168  else {
169  REPLAY_RETURN res = do_replay(true);
170  if(res == REPLAY_FOUND_END_MOVE) {
171  stop_condition_->move_done();
172  }
173  if(res == REPLAY_FOUND_END_TURN) {
174  return res;
175  }
176  if(res == REPLAY_RETURN_AT_END) {
177  stop_replay();
178  }
179  if(res == REPLAY_FOUND_INIT_TURN)
180  {
182  }
183  }
184  controller_.play_slice(false);
185 
186  // Update the buttons once, on the transition from not-stopped to stopped.
187  if(stop_condition_->should_stop()) {
189  }
190  }
191  else
192  {
193  // Don't move the update_enabled_buttons() call here. This play_slice() should block
194  // until the next event occurs, but on X11/Linux update_enabled_buttons() seems to put
195  // an event in the queue, turning this into a busy loop.
196  controller_.play_slice(true);
197  }
198  }
199  return REPLAY_FOUND_END_MOVE;
200 }
202 {
203  switch(cmd.command) {
205  return true;
209  return is_controlling_view();
210  //commands we only can do before the end of the replay
212  return !recorder_at_end();
217  //we have one events_disabler when starting the replay_controller and a second when entering the synced context.
218  return should_stop() && (events::commands_disabled <= 1 ) && !recorder_at_end();
221  default:
222  assert(false);
223  return false;
224  }
225 }
226 
228 {
229  vision_ = SHOW_ALL;
230  update_teams();
231 }
232 
234 {
236  update_teams();
237 }
238 
240 {
242  update_teams();
243 }
244 
246 {
249  update_gui();
250 }
251 
253 {
254  assert(vision_);
256 }
257 
259 {
260  return vision_ == SHOW_ALL;
261 }
std::shared_ptr< gui::button > find_action_button(const std::string &id)
Retrieves a pointer to a theme UI button.
Definition: display.cpp:825
std::function< void()> on_end_replay_
void queue_rerender()
Marks everything for rendering including all tiles and sidebar.
Definition: display.cpp:2322
theme & get_theme()
Definition: display.hpp:375
replay_controller(play_controller &controller, bool control_view, const std::shared_ptr< config > &reset_state, const std::function< void()> &on_end_replay=nop)
bool is_skipping_replay() const
static lg::log_domain log_engine("engine")
game_display & get_display() override
Get a reference to a display member a derived class uses.
bool at_end() const
Definition: replay.cpp:636
int first_human_team_
Definition: game_state.hpp:75
Stores all information related to functions that can be bound to hotkeys.
virtual void play_slice(bool is_delay_enabled=true)
Replay control code.
HOTKEY_COMMAND command
The command associated with this hotkey.
REPLAY_RETURN do_replay(bool one_move)
Definition: replay.cpp:687
bool recorder_at_end() const
void handle_generic_event(const std::string &name) override
REPLAY_RETURN
Definition: replay.hpp:156
static const config & get_theme_config(const std::string &id)
Returns the saved config for the theme with the given ID.
Definition: theme.cpp:1004
std::unique_ptr< replay_stop_condition > stop_condition_
std::optional< REPLAY_VISION > vision_
events::generic_event & theme_reset_event()
Definition: theme.hpp:284
void update_enabled_buttons()
Refresh the states of the replay-control buttons, this will cause the hotkey framework to query can_e...
void modify(const config &cfg)
Definition: theme.cpp:820
tod_manager tod_manager_
Definition: game_state.hpp:48
replay * recorder
Definition: resources.cpp:29
std::string theme() const
utils::optional_reference< config > optional_child(config_key_type key, int n=0)
Euivalent to child, but returns an empty optional if the nth child was not found. ...
Definition: config.cpp:445
REPLAY_RETURN play_side_impl()
void invalidate_all()
Function to invalidate all tiles.
Definition: display.cpp:3134
void update_gui_to_player(const int team_index, const bool observe=false)
Changes the UI for this client to the passed side index.
bool allow_reset_replay() const
virtual bool attach_handler(observer *obs)
bool can_execute_command(const hotkey::hotkey_command &cmd, int index) const
static lg::log_domain log_replay("replay")
game_state & gamestate()
play_controller & controller_
bool is_controlling_view() const
Standard logging facilities (interface).
int current_side() const
Returns the number of the side whose turn it is.
virtual bool detach_handler(observer *obs)
int turn() const
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:60
bool should_stop() const