Whiteboard: Get rid of the find_visitor, and refactor side_actions...

...to use iterators instead of a mix of indexes and shared pointers
This commit is contained in:
Gabriel Morin 2010-07-01 09:32:41 +00:00
parent 5b89f29054
commit b1b1fa87a4
10 changed files with 161 additions and 272 deletions

View file

@ -467,7 +467,6 @@ set(wesnoth-main_SRC
whiteboard/action.cpp
whiteboard/manager.cpp
whiteboard/move.cpp
whiteboard/find_visitor.cpp
whiteboard/highlight_visitor.cpp
whiteboard/mapbuilder_visitor.cpp
whiteboard/side_actions.cpp

View file

@ -284,7 +284,6 @@ wesnoth_source = \
whiteboard/action.cpp \
whiteboard/move.cpp \
whiteboard/manager.cpp \
whiteboard/find_visitor.cpp \
whiteboard/highlight_visitor.cpp \
whiteboard/mapbuilder_visitor.cpp \
whiteboard/side_actions.cpp \

View file

@ -266,7 +266,6 @@ wesnoth_sources = Split("""
whiteboard/action.cpp
whiteboard/manager.cpp
whiteboard/move.cpp
whiteboard/find_visitor.cpp
whiteboard/highlight_visitor.cpp
whiteboard/mapbuilder_visitor.cpp
whiteboard/side_actions.cpp

View file

@ -607,7 +607,7 @@ void mouse_handler::select_hex(const map_location& hex, const bool browse) {
if (!browse && !commands_disabled && u->side() == gui().viewing_side()) {
sound::play_UI_sound("select-unit.wav");
if (!(resources::whiteboard->is_active() && resources::whiteboard->get_first_action_of(*u))) {
if (!(resources::whiteboard->is_active() && resources::whiteboard->unit_has_actions(*u))) {
u->set_selecting();
game_events::fire("select", hex);
}

View file

@ -1,72 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2010 by Gabriel Morin <gabrielmorin (at) gmail (dot) com>
Part of the Battle for Wesnoth Project http://www.wesnoth.org
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
or at your option any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file find_visitor.cpp
*/
#include "find_visitor.hpp"
#include "manager.hpp"
#include "move.hpp"
#include "foreach.hpp"
namespace wb
{
find_visitor::find_visitor()
: search_target_(NULL)
, search_result_()
{
}
find_visitor::~find_visitor()
{
}
void find_visitor::visit_move(boost::shared_ptr<move> move)
{
if( &move->get_unit() == search_target_ )
{
search_result_.push_back(move);
}
}
action_set find_visitor::find_actions_of(const unit& unit, action_set actions)
{
search_target_ = &unit;
search_result_.clear();
foreach (action_ptr a, actions)
{
a->accept(*this);
}
return search_result_;
}
action_ptr find_visitor::find_first_action_of(const unit& unit, action_set actions)
{
search_target_ = &unit;
search_result_.clear();
foreach (action_ptr a, actions)
{
a->accept(*this);
if (!search_result_.empty())
{
return search_result_[0];
}
}
return action_ptr();
}
}//end namespace wb

View file

@ -1,51 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2010 by Gabriel Morin <gabrielmorin (at) gmail (dot) com>
Part of the Battle for Wesnoth Project http://www.wesnoth.org
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2
or at your option any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY.
See the COPYING file for more details.
*/
/**
* @file find_visitor.hpp
*/
#ifndef WB_FIND_VISITOR_HPP_
#define WB_FIND_VISITOR_HPP_
#include "visitor.hpp"
#include "side_actions.hpp"
class unit;
namespace wb
{
/**
* Visitor to find the action(s) associated with a unit.
*/
class find_visitor: public visitor
{
public:
find_visitor();
virtual ~find_visitor();
virtual void visit_move(boost::shared_ptr<move> move);
virtual action_set find_actions_of(const unit& unit, action_set actions);
virtual action_ptr find_first_action_of(const unit& unit, action_set actions);
private:
const unit* search_target_;
action_set search_result_;
};
}
#endif /* WB_FIND_VISITOR_HPP_ */

View file

@ -19,10 +19,10 @@
#include "manager.hpp"
#include "action.hpp"
#include "find_visitor.hpp"
#include "highlight_visitor.hpp"
#include "mapbuilder_visitor.hpp"
#include "move.hpp"
#include "side_actions.hpp"
#include "arrow.hpp"
#include "foreach.hpp"
@ -44,7 +44,7 @@ manager::manager():
selected_unit_(NULL),
highlighted_unit_(NULL),
move_saving_mutex_(),
planned_unit_map_(false)
planned_unit_map_active_(false)
{
}
@ -56,7 +56,7 @@ manager::~manager()
}
}
static side_actions_ptr get_current_side_actions()
static side_actions_ptr current_actions()
{
int current_side = resources::controller->current_side();
team& current_team = (*resources::teams)[current_side - 1];
@ -68,18 +68,18 @@ void manager::set_planned_unit_map()
{
if (active_)
{
assert (!planned_unit_map_);
if (!planned_unit_map_)
assert (!planned_unit_map_active_);
if (!planned_unit_map_active_)
{
mapbuilder_.reset(new mapbuilder_visitor(*resources::units));
const action_set& actions = get_current_side_actions()->actions();
const action_set& actions = current_actions()->actions();
DBG_WB << "Building planned unit map.\n";
foreach (const action_ptr &action, actions)
{
assert(action);
action->accept(*mapbuilder_);
}
planned_unit_map_ = true;
planned_unit_map_active_ = true;
}
}
}
@ -88,18 +88,20 @@ void manager::set_real_unit_map()
{
if (active_)
{
assert (planned_unit_map_);
if (planned_unit_map_)
assert (planned_unit_map_active_);
if (planned_unit_map_active_)
{
DBG_WB << "Restoring regular unit map.\n";
mapbuilder_.reset();
planned_unit_map_ = false;
planned_unit_map_active_ = false;
}
}
}
void manager::on_mouseover_change(const map_location& hex)
{
//FIXME: Detect if a WML event is executing, and if so, avoid modifying the unit map during that time.
// Acting otherwise causes a crash.
if (active_ && !selected_unit_)
{
remove_highlight();
@ -123,7 +125,7 @@ void manager::highlight_hex(const map_location& hex)
}
else
{
action_set actions = get_current_side_actions()->actions();
action_set actions = current_actions()->actions();
foreach(action_ptr action, actions)
{
if (action->is_related_to(hex))
@ -137,7 +139,7 @@ void manager::highlight_hex(const map_location& hex)
{
highlight_visitor highlighter(true);
action_set actions = get_current_side_actions()->actions();
action_set actions = current_actions()->actions();
foreach(action_ptr action, actions)
{
if (action->is_related_to(*highlighted_unit_))
@ -152,7 +154,7 @@ void manager::remove_highlight()
{
highlight_visitor unhighlighter(false);
action_set actions = get_current_side_actions()->actions();
action_set actions = current_actions()->actions();
foreach(action_ptr action, actions)
{
action->accept(unhighlighter);
@ -164,8 +166,8 @@ void manager::on_unit_select(unit& unit)
{
erase_temp_move();
remove_highlight();
get_current_side_actions()->set_future_view(true);
action_set actions = get_current_side_actions()->actions();
current_actions()->set_future_view(true);
action_set actions = current_actions()->actions();
highlight_visitor highlighter(true);
foreach(action_ptr action, actions)
{
@ -232,7 +234,8 @@ void manager::erase_temp_move()
//reset src unit back to normal, if it lacks any planned action,
//and we're not in the process of saving a move
wb_scoped_lock try_lock(move_saving_mutex_, boost::interprocess::try_to_lock);
if (try_lock && selected_unit_ && !get_first_action_of(*selected_unit_))
if (try_lock && selected_unit_
&& current_actions()->find_first_action_of(*selected_unit_) == current_actions()->end())
{
selected_unit_->set_standing(true);
}
@ -251,7 +254,9 @@ void manager::save_temp_move()
fake_unit_ptr fake_unit;
unit* target_unit;
{ //This block of code protected against simultaneous execution by multiple threads
{
// Wait until the block is finished and the variables have finished copying,
// before granting another thread access.
wb_scoped_lock lock(move_saving_mutex_); //waits for lock
route = route_;
@ -263,8 +268,6 @@ void manager::save_temp_move()
erase_temp_move();
selected_unit_ = NULL;
//TODO: properly handle movement points
LOG_WB << "Creating move for unit " << target_unit->name() << " [" << target_unit->id() << "]"
<< " from " << route.front()
@ -273,10 +276,10 @@ void manager::save_temp_move()
assert(!has_planned_unit_map());
target_unit->set_ghosted(false); //FIXME: doesn't take effect until after the move animation, boucman: help!
target_unit->set_ghosted(false);
unit_display::move_unit(route, *fake_unit, *resources::teams, true);
get_current_side_actions()->queue_move(*target_unit, route.front(), route.back(), move_arrow, fake_unit);
current_actions()->queue_move(*target_unit, route.front(), route.back(), move_arrow, fake_unit);
}
void manager::contextual_execute()
@ -285,25 +288,28 @@ void manager::contextual_execute()
if (!try_lock)
return;
//TODO: catch end_turn_exception somewhere here?
//TODO: properly handle movement points
get_current_side_actions()->set_future_view(false);
if (!current_actions()->empty())
{
//TODO: catch end_turn_exception somewhere here?
//TODO: properly handle movement points, probably through the mapbuilder_visitor
current_actions()->set_future_view(false);
if (selected_unit_)
{
get_current_side_actions()->execute(get_first_action_of(*selected_unit_));
}
else if (highlighted_unit_)
{
get_current_side_actions()->execute(get_first_action_of(*highlighted_unit_));
}
else
{
get_current_side_actions()->execute_next();
}
if (selected_unit_)
{
current_actions()->execute(current_actions()->find_first_action_of(*selected_unit_));
}
else if (highlighted_unit_)
{
current_actions()->execute(current_actions()->find_first_action_of(*highlighted_unit_));
}
else
{
current_actions()->execute_next();
}
get_current_side_actions()->set_future_view(true);
current_actions()->set_future_view(true);
}
}
//TODO: transfer most of this function into side_actions
@ -313,15 +319,14 @@ void manager::delete_last()
if (!try_lock)
return;
get_current_side_actions()->remove_action(get_current_side_actions()->end() - 1);
if (!current_actions()->empty())
current_actions()->remove_action(current_actions()->end() - 1);
}
//TODO: better to move this function into side_actions
action_ptr manager::get_first_action_of(const unit& unit) const
bool manager::unit_has_actions(const unit& unit) const
{
find_visitor finder;
action_ptr action = finder.find_first_action_of(unit, get_current_side_actions()->actions());
return action;
return current_actions()->find_first_action_of(unit)
!= current_actions()->end();
}
scoped_planned_unit_map::scoped_planned_unit_map()

View file

@ -59,7 +59,7 @@ public:
*/
void set_planned_unit_map();
void set_real_unit_map();
bool has_planned_unit_map() { return planned_unit_map_; }
bool has_planned_unit_map() const { return planned_unit_map_active_; }
/**
* Highlights the action for this unit,
@ -95,9 +95,8 @@ public:
/** Deletes last action in the queue for current side */
void delete_last();
/** Checks whether the specified unit has at least one planned action,
* and returns the first action found. */
boost::shared_ptr<action> get_first_action_of(const unit& unit) const;
/** Checks whether the specified unit has at least one planned action */
bool unit_has_actions(const unit& unit) const;
private:
/**
@ -123,7 +122,7 @@ private:
//TODO: this mutex might find a better home within the side_actions class.
wb_mutex actions_modification_mutex_;
bool planned_unit_map_;
bool planned_unit_map_active_;
};
struct scoped_planned_unit_map

View file

@ -42,87 +42,79 @@ const action_set& side_actions::actions() const
return actions_;
}
void side_actions::execute_next()
side_actions::iterator side_actions::execute_next()
{
if (!actions_.empty())
{
bool finished = actions_.front()->execute();
if (finished)
{
actions_.pop_front();
update_last_action_display();
}
validate_actions();
execute(begin());
return begin();
}
return end();
}
void side_actions::execute(size_t index)
side_actions::iterator side_actions::execute(side_actions::iterator position)
{
if (!actions_.empty() && index < end())
{
execute(actions_[index]);
}
}
void side_actions::execute(action_ptr action)
{
if (!actions_.empty())
if (!actions_.empty() && validate_iterator(position))
{
size_t distance = std::distance(begin(), position);
action_ptr& action = *position;
bool finished = action->execute();
if (finished)
{
remove_action(action);
remove_action(position);
}
validate_actions();
return begin() + distance;
}
else
{
return end();
}
}
void side_actions::insert_move(unit& subject, const map_location& source_hex, const map_location& target_hex, size_t index, boost::shared_ptr<arrow> arrow,
boost::shared_ptr<unit> fake_unit)
side_actions::iterator side_actions::insert_move(unit& subject, const map_location& source_hex, const map_location& target_hex, side_actions::iterator position,
boost::shared_ptr<arrow> arrow, boost::shared_ptr<unit> fake_unit)
{
action_ptr action(new move(subject, source_hex, target_hex, arrow, fake_unit));
assert(index < end());
actions_.insert(actions_.begin() + index, action);
assert(position < end());
iterator valid_position = actions_.insert(position, action);
validate_actions();
update_last_action_display();
return valid_position;
}
void side_actions::queue_move(unit& subject, const map_location& source_hex, const map_location& target_hex,
side_actions::iterator side_actions::queue_move(unit& subject, const map_location& source_hex, const map_location& target_hex,
boost::shared_ptr<arrow> arrow, boost::shared_ptr<unit> fake_unit)
{
action_ptr action(new move(subject, source_hex, target_hex, arrow, fake_unit));
actions_.push_back(action);
// Contrary to insert_move, no need to validate actions here since we're adding to the end of the queue
update_last_action_display();
return end() - 1;
}
void side_actions::move_earlier(size_t index, size_t increment)
side_actions::iterator side_actions::move_earlier(side_actions::iterator position, size_t increment)
{
move_in_queue(index, - (int) increment);
return move_in_queue(position, - (int) increment);
}
void side_actions::move_later(size_t index, size_t increment)
side_actions::iterator side_actions::move_later(side_actions::iterator position, size_t increment)
{
move_in_queue(index, (int) increment);
return move_in_queue(position, (int) increment);
}
void side_actions::remove_action(size_t index)
side_actions::iterator side_actions::remove_action(side_actions::iterator position)
{
if(!actions_.empty())
assert((!actions_.empty() && validate_iterator(position)));
size_t distance = std::distance(begin(), position);
if (!actions_.empty() && validate_iterator(position))
{
assert(index < end());
action_set::iterator position = actions_.begin()+index;
if (position < actions_.end())
{
actions_.erase(position);
validate_actions();
update_last_action_display();
}
actions_.erase(position);
validate_actions();
}
return begin() + distance;
}
void side_actions::remove_action(action_ptr action)
side_actions::iterator side_actions::get_position_of(action_ptr action)
{
if (!actions_.empty())
{
@ -131,13 +123,30 @@ void side_actions::remove_action(action_ptr action)
{
if (*position == action)
{
actions_.erase(position);
validate_actions();
update_last_action_display();
break;
return position;
}
}
}
return end();
}
side_actions::iterator side_actions::find_first_action_of(const unit& unit, side_actions::iterator start_position)
{
if (start_position == side_actions::iterator())
{
start_position = begin();
}
side_actions::iterator position;
for (position = start_position; position != end(); ++position)
{
action_ptr& action = *position;
if (&action->get_unit() == &unit)
{
return position;
}
}
return end();
}
void side_actions::set_future_view(bool future_view)
@ -175,43 +184,23 @@ void side_actions::validate_actions()
{
action->accept(validator);
}
update_last_action_display();
}
/*
* Utility function to move actions around the queue.
* Positive increment = move toward back of the queue and later execution.
* Negative increment = move toward front of the queue and earlier execution.
*/
void side_actions::move_in_queue(size_t index, int increment)
side_actions::iterator side_actions::move_in_queue(side_actions::iterator position, int increment)
{
assert(!actions_.empty());
assert(index < end());
if (actions_.empty() || index >= end())
{
return;
}
assert(validate_iterator(position));
if (actions_.empty() || !validate_iterator(position))
return end();
action_set::iterator position;
position = actions_.begin() + index;
assert(index + increment < end());
if (index + increment >= end())
{
increment = int(end()) - index;
}
assert(int(index) + increment >= 0);
if (int(index) + increment < 0)
{
increment = -index;
}
action_ptr action = *position;
const action_ptr& action = *position;
action_set::iterator after = actions_.erase(position);
//be careful, previous iterators have just been invalidated by erase()
action_set::iterator destination = after + increment;
actions_.insert(destination, action);
action_set::iterator valid_position = actions_.insert(destination, action);
validate_actions();
return valid_position;
}

View file

@ -40,6 +40,10 @@ typedef std::deque<action_ptr> action_set;
class side_actions
{
public:
typedef action_set::iterator iterator;
typedef action_set::const_iterator const_iterator;
side_actions();
virtual ~side_actions();
@ -47,62 +51,78 @@ public:
/**
* Executes the first action in the queue, and then deletes it.
* TODO: Validate all subsequent actions after doing this!
* @return An iterator to the action itself if not finished, or else to the new first in line.
* Returns end() if no actions remain.
*/
void execute_next();
iterator execute_next();
/**
* Executes the specified action, if it exists in the queue.
* @return An iterator to the action itself if not finished, or else the next action in the queue.
* Returns end() if no actions remain.
*/
void execute(size_t index);
void execute(action_ptr action);
iterator execute(iterator position);
/**
* Returns the index for the first (executed earlier) action within the actions set.
* Returns the iterator for the first (executed earlier) action within the actions set.
*/
size_t begin() {return 0; }
iterator begin() {return actions_.begin(); }
/**
* Returns the index for the position *after* the last executed action within the actions set.
* Returns the iterator for the position *after* the last executed action within the actions set.
*/
size_t end() { return actions_.size(); }
iterator end() { return actions_.end(); }
/**
* Inserts a move at the specified index. The begin() and end() functions might prove useful here.
* Indicates whether the action queue is empty.
*/
void insert_move(unit& subject, const map_location& source_hex, const map_location& target_hex, size_t index,
bool empty() { return actions_.empty(); }
/**
* Inserts a move at the specified position. The begin() and end() functions might prove useful here.
* @return The inserted move's position.
*/
iterator insert_move(unit& subject, const map_location& source_hex, const map_location& target_hex, iterator position,
boost::shared_ptr<arrow> arrow, boost::shared_ptr<unit> fake_unit);
/**
* Inserts a move to be executed last (i.e. at the back of the queue)
* @return The queued move's position
*/
void queue_move(unit& subject, const map_location& source_hex, const map_location& target_hex,
iterator queue_move(unit& subject, const map_location& source_hex, const map_location& target_hex,
boost::shared_ptr<arrow> arrow, boost::shared_ptr<unit> fake_unit);
/**
* Moves an action earlier in the execution order (i.e. at the front of the queue),
* by the specified increment.
* If the increment is too large, the action will be simply moved at the earliest position.
* @return The action's new position.
*/
void move_earlier(size_t index, size_t increment);
iterator move_earlier(iterator position, size_t increment);
/**
* Moves an action later in the execution order (i.e. at the back of the queue),
* by the specified increment.
* If the increment is too large, the action will be simply moved at the latest position.
* @return The action's new position.
*/
void move_later(size_t index, size_t increment);
iterator move_later(iterator position, size_t increment);
/**
* Deletes the action at the specified index. The begin() and end() functions might prove useful here.
* If the index doesn't exist, the function does nothing.
* Deletes the action at the specified position.
* @return The position of the element after the one deleted, or end() if the queue is empty.
*/
void remove_action(size_t index);
iterator remove_action(iterator position);
/**
* Deletes the specified action. If the action doesn't exist, the function does nothing.
* @param action The action whose position you're looking for
* @return The action's position within the queue, or end() if action wasn't found.
*/
void remove_action(action_ptr action);
iterator get_position_of(action_ptr action);
/**
* Finds the first action that belongs to this unit, starting the search at the specified position.
* @return The position, or end() if not found.
*/
iterator find_first_action_of(const unit& unit, iterator start_position = iterator());
/**
* future_view = true : units are shown at their future positions
@ -118,14 +138,16 @@ public:
void validate_actions();
private:
/**
* Utility function to move actions around the queue.
* Utility function to move an action around the queue.
* Positive increment = move toward back of the queue and later execution.
* Negative increment = move toward front of the queue and earlier execution.
* @return The action's new position.
*/
void move_in_queue(size_t index, int increment);
iterator move_in_queue(iterator position, int increment);
bool validate_iterator(iterator position) { return position >= begin() && position < end(); }
action_set actions_;
};