00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018 #include "global.hpp"
00019
00020 #include "config.hpp"
00021 #include "gettext.hpp"
00022 #include "log.hpp"
00023 #include "map.hpp"
00024 #include "pathfind.hpp"
00025 #include "util.hpp"
00026 #include "serialization/string_utils.hpp"
00027 #include "serialization/parser.hpp"
00028 #include "wml_exception.hpp"
00029
00030 #include <algorithm>
00031 #include <cassert>
00032 #include <cctype>
00033 #include <cstdlib>
00034 #include <iostream>
00035 #include <sstream>
00036
00037 #define ERR_CF LOG_STREAM(err, config)
00038 #define LOG_G LOG_STREAM(info, general)
00039 #define DBG_G LOG_STREAM(debug, general)
00040
00041 std::ostream &operator<<(std::ostream &s, gamemap::location const &l) {
00042 s << (l.x + 1) << ',' << (l.y + 1);
00043 return s;
00044 }
00045
00046 const gamemap::location gamemap::location::null_location;
00047
00048 const std::string gamemap::default_map_header = "usage=map\nborder_size=1\n\n";
00049 const gamemap::tborder gamemap::default_border = gamemap::SINGLE_TILE_BORDER;
00050
00051 const t_translation::t_list& gamemap::underlying_mvt_terrain(t_translation::t_terrain terrain) const
00052 {
00053 const std::map<t_translation::t_terrain,terrain_type>::const_iterator i =
00054 tcodeToTerrain_.find(terrain);
00055
00056 if(i == tcodeToTerrain_.end()) {
00057 static t_translation::t_list result(1);
00058 result[0] = terrain;
00059 return result;
00060 } else {
00061 return i->second.mvt_type();
00062 }
00063 }
00064
00065 const t_translation::t_list& gamemap::underlying_def_terrain(t_translation::t_terrain terrain) const
00066 {
00067 const std::map<t_translation::t_terrain, terrain_type>::const_iterator i =
00068 tcodeToTerrain_.find(terrain);
00069
00070 if(i == tcodeToTerrain_.end()) {
00071 static t_translation::t_list result(1);
00072 result[0] = terrain;
00073 return result;
00074 } else {
00075 return i->second.def_type();
00076 }
00077 }
00078
00079 const t_translation::t_list& gamemap::underlying_union_terrain(t_translation::t_terrain terrain) const
00080 {
00081 const std::map<t_translation::t_terrain,terrain_type>::const_iterator i =
00082 tcodeToTerrain_.find(terrain);
00083
00084 if(i == tcodeToTerrain_.end()) {
00085 static t_translation::t_list result(1);
00086 result[0] = terrain;
00087 return result;
00088 } else {
00089 return i->second.union_type();
00090 }
00091 }
00092
00093 void gamemap::write_terrain(const gamemap::location &loc, config& cfg) const
00094 {
00095 cfg["terrain"] = t_translation::write_terrain_code(get_terrain(loc));
00096 }
00097
00098 gamemap::location::DIRECTION gamemap::location::parse_direction(const std::string& str)
00099 {
00100 if(!str.empty()) {
00101 if(str == "n") {
00102 return NORTH;
00103 } else if(str == "ne") {
00104 return NORTH_EAST;
00105 } else if(str == "se") {
00106 return SOUTH_EAST;
00107 } else if(str == "s") {
00108 return SOUTH;
00109 } else if(str == "sw") {
00110 return SOUTH_WEST;
00111 } else if(str == "nw") {
00112 return NORTH_WEST;
00113 } else if(str[0] == '-' && str.length() <= 10) {
00114
00115 return get_opposite_dir(parse_direction(str.substr(1)));
00116 }
00117 }
00118 return NDIRECTIONS;
00119 }
00120
00121 std::vector<gamemap::location::DIRECTION> gamemap::location::parse_directions(const std::string& str)
00122 {
00123 gamemap::location::DIRECTION temp;
00124 std::vector<gamemap::location::DIRECTION> to_return;
00125 std::vector<std::string> dir_strs = utils::split(str);
00126 std::vector<std::string>::const_iterator i, i_end=dir_strs.end();
00127 for(i = dir_strs.begin(); i != i_end; ++i) {
00128 temp = gamemap::location::parse_direction(*i);
00129
00130 if(temp != NDIRECTIONS) {
00131 to_return.push_back(temp);
00132 }
00133 }
00134 return to_return;
00135 }
00136
00137 std::string gamemap::location::write_direction(gamemap::location::DIRECTION dir)
00138 {
00139 switch(dir) {
00140 case NORTH:
00141 return std::string("n");
00142 case NORTH_EAST:
00143 return std::string("ne");
00144 case NORTH_WEST:
00145 return std::string("nw");
00146 case SOUTH:
00147 return std::string("s");
00148 case SOUTH_EAST:
00149 return std::string("se");
00150 case SOUTH_WEST:
00151 return std::string("sw");
00152 default:
00153 return std::string();
00154
00155 }
00156 }
00157
00158 gamemap::location::location(const config& cfg, const variable_set *variables) :
00159 x(-1000),
00160 y(-1000)
00161 {
00162 std::string xs = cfg["x"], ys = cfg["y"];
00163 if (variables)
00164 {
00165 xs = utils::interpolate_variables_into_string( xs, *variables);
00166 ys = utils::interpolate_variables_into_string( ys, *variables);
00167 }
00168
00169
00170 if(xs.empty() == false)
00171 x = atoi(xs.c_str()) - 1;
00172
00173 if(ys.empty() == false)
00174 y = atoi(ys.c_str()) - 1;
00175 }
00176
00177 void gamemap::location::write(config& cfg) const
00178 {
00179 char buf[50];
00180 snprintf(buf,sizeof(buf),"%d",x+1);
00181 cfg["x"] = buf;
00182 snprintf(buf,sizeof(buf),"%d",y+1);
00183 cfg["y"] = buf;
00184 }
00185
00186 gamemap::location gamemap::location::operator-() const
00187 {
00188 location ret;
00189 ret.x = -x;
00190 ret.y = -y;
00191
00192 return ret;
00193 }
00194
00195 gamemap::location gamemap::location::operator+(const gamemap::location& a) const
00196 {
00197 gamemap::location ret = *this;
00198 ret += a;
00199 return ret;
00200 }
00201
00202 gamemap::location& gamemap::location::operator+=(const gamemap::location &a)
00203 {
00204 bool parity = (x & 1) != 0;
00205
00206 x += a.x;
00207 y += a.y;
00208
00209 if((a.x > 0) && (a.x % 2) && parity)
00210 y++;
00211 if((a.x < 0) && (a.x % 2) && !parity)
00212 y--;
00213
00214 return *this;
00215 }
00216
00217 gamemap::location gamemap::location::operator-(const gamemap::location &a) const
00218 {
00219 return operator+(-a);
00220 }
00221
00222 gamemap::location& gamemap::location::operator-=(const gamemap::location &a)
00223 {
00224 return operator+=(-a);
00225 }
00226
00227 gamemap::location gamemap::location::get_direction(
00228 gamemap::location::DIRECTION dir, int n) const
00229 {
00230 if (n < 0 ) {
00231 dir = get_opposite_dir(dir);
00232 n = -n;
00233 }
00234 switch(dir) {
00235 case NORTH: return gamemap::location(x, y - n);
00236 case SOUTH: return gamemap::location(x, y + n);
00237 case SOUTH_EAST: return gamemap::location(x + n, y + (n+is_odd(x))/2 );
00238 case SOUTH_WEST: return gamemap::location(x - n, y + (n+is_odd(x))/2 );
00239 case NORTH_EAST: return gamemap::location(x + n, y - (n+is_even(x))/2 );
00240 case NORTH_WEST: return gamemap::location(x - n, y - (n+is_even(x))/2 );
00241 default:
00242 assert(false);
00243 return gamemap::location();
00244 }
00245 }
00246
00247 gamemap::location::DIRECTION gamemap::location::get_relative_dir(gamemap::location loc) const {
00248 location diff = loc -*this;
00249 if(diff == location(0,0)) return NDIRECTIONS;
00250 if( diff.y < 0 && diff.x >= 0 && abs(diff.x) >= abs(diff.y)) return NORTH_EAST;
00251 if( diff.y < 0 && diff.x < 0 && abs(diff.x) >= abs(diff.y)) return NORTH_WEST;
00252 if( diff.y < 0 && abs(diff.x) < abs(diff.y)) return NORTH;
00253
00254 if( diff.y >= 0 && diff.x >= 0 && abs(diff.x) >= abs(diff.y)) return SOUTH_EAST;
00255 if( diff.y >= 0 && diff.x < 0 && abs(diff.x) >= abs(diff.y)) return SOUTH_WEST;
00256 if( diff.y >= 0 && abs(diff.x) < abs(diff.y)) return SOUTH;
00257
00258
00259 assert(false);
00260 return NDIRECTIONS;
00261
00262
00263 }
00264 gamemap::location::DIRECTION gamemap::location::get_opposite_dir(gamemap::location::DIRECTION d) {
00265 switch (d) {
00266 case NORTH:
00267 return SOUTH;
00268 case NORTH_EAST:
00269 return SOUTH_WEST;
00270 case SOUTH_EAST:
00271 return NORTH_WEST;
00272 case SOUTH:
00273 return NORTH;
00274 case SOUTH_WEST:
00275 return NORTH_EAST;
00276 case NORTH_WEST:
00277 return SOUTH_EAST;
00278 case NDIRECTIONS:
00279 default:
00280 return NDIRECTIONS;
00281 }
00282 }
00283
00284
00285
00286
00287
00288 gamemap::gamemap(const config& cfg, const std::string& data):
00289 tiles_(1),
00290 terrainList_(),
00291 tcodeToTerrain_(),
00292 villages_(),
00293 borderCache_(),
00294 terrainFrequencyCache_(),
00295 w_(-1),
00296 h_(-1),
00297 total_width_(0),
00298 total_height_(0),
00299 border_size_(NO_BORDER),
00300 usage_(IS_MAP)
00301 {
00302 DBG_G << "loading map: '" << data << "'\n";
00303 const config::child_list& terrains = cfg.get_children("terrain");
00304 create_terrain_maps(terrains,terrainList_,tcodeToTerrain_);
00305
00306 read(data);
00307 }
00308
00309
00310
00311
00312 void gamemap::read(const std::string& data)
00313 {
00314
00315 tiles_.clear();
00316 villages_.clear();
00317 std::fill(startingPositions_, startingPositions_ +
00318 sizeof(startingPositions_) / sizeof(*startingPositions_), location());
00319 std::map<int, t_translation::coordinate> starting_positions;
00320
00321 if(data.empty()) {
00322 w_ = 0;
00323 h_ = 0;
00324 return;
00325 }
00326
00327
00328 size_t header_offset = data.find("\n\n");
00329 if(header_offset == std::string::npos) {
00330
00331
00332
00333
00334 header_offset = data.find("\r\n\r\n");
00335 }
00336 const size_t comma_offset = data.find(",");
00337
00338
00339
00340 VALIDATE(
00341 !(header_offset == std::string::npos || comma_offset < header_offset),
00342 _("A map without a header is not supported"));
00343
00344 std::string header_str(std::string(data, 0, header_offset + 1));
00345 config header;
00346 ::read(header, header_str);
00347
00348 border_size_ = lexical_cast_default<int>(header["border_size"], 0);
00349 const std::string usage = header["usage"];
00350
00351 utils::string_map symbols;
00352 symbols["border_size_key"] = "border_size";
00353 symbols["usage_key"] = "usage";
00354 symbols["usage_val"] = usage;
00355 const std::string msg = "'$border_size_key|' should be "
00356 "'$border_size_val|' when '$usage_key| = $usage_val|'";
00357
00358 if(usage == "map") {
00359 usage_ = IS_MAP;
00360 symbols["border_size_val"] = "1";
00361 VALIDATE(border_size_ == 1, vgettext(msg.c_str(), symbols));
00362 } else if(usage == "mask") {
00363 usage_ = IS_MASK;
00364 symbols["border_size_val"] = "0";
00365 VALIDATE(border_size_ == 0, vgettext(msg.c_str(), symbols));
00366 } else if(usage == "") {
00367 throw incorrect_format_exception("Map has a header but no usage");
00368 } else {
00369 std::string msg = "Map has a header but an unknown usage:" + usage;
00370 throw incorrect_format_exception(msg.c_str());
00371 }
00372
00373
00374 const std::string& map = std::string(data, header_offset + 2, std::string::npos);
00375
00376 try {
00377 tiles_ = t_translation::read_game_map(map, starting_positions);
00378
00379 } catch(t_translation::error& e) {
00380
00381
00382 throw incorrect_format_exception(e.message.c_str());
00383 }
00384
00385
00386 std::map<int, t_translation::coordinate>::const_iterator itor =
00387 starting_positions.begin();
00388
00389 for(; itor != starting_positions.end(); ++itor) {
00390
00391
00392
00393
00394 if(itor->first < 1 || itor->first >= MAX_PLAYERS+1) {
00395 ERR_CF << "Starting position " << itor->first << " out of range\n";
00396 throw incorrect_format_exception("Illegal starting position found"
00397 " in map. The scenario cannot be loaded.");
00398 }
00399
00400
00401 startingPositions_[itor->first] = location(itor->second.x - 1, itor->second.y - 1);
00402 }
00403
00404
00405 total_width_ = tiles_.size();
00406 total_height_ = total_width_ > 0 ? tiles_[0].size() : 0;
00407 w_ = total_width_ - 2 * border_size_;
00408 h_ = total_height_ - 2 * border_size_;
00409
00410 for(int x = 0; x < total_width_; ++x) {
00411 for(int y = 0; y < total_height_; ++y) {
00412
00413
00414 if(tcodeToTerrain_.count(tiles_[x][y]) == 0) {
00415 if(!try_merge_terrains(tiles_[x][y])) {
00416 ERR_CF << "Illegal character in map: (" << t_translation::write_terrain_code(tiles_[x][y])
00417 << ") '" << tiles_[x][y] << "'\n";
00418 throw incorrect_format_exception("Illegal character found in map. The scenario cannot be loaded.");
00419 }
00420 }
00421
00422
00423 if(x >= border_size_ && y >= border_size_
00424 && x < total_width_-border_size_ && y < total_height_-border_size_
00425 && is_village(tiles_[x][y])) {
00426 villages_.push_back(location(x-border_size_, y-border_size_));
00427 }
00428 }
00429 }
00430 }
00431
00432 std::string gamemap::write() const
00433 {
00434 std::map<int, t_translation::coordinate> starting_positions = std::map<int, t_translation::coordinate>();
00435
00436
00437 for(int i = 0; i < MAX_PLAYERS+1; ++i) {
00438 if(on_board(startingPositions_[i])) {
00439 const struct t_translation::coordinate position =
00440 {startingPositions_[i].x + border_size_, startingPositions_[i].y + border_size_};
00441
00442 starting_positions.insert(std::pair<int, t_translation::coordinate>(i, position));
00443 }
00444 }
00445
00446
00447 const std::string& data = t_translation::write_game_map(tiles_, starting_positions);
00448 const std::string& header = "border_size=" + lexical_cast<std::string>(border_size_)
00449 + "\nusage=" + (usage_ == IS_MAP ? "map" : "mask");
00450 return header + "\n\n" + data;
00451 }
00452
00453 void gamemap::overlay(const gamemap& m, const config& rules_cfg, const int xpos, const int ypos)
00454 {
00455 const config::child_list& rules = rules_cfg.get_children("rule");
00456
00457 const int xstart = maximum<int>(0, -xpos);
00458 const int ystart = maximum<int>(0, -ypos-((xpos & 1) ? 1 : 0));
00459 const int xend = minimum<int>(m.w(),w()-xpos);
00460 const int yend = minimum<int>(m.h(),h()-ypos);
00461 for(int x1 = xstart; x1 < xend; ++x1) {
00462 for(int y1 = ystart; y1 < yend; ++y1) {
00463 const int x2 = x1 + xpos;
00464 const int y2 = y1 + ypos +
00465 ((xpos & 1) && (x1 & 1) ? 1 : 0);
00466 if (y2 < 0 || y2 >= h()) {
00467 continue;
00468 }
00469 const t_translation::t_terrain t = m[x1][y1 + m.border_size_];
00470 const t_translation::t_terrain current = (*this)[x2][y2 + border_size_];
00471
00472 if(t == t_translation::FOGGED || t == t_translation::VOID_TERRAIN) {
00473 continue;
00474 }
00475
00476
00477 config::child_list::const_iterator rule = rules.begin();
00478 for( ; rule != rules.end(); ++rule) {
00479 static const std::string src_key = "old", src_not_key = "old_not",
00480 dst_key = "new", dst_not_key = "new_not";
00481 const config& cfg = **rule;
00482 const t_translation::t_list& src = t_translation::read_list(cfg[src_key]);
00483
00484 if(!src.empty() && t_translation::terrain_matches(current, src) == false) {
00485 continue;
00486 }
00487
00488 const t_translation::t_list& src_not = t_translation::read_list(cfg[src_not_key]);
00489
00490 if(!src_not.empty() && t_translation::terrain_matches(current, src_not)) {
00491 continue;
00492 }
00493
00494 const t_translation::t_list& dst = t_translation::read_list(cfg[dst_key]);
00495
00496 if(!dst.empty() && t_translation::terrain_matches(t, dst) == false) {
00497 continue;
00498 }
00499
00500 const t_translation::t_list& dst_not = t_translation::read_list(cfg[dst_not_key]);
00501
00502 if(!dst_not.empty() && t_translation::terrain_matches(t, dst_not)) {
00503 continue;
00504 }
00505
00506 break;
00507 }
00508
00509
00510 if(rule != rules.end()) {
00511 const config& cfg = **rule;
00512 const t_translation::t_list& terrain = t_translation::read_list(cfg["terrain"]);
00513
00514 tmerge_mode mode = BOTH;
00515 if (cfg["layer"] == "base") {
00516 mode = BASE;
00517 }
00518 else if (cfg["layer"] == "overlay") {
00519 mode = OVERLAY;
00520 }
00521
00522 t_translation::t_terrain new_terrain = t;
00523 if(!terrain.empty()) {
00524 new_terrain = terrain[0];
00525 }
00526
00527 if(!utils::string_bool(cfg["use_old"])) {
00528 set_terrain(location(x2,y2), new_terrain, mode, utils::string_bool(cfg["replace_if_failed"]));
00529 }
00530
00531 } else {
00532 set_terrain(location(x2,y2),t);
00533 }
00534 }
00535 }
00536
00537 for(const location* pos = m.startingPositions_;
00538 pos != m.startingPositions_ + sizeof(m.startingPositions_)/sizeof(*m.startingPositions_);
00539 ++pos) {
00540
00541 if(pos->valid()) {
00542 startingPositions_[pos - m.startingPositions_] = *pos;
00543 }
00544 }
00545 }
00546
00547 t_translation::t_terrain gamemap::get_terrain(const gamemap::location& loc) const
00548 {
00549
00550 if(on_board(loc, true)) {
00551 return tiles_[loc.x + border_size_][loc.y + border_size_];
00552 }
00553
00554 const std::map<location, t_translation::t_terrain>::const_iterator itor = borderCache_.find(loc);
00555 if(itor != borderCache_.end())
00556 return itor->second;
00557
00558
00559 t_translation::t_terrain items[6];
00560 int nitems = 0;
00561
00562 location adj[6];
00563 get_adjacent_tiles(loc,adj);
00564 for(int n = 0; n != 6; ++n) {
00565 if(on_board(adj[n])) {
00566 items[nitems] = tiles_[adj[n].x][adj[n].y];
00567 ++nitems;
00568 } else {
00569
00570
00571
00572
00573
00574
00575
00576
00577
00578 std::map<location, t_translation::t_terrain>::const_iterator itor =
00579 borderCache_.find(adj[n]);
00580
00581
00582 if(itor != borderCache_.end() &&
00583 itor->second != t_translation::NONE_TERRAIN) {
00584
00585 items[nitems] = itor->second;
00586 ++nitems;
00587 }
00588 }
00589
00590 }
00591
00592
00593
00594 t_translation::t_terrain used_terrain;
00595 int terrain_count = 0;
00596 for(int i = 0; i != nitems; ++i) {
00597 if(items[i] != used_terrain && !is_village(items[i]) && !is_keep(items[i])) {
00598 const int c = std::count(items+i+1,items+nitems,items[i]) + 1;
00599 if(c > terrain_count) {
00600 used_terrain = items[i];
00601 terrain_count = c;
00602 }
00603 }
00604 }
00605
00606 borderCache_.insert(std::pair<location, t_translation::t_terrain>(loc,used_terrain));
00607 return used_terrain;
00608
00609 }
00610
00611 const gamemap::location& gamemap::starting_position(int n) const
00612 {
00613 if(size_t(n) < sizeof(startingPositions_)/sizeof(*startingPositions_)) {
00614 return startingPositions_[n];
00615 } else {
00616 static const gamemap::location null_loc;
00617 return null_loc;
00618 }
00619 }
00620
00621 int gamemap::num_valid_starting_positions() const
00622 {
00623 const int res = is_starting_position(gamemap::location());
00624 if(res == -1)
00625 return num_starting_positions()-1;
00626 else
00627 return res;
00628 }
00629
00630 int gamemap::is_starting_position(const gamemap::location& loc) const
00631 {
00632 const gamemap::location* const beg = startingPositions_+1;
00633 const gamemap::location* const end = startingPositions_+num_starting_positions();
00634 const gamemap::location* const pos = std::find(beg,end,loc);
00635
00636 return pos == end ? -1 : pos - beg;
00637 }
00638
00639 void gamemap::set_starting_position(int side, const gamemap::location& loc)
00640 {
00641 if(side >= 0 && side < num_starting_positions()) {
00642 startingPositions_[side] = loc;
00643 }
00644 }
00645
00646 bool gamemap::on_board(const location& loc, const bool include_border) const
00647 {
00648 if(!include_border) {
00649 return loc.valid() && loc.x < w_ && loc.y < h_;
00650 } else if(tiles_.empty()) {
00651 return false;
00652 } else {
00653 return loc.x >= (0 - border_size_) && loc.x < (w_ + border_size_) &&
00654 loc.y >= (0 - border_size_) && loc.y < (h_ + border_size_);
00655 }
00656 }
00657
00658 const terrain_type& gamemap::get_terrain_info(const t_translation::t_terrain terrain) const
00659 {
00660 static const terrain_type default_terrain;
00661 const std::map<t_translation::t_terrain,terrain_type>::const_iterator i =
00662 tcodeToTerrain_.find(terrain);
00663
00664 if(i != tcodeToTerrain_.end())
00665 return i->second;
00666 else
00667 return default_terrain;
00668 }
00669
00670 bool gamemap::location::matches_range(const std::string& xloc, const std::string &yloc) const
00671 {
00672 if(std::find(xloc.begin(),xloc.end(),',') != xloc.end()
00673 || std::find(yloc.begin(),yloc.end(),',') != yloc.end()) {
00674 std::vector<std::string> xlocs = utils::split(xloc);
00675 std::vector<std::string> ylocs = utils::split(yloc);
00676
00677 size_t size;
00678 for(size = xlocs.size(); size < ylocs.size(); ++size) {
00679 xlocs.push_back("");
00680 }
00681 while(size > ylocs.size()) {
00682 ylocs.push_back("");
00683 }
00684 for(size_t i = 0; i != size; ++i) {
00685 if(matches_range(xlocs[i],ylocs[i]))
00686 return true;
00687 }
00688 return false;
00689 }
00690 if(!xloc.empty()) {
00691 const std::string::const_iterator dash =
00692 std::find(xloc.begin(),xloc.end(),'-');
00693 if(dash != xloc.end()) {
00694 const std::string beg(xloc.begin(),dash);
00695 const std::string end(dash+1,xloc.end());
00696
00697 const int bot = atoi(beg.c_str()) - 1;
00698 const int top = atoi(end.c_str()) - 1;
00699
00700 if(x < bot || x > top)
00701 return false;
00702 } else {
00703 const int xval = atoi(xloc.c_str()) - 1;
00704 if(xval != x)
00705 return false;
00706 }
00707 }
00708 if(!yloc.empty()) {
00709 const std::string::const_iterator dash =
00710 std::find(yloc.begin(),yloc.end(),'-');
00711
00712 if(dash != yloc.end()) {
00713 const std::string beg(yloc.begin(),dash);
00714 const std::string end(dash+1,yloc.end());
00715
00716 const int bot = atoi(beg.c_str()) - 1;
00717 const int top = atoi(end.c_str()) - 1;
00718
00719 if(y < bot || y > top)
00720 return false;
00721 } else {
00722 const int yval = atoi(yloc.c_str()) - 1;
00723 if(yval != y)
00724 return false;
00725 }
00726 }
00727 return true;
00728 }
00729
00730 void gamemap::set_terrain(const gamemap::location& loc, const t_translation::t_terrain terrain, const tmerge_mode mode, bool replace_if_failed) {
00731 if(!on_board(loc, true)) {
00732
00733 return;
00734 }
00735
00736 t_translation::t_terrain new_terrain = merge_terrains(get_terrain(loc), terrain, mode, replace_if_failed);
00737
00738 if(new_terrain == t_translation::NONE_TERRAIN) {
00739 return;
00740 }
00741
00742 if(on_board(loc, false)) {
00743 const bool old_village = is_village(loc);
00744 const bool new_village = is_village(new_terrain);
00745
00746 if(old_village && !new_village) {
00747 villages_.erase(std::remove(villages_.begin(),villages_.end(),loc),villages_.end());
00748 } else if(!old_village && new_village) {
00749 villages_.push_back(loc);
00750 }
00751 }
00752
00753 tiles_[loc.x + border_size_][loc.y + border_size_] = new_terrain;
00754
00755
00756 location adj[6];
00757 get_adjacent_tiles(loc,adj);
00758
00759 for(int n = 0; n < 6; ++n) {
00760 remove_from_border_cache(adj[n]);
00761 }
00762 }
00763
00764 std::vector<gamemap::location> parse_location_range(const std::string& x, const std::string& y,
00765 const gamemap *const map)
00766 {
00767 std::vector<gamemap::location> res;
00768 const std::vector<std::string> xvals = utils::split(x);
00769 const std::vector<std::string> yvals = utils::split(y);
00770
00771 for(unsigned int i = 0; i < xvals.size() || i < yvals.size(); ++i) {
00772 std::pair<int,int> xrange, yrange;
00773
00774
00775 if(i < xvals.size()) {
00776 xrange = utils::parse_range(xvals[i]);
00777 } else if (map != NULL) {
00778 xrange.first = 1;
00779 xrange.second = map->w();
00780 } else {
00781 break;
00782 }
00783
00784
00785 if(i < yvals.size()) {
00786 yrange = utils::parse_range(yvals[i]);
00787 } else if (map != NULL) {
00788 yrange.first = 1;
00789 yrange.second = map->h();
00790 } else {
00791 break;
00792 }
00793
00794 for(int x = xrange.first; x <= xrange.second; ++x) {
00795 for(int y = yrange.first; y <= yrange.second; ++y) {
00796 res.push_back(gamemap::location(x-1,y-1));
00797 }
00798 }
00799 }
00800 return res;
00801 }
00802
00803 const std::map<t_translation::t_terrain, size_t>& gamemap::get_weighted_terrain_frequencies() const
00804 {
00805 if(terrainFrequencyCache_.empty() == false) {
00806 return terrainFrequencyCache_;
00807 }
00808
00809 const location center(w()/2,h()/2);
00810
00811 const size_t furthest_distance = distance_between(location(0,0),center);
00812
00813 const size_t weight_at_edge = 100;
00814 const size_t additional_weight_at_center = 200;
00815
00816 for(size_t i = 0; i != size_t(w()); ++i) {
00817 for(size_t j = 0; j != size_t(h()); ++j) {
00818 const size_t distance = distance_between(location(i,j),center);
00819 terrainFrequencyCache_[(*this)[i][j]] += weight_at_edge +
00820 (furthest_distance-distance)*additional_weight_at_center;
00821 }
00822 }
00823
00824 return terrainFrequencyCache_;
00825 }
00826
00827 bool gamemap::try_merge_terrains(const t_translation::t_terrain terrain) {
00828
00829 if(tcodeToTerrain_.count(terrain) == 0) {
00830 const std::map<t_translation::t_terrain, terrain_type>::const_iterator base_iter =
00831 tcodeToTerrain_.find(t_translation::t_terrain(terrain.base, t_translation::NO_LAYER));
00832 const std::map<t_translation::t_terrain, terrain_type>::const_iterator overlay_iter =
00833 tcodeToTerrain_.find(t_translation::t_terrain(t_translation::NO_LAYER, terrain.overlay));
00834
00835 if(base_iter == tcodeToTerrain_.end() || overlay_iter == tcodeToTerrain_.end()) {
00836 return false;
00837 }
00838
00839 terrain_type new_terrain(base_iter->second, overlay_iter->second);
00840 terrainList_.push_back(new_terrain.number());
00841 tcodeToTerrain_.insert(std::pair<t_translation::t_terrain, terrain_type>(
00842 new_terrain.number(), new_terrain));
00843 return true;
00844 }
00845 return true;
00846 }
00847
00848 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) {
00849 t_translation::t_terrain result = t_translation::NONE_TERRAIN;
00850
00851 if(mode == OVERLAY) {
00852 const t_translation::t_terrain t = t_translation::t_terrain(old_t.base, new_t.overlay);
00853 if (try_merge_terrains(t)) {
00854 result = t;
00855 }
00856 }
00857 else if(mode == BASE) {
00858 const t_translation::t_terrain t = t_translation::t_terrain(new_t.base, old_t.overlay);
00859 if (try_merge_terrains(t)) {
00860 result = t;
00861 }
00862 }
00863 else if(mode == BOTH && new_t.base != t_translation::NO_LAYER) {
00864
00865 if (try_merge_terrains(new_t)) {
00866 result = new_t;
00867 }
00868 }
00869
00870
00871
00872
00873 if(result == t_translation::NONE_TERRAIN && replace_if_failed && tcodeToTerrain_.count(new_t) > 0) {
00874 if(new_t.base != t_translation::NO_LAYER) {
00875
00876 if (try_merge_terrains(new_t)) {
00877 result = new_t;
00878 }
00879 }
00880 else if (get_terrain_info(new_t).default_base() != t_translation::NONE_TERRAIN) {
00881 result = get_terrain_info(new_t).terrain_with_default_base();
00882 }
00883 }
00884 return result;
00885 }