fixed Li'sar dying in Hasty Alliance not resulting in a loss

This commit is contained in:
Dave White 2004-04-25 20:11:24 +00:00
parent 7ce8cf8a6b
commit 3ee8192b53
12 changed files with 250 additions and 187 deletions

View file

@ -361,5 +361,22 @@ Defeat:
[/unit]
[/event]
#deaths.cfg only handles death of Li'sar is she's on side '1', so handle
#it explicitly here.
[event]
name=die
[filter]
description=Li'sar
[/filter]
[message]
id=msg_lisar_die
speaker=unit
message="I can't believe it should end like this!"
[/message]
[endlevel]
result=defeat
[/endlevel]
[/event]
{deaths.cfg}
[/scenario]

View file

@ -542,9 +542,9 @@ hexes="hexes"
ambushed="Ambushed!"
enemy_unit_sighted="Enemy unit sighted!"
friendly_unit_sighted="Friendly unit sighted"
enemy_units_sighted="%enemies Enemy units sighted!"
friendly_units_sighted="%friends Friendly units sighted"
units_sighted="Units sighted! (%friends friendly, %enemies enemy)"
enemy_units_sighted="$enemies Enemy units sighted!"
friendly_units_sighted="$friends Friendly units sighted"
units_sighted="Units sighted! ($friends friendly, $enemies enemy)"
# Weapon special effect descriptions
weapon_special_backstab_description="Backstab:

View file

@ -1620,7 +1620,7 @@ size_t move_unit(display* disp, const game_data& gamedata,
symbols["enemies"] = lexical_cast<std::string>(nenemies);
std::cerr << "formatting string...\n";
const std::string message = format_string(msg_id,symbols);
const std::string message = config::interpolate_variables_into_string(msg_id,symbols);
std::cerr << "displaying label...\n";
font::add_floating_label(message,24,font::BAD_COLOUR,

View file

@ -1204,6 +1204,44 @@ bool config::has_value(const std::string& values, const std::string& val)
return std::count(vals.begin(),vals.end(),val) > 0;
}
namespace {
bool not_id(char c)
{
return !isalpha(c);
}
void do_interpolation(std::string& res, size_t npos, const string_map& m)
{
const std::string::iterator i = std::find(res.begin()+npos,res.end(),'$');
if(i == res.end() || i+1 == res.end()) {
return;
}
npos = i - res.begin() + 1;
const std::string::iterator end = std::find_if(i+1,res.end(),not_id);
const std::string key(i+1,end);
res.erase(i,end);
const string_map::const_iterator itor = m.find(key);
if(itor != m.end()) {
res.insert(npos-1,itor->second);
}
do_interpolation(res,npos,m);
}
}
std::string config::interpolate_variables_into_string(const std::string& str, const string_map& symbols)
{
std::string res = str;
do_interpolation(res,0,symbols);
return res;
}
void config::clear()
{
for(std::map<std::string,std::vector<config*> >::iterator i =

View file

@ -159,6 +159,8 @@ struct config
static std::string& strip(std::string& str);
static bool has_value(const std::string& values, const std::string& val);
static std::string interpolate_variables_into_string(const std::string& str, const string_map& symbols);
void clear();
bool empty() const;
@ -232,4 +234,20 @@ private:
const std::string name_, value_;
};
//an object which wraps around a config object and interpolates
//variables into all strings it returns
struct config_interpolater {
config_interpolater(const config& cfg, const string_map& m) : cfg_(cfg), map_(m)
{}
std::string operator[](const std::string& str) const
{
return config::interpolate_variables_into_string(cfg_[str],map_);
}
private:
const config& cfg_;
const string_map& map_;
};
#endif

View file

@ -442,12 +442,12 @@ bool event_handler::handle_event_command(const queued_event& event_info, const s
//setting a variable
else if(cmd == "set_variable") {
const std::string& name = cfg["name"];
const std::string& value = cfg["value"];
const std::string& value = config::interpolate_variables_into_string(cfg["value"],state_of_game->variables);
if(value.empty() == false) {
state_of_game->variables[name] = value;
}
const std::string& add = cfg["add"];
const std::string& add = config::interpolate_variables_into_string(cfg["add"],state_of_game->variables);;
if(add.empty() == false) {
double value = atof(state_of_game->variables[name].c_str());
value += atof(add.c_str());
@ -456,7 +456,7 @@ bool event_handler::handle_event_command(const queued_event& event_info, const s
state_of_game->variables[name] = buf;
}
const std::string& multiply = cfg["multiply"];
const std::string& multiply = config::interpolate_variables_into_string(cfg["multiply"],state_of_game->variables);;
if(multiply.empty() == false) {
double value = atof(state_of_game->variables[name].c_str());
value *= atof(multiply.c_str());
@ -470,7 +470,7 @@ bool event_handler::handle_event_command(const queued_event& event_info, const s
// Each element in the list will be considered a separate choice,
// unless it contains "..". In this case, it must be a numerical
// range. (i.e. -1..-10, 0..100, -10..10, etc)
const std::string& random = cfg["random"];
const std::string& random = config::interpolate_variables_into_string(cfg["random"],state_of_game->variables);;
if(random.empty() == false) {
std::string random_value, word;
std::vector<std::string> words;
@ -819,11 +819,11 @@ bool event_handler::handle_event_command(const queued_event& event_info, const s
const std::string& lang_message = string_table[id];
int option_chosen = -1;
//if we're not replaying, or if there is no choice to be made, show
//the dialog.
//if we're not replaying, or if we are replaying and there is no choice
//to be made, show the dialog.
if(recorder.at_end() || options.empty()) {
option_chosen = gui::show_dialog(*screen,surface,caption,
lang_message.empty() ? cfg["message"] : lang_message,
const std::string msg = config::interpolate_variables_into_string(lang_message.empty() ? cfg["message"] : lang_message,state_of_game->variables);
option_chosen = gui::show_dialog(*screen,surface,caption,msg,
options.empty() ? gui::MESSAGE : gui::OK_ONLY,
options.empty() ? NULL : &options);

View file

@ -61,44 +61,6 @@ const std::string& translate_string_default(const std::string& str, const std::s
return default_val;
}
namespace {
bool not_id(char c)
{
return !isalpha(c);
}
void do_formatting(std::string& res, size_t npos, const string_map& m)
{
const std::string::iterator i = std::find(res.begin()+npos,res.end(),'%');
if(i == res.end() || i+1 == res.end()) {
return;
}
npos = i - res.begin() + 1;
const std::string::iterator end = std::find_if(i+1,res.end(),not_id);
const std::string key(i+1,end);
res.erase(i,end);
const string_map::const_iterator itor = m.find(key);
if(itor != m.end()) {
res.insert(npos-1,itor->second);
}
do_formatting(res,npos,m);
}
}
std::string format_string(const std::string& key, const string_map& m)
{
std::string res = string_table[key];
do_formatting(res,0,m);
return res;
}
std::vector<std::string> get_languages()
{
std::vector<std::string> res;

View file

@ -40,11 +40,6 @@ const std::string& translate_string(const std::string& str);
//and otherwise returns default_val
const std::string& translate_string_default(const std::string& key, const std::string& default_val);
//a function which can take an id to a string in the string table, and a
//map of key/value pairs. Any token in the string of the form %identifier
//will be substituted with m["identifier"]
std::string format_string(const std::string& key, const string_map& m);
//function which, given the main configuration object, will return
//a list of the translations of the game available.
std::vector<std::string> get_languages();

View file

@ -326,10 +326,12 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
int turn = 1;
std::cerr << "starting main loop\n";
for(bool first_time = true; true; first_time = false, first_player = 0) {
int player_number = 0;
try {
int player_number = 0;
try {
for(bool first_time = true; true; first_time = false, first_player = 0) {
player_number = 0;
if(first_time) {
const hotkey::basic_handler key_events_handler(&gui);
@ -505,11 +507,11 @@ redo_turn:
game_events::pump();
check_victory(units,teams);
}
//time has run out
if(!status.next_turn()) {
if(non_interactive()) {
std::cout << "time over (draw)\n";
}
@ -530,100 +532,99 @@ redo_turn:
game_events::fire("turn " + turn_num);
game_events::fire("new turn");
}
} //end for loop
} catch(end_level_exception& end_level) {
} catch(end_level_exception& end_level) {
if(end_level.result == QUIT || end_level.result == REPLAY) {
return end_level.result;
} else if(end_level.result == DEFEAT) {
try {
game_events::fire("defeat");
} catch(end_level_exception&) {
if(end_level.result == QUIT || end_level.result == REPLAY) {
return end_level.result;
} else if(end_level.result == DEFEAT) {
try {
game_events::fire("defeat");
} catch(end_level_exception&) {
}
gui::show_dialog(gui,NULL,
string_table["defeat_heading"],
string_table["defeat_message"],
gui::OK_ONLY);
return DEFEAT;
} else if(end_level.result == VICTORY || end_level.result == CONTINUE) {
try {
game_events::fire("victory");
} catch(end_level_exception&) {
}
//add all the units that survived the scenario
for(std::map<gamemap::location,unit>::iterator un = units.begin(); un != units.end(); ++un) {
if(un->second.side() == 1) {
un->second.new_turn();
un->second.new_level();
state_of_game.available_units.push_back(un->second);
}
}
gui::show_dialog(gui,NULL,
string_table["defeat_heading"],
string_table["defeat_message"],
gui::OK_ONLY);
return DEFEAT;
} else if(end_level.result == VICTORY || end_level.result == CONTINUE) {
try {
game_events::fire("victory");
} catch(end_level_exception&) {
}
//add all the units that survived the scenario
for(std::map<gamemap::location,unit>::iterator un = units.begin(); un != units.end(); ++un) {
if(un->second.side() == 1) {
un->second.new_turn();
un->second.new_level();
state_of_game.available_units.push_back(un->second);
}
}
//'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();
return VICTORY;
}
if((*level)["disallow_recall"] == "yes") {
return VICTORY;
}
const int remaining_gold = teams[0].gold();
const int finishing_bonus_per_turn = map.villages().size()*game_config::village_income + game_config::base_income;
const int turns_left = maximum<int>(0,status.number_of_turns() - status.turn());
const int finishing_bonus = end_level.gold_bonus ?
(finishing_bonus_per_turn * turns_left) : 0;
state_of_game.gold = ((remaining_gold+finishing_bonus)*80)/100;
gui::show_dialog(gui,NULL,string_table["victory_heading"],
string_table["victory_message"],gui::OK_ONLY);
std::stringstream report;
report << string_table["remaining_gold"] << ": "
<< remaining_gold << "\n";
if(end_level.gold_bonus) {
report << string_table["early_finish_bonus"] << ": "
<< finishing_bonus_per_turn
<< " " << string_table["per_turn"] << "\n"
<< string_table["turns_finished_early"] << ": "
<< turns_left << "\n"
<< string_table["bonus"] << ": "
<< finishing_bonus << "\n"
<< string_table["gold"] << ": "
<< (remaining_gold+finishing_bonus);
}
report << "\n" << string_table["fifty_percent"] << "\n"
<< string_table["retained_gold"] << ": "
<< state_of_game.gold;
gui::show_dialog(gui,NULL,"",report.str(),gui::OK_ONLY);
//'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();
return VICTORY;
}
} //end catch
catch(replay::error& e) {
std::cerr << "caught replay::error\n";
gui::show_dialog(gui,NULL,"",string_table["bad_save_message"],
gui::OK_ONLY);
return QUIT;
}
catch(network::error& e) {
if(e.socket) {
e.disconnect();
if((*level)["disallow_recall"] == "yes") {
return VICTORY;
}
turn_info turn_data(gameinfo,state_of_game,status,
game_config,level,key,gui,
map,teams,player_number,units,true);
const int remaining_gold = teams[0].gold();
const int finishing_bonus_per_turn = map.villages().size()*game_config::village_income + game_config::base_income;
const int turns_left = maximum<int>(0,status.number_of_turns() - status.turn());
const int finishing_bonus = end_level.gold_bonus ?
(finishing_bonus_per_turn * turns_left) : 0;
state_of_game.gold = ((remaining_gold+finishing_bonus)*80)/100;
turn_data.save_game(string_table["save_game_error"]);
throw network::error();
gui::show_dialog(gui,NULL,string_table["victory_heading"],
string_table["victory_message"],gui::OK_ONLY);
std::stringstream report;
report << string_table["remaining_gold"] << ": "
<< remaining_gold << "\n";
if(end_level.gold_bonus) {
report << string_table["early_finish_bonus"] << ": "
<< finishing_bonus_per_turn
<< " " << string_table["per_turn"] << "\n"
<< string_table["turns_finished_early"] << ": "
<< turns_left << "\n"
<< string_table["bonus"] << ": "
<< finishing_bonus << "\n"
<< string_table["gold"] << ": "
<< (remaining_gold+finishing_bonus);
}
report << "\n" << string_table["fifty_percent"] << "\n"
<< string_table["retained_gold"] << ": "
<< state_of_game.gold;
gui::show_dialog(gui,NULL,"",report.str(),gui::OK_ONLY);
return VICTORY;
}
} //end catch
catch(replay::error& e) {
std::cerr << "caught replay::error\n";
gui::show_dialog(gui,NULL,"",string_table["bad_save_message"],
gui::OK_ONLY);
return QUIT;
}
catch(network::error& e) {
if(e.socket) {
e.disconnect();
}
} //end for(;;)
turn_info turn_data(gameinfo,state_of_game,status,
game_config,level,key,gui,
map,teams,player_number,units,true);
turn_data.save_game(string_table["save_game_error"]);
throw network::error();
}
return QUIT;
}

View file

@ -1634,19 +1634,15 @@ gui::dialog_button_action::RESULT delete_recall_unit::button_pressed(int menu_se
//about it
std::string message = "";
if(u.type().level() > 1) {
message = string_table["really_delete_veteran_unit"];
message = "really_delete_veteran_unit";
} else if(u.experience() > u.max_experience()/2) {
message = string_table["really_delete_xp_unit"];
message = "really_delete_xp_unit";
}
if(message != "") {
const std::string replace_str("$noun");
const std::string::iterator itor = std::search(message.begin(),message.end(),replace_str.begin(),replace_str.end());
if(itor != message.end()) {
const std::string::size_type index = itor - message.begin();
message.erase(itor,itor+replace_str.size());
message.insert(index,string_table[u.type().gender() == unit_race::MALE ? "noun_male" : "noun_female"]);
}
string_map symbols;
symbols["noun"] = string_table[u.type().gender() == unit_race::MALE ? "noun_male" : "noun_female"];
message = config::interpolate_variables_into_string(message,symbols);
const int res = gui::show_dialog(disp_,NULL,"",message,gui::YES_NO);
if(res != 0) {

View file

@ -279,83 +279,119 @@ const std::string& unit_movement_type::name() const
return res;
}
int unit_movement_type::movement_cost(const gamemap& map,
gamemap::TERRAIN terrain) const
int unit_movement_type::movement_cost(const gamemap& map,gamemap::TERRAIN terrain,int recurse_count) const
{
const std::map<gamemap::TERRAIN,int>::const_iterator i = moveCosts_.find(terrain);
if(i != moveCosts_.end()) {
return i->second;
}
//if this is an alias, then select the best of all underlying terrains
const std::string& underlying = map.underlying_terrain(terrain);
if(underlying.size() != 1 || underlying[0] != terrain) {
if(recurse_count >= 100) {
return 100;
}
int min_value = 100;
for(std::string::const_iterator i = underlying.begin(); i != underlying.end(); ++i) {
const int value = movement_cost(map,*i,recurse_count+1);
if(value < min_value) {
min_value = value;
}
}
moveCosts_.insert(std::pair<gamemap::TERRAIN,int>(terrain,min_value));
return min_value;
}
const config* movement_costs = cfg_.child("movement costs");
int res = -1;
if(movement_costs != NULL) {
const std::vector<std::string> names = map.underlying_terrain_name(terrain);
for(std::vector<std::string>::const_iterator i = names.begin(); i != names.end(); ++i) {
const std::string& val = (*movement_costs)[*i];
if(names.size() != 1) {
std::cerr << "terrain '" << terrain << "' has " << names.size() << " underlying names - 0 expected\n";
return 100;
}
if(val != "") {
const int value = atoi(val.c_str());
if(res == -1 || value < res) {
res = value;
}
}
const std::string& name = names.front();
const std::string& val = (*movement_costs)[name];
if(val != "") {
res = atoi(val.c_str());
}
}
if(parent_ != NULL) {
const int value = parent_->movement_cost(map,terrain);
if(res == -1 || value < res) {
res = value;
}
if(res <= 0 && parent_ != NULL) {
res = parent_->movement_cost(map,terrain);
}
if(res <= 0)
if(res <= 0) {
res = 100;
}
moveCosts_.insert(std::pair<gamemap::TERRAIN,int>(terrain,res));
return res;
}
int unit_movement_type::defense_modifier(const gamemap& map,
gamemap::TERRAIN terrain) const
int unit_movement_type::defense_modifier(const gamemap& map,gamemap::TERRAIN terrain, int recurse_count) const
{
const std::map<gamemap::TERRAIN,int>::const_iterator i =
defenseMods_.find(terrain);
const std::map<gamemap::TERRAIN,int>::const_iterator i = defenseMods_.find(terrain);
if(i != defenseMods_.end()) {
return i->second;
}
//if this is an alias, then select the best of all underlying terrains
const std::string& underlying = map.underlying_terrain(terrain);
if(underlying.size() != 1 || underlying[0] != terrain) {
if(recurse_count >= 100) {
return 100;
}
int min_value = 100;
for(std::string::const_iterator i = underlying.begin(); i != underlying.end(); ++i) {
const int value = defense_modifier(map,*i,recurse_count+1);
if(value < min_value) {
min_value = value;
}
}
defenseMods_.insert(std::pair<gamemap::TERRAIN,int>(terrain,min_value));
return min_value;
}
int res = -1;
const config* const defense = cfg_.child("defense");
if(defense != NULL) {
const std::vector<std::string> names = map.underlying_terrain_name(terrain);
for(std::vector<std::string>::const_iterator i = names.begin(); i != names.end(); ++i) {
const std::string& val = (*defense)[*i];
if(names.size() != 1) {
std::cerr << "terrain '" << terrain << "' has " << names.size() << " underlying names - 0 expected\n";
return 100;
}
if(val != "") {
const int value = atoi(val.c_str());
if(res == -1 || value < res) {
res = value;
}
}
const std::string& name = names.front();
const std::string& val = (*defense)[name];
if(val != "") {
res = atoi(val.c_str());
}
}
if(parent_ != NULL) {
const int value = parent_->defense_modifier(map,terrain);
if(res == -1 || value < res) {
res = value;
}
if(res <= 0 && parent_ != NULL) {
res = parent_->defense_modifier(map,terrain);
}
if(res < 0)
if(res <= 0) {
res = 50;
}
defenseMods_.insert(std::pair<gamemap::TERRAIN,int>(terrain,res));

View file

@ -114,8 +114,8 @@ public:
unit_movement_type(const config& cfg, const unit_movement_type* parent=NULL);
const std::string& name() const;
int movement_cost(const gamemap& map, gamemap::TERRAIN terrain) const;
int defense_modifier(const gamemap& map, gamemap::TERRAIN terrain) const;
int movement_cost(const gamemap& map, gamemap::TERRAIN terrain, int recurse_count=0) const;
int defense_modifier(const gamemap& map, gamemap::TERRAIN terrain, int recurse_count=0) const;
int damage_against(const attack_type& attack) const;
int resistance_against(const attack_type& attack) const;