use the original action codepath when redoding
this removes all those redo() functions for each undo actions. Instead, the game now just uses the replaywml that was taken from the replay when undoing the action to redo it by calling the usual synced:context::run function, just like when that action was executed for the first time. The main advantage is that [on_redo] is no longer nesasary since the game will ow just fire the action related wml events again when redoing the actions. See also http://gna.org/bugs/?25472
This commit is contained in:
parent
6140f83eb1
commit
532e4c7de6
12 changed files with 16 additions and 227 deletions
|
@ -310,15 +310,7 @@ void undo_list::read(const config & cfg)
|
|||
// Build the redo stack.
|
||||
for (const config & child : cfg.child_range("redo")) {
|
||||
try {
|
||||
undo_action_base * action = create_action(child);
|
||||
if ( undo_action* undoable_action = dynamic_cast<undo_action*>(action)) {
|
||||
redos_.push_back(undoable_action);
|
||||
} else {
|
||||
delete action;
|
||||
ERR_NG << "Error: redo contained action that is not undoable" << std::endl;
|
||||
ERR_NG << "config was: " << child.debug() << std::endl;
|
||||
ERR_NG << "Skipping this redo action..." << std::endl;
|
||||
}
|
||||
redos_.push_back(new config(child));
|
||||
} catch (bad_lexical_cast &) {
|
||||
ERR_NG << "Error when parsing redo list from config: bad lexical cast." << std::endl;
|
||||
ERR_NG << "config was: " << child.debug() << std::endl;
|
||||
|
@ -344,7 +336,7 @@ void undo_list::write(config & cfg) const
|
|||
it->write(cfg.add_child("undo"));
|
||||
|
||||
for ( redos_list::const_iterator it = redos_.begin(); it != redos_.end(); ++it )
|
||||
it->write(cfg.add_child("redo"));
|
||||
cfg.add_child("redo") = *it;
|
||||
}
|
||||
|
||||
|
||||
|
@ -375,9 +367,9 @@ void undo_list::undo()
|
|||
resources::gameboard->unit_id_manager().set_save_id(last_unit_id - undoable_action->unit_id_diff);
|
||||
|
||||
// Bookkeeping.
|
||||
resources::recorder->undo_cut(undoable_action->replay_data);
|
||||
//we can do a static cast here because we alreeady checked with the dynamic cast above.
|
||||
redos_.push_back(static_cast<undo_action*>(action.release()));
|
||||
redos_.push_back(new config());
|
||||
resources::recorder->undo_cut(redos_.back());
|
||||
|
||||
resources::whiteboard->on_gamestate_change();
|
||||
|
||||
// Screen updates.
|
||||
|
@ -415,39 +407,20 @@ void undo_list::redo()
|
|||
// only if the redo is successful.)
|
||||
redos_list::auto_type action = redos_.pop_back();
|
||||
|
||||
if (preferences::get("no_on_redo", false)) {
|
||||
// And alterntive redo codepath which just call the original actions, using this over the othr one has some advantages:
|
||||
// 1) wml no longer needs [on_redo] since the engine will execute the original moveto etc. events
|
||||
// 2) It simplifies the c++ code since all undo_actions::redo functions can be removed.
|
||||
|
||||
const config& command_wml = action->replay_data.child("command");
|
||||
std::string commandname = command_wml.all_children_range().front().key;
|
||||
const config& data = command_wml.all_children_range().front().cfg;
|
||||
const config& command_wml = action->child("command");
|
||||
std::string commandname = command_wml.all_children_range().front().key;
|
||||
const config& data = command_wml.all_children_range().front().cfg;
|
||||
|
||||
resources::recorder->redo(const_cast<const config&>(action->replay_data));
|
||||
resources::recorder->redo(const_cast<const config&>(*action));
|
||||
|
||||
|
||||
// synced_context::run readds the undo command with the normal undo_lis::add function whihc clears the
|
||||
// redo stack which makes redoign of more than one move impossible. to work around that we save redo stack here and set it later.
|
||||
redos_list temp;
|
||||
temp.swap(redos_);
|
||||
synced_context::run(commandname, data, /*use_undo*/ true, /*show*/ true);
|
||||
temp.swap(redos_);
|
||||
}
|
||||
else {
|
||||
int last_unit_id = resources::gameboard->unit_id_manager().get_save_id();
|
||||
if (!action->redo(side_)) {
|
||||
return;
|
||||
}
|
||||
if (last_unit_id + action->unit_id_diff < static_cast<int>(resources::gameboard->unit_id_manager().get_save_id())) {
|
||||
ERR_NG << "Too many units were generated during redoing." << std::endl;
|
||||
}
|
||||
resources::gameboard->unit_id_manager().set_save_id(last_unit_id + action->unit_id_diff);
|
||||
// synced_context::run readds the undo command with the normal undo_lis::add function whihc clears the
|
||||
// redo stack which makes redoign of more than one move impossible. to work around that we save redo stack here and set it later.
|
||||
redos_list temp;
|
||||
temp.swap(redos_);
|
||||
synced_context::run(commandname, data, /*use_undo*/ true, /*show*/ true);
|
||||
temp.swap(redos_);
|
||||
|
||||
// Bookkeeping.
|
||||
undos_.push_back(action.release());
|
||||
resources::whiteboard->on_gamestate_change();
|
||||
}
|
||||
// Screen updates.
|
||||
gui.invalidate_unit();
|
||||
gui.invalidate_game_status();
|
||||
|
|
|
@ -36,7 +36,7 @@ namespace actions {
|
|||
class undo_list {
|
||||
|
||||
typedef boost::ptr_vector<undo_action_base> action_list;
|
||||
typedef boost::ptr_vector<undo_action> redos_list;
|
||||
typedef boost::ptr_vector<config> redos_list;
|
||||
|
||||
public:
|
||||
undo_list(const undo_list&) = delete;
|
||||
|
|
|
@ -49,27 +49,21 @@ undo_event::undo_event(const config& first, const config& second, const config&
|
|||
|
||||
undo_action::undo_action()
|
||||
: undo_action_base()
|
||||
, replay_data()
|
||||
, unit_id_diff(synced_context::get_unit_id_diff())
|
||||
{
|
||||
auto& undo = synced_context::get_undo_commands();
|
||||
auto& redo = synced_context::get_redo_commands();
|
||||
auto command_transformer = [](const std::pair<config, game_events::queued_event>& p) {
|
||||
return undo_event(p.first, p.second);
|
||||
};
|
||||
std::transform(undo.begin(), undo.end(), std::back_inserter(umc_commands_undo), command_transformer);
|
||||
std::transform(redo.begin(), redo.end(), std::back_inserter(umc_commands_redo), command_transformer);
|
||||
undo.clear();
|
||||
redo.clear();
|
||||
}
|
||||
|
||||
undo_action::undo_action(const config& cfg)
|
||||
: undo_action_base()
|
||||
, replay_data(cfg.child_or_empty("replay_data"))
|
||||
, unit_id_diff(cfg["unit_id_diff"])
|
||||
{
|
||||
read_event_vector(umc_commands_undo, cfg, "undo_actions");
|
||||
read_event_vector(umc_commands_redo, cfg, "redo_actions");
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
@ -120,22 +114,11 @@ void undo_action::execute_undo_umc_wml()
|
|||
}
|
||||
}
|
||||
|
||||
void undo_action::execute_redo_umc_wml()
|
||||
{
|
||||
assert(resources::lua_kernel);
|
||||
assert(resources::gamedata);
|
||||
for(const undo_event& e : umc_commands_redo)
|
||||
{
|
||||
execute_event(e, "redo");
|
||||
}
|
||||
}
|
||||
|
||||
void undo_action::write(config & cfg) const
|
||||
{
|
||||
cfg.add_child("replay_data", replay_data);
|
||||
cfg["unit_id_diff"] = unit_id_diff;
|
||||
write_event_vector(umc_commands_undo, cfg, "undo_actions");
|
||||
write_event_vector(umc_commands_redo, cfg, "redo_actions");
|
||||
undo_action_base::write(cfg);
|
||||
}
|
||||
|
||||
|
|
|
@ -60,22 +60,13 @@ namespace actions {
|
|||
/// Undoes this action.
|
||||
/// @return true on success; false on an error.
|
||||
virtual bool undo(int side) = 0;
|
||||
/// Redoes this action.
|
||||
/// @return true on success; false on an error.
|
||||
virtual bool redo(int side) = 0;
|
||||
/// the replay data to do this action, this is only !empty() when this action is on the redo stack
|
||||
/// we need this because we don't recalculate the redos like they would be in real game,
|
||||
/// but even undoable commands can have "dependent" (= user_input) commands, which we save here.
|
||||
config replay_data;
|
||||
/// the difference in the unit ids
|
||||
/// TODO: does it really make sense to allow undoing if the unit id counter has changed?
|
||||
int unit_id_diff;
|
||||
/// actions wml (specified by wml) that should be executed when undoing this command.
|
||||
typedef std::vector<undo_event> event_vector;
|
||||
event_vector umc_commands_undo;
|
||||
event_vector umc_commands_redo;
|
||||
void execute_undo_umc_wml();
|
||||
void execute_redo_umc_wml();
|
||||
|
||||
static void read_event_vector(event_vector& vec, const config& cfg, const std::string& tag);
|
||||
static void write_event_vector(const event_vector& vec, config& cfg, const std::string& tag);
|
||||
|
@ -99,12 +90,6 @@ namespace actions {
|
|||
{
|
||||
return true;
|
||||
}
|
||||
/// Redoes this action.
|
||||
virtual bool redo(int)
|
||||
{
|
||||
replay_data.clear();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -31,20 +31,5 @@ bool dismiss_action::undo(int side)
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redoes this action.
|
||||
* @return true on success; false on an error.
|
||||
*/
|
||||
bool dismiss_action::redo(int side)
|
||||
{
|
||||
team ¤t_team = resources::gameboard->teams()[side-1];
|
||||
|
||||
resources::recorder->redo(replay_data);
|
||||
replay_data.clear();
|
||||
current_team.recall_list().erase_if_matches_id(dismissed_unit->id());
|
||||
execute_redo_umc_wml();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -32,8 +32,6 @@ struct dismiss_action : undo_action
|
|||
|
||||
/// Undoes this action.
|
||||
virtual bool undo(int side);
|
||||
/// Redoes this action.
|
||||
virtual bool redo(int side);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -79,46 +79,6 @@ bool move_action::undo(int)
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redoes this action.
|
||||
* @return true on success; false on an error.
|
||||
*/
|
||||
bool move_action::redo(int)
|
||||
{
|
||||
game_display & gui = *resources::screen;
|
||||
unit_map & units = resources::gameboard->units();
|
||||
|
||||
// Check units.
|
||||
unit_map::iterator u = units.find(route.front());
|
||||
if ( u == units.end() ) {
|
||||
ERR_NG << "Illegal movement 'redo'." << std::endl;
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Adjust starting moves.
|
||||
const int saved_moves = starting_moves;
|
||||
starting_moves = u->movement_left();
|
||||
|
||||
// Move the unit.
|
||||
unit_display::move_unit(route, u.get_shared_ptr());
|
||||
units.move(u->get_location(), route.back());
|
||||
u = units.find(route.back());
|
||||
unit::clear_status_caches();
|
||||
|
||||
// Set the unit's state.
|
||||
u->set_goto(goto_hex);
|
||||
u->set_movement(saved_moves, true);
|
||||
u->anim_comp().set_standing();
|
||||
|
||||
this->take_village();
|
||||
|
||||
gui.invalidate_unit_after_move(route.front(), route.back());
|
||||
resources::recorder->redo(replay_data, true);
|
||||
replay_data.clear();
|
||||
execute_redo_umc_wml();
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -46,8 +46,6 @@ struct move_action : undo_action, shroud_clearing_action
|
|||
|
||||
/// Undoes this action.
|
||||
virtual bool undo(int side);
|
||||
/// Redoes this action.
|
||||
virtual bool redo(int side);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -72,47 +72,5 @@ bool recall_action::undo(int side)
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redoes this action.
|
||||
* @return true on success; false on an error.
|
||||
*/
|
||||
bool recall_action::redo(int side)
|
||||
{
|
||||
game_display & gui = *resources::screen;
|
||||
team ¤t_team = resources::gameboard->teams()[side-1];
|
||||
|
||||
map_location loc = route.front();
|
||||
map_location from = recall_from;
|
||||
|
||||
unit_ptr un = current_team.recall_list().find_if_matches_id(id);
|
||||
if ( !un ) {
|
||||
ERR_NG << "Trying to redo a recall of '" << id
|
||||
<< "', but that unit is not in the recall list.";
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string &msg = find_recall_location(side, loc, from, *un);
|
||||
if ( msg.empty() ) {
|
||||
resources::recorder->redo(replay_data);
|
||||
replay_data.clear();
|
||||
set_scontext_synced sync;
|
||||
recall_unit(id, current_team, loc, from, true, false);
|
||||
|
||||
// Quick error check. (Abuse of [allow_undo]?)
|
||||
if ( loc != route.front() ) {
|
||||
ERR_NG << "When redoing a recall at " << route.front()
|
||||
<< ", the location was moved to " << loc << ".\n";
|
||||
// Not really fatal, I suppose. Just update the action so
|
||||
// undoing this works.
|
||||
route.front() = loc;
|
||||
}
|
||||
sync.do_final_checkup();
|
||||
} else {
|
||||
gui2::show_transient_message(gui.video(), "", msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,8 +39,6 @@ struct recall_action : undo_action, shroud_clearing_action
|
|||
|
||||
/// Undoes this action.
|
||||
virtual bool undo(int side);
|
||||
/// Redoes this action.
|
||||
virtual bool redo(int side);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -65,52 +65,5 @@ bool recruit_action::undo(int side)
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Redoes this action.
|
||||
* @return true on success; false on an error.
|
||||
*/
|
||||
bool recruit_action::redo(int side)
|
||||
{
|
||||
game_display & gui = *resources::screen;
|
||||
team ¤t_team = resources::gameboard->teams()[side-1];
|
||||
|
||||
map_location loc = route.front();
|
||||
map_location from = recruit_from;
|
||||
const std::string & name = u_type.base_id();
|
||||
|
||||
//search for the unit to be recruited in recruits
|
||||
if ( !util::contains(get_recruits(side, loc), name) ) {
|
||||
ERR_NG << "Trying to redo a recruit for side " << side
|
||||
<< ", which does not recruit type \"" << name << "\"\n";
|
||||
assert(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
current_team.last_recruit(name);
|
||||
const std::string &msg = find_recruit_location(side, loc, from, name);
|
||||
if ( msg.empty() ) {
|
||||
//MP_COUNTDOWN: restore recruitment bonus
|
||||
current_team.set_action_bonus_count(1 + current_team.action_bonus_count());
|
||||
resources::recorder->redo(replay_data);
|
||||
replay_data.clear();
|
||||
set_scontext_synced sync;
|
||||
recruit_unit(u_type, side, loc, from, true, false);
|
||||
|
||||
// Quick error check. (Abuse of [allow_undo]?)
|
||||
if ( loc != route.front() ) {
|
||||
ERR_NG << "When redoing a recruit at " << route.front()
|
||||
<< ", the location was moved to " << loc << ".\n";
|
||||
// Not really fatal, I suppose. Just update the action so
|
||||
// undoing this works.
|
||||
route.front() = loc;
|
||||
}
|
||||
sync.do_final_checkup();
|
||||
} else {
|
||||
gui2::show_transient_message(gui.video(), "", msg);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,8 +38,6 @@ struct recruit_action : undo_action, shroud_clearing_action
|
|||
|
||||
/// Undoes this action.
|
||||
virtual bool undo(int side);
|
||||
/// Redoes this action.
|
||||
virtual bool redo(int side);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue