map.cpp

Go to the documentation of this file.
00001 /* $Id: map.cpp 53491 2012-03-10 13:29:36Z mordante $ */
00002 /*
00003    Copyright (C) 2003 - 2012 by David White <dave@whitevine.net>
00004    Part of the Battle for Wesnoth Project http://www.wesnoth.org/
00005 
00006    This program is free software; you can redistribute it and/or modify
00007    it under the terms of the GNU General Public License as published by
00008    the Free Software Foundation; either version 2 of the License, or
00009    (at your option) any later version.
00010    This program is distributed in the hope that it will be useful,
00011    but WITHOUT ANY WARRANTY.
00012 
00013    See the COPYING file for more details.
00014 */
00015 
00016 /**
00017  * @file
00018  * Routines related to game-maps, terrain, locations, directions. etc.
00019  */
00020 
00021 #include "global.hpp"
00022 
00023 #include <cassert>
00024 
00025 #include "map.hpp"
00026 
00027 #include "formula_string_utils.hpp"
00028 #include "gettext.hpp"
00029 #include "log.hpp"
00030 #include "map_exception.hpp"
00031 #include "serialization/parser.hpp"
00032 #include "util.hpp"
00033 #include "wml_exception.hpp"
00034 
00035 static lg::log_domain log_config("config");
00036 #define ERR_CF LOG_STREAM(err, log_config)
00037 #define LOG_G LOG_STREAM(info, lg::general)
00038 #define DBG_G LOG_STREAM(debug, lg::general)
00039 
00040 const gamemap::tborder gamemap::default_border = gamemap::SINGLE_TILE_BORDER;
00041 
00042 const t_translation::t_list& gamemap::underlying_mvt_terrain(t_translation::t_terrain terrain) const
00043 {
00044     const std::map<t_translation::t_terrain,terrain_type>::const_iterator i =
00045         tcodeToTerrain_.find(terrain);
00046 
00047     if(i == tcodeToTerrain_.end()) {
00048         static t_translation::t_list result(1);
00049         result[0] = terrain;
00050         return result;
00051     } else {
00052         return i->second.mvt_type();
00053     }
00054 }
00055 
00056 const t_translation::t_list& gamemap::underlying_def_terrain(t_translation::t_terrain terrain) const
00057 {
00058     const std::map<t_translation::t_terrain, terrain_type>::const_iterator i =
00059         tcodeToTerrain_.find(terrain);
00060 
00061     if(i == tcodeToTerrain_.end()) {
00062         static t_translation::t_list result(1);
00063         result[0] = terrain;
00064         return result;
00065     } else {
00066         return i->second.def_type();
00067     }
00068 }
00069 
00070 const t_translation::t_list& gamemap::underlying_union_terrain(t_translation::t_terrain terrain) const
00071 {
00072     const std::map<t_translation::t_terrain,terrain_type>::const_iterator i =
00073         tcodeToTerrain_.find(terrain);
00074 
00075     if(i == tcodeToTerrain_.end()) {
00076         static t_translation::t_list result(1);
00077         result[0] = terrain;
00078         return result;
00079     } else {
00080         return i->second.union_type();
00081     }
00082 }
00083 
00084 
00085 
00086 std::string gamemap::get_terrain_string(const t_translation::t_terrain& terrain) const
00087 {
00088     std::string str =
00089         get_terrain_info(terrain).description();
00090 
00091     str += get_underlying_terrain_string(terrain);
00092 
00093     return str;
00094 }
00095 
00096 std::string gamemap::get_terrain_editor_string(const t_translation::t_terrain& terrain) const
00097 {
00098     std::string str =
00099         get_terrain_info(terrain).editor_name();
00100     const std::string desc =
00101         get_terrain_info(terrain).description();
00102 
00103     if(str != desc) {
00104         str += "/";
00105         str += desc;
00106     }
00107 
00108     str += get_underlying_terrain_string(terrain);
00109 
00110     return str;
00111 }
00112 
00113 std::string gamemap::get_underlying_terrain_string(const t_translation::t_terrain& terrain) const
00114 {
00115     std::string str;
00116 
00117     const t_translation::t_list& underlying = underlying_union_terrain(terrain);
00118     assert(!underlying.empty());
00119 
00120     if(underlying.size() > 1 || underlying[0] != terrain) {
00121         str += " (";
00122         t_translation::t_list::const_iterator i = underlying.begin();
00123         str += get_terrain_info(*i).name();
00124         while (++i != underlying.end()) {
00125             str += ", " + get_terrain_info(*i).name();
00126         }
00127         str += ")";
00128     }
00129 
00130     return str;
00131 }
00132 
00133 void gamemap::write_terrain(const map_location &loc, config& cfg) const
00134 {
00135     cfg["terrain"] = t_translation::write_terrain_code(get_terrain(loc));
00136 }
00137 
00138 gamemap::gamemap(const config& cfg, const std::string& data):
00139         tiles_(1),
00140         terrainList_(),
00141         tcodeToTerrain_(),
00142         villages_(),
00143         borderCache_(),
00144         terrainFrequencyCache_(),
00145         w_(-1),
00146         h_(-1),
00147         total_width_(0),
00148         total_height_(0),
00149         border_size_(gamemap::SINGLE_TILE_BORDER),
00150         usage_(IS_MAP)
00151 {
00152     DBG_G << "loading map: '" << data << "'\n";
00153     const config::const_child_itors &terrains = cfg.child_range("terrain_type");
00154     create_terrain_maps(terrains, terrainList_, tcodeToTerrain_);
00155 
00156     read(data);
00157 }
00158 
00159 gamemap::gamemap(const config& cfg, const config& level):
00160         tiles_(1),
00161         terrainList_(),
00162         tcodeToTerrain_(),
00163         villages_(),
00164         borderCache_(),
00165         terrainFrequencyCache_(),
00166         w_(-1),
00167         h_(-1),
00168         total_width_(0),
00169         total_height_(0),
00170         border_size_(gamemap::SINGLE_TILE_BORDER),
00171         usage_(IS_MAP)
00172 {
00173     DBG_G << "loading map: '" << level.debug() << "'\n";
00174     const config::const_child_itors &terrains = cfg.child_range("terrain_type");
00175     create_terrain_maps(terrains, terrainList_, tcodeToTerrain_);
00176 
00177     const config& map_child = level.child_or_empty("map");
00178 
00179     if (map_child.empty()) {
00180         const std::string& map_data = level["map_data"];
00181         if (!map_data.empty()) {
00182             read(map_data);
00183         } else {
00184             w_ = 0;
00185             h_ = 0;
00186             total_width_ = 0;
00187             total_height_ = 0;
00188         }
00189     } else {
00190         read(map_child["data"], true, map_child["border_size"], map_child["usage"]);
00191     }
00192 }
00193 
00194 gamemap::~gamemap()
00195 {
00196 }
00197 
00198 void gamemap::read(const std::string& data, const bool allow_invalid, int border_size, std::string usage) {
00199 
00200     // Initial stuff
00201     border_size_ = border_size;
00202     set_usage(usage);
00203     tiles_.clear();
00204     villages_.clear();
00205     std::fill(startingPositions_, startingPositions_ +
00206         sizeof(startingPositions_) / sizeof(*startingPositions_), map_location());
00207     std::map<int, t_translation::coordinate> starting_positions;
00208 
00209     if(data.empty()) {
00210         w_ = 0;
00211         h_ = 0;
00212         total_width_ = 0;
00213         total_height_ = 0;
00214         if(allow_invalid) return;
00215     }
00216 
00217     int offset = read_header(data);
00218 
00219     const std::string& data_only = std::string(data, offset);
00220 
00221     try {
00222         tiles_ = t_translation::read_game_map(data_only, starting_positions);
00223 
00224     } catch(t_translation::error& e) {
00225         // We re-throw the error but as map error.
00226         // Since all codepaths test for this, it's the least work.
00227         throw incorrect_map_format_error(e.message);
00228     }
00229 
00230     // Convert the starting positions to the array
00231     std::map<int, t_translation::coordinate>::const_iterator itor =
00232         starting_positions.begin();
00233 
00234     for(; itor != starting_positions.end(); ++itor) {
00235 
00236         // Check for valid position,
00237         // the first valid position is 1,
00238         // so the offset 0 in the array is never used.
00239         if(itor->first < 1 || itor->first >= MAX_PLAYERS+1) {
00240             std::stringstream ss;
00241             ss << "Starting position " << itor->first << " out of range\n";
00242             ERR_CF << ss.str();
00243             ss << "The map cannot be loaded.";
00244             throw incorrect_map_format_error(ss.str().c_str());
00245         }
00246 
00247         // Add to the starting position array
00248         startingPositions_[itor->first] = map_location(itor->second.x - 1, itor->second.y - 1);
00249     }
00250 
00251     // Post processing on the map
00252     total_width_ = tiles_.size();
00253     total_height_ = total_width_ > 0 ? tiles_[0].size() : 0;
00254     w_ = total_width_ - 2 * border_size_;
00255     h_ = total_height_ - 2 * border_size_;
00256     //Disabled since there are callcases which pass along a valid map header but empty
00257     //map data. Still, loading (and actually applying) an empty map causes problems later on.
00258     //Other callcases which need to load a dummy map use completely empty data :(.
00259     //VALIDATE((w_ >= 1 && h_ >= 1), "A map needs at least 1 tile, the map cannot be loaded.");
00260 
00261     for(int x = 0; x < total_width_; ++x) {
00262         for(int y = 0; y < total_height_; ++y) {
00263 
00264             // Is the terrain valid?
00265             if(tcodeToTerrain_.count(tiles_[x][y]) == 0) {
00266                 if(!try_merge_terrains(tiles_[x][y])) {
00267                     std::stringstream ss;
00268                     ss << "Illegal tile in map: (" << t_translation::write_terrain_code(tiles_[x][y])
00269                            << ") '" << tiles_[x][y] << "'\n";
00270                     ERR_CF << ss.str();
00271                     ss << "The map cannot be loaded.";
00272                     throw incorrect_map_format_error(ss.str().c_str());
00273                 }
00274             }
00275 
00276             // Is it a village?
00277             if(x >= border_size_ && y >= border_size_
00278                     && x < total_width_-border_size_  && y < total_height_-border_size_
00279                     && is_village(tiles_[x][y])) {
00280                 villages_.push_back(map_location(x-border_size_, y-border_size_));
00281             }
00282         }
00283     }
00284 }
00285 
00286 void gamemap::set_usage(const std::string& usage)
00287 {
00288     utils::string_map symbols;
00289     symbols["border_size_key"] = "border_size";
00290     symbols["usage_key"] = "usage";
00291     symbols["usage_val"] = usage;
00292     const std::string msg = "'$border_size_key|' should be "
00293         "'$border_size_val|' when '$usage_key| = $usage_val|'";
00294 
00295     if(usage == "map") {
00296         usage_ = IS_MAP;
00297         symbols["border_size_val"] = "1";
00298         VALIDATE(border_size_ == 1, vgettext(msg.c_str(), symbols));
00299     } else if(usage == "mask") {
00300         usage_ = IS_MASK;
00301         symbols["border_size_val"] = "0";
00302         VALIDATE(border_size_ == 0, vgettext(msg.c_str(), symbols));
00303     } else if(usage == "") {
00304         throw incorrect_map_format_error("Map has a header but no usage");
00305     } else {
00306         std::string msg = "Map has a header but an unknown usage:" + usage;
00307         throw incorrect_map_format_error(msg);
00308     }
00309 }
00310 
00311 int gamemap::read_header(const std::string& data)
00312 {
00313     // Test whether there is a header section
00314     size_t header_offset = data.find("\n\n");
00315     if(header_offset == std::string::npos) {
00316         // For some reason Windows will fail to load a file with \r\n
00317         // lineending properly no problems on Linux with those files.
00318         // This workaround fixes the problem the copy later will copy
00319         // the second \r\n to the map, but that's no problem.
00320         header_offset = data.find("\r\n\r\n");
00321     }
00322     const size_t comma_offset = data.find(",");
00323     // The header shouldn't contain commas, so if the comma is found
00324     // before the header, we hit a \n\n inside or after a map.
00325     // This is no header, so don't parse it as it would be.
00326 
00327     if (!(!(header_offset == std::string::npos || comma_offset < header_offset)))
00328         return 0;
00329 
00330     std::string header_str(std::string(data, 0, header_offset + 1));
00331     config header;
00332 	::read(header, header_str);
00333 
00334     border_size_ = header["border_size"];
00335     set_usage(header["usage"]);
00336 
00337     return header_offset + 2;
00338 }
00339 
00340 
00341 void gamemap::write(config& cfg) const
00342 {
00343     // Convert the starting positions to a map
00344     std::map<int, t_translation::coordinate> starting_positions;
00345     for (int i = 0; i < MAX_PLAYERS + 1; ++i)
00346     {
00347         if (!on_board(startingPositions_[i])) continue;
00348         t_translation::coordinate position(
00349                   startingPositions_[i].x + border_size_
00350                 , startingPositions_[i].y + border_size_);
00351         starting_positions[i] = position;
00352     }
00353 
00354     cfg["border_size"] = border_size_;
00355     cfg["usage"] = (usage_ == IS_MAP ? "map" : "mask");
00356 
00357     std::ostringstream s;
00358     s << t_translation::write_game_map(tiles_, starting_positions);
00359     cfg["data"] = s.str();
00360 }
00361 
00362 void gamemap::overlay(const gamemap& m, const config& rules_cfg, int xpos, int ypos, bool border)
00363 {
00364     const config::const_child_itors &rules = rules_cfg.child_range("rule");
00365     int actual_border = (m.border_size() == border_size()) && border ? border_size() : 0;
00366 
00367     const int xstart = std::max<int>(-actual_border, -xpos - actual_border);
00368     const int ystart = std::max<int>(-actual_border, -ypos - actual_border - ((xpos & 1) ? 1 : 0));
00369     const int xend = std::min<int>(m.w() + actual_border, w() + actual_border - xpos);
00370     const int yend = std::min<int>(m.h() + actual_border, h() + actual_border - ypos);
00371     for(int x1 = xstart; x1 < xend; ++x1) {
00372         for(int y1 = ystart; y1 < yend; ++y1) {
00373             const int x2 = x1 + xpos;
00374             const int y2 = y1 + ypos +
00375                 ((xpos & 1) && (x1 & 1) ? 1 : 0);
00376 
00377             const t_translation::t_terrain t = m[x1][y1 + m.border_size_];
00378             const t_translation::t_terrain current = (*this)[x2][y2 + border_size_];
00379 
00380             if(t == t_translation::FOGGED || t == t_translation::VOID_TERRAIN) {
00381                 continue;
00382             }
00383 
00384             // See if there is a matching rule
00385             config::const_child_iterator rule = rules.first;
00386             for( ; rule != rules.second; ++rule)
00387             {
00388                 static const std::string src_key = "old", src_not_key = "old_not",
00389                                          dst_key = "new", dst_not_key = "new_not";
00390                 const config &cfg = *rule;
00391                 const t_translation::t_list& src = t_translation::read_list(cfg[src_key]);
00392 
00393                 if(!src.empty() && t_translation::terrain_matches(current, src) == false) {
00394                     continue;
00395                 }
00396 
00397                 const t_translation::t_list& src_not = t_translation::read_list(cfg[src_not_key]);
00398 
00399                 if(!src_not.empty() && t_translation::terrain_matches(current, src_not)) {
00400                     continue;
00401                 }
00402 
00403                 const t_translation::t_list& dst = t_translation::read_list(cfg[dst_key]);
00404 
00405                 if(!dst.empty() && t_translation::terrain_matches(t, dst) == false) {
00406                     continue;
00407                 }
00408 
00409                 const t_translation::t_list& dst_not = t_translation::read_list(cfg[dst_not_key]);
00410 
00411                 if(!dst_not.empty() && t_translation::terrain_matches(t, dst_not)) {
00412                     continue;
00413                 }
00414 
00415                 break;
00416             }
00417 
00418 
00419             if (rule != rules.second)
00420             {
00421                 const config &cfg = *rule;
00422                 const t_translation::t_list& terrain = t_translation::read_list(cfg["terrain"]);
00423 
00424                 tmerge_mode mode = BOTH;
00425                 if (cfg["layer"] == "base") {
00426                     mode = BASE;
00427                 }
00428                 else if (cfg["layer"] == "overlay") {
00429                     mode = OVERLAY;
00430                 }
00431 
00432                 t_translation::t_terrain new_terrain = t;
00433                 if(!terrain.empty()) {
00434                     new_terrain = terrain[0];
00435                 }
00436 
00437                 if (!cfg["use_old"].to_bool()) {
00438                     set_terrain(map_location(x2, y2), new_terrain, mode, cfg["replace_if_failed"].to_bool());
00439                 }
00440 
00441             } else {
00442                 set_terrain(map_location(x2,y2),t);
00443             }
00444         }
00445     }
00446 
00447     for(const map_location* pos = m.startingPositions_;
00448             pos != m.startingPositions_ + sizeof(m.startingPositions_)/sizeof(*m.startingPositions_);
00449             ++pos) {
00450 
00451         if(pos->valid()) {
00452             startingPositions_[pos - m.startingPositions_] = *pos;
00453         }
00454     }
00455 }
00456 
00457 t_translation::t_terrain gamemap::get_terrain(const map_location& loc) const
00458 {
00459 
00460     if(on_board_with_border(loc)) {
00461         return tiles_[loc.x + border_size_][loc.y + border_size_];
00462     }
00463 
00464     const std::map<map_location, t_translation::t_terrain>::const_iterator itor = borderCache_.find(loc);
00465     if(itor != borderCache_.end())
00466         return itor->second;
00467 
00468     // If not on the board, decide based on what surrounding terrain is
00469     t_translation::t_terrain items[6];
00470     int number_of_items = 0;
00471 
00472     map_location adj[6];
00473     get_adjacent_tiles(loc,adj);
00474     for(int n = 0; n != 6; ++n) {
00475         if(on_board(adj[n])) {
00476             items[number_of_items] = tiles_[adj[n].x][adj[n].y];
00477             ++number_of_items;
00478         } else {
00479             // If the terrain is off map but already in the border cache,
00480             // this will be used to determine the terrain.
00481             // This avoids glitches
00482             // * on map with an even width in the top right corner
00483             // * on map with an odd height in the bottom left corner.
00484             // It might also change the result on other map and become random,
00485             // but the border tiles will be determined in the future, so then
00486             // this will no longer be used in the game
00487             // (The editor will use this feature to expand maps in a better way).
00488             std::map<map_location, t_translation::t_terrain>::const_iterator itor =
00489                 borderCache_.find(adj[n]);
00490 
00491             // Only add if it is in the cache and a valid terrain
00492             if(itor != borderCache_.end() &&
00493                     itor->second != t_translation::NONE_TERRAIN)  {
00494 
00495                 items[number_of_items] = itor->second;
00496                 ++number_of_items;
00497             }
00498         }
00499 
00500     }
00501 
00502     // Count all the terrain types found,
00503     // and see which one is the most common, and use it.
00504     t_translation::t_terrain used_terrain;
00505     int terrain_count = 0;
00506     for(int i = 0; i != number_of_items; ++i) {
00507         if(items[i] != used_terrain && !is_village(items[i]) && !is_keep(items[i])) {
00508             const int c = std::count(items+i+1,items+number_of_items,items[i]) + 1;
00509             if(c > terrain_count) {
00510                 used_terrain = items[i];
00511                 terrain_count = c;
00512             }
00513         }
00514     }
00515 
00516     borderCache_.insert(std::pair<map_location, t_translation::t_terrain>(loc,used_terrain));
00517     return used_terrain;
00518 
00519 }
00520 
00521 const map_location& gamemap::starting_position(int n) const
00522 {
00523     if(size_t(n) < sizeof(startingPositions_)/sizeof(*startingPositions_)) {
00524         return startingPositions_[n];
00525     } else {
00526         static const map_location null_loc;
00527         return null_loc;
00528     }
00529 }
00530 
00531 int gamemap::num_valid_starting_positions() const
00532 {
00533     const int res = is_starting_position(map_location());
00534     if(res == -1)
00535         return num_starting_positions()-1;
00536     else
00537         return res;
00538 }
00539 
00540 int gamemap::is_starting_position(const map_location& loc) const
00541 {
00542     const map_location* const beg = startingPositions_+1;
00543     const map_location* const end = startingPositions_+num_starting_positions();
00544     const map_location* const pos = std::find(beg,end,loc);
00545 
00546     return pos == end ? -1 : pos - beg;
00547 }
00548 
00549 void gamemap::set_starting_position(int side, const map_location& loc)
00550 {
00551     if(side >= 0 && side < num_starting_positions()) {
00552         startingPositions_[side] = loc;
00553     }
00554 }
00555 
00556 bool gamemap::on_board(const map_location& loc) const
00557 {
00558     return loc.valid() && loc.x < w_ && loc.y < h_;
00559 }
00560 
00561 bool gamemap::on_board_with_border(const map_location& loc) const
00562 {
00563     if(tiles_.empty()) {
00564         return false;
00565     } else {
00566         return loc.x >= (0 - border_size_) && loc.x < (w_ + border_size_) &&
00567             loc.y >= (0 - border_size_) && loc.y < (h_ + border_size_);
00568     }
00569 }
00570 
00571 const terrain_type& gamemap::get_terrain_info(const t_translation::t_terrain terrain) const
00572 {
00573     static const terrain_type default_terrain;
00574     const std::map<t_translation::t_terrain,terrain_type>::const_iterator i =
00575         tcodeToTerrain_.find(terrain);
00576 
00577     if(i != tcodeToTerrain_.end())
00578         return i->second;
00579     else
00580         return default_terrain;
00581 }
00582 
00583 void gamemap::set_terrain(const map_location& loc, const t_translation::t_terrain terrain, const tmerge_mode mode, bool replace_if_failed) {
00584     if(!on_board_with_border(loc)) {
00585         // off the map: ignore request
00586         return;
00587     }
00588 
00589     t_translation::t_terrain new_terrain = merge_terrains(get_terrain(loc), terrain, mode, replace_if_failed);
00590 
00591     if(new_terrain == t_translation::NONE_TERRAIN) {
00592         return;
00593     }
00594 
00595     if(on_board(loc)) {
00596         const bool old_village = is_village(loc);
00597         const bool new_village = is_village(new_terrain);
00598 
00599         if(old_village && !new_village) {
00600             villages_.erase(std::remove(villages_.begin(),villages_.end(),loc),villages_.end());
00601         } else if(!old_village && new_village) {
00602             villages_.push_back(loc);
00603         }
00604     }
00605 
00606     tiles_[loc.x + border_size_][loc.y + border_size_] = new_terrain;
00607 
00608     // Update the off-map autogenerated tiles
00609     map_location adj[6];
00610     get_adjacent_tiles(loc,adj);
00611 
00612     for(int n = 0; n < 6; ++n) {
00613         remove_from_border_cache(adj[n]);
00614     }
00615 }
00616 
00617 const std::map<t_translation::t_terrain, size_t>& gamemap::get_weighted_terrain_frequencies() const
00618 {
00619     if(terrainFrequencyCache_.empty() == false) {
00620         return terrainFrequencyCache_;
00621     }
00622 
00623     const map_location center(w()/2,h()/2);
00624 
00625     const size_t furthest_distance = distance_between(map_location(0,0),center);
00626 
00627     const size_t weight_at_edge = 100;
00628     const size_t additional_weight_at_center = 200;
00629 
00630     for(size_t i = 0; i != size_t(w()); ++i) {
00631         for(size_t j = 0; j != size_t(h()); ++j) {
00632             const size_t distance = distance_between(map_location(i,j),center);
00633             terrainFrequencyCache_[(*this)[i][j]] += weight_at_edge +
00634                 (furthest_distance-distance)*additional_weight_at_center;
00635         }
00636     }
00637 
00638     return terrainFrequencyCache_;
00639 }
00640 
00641 bool gamemap::try_merge_terrains(const t_translation::t_terrain terrain) {
00642 
00643     if(tcodeToTerrain_.count(terrain) == 0) {
00644         const std::map<t_translation::t_terrain, terrain_type>::const_iterator base_iter =
00645             tcodeToTerrain_.find(t_translation::t_terrain(terrain.base, t_translation::NO_LAYER));
00646         const std::map<t_translation::t_terrain, terrain_type>::const_iterator overlay_iter =
00647             tcodeToTerrain_.find(t_translation::t_terrain(t_translation::NO_LAYER, terrain.overlay));
00648 
00649         if(base_iter == tcodeToTerrain_.end() || overlay_iter == tcodeToTerrain_.end()) {
00650             return false;
00651         }
00652 
00653         terrain_type new_terrain(base_iter->second, overlay_iter->second);
00654         terrainList_.push_back(new_terrain.number());
00655         tcodeToTerrain_.insert(std::pair<t_translation::t_terrain, terrain_type>(
00656                                    new_terrain.number(), new_terrain));
00657         return true;
00658     }
00659     return true; // Terrain already exists, nothing to do
00660 }
00661 
00662 t_translation::t_terrain gamemap::merge_terrains(const t_translation::t_terrain old_t, const t_translation::t_terrain new_t, const tmerge_mode mode, bool replace_if_failed) {
00663     t_translation::t_terrain result = t_translation::NONE_TERRAIN;
00664 
00665     if(mode == OVERLAY) {
00666         const t_translation::t_terrain t = t_translation::t_terrain(old_t.base, new_t.overlay);
00667         if (try_merge_terrains(t)) {
00668             result = t;
00669         }
00670     }
00671     else if(mode == BASE) {
00672         const t_translation::t_terrain t = t_translation::t_terrain(new_t.base, old_t.overlay);
00673         if (try_merge_terrains(t)) {
00674             result = t;
00675         }
00676     }
00677     else if(mode == BOTH && new_t.base != t_translation::NO_LAYER) {
00678         // We need to merge here, too, because the dest terrain might be a combined one.
00679         if (try_merge_terrains(new_t)) {
00680             result = new_t;
00681         }
00682     }
00683 
00684     // if merging of overlay and base failed, and replace_if_failed is set,
00685     // replace the terrain with the complete new terrain (if given)
00686     // or with (default base)^(new overlay)
00687     if(result == t_translation::NONE_TERRAIN && replace_if_failed && tcodeToTerrain_.count(new_t) > 0) {
00688         if(new_t.base != t_translation::NO_LAYER) {
00689             // Same as above
00690             if (try_merge_terrains(new_t)) {
00691                 result = new_t;
00692             }
00693         }
00694         else if (get_terrain_info(new_t).default_base() != t_translation::NONE_TERRAIN) {
00695             result = get_terrain_info(new_t).terrain_with_default_base();
00696         }
00697     }
00698     return result;
00699 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines

Generated by doxygen 1.7.1 on Fri May 25 2012 01:03:05 for The Battle for Wesnoth
Gna! | Forum | Wiki | CIA | devdocs