add recall list manager

This commit adds a dedicated recall list manager class.

The purpose of this is to

- Simplify the code that interacts with the recall list. Prior to
the commit most such code was based on iteration with explicit
iterators, and called global helper functions implemented in
unit.cpp to wrap the code that finds a unit in a vector. It turns
out that interacting with the recall list was the *only* use of
that code, so we make it a member function of the recall list
manager and take it out of unit.cpp.

Most of the code that touches the recall list was previously
7 or 8 lines with a for loop, now it tends to be 1 or 2 lines,
although further refactor may be possible.

- Improve encapsulation. This makes it possible to track how
other classes are interacting with the recall list, and may
make it easier to debug recall list problems by adding debugging
output to the class.
This commit is contained in:
Chris Beck 2014-06-18 08:32:49 -04:00
parent 12515a6466
commit a4b194c079
25 changed files with 337 additions and 193 deletions

View File

@ -862,6 +862,8 @@
<Unit filename="../../src/random_new_deterministic.hpp" />
<Unit filename="../../src/random_new_synced.cpp" />
<Unit filename="../../src/random_new_synced.hpp" />
<Unit filename="../../src/recall_list_manager.cpp" />
<Unit filename="../../src/recall_list_manager.hpp" />
<Unit filename="../../src/reference_counted_object.hpp" />
<Unit filename="../../src/replay.cpp" />
<Unit filename="../../src/replay.hpp" />

View File

@ -895,6 +895,8 @@
<Unit filename="..\..\src\random_new_deterministic.hpp" />
<Unit filename="..\..\src\random_new_synced.cpp" />
<Unit filename="..\..\src\random_new_synced.hpp" />
<Unit filename="..\..\src\recall_list_manager.cpp" />
<Unit filename="..\..\src\recall_list_manager.hpp" />
<Unit filename="..\..\src\reference_counted_object.hpp" />
<Unit filename="..\..\src\replay.cpp" />
<Unit filename="..\..\src\replay.hpp" />

View File

@ -20772,6 +20772,14 @@
RelativePath="..\..\src\random_new_synced.hpp"
>
</File>
<File
RelativePath="..\..\src\recall_list_manager.cpp"
>
</File>
<File
RelativePath="..\..\src\recall_list_manager.hpp"
>
</File>
<File
RelativePath="..\..\src\replay.cpp"
>

View File

@ -857,6 +857,7 @@ set(wesnoth-main_SRC
random_new.cpp
random_new_deterministic.cpp
random_new_synced.cpp
recall_list_manager.cpp
replay.cpp
replay_helper.cpp
replay_controller.cpp

View File

@ -491,6 +491,7 @@ wesnoth_sources = Split("""
random_new.cpp
random_new_deterministic.cpp
random_new_synced.cpp
recall_list_manager.cpp
replay.cpp
replay_helper.cpp
replay_controller.cpp

View File

@ -34,6 +34,7 @@
#include "../log.hpp"
#include "../map.hpp"
#include "../pathfind/pathfind.hpp"
#include "../recall_list_manager.hpp"
#include "../replay.hpp"
#include "../replay_helper.hpp"
#include "../resources.hpp"
@ -190,10 +191,9 @@ void unit_creator::add_unit(const config &cfg, const vconfig* vcfg)
bool animate = temp_cfg["animate"].to_bool();
temp_cfg.remove_attribute("animate");
std::vector<UnitPtr > &recall_list = team_.recall_list();
std::vector<UnitPtr >::iterator recall_list_element = find_if_matches_id(recall_list, id);
UnitPtr recall_list_element = team_.recall_list().find_if_matches_id(id);
if ( recall_list_element == recall_list.end() ) {
if ( !recall_list_element ) {
//make the new unit
UnitPtr new_unit(new unit(temp_cfg, true, vcfg));
map_location loc = find_location(temp_cfg, new_unit.get());
@ -205,20 +205,19 @@ void unit_creator::add_unit(const config &cfg, const vconfig* vcfg)
}
else if ( add_to_recall_ ) {
//add to recall list
recall_list.push_back(new_unit);
team_.recall_list().add(new_unit);
DBG_NG << "inserting unit with id=["<<id<<"] on recall list for side " << new_unit->side() << "\n";
preferences::encountered_units().insert(new_unit->type_id());
}
} else {
//get unit from recall list
const UnitPtr & u_ptr = *recall_list_element;
map_location loc = find_location(temp_cfg, u_ptr.get());
map_location loc = find_location(temp_cfg, recall_list_element.get());
if ( loc.valid() ) {
resources::units->replace(loc, *u_ptr);
LOG_NG << "inserting unit from recall list for side " << u_ptr->side()<< " with id="<< id << "\n";
resources::units->replace(loc, *recall_list_element);
LOG_NG << "inserting unit from recall list for side " << recall_list_element->side()<< " with id="<< id << "\n";
post_create(loc,*(resources::units->find(loc)),animate);
//if id is not empty, delete units with this ID from recall list
erase_if_matches_id(recall_list, id);
team_.recall_list().erase_if_matches_id( id);
}
else if ( add_to_recall_ ) {
LOG_NG << "wanted to insert unit on recall list, but recall list for side " << (cfg)["side"] << "already contains id=" <<id<<"\n";
@ -418,10 +417,9 @@ namespace { // Helpers for get_recalls()
std::set<size_t> * already_added = NULL)
{
const team& leader_team = (*resources::teams)[leader->side()-1];
const std::vector<UnitPtr >& recall_list = leader_team.recall_list();
const std::string& save_id = leader_team.save_id();
BOOST_FOREACH(const UnitConstPtr & recall_unit_ptr, recall_list)
BOOST_FOREACH(const UnitConstPtr & recall_unit_ptr, leader_team.recall_list())
{
const unit & recall_unit = *recall_unit_ptr;
// Do not add a unit twice.
@ -429,9 +427,7 @@ namespace { // Helpers for get_recalls()
if ( !already_added || already_added->count(underlying_id) == 0 )
{
// Only units that match the leader's recall filter are valid.
const std::vector<UnitPtr >::const_iterator rit = find_if_matches_id(recall_list, recall_unit.id());
scoped_recall_unit this_unit("this_unit", save_id, rit - recall_list.begin());
scoped_recall_unit this_unit("this_unit", save_id, leader_team.recall_list().find_index(recall_unit.id()));
if ( recall_unit.matches_filter(vconfig(leader->recall_filter()), map_location::null_location()) )
{
@ -503,8 +499,7 @@ std::vector<UnitConstPtr > get_recalls(int side, const map_location &recall_loc)
if ( !leader_in_place )
{
// Return the full recall list.
const std::vector< UnitPtr >& recall_list = (*resources::teams)[side-1].recall_list();
BOOST_FOREACH(const UnitConstPtr & recall, recall_list)
BOOST_FOREACH(const UnitConstPtr & recall, (*resources::teams)[side-1].recall_list())
{
result.push_back(recall);
}
@ -530,9 +525,8 @@ namespace { // Helpers for check_recall_location()
// Make sure the recalling unit can recall this specific unit.
team& recall_team = (*resources::teams)[recaller.side()-1];
std::vector<UnitPtr >::iterator it = find_if_matches_id(recall_team.recall_list(), recall_unit.id());
scoped_recall_unit this_unit("this_unit", recall_team.save_id(),
it - recall_team.recall_list().begin());
recall_team.recall_list().find_index(recall_unit.id()));
if ( !recall_unit.matches_filter(vconfig(recaller.recall_filter()),
map_location::null_location()) )
return RECRUIT_NO_ABLE_LEADER;
@ -1014,17 +1008,12 @@ bool recall_unit(const std::string & id, team & current_team,
const map_location & loc, const map_location & from,
bool show, bool use_undo)
{
std::vector<UnitPtr > & recall_list = current_team.recall_list();
UnitPtr recall = current_team.recall_list().extract_if_matches_id(id);
// Find the unit to recall.
std::vector<UnitPtr >::iterator recall_it = find_if_matches_id(recall_list, id);
if ( recall_it == recall_list.end() )
if ( !recall )
return false;
// Make a copy of the unit before erasing it from the list.
UnitPtr recall(*recall_it);
recall_list.erase(recall_it);
// ** IMPORTANT: id might become invalid at this point!
// (Use recall.id() instead, if needed.)

View File

@ -25,6 +25,7 @@
#include "../game_display.hpp"
#include "../log.hpp"
#include "../play_controller.hpp"
#include "../recall_list_manager.hpp"
#include "../replay.hpp"
#include "../replay_helper.hpp"
#include "../resources.hpp"
@ -623,7 +624,7 @@ bool undo_list::dismiss_action::undo(int side, undo_list & /*undos*/)
return false;
}
current_team.recall_list().push_back(dismissed_unit);
current_team.recall_list().add(dismissed_unit);
return true;
}
@ -663,7 +664,7 @@ bool undo_list::recall_action::undo(int side, undo_list & /*undos*/)
current_team.spend_gold(-cost);
}
current_team.recall_list().push_back(un);
current_team.recall_list().add(un);
// invalidate before erasing allow us
// to also do the overlapped hexes
gui.invalidate(recall_loc);
@ -834,7 +835,7 @@ bool undo_list::dismiss_action::redo(int side)
}
recorder.redo(replay_data);
replay_data.clear();
erase_if_matches_id(current_team.recall_list(), dismissed_unit->id());
current_team.recall_list().erase_if_matches_id(dismissed_unit->id());
return true;
}
@ -856,15 +857,14 @@ bool undo_list::recall_action::redo(int side)
map_location loc = route.front();
map_location from = recall_from;
const std::vector<UnitPtr > & recalls = current_team.recall_list();
std::vector<UnitPtr >::const_iterator unit_it = find_if_matches_id(recalls, id);
if ( unit_it == recalls.end() ) {
UnitPtr 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, **unit_it);
const std::string &msg = find_recall_location(side, loc, from, *un);
if ( msg.empty() ) {
recorder.redo(replay_data);
replay_data.clear();

View File

@ -47,6 +47,7 @@
#include "../mouse_handler_base.hpp"
#include "../pathfind/teleport.hpp"
#include "../play_controller.hpp"
#include "../recall_list_manager.hpp"
#include "../replay.hpp"
#include "../replay_helper.hpp"
#include "../resources.hpp"
@ -503,12 +504,11 @@ recall_result::recall_result(side_number side,
UnitConstPtr recall_result::get_recall_unit(const team &my_team)
{
const std::vector<UnitPtr >::const_iterator rec = find_if_matches_id(my_team.recall_list(), unit_id_);
if (rec == my_team.recall_list().end()) {
UnitConstPtr rec = my_team.recall_list().find_if_matches_id(unit_id_);
if (!rec) {
set_error(E_NOT_AVAILABLE_FOR_RECALLING);
return UnitConstPtr();
}
return *rec;
return rec;
}
bool recall_result::test_enough_gold(const team &my_team)

View File

@ -37,6 +37,7 @@
#include "../log.hpp"
#include "../map.hpp"
#include "../mouse_handler_base.hpp"
#include "../recall_list_manager.hpp"
#include "../resources.hpp"
#include "../tod_manager.hpp"
#include "../unit.hpp"
@ -784,7 +785,7 @@ const std::vector<UnitPtr>& readonly_context_impl::get_recall_list() const
return dummy_units;
}
return current_team().recall_list();
return current_team().recall_list().recall_list_; //TODO: Refactor ai so that friend of ai context is not required of recall_list_manager at this line
}
stage_ptr readonly_context_impl::get_recruitment(ai_context &context) const

View File

@ -30,6 +30,7 @@
#include "../../game_classification.hpp"
#include "../../log.hpp"
#include "../../mouse_handler_base.hpp"
#include "../../recall_list_manager.hpp"
#include "../../resources.hpp"
#include "../../terrain_filter.hpp"
#include "../../unit.hpp"
@ -763,13 +764,12 @@ bool ai_default_recruitment_stage::analyze_recall_list()
return false;
}
const std::vector<UnitPtr > &recalls = current_team().recall_list();
if (recalls.empty()) {
if (current_team().recall_list().empty()) {
return false;
}
std::transform(recalls.begin(), recalls.end(), std::back_inserter< std::vector <std::pair<std::string,double> > > (recall_list_scores_), unit_combat_score_getter(*this) );
std::transform(current_team().recall_list().begin(), current_team().recall_list().end(),
std::back_inserter< std::vector <std::pair<std::string,double> > > (recall_list_scores_), unit_combat_score_getter(*this) );
debug_print_recall_list_scores(recall_list_scores_,"Recall list, after scoring:");

View File

@ -38,6 +38,7 @@
#include "menu_events.hpp"
#include "mouse_handler_base.hpp"
#include "minimap.hpp"
#include "recall_list_manager.hpp"
#include "replay.hpp"
#include "replay_helper.hpp"
#include "resources.hpp"
@ -133,14 +134,11 @@ gui::dialog_button_action::RESULT delete_recall_unit::button_pressed(int menu_se
resources::undo_stack->add_dismissal(u_ptr);
// Find the unit in the recall list.
std::vector<UnitPtr >& recall_list = (*resources::teams)[u.side() -1].recall_list();
assert(!recall_list.empty());
std::vector<UnitPtr >::iterator dismissed_unit =
find_if_matches_id(recall_list, u.id());
assert(dismissed_unit != recall_list.end());
UnitPtr dismissed_unit = (*resources::teams)[u.side() -1].recall_list().find_if_matches_id(u.id());
assert(dismissed_unit);
// Record the dismissal, then delete the unit.
synced_context::run_in_synced_context("disband", replay_helper::get_disband((*dismissed_unit)->id()));
synced_context::run_in_synced_context("disband", replay_helper::get_disband(dismissed_unit->id()));
//recorder.add_disband(dismissed_unit->id());
//recall_list.erase(dismissed_unit);

View File

@ -17,6 +17,7 @@
#include "game_preferences.hpp"
#include "log.hpp"
#include "map.hpp"
#include "recall_list_manager.hpp"
#include "unit.hpp"
#include "utils/foreach.tpp"
@ -144,7 +145,7 @@ void game_board::side_change_controller(int side_num, team::CONTROLLER ctrl, con
bool game_board::try_add_unit_to_recall_list(const map_location& loc, const UnitPtr u)
{
if(teams_[u->side()-1].persistent()) {
teams_[u->side()-1].recall_list().push_back(u);
teams_[u->side()-1].recall_list().add(u);
return true;
} else {
ERR_RG << "unit with id " << u->id() << ": location (" << loc.x << "," << loc.y <<") is not on the map, and player "

View File

@ -49,6 +49,7 @@
#include "../pathfind/pathfind.hpp"
#include "../persist_var.hpp"
#include "../play_controller.hpp"
#include "../recall_list_manager.hpp"
#include "../replay.hpp"
#include "../replay_helper.hpp"
#include "../random_new.hpp"
@ -57,6 +58,7 @@
#include "../sound.hpp"
#include "../soundsource.hpp"
#include "../synced_context.hpp"
#include "../team.hpp"
#include "../terrain_filter.hpp"
#include "../unit.hpp"
#include "../unit_animation_component.hpp"
@ -975,11 +977,10 @@ WML_HANDLER_FUNCTION(kill, event_info, cfg)
for(std::vector<team>::iterator pi = resources::teams->begin();
pi!=resources::teams->end(); ++pi)
{
std::vector<UnitPtr>& avail_units = pi->recall_list();
for(std::vector<UnitPtr>::iterator j = avail_units.begin(); j != avail_units.end();) {
scoped_recall_unit auto_store("this_unit", pi->save_id(), j - avail_units.begin());
for(std::vector<UnitPtr>::iterator j = pi->recall_list().begin(); j != pi->recall_list().end();) { //TODO: This block is really messy, cleanup somehow...
scoped_recall_unit auto_store("this_unit", pi->save_id(), j - pi->recall_list().begin());
if ((*j)->matches_filter(cfg, map_location())) {
j = avail_units.erase(j);
j = pi->recall_list().erase(j);
} else {
++j;
}
@ -1578,7 +1579,7 @@ WML_HANDLER_FUNCTION(recall, /*event_info*/, cfg)
continue;
}
std::vector<UnitPtr>& avail = (*resources::teams)[index].recall_list();
recall_list_manager & avail = (*resources::teams)[index].recall_list();
std::vector<unit_map::unit_iterator> leaders = resources::units->find_leaders(index + 1);
for(std::vector<UnitPtr>::iterator u = avail.begin(); u != avail.end(); ++u) {
@ -1799,8 +1800,8 @@ WML_HANDLER_FUNCTION(role, /*event_info*/, cfg)
}
// Iterate over the player's recall list to find a match
for(size_t i=0; i < pi->recall_list().size(); ++i) {
UnitPtr & u = pi->recall_list()[i];
scoped_recall_unit auto_store("this_unit", player_id, i);
UnitPtr u = pi->recall_list()[i];
scoped_recall_unit auto_store("this_unit", player_id, i); //TODO: Should this not be inside the if? Explain me.
if (u->matches_filter(filter, map_location())) {
u->set_role(cfg["role"]);
found=true;
@ -2574,12 +2575,10 @@ WML_HANDLER_FUNCTION(unstore_unit, /*event_info*/, cfg)
// replaced by the wrong unit.
if(t.recall_list().size() > 1) {
std::vector<size_t> desciptions;
for(std::vector<UnitPtr>::const_iterator citor =
t.recall_list().begin();
citor != t.recall_list().end(); ++citor) {
BOOST_FOREACH ( const UnitConstPtr & pt, t.recall_list() ) {
const size_t desciption =
(*citor)->underlying_id();
pt->underlying_id();
if(std::find(desciptions.begin(), desciptions.end(),
desciption) != desciptions.end()) {
@ -2597,19 +2596,13 @@ WML_HANDLER_FUNCTION(unstore_unit, /*event_info*/, cfg)
* @todo it would be better to change recall_list() from
* a vector to a map and use the underlying_id as key.
*/
const size_t key = u->underlying_id();
for(std::vector<UnitPtr>::iterator itor =
t.recall_list().begin();
itor != t.recall_list().end(); ++itor) {
size_t old_size = t.recall_list().size();
t.recall_list().erase_by_underlying_id(u->underlying_id());
if (t.recall_list().size() != old_size) {
LOG_NG << "Replaced unit '"
<< key << "' on the recall list\n";
if((*itor)->underlying_id() == key) {
t.recall_list().erase(itor);
break;
}
<< u->underlying_id() << "' on the recall list\n";
}
t.recall_list().push_back(u);
t.recall_list().add(u);
} else {
ERR_NG << "Cannot unstore unit: recall list is empty for player " << u->side()
<< " and the map location is invalid.\n";

View File

@ -23,6 +23,7 @@
#include "../config.hpp"
#include "../gamestatus.hpp"
#include "../log.hpp"
#include "../recall_list_manager.hpp"
#include "../resources.hpp"
#include "../serialization/string_utils.hpp"
#include "../team.hpp"
@ -89,13 +90,12 @@ namespace { // Support functions
if(counts == default_counts && match_count) {
break;
}
const std::vector<UnitPtr>& avail_units = team->recall_list();
for(std::vector<UnitPtr>::const_iterator unit = avail_units.begin(); unit!=avail_units.end(); ++unit) {
for(size_t t = 0; t < team->recall_list().size(); ++t) {
if(counts == default_counts && match_count) {
break;
}
scoped_recall_unit auto_store("this_unit", team->save_id(), unit - avail_units.begin());
if ( (*unit)->matches_filter(*u) ) {
scoped_recall_unit auto_store("this_unit", team->save_id(), t);
if ( team->recall_list()[t]->matches_filter(*u) ) {
++match_count;
}
}

View File

@ -30,6 +30,7 @@
#include "gettext.hpp"
#include "log.hpp"
#include "map.hpp"
#include "recall_list_manager.hpp"
#include "replay.hpp"
#include "resources.hpp"
#include "serialization/binary_or_text.hpp"
@ -255,7 +256,7 @@ protected:
//seen before
config u_tmp = u;
u_tmp["side"] = str_cast(side_);
t_->recall_list().push_back(UnitPtr(new unit(u_tmp,true)));
t_->recall_list().add(UnitPtr(new unit(u_tmp,true)));
} else {
//not seen before
unit_configs_.push_back(&u);

View File

@ -32,6 +32,7 @@
#include "utils/foreach.tpp"
#include "../../gamestatus.hpp"
#include "../../recall_list_manager.hpp"
#include "../../resources.hpp"
#include "../../team.hpp"
#include "../../unit.hpp"
@ -416,40 +417,34 @@ public:
}
if(selected == 3) {
const std::vector<UnitPtr> recall_list
= resources::teams
? resources::teams->at(side_ - 1).recall_list()
: std::vector<UnitPtr>();
std::stringstream s;
FOREACH(const AUTO & u, recall_list)
{
s << "id=\"" << u->id() << "\" (" << u->type_id() << ")\nL"
<< u->level() << "; " << u->experience() << "/"
<< u->max_experience() << " xp; " << u->hitpoints() << "/"
<< u->max_hitpoints() << " hp\n";
FOREACH(const AUTO & str, u->get_traits_list())
if (resources::teams) {
FOREACH(const AUTO & u, resources::teams->at(side_ - 1).recall_list())
{
s << "\t" << str << std::endl;
s << "id=\"" << u->id() << "\" (" << u->type_id() << ")\nL"
<< u->level() << "; " << u->experience() << "/"
<< u->max_experience() << " xp; " << u->hitpoints() << "/"
<< u->max_hitpoints() << " hp\n";
FOREACH(const AUTO & str, u->get_traits_list())
{
s << "\t" << str << std::endl;
}
s << std::endl;
}
s << std::endl;
}
model_.set_inspect_window_text(s.str());
return;
}
if(selected == 4) {
const std::vector<UnitPtr> recall_list
= resources::teams
? resources::teams->at(side_ - 1).recall_list()
: std::vector<UnitPtr>();
config c;
FOREACH(const AUTO & u, recall_list)
{
config c_unit;
u->write(c_unit);
c.add_child("unit", c_unit);
if (resources::teams) {
FOREACH(const AUTO & u, resources::teams->at(side_ - 1).recall_list())
{
config c_unit;
u->write(c_unit);
c.add_child("unit", c_unit);
}
}
model_.set_inspect_window_text(config_to_string(c));
return;

149
src/recall_list_manager.cpp Normal file
View File

@ -0,0 +1,149 @@
/*
Copyright (C) 2014 by Chris Beck <render787@gmail.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 as published by
the Free Software Foundation; either version 2 of the License, 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.
*/
#include "recall_list_manager.hpp"
#include "unit.hpp"
#include "unit_ptr.hpp"
#include <algorithm>
#include <string>
#include <vector>
#include <boost/bind.hpp>
#include <boost/foreach.hpp>
bool find_if_matches_helper(const UnitPtr & ptr, const std::string & unit_id)
{
return ptr->matches_id(unit_id);
}
/**
* Used to find units in vectors by their ID.
*/
UnitPtr recall_list_manager::find_if_matches_id(const std::string &unit_id)
{
std::vector<UnitPtr >::iterator it = std::find_if(recall_list_.begin(), recall_list_.end(),
boost::bind(&find_if_matches_helper, _1, unit_id));
if (it != recall_list_.end()) {
return *it;
} else {
return UnitPtr();
}
}
/**
* Used to find units in vectors by their ID.
*/
UnitConstPtr recall_list_manager::find_if_matches_id(const std::string &unit_id) const
{
std::vector<UnitPtr >::const_iterator it = std::find_if(recall_list_.begin(), recall_list_.end(),
boost::bind(&find_if_matches_helper, _1, unit_id));
if (it != recall_list_.end()) {
return *it;
} else {
return UnitPtr();
}
}
/**
* Used to erase units from vectors by their ID.
*/
void recall_list_manager::erase_if_matches_id(const std::string &unit_id)
{
recall_list_.erase(std::remove_if(recall_list_.begin(), recall_list_.end(),
boost::bind(&find_if_matches_helper, _1, unit_id)),
recall_list_.end());
}
void recall_list_manager::add (const UnitPtr & ptr)
{
recall_list_.push_back(ptr);
}
size_t recall_list_manager::find_index(const std::string & unit_id) const
{
std::vector<UnitPtr >::const_iterator it = std::find_if(recall_list_.begin(), recall_list_.end(),
boost::bind(&find_if_matches_helper, _1, unit_id));
return it - recall_list_.begin();
}
UnitPtr recall_list_manager::extract_if_matches_id(const std::string &unit_id)
{
std::vector<UnitPtr >::iterator it = std::find_if(recall_list_.begin(), recall_list_.end(),
boost::bind(&find_if_matches_helper, _1, unit_id));
if (it != recall_list_.end()) {
UnitPtr ret = *it;
recall_list_.erase(it);
return ret;
} else {
return UnitPtr();
}
}
bool find_if_matches_uid_helper(const UnitPtr & ptr, size_t uid)
{
return ptr->underlying_id() == uid;
}
UnitPtr recall_list_manager::find_if_matches_underlying_id(size_t uid)
{
std::vector<UnitPtr >::iterator it = std::find_if(recall_list_.begin(), recall_list_.end(),
boost::bind(&find_if_matches_uid_helper, _1, uid));
if (it != recall_list_.end()) {
return *it;
} else {
return UnitPtr();
}
}
UnitConstPtr recall_list_manager::find_if_matches_underlying_id(size_t uid) const
{
std::vector<UnitPtr >::const_iterator it = std::find_if(recall_list_.begin(), recall_list_.end(),
boost::bind(&find_if_matches_uid_helper, _1, uid));
if (it != recall_list_.end()) {
return *it;
} else {
return UnitPtr();
}
}
void recall_list_manager::erase_by_underlying_id(size_t uid)
{
recall_list_.erase(std::remove_if(recall_list_.begin(), recall_list_.end(),
boost::bind(&find_if_matches_uid_helper, _1, uid)),
recall_list_.end());
}
UnitPtr recall_list_manager::extract_if_matches_underlying_id(size_t uid)
{
std::vector<UnitPtr >::iterator it = std::find_if(recall_list_.begin(), recall_list_.end(),
boost::bind(&find_if_matches_uid_helper, _1, uid));
if (it != recall_list_.end()) {
UnitPtr ret = *it;
recall_list_.erase(it);
return ret;
} else {
return UnitPtr();
}
}
std::vector<UnitPtr>::iterator recall_list_manager::erase_index(size_t idx) {
assert(idx < recall_list_.size());
return recall_list_.erase(recall_list_.begin()+idx);
}
std::vector<UnitPtr>::iterator recall_list_manager::erase(std::vector<UnitPtr>::iterator it) {
return recall_list_.erase(it);
}

View File

@ -0,0 +1,68 @@
/*
Copyright (C) 2014 by Chris Beck <render787@gmail.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 as published by
the Free Software Foundation; either version 2 of the License, 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.
*/
// This class encapsulates the recall list of a team
#ifndef RECALL_LIST_MGR_HPP
#define RECALL_LIST_MGR_HPP
#include "unit_ptr.hpp"
#include <string>
#include <vector>
namespace ai {
class readonly_context_impl;
}
class recall_list_manager {
public:
typedef std::vector<UnitPtr >::iterator iterator;
typedef std::vector<UnitPtr >::const_iterator const_iterator;
iterator begin() { return recall_list_.begin();}
iterator end() { return recall_list_.end(); }
const_iterator begin() const { return recall_list_.begin();}
const_iterator end() const { return recall_list_.end(); }
UnitPtr operator[](size_t index) { return recall_list_[index]; }
UnitConstPtr operator[](size_t index) const { return recall_list_[index]; }
UnitPtr find_if_matches_id(const std::string & unit_id);
UnitPtr extract_if_matches_id(const std::string & unit_id);
UnitConstPtr find_if_matches_id(const std::string & unit_id) const;
void erase_if_matches_id(const std::string & unit_id);
UnitPtr find_if_matches_underlying_id(size_t uid);
UnitPtr extract_if_matches_underlying_id(size_t uid);
UnitConstPtr find_if_matches_underlying_id(size_t uid) const;
void erase_by_underlying_id(size_t uid);
iterator erase_index(size_t index);
iterator erase(iterator it);
size_t find_index(const std::string & unit_id) const;
size_t size() const { return recall_list_.size(); }
bool empty() const { return recall_list_.empty(); }
void add(const UnitPtr & ptr);
private:
std::vector<UnitPtr > recall_list_;
friend class ai::readonly_context_impl;
};
#endif

View File

@ -56,6 +56,7 @@
#include "pathfind/pathfind.hpp"
#include "pathfind/teleport.hpp"
#include "play_controller.hpp"
#include "recall_list_manager.hpp"
#include "replay.hpp"
#include "reports.hpp"
#include "resources.hpp"
@ -853,9 +854,8 @@ static int intf_match_unit(lua_State *L)
if (int side = lu->on_recall_list()) {
team &t = (*resources::teams)[side - 1];
std::vector<UnitPtr>::iterator it = find_if_matches_id(t.recall_list(), u->id());
scoped_recall_unit auto_store("this_unit",
t.save_id(), it - t.recall_list().begin());
t.save_id(), t.recall_list().find_index(u->id()));
lua_pushboolean(L, u->matches_filter(filter, map_location()));
return 1;
}
@ -887,9 +887,8 @@ static int intf_get_recall_units(lua_State *L)
BOOST_FOREACH(UnitPtr & u, t.recall_list())
{
if (!filter.null()) {
std::vector<UnitPtr>::iterator it = find_if_matches_id(t.recall_list(), u->id());
scoped_recall_unit auto_store("this_unit",
t.save_id(), it - t.recall_list().begin());
t.save_id(), t.recall_list().find_index(u->id()));
if (!u->matches_filter(filter, map_location()))
continue;
}
@ -2232,18 +2231,11 @@ static int intf_put_recall_unit(lua_State *L)
team &t = (*resources::teams)[side - 1];
if (!t.persistent())
return luaL_argerror(L, 2, "nonpersistent side");
std::vector<UnitPtr> &rl = t.recall_list();
// Avoid duplicates in the recall list.
size_t uid = u->underlying_id();
std::vector<UnitPtr>::iterator i = rl.begin();
while (i != rl.end()) {
if ((*i)->underlying_id() == u->underlying_id()) {
i = rl.erase(i);
} else ++i;
}
rl.push_back(u);
t.recall_list().erase_by_underlying_id(uid);
t.recall_list().add(u);
if (lu) {
if (lu->on_map())
resources::units->erase(u->get_location());
@ -2273,9 +2265,7 @@ static int intf_extract_unit(lua_State *L)
} else if (int side = lu->on_recall_list()) {
team &t = (*resources::teams)[side - 1];
UnitPtr v = UnitPtr(new unit(*u));
std::vector<UnitPtr> &rl = t.recall_list();
std::vector<UnitPtr>::iterator it = find_if_matches_id(t.recall_list(), u->id());
rl.erase(rl.begin() + (it - rl.begin()));
t.recall_list().erase_if_matches_id(u->id());
u = v;
} else {
return 0;

View File

@ -21,6 +21,7 @@
#include "config.hpp"
#include "game_display.hpp"
#include "log.hpp"
#include "recall_list_manager.hpp"
#include "resources.hpp"
#include "tstring.hpp"
#include "unit.hpp"
@ -394,10 +395,7 @@ UnitPtr lua_unit::get()
{
if (ptr) return ptr;
if (side) {
BOOST_FOREACH(UnitPtr &u, (*resources::teams)[side - 1].recall_list()) {
if (u->underlying_id() == uid) return u;
}
return UnitPtr();
return (*resources::teams)[side - 1].recall_list().find_if_matches_underlying_id(uid);
}
unit_map::unit_iterator ui = resources::units->find(uid);
if (!ui.valid()) return UnitPtr();
@ -422,18 +420,11 @@ bool lua_unit::put_map(const map_location &loc)
return false;
}
} else if (side) { // recall list
std::vector<UnitPtr> &recall_list = (*resources::teams)[side - 1].recall_list();
std::vector<UnitPtr>::iterator it = recall_list.begin();
for(; it != recall_list.end(); ++it) {
if ((*it)->underlying_id() == uid) {
break;
}
}
if (it != recall_list.end()) {
UnitPtr it = (*resources::teams)[side - 1].recall_list().extract_if_matches_underlying_id(uid);
if (it) {
side = 0;
// uid may be changed by unit_map on insertion
uid = resources::units->replace(loc, **it).first->underlying_id();
recall_list.erase(it);
uid = resources::units->replace(loc, *it).first->underlying_id();
} else {
ERR_LUA << "Could not find unit " << uid << " on recall list of side " << side << '\n';
return false;

View File

@ -18,6 +18,7 @@
#include "config.hpp"
#include "log.hpp"
#include "recall_list_manager.hpp"
#include "resources.hpp"
#include "side_filter.hpp"
#include "variable.hpp"
@ -139,10 +140,8 @@ bool side_filter::match_internal(const team &t) const
}
}
if(!found && unit_filter["search_recall_list"].to_bool(false)) {
const std::vector<UnitPtr>& recall_list = t.recall_list();
BOOST_FOREACH(const UnitConstPtr & u, recall_list) {
std::vector<UnitPtr>::const_iterator it = find_if_matches_id(recall_list, u->id());
scoped_recall_unit this_unit("this_unit", t.save_id(), it - recall_list.begin());
BOOST_FOREACH(const UnitConstPtr & u, t.recall_list()) {
scoped_recall_unit this_unit("this_unit", t.save_id(),t.recall_list().find_index(u->id()));
if(u->matches_filter(unit_filter, u->get_location(), flat_)) {
found = true;
break;

View File

@ -29,6 +29,7 @@
#include "game_events/pump.hpp"
#include "dialogs.hpp"
#include "unit_helper.hpp"
#include "recall_list_manager.hpp"
#include "replay.hpp" //user choice
#include "resources.hpp"
#include <boost/foreach.hpp>
@ -222,12 +223,10 @@ SYNCED_COMMAND_HANDLER_FUNCTION(disband, child, /*use_undo*/, /*show*/, error_ha
team &current_team = (*resources::teams)[current_team_num - 1];
const std::string& unit_id = child["value"];
std::vector<UnitPtr>::iterator disband_unit =
find_if_matches_id(current_team.recall_list(), unit_id);
size_t old_size = current_team.recall_list().size();
current_team.recall_list().erase_if_matches_id(unit_id);
if(disband_unit != current_team.recall_list().end()) {
current_team.recall_list().erase(disband_unit);
} else {
if (old_size == current_team.recall_list().size()) {
error_handler("illegal disband\n", true);
return false;
}

View File

@ -18,6 +18,7 @@
#include "game_config.hpp"
#include "make_enum.hpp"
#include "map_location.hpp"
#include "recall_list_manager.hpp"
#include "savegame_config.hpp"
#include "unit_ptr.hpp"
#include "util.hpp"
@ -185,8 +186,8 @@ public:
{ countdown_time_ = amount; }
int action_bonus_count() const { return action_bonus_count_; }
void set_action_bonus_count(const int count) { action_bonus_count_ = count; }
std::vector<UnitPtr >& recall_list() {return recall_list_;}
const std::vector<UnitPtr >& recall_list() const {return recall_list_;}
recall_list_manager& recall_list() {return recall_list_;}
const recall_list_manager & recall_list() const {return recall_list_;}
void set_current_player(const std::string& player)
{ info_.current_player = player; }
@ -349,7 +350,7 @@ private:
mutable int countdown_time_;
int action_bonus_count_;
std::vector<UnitPtr > recall_list_;
recall_list_manager recall_list_;
std::string last_recruit_;
bool calculate_enemies(size_t index) const;

View File

@ -2466,48 +2466,6 @@ bool unit::matches_id(const std::string& unit_id) const
return id_ == unit_id;
}
bool find_if_matches_helper(const UnitPtr & ptr, const std::string & unit_id)
{
return ptr->matches_id(unit_id);
}
/**
* Used to find units in vectors by their ID. (Convenience wrapper)
* @returns what std::find_if() returns.
*/
std::vector<UnitPtr >::iterator find_if_matches_id(
std::vector<UnitPtr > &unit_list, // Not const so we can get a non-const iterator to return.
const std::string &unit_id)
{
return std::find_if(unit_list.begin(), unit_list.end(),
boost::bind(&find_if_matches_helper, _1, unit_id));
}
/**
* Used to find units in vectors by their ID. (Convenience wrapper; const version)
* @returns what std::find_if() returns.
*/
std::vector<UnitPtr >::const_iterator find_if_matches_id(
const std::vector<UnitPtr > &unit_list,
const std::string &unit_id)
{
return std::find_if(unit_list.begin(), unit_list.end(),
boost::bind(&find_if_matches_helper, _1, unit_id));
}
/**
* Used to erase units from vectors by their ID. (Convenience wrapper)
* @returns what std::vector<>::erase() returns.
*/
std::vector<UnitPtr >::iterator erase_if_matches_id(
std::vector<UnitPtr > &unit_list,
const std::string &unit_id)
{
return unit_list.erase(std::remove_if(unit_list.begin(), unit_list.end(),
boost::bind(&find_if_matches_helper, _1, unit_id)),
unit_list.end());
}
int side_units(int side)
{
int res = 0;

View File

@ -27,6 +27,7 @@
#include "fake_unit.hpp"
#include "fake_unit_manager.hpp"
#include "game_display.hpp"
#include "recall_list_manager.hpp"
#include "resources.hpp"
#include "replay_helper.hpp"
#include "statistics.hpp"
@ -147,18 +148,15 @@ void recall::apply_temp_modifier(unit_map& unit_map)
<< "] at position " << temp_unit_->get_location() << ".\n";
//temporarily remove unit from recall list
std::vector<UnitPtr>& recalls = resources::teams->at(team_index()).recall_list();
std::vector<UnitPtr>::iterator it = find_if_matches_id(recalls, temp_unit_->id());
assert(it != recalls.end());
UnitPtr it = resources::teams->at(team_index()).recall_list().extract_if_matches_id(temp_unit_->id());
assert(it);
//Add cost to money spent on recruits.
int cost = resources::teams->at(team_index()).recall_cost();
if ((*it)->recall_cost() > -1) {
cost = (*it)->recall_cost();
if (it->recall_cost() > -1) {
cost = it->recall_cost();
}
recalls.erase(it);
// Temporarily insert unit into unit_map
//unit map takes ownership of temp_unit
unit_map.insert(temp_unit_);
@ -174,7 +172,7 @@ void recall::remove_temp_modifier(unit_map& unit_map)
assert(temp_unit_.get());
//Put unit back into recall list
resources::teams->at(team_index()).recall_list().push_back(temp_unit_);
resources::teams->at(team_index()).recall_list().add(temp_unit_);
}
void recall::draw_hex(map_location const& hex)
@ -212,8 +210,7 @@ action::error recall::check_validity() const
return LOCATION_OCCUPIED;
}
//Check that unit to recall is still in side's recall list
const std::vector<UnitPtr>& recalls = (*resources::teams)[team_index()].recall_list();
if( find_if_matches_id(recalls, temp_unit_->id()) == recalls.end() ) {
if( !(*resources::teams)[team_index()].recall_list().find_if_matches_id(temp_unit_->id()) ) {
return UNIT_UNAVAILABLE;
}
//Check that there is still enough gold to recall this unit