mapgen.cpp

Go to the documentation of this file.
00001 /* $Id: mapgen.cpp 53169 2012-02-23 02:39:58Z fendrin $ */
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  * Map-generator, with standalone testprogram.
00019  */
00020 
00021 #include "global.hpp"
00022 
00023 #include "foreach.hpp"
00024 #include "gettext.hpp"
00025 #include "language.hpp"
00026 #include "log.hpp"
00027 #include "map.hpp"
00028 #include "mapgen.hpp"
00029 #include "pathfind/pathfind.hpp"
00030 #include "pathutils.hpp"
00031 #include "race.hpp"
00032 #include "util.hpp"
00033 #include "wml_exception.hpp"
00034 #include "formula_string_utils.hpp"
00035 #include "SDL.h"
00036 
00037 
00038 static lg::log_domain log_engine("engine");
00039 #define ERR_NG LOG_STREAM(err, log_engine)
00040 #define LOG_NG LOG_STREAM(info, log_engine)
00041 
00042 config map_generator::create_scenario(const std::vector<std::string>& args)
00043 {
00044     config res;
00045     res["data"] = create_map(args);
00046     return res;
00047 }
00048 
00049 typedef std::vector<std::vector<int> > height_map;
00050 typedef t_translation::t_map terrain_map;
00051 
00052 typedef map_location location;
00053 
00054 /**
00055  * Generate a height-map.
00056  *
00057  * Basically we generate a lot of hills, each hill being centered at a certain
00058  * point, with a certain radius - being a half sphere.  Hills are combined
00059  * additively to form a bumpy surface.  The size of each hill varies randomly
00060  * from 1-hill_size.  We generate 'iterations' hills in total.  The range of
00061  * heights is normalized to 0-1000.  'island_size' controls whether or not the
00062  * map should tend toward an island shape, and if so, how large the island
00063  * should be.  Hills with centers that are more than 'island_size' away from
00064  * the center of the map will be inverted (i.e. be valleys).  'island_size' as
00065  * 0 indicates no island.
00066  */
00067 static height_map generate_height_map(size_t width, size_t height,
00068                                size_t iterations, size_t hill_size,
00069                                size_t island_size, size_t island_off_center)
00070 {
00071     height_map res(width,std::vector<int>(height,0));
00072 
00073     size_t center_x = width/2;
00074     size_t center_y = height/2;
00075 
00076     LOG_NG << "off-centering...\n";
00077 
00078     if(island_off_center != 0) {
00079         switch(rand()%4) {
00080         case 0:
00081             center_x += island_off_center;
00082             break;
00083         case 1:
00084             center_y += island_off_center;
00085             break;
00086         case 2:
00087             if(center_x < island_off_center)
00088                 center_x = 0;
00089             else
00090                 center_x -= island_off_center;
00091             break;
00092         case 3:
00093             if(center_y < island_off_center)
00094                 center_y = 0;
00095             else
00096                 center_y -= island_off_center;
00097             break;
00098         }
00099     }
00100 
00101     for(size_t i = 0; i != iterations; ++i) {
00102 
00103         // (x1,y1) is the location of the hill,
00104         // and 'radius' is the radius of the hill.
00105         // We iterate over all points, (x2,y2).
00106         // The formula for the amount the height is increased by is:
00107         // radius - sqrt((x2-x1)^2 + (y2-y1)^2) with negative values ignored.
00108         //
00109         // Rather than iterate over every single point, we can reduce the points
00110         // to a rectangle that contains all the positive values for this formula --
00111         // the rectangle is given by min_x,max_x,min_y,max_y.
00112 
00113         // Is this a negative hill? (i.e. a valley)
00114         bool is_valley = false;
00115 
00116         int x1 = island_size > 0 ? center_x - island_size + (rand()%(island_size*2)) :
00117                                              int(rand()%width);
00118         int y1 = island_size > 0 ? center_y - island_size + (rand()%(island_size*2)) :
00119                                              int(rand()%height);
00120 
00121         // We have to check whether this is actually a valley
00122         if(island_size != 0) {
00123             const size_t diffx = abs(x1 - int(center_x));
00124             const size_t diffy = abs(y1 - int(center_y));
00125             const size_t dist = size_t(std::sqrt(double(diffx*diffx + diffy*diffy)));
00126             is_valley = dist > island_size;
00127         }
00128 
00129         const int radius = rand()%hill_size + 1;
00130 
00131         const int min_x = x1 - radius > 0 ? x1 - radius : 0;
00132         const int max_x = x1 + radius < static_cast<long>(res.size()) ? x1 + radius : res.size();
00133         const int min_y = y1 - radius > 0 ? y1 - radius : 0;
00134         const int max_y = y1 + radius < static_cast<long>(res.front().size()) ? y1 + radius : res.front().size();
00135 
00136         for(int x2 = min_x; x2 < max_x; ++x2) {
00137             for(int y2 = min_y; y2 < max_y; ++y2) {
00138                 const int xdiff = (x2-x1);
00139                 const int ydiff = (y2-y1);
00140 
00141                 const int height = radius - int(std::sqrt(double(xdiff*xdiff + ydiff*ydiff)));
00142 
00143                 if(height > 0) {
00144                     if(is_valley) {
00145                         if(height > res[x2][y2]) {
00146                             res[x2][y2] = 0;
00147                         } else {
00148                             res[x2][y2] -= height;
00149                         }
00150                     } else {
00151                         res[x2][y2] += height;
00152                     }
00153                 }
00154             }
00155         }
00156     }
00157 
00158     // Find the highest and lowest points on the map for normalization:
00159     int heighest = 0, lowest = 100000, x;
00160     for(x = 0; size_t(x) != res.size(); ++x) {
00161         for(int y = 0; size_t(y) != res[x].size(); ++y) {
00162             if(res[x][y] > heighest)
00163                 heighest = res[x][y];
00164 
00165             if(res[x][y] < lowest)
00166                 lowest = res[x][y];
00167         }
00168     }
00169 
00170     // Normalize the heights to the range 0-1000:
00171     heighest -= lowest;
00172     for(x = 0; size_t(x) != res.size(); ++x) {
00173         for(int y = 0; size_t(y) != res[x].size(); ++y) {
00174             res[x][y] -= lowest;
00175             res[x][y] *= 1000;
00176             if(heighest != 0)
00177                 res[x][y] /= heighest;
00178         }
00179     }
00180 
00181     return res;
00182 }
00183 
00184 /**
00185  * Generate a lake.
00186  *
00187  * It will create water at (x,y), and then have 'lake_fall_off' % chance to
00188  * make another water tile in each of the directions n,s,e,w.  In each of the
00189  * directions it does make another water tile, it will have 'lake_fall_off'/2 %
00190  * chance to make another water tile in each of the directions. This will
00191  * continue recursively.
00192  */
00193 static bool generate_lake(terrain_map& terrain, int x, int y, int lake_fall_off, std::set<location>& locs_touched)
00194 {
00195     if(x < 0 || y < 0 || size_t(x) >= terrain.size() || size_t(y) >= terrain.front().size()) {
00196         return false;
00197     }
00198 
00199     terrain[x][y] = t_translation::SHALLOW_WATER;
00200     locs_touched.insert(location(x,y));
00201 
00202     if((rand()%100) < lake_fall_off) {
00203         generate_lake(terrain,x+1,y,lake_fall_off/2,locs_touched);
00204     }
00205 
00206     if((rand()%100) < lake_fall_off) {
00207         generate_lake(terrain,x-1,y,lake_fall_off/2,locs_touched);
00208     }
00209 
00210     if((rand()%100) < lake_fall_off) {
00211         generate_lake(terrain,x,y+1,lake_fall_off/2,locs_touched);
00212     }
00213 
00214     if((rand()%100) < lake_fall_off) {
00215         generate_lake(terrain,x,y-1,lake_fall_off/2,locs_touched);
00216     }
00217 
00218     return true;
00219 }
00220 
00221 /**
00222  * River generation.
00223  *
00224  * Rivers have a source, and then keep on flowing until they meet another body
00225  * of water, which they flow into, or until they reach the edge of the map.
00226  * Rivers will always flow downhill, except that they can flow a maximum of
00227  * 'river_uphill' uphill.  This is to represent the water eroding the higher
00228  * ground lower.
00229  *
00230  * Every possible path for a river will be attempted, in random order, and the
00231  * first river path that can be found that makes the river flow into another
00232  * body of water or off the map will be used.
00233  *
00234  * If no path can be found, then the river's generation will be aborted, and
00235  * false will be returned.  true is returned if the river is generated
00236  * successfully.
00237  */
00238 static bool generate_river_internal(const height_map& heights,
00239     terrain_map& terrain, int x, int y, std::vector<location>& river,
00240     std::set<location>& seen_locations, int river_uphill)
00241 {
00242     const bool on_map = x >= 0 && y >= 0 &&
00243         x < static_cast<long>(heights.size()) &&
00244         y < static_cast<long>(heights.back().size());
00245 
00246     if(on_map && !river.empty() && heights[x][y] >
00247             heights[river.back().x][river.back().y] + river_uphill) {
00248 
00249         return false;
00250     }
00251 
00252     // If we're at the end of the river
00253     if(!on_map || terrain[x][y] == t_translation::SHALLOW_WATER ||
00254             terrain[x][y] == t_translation::DEEP_WATER) {
00255 
00256         LOG_NG << "generating river...\n";
00257 
00258         // Generate the river
00259         for(std::vector<location>::const_iterator i = river.begin();
00260             i != river.end(); ++i) {
00261             terrain[i->x][i->y] = t_translation::SHALLOW_WATER;
00262         }
00263 
00264         LOG_NG << "done generating river\n";
00265 
00266         return true;
00267     }
00268 
00269     location current_loc(x,y);
00270     location adj[6];
00271     get_adjacent_tiles(current_loc,adj);
00272     static int items[6] = {0,1,2,3,4,5};
00273     std::random_shuffle(items,items+4);
00274 
00275     // Mark that we have attempted from this location
00276     seen_locations.insert(current_loc);
00277     river.push_back(current_loc);
00278     for(int a = 0; a != 6; ++a) {
00279         const location& loc = adj[items[a]];
00280         if(seen_locations.count(loc) == 0) {
00281             const bool res = generate_river_internal(heights,terrain,loc.x,loc.y,river,seen_locations,river_uphill);
00282             if(res) {
00283                 return true;
00284             }
00285 
00286         }
00287     }
00288 
00289     river.pop_back();
00290 
00291     return false;
00292 }
00293 
00294 static std::vector<location> generate_river(const height_map& heights, terrain_map& terrain, int x, int y, int river_uphill)
00295 {
00296     std::vector<location> river;
00297     std::set<location> seen_locations;
00298     const bool res = generate_river_internal(heights,terrain,x,y,river,seen_locations,river_uphill);
00299     if(!res) {
00300         river.clear();
00301     }
00302 
00303     return river;
00304 }
00305 
00306 /**
00307  * Returns a random tile at one of the borders of a map that is of the given
00308  * dimensions.
00309  */
00310 static location random_point_at_side(size_t width, size_t height)
00311 {
00312     const int side = rand()%4;
00313     if(side < 2) {
00314         const int x = rand()%width;
00315         const int y = side == 0 ? 0 : height-1;
00316         return location(x,y);
00317     } else {
00318         const int y = rand()%height;
00319         const int x = side == 2 ? 0 : width-1;
00320         return location(x,y);
00321     }
00322 }
00323 
00324 /** Function which, given the map will output it in a valid format. */
00325 static std::string output_map(const terrain_map& terrain,
00326         std::map<int, t_translation::coordinate> starting_positions)
00327 {
00328     // Remember that we only want the middle 1/9th of the map.
00329     // All other segments of the map are there only to give
00330     // the important middle part some context.
00331     // We also have a border so also adjust for that.
00332     const size_t begin_x = terrain.size() / 3 - gamemap::default_border ;
00333     const size_t end_x = terrain.size() * 2 / 3 + gamemap::default_border;
00334     const size_t begin_y = terrain.front().size() / 3 - gamemap::default_border;
00335     const size_t end_y = terrain.front().size() * 2 / 3 + gamemap::default_border;
00336 
00337     terrain_map map;
00338     map.resize(end_x - begin_x);
00339     for(size_t y = begin_y; y != end_y; ++y) {
00340         for(size_t x = begin_x; x != end_x; ++x) {
00341             if((y - begin_y) == 0){
00342                 map[x - begin_x].resize(end_y - begin_y);
00343             }
00344             map[x - begin_x][y - begin_y] = terrain[x][y];
00345         }
00346     }
00347 
00348     // Since the map has been resized,
00349     // the starting locations also need to be fixed
00350     std::map<int, t_translation::coordinate>::iterator itor = starting_positions.begin();
00351     for(; itor != starting_positions.end(); ++itor) {
00352         itor->second.x -= begin_x;
00353         itor->second.y -= begin_y;
00354     }
00355 
00356     return t_translation::write_game_map(map, starting_positions);
00357 }
00358 
00359 namespace {
00360 
00361 /**
00362  * Calculates the cost of building a road over terrain.  For use in the
00363  * a_star_search algorithm.
00364  */
00365 struct road_path_calculator : pathfind::cost_calculator
00366 {
00367     road_path_calculator(const terrain_map& terrain, const config& cfg) :
00368         calls(0),
00369         map_(terrain),
00370         cfg_(cfg),
00371         // Find out how windy roads should be.
00372         windiness_(std::max<int>(1, cfg["road_windiness"].to_int())),
00373         seed_(rand()),
00374         cache_()
00375     {
00376     }
00377 
00378     virtual double cost(const location& loc, const double so_far) const;
00379 
00380     mutable int calls;
00381 private:
00382     const terrain_map& map_;
00383     const config& cfg_;
00384     int windiness_;
00385     int seed_;
00386     mutable std::map<t_translation::t_terrain, double> cache_;
00387 };
00388 
00389 double road_path_calculator::cost(const location& loc,
00390     const double /*so_far*/) const
00391 {
00392     ++calls;
00393     if (loc.x < 0 || loc.y < 0 || loc.x >= static_cast<long>(map_.size()) ||
00394             loc.y >= static_cast<long>(map_.front().size())) {
00395 
00396         return (pathfind::cost_calculator::getNoPathValue());
00397     }
00398 
00399     // We multiply the cost by a random amount,
00400     // depending upon how 'windy' the road should be.
00401     // If windiness is 1, that will mean that the cost is always genuine,
00402     // and so the road always takes the shortest path.
00403     // If windiness is greater than 1, we sometimes over-report costs
00404     // for some segments, to make the road wind a little.
00405 
00406     double windiness = 1.0;
00407 
00408     if (windiness_ > 1) {
00409         // modified pseudo_random taken from builder.cpp
00410         unsigned int a = (loc.x + 92872973) ^ 918273;
00411         unsigned int b = (loc.y + 1672517) ^ 128123;
00412         unsigned int c = a*b + a + b + seed_;
00413         unsigned int random = c*c;
00414         // this is just "big random number modulo windiness_"
00415         // but avoid the "modulo by a low number (like 2)"
00416         // because it can increase arithmetic patterns
00417         int noise = random % (windiness_ * 137) / 137;
00418         windiness += noise;
00419     }
00420 
00421     const t_translation::t_terrain c = map_[loc.x][loc.y];
00422     const std::map<t_translation::t_terrain, double>::const_iterator itor = cache_.find(c);
00423     if(itor != cache_.end()) {
00424         return itor->second*windiness;
00425     }
00426 
00427     static std::string terrain;
00428     terrain = t_translation::write_terrain_code(c);
00429     double res = getNoPathValue();
00430     if (const config &child = cfg_.find_child("road_cost", "terrain", terrain)) {
00431         res = child["cost"].to_double();
00432     }
00433 
00434     cache_.insert(std::pair<t_translation::t_terrain, double>(c,res));
00435     return windiness*res;
00436 }
00437 
00438 struct is_valid_terrain
00439 {
00440     is_valid_terrain(const t_translation::t_map& map,
00441             const t_translation::t_list& terrain_list);
00442     bool operator()(int x, int y) const;
00443 private:
00444     t_translation::t_map map_;
00445     const t_translation::t_list& terrain_;
00446 };
00447 
00448 is_valid_terrain::is_valid_terrain(const t_translation::t_map& map,
00449         const t_translation::t_list& terrain_list)
00450 : map_(map), terrain_(terrain_list)
00451 {}
00452 
00453 bool is_valid_terrain::operator()(int x, int y) const
00454 {
00455     if(x < 0 || x >= static_cast<long>(map_.size()) ||
00456             y < 0 || y >= static_cast<long>(map_[x].size())) {
00457 
00458         return false;
00459     }
00460 
00461     return std::find(terrain_.begin(),terrain_.end(),map_[x][y]) != terrain_.end();
00462 }
00463 
00464 }
00465 
00466 static int rank_castle_location(int x, int y, const is_valid_terrain& valid_terrain, int min_x, int max_x, int min_y, int max_y,
00467                          size_t min_distance, const std::vector<map_location>& other_castles, int highest_ranking)
00468 {
00469     const map_location loc(x,y);
00470 
00471     size_t avg_distance = 0, lowest_distance = 1000;
00472 
00473     for(std::vector<map_location>::const_iterator c = other_castles.begin(); c != other_castles.end(); ++c) {
00474         const size_t distance = distance_between(loc,*c);
00475         if(distance < 6) {
00476             return 0;
00477         }
00478 
00479         if(distance < lowest_distance) {
00480             lowest_distance = distance;
00481         }
00482 
00483         if(distance < min_distance) {
00484             avg_distance = 0;
00485             return -1;
00486         }
00487 
00488         avg_distance += distance;
00489     }
00490 
00491     if(other_castles.empty() == false) {
00492         avg_distance /= other_castles.size();
00493     }
00494 
00495     for(int i = x-1; i <= x+1; ++i) {
00496         for(int j = y-1; j <= y+1; ++j) {
00497             if(!valid_terrain(i,j)) {
00498                 return 0;
00499             }
00500         }
00501     }
00502 
00503     const int x_from_border = std::min<int>(x - min_x,max_x - x);
00504     const int y_from_border = std::min<int>(y - min_y,max_y - y);
00505 
00506     const int border_ranking = min_distance - std::min<int>(x_from_border,y_from_border) +
00507                                min_distance - x_from_border - y_from_border;
00508 
00509     int current_ranking = border_ranking*2 + avg_distance*10 + lowest_distance*10;
00510     static const int num_nearby_locations = 11*11;
00511 
00512     const int max_possible_ranking = current_ranking + num_nearby_locations;
00513 
00514     if(max_possible_ranking < highest_ranking) {
00515         return current_ranking;
00516     }
00517 
00518     int surrounding_ranking = 0;
00519 
00520     for(int xpos = x-5; xpos <= x+5; ++xpos) {
00521         for(int ypos = y-5; ypos <= y+5; ++ypos) {
00522             if(valid_terrain(xpos,ypos)) {
00523                 ++surrounding_ranking;
00524             }
00525         }
00526     }
00527 
00528     return surrounding_ranking + current_ranking;
00529 }
00530 
00531 typedef std::map<t_translation::t_terrain, t_translation::t_list> tcode_list_cache;
00532 
00533 static map_location place_village(const t_translation::t_map& map,
00534     const size_t x, const size_t y, const size_t radius, const config& cfg,
00535     tcode_list_cache &adj_liked_cache)
00536 {
00537     const map_location loc(x,y);
00538     std::set<map_location> locs;
00539     get_tiles_radius(loc,radius,locs);
00540     map_location best_loc;
00541     int best_rating = 0;
00542     for(std::set<map_location>::const_iterator i = locs.begin();
00543             i != locs.end(); ++i) {
00544 
00545         if(i->x < 0 || i->y < 0 || i->x >= static_cast<long>(map.size()) ||
00546                 i->y >= static_cast<long>(map[i->x].size())) {
00547 
00548             continue;
00549         }
00550 
00551         const t_translation::t_terrain t = map[i->x][i->y];
00552         const std::string str = t_translation::write_terrain_code(t);
00553         if (const config &child = cfg.find_child("village", "terrain", str)) {
00554             tcode_list_cache::iterator l = adj_liked_cache.find(t);
00555             t_translation::t_list *adjacent_liked;
00556             if (l != adj_liked_cache.end()) {
00557                 adjacent_liked = &(l->second);
00558             } else {
00559                 adj_liked_cache[t] = t_translation::read_list(child["adjacent_liked"]);
00560                 adjacent_liked = &(adj_liked_cache[t]);
00561             }
00562 
00563             int rating = child["rating"];
00564             map_location adj[6];
00565             get_adjacent_tiles(map_location(i->x,i->y),adj);
00566             for(size_t n = 0; n != 6; ++n) {
00567                 if(adj[n].x < 0 || adj[n].y < 0 ||
00568                         adj[n].x >= static_cast<long>(map.size()) ||
00569                         adj[n].y >= static_cast<long>(map[adj[n].x].size())) {
00570 
00571                     continue;
00572                 }
00573 
00574                 const t_translation::t_terrain t2 = map[adj[n].x][adj[n].y];
00575                 rating += std::count(adjacent_liked->begin(),adjacent_liked->end(),t2);
00576             }
00577 
00578             if(rating > best_rating) {
00579                 best_loc = map_location(i->x,i->y);
00580                 best_rating = rating;
00581             }
00582         }
00583     }
00584 
00585     return best_loc;
00586 }
00587 
00588 static std::string generate_name(const unit_race& name_generator, const std::string& id,
00589         std::string* base_name=NULL,
00590         utils::string_map* additional_symbols=NULL)
00591 {
00592     const std::vector<std::string>& options = utils::split(string_table[id].str());
00593     if(options.empty() == false) {
00594         const size_t choice = rand()%options.size();
00595         LOG_NG << "calling name generator...\n";
00596         const std::string& name = name_generator.generate_name(unit_race::MALE);
00597         LOG_NG << "name generator returned '" << name << "'\n";
00598         if(base_name != NULL) {
00599             *base_name = name;
00600         }
00601 
00602         LOG_NG << "assigned base name..\n";
00603         utils::string_map  table;
00604         if(additional_symbols == NULL) {
00605             additional_symbols = &table;
00606         }
00607 
00608         LOG_NG << "got additional symbols\n";
00609 
00610         (*additional_symbols)["name"] = name;
00611         LOG_NG << "interpolation variables into '" << options[choice] << "'\n";
00612         return utils::interpolate_variables_into_string(options[choice], additional_symbols);
00613     }
00614 
00615     return "";
00616 }
00617 
00618 // "flood fill" a tile name to adjacent tiles of certain terrain
00619 static void flood_name(const map_location& start, const std::string& name, std::map<map_location,std::string>& tile_names,
00620     const t_translation::t_match& tile_types, const terrain_map& terrain,
00621     unsigned width, unsigned height,
00622     size_t label_count, std::map<map_location,std::string>* labels, const std::string& full_name) {
00623     map_location adj[6];
00624     get_adjacent_tiles(start,adj);
00625     size_t n;
00626     //if adjacent tiles are tiles and unnamed, name them
00627     for (n = 0; n < 6; n++) {
00628         //we do not care for tiles outside the middle part
00629         //cast to unsigned to skip x < 0 || y < 0 as well.
00630         if (unsigned(adj[n].x) >= width / 3 || unsigned(adj[n].y) >= height / 3) {
00631             continue;
00632         }
00633 
00634         const t_translation::t_terrain terr = terrain[adj[n].x + (width / 3)][adj[n].y + (height / 3)];
00635         const location loc(adj[n].x, adj[n].y);
00636         if((t_translation::terrain_matches(terr, tile_types)) && (tile_names.find(loc) == tile_names.end())) {
00637             tile_names.insert(std::pair<location, std::string>(loc, name));
00638             //labeling decision: this is result of trial and error on what looks best in game
00639             if (label_count % 6 == 0) { //ensure that labels do not occur more often than every 6 recursions
00640                 labels->insert(std::pair<map_location, std::string>(loc, full_name));
00641                 label_count++; //ensure that no adjacent tiles get labeled
00642             }
00643             flood_name(adj[n], name, tile_names, tile_types, terrain, width, height, label_count++, labels, full_name);
00644         }
00645     }
00646 }
00647 
00648 namespace {
00649 
00650 // the configuration file should contain a number of [height] tags:
00651 //   [height]
00652 //     height=n
00653 //     terrain=x
00654 //   [/height]
00655 // These should be in descending order of n.
00656 // They are checked sequentially, and if height is greater than n for that tile,
00657 // then the tile is set to terrain type x.
00658 class terrain_height_mapper
00659 {
00660 public:
00661     explicit terrain_height_mapper(const config& cfg);
00662 
00663     bool convert_terrain(const int height) const;
00664     t_translation::t_terrain convert_to() const;
00665 
00666 private:
00667     int terrain_height;
00668     t_translation::t_terrain to;
00669 };
00670 
00671 terrain_height_mapper::terrain_height_mapper(const config& cfg) :
00672     terrain_height(cfg["height"]),
00673     to(t_translation::GRASS_LAND)
00674 {
00675     const std::string& terrain = cfg["terrain"];
00676     if(terrain != "") {
00677         to = t_translation::read_terrain_code(terrain);
00678     }
00679 }
00680 
00681 bool terrain_height_mapper::convert_terrain(const int height) const
00682 {
00683     return height >= terrain_height;
00684 }
00685 
00686 t_translation::t_terrain terrain_height_mapper::convert_to() const
00687 {
00688     return to;
00689 }
00690 
00691 class terrain_converter
00692 {
00693 public:
00694     explicit terrain_converter(const config& cfg);
00695 
00696     bool convert_terrain(const t_translation::t_terrain terrain, const int height, const int temperature) const;
00697     t_translation::t_terrain convert_to() const;
00698 
00699 private:
00700     int min_temp, max_temp, min_height, max_height;
00701     t_translation::t_list from;
00702     t_translation::t_terrain to;
00703 };
00704 
00705 terrain_converter::terrain_converter(const config& cfg) : min_temp(-1),
00706       max_temp(-1), min_height(-1), max_height(-1),
00707       from(t_translation::read_list(cfg["from"])),
00708       to(t_translation::NONE_TERRAIN)
00709 {
00710     min_temp = cfg["min_temperature"].to_int(-100000);
00711     max_temp = cfg["max_temperature"].to_int(100000);
00712     min_height = cfg["min_height"].to_int(-100000);
00713     max_height = cfg["max_height"].to_int(100000);
00714 
00715     const std::string& to_str = cfg["to"];
00716     if(to_str != "") {
00717         to = t_translation::read_terrain_code(to_str);
00718     }
00719 }
00720 
00721 bool terrain_converter::convert_terrain(const t_translation::t_terrain terrain,
00722         const int height, const int temperature) const
00723 {
00724     return std::find(from.begin(),from.end(),terrain) != from.end() && height >= min_height && height <= max_height &&
00725            temperature >= min_temp && temperature <= max_temp && to != t_translation::NONE_TERRAIN;
00726 }
00727 
00728 t_translation::t_terrain terrain_converter::convert_to() const
00729 {
00730     return to;
00731 }
00732 
00733 } // end anon namespace
00734 
00735 std::string default_generate_map(size_t width, size_t height, size_t island_size, size_t island_off_center,
00736                                  size_t iterations, size_t hill_size,
00737                                  size_t max_lakes, size_t nvillages, size_t castle_size, size_t nplayers, bool roads_between_castles,
00738                                  std::map<map_location,std::string>* labels, const config& cfg)
00739 {
00740     log_scope("map generation");
00741 
00742     // Odd widths are nasty
00743     VALIDATE(is_even(width), _("Random maps with an odd width aren't supported."));
00744 
00745     int ticks = SDL_GetTicks();
00746 
00747     // Find out what the 'flatland' on this map is, i.e. grassland.
00748     std::string flatland = cfg["default_flatland"];
00749     if(flatland == "") {
00750         flatland = t_translation::write_terrain_code(t_translation::GRASS_LAND);
00751     }
00752 
00753     const t_translation::t_terrain grassland = t_translation::read_terrain_code(flatland);
00754 
00755     // We want to generate a map that is 9 times bigger
00756     // than the actual size desired.
00757     // Only the middle part of the map will be used,
00758     // but the rest is so that the map we end up using
00759     // can have a context (e.g. rivers flowing from
00760     // out of the map into the map, same for roads, etc.)
00761     width *= 3;
00762     height *= 3;
00763 
00764     LOG_NG << "generating height map...\n";
00765     // Generate the height of everything.
00766     const height_map heights = generate_height_map(width,height,iterations,hill_size,island_size,island_off_center);
00767     LOG_NG << "done generating height map...\n";
00768     LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();
00769 
00770     config naming = cfg.child_or_empty("naming");
00771     // HACK: dummy names to satisfy unit_race requirements
00772     naming["id"] = "village_naming";
00773     naming["plural_name"] = "villages";
00774 
00775     // Make a dummy race for generating names
00776     const unit_race name_generator(naming);
00777 
00778     std::vector<terrain_height_mapper> height_conversion;
00779 
00780     foreach (const config &h, cfg.child_range("height")) {
00781         height_conversion.push_back(terrain_height_mapper(h));
00782     }
00783 
00784     terrain_map terrain(width, t_translation::t_list(height, grassland));
00785     size_t x, y;
00786     for(x = 0; x != heights.size(); ++x) {
00787         for(y = 0; y != heights[x].size(); ++y) {
00788             for(std::vector<terrain_height_mapper>::const_iterator i = height_conversion.begin();
00789                 i != height_conversion.end(); ++i) {
00790                 if(i->convert_terrain(heights[x][y])) {
00791                     terrain[x][y] = i->convert_to();
00792                     break;
00793                 }
00794             }
00795         }
00796     }
00797 
00798     std::map<int, t_translation::coordinate> starting_positions;
00799     LOG_NG << output_map(terrain, starting_positions);
00800     LOG_NG << "placed land forms\n";
00801     LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();
00802 
00803     // Now that we have our basic set of flatland/hills/mountains/water,
00804     // we can place lakes and rivers on the map.
00805     // All rivers are sourced at a lake.
00806     // Lakes must be in high land - at least 'min_lake_height'.
00807     // (Note that terrain below a certain altitude may be made
00808     // into bodies of water in the code above - i.e. 'sea',
00809     // but these are not considered 'lakes', because
00810     // they are not sources of rivers).
00811     //
00812     // We attempt to place 'max_lakes' lakes.
00813     // Each lake will be placed at a random location,
00814     // if that random location meets the minimum terrain requirements for a lake.
00815     // We will also attempt to source a river from each lake.
00816     std::set<location> lake_locs;
00817 
00818     std::map<location, std::string> river_names, lake_names, road_names, bridge_names, mountain_names, forest_names, swamp_names;
00819 
00820     const size_t nlakes = max_lakes > 0 ? (rand()%max_lakes) : 0;
00821     for(size_t lake = 0; lake != nlakes; ++lake) {
00822         for(int tries = 0; tries != 100; ++tries) {
00823             const int x = rand()%width;
00824             const int y = rand()%height;
00825             if (heights[x][y] > cfg["min_lake_height"].to_int()) {
00826                 std::vector<location> river = generate_river(heights,
00827                     terrain, x, y, cfg["river_frequency"]);
00828 
00829                 if(river.empty() == false && labels != NULL) {
00830                     std::string base_name;
00831                     LOG_NG << "generating name for river...\n";
00832                     const std::string& name = generate_name(name_generator,"river_name",&base_name);
00833                     LOG_NG << "named river '" << name << "'\n";
00834                     size_t name_frequency = 20;
00835                     for(std::vector<location>::const_iterator r = river.begin(); r != river.end(); ++r) {
00836 
00837                         const map_location loc(r->x-width/3,r->y-height/3);
00838 
00839                         if(((r - river.begin())%name_frequency) == name_frequency/2) {
00840                             labels->insert(std::pair<map_location,std::string>(loc,name));
00841                         }
00842 
00843                         river_names.insert(std::pair<location,std::string>(loc,base_name));
00844                     }
00845 
00846                     LOG_NG << "put down river name...\n";
00847                 }
00848 
00849                 LOG_NG << "generating lake...\n";
00850                 std::set<location> locs;
00851                 bool res = generate_lake(terrain, x, y, cfg["lake_size"], locs);
00852                 if(res && labels != NULL) {
00853                     bool touches_other_lake = false;
00854 
00855                     std::string base_name;
00856                     const std::string& name = generate_name(name_generator,"lake_name",&base_name);
00857 
00858                     std::set<location>::const_iterator i;
00859 
00860                     // Only generate a name if the lake hasn't touched any other lakes,
00861                     // so that we don't end up with one big lake with multiple names.
00862                     for(i = locs.begin(); i != locs.end(); ++i) {
00863                         if(lake_locs.count(*i) != 0) {
00864                             touches_other_lake = true;
00865 
00866                             // Reassign the name of this lake to be the same as the other lake
00867                             const location loc(i->x-width/3,i->y-height/3);
00868                             const std::map<location,std::string>::const_iterator other_name = lake_names.find(loc);
00869                             if(other_name != lake_names.end()) {
00870                                 base_name = other_name->second;
00871                             }
00872                         }
00873 
00874                         lake_locs.insert(*i);
00875                     }
00876 
00877                     if(!touches_other_lake) {
00878                         const map_location loc(x-width/3,y-height/3);
00879                         labels->erase(loc);
00880                         labels->insert(std::pair<map_location,std::string>(loc,name));
00881                     }
00882 
00883                     for(i = locs.begin(); i != locs.end(); ++i) {
00884                         const location loc(i->x-width/3,i->y-height/3);
00885                         lake_names.insert(std::pair<location, std::string>(loc, base_name));
00886                     }
00887                 }
00888 
00889                 break;
00890             }
00891         }
00892     }
00893 
00894     LOG_NG << "done generating rivers...\n";
00895     LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();
00896 
00897     const size_t default_dimensions = 40*40*9;
00898 
00899     /*
00900      * Convert grassland terrain to other types of flat terrain.
00901      *
00902      * We generate a 'temperature map' which uses the height generation
00903      * algorithm to generate the temperature levels all over the map.  Then we
00904      * can use a combination of height and terrain to divide terrain up into
00905      * more interesting types than the default.
00906      */
00907     const height_map temperature_map = generate_height_map(width,height,
00908         cfg["temperature_iterations"].to_int() * width * height / default_dimensions,
00909         cfg["temperature_size"], 0, 0);
00910 
00911     LOG_NG << "generated temperature map...\n";
00912     LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();
00913 
00914     std::vector<terrain_converter> converters;
00915     foreach (const config &cv, cfg.child_range("convert")) {
00916         converters.push_back(terrain_converter(cv));
00917     }
00918 
00919     LOG_NG << "created terrain converters\n";
00920     LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();
00921 
00922 
00923     // Iterate over every flatland tile, and determine
00924     // what type of flatland it is, based on our [convert] tags.
00925     for(x = 0; x != width; ++x) {
00926         for(y = 0; y != height; ++y) {
00927             for(std::vector<terrain_converter>::const_iterator i = converters.begin(); i != converters.end(); ++i) {
00928                 if(i->convert_terrain(terrain[x][y],heights[x][y],temperature_map[x][y])) {
00929                     terrain[x][y] = i->convert_to();
00930                     break;
00931                 }
00932             }
00933         }
00934     }
00935 
00936     LOG_NG << "placing villages...\n";
00937     LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();
00938 
00939     /*
00940      * Place villages in a 'grid', to make placing fair, but with villages
00941      * displaced from their position according to terrain and randomness, to
00942      * add some variety.
00943      */
00944     std::set<location> villages;
00945 
00946     LOG_NG << "placing castles...\n";
00947 
00948     /** Try to find configuration for castles. */
00949     const config &castle_config = cfg.child("castle");
00950     if (!castle_config) {
00951         LOG_NG << "Could not find castle configuration\n";
00952         return std::string();
00953     }
00954 
00955     /*
00956      * Castle configuration tag contains a 'valid_terrain' attribute which is a
00957      * list of terrains that the castle may appear on.
00958      */
00959     const t_translation::t_list list =
00960         t_translation::read_list(castle_config["valid_terrain"]);
00961 
00962     const is_valid_terrain terrain_tester(terrain, list);
00963 
00964     /*
00965      * Attempt to place castles at random.
00966      *
00967      * Once we have placed castles, we run a sanity check to make sure that the
00968      * castles are well-placed.  If the castles are not well-placed, we try
00969      * again.  Definition of 'well-placed' is if no two castles are closer than
00970      * 'min_distance' hexes from each other, and the castles appear on a
00971      * terrain listed in 'valid_terrain'.
00972      */
00973     std::vector<location> castles;
00974     std::set<location> failed_locs;
00975 
00976     for(size_t player = 0; player != nplayers; ++player) {
00977         LOG_NG << "placing castle for " << player << "\n";
00978         log_scope("placing castle");
00979         const int min_x = width/3 + 3;
00980         const int min_y = height/3 + 3;
00981         const int max_x = (width/3)*2 - 4;
00982         const int max_y = (height/3)*2 - 4;
00983         int min_distance = castle_config["min_distance"];
00984 
00985         location best_loc;
00986         int best_ranking = 0;
00987         for(int x = min_x; x != max_x; ++x) {
00988             for(int y = min_y; y != max_y; ++y) {
00989                 const location loc(x,y);
00990                 if(failed_locs.count(loc)) {
00991                     continue;
00992                 }
00993 
00994                 const int ranking = rank_castle_location(x,y,terrain_tester,min_x,max_x,min_y,max_y,min_distance,castles,best_ranking);
00995                 if(ranking <= 0) {
00996                     failed_locs.insert(loc);
00997                 }
00998 
00999                 if(ranking > best_ranking) {
01000                     best_ranking = ranking;
01001                     best_loc = loc;
01002                 }
01003             }
01004         }
01005         if(best_ranking == 0) {
01006             ERR_NG << "No castle location found, aborting.\n";
01007             std::string error = _("No valid castle location found. Too many or too few mountain hexes? (please check the 'max hill size' parameter)");
01008             throw mapgen_exception(error);
01009         }
01010         assert(std::find(castles.begin(), castles.end(), best_loc) == castles.end());
01011         castles.push_back(best_loc);
01012         // Make sure the location can't get a second castle.
01013         failed_locs.insert(best_loc);
01014     }
01015 
01016     LOG_NG << "placing roads...\n";
01017     LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();
01018 
01019     // Place roads.
01020     // We select two tiles at random locations on the borders
01021     // of the map, and try to build roads between them.
01022     int nroads = cfg["roads"];
01023     if(roads_between_castles) {
01024         nroads += castles.size()*castles.size();
01025     }
01026 
01027     std::set<location> bridges;
01028 
01029     road_path_calculator calc(terrain,cfg);
01030     for (int road = 0; road != nroads; ++road) {
01031         log_scope("creating road");
01032 
01033         /*
01034          * We want the locations to be on the portion of the map we're actually
01035          * going to use, since roads on other parts of the map won't have any
01036          * influence, and doing it like this will be quicker.
01037          */
01038         location src = random_point_at_side(width/3 + 2,height/3 + 2);
01039         location dst = random_point_at_side(width/3 + 2,height/3 + 2);
01040 
01041         src.x += width/3 - 1;
01042         src.y += height/3 - 1;
01043         dst.x += width/3 - 1;
01044         dst.y += height/3 - 1;
01045 
01046         if (roads_between_castles && road < int(castles.size() * castles.size())) {
01047             const size_t src_castle = road/castles.size();
01048             const size_t dst_castle = road%castles.size();
01049             if(src_castle >= dst_castle) {
01050                 continue;
01051             }
01052 
01053             src = castles[src_castle];
01054             dst = castles[dst_castle];
01055         }
01056 
01057         // If the road isn't very interesting (on the same border), don't draw it.
01058         else if(src.x == dst.x || src.y == dst.y) {
01059             continue;
01060         }
01061 
01062         if (calc.cost(src, 0.0) >= 1000.0 || calc.cost(dst, 0.0) >= 1000.0) {
01063             continue;
01064         }
01065 
01066         // Search a path out for the road
01067         pathfind::plain_route rt = pathfind::a_star_search(src, dst, 10000.0, &calc, width, height);
01068 
01069         std::string road_base_name;
01070         const std::string& name = generate_name(name_generator, "road_name", &road_base_name);
01071         const int name_frequency = 20;
01072         int name_count = 0;
01073 
01074         bool on_bridge = false;
01075 
01076         // Draw the road.
01077         // If the search failed, rt.steps will simply be empty.
01078         for(std::vector<location>::const_iterator step = rt.steps.begin();
01079                 step != rt.steps.end(); ++step) {
01080 
01081             const int x = step->x;
01082             const int y = step->y;
01083 
01084             if(x < 0 || y < 0 || x >= static_cast<long>(width) ||
01085                     y >= static_cast<long>(height)) {
01086 
01087                 continue;
01088             }
01089 
01090             // Find the configuration which tells us
01091             // what to convert this tile to, to make it into a road.
01092             if (const config &child = cfg.find_child("road_cost", "terrain",
01093                     t_translation::write_terrain_code(terrain[x][y])))
01094             {
01095                 // Convert to bridge means that we want to convert
01096                 // depending upon the direction the road is going.
01097                 // Typically it will be in a format like,
01098                 // convert_to_bridge=\,|,/
01099                 // '|' will be used if the road is going north-south
01100                 // '/' will be used if the road is going south west-north east
01101                 // '\' will be used if the road is going south east-north west
01102                 // The terrain will be left unchanged otherwise
01103                 // (if there is no clear direction).
01104                 const std::string &convert_to_bridge = child["convert_to_bridge"];
01105                 if(convert_to_bridge.empty() == false) {
01106                     if(step == rt.steps.begin() || step+1 == rt.steps.end())
01107                         continue;
01108 
01109                     const location& last = *(step-1);
01110                     const location& next = *(step+1);
01111 
01112                     location adj[6];
01113                     get_adjacent_tiles(*step,adj);
01114 
01115                     int direction = -1;
01116 
01117                     // If we are going north-south
01118                     if((last == adj[0] && next == adj[3]) || (last == adj[3] && next == adj[0])) {
01119                         direction = 0;
01120                     }
01121 
01122                     // If we are going south west-north east
01123                     else if((last == adj[1] && next == adj[4]) || (last == adj[4] && next == adj[1])) {
01124                         direction = 1;
01125                     }
01126 
01127                     // If we are going south east-north west
01128                     else if((last == adj[2] && next == adj[5]) || (last == adj[5] && next == adj[2])) {
01129                         direction = 2;
01130                     }
01131 
01132                     if(labels != NULL && on_bridge == false) {
01133                         on_bridge = true;
01134                         std::string bridge_base_name;
01135                         const std::string& name = generate_name(name_generator, "bridge_name", &bridge_base_name);
01136                         const location loc(x - width / 3, y-height/3);
01137                         labels->insert(std::pair<map_location,std::string>(loc,name));
01138                         bridge_names.insert(std::pair<location,std::string>(loc, bridge_base_name)); //add to use for village naming
01139                         bridges.insert(loc);
01140                     }
01141 
01142                     if(direction != -1) {
01143                         const std::vector<std::string> items = utils::split(convert_to_bridge);
01144                         if(size_t(direction) < items.size() && items[direction].empty() == false) {
01145                             terrain[x][y] = t_translation::read_terrain_code(items[direction]);
01146                         }
01147 
01148                         continue;
01149                     }
01150                 } else {
01151                     on_bridge = false;
01152                 }
01153 
01154                 // Just a plain terrain substitution for a road
01155                 const std::string &convert_to = child["convert_to"];
01156                 if(convert_to.empty() == false) {
01157                     const t_translation::t_terrain letter =
01158                         t_translation::read_terrain_code(convert_to);
01159                     if(labels != NULL && terrain[x][y] != letter && name_count++ == name_frequency && on_bridge == false) {
01160                         labels->insert(std::pair<map_location,std::string>(map_location(x-width/3,y-height/3),name));
01161                         name_count = 0;
01162                     }
01163 
01164                     terrain[x][y] = letter;
01165                     const location loc(x - width / 3, y - height / 3); //add to use for village naming
01166                     road_names.insert(std::pair<location,std::string>(loc, road_base_name));
01167                 }
01168             }
01169         }
01170 
01171         LOG_NG << "looked at " << calc.calls << " locations\n";
01172     }
01173 
01174 
01175     // Now that road drawing is done, we can plonk down the castles.
01176     for(std::vector<location>::const_iterator c = castles.begin(); c != castles.end(); ++c) {
01177         if(c->valid() == false) {
01178             continue;
01179         }
01180 
01181         const int x = c->x;
01182         const int y = c->y;
01183         const int player = c - castles.begin() + 1;
01184         const struct t_translation::coordinate coord(x, y);
01185         starting_positions.insert(std::pair<int, t_translation::coordinate>(player, coord));
01186         terrain[x][y] = t_translation::HUMAN_KEEP;
01187 
01188         const int castles[13][2] = {
01189           {-1, 0}, {-1, -1}, {0, -1}, {1, -1}, {1, 0}, {0, 1}, {-1, 1},
01190           {-2, 1}, {-2, 0}, {-2, -1}, {-1, -2}, {0, -2}, {1, -2}
01191         };
01192 
01193         for (size_t i = 0; i < castle_size - 1; i++) {
01194           terrain[x+castles[i][0]][y+castles[i][1]] = t_translation::HUMAN_CASTLE;
01195         }
01196 
01197         // Remove all labels under the castle tiles
01198         if(labels != NULL) {
01199           labels->erase(location(x-width/3,y-height/3));
01200           for (size_t i = 0; i < castle_size - 1; i++) {
01201             labels->erase(location(x+castles[i][0]-width/3,
01202                        y+castles[i][1]-height/3));
01203           }
01204 
01205         }
01206 
01207     }
01208 
01209     LOG_NG << "placed castles\n";
01210     LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();
01211 
01212     /*Random naming for landforms: mountains, forests, swamps, hills
01213      *we name these now that everything else is placed (as e.g., placing
01214      * roads could split a forest)
01215      */
01216     for (x = width / 3; x < (width / 3)*2; x++) {
01217         for (y = height / 3; y < (height / 3) * 2;y++) {
01218         //check the terrain of the tile
01219         const location loc(x - width / 3, y - height / 3);
01220         const t_translation::t_terrain terr = terrain[x][y];
01221         std::string name, base_name;
01222         std::set<std::string> used_names;
01223         if (t_translation::terrain_matches(terr, t_translation::ALL_MOUNTAINS)) {
01224             //name every 15th mountain
01225             if ((rand()%15) == 0) {
01226                 for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) {
01227                     name = generate_name(name_generator, "mountain_name", &base_name);
01228                 }
01229                 labels->insert(std::pair<map_location, std::string>(loc, name));
01230                 mountain_names.insert(std::pair<location, std::string>(loc, base_name));
01231             }
01232         }
01233         else if (t_translation::terrain_matches(terr, t_translation::ALL_FORESTS)) {
01234             //if the forest tile is not named yet, name it
01235             const std::map<location, std::string>::const_iterator forest_name = forest_names.find(loc);
01236             if(forest_name == forest_names.end()) {
01237                 for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) {
01238                     name = generate_name(name_generator, "forest_name", &base_name);
01239                 }
01240                 forest_names.insert(std::pair<location, std::string>(loc, base_name));
01241                 // name all connected forest tiles accordingly
01242                 flood_name(loc, base_name, forest_names, t_translation::ALL_FORESTS, terrain, width, height, 0, labels, name);
01243             }
01244         }
01245         else if (t_translation::terrain_matches(terr, t_translation::ALL_SWAMPS)) {
01246             //if the swamp tile is not named yet, name it
01247             const std::map<location, std::string>::const_iterator swamp_name = swamp_names.find(loc);
01248             if(swamp_name == swamp_names.end()) {
01249                 for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) {
01250                     name = generate_name(name_generator, "swamp_name", &base_name);
01251                 }
01252                 swamp_names.insert(std::pair<location, std::string>(loc, base_name));
01253                 // name all connected swamp tiles accordingly
01254                 flood_name(loc, base_name, swamp_names, t_translation::ALL_SWAMPS, terrain, width, height, 0, labels, name);
01255             }
01256         }
01257 
01258         }
01259     }
01260 
01261     if (nvillages > 0)
01262     {
01263         config naming_cfg = cfg.child_or_empty("village_naming");
01264         // HACK: dummy names to satisfy unit_race requirements
01265         naming_cfg["id"] = "village_naming";
01266         naming_cfg["plural_name"] = "villages";
01267 
01268         const unit_race village_names_generator(naming_cfg);
01269 
01270         // First we work out the size of the x and y distance between villages
01271         const size_t tiles_per_village = ((width*height)/9)/nvillages;
01272         size_t village_x = 1, village_y = 1;
01273 
01274         // Alternate between incrementing the x and y value.
01275         // When they are high enough to equal or exceed the tiles_per_village,
01276         // then we have them to the value we want them at.
01277         while(village_x*village_y < tiles_per_village) {
01278             if(village_x < village_y) {
01279                 ++village_x;
01280             } else {
01281                 ++village_y;
01282             }
01283         }
01284 
01285         std::set<std::string> used_names;
01286         tcode_list_cache adj_liked_cache;
01287 
01288         for(size_t vx = 0; vx < width; vx += village_x) {
01289             LOG_NG << "village at " << vx << "\n";
01290             for(size_t vy = rand()%village_y; vy < height; vy += village_y) {
01291 
01292                 const size_t add_x = rand()%3;
01293                 const size_t add_y = rand()%3;
01294                 const size_t x = (vx + add_x) - 1;
01295                 const size_t y = (vy + add_y) - 1;
01296 
01297                 const map_location res = place_village(terrain,x,y,2,cfg,adj_liked_cache);
01298 
01299                 if(res.x >= static_cast<long>(width) / 3 &&
01300                         res.x  < static_cast<long>(width * 2) / 3 &&
01301                         res.y >= static_cast<long>(height) / 3 &&
01302                         res.y  < static_cast<long>(height * 2) / 3) {
01303 
01304                     const std::string str =
01305                         t_translation::write_terrain_code(terrain[res.x][res.y]);
01306                     if (const config &child = cfg.find_child("village", "terrain", str))
01307                     {
01308                         const std::string &convert_to = child["convert_to"];
01309                         if(convert_to != "") {
01310                             terrain[res.x][res.y] =
01311                                 t_translation::read_terrain_code(convert_to);
01312 
01313                             villages.insert(res);
01314 
01315                             if(labels != NULL && naming_cfg.empty() == false) {
01316                                 const map_location loc(res.x-width/3,res.y-height/3);
01317 
01318                                 map_location adj[6];
01319                                 get_adjacent_tiles(loc,adj);
01320 
01321                                 std::string name_type = "village_name";
01322                                 const t_translation::t_list
01323                                     field    = t_translation::t_list(1, t_translation::GRASS_LAND),
01324                                     forest   = t_translation::t_list(1, t_translation::FOREST),
01325                                     mountain = t_translation::t_list(1, t_translation::MOUNTAIN),
01326                                     hill     = t_translation::t_list(1, t_translation::HILL);
01327 
01328                                 size_t field_count = 0, forest_count = 0, mountain_count = 0, hill_count = 0;
01329 
01330                                 utils::string_map symbols;
01331 
01332                                 size_t n;
01333                                 for(n = 0; n != 6; ++n) {
01334                                     const std::map<location,std::string>::const_iterator road_name = road_names.find(adj[n]);
01335                                     if(road_name != road_names.end()) {
01336                                         symbols["road"] = road_name->second;
01337                                         name_type = "village_name_road";
01338                                         break;
01339                                     }
01340 
01341                                     const std::map<location,std::string>::const_iterator river_name = river_names.find(adj[n]);
01342                                     if(river_name != river_names.end()) {
01343                                         symbols["river"] = river_name->second;
01344                                         name_type = "village_name_river";
01345 
01346                                         const std::map<location,std::string>::const_iterator bridge_name = bridge_names.find(adj[n]);
01347                                         if(bridge_name != bridge_names.end()) {
01348                                         //we should always end up here, since if there is an adjacent bridge, there has to be an adjacent river too
01349                                         symbols["bridge"] = bridge_name->second;
01350                                         name_type = "village_name_river_bridge";
01351                                         }
01352 
01353                                         break;
01354                                     }
01355 
01356                                     const std::map<location,std::string>::const_iterator forest_name = forest_names.find(adj[n]);
01357                                     if(forest_name != forest_names.end()) {
01358                                         symbols["forest"] = forest_name->second;
01359                                         name_type = "village_name_forest";
01360                                         break;
01361                                     }
01362 
01363                                     const std::map<location,std::string>::const_iterator lake_name = lake_names.find(adj[n]);
01364                                     if(lake_name != lake_names.end()) {
01365                                         symbols["lake"] = lake_name->second;
01366                                         name_type = "village_name_lake";
01367                                         break;
01368                                     }
01369 
01370                                     const std::map<location,std::string>::const_iterator mountain_name = mountain_names.find(adj[n]);
01371                                     if(mountain_name != mountain_names.end()) {
01372                                         symbols["mountain"] = mountain_name->second;
01373                                         name_type = "village_name_mountain";
01374                                         break;
01375                                     }
01376 
01377                                     const std::map<location,std::string>::const_iterator swamp_name = swamp_names.find(adj[n]);
01378                                     if(swamp_name != swamp_names.end()) {
01379                                         symbols["swamp"] = swamp_name->second;
01380                                         name_type = "village_name_swamp";
01381                                         break;
01382                                     }
01383 
01384                                     const t_translation::t_terrain terr =
01385                                         terrain[adj[n].x+width/3][adj[n].y+height/3];
01386 
01387                                     if(std::count(field.begin(),field.end(),terr) > 0) {
01388                                         ++field_count;
01389                                     } else if(std::count(forest.begin(),forest.end(),terr) > 0) {
01390                                         ++forest_count;
01391                                     } else if(std::count(hill.begin(),hill.end(),terr) > 0) {
01392                                         ++hill_count;
01393                                     } else if(std::count(mountain.begin(),mountain.end(),terr) > 0) {
01394                                         ++mountain_count;
01395                                     }
01396                                 }
01397 
01398                                 if(n == 6) {
01399                                     if(field_count == 6) {
01400                                         name_type = "village_name_grassland";
01401                                     } else if(forest_count >= 2) {
01402                                         name_type = "village_name_forest";
01403                                     } else if(mountain_count >= 1) {
01404                                         name_type = "village_name_mountain_anonymous";
01405                                     } else if(hill_count >= 2) {
01406                                         name_type = "village_name_hill";
01407                                     }
01408                                 }
01409 
01410                                 std::string name;
01411                                 for(size_t ntry = 0; ntry != 30 && (ntry == 0 || used_names.count(name) > 0); ++ntry) {
01412                                     name = generate_name(village_names_generator,name_type,NULL,&symbols);
01413                                 }
01414 
01415                                 used_names.insert(name);
01416                                 labels->insert(std::pair<map_location,std::string>(loc,name));
01417                             }
01418                         }
01419                     }
01420                 }
01421             }
01422         }
01423     }
01424 
01425     LOG_NG << "placed villages\n";
01426     LOG_NG << (SDL_GetTicks() - ticks) << "\n"; ticks = SDL_GetTicks();
01427 
01428     return output_map(terrain, starting_positions);
01429 }
01430 
01431 namespace {
01432 
01433 typedef std::map<std::string,map_generator*> generator_map;
01434 generator_map generators;
01435 
01436 }
01437 
01438 #ifdef TEST_MAPGEN
01439 
01440 /** Standalone testprogram for the mapgenerator. */
01441 int main(int argc, char** argv)
01442 {
01443     int x = 50, y = 50, iterations = 50,
01444         hill_size = 50, lakes=3,
01445         nvillages = 25, nplayers = 2;
01446     if(argc >= 2) {
01447         x = atoi(argv[1]);
01448     }
01449 
01450     if(argc >= 3) {
01451         y = atoi(argv[2]);
01452     }
01453 
01454     if(argc >= 4) {
01455         iterations = atoi(argv[3]);
01456     }
01457 
01458     if(argc >= 5) {
01459         hill_size = atoi(argv[4]);
01460     }
01461 
01462     if(argc >= 6) {
01463         lakes = atoi(argv[5]);
01464     }
01465 
01466     if(argc >= 7) {
01467         nvillages = atoi(argv[6]);
01468     }
01469 
01470     if(argc >= 8) {
01471         nplayers = atoi(argv[7]);
01472     }
01473 
01474     srand(time(NULL));
01475     std::cout << generate_map(x,y,iterations,hill_size,lakes,nvillages,nplayers) << "\n";
01476 }
01477 
01478 #endif
 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