advanced terrain alias.

you can now specify a best of or worst of policy, you can also
separately specify the alias for movement and the aliases for defense
This commit is contained in:
Jérémy Rosen 2006-01-28 23:21:21 +00:00
parent b99fabe302
commit b42308c570
15 changed files with 167 additions and 59 deletions

View file

@ -89,6 +89,10 @@ SVN trunk (1.1+svn):
specify a different list of randomly selectable leaders
* added level as a possible argument for standard unit [filter]
* terrain can be given a "light" flag to specify local light modification
* combined terrain can now take "best of" or "worst of" values for
combining
* choice policy for combined terrain can be separately specified for def
and mvt
* music:
* support for more than one music file per scenario with adjustable spacing
* Miscellaneous

View file

@ -325,7 +325,7 @@ battle_stats evaluate_battle_stats(const gamemap& map,
const bool charge = res.attacker_special == charge_string;
bool steadfast = d->second.type().steadfast();
if (steadfast) {
steadfast = d->second.type().steadfast_filter().matches_filter(map.underlying_terrain(defender_terrain), state.get_time_of_day().lawful_bonus);
steadfast = d->second.type().steadfast_filter().matches_filter(map.underlying_union_terrain(defender_terrain), state.get_time_of_day().lawful_bonus);
}
const bool steadfast_percent = d->second.type().steadfast_ispercent();
const int steadfast_bonus = d->second.type().steadfast_bonus();
@ -1359,7 +1359,7 @@ void calculate_healing(display& disp, const gamestatus& status, const gamemap& m
const bool show_healing = !disp.turbo() && !recorder.is_skipping() &&
!disp.fogged(loc.x,loc.y) &&
(!u.invisible(map.underlying_terrain(map[h->first.x][h->first.y]),
(!u.invisible(map.underlying_union_terrain(map[h->first.x][h->first.y]),
status.get_time_of_day().lawful_bonus,h->first,units,teams) ||
teams[disp.viewing_team()].is_enemy(side) == false);
@ -1610,7 +1610,7 @@ const time_of_day& timeofday_at(const gamestatus& status,const unit_map& units,c
const unit_map::const_iterator itor = units.find(locs[i]);
if(itor != units.end() &&
itor->second.type().illuminates() && itor->second.incapacitated() == false) {
if (itor->second.type().illuminates_filter().matches_filter(map.underlying_terrain(map[loc.x][loc.y]), status.get_time_of_day().lawful_bonus)) {
if (itor->second.type().illuminates_filter().matches_filter(map.underlying_union_terrain(map[loc.x][loc.y]), status.get_time_of_day().lawful_bonus)) {
lighten = maximum<int>(itor->second.type().illuminates() , lighten);
darken = minimum<int>(itor->second.type().illuminates() , darken);
}
@ -1703,7 +1703,7 @@ bool clear_shroud_unit(const gamemap& map,
const unit_map::const_iterator sighted = units.find(*it);
if(sighted != units.end() &&
(sighted->second.invisible(map.underlying_terrain(map[it->x][it->y]),status.get_time_of_day().lawful_bonus,*it,units,teams) == false
(sighted->second.invisible(map.underlying_union_terrain(map[it->x][it->y]),status.get_time_of_day().lawful_bonus,*it,units,teams) == false
|| teams[team].is_enemy(sighted->second.side()) == false)) {
if(seen_units == NULL || known_units == NULL) {
static const std::string sighted("sighted");
@ -1857,7 +1857,7 @@ size_t move_unit(display* disp, const game_data& gamedata,
const std::map<gamemap::location,unit>::const_iterator it = units.find(adjacent[i]);
if(it != units.end() && teams[u.side()-1].is_enemy(it->second.side()) &&
it->second.invisible(map.underlying_terrain(map[it->first.x][it->first.y]),
it->second.invisible(map.underlying_union_terrain(map[it->first.x][it->first.y]),
status.get_time_of_day().lawful_bonus,it->first,units,teams)) {
discovered_unit = true;
should_clear_stack = true;

View file

@ -640,7 +640,7 @@ void ai_interface::calculate_possible_moves(std::map<location,paths>& res, move_
}
//we can't see where invisible enemy units might move
if(enemy && un_it->second.invisible(info_.map.underlying_terrain(info_.map.get_terrain(un_it->first)),
if(enemy && un_it->second.invisible(info_.map.underlying_union_terrain(info_.map.get_terrain(un_it->first)),
info_.state.get_time_of_day().lawful_bonus,un_it->first,info_.units,info_.teams)) {
continue;
}

View file

@ -690,7 +690,7 @@ std::vector<ai::attack_analysis> ai::analyze_targets(
//attack anyone who is on the enemy side, and who is not invisible or turned to stone
if(current_team().is_enemy(j->second.side()) && j->second.stone() == false &&
j->second.invisible(map_.underlying_terrain(map_[j->first.x][j->first.y]),
j->second.invisible(map_.underlying_union_terrain(map_[j->first.x][j->first.y]),
state_.get_time_of_day().lawful_bonus,j->first,
units_,teams_) == false) {
location adjacent[6];

View file

@ -1193,7 +1193,7 @@ void display::draw_minimap(int x, int y, int w, int h)
for(unit_map::const_iterator u = units_.begin(); u != units_.end(); ++u) {
if(fogged(u->first.x,u->first.y) ||
(teams_[currentTeam_].is_enemy(u->second.side()) &&
u->second.invisible(map_.underlying_terrain(map_[u->first.x][u->first.y]),
u->second.invisible(map_.underlying_union_terrain(map_[u->first.x][u->first.y]),
status_.get_time_of_day().lawful_bonus,u->first,
units_,teams_))) {
continue;
@ -1331,7 +1331,7 @@ void display::draw_unit_on_tile(int x, int y, surface unit_image_override,
highlight_ratio = it->second.alpha();
}
if(u.invisible(map_.underlying_terrain(map_[x][y]),
if(u.invisible(map_.underlying_union_terrain(map_[x][y]),
status_.get_time_of_day().lawful_bonus,loc,
units_,teams_) &&
highlight_ratio > ftofxp(0.5)) {
@ -1366,7 +1366,7 @@ void display::draw_unit_on_tile(int x, int y, surface unit_image_override,
if(unit_image == NULL || fogged(x,y) ||
(teams_[currentTeam_].is_enemy(it->second.side()) &&
it->second.invisible(map_.underlying_terrain(map_[x][y]),
it->second.invisible(map_.underlying_union_terrain(map_[x][y]),
status_.get_time_of_day().lawful_bonus,loc,
units_,teams_))) {
return;

View file

@ -1207,7 +1207,7 @@ public:
if (terrain == gamemap::FOGGED || terrain == gamemap::VOID_TERRAIN)
continue;
const terrain_type& info = map->get_terrain_info(terrain);
if (!info.is_alias() && info.is_nonnull()) {
if (info.union_type().size() == 1 && info.union_type()[0] == info.letter() && info.is_nonnull()) {
std::vector<item> row;
const std::string& name = info.name();
const std::string id = info.id();
@ -1290,8 +1290,8 @@ struct terrain_topic_generator: topic_generator
virtual std::string operator()() const {
std::stringstream ss;
ss << "<img>src='terrain/" << type.symbol_image() << ".png'</img>\n\n";
if (type.is_alias()) {
const std::string aliased_terrains = type.type();
if (type.mvt_type().size() != 1 || type.mvt_type()[0] != type.letter()) {
const std::string aliased_terrains = type.mvt_type();
std::stringstream alias_ss;
for (std::string::const_iterator it = aliased_terrains.begin();
it != aliased_terrains.end(); it++) {
@ -1307,9 +1307,35 @@ struct terrain_topic_generator: topic_generator
utils::string_map sm;
sm["terrains"] = alias_ss.str();
ss << utils::interpolate_variables_into_string(
_("This terrain acts as $terrains for movement and defense purposes."), &sm);
if (aliased_terrains.size() > 1)
_("This terrain acts as $terrains for movement purposes."), &sm);
if (aliased_terrains.size() > 1 && aliased_terrains[0] != '-')
ss << " " << _("The terrain with the best modifier is chosen automatically.");
else
ss << " " << _("The terrain with the worst modifier is chosen automatically.");
ss << "\n\n";
}
if (type.def_type().size() != 1 || type.def_type()[0] != type.letter()) {
const std::string aliased_terrains = type.def_type();
std::stringstream alias_ss;
for (std::string::const_iterator it = aliased_terrains.begin();
it != aliased_terrains.end(); it++) {
const gamemap::TERRAIN t = *it;
const std::string &alias_name = map->get_terrain_info(t).name();
alias_ss << "<ref>text='" << escape(alias_name) << "' dst='"
<< escape(std::string("terrain_") + t) << "'</ref>";
if (it + 2 == aliased_terrains.end())
alias_ss << " " << _("or") << " ";
else if (it + 1 != aliased_terrains.end())
alias_ss << ", ";
}
utils::string_map sm;
sm["terrains"] = alias_ss.str();
ss << utils::interpolate_variables_into_string(
_("This terrain acts as $terrains for defense purposes."), &sm);
if (aliased_terrains.size() > 1 && aliased_terrains[0] != '-')
ss << " " << _("The terrain with the best modifier is chosen automatically.");
else
ss << " " << _("The terrain with the worst modifier is chosen automatically.");
ss << "\n\n";
}
if (type.is_keep())

View file

@ -44,7 +44,7 @@ std::ostream &operator<<(std::ostream &s, gamemap::location const &l) {
gamemap::location gamemap::location::null_location;
const std::string& gamemap::underlying_terrain(TERRAIN terrain) const
const std::string& gamemap::underlying_mvt_terrain(TERRAIN terrain) const
{
const std::map<TERRAIN,terrain_type>::const_iterator i = letterToTerrain_.find(terrain);
if(i == letterToTerrain_.end()) {
@ -53,7 +53,31 @@ const std::string& gamemap::underlying_terrain(TERRAIN terrain) const
res[0] = terrain;
return res;
} else {
return i->second.type();
return i->second.mvt_type();
}
}
const std::string& gamemap::underlying_def_terrain(TERRAIN terrain) const
{
const std::map<TERRAIN,terrain_type>::const_iterator i = letterToTerrain_.find(terrain);
if(i == letterToTerrain_.end()) {
static std::string res;
res.resize(1);
res[0] = terrain;
return res;
} else {
return i->second.def_type();
}
}
const std::string& gamemap::underlying_union_terrain(TERRAIN terrain) const
{
const std::map<TERRAIN,terrain_type>::const_iterator i = letterToTerrain_.find(terrain);
if(i == letterToTerrain_.end()) {
static std::string res;
res.resize(1);
res[0] = terrain;
return res;
} else {
return i->second.union_type();
}
}

View file

@ -47,7 +47,9 @@ public:
//is the name of the terrain for game-logic purposes. I.e. if the terrain
//is simply an alias, the underlying terrain name is the name of the
//terrain that it's aliased to
const std::string& underlying_terrain(TERRAIN terrain) const;
const std::string& underlying_mvt_terrain(TERRAIN terrain) const;
const std::string& underlying_def_terrain(TERRAIN terrain) const;
const std::string& underlying_union_terrain(TERRAIN terrain) const;
//exception thrown if the map file is not in the correct format.
struct incorrect_format_exception {
@ -96,8 +98,12 @@ public:
static location null_location;
};
const std::string& underlying_terrain(const location& loc) const
{ return underlying_terrain(get_terrain(loc)); }
const std::string& underlying_mvt_terrain(const location& loc) const
{ return underlying_mvt_terrain(get_terrain(loc)); }
const std::string& underlying_def_terrain(const location& loc) const
{ return underlying_def_terrain(get_terrain(loc)); }
const std::string& underlying_union_terrain(const location& loc) const
{ return underlying_union_terrain(get_terrain(loc)); }
bool is_village(TERRAIN terrain) const;
bool gives_healing(TERRAIN terrain) const;

View file

@ -1254,7 +1254,7 @@ bool turn_info::unit_in_cycle(unit_map::const_iterator it) const
{
if(it->second.side() == team_num_ && unit_can_move(it->first,units_,map_,teams_) && it->second.user_end_turn() == false && !gui_.fogged(it->first.x,it->first.y)) {
const bool is_enemy = current_team().is_enemy(int(gui_.viewing_team()+1));
return is_enemy == false || it->second.invisible(map_.underlying_terrain(it->first),status_.get_time_of_day().lawful_bonus,it->first,units_,teams_) == false;
return is_enemy == false || it->second.invisible(map_.underlying_union_terrain(it->first),status_.get_time_of_day().lawful_bonus,it->first,units_,teams_) == false;
}
return false;
@ -2722,7 +2722,7 @@ void turn_info::show_enemy_moves(bool ignore_units)
// Compute enemy movement positions
for(unit_map::iterator u = units_.begin(); u != units_.end(); ++u) {
bool invisible = u->second.invisible(map_.underlying_terrain(u->first),
bool invisible = u->second.invisible(map_.underlying_union_terrain(u->first),
status_.get_time_of_day()
.lawful_bonus,
u->first, units_, teams_);

View file

@ -133,7 +133,7 @@ report generate_report(TYPE type, const gamemap& map, const unit_map& units,
std::stringstream tooltip;
report res;
if(map.on_board(loc) && u->second.invisible(map.underlying_terrain(map[loc.x][loc.y]),status.get_time_of_day().lawful_bonus,loc,units,teams)) {
if(map.on_board(loc) && u->second.invisible(map.underlying_union_terrain(map[loc.x][loc.y]),status.get_time_of_day().lawful_bonus,loc,units,teams)) {
unit_status << "misc/invisible.png";
tooltip << _("invisible: ") << _("This unit is invisible. It cannot be seen or attacked by enemy units.");
res.add_image(unit_status,tooltip);
@ -369,7 +369,7 @@ Units cannot be killed by poison alone. The poison will not reduce it below 1 HP
break;
const gamemap::TERRAIN terrain = map.get_terrain(mouseover);
const std::string& underlying = map.underlying_terrain(terrain);
const std::string& underlying = map.underlying_union_terrain(terrain);
if(map.is_village(mouseover)) {
const int owner = village_owner(mouseover,teams)+1;

View file

@ -19,7 +19,7 @@
#include <cstdlib>
#include <iostream>
terrain_type::terrain_type() : symbol_image_("void"), letter_(' '), type_(" "),
terrain_type::terrain_type() : symbol_image_("void"), letter_(' '),mvt_type_(" "), def_type_(" "),
height_adjust_(0), submerge_(0.0),
heals_(false), village_(false), castle_(false), keep_(false)
{}
@ -38,13 +38,29 @@ terrain_type::terrain_type(const config& cfg)
letter_ = letter[0];
}
mvt_type_.resize(1);
mvt_type_[0] = letter_;
def_type_.resize(1);
def_type_[0] = letter_;
const std::string& alias = cfg["aliasof"];
if(alias.empty()) {
type_.resize(1);
type_[0] = letter_;
} else {
type_ = alias;
if(!alias.empty()) {
mvt_type_ = alias;
def_type_ = alias;
}
const std::string& mvt_alias = cfg["mvt_alias"];
if(!mvt_alias.empty()) {
mvt_type_ = mvt_alias;
}
const std::string& def_alias = cfg["def_alias"];
if(!def_alias.empty()) {
def_type_ = def_alias;
}
union_type_ = mvt_type_ +def_type_;
union_type_.erase(std::remove(union_type_.begin(),union_type_.end(),'-'),union_type_.end());
union_type_.erase(std::remove(union_type_.begin(),union_type_.end(),'+'),union_type_.end());
std::sort(union_type_.begin(),union_type_.end());
union_type_.erase(std::unique(union_type_.begin(),union_type_.end()),union_type_.end());
height_adjust_ = atoi(cfg["unit_height_adjust"].c_str());
submerge_ = atof(cfg["submerge"].c_str());
@ -82,21 +98,27 @@ bool terrain_type::is_nonnull() const
return (letter_ != 0) && (letter_ != ' ');
}
const std::string& terrain_type::type() const
const std::string& terrain_type::mvt_type() const
{
return type_;
return mvt_type_;
}
const std::string& terrain_type::def_type() const
{
return def_type_;
}
const std::string& terrain_type::union_type() const
{
return union_type_;
}
int terrain_type::light_modification() const
{
return light_modification_;
}
bool terrain_type::is_alias() const
{
return type_.size() != 1 || type_[0] != letter_;
}
int terrain_type::unit_height_adjust() const
{
return height_adjust_;

View file

@ -34,11 +34,12 @@ public:
char letter() const;
//the underlying type of the terrain
const std::string& type() const;
const std::string& mvt_type() const;
const std::string& def_type() const;
const std::string& union_type() const;
bool is_nonnull() const;
int light_modification() const;
bool is_alias() const;
int unit_height_adjust() const;
double unit_submerge() const;
@ -57,7 +58,9 @@ private:
//terrain type. The 'type' is a list of the 'underlying types'
//of the terrain. This may simply be the same as the letter.
char letter_;
std::string type_;
std::string mvt_type_;
std::string def_type_;
std::string union_type_;
int height_adjust_;

View file

@ -1532,7 +1532,7 @@ unit_map::iterator find_visible_unit(unit_map& units,
if(u != units.end()){
if(current_team.is_enemy(u->second.side()) &&
u->second.invisible(
map.underlying_terrain(map[loc.x][loc.y]),lawful_bonus,
map.underlying_union_terrain(map[loc.x][loc.y]),lawful_bonus,
loc,units,teams)) {
return units.end();
}
@ -1551,7 +1551,7 @@ unit_map::const_iterator find_visible_unit(const unit_map& units,
if(u != units.end()){
if(current_team.is_enemy(u->second.side()) &&
u->second.invisible(
map.underlying_terrain(map[loc.x][loc.y]),lawful_bonus,
map.underlying_union_terrain(map[loc.x][loc.y]),lawful_bonus,
loc,units,teams)) {
return units.end();
}

View file

@ -111,7 +111,7 @@ void move_unit_between(display& disp, const gamemap& map, const gamemap::locatio
const unsigned int start_time = SDL_GetTicks();
int mvt_time = SDL_GetTicks() -start_time;
while(mvt_time < total_mvt_time) {
u.set_walking(map.underlying_terrain(src_terrain),a.get_relative_dir(b),acceleration);
u.set_walking(map.underlying_mvt_terrain(src_terrain),a.get_relative_dir(b),acceleration);
surface image(image::get_image(u.image_loc()));
if (!face_left) {
image.assign(image::reverse_image(image));
@ -191,8 +191,8 @@ bool unit_visible_on_path(display& disp, const gamemap& map, const std::vector<g
{
for(size_t i = 0; i+1 < path.size(); ++i) {
const bool invisible = teams[u.side()-1].is_enemy(int(disp.viewing_team()+1)) &&
u.invisible(map.underlying_terrain(path[i]),tod.lawful_bonus,path[i],units,teams) &&
u.invisible(map.underlying_terrain(path[i+1]),tod.lawful_bonus,path[i+1],units,teams);
u.invisible(map.underlying_union_terrain(path[i]),tod.lawful_bonus,path[i],units,teams) &&
u.invisible(map.underlying_union_terrain(path[i+1]),tod.lawful_bonus,path[i+1],units,teams);
if(!invisible) {
return true;
}
@ -219,8 +219,8 @@ void move_unit(display& disp, const gamemap& map, const std::vector<gamemap::loc
invisible = false;
} else {
invisible = teams[u.side()-1].is_enemy(int(disp.viewing_team()+1)) &&
u.invisible(map.underlying_terrain(path[i]),tod.lawful_bonus,path[i],units,teams) &&
u.invisible(map.underlying_terrain(path[i+1]),tod.lawful_bonus,path[i+1],units,teams);
u.invisible(map.underlying_union_terrain(path[i]),tod.lawful_bonus,path[i],units,teams) &&
u.invisible(map.underlying_union_terrain(path[i+1]),tod.lawful_bonus,path[i+1],units,teams);
}
if(!invisible) {

View file

@ -299,23 +299,33 @@ int unit_movement_type::movement_cost(const gamemap& map,gamemap::TERRAIN terrai
}
//if this is an alias, then select the best of all underlying terrains
const std::string& underlying = map.underlying_terrain(terrain);
const std::string& underlying = map.underlying_mvt_terrain(terrain);
if(underlying.size() != 1 || underlying[0] != terrain) {
bool revert = (underlying[0] == '-'?true:false);
if(recurse_count >= 100) {
return impassable;
}
int min_value = impassable;
int ret_value = revert?0:impassable;
for(std::string::const_iterator i = underlying.begin(); i != underlying.end(); ++i) {
if(*i == '+') {
revert = false;
continue;
} else if(*i == '-') {
revert = true;
continue;
}
const int value = movement_cost(map,*i,recurse_count+1);
if(value < min_value) {
min_value = value;
if(value < ret_value && !revert) {
ret_value = value;
} else if(value > ret_value && revert) {
ret_value = value;
}
}
moveCosts_.insert(std::pair<gamemap::TERRAIN,int>(terrain,min_value));
moveCosts_.insert(std::pair<gamemap::TERRAIN,int>(terrain,ret_value));
return min_value;
return ret_value;
}
const config* movement_costs = cfg_.child("movement_costs");
@ -358,23 +368,36 @@ int unit_movement_type::defense_modifier(const gamemap& map,gamemap::TERRAIN ter
}
//if this is an alias, then select the best of all underlying terrains
const std::string& underlying = map.underlying_terrain(terrain);
const std::string& underlying = map.underlying_mvt_terrain(terrain);
if(underlying.size() != 1 || underlying[0] != terrain) {
bool revert = (underlying[0] == '-'?true:false);
if(recurse_count >= 100) {
return 100;
}
int min_value = 100;
int ret_value = revert?0:100;
for(std::string::const_iterator i = underlying.begin(); i != underlying.end(); ++i) {
if(*i == '+') {
revert = false;
continue;
} else if(*i == '-') {
revert = true;
continue;
}
const int value = defense_modifier(map,*i,recurse_count+1);
if(value < min_value) {
min_value = value;
if(value < ret_value && !revert) {
ret_value = value;
} else if(value > ret_value && revert) {
ret_value = value;
}
if(value < ret_value) {
ret_value = value;
}
}
defenseMods_.insert(std::pair<gamemap::TERRAIN,int>(terrain,min_value));
defenseMods_.insert(std::pair<gamemap::TERRAIN,int>(terrain,ret_value));
return min_value;
return ret_value;
}
int res = -1;