Add support for overlay terrains.

Converted bridges to use the new feature.
This commit is contained in:
Moritz Göbelbecker 2008-04-04 18:43:57 +00:00
parent 57b6e02b29
commit f88efeb810
14 changed files with 376 additions and 73 deletions

View File

@ -76,6 +76,7 @@ Version 1.5.0-svn:
* new [unit_side] theme element : this is the side of the current unit (flag)
* Preprocessor now logs when it encounters an undefined macro, and when it
fails opening a file.
* Add support for overlay terrains (terrains which can be placed above any base terrain)
* miscellaneous and bug fixes:
* Fixed error message for broken add-on campaigns (bug #11078)
* Refactored the in-game console and added help function (patch #1036)

View File

@ -97,6 +97,10 @@
name = "Martin Renold (maxy/martinxyz)"
comment = "performance and gui bug fixes"
[/entry]
[entry]
name = "Moritz Göbelbecker (mog)"
comment = "Work on the terrain engine"
[/entry]
[entry]
name = "Nicolas Weeger (Ryo)"
comment = "Python API"

View File

@ -55,89 +55,118 @@
# Bridges
#
# >>>>>>>> Shallow Water Bridge
[terrain]
symbol_image=bridge-n-s-tile
symbol_image=bridge-n-s
id=bridge
name= _ "Bridge"
string=Ww^Bw|
aliasof=Gt, Ww
string=^Bw|
aliasof=_bas, Gt
editor_group=bridge, water
[/terrain]
[terrain]
symbol_image=bridge-ne-sw-tile
symbol_image=bridge-ne-sw
id=bridgediag1
name= _ "Bridge"
string=Ww^Bw/
aliasof=Gt, Ww
string=^Bw/
aliasof=_bas, Gt
editor_group=bridge, water
[/terrain]
[terrain]
symbol_image=bridge-se-nw-tile
symbol_image=bridge-se-nw
id=bridgediag2
name= _ "Bridge"
string=Ww^Bw\
aliasof=Gt, Ww
string=^Bw\
aliasof=_bas, Gt
editor_group=bridge, water
[/terrain]
# >>>>>>>> Shallow Water Bridge
# [terrain]
# symbol_image=bridge-n-s-tile
# id=bridge
# name= _ "Bridge"
# string=Ww^Bw|
# aliasof=Gt, Ww
# editor_group=bridge, water
# [/terrain]
# [terrain]
# symbol_image=bridge-ne-sw-tile
# id=bridgediag1
# name= _ "Bridge"
# string=Ww^Bw/
# aliasof=Gt, Ww
# editor_group=bridge, water
# [/terrain]
# [terrain]
# symbol_image=bridge-se-nw-tile
# id=bridgediag2
# name= _ "Bridge"
# string=Ww^Bw\
# aliasof=Gt, Ww
# editor_group=bridge, water
# [/terrain]
# >>>>>>>> Deep Water Bridge
[terrain]
symbol_image=bridge-deep-n-s-tile
id=bridgedeep
name= _ "Bridge"
string=Wo^Bw|
aliasof=Gt, Wo
editor_group=bridge, water
[/terrain]
#[terrain]
# symbol_image=bridge-deep-n-s-tile
# id=bridgedeep
# name= _ "Bridge"
# string=Wo^Bw|
# aliasof=Gt, Wo
# editor_group=bridge, water
#[/terrain]
#
#[terrain]
# symbol_image=bridge-deep-ne-sw-tile
# id=bridgedeepdiag1
# name= _ "Bridge"
# string=Wo^Bw/
# aliasof=Gt, Wo
# editor_group=bridge, water
#[/terrain]
[terrain]
symbol_image=bridge-deep-ne-sw-tile
id=bridgedeepdiag1
name= _ "Bridge"
string=Wo^Bw/
aliasof=Gt, Wo
editor_group=bridge, water
[/terrain]
# [terrain]
# symbol_image=bridge-deep-se-nw-tile
# id=bridgedeepdiag2
# name= _ "Bridge"
# string=Wo^Bw\
# aliasof=Gt, Wo
# editor_group=bridge, water
# [/terrain]
[terrain]
symbol_image=bridge-deep-se-nw-tile
id=bridgedeepdiag2
name= _ "Bridge"
string=Wo^Bw\
aliasof=Gt, Wo
editor_group=bridge, water
[/terrain]
# # >>>>>>>> Swamp Water Bridge
# [terrain]
# symbol_image=bridge-swamp-n-s-tile
# id=bridgeswamp
# name= _ "Bridge"
# string=Ss^Bw|
# aliasof=Gt, Ss
# editor_group=bridge, water
# [/terrain]
# >>>>>>>> Swamp Water Bridge
[terrain]
symbol_image=bridge-swamp-n-s-tile
id=bridgeswamp
name= _ "Bridge"
string=Ss^Bw|
aliasof=Gt, Ss
editor_group=bridge, water
[/terrain]
# [terrain]
# symbol_image=bridge-swamp-ne-sw-tile
# id=bridgeswampdiag1
# name= _ "Bridge"
# string=Ss^Bw/
# aliasof=Gt, Ss
# editor_group=bridge, water
# [/terrain]
[terrain]
symbol_image=bridge-swamp-ne-sw-tile
id=bridgeswampdiag1
name= _ "Bridge"
string=Ss^Bw/
aliasof=Gt, Ss
editor_group=bridge, water
[/terrain]
[terrain]
symbol_image=bridge-swamp-se-nw-tile
id=bridgeswampdiag2
name= _ "Bridge"
string=Ss^Bw\
aliasof=Gt, Ss
editor_group=bridge, water
[/terrain]
# [terrain]
# symbol_image=bridge-swamp-se-nw-tile
# id=bridgeswampdiag2
# name= _ "Bridge"
# string=Ss^Bw\
# aliasof=Gt, Ss
# editor_group=bridge, water
# [/terrain]
# >>>>>>>> Chasm Bridge

View File

@ -224,6 +224,16 @@ void terrain_builder::rebuild_terrain(const gamemap::location &loc)
img_loc.add_frame(100,image::locator("terrain/" + filename + ".png"));
img_loc.start_animation(0, true);
btile.images_background.push_back(img_loc);
//Combine base and overlay image if neccessary
if(map_.get_terrain_info(map_.get_terrain(loc)).is_combined()) {
const std::string filename_ovl =
map_.get_terrain_info(map_.get_terrain(loc)).minimap_image_overlay();
animated<image::locator> img_loc_ovl;
img_loc_ovl.add_frame(100,image::locator("terrain/" + filename_ovl + ".png"));
img_loc_ovl.start_animation(0, true);
btile.images_background.push_back(img_loc_ovl);
}
}
}

View File

@ -863,7 +863,12 @@ void map_editor::perform_fill_hexes(std::set<gamemap::location> &fill_hexes,
for (it = fill_hexes.begin(); it != fill_hexes.end(); it++) {
if (map_.on_board(*it, true)) {
undo_action.add_terrain(map_.get_terrain(*it), terrain, *it);
map_.set_terrain(*it, terrain);
if (terrain.base == t_translation::NO_LAYER) {
map_.set_overlay(*it, terrain);
}
else {
map_.set_terrain(*it, terrain);
}
terrain_changed(*it);
}
}
@ -987,7 +992,12 @@ void map_editor::redo() {
for(std::map<gamemap::location, t_translation::t_terrain>::const_iterator it =
action.redo_terrains().begin();
it != action.redo_terrains().end(); ++it) {
map_.set_terrain(it->first, it->second);
if (it->second.base == t_translation::NO_LAYER) {
map_.set_overlay(it->first, it->second);
}
else {
map_.set_terrain(it->first, it->second);
}
terrain_changed(it->first);
}
}
@ -1204,7 +1214,12 @@ void map_editor::draw_terrain(const t_translation::t_terrain terrain,
const t_translation::t_terrain old_terrain = map_.get_terrain(*it);
if(terrain != old_terrain) {
undo_action.add_terrain(old_terrain, terrain, *it);
map_.set_terrain(*it, terrain);
if (terrain.base == t_translation::NO_LAYER) {
map_.set_overlay(*it, terrain);
}
else {
map_.set_terrain(*it, terrain);
}
// always rebuild localy to show the drawing progress
gui_.rebuild_terrain(*it);
gui_.invalidate(*it);

View File

@ -72,14 +72,20 @@ terrain_palette::terrain_palette(display &gui, const size_specs &sizes,
// The rest of the code assumes this is a valid pointer
assert(checked_group_btn_ != 0);
// Add the groups for all terrains to the map
// add the groups for all terrains to the map
t_translation::t_list::const_iterator t_itor = terrains_.begin();
for(; t_itor != terrains_.end(); ++t_itor) {
// Add the terrain to the requested groups
const std::vector<std::string>& key =
utils::split(map_.get_terrain_info(*t_itor).editor_group());
const terrain_type& t_info = map_.get_terrain_info(*t_itor);
for(std::vector<std::string>::const_iterator k_itor = key.begin();
// don't display terrains that were automatically created from base+overlay
if (t_info.is_combined())
continue;
// add the terrain to the requested groups
const std::vector<std::string>& key =
utils::split(t_info.editor_group());
for(std::vector<std::string>::const_iterator k_itor = key.begin();
k_itor != key.end(); ++k_itor)
{
terrain_map_[*k_itor].push_back(*t_itor);

View File

@ -338,7 +338,12 @@ void flood_fill(gamemap &map, const gamemap::location &start_loc,
if (log != NULL) {
log->push_back(std::make_pair(loc, terrain_to_fill));
}
map.set_terrain(loc, fill_with);
if (fill_with.base == t_translation::NO_LAYER) {
map.set_overlay(loc, fill_with);
}
else {
map.set_terrain(loc, fill_with);
}
}
}

View File

@ -412,9 +412,22 @@ void gamemap::read(const std::string& data)
// Is the terrain valid?
if(tcodeToTerrain_.count(tiles_[x][y]) == 0) {
ERR_CF << "Illegal character in map: (" << t_translation::write_terrain_code(tiles_[x][y])
<< ") '" << tiles_[x][y] << "'\n";
throw incorrect_format_exception("Illegal character found in map. The scenario cannot be loaded.");
const std::map<t_translation::t_terrain, terrain_type>::const_iterator base_iter =
tcodeToTerrain_.find(t_translation::t_terrain(tiles_[x][y].base, t_translation::NO_LAYER));
const std::map<t_translation::t_terrain, terrain_type>::const_iterator overlay_iter =
tcodeToTerrain_.find(t_translation::t_terrain(t_translation::NO_LAYER, tiles_[x][y].overlay));
if(base_iter == tcodeToTerrain_.end() || overlay_iter == tcodeToTerrain_.end()) {
ERR_CF << "Illegal character in map: (" << t_translation::write_terrain_code(tiles_[x][y])
<< ") '" << tiles_[x][y] << "'\n";
throw incorrect_format_exception("Illegal character found in map. The scenario cannot be loaded.");
}
terrain_type new_terrain(base_iter->second, overlay_iter->second);
terrainList_.push_back(new_terrain.number());
tcodeToTerrain_.insert(std::pair<t_translation::t_terrain, terrain_type>(
new_terrain.number(), new_terrain));
}
// Is it a village?
@ -742,6 +755,77 @@ void gamemap::set_terrain(const gamemap::location& loc, const t_translation::t_t
}
}
void gamemap::set_overlay(const gamemap::location& loc, const t_translation::t_terrain terrain)
{
if(!on_board(loc, true)) {
// off the map: ignore request
return;
}
//check if base only is a valid terrain
const std::map<t_translation::t_terrain, terrain_type>::const_iterator base_iter =
tcodeToTerrain_.find(t_translation::t_terrain(tiles_[loc.x + border_size_][loc.y + border_size_].base, t_translation::NO_LAYER));
//check if overlay only is a valid terrain
const std::map<t_translation::t_terrain, terrain_type>::const_iterator overlay_iter =
tcodeToTerrain_.find(t_translation::t_terrain(t_translation::NO_LAYER, terrain.overlay));
//check if base^overlay is already a valid terrain
const std::map<t_translation::t_terrain, terrain_type>::const_iterator all_iter =
tcodeToTerrain_.find(t_translation::t_terrain(tiles_[loc.x + border_size_][loc.y + border_size_].base, terrain.overlay));
// if neither, return
if((base_iter == tcodeToTerrain_.end() || overlay_iter == tcodeToTerrain_.end())
&& all_iter == tcodeToTerrain_.end()) {
return;
}
//Create new terrain
if (all_iter == tcodeToTerrain_.end()) {
terrain_type new_terrain(base_iter->second, overlay_iter->second);
terrainList_.push_back(new_terrain.number());
tcodeToTerrain_.insert(std::pair<t_translation::t_terrain, terrain_type>(
new_terrain.number(), new_terrain));
}
set_terrain(loc, t_translation::t_terrain(tiles_[loc.x + border_size_][loc.y + border_size_].base, terrain.overlay));
}
void gamemap::set_base(const gamemap::location& loc, const t_translation::t_terrain terrain)
{
if(!on_board(loc, true)) {
// off the map: ignore request
return;
}
//check if base only is a valid terrain
const std::map<t_translation::t_terrain, terrain_type>::const_iterator base_iter =
tcodeToTerrain_.find(t_translation::t_terrain(terrain.base, t_translation::NO_LAYER));
//check if overlay only is a valid terrain
const std::map<t_translation::t_terrain, terrain_type>::const_iterator overlay_iter =
tcodeToTerrain_.find(t_translation::t_terrain(t_translation::NO_LAYER, tiles_[loc.x + border_size_][loc.y + border_size_].overlay));
//check if base^overlay is already a valid terrain
const std::map<t_translation::t_terrain, terrain_type>::const_iterator all_iter =
tcodeToTerrain_.find(t_translation::t_terrain(terrain.base, tiles_[loc.x + border_size_][loc.y + border_size_].overlay));
// if neither, return
if((base_iter == tcodeToTerrain_.end() || overlay_iter == tcodeToTerrain_.end())
&& all_iter == tcodeToTerrain_.end()) {
return;
}
//Create new terrain
if (all_iter == tcodeToTerrain_.end()) {
terrain_type new_terrain(base_iter->second, overlay_iter->second);
terrainList_.push_back(new_terrain.number());
tcodeToTerrain_.insert(std::pair<t_translation::t_terrain, terrain_type>(
new_terrain.number(), new_terrain));
}
set_terrain(loc, t_translation::t_terrain(terrain.base, tiles_[loc.x + border_size_][loc.y + border_size_].overlay));
}
std::vector<gamemap::location> parse_location_range(const std::string& x, const std::string& y,
const gamemap *const map)
{

View File

@ -217,6 +217,16 @@ public:
//! Clobbers over the terrain at location 'loc', with the given terrain.
void set_terrain(const location& loc, const t_translation::t_terrain terrain);
//! clobbers over the overlay terrain at location 'loc', with the given terrain.
//! This method checks if the base and new overlay can be combined, or if
//! base+new overlay are already valid, if it isn't it will fail silently.
void set_overlay(const location& loc, const t_translation::t_terrain terrain);
//! clobbers over the base terrain at location 'loc', with the given terrain.
//! This method checks if the new base and old overlay can be combined, or if
//! new base+overlay are already valid, if it isn't it will fail silently.
void set_base(const location& loc, const t_translation::t_terrain terrain);
//! Returns a list of the frequencies of different terrain types on the map,
//! with terrain nearer the center getting weighted higher.
const std::map<t_translation::t_terrain, size_t>& get_weighted_terrain_frequencies() const;

View File

@ -88,6 +88,24 @@ surface getMinimap(int w, int h, const gamemap& map, const viewpoint* vw)
VALIDATE(false, msg);
}
//Compose images of base and overlay if neccessary
if(map.get_terrain_info(terrain).is_combined()) {
surface overlay(get_image("terrain/" + map.get_terrain_info(terrain).minimap_image_overlay() + ".png",image::HEXED));
if(overlay != 0) {
surface combined = create_compatible_surface(tile, tile->w, tile->h);
SDL_Rect r;
r.x = 0;
r.y = 0;
SDL_BlitSurface(tile, NULL, combined, &r);
r.x = (tile->w - overlay->w)/2;
r.y = (tile->h - overlay->h)/2;
SDL_BlitSurface(overlay, NULL, combined, &r);
tile = combined;
}
}
surf = surface(scale_surface_blended(tile,scale,scale));
VALIDATE(surf != NULL, _("Error creating or aquiring an image."));

View File

@ -30,6 +30,7 @@
terrain_type::terrain_type() :
minimap_image_("void"),
minimap_image_overlay_("void"),
editor_image_("void"),
id_(),
name_(),
@ -48,11 +49,14 @@ terrain_type::terrain_type() :
editor_group_(),
village_(false),
castle_(false),
keep_(false)
keep_(false),
overlay_(false),
combined_(false)
{}
terrain_type::terrain_type(const config& cfg) :
minimap_image_(cfg["symbol_image"]),
minimap_image_overlay_("void"),
editor_image_(cfg["editor_image"]),
id_(cfg["id"]),
name_(cfg["name"]),
@ -92,6 +96,9 @@ terrain_type::terrain_type(const config& cfg) :
editor_image_ = minimap_image_;
}
combined_ = false;
overlay_ = (number_.base == t_translation::NO_LAYER) ? true : false;
mvt_type_.push_back(number_);
def_type_.push_back(number_);
const t_translation::t_list& alias = t_translation::read_list(cfg["aliasof"]);
@ -152,6 +159,69 @@ terrain_type::terrain_type(const config& cfg) :
}
}
terrain_type::terrain_type(const terrain_type& base, const terrain_type& overlay) :
overlay_(false),
combined_(true)
{
number_ = t_translation::t_terrain(base.number_.base, overlay.number_.overlay);
minimap_image_ = base.minimap_image_;
minimap_image_overlay_ = overlay.minimap_image_;
editor_image_ = overlay.editor_image_;
name_ = overlay.name_;
id_ = base.id_+"^"+overlay.id_;
mvt_type_ = overlay.mvt_type_;
def_type_ = overlay.def_type_;
merge_alias_lists(mvt_type_, base.mvt_type_);
merge_alias_lists(def_type_, base.def_type_);
union_type_ = mvt_type_;
union_type_.insert( union_type_.end(), def_type_.begin(), def_type_.end() );
// remove + and -
union_type_.erase(std::remove(union_type_.begin(), union_type_.end(),
t_translation::MINUS), union_type_.end());
union_type_.erase(std::remove(union_type_.begin(), union_type_.end(),
t_translation::PLUS), union_type_.end());
// remove doubles
std::sort(union_type_.begin(),union_type_.end());
union_type_.erase(std::unique(union_type_.begin(), union_type_.end()), union_type_.end());
height_adjust_ = overlay.height_adjust_;
submerge_ = overlay.submerge_;
light_modification_ = base.light_modification_ + overlay.light_modification_;
heals_ = maximum<int>(base.heals_, overlay.heals_);
village_ = base.village_ | overlay.village_;
castle_ = base.castle_ | overlay.castle_;
keep_ = base.keep_ | overlay.keep_;
//mouse over message are only shown on villages
if(base.village_) {
income_description_ = base.income_description_;
income_description_ally_ = base.income_description_ally_;
income_description_enemy_ = base.income_description_enemy_;
income_description_own_ = base.income_description_own_;
}
else if (overlay.village_) {
income_description_ = overlay.income_description_;
income_description_ally_ = overlay.income_description_ally_;
income_description_enemy_ = overlay.income_description_enemy_;
income_description_own_ = overlay.income_description_own_;
}
editor_group_ = "";
}
void create_terrain_maps(const std::vector<config*>& cfgs,
t_translation::t_list& terrain_list,
std::map<t_translation::t_terrain, terrain_type>& letter_to_terrain)
@ -164,3 +234,41 @@ void create_terrain_maps(const std::vector<config*>& cfgs,
terrain.number(),terrain));
}
}
void merge_alias_lists(t_translation::t_list& first, const t_translation::t_list& second)
{
// Insert second vector into first when the terrain _ref^base is encountered
bool revert = (first.front() == t_translation::MINUS ? true : false);
t_translation::t_list::iterator i;
for(i = first.begin(); i != first.end(); i++) {
if(*i == t_translation::PLUS) {
revert = false;
continue;
} else if(*i == t_translation::MINUS) {
revert = true;
continue;
}
if(*i == t_translation::BASE) {
t_translation::t_list::iterator insert_it = first.erase(i);
//if we are in reverse mode, insert PLUS before and MINUS after the base list
//so calculation of base aliases will work normal
if(revert) {
insert_it = first.insert(insert_it, t_translation::PLUS);
insert_it++;
insert_it = first.insert(insert_it, t_translation::MINUS);
}
else {
//else insert PLUS after the base aliases to restore previous "reverse state"
insert_it = first.insert(insert_it, t_translation::PLUS);
}
first.insert(insert_it, second.begin(), second.end());
break;
}
}
}

View File

@ -28,8 +28,10 @@ public:
terrain_type();
terrain_type(const config& cfg);
terrain_type(const terrain_type& base, const terrain_type& overlay);
const std::string& minimap_image() const { return minimap_image_; }
const std::string& minimap_image_overlay() const { return minimap_image_overlay_; }
const std::string& editor_image() const { return editor_image_; }
const t_string& name() const { return name_; }
const std::string& id() const { return id_; }
@ -63,9 +65,13 @@ public:
const std::string& editor_group() const { return editor_group_; }
bool is_overlay() const { return overlay_; }
bool is_combined() const { return combined_; }
private:
//! The image used in the minimap
std::string minimap_image_;
std::string minimap_image_overlay_;
//! The image used in the editor pallete if not defined in WML it will
//! be initialized with the value of minimap_image_
@ -96,9 +102,14 @@ private:
std::string editor_group_;
bool village_, castle_, keep_;
bool overlay_, combined_;
};
void create_terrain_maps(const std::vector<config*>& cfgs,
t_translation::t_list& terrain_list,
std::map<t_translation::t_terrain, terrain_type>& letter_to_terrain);
void merge_alias_lists(t_translation::t_list& first, const t_translation::t_list& second);
#endif

View File

@ -127,6 +127,7 @@ const t_terrain PLUS = string_to_number_("+");
const t_terrain MINUS = string_to_number_("-");
const t_terrain NOT = string_to_number_("!");
const t_terrain STAR = string_to_number_("*");
const t_terrain BASE = string_to_number_("_bas");
/***************************************************************************************/

View File

@ -128,6 +128,7 @@ namespace t_translation {
extern const t_terrain MINUS; // -
extern const t_terrain NOT; // !
extern const t_terrain STAR; // *
extern const t_terrain BASE; // references the base terrain in movement/defence aliases
/**
* Reads a single terrain from a string.