The Battle for Wesnoth  1.13.10+dev
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
undo.cpp
Go to the documentation of this file.
1 /*
2  Copyright (C) 2003 - 2017 by David White <dave@whitevine.net>
3  Part of the Battle for Wesnoth Project http://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 /**
16  * @file
17  * Undoing, redoing.
18  */
19 
20 #include "actions/undo.hpp"
21 
22 #include "game_board.hpp" // for game_board
23 #include "game_display.hpp" // for game_display
24 #include "log.hpp" // for LOG_STREAM, logger, etc
25 #include "map/map.hpp" // for gamemap
26 #include "map/location.hpp" // for map_location, operator<<, etc
27 #include "mouse_handler_base.hpp" // for command_disabler
28 #include "preferences/general.hpp"
29 #include "recall_list_manager.hpp" // for recall_list_manager
30 #include "replay.hpp" // for recorder, replay
31 #include "replay_helper.hpp" // for replay_helper
32 #include "resources.hpp" // for screen, teams, units, etc
33 #include "synced_context.hpp" // for set_scontext_synced
34 #include "team.hpp" // for team
35 #include "units/unit.hpp" // for unit
37 #include "units/id.hpp"
38 #include "units/map.hpp" // for unit_map, etc
39 #include "units/ptr.hpp" // for unit_const_ptr, unit_ptr
40 #include "units/types.hpp" // for unit_type, unit_type_data, etc
41 #include "whiteboard/manager.hpp" // for manager
42 
43 #include "actions/create.hpp" // for find_recall_location, etc
44 #include "actions/move.hpp" // for get_village
45 #include "actions/vision.hpp" // for clearer_info, etc
52 
53 #include <algorithm> // for reverse
54 #include <cassert> // for assert
55 #include <ostream> // for operator<<, basic_ostream, etc
56 #include <set> // for set
57 
58 static lg::log_domain log_engine("engine");
59 #define ERR_NG LOG_STREAM(err, log_engine)
60 #define LOG_NG LOG_STREAM(info, log_engine)
61 
62 
63 namespace actions {
64 
65 
66 
67 /**
68  * Creates an undo_action based on a config.
69  * @return a pointer that must be deleted, or nullptr if the @a cfg could not be parsed.
70  */
72 {
73  const std::string str = cfg["type"];
74  undo_action_base * res = nullptr;
75  // The general division of labor in this function is that the various
76  // constructors will parse the "unit" child config, while this function
77  // parses everything else.
78 
79  if ( str == "move" ) {
80  res = new undo::move_action(cfg, cfg.child_or_empty("unit"),
81  cfg["starting_moves"],
82  map_location::parse_direction(cfg["starting_direction"]));
83  }
84 
85  else if ( str == "recruit" ) {
86  // Validate the unit type.
87  const config & child = cfg.child("unit");
88  const unit_type * u_type = unit_types.find(child["type"]);
89 
90  if ( !u_type ) {
91  // Bad data.
92  ERR_NG << "Invalid recruit found in [undo] or [redo]; unit type '"
93  << child["type"] << "' was not found.\n";
94  return nullptr;
95  }
96  res = new undo::recruit_action(cfg, *u_type, map_location(cfg.child_or_empty("leader"), nullptr));
97  }
98 
99  else if ( str == "recall" )
100  res = new undo::recall_action(cfg, map_location(cfg.child_or_empty("leader"), nullptr));
101 
102  else if ( str == "dismiss" )
103  res = new undo::dismiss_action(cfg, cfg.child("unit"));
104 
105  else if ( str == "auto_shroud" )
106  res = new undo::auto_shroud_action(cfg["active"].to_bool());
107 
108  else if ( str == "update_shroud" )
109  res = new undo::update_shroud_action();
110  else
111  {
112  // Unrecognized type.
113  ERR_NG << "Unrecognized undo action type: " << str << "." << std::endl;
114  return nullptr;
115  }
116  return res;
117 }
118 
119 
120 /**
121  * Constructor.
122  * The config is allowed to be invalid.
123  */
125  undos_(), redos_(), side_(1), committed_actions_(false)
126 {
127  if ( cfg )
128  read(cfg);
129 }
130 
131 /**
132  * Destructor.
133  */
135 {
136  // Default destructor, but defined out-of-line to localize the templating.
137  // (Might make compiles faster.)
138 }
139 
140 
141 /**
142  * Adds an auto-shroud toggle to the undo stack.
143  */
144 void undo_list::add_auto_shroud(bool turned_on)
145 {
146  /// @todo: Consecutive shroud actions can be collapsed into one.
147 
148  // Do not call add(), as this should not clear the redo stack.
149  add(new undo::auto_shroud_action(turned_on));
150 }
151 
153 {
154  /// @todo: Consecutive shroud actions can be collapsed into one.
155 
156  // Do not call add(), as this should not clear the redo stack.
157  add(new undo_dummy_action());
158 }
159 
160 /**
161  * Adds a dismissal to the undo stack.
162  */
164 {
165  add(new undo::dismiss_action(u));
166 }
167 
168 /**
169  * Adds a move to the undo stack.
170  */
172  const std::vector<map_location>::const_iterator & begin,
173  const std::vector<map_location>::const_iterator & end,
174  int start_moves, int timebonus, int village_owner,
175  const map_location::DIRECTION dir)
176 {
177  add(new undo::move_action(u, begin, end, start_moves, timebonus, village_owner, dir));
178 }
179 
180 /**
181  * Adds a recall to the undo stack.
182  */
184  const map_location& from, int orig_village_owner, bool time_bonus)
185 {
186  add(new undo::recall_action(u, loc, from, orig_village_owner, time_bonus));
187 }
188 
189 /**
190  * Adds a recruit to the undo stack.
191  */
193  const map_location& from, int orig_village_owner, bool time_bonus)
194 {
195  add(new undo::recruit_action(u, loc, from, orig_village_owner, time_bonus));
196 }
197 
198 /**
199  * Adds a shroud update to the undo stack.
200  * This is called from within commit_vision(), so there should be no need
201  * for this to be publicly visible.
202  */
204 {
205  /// @todo: Consecutive shroud actions can be collapsed into one.
206 
208 }
209 
210 
211 /**
212  * Clears the stack of undoable (and redoable) actions.
213  * (Also handles updating fog/shroud if needed.)
214  * Call this if an action alters the game state, but add that action to the
215  * stack before calling this (if the action is a kind that can be undone).
216  * This may fire events and change the game state.
217  */
219 {
220  // The fact that this function was called indicates that something was done.
221  // (Some actions, such as attacks, are never put on the stack.)
222  committed_actions_ = true;
223 
224  // We can save some overhead by not calling apply_shroud_changes() for an
225  // empty stack.
226  if ( !undos_.empty() ) {
228  undos_.clear();
229  }
230  // No special handling for redos, so just clear that stack.
231  redos_.clear();
232 }
233 
234 
235 /**
236  * Updates fog/shroud based on the undo stack, then updates stack as needed.
237  * Call this when "updating shroud now".
238  * This may fire events and change the game state.
239  * @param[in] is_replay Set to true when this is called during a replay.
240  */
242 {
243  // Update fog/shroud.
244  bool cleared_something = apply_shroud_changes();
245 
246  if (cleared_something) {
247  // The actions that led to information being revealed can no longer
248  // be undone.
249  undos_.clear();
250  //undos_.erase(undos_.begin(), undos_.begin() + erase_to);
251  committed_actions_ = true;
252  }
253 }
254 
255 
256 /**
257  * Performs some initializations and error checks when starting a new side-turn.
258  * @param[in] side The side whose turn is about to start.
259  */
261 {
262  // Error checks.
263  if ( !undos_.empty() ) {
264  ERR_NG << "Undo stack not empty in new_side_turn()." << std::endl;
265  // At worst, someone missed some sighted events, so try to recover.
266  undos_.clear();
267  redos_.clear();
268  }
269  else if ( !redos_.empty() ) {
270  ERR_NG << "Redo stack not empty in new_side_turn()." << std::endl;
271  // Sloppy tracking somewhere, but not critically so.
272  redos_.clear();
273  }
274 
275  // Reset the side.
276  side_ = side;
277  committed_actions_ = false;
278 }
279 
280 
281 /**
282  * Read the undo_list from the provided config.
283  * Currently, this is only used when the undo_list is empty, but in theory
284  * it could be used to append the config to the current data.
285  */
286 void undo_list::read(const config & cfg)
287 {
288  // Merge header data.
289  side_ = cfg["side"].to_int(side_);
290  committed_actions_ = committed_actions_ || cfg["committed"].to_bool();
291 
292  // Build the undo stack.
293  for (const config & child : cfg.child_range("undo")) {
294  try {
295  undo_action_base * action = create_action(child);
296  if ( action ) {
297  undos_.push_back(action);
298  }
299  } catch (bad_lexical_cast &) {
300  ERR_NG << "Error when parsing undo list from config: bad lexical cast." << std::endl;
301  ERR_NG << "config was: " << child.debug() << std::endl;
302  ERR_NG << "Skipping this undo action..." << std::endl;
303  } catch (config::error& e) {
304  ERR_NG << "Error when parsing undo list from config: " << e.what() << std::endl;
305  ERR_NG << "config was: " << child.debug() << std::endl;
306  ERR_NG << "Skipping this undo action..." << std::endl;
307  }
308  }
309 
310  // Build the redo stack.
311  for (const config & child : cfg.child_range("redo")) {
312  try {
313  redos_.push_back(new config(child));
314  } catch (bad_lexical_cast &) {
315  ERR_NG << "Error when parsing redo list from config: bad lexical cast." << std::endl;
316  ERR_NG << "config was: " << child.debug() << std::endl;
317  ERR_NG << "Skipping this redo action..." << std::endl;
318  } catch (config::error& e) {
319  ERR_NG << "Error when parsing redo list from config: " << e.what() << std::endl;
320  ERR_NG << "config was: " << child.debug() << std::endl;
321  ERR_NG << "Skipping this redo action..." << std::endl;
322  }
323  }
324 }
325 
326 
327 /**
328  * Write the undo_list into the provided config.
329  */
330 void undo_list::write(config & cfg) const
331 {
332  cfg["side"] = side_;
333  cfg["committed"] = committed_actions_;
334 
335  for ( action_list::const_iterator it = undos_.begin(); it != undos_.end(); ++it )
336  it->write(cfg.add_child("undo"));
337 
338  for ( redos_list::const_iterator it = redos_.begin(); it != redos_.end(); ++it )
339  cfg.add_child("redo") = *it;
340 }
341 
342 
343 /**
344  * Undoes the top action on the undo stack.
345  */
347 {
348  if ( undos_.empty() )
349  return;
350 
351  const events::command_disabler disable_commands;
352 
354 
355  // Get the action to undo. (This will be placed on the redo stack, but
356  // only if the undo is successful.)
357  action_list::auto_type action = undos_.pop_back();
358  if (undo_action* undoable_action = dynamic_cast<undo_action*>(action.ptr()))
359  {
360  int last_unit_id = resources::gameboard->unit_id_manager().get_save_id();
361  if ( !undoable_action->undo(side_) ) {
362  return;
363  }
364  if(last_unit_id - undoable_action->unit_id_diff < 0) {
365  ERR_NG << "Next unit id is below 0 after undoing" << std::endl;
366  }
367  resources::gameboard->unit_id_manager().set_save_id(last_unit_id - undoable_action->unit_id_diff);
368 
369  // Bookkeeping.
370  redos_.push_back(new config());
372 
373  resources::whiteboard->on_gamestate_change();
374 
375  // Screen updates.
376  gui.invalidate_unit();
378  gui.redraw_minimap();
379  }
380  else
381  {
382  //ignore this action, and undo the previous one.
383  config replay_data;
384  resources::recorder->undo_cut(replay_data);
385  undo();
386  resources::recorder->redo(replay_data);
387  undos_.push_back(action.release());
388  }
389 }
390 
391 
392 
393 /**
394  * Redoes the top action on the redo stack.
395  */
397 {
398  if ( redos_.empty() )
399  return;
400 
401  const events::command_disabler disable_commands;
402 
404 
405  // Get the action to redo. (This will be placed on the undo stack, but
406  // only if the redo is successful.)
407  redos_list::auto_type action = redos_.pop_back();
408 
409  const config& command_wml = action->child("command");
410  std::string commandname = command_wml.all_children_range().front().key;
411  const config& data = command_wml.all_children_range().front().cfg;
412 
413  resources::recorder->redo(const_cast<const config&>(*action));
414 
415 
416  // synced_context::run readds the undo command with the normal undo_lis::add function whihc clears the
417  // redo stack which makes redoign of more than one move impossible. to work around that we save redo stack here and set it later.
418  redos_list temp;
419  temp.swap(redos_);
420  synced_context::run(commandname, data, /*use_undo*/ true, /*show*/ true);
421  temp.swap(redos_);
422 
423  // Screen updates.
424  gui.invalidate_unit();
426  gui.redraw_minimap();
427 }
428 
429 
430 
431 
432 
433 /**
434  * Applies the pending fog/shroud changes from the undo stack.
435  * Does nothing if the the current side does not use fog or shroud.
436  * @returns true if shroud or fog was cleared.
437  */
439 {
442  // No need to do clearing if fog/shroud has been kept up-to-date.
443  if ( tm.auto_shroud_updates() || !tm.fog_or_shroud() ) {
444  return false;
445  }
446  shroud_clearer clearer;
447  bool cleared_shroud = false;
448  const size_t list_size = undos_.size();
449 
450 
451  // Loop through the list of undo_actions.
452  for( size_t i = 0; i != list_size; ++i ) {
453  if (const shroud_clearing_action* action = dynamic_cast<const shroud_clearing_action*>(&undos_[i])) {
454  LOG_NG << "Turning an undo...\n";
455 
456  // Clear the hexes this unit can see from each hex occupied during
457  // the action.
458  std::vector<map_location>::const_iterator step;
459  for (step = action->route.begin(); step != action->route.end(); ++step) {
460  // Clear the shroud, collecting new sighted events.
461  // (This can be made gradual by changing "true" to "false".)
462  if ( clearer.clear_unit(*step, tm, action->view_info, true) ) {
463  cleared_shroud = true;
464  }
465  }
466  }
467  }
468 
469 
470  if (!cleared_shroud) {
471  return false;
472  }
473  // If we clear fog or shroud outside a synced context we get OOS
474  // Note that it can happen that we call this function from ouside a synced context
475  // when we reload a game and want to prevent undoing. But in this case this is
476  // preceeded by a manual update_shroud call so that cleared_shroud is false.
477  assert(synced_context::is_synced());
478 
479  // The entire stack needs to be cleared in order to preserve replays.
480  // (The events that fired might depend on current unit positions.)
481  // (Also the events that did not fire might depend on unit positions (they whould have fired if the unit would have standed on different positions, for example this can happen if they have a [have_unit] in [filter_condition]))
482 
483  // Update the display before pumping events.
484  clearer.invalidate_after_clear();
485 
486  // Fire sighted events
487  if ( std::get<0>(clearer.fire_events() )) {
488  // Fix up the display in case WML changed stuff.
490  disp.invalidate_unit();
491  }
492 
493  return true;
494 }
495 
496 
497 }//namespace actions
498 
void clear()
Clears the stack of undoable (and redoable) actions.
Definition: undo.cpp:218
boost::intrusive_ptr< const unit > unit_const_ptr
Definition: ptr.hpp:30
base class for classes that clear srhoud (move/recruit/recall)
bool committed_actions_
Tracks if actions have been cleared from the stack since the turn began.
Definition: undo.hpp:114
static DIRECTION parse_direction(const std::string &str)
Definition: location.cpp:64
config & child(config_key_type key, int n=0)
Returns the nth child with the given key, or a reference to an invalid config if there is none...
Definition: config.cpp:400
const char * what() const NOEXCEPT
Definition: exceptions.hpp:37
std::vector< char_t > string
void invalidate_game_status()
Function to invalidate the game status displayed on the sidebar.
Definition: display.hpp:287
bool clear_unit(const map_location &view_loc, team &view_team, size_t viewer_id, int sight_range, bool slowed, const movetype::terrain_costs &costs, const map_location &real_loc, const std::set< map_location > *known_units=nullptr, size_t *enemy_count=nullptr, size_t *friend_count=nullptr, move_unit_spectator *spectator=nullptr, bool instant=true)
Clears shroud (and fog) around the provided location for view_team based on sight_range, costs, and slowed.
Definition: vision.cpp:330
Various functions implementing vision (through fog of war and shroud).
size_t get_save_id() const
Used for saving id to savegame.
Definition: id.cpp:41
static bool run(const std::string &commandname, const config &data, bool use_undo=true, bool show=true, synced_command::error_handler_function error_handler=default_error_function)
Sets the context to 'synced', initialises random context, and calls the given function.
action_list undos_
Definition: undo.hpp:108
game_display * screen
Definition: resources.cpp:27
child_itors child_range(config_key_type key)
Definition: config.cpp:343
General purpose widgets.
void invalidate_unit()
Function to invalidate that unit status displayed on the sidebar.
void undo()
Undoes the top action on the undo stack.
Definition: undo.cpp:346
void add(undo_action_base *action)
Adds an action to the undo stack.
Definition: undo.hpp:102
int side_
Tracks the current side.
Definition: undo.hpp:112
unit_type_data unit_types
Definition: types.cpp:1455
void new_side_turn(int side)
Performs some initializations and error checks when starting a new side-turn.
Definition: undo.cpp:260
void set_save_id(size_t)
Definition: id.cpp:46
Replay control code.
bool apply_shroud_changes() const
Applies the pending fog/shroud changes from the undo stack.
Definition: undo.cpp:438
-file sdl_utils.hpp
void redraw_minimap()
Schedule the minimap to be redrawn.
Definition: display.hpp:616
A single unit type that the player may recruit.
Definition: types.hpp:43
Records information to be able to undo an action.
Definition: undo_action.hpp:38
void read(const config &cfg)
Read the undo_list from the provided config.
Definition: undo.cpp:286
undo_list(const undo_list &)=delete
This class stores all the data for a single 'side' (in game nomenclature).
Definition: team.hpp:44
bool fog_or_shroud() const
Definition: team.hpp:314
team & get_team(int i)
Definition: game_board.hpp:94
game_events::pump_result_t fire_events()
Fires the sighted events that were earlier recorded by fog/shroud clearing.
Definition: vision.cpp:546
void redo(const config &dst, bool set_to_end=false)
Definition: replay.cpp:405
#define ERR_NG
Definition: undo.cpp:59
bool auto_shroud_updates() const
Definition: team.hpp:332
boost::ptr_vector< config > redos_list
Definition: undo.hpp:38
game_board * gameboard
Definition: resources.cpp:20
static lg::log_domain log_engine("engine")
replay * recorder
Definition: resources.cpp:29
void commit_vision()
Updates fog/shroud based on the undo stack, then updates stack as needed.
Definition: undo.cpp:241
Encapsulates the map of the game.
Definition: location.hpp:40
Various functions related to moving units.
void add_recruit(const unit_const_ptr u, const map_location &loc, const map_location &from, int orig_village_owner, bool time_bonus)
Adds a recruit to the undo stack.
Definition: undo.cpp:192
std::shared_ptr< wb::manager > whiteboard
Definition: resources.cpp:35
Various functions related to the creation of units (recruits, recalls, and placed units)...
#define LOG_NG
Definition: undo.cpp:60
void add_dismissal(const unit_const_ptr u)
Adds a dismissal to the undo stack.
Definition: undo.cpp:163
static undo_action_base * create_action(const config &cfg)
Creates an undo_action based on a config.
Definition: undo.cpp:71
entry for player actions that do not need any special code to be performed when undoing such as right...
Definition: undo_action.hpp:89
void redo()
Redoes the top action on the redo stack.
Definition: undo.cpp:396
redos_list redos_
Definition: undo.hpp:109
void add_recall(const unit_const_ptr u, const map_location &loc, const map_location &from, int orig_village_owner, bool time_bonus)
Adds a recall to the undo stack.
Definition: undo.cpp:183
const config & child_or_empty(config_key_type key) const
Returns the first child with the given key, or an empty config if there is none.
Definition: config.cpp:433
size_t i
Definition: function.cpp:933
void add_move(const unit_const_ptr u, const std::vector< map_location >::const_iterator &begin, const std::vector< map_location >::const_iterator &end, int start_moves, int timebonus=0, int village_owner=-1, const map_location::DIRECTION dir=map_location::NDIRECTIONS)
Adds a move to the undo stack.
Definition: undo.cpp:171
DIRECTION
Valid directions which can be moved in our hexagonal world.
Definition: location.hpp:42
config & add_child(config_key_type key)
Definition: config.cpp:456
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:754
void add_update_shroud()
Adds a shroud update to the undo stack.
Definition: undo.cpp:203
n_unit::id_manager & unit_id_manager()
Definition: game_board.hpp:86
Various functions that implement the undoing (and redoing) of in-game commands.
void undo_cut(config &dst)
Definition: replay.cpp:485
Standard logging facilities (interface).
void add_dummy()
Adds an auto-shroud toggle to the undo stack.
Definition: undo.cpp:152
Class to encapsulate fog/shroud clearing and the resultant sighted events.
Definition: vision.hpp:57
const unit_type * find(const std::string &key, unit_type::BUILD_STATUS status=unit_type::FULL) const
Finds a unit_type by its id() and makes sure it is built to the specified level.
Definition: types.cpp:1273
#define e
void add_auto_shroud(bool turned_on)
Adds an auto-shroud toggle to the undo stack.
Definition: undo.cpp:144
void invalidate_after_clear()
The invalidations that should occur after invoking clear_unit().
Definition: vision.cpp:581
A config object defines a single node in a WML file, with access to child nodes.
Definition: config.hpp:93
Thrown when a lexical_cast fails.
static bool is_synced()
const_all_children_itors all_children_range() const
In-order iteration over all children.
Definition: config.cpp:848
actions that are undoable (this does not include update_shroud and auto_shroud)
Definition: undo_action.hpp:60
~undo_list()
Destructor.
Definition: undo.cpp:134
void write(config &cfg) const
Write the undo_list into the provided config.
Definition: undo.cpp:330