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