Arrows/Whiteboard: big and hopefully final refactoring;...

...got rid of observer pattern and settled on a lifecycle management
strategy.
This commit is contained in:
Gabriel Morin 2010-06-15 20:37:35 +00:00
parent d3da33b68b
commit 1c7c79a2c3
11 changed files with 94 additions and 147 deletions

View file

@ -18,11 +18,11 @@
*/
#include "arrow.hpp"
#include "arrow_observer.hpp"
#include "foreach.hpp"
#include "log.hpp"
#include "map_location.hpp"
#include "resources.hpp"
static lg::log_domain log_arrows("arrows");
#define ERR_ARR LOG_STREAM(err, log_arrows)
@ -30,8 +30,8 @@ static lg::log_domain log_arrows("arrows");
#define LOG_ARR LOG_STREAM(info, log_arrows)
#define DBG_ARR LOG_STREAM(debug, log_arrows)
arrow::arrow(display* screen)
: screen_(screen)
arrow::arrow()
: screen_((display*&) resources::screen)
, layer_(display::LAYER_ARROWS)
, color_("red")
, style_("")
@ -39,12 +39,15 @@ arrow::arrow(display* screen)
, path_()
, previous_path_()
, symbols_map_()
, observers_()
{
}
arrow::~arrow()
{
if (screen_)
{
screen_->remove_arrow(*this);
}
}
bool arrow::set_path(const arrow_path_t &path)
@ -62,6 +65,15 @@ bool arrow::set_path(const arrow_path_t &path)
}
}
void arrow::clear_path()
{
invalidate_arrow_path(path_);
path_.clear();
previous_path_.clear();
symbols_map_.clear();
notify_arrow_changed();
}
void arrow::set_color(const std::string& color)
{
color_ = color;
@ -111,6 +123,8 @@ const arrow_path_t & arrow::get_previous_path() const
void arrow::draw_hex(const map_location & loc)
{
if(!screen_) return;
screen_->render_image(screen_->get_location_x(loc), screen_->get_location_y(loc), layer_,
loc, image::get_image(symbols_map_[loc], image::SCALED_TO_ZOOM), false, false, alpha_);
}
@ -123,16 +137,6 @@ bool arrow::valid_path(arrow_path_t path) const
return false;
}
void arrow::add_observer(arrow_observer & observer)
{
observers_.push_back(&observer);
}
void arrow::remove_observer(arrow_observer & observer)
{
observers_.remove(&observer);
}
void arrow::update_symbols(arrow_path_t old_path)
{
if (!valid_path(path_))
@ -250,6 +254,8 @@ void arrow::update_symbols(arrow_path_t old_path)
void arrow::invalidate_arrow_path(arrow_path_t path)
{
if(!screen_) return;
foreach(const map_location& loc, path)
{
screen_->invalidate(loc);
@ -258,8 +264,7 @@ void arrow::invalidate_arrow_path(arrow_path_t path)
void arrow::notify_arrow_changed()
{
foreach(arrow_observer* observer, observers_)
{
observer->arrow_changed(*this);
}
if(!screen_) return;
screen_->update_arrow(*this);
}

View file

@ -26,34 +26,32 @@
#include <list>
#include <map>
typedef std::map<map_location, image::locator> arrow_symbols_map_t;
typedef std::vector<map_location> arrow_path_t;
class arrow_observer;
/**
* Arrows destined to be drawn on the map. Created for the whiteboard system.
*/
class arrow {
typedef std::map<map_location, image::locator> arrow_symbols_map_t;
public:
//operations
arrow(display* screen);
arrow();
virtual ~arrow();
/// returns false if the received path is invalid
virtual bool set_path(const arrow_path_t &path);
virtual void clear_path();
/**
* The string color parameter is in the same format expected by the
* image::locator modifiers parameter. Examples: red is "red" or "FF0000" or "255,0,0".
* Feel free to add another method that accepts an Uint32 as a parameter instead.
*/
void set_color(const std::string& color);
virtual void set_color(const std::string& color);
/**
* The style is simply the name of a subdirectory under images/arrows,
@ -73,14 +71,12 @@ public:
const arrow_path_t & get_previous_path() const;
void draw_hex(const map_location & hex);
virtual void draw_hex(const map_location & hex);
/// Checks that the path is not of length 0 or 1
bool valid_path(arrow_path_t path) const;
virtual bool valid_path(arrow_path_t path) const;
void add_observer(arrow_observer & observer);
void remove_observer(arrow_observer & observer);
virtual void notify_arrow_changed();
protected:
//operations
@ -88,19 +84,14 @@ protected:
/**
* @param old_path : the path to erase and replace with the new symbols
*/
void update_symbols(arrow_path_t old_path);
virtual void update_symbols(arrow_path_t old_path);
void invalidate_arrow_path(arrow_path_t path);
private:
//operations
void notify_arrow_changed();
virtual void invalidate_arrow_path(arrow_path_t path);
protected:
//properties
display* screen_;
display*& screen_;
display::tdrawing_layer layer_;
@ -115,10 +106,5 @@ protected:
arrow_path_t previous_path_;
arrow_symbols_map_t symbols_map_;
private:
//properties
std::list<arrow_observer*> observers_;
};
#endif

View file

@ -1,35 +0,0 @@
/* $Id$ */
/*
Copyright (C) 2010 by Gabriel Morin <gabrielmorin (at) gmail (dot) org>
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 arrow_observer.hpp
*/
#ifndef ARROW_OBSERVER_HPP_
#define ARROW_OBSERVER_HPP_
/**
* Interface implemented by any code interested of tracking an arrow's
* changes (currently, only display implements it).
*/
class arrow_observer {
public:
virtual ~arrow_observer() {}
virtual void arrow_changed(arrow & a) = 0;
};
#endif /* ARROW_OBSERVER_HPP_ */

View file

@ -2339,7 +2339,6 @@ void display::add_arrow(arrow& arrow)
{
arrows_map_[loc].push_back(&arrow);
}
arrow.add_observer(*this);
}
void display::remove_arrow(arrow& arrow)
@ -2349,19 +2348,18 @@ void display::remove_arrow(arrow& arrow)
{
arrows_map_[loc].remove(&arrow);
}
arrow.remove_observer(*this);
}
void display::arrow_changed(arrow & changed)
void display::update_arrow(arrow & arrow)
{
const arrow_path_t & previous_path = changed.get_previous_path();
const arrow_path_t & previous_path = arrow.get_previous_path();
foreach (const map_location& loc, previous_path)
{
arrows_map_[loc].remove(&changed);
arrows_map_[loc].remove(&arrow);
}
const arrow_path_t & arrow_path = changed.get_path();
const arrow_path_t & arrow_path = arrow.get_path();
foreach (const map_location& loc, arrow_path)
{
arrows_map_[loc].push_back(&changed);
arrows_map_[loc].push_back(&arrow);
}
}

View file

@ -49,7 +49,6 @@ class arrow;
#include "theme.hpp"
#include "video.hpp"
#include "widgets/button.hpp"
#include "arrow_observer.hpp"
#include "SDL.h"
@ -61,13 +60,13 @@ class arrow;
#include <boost/function.hpp>
#include <boost/scoped_ptr.hpp>
typedef std::list<arrow*> arrows_list_t;
typedef std::map<map_location, arrows_list_t > arrows_map_t;
class gamemap;
class display: public arrow_observer
class display
{
typedef std::list<arrow*> arrows_list_t;
typedef std::map<map_location, arrows_list_t > arrows_map_t;
public:
display(CVideo& video, const gamemap* map, const config& theme_cfg,
const config& level);
@ -792,7 +791,7 @@ public: //operations for the arrow framework
void remove_arrow(arrow&);
/** Called by arrow objects when they change. You should not need to call this directly. */
virtual void arrow_changed(arrow & a);
void update_arrow(arrow & a);
private:
/** Handle for the label which displays frames per second. */

View file

@ -34,24 +34,20 @@ manager::manager():
active_(false),
mapbuilder_(NULL),
route_(),
move_arrow_(NULL),
fake_unit_(NULL),
move_arrow_(),
fake_unit_(),
selected_unit_(NULL)
{
}
manager::~manager()
{
if (resources::screen != NULL)
if (resources::screen)
{
if (fake_unit_.get() != NULL)
if (fake_unit_)
{
resources::screen->remove_temporary_unit(fake_unit_.get());
}
if (move_arrow_.get() != NULL)
{
resources::screen->remove_arrow(*move_arrow_);
}
}
}
@ -78,7 +74,6 @@ void manager::remove_temp_modifiers()
{
DBG_WB << "Removing temporary modifiers.\n";
mapbuilder_.reset();
DBG_WB << "Removed temporary modifiers.\n";
}
void manager::select_unit(unit& unit)
@ -89,8 +84,11 @@ void manager::select_unit(unit& unit)
void manager::deselect_unit()
{
DBG_WB << "Deselecting unit " << selected_unit_->name() << " [" << selected_unit_->id() << "]\n";
selected_unit_ = NULL;
if (selected_unit_)
{
DBG_WB << "Deselecting unit " << selected_unit_->name() << " [" << selected_unit_->id() << "]\n";
selected_unit_ = NULL;
}
}
void manager::create_temp_move(const std::vector<map_location> &steps)
@ -100,21 +98,20 @@ void manager::create_temp_move(const std::vector<map_location> &steps)
{
bool show_ghosted_unit_bars = false;
if (move_arrow_.get() == NULL)
if (!move_arrow_)
{
// Create temp arrow
game_display *screen = resources::screen;
move_arrow_.reset(new arrow((display*) screen));
move_arrow_.reset(new arrow());
int current_side = resources::controller->current_side();
move_arrow_->set_color(team::get_side_color_index(current_side));
move_arrow_->set_alpha(2.0);
screen->add_arrow(*move_arrow_);
resources::screen->add_arrow(*move_arrow_);
// Create temp ghost unit
fake_unit_.reset(new unit(*selected_unit_));
fake_unit_->set_location(route_.back());
fake_unit_->set_ghosted(show_ghosted_unit_bars);
screen->place_temporary_unit(fake_unit_.get());
resources::screen->place_temporary_unit(fake_unit_.get());
}
move_arrow_->set_path(route_);
@ -125,10 +122,9 @@ void manager::create_temp_move(const std::vector<map_location> &steps)
void manager::erase_temp_move()
{
if (move_arrow_.get() != NULL)
if (move_arrow_)
{
resources::screen->remove_arrow(*move_arrow_);
move_arrow_.reset();
move_arrow_.reset(); //auto-removes itself from display
resources::screen->remove_temporary_unit(fake_unit_.get());
fake_unit_.reset();
}
@ -137,17 +133,17 @@ void manager::erase_temp_move()
void manager::save_temp_move()
{
//If selected unit already has a move defined, erase it first
// TODO: implement a find_and_erase method in find_visitor to avoid iterating twice over actions
{ // scope-limiting block
find_visitor finder;
action_ptr action = finder.find_first_action_of(*selected_unit_, get_current_side_actions().actions());
if (action)
{
LOG_WB << "Previous action found for unit " << selected_unit_->name() << " [" << selected_unit_->id() << "]"
<< ", erasing action.\n";
get_current_side_actions().remove_action(action);
}
} // end scope-limiting block
find_visitor finder;
action_ptr action = finder.find_first_action_of(*selected_unit_, get_current_side_actions().actions());
if (action)
{
LOG_WB << "Previous action found for unit " << selected_unit_->name() << " [" << selected_unit_->id() << "]"
<< ", erasing action.\n";
get_current_side_actions().remove_action(action);
}
//Define the new move
LOG_WB << "Creating move for unit " << selected_unit_->name() << " [" << selected_unit_->id() << "]"
@ -156,9 +152,9 @@ void manager::save_temp_move()
move_arrow_->set_alpha(0.6);
get_current_side_actions().queue_move(*selected_unit_, route_.back(),
*(move_arrow_.release()) /* ownership of the arrow transferred to the new move action */,
*(fake_unit_.release()) /* ownership of the fake unit transferred to the new move action */);
get_current_side_actions().queue_move(*selected_unit_, route_.back(), move_arrow_, fake_unit_);
move_arrow_.reset();
fake_unit_.reset();
}
} // end namespace wb

View file

@ -22,6 +22,7 @@
#include "map_location.hpp"
#include <boost/noncopyable.hpp>
#include <boost/shared_ptr.hpp>
#include <memory>
#include <vector>
@ -81,8 +82,8 @@ private:
std::vector<map_location> route_;
std::auto_ptr<arrow> move_arrow_;
std::auto_ptr<unit> fake_unit_;
boost::shared_ptr<arrow> move_arrow_;
boost::shared_ptr<unit> fake_unit_;
unit* selected_unit_;
};

View file

@ -28,27 +28,24 @@
namespace wb {
move::move(unit& subject, const map_location& target_hex, arrow& arrow, unit& fake_unit)
move::move(unit& subject, const map_location& target_hex, boost::shared_ptr<arrow> arrow,
boost::shared_ptr<unit> fake_unit)
: unit_(subject),
orig_hex_(subject.get_location()),
dest_hex_(target_hex),
arrow_(&arrow),
fake_unit_(&fake_unit)
arrow_(arrow),
fake_unit_(fake_unit)
{
}
move::~move()
{
if (resources::screen != NULL)
if (resources::screen)
{
if (fake_unit_.get() != NULL)
if (fake_unit_)
{
resources::screen->remove_temporary_unit(fake_unit_.get());
}
if (arrow_.get() != NULL)
{
resources::screen->remove_arrow(*arrow_);
}
}
}

View file

@ -22,8 +22,6 @@
#include "action.hpp"
#include "map_location.hpp"
#include <memory>
class arrow;
class config;
class unit;
@ -38,8 +36,8 @@ namespace wb {
class move: public action
{
public:
/// Takes ownership of arrow and fake_unit
move(unit& subject, const map_location& target_hex, arrow& arrow, unit& fake_unit);
move(unit& subject, const map_location& target_hex, boost::shared_ptr<arrow> arrow,
boost::shared_ptr<unit> fake_unit);
virtual ~move();
virtual void accept(visitor& v);
@ -58,8 +56,8 @@ private:
map_location orig_hex_;
map_location dest_hex_;
std::auto_ptr<arrow> arrow_;
std::auto_ptr<unit> fake_unit_;
boost::shared_ptr<arrow> arrow_;
boost::shared_ptr<unit> fake_unit_;
};
} // end namespace wb

View file

@ -38,14 +38,16 @@ const action_set& side_actions::actions() const
return actions_;
}
void side_actions::insert_move(unit& subject, const map_location& target_hex, arrow& arrow, size_t index, unit& fake_unit)
void side_actions::insert_move(unit& subject, const map_location& target_hex, size_t index, boost::shared_ptr<arrow> arrow,
boost::shared_ptr<unit> fake_unit)
{
action_ptr action(new move(subject, target_hex, arrow, fake_unit));
assert(index < end());
actions_.insert(actions_.begin() + index, action);
}
void side_actions::queue_move(unit& subject, const map_location& target_hex, arrow& arrow, unit& fake_unit)
void side_actions::queue_move(unit& subject, const map_location& target_hex, boost::shared_ptr<arrow> arrow,
boost::shared_ptr<unit> fake_unit)
{
action_ptr action(new move(subject, target_hex, arrow, fake_unit));
actions_.push_back(action);

View file

@ -57,15 +57,15 @@ public:
/**
* Inserts a move at the specified index. The begin() and end() functions might prove useful here.
* Gives ownership of the arrow and fake_unit to the new move object
*/
void insert_move(unit& subject, const map_location& target_hex, arrow& arrow, size_t index, unit& fake_unit);
void insert_move(unit& subject, const map_location& target_hex, size_t index, 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)
* Gives ownership of the arrow and fake_unit to the new move object
*/
void queue_move(unit& subject, const map_location& target_hex, arrow& arrow, unit& fake_unit);
void queue_move(unit& subject, 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),