Added a "continue move" action.

There are four parts to this patch:

1) add the action to the usual places 2) add a gamemap::location to class unit,
which stores the location we were trying to move to, 3) clear this
location on end-of-turn, or if the unit moves again this turn, and 4)
if a move is interrupted, reslect the unit's hex.

The new hotkey is 't'. To use this feature, select a unit that had its
move interrupted by seeing allies/enemies, and right click >> "Continue
Move", or press the 't' key.
This commit is contained in:
John B. Messerly 2004-05-04 01:50:19 +00:00
parent a9c9423cd7
commit 08e46b18d9
10 changed files with 144 additions and 85 deletions

View file

@ -39,7 +39,7 @@ height=600
[menu]
is_context_menu=true
items=undo,redo,cycle,describeunit,speak,recruit,recall,createunit,renameunit,labelterrain,showenemymoves,bestenemymoves,delayshroud,updateshroud,endturn
items=undo,redo,cycle,describeunit,speak,continue,recruit,recall,createunit,renameunit,labelterrain,showenemymoves,bestenemymoves,delayshroud,updateshroud,endturn
[/menu]
# top panel

View file

@ -138,6 +138,10 @@ language="English"
command=speak
key=m
[/hotkey]
[hotkey]
command=continue
key=t
[/hotkey]
game_title="The Battle for Wesnoth"
version="Version"
@ -465,6 +469,7 @@ action_showenemymoves="Show Enemy Moves"
action_bestenemymoves="Best Possible Enemy Moves"
action_delayshroud="Delay Shroud Updates"
action_updateshroud="Update Shroud Now"
action_continue="Continue Move"
action_editnewmap="New Map"
action_editloadmap="Load Map"
action_editsavemap="Save Map"

View file

@ -1440,7 +1440,8 @@ size_t move_unit(display* disp, const game_data& gamedata,
const gamestatus& status, const gamemap& map,
unit_map& units, std::vector<team>& teams,
const std::vector<gamemap::location>& route,
replay* move_recorder, undo_list* undo_stack, gamemap::location *next_unit)
replay* move_recorder, undo_list* undo_stack,
gamemap::location *next_unit, bool continue_move)
{
//stop the user from issuing any commands while the unit is moving
const command_disabler disable_commands(disp);
@ -1459,14 +1460,15 @@ size_t move_unit(display* disp, const game_data& gamedata,
const bool skirmisher = u.type().is_skirmisher();
const bool check_shroud = teams[team_num].auto_shroud_updates() &&
(teams[team_num].uses_shroud() || teams[team_num].uses_fog());
team& team = teams[team_num];
const bool check_shroud = team.auto_shroud_updates() &&
(team.uses_shroud() || team.uses_fog());
//if we use shroud/fog of war, count out the units we can currently see
std::set<gamemap::location> known_units;
if(check_shroud) {
for(unit_map::const_iterator u = units.begin(); u != units.end(); ++u) {
if(teams[team_num].fogged(u->first.x,u->first.y) == false) {
if(team.fogged(u->first.x,u->first.y) == false) {
known_units.insert(u->first);
}
}
@ -1485,14 +1487,14 @@ size_t move_unit(display* disp, const game_data& gamedata,
const unit_map::const_iterator enemy_unit = units.find(*step);
const int mv = u.movement_cost(map,terrain);
if(discovered_unit || seen_units.empty() == false || mv > moves_left || enemy_unit != units.end() &&
teams[team_num].is_enemy(enemy_unit->second.side())) {
if(discovered_unit || continue_move == false && seen_units.empty() == false ||
mv > moves_left || enemy_unit != units.end() && team.is_enemy(enemy_unit->second.side())) {
break;
} else {
moves_left -= mv;
}
if(!skirmisher && enemy_zoc(map,status,units,teams,*step,teams[team_num],u.side())) {
if(!skirmisher && enemy_zoc(map,status,units,teams,*step,team,u.side())) {
moves_left = 0;
}
@ -1525,7 +1527,8 @@ size_t move_unit(display* disp, const game_data& gamedata,
const std::map<gamemap::location,unit>::const_iterator it = units.find(adjacent[i]);
if(it != units.end() && teams[u.side()-1].is_enemy(it->second.side()) &&
it->second.invisible(map.underlying_terrain(map[it->first.x][it->first.y]),status.get_time_of_day().lawful_bonus,it->first,units,teams)) {
it->second.invisible(map.underlying_terrain(map[it->first.x][it->first.y]),
status.get_time_of_day().lawful_bonus,it->first,units,teams)) {
discovered_unit = true;
should_clear_stack = true;
break;
@ -1543,16 +1546,20 @@ size_t move_unit(display* disp, const game_data& gamedata,
assert(steps.size() <= route.size());
if (next_unit != NULL )
if (next_unit != NULL)
*next_unit = steps.back();
//if we can't get all the way there and have to set a go-to,
//unless we stop early because of sighting a unit
if(steps.size() != route.size() && seen_units.empty()) {
ui->second.set_goto(route.back());
u.set_goto(route.back());
//if we can't get all the way there and have to set a go-to.
if(steps.size() != route.size() && discovered_unit == false) {
if(seen_units.empty() == false) {
u.set_interrupted_move(route.back());
} else {
u.set_goto(route.back());
}
} else {
u.set_interrupted_move(gamemap::location());
}
if(steps.size() < 2) {
return 0;
}
@ -1605,7 +1612,7 @@ size_t move_unit(display* disp, const game_data& gamedata,
0.0,0.0,100,disp->map_area(),font::CENTER_ALIGN);
}
if(seen_units.empty() == false) {
if(continue_move == false && seen_units.empty() == false) {
//the message depends on how many units have been sighted, and whether
//they are allies or enemies, so calculate that out here
int nfriends = 0, nenemies = 0;
@ -1613,7 +1620,7 @@ size_t move_unit(display* disp, const game_data& gamedata,
std::cerr << "processing unit at " << (i->x+1) << "," << (i->y+1) << "\n";
const unit_map::const_iterator u = units.find(*i);
assert(u != units.end());
if(teams[team_num].is_enemy(u->second.side())) {
if(team.is_enemy(u->second.side())) {
++nenemies;
} else {
++nfriends;

View file

@ -180,11 +180,12 @@ typedef std::deque<undo_action> undo_list;
//a goto order will be set. If move_recorder is not NULL, the move will
//be recorded in it. If undos is not NULL, undo information will be added.
size_t move_unit(display* disp, const game_data& gamedata,
const gamestatus& status, const gamemap& map,
unit_map& units, std::vector<team>& teams,
const std::vector<gamemap::location>& steps,
replay* move_recorder, undo_list* undos,
gamemap::location *next_unit = NULL);
const gamestatus& status, const gamemap& map,
unit_map& units, std::vector<team>& teams,
const std::vector<gamemap::location>& steps,
replay* move_recorder, undo_list* undos,
gamemap::location *next_unit = NULL,
bool continue_move = false);
//function which recalculates the fog
void recalculate_fog(const gamemap& map, const gamestatus& status,

View file

@ -80,6 +80,7 @@ HOTKEY_COMMAND string_to_command(const std::string& str)
m.insert(val("editpaste",HOTKEY_EDIT_PASTE));
m.insert(val("delayshroud",HOTKEY_DELAY_SHROUD));
m.insert(val("updateshroud",HOTKEY_UPDATE_SHROUD));
m.insert(val("continue",HOTKEY_CONTINUE_MOVE));
}
const std::map<std::string,HOTKEY_COMMAND>::const_iterator i = m.find(str);
@ -381,11 +382,17 @@ void execute_command(display& disp, HOTKEY_COMMAND command, command_executor* ex
executor->show_enemy_moves(true);
break;
case HOTKEY_DELAY_SHROUD:
if(executor) executor->toggle_shroud_updates();
if(executor)
executor->toggle_shroud_updates();
break;
case HOTKEY_UPDATE_SHROUD:
if(executor) executor->update_shroud_now();
if(executor)
executor->update_shroud_now();
break;
case HOTKEY_CONTINUE_MOVE:
if(executor)
executor->continue_move();
break;
case HOTKEY_QUIT_GAME: {
const int res = gui::show_dialog(disp,NULL,"",string_table["quit_message"],gui::YES_NO);
if(res == 0) {

View file

@ -34,7 +34,7 @@ enum HOTKEY_COMMAND { HOTKEY_CYCLE_UNITS, HOTKEY_END_UNIT_TURN, HOTKEY_LEADER,
HOTKEY_SPEAK, HOTKEY_CREATE_UNIT, HOTKEY_PREFERENCES,
HOTKEY_OBJECTIVES, HOTKEY_UNIT_LIST, HOTKEY_STATISTICS, HOTKEY_QUIT_GAME,
HOTKEY_LABEL_TERRAIN, HOTKEY_SHOW_ENEMY_MOVES, HOTKEY_BEST_ENEMY_MOVES,
HOTKEY_DELAY_SHROUD, HOTKEY_UPDATE_SHROUD,
HOTKEY_DELAY_SHROUD, HOTKEY_UPDATE_SHROUD, HOTKEY_CONTINUE_MOVE,
//editing specific commands
HOTKEY_EDIT_SET_TERRAIN,
@ -114,6 +114,7 @@ public:
virtual void show_enemy_moves(bool /*ignore_units*/) {};
virtual void toggle_shroud_updates() {};
virtual void update_shroud_now() {};
virtual void continue_move() {};
// Map editor stuff.
virtual void edit_set_terrain() {}

View file

@ -95,36 +95,8 @@ void play_turn(game_data& gameinfo, game_state& state_of_game,
for(std::vector<gamemap::location>::const_iterator g = gotos.begin();
g != gotos.end(); ++g) {
const unit_map::iterator ui = units.find(*g);
assert(ui != units.end());
unit u = ui->second;
const shortest_path_calculator calc(u,current_team,units,teams,map,status);
const std::set<gamemap::location>* teleports = NULL;
std::set<gamemap::location> allowed_teleports;
if(u.type().teleports()) {
allowed_teleports = vacant_villages(current_team.villages(),units);
teleports = &allowed_teleports;
if(current_team.villages().count(ui->first))
allowed_teleports.insert(ui->first);
}
paths::route route = a_star_search(ui->first,ui->second.get_goto(),
10000.0,calc,teleports);
if(route.steps.empty())
continue;
assert(route.steps.front() == *g);
route.move_left = route_turns_to_complete(ui->second,map,route);
gui.set_route(&route);
move_unit(&gui,gameinfo,status,map,units,teams,route.steps,
&recorder,&turn_data.undos());
gui.invalidate_game_status();
unit_map::const_iterator ui = units.find(*g);
turn_data.move_unit_to_loc(ui,ui->second.get_goto(),false);
}
std::cerr << "done gotos\n";
@ -735,17 +707,17 @@ void turn_info::left_click(const SDL_MouseButtonEvent& event)
enemy == units_.end() && !current_route_.steps.empty() &&
current_route_.steps.front() == selected_hex_) {
const size_t moves = move_unit(&gui_,gameinfo_,status_,map_,units_,teams_,
current_route_.steps,&recorder,&undo_stack_, &next_unit_);
current_route_.steps,&recorder,&undo_stack_,&next_unit_);
cursor::set(cursor::NORMAL);
gui_.invalidate_game_status();
selected_hex_ = gamemap::location();
gui_.set_route(NULL);
gui_.select_hex(gamemap::location());
gui_.set_route(NULL);
gui_.set_paths(NULL);
current_paths_ = paths();
@ -760,14 +732,16 @@ void turn_info::left_click(const SDL_MouseButtonEvent& event)
//u may be equal to units_.end() in the case of e.g. a [teleport]
if(u != units_.end()) {
assert(u != units_.end());
//Reselect the unit if the move was interrupted
if(dst != current_route_.steps.back()) {
selected_hex_ = dst;
gui_.select_hex(dst);
}
const int range = u->second.longest_range();
current_route_.steps.clear();
show_attack_options(u);
if(current_paths_.routes.empty() == false) {
current_paths_.routes[dst] = paths::route();
selected_hex_ = dst;
@ -844,6 +818,35 @@ void turn_info::show_attack_options(unit_map::const_iterator u)
}
}
void turn_info::move_unit_to_loc(const unit_map::const_iterator& ui, const gamemap::location& target, bool continue_move)
{
assert(ui != units_.end());
unit u = ui->second;
const shortest_path_calculator calc(u,current_team(),units_,teams_,map_,status_);
const std::set<gamemap::location>* teleports = NULL;
std::set<gamemap::location> allowed_teleports;
if(u.type().teleports()) {
allowed_teleports = vacant_villages(current_team().villages(),units_);
teleports = &allowed_teleports;
if(current_team().villages().count(ui->first))
allowed_teleports.insert(ui->first);
}
paths::route route = a_star_search(ui->first,target,10000.0,calc,teleports);
if(route.steps.empty())
return;
assert(route.steps.front() == ui->first);
route.move_left = route_turns_to_complete(ui->second,map_,route);
gui_.set_route(&route);
move_unit(&gui_,gameinfo_,status_,map_,units_,teams_,route.steps,&recorder,&undos(),NULL,continue_move);
gui_.invalidate_game_status();
}
hotkey::ACTION_STATE turn_info::get_action_state(hotkey::HOTKEY_COMMAND command) const
{
switch(command) {
@ -862,11 +865,10 @@ bool turn_info::in_context_menu(hotkey::HOTKEY_COMMAND command) const
//Only display these if the mouse is over a castle or keep tile
case hotkey::HOTKEY_RECRUIT:
case hotkey::HOTKEY_REPEAT_RECRUIT:
case hotkey::HOTKEY_RECALL: {
case hotkey::HOTKEY_RECALL:
// last_hex_ is set by turn_info::mouse_motion
// Enable recruit/recall on castle/keep tiles
return map_.is_castle(last_hex_);
}
default:
return true;
}
@ -905,10 +907,18 @@ bool turn_info::can_execute_command(hotkey::HOTKEY_COMMAND command) const
case hotkey::HOTKEY_UNDO:
return !browse_ && !undo_stack_.empty();
case hotkey::HOTKEY_CONTINUE_MOVE: {
if(browse_) return false;
if(current_unit()->second.move_interrupted()) return true;
const unit_map::const_iterator i = units_.find(selected_hex_);
if (i == units_.end()) return false;
return i->second.move_interrupted();
}
case hotkey::HOTKEY_DELAY_SHROUD:
return !browse_ && (current_team().uses_fog() || current_team().uses_shroud());
case hotkey::HOTKEY_UPDATE_SHROUD:
return !browse_ && !current_team().auto_shroud_updates();
return !browse_ && current_team().auto_shroud_updates() == false;
//commands we can only do if we are actually playing, not just viewing
case hotkey::HOTKEY_END_UNIT_TURN:
@ -2025,43 +2035,53 @@ void turn_info::show_enemy_moves(bool ignore_units)
}
void turn_info::toggle_shroud_updates() {
bool auto_shroud = teams_[team_num_-1].auto_shroud_updates();
bool auto_shroud = current_team().auto_shroud_updates();
// If we're turning automatic shroud updates on, then commit all moves
if(auto_shroud == false) update_shroud_now();
teams_[team_num_-1].set_auto_shroud_updates(!auto_shroud);
current_team().set_auto_shroud_updates(!auto_shroud);
}
void turn_info::update_shroud_now() {
clear_undo_stack();
}
bool turn_info::clear_shroud() {
return teams_[team_num_-1].auto_shroud_updates() &&
bool turn_info::clear_shroud()
{
return current_team().auto_shroud_updates() &&
::clear_shroud(gui_,status_,map_,gameinfo_,units_,teams_,team_num_-1);
}
void turn_info::clear_undo_stack() {
if (teams_[team_num_-1].auto_shroud_updates() == false)
void turn_info::clear_undo_stack()
{
if(current_team().auto_shroud_updates() == false)
apply_shroud_changes(undo_stack_,&gui_,status_,map_,gameinfo_,units_,teams_,team_num_-1);
undo_stack_.clear();
}
void turn_info::update_shroud_now()
{
clear_undo_stack();
}
void turn_info::continue_move()
{
unit_map::iterator i = current_unit();
if(i == units_.end() || i->second.move_interrupted() == false) {
i = units_.find(selected_hex_);
if (i == units_.end() || i->second.move_interrupted() == false) return;
}
move_unit_to_loc(i,i->second.get_interrupted_move(),true);
}
unit_map::iterator turn_info::current_unit()
{
unit_map::iterator i = units_.end();
if(gui_.fogged(last_hex_.x,last_hex_.y) == false){
i = find_visible_unit(units_,
last_hex_, map_,
status_.get_time_of_day().lawful_bonus,teams_,teams_[team_num_-1]);
i = find_visible_unit(units_,last_hex_,map_,
status_.get_time_of_day().lawful_bonus,teams_,current_team());
}
if(gui_.fogged(selected_hex_.x,selected_hex_.y) == false){
if(i == units_.end()) {
unit_map::iterator i = find_visible_unit(units_, selected_hex_,
map_,
status_.get_time_of_day().lawful_bonus,teams_,teams_[team_num_-1]);
unit_map::iterator i = find_visible_unit(units_, selected_hex_,map_,
status_.get_time_of_day().lawful_bonus,teams_,current_team());
}
}

View file

@ -73,6 +73,8 @@ public:
bool can_execute_command(hotkey::HOTKEY_COMMAND command) const;
bool in_context_menu(hotkey::HOTKEY_COMMAND command) const;
void move_unit_to_loc(const unit_map::const_iterator& ui, const gamemap::location& target, bool continue_move);
void save_game(const std::string& message);
@ -121,6 +123,7 @@ private:
virtual void show_enemy_moves(bool ignore_units);
virtual void toggle_shroud_updates();
virtual void update_shroud_now();
virtual void continue_move();
virtual hotkey::ACTION_STATE get_action_state(hotkey::HOTKEY_COMMAND command) const;
void do_recruit(const std::string& name);
@ -136,7 +139,7 @@ private:
bool clear_shroud();
void clear_undo_stack();
unit_map::iterator current_unit();
unit_map::const_iterator current_unit() const;

View file

@ -256,12 +256,24 @@ void unit::new_turn()
set_attacked();
}
bool unit::move_interrupted() const {
return movement_left() > 0 && interrupted_move_.x >= 0 && interrupted_move_.y >= 0;
}
const gamemap::location& unit::get_interrupted_move() const {
return interrupted_move_;
}
void unit::set_interrupted_move(const gamemap::location& interrupted_move) {
interrupted_move_ = interrupted_move;
}
void unit::end_turn()
{
remove_flag("slowed");
if((moves_ != total_movement()) && (moves_ != NOT_MOVED)){
resting_ = false;
}
//clear interrupted move
set_interrupted_move(gamemap::location());
}
void unit::new_level()

View file

@ -131,6 +131,9 @@ public:
const std::string& modification_description(const std::string& type) const;
bool move_interrupted() const;
const gamemap::location& get_interrupted_move() const;
void set_interrupted_move(const gamemap::location& interrupted_move);
private:
const unit_type* type_;
@ -183,7 +186,7 @@ private:
bool guardian_;
gamemap::location goto_;
gamemap::location goto_, interrupted_move_;
enum UPKEEP_COST { UPKEEP_FREE, UPKEEP_LOYAL, UPKEEP_FULL_PRICE };