[[Terrain and calculations]]

- added cave-based terrain generator

- added calculations for battles
This commit is contained in:
Dave White 2004-03-21 19:05:23 +00:00
parent 834cac4b67
commit 1640dcd596
27 changed files with 395 additions and 77 deletions

View file

@ -214,7 +214,7 @@ Defeat:
[message]
id=choice15e
description=Kalenz
message="We may take two paths which avoid the river. North, through the ancient home of my people, and then east to where they now live. Or we can go south, passing through the Swamps, before turning east, and then sorth. We would cross the river one time more if we chose that route, but I know a safe place for the crossing."
message="We may take two paths which avoid the river. North, through the ancient home of my people, and then east to where they now live. Or we can go south, passing through the Swamps, before turning east, and then north. We would cross the river one time more if we chose that route, but I know a safe place for the crossing."
[/message]
[message]
id=choice15f

View file

@ -104,8 +104,8 @@ Defeat:
side=4
canrecruit=1
recruit=Elvish Fighter,Elvish Archer,Elvish Ranger,Elvish Scout,Elvish Hero,Elvish Marksman
gold=200
income=20
gold=100
income=10
#the Elves focus on getting to Konrad so they can
#protect him

View file

@ -1,3 +1,52 @@
[map_generator]
name=cave
map_width=50
map_height=50
[chamber]
id=1
size=6
[items]
[side]
side=1
[/side]
[/items]
[/chamber]
[chamber]
id=2
size=20
jagged=70
[items]
[side]
side=2
[/side]
[/items]
[passage]
destination=1
width=3
jagged=50
windiness=20
[/passage]
[/chamber]
[chamber]
id=3
size=5
[passage]
destination=2
width=2
jagged=50
windiness=8
laziness=3
[/passage]
[passage]
destination=1
width=2
jagged=50
windiness=8
laziness=5
[/passage]
[/chamber]
[/map_generator]
[map_generator]
name=default
map_width=60

View file

@ -23,7 +23,7 @@ height=600
[menu]
title=main_menu
image=lite
items=speak,objectives,unitlist,recruit,recall,statustable,endturn,undo,redo,save,preferences,quit
items=speak,objectives,unitlist,recruit,recall,statustable,endturn,undo,redo,save,statistics,preferences,quit
rect=3,1,107,22
xanchor=fixed
yanchor=fixed

View file

@ -192,6 +192,15 @@ your_turn="It is now your turn"
leader="Leader"
#fighting statistics
base_damage="base damage"
minimum_damage="minimum damage"
damage_calculations="Damage Calculations"
attacker="Attacker"
defender="Defender"
attacker_resistance="attacker resistance vs"
defender_resistance="defender resistance vs"
#buttons
main_menu="Menu"
game_menu="Game"

View file

@ -9,7 +9,7 @@ experience=500
level=2
alignment=neutral
advanceto=null
cost=50
cost=40
usage=scout
unit_description="Half lion, half bird, these majestic creatures dominate the skies of the world. Since they are wary of other intelligent races, Gryphons should not be disturbed without a good reason."
get_hit_sound=groan.wav

View file

@ -24,6 +24,7 @@
#include "playturn.hpp"
#include "replay.hpp"
#include "sound.hpp"
#include "statistics.hpp"
#include "util.hpp"
#include <cmath>
@ -235,11 +236,6 @@ battle_stats evaluate_battle_stats(
if(res.attack_special == "backstab" && !backstab)
res.attack_special = "";
//the leader is immune to being turned to stone
if(d->second.can_recruit() && res.attack_special == "stone") {
res.attack_special = "";
}
res.range = (attack.range() == attack_type::SHORT_RANGE ?
"Melee" : "Ranged");
}
@ -270,14 +266,51 @@ battle_stats evaluate_battle_stats(
const int modifier = combat_modifier(state,units,d->first,d->second.type().alignment());
res.damage_attacker_takes = (base_damage * (100+modifier))/100;
if(charge)
if(include_strings) {
std::stringstream str_base;
str_base << string_table["base_damage"] << ", ," << defender_attacks[defend].damage();
res.defend_calculations.push_back(str_base.str());
std::stringstream str_resist;
const int resist = a->second.type().movement_type().resistance_against(defender_attacks[defend]) - 100;
str_resist << string_table["attacker_resistance"] << " " << translate_string(defender_attacks[defend].type())
<< ",^" << (resist > 0 ? "+" : "") << resist << "%," << base_damage;
res.defend_calculations.push_back(str_resist.str());
std::stringstream str_mod;
const time_of_day& tod = timeofday_at(state,units,d->first);
str_mod << translate_string_default(tod.id,tod.name) << ",^"
<< (modifier > 0 ? "+" : "") << modifier << "%," << res.damage_attacker_takes;
res.defend_calculations.push_back(str_mod.str());
}
if(charge) {
res.damage_attacker_takes *= 2;
if(include_strings) {
std::stringstream str;
str << translate_string("charge") << ",^+100%," << res.damage_attacker_takes;
res.defend_calculations.push_back(str.str());
}
}
if(under_leadership(units,defender))
if(under_leadership(units,defender)) {
res.damage_attacker_takes += res.damage_attacker_takes/8 + 1;
if(include_strings) {
std::stringstream str;
str << translate_string("leadership") << ",^+12.5%," << res.damage_attacker_takes;
res.defend_calculations.push_back(str.str());
}
}
if(res.damage_attacker_takes < 1)
if(res.damage_attacker_takes < 1) {
res.damage_attacker_takes = 1;
if(include_strings) {
std::stringstream str;
str << translate_string("minimum_damage") << ", ,1";
res.defend_calculations.push_back(str.str());
}
}
res.ndefends = defender_attacks[defend].num_attacks();
@ -287,10 +320,6 @@ battle_stats evaluate_battle_stats(
if(include_strings) {
res.defend_special = defender_attacks[defend].special();
res.defend_icon = defender_attacks[defend].icon();
//leaders are immune to being turned to stone
if(a->second.can_recruit() && res.defend_special == "stone")
res.defend_special = "";
}
//if the defender drains, and the attacker is a living creature, then
@ -315,11 +344,54 @@ battle_stats evaluate_battle_stats(
const int base_damage = d->second.damage_against(attack);
const int modifier = combat_modifier(state,units,a->first,a->second.type().alignment());
res.damage_defender_takes = ((base_damage * (100 + modifier))/100)
* (charge ? 2 : 1) * (backstab ? 2 : 1);
res.damage_defender_takes = ((base_damage * (100 + modifier))/100);
if(under_leadership(units,attacker))
if(include_strings) {
std::stringstream str_base;
str_base << string_table["base_damage"] << ", ," << attack.damage();
res.attack_calculations.push_back(str_base.str());
std::stringstream str_resist;
const int resist = d->second.type().movement_type().resistance_against(attack) - 100;
str_resist << string_table["defender_resistance"] << " " << translate_string(attack.type())
<< ",^" << (resist > 0 ? "+" : "") << resist << "%," << base_damage;
res.attack_calculations.push_back(str_resist.str());
std::stringstream str_mod;
const time_of_day& tod = timeofday_at(state,units,a->first);
str_mod << translate_string_default(tod.id,tod.name) << ",^"
<< (modifier > 0 ? "+" : "") << modifier << "%," << res.damage_defender_takes;
res.attack_calculations.push_back(str_mod.str());
}
if(charge) {
res.damage_defender_takes *= 2;
if(include_strings) {
std::stringstream str;
str << translate_string("charge") << ",^+100%," << res.damage_defender_takes;
res.attack_calculations.push_back(str.str());
}
}
if(backstab) {
res.damage_defender_takes *= 2;
if(include_strings) {
std::stringstream str;
str << translate_string("backstab") << ",^+100%," << res.damage_defender_takes;
res.attack_calculations.push_back(str.str());
}
}
if(under_leadership(units,attacker)) {
res.damage_defender_takes += res.damage_defender_takes/8 + 1;
if(include_strings) {
std::stringstream str;
str << translate_string("leadership") << ",^+12.5%," << res.damage_defender_takes;
res.attack_calculations.push_back(str.str());
}
}
//if the attacker drains, and the defender is a living creature, then
//the attacker will drain for half the damage it does
@ -348,7 +420,6 @@ void attack(display& gui, const gamemap& map,
const gamestatus& state,
const game_data& info, bool player_is_attacker)
{
//stop the user from issuing any commands while the units are fighting
const command_disabler disable_commands;
@ -373,6 +444,8 @@ void attack(display& gui, const gamemap& map,
battle_stats stats = evaluate_battle_stats(map,attacker,defender,
attack_with,units,state,info);
statistics::attack_context attack_stats(a->second,d->second,stats);
static const std::string poison_string("poison");
while(stats.nattacks > 0 || stats.ndefends > 0) {
@ -415,6 +488,9 @@ void attack(display& gui, const gamemap& map,
hits ? stats.damage_defender_takes : 0,
a->second.attacks()[attack_with]);
attack_stats.attack_result(hits ? (dies ? statistics::attack_context::KILLS : statistics::attack_context::HITS)
: statistics::attack_context::MISSES);
if(ran_results == NULL) {
config cfg;
cfg["hits"] = (hits ? "yes" : "no");
@ -541,6 +617,9 @@ void attack(display& gui, const gamemap& map,
hits ? stats.damage_attacker_takes : 0,
d->second.attacks()[stats.defend_with]);
attack_stats.defend_result(hits ? (dies ? statistics::attack_context::KILLS : statistics::attack_context::HITS)
: statistics::attack_context::MISSES);
if(ran_results == NULL) {
config cfg;
cfg["hits"] = (hits ? "yes" : "no");

View file

@ -58,6 +58,7 @@ struct battle_stats
int ndefends, nattacks;
int attack_with, defend_with;
bool attacker_plague, defender_plague;
std::vector<std::string> attack_calculations, defend_calculations;
};
//evaluate_battle_stats: a function which, if given an attacker

View file

@ -127,8 +127,7 @@ struct config
typedef std::vector<config*>::const_iterator const_child_iterator;
typedef std::pair<child_iterator,child_iterator> child_itors;
typedef std::pair<const_child_iterator,const_child_iterator>
const_child_itors;
typedef std::pair<const_child_iterator,const_child_iterator> const_child_itors;
child_itors child_range(const std::string& key);
const_child_itors child_range(const std::string& key) const;

View file

@ -739,6 +739,11 @@ void display::draw_report(reports::TYPE report_num)
for(std::vector<std::string>::const_iterator col = cols.begin(); col != cols.end(); ++col) {
SDL_Rect clip_rect = rect;
scoped_sdl_surface image(image::get_image(*col,image::UNSCALED));
if(image == NULL) {
std::cerr << "could not find image for report: '" << *col << "'\n";
return;
}
blit_surface(x,y,image,NULL,&clip_rect);
if(image->h > tallest_image)
tallest_image = image->h;

View file

@ -277,6 +277,8 @@ public:
const theme::menu* menu_pressed(int mousex, int mousey, bool button_pressed);
void unit_die(const gamemap::location& loc, SDL_Surface* image=NULL);
private:
display(const display&);
void operator=(const display&);
@ -299,8 +301,6 @@ private:
bool reverse, bool upside_down=false,
double alpha=1.0, Uint32 blendto=0, double submerged=0.0);
void unit_die(const gamemap::location& loc, SDL_Surface* image=NULL);
bool unit_attack_ranged(const gamemap::location& a,
const gamemap::location& b,
int damage, const attack_type& attack);

View file

@ -830,6 +830,11 @@ bool event_handler::handle_event_command(const queued_event& event_info, const s
for(std::map<gamemap::location,unit>::iterator un = units->begin();
un != units->end(); ++un) {
while(un != units->end() && game_events::unit_matches_filter(un,cfg)) {
if(cfg["animate"] == "yes") {
screen->scroll_to_tile(un->first.x,un->first.y,display::WARP);
screen->unit_die(un->first);
}
units->erase(un);
un = units->begin();
}

View file

@ -62,6 +62,7 @@ HOTKEY_COMMAND string_to_command(const std::string& str)
m.insert(val("preferences",HOTKEY_PREFERENCES));
m.insert(val("objectives",HOTKEY_OBJECTIVES));
m.insert(val("unitlist",HOTKEY_UNIT_LIST));
m.insert(val("statistics",HOTKEY_STATISTICS));
m.insert(val("quit",HOTKEY_QUIT_GAME));
}
@ -348,6 +349,10 @@ void execute_command(display& disp, HOTKEY_COMMAND command, command_executor* ex
if(executor)
executor->unit_list();
break;
case HOTKEY_STATISTICS:
if(executor)
executor->show_statistics();
break;
case HOTKEY_QUIT_GAME: {
const int res = gui::show_dialog(disp,NULL,"",string_table["quit_message"],gui::YES_NO);
if(res == 0) {

View file

@ -32,7 +32,7 @@ enum HOTKEY_COMMAND { HOTKEY_CYCLE_UNITS, HOTKEY_END_UNIT_TURN, HOTKEY_LEADER,
HOTKEY_RECRUIT, HOTKEY_REPEAT_RECRUIT, HOTKEY_RECALL, HOTKEY_ENDTURN,
HOTKEY_TOGGLE_GRID, HOTKEY_STATUS_TABLE, HOTKEY_MUTE,
HOTKEY_SPEAK, HOTKEY_CREATE_UNIT, HOTKEY_PREFERENCES,
HOTKEY_OBJECTIVES, HOTKEY_UNIT_LIST, HOTKEY_QUIT_GAME,
HOTKEY_OBJECTIVES, HOTKEY_UNIT_LIST, HOTKEY_STATISTICS, HOTKEY_QUIT_GAME,
HOTKEY_NULL };
struct hotkey_item {
@ -96,6 +96,7 @@ public:
virtual void preferences() = 0;
virtual void objectives() = 0;
virtual void unit_list() = 0;
virtual void show_statistics() = 0;
virtual bool can_execute_command(HOTKEY_COMMAND command) const = 0;
};

View file

@ -9,6 +9,7 @@
#include <ctime>
#include <vector>
#include "cavegen.hpp"
#include "mapgen.hpp"
#include "mapgen_dialog.hpp"
#include "pathfind.hpp"
@ -31,6 +32,29 @@ std::string random_generate_map(const std::string& parms)
return generator->create_map(parameters);
}
config random_generate_scenario(const std::string& parms)
{
//the first token is the name of the generator, tokens after
//that are arguments to the generator
std::vector<std::string> parameters = config::split(parms,' ');
map_generator* const generator = get_map_generator(parameters.front());
if(generator == NULL) {
std::cerr << "could not find map generator '" << parameters.front() << "'\n";
return "";
}
parameters.erase(parameters.begin());
return generator->create_scenario(parameters);
}
config map_generator::create_scenario(const std::vector<std::string>& args)
{
config res;
config& item = res.add_child("scenario");
item.values["map_data"] = create_map(args);
return res;
}
namespace {
typedef std::vector<std::vector<int> > height_map;
@ -778,9 +802,12 @@ generator_map generators;
map_generator::manager::manager(const config& game_config)
{
map_generator* const gen = new default_map_generator(game_config);
map_generator* gen = new default_map_generator(game_config);
assert(generators.count(gen->name()) == 0);
generators[gen->name()] = gen;
gen = new cave_map_generator(game_config);
generators[gen->name()] = gen;
}
map_generator::manager::~manager()

View file

@ -7,6 +7,7 @@
#include <string>
std::string random_generate_map(const std::string& parms);
config random_generate_scenario(const std::string& parms);
class map_generator
{
@ -27,7 +28,9 @@ public:
//creates a new map and returns it. args may contain arguments to
//the map generator
virtual std::string create_map(const std::vector<std::string>& args) const = 0;
virtual std::string create_map(const std::vector<std::string>& args) = 0;
virtual config create_scenario(const std::vector<std::string>& args);
struct manager
{

View file

@ -229,7 +229,7 @@ void default_map_generator::user_config(display& disp)
std::string default_map_generator::name() const { return "default"; }
std::string default_map_generator::create_map(const std::vector<std::string>& args) const
std::string default_map_generator::create_map(const std::vector<std::string>& args)
{
if(cfg_ != NULL)
return default_generate_map(width_,height_,iterations_,hill_size_,max_lakes_,nvillages_,nplayers_,*cfg_);

View file

@ -13,7 +13,7 @@ public:
std::string name() const;
std::string create_map(const std::vector<std::string>& args) const;
std::string create_map(const std::vector<std::string>& args);
private:
size_t width_, height_, iterations_, hill_size_, max_lakes_, nvillages_, nplayers_;

View file

@ -136,6 +136,7 @@ public:
return GAME_CANCELLED;
} else {
sides_ = reply;
std::cerr << "got some sides. Current number of sides = " << sides_.get_children("side").size() << "," << reply.get_children("side").size() << "\n";
}
}

View file

@ -560,10 +560,7 @@ redo_turn:
string_table["defeat_message"],
gui::OK_ONLY);
return DEFEAT;
} else if(end_level.result == CONTINUE) {
//basically like a victory but without all the celebrations
return VICTORY;
} else if(end_level.result == VICTORY) {
} else if(end_level.result == VICTORY || end_level.result == CONTINUE) {
try {
game_events::fire("victory");
} catch(end_level_exception&) {
@ -578,6 +575,12 @@ redo_turn:
}
}
//'continue' is like a victory, except it doesn't announce victory,
//and the player returns 100% of gold.
if(end_level.result == CONTINUE) {
state_of_game.gold = teams[0].gold();
}
if((*level)["disallow_recall"] == "yes") {
return VICTORY;
}

View file

@ -24,6 +24,7 @@
#include "preferences.hpp"
#include "replay.hpp"
#include "sound.hpp"
#include "statistics.hpp"
#include "tooltips.hpp"
#include "util.hpp"
@ -411,6 +412,62 @@ void turn_info::mouse_press(const SDL_MouseButtonEvent& event)
}
}
//a class which, when a button is pressed, will display
//how an attack was calculated
namespace {
class attack_calculations_displayer : public gui::dialog_button_action
{
public:
attack_calculations_displayer(display& disp, std::vector<battle_stats>& stats)
: disp_(disp), stats_(stats)
{}
void button_pressed(int selection);
private:
display& disp_;
std::vector<battle_stats>& stats_;
};
void attack_calculations_displayer::button_pressed(int selection)
{
const size_t index = size_t(selection);
if(index < stats_.size()) {
const battle_stats& stats = stats_[index];
std::vector<std::string> calcs;
std::stringstream str;
str << string_table["attacker"] << ", , ,";
if(stats.defend_calculations.empty() == false) {
str << string_table["defender"];
}
calcs.push_back(str.str());
for(size_t i = 0; i < maximum<size_t>(stats.attack_calculations.size(),stats.defend_calculations.size()); ++i) {
std::stringstream str;
if(i < stats.attack_calculations.size() && stats.attack_calculations.empty() == false) {
str << stats.attack_calculations[i];
} else {
str << ", , ";
}
str << ",";
if(i < stats.defend_calculations.size() && stats.defend_calculations.empty() == false) {
str << stats.defend_calculations[i];
} else {
str << " , , ";
}
calcs.push_back(str.str());
}
gui::show_dialog(disp_,NULL,"",string_table["damage_calculations"],gui::OK_ONLY,&calcs);
}
}
}
void turn_info::left_click(const SDL_MouseButtonEvent& event)
{
const team& current_team = teams_[team_num_-1];
@ -443,8 +500,7 @@ void turn_info::left_click(const SDL_MouseButtonEvent& event)
route = enemy_paths_ ? current_paths_.routes.end() :
current_paths_.routes.find(hex);
unit_map::iterator enemy = find_visible_unit(units_,
hex, map_,
unit_map::iterator enemy = find_visible_unit(units_, hex, map_,
status_.get_time_of_day().lawful_bonus,teams_,current_team);
//see if we're trying to attack an enemy
@ -464,18 +520,20 @@ void turn_info::left_click(const SDL_MouseButtonEvent& event)
int best_weapon_index = -1;
int best_weapon_rating = 0;
std::vector<battle_stats> stats;
for(size_t a = 0; a != attacks.size(); ++a) {
if(attacks[a].hexes() < range)
continue;
attacks_in_range.push_back(a);
const battle_stats stats = evaluate_battle_stats(
stats.push_back(evaluate_battle_stats(
map_,selected_hex_,hex,
a,units_,status_,gameinfo_);
a,units_,status_,gameinfo_));
int weapon_rating = stats.chance_to_hit_defender *
stats.damage_defender_takes * stats.nattacks;
int weapon_rating = stats.back().chance_to_hit_defender *
stats.back().damage_defender_takes * stats.back().nattacks;
if (best_weapon_index < 0 || best_weapon_rating < weapon_rating) {
best_weapon_index = items.size();
@ -483,42 +541,41 @@ void turn_info::left_click(const SDL_MouseButtonEvent& event)
}
const std::string& lang_attack_name =
string_table["weapon_name_"+stats.attack_name];
string_table["weapon_name_"+stats.back().attack_name];
const std::string& lang_attack_type =
string_table["weapon_type_"+stats.attack_type];
string_table["weapon_type_"+stats.back().attack_type];
const std::string& lang_range =
string_table[stats.range == "Melee" ?
string_table[stats.back().range == "Melee" ?
"short_range" : "long_range"];
const std::string& lang_defend_name =
string_table["weapon_name_"+stats.defend_name];
string_table["weapon_name_"+stats.back().defend_name];
const std::string& lang_defend_type =
string_table["weapon_type_"+stats.defend_type];
string_table["weapon_type_"+stats.back().defend_type];
const std::string& attack_name = lang_attack_name.empty() ?
stats.attack_name : lang_attack_name;
stats.back().attack_name : lang_attack_name;
const std::string& attack_type = lang_attack_type.empty() ?
stats.attack_type : lang_attack_type;
stats.back().attack_type : lang_attack_type;
const std::string& defend_name = lang_defend_name.empty() ?
stats.defend_name : lang_defend_name;
stats.back().defend_name : lang_defend_name;
const std::string& defend_type = lang_defend_type.empty() ?
stats.defend_type : lang_defend_type;
stats.back().defend_type : lang_defend_type;
const std::string& range = lang_range.empty() ?
stats.range : lang_range;
stats.back().range : lang_range;
std::stringstream att;
att << "&" << stats.attack_icon << "," << attack_name
<< " " << stats.damage_defender_takes << "-"
<< stats.nattacks << " " << range << " "
<< stats.chance_to_hit_defender
<< "%";
att << "&" << stats.back().attack_icon << "," << attack_name
<< " " << stats.back().damage_defender_takes << "-"
<< stats.back().nattacks << " " << range << " "
<< stats.back().chance_to_hit_defender << "%";
att << "," << string_table["versus"] << ",";
att << defend_name << " " << stats.damage_attacker_takes << "-"
<< stats.ndefends << " "
<< stats.chance_to_hit_attacker
<< "%,&" << stats.defend_icon;
att << defend_name << " " << stats.back().damage_attacker_takes << "-"
<< stats.back().ndefends << " "
<< stats.back().chance_to_hit_attacker
<< "%,&" << stats.back().defend_icon;
items.push_back(att.str());
units_list.push_back(enemy->second);
@ -534,9 +591,14 @@ void turn_info::left_click(const SDL_MouseButtonEvent& event)
gui_.highlight_hex(gamemap::location());
gui_.draw(true,true);
attack_calculations_displayer calc_displayer(gui_,stats);
std::vector<gui::dialog_button> buttons;
buttons.push_back(gui::dialog_button(&calc_displayer,string_table["damage_calculations"]));
int res = gui::show_dialog(gui_,NULL,"",
string_table["choose_weapon"]+":\n",
gui::OK_CANCEL,&items,&units_list);
gui::OK_CANCEL,&items,&units_list,"",NULL,NULL,NULL,-1,-1,
NULL,&buttons);
if(size_t(res) < attacks_in_range.size()) {
res = attacks_in_range[res];
@ -726,11 +788,12 @@ bool turn_info::can_execute_command(hotkey::HOTKEY_COMMAND command) const
case hotkey::HOTKEY_PREFERENCES:
case hotkey::HOTKEY_OBJECTIVES:
case hotkey::HOTKEY_UNIT_LIST:
case hotkey::HOTKEY_STATISTICS:
case hotkey::HOTKEY_QUIT_GAME:
return true;
case hotkey::HOTKEY_SPEAK:
return !browse_ && network::nconnections() > 0;
return network::nconnections() > 0;
case hotkey::HOTKEY_REDO:
return !browse_ && !redo_stack_.empty();
@ -1569,6 +1632,14 @@ void turn_info::unit_list()
}
}
void turn_info::show_statistics()
{
std::stringstream str;
str << "Kills: " << statistics::calculate_stats(0,team_num_).killed.size() << "\n"
<< "Deaths: " << statistics::calculate_stats(0,team_num_).deaths.size() << "\n";
gui::show_dialog(gui_,NULL,"",str.str(),gui::OK_ONLY);
}
unit_map::iterator turn_info::current_unit()
{
unit_map::iterator i = units_.end();

View file

@ -98,6 +98,7 @@ private:
void preferences();
void objectives();
void unit_list();
void show_statistics();
void do_recruit(const std::string& name);

View file

@ -214,7 +214,7 @@ int show_dialog(display& disp, SDL_Surface* image,
const std::string& text_widget_label,
std::string* text_widget_text,
dialog_action* action, std::vector<check_item>* options, int xloc, int yloc,
const std::string* dialog_style)
const std::string* dialog_style, std::vector<dialog_button>* action_buttons)
{
if(disp.update_locked())
return -1;
@ -347,6 +347,16 @@ int show_dialog(display& disp, SDL_Surface* image,
}
}
if(action_buttons != NULL) {
for(std::vector<dialog_button>::const_iterator i = action_buttons->begin(); i != action_buttons->end(); ++i) {
button new_button(disp,i->label);
check_button_height += new_button.height() + button_height_padding;
check_button_width = maximum(new_button.width(),check_button_width);
check_buttons.push_back(new_button);
}
}
const int left_padding = 10;
const int right_padding = 10;
const int image_h_padding = image != NULL ? 10 : 0;
@ -470,14 +480,17 @@ int show_dialog(display& disp, SDL_Surface* image,
//set the position of any tick boxes. they go right below the menu, slammed against
//the right side of the dialog
if(options != NULL) {
if(check_buttons.empty() == false) {
int options_y = text_widget_y + text_widget_height + menu_.height() + button_height_padding + menu_hpadding;
for(size_t i = 0; i != check_buttons.size(); ++i) {
check_buttons[i].set_x(xloc + total_width - padding_width - check_buttons[i].width());
check_buttons[i].set_y(options_y);
options_y += check_buttons[i].height() + button_height_padding;
check_buttons[i].set_check((*options)[i].checked);
if(options != NULL && i < options->size()) {
check_buttons[i].set_check((*options)[i].checked);
}
}
}
@ -623,9 +636,17 @@ int show_dialog(display& disp, SDL_Surface* image,
}
for(unsigned int n = 0; n != check_buttons.size(); ++n) {
check_buttons[n].process(mousex,mousey,left_button);
const bool pressed = check_buttons[n].process(mousex,mousey,left_button);
check_buttons[n].draw();
(*options)[n].checked = check_buttons[n].checked();
if(options != NULL && n < options->size()) {
(*options)[n].checked = check_buttons[n].checked();
} else if(pressed) {
const size_t options_size = options == NULL ? 0 : options->size();
assert(action_buttons != NULL && action_buttons->size() > n - options_size);
(*action_buttons)[n - options_size].handler->button_pressed(menu_.selection());
}
}
if(action != NULL) {

View file

@ -58,6 +58,23 @@ public:
enum { CONTINUE_DIALOG=-2 };
};
class dialog_button_action
{
public:
virtual ~dialog_button_action() {}
virtual void button_pressed(int menu_selection) = 0;
};
struct dialog_button
{
dialog_button(dialog_button_action* handler, const std::string& label) : handler(handler), label(label)
{}
dialog_button_action* handler;
std::string label;
};
enum { ESCAPE_DIALOG=-3 };
enum DIALOG_TYPE { MESSAGE, OK_ONLY, YES_NO, OK_CANCEL, CANCEL_ONLY };
@ -83,7 +100,8 @@ int show_dialog(display& screen, SDL_Surface* image,
std::string* text_widget_text=NULL,
dialog_action* action=NULL,
std::vector<check_item>* options=NULL, int xloc=-1, int yloc=-1,
const std::string* dialog_style=NULL
const std::string* dialog_style=NULL,
std::vector<dialog_button>* buttons=NULL
);
network::connection network_data_dialog(display& disp, const std::string& msg, config& cfg, network::connection connection_num=0);

View file

@ -325,24 +325,28 @@ int unit_movement_type::defense_modifier(const gamemap& map,
int unit_movement_type::damage_against(const attack_type& attack) const
{
int res = -1;
const int resist = resistance_against(attack);
return (attack.damage()*resist)/100;
}
int unit_movement_type::resistance_against(const attack_type& attack) const
{
bool result_found = false;
int res = 0;
const config* const resistance = cfg_.child("resistance");
if(resistance != NULL) {
const std::string& val = (*resistance)[attack.type()];
if(val != "") {
const int resist = atoi(val.c_str());
res = (resist*attack.damage())/100;
res = atoi(val.c_str());
result_found = true;
}
}
if(res == -1 && parent_ != NULL) {
res = parent_->damage_against(attack);
if(!result_found && parent_ != NULL) {
res = parent_->resistance_against(attack);
}
if(res <= 0)
res = 1;
return res;
}

View file

@ -107,6 +107,7 @@ public:
int movement_cost(const gamemap& map, gamemap::TERRAIN terrain) const;
int defense_modifier(const gamemap& map, gamemap::TERRAIN terrain) const;
int damage_against(const attack_type& attack) const;
int resistance_against(const attack_type& attack) const;
string_map damage_table() const;

View file

@ -66,7 +66,6 @@ LINK32=link.exe
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MD /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
@ -79,7 +78,7 @@ BSC32=bscmake.exe
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib SDL.lib SDLmain.lib SDL_image.lib SDL_ttf.lib SDL_mixer.lib SDL_net.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
!ENDIF
@ -116,6 +115,10 @@ SOURCE=.\src\widgets\button.cpp
# End Source File
# Begin Source File
SOURCE=.\src\cavegen.cpp
# End Source File
# Begin Source File
SOURCE=.\src\widgets\combo.cpp
# End Source File
# Begin Source File
@ -272,6 +275,10 @@ SOURCE=.\src\sound.cpp
# End Source File
# Begin Source File
SOURCE=.\src\statistics.cpp
# End Source File
# Begin Source File
SOURCE=.\src\team.cpp
# End Source File
# Begin Source File
@ -332,6 +339,10 @@ SOURCE=.\src\widgets\button.hpp
# End Source File
# Begin Source File
SOURCE=.\src\cavegen.hpp
# End Source File
# Begin Source File
SOURCE=.\src\widgets\combo.hpp
# End Source File
# Begin Source File
@ -492,6 +503,10 @@ SOURCE=.\src\sound.hpp
# End Source File
# Begin Source File
SOURCE=.\src\statistics.hpp
# End Source File
# Begin Source File
SOURCE=.\src\team.hpp
# End Source File
# Begin Source File