Transfer of the validation logic into the action hierarchy.
This commit is contained in:
parent
72fd497294
commit
18e0ef7822
15 changed files with 420 additions and 449 deletions
|
@ -42,6 +42,7 @@ struct dummy_action: action{
|
|||
fake_unit_ptr get_fake_unit(){ return fake_unit_ptr(); }
|
||||
void set_valid(bool){}
|
||||
bool is_valid() const { return true; }
|
||||
bool validate(){ return action_ptr(); }
|
||||
};
|
||||
|
||||
BOOST_AUTO_TEST_SUITE( whiteboard_side_actions_container )
|
||||
|
|
|
@ -114,7 +114,8 @@ action::~action()
|
|||
{
|
||||
}
|
||||
|
||||
size_t action::get_unit_id() const {
|
||||
size_t action::get_unit_id() const
|
||||
{
|
||||
unit *ret = get_unit();
|
||||
return ret ? ret->underlying_id() : 0;
|
||||
}
|
||||
|
|
|
@ -90,6 +90,13 @@ public:
|
|||
virtual void set_valid(bool valid) = 0;
|
||||
virtual bool is_valid() const = 0;
|
||||
|
||||
/**
|
||||
* Recalculate the validity of the action.
|
||||
*
|
||||
* @return whether the action should be kept.
|
||||
* */
|
||||
virtual bool validate() = 0;
|
||||
|
||||
/** Constructs and returns a config object representing this object. */
|
||||
virtual config to_config() const;
|
||||
/** Constructs an object of a subclass of wb::action using a config. Current behavior is to return a null pointer for unrecognized config. */
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "attack.hpp"
|
||||
|
||||
#include "visitor.hpp"
|
||||
#include "utility.hpp"
|
||||
|
||||
#include "arrow.hpp"
|
||||
#include "play_controller.hpp"
|
||||
|
@ -214,6 +215,42 @@ void attack::draw_hex(const map_location& hex)
|
|||
}
|
||||
}
|
||||
|
||||
bool attack::validate()
|
||||
{
|
||||
DBG_WB <<"validating attack from " << get_dest_hex()
|
||||
<< " to " << target_hex_ << "\n";
|
||||
//invalidate target hex to make sure attack indicators are updated
|
||||
resources::screen->invalidate(get_dest_hex());
|
||||
resources::screen->invalidate(target_hex_);
|
||||
|
||||
if(
|
||||
// Verify that the unit that planned this attack exists
|
||||
get_unit()
|
||||
// Verify that the target hex is still valid
|
||||
&& target_hex_.valid()
|
||||
// Verify that the target hex isn't empty
|
||||
&& resources::units->find(target_hex_) != resources::units->end()
|
||||
// Verify that the attacking unit has attacks left
|
||||
&& get_unit()->attacks_left() > 0
|
||||
// Verify that the attacker and target are enemies
|
||||
&& (*resources::teams)[get_unit()->side() - 1].is_enemy(resources::units->find(target_hex_)->side())
|
||||
|
||||
//@todo: (maybe) verify that the target hex contains the same unit that before,
|
||||
// comparing for example the unit ID
|
||||
) {
|
||||
//All checks pass, so call validate on the superclass
|
||||
return move::validate();
|
||||
} else {
|
||||
set_valid(false);
|
||||
|
||||
if(viewer_team() == team_index()) { //< Don't mess with any other team's queue -- only our own
|
||||
LOG_WB << "Worthless invalid attack detected, adding to actions_to_erase_.\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
config attack::to_config() const
|
||||
{
|
||||
config final_cfg = move::to_config();
|
||||
|
|
|
@ -28,8 +28,6 @@ namespace wb
|
|||
class attack: public move
|
||||
{
|
||||
public:
|
||||
friend class validate_visitor;
|
||||
|
||||
attack(size_t team_index, bool hidden, unit& mover, const map_location& target_hex, int weapon_choice, const pathfind::marked_route& route,
|
||||
arrow_ptr arrow, fake_unit_ptr fake_unit);
|
||||
attack(config const&, bool hidden); // For deserialization
|
||||
|
@ -51,6 +49,8 @@ public:
|
|||
|
||||
map_location const& get_target_hex() const {return target_hex_; }
|
||||
|
||||
bool validate();
|
||||
|
||||
virtual config to_config() const;
|
||||
|
||||
protected:
|
||||
|
|
|
@ -421,6 +421,90 @@ void move::set_valid(bool valid)
|
|||
}
|
||||
}
|
||||
|
||||
// This helper function determines whether there are any invalid actions planned for (*itor)->get_unit()
|
||||
// that occur earlier in viewer_actions() than itor.
|
||||
static bool no_previous_invalids(side_actions::iterator const& itor)
|
||||
{
|
||||
if(itor == viewer_actions()->begin()) {
|
||||
return true;
|
||||
}
|
||||
side_actions::iterator prev_action_of_unit = viewer_actions()->find_last_action_of(*((*itor)->get_unit()),itor-1);
|
||||
if(prev_action_of_unit == viewer_actions()->end()) {
|
||||
return true;
|
||||
}
|
||||
return (*prev_action_of_unit)->is_valid();
|
||||
}
|
||||
|
||||
move::VALIDITY move::evaluate_validity()
|
||||
{
|
||||
if(!(get_source_hex().valid() && get_dest_hex().valid())) {
|
||||
return WORTHLESS;
|
||||
}
|
||||
|
||||
//Check that the unit still exists in the source hex
|
||||
unit_map::iterator unit_it;
|
||||
unit_it = resources::units->find(get_source_hex());
|
||||
if(unit_it == resources::units->end()) {
|
||||
return WORTHLESS;
|
||||
}
|
||||
|
||||
//check if the unit in the source hex has the same unit id as before,
|
||||
//i.e. that it's the same unit
|
||||
if(unit_id_ != unit_it->id() || unit_underlying_id_ != unit_it->underlying_id()) {
|
||||
return WORTHLESS;
|
||||
}
|
||||
|
||||
//If the path has at least two hexes (it can have less with the attack subclass), ensure destination hex is free
|
||||
if(get_route().steps.size() >= 2 && get_visible_unit(get_dest_hex(),resources::teams->at(viewer_team())) != NULL) {
|
||||
return WORTHLESS;
|
||||
}
|
||||
|
||||
//check that the path is good
|
||||
if(get_source_hex() != get_dest_hex()) { //skip zero-hex move used by attack subclass
|
||||
// Mark the plain route to see if the move can still be done in one turn,
|
||||
// which is always the case for planned moves
|
||||
pathfind::marked_route checked_route = pathfind::mark_route(get_route().route);
|
||||
|
||||
if(checked_route.marks[checked_route.steps.back()].turns != 1) {
|
||||
return OBSTRUCTED;
|
||||
}
|
||||
}
|
||||
|
||||
return VALID;
|
||||
}
|
||||
|
||||
bool move::validate()
|
||||
{
|
||||
DBG_WB <<"validating move from " << get_source_hex()
|
||||
<< " to " << get_dest_hex() << "\n";
|
||||
//invalidate start and end hexes so number display is updated properly
|
||||
resources::screen->invalidate(get_source_hex());
|
||||
resources::screen->invalidate(get_dest_hex());
|
||||
|
||||
switch(evaluate_validity()) { //< private helper fcn
|
||||
case VALID:
|
||||
// Now call the superclass to apply the result of this move to the unit map,
|
||||
// so that further pathfinding takes it into account.
|
||||
set_valid(true);
|
||||
break;
|
||||
case OBSTRUCTED:
|
||||
set_valid(false);
|
||||
break;
|
||||
case WORTHLESS: {
|
||||
set_valid(false);
|
||||
// Erase only if no previous invalid actions are planned for this unit -- otherwise, just mark it invalid.
|
||||
// Otherwise, we wouldn't be able to keep invalid actions that depend on previous invalid actions.
|
||||
side_actions::iterator itor = viewer_actions()->get_position_of(shared_from_this());
|
||||
if(viewer_team() == team_index() && no_previous_invalids(itor)) { //< Don't mess with any other team's queue -- only our own
|
||||
LOG_WB << "Worthless invalid move detected, adding to actions_to_erase_.\n";
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
config move::to_config() const
|
||||
{
|
||||
config final_cfg = action::to_config();
|
||||
|
|
|
@ -33,8 +33,6 @@ namespace wb {
|
|||
class move : public action
|
||||
{
|
||||
public:
|
||||
friend class validate_visitor;
|
||||
|
||||
move(size_t team_index, bool hidden, unit& mover, const pathfind::marked_route& route,
|
||||
arrow_ptr arrow, fake_unit_ptr fake_unit);
|
||||
move(config const&, bool hidden); // For deserialization
|
||||
|
@ -76,6 +74,7 @@ public:
|
|||
|
||||
virtual void set_valid(bool valid);
|
||||
virtual bool is_valid() const { return valid_; }
|
||||
bool validate();
|
||||
|
||||
virtual config to_config() const;
|
||||
|
||||
|
@ -115,6 +114,9 @@ private:
|
|||
void hide_fake_unit();
|
||||
void show_fake_unit();
|
||||
|
||||
enum VALIDITY {VALID, OBSTRUCTED, WORTHLESS};
|
||||
VALIDITY evaluate_validity();
|
||||
|
||||
void init();
|
||||
void update_arrow_style();
|
||||
boost::scoped_ptr<temporary_unit_mover> mover_;
|
||||
|
|
|
@ -176,6 +176,46 @@ void recall::draw_hex(map_location const& hex)
|
|||
}
|
||||
}
|
||||
|
||||
bool recall::validate()
|
||||
{
|
||||
DBG_WB << "validating recall on hex " << recall_hex_ << "\n";
|
||||
//invalidate recall hex so number display is updated properly
|
||||
resources::screen->invalidate(recall_hex_);
|
||||
|
||||
//Check that destination hex is still free
|
||||
if(resources::units->find(recall_hex_) != resources::units->end()) {
|
||||
LOG_WB << "Recall set as invalid because target hex is occupied.\n";
|
||||
set_valid(false);
|
||||
}
|
||||
//Check that unit to recall is still in side's recall list
|
||||
if(is_valid()) {
|
||||
const std::vector<unit>& recalls = (*resources::teams)[team_index()].recall_list();
|
||||
if( find_if_matches_id(recalls, temp_unit_->id()) == recalls.end() ) {
|
||||
set_valid(false);
|
||||
LOG_WB << " Validate visitor: Planned recall invalid since unit is not in recall list anymore.\n";
|
||||
}
|
||||
}
|
||||
//Check that there is still enough gold to recall this unit
|
||||
if(is_valid() && (*resources::teams)[team_index()].recall_cost() > (*resources::teams)[team_index()].gold()) {
|
||||
LOG_WB << "Recall set as invalid, team doesn't have enough gold.\n";
|
||||
set_valid(false);
|
||||
}
|
||||
//Check that there is a leader available to recall this unit
|
||||
if(is_valid() && !find_recruiter(team_index(),get_recall_hex())) {
|
||||
LOG_WB << "Recall set as invalid, no leader can recall this unit.\n";
|
||||
set_valid(false);
|
||||
}
|
||||
|
||||
if(!is_valid()) {
|
||||
if(viewer_team() == team_index()) { //< Don't mess with any other team's queue -- only our own
|
||||
LOG_WB << "Invalid recall detected, adding to actions_to_erase_.\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
///@todo Find a better way to serialize unit_ because underlying_id isn't cutting it
|
||||
config recall::to_config() const
|
||||
{
|
||||
|
|
|
@ -32,8 +32,6 @@ public:
|
|||
recall(config const&, bool hidden); // For deserialization
|
||||
virtual ~recall();
|
||||
|
||||
friend class validate_visitor;
|
||||
|
||||
virtual std::ostream& print(std::ostream& s) const;
|
||||
|
||||
virtual void accept(visitor& v);
|
||||
|
@ -66,6 +64,7 @@ public:
|
|||
*/
|
||||
virtual void set_valid(bool valid) { valid_ = valid; }
|
||||
virtual bool is_valid() const { return valid_; }
|
||||
bool validate();
|
||||
|
||||
virtual config to_config() const;
|
||||
|
||||
|
|
|
@ -174,6 +174,46 @@ std::auto_ptr<unit> recruit::create_corresponding_unit()
|
|||
return result; //ownership gets transferred to returned auto_ptr copy
|
||||
}
|
||||
|
||||
bool recruit::validate()
|
||||
{
|
||||
DBG_WB << "validating recruit on hex " << recruit_hex_ << "\n";
|
||||
//invalidate recruit hex so number display is updated properly
|
||||
resources::screen->invalidate(recruit_hex_);
|
||||
|
||||
//Check that destination hex is still free
|
||||
if(resources::units->find(recruit_hex_) != resources::units->end()) {
|
||||
LOG_WB << "Recruit set as invalid because target hex is occupied.\n";
|
||||
set_valid(false);
|
||||
}
|
||||
//Check that unit to recruit is still in side's recruit list
|
||||
if(is_valid()) {
|
||||
const std::set<std::string>& recruits = (*resources::teams)[team_index()].recruits();
|
||||
if(recruits.find(unit_name_) == recruits.end()) {
|
||||
set_valid(false);
|
||||
LOG_WB << " Validate visitor: Planned recruit invalid since unit is not in recruit list anymore.\n";
|
||||
}
|
||||
}
|
||||
//Check that there is still enough gold to recruit this unit
|
||||
if(is_valid() && temp_unit_->cost() > (*resources::teams)[team_index()].gold()) {
|
||||
LOG_WB << "Recruit set as invalid, team doesn't have enough gold.\n";
|
||||
set_valid(false);
|
||||
}
|
||||
//Check that there is a leader available to recruit this unit
|
||||
if(is_valid() && !find_recruiter(team_index(),get_recruit_hex())) {
|
||||
LOG_WB << "Recruit set as invalid, no leader can recruit this unit.\n";
|
||||
set_valid(false);
|
||||
}
|
||||
|
||||
if(!is_valid()) {
|
||||
if(viewer_team() == team_index()) { //< Don't mess with any other team's queue -- only our own
|
||||
LOG_WB << "Invalid recruit detected, adding to actions_to_erase_.\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
config recruit::to_config() const
|
||||
{
|
||||
config final_cfg = action::to_config();
|
||||
|
|
|
@ -36,8 +36,6 @@ public:
|
|||
recruit(config const&, bool hidden); // For deserialization
|
||||
virtual ~recruit();
|
||||
|
||||
friend class validate_visitor;
|
||||
|
||||
virtual std::ostream& print(std::ostream& s) const;
|
||||
|
||||
virtual void accept(visitor& v);
|
||||
|
@ -70,6 +68,7 @@ public:
|
|||
*/
|
||||
virtual void set_valid(bool valid) { valid_ = valid; }
|
||||
virtual bool is_valid() const { return valid_; }
|
||||
bool validate();
|
||||
|
||||
virtual config to_config() const;
|
||||
|
||||
|
|
|
@ -40,137 +40,173 @@
|
|||
namespace wb
|
||||
{
|
||||
|
||||
std::ostream& operator<<(std::ostream &s, suppose_dead_ptr sup_d)
|
||||
{
|
||||
assert(sup_d);
|
||||
return sup_d->print(s);
|
||||
}
|
||||
std::ostream& operator<<(std::ostream &s, suppose_dead_ptr sup_d)
|
||||
{
|
||||
assert(sup_d);
|
||||
return sup_d->print(s);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream &s, suppose_dead_const_ptr sup_d)
|
||||
{
|
||||
assert(sup_d);
|
||||
return sup_d->print(s);
|
||||
}
|
||||
std::ostream& operator<<(std::ostream &s, suppose_dead_const_ptr sup_d)
|
||||
{
|
||||
assert(sup_d);
|
||||
return sup_d->print(s);
|
||||
}
|
||||
|
||||
std::ostream& suppose_dead::print(std::ostream &s) const
|
||||
{
|
||||
s << "Suppose-dead for unit " << get_unit()->name() << " [" << get_unit()->id() << "] "
|
||||
<< "at (" << loc_ << ")";
|
||||
return s;
|
||||
}
|
||||
std::ostream& suppose_dead::print(std::ostream &s) const
|
||||
{
|
||||
s << "Suppose-dead for unit " << get_unit()->name() << " [" << get_unit()->id() << "] "
|
||||
<< "at (" << loc_ << ")";
|
||||
return s;
|
||||
}
|
||||
|
||||
suppose_dead::suppose_dead(size_t team_index, bool hidden, unit& curr_unit, map_location const& loc)
|
||||
: action(team_index,hidden)
|
||||
, unit_underlying_id_(curr_unit.underlying_id())
|
||||
, unit_id_(curr_unit.id())
|
||||
, loc_(loc)
|
||||
, valid_(true)
|
||||
{
|
||||
this->init();
|
||||
}
|
||||
suppose_dead::suppose_dead(size_t team_index, bool hidden, unit& curr_unit, map_location const& loc)
|
||||
: action(team_index,hidden)
|
||||
, unit_underlying_id_(curr_unit.underlying_id())
|
||||
, unit_id_(curr_unit.id())
|
||||
, loc_(loc)
|
||||
, valid_(true)
|
||||
{
|
||||
this->init();
|
||||
}
|
||||
|
||||
suppose_dead::suppose_dead(config const& cfg, bool hidden)
|
||||
: action(cfg,hidden)
|
||||
, unit_underlying_id_(0)
|
||||
, unit_id_()
|
||||
, loc_(cfg.child("loc_")["x"],cfg.child("loc_")["y"])
|
||||
, valid_(true)
|
||||
{
|
||||
// Construct and validate unit_
|
||||
unit_map::iterator unit_itor = resources::units->find(cfg["unit_"]);
|
||||
if(unit_itor == resources::units->end())
|
||||
throw action::ctor_err("suppose_dead: Invalid underlying_id");
|
||||
suppose_dead::suppose_dead(config const& cfg, bool hidden)
|
||||
: action(cfg,hidden)
|
||||
, unit_underlying_id_(0)
|
||||
, unit_id_()
|
||||
, loc_(cfg.child("loc_")["x"],cfg.child("loc_")["y"])
|
||||
, valid_(true)
|
||||
{
|
||||
// Construct and validate unit_
|
||||
unit_map::iterator unit_itor = resources::units->find(cfg["unit_"]);
|
||||
if(unit_itor == resources::units->end())
|
||||
throw action::ctor_err("suppose_dead: Invalid underlying_id");
|
||||
|
||||
unit_underlying_id_ = unit_itor->underlying_id();
|
||||
unit_id_ = unit_itor->id();
|
||||
unit_underlying_id_ = unit_itor->underlying_id();
|
||||
unit_id_ = unit_itor->id();
|
||||
|
||||
this->init();
|
||||
}
|
||||
this->init();
|
||||
}
|
||||
|
||||
void suppose_dead::init()
|
||||
{
|
||||
void suppose_dead::init()
|
||||
{
|
||||
resources::screen->invalidate(loc_);
|
||||
}
|
||||
|
||||
suppose_dead::~suppose_dead()
|
||||
{
|
||||
//invalidate hex so that skull indicator is properly cleared
|
||||
if(resources::screen)
|
||||
resources::screen->invalidate(loc_);
|
||||
}
|
||||
|
||||
unit* suppose_dead::get_unit() const
|
||||
{
|
||||
unit_map::iterator itor = resources::units->find(unit_underlying_id_);
|
||||
if (itor.valid())
|
||||
return &*itor;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void suppose_dead::accept(visitor& v)
|
||||
{
|
||||
v.visit(shared_from_this());
|
||||
}
|
||||
|
||||
void suppose_dead::execute(bool& success, bool& complete)
|
||||
{success = false; complete = true;}
|
||||
|
||||
void suppose_dead::apply_temp_modifier(unit_map& unit_map)
|
||||
{
|
||||
// Remove the unit
|
||||
unit const* removed_unit = unit_map.extract(loc_);
|
||||
DBG_WB << "Suppose dead: Temporarily removing unit " << removed_unit->name() << " [" << removed_unit->id()
|
||||
<< "] from (" << loc_ << ")\n";
|
||||
|
||||
// Just check to make sure we removed the unit we expected to remove
|
||||
assert(get_unit() == removed_unit);
|
||||
}
|
||||
|
||||
void suppose_dead::remove_temp_modifier(unit_map& unit_map)
|
||||
{
|
||||
// Just check to make sure the hex is empty
|
||||
unit_map::iterator unit_it = resources::units->find(loc_);
|
||||
assert(unit_it == resources::units->end());
|
||||
|
||||
// Restore the unit
|
||||
unit_map.insert(get_unit());
|
||||
}
|
||||
|
||||
void suppose_dead::draw_hex(const map_location& hex)
|
||||
{
|
||||
if(hex == loc_) //add symbol to hex
|
||||
{
|
||||
//@todo: Possibly use a different layer
|
||||
const display::tdrawing_layer layer = display::LAYER_ARROWS;
|
||||
|
||||
int xpos = resources::screen->get_location_x(loc_);
|
||||
int ypos = resources::screen->get_location_y(loc_);
|
||||
|
||||
resources::screen->drawing_buffer_add(layer, loc_, xpos, ypos,
|
||||
image::get_image("whiteboard/suppose_dead.png", image::SCALED_TO_HEX));
|
||||
}
|
||||
}
|
||||
|
||||
void suppose_dead::set_valid(bool valid)
|
||||
{
|
||||
valid_ = valid;
|
||||
}
|
||||
|
||||
bool suppose_dead::validate()
|
||||
{
|
||||
DBG_WB << "validating suppose_dead on hex " << loc_ << "\n";
|
||||
//invalidate suppose-dead hex so number display is updated properly
|
||||
resources::screen->invalidate(loc_);
|
||||
|
||||
if(!get_source_hex().valid()) {
|
||||
set_valid(false);
|
||||
}
|
||||
|
||||
suppose_dead::~suppose_dead()
|
||||
{
|
||||
//invalidate hex so that skull indicator is properly cleared
|
||||
if(resources::screen)
|
||||
resources::screen->invalidate(loc_);
|
||||
}
|
||||
unit_map::const_iterator unit_it;
|
||||
//Check that the unit still exists in the source hex
|
||||
if(is_valid()) {
|
||||
unit_it = resources::units->find(get_source_hex());
|
||||
|
||||
unit* suppose_dead::get_unit() const
|
||||
{
|
||||
unit_map::iterator itor = resources::units->find(unit_underlying_id_);
|
||||
if (itor.valid())
|
||||
return &*itor;
|
||||
else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void suppose_dead::accept(visitor& v)
|
||||
{
|
||||
v.visit(shared_from_this());
|
||||
}
|
||||
|
||||
void suppose_dead::execute(bool& success, bool& complete)
|
||||
{success = false; complete = true;}
|
||||
|
||||
void suppose_dead::apply_temp_modifier(unit_map& unit_map)
|
||||
{
|
||||
// Remove the unit
|
||||
unit const* removed_unit = unit_map.extract(loc_);
|
||||
DBG_WB << "Suppose dead: Temporarily removing unit " << removed_unit->name() << " [" << removed_unit->id()
|
||||
<< "] from (" << loc_ << ")\n";
|
||||
|
||||
// Just check to make sure we removed the unit we expected to remove
|
||||
assert(get_unit() == removed_unit);
|
||||
}
|
||||
|
||||
void suppose_dead::remove_temp_modifier(unit_map& unit_map)
|
||||
{
|
||||
// Just check to make sure the hex is empty
|
||||
unit_map::iterator unit_it = resources::units->find(loc_);
|
||||
assert(unit_it == resources::units->end());
|
||||
|
||||
// Restore the unit
|
||||
unit_map.insert(get_unit());
|
||||
}
|
||||
|
||||
void suppose_dead::draw_hex(const map_location& hex)
|
||||
{
|
||||
if(hex == loc_) //add symbol to hex
|
||||
{
|
||||
//@todo: Possibly use a different layer
|
||||
const display::tdrawing_layer layer = display::LAYER_ARROWS;
|
||||
|
||||
int xpos = resources::screen->get_location_x(loc_);
|
||||
int ypos = resources::screen->get_location_y(loc_);
|
||||
|
||||
resources::screen->drawing_buffer_add(layer, loc_, xpos, ypos,
|
||||
image::get_image("whiteboard/suppose_dead.png", image::SCALED_TO_HEX));
|
||||
if(unit_it == resources::units->end()) {
|
||||
set_valid(false);
|
||||
}
|
||||
}
|
||||
|
||||
void suppose_dead::set_valid(bool valid)
|
||||
{
|
||||
valid_ = valid;
|
||||
//check if the unit in the source hex has the same unit id as before,
|
||||
//i.e. that it's the same unit
|
||||
if(is_valid() && unit_id_ != unit_it->id()) {
|
||||
set_valid(false);
|
||||
}
|
||||
|
||||
config suppose_dead::to_config() const
|
||||
{
|
||||
config final_cfg = action::to_config();
|
||||
|
||||
final_cfg["type"]="suppose_dead";
|
||||
final_cfg["unit_"]=static_cast<int>(unit_underlying_id_);
|
||||
final_cfg["unit_id_"]=unit_id_;
|
||||
|
||||
config loc_cfg;
|
||||
loc_cfg["x"]=loc_.x;
|
||||
loc_cfg["y"]=loc_.y;
|
||||
final_cfg.add_child("loc_",loc_cfg);
|
||||
|
||||
return final_cfg;
|
||||
if(!is_valid()) {
|
||||
if(viewer_team() == team_index()) { //< Don't mess with any other team's queue -- only our own
|
||||
LOG_WB << "Invalid suppose_dead detected, adding to actions_to_erase_.\n";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
config suppose_dead::to_config() const
|
||||
{
|
||||
config final_cfg = action::to_config();
|
||||
|
||||
final_cfg["type"]="suppose_dead";
|
||||
final_cfg["unit_"]=static_cast<int>(unit_underlying_id_);
|
||||
final_cfg["unit_id_"]=unit_id_;
|
||||
|
||||
config loc_cfg;
|
||||
loc_cfg["x"]=loc_.x;
|
||||
loc_cfg["y"]=loc_.y;
|
||||
final_cfg.add_child("loc_",loc_cfg);
|
||||
|
||||
return final_cfg;
|
||||
}
|
||||
|
||||
} // end namespace wb
|
||||
|
|
|
@ -24,70 +24,65 @@
|
|||
|
||||
namespace wb {
|
||||
|
||||
/**
|
||||
* A planned action that temporarily removes a unit from the map
|
||||
* for planning purposes
|
||||
*/
|
||||
class suppose_dead
|
||||
: public action
|
||||
{
|
||||
friend class validate_visitor;
|
||||
/**
|
||||
* A planned action that temporarily removes a unit from the map
|
||||
* for planning purposes
|
||||
*/
|
||||
class suppose_dead: public action
|
||||
{
|
||||
public:
|
||||
suppose_dead(size_t team_index, bool hidden, unit& curr_unit, map_location const& loc);
|
||||
suppose_dead(config const&, bool hidden); // For deserialization
|
||||
virtual ~suppose_dead();
|
||||
|
||||
public:
|
||||
suppose_dead(size_t team_index, bool hidden, unit& curr_unit, map_location const& loc);
|
||||
suppose_dead(config const&, bool hidden); // For deserialization
|
||||
virtual ~suppose_dead();
|
||||
/** Return the unit targeted by this action. Null if unit doesn't exist. */
|
||||
virtual unit* get_unit() const;
|
||||
/** @return null pointer */
|
||||
virtual fake_unit_ptr get_fake_unit() { return fake_unit_ptr(); }
|
||||
/** Return the location at which this action was planned. */
|
||||
virtual map_location get_source_hex() const { return loc_; }
|
||||
|
||||
/** Return the unit targeted by this action. Null if unit doesn't exist. */
|
||||
virtual unit* get_unit() const;
|
||||
/** @return null pointer */
|
||||
virtual fake_unit_ptr get_fake_unit() { return fake_unit_ptr(); }
|
||||
/** Return the location at which this action was planned. */
|
||||
virtual map_location get_source_hex() const { return loc_; }
|
||||
virtual std::ostream& print(std::ostream& s) const;
|
||||
|
||||
// Inherits from action
|
||||
// {
|
||||
virtual std::ostream& print(std::ostream& s) const;
|
||||
virtual void accept(visitor &v);
|
||||
|
||||
virtual void accept(visitor &v);
|
||||
virtual void execute(bool& success, bool& complete);
|
||||
|
||||
virtual void execute(bool& success, bool& complete);
|
||||
/** Applies temporarily the result of this action to the specified unit map. */
|
||||
virtual void apply_temp_modifier(unit_map& unit_map);
|
||||
/** Removes the result of this action from the specified unit map. */
|
||||
virtual void remove_temp_modifier(unit_map& unit_map);
|
||||
|
||||
/** Applies temporarily the result of this action to the specified unit map. */
|
||||
virtual void apply_temp_modifier(unit_map& unit_map);
|
||||
/** Removes the result of this action from the specified unit map. */
|
||||
virtual void remove_temp_modifier(unit_map& unit_map);
|
||||
/** Gets called by display when drawing a hex, to allow actions to draw to the screen. */
|
||||
virtual void draw_hex(const map_location& hex);
|
||||
|
||||
/** Gets called by display when drawing a hex, to allow actions to draw to the screen. */
|
||||
virtual void draw_hex(const map_location& hex);
|
||||
virtual map_location get_numbering_hex() const { return loc_; }
|
||||
|
||||
virtual map_location get_numbering_hex() const { return loc_; }
|
||||
virtual void set_valid(bool valid);
|
||||
virtual bool is_valid() const { return valid_; }
|
||||
bool validate();
|
||||
|
||||
virtual void set_valid(bool valid);
|
||||
virtual bool is_valid() const { return valid_; }
|
||||
virtual config to_config() const;
|
||||
|
||||
virtual config to_config() const;
|
||||
// } End Inherits from action
|
||||
protected:
|
||||
|
||||
protected:
|
||||
boost::shared_ptr<suppose_dead> shared_from_this() {
|
||||
return boost::static_pointer_cast<suppose_dead>(action::shared_from_this());
|
||||
}
|
||||
|
||||
boost::shared_ptr<suppose_dead> shared_from_this() {
|
||||
return boost::static_pointer_cast<suppose_dead>(action::shared_from_this());
|
||||
}
|
||||
size_t unit_underlying_id_;
|
||||
std::string unit_id_;
|
||||
map_location loc_;
|
||||
|
||||
size_t unit_underlying_id_;
|
||||
std::string unit_id_;
|
||||
map_location loc_;
|
||||
bool valid_;
|
||||
|
||||
bool valid_;
|
||||
private:
|
||||
void init();
|
||||
};
|
||||
|
||||
private:
|
||||
void init();
|
||||
};
|
||||
|
||||
/** Dumps a suppose_dead on a stream, for debug purposes. */
|
||||
std::ostream &operator<<(std::ostream &s, suppose_dead_ptr sup_d);
|
||||
std::ostream &operator<<(std::ostream &s, suppose_dead_const_ptr sup_d);
|
||||
/** Dumps a suppose_dead on a stream, for debug purposes. */
|
||||
std::ostream &operator<<(std::ostream &s, suppose_dead_ptr sup_d);
|
||||
std::ostream &operator<<(std::ostream &s, suppose_dead_const_ptr sup_d);
|
||||
} // end namespace wb
|
||||
|
||||
#endif /* WB_SUPPOSE_DEAD_HPP_ */
|
||||
|
|
|
@ -57,284 +57,26 @@ bool validate_visitor::validate_actions()
|
|||
|
||||
//FIXME: by reverse iterating this can be done in a more efficiant way
|
||||
// by using the iterator returned by remove_action it could even be done in visit_all above
|
||||
if (!actions_to_erase_.empty())
|
||||
{
|
||||
if (!actions_to_erase_.empty()) {
|
||||
int side_actions_size_before = viewer_actions_.size();
|
||||
LOG_WB << "Erasing " << actions_to_erase_.size() << " invalid actions.\n";
|
||||
BOOST_FOREACH(action_ptr action, actions_to_erase_)
|
||||
{
|
||||
BOOST_FOREACH(action_ptr action, actions_to_erase_) {
|
||||
viewer_actions_.remove_action(viewer_actions_.get_position_of(action), false);
|
||||
}
|
||||
assert(side_actions_size_before - viewer_actions_.size() == actions_to_erase_.size());
|
||||
actions_to_erase_.clear();
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* private */
|
||||
validate_visitor::VALIDITY validate_visitor::evaluate_move_validity(move_ptr m_ptr)
|
||||
{
|
||||
move& m = *m_ptr;
|
||||
|
||||
if (!(m.get_source_hex().valid() && m.get_dest_hex().valid()))
|
||||
return WORTHLESS;
|
||||
|
||||
//Check that the unit still exists in the source hex
|
||||
unit_map::iterator unit_it;
|
||||
unit_it = resources::units->find(m.get_source_hex());
|
||||
if (unit_it == resources::units->end())
|
||||
return WORTHLESS;
|
||||
|
||||
//check if the unit in the source hex has the same unit id as before,
|
||||
//i.e. that it's the same unit
|
||||
if (m.unit_id_ != unit_it->id() || m.unit_underlying_id_ != unit_it->underlying_id())
|
||||
return WORTHLESS;
|
||||
|
||||
//If the path has at least two hexes (it can have less with the attack subclass), ensure destination hex is free
|
||||
if (m.get_route().steps.size() >= 2 && get_visible_unit(m.get_dest_hex(),resources::teams->at(viewer_team())) != NULL) {
|
||||
return WORTHLESS;
|
||||
}
|
||||
|
||||
//check that the path is good
|
||||
if (m.get_source_hex() != m.get_dest_hex()) //skip zero-hex move used by attack subclass
|
||||
{
|
||||
// Mark the plain route to see if the move can still be done in one turn,
|
||||
// which is always the case for planned moves
|
||||
pathfind::marked_route checked_route = pathfind::mark_route(m.get_route().route);
|
||||
|
||||
if (checked_route.marks[checked_route.steps.back()].turns != 1)
|
||||
return OBSTRUCTED;
|
||||
}
|
||||
|
||||
return VALID;
|
||||
}
|
||||
|
||||
// This helper function determines whether there are any invalid actions planned for (*itor)->get_unit()
|
||||
// that occur earlier in viewer_actions_ than itor.
|
||||
/* private */
|
||||
bool validate_visitor::no_previous_invalids(side_actions::iterator const& itor)
|
||||
{
|
||||
if(itor == viewer_actions_.begin())
|
||||
return true;
|
||||
side_actions::iterator prev_action_of_unit = viewer_actions_.find_last_action_of(*((*itor)->get_unit()),itor-1);
|
||||
if(prev_action_of_unit == viewer_actions_.end())
|
||||
return true;
|
||||
return (*prev_action_of_unit)->is_valid();
|
||||
}
|
||||
|
||||
void validate_visitor::visit(move_ptr move)
|
||||
{
|
||||
DBG_WB <<"visiting move from " << move->get_source_hex()
|
||||
<< " to " << move->get_dest_hex() << "\n";
|
||||
//invalidate start and end hexes so number display is updated properly
|
||||
resources::screen->invalidate(move->get_source_hex());
|
||||
resources::screen->invalidate(move->get_dest_hex());
|
||||
|
||||
switch(evaluate_move_validity(move)) //< private helper fcn
|
||||
{
|
||||
case VALID:
|
||||
// Now call the superclass to apply the result of this move to the unit map,
|
||||
// so that further pathfinding takes it into account.
|
||||
move->set_valid(true);
|
||||
break;
|
||||
case OBSTRUCTED:
|
||||
move->set_valid(false);
|
||||
break;
|
||||
case WORTHLESS:
|
||||
move->set_valid(false);
|
||||
// Erase only if no previous invalid actions are planned for this unit -- otherwise, just mark it invalid.
|
||||
// Otherwise, we wouldn't be able to keep invalid actions that depend on previous invalid actions.
|
||||
if(viewer_team() == move->team_index() //< Don't mess with any other team's queue -- only our own
|
||||
&& no_previous_invalids(arg_itor_)) //< private helper fcn
|
||||
{
|
||||
LOG_WB << "Worthless invalid move detected, adding to actions_to_erase_.\n";
|
||||
actions_to_erase_.insert(move);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void validate_visitor::visit(attack_ptr attack)
|
||||
{
|
||||
DBG_WB <<"visiting attack from " << attack->get_dest_hex()
|
||||
<< " to " << attack->target_hex_ << "\n";
|
||||
//invalidate target hex to make sure attack indicators are updated
|
||||
resources::screen->invalidate(attack->get_dest_hex());
|
||||
resources::screen->invalidate(attack->target_hex_);
|
||||
|
||||
if (
|
||||
// Verify that the unit that planned this attack exists
|
||||
attack->get_unit()
|
||||
// Verify that the target hex is still valid
|
||||
&& attack->target_hex_.valid()
|
||||
// Verify that the target hex isn't empty
|
||||
&& resources::units->find(attack->target_hex_) != resources::units->end()
|
||||
// Verify that the attacking unit has attacks left
|
||||
&& attack->get_unit()->attacks_left() > 0
|
||||
// Verify that the attacker and target are enemies
|
||||
&& (*resources::teams)[attack->get_unit()->side() - 1].is_enemy(resources::units->find(attack->target_hex_)->side())
|
||||
|
||||
//@todo: (maybe) verify that the target hex contains the same unit that before,
|
||||
// comparing for example the unit ID
|
||||
)
|
||||
{
|
||||
//All checks pass, so call the visitor on the superclass
|
||||
visit(boost::static_pointer_cast<move>(attack));
|
||||
}
|
||||
else
|
||||
{
|
||||
attack->set_valid(false);
|
||||
|
||||
if (viewer_team() == attack->team_index()) //< Don't mess with any other team's queue -- only our own
|
||||
{
|
||||
LOG_WB << "Worthless invalid attack detected, adding to actions_to_erase_.\n";
|
||||
actions_to_erase_.insert(attack);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void validate_visitor::visit(recruit_ptr recruit)
|
||||
{
|
||||
DBG_WB << "visiting recruit on hex " << recruit->recruit_hex_ << "\n";
|
||||
//invalidate recruit hex so number display is updated properly
|
||||
resources::screen->invalidate(recruit->recruit_hex_);
|
||||
|
||||
size_t team_index = recruit->team_index();
|
||||
|
||||
//Check that destination hex is still free
|
||||
if(resources::units->find(recruit->recruit_hex_) != resources::units->end())
|
||||
{
|
||||
LOG_WB << "Recruit set as invalid because target hex is occupied.\n";
|
||||
recruit->set_valid(false);
|
||||
}
|
||||
//Check that unit to recruit is still in side's recruit list
|
||||
if (recruit->is_valid())
|
||||
{
|
||||
const std::set<std::string>& recruits = (*resources::teams)[team_index].recruits();
|
||||
if (recruits.find(recruit->unit_name_) == recruits.end())
|
||||
{
|
||||
recruit->set_valid(false);
|
||||
LOG_WB << " Validate visitor: Planned recruit invalid since unit is not in recruit list anymore.\n";
|
||||
}
|
||||
}
|
||||
//Check that there is still enough gold to recruit this unit
|
||||
if (recruit->is_valid() && recruit->temp_unit_->cost() > (*resources::teams)[team_index].gold())
|
||||
{
|
||||
LOG_WB << "Recruit set as invalid, team doesn't have enough gold.\n";
|
||||
recruit->set_valid(false);
|
||||
}
|
||||
//Check that there is a leader available to recruit this unit
|
||||
if(recruit->is_valid() && !find_recruiter(recruit->team_index(),recruit->get_recruit_hex()))
|
||||
{
|
||||
LOG_WB << "Recruit set as invalid, no leader can recruit this unit.\n";
|
||||
recruit->set_valid(false);
|
||||
}
|
||||
|
||||
if(!recruit->is_valid())
|
||||
{
|
||||
if(viewer_team() == recruit->team_index()) //< Don't mess with any other team's queue -- only our own
|
||||
{
|
||||
LOG_WB << "Invalid recruit detected, adding to actions_to_erase_.\n";
|
||||
actions_to_erase_.insert(recruit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void validate_visitor::visit(recall_ptr recall)
|
||||
{
|
||||
DBG_WB << "visiting recall on hex " << recall->recall_hex_ << "\n";
|
||||
//invalidate recall hex so number display is updated properly
|
||||
resources::screen->invalidate(recall->recall_hex_);
|
||||
|
||||
size_t team_index = recall->team_index();
|
||||
|
||||
//Check that destination hex is still free
|
||||
if(resources::units->find(recall->recall_hex_) != resources::units->end())
|
||||
{
|
||||
LOG_WB << "Recall set as invalid because target hex is occupied.\n";
|
||||
recall->set_valid(false);
|
||||
}
|
||||
//Check that unit to recall is still in side's recall list
|
||||
if (recall->is_valid())
|
||||
{
|
||||
const std::vector<unit>& recalls = (*resources::teams)[team_index].recall_list();
|
||||
if ( find_if_matches_id(recalls, recall->temp_unit_->id()) == recalls.end() )
|
||||
{
|
||||
recall->set_valid(false);
|
||||
LOG_WB << " Validate visitor: Planned recall invalid since unit is not in recall list anymore.\n";
|
||||
}
|
||||
}
|
||||
//Check that there is still enough gold to recall this unit
|
||||
if (recall->is_valid()
|
||||
&& (*resources::teams)[team_index].recall_cost() > (*resources::teams)[team_index].gold())
|
||||
{
|
||||
LOG_WB << "Recall set as invalid, team doesn't have enough gold.\n";
|
||||
recall->set_valid(false);
|
||||
}
|
||||
//Check that there is a leader available to recall this unit
|
||||
if(recall->is_valid() && !find_recruiter(recall->team_index(),recall->get_recall_hex()))
|
||||
{
|
||||
LOG_WB << "Recall set as invalid, no leader can recall this unit.\n";
|
||||
recall->set_valid(false);
|
||||
}
|
||||
|
||||
|
||||
if(!recall->is_valid())
|
||||
{
|
||||
if(viewer_team() == recall->team_index()) //< Don't mess with any other team's queue -- only our own
|
||||
{
|
||||
LOG_WB << "Invalid recall detected, adding to actions_to_erase_.\n";
|
||||
actions_to_erase_.insert(recall);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void validate_visitor::visit(suppose_dead_ptr sup_d)
|
||||
{
|
||||
DBG_WB << "visiting suppose_dead on hex " << sup_d->loc_ << "\n";
|
||||
//invalidate suppose-dead hex so number display is updated properly
|
||||
resources::screen->invalidate(sup_d->loc_);
|
||||
|
||||
if(!sup_d->get_source_hex().valid())
|
||||
sup_d->set_valid(false);
|
||||
|
||||
unit_map::const_iterator unit_it;
|
||||
//Check that the unit still exists in the source hex
|
||||
if(sup_d->valid_)
|
||||
{
|
||||
unit_it = resources::units->find(sup_d->get_source_hex());
|
||||
|
||||
if(unit_it == resources::units->end())
|
||||
{
|
||||
sup_d->set_valid(false);
|
||||
}
|
||||
}
|
||||
|
||||
//check if the unit in the source hex has the same unit id as before,
|
||||
//i.e. that it's the same unit
|
||||
if(sup_d->valid_ && sup_d->unit_id_ != unit_it->id())
|
||||
{
|
||||
sup_d->set_valid(false);
|
||||
}
|
||||
|
||||
if(!sup_d->valid_)
|
||||
{
|
||||
if(viewer_team() == sup_d->team_index()) //< Don't mess with any other team's queue -- only our own
|
||||
{
|
||||
LOG_WB << "Invalid suppose_dead detected, adding to actions_to_erase_.\n";
|
||||
actions_to_erase_.insert(sup_d);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void validate_visitor::helper::validate(side_actions::iterator const& itor)
|
||||
{
|
||||
parent_.arg_itor_ = itor;
|
||||
(*itor)->accept(parent_);
|
||||
if((*itor)->validate()) {
|
||||
parent_.actions_to_erase_.insert(*itor);
|
||||
}
|
||||
}
|
||||
|
||||
}//end namespace wb
|
||||
|
|
|
@ -36,7 +36,6 @@ namespace wb
|
|||
* * Some invalid actions are deleted.
|
||||
*/
|
||||
class validate_visitor
|
||||
: private visitor
|
||||
{
|
||||
public:
|
||||
explicit validate_visitor(unit_map& unit_map);
|
||||
|
@ -47,17 +46,6 @@ public:
|
|||
bool validate_actions();
|
||||
|
||||
private:
|
||||
virtual void visit(move_ptr move);
|
||||
virtual void visit(attack_ptr attack);
|
||||
virtual void visit(recruit_ptr recruit);
|
||||
virtual void visit(recall_ptr recall);
|
||||
virtual void visit(suppose_dead_ptr sup_d);
|
||||
|
||||
enum VALIDITY {VALID, OBSTRUCTED, WORTHLESS};
|
||||
VALIDITY evaluate_move_validity(move_ptr);
|
||||
|
||||
bool no_previous_invalids(side_actions::iterator const&);
|
||||
|
||||
struct helper: public mapbuilder
|
||||
{
|
||||
helper(unit_map& umap, validate_visitor& parent)
|
||||
|
|
Loading…
Add table
Reference in a new issue