mirror of
https://github.com/wesnoth/wesnoth
synced 2025-04-22 20:39:29 +00:00
839 lines
26 KiB
C++
839 lines
26 KiB
C++
/* $Id$ */
|
|
/*
|
|
Copyright (C) 2003 by David White <davidnwhite@optusnet.com.au>
|
|
Part of the Battle for Wesnoth Project http://wesnoth.whitevine.net
|
|
|
|
This program is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU General Public License.
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY.
|
|
|
|
See the COPYING file for more details.
|
|
*/
|
|
#include "hotkeys.hpp"
|
|
#include "language.hpp"
|
|
#include "playlevel.hpp"
|
|
#include "playturn.hpp"
|
|
#include "preferences.hpp"
|
|
#include "replay.hpp"
|
|
|
|
#include <cmath>
|
|
#include <deque>
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <string>
|
|
|
|
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) {}
|
|
std::vector<gamemap::location> route;
|
|
int starting_moves;
|
|
int original_village_owner;
|
|
};
|
|
|
|
void play_turn(game_data& gameinfo, game_state& state_of_game,
|
|
gamestatus& status, config& terrain_config, config* level,
|
|
CVideo& video, CKey& key, display& gui,
|
|
game_events::manager& events_manager, gamemap& map,
|
|
std::vector<team>& teams, int team_num,
|
|
std::map<gamemap::location,unit>& units)
|
|
{
|
|
gui.set_team(team_num-1);
|
|
gui.invalidate_all();
|
|
gui.draw();
|
|
gui.update_display();
|
|
|
|
team& current_team = teams[team_num-1];
|
|
|
|
const double scroll_speed = 30.0;
|
|
const double zoom_amount = 5.0;
|
|
|
|
const std::string menu_items[] = {"scenario_objectives","recruit",
|
|
"recall","unit_list","save_game",
|
|
"preferences","end_turn"};
|
|
std::vector<std::string> menu;
|
|
for(const std::string* menu_items_ptr = menu_items;
|
|
menu_items_ptr != menu_items + sizeof(menu_items)/sizeof(*menu_items);
|
|
++menu_items_ptr) {
|
|
menu.push_back(string_table[*menu_items_ptr]);
|
|
}
|
|
|
|
typedef std::map<gamemap::location,unit> units_map;
|
|
|
|
gamemap::location next_unit;
|
|
|
|
bool left_button = false, right_button = false;
|
|
|
|
const paths_wiper wiper(gui);
|
|
paths current_paths;
|
|
bool enemy_paths = false;
|
|
|
|
gamemap::location last_hex;
|
|
gamemap::location selected_hex;
|
|
|
|
std::deque<undo_action> undo_stack, redo_stack;
|
|
|
|
for(;;) {
|
|
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;
|
|
|
|
gamemap::location new_hex = gui.hex_clicked_on(mousex,mousey);
|
|
|
|
//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;
|
|
}
|
|
}
|
|
|
|
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 units_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();
|
|
current_paths = paths(map,gameinfo,units,new_hex,teams,
|
|
ignore_zocs,teleport);
|
|
gui.set_paths(¤t_paths);
|
|
enemy_paths = true;
|
|
}
|
|
}
|
|
|
|
last_hex = new_hex;
|
|
|
|
if(!left_button && new_left_button) {
|
|
const gamemap::location& hex = gui.hex_clicked_on(mousex,mousey);
|
|
|
|
units_map::iterator u = units.find(selected_hex);
|
|
|
|
//if we can move to that tile
|
|
const std::map<gamemap::location,paths::route>::const_iterator
|
|
route = enemy_paths ? current_paths.routes.end() :
|
|
current_paths.routes.find(hex);
|
|
units_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 &&
|
|
enemy->second.side() != u->second.side()) {
|
|
|
|
const unit_type& type = u->second.type();
|
|
const unit_type& enemy_type = enemy->second.type();
|
|
const std::vector<attack_type>& attacks = u->second.attacks();
|
|
std::vector<std::string> items;
|
|
const std::vector<attack_type>& defends =
|
|
enemy->second.attacks();
|
|
|
|
std::vector<unit> units_list;
|
|
|
|
for(int 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(ceil(100.0*stats.chance_to_hit_defender)) << "%";
|
|
|
|
att << "," << string_table["versus"] << ",";
|
|
att << defend_name << " (" << defend_type
|
|
<< ") " << stats.damage_attacker_takes << "-"
|
|
<< stats.ndefends << " "
|
|
<< int(ceil(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(res >= 0 && res < attacks.size()) {
|
|
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() ||
|
|
res >= u->second.attacks().size())
|
|
continue;
|
|
|
|
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();
|
|
|
|
gui.invalidate_unit();
|
|
gui.draw(); //clear the screen
|
|
|
|
const int winner = check_victory(units);
|
|
|
|
//if the player has won
|
|
if(winner == team_num) {
|
|
throw end_level_exception(VICTORY);
|
|
} else if(winner >= 1) {
|
|
throw end_level_exception(DEFEAT);
|
|
}
|
|
}
|
|
}
|
|
|
|
//otherwise we're just trying to move to a hex
|
|
else if(selected_hex.valid() && selected_hex != hex &&
|
|
enemy == units.end() &&
|
|
route != current_paths.routes.end()) {
|
|
std::vector<gamemap::location> steps = route->second.steps;
|
|
steps.push_back(hex);
|
|
|
|
//if an event mutates the game environment, then this
|
|
//move must be marked un-redoable
|
|
bool event_mutated = false;
|
|
|
|
int orig_tower_owner = -1;
|
|
int starting_moves = 0;
|
|
int unit_side = 0;
|
|
if(u != units.end()) {
|
|
unit un = u->second;
|
|
starting_moves = un.movement_left();
|
|
unit_side = un.side();
|
|
units.erase(u);
|
|
gui.move_unit(steps,un);
|
|
recorder.add_movement(selected_hex,hex);
|
|
un.set_movement(route->second.move_left);
|
|
|
|
//see if the unit has gotten a tower
|
|
if(map[hex.x][hex.y] == gamemap::TOWER) {
|
|
orig_tower_owner = tower_owner(hex,teams);
|
|
get_tower(hex,teams,team_num-1);
|
|
un.set_movement(0); //end of turn if you get a tower
|
|
}
|
|
|
|
units.insert(std::pair<gamemap::location,unit>(hex,un));
|
|
gui.invalidate_unit();
|
|
|
|
//fire the move to event. If the event mutates
|
|
//the game state, then the user cannot undo the move
|
|
event_mutated = game_events::fire("moveto",hex);
|
|
} else {
|
|
assert(false);
|
|
continue;
|
|
}
|
|
|
|
selected_hex = gamemap::location();
|
|
gui.select_hex(gamemap::location());
|
|
gui.set_paths(NULL);
|
|
current_paths = paths();
|
|
|
|
//if there is an enemy in a surrounding hex, then
|
|
//highlight attack options
|
|
gamemap::location adj[6];
|
|
get_adjacent_tiles(steps.back(),adj);
|
|
|
|
int n;
|
|
for(n = 0; n != 6; ++n) {
|
|
const units_map::const_iterator u_it = units.find(adj[n]);
|
|
if(u_it != units.end() && u_it->second.side() != unit_side
|
|
&& current_team.is_enemy(u_it->second.side())){
|
|
current_paths.routes[adj[n]] = paths::route();
|
|
}
|
|
}
|
|
|
|
if(current_paths.routes.empty() == false) {
|
|
current_paths.routes[steps.back()] = paths::route();
|
|
selected_hex = steps.back();
|
|
gui.select_hex(steps.back());
|
|
gui.set_paths(¤t_paths);
|
|
}
|
|
|
|
undo_stack.push_back(undo_action(steps,starting_moves,
|
|
orig_tower_owner));
|
|
redo_stack.clear();
|
|
|
|
if(event_mutated) {
|
|
undo_stack.clear();
|
|
}
|
|
|
|
} else {
|
|
gui.set_paths(NULL);
|
|
current_paths = paths();
|
|
|
|
selected_hex = hex;
|
|
gui.select_hex(hex);
|
|
|
|
const units_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);
|
|
gui.set_paths(¤t_paths);
|
|
}
|
|
}
|
|
}
|
|
|
|
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();
|
|
} else {
|
|
const units_map::const_iterator un = units.find(
|
|
gui.hex_clicked_on(mousex,mousey));
|
|
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"]) {
|
|
|
|
const std::string description
|
|
= un->second.type().unit_description() +
|
|
"\n\n" + string_table["see_also"];
|
|
|
|
std::vector<std::string> options;
|
|
|
|
options.push_back(string_table["terrain_info"]);
|
|
options.push_back(string_table["attack_resistance"]);
|
|
options.push_back(string_table["close_window"]);
|
|
|
|
std::vector<unit> units_list(options.size(),un->second);
|
|
|
|
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,&units_list);
|
|
|
|
//terrain table
|
|
if(res == 0) {
|
|
command = HOTKEY_TERRAIN_TABLE;
|
|
}
|
|
|
|
//attack resistance table
|
|
else if(res == 1) {
|
|
command = HOTKEY_ATTACK_RESISTANCE;
|
|
}
|
|
}
|
|
|
|
else if(result == string_table["preferences"]) {
|
|
preferences::show_preferences_dialog(gui);
|
|
}
|
|
|
|
else if(result == string_table["end_turn"]) {
|
|
recorder.end_turn();
|
|
return;
|
|
}
|
|
|
|
else if(result == string_table["scenario_objectives"]) {
|
|
static const std::string no_objectives(
|
|
string_table["no_objectives"]);
|
|
const std::string& id = level->values["id"];
|
|
const std::string& lang_objectives =
|
|
string_table[id + "_objectives"];
|
|
|
|
const std::string& objectives = lang_objectives.empty() ?
|
|
level->values["objectives"] : lang_objectives;
|
|
gui::show_dialog(
|
|
gui, NULL, "",
|
|
objectives.empty() ? no_objectives : objectives,
|
|
gui::OK_ONLY);
|
|
}
|
|
|
|
else if(result == string_table["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(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<std::string> options;
|
|
for(std::vector<unit>::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();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if(result == string_table["recruit"]) {
|
|
|
|
std::vector<unit> sample_units;
|
|
|
|
gui.draw(); //clear the old menu
|
|
std::vector<std::string> item_keys;
|
|
std::vector<std::string> items;
|
|
const std::set<std::string>& recruits
|
|
= current_team.recruits();
|
|
for(std::set<std::string>::const_iterator it =
|
|
recruits.begin(); it != recruits.end(); ++it) {
|
|
item_keys.push_back(*it);
|
|
const std::map<std::string,unit_type>::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);
|
|
continue;
|
|
}
|
|
|
|
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<std::string,unit_type>::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 {
|
|
recorder.add_recruit(recruit_res,last_hex);
|
|
|
|
//create a unit with traits
|
|
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
|
|
gui::show_dialog(gui,NULL,"",msg,gui::OK_ONLY);
|
|
|
|
undo_stack.clear();
|
|
redo_stack.clear();
|
|
|
|
gui.invalidate_game_status();
|
|
}
|
|
}
|
|
} 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<std::string> items;
|
|
items.push_back(heading);
|
|
|
|
std::vector<unit> units_list;
|
|
for(units_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 << "-" << i->first.y;
|
|
|
|
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"]) {
|
|
std::stringstream stream;
|
|
stream << string_table["scenario"]
|
|
<< " " << (state_of_game.scenario+1)
|
|
<< " " << 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
right_button = new_right_button;
|
|
|
|
if(key[KEY_UP] || mousey == 0)
|
|
gui.scroll(0.0,-scroll_speed);
|
|
|
|
if(key[KEY_DOWN] || mousey == gui.y()-1)
|
|
gui.scroll(0.0,scroll_speed);
|
|
|
|
if(key[KEY_LEFT] || mousex == 0)
|
|
gui.scroll(-scroll_speed,0.0);
|
|
|
|
if(key[KEY_RIGHT] || mousex == gui.x()-1)
|
|
gui.scroll(scroll_speed,0.0);
|
|
|
|
if(command == HOTKEY_NULL)
|
|
command = check_keys(gui);
|
|
|
|
units_map::const_iterator un = units.find(new_hex);
|
|
if(un == units.end())
|
|
un = units.find(selected_hex);
|
|
|
|
if(un != units.end() && command == HOTKEY_ATTACK_RESISTANCE) {
|
|
gui.draw();
|
|
|
|
std::vector<std::string> items;
|
|
items.push_back(string_table["attack_type"] + "," +
|
|
string_table["attack_resistance"]);
|
|
const std::map<std::string,std::string>& table =
|
|
un->second.type().movement_type().damage_table();
|
|
for(std::map<std::string,std::string>::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=int(100.0-ceil(100.0*resistance));
|
|
|
|
std::stringstream str;
|
|
str << i->first << "," << prefix << resist << "%";
|
|
items.push_back(str.str());
|
|
}
|
|
|
|
const std::vector<unit> 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(),
|
|
"Unit resistance table",
|
|
gui::MESSAGE,&items,&units_list);
|
|
}
|
|
|
|
if(un != units.end() && command == HOTKEY_TERRAIN_TABLE) {
|
|
gui.draw();
|
|
|
|
std::vector<std::string> 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<gamemap::TERRAIN>& terrains =
|
|
map.get_terrain_precedence();
|
|
for(std::vector<gamemap::TERRAIN>::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 = int(100.0-ceil(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<unit> 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(command == HOTKEY_END_UNIT_TURN) {
|
|
const units_map::iterator un = units.find(selected_hex);
|
|
if(un != units.end() && un->second.side() == team_num &&
|
|
un->second.movement_left() > 0) {
|
|
std::vector<gamemap::location> 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) {
|
|
units_map::const_iterator it = units.find(next_unit);
|
|
if(it != units.end()) {
|
|
for(++it; it != units.end(); ++it) {
|
|
if(it->second.side() == team_num &&
|
|
it->second.movement_left() > 0) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(it == units.end()) {
|
|
for(it = units.begin(); it != units.end();
|
|
++it) {
|
|
if(it->second.side() == team_num &&
|
|
it->second.movement_left() > 0) {
|
|
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);
|
|
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);
|
|
} else
|
|
next_unit = gamemap::location();
|
|
}
|
|
|
|
if(command == HOTKEY_LEADER) {
|
|
for(units_map::const_iterator i = units.begin(); i != units.end();
|
|
++i) {
|
|
if(i->second.side() == team_num && i->second.can_recruit()) {
|
|
gui.scroll_to_tile(i->first.x,i->first.y,display::WARP);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//undo
|
|
if(command == HOTKEY_UNDO && !undo_stack.empty()) {
|
|
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 units_map::iterator u = units.find(route.front());
|
|
if(u == units.end()) {
|
|
assert(false);
|
|
continue;
|
|
}
|
|
|
|
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;
|
|
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.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();
|
|
|
|
recorder.undo();
|
|
}
|
|
|
|
if(command == HOTKEY_REDO && !redo_stack.empty()) {
|
|
const int starting_moves = redo_stack.back().starting_moves;
|
|
std::vector<gamemap::location> route = redo_stack.back().route;
|
|
const units_map::iterator u = units.find(route.front());
|
|
if(u == units.end()) {
|
|
assert(false);
|
|
continue;
|
|
}
|
|
|
|
redo_stack.back().starting_moves = u->second.movement_left();
|
|
|
|
unit un = u->second;
|
|
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();
|
|
|
|
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();
|
|
}
|
|
}
|