Added the ability to undo recall actions.
Involved changing: * recruit_unit - location parameter is now a reference so we can return the actual position the unit was placed at (saves O(N) search) * all locations that call recruit_unit (but don't need the actual position) * turn_info::recall,::undo,::redo - added support for undoing recalls * undo_action - added new fields for recall actions
This commit is contained in:
parent
7d02b51702
commit
634e1dc978
7 changed files with 114 additions and 75 deletions
|
@ -69,7 +69,7 @@ namespace victory_conditions
|
|||
|
||||
std::string recruit_unit(const gamemap& map, int side,
|
||||
std::map<gamemap::location,unit>& units, unit& new_unit,
|
||||
gamemap::location recruit_location, display* disp, bool need_castle, bool full_movement)
|
||||
gamemap::location& recruit_location, display* disp, bool need_castle, bool full_movement)
|
||||
{
|
||||
std::cerr << "recruiting unit for side " << side << "\n";
|
||||
typedef std::map<gamemap::location,unit> units_map;
|
||||
|
|
|
@ -40,7 +40,7 @@
|
|||
//If the unit cannot be recruited, then a human-readable message
|
||||
//describing why not will be returned. On success, the return string is empty
|
||||
std::string recruit_unit(const gamemap& map, int team, unit_map& units,
|
||||
unit& u, gamemap::location preferred_location,
|
||||
unit& u, gamemap::location& recruit_location,
|
||||
display *disp=NULL, bool need_castle=true, bool full_movement=false);
|
||||
|
||||
//a structure which defines all the statistics for a potential
|
||||
|
@ -162,10 +162,14 @@ int combat_modifier(const gamestatus& status,
|
|||
//structure which records information to be able to undo a movement
|
||||
struct undo_action {
|
||||
undo_action(const std::vector<gamemap::location>& rt,int sm,int orig=-1)
|
||||
: route(rt), starting_moves(sm), original_village_owner(orig) {}
|
||||
: route(rt), starting_moves(sm), original_village_owner(orig), recall_pos(-1) {}
|
||||
undo_action(const gamemap::location& loc, int pos) : recall_loc(loc), recall_pos(pos) {}
|
||||
std::vector<gamemap::location> route;
|
||||
int starting_moves;
|
||||
int original_village_owner;
|
||||
gamemap::location recall_loc;
|
||||
int recall_pos;
|
||||
bool is_recall() const { return recall_pos >= 0; }
|
||||
};
|
||||
|
||||
typedef std::deque<undo_action> undo_list;
|
||||
|
|
|
@ -660,7 +660,8 @@ bool event_handler::handle_event_command(const queued_event& event_info, const s
|
|||
std::vector<unit>& avail = state_of_game->available_units;
|
||||
for(std::vector<unit>::iterator u = avail.begin(); u != avail.end(); ++u) {
|
||||
if(u->matches_filter(cfg)) {
|
||||
recruit_unit(*game_map,1,*units,*u,gamemap::location(),screen,false,true);
|
||||
gamemap::location loc;
|
||||
recruit_unit(*game_map,1,*units,*u,loc,screen,false,true);
|
||||
avail.erase(u);
|
||||
break;
|
||||
}
|
||||
|
|
161
src/playturn.cpp
161
src/playturn.cpp
|
@ -1075,33 +1075,46 @@ void turn_info::undo()
|
|||
|
||||
const command_disabler disable_commands(&gui_);
|
||||
|
||||
const int starting_moves = undo_stack_.back().starting_moves;
|
||||
std::vector<gamemap::location> route = undo_stack_.back().route;
|
||||
std::reverse(route.begin(),route.end());
|
||||
const unit_map::iterator u = units_.find(route.front());
|
||||
if(u == units_.end()) {
|
||||
assert(false);
|
||||
return;
|
||||
undo_action& action = undo_stack_.back();
|
||||
if (action.is_recall()) {
|
||||
// Undo a recall action
|
||||
team& current_team = teams_[team_num_-1];
|
||||
const unit& un = units_.find(action.recall_loc)->second;
|
||||
statistics::un_recall_unit(un);
|
||||
current_team.spend_gold(-game_config::recall_cost);
|
||||
std::vector<unit>& recall_list = state_of_game_.available_units;
|
||||
recall_list.insert(recall_list.begin()+action.recall_pos,un);
|
||||
units_.erase(action.recall_loc);
|
||||
gui_.draw_tile(action.recall_loc.x,action.recall_loc.y);
|
||||
} else {
|
||||
// Undo a move action
|
||||
const int starting_moves = action.starting_moves;
|
||||
std::vector<gamemap::location> route = action.route;
|
||||
std::reverse(route.begin(),route.end());
|
||||
const unit_map::iterator u = units_.find(route.front());
|
||||
if(u == units_.end()) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
if(map_.underlying_terrain(map_[route.front().x][route.front().y]) == gamemap::TOWER) {
|
||||
get_tower(route.front(),teams_,action.original_village_owner,units_);
|
||||
}
|
||||
|
||||
action.starting_moves = u->second.movement_left();
|
||||
|
||||
unit un = u->second;
|
||||
un.set_goto(gamemap::location());
|
||||
units_.erase(u);
|
||||
gui_.move_unit(route,un);
|
||||
un.set_movement(starting_moves);
|
||||
units_.insert(std::pair<gamemap::location,unit>(route.back(),un));
|
||||
gui_.draw_tile(route.back().x,route.back().y);
|
||||
}
|
||||
|
||||
if(map_.underlying_terrain(map_[route.front().x][route.front().y]) == gamemap::TOWER) {
|
||||
get_tower(route.front(),teams_,
|
||||
undo_stack_.back().original_village_owner,units_);
|
||||
}
|
||||
|
||||
undo_stack_.back().starting_moves = u->second.movement_left();
|
||||
|
||||
unit un = u->second;
|
||||
un.set_goto(gamemap::location());
|
||||
units_.erase(u);
|
||||
gui_.move_unit(route,un);
|
||||
un.set_movement(starting_moves);
|
||||
units_.insert(std::pair<gamemap::location,unit>(route.back(),un));
|
||||
gui_.invalidate_unit();
|
||||
gui_.invalidate_game_status();
|
||||
gui_.draw_tile(route.back().x,route.back().y);
|
||||
|
||||
redo_stack_.push_back(undo_stack_.back());
|
||||
redo_stack_.push_back(action);
|
||||
undo_stack_.pop_back();
|
||||
|
||||
gui_.set_paths(NULL);
|
||||
|
@ -1135,34 +1148,50 @@ void turn_info::redo()
|
|||
current_route_.steps.clear();
|
||||
gui_.set_route(NULL);
|
||||
|
||||
const int starting_moves = redo_stack_.back().starting_moves;
|
||||
std::vector<gamemap::location> route = redo_stack_.back().route;
|
||||
const unit_map::iterator u = units_.find(route.front());
|
||||
if(u == units_.end()) {
|
||||
assert(false);
|
||||
return;
|
||||
undo_action& action = redo_stack_.back();
|
||||
if (action.is_recall()) {
|
||||
// Redo recall
|
||||
std::vector<unit>& recall_list = state_of_game_.available_units;
|
||||
unit& un = recall_list[action.recall_pos];
|
||||
recruit_unit(map_,team_num_,units_,un,action.recall_loc,&gui_);
|
||||
statistics::recall_unit(un);
|
||||
team& current_team = teams_[team_num_-1];
|
||||
current_team.spend_gold(game_config::recall_cost);
|
||||
recall_list.erase(recall_list.begin()+action.recall_pos);
|
||||
recorder.add_recall(action.recall_pos,action.recall_loc);
|
||||
|
||||
gui_.draw_tile(action.recall_loc.x,action.recall_loc.y);
|
||||
} else {
|
||||
// Redo movement action
|
||||
const int starting_moves = action.starting_moves;
|
||||
std::vector<gamemap::location> route = action.route;
|
||||
const unit_map::iterator u = units_.find(route.front());
|
||||
if(u == units_.end()) {
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
|
||||
action.starting_moves = u->second.movement_left();
|
||||
|
||||
unit un = u->second;
|
||||
un.set_goto(gamemap::location());
|
||||
units_.erase(u);
|
||||
gui_.move_unit(route,un);
|
||||
un.set_movement(starting_moves);
|
||||
units_.insert(std::pair<gamemap::location,unit>(route.back(),un));
|
||||
|
||||
if(map_.underlying_terrain(map_[route.back().x][route.back().y]) == gamemap::TOWER) {
|
||||
get_tower(route.back(),teams_,un.side()-1,units_);
|
||||
}
|
||||
|
||||
gui_.draw_tile(route.back().x,route.back().y);
|
||||
|
||||
recorder.add_movement(route.front(),route.back());
|
||||
}
|
||||
|
||||
redo_stack_.back().starting_moves = u->second.movement_left();
|
||||
|
||||
unit un = u->second;
|
||||
un.set_goto(gamemap::location());
|
||||
units_.erase(u);
|
||||
gui_.move_unit(route,un);
|
||||
un.set_movement(starting_moves);
|
||||
units_.insert(std::pair<gamemap::location,unit>(route.back(),un));
|
||||
gui_.invalidate_unit();
|
||||
gui_.invalidate_game_status();
|
||||
|
||||
if(map_.underlying_terrain(map_[route.back().x][route.back().y]) == gamemap::TOWER) {
|
||||
get_tower(route.back(),teams_,un.side()-1,units_);
|
||||
}
|
||||
|
||||
gui_.draw_tile(route.back().x,route.back().y);
|
||||
|
||||
recorder.add_movement(route.front(),route.back());
|
||||
|
||||
undo_stack_.push_back(redo_stack_.back());
|
||||
undo_stack_.push_back(action);
|
||||
redo_stack_.pop_back();
|
||||
}
|
||||
|
||||
|
@ -1505,8 +1534,8 @@ void turn_info::do_recruit(const std::string& name)
|
|||
//create a unit with traits
|
||||
recorder.add_recruit(recruit_num,last_hex_);
|
||||
unit new_unit(&(u_type->second),team_num_,true);
|
||||
const std::string& msg =
|
||||
recruit_unit(map_,team_num_,units_,new_unit,last_hex_,&gui_);
|
||||
gamemap::location loc = last_hex_;
|
||||
const std::string& msg = recruit_unit(map_,team_num_,units_,new_unit,loc,&gui_);
|
||||
if(msg.empty()) {
|
||||
current_team.spend_gold(u_type->second.cost());
|
||||
statistics::recruit_unit(new_unit);
|
||||
|
@ -1591,18 +1620,17 @@ void turn_info::recall()
|
|||
return;
|
||||
|
||||
team& current_team = teams_[team_num_-1];
|
||||
std::vector<unit>& recall_list = state_of_game_.available_units;
|
||||
|
||||
//sort the available units into order by value
|
||||
//so that the most valuable units are shown first
|
||||
std::sort(state_of_game_.available_units.begin(),
|
||||
state_of_game_.available_units.end(),
|
||||
compare_unit_values());
|
||||
std::sort(recall_list.begin(),recall_list.end(),compare_unit_values());
|
||||
|
||||
gui_.draw(); //clear the old menu
|
||||
|
||||
if((*level_)["disallow_recall"] == "yes") {
|
||||
gui::show_dialog(gui_,NULL,"",string_table["recall_disallowed"]);
|
||||
} else if(state_of_game_.available_units.empty()) {
|
||||
} else if(recall_list.empty()) {
|
||||
gui::show_dialog(gui_,NULL,"",string_table["no_recall"]);
|
||||
} else if(current_team.gold() < game_config::recall_cost) {
|
||||
std::stringstream msg;
|
||||
|
@ -1612,9 +1640,7 @@ void turn_info::recall()
|
|||
gui::show_dialog(gui_,NULL,"",msg.str());
|
||||
} else {
|
||||
std::vector<std::string> options;
|
||||
for(std::vector<unit>::const_iterator unit =
|
||||
state_of_game_.available_units.begin();
|
||||
unit != state_of_game_.available_units.end(); ++unit) {
|
||||
for(std::vector<unit>::const_iterator unit = recall_list.begin(); unit != recall_list.end(); ++unit) {
|
||||
std::stringstream option;
|
||||
const std::string& description = unit->description().empty() ? "-" : unit->description();
|
||||
option << "&" << unit->type().image() << "," << unit->type().language_name() << "," << description << ","
|
||||
|
@ -1630,7 +1656,7 @@ void turn_info::recall()
|
|||
options.push_back(option.str());
|
||||
}
|
||||
|
||||
delete_recall_unit recall_deleter(gui_,state_of_game_.available_units);
|
||||
delete_recall_unit recall_deleter(gui_,recall_list);
|
||||
gui::dialog_button delete_button(&recall_deleter,string_table["delete_unit"]);
|
||||
std::vector<gui::dialog_button> buttons;
|
||||
buttons.push_back(delete_button);
|
||||
|
@ -1638,26 +1664,23 @@ void turn_info::recall()
|
|||
const int res = gui::show_dialog(gui_,NULL,"",
|
||||
string_table["select_unit"] + ":\n",
|
||||
gui::OK_CANCEL,&options,
|
||||
&state_of_game_.available_units,"",NULL,
|
||||
&recall_list,"",NULL,
|
||||
NULL,NULL,-1,-1,NULL,&buttons);
|
||||
if(res >= 0) {
|
||||
const std::string err = recruit_unit(map_,team_num_,
|
||||
units_,state_of_game_.available_units[res],
|
||||
last_hex_,&gui_);
|
||||
unit& un = recall_list[res];
|
||||
gamemap::location loc = last_hex_;
|
||||
const std::string err = recruit_unit(map_,team_num_,units_,un,loc,&gui_);
|
||||
if(!err.empty()) {
|
||||
gui::show_dialog(gui_,NULL,"",err,gui::OK_ONLY);
|
||||
} else {
|
||||
statistics::recall_unit(state_of_game_.available_units[res]);
|
||||
|
||||
statistics::recall_unit(un);
|
||||
current_team.spend_gold(game_config::recall_cost);
|
||||
state_of_game_.available_units.erase(
|
||||
state_of_game_.available_units.begin()+res);
|
||||
recorder.add_recall(res,loc);
|
||||
|
||||
recorder.add_recall(res,last_hex_);
|
||||
|
||||
undo_stack_.clear();
|
||||
undo_stack_.push_back(undo_action(loc,res));
|
||||
redo_stack_.clear();
|
||||
|
||||
|
||||
recall_list.erase(recall_list.begin()+res);
|
||||
gui_.invalidate_game_status();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -591,7 +591,7 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
const std::string& recruit_num = (*child)["value"];
|
||||
const int val = atoi(recruit_num.c_str());
|
||||
|
||||
const gamemap::location loc(*child);
|
||||
gamemap::location loc(*child);
|
||||
|
||||
const std::set<std::string>& recruits = current_team.recruits();
|
||||
|
||||
|
@ -629,7 +629,7 @@ bool do_replay(display& disp, const gamemap& map, const game_data& gameinfo,
|
|||
const std::string recall_num = (*child)["value"];
|
||||
const int val = atoi(recall_num.c_str());
|
||||
|
||||
const gamemap::location loc(*child);
|
||||
gamemap::location loc(*child);
|
||||
|
||||
if(val >= 0 && val < int(state_of_game.available_units.size())) {
|
||||
statistics::recall_unit(state_of_game.available_units[val]);
|
||||
|
|
|
@ -318,6 +318,16 @@ void recall_unit(const unit& u)
|
|||
s.recall_cost += u.type().cost();
|
||||
}
|
||||
|
||||
void un_recall_unit(const unit& u)
|
||||
{
|
||||
if(stats_disabled > 0)
|
||||
return;
|
||||
|
||||
stats& s = get_stats(u.side());
|
||||
s.recalls[u.type().name()]--;
|
||||
s.recall_cost -= u.type().cost();
|
||||
}
|
||||
|
||||
void advance_unit(const unit& u)
|
||||
{
|
||||
if(stats_disabled > 0)
|
||||
|
|
|
@ -67,6 +67,7 @@ namespace statistics
|
|||
|
||||
void recruit_unit(const unit& u);
|
||||
void recall_unit(const unit& u);
|
||||
void un_recall_unit(const unit& u);
|
||||
|
||||
void advance_unit(const unit& u);
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue