refactor undo_action class
First we split the undo_ation into subclasses (undo_action_base, undo_action and shroud_clearing_action) so that undo actions that don't clear shroud don't contain the route and view_info data. Also the non undoable actions now don't need to implement undo() and redo() Second we move the undo action classes to different files.
This commit is contained in:
parent
cd756b74db
commit
138f7c92c4
18 changed files with 909 additions and 777 deletions
|
@ -701,7 +701,14 @@ set(wesnoth-main_SRC
|
||||||
actions/create.cpp
|
actions/create.cpp
|
||||||
actions/heal.cpp
|
actions/heal.cpp
|
||||||
actions/move.cpp
|
actions/move.cpp
|
||||||
|
actions/shroud_clearing_action.cpp
|
||||||
actions/undo.cpp
|
actions/undo.cpp
|
||||||
|
actions/undo_action.cpp
|
||||||
|
actions/undo_dismiss_action.cpp
|
||||||
|
actions/undo_move_action.cpp
|
||||||
|
actions/undo_recall_action.cpp
|
||||||
|
actions/undo_recruit_action.cpp
|
||||||
|
actions/undo_update_shroud_action.cpp
|
||||||
actions/vision.cpp
|
actions/vision.cpp
|
||||||
addon/client.cpp
|
addon/client.cpp
|
||||||
addon/info.cpp
|
addon/info.cpp
|
||||||
|
|
|
@ -189,7 +189,14 @@ wesnoth_sources = Split("""
|
||||||
actions/create.cpp
|
actions/create.cpp
|
||||||
actions/heal.cpp
|
actions/heal.cpp
|
||||||
actions/move.cpp
|
actions/move.cpp
|
||||||
|
actions/shroud_clearing_action.cpp
|
||||||
actions/undo.cpp
|
actions/undo.cpp
|
||||||
|
actions/undo_action.cpp
|
||||||
|
actions/undo_dismiss_action.cpp
|
||||||
|
actions/undo_move_action.cpp
|
||||||
|
actions/undo_recall_action.cpp
|
||||||
|
actions/undo_recruit_action.cpp
|
||||||
|
actions/undo_update_shroud_action.cpp
|
||||||
actions/vision.cpp
|
actions/vision.cpp
|
||||||
addon/client.cpp
|
addon/client.cpp
|
||||||
addon/info.cpp
|
addon/info.cpp
|
||||||
|
|
1
src/actions/shroud_clearing_action.cpp
Normal file
1
src/actions/shroud_clearing_action.cpp
Normal file
|
@ -0,0 +1 @@
|
||||||
|
#include "shroud_clearing_action.hpp"
|
53
src/actions/shroud_clearing_action.hpp
Normal file
53
src/actions/shroud_clearing_action.hpp
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "vision.hpp"
|
||||||
|
#include "../map_location.hpp"
|
||||||
|
#include "../unit_ptr.hpp"
|
||||||
|
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <boost/ptr_container/ptr_vector.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
namespace actions
|
||||||
|
{
|
||||||
|
/// base class for classes that clear srhoud (move/recruit/recall)
|
||||||
|
struct shroud_clearing_action
|
||||||
|
{
|
||||||
|
|
||||||
|
shroud_clearing_action(const config& cfg)
|
||||||
|
: route()
|
||||||
|
, view_info(cfg.child_or_empty("unit"))
|
||||||
|
{
|
||||||
|
read_locations(cfg, route);
|
||||||
|
}
|
||||||
|
|
||||||
|
shroud_clearing_action(const unit_const_ptr u, const map_location& loc)
|
||||||
|
: route(1, loc)
|
||||||
|
, view_info(*u)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef std::vector<map_location> t_route;
|
||||||
|
|
||||||
|
shroud_clearing_action(const unit_const_ptr u, const t_route::const_iterator& begin, const t_route::const_iterator& end)
|
||||||
|
: route(begin, end)
|
||||||
|
, view_info(*u)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The hexes occupied by the affected unit during this action.
|
||||||
|
/// For recruits and recalls this only contains one hex.
|
||||||
|
t_route route;
|
||||||
|
/// A record of the affected unit's ability to see.
|
||||||
|
clearer_info view_info;
|
||||||
|
|
||||||
|
void write(config & cfg) const
|
||||||
|
{
|
||||||
|
write_locations(route, cfg);
|
||||||
|
view_info.write(cfg.add_child("unit"));
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~shroud_clearing_action() {}
|
||||||
|
};
|
||||||
|
}
|
|
@ -47,6 +47,12 @@
|
||||||
#include "create.hpp" // for find_recall_location, etc
|
#include "create.hpp" // for find_recall_location, etc
|
||||||
#include "move.hpp" // for get_village
|
#include "move.hpp" // for get_village
|
||||||
#include "vision.hpp" // for clearer_info, etc
|
#include "vision.hpp" // for clearer_info, etc
|
||||||
|
#include "shroud_clearing_action.hpp"
|
||||||
|
#include "undo_dismiss_action.hpp"
|
||||||
|
#include "undo_move_action.hpp"
|
||||||
|
#include "undo_recall_action.hpp"
|
||||||
|
#include "undo_recruit_action.hpp"
|
||||||
|
#include "undo_update_shroud_action.hpp"
|
||||||
|
|
||||||
#include <algorithm> // for reverse
|
#include <algorithm> // for reverse
|
||||||
#include <boost/foreach.hpp> // for auto_any_base, etc
|
#include <boost/foreach.hpp> // for auto_any_base, etc
|
||||||
|
@ -68,214 +74,21 @@ static lg::log_domain log_engine("engine");
|
||||||
namespace actions {
|
namespace actions {
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Virtual destructor
|
|
||||||
*/
|
|
||||||
undo_list::undo_action::~undo_action()
|
|
||||||
{
|
|
||||||
delete view_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
struct undo_list::dismiss_action : undo_list::undo_action {
|
|
||||||
unit_ptr dismissed_unit;
|
|
||||||
|
|
||||||
|
|
||||||
explicit dismiss_action(const unit_const_ptr dismissed) : undo_action(),
|
|
||||||
dismissed_unit(new unit(*dismissed))
|
|
||||||
{
|
|
||||||
this->unit_id_diff = synced_context::get_unit_id_diff();
|
|
||||||
}
|
|
||||||
explicit dismiss_action(const config & unit_cfg) : undo_action(),
|
|
||||||
dismissed_unit(new unit(unit_cfg))
|
|
||||||
{}
|
|
||||||
virtual ~dismiss_action();
|
|
||||||
|
|
||||||
/// Writes this into the provided config.
|
|
||||||
virtual void write(config & cfg) const;
|
|
||||||
|
|
||||||
/// Undoes this action.
|
|
||||||
virtual bool undo(int side, undo_list & undos);
|
|
||||||
/// Redoes this action.
|
|
||||||
virtual bool redo(int side);
|
|
||||||
};
|
|
||||||
undo_list::dismiss_action::~dismiss_action()
|
|
||||||
{}
|
|
||||||
|
|
||||||
struct undo_list::move_action : undo_list::undo_action {
|
|
||||||
int starting_moves;
|
|
||||||
int original_village_owner;
|
|
||||||
int countdown_time_bonus;
|
|
||||||
map_location::DIRECTION starting_dir;
|
|
||||||
map_location goto_hex;
|
|
||||||
|
|
||||||
|
|
||||||
move_action(const unit_const_ptr moved,
|
|
||||||
const std::vector<map_location>::const_iterator & begin,
|
|
||||||
const std::vector<map_location>::const_iterator & end,
|
|
||||||
int sm, int timebonus, int orig, const map_location::DIRECTION dir) :
|
|
||||||
undo_action(moved, begin, end),
|
|
||||||
starting_moves(sm),
|
|
||||||
original_village_owner(orig),
|
|
||||||
countdown_time_bonus(timebonus),
|
|
||||||
starting_dir(dir == map_location::NDIRECTIONS ? moved->facing() : dir),
|
|
||||||
goto_hex(moved->get_goto())
|
|
||||||
{
|
|
||||||
this->unit_id_diff = synced_context::get_unit_id_diff();
|
|
||||||
}
|
|
||||||
move_action(const config & unit_cfg, const config & route_cfg,
|
|
||||||
int sm, int timebonus, int orig, const map_location::DIRECTION dir) :
|
|
||||||
undo_action(unit_cfg),
|
|
||||||
starting_moves(sm),
|
|
||||||
original_village_owner(orig),
|
|
||||||
countdown_time_bonus(timebonus),
|
|
||||||
starting_dir(dir),
|
|
||||||
goto_hex(unit_cfg["goto_x"].to_int(-999) - 1,
|
|
||||||
unit_cfg["goto_y"].to_int(-999) - 1)
|
|
||||||
{
|
|
||||||
read_locations(route_cfg, route);
|
|
||||||
}
|
|
||||||
virtual ~move_action();
|
|
||||||
|
|
||||||
/// Writes this into the provided config.
|
|
||||||
virtual void write(config & cfg) const;
|
|
||||||
|
|
||||||
/// Undoes this action.
|
|
||||||
virtual bool undo(int side, undo_list & undos);
|
|
||||||
/// Redoes this action.
|
|
||||||
virtual bool redo(int side);
|
|
||||||
};
|
|
||||||
undo_list::move_action::~move_action()
|
|
||||||
{}
|
|
||||||
|
|
||||||
struct undo_list::recall_action : undo_list::undo_action {
|
|
||||||
std::string id;
|
|
||||||
map_location recall_from;
|
|
||||||
|
|
||||||
|
|
||||||
recall_action(const unit_const_ptr recalled, const map_location& loc,
|
|
||||||
const map_location& from) :
|
|
||||||
undo_action(recalled, loc),
|
|
||||||
id(recalled->id()),
|
|
||||||
recall_from(from)
|
|
||||||
{
|
|
||||||
this->unit_id_diff = synced_context::get_unit_id_diff();
|
|
||||||
}
|
|
||||||
recall_action(const config & unit_cfg, const map_location & loc,
|
|
||||||
const map_location & from) :
|
|
||||||
undo_action(unit_cfg, loc),
|
|
||||||
id(unit_cfg["id"]),
|
|
||||||
recall_from(from)
|
|
||||||
{}
|
|
||||||
virtual ~recall_action();
|
|
||||||
|
|
||||||
/// Writes this into the provided config.
|
|
||||||
virtual void write(config & cfg) const;
|
|
||||||
|
|
||||||
/// Undoes this action.
|
|
||||||
virtual bool undo(int side, undo_list & undos);
|
|
||||||
/// Redoes this action.
|
|
||||||
virtual bool redo(int side);
|
|
||||||
};
|
|
||||||
undo_list::recall_action::~recall_action()
|
|
||||||
{}
|
|
||||||
|
|
||||||
struct undo_list::recruit_action : undo_list::undo_action {
|
|
||||||
const unit_type & u_type;
|
|
||||||
map_location recruit_from;
|
|
||||||
|
|
||||||
|
|
||||||
recruit_action(const unit_const_ptr recruited, const map_location& loc,
|
|
||||||
const map_location& from) :
|
|
||||||
undo_action(recruited, loc),
|
|
||||||
u_type(recruited->type()),
|
|
||||||
recruit_from(from)
|
|
||||||
{
|
|
||||||
this->unit_id_diff = synced_context::get_unit_id_diff();
|
|
||||||
}
|
|
||||||
recruit_action(const config & unit_cfg, const unit_type & type,
|
|
||||||
const map_location& loc, const map_location& from) :
|
|
||||||
undo_action(unit_cfg, loc),
|
|
||||||
u_type(type),
|
|
||||||
recruit_from(from)
|
|
||||||
{}
|
|
||||||
virtual ~recruit_action();
|
|
||||||
|
|
||||||
/// Writes this into the provided config.
|
|
||||||
virtual void write(config & cfg) const;
|
|
||||||
|
|
||||||
/// Undoes this action.
|
|
||||||
virtual bool undo(int side, undo_list & undos);
|
|
||||||
/// Redoes this action.
|
|
||||||
virtual bool redo(int side);
|
|
||||||
};
|
|
||||||
undo_list::recruit_action::~recruit_action()
|
|
||||||
{}
|
|
||||||
|
|
||||||
struct undo_list::auto_shroud_action : undo_list::undo_action {
|
|
||||||
bool active;
|
|
||||||
|
|
||||||
|
|
||||||
explicit auto_shroud_action(bool turned_on) :
|
|
||||||
undo_action(),
|
|
||||||
active(turned_on)
|
|
||||||
{}
|
|
||||||
explicit auto_shroud_action(bool turned_on, int unit_id_diff) :
|
|
||||||
undo_action(),
|
|
||||||
active(turned_on)
|
|
||||||
{
|
|
||||||
this->unit_id_diff = unit_id_diff;
|
|
||||||
}
|
|
||||||
virtual ~auto_shroud_action();
|
|
||||||
|
|
||||||
/// Writes this into the provided config.
|
|
||||||
virtual void write(config & cfg) const;
|
|
||||||
|
|
||||||
/// Undoes this action.
|
|
||||||
virtual bool undo(int side, undo_list & undos);
|
|
||||||
/// Redoes this action.
|
|
||||||
virtual bool redo(int side);
|
|
||||||
};
|
|
||||||
undo_list::auto_shroud_action::~auto_shroud_action()
|
|
||||||
{}
|
|
||||||
|
|
||||||
struct undo_list::update_shroud_action : undo_list::undo_action {
|
|
||||||
// No additional data.
|
|
||||||
|
|
||||||
update_shroud_action() : undo_action() {}
|
|
||||||
update_shroud_action(int unit_id_diff) : undo_action()
|
|
||||||
{
|
|
||||||
this->unit_id_diff = unit_id_diff;
|
|
||||||
}
|
|
||||||
virtual ~update_shroud_action();
|
|
||||||
|
|
||||||
/// Writes this into the provided config.
|
|
||||||
virtual void write(config & cfg) const;
|
|
||||||
|
|
||||||
/// Undoes this action.
|
|
||||||
virtual bool undo(int side, undo_list & undos);
|
|
||||||
/// Redoes this action.
|
|
||||||
virtual bool redo(int side);
|
|
||||||
};
|
|
||||||
undo_list::update_shroud_action::~update_shroud_action()
|
|
||||||
{}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an undo_action based on a config.
|
* Creates an undo_action based on a config.
|
||||||
* @return a pointer that must be deleted, or NULL if the @a cfg could not be parsed.
|
* @return a pointer that must be deleted, or NULL if the @a cfg could not be parsed.
|
||||||
*/
|
*/
|
||||||
undo_list::undo_action *
|
undo_action_base * undo_list::create_action(const config & cfg)
|
||||||
undo_list::undo_action::create(const config & cfg)
|
|
||||||
{
|
{
|
||||||
const std::string str = cfg["type"];
|
const std::string str = cfg["type"];
|
||||||
undo_list::undo_action * res = NULL;
|
undo_action_base * res = NULL;
|
||||||
// The general division of labor in this function is that the various
|
// The general division of labor in this function is that the various
|
||||||
// constructors will parse the "unit" child config, while this function
|
// constructors will parse the "unit" child config, while this function
|
||||||
// parses everything else.
|
// parses everything else.
|
||||||
|
|
||||||
if ( str == "move" ) {
|
if ( str == "move" ) {
|
||||||
res = new move_action(cfg.child("unit"), cfg,
|
res = new undo::move_action(cfg, cfg.child_or_empty("unit"),
|
||||||
cfg["starting_moves"],
|
cfg["starting_moves"],
|
||||||
cfg["time_bonus"],
|
cfg["time_bonus"],
|
||||||
cfg["village_owner"],
|
cfg["village_owner"],
|
||||||
|
@ -293,122 +106,30 @@ undo_list::undo_action::create(const config & cfg)
|
||||||
<< child["type"] << "' was not found.\n";
|
<< child["type"] << "' was not found.\n";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
res = new recruit_action(child, *u_type,
|
res = new undo::recruit_action(cfg, *u_type, map_location(cfg.child_or_empty("leader"), NULL));
|
||||||
map_location(cfg, NULL),
|
|
||||||
map_location(cfg.child_or_empty("leader"), NULL));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
else if ( str == "recall" )
|
else if ( str == "recall" )
|
||||||
res = new recall_action(cfg.child("unit"),
|
res = new undo::recall_action(cfg, map_location(cfg.child_or_empty("leader"), NULL));
|
||||||
map_location(cfg, NULL),
|
|
||||||
map_location(cfg.child_or_empty("leader"), NULL));
|
|
||||||
|
|
||||||
else if ( str == "dismiss" )
|
else if ( str == "dismiss" )
|
||||||
res = new dismiss_action(cfg.child("unit"));
|
res = new undo::dismiss_action(cfg, cfg.child("unit"));
|
||||||
|
|
||||||
else if ( str == "auto_shroud" )
|
else if ( str == "auto_shroud" )
|
||||||
res = new auto_shroud_action(cfg["active"].to_bool());
|
res = new undo::auto_shroud_action(cfg["active"].to_bool());
|
||||||
|
|
||||||
else if ( str == "update_shroud" )
|
else if ( str == "update_shroud" )
|
||||||
res = new update_shroud_action;
|
res = new undo::update_shroud_action();
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Unrecognized type.
|
// Unrecognized type.
|
||||||
ERR_NG << "Unrecognized undo action type: " << str << "." << std::endl;
|
ERR_NG << "Unrecognized undo action type: " << str << "." << std::endl;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
res->replay_data = cfg.child_or_empty("replay_data");
|
|
||||||
res->unit_id_diff = cfg["unit_id_diff"];
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes this into the provided config.
|
|
||||||
*/
|
|
||||||
void undo_list::dismiss_action::write(config & cfg) const
|
|
||||||
{
|
|
||||||
cfg.add_child("replay_data", replay_data);
|
|
||||||
cfg["unit_id_diff"] = unit_id_diff;
|
|
||||||
cfg["type"] = "dismiss";
|
|
||||||
dismissed_unit->write(cfg.add_child("unit"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes this into the provided config.
|
|
||||||
*/
|
|
||||||
void undo_list::recall_action::write(config & cfg) const
|
|
||||||
{
|
|
||||||
cfg.add_child("replay_data", replay_data);
|
|
||||||
cfg["unit_id_diff"] = unit_id_diff;
|
|
||||||
cfg["type"] = "recall";
|
|
||||||
route.front().write(cfg);
|
|
||||||
recall_from.write(cfg.add_child("leader"));
|
|
||||||
|
|
||||||
config & child = cfg.add_child("unit");
|
|
||||||
view_info->write(child);
|
|
||||||
child["id"] = id;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes this into the provided config.
|
|
||||||
*/
|
|
||||||
void undo_list::recruit_action::write(config & cfg) const
|
|
||||||
{
|
|
||||||
cfg.add_child("replay_data", replay_data);
|
|
||||||
cfg["unit_id_diff"] = unit_id_diff;
|
|
||||||
cfg["type"] = "recruit";
|
|
||||||
route.front().write(cfg);
|
|
||||||
recruit_from.write(cfg.add_child("leader"));
|
|
||||||
|
|
||||||
config & child = cfg.add_child("unit");
|
|
||||||
view_info->write(child);
|
|
||||||
child["type"] = u_type.base_id();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes this into the provided config.
|
|
||||||
*/
|
|
||||||
void undo_list::move_action::write(config & cfg) const
|
|
||||||
{
|
|
||||||
cfg.add_child("replay_data", replay_data);
|
|
||||||
cfg["unit_id_diff"] = unit_id_diff;
|
|
||||||
cfg["type"] = "move";
|
|
||||||
cfg["starting_direction"] = map_location::write_direction(starting_dir);
|
|
||||||
cfg["starting_moves"] = starting_moves;
|
|
||||||
cfg["time_bonus"] = countdown_time_bonus;
|
|
||||||
cfg["village_owner"] = original_village_owner;
|
|
||||||
write_locations(route, cfg);
|
|
||||||
|
|
||||||
config & child = cfg.add_child("unit");
|
|
||||||
view_info->write(child);
|
|
||||||
child["goto_x"] = goto_hex.x + 1;
|
|
||||||
child["goto_y"] = goto_hex.y + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes this into the provided config.
|
|
||||||
*/
|
|
||||||
void undo_list::auto_shroud_action::write(config & cfg) const
|
|
||||||
{
|
|
||||||
cfg.add_child("replay_data", replay_data);
|
|
||||||
cfg["unit_id_diff"] = unit_id_diff;
|
|
||||||
cfg["type"] = "auto_shroud";
|
|
||||||
cfg["active"] = active;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Writes this into the provided config.
|
|
||||||
*/
|
|
||||||
void undo_list::update_shroud_action::write(config & cfg) const
|
|
||||||
{
|
|
||||||
cfg.add_child("replay_data", replay_data);
|
|
||||||
cfg["unit_id_diff"] = unit_id_diff;
|
|
||||||
cfg["type"] = "update_shroud";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor.
|
* Constructor.
|
||||||
* The config is allowed to be invalid.
|
* The config is allowed to be invalid.
|
||||||
|
@ -433,14 +154,12 @@ undo_list::~undo_list()
|
||||||
/**
|
/**
|
||||||
* Adds an auto-shroud toggle to the undo stack.
|
* Adds an auto-shroud toggle to the undo stack.
|
||||||
*/
|
*/
|
||||||
void undo_list::add_auto_shroud(bool turned_on, boost::optional<int> unit_id_diff)
|
void undo_list::add_auto_shroud(bool turned_on)
|
||||||
{
|
{
|
||||||
if(!unit_id_diff)
|
|
||||||
unit_id_diff = synced_context::get_unit_id_diff();
|
|
||||||
/// @todo: Consecutive shroud actions can be collapsed into one.
|
/// @todo: Consecutive shroud actions can be collapsed into one.
|
||||||
|
|
||||||
// Do not call add(), as this should not clear the redo stack.
|
// Do not call add(), as this should not clear the redo stack.
|
||||||
undos_.push_back(new auto_shroud_action(turned_on, unit_id_diff.get()));
|
undos_.push_back(new undo::auto_shroud_action(turned_on));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -448,7 +167,7 @@ void undo_list::add_auto_shroud(bool turned_on, boost::optional<int> unit_id_dif
|
||||||
*/
|
*/
|
||||||
void undo_list::add_dismissal(const unit_const_ptr u)
|
void undo_list::add_dismissal(const unit_const_ptr u)
|
||||||
{
|
{
|
||||||
add(new dismiss_action(u));
|
add(new undo::dismiss_action(u));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -460,7 +179,7 @@ void undo_list::add_move(const unit_const_ptr u,
|
||||||
int start_moves, int timebonus, int village_owner,
|
int start_moves, int timebonus, int village_owner,
|
||||||
const map_location::DIRECTION dir)
|
const map_location::DIRECTION dir)
|
||||||
{
|
{
|
||||||
add(new move_action(u, begin, end, start_moves, timebonus, village_owner, dir));
|
add(new undo::move_action(u, begin, end, start_moves, timebonus, village_owner, dir));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -469,7 +188,7 @@ void undo_list::add_move(const unit_const_ptr u,
|
||||||
void undo_list::add_recall(const unit_const_ptr u, const map_location& loc,
|
void undo_list::add_recall(const unit_const_ptr u, const map_location& loc,
|
||||||
const map_location& from)
|
const map_location& from)
|
||||||
{
|
{
|
||||||
add(new recall_action(u, loc, from));
|
add(new undo::recall_action(u, loc, from));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -478,7 +197,7 @@ void undo_list::add_recall(const unit_const_ptr u, const map_location& loc,
|
||||||
void undo_list::add_recruit(const unit_const_ptr u, const map_location& loc,
|
void undo_list::add_recruit(const unit_const_ptr u, const map_location& loc,
|
||||||
const map_location& from)
|
const map_location& from)
|
||||||
{
|
{
|
||||||
add(new recruit_action(u, loc, from));
|
add(new undo::recruit_action(u, loc, from));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -486,14 +205,12 @@ void undo_list::add_recruit(const unit_const_ptr u, const map_location& loc,
|
||||||
* This is called from within commit_vision(), so there should be no need
|
* This is called from within commit_vision(), so there should be no need
|
||||||
* for this to be publicly visible.
|
* for this to be publicly visible.
|
||||||
*/
|
*/
|
||||||
void undo_list::add_update_shroud(boost::optional<int> unit_id_diff)
|
void undo_list::add_update_shroud()
|
||||||
{
|
{
|
||||||
if(!unit_id_diff)
|
|
||||||
unit_id_diff = synced_context::get_unit_id_diff();
|
|
||||||
/// @todo: Consecutive shroud actions can be collapsed into one.
|
/// @todo: Consecutive shroud actions can be collapsed into one.
|
||||||
|
|
||||||
// Do not call add(), as this should not clear the redo stack.
|
// Do not call add(), as this should not clear the redo stack.
|
||||||
undos_.push_back(new update_shroud_action(unit_id_diff.get()));
|
undos_.push_back(new undo::update_shroud_action());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -583,7 +300,7 @@ void undo_list::read(const config & cfg)
|
||||||
// Build the undo stack.
|
// Build the undo stack.
|
||||||
BOOST_FOREACH( const config & child, cfg.child_range("undo") ) {
|
BOOST_FOREACH( const config & child, cfg.child_range("undo") ) {
|
||||||
try {
|
try {
|
||||||
undo_action * action = undo_action::create(child);
|
undo_action_base * action = create_action(child);
|
||||||
if ( action ) {
|
if ( action ) {
|
||||||
undos_.push_back(action);
|
undos_.push_back(action);
|
||||||
}
|
}
|
||||||
|
@ -601,9 +318,9 @@ void undo_list::read(const config & cfg)
|
||||||
// Build the redo stack.
|
// Build the redo stack.
|
||||||
BOOST_FOREACH( const config & child, cfg.child_range("redo") ) {
|
BOOST_FOREACH( const config & child, cfg.child_range("redo") ) {
|
||||||
try {
|
try {
|
||||||
undo_action * action = undo_action::create(child);
|
undo_action_base * action = create_action(child);
|
||||||
if ( action ) {
|
if ( undo_action* undoable_action = dynamic_cast<undo_action*>(action)) {
|
||||||
redos_.push_back(action);
|
redos_.push_back(undoable_action);
|
||||||
}
|
}
|
||||||
} catch (bad_lexical_cast &) {
|
} catch (bad_lexical_cast &) {
|
||||||
ERR_NG << "Error when parsing redo list from config: bad lexical cast." << std::endl;
|
ERR_NG << "Error when parsing redo list from config: bad lexical cast." << std::endl;
|
||||||
|
@ -629,7 +346,7 @@ void undo_list::write(config & cfg) const
|
||||||
for ( action_list::const_iterator it = undos_.begin(); it != undos_.end(); ++it )
|
for ( action_list::const_iterator it = undos_.begin(); it != undos_.end(); ++it )
|
||||||
it->write(cfg.add_child("undo"));
|
it->write(cfg.add_child("undo"));
|
||||||
|
|
||||||
for ( action_list::const_iterator it = redos_.begin(); it != redos_.end(); ++it )
|
for ( redos_list::const_iterator it = redos_.begin(); it != redos_.end(); ++it )
|
||||||
it->write(cfg.add_child("redo"));
|
it->write(cfg.add_child("redo"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -649,193 +366,40 @@ void undo_list::undo()
|
||||||
// Get the action to undo. (This will be placed on the redo stack, but
|
// Get the action to undo. (This will be placed on the redo stack, but
|
||||||
// only if the undo is successful.)
|
// only if the undo is successful.)
|
||||||
action_list::auto_type action = undos_.pop_back();
|
action_list::auto_type action = undos_.pop_back();
|
||||||
int last_unit_id = n_unit::id_manager::instance().get_save_id();
|
if (undo_action* undoable_action = dynamic_cast<undo_action*>(action.ptr()))
|
||||||
if ( !action->undo(side_, *this) ) {
|
{
|
||||||
return;
|
int last_unit_id = n_unit::id_manager::instance().get_save_id();
|
||||||
}
|
if ( !undoable_action->undo(side_) ) {
|
||||||
if(last_unit_id - action->unit_id_diff < 0) {
|
return;
|
||||||
ERR_NG << "Next unit id is below 0 after undoing" << std::endl;
|
|
||||||
}
|
|
||||||
n_unit::id_manager::instance().set_save_id(last_unit_id - action->unit_id_diff);
|
|
||||||
|
|
||||||
// Bookkeeping.
|
|
||||||
resources::recorder->undo_cut(action->get_replay_data());
|
|
||||||
redos_.push_back(action.release());
|
|
||||||
resources::whiteboard->on_gamestate_change();
|
|
||||||
|
|
||||||
// Screen updates.
|
|
||||||
gui.invalidate_unit();
|
|
||||||
gui.invalidate_game_status();
|
|
||||||
gui.redraw_minimap();
|
|
||||||
gui.draw();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Undoes this action.
|
|
||||||
* @return true on success; false on an error.
|
|
||||||
*/
|
|
||||||
bool undo_list::dismiss_action::undo(int side, undo_list & /*undos*/)
|
|
||||||
{
|
|
||||||
team ¤t_team = (*resources::teams)[side-1];
|
|
||||||
|
|
||||||
current_team.recall_list().add(dismissed_unit);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Undoes this action.
|
|
||||||
* @return true on success; false on an error.
|
|
||||||
*/
|
|
||||||
bool undo_list::recall_action::undo(int side, undo_list & /*undos*/)
|
|
||||||
{
|
|
||||||
game_display & gui = *resources::screen;
|
|
||||||
unit_map & units = *resources::units;
|
|
||||||
team ¤t_team = (*resources::teams)[side-1];
|
|
||||||
|
|
||||||
const map_location & recall_loc = route.front();
|
|
||||||
unit_map::iterator un_it = units.find(recall_loc);
|
|
||||||
if ( un_it == units.end() ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
unit_ptr un = un_it.get_shared_ptr();
|
|
||||||
if (!un) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
statistics::un_recall_unit(*un);
|
|
||||||
int cost = statistics::un_recall_unit_cost(*un);
|
|
||||||
if (cost < 0) {
|
|
||||||
current_team.spend_gold(-current_team.recall_cost());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
current_team.spend_gold(-cost);
|
|
||||||
}
|
|
||||||
|
|
||||||
current_team.recall_list().add(un);
|
|
||||||
// invalidate before erasing allow us
|
|
||||||
// to also do the overlapped hexes
|
|
||||||
gui.invalidate(recall_loc);
|
|
||||||
units.erase(recall_loc);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Undoes this action.
|
|
||||||
* @return true on success; false on an error.
|
|
||||||
*/
|
|
||||||
bool undo_list::recruit_action::undo(int side, undo_list & /*undos*/)
|
|
||||||
{
|
|
||||||
game_display & gui = *resources::screen;
|
|
||||||
unit_map & units = *resources::units;
|
|
||||||
team ¤t_team = (*resources::teams)[side-1];
|
|
||||||
|
|
||||||
const map_location & recruit_loc = route.front();
|
|
||||||
unit_map::iterator un_it = units.find(recruit_loc);
|
|
||||||
if ( un_it == units.end() ) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const unit &un = *un_it;
|
|
||||||
statistics::un_recruit_unit(un);
|
|
||||||
current_team.spend_gold(-un.type().cost());
|
|
||||||
|
|
||||||
//MP_COUNTDOWN take away recruit bonus
|
|
||||||
current_team.set_action_bonus_count(current_team.action_bonus_count() - 1);
|
|
||||||
|
|
||||||
// invalidate before erasing allow us
|
|
||||||
// to also do the overlapped hexes
|
|
||||||
gui.invalidate(recruit_loc);
|
|
||||||
units.erase(recruit_loc);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Undoes this action.
|
|
||||||
* @return true on success; false on an error.
|
|
||||||
*/
|
|
||||||
bool undo_list::move_action::undo(int side, undo_list & /*undos*/)
|
|
||||||
{
|
|
||||||
game_display & gui = *resources::screen;
|
|
||||||
unit_map & units = *resources::units;
|
|
||||||
team ¤t_team = (*resources::teams)[side-1];
|
|
||||||
|
|
||||||
// Copy some of our stored data.
|
|
||||||
const int saved_moves = starting_moves;
|
|
||||||
std::vector<map_location> rev_route = route;
|
|
||||||
std::reverse(rev_route.begin(), rev_route.end());
|
|
||||||
|
|
||||||
// Check units.
|
|
||||||
unit_map::iterator u = units.find(rev_route.front());
|
|
||||||
const unit_map::iterator u_end = units.find(rev_route.back());
|
|
||||||
if ( u == units.end() || u_end != units.end() ) {
|
|
||||||
//this can actually happen if the scenario designer has abused the [allow_undo] command
|
|
||||||
ERR_NG << "Illegal 'undo' found. Possible abuse of [allow_undo]?" << std::endl;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ( resources::gameboard->map().is_village(rev_route.front()) ) {
|
|
||||||
get_village(rev_route.front(), original_village_owner + 1);
|
|
||||||
//MP_COUNTDOWN take away capture bonus
|
|
||||||
if ( countdown_time_bonus )
|
|
||||||
{
|
|
||||||
current_team.set_action_bonus_count(current_team.action_bonus_count() - 1);
|
|
||||||
}
|
}
|
||||||
|
if(last_unit_id - undoable_action->unit_id_diff < 0) {
|
||||||
|
ERR_NG << "Next unit id is below 0 after undoing" << std::endl;
|
||||||
|
}
|
||||||
|
n_unit::id_manager::instance().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()));
|
||||||
|
resources::whiteboard->on_gamestate_change();
|
||||||
|
|
||||||
|
// Screen updates.
|
||||||
|
gui.invalidate_unit();
|
||||||
|
gui.invalidate_game_status();
|
||||||
|
gui.redraw_minimap();
|
||||||
|
gui.draw();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
//ignore this action, and undo the previous one.
|
||||||
|
config replay_data;
|
||||||
|
resources::recorder->undo_cut(replay_data);
|
||||||
|
undo();
|
||||||
|
resources::recorder->redo(replay_data);
|
||||||
|
undos_.push_back(action.release());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Record the unit's current state so it can be redone.
|
|
||||||
starting_moves = u->movement_left();
|
|
||||||
goto_hex = u->get_goto();
|
|
||||||
|
|
||||||
// Move the unit.
|
|
||||||
unit_display::move_unit(rev_route, u.get_shared_ptr(), true, starting_dir);
|
|
||||||
units.move(u->get_location(), rev_route.back());
|
|
||||||
unit::clear_status_caches();
|
|
||||||
|
|
||||||
// Restore the unit's old state.
|
|
||||||
u = units.find(rev_route.back());
|
|
||||||
u->set_goto(map_location());
|
|
||||||
u->set_movement(saved_moves, true);
|
|
||||||
u->anim_comp().set_standing();
|
|
||||||
|
|
||||||
gui.invalidate_unit_after_move(rev_route.front(), rev_route.back());
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Undoes this action.
|
|
||||||
* @return true on success; false on an error.
|
|
||||||
*/
|
|
||||||
bool undo_list::auto_shroud_action::undo(int /*side*/, undo_list & undos)
|
|
||||||
{
|
|
||||||
// This does not count as an undoable action, so undo the next
|
|
||||||
// action instead.
|
|
||||||
resources::recorder->undo();
|
|
||||||
undos.undo();
|
|
||||||
// Now keep the auto-shroud toggle at the top of the undo stack.
|
|
||||||
resources::recorder->add_synced_command("auto_shroud", replay_helper::get_auto_shroud(active));
|
|
||||||
undos.add_auto_shroud(active, this->unit_id_diff);
|
|
||||||
// Shroud actions never get moved to the redo stack, so claim an error.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Undoes this action.
|
|
||||||
* @return true on success; false on an error.
|
|
||||||
*/
|
|
||||||
bool undo_list::update_shroud_action::undo(int /*side*/, undo_list & undos)
|
|
||||||
{
|
|
||||||
// This does not count as an undoable action, so undo the next
|
|
||||||
// action instead.
|
|
||||||
resources::recorder->undo();
|
|
||||||
undos.undo();
|
|
||||||
// Now keep the shroud update at the top of the undo stack.
|
|
||||||
resources::recorder->add_synced_command("update_shroud", replay_helper::get_update_shroud());
|
|
||||||
|
|
||||||
undos.add_update_shroud(this->unit_id_diff);
|
|
||||||
// Shroud actions never get moved to the redo stack, so claim an error.
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -852,7 +416,7 @@ void undo_list::redo()
|
||||||
|
|
||||||
// Get the action to redo. (This will be placed on the undo stack, but
|
// Get the action to redo. (This will be placed on the undo stack, but
|
||||||
// only if the redo is successful.)
|
// only if the redo is successful.)
|
||||||
action_list::auto_type action = redos_.pop_back();
|
redos_list::auto_type action = redos_.pop_back();
|
||||||
int last_unit_id = n_unit::id_manager::instance().get_save_id();
|
int last_unit_id = n_unit::id_manager::instance().get_save_id();
|
||||||
if ( !action->redo(side_) ) {
|
if ( !action->redo(side_) ) {
|
||||||
return;
|
return;
|
||||||
|
@ -873,182 +437,8 @@ void undo_list::redo()
|
||||||
gui.draw();
|
gui.draw();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Redoes this action.
|
|
||||||
* @return true on success; false on an error.
|
|
||||||
*/
|
|
||||||
bool undo_list::dismiss_action::redo(int side)
|
|
||||||
{
|
|
||||||
team ¤t_team = (*resources::teams)[side-1];
|
|
||||||
|
|
||||||
resources::recorder->redo(replay_data);
|
|
||||||
replay_data.clear();
|
|
||||||
current_team.recall_list().erase_if_matches_id(dismissed_unit->id());
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redoes this action.
|
|
||||||
* @return true on success; false on an error.
|
|
||||||
*/
|
|
||||||
bool undo_list::recall_action::redo(int side)
|
|
||||||
{
|
|
||||||
game_display & gui = *resources::screen;
|
|
||||||
team ¤t_team = (*resources::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 {
|
|
||||||
gui::dialog(gui, "", msg, gui::OK_ONLY).show();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redoes this action.
|
|
||||||
* @return true on success; false on an error.
|
|
||||||
*/
|
|
||||||
bool undo_list::recruit_action::redo(int side)
|
|
||||||
{
|
|
||||||
game_display & gui = *resources::screen;
|
|
||||||
team ¤t_team = (*resources::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 {
|
|
||||||
gui::dialog(gui, "", msg, gui::OK_ONLY).show();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redoes this action.
|
|
||||||
* @return true on success; false on an error.
|
|
||||||
*/
|
|
||||||
bool undo_list::move_action::redo(int side)
|
|
||||||
{
|
|
||||||
game_display & gui = *resources::screen;
|
|
||||||
unit_map & units = *resources::units;
|
|
||||||
team ¤t_team = (*resources::teams)[side-1];
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
|
|
||||||
if ( resources::gameboard->map().is_village(route.back()) ) {
|
|
||||||
get_village(route.back(), u->side());
|
|
||||||
//MP_COUNTDOWN restore capture bonus
|
|
||||||
if ( countdown_time_bonus )
|
|
||||||
{
|
|
||||||
current_team.set_action_bonus_count(1 + current_team.action_bonus_count());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gui.invalidate_unit_after_move(route.front(), route.back());
|
|
||||||
resources::recorder->redo(replay_data);
|
|
||||||
replay_data.clear();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redoes this action.
|
|
||||||
* @return true on success; false on an error.
|
|
||||||
*/
|
|
||||||
bool undo_list::auto_shroud_action::redo(int /*side*/)
|
|
||||||
{
|
|
||||||
// This should never happen.
|
|
||||||
ERR_NG << "Attempt to redo an auto shroud toggle." << std::endl;
|
|
||||||
assert(false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Redoes this action.
|
|
||||||
* @return true on success; false on an error.
|
|
||||||
*/
|
|
||||||
bool undo_list::update_shroud_action::redo(int /*side*/)
|
|
||||||
{
|
|
||||||
// This should never happen.
|
|
||||||
ERR_NG << "Attempt to redo a shroud update." << std::endl;
|
|
||||||
assert(false);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1074,21 +464,19 @@ size_t undo_list::apply_shroud_changes() const
|
||||||
|
|
||||||
// Loop through the list of undo_actions.
|
// Loop through the list of undo_actions.
|
||||||
for( size_t i = 0; i != list_size; ++i ) {
|
for( size_t i = 0; i != list_size; ++i ) {
|
||||||
const undo_action & action = undos_[i];
|
if (const shroud_clearing_action* action = dynamic_cast<const shroud_clearing_action*>(&undos_[i])) {
|
||||||
// Only actions with vision data are relevant.
|
LOG_NG << "Turning an undo...\n";
|
||||||
if ( !action.view_info )
|
|
||||||
continue;
|
|
||||||
LOG_NG << "Turning an undo...\n";
|
|
||||||
|
|
||||||
// Clear the hexes this unit can see from each hex occupied during
|
// Clear the hexes this unit can see from each hex occupied during
|
||||||
// the action.
|
// the action.
|
||||||
std::vector<map_location>::const_iterator step;
|
std::vector<map_location>::const_iterator step;
|
||||||
for (step = action.route.begin(); step != action.route.end(); ++step) {
|
for (step = action->route.begin(); step != action->route.end(); ++step) {
|
||||||
// Clear the shroud, collecting new sighted events.
|
// Clear the shroud, collecting new sighted events.
|
||||||
// (This can be made gradual by changing "true" to "false".)
|
// (This can be made gradual by changing "true" to "false".)
|
||||||
if ( clearer.clear_unit(*step, tm, *action.view_info, true) ) {
|
if ( clearer.clear_unit(*step, tm, action->view_info, true) ) {
|
||||||
cleared_shroud = true;
|
cleared_shroud = true;
|
||||||
erase_to = i + 1;
|
erase_to = i + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include "vision.hpp"
|
#include "vision.hpp"
|
||||||
#include "../map_location.hpp"
|
#include "../map_location.hpp"
|
||||||
#include "../unit_ptr.hpp"
|
#include "../unit_ptr.hpp"
|
||||||
|
#include "undo_action.hpp"
|
||||||
|
|
||||||
#include <boost/noncopyable.hpp>
|
#include <boost/noncopyable.hpp>
|
||||||
#include <boost/ptr_container/ptr_vector.hpp>
|
#include <boost/ptr_container/ptr_vector.hpp>
|
||||||
|
@ -35,106 +36,21 @@ namespace actions {
|
||||||
|
|
||||||
/// Class to store the actions that a player can undo and redo.
|
/// Class to store the actions that a player can undo and redo.
|
||||||
class undo_list : boost::noncopyable {
|
class undo_list : boost::noncopyable {
|
||||||
/// Records information to be able to undo an action.
|
|
||||||
/// Each type of action gets its own derived type.
|
|
||||||
struct undo_action : boost::noncopyable {
|
|
||||||
/// Constructor for move actions.
|
|
||||||
undo_action(const unit_const_ptr u,
|
|
||||||
const std::vector<map_location>::const_iterator & begin,
|
|
||||||
const std::vector<map_location>::const_iterator & end) :
|
|
||||||
replay_data(),
|
|
||||||
unit_id_diff(),
|
|
||||||
route(begin, end),
|
|
||||||
view_info(new clearer_info(*u))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
/// Constructor for recruit and recall actions.
|
|
||||||
/// These types of actions are guaranteed to have a non-empty route.
|
|
||||||
undo_action(const unit_const_ptr u, const map_location& loc) :
|
|
||||||
replay_data(),
|
|
||||||
unit_id_diff(),
|
|
||||||
route(1, loc),
|
|
||||||
view_info(new clearer_info(*u))
|
|
||||||
{}
|
|
||||||
/// Constructor from a config storing the view info.
|
|
||||||
/// Does not set @a route.
|
|
||||||
explicit undo_action(const config & cfg) :
|
|
||||||
replay_data(),
|
|
||||||
unit_id_diff(),
|
|
||||||
route(),
|
|
||||||
view_info(new clearer_info(cfg))
|
|
||||||
{}
|
|
||||||
/// Constructor from a config storing the view info and a location.
|
|
||||||
/// Guarantees a non-empty route.
|
|
||||||
explicit undo_action(const config & cfg, const map_location & loc) :
|
|
||||||
replay_data(),
|
|
||||||
unit_id_diff(),
|
|
||||||
route(1, loc),
|
|
||||||
view_info(new clearer_info(cfg))
|
|
||||||
{}
|
|
||||||
/// Default constructor.
|
|
||||||
/// This is the only way to get NULL view_info.
|
|
||||||
undo_action() :
|
|
||||||
replay_data(),
|
|
||||||
unit_id_diff(),
|
|
||||||
route(),
|
|
||||||
view_info(NULL)
|
|
||||||
{}
|
|
||||||
// Virtual destructor to support derived classes.
|
|
||||||
virtual ~undo_action();
|
|
||||||
|
|
||||||
|
typedef boost::ptr_vector<undo_action_base> action_list;
|
||||||
/// Creates an undo_action based on a config.
|
typedef boost::ptr_vector<undo_action> redos_list;
|
||||||
/// Throws bad_lexical_cast or config::error if it cannot parse the config properly.
|
|
||||||
static undo_action * create(const config & cfg);
|
|
||||||
/// Writes this into the provided config.
|
|
||||||
virtual void write(config & cfg) const = 0;
|
|
||||||
|
|
||||||
/// Undoes this action.
|
|
||||||
/// @return true on success; false on an error.
|
|
||||||
virtual bool undo(int side, undo_list & undos) = 0;
|
|
||||||
/// Redoes this action.
|
|
||||||
/// @return true on success; false on an error.
|
|
||||||
virtual bool redo(int side) = 0;
|
|
||||||
|
|
||||||
config& get_replay_data() { return replay_data; }
|
|
||||||
|
|
||||||
// Data:
|
|
||||||
/// 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;
|
|
||||||
|
|
||||||
int unit_id_diff;
|
|
||||||
/// The hexes occupied by the affected unit during this action.
|
|
||||||
std::vector<map_location> route;
|
|
||||||
/// A record of the affected unit's ability to see.
|
|
||||||
/// For derived classes that use this, it must be never NULL.
|
|
||||||
clearer_info * const view_info;
|
|
||||||
// This pointer is the reason for deriving from noncopyable (an
|
|
||||||
// alternative would be to implement deep copies, but we have no
|
|
||||||
// need for copying, so noncopyable is simpler).
|
|
||||||
};
|
|
||||||
// The structs derived from undo_action.
|
|
||||||
struct dismiss_action;
|
|
||||||
struct move_action;
|
|
||||||
struct recall_action;
|
|
||||||
struct recruit_action;
|
|
||||||
struct auto_shroud_action;
|
|
||||||
struct update_shroud_action;
|
|
||||||
// The update_shroud_action needs to be able to call add_update_shroud().
|
|
||||||
friend struct update_shroud_action;
|
|
||||||
|
|
||||||
typedef boost::ptr_vector<undo_action> action_list;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit undo_list(const config & cfg);
|
explicit undo_list(const config & cfg);
|
||||||
~undo_list();
|
~undo_list();
|
||||||
|
/// Creates an undo_action based on a config.
|
||||||
|
/// Throws bad_lexical_cast or config::error if it cannot parse the config properly.
|
||||||
|
static undo_action_base * create_action(const config & cfg);
|
||||||
|
|
||||||
// Functions related to managing the undo stack:
|
// Functions related to managing the undo stack:
|
||||||
|
|
||||||
/// Adds an auto-shroud toggle to the undo stack.
|
/// Adds an auto-shroud toggle to the undo stack.
|
||||||
void add_auto_shroud(bool turned_on, boost::optional<int> unit_id_diff = boost::optional<int>());
|
void add_auto_shroud(bool turned_on);
|
||||||
/// Adds a dismissal to the undo stack.
|
/// Adds a dismissal to the undo stack.
|
||||||
void add_dismissal(const unit_const_ptr u);
|
void add_dismissal(const unit_const_ptr u);
|
||||||
/// Adds a move to the undo stack.
|
/// Adds a move to the undo stack.
|
||||||
|
@ -151,7 +67,7 @@ public:
|
||||||
const map_location& from);
|
const map_location& from);
|
||||||
private:
|
private:
|
||||||
/// Adds a shroud update to the undo stack.
|
/// Adds a shroud update to the undo stack.
|
||||||
void add_update_shroud(boost::optional<int> unit_id_diff = boost::optional<int>());
|
void add_update_shroud();
|
||||||
public:
|
public:
|
||||||
/// Clears the stack of undoable (and redoable) actions.
|
/// Clears the stack of undoable (and redoable) actions.
|
||||||
void clear();
|
void clear();
|
||||||
|
@ -180,14 +96,14 @@ public:
|
||||||
|
|
||||||
private: // functions
|
private: // functions
|
||||||
/// Adds an action to the undo stack.
|
/// Adds an action to the undo stack.
|
||||||
void add(undo_action * action)
|
void add(undo_action_base * action)
|
||||||
{ undos_.push_back(action); redos_.clear(); }
|
{ undos_.push_back(action); redos_.clear(); }
|
||||||
/// Applies the pending fog/shroud changes from the undo stack.
|
/// Applies the pending fog/shroud changes from the undo stack.
|
||||||
size_t apply_shroud_changes() const;
|
size_t apply_shroud_changes() const;
|
||||||
|
|
||||||
private: // data
|
private: // data
|
||||||
action_list undos_;
|
action_list undos_;
|
||||||
action_list redos_;
|
redos_list redos_;
|
||||||
|
|
||||||
/// Tracks the current side.
|
/// Tracks the current side.
|
||||||
int side_;
|
int side_;
|
||||||
|
|
1
src/actions/undo_action.cpp
Normal file
1
src/actions/undo_action.cpp
Normal file
|
@ -0,0 +1 @@
|
||||||
|
#include "undo_action.hpp"
|
77
src/actions/undo_action.hpp
Normal file
77
src/actions/undo_action.hpp
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "vision.hpp"
|
||||||
|
#include "../map_location.hpp"
|
||||||
|
#include "../unit_ptr.hpp"
|
||||||
|
#include "../synced_context.hpp"
|
||||||
|
|
||||||
|
#include <boost/noncopyable.hpp>
|
||||||
|
#include <boost/ptr_container/ptr_vector.hpp>
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
namespace actions {
|
||||||
|
class undo_list;
|
||||||
|
}
|
||||||
|
namespace actions {
|
||||||
|
|
||||||
|
/// Records information to be able to undo an action.
|
||||||
|
/// Each type of action gets its own derived type.
|
||||||
|
/// Base class for all entries in the undo stack, also contains non undoable actions like update_shroud or auto_shroud.
|
||||||
|
struct undo_action_base : boost::noncopyable
|
||||||
|
{
|
||||||
|
/// Default constructor.
|
||||||
|
/// This is the only way to get NULL view_info.
|
||||||
|
undo_action_base()
|
||||||
|
{ }
|
||||||
|
// Virtual destructor to support derived classes.
|
||||||
|
virtual ~undo_action_base() {}
|
||||||
|
|
||||||
|
/// Writes this into the provided config.
|
||||||
|
virtual void write(config & cfg) const
|
||||||
|
{
|
||||||
|
cfg["type"] = this->get_type();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual const char* get_type() const = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/// actions that are undoable (this does not include update_shroud and auto_shroud)
|
||||||
|
struct undo_action : undo_action_base
|
||||||
|
{
|
||||||
|
/// Default constructor.
|
||||||
|
/// It is assumed that undo actions are contructed after the action is performed
|
||||||
|
/// so that the unit id diff does not change after this contructor.
|
||||||
|
undo_action()
|
||||||
|
: undo_action_base()
|
||||||
|
, replay_data()
|
||||||
|
, unit_id_diff(synced_context::get_unit_id_diff())
|
||||||
|
{ }
|
||||||
|
undo_action(const config& cfg)
|
||||||
|
: undo_action_base()
|
||||||
|
, replay_data(cfg.child_or_empty("replay_data"))
|
||||||
|
, unit_id_diff(cfg["unit_id_diff"])
|
||||||
|
{ }
|
||||||
|
// Virtual destructor to support derived classes.
|
||||||
|
virtual ~undo_action() {}
|
||||||
|
|
||||||
|
/// Writes this into the provided config.
|
||||||
|
virtual void write(config & cfg) const
|
||||||
|
{
|
||||||
|
cfg.add_child("replay_data", replay_data);
|
||||||
|
cfg["unit_id_diff"] = unit_id_diff;
|
||||||
|
undo_action_base::write(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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;
|
||||||
|
|
||||||
|
int unit_id_diff;
|
||||||
|
};
|
||||||
|
}
|
47
src/actions/undo_dismiss_action.cpp
Normal file
47
src/actions/undo_dismiss_action.cpp
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#include "undo_dismiss_action.hpp"
|
||||||
|
#include "../resources.hpp"
|
||||||
|
#include "../team.hpp"
|
||||||
|
#include "../replay.hpp"
|
||||||
|
|
||||||
|
namespace actions
|
||||||
|
{
|
||||||
|
namespace undo
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes this into the provided config.
|
||||||
|
*/
|
||||||
|
void dismiss_action::write(config & cfg) const
|
||||||
|
{
|
||||||
|
undo_action::write(cfg);
|
||||||
|
dismissed_unit->write(cfg.add_child("unit"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undoes this action.
|
||||||
|
* @return true on success; false on an error.
|
||||||
|
*/
|
||||||
|
bool dismiss_action::undo(int side)
|
||||||
|
{
|
||||||
|
team ¤t_team = (*resources::teams)[side-1];
|
||||||
|
|
||||||
|
current_team.recall_list().add(dismissed_unit);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redoes this action.
|
||||||
|
* @return true on success; false on an error.
|
||||||
|
*/
|
||||||
|
bool dismiss_action::redo(int side)
|
||||||
|
{
|
||||||
|
team ¤t_team = (*resources::teams)[side-1];
|
||||||
|
|
||||||
|
resources::recorder->redo(replay_data);
|
||||||
|
replay_data.clear();
|
||||||
|
current_team.recall_list().erase_if_matches_id(dismissed_unit->id());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
40
src/actions/undo_dismiss_action.hpp
Normal file
40
src/actions/undo_dismiss_action.hpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "undo_action.hpp"
|
||||||
|
#include "../unit_ptr.hpp"
|
||||||
|
#include "../unit.hpp"
|
||||||
|
|
||||||
|
namespace actions
|
||||||
|
{
|
||||||
|
namespace undo
|
||||||
|
{
|
||||||
|
|
||||||
|
struct dismiss_action : undo_action
|
||||||
|
{
|
||||||
|
unit_ptr dismissed_unit;
|
||||||
|
|
||||||
|
|
||||||
|
explicit dismiss_action(const unit_const_ptr dismissed)
|
||||||
|
: undo_action()
|
||||||
|
, dismissed_unit(new unit(*dismissed))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
explicit dismiss_action(const config & cfg, const config & unit_cfg)
|
||||||
|
: undo_action(cfg)
|
||||||
|
, dismissed_unit(new unit(unit_cfg))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual const char* get_type() const { return "dismiss"; }
|
||||||
|
virtual ~dismiss_action() {};
|
||||||
|
|
||||||
|
/// Writes this into the provided config.
|
||||||
|
virtual void write(config & cfg) const;
|
||||||
|
|
||||||
|
/// Undoes this action.
|
||||||
|
virtual bool undo(int side);
|
||||||
|
/// Redoes this action.
|
||||||
|
virtual bool redo(int side);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
143
src/actions/undo_move_action.cpp
Normal file
143
src/actions/undo_move_action.cpp
Normal file
|
@ -0,0 +1,143 @@
|
||||||
|
#include "undo_move_action.hpp"
|
||||||
|
#include "move.hpp"
|
||||||
|
|
||||||
|
#include "../construct_dialog.hpp"
|
||||||
|
#include "../resources.hpp"
|
||||||
|
#include "../team.hpp"
|
||||||
|
#include "../replay.hpp"
|
||||||
|
#include "../unit_map.hpp"
|
||||||
|
#include "../unit_animation_component.hpp"
|
||||||
|
#include "../log.hpp"
|
||||||
|
#include "../game_display.hpp"
|
||||||
|
#include "../unit_display.hpp"
|
||||||
|
#include "../game_board.hpp"
|
||||||
|
#include "../map.hpp"
|
||||||
|
|
||||||
|
static lg::log_domain log_engine("engine");
|
||||||
|
#define ERR_NG LOG_STREAM(err, log_engine)
|
||||||
|
#define LOG_NG LOG_STREAM(info, log_engine)
|
||||||
|
|
||||||
|
namespace actions
|
||||||
|
{
|
||||||
|
namespace undo
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes this into the provided config.
|
||||||
|
*/
|
||||||
|
void move_action::write(config & cfg) const
|
||||||
|
{
|
||||||
|
undo_action::write(cfg);
|
||||||
|
shroud_clearing_action::write(cfg);
|
||||||
|
cfg["starting_direction"] = map_location::write_direction(starting_dir);
|
||||||
|
cfg["starting_moves"] = starting_moves;
|
||||||
|
cfg["time_bonus"] = countdown_time_bonus;
|
||||||
|
cfg["village_owner"] = original_village_owner;
|
||||||
|
config & child = cfg.child("unit");
|
||||||
|
child["goto_x"] = goto_hex.x + 1;
|
||||||
|
child["goto_y"] = goto_hex.y + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undoes this action.
|
||||||
|
* @return true on success; false on an error.
|
||||||
|
*/
|
||||||
|
bool move_action::undo(int side)
|
||||||
|
{
|
||||||
|
game_display & gui = *resources::screen;
|
||||||
|
unit_map & units = *resources::units;
|
||||||
|
team ¤t_team = (*resources::teams)[side-1];
|
||||||
|
|
||||||
|
// Copy some of our stored data.
|
||||||
|
const int saved_moves = starting_moves;
|
||||||
|
std::vector<map_location> rev_route = route;
|
||||||
|
std::reverse(rev_route.begin(), rev_route.end());
|
||||||
|
|
||||||
|
// Check units.
|
||||||
|
unit_map::iterator u = units.find(rev_route.front());
|
||||||
|
const unit_map::iterator u_end = units.find(rev_route.back());
|
||||||
|
if ( u == units.end() || u_end != units.end() ) {
|
||||||
|
//this can actually happen if the scenario designer has abused the [allow_undo] command
|
||||||
|
ERR_NG << "Illegal 'undo' found. Possible abuse of [allow_undo]?" << std::endl;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ( resources::gameboard->map().is_village(rev_route.front()) ) {
|
||||||
|
get_village(rev_route.front(), original_village_owner + 1);
|
||||||
|
//MP_COUNTDOWN take away capture bonus
|
||||||
|
if ( countdown_time_bonus )
|
||||||
|
{
|
||||||
|
current_team.set_action_bonus_count(current_team.action_bonus_count() - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Record the unit's current state so it can be redone.
|
||||||
|
starting_moves = u->movement_left();
|
||||||
|
goto_hex = u->get_goto();
|
||||||
|
|
||||||
|
// Move the unit.
|
||||||
|
unit_display::move_unit(rev_route, u.get_shared_ptr(), true, starting_dir);
|
||||||
|
units.move(u->get_location(), rev_route.back());
|
||||||
|
unit::clear_status_caches();
|
||||||
|
|
||||||
|
// Restore the unit's old state.
|
||||||
|
u = units.find(rev_route.back());
|
||||||
|
u->set_goto(map_location());
|
||||||
|
u->set_movement(saved_moves, true);
|
||||||
|
u->anim_comp().set_standing();
|
||||||
|
|
||||||
|
gui.invalidate_unit_after_move(rev_route.front(), rev_route.back());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Redoes this action.
|
||||||
|
* @return true on success; false on an error.
|
||||||
|
*/
|
||||||
|
bool move_action::redo(int side)
|
||||||
|
{
|
||||||
|
game_display & gui = *resources::screen;
|
||||||
|
unit_map & units = *resources::units;
|
||||||
|
team ¤t_team = (*resources::teams)[side-1];
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
|
||||||
|
if ( resources::gameboard->map().is_village(route.back()) ) {
|
||||||
|
get_village(route.back(), u->side());
|
||||||
|
//MP_COUNTDOWN restore capture bonus
|
||||||
|
if ( countdown_time_bonus )
|
||||||
|
{
|
||||||
|
current_team.set_action_bonus_count(1 + current_team.action_bonus_count());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gui.invalidate_unit_after_move(route.front(), route.back());
|
||||||
|
resources::recorder->redo(replay_data);
|
||||||
|
replay_data.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
60
src/actions/undo_move_action.hpp
Normal file
60
src/actions/undo_move_action.hpp
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "undo_action.hpp"
|
||||||
|
#include "shroud_clearing_action.hpp"
|
||||||
|
#include "../unit_ptr.hpp"
|
||||||
|
#include "../unit.hpp"
|
||||||
|
|
||||||
|
namespace actions
|
||||||
|
{
|
||||||
|
namespace undo
|
||||||
|
{
|
||||||
|
|
||||||
|
struct move_action : undo_action, shroud_clearing_action
|
||||||
|
{
|
||||||
|
int starting_moves;
|
||||||
|
int original_village_owner;
|
||||||
|
int countdown_time_bonus;
|
||||||
|
map_location::DIRECTION starting_dir;
|
||||||
|
map_location goto_hex;
|
||||||
|
|
||||||
|
|
||||||
|
move_action(const unit_const_ptr moved,
|
||||||
|
const std::vector<map_location>::const_iterator & begin,
|
||||||
|
const std::vector<map_location>::const_iterator & end,
|
||||||
|
int sm, int timebonus, int orig, const map_location::DIRECTION dir)
|
||||||
|
: undo_action()
|
||||||
|
, shroud_clearing_action(moved, begin, end)
|
||||||
|
, starting_moves(sm)
|
||||||
|
, original_village_owner(orig)
|
||||||
|
, countdown_time_bonus(timebonus)
|
||||||
|
, starting_dir(dir == map_location::NDIRECTIONS ? moved->facing() : dir)
|
||||||
|
, goto_hex(moved->get_goto())
|
||||||
|
{
|
||||||
|
}
|
||||||
|
move_action(const config & cfg, const config & unit_cfg,
|
||||||
|
int sm, int timebonus, int orig, const map_location::DIRECTION dir)
|
||||||
|
: undo_action(cfg)
|
||||||
|
, shroud_clearing_action(cfg)
|
||||||
|
, starting_moves(sm)
|
||||||
|
, original_village_owner(orig)
|
||||||
|
, countdown_time_bonus(timebonus)
|
||||||
|
, starting_dir(dir)
|
||||||
|
, goto_hex(unit_cfg["goto_x"].to_int(-999) - 1,
|
||||||
|
unit_cfg["goto_y"].to_int(-999) - 1)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
virtual const char* get_type() const { return "move"; }
|
||||||
|
virtual ~move_action() {}
|
||||||
|
|
||||||
|
/// Writes this into the provided config.
|
||||||
|
virtual void write(config & cfg) const;
|
||||||
|
|
||||||
|
/// Undoes this action.
|
||||||
|
virtual bool undo(int side);
|
||||||
|
/// Redoes this action.
|
||||||
|
virtual bool redo(int side);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
115
src/actions/undo_recall_action.cpp
Normal file
115
src/actions/undo_recall_action.cpp
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
#include "undo_recall_action.hpp"
|
||||||
|
#include "create.hpp"
|
||||||
|
|
||||||
|
#include "../construct_dialog.hpp"
|
||||||
|
#include "../resources.hpp"
|
||||||
|
#include "../team.hpp"
|
||||||
|
#include "../replay.hpp"
|
||||||
|
#include "../unit_map.hpp"
|
||||||
|
#include "../statistics.hpp"
|
||||||
|
#include "../log.hpp"
|
||||||
|
#include "../game_display.hpp"
|
||||||
|
|
||||||
|
static lg::log_domain log_engine("engine");
|
||||||
|
#define ERR_NG LOG_STREAM(err, log_engine)
|
||||||
|
#define LOG_NG LOG_STREAM(info, log_engine)
|
||||||
|
|
||||||
|
namespace actions
|
||||||
|
{
|
||||||
|
namespace undo
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes this into the provided config.
|
||||||
|
*/
|
||||||
|
void recall_action::write(config & cfg) const
|
||||||
|
{
|
||||||
|
undo_action::write(cfg);
|
||||||
|
shroud_clearing_action::write(cfg);
|
||||||
|
|
||||||
|
recall_from.write(cfg.add_child("leader"));
|
||||||
|
cfg["id"] = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undoes this action.
|
||||||
|
* @return true on success; false on an error.
|
||||||
|
*/
|
||||||
|
bool recall_action::undo(int side)
|
||||||
|
{
|
||||||
|
game_display & gui = *resources::screen;
|
||||||
|
unit_map & units = *resources::units;
|
||||||
|
team ¤t_team = (*resources::teams)[side-1];
|
||||||
|
|
||||||
|
const map_location & recall_loc = route.front();
|
||||||
|
unit_map::iterator un_it = units.find(recall_loc);
|
||||||
|
if ( un_it == units.end() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
unit_ptr un = un_it.get_shared_ptr();
|
||||||
|
if (!un) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
statistics::un_recall_unit(*un);
|
||||||
|
int cost = statistics::un_recall_unit_cost(*un);
|
||||||
|
if (cost < 0) {
|
||||||
|
current_team.spend_gold(-current_team.recall_cost());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
current_team.spend_gold(-cost);
|
||||||
|
}
|
||||||
|
|
||||||
|
current_team.recall_list().add(un);
|
||||||
|
// invalidate before erasing allow us
|
||||||
|
// to also do the overlapped hexes
|
||||||
|
gui.invalidate(recall_loc);
|
||||||
|
units.erase(recall_loc);
|
||||||
|
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::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 {
|
||||||
|
gui::dialog(gui, "", msg, gui::OK_ONLY).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
47
src/actions/undo_recall_action.hpp
Normal file
47
src/actions/undo_recall_action.hpp
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "undo_action.hpp"
|
||||||
|
#include "shroud_clearing_action.hpp"
|
||||||
|
#include "../unit_ptr.hpp"
|
||||||
|
#include "../unit.hpp"
|
||||||
|
|
||||||
|
namespace actions
|
||||||
|
{
|
||||||
|
namespace undo
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
struct recall_action : undo_action, shroud_clearing_action
|
||||||
|
{
|
||||||
|
std::string id;
|
||||||
|
map_location recall_from;
|
||||||
|
|
||||||
|
|
||||||
|
recall_action(const unit_const_ptr recalled, const map_location& loc,
|
||||||
|
const map_location& from)
|
||||||
|
: undo_action()
|
||||||
|
, shroud_clearing_action(recalled, loc)
|
||||||
|
, id(recalled->id())
|
||||||
|
, recall_from(from)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
recall_action(const config & cfg, const map_location & from)
|
||||||
|
: undo_action(cfg)
|
||||||
|
, shroud_clearing_action(cfg)
|
||||||
|
, id(cfg["id"])
|
||||||
|
, recall_from(from)
|
||||||
|
{}
|
||||||
|
virtual const char* get_type() const { return "recall"; }
|
||||||
|
virtual ~recall_action() {}
|
||||||
|
|
||||||
|
/// Writes this into the provided config.
|
||||||
|
virtual void write(config & cfg) const;
|
||||||
|
|
||||||
|
/// Undoes this action.
|
||||||
|
virtual bool undo(int side);
|
||||||
|
/// Redoes this action.
|
||||||
|
virtual bool redo(int side);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
113
src/actions/undo_recruit_action.cpp
Normal file
113
src/actions/undo_recruit_action.cpp
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
#include "undo_recruit_action.hpp"
|
||||||
|
#include "create.hpp"
|
||||||
|
|
||||||
|
#include "../construct_dialog.hpp"
|
||||||
|
#include "../resources.hpp"
|
||||||
|
#include "../team.hpp"
|
||||||
|
#include "../replay.hpp"
|
||||||
|
#include "../unit_map.hpp"
|
||||||
|
#include "../statistics.hpp"
|
||||||
|
#include "../log.hpp"
|
||||||
|
#include "../game_display.hpp"
|
||||||
|
|
||||||
|
static lg::log_domain log_engine("engine");
|
||||||
|
#define ERR_NG LOG_STREAM(err, log_engine)
|
||||||
|
#define LOG_NG LOG_STREAM(info, log_engine)
|
||||||
|
|
||||||
|
namespace actions
|
||||||
|
{
|
||||||
|
namespace undo
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes this into the provided config.
|
||||||
|
*/
|
||||||
|
void recruit_action::write(config & cfg) const
|
||||||
|
{
|
||||||
|
undo_action::write(cfg);
|
||||||
|
shroud_clearing_action::write(cfg);
|
||||||
|
|
||||||
|
recruit_from.write(cfg.add_child("leader"));
|
||||||
|
config & child = cfg.child("unit");
|
||||||
|
child["type"] = u_type.base_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Undoes this action.
|
||||||
|
* @return true on success; false on an error.
|
||||||
|
*/
|
||||||
|
bool recruit_action::undo(int side)
|
||||||
|
{
|
||||||
|
game_display & gui = *resources::screen;
|
||||||
|
unit_map & units = *resources::units;
|
||||||
|
team ¤t_team = (*resources::teams)[side-1];
|
||||||
|
|
||||||
|
const map_location & recruit_loc = route.front();
|
||||||
|
unit_map::iterator un_it = units.find(recruit_loc);
|
||||||
|
if ( un_it == units.end() ) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const unit &un = *un_it;
|
||||||
|
statistics::un_recruit_unit(un);
|
||||||
|
current_team.spend_gold(-un.type().cost());
|
||||||
|
|
||||||
|
//MP_COUNTDOWN take away recruit bonus
|
||||||
|
current_team.set_action_bonus_count(current_team.action_bonus_count() - 1);
|
||||||
|
|
||||||
|
// invalidate before erasing allow us
|
||||||
|
// to also do the overlapped hexes
|
||||||
|
gui.invalidate(recruit_loc);
|
||||||
|
units.erase(recruit_loc);
|
||||||
|
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::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 {
|
||||||
|
gui::dialog(gui, "", msg, gui::OK_ONLY).show();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
46
src/actions/undo_recruit_action.hpp
Normal file
46
src/actions/undo_recruit_action.hpp
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "undo_action.hpp"
|
||||||
|
#include "shroud_clearing_action.hpp"
|
||||||
|
#include "../unit_ptr.hpp"
|
||||||
|
#include "../unit.hpp"
|
||||||
|
|
||||||
|
namespace actions
|
||||||
|
{
|
||||||
|
namespace undo
|
||||||
|
{
|
||||||
|
|
||||||
|
struct recruit_action : undo_action, shroud_clearing_action
|
||||||
|
{
|
||||||
|
const unit_type & u_type;
|
||||||
|
map_location recruit_from;
|
||||||
|
|
||||||
|
|
||||||
|
recruit_action(const unit_const_ptr recruited, const map_location& loc,
|
||||||
|
const map_location& from)
|
||||||
|
: undo_action()
|
||||||
|
, shroud_clearing_action(recruited, loc)
|
||||||
|
, u_type(recruited->type())
|
||||||
|
, recruit_from(from)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
recruit_action(const config & cfg, const unit_type & type, const map_location& from)
|
||||||
|
: undo_action(cfg)
|
||||||
|
, shroud_clearing_action(cfg)
|
||||||
|
, u_type(type)
|
||||||
|
, recruit_from(from)
|
||||||
|
{}
|
||||||
|
virtual const char* get_type() const { return "recruit"; }
|
||||||
|
virtual ~recruit_action() {}
|
||||||
|
|
||||||
|
/// Writes this into the provided config.
|
||||||
|
virtual void write(config & cfg) const;
|
||||||
|
|
||||||
|
/// Undoes this action.
|
||||||
|
virtual bool undo(int side);
|
||||||
|
/// Redoes this action.
|
||||||
|
virtual bool redo(int side);
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
31
src/actions/undo_update_shroud_action.cpp
Normal file
31
src/actions/undo_update_shroud_action.cpp
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
#include "undo_update_shroud_action.hpp"
|
||||||
|
#include "../resources.hpp"
|
||||||
|
#include "../team.hpp"
|
||||||
|
#include "../replay.hpp"
|
||||||
|
|
||||||
|
namespace actions
|
||||||
|
{
|
||||||
|
namespace undo
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes this into the provided config.
|
||||||
|
*/
|
||||||
|
void auto_shroud_action::write(config & cfg) const
|
||||||
|
{
|
||||||
|
undo_action_base::write(cfg);
|
||||||
|
cfg["active"] = active;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes this into the provided config.
|
||||||
|
*/
|
||||||
|
void update_shroud_action::write(config & cfg) const
|
||||||
|
{
|
||||||
|
undo_action_base::write(cfg);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
40
src/actions/undo_update_shroud_action.hpp
Normal file
40
src/actions/undo_update_shroud_action.hpp
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "undo_action.hpp"
|
||||||
|
#include "../unit_ptr.hpp"
|
||||||
|
#include "../unit.hpp"
|
||||||
|
|
||||||
|
namespace actions
|
||||||
|
{
|
||||||
|
namespace undo
|
||||||
|
{
|
||||||
|
|
||||||
|
struct auto_shroud_action : undo_action_base {
|
||||||
|
bool active;
|
||||||
|
|
||||||
|
explicit auto_shroud_action(bool turned_on)
|
||||||
|
: undo_action_base()
|
||||||
|
, active(turned_on)
|
||||||
|
{}
|
||||||
|
virtual const char* get_type() const { return "auto_shroud"; }
|
||||||
|
virtual ~auto_shroud_action() {}
|
||||||
|
|
||||||
|
/// Writes this into the provided config.
|
||||||
|
virtual void write(config & cfg) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct update_shroud_action : undo_action_base {
|
||||||
|
// No additional data.
|
||||||
|
|
||||||
|
update_shroud_action()
|
||||||
|
: undo_action_base()
|
||||||
|
{}
|
||||||
|
virtual const char* get_type() const { return "update_shroud"; }
|
||||||
|
virtual ~update_shroud_action() {}
|
||||||
|
|
||||||
|
/// Writes this into the provided config.
|
||||||
|
virtual void write(config & cfg) const;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue