From e41c8bd858f60099ebca8725d4fc63f6ab3752a2 Mon Sep 17 00:00:00 2001 From: Dave White Date: Sat, 1 Nov 2003 04:38:26 +0000 Subject: [PATCH] extensive changes to the way event handling works --- src/display.cpp | 9 +- src/events.cpp | 8 +- src/events.hpp | 4 +- src/hotkeys.cpp | 159 +++- src/hotkeys.hpp | 45 +- src/language.cpp | 2 +- src/playlevel.cpp | 15 +- src/playturn.cpp | 1928 +++++++++++++++++++++---------------------- src/playturn.hpp | 84 +- src/preferences.cpp | 2 +- src/show_dialog.cpp | 11 + src/show_dialog.hpp | 10 + src/video.cpp | 2 +- 13 files changed, 1203 insertions(+), 1076 deletions(-) diff --git a/src/display.cpp b/src/display.cpp index 9bcd75ececd..97b59499caf 100644 --- a/src/display.cpp +++ b/src/display.cpp @@ -13,6 +13,7 @@ #include "actions.hpp" #include "display.hpp" +#include "events.hpp" #include "font.hpp" #include "game.hpp" #include "game_config.hpp" @@ -319,7 +320,7 @@ void display::scroll_to_tile(int x, int y, SCROLL_TYPE scroll_type) double xdiff = 0.0, ydiff = 0.0; for(int i = 0; i != num_moves; ++i) { - check_keys(*this); + events::pump(); xdiff += xmove/divisor; ydiff += ymove/divisor; @@ -1808,7 +1809,7 @@ bool display::unit_attack_ranged(const gamemap::location& a, int ticks = SDL_GetTicks(); for(int i = begin_at; i < end_at; i += time_resolution*acceleration) { - check_keys(*this); + events::pump(); //this is a while instead of an if, because there might be multiple //sounds playing simultaneously or close together @@ -2041,7 +2042,7 @@ bool display::unit_attack(const gamemap::location& a, hiddenUnit_ = a; for(int i = begin_at; i < end_at; i += time_resolution*acceleration) { - check_keys(*this); + events::pump(); //this is a while instead of an if, because there might be multiple //sounds playing simultaneously or close together @@ -2151,7 +2152,7 @@ void display::move_unit_between(const gamemap::location& a, int skips = 0; for(double i = 0.0; i < nsteps; i += 1.0) { - check_keys(*this); + events::pump(); //checking keys may have invalidated all images (if they have //zoomed in or out), so reget the image here diff --git a/src/events.cpp b/src/events.cpp index 9f8358136d2..bbc235eae5f 100644 --- a/src/events.cpp +++ b/src/events.cpp @@ -55,9 +55,11 @@ void pump() SDL_Event event; while(SDL_PollEvent(&event)) { - for(std::vector::iterator i = event_handlers.begin(); - i != event_handlers.end(); ++i) { - (*i)->handle_event(event); + //events may cause more event handlers to be added and/or removed, + //so we must use indexes instead of iterators here. + for(size_t i1 = 0, i2 = event_handlers.size(); i1 != i2; ++i1) { + assert(i1 < event_handlers.size()); + event_handlers[i1]->handle_event(event); } switch(event.type) { diff --git a/src/events.hpp b/src/events.hpp index 82bf34ecd58..b3b3f8764c3 100644 --- a/src/events.hpp +++ b/src/events.hpp @@ -12,10 +12,12 @@ struct resize_lock { //any classes that derive from this class will automatically //receive sdl events through the handle function for their lifetime +//note that handlers should *always* be allocated as automatic variables +//(never on the free store or in static memory) class handler { public: - virtual void handle_event(const SDL_Event& event) {}; + virtual void handle_event(const SDL_Event& event) = 0; protected: handler(); virtual ~handler(); diff --git a/src/hotkeys.cpp b/src/hotkeys.cpp index 70d7206d925..b6045a44cce 100644 --- a/src/hotkeys.cpp +++ b/src/hotkeys.cpp @@ -20,12 +20,24 @@ #include "show_dialog.hpp" #include "video.hpp" +#include "SDL.h" + #include #include #include namespace { +enum HOTKEY_COMMAND { HOTKEY_CYCLE_UNITS, HOTKEY_END_UNIT_TURN, HOTKEY_LEADER, + HOTKEY_UNDO, HOTKEY_REDO, + HOTKEY_ZOOM_IN, HOTKEY_ZOOM_OUT, HOTKEY_ZOOM_DEFAULT, + HOTKEY_FULLSCREEN, HOTKEY_ACCELERATED, + HOTKEY_TERRAIN_TABLE, HOTKEY_ATTACK_RESISTANCE, + HOTKEY_UNIT_DESCRIPTION, HOTKEY_SAVE_GAME, + HOTKEY_RECRUIT, HOTKEY_RECALL, HOTKEY_ENDTURN, + HOTKEY_TOGGLE_GRID, HOTKEY_STATUS_TABLE, + HOTKEY_NULL }; + HOTKEY_COMMAND string_to_command(const std::string& str) { static std::map m; @@ -59,8 +71,8 @@ HOTKEY_COMMAND string_to_command(const std::string& str) return i->second; } -struct hotkey { - explicit hotkey(config& cfg); +struct hotkey_item { + explicit hotkey_item(config& cfg); HOTKEY_COMMAND action; int keycode; @@ -68,7 +80,7 @@ struct hotkey { mutable bool lastres; }; -hotkey::hotkey(config& cfg) : lastres(false) +hotkey_item::hotkey_item(config& cfg) : lastres(false) { std::map& m = cfg.values; action = string_to_command(m["command"]); @@ -79,61 +91,48 @@ hotkey::hotkey(config& cfg) : lastres(false) shift = (m["shift"] == "yes"); } -bool operator==(const hotkey& a, const hotkey& b) +bool operator==(const hotkey_item& a, const hotkey_item& b) { return a.keycode == b.keycode && a.alt == b.alt && a.ctrl == b.ctrl && a.shift == b.shift; } -bool operator!=(const hotkey& a, const hotkey& b) +bool operator!=(const hotkey_item& a, const hotkey_item& b) { return !(a == b); } -std::vector hotkeys; +std::vector hotkeys; } struct hotkey_pressed { - hotkey_pressed(CKey& key); + hotkey_pressed(const SDL_KeyboardEvent& event); - bool operator()(const hotkey& hk) const; + bool operator()(const hotkey_item& hk) const; private: - const bool shift_, ctrl_, alt_; - CKey& key_; + int keycode_; + bool shift_, ctrl_, alt_; }; -hotkey_pressed::hotkey_pressed(CKey& key) : - shift_(key[SDLK_LSHIFT] || key[SDLK_RSHIFT]), - ctrl_(key[SDLK_LCTRL] || key[SDLK_RCTRL]), - alt_(key[SDLK_LALT] || key[SDLK_RALT]), key_(key) {} +hotkey_pressed::hotkey_pressed(const SDL_KeyboardEvent& event) + : keycode_(event.keysym.sym), shift_(event.keysym.mod&KMOD_SHIFT), + ctrl_(event.keysym.mod&KMOD_CTRL), alt_(event.keysym.mod&KMOD_ALT) +{} -bool hotkey_pressed::operator()(const hotkey& hk) const +bool hotkey_pressed::operator()(const hotkey_item& hk) const { - const bool res = shift_ == hk.shift && ctrl_ == hk.ctrl && - alt_ == hk.alt && key_[hk.keycode]; - - //for zoom in and zoom out, allow it to happen multiple consecutive times - if(hk.action == HOTKEY_ZOOM_IN || hk.action == HOTKEY_ZOOM_OUT) { - hk.lastres = false; - return res; - } - - //don't let it return true on multiple consecutive occurrences - if(hk.lastres) { - hk.lastres = res; - return false; - } else { - hk.lastres = res; - return res; - } + return hk.keycode == keycode_ && shift_ == hk.shift && + ctrl_ == hk.ctrl && alt_ == hk.alt; } +namespace { + void add_hotkey(config& cfg) { - const hotkey new_hotkey(cfg); - const std::vector::iterator i = + const hotkey_item new_hotkey(cfg); + const std::vector::iterator i = std::find(hotkeys.begin(),hotkeys.end(),new_hotkey); if(i != hotkeys.end()) { *i = new_hotkey; @@ -142,6 +141,19 @@ void add_hotkey(config& cfg) } } +} + +namespace hotkey { + +basic_handler::basic_handler(display& disp) : disp_(disp) {} + +void basic_handler::handle_event(const SDL_Event& event) +{ + if(event.type == SDL_KEYDOWN && !gui::in_dialog()) { + key_event(disp_,event.key,NULL); + } +} + void add_hotkeys(config& cfg) { std::vector& children = cfg.children["hotkey"]; @@ -151,25 +163,26 @@ void add_hotkeys(config& cfg) } } -HOTKEY_COMMAND check_keys(display& disp) +void key_event(display& disp, const SDL_KeyboardEvent& event, + command_executor* executor) { const double zoom_amount = 5.0; - events::pump(); - - CKey key; - if(key[KEY_ESCAPE]) { + if(event.keysym.sym == SDLK_ESCAPE) { const int res = gui::show_dialog(disp,NULL,"", string_table["quit_message"],gui::YES_NO); if(res == 0) { throw end_level_exception(QUIT); + } else { + return; } } - const std::vector::iterator i = - std::find_if(hotkeys.begin(),hotkeys.end(),hotkey_pressed(key)); + const std::vector::iterator i = + std::find_if(hotkeys.begin(),hotkeys.end(),hotkey_pressed(event)); + if(i == hotkeys.end()) - return HOTKEY_NULL; + return; switch(i->action) { case HOTKEY_ZOOM_IN: @@ -187,9 +200,65 @@ HOTKEY_COMMAND check_keys(display& disp) case HOTKEY_ACCELERATED: preferences::set_turbo(!preferences::turbo()); break; + case HOTKEY_CYCLE_UNITS: + if(executor) + executor->cycle_units(); + break; + case HOTKEY_ENDTURN: + if(executor) + executor->end_turn(); + break; + case HOTKEY_END_UNIT_TURN: + if(executor) + executor->end_unit_turn(); + break; + case HOTKEY_LEADER: + if(executor) + executor->goto_leader(); + break; + case HOTKEY_UNDO: + if(executor) + executor->undo(); + break; + case HOTKEY_REDO: + if(executor) + executor->redo(); + break; + case HOTKEY_TERRAIN_TABLE: + if(executor) + executor->terrain_table(); + break; + case HOTKEY_ATTACK_RESISTANCE: + if(executor) + executor->attack_resistance(); + break; + case HOTKEY_UNIT_DESCRIPTION: + if(executor) + executor->unit_description(); + break; + case HOTKEY_SAVE_GAME: + if(executor) + executor->save_game(); + break; + case HOTKEY_TOGGLE_GRID: + if(executor) + executor->toggle_grid(); + break; + case HOTKEY_STATUS_TABLE: + if(executor) + executor->status_table(); + break; + case HOTKEY_RECALL: + if(executor) + executor->recall(); + break; + case HOTKEY_RECRUIT: + if(executor) + executor->recruit(); + break; default: - return i->action; + break; } - - return HOTKEY_NULL; +} + } diff --git a/src/hotkeys.hpp b/src/hotkeys.hpp index 30ecb4af6b7..dc0f5093b5b 100644 --- a/src/hotkeys.hpp +++ b/src/hotkeys.hpp @@ -15,20 +15,45 @@ #include "config.hpp" #include "display.hpp" +#include "events.hpp" #include "key.hpp" -enum HOTKEY_COMMAND { HOTKEY_CYCLE_UNITS, HOTKEY_END_UNIT_TURN, HOTKEY_LEADER, - HOTKEY_UNDO, HOTKEY_REDO, - HOTKEY_ZOOM_IN, HOTKEY_ZOOM_OUT, HOTKEY_ZOOM_DEFAULT, - HOTKEY_FULLSCREEN, HOTKEY_ACCELERATED, - HOTKEY_TERRAIN_TABLE, HOTKEY_ATTACK_RESISTANCE, - HOTKEY_UNIT_DESCRIPTION, HOTKEY_SAVE_GAME, - HOTKEY_RECRUIT, HOTKEY_RECALL, HOTKEY_ENDTURN, - HOTKEY_TOGGLE_GRID, HOTKEY_STATUS_TABLE, - HOTKEY_NULL }; +namespace hotkey { +class command_executor +{ +public: + virtual void cycle_units() = 0; + virtual void end_turn() = 0; + virtual void goto_leader() = 0; + virtual void end_unit_turn() = 0; + virtual void undo() = 0; + virtual void redo() = 0; + virtual void terrain_table() = 0; + virtual void attack_resistance() = 0; + virtual void unit_description() = 0; + virtual void save_game() = 0; + virtual void toggle_grid() = 0; + virtual void status_table() = 0; + virtual void recall() = 0; + virtual void recruit() = 0; +}; + +//object which will ensure that basic keyboard events like escape +//are handled properly for the duration of its lifetime +struct basic_handler : public events::handler { + basic_handler(display& disp); + + void handle_event(const SDL_Event& event); + +private: + display& disp_; +}; void add_hotkeys(config& cfg); -HOTKEY_COMMAND check_keys(display& disp); +void key_event(display& disp, const SDL_KeyboardEvent& event, + command_executor* executor); + +} #endif diff --git a/src/language.cpp b/src/language.cpp index 24b3db875f6..3dad4974c3a 100644 --- a/src/language.cpp +++ b/src/language.cpp @@ -48,7 +48,7 @@ bool internal_set_language(const std::string& locale, config& cfg) string_table[j->first] = j->second; } - add_hotkeys(**i); + hotkey::add_hotkeys(**i); return true; } } diff --git a/src/playlevel.cpp b/src/playlevel.cpp index eea5517a4e4..aa75f79b2c3 100644 --- a/src/playlevel.cpp +++ b/src/playlevel.cpp @@ -13,6 +13,7 @@ #include "events.hpp" #include "game_events.hpp" +#include "hotkeys.hpp" #include "intro.hpp" #include "language.hpp" #include "network.hpp" @@ -168,10 +169,12 @@ LEVEL_RESULT play_level(game_data& gameinfo, config& terrain_config, find_leader(units,player_number); if(leader != units.end() && !recorder.skipping()) { + const hotkey::basic_handler key_events_handler(gui); gui.scroll_to_tile(leader->first.x,leader->first.y); } if(replaying) { + const hotkey::basic_handler key_events_handler(gui); std::cerr << "doing replay " << player_number << "\n"; replaying = do_replay(gui,map,gameinfo,units,teams, player_number,status,state_of_game); @@ -198,6 +201,8 @@ LEVEL_RESULT play_level(game_data& gameinfo, config& terrain_config, display::clear_debug_highlights(); } else if(!replaying && team_it->is_ai()) { + const hotkey::basic_handler key_events_handler(gui); + const int start_command = recorder.ncommands(); ai::do_move(gui,map,gameinfo,units,teams, @@ -225,8 +230,9 @@ LEVEL_RESULT play_level(game_data& gameinfo, config& terrain_config, config cfg; - turn_info turn_data; - const paths_wiper wiper(gui); + turn_info turn_data(gameinfo,state_of_game,status, + terrain_config,level,key,gui, + map,teams,player_number,units,true); for(;;) { network::connection res = @@ -239,9 +245,8 @@ LEVEL_RESULT play_level(game_data& gameinfo, config& terrain_config, break; } - turn_slice(gameinfo,state_of_game,status, - terrain_config,level,video,key,gui,map, - teams,player_number,units,turn_data,true); + turn_data.turn_slice(); + gui.draw(); } std::cerr << "replay: '" << cfg.children["turn"].front()->write() << "'\n"; diff --git a/src/playturn.cpp b/src/playturn.cpp index e71a17ae9aa..23491246f78 100644 --- a/src/playturn.cpp +++ b/src/playturn.cpp @@ -30,20 +30,6 @@ #include #include -namespace { - -int get_turns_movement(CKey& key) -{ - for(char c = '1'; c != '7'; ++c) { - if(key[c]) - return c - '1'; - } - - return 0; -} - -} - void play_turn(game_data& gameinfo, game_state& state_of_game, gamestatus& status, config& terrain_config, config* level, CVideo& video, CKey& key, display& gui, @@ -70,7 +56,8 @@ void play_turn(game_data& gameinfo, game_state& state_of_game, gui::show_dialog(gui,NULL,"",string_table["your_turn"],gui::MESSAGE); } - turn_info turn_data; + turn_info turn_data(gameinfo,state_of_game,status,terrain_config,level, + key,gui,map,teams,team_num,units,false); int start_command = recorder.ncommands(); @@ -107,1021 +94,980 @@ void play_turn(game_data& gameinfo, game_state& state_of_game, route.move_left = route_turns_to_complete(ui->second,map,route); gui.set_route(&route); - const size_t moves = move_unit(&gui,map,units,teams,route.steps, - &recorder,&turn_data.undo_stack); - if(moves > 0) { - turn_data.redo_stack.clear(); - } + move_unit(&gui,map,units,teams,route.steps, + &recorder,&turn_data.undos()); } std::cerr << "done gotos\n"; - for(;;) { - bool res = false; + while(!turn_data.turn_over()) { try { - res = turn_slice(gameinfo,state_of_game,status, - terrain_config,level,video,key,gui,map, - teams,team_num,units,turn_data,false); + turn_data.turn_slice(); } catch(end_level_exception& e) { - - //if the game is over, and it's a networked game, then - //tell the foreign hosts of the final moves - if(network::nconnections() > 0 && - start_command < recorder.ncommands()) { - config cfg; - cfg.children["turn"].push_back( - new config(recorder.get_data_range(start_command, - recorder.ncommands()))); - network::send_data(cfg); - } - + turn_data.send_data(start_command); throw e; } - //send network data if any moves have been made - if(network::nconnections() > 0 && - (turn_data.undo_stack.empty() || res) && - start_command < recorder.ncommands()) { - config cfg; - cfg.children["turn"].push_back( - new config(recorder.get_data_range(start_command, - recorder.ncommands()))); - network::send_data(cfg); + gui.draw(); - start_command = recorder.ncommands(); - } - - if(res) { - break; - } + start_command = turn_data.send_data(start_command); } } -bool turn_slice(game_data& gameinfo, game_state& state_of_game, - gamestatus& status, config& terrain_config, config* level, - CVideo& video, CKey& key, display& gui, gamemap& map, - std::vector& teams, int team_num, - unit_map& units, turn_info& turn_data, bool browse) +turn_info::turn_info(game_data& gameinfo, game_state& state_of_game, + gamestatus& status, config& terrain_config, config* level, + CKey& key, display& gui, gamemap& map, + std::vector& teams, int team_num, + unit_map& units, bool browse) + : paths_wiper(gui), + gameinfo_(gameinfo), state_of_game_(state_of_game), status_(status), + terrain_config_(terrain_config), level_(level), + key_(key), gui_(gui), map_(map), teams_(teams), team_num_(team_num), + units_(units), browse_(browse), + left_button_(false), right_button_(false), middle_button_(false), + enemy_paths_(false), path_turns_(0), end_turn_(false) { - bool& left_button = turn_data.left_button; - bool& right_button = turn_data.right_button; - bool& middle_button = turn_data.middle_button; - gamemap::location& next_unit = turn_data.next_unit; - paths& current_paths = turn_data.current_paths; - paths::route& current_route = turn_data.current_route; - bool& enemy_paths = turn_data.enemy_paths; - gamemap::location& last_hex = turn_data.last_hex; - gamemap::location& selected_hex = turn_data.selected_hex; - undo_list& undo_stack = turn_data.undo_stack; - undo_list& redo_stack = turn_data.redo_stack; +} - team& current_team = teams[team_num-1]; +void turn_info::turn_slice() +{ + events::pump(); int mousex, mousey; - const int mouse_flags = SDL_GetMouseState(&mousex,&mousey); - const bool new_left_button = mouse_flags & SDL_BUTTON_LMASK; - const bool new_right_button = mouse_flags & SDL_BUTTON_RMASK; - const bool new_middle_button = mouse_flags & SDL_BUTTON_MMASK; + SDL_GetMouseState(&mousex,&mousey); - gamemap::location new_hex = gui.hex_clicked_on(mousex,mousey); + if(key_[KEY_UP] || mousey == 0) + gui_.scroll(0.0,-preferences::scroll_speed()); - //if the player has pressed a number, we want to show how far - //the selected unit could get in that many turns - const int new_path_turns = get_turns_movement(key); - if(new_path_turns != turn_data.path_turns) { - turn_data.path_turns = new_path_turns; + if(key_[KEY_DOWN] || mousey == gui_.y()-1) + gui_.scroll(0.0,preferences::scroll_speed()); - unit_map::iterator u = units.find(selected_hex); - if(u == units.end()) { - u = units.find(last_hex); - if(u != units.end() && u->second.side() == team_num) { - u = units.end(); + if(key_[KEY_LEFT] || mousex == 0) + gui_.scroll(-preferences::scroll_speed(),0.0); + + if(key_[KEY_RIGHT] || mousex == gui_.x()-1) + gui_.scroll(preferences::scroll_speed(),0.0); +} + +bool turn_info::turn_over() const { return end_turn_; } + +int turn_info::send_data(int first_command) +{ + if(network::nconnections() > 0 && (undo_stack_.empty() || end_turn_) && + first_command < recorder.ncommands()) { + config cfg; + cfg.children["turn"].push_back( + new config(recorder.get_data_range(first_command, + recorder.ncommands()))); + network::send_data(cfg); + return recorder.ncommands(); + } else { + return first_command; + } +} + +void turn_info::handle_event(const SDL_Event& event) +{ + if(gui::in_dialog()) + return; + + switch(event.type) { + case SDL_KEYDOWN: + hotkey::key_event(gui_,event.key,this); + + //intentionally fall-through + case SDL_KEYUP: + + //if the user has pressed 1 through 9, we want to show how far + //the unit can move in that many turns + if(event.key.keysym.sym >= '1' && event.key.keysym.sym <= '7') { + const int new_path_turns = (event.type == SDL_KEYDOWN) ? + event.key.keysym.sym - '1' : 0; + + if(new_path_turns != path_turns_) { + path_turns_ = new_path_turns; + + unit_map::iterator u = units_.find(selected_hex_); + if(u == units_.end()) { + u = units_.find(last_hex_); + if(u != units_.end() && u->second.side() == team_num_) { + u = units_.end(); + } + } else if(u->second.side() != team_num_) { + u = units_.end(); + } + + if(u != units_.end()) { + const bool ignore_zocs = u->second.type().is_skirmisher(); + const bool teleport = u->second.type().teleports(); + current_paths_ = paths(map_,gameinfo_,units_,u->first, + teams_,ignore_zocs,teleport, + path_turns_); + gui_.set_paths(¤t_paths_); + } } - } else if(u->second.side() != team_num) { - u = units.end(); } - if(u != units.end()) { - const bool ignore_zocs = u->second.type().is_skirmisher(); - const bool teleport = u->second.type().teleports(); - current_paths = paths(map,gameinfo,units,u->first,teams, - ignore_zocs,teleport,turn_data.path_turns); - gui.set_paths(¤t_paths); - } + break; + case SDL_MOUSEMOTION: + mouse_motion(event.motion); + break; + case SDL_MOUSEBUTTONDOWN: + case SDL_MOUSEBUTTONUP: + mouse_press(event.button); + break; + default: + break; } +} - if(new_hex.valid() == false) { - current_route.steps.clear(); - gui.set_route(NULL); - } +void turn_info::mouse_motion(const SDL_MouseMotionEvent& event) +{ + const team& current_team = teams_[team_num_-1]; + const gamemap::location new_hex = gui_.hex_clicked_on(event.x,event.y); - //highlight the hex that is currently moused over - if(new_hex != last_hex) { - gui.highlight_hex(new_hex); - - if(enemy_paths && new_hex != last_hex) { - gui.set_paths(NULL); - current_paths = paths(); - enemy_paths = false; + if(new_hex != last_hex_) { + if(new_hex.valid() == false) { + current_route_.steps.clear(); + gui_.set_route(NULL); } - if(new_hex == selected_hex) { - current_route.steps.clear(); - gui.set_route(NULL); - } else if(!enemy_paths && new_hex != last_hex && - !current_paths.routes.empty() && map.on_board(selected_hex) && - map.on_board(new_hex)) { + gui_.highlight_hex(new_hex); + + if(new_hex == selected_hex_) { + current_route_.steps.clear(); + gui_.set_route(NULL); + } else if(!enemy_paths_ && new_hex != last_hex_ && + !current_paths_.routes.empty() && map_.on_board(selected_hex_) && + map_.on_board(new_hex)) { - const unit_map::const_iterator un = units.find(selected_hex); - if(un != units.end()) { + const unit_map::const_iterator un = units_.find(selected_hex_); + if(un != units_.end()) { const shortest_path_calculator calc(un->second,current_team, - units,map); + units_,map_); const bool can_teleport = un->second.type().teleports(); const std::set* const teleports = can_teleport ? ¤t_team.towers() : NULL; - current_route = a_star_search(selected_hex,new_hex, - 10000.0,calc,teleports); + current_route_ = a_star_search(selected_hex_,new_hex, + 10000.0,calc,teleports); - current_route.move_left = - route_turns_to_complete(un->second,map,current_route); - gui.set_route(¤t_route); + current_route_.move_left = + route_turns_to_complete(un->second,map_,current_route_); + gui_.set_route(¤t_route_); } } } - HOTKEY_COMMAND command = HOTKEY_NULL; - - if(new_left_button) { - const gamemap::location loc = gui.minimap_location_on(mousex,mousey); - if(loc.valid()) { - gui.scroll_to_tile(loc.x,loc.y,display::WARP); - } - } else if(new_hex != last_hex && current_paths.routes.empty()) { - const unit_map::iterator u = units.find(new_hex); - if(u != units.end() && u->second.side() != team_num) { - const bool ignore_zocs = u->second.type().is_skirmisher(); - const bool teleport = u->second.type().teleports(); - - //temporarily set moves to full to see how far - //it could move - const unit_movement_resetter move_change(u->second); - - current_paths = paths(map,gameinfo,units,new_hex,teams, - ignore_zocs,teleport,turn_data.path_turns); - gui.set_paths(¤t_paths); - enemy_paths = true; - } - } - - last_hex = new_hex; - - if(!left_button && new_left_button) { - gamemap::location hex = gui.hex_clicked_on(mousex,mousey); - - unit_map::iterator u = units.find(selected_hex); - - //if the unit is selected and then itself clicked on, - //any goto command is cancelled - if(u != units.end() && !browse && - selected_hex == hex && u->second.side() == team_num) { - u->second.set_goto(gamemap::location()); - } - - //if we can move to that tile - std::map::const_iterator - route = enemy_paths ? current_paths.routes.end() : - current_paths.routes.find(hex); - unit_map::iterator enemy = units.find(hex); - - //see if we're trying to attack an enemy - if(route != current_paths.routes.end() && enemy != units.end() && - hex != selected_hex && !browse && - enemy->second.side() != u->second.side() && - current_team.is_enemy(enemy->second.side())) { - - const std::vector& attacks = u->second.attacks(); - std::vector items; - - std::vector units_list; - - for(size_t a = 0; a != attacks.size(); ++a) { - const battle_stats stats = evaluate_battle_stats( - map,selected_hex,hex, - a,units,status,gameinfo); - - const std::string& lang_attack_name = - string_table["weapon_name_"+stats.attack_name]; - const std::string& lang_attack_type = - string_table["weapon_type_"+stats.attack_type]; - const std::string& lang_range = - string_table[stats.range == "Melee" ? - "short_range" : "long_range"]; - - const std::string& lang_defend_name = - string_table["weapon_name_"+stats.defend_name]; - const std::string& lang_defend_type = - string_table["weapon_type_"+stats.defend_type]; - - const std::string& attack_name = lang_attack_name.empty() ? - stats.attack_name : lang_attack_name; - const std::string& attack_type = lang_attack_type.empty() ? - stats.attack_type : lang_attack_type; - const std::string& defend_name = lang_defend_name.empty() ? - stats.defend_name : lang_defend_name; - const std::string& defend_type = lang_defend_type.empty() ? - stats.defend_type : lang_defend_type; - - const std::string& range = lang_range.empty() ? - stats.range : lang_range; - - std::stringstream att; - att << attack_name << " (" << attack_type - << ") " << stats.damage_defender_takes << "-" - << stats.nattacks << " " << range << " " - << int(util::round(100.0*stats.chance_to_hit_defender)) - << "%"; - - att << "," << string_table["versus"] << ","; - att << defend_name << " (" << defend_type - << ") " << stats.damage_attacker_takes << "-" - << stats.ndefends << " " - << int(util::round(100.0*stats.chance_to_hit_attacker)) - << "%"; - - items.push_back(att.str()); - units_list.push_back(enemy->second); - } - - //make it so that when we attack an enemy, the attacking unit - //is again shown in the status bar, so that we can easily - //compare between the attacking and defending unit - gui.highlight_hex(gamemap::location()); - gui.draw(true,true); - - const int res = gui::show_dialog(gui,NULL,"", - string_table["choose_weapon"]+":\n", - gui::OK_CANCEL,&items,&units_list); - - if(size_t(res) < attacks.size()) { - u->second.set_goto(gamemap::location()); - undo_stack.clear(); - redo_stack.clear(); - - current_paths = paths(); - gui.set_paths(NULL); - - game_events::fire("attack",selected_hex,hex); - - //the event could have killed either the attacker or - //defender, so we have to make sure they still exist - u = units.find(selected_hex); - enemy = units.find(hex); - - if(u == units.end() || enemy == units.end() || - size_t(res) >= u->second.attacks().size()) - return false; - - gui.invalidate_all(); - gui.draw(); - - recorder.add_attack(selected_hex,hex,res); - - attack(gui,map,selected_hex,hex,res,units, - status,gameinfo,true); - - dialogs::advance_unit(gameinfo,units,selected_hex,gui); - dialogs::advance_unit(gameinfo,units,hex,gui); - - selected_hex = gamemap::location(); - current_route.steps.clear(); - gui.set_route(NULL); - - check_victory(units,teams); - - gui.invalidate_all(); - gui.draw(); //clear the screen - } - } - - //otherwise we're trying to move to a hex - else if(!browse && selected_hex.valid() && selected_hex != hex && - units.count(selected_hex) && - enemy == units.end() && !current_route.steps.empty() && - current_route.steps.front() == selected_hex) { - - const size_t moves = move_unit(&gui,map,units,teams, - current_route.steps,&recorder,&undo_stack); - - redo_stack.clear(); - - selected_hex = gamemap::location(); - gui.set_route(NULL); - gui.select_hex(gamemap::location()); - gui.set_paths(NULL); - current_paths = paths(); - - assert(moves <= current_route.steps.size()); - const gamemap::location& dst = current_route.steps[moves-1]; - - current_route.steps.clear(); - - //if there is an enemy in a surrounding hex, then - //highlight attack options - gamemap::location adj[6]; - get_adjacent_tiles(dst,adj); - - int n; - for(n = 0; n != 6; ++n) { - const unit_map::const_iterator u_it = units.find(adj[n]); - if(u_it != units.end() && u_it->second.side() != team_num - && current_team.is_enemy(u_it->second.side())){ - current_paths.routes[adj[n]] = paths::route(); - } - } - - if(current_paths.routes.empty() == false) { - current_paths.routes[dst] = paths::route(); - selected_hex = dst; - gui.select_hex(dst); - gui.set_paths(¤t_paths); - } - - if(clear_shroud(gui,map,gameinfo,units,teams,team_num-1)) { - undo_stack.clear(); - } - } else { - gui.set_paths(NULL); - current_paths = paths(); - - selected_hex = hex; - gui.select_hex(hex); - current_route.steps.clear(); - gui.set_route(NULL); - - const unit_map::iterator it = units.find(hex); - if(it != units.end() && it->second.side() == team_num) { - const bool ignore_zocs = it->second.type().is_skirmisher(); - const bool teleport = it->second.type().teleports(); - current_paths = paths(map,gameinfo,units,hex,teams, - ignore_zocs,teleport,turn_data.path_turns); - gui.set_paths(¤t_paths); - - unit u = it->second; - const gamemap::location go_to = u.get_goto(); - if(map.on_board(go_to)) { - const shortest_path_calculator calc(u,current_team, - units,map); - - const std::set* const teleports = - teleport ? ¤t_team.towers() : NULL; - - paths::route route = a_star_search(it->first,go_to, - 10000.0,calc,teleports); - route.move_left = - route_turns_to_complete(it->second,map,route); - gui.set_route(&route); - } - } - } - } - - left_button = new_left_button; - - if(!right_button && new_right_button) { - if(!current_paths.routes.empty()) { - selected_hex = gamemap::location(); - gui.select_hex(gamemap::location()); - gui.set_paths(NULL); - current_paths = paths(); - current_route.steps.clear(); - gui.set_route(NULL); - } else { - const unit_map::const_iterator un = units.find( - gui.hex_clicked_on(mousex,mousey)); - - static const std::string menu_items[] = - {"scenario_objectives","recruit", - "recall","unit_list","status_table", - "save_game","preferences","end_turn",""}; - static const std::string browse_menu_items[] = - {"scenario_objectives","unit_list", - "status_table","save_game","preferences", - ""}; - std::vector menu; - - const std::string* items = browse ? browse_menu_items : menu_items; - for(; items->empty() == false; ++items) { - menu.push_back(string_table[*items]); - } - - if(un != units.end()) { - menu.push_back(string_table["describe_unit"]); - } - - const int res = gui::show_dialog(gui,NULL,"", - string_table["options"]+":\n", - gui::MESSAGE,&menu); - - const std::string result = res != -1 ? menu[res] : ""; - - if(un != units.end()) { - menu.pop_back(); - } - - if(result == string_table["describe_unit"]) { - command = HOTKEY_UNIT_DESCRIPTION; - } - - else if(result == string_table["preferences"]) { - preferences::show_preferences_dialog(gui); - } - - else if(result == string_table["end_turn"]) { - command = HOTKEY_ENDTURN; - } - - else if(result == string_table["scenario_objectives"]) { - dialogs::show_objectives(gui,*level); - } - - else if(result == string_table["recall"]) { - command = HOTKEY_RECALL; - } - else if(result == string_table["recruit"]) { - command = HOTKEY_RECRUIT; - } else if(result == string_table["status_table"]) { - command = HOTKEY_STATUS_TABLE; - } else if(result == string_table["unit_list"]) { - const std::string heading = string_table["name"] + "," + - string_table["hp"] + "," + - string_table["xp"] + "," + - string_table["moves"] + "," + - string_table["location"]; - - std::vector items; - items.push_back(heading); - - std::vector units_list; - for(unit_map::const_iterator i = units.begin(); - i != units.end(); ++i) { - if(i->second.side() != team_num) - continue; - - std::stringstream row; - row << i->second.name() << "," << i->second.hitpoints() - << "/" << i->second.max_hitpoints() << "," - << i->second.experience() << "/" - << i->second.max_experience() << "," - << i->second.movement_left() << "/" - << i->second.total_movement() << "," - << (i->first.x+1) << "-" << (i->first.y+1); - - items.push_back(row.str()); - - //extra unit for the first row to make up the heading - if(units_list.empty()) - units_list.push_back(i->second); - - units_list.push_back(i->second); - } - - gui::show_dialog(gui,NULL,string_table["unit_list"],"", - gui::OK_ONLY,&items,&units_list); - } else if(result == string_table["save_game"]) { - command = HOTKEY_SAVE_GAME; - } - } - } - - right_button = new_right_button; - - if(key[KEY_UP] || mousey == 0) - gui.scroll(0.0,-preferences::scroll_speed()); - - if(key[KEY_DOWN] || mousey == gui.y()-1) - gui.scroll(0.0,preferences::scroll_speed()); - - if(key[KEY_LEFT] || mousex == 0) - gui.scroll(-preferences::scroll_speed(),0.0); - - if(key[KEY_RIGHT] || mousex == gui.x()-1) - gui.scroll(preferences::scroll_speed(),0.0); - - if(new_middle_button && !middle_button && - mousex < gui.mapx() && mousey < gui.y()) { - const int centerx = gui.mapx()/2; - const int centery = gui.y()/2; - const double movex = double(mousex - centerx); - const double movey = double(mousey - centery); - gui.scroll(movex,movey); - } - - middle_button = new_middle_button; - - // MOUSE WHEEL BOUNDARY CHECK - - if(gui::scrollamount() && (mousex > 50 && mousex < gui.mapx()-50) && - (mousey > 50 && mousey < gui.y()-50)) { - // Reset scrolling ... it is just a clean-up so wheel turns - //don't cache - gui::scroll_reset() ; - } else { - if(gui::scrollamount()) { - // Now scroll accordingly as we are in hot-spots - if((mousex >= gui.mapx()-50 && mousey <= 50) || - (mousex <= 50 && mousey >= gui.y()-50)) { - // firstly filtering the 2 conflicting corners - if(gui::scrollamount() > 0) { - gui.scroll(0.0,preferences::scroll_speed()); - gui.scroll(-preferences::scroll_speed(),0.0); - } else { - gui.scroll(0.0,-preferences::scroll_speed()); - gui.scroll(preferences::scroll_speed(),0.0); - } - } else { - // Now general case... - if(mousex <= 50 || mousex >= gui.mapx()-50) { - // SCROLL HORIZONTALLY... we are in E or W sides - gui::scrollamount() < 0 ? - gui.scroll(-preferences::scroll_speed(),0.0) - : gui.scroll(preferences::scroll_speed(),0.0); - } - if(mousey <= 50 || mousey >= gui.y()-50) { - // SCROLL VERTICALLY... we are in E or W sides - gui::scrollamount() < 0 ? - gui.scroll(0.0,-preferences::scroll_speed()) - : gui.scroll(0.0,preferences::scroll_speed()); - } - } - // We must now reduce scrollamount - gui::scroll_reduce() ; - } - } - - if(command == HOTKEY_NULL) - command = check_keys(gui); - - if(!browse && command == HOTKEY_ENDTURN) { - recorder.save_game(gameinfo,string_table["auto_save"]); - recorder.end_turn(); - return true; - } - - if(!browse && command == HOTKEY_RECRUIT) { - std::vector sample_units; - - gui.draw(); //clear the old menu - std::vector item_keys; - std::vector items; - const std::set& recruits - = current_team.recruits(); - for(std::set::const_iterator it = - recruits.begin(); it != recruits.end(); ++it) { - item_keys.push_back(*it); - const std::map::const_iterator - u_type = gameinfo.unit_types.find(*it); - if(u_type == gameinfo.unit_types.end()) { - std::cerr << "could not find " << *it << std::endl; - assert(false); - return false; - } - - const unit_type& type = u_type->second; - std::stringstream description; - - description << type.language_name() << "," - << type.cost() << " gold"; - items.push_back(description.str()); - sample_units.push_back(unit(&type,team_num)); - } - - const int recruit_res = - gui::show_dialog(gui,NULL,"", - string_table["recruit_unit"] + ":\n", - gui::OK_CANCEL,&items,&sample_units); - if(recruit_res != -1) { - const std::string& name = item_keys[recruit_res]; - const std::map::const_iterator - u_type = gameinfo.unit_types.find(name); - assert(u_type != gameinfo.unit_types.end()); - - if(u_type->second.cost() > current_team.gold()) { - gui::show_dialog(gui,NULL,"", - string_table["not_enough_gold_to_recruit"], - gui::OK_ONLY); - } else { - //create a unit with traits - recorder.add_recruit(recruit_res,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); - if(msg.empty()) { - current_team.spend_gold(u_type->second.cost()); - } else { - recorder.undo(); - gui::show_dialog(gui,NULL,"",msg,gui::OK_ONLY); - } - - undo_stack.clear(); - redo_stack.clear(); - - gui.invalidate_game_status(); - } - } - } - - if(!browse && command == HOTKEY_RECALL) { - //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()); - - 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()) { - gui::show_dialog(gui,NULL,"",string_table["no_recall"]); - } else if(current_team.gold() < game_config::recall_cost) { - std::stringstream msg; - msg << string_table["not_enough_gold_to_recall_1"] - << " " << game_config::recall_cost << " " - << string_table["not_enough_gold_to_recall_2"]; - gui::show_dialog(gui, NULL,"",msg.str()); - } else { - std::vector options; - for(std::vector::const_iterator unit = - state_of_game.available_units.begin(); - unit != state_of_game.available_units.end(); ++unit) { - std::stringstream option; - option << unit->type().language_name() << "," - << string_table["level"] << ": " - << unit->type().level() << "," - << string_table["xp"] << ": " - << unit->experience() << "/" - << unit->max_experience(); - options.push_back(option.str()); - } - - const int res = gui::show_dialog(gui,NULL,"", - string_table["select_unit"] + ":\n", - gui::OK_CANCEL,&options, - &state_of_game.available_units); - if(res >= 0) { - - const std::string err = recruit_unit(map,team_num, - units,state_of_game.available_units[res], - last_hex,&gui); - if(!err.empty()) { - gui::show_dialog(gui,NULL,"",err,gui::OK_ONLY); - } else { - 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,last_hex); - - undo_stack.clear(); - redo_stack.clear(); - - gui.invalidate_game_status(); - } - } - } - } - - if(command == HOTKEY_SAVE_GAME) { - std::stringstream stream; - stream << state_of_game.scenario - << " " << string_table["turn"] - << " " << status.turn(); - std::string label = stream.str(); - - const int res = gui::show_dialog(gui, NULL, "", "", - gui::OK_CANCEL,NULL,NULL, - string_table["save_game_label"], - &label); - - if(res == 0) { - recorder.save_game(gameinfo,label); - gui::show_dialog(gui,NULL,"", - string_table["save_confirm_message"], gui::OK_ONLY); - } - } - - unit_map::const_iterator un = units.find(new_hex); - if(un == units.end()) - un = units.find(selected_hex); - - if(command == HOTKEY_UNIT_DESCRIPTION && un != units.end()) { - const std::string description = un->second.type().unit_description() - + "\n\n" + string_table["see_also"]; - - std::vector options; - - options.push_back(string_table["terrain_info"]); - options.push_back(string_table["attack_resistance"]); - options.push_back(string_table["close_window"]); - - SDL_Surface* const unit_image = gui.getImage( - un->second.type().image_profile(), display::UNSCALED); - - const int res = gui::show_dialog(gui,unit_image, - un->second.type().language_name(), - description,gui::MESSAGE, - &options); - - //terrain table - if(res == 0) { - command = HOTKEY_TERRAIN_TABLE; - } - - //attack resistance table - else if(res == 1) { - command = HOTKEY_ATTACK_RESISTANCE; - } - } - - if(un != units.end() && command == HOTKEY_ATTACK_RESISTANCE) { - gui.draw(); - - std::vector items; - items.push_back(string_table["attack_type"] + "," + - string_table["attack_resistance"]); - const std::map& table = - un->second.type().movement_type().damage_table(); - for(std::map::const_iterator i - = table.begin(); i != table.end(); ++i) { - double resistance = atof(i->second.c_str()); - - //if resistance is less than 0, display in red - std::string prefix = ""; - if(resistance > 1.0) { - prefix = "#"; - } - - const int resist = 100 - int(util::round(100.0*resistance)); - - const std::string& lang_weapon = - string_table["weapon_type_" + i->first]; - const std::string& weap = lang_weapon.empty() ? i->first : - lang_weapon; - - std::stringstream str; - str << weap << "," << prefix << resist << "%"; - items.push_back(str.str()); - } - - const std::vector units_list(items.size(), - un->second); - SDL_Surface* const unit_image = - gui.getImage(un->second.type().image_profile(),display::UNSCALED); - gui::show_dialog(gui,unit_image, - un->second.type().language_name(), - string_table["unit_resistance_table"], - gui::MESSAGE,&items,&units_list); - } - - if(un != units.end() && command == HOTKEY_TERRAIN_TABLE) { - gui.draw(); - - std::vector items; - items.push_back(string_table["terrain"] + "," + - string_table["movement"] + "," + - string_table["defense"]); - - const unit_type& type = un->second.type(); - const unit_movement_type& move_type = - type.movement_type(); - const std::vector& terrains = - map.get_terrain_precedence(); - for(std::vector::const_iterator t = - terrains.begin(); t != terrains.end(); ++t) { - const terrain_type& info = map.get_terrain_info(*t); - if(!info.is_alias()) { - const std::string& name = map.terrain_name(*t); - const std::string& lang_name = string_table[name]; - const int moves = move_type.movement_cost(map,*t); - - const double defense = move_type.defense_modifier(map,*t); - - const int def = 100-int(util::round(100.0*defense)); - - std::stringstream str; - str << lang_name << ","; - if(moves < 10) - str << moves; - else - str << "--"; - - str << "," << def << "%"; - - items.push_back(str.str()); - } - } - - const std::vector units_list(items.size(),un->second); - SDL_Surface* const unit_image = - gui.getImage(un->second.type().image_profile(),display::UNSCALED); - gui::show_dialog(gui,unit_image,un->second.type().language_name(), - string_table["terrain_info"], - gui::MESSAGE,&items,&units_list); - } - - if(!browse && command == HOTKEY_END_UNIT_TURN) { - const unit_map::iterator un = units.find(selected_hex); - if(un != units.end() && un->second.side() == team_num && - un->second.movement_left() > 0) { - std::vector steps; - steps.push_back(selected_hex); - undo_stack.push_back(undo_action( - steps,un->second.movement_left(),-1)); - redo_stack.clear(); - un->second.set_movement(0); - gui.draw_tile(selected_hex.x,selected_hex.y); - - gui.set_paths(NULL); - current_paths = paths(); - recorder.add_movement(selected_hex,selected_hex); - - command = HOTKEY_CYCLE_UNITS; - } - } - - //look for the next unit that is unmoved on our side - if(command == HOTKEY_CYCLE_UNITS) { - unit_map::const_iterator it = units.find(next_unit); - if(it != units.end()) { - for(++it; it != units.end(); ++it) { - if(it->second.side() == team_num && - unit_can_move(it->first,units,map,teams)) { - break; - } - } - } - - if(it == units.end()) { - for(it = units.begin(); it != units.end(); ++it) { - if(it->second.side() == team_num && - unit_can_move(it->first,units,map,teams)) { - break; - } - } - } - - if(it != units.end()) { - const bool ignore_zocs = - it->second.type().is_skirmisher(); - const bool teleport = it->second.type().teleports(); - current_paths = paths(map,gameinfo,units, - it->first,teams,ignore_zocs,teleport,turn_data.path_turns); - gui.set_paths(¤t_paths); - - gui.scroll_to_tile(it->first.x,it->first.y, - display::WARP); - } - - if(it != units.end()) { - next_unit = it->first; - selected_hex = next_unit; - gui.select_hex(selected_hex); - current_route.steps.clear(); - gui.set_route(NULL); - } else - next_unit = gamemap::location(); - } - - if(command == HOTKEY_LEADER) { - const unit_map::const_iterator i = team_leader(team_num,units); - if(i != units.end()) { - gui.scroll_to_tile(i->first.x,i->first.y,display::WARP); - } - } - - if(command == HOTKEY_STATUS_TABLE) { - std::vector items; - std::stringstream heading; - heading << string_table["leader"] << "," - << string_table["villages"] << "," - << string_table["units"] << "," - << string_table["upkeep"] << "," - << string_table["income"]; - - items.push_back(heading.str()); - - for(size_t n = 0; n != teams.size(); ++n) { - const team_data data = calculate_team_data(teams[n],n+1,units); - - std::stringstream str; - - //output the number of the side first, and this will - //cause it to be displayed in the correct colour - str << (char)(n+1) << team_name(n+1,units) << ","; - - str << data.villages << "," - << data.units << "," - << data.upkeep << "," - << (data.net_income < 0 ? "#":"") << data.net_income; - - items.push_back(str.str()); - } - - gui::show_dialog(gui,NULL,"","",gui::MESSAGE,&items); - } - - if(command == HOTKEY_TOGGLE_GRID) { - preferences::set_grid(!preferences::grid()); - gui.invalidate_all(); - } - - //undo - if(!browse && command == HOTKEY_UNDO && !undo_stack.empty()) { - const int starting_moves = undo_stack.back().starting_moves; - std::vector 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 false; - } - - if(map[route.front().x][route.front().y] == gamemap::TOWER) { - get_tower(route.front(),teams, - undo_stack.back().original_village_owner); - } - - 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(route.back(),un)); - gui.invalidate_unit(); - gui.draw_tile(route.back().x,route.back().y); - - redo_stack.push_back(undo_stack.back()); - undo_stack.pop_back(); - - gui.set_paths(NULL); - current_paths = paths(); - selected_hex = gamemap::location(); - current_route.steps.clear(); - gui.set_route(NULL); - - recorder.undo(); - } - - if(!browse && command == HOTKEY_REDO && !redo_stack.empty()) { - - //clear routes, selected hex, etc - gui.set_paths(NULL); - current_paths = paths(); - selected_hex = gamemap::location(); - current_route.steps.clear(); - gui.set_route(NULL); - - const int starting_moves = redo_stack.back().starting_moves; - std::vector route = redo_stack.back().route; - const unit_map::iterator u = units.find(route.front()); - if(u == units.end()) { - assert(false); - return false; - } - - 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(route.back(),un)); - gui.invalidate_unit(); - - recorder.add_movement(route.front(),route.back()); - - if(map[route.back().x][route.back().y] == gamemap::TOWER) { - get_tower(route.back(),teams,un.side()-1); - } - - gui.draw_tile(route.back().x,route.back().y); - - undo_stack.push_back(redo_stack.back()); - redo_stack.pop_back(); - } - - gui.draw(); - - game_events::pump(); - - events::pump(); - - return false; + last_hex_ = new_hex; +} + +void turn_info::mouse_press(const SDL_MouseButtonEvent& event) +{ + const team& current_team = teams_[team_num_-1]; + + if(event.button == SDL_BUTTON_LEFT && event.state == SDL_PRESSED) { + left_click(event); + } else if(event.button == SDL_BUTTON_RIGHT && event.state == SDL_PRESSED) { + if(!current_paths_.routes.empty()) { + selected_hex_ = gamemap::location(); + gui_.select_hex(gamemap::location()); + gui_.set_paths(NULL); + current_paths_ = paths(); + current_route_.steps.clear(); + gui_.set_route(NULL); + } else { + show_menu(); + } + } +} + +void turn_info::left_click(const SDL_MouseButtonEvent& event) +{ + const team& current_team = teams_[team_num_-1]; + + const gamemap::location& loc = gui_.minimap_location_on(event.x,event.y); + if(loc.valid()) { + gui_.scroll_to_tile(loc.x,loc.y,display::WARP); + } + + gamemap::location hex = gui_.hex_clicked_on(event.x,event.y); + + unit_map::iterator u = units_.find(selected_hex_); + + //if the unit is selected and then itself clicked on, + //any goto command is cancelled + if(u != units_.end() && !browse_ && + selected_hex_ == hex && u->second.side() == team_num_) { + u->second.set_goto(gamemap::location()); + } + + //if we can move to that tile + std::map::const_iterator + route = enemy_paths_ ? current_paths_.routes.end() : + current_paths_.routes.find(hex); + unit_map::iterator enemy = units_.find(hex); + + //see if we're trying to attack an enemy + if(route != current_paths_.routes.end() && enemy != units_.end() && + hex != selected_hex_ && !browse_ && + enemy->second.side() != u->second.side() && + current_team.is_enemy(enemy->second.side())) { + + const std::vector& attacks = u->second.attacks(); + std::vector items; + + std::vector units_list; + + for(size_t a = 0; a != attacks.size(); ++a) { + const battle_stats stats = evaluate_battle_stats( + map_,selected_hex_,hex, + a,units_,status_,gameinfo_); + + const std::string& lang_attack_name = + string_table["weapon_name_"+stats.attack_name]; + const std::string& lang_attack_type = + string_table["weapon_type_"+stats.attack_type]; + const std::string& lang_range = + string_table[stats.range == "Melee" ? + "short_range" : "long_range"]; + + const std::string& lang_defend_name = + string_table["weapon_name_"+stats.defend_name]; + const std::string& lang_defend_type = + string_table["weapon_type_"+stats.defend_type]; + + const std::string& attack_name = lang_attack_name.empty() ? + stats.attack_name : lang_attack_name; + const std::string& attack_type = lang_attack_type.empty() ? + stats.attack_type : lang_attack_type; + const std::string& defend_name = lang_defend_name.empty() ? + stats.defend_name : lang_defend_name; + const std::string& defend_type = lang_defend_type.empty() ? + stats.defend_type : lang_defend_type; + + const std::string& range = lang_range.empty() ? + stats.range : lang_range; + + std::stringstream att; + att << attack_name << " (" << attack_type + << ") " << stats.damage_defender_takes << "-" + << stats.nattacks << " " << range << " " + << int(util::round(100.0*stats.chance_to_hit_defender)) + << "%"; + + att << "," << string_table["versus"] << ","; + att << defend_name << " (" << defend_type + << ") " << stats.damage_attacker_takes << "-" + << stats.ndefends << " " + << int(util::round(100.0*stats.chance_to_hit_attacker)) + << "%"; + + items.push_back(att.str()); + units_list.push_back(enemy->second); + } + + //make it so that when we attack an enemy, the attacking unit + //is again shown in the status bar, so that we can easily + //compare between the attacking and defending unit + gui_.highlight_hex(gamemap::location()); + gui_.draw(true,true); + + const int res = gui::show_dialog(gui_,NULL,"", + string_table["choose_weapon"]+":\n", + gui::OK_CANCEL,&items,&units_list); + + if(size_t(res) < attacks.size()) { + u->second.set_goto(gamemap::location()); + undo_stack_.clear(); + redo_stack_.clear(); + + current_paths_ = paths(); + gui_.set_paths(NULL); + + game_events::fire("attack",selected_hex_,hex); + + //the event could have killed either the attacker or + //defender, so we have to make sure they still exist + u = units_.find(selected_hex_); + enemy = units_.find(hex); + + if(u == units_.end() || enemy == units_.end() || + size_t(res) >= u->second.attacks().size()) { + return; + } + + gui_.invalidate_all(); + gui_.draw(); + + recorder.add_attack(selected_hex_,hex,res); + + attack(gui_,map_,selected_hex_,hex,res,units_, + status_,gameinfo_,true); + + dialogs::advance_unit(gameinfo_,units_,selected_hex_,gui_); + dialogs::advance_unit(gameinfo_,units_,hex,gui_); + + selected_hex_ = gamemap::location(); + current_route_.steps.clear(); + gui_.set_route(NULL); + + check_victory(units_,teams_); + + gui_.invalidate_all(); + gui_.draw(); //clear the screen + } + } + + //otherwise we're trying to move to a hex + else if(!browse_ && selected_hex_.valid() && selected_hex_ != hex && + units_.count(selected_hex_) && + enemy == units_.end() && !current_route_.steps.empty() && + current_route_.steps.front() == selected_hex_) { + + const size_t moves = move_unit(&gui_,map_,units_,teams_, + current_route_.steps,&recorder,&undo_stack_); + + redo_stack_.clear(); + + selected_hex_ = gamemap::location(); + gui_.set_route(NULL); + gui_.select_hex(gamemap::location()); + gui_.set_paths(NULL); + current_paths_ = paths(); + + assert(moves <= current_route_.steps.size()); + const gamemap::location& dst = current_route_.steps[moves-1]; + + current_route_.steps.clear(); + + //if there is an enemy in a surrounding hex, then + //highlight attack options + gamemap::location adj[6]; + get_adjacent_tiles(dst,adj); + + int n; + for(n = 0; n != 6; ++n) { + const unit_map::const_iterator u_it = units_.find(adj[n]); + if(u_it != units_.end() && u_it->second.side() != team_num_ + && current_team.is_enemy(u_it->second.side())){ + current_paths_.routes[adj[n]] = paths::route(); + } + } + + if(current_paths_.routes.empty() == false) { + current_paths_.routes[dst] = paths::route(); + selected_hex_ = dst; + gui_.select_hex(dst); + gui_.set_paths(¤t_paths_); + } + + if(clear_shroud(gui_,map_,gameinfo_,units_,teams_,team_num_-1)) { + undo_stack_.clear(); + } + } else { + gui_.set_paths(NULL); + current_paths_ = paths(); + + selected_hex_ = hex; + gui_.select_hex(hex); + current_route_.steps.clear(); + gui_.set_route(NULL); + + const unit_map::iterator it = units_.find(hex); + if(it != units_.end() && it->second.side() == team_num_) { + const bool ignore_zocs = it->second.type().is_skirmisher(); + const bool teleport = it->second.type().teleports(); + current_paths_ = paths(map_,gameinfo_,units_,hex,teams_, + ignore_zocs,teleport,path_turns_); + gui_.set_paths(¤t_paths_); + + unit u = it->second; + const gamemap::location go_to = u.get_goto(); + if(map_.on_board(go_to)) { + const shortest_path_calculator calc(u,current_team, + units_,map_); + + const std::set* const teleports = + teleport ? ¤t_team.towers() : NULL; + + paths::route route = a_star_search(it->first,go_to, + 10000.0,calc,teleports); + route.move_left = + route_turns_to_complete(it->second,map_,route); + gui_.set_route(&route); + } + } + } +} + +void turn_info::show_menu() +{ + const unit_map::const_iterator un = units_.find(last_hex_); + + static const std::string menu_items[] = + {"scenario_objectives","recruit", + "recall","unit_list","status_table", + "save_game","preferences","end_turn",""}; + static const std::string browse_menu_items[] = + {"scenario_objectives","unit_list", + "status_table","save_game","preferences", + ""}; + std::vector menu; + + const std::string* items = browse_ ? browse_menu_items : menu_items; + for(; items->empty() == false; ++items) { + menu.push_back(string_table[*items]); + } + + if(un != units_.end()) { + menu.push_back(string_table["describe_unit"]); + } + + const int res = gui::show_dialog(gui_,NULL,"", + string_table["options"]+":\n", + gui::MESSAGE,&menu); + + const std::string result = res != -1 ? menu[res] : ""; + + if(un != units_.end()) { + menu.pop_back(); + } + + if(result == string_table["describe_unit"]) { + unit_description(); + } else if(result == string_table["preferences"]) { + preferences::show_preferences_dialog(gui_); + } else if(result == string_table["end_turn"]) { + end_turn(); + } else if(result == string_table["scenario_objectives"]) { + dialogs::show_objectives(gui_,*level_); + } else if(result == string_table["recall"]) { + recall(); + } else if(result == string_table["recruit"]) { + recruit(); + } else if(result == string_table["status_table"]) { + status_table(); + } else if(result == string_table["unit_list"]) { + const std::string heading = string_table["name"] + "," + + string_table["hp"] + "," + + string_table["xp"] + "," + + string_table["moves"] + "," + + string_table["location"]; + + std::vector items; + items.push_back(heading); + + std::vector units_list; + for(unit_map::const_iterator i = units_.begin(); + i != units_.end(); ++i) { + if(i->second.side() != team_num_) + continue; + + std::stringstream row; + row << i->second.name() << "," << i->second.hitpoints() + << "/" << i->second.max_hitpoints() << "," + << i->second.experience() << "/" + << i->second.max_experience() << "," + << i->second.movement_left() << "/" + << i->second.total_movement() << "," + << (i->first.x+1) << "-" << (i->first.y+1); + + items.push_back(row.str()); + + //extra unit for the first row to make up the heading + if(units_list.empty()) + units_list.push_back(i->second); + + units_list.push_back(i->second); + } + + gui::show_dialog(gui_,NULL,string_table["unit_list"],"", + gui::OK_ONLY,&items,&units_list); + } else if(result == string_table["save_game"]) { + save_game(); + } +} + +void turn_info::cycle_units() +{ + unit_map::const_iterator it = units_.find(next_unit_); + if(it != units_.end()) { + for(++it; it != units_.end(); ++it) { + if(it->second.side() == team_num_ && + unit_can_move(it->first,units_,map_,teams_)) { + break; + } + } + } + + if(it == units_.end()) { + for(it = units_.begin(); it != units_.end(); ++it) { + if(it->second.side() == team_num_ && + unit_can_move(it->first,units_,map_,teams_)) { + break; + } + } + } + + if(it != units_.end()) { + const bool ignore_zocs = it->second.type().is_skirmisher(); + const bool teleport = it->second.type().teleports(); + current_paths_ = paths(map_,gameinfo_,units_, + it->first,teams_,ignore_zocs,teleport,path_turns_); + gui_.set_paths(¤t_paths_); + + gui_.scroll_to_tile(it->first.x,it->first.y,display::WARP); + } + + if(it != units_.end()) { + next_unit_ = it->first; + selected_hex_ = next_unit_; + gui_.select_hex(selected_hex_); + current_route_.steps.clear(); + gui_.set_route(NULL); + } else + next_unit_ = gamemap::location(); +} + +void turn_info::end_turn() +{ + if(browse_) + return; + + end_turn_ = true; + recorder.save_game(gameinfo_,string_table["auto_save"]); + recorder.end_turn(); +} + +void turn_info::goto_leader() +{ + const unit_map::const_iterator i = team_leader(team_num_,units_); + if(i != units_.end()) { + gui_.scroll_to_tile(i->first.x,i->first.y,display::WARP); + } +} + +void turn_info::end_unit_turn() +{ + if(browse_) + return; + + const unit_map::iterator un = units_.find(selected_hex_); + if(un != units_.end() && un->second.side() == team_num_ && + un->second.movement_left() > 0) { + std::vector steps; + steps.push_back(selected_hex_); + undo_stack_.push_back(undo_action(steps,un->second.movement_left(),-1)); + redo_stack_.clear(); + un->second.set_movement(0); + gui_.draw_tile(selected_hex_.x,selected_hex_.y); + + gui_.set_paths(NULL); + current_paths_ = paths(); + recorder.add_movement(selected_hex_,selected_hex_); + + cycle_units(); + } +} + +void turn_info::undo() +{ + if(undo_stack_.empty()) + return; + + const int starting_moves = undo_stack_.back().starting_moves; + std::vector 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; + } + + if(map_[route.front().x][route.front().y] == gamemap::TOWER) { + get_tower(route.front(),teams_, + undo_stack_.back().original_village_owner); + } + + 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(route.back(),un)); + gui_.invalidate_unit(); + gui_.draw_tile(route.back().x,route.back().y); + + redo_stack_.push_back(undo_stack_.back()); + undo_stack_.pop_back(); + + gui_.set_paths(NULL); + current_paths_ = paths(); + selected_hex_ = gamemap::location(); + current_route_.steps.clear(); + gui_.set_route(NULL); + + recorder.undo(); +} + +void turn_info::redo() +{ + if(redo_stack_.empty()) + return; + + //clear routes, selected hex, etc + gui_.set_paths(NULL); + current_paths_ = paths(); + selected_hex_ = gamemap::location(); + current_route_.steps.clear(); + gui_.set_route(NULL); + + const int starting_moves = redo_stack_.back().starting_moves; + std::vector route = redo_stack_.back().route; + const unit_map::iterator u = units_.find(route.front()); + if(u == units_.end()) { + assert(false); + return; + } + + 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(route.back(),un)); + gui_.invalidate_unit(); + + recorder.add_movement(route.front(),route.back()); + + if(map_[route.back().x][route.back().y] == gamemap::TOWER) { + get_tower(route.back(),teams_,un.side()-1); + } + + gui_.draw_tile(route.back().x,route.back().y); + + undo_stack_.push_back(redo_stack_.back()); + redo_stack_.pop_back(); +} + +void turn_info::terrain_table() +{ + unit_map::const_iterator un = current_unit(); + + if(un == units_.end()) { + return; + } + + gui_.draw(); + + std::vector items; + items.push_back(string_table["terrain"] + "," + + string_table["movement"] + "," + + string_table["defense"]); + + const unit_type& type = un->second.type(); + const unit_movement_type& move_type = type.movement_type(); + const std::vector& terrains = + map_.get_terrain_precedence(); + for(std::vector::const_iterator t = + terrains.begin(); t != terrains.end(); ++t) { + const terrain_type& info = map_.get_terrain_info(*t); + if(!info.is_alias()) { + const std::string& name = map_.terrain_name(*t); + const std::string& lang_name = string_table[name]; + const int moves = move_type.movement_cost(map_,*t); + + const double defense = move_type.defense_modifier(map_,*t); + + const int def = 100-int(util::round(100.0*defense)); + + std::stringstream str; + str << lang_name << ","; + if(moves < 10) + str << moves; + else + str << "--"; + + str << "," << def << "%"; + + items.push_back(str.str()); + } + } + + const std::vector units_list(items.size(),un->second); + SDL_Surface* const unit_image = + gui_.getImage(un->second.type().image_profile(),display::UNSCALED); + gui::show_dialog(gui_,unit_image,un->second.type().language_name(), + string_table["terrain_info"], + gui::MESSAGE,&items,&units_list); +} + +void turn_info::attack_resistance() +{ + const unit_map::const_iterator un = current_unit(); + if(un == units_.end()) + return; + + gui_.draw(); + + std::vector items; + items.push_back(string_table["attack_type"] + "," + + string_table["attack_resistance"]); + const std::map& table = + un->second.type().movement_type().damage_table(); + for(std::map::const_iterator i + = table.begin(); i != table.end(); ++i) { + double resistance = atof(i->second.c_str()); + + //if resistance is less than 0, display in red + std::string prefix = ""; + if(resistance > 1.0) { + prefix = "#"; + } + + const int resist = 100 - int(util::round(100.0*resistance)); + + const std::string& lang_weapon = + string_table["weapon_type_" + i->first]; + const std::string& weap = lang_weapon.empty() ? i->first : lang_weapon; + + std::stringstream str; + str << weap << "," << prefix << resist << "%"; + items.push_back(str.str()); + } + + const std::vector units_list(items.size(), un->second); + SDL_Surface* const unit_image = + gui_.getImage(un->second.type().image_profile(),display::UNSCALED); + gui::show_dialog(gui_,unit_image, + un->second.type().language_name(), + string_table["unit_resistance_table"], + gui::MESSAGE,&items,&units_list); +} + +void turn_info::unit_description() +{ + const unit_map::const_iterator un = current_unit(); + if(un == units_.end()) + return; + + const std::string description = un->second.type().unit_description() + + "\n\n" + string_table["see_also"]; + + std::vector options; + + options.push_back(string_table["terrain_info"]); + options.push_back(string_table["attack_resistance"]); + options.push_back(string_table["close_window"]); + + SDL_Surface* const unit_image = gui_.getImage( + un->second.type().image_profile(), display::UNSCALED); + + const int res = gui::show_dialog(gui_,unit_image, + un->second.type().language_name(), + description,gui::MESSAGE, &options); + if(res == 0) { + terrain_table(); + } else if(res == 1) { + attack_resistance(); + } +} + +void turn_info::save_game() +{ + std::stringstream stream; + stream << state_of_game_.scenario << " " << string_table["turn"] + << " " << status_.turn(); + std::string label = stream.str(); + + const int res = gui::show_dialog(gui_,NULL,"","",gui::OK_CANCEL,NULL,NULL, + string_table["save_game_label"],&label); + + if(res == 0) { + recorder.save_game(gameinfo_,label); + gui::show_dialog(gui_,NULL,"", + string_table["save_confirm_message"], gui::OK_ONLY); + } +} + +void turn_info::toggle_grid() +{ + preferences::set_grid(!preferences::grid()); + gui_.invalidate_all(); +} + +void turn_info::status_table() +{ + std::vector items; + std::stringstream heading; + heading << string_table["leader"] << "," << string_table["villages"] << "," + << string_table["units"] << "," << string_table["upkeep"] << "," + << string_table["income"]; + + items.push_back(heading.str()); + + for(size_t n = 0; n != teams_.size(); ++n) { + const team_data data = calculate_team_data(teams_[n],n+1,units_); + + std::stringstream str; + + //output the number of the side first, and this will + //cause it to be displayed in the correct colour + str << (char)(n+1) << team_name(n+1,units_) << ","; + + str << data.villages << "," << data.units << "," << data.upkeep << "," + << (data.net_income < 0 ? "#":"") << data.net_income; + + items.push_back(str.str()); + } + + gui::show_dialog(gui_,NULL,"","",gui::MESSAGE,&items); +} + +void turn_info::recruit() +{ + if(browse_) + return; + + team& current_team = teams_[team_num_-1]; + + std::vector sample_units; + + gui_.draw(); //clear the old menu + std::vector item_keys; + std::vector items; + const std::set& recruits = current_team.recruits(); + for(std::set::const_iterator it = + recruits.begin(); it != recruits.end(); ++it) { + item_keys.push_back(*it); + const std::map::const_iterator + u_type = gameinfo_.unit_types.find(*it); + if(u_type == gameinfo_.unit_types.end()) { + std::cerr << "could not find " << *it << std::endl; + assert(false); + return; + } + + const unit_type& type = u_type->second; + std::stringstream description; + + description << type.language_name() << "," << type.cost() << " gold"; + items.push_back(description.str()); + sample_units.push_back(unit(&type,team_num_)); + } + + const int recruit_res = gui::show_dialog(gui_,NULL,"", + string_table["recruit_unit"] + ":\n", + gui::OK_CANCEL,&items,&sample_units); + if(recruit_res != -1) { + const std::string& name = item_keys[recruit_res]; + const std::map::const_iterator + u_type = gameinfo_.unit_types.find(name); + assert(u_type != gameinfo_.unit_types.end()); + + if(u_type->second.cost() > current_team.gold()) { + gui::show_dialog(gui_,NULL,"", + string_table["not_enough_gold_to_recruit"],gui::OK_ONLY); + } else { + //create a unit with traits + recorder.add_recruit(recruit_res,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_); + if(msg.empty()) { + current_team.spend_gold(u_type->second.cost()); + } else { + recorder.undo(); + gui::show_dialog(gui_,NULL,"",msg,gui::OK_ONLY); + } + + undo_stack_.clear(); + redo_stack_.clear(); + + gui_.invalidate_game_status(); + } + } +} + +void turn_info::recall() +{ + if(browse_) + return; + + team& current_team = teams_[team_num_-1]; + + //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()); + + 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()) { + gui::show_dialog(gui_,NULL,"",string_table["no_recall"]); + } else if(current_team.gold() < game_config::recall_cost) { + std::stringstream msg; + msg << string_table["not_enough_gold_to_recall_1"] + << " " << game_config::recall_cost << " " + << string_table["not_enough_gold_to_recall_2"]; + gui::show_dialog(gui_,NULL,"",msg.str()); + } else { + std::vector options; + for(std::vector::const_iterator unit = + state_of_game_.available_units.begin(); + unit != state_of_game_.available_units.end(); ++unit) { + std::stringstream option; + option << unit->type().language_name() << "," + << string_table["level"] << ": " + << unit->type().level() << "," + << string_table["xp"] << ": " + << unit->experience() << "/" + << unit->max_experience(); + options.push_back(option.str()); + } + + const int res = gui::show_dialog(gui_,NULL,"", + string_table["select_unit"] + ":\n", + gui::OK_CANCEL,&options, + &state_of_game_.available_units); + if(res >= 0) { + const std::string err = recruit_unit(map_,team_num_, + units_,state_of_game_.available_units[res], + last_hex_,&gui_); + if(!err.empty()) { + gui::show_dialog(gui_,NULL,"",err,gui::OK_ONLY); + } else { + 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,last_hex_); + + undo_stack_.clear(); + redo_stack_.clear(); + + gui_.invalidate_game_status(); + } + } + } +} + +unit_map::const_iterator turn_info::current_unit() +{ + unit_map::const_iterator i = units_.find(last_hex_); + if(i == units_.end()) { + i = units_.find(selected_hex_); + } + + return i; } diff --git a/src/playturn.hpp b/src/playturn.hpp index 880230fd986..32ef8e75b1a 100644 --- a/src/playturn.hpp +++ b/src/playturn.hpp @@ -18,9 +18,11 @@ #include "config.hpp" #include "dialogs.hpp" #include "display.hpp" +#include "events.hpp" #include "game_config.hpp" #include "game_events.hpp" #include "gamestatus.hpp" +#include "hotkeys.hpp" #include "key.hpp" #include "pathfind.hpp" #include "show_dialog.hpp" @@ -43,21 +45,75 @@ private: display& gui_; }; -struct turn_info { - turn_info() : left_button(false), right_button(false), middle_button(false), - enemy_paths(false), path_turns(0) - {} +class turn_info : public hotkey::command_executor, public events::handler, + private paths_wiper +{ +public: + turn_info(game_data& gameinfo, game_state& state_of_game, + gamestatus& status, config& terrain_config, config* level, + CKey& key, display& gui, gamemap& map, + std::vector& teams, int team_num, + unit_map& units, bool browse_only); - bool left_button, right_button, middle_button; - gamemap::location next_unit; - paths current_paths; - paths::route current_route; - bool enemy_paths; - gamemap::location last_hex; - gamemap::location selected_hex; - undo_list undo_stack; - undo_list redo_stack; - int path_turns; + void turn_slice(); + + bool turn_over() const; + + int send_data(int first_command); + + undo_list& undos() { return undo_stack_; } + +private: + + void cycle_units(); + void end_turn(); + void goto_leader(); + void end_unit_turn(); + void undo(); + void redo(); + void terrain_table(); + void attack_resistance(); + void unit_description(); + void save_game(); + void toggle_grid(); + void status_table(); + void recruit(); + void recall(); + + void handle_event(const SDL_Event& event); + void mouse_motion(const SDL_MouseMotionEvent& event); + void mouse_press(const SDL_MouseButtonEvent& event); + + void left_click(const SDL_MouseButtonEvent& event); + void show_menu(); + + unit_map::const_iterator current_unit(); + + game_data& gameinfo_; + game_state& state_of_game_; + gamestatus& status_; + config& terrain_config_; + config* level_; + CKey key_; + display& gui_; + gamemap& map_; + std::vector& teams_; + int team_num_; + unit_map& units_; + bool browse_; + + bool left_button_, right_button_, middle_button_; + gamemap::location next_unit_; + paths current_paths_; + paths::route current_route_; + bool enemy_paths_; + gamemap::location last_hex_; + gamemap::location selected_hex_; + undo_list undo_stack_; + undo_list redo_stack_; + int path_turns_; + + bool end_turn_; }; void play_turn(game_data& gameinfo, game_state& state_of_game, diff --git a/src/preferences.cpp b/src/preferences.cpp index 2aac30e4e96..cf8a9cf62ab 100644 --- a/src/preferences.cpp +++ b/src/preferences.cpp @@ -55,7 +55,7 @@ display_manager::display_manager(display* d) { disp = d; - add_hotkeys(prefs); + hotkey::add_hotkeys(prefs); set_grid(grid()); set_turbo(turbo()); diff --git a/src/show_dialog.cpp b/src/show_dialog.cpp index 8d3d5fe542c..481cfd953bd 100644 --- a/src/show_dialog.cpp +++ b/src/show_dialog.cpp @@ -28,8 +28,17 @@ #include #include +namespace { +bool is_in_dialog = false; +} + namespace gui { +bool in_dialog() { return is_in_dialog; } + +dialog_manager::dialog_manager() : reset_to(is_in_dialog) {is_in_dialog = true;} +dialog_manager::~dialog_manager() { is_in_dialog = reset_to; } + void draw_dialog_frame(int x, int y, int w, int h, display& disp) { draw_dialog_background(x,y,w,h,disp); @@ -190,6 +199,8 @@ int show_dialog(display& disp, SDL_Surface* image, if(disp.update_locked()) return -1; + const dialog_manager manager; + const events::resize_lock prevent_resizing; const std::vector& menu_items = diff --git a/src/show_dialog.hpp b/src/show_dialog.hpp index 78cac3e78ed..66d77591991 100644 --- a/src/show_dialog.hpp +++ b/src/show_dialog.hpp @@ -25,6 +25,16 @@ namespace gui { +bool in_dialog(); + +struct dialog_manager { + dialog_manager(); + ~dialog_manager(); + +private: + bool reset_to; +}; + void draw_dialog_frame(int x, int y, int w, int h, display& disp); void draw_dialog_background(int x, int y, int w, int h, display& disp); diff --git a/src/video.cpp b/src/video.cpp index 039788e0384..06bc686f766 100644 --- a/src/video.cpp +++ b/src/video.cpp @@ -213,7 +213,7 @@ void CVideo::flip() { if(update_all) { ::SDL_Flip(frameBuffer); - } else { + } else if(update_rects.empty() == false) { SDL_UpdateRects(frameBuffer,update_rects.size(),&update_rects[0]); }