Added multi-turn move whiteboard plans.

This commit is contained in:
Tommy Schmitz 2011-08-18 23:40:11 +00:00
parent b4132d5332
commit 2c07b91e6e
8 changed files with 260 additions and 136 deletions

View file

@ -704,7 +704,7 @@ config play_controller::to_config() const
void play_controller::finish_side_turn(){
resources::whiteboard->on_finish_side_turn();
resources::whiteboard->on_finish_side_turn(player_number_);
for(unit_map::iterator uit = units_.begin(); uit != units_.end(); ++uit) {
if (uit->side() == player_number_)

View file

@ -60,10 +60,10 @@ manager::manager():
mapbuilder_(),
highlighter_(),
route_(),
move_arrow_(),
fake_unit_(),
move_arrows_(),
fake_units_(),
key_poller_(new CKey),
hidden_unit_hex_(),
hidden_unit_hexes_(),
net_buffer_(resources::teams->size()),
team_plans_hidden_(resources::teams->size(),false)
{
@ -244,9 +244,11 @@ void manager::on_init_side()
}
}
void manager::on_finish_side_turn()
void manager::on_finish_side_turn(int side)
{
wait_for_side_init_ = true;
if(side == viewer_side())
viewer_actions()->synced_turn_shift();
highlighter_.reset();
erase_temp_move();
LOG_WB << "on_finish_side_turn()\n";
@ -462,11 +464,9 @@ void manager::draw_hex(const map_location& hex)
void manager::on_mouseover_change(const map_location& hex)
{
if (hidden_unit_hex_.valid())
{
resources::screen->remove_exclusive_draw(hidden_unit_hex_);
hidden_unit_hex_ = map_location();
}
foreach(map_location const& hex, hidden_unit_hexes_)
resources::screen->remove_exclusive_draw(hex);
hidden_unit_hexes_.clear();
map_location selected_hex = resources::screen->selected_hex();
unit_map::iterator it;
@ -539,6 +539,11 @@ void manager::create_temp_move()
{
route_.reset();
/*
* CHECK PRE-CONDITIONS
* (This section has multiple return paths.)
*/
if(!active_
|| wait_for_side_init_
|| executing_actions_
@ -548,11 +553,6 @@ void manager::create_temp_move()
assert(!has_planned_unit_map());
/*
* CHECK PRE-CONDITIONS
* (This section has multiple return paths.)
*/
pathfind::marked_route const& route =
resources::controller->get_mouse_handler_base().get_current_route();
@ -562,25 +562,6 @@ void manager::create_temp_move()
if (!selected_unit) return;
if (selected_unit->side() != resources::screen->viewing_side()) return;
//FIXME: Temporary: Don't draw move arrow if move goes beyond range.
bool cancel = false;
foreach (const map_location& hex, route.steps)
{
if (cancel)
{
erase_temp_move();
return;
}
pathfind::marked_route::mark_map::const_iterator w =
route.marks.find(hex);
//We only accept an end-of-first-turn or a capture mark if this is the move's last hex.
if (w != route.marks.end() && (w->second.turns == 1
|| w->second.capture))
{
cancel = true;
}
}
/*
* DONE CHECKING PRE-CONDITIONS, CREATE THE TEMP MOVE
* (This section has only one return path.)
@ -590,70 +571,110 @@ void manager::create_temp_move()
// wb::move object
route_.reset(new pathfind::marked_route(route));
//NOTE: route_.steps.back() = dst, and route_.steps.front() = src
//NOTE: route_->steps.back() = dst, and route_->steps.front() = src
if (!move_arrow_)
size_t turn = 0;
std::vector<map_location>::iterator prev_itor = route.steps.begin();
std::vector<map_location>::iterator curr_itor = prev_itor;
std::vector<map_location>::iterator end_itor = route.steps.end();
for(; curr_itor!=end_itor; ++curr_itor)
{
// Create temp arrow
move_arrow_.reset(new arrow());
move_arrow_->set_color(team::get_side_color_index(
viewer_side()));
move_arrow_->set_style(arrow::STYLE_HIGHLIGHTED);
const map_location& hex = *curr_itor;
//search for end-of-turn marks
pathfind::marked_route::mark_map::const_iterator w =
route.marks.find(hex);
if(w != route.marks.end() && w->second.turns > 0)
{
turn = w->second.turns-1;
if(turn >= move_arrows_.size())
move_arrows_.resize(turn+1);
if(turn >= fake_units_.size())
fake_units_.resize(turn+1);
arrow_ptr& move_arrow = move_arrows_[turn];
fake_unit_ptr& fake_unit = fake_units_[turn];
if(!move_arrow)
{
// Create temp arrow
move_arrow.reset(new arrow());
move_arrow->set_color(team::get_side_color_index(
viewer_side()));
move_arrow->set_style(arrow::STYLE_HIGHLIGHTED);
}
arrow_path_t path(prev_itor,curr_itor+1);
move_arrow->set_path(path);
if(path.size() >= 2)
{
if(!fake_unit)
{
// Create temp ghost unit
fake_unit.reset(new unit(*selected_unit),
wb::fake_unit_deleter());
resources::screen->place_temporary_unit(fake_unit.get());
fake_unit->set_ghosted(false);
}
unit_display::move_unit(path, *fake_unit, *resources::teams,
false); //get facing right
fake_unit->set_location(*curr_itor);
fake_unit->set_ghosted(false);
//if destination is over another unit, temporarily hide it
resources::screen->add_exclusive_draw(fake_unit->get_location(), *fake_unit);
hidden_unit_hexes_.push_back(fake_unit->get_location());
}
else //zero-hex path -- don't bother drawing a fake unit
fake_unit.reset();
prev_itor = curr_itor;
}
}
if (!fake_unit_)
{
// Create temp ghost unit
fake_unit_.reset(new unit(*selected_unit),
wb::fake_unit_deleter());
resources::screen->place_temporary_unit(fake_unit_.get());
fake_unit_->set_ghosted(false);
}
move_arrow_->set_path(route_->steps);
unit_display::move_unit(route_->steps, *fake_unit_, *resources::teams,
false); //get facing right
fake_unit_->set_location(route_->steps.back());
fake_unit_->set_ghosted(false);
//if destination is over another unit, temporarily hide it
resources::screen->add_exclusive_draw(fake_unit_->get_location(), *fake_unit_);
hidden_unit_hex_ = fake_unit_->get_location();
//toss out old arrows and fake units
move_arrows_.resize(turn+1);
fake_units_.resize(turn+1);
}
void manager::erase_temp_move()
{
if (move_arrow_)
{
move_arrow_.reset(); //auto-removes itself from display
}
if (fake_unit_)
{
fake_unit_.reset(); //auto-removes itself from display thanks to custom deleter in the shared_ptr
}
if (route_)
{
route_.reset();
}
move_arrows_.clear();
fake_units_.clear();
route_.reset();
}
void manager::save_temp_move()
{
if (has_temp_move() && !executing_actions_ && !resources::controller->is_linger_mode())
{
arrow_ptr move_arrow;
fake_unit_ptr fake_unit;
side_actions& sa = *viewer_actions();
unit const *const u = future_visible_unit(route_->steps.front());
assert(u);
size_t first_turn = sa.get_turn_num_of(*u);
move_arrow = arrow_ptr(move_arrow_);
fake_unit = fake_unit_ptr(fake_unit_);
assert(move_arrows_.size() == fake_units_.size());
size_t size = move_arrows_.size();
for(size_t i=0; i<size; ++i)
{
arrow_ptr move_arrow = move_arrows_[i];
if(!arrow::valid_path(move_arrow->get_path()))
continue;
viewer_actions()->queue_move(*route_, move_arrow, fake_unit);
size_t turn = first_turn + i;
fake_unit_ptr fake_unit = fake_units_[i];
pathfind::marked_route route;
route.steps = move_arrow->get_path();
route.move_cost = path_cost(route.steps,*u);
sa.queue_move(turn,route,move_arrow,fake_unit);
}
erase_temp_move();
on_save_action();
LOG_WB << *viewer_actions() << "\n";
print_help_once();
}
}
@ -668,8 +689,10 @@ void manager::save_temp_attack(const map_location& attack_from, const map_locati
map_location source_hex;
if (route_ && !route_->steps.empty())
{
move_arrow = arrow_ptr(move_arrow_);
fake_unit = fake_unit_ptr(fake_unit_);
assert(move_arrows_.size() == 1);
assert(fake_units_.size() == 1);
move_arrow = move_arrows_.front();
fake_unit = fake_units_.front();
assert(route_->steps.back() == attack_from);
source_hex = route_->steps.front();
@ -693,7 +716,8 @@ void manager::save_temp_attack(const map_location& attack_from, const map_locati
if (weapon_choice >= 0)
{
viewer_actions()->queue_attack(target_hex, weapon_choice, *route_, move_arrow, fake_unit);
side_actions& sa = *viewer_actions();
sa.queue_attack(sa.get_turn_num_of(*attacking_unit),target_hex,weapon_choice,*route_,move_arrow,fake_unit);
on_save_action();
@ -718,7 +742,11 @@ bool manager::save_recruit(const std::string& name, int side_num, const map_loca
}
else
{
viewer_actions()->queue_recruit(name, recruit_hex);
side_actions& sa = *viewer_actions();
size_t turn = sa.num_turns();
if(turn > 0)
--turn;
sa.queue_recruit(turn,name,recruit_hex);
created_planned_recruit = true;
on_save_action();
@ -742,7 +770,11 @@ bool manager::save_recall(const unit& unit, int side_num, const map_location& re
}
else
{
viewer_actions()->queue_recall(unit, recall_hex);
side_actions& sa = *viewer_actions();
size_t turn = sa.num_turns();
if(turn > 0)
--turn;
sa.queue_recall(turn,unit,recall_hex);
created_planned_recall = true;
on_save_action();
@ -757,7 +789,8 @@ void manager::save_suppose_dead(unit& curr_unit, map_location const& loc)
{
if(active_ && !executing_actions_ && !resources::controller->is_linger_mode())
{
viewer_actions()->queue_suppose_dead(curr_unit,loc);
side_actions& sa = *viewer_actions();
sa.queue_suppose_dead(sa.get_turn_num_of(curr_unit),curr_unit,loc);
on_save_action();
}
}

View file

@ -71,7 +71,7 @@ public:
* The on_* methods below inform the whiteboard of specific events
*/
void on_init_side();
void on_finish_side_turn();
void on_finish_side_turn(int side);
void on_mouseover_change(const map_location& hex);
void on_deselect_hex(){ erase_temp_move();}
void on_gamestate_change();
@ -109,7 +109,7 @@ public:
/// Creates a temporary visual arrow, that follows the cursor, for move creation purposes
void create_temp_move();
/// Informs whether an arrow is being displayed for move creation purposes
bool has_temp_move() const { return route_ && fake_unit_ && move_arrow_; }
bool has_temp_move() const { return route_ && !fake_units_.empty() && !move_arrows_.empty(); }
/// Erase the temporary arrow
void erase_temp_move();
/// Creates a move action for the current side, and erases the temp move.
@ -186,12 +186,12 @@ private:
boost::scoped_ptr<pathfind::marked_route> route_;
arrow_ptr move_arrow_;
fake_unit_ptr fake_unit_;
std::vector<arrow_ptr> move_arrows_;
std::vector<fake_unit_ptr> fake_units_;
boost::scoped_ptr<CKey> key_poller_;
map_location hidden_unit_hex_;
std::vector<map_location> hidden_unit_hexes_;
///net_buffer_[i] = whiteboard network data to be sent "from" teams[i].
std::vector<config> net_buffer_;

View file

@ -124,7 +124,7 @@ void side_actions::get_numbers(const map_location& hex, numbers_t& result)
bool side_actions::execute_next()
{
if (!actions_.empty())
if (!actions_.front().empty())
{
return execute(begin());
}
@ -154,7 +154,7 @@ void side_actions::execute_all()
{
iterator position = begin();
bool finished = execute(position);
keep_executing = finished && !empty();
keep_executing = finished && !actions_.front().empty();
}
}
@ -165,7 +165,7 @@ bool side_actions::execute(side_actions::iterator position)
ERR_WB << "Modifying action queue while temp modifiers are applied!!!\n";
}
if (actions_.empty() || !validate_iterator(position))
if(actions_.empty() || !validate_iterator(position) || position.turn_num_ > 0)
return false;
LOG_WB << "Before execution, " << *this << "\n";
@ -253,41 +253,41 @@ void side_actions::show()
act->show();
}
side_actions::iterator side_actions::queue_move(const pathfind::marked_route& route, arrow_ptr arrow, fake_unit_ptr fake_unit)
side_actions::iterator side_actions::queue_move(size_t turn, const pathfind::marked_route& route, arrow_ptr arrow, fake_unit_ptr fake_unit)
{
move_ptr new_move;
new_move.reset(new move(team_index(), hidden_, route, arrow, fake_unit));
return queue_action(new_move);
return queue_action(turn,new_move);
}
side_actions::iterator side_actions::queue_attack(const map_location& target_hex, int weapon_choice,
side_actions::iterator side_actions::queue_attack(size_t turn, const map_location& target_hex, int weapon_choice,
const pathfind::marked_route& route,
arrow_ptr arrow, fake_unit_ptr fake_unit)
{
attack_ptr new_attack;
new_attack.reset(new attack(team_index(), hidden_, target_hex, weapon_choice, route, arrow, fake_unit));
return queue_action(new_attack);
return queue_action(turn,new_attack);
}
side_actions::iterator side_actions::queue_recruit(const std::string& unit_name, const map_location& recruit_hex)
side_actions::iterator side_actions::queue_recruit(size_t turn, const std::string& unit_name, const map_location& recruit_hex)
{
recruit_ptr new_recruit;
new_recruit.reset(new recruit(team_index(), hidden_, unit_name, recruit_hex));
return queue_action(new_recruit);
return queue_action(turn,new_recruit);
}
side_actions::iterator side_actions::queue_recall(const unit& unit, const map_location& recall_hex)
side_actions::iterator side_actions::queue_recall(size_t turn, const unit& unit, const map_location& recall_hex)
{
recall_ptr new_recall;
new_recall.reset(new recall(team_index(), hidden_, unit, recall_hex));
return queue_action(new_recall);
return queue_action(turn,new_recall);
}
side_actions::iterator side_actions::queue_suppose_dead(unit& curr_unit, map_location const& loc)
side_actions::iterator side_actions::queue_suppose_dead(size_t turn, unit& curr_unit, map_location const& loc)
{
suppose_dead_ptr new_suppose_dead;
new_suppose_dead.reset(new suppose_dead(team_index(),hidden_,curr_unit,loc));
return queue_action(new_suppose_dead);
return queue_action(turn,new_suppose_dead);
}
side_actions::iterator side_actions::insert_action(iterator position, action_ptr action)
@ -303,11 +303,10 @@ side_actions::iterator side_actions::insert_action(iterator position, action_ptr
return valid_position;
}
side_actions::iterator side_actions::queue_action(action_ptr action)
side_actions::iterator side_actions::queue_action(size_t turn_num, action_ptr action)
{
if(resources::whiteboard->has_planned_unit_map())
ERR_WB << "Modifying action queue while temp modifiers are applied!!!\n";
size_t turn_num = 0; //< this will need to change eventually
iterator result = synced_enqueue(turn_num, action);
LOG_WB << "Inserted into turn #" << (turn_num+1) << " at position #" << actions_[turn_num].size()
<< " : " << action <<"\n";
@ -538,6 +537,14 @@ void side_actions::remove_invalid_of(unit const* u)
}
}
size_t side_actions::get_turn_num_of(unit const& u) const
{
const_iterator itor = const_cast<side_actions*>(this)->find_last_action_of(&u);
if(itor == end())
return 0;
return itor.base_.turn_num_;
}
void side_actions::validate_actions()
{
if (resources::whiteboard->has_planned_unit_map())
@ -840,4 +847,49 @@ side_actions::const_iterator side_actions::end() const
side_actions::const_reverse_iterator side_actions::rend() const
{ return const_reverse_iterator(const_cast<side_actions*>(this)->rend()); }
void side_actions::raw_turn_shift()
{
//optimization
if(actions_.size() < 2)
return;
//find units who still have plans for turn 0 (i.e. were too lazy to finish their jobs)
std::set<unit const*> lazy_units;
foreach(action_ptr const& act, iter_turn(0))
{
unit const* u = act->get_unit();
if(u)
lazy_units.insert(u);
}
//push their plans back one turn
std::set<unit const*>::iterator lazy_end = lazy_units.end();
iterator itor = end();
while(itor != begin())
{
--itor;
action_ptr act = *itor;
if(lazy_units.find(act->get_unit()) != lazy_end)
{
raw_enqueue(itor.turn_num_+1,act);
itor = raw_erase(itor);
}
}
//push any remaining first-turn plans into the second turn
foreach(action_ptr act, actions_.front())
actions_[1].push_front(act);
actions_.front().clear();
//shift everything forward one turn
actions_.pop_front();
}
void side_actions::synced_turn_shift()
{
raw_turn_shift();
resources::whiteboard->queue_net_cmd(team_index(),make_net_cmd_refresh());
}
} //end namespace wb

View file

@ -41,7 +41,7 @@ class side_actions: public boost::enable_shared_from_this<side_actions>
* actions_.empty() || !actions_.back().empty();
*/
typedef std::vector<action_queue> contents_t;
typedef std::deque<action_queue> contents_t;
public:
class iterator;
@ -144,33 +144,33 @@ public:
* Queues a move to be executed last
* @return The queued move's position
*/
iterator queue_move(const pathfind::marked_route& route,
iterator queue_move(size_t turn_num, const pathfind::marked_route& route,
arrow_ptr arrow, fake_unit_ptr fake_unit);
/**
* Queues an attack or attack-move to be executed last
* @return The queued attack's position
*/
iterator queue_attack(const map_location& target_hex, int weapon_choice, const pathfind::marked_route& route,
iterator queue_attack(size_t turn_num, const map_location& target_hex, int weapon_choice, const pathfind::marked_route& route,
arrow_ptr arrow, fake_unit_ptr fake_unit);
/**
* Queues a recruit to be executed last
* @return The queued recruit's position
*/
iterator queue_recruit(const std::string& unit_name, const map_location& recruit_hex);
iterator queue_recruit(size_t turn_num, const std::string& unit_name, const map_location& recruit_hex);
/**
* Queues a recall to be executed last
* @return The queued recall's position
*/
iterator queue_recall(const unit& unit, const map_location& recall_hex);
iterator queue_recall(size_t turn_num, const unit& unit, const map_location& recall_hex);
/**
* Queues a suppose_dead to be executed last
* @return The queued suppose_dead's position (an iterator to it)
*/
iterator queue_suppose_dead(unit& curr_unit, map_location const& loc);
iterator queue_suppose_dead(size_t turn_num, unit& curr_unit, map_location const& loc);
/**
* Inserts an action at the specified position. The begin() and end() functions might prove useful here.
@ -182,7 +182,7 @@ public:
* Queues an action to be executed last
* @return The queued action's position
*/
iterator queue_action(action_ptr action);
iterator queue_action(size_t turn_num, action_ptr action);
/**
* Moves an action earlier in the execution order (i.e. at the front of the queue),
@ -232,6 +232,9 @@ public:
///Removes all invalid actions "attached" to the unit
void remove_invalid_of(unit const*);
///Determines the appropriate turn number for the next action planned for this unit
size_t get_turn_num_of(unit const&) const;
///Validates all planned actions in the queue
void validate_actions();
@ -240,6 +243,9 @@ public:
///Used to track gold spending by recruits/recalls when building the future unit map
void change_gold_spent_by(int difference) { gold_spent_ += difference; assert(gold_spent_ >= 0);}
void side_actions::raw_turn_shift();
void side_actions::synced_turn_shift();
/**
* Network code. A net_cmd object (a config in disguise) represents a modification
* to a side_actions object. execute_net_cmd() translates one of these into

View file

@ -105,6 +105,19 @@ unit* future_visible_unit(int on_side, map_location hex, int viewer_side)
return NULL;
}
int path_cost(std::vector<map_location> const& path, unit const& u)
{
assert(!path.empty());
int result = 0;
gamemap const& map = *resources::game_map;
foreach(map_location const& loc, std::make_pair(path.begin()+1,path.end()))
result += u.movement_cost(map[loc]);
return result;
}
temporary_unit_hider::temporary_unit_hider(unit& u)
: unit_(&u)
{unit_->set_hidden(true);}

View file

@ -20,6 +20,8 @@
#ifndef WB_UTILITY_HPP_
#define WB_UTILITY_HPP_
#include <vector>
#include "typedefs.hpp"
class unit;
@ -58,6 +60,9 @@ unit* future_visible_unit(map_location hex, int viewer_side = wb::viewer_side())
/// @param on_side Only search for units of this side.
unit* future_visible_unit(int on_side, map_location hex, int viewer_side = wb::viewer_side());
/// Computes the MP cost for u to travel path
int path_cost(std::vector<map_location> const& path, unit const& u);
struct temporary_unit_hider {
temporary_unit_hider(unit& u);
~temporary_unit_hider();

View file

@ -34,6 +34,7 @@
#include "side_actions.hpp"
#include "typedefs.hpp"
#include "foreach.hpp"
#include "play_controller.hpp"
#include "resources.hpp"
#include "team.hpp"
@ -79,37 +80,51 @@ private:
{
Derived* const new_this = static_cast<Derived*>(this);
//Determine how many turns' worth of plans there are
size_t max_turns = 0;
foreach(team& t, *resources::teams)
max_turns = std::max(max_turns,t.get_side_actions()->num_turns());
size_t const current_team = resources::controller->current_side() - 1;
size_t const num_teams = resources::teams->size();
for(size_t iteration = 0; iteration < num_teams; ++iteration)
//for each turn with any planned actions
for(size_t turn_iter=0; turn_iter<max_turns; ++turn_iter)
{
size_t const team_index
= (current_team+num_teams+(reverse? -1-iteration: iteration)) % num_teams;
team& t = resources::teams->at(team_index);
side_actions& sa = *t.get_side_actions();
if(!new_this->pre_visit_team(team_index,t,sa))
continue; //< Skip this team's actions
size_t const turn = (reverse? max_turns-1-turn_iter: turn_iter);
if(reverse)
//for each team
for(size_t team_iter = 0; team_iter < num_teams; ++team_iter)
{
side_actions::reverse_iterator itor = sa.rbegin();
side_actions::reverse_iterator end = sa.rend();
while(itor!=end) {
++itor;
if(!new_this->visit(team_index,t,sa,side_actions::iterator(itor)))
return; //< Early abort
size_t const team_index
= (current_team+num_teams+(reverse? -1-team_iter: team_iter)) % num_teams;
team& t = resources::teams->at(team_index);
side_actions& sa = *t.get_side_actions();
if(!new_this->pre_visit_team(team_index,t,sa))
continue; //< Skip this team's actions
if(reverse)
{
side_actions::rrange_t acts = sa.riter_turn(turn);
side_actions::reverse_iterator itor = acts.first;
side_actions::reverse_iterator end = acts.second;
while(itor!=end) {
++itor;
if(!new_this->visit(team_index,t,sa,itor.base()))
return; //< Early abort
}
}
else //forward
{
side_actions::range_t acts = sa.iter_turn(turn);
side_actions::iterator itor = acts.first;
side_actions::iterator end = acts.second;
for(; itor!=end; ++itor)
if(!new_this->visit(team_index,t,sa,itor))
return; //< Early abort
}
if(!new_this->post_visit_team(team_index,t,sa))
break; //< Early abort
}
else //forward
{
side_actions::iterator itor = sa.begin();
side_actions::iterator end = sa.end();
for(; itor!=end; ++itor)
if(!new_this->visit(team_index,t,sa,itor))
return; //< Early abort
}
if(!new_this->post_visit_team(team_index,t,sa))
break; //< Early abort
}
}
};