Merge pull request #936 from gfgtdf/no_on_redo

use the original action codepath when redoing
This commit is contained in:
gfgtdf 2017-03-10 12:34:49 +01:00 committed by GitHub
commit 33c2e6aa67
15 changed files with 17 additions and 252 deletions

View file

@ -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();

View file

@ -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;

View file

@ -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);
}

View file

@ -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;
}
};
}

View file

@ -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 &current_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;
}
}
}

View file

@ -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);
};
}

View file

@ -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;
}
}
}

View file

@ -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);
};
}

View file

@ -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 &current_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;
}
}
}

View file

@ -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);
};
}

View file

@ -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 &current_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;
}
}
}

View file

@ -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);
};
}

View file

@ -966,13 +966,8 @@ WML_HANDLER_FUNCTION(on_undo, event_info, cfg)
}
}
WML_HANDLER_FUNCTION(on_redo, event_info, cfg)
WML_HANDLER_FUNCTION(on_redo, , )
{
if(cfg["delayed_variable_substitution"].to_bool(false)) {
synced_context::add_redo_commands(cfg.get_config(), event_info);
} else {
synced_context::add_redo_commands(cfg.get_parsed_config(), event_info);
}
}
} // end namespace game_events

View file

@ -51,7 +51,6 @@ static lg::log_domain log_replay("replay");
synced_context::synced_state synced_context::state_ = synced_context::UNSYNCED;
int synced_context::last_unit_id_ = 0;
synced_context::event_list synced_context::undo_commands_;
synced_context::event_list synced_context::redo_commands_;
bool synced_context::is_simultaneously_ = false;
bool synced_context::run(const std::string& commandname, const config& data, bool use_undo, bool show, synced_command::error_handler_function error_handler)
@ -371,21 +370,11 @@ void synced_context::add_undo_commands(const config& commands, const game_events
undo_commands_.emplace_front(commands, ctx);
}
void synced_context::add_redo_commands(const config& commands, const game_events::queued_event& ctx)
{
redo_commands_.emplace_front(commands, ctx);
}
void synced_context::reset_undo_commands()
{
undo_commands_.clear();
}
void synced_context::reset_redo_commands()
{
redo_commands_.clear();
}
set_scontext_synced_base::set_scontext_synced_base()
: new_rng_(synced_context::get_rng_for_action())
, old_rng_(random_new::generator)
@ -397,7 +386,6 @@ set_scontext_synced_base::set_scontext_synced_base()
synced_context::reset_is_simultaneously();
synced_context::set_last_unit_id(resources::gameboard->unit_id_manager().get_save_id());
synced_context::reset_undo_commands();
synced_context::reset_redo_commands();
old_rng_ = random_new::generator;
random_new::generator = new_rng_.get();
}

View file

@ -148,11 +148,8 @@ public:
typedef std::deque<std::pair<config,game_events::queued_event>> event_list;
static event_list& get_undo_commands() { return undo_commands_; }
static event_list& get_redo_commands() { return redo_commands_; }
static void add_undo_commands(const config& commands, const game_events::queued_event& ctx);
static void add_redo_commands(const config& commands, const game_events::queued_event& ctx);
static void reset_undo_commands();
static void reset_redo_commands();
private:
/*
weather we are in a synced move, in a user_choice, or none of them
@ -175,10 +172,6 @@ private:
Actions wml to be executed when the current actio is undone.
*/
static event_list undo_commands_;
/**
Actions wml to be executed when the current actio is redone.
*/
static event_list redo_commands_;
};