- fixed AI so that it no longer attacks from silly places...
(e.g. move off village into water and attack with a land unit) - fixed bug where zooming would mess up labels
This commit is contained in:
parent
39f7adcc1c
commit
b48f8cac73
16 changed files with 142 additions and 34 deletions
|
@ -24,7 +24,7 @@ language="English"
|
|||
command=repeatrecruit
|
||||
key=r
|
||||
ctrl=yes
|
||||
alt=yes
|
||||
shift=yes
|
||||
[/hotkey]
|
||||
[hotkey]
|
||||
command=recall
|
||||
|
@ -320,6 +320,7 @@ map_to_play="Map to play"
|
|||
village_gold="Village Gold"
|
||||
xp_modifier="Experience Requirements"
|
||||
turns="Turns"
|
||||
unlimited="Unlimited"
|
||||
|
||||
login="Login"
|
||||
must_login="You must log in to this server"
|
||||
|
|
|
@ -537,7 +537,7 @@ void attack(display& gui, const gamemap& map,
|
|||
|
||||
const int orig_attacks = stats.nattacks;
|
||||
const int orig_defends = stats.ndefends;
|
||||
int to_the_death = stats.to_the_death ? 10 : 0;
|
||||
int to_the_death = stats.to_the_death ? 30 : 0;
|
||||
|
||||
static const std::string poison_string("poison");
|
||||
|
||||
|
|
30
src/ai.cpp
30
src/ai.cpp
|
@ -927,3 +927,33 @@ void ai::move_leader_after_recruit(const move_map& enemy_dstsrc)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ai::rate_terrain(const unit& u, const gamemap::location& loc)
|
||||
{
|
||||
const gamemap::TERRAIN terrain = map_.get_terrain(loc);
|
||||
const int defense = u.defense_modifier(map_,terrain);
|
||||
int rating = 100 - defense;
|
||||
|
||||
const int healing_value = 10;
|
||||
const int friendly_village_value = 5;
|
||||
const int neutral_village_value = 10;
|
||||
const int enemy_village_value = 15;
|
||||
|
||||
if(map_.gives_healing(terrain)) {
|
||||
rating += healing_value;
|
||||
}
|
||||
|
||||
if(map_.is_village(terrain)) {
|
||||
const int village_owner = tower_owner(loc,teams_);
|
||||
|
||||
if(village_owner == team_num_) {
|
||||
rating += friendly_village_value;
|
||||
} else if(village_owner == -1) {
|
||||
rating += neutral_village_value;
|
||||
} else {
|
||||
rating += enemy_village_value;
|
||||
}
|
||||
}
|
||||
|
||||
return rating;
|
||||
}
|
|
@ -380,6 +380,9 @@ private:
|
|||
|
||||
std::pair<location,location> choose_move(std::vector<target>& targets,const move_map& dstsrc, const move_map& enemy_srcdst, const move_map& enemy_dstsrc);
|
||||
|
||||
//function which rates the value of moving onto certain terrain for a unit
|
||||
int rate_terrain(const unit& u, const location& loc);
|
||||
|
||||
display& disp_;
|
||||
const gamemap& map_;
|
||||
const game_data& gameinfo_;
|
||||
|
|
|
@ -69,12 +69,23 @@ void ai::do_attack_analysis(
|
|||
|
||||
for(size_t i = 0; i != units.size(); ++i) {
|
||||
const location current_unit = units[i];
|
||||
units.erase(units.begin() + i);
|
||||
|
||||
const unit_map::const_iterator unit_itor = units_.find(current_unit);
|
||||
assert(unit_itor != units_.end());
|
||||
|
||||
double best_vulnerability = 0.0, best_support = 0.0;
|
||||
int best_rating = 0;
|
||||
int cur_position = -1;
|
||||
|
||||
//iterate over positions adjacent to the unit, finding the best rated one
|
||||
for(int j = 0; j != 6; ++j) {
|
||||
if(used_locations[j])
|
||||
continue;
|
||||
|
||||
//if in this planned attack, a unit is already in this location
|
||||
if(used_locations[j]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//see if the current unit can reach that position
|
||||
typedef std::multimap<location,location>::const_iterator Itor;
|
||||
std::pair<Itor,Itor> its = dstsrc.equal_range(tiles[j]);
|
||||
while(its.first != its.second) {
|
||||
|
@ -83,40 +94,65 @@ void ai::do_attack_analysis(
|
|||
++its.first;
|
||||
}
|
||||
|
||||
if(its.first == its.second)
|
||||
//if the unit can't move to this location
|
||||
if(its.first == its.second) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cur_analysis.movements.push_back(std::pair<location,location>(current_unit,tiles[j]));
|
||||
//see if this position is the best rated we've seen so far
|
||||
const int rating = rate_terrain(unit_itor->second,tiles[j]);
|
||||
if(cur_position >= 0 && rating < best_rating) {
|
||||
continue;
|
||||
}
|
||||
|
||||
//find out how vulnerable we are to attack from enemy units in this hex
|
||||
const double vulnerability = power_projection(tiles[j],enemy_srcdst,enemy_dstsrc);
|
||||
cur_analysis.vulnerability += vulnerability;
|
||||
|
||||
//calculate how much support we have on this hex from allies. Support does not
|
||||
//take into account terrain, because we don't want to move into a hex that is
|
||||
//surrounded by good defensive terrain
|
||||
const double support = power_projection(tiles[j],srcdst,dstsrc,false);
|
||||
cur_analysis.support += support;
|
||||
|
||||
//if this is a position with equal defense to another position, but more vulnerability
|
||||
//then we don't want to use it
|
||||
if(cur_position >= 0 && rating == best_rating && vulnerability - support >= best_vulnerability - best_support) {
|
||||
continue;
|
||||
}
|
||||
|
||||
cur_position = j;
|
||||
best_rating = rating;
|
||||
best_vulnerability = vulnerability;
|
||||
best_support = support;
|
||||
}
|
||||
|
||||
if(cur_position != -1) {
|
||||
units.erase(units.begin() + i);
|
||||
|
||||
cur_analysis.movements.push_back(std::pair<location,location>(current_unit,tiles[cur_position]));
|
||||
|
||||
cur_analysis.vulnerability += best_vulnerability;
|
||||
|
||||
cur_analysis.support += best_support;
|
||||
|
||||
cur_analysis.analyze(map_,units_,state_,gameinfo_,50,*this);
|
||||
|
||||
if(cur_analysis.rating(0.0) > rating_to_beat) {
|
||||
|
||||
result.push_back(cur_analysis);
|
||||
used_locations[j] = true;
|
||||
used_locations[cur_position] = true;
|
||||
do_attack_analysis(loc,srcdst,dstsrc,enemy_srcdst,enemy_dstsrc,
|
||||
tiles,used_locations,
|
||||
units,result,cur_analysis);
|
||||
used_locations[j] = false;
|
||||
used_locations[cur_position] = false;
|
||||
}
|
||||
|
||||
cur_analysis.vulnerability -= vulnerability;
|
||||
cur_analysis.support -= support;
|
||||
cur_analysis.vulnerability -= best_vulnerability;
|
||||
cur_analysis.support -= best_support;
|
||||
|
||||
cur_analysis.movements.pop_back();
|
||||
}
|
||||
|
||||
units.insert(units.begin() + i, current_unit);
|
||||
units.insert(units.begin() + i, current_unit);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -322,7 +322,6 @@ void display::zoom(double amount)
|
|||
bounds_check_position();
|
||||
|
||||
if(zoom_ != prev_zoom) {
|
||||
map_labels_.recalculate_labels();
|
||||
xpos_ = orig_xpos;
|
||||
ypos_ = orig_ypos;
|
||||
zoom_ = orig_zoom;
|
||||
|
@ -332,6 +331,7 @@ void display::zoom(double amount)
|
|||
energy_bar_rect_.x = -1;
|
||||
|
||||
image::set_zoom(zoom_);
|
||||
map_labels_.recalculate_labels();
|
||||
invalidate_all();
|
||||
}
|
||||
|
||||
|
|
|
@ -367,10 +367,8 @@ int play_game(int argc, char** argv)
|
|||
display disp(u_map,video,gamemap(dummy_cfg,"1"),gamestatus(dummy_cfg,0),
|
||||
std::vector<team>(),dummy_cfg,dummy_cfg);
|
||||
|
||||
//we don't have a translation loaded yet, so tell it what "Ok" should be.
|
||||
string_table["ok_button"] = "Ok";
|
||||
gui::show_dialog(disp,NULL,"","Error loading game configuration files: '" + e.message + "' (The game will now exit)",
|
||||
gui::OK_ONLY);
|
||||
gui::MESSAGE);
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
|
|
@ -180,7 +180,7 @@ size_t gamestatus::turn() const
|
|||
return turn_;
|
||||
}
|
||||
|
||||
size_t gamestatus::number_of_turns() const
|
||||
int gamestatus::number_of_turns() const
|
||||
{
|
||||
return numTurns_;
|
||||
}
|
||||
|
@ -188,7 +188,7 @@ size_t gamestatus::number_of_turns() const
|
|||
bool gamestatus::next_turn()
|
||||
{
|
||||
++turn_;
|
||||
return turn_ <= numTurns_;
|
||||
return numTurns_ == -1 || turn_ <= size_t(numTurns_);
|
||||
}
|
||||
|
||||
game_state read_game(game_data& data, const config* cfg)
|
||||
|
|
|
@ -57,7 +57,7 @@ public:
|
|||
const time_of_day& get_previous_time_of_day() const;
|
||||
const time_of_day& get_time_of_day(bool illuminated, const gamemap::location& loc) const;
|
||||
size_t turn() const;
|
||||
size_t number_of_turns() const;
|
||||
int number_of_turns() const;
|
||||
|
||||
//function to move to the next turn. Returns true iff time
|
||||
//has expired.
|
||||
|
@ -100,7 +100,7 @@ private:
|
|||
std::vector<area_time_of_day> areas_;
|
||||
|
||||
size_t turn_;
|
||||
size_t numTurns_;
|
||||
int numTurns_;
|
||||
};
|
||||
|
||||
//object which holds all the data needed to start a scenario.
|
||||
|
|
|
@ -24,11 +24,28 @@
|
|||
namespace {
|
||||
CHARSET charset_used = CHARSET_LATIN1;
|
||||
std::string current_language;
|
||||
string_map strings_;
|
||||
}
|
||||
|
||||
CHARSET charset() { return charset_used; }
|
||||
|
||||
string_map string_table;
|
||||
symbol_table string_table;
|
||||
|
||||
const std::string& symbol_table::operator[](const std::string& key) const
|
||||
{
|
||||
const string_map::const_iterator i = strings_.find(key);
|
||||
if(i != strings_.end()) {
|
||||
return i->second;
|
||||
} else {
|
||||
static std::string empty_string;
|
||||
return empty_string;
|
||||
}
|
||||
}
|
||||
|
||||
const std::string& symbol_table::operator[](const char* key) const
|
||||
{
|
||||
return (*this)[std::string(key)];
|
||||
}
|
||||
|
||||
const std::string& translate_string(const std::string& str)
|
||||
{
|
||||
|
@ -37,8 +54,8 @@ const std::string& translate_string(const std::string& str)
|
|||
|
||||
const std::string& translate_string_default(const std::string& str, const std::string& default_val)
|
||||
{
|
||||
const string_map::const_iterator i = string_table.find(str);
|
||||
if(i != string_table.end() && i->second != "")
|
||||
const string_map::const_iterator i = strings_.find(str);
|
||||
if(i != strings_.end() && i->second != "")
|
||||
return i->second;
|
||||
else
|
||||
return default_val;
|
||||
|
@ -125,7 +142,7 @@ bool internal_set_language(const std::string& locale, config& cfg)
|
|||
}
|
||||
|
||||
for(string_map::const_iterator j = (*i)->values.begin(); j != (*i)->values.end(); ++j) {
|
||||
string_table[j->first] = j->second;
|
||||
strings_[j->first] = j->second;
|
||||
}
|
||||
|
||||
hotkey::add_hotkeys(**i,false);
|
||||
|
@ -142,7 +159,7 @@ bool internal_set_language(const std::string& locale, config& cfg)
|
|||
|
||||
bool set_language(const std::string& locale)
|
||||
{
|
||||
string_table.clear();
|
||||
strings_.clear();
|
||||
|
||||
std::string locale_lc;
|
||||
locale_lc.resize(locale.size());
|
||||
|
|
|
@ -21,10 +21,16 @@
|
|||
|
||||
//this module controls internationalization.
|
||||
|
||||
struct symbol_table
|
||||
{
|
||||
const std::string& operator[](const std::string& key) const;
|
||||
const std::string& operator[](const char* key) const;
|
||||
};
|
||||
|
||||
//table of strings which are displayed to the user. Maps ids -> text.
|
||||
//this table should be consulted whenever something is to be
|
||||
//displayed on screen.
|
||||
extern std::map<std::string,std::string> string_table;
|
||||
extern symbol_table string_table;
|
||||
|
||||
//function which translates a string if the string is available in
|
||||
//the string table, and otherwise simply returns the string itself
|
||||
|
|
|
@ -259,7 +259,7 @@ int play_multiplayer(display& disp, game_data& units_data, const config& cfg,
|
|||
mp_connect connector(disp, name_entry.text(), cfg, units_data, state);
|
||||
|
||||
const int res = connector.load_map((*era_list[era_combo.selected()])["id"],
|
||||
maps_menu.selection(), cur_turns, cur_villagegold, cur_xpmod, fog_game.checked(), shroud_game.checked(), observers_game.checked());
|
||||
maps_menu.selection(), cur_turns < 100 ? cur_turns : -1, cur_villagegold, cur_xpmod, fog_game.checked(), shroud_game.checked(), observers_game.checked());
|
||||
if(res == -1)
|
||||
return -1;
|
||||
|
||||
|
@ -287,7 +287,13 @@ int play_multiplayer(display& disp, game_data& units_data, const config& cfg,
|
|||
//Turns per game
|
||||
cur_turns = turns_slider.value();
|
||||
SDL_BlitSurface(village_bg, NULL, disp.video().getSurface(), &turns_rect);
|
||||
sprintf(buf,"Turns: %d", cur_turns);
|
||||
|
||||
if(cur_turns < 100) {
|
||||
sprintf(buf,"Turns: %d", cur_turns);
|
||||
} else {
|
||||
sprintf(buf,"Turns: %s", string_table["unlimited"].c_str());
|
||||
}
|
||||
|
||||
font::draw_text(&disp,disp.screen_area(),12,font::GOOD_COLOUR,
|
||||
buf,turns_rect.x,turns_rect.y);
|
||||
update_rect(turns_rect);
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include "sound.hpp"
|
||||
#include "statistics.hpp"
|
||||
#include "tooltips.hpp"
|
||||
#include "util.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <iterator>
|
||||
|
@ -100,6 +101,7 @@ LEVEL_RESULT play_level(game_data& gameinfo, const config& game_config,
|
|||
game_state& state_of_game,
|
||||
const std::vector<config*>& story)
|
||||
{
|
||||
std::cerr << "starting level '" << string_table["defeat_message"] << "'\n";
|
||||
//if the entire scenario should be randomly generated
|
||||
if((*level)["scenario_generation"] != "") {
|
||||
std::cerr << "randomly generating scenario...\n";
|
||||
|
@ -513,6 +515,7 @@ redo_turn:
|
|||
}
|
||||
|
||||
game_events::fire("time over");
|
||||
|
||||
throw end_level_exception(DEFEAT);
|
||||
}
|
||||
|
||||
|
@ -571,7 +574,7 @@ redo_turn:
|
|||
|
||||
const int remaining_gold = teams[0].gold();
|
||||
const int finishing_bonus_per_turn = map.towers().size()*game_config::tower_income + game_config::base_income;
|
||||
const int turns_left = status.number_of_turns() - status.turn();
|
||||
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;
|
||||
|
|
|
@ -218,7 +218,13 @@ report generate_report(TYPE type, const gamemap& map, const unit_map& units,
|
|||
return report("",tod.image,tooltip.str());
|
||||
}
|
||||
case TURN:
|
||||
str << status.turn() << "/" << status.number_of_turns() << "\n";
|
||||
str << status.turn();
|
||||
|
||||
if(status.number_of_turns() != -1) {
|
||||
str << "/" << status.number_of_turns();
|
||||
}
|
||||
|
||||
str << "\n";
|
||||
break;
|
||||
case GOLD:
|
||||
str << (current_team.gold() < 0 ? font::BAD_TEXT : font::NULL_MARKUP) << current_team.gold();
|
||||
|
|
|
@ -234,6 +234,8 @@ int show_dialog(display& disp, SDL_Surface* image,
|
|||
if(disp.update_locked())
|
||||
return -1;
|
||||
|
||||
std::cerr << "showing dialog '" << caption << "' '" << msg << "'\n";
|
||||
|
||||
const events::event_context dialog_events_context;
|
||||
const dialog_manager manager;
|
||||
|
||||
|
|
|
@ -349,7 +349,7 @@ int unit_movement_type::defense_modifier(const gamemap& map,
|
|||
}
|
||||
|
||||
if(res < 0)
|
||||
res = 100;
|
||||
res = 50;
|
||||
|
||||
defenseMods_.insert(std::pair<gamemap::TERRAIN,int>(terrain,res));
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue