This version has the last major feature I wanted to introduce:
City generation based on landscape.
The basic idea is, use the information the river generator collects anyway while it runs for positioning cities in a more real-world-like fashion than the old generator could. For this purpose, I first generate a bunch of scores (e.g. river_size_score, river_branch_score, flat_land_score) for each 16x16 grid section on the map.
Remember: In terms of town generation, the term "city" refers to big town that grows faster, and the term "town" to any other town.
Then, a bunch of so-called TownPlacers run with certain probabilities, and try to choose a tile that fits their particular requirement to a city. A short look into the source code:
Code: Select all
HugeRiverCityPlacer huge_river_city_placer;
RiverBranchCityPlacer river_branch_city_placer;
SmallRiverCityPlacer small_river_city_placer;
LakeCityPlacer lake_city_placer;
FlatLandPlacer flat_land_placer;
CoastPlacer coast_placer;
std::vector<TownPlacerWithProbability> city_placers = std::vector<TownPlacerWithProbability>();
city_placers.push_back(TownPlacerWithProbability(&huge_river_city_placer, 300));
city_placers.push_back(TownPlacerWithProbability(&river_branch_city_placer, 200));
city_placers.push_back(TownPlacerWithProbability(&small_river_city_placer, 200));
city_placers.push_back(TownPlacerWithProbability(&lake_city_placer, 50));
city_placers.push_back(TownPlacerWithProbability(&flat_land_placer, 200));
city_placers.push_back(TownPlacerWithProbability(&coast_placer, 50));
this->PlaceTowns(city_scores, cities_generated, number_of_cities, 333, towns_generated,
number_of_cities * 2, city_placers, townnameparts, town_names, do_continue);
DEBUG(misc, 0, "Finished city generation, generated %u cities and %u towns", cities_generated, towns_generated);
SmallRiverTownPlacer small_river_town_placer;
MountainTownPlacer mountain_town_placer;
ValleyTownPlacer valley_town_placer;
std::vector<TownPlacerWithProbability> town_placers = std::vector<TownPlacerWithProbability>();
town_placers.push_back(TownPlacerWithProbability(&small_river_town_placer, 400));
town_placers.push_back(TownPlacerWithProbability(&mountain_town_placer, 150));
town_placers.push_back(TownPlacerWithProbability(&flat_land_placer, 300));
town_placers.push_back(TownPlacerWithProbability(&coast_placer, 50));
town_placers.push_back(TownPlacerWithProbability(&valley_town_placer, 100));
this->PlaceTowns(city_scores, cities_generated, number_of_cities, 0, towns_generated,
number_of_towns, town_placers, townnameparts, town_names, do_continue);
First place 1/3 cities and 2/3 towns, then place only towns until all required cities and towns are placed. The total numbers is based on the well-known config-settings, I didn´t touch them.
In each of those two steps, iteratively call one out of the placers with the given probability. E.g. a HugeRiverCityPlacer is intended to place a city somewhere near a huge river. Its special implementation is rather straightforward:
Code: Select all
struct HugeRiverCityPlacer : public TownPlacer {
public:
virtual const char* ToString() { return "HugeRiverCityPlacer"; }
virtual bool PlaceInGridSection(CityGridIndex c, CityScore *score)
{
return score->river_size_score > 300
&& (score->flat_score > 400 || RandomRange(3) < 1);
}
virtual bool PlaceAtTile(CityGridIndex c, CityScore *score, TileIndex tile)
{
return TileHeight(tile) < _settings_game.construction.max_heightlevel / 3;
}
};
Only consider grid sections that contain a river with at least 30 percent the flow of the biggest river on map, and with at least 40 percent flat tiles. Inside such a section, only consider tiles that are lower than 1/3 the allowed max heightlevel, to avoid funding cities somewhere up in the mountains.
Similar, the RiverBranchCityPlacer founds a city near the point where two or more rivers merge. The corresponding score is the higher the more equal-sized the rivers are, and the more flow they have.
The I introduced a two-step-behaviour where in the first step also 2/3 towns are created to avoid the effect that the cities occupy all the space suitable for them before any town is founded, which can lead to the effect that there are whole regions of map with only cities, but no towns at all.
In the current version, the configuration shown above is hardcoded. Also, you cannot yet disable city generation (if you really want to get rid of it, outcomment the respective call near the end of rivers_rainfall.cpp, and return false to notify the old city generator that it should do the job) In the end, this would probably need another configuration window similar to the one I introduced for the rivers. But this is nothing I can implement right now (for reasons of real-world-time-constraint). Also, before implementing such a window, probably some experience what config options are actually required would be useful.
For example, the above settings are tested on a rather mountainious heightmap with some big valley in the middle (see attached screenshot). If in constrast, the map contains a lot of ocean, then one might want to apply the CoastPlacer with a much higher probability, i.e. the probabilities will certainly be specific to the kind of map being generated to some extent (although of course a Placer that doesn´t find an appropriate place will do nothing after some computation attempts, and leave room for the next Placer).
Have fun.