changeset 13999:9d1494492b63 draft

(svn r18541) -Feature: Additional map variety option for TGP landscape generator. Evolved from curve map idea from Zephyris.
author peter1138 <peter1138@openttd.org>
date Sat, 19 Dec 2009 18:00:38 +0000
parents 3d6fd0265851
children fa28786162a2
files src/genworld_gui.cpp src/lang/english.txt src/settings_type.h src/table/settings.h src/tgp.cpp
diffstat 5 files changed, 161 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- a/src/genworld_gui.cpp
+++ b/src/genworld_gui.cpp
@@ -90,6 +90,7 @@
 	GLAND_TERRAIN_PULLDOWN,
 	GLAND_WATER_PULLDOWN,
 	GLAND_SMOOTHNESS_PULLDOWN,
+	GLAND_VARIETY_PULLDOWN,
 
 	GLAND_BORDER_TYPES,
 	GLAND_BORDERS_RANDOM,
@@ -130,6 +131,7 @@
 					NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_RANDOM_SEED, STR_NULL), SetFill(1, 1),
 					NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_QUANTITY_OF_SEA_LAKES, STR_NULL), SetFill(1, 1),
 					NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_TREE_PLACER, STR_NULL), SetFill(1, 1),
+					NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_VARIETY, STR_NULL), SetFill(1, 1),
 					NWidget(WWT_TEXT, COLOUR_ORANGE, GLAND_BORDER_TYPES), SetDataTip(STR_MAPGEN_BORDER_TYPE, STR_NULL), SetFill(1, 1),
 				EndContainer(),
 				/* Widgets at the right of the labels. */
@@ -146,6 +148,7 @@
 					NWidget(WWT_EDITBOX, COLOUR_WHITE, GLAND_RANDOM_EDITBOX), SetDataTip(STR_MAPGEN_RANDOM_SEED_OSKTITLE, STR_MAPGEN_RANDOM_SEED_HELP), SetFill(1, 1),
 					NWidget(WWT_DROPDOWN, COLOUR_ORANGE, GLAND_WATER_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0),
 					NWidget(WWT_DROPDOWN, COLOUR_ORANGE, GLAND_TREE_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0),
+					NWidget(WWT_DROPDOWN, COLOUR_ORANGE, GLAND_VARIETY_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0),
 					NWidget(WWT_PUSHTXTBTN, COLOUR_ORANGE, GLAND_BORDERS_RANDOM), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0),
 				EndContainer(),
 			EndContainer(),
@@ -239,6 +242,7 @@
 				NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_NUMBER_OF_INDUSTRIES, STR_NULL), SetFill(1, 1),
 				NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_RANDOM_SEED, STR_NULL), SetFill(1, 1),
 				NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_TREE_PLACER, STR_NULL), SetFill(1, 1),
+				NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_VARIETY, STR_NULL), SetFill(1, 1),
 				NWidget(WWT_TEXT, COLOUR_ORANGE), SetDataTip(STR_MAPGEN_HEIGHTMAP_ROTATION, STR_NULL), SetFill(1, 1),
 			EndContainer(),
 			/* Widgets at the right of the labels. */
@@ -254,6 +258,7 @@
 				NWidget(WWT_DROPDOWN, COLOUR_ORANGE, GLAND_INDUSTRY_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0),
 				NWidget(WWT_EDITBOX, COLOUR_WHITE, GLAND_RANDOM_EDITBOX), SetDataTip(STR_MAPGEN_RANDOM_SEED_OSKTITLE, STR_MAPGEN_RANDOM_SEED_HELP), SetFill(1, 1),
 				NWidget(WWT_DROPDOWN, COLOUR_ORANGE, GLAND_TREE_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0),
+				NWidget(WWT_DROPDOWN, COLOUR_ORANGE, GLAND_VARIETY_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0),
 				NWidget(WWT_DROPDOWN, COLOUR_ORANGE, GLAND_HEIGHTMAP_ROTATION_PULLDOWN), SetDataTip(STR_JUST_STRING, STR_NULL), SetFill(1, 0),
 			EndContainer(),
 			NWidget(NWID_VERTICAL), SetPIP(0, 4, 0),
@@ -330,6 +335,7 @@
 static const StringID _landscape[]   = {STR_CONFIG_SETTING_LAND_GENERATOR_ORIGINAL, STR_CONFIG_SETTING_LAND_GENERATOR_TERRA_GENESIS, INVALID_STRING_ID};
 static const StringID _num_towns[]   = {STR_NUM_VERY_LOW, STR_NUM_LOW, STR_NUM_NORMAL, STR_NUM_HIGH, STR_NUM_CUSTOM, INVALID_STRING_ID};
 static const StringID _num_inds[]    = {STR_NONE, STR_NUM_VERY_LOW, STR_NUM_LOW, STR_NUM_NORMAL, STR_NUM_HIGH, INVALID_STRING_ID};
+static const StringID _variety[]     = {STR_NONE, STR_NUM_VERY_LOW, STR_NUM_LOW, STR_NUM_MEDIUM, STR_NUM_HIGH, STR_NUM_VERY_HIGH, INVALID_STRING_ID};
 
 struct GenerateLandscapeWindow : public QueryStringBaseWindow {
 	uint widget_id;
@@ -369,6 +375,7 @@
 			case GLAND_TERRAIN_PULLDOWN:    SetDParam(0, _elevations[_settings_newgame.difficulty.terrain_type]); break;
 			case GLAND_WATER_PULLDOWN:      SetDParam(0, _sea_lakes[_settings_newgame.difficulty.quantity_sea_lakes]); break;
 			case GLAND_SMOOTHNESS_PULLDOWN: SetDParam(0, _smoothness[_settings_newgame.game_creation.tgen_smoothness]); break;
+			case GLAND_VARIETY_PULLDOWN:    SetDParam(0, _variety[_settings_newgame.game_creation.variety]); break;
 			case GLAND_BORDERS_RANDOM:      SetDParam(0, (_settings_newgame.game_creation.water_borders == BORDERS_RANDOM) ? STR_MAPGEN_BORDER_RANDOMIZE : STR_MAPGEN_BORDER_MANUAL); break;
 			case GLAND_WATER_NE: SetDParam(0, (_settings_newgame.game_creation.water_borders == BORDERS_RANDOM) ? STR_MAPGEN_BORDER_RANDOM : HasBit(_settings_newgame.game_creation.water_borders, BORDER_NE) ? STR_MAPGEN_BORDER_WATER : STR_MAPGEN_BORDER_FREEFORM); break;
 			case GLAND_WATER_NW: SetDParam(0, (_settings_newgame.game_creation.water_borders == BORDERS_RANDOM) ? STR_MAPGEN_BORDER_RANDOM : HasBit(_settings_newgame.game_creation.water_borders, BORDER_NW) ? STR_MAPGEN_BORDER_WATER : STR_MAPGEN_BORDER_FREEFORM); break;
@@ -421,6 +428,7 @@
 			case GLAND_TERRAIN_PULLDOWN:    strs = _elevations; break;
 			case GLAND_WATER_PULLDOWN:      strs = _sea_lakes; break;
 			case GLAND_SMOOTHNESS_PULLDOWN: strs = _smoothness; break;
+			case GLAND_VARIETY_PULLDOWN:    strs = _variety; break;
 			case GLAND_HEIGHTMAP_ROTATION_PULLDOWN: strs = _rotation; break;
 			case GLAND_BORDERS_RANDOM:
 				*size = maxdim(GetStringBoundingBox(STR_MAPGEN_BORDER_RANDOMIZE), GetStringBoundingBox(STR_MAPGEN_BORDER_MANUAL));
@@ -469,6 +477,7 @@
 		/* You can't select smoothness / non-water borders if not terragenesis */
 		if (mode == GLWP_GENERATE) {
 			this->SetWidgetDisabledState(GLAND_SMOOTHNESS_PULLDOWN, _settings_newgame.game_creation.land_generator == 0);
+			this->SetWidgetDisabledState(GLAND_VARIETY_PULLDOWN, _settings_newgame.game_creation.land_generator == 0);
 			this->SetWidgetDisabledState(GLAND_BORDERS_RANDOM, _settings_newgame.game_creation.land_generator == 0 || !_settings_newgame.construction.freeform_edges);
 			this->SetWidgetsDisabledState(_settings_newgame.game_creation.land_generator == 0 || !_settings_newgame.construction.freeform_edges || _settings_newgame.game_creation.water_borders == BORDERS_RANDOM,
 					GLAND_WATER_NW, GLAND_WATER_NE, GLAND_WATER_SE, GLAND_WATER_SW, WIDGET_LIST_END);
@@ -614,6 +623,10 @@
 				ShowDropDownMenu(this, _smoothness, _settings_newgame.game_creation.tgen_smoothness, GLAND_SMOOTHNESS_PULLDOWN, 0, 0);
 				break;
 
+			case GLAND_VARIETY_PULLDOWN: // Map variety
+				ShowDropDownMenu(this, _variety, _settings_newgame.game_creation.variety, GLAND_VARIETY_PULLDOWN, 0, 0);
+				break;
+
 			/* Freetype map borders */
 			case GLAND_WATER_NW:
 				_settings_newgame.game_creation.water_borders = ToggleBit(_settings_newgame.game_creation.water_borders, BORDER_NW);
@@ -673,6 +686,7 @@
 			case GLAND_MAPSIZE_Y_PULLDOWN:     _settings_newgame.game_creation.map_y = index; break;
 			case GLAND_TREE_PULLDOWN:          _settings_newgame.game_creation.tree_placer = index; break;
 			case GLAND_SMOOTHNESS_PULLDOWN:    _settings_newgame.game_creation.tgen_smoothness = index;  break;
+			case GLAND_VARIETY_PULLDOWN:       _settings_newgame.game_creation.variety = index; break;
 
 			case GLAND_TOWN_PULLDOWN:
 				if ((uint)index == CUSTOM_TOWN_NUMBER_DIFFICULTY) {
--- a/src/lang/english.txt
+++ b/src/lang/english.txt
@@ -1008,6 +1008,9 @@
 STR_NUM_HIGH                                                    :High
 STR_NUM_CUSTOM                                                  :Custom
 
+STR_NUM_MEDIUM                                                  :Medium
+STR_NUM_VERY_HIGH                                               :Very High
+
 STR_AI_SPEED_VERY_SLOW                                          :Very Slow
 STR_AI_SPEED_SLOW                                               :Slow
 STR_AI_SPEED_MEDIUM                                             :Medium
@@ -1032,6 +1035,7 @@
 STR_TERRAIN_TYPE_FLAT                                           :Flat
 STR_TERRAIN_TYPE_HILLY                                          :Hilly
 STR_TERRAIN_TYPE_MOUNTAINOUS                                    :Mountainous
+STR_TERRAIN_TYPE_MIXED                                          :Mixed
 
 STR_ECONOMY_STEADY                                              :Steady
 STR_ECONOMY_FLUCTUATING                                         :Fluctuating
@@ -2231,6 +2235,7 @@
 STR_MAPGEN_TERRAIN_TYPE                                         :{BLACK}Terrain type:
 STR_MAPGEN_QUANTITY_OF_SEA_LAKES                                :{BLACK}Sea level:
 STR_MAPGEN_SMOOTHNESS                                           :{BLACK}Smoothness:
+STR_MAPGEN_VARIETY                                              :{BLACK}Variety distribution:
 STR_MAPGEN_GENERATE                                             :{WHITE}Generate
 
 # Strings for map borders at game generation
--- a/src/settings_type.h
+++ b/src/settings_type.h
@@ -169,6 +169,7 @@
 	byte   snow_line;                        ///< the snowline level in this game
 	byte   water_borders;                    ///< bitset of the borders that are water
 	uint16 custom_town_number;               ///< manually entered number of towns
+	byte   variety;                          ///< variety level applied to TGP
 };
 
 /** Settings related to construction in-game */
--- a/src/table/settings.h
+++ b/src/table/settings.h
@@ -513,6 +513,7 @@
 	 SDT_CONDVAR(GameSettings, game_creation.land_generator,                  SLE_UINT8, 30, SL_MAX_VERSION, 0,MS,     1,                     0,       1, 0, STR_CONFIG_SETTING_LAND_GENERATOR,        NULL),
 	 SDT_CONDVAR(GameSettings, game_creation.oil_refinery_limit,              SLE_UINT8, 30, SL_MAX_VERSION, 0, 0,    32,                    12,      48, 0, STR_CONFIG_SETTING_OIL_REF_EDGE_DISTANCE, NULL),
 	 SDT_CONDVAR(GameSettings, game_creation.tgen_smoothness,                 SLE_UINT8, 30, SL_MAX_VERSION, 0,MS,     1,                     0,       3, 0, STR_CONFIG_SETTING_ROUGHNESS_OF_TERRAIN,  NULL),
+	     SDT_VAR(GameSettings, game_creation.variety,                         SLE_UINT8,                     S, 0,     0,                     0,       5, 0, STR_NULL,                                 NULL),
 	 SDT_CONDVAR(GameSettings, game_creation.generation_seed,                SLE_UINT32, 30, SL_MAX_VERSION, 0, 0,      GENERATE_NEW_SEED, 0, UINT32_MAX, 0, STR_NULL,                                 NULL),
 	 SDT_CONDVAR(GameSettings, game_creation.tree_placer,                     SLE_UINT8, 30, SL_MAX_VERSION, 0,MS,     2,                     0,       2, 0, STR_CONFIG_SETTING_TREE_PLACER,           NULL),
 	     SDT_VAR(GameSettings, game_creation.heightmap_rotation,              SLE_UINT8,                     S,MS,     0,                     0,       1, 0, STR_CONFIG_SETTING_HEIGHTMAP_ROTATION,    NULL),
--- a/src/tgp.cpp
+++ b/src/tgp.cpp
@@ -510,6 +510,141 @@
 	}
 }
 
+/* Additional map variety is provided by applying different curve maps
+ * to different parts of the map. A randomized low resolution grid contains
+ * which curve map to use on each part of the make. This filtered non-linearly
+ * to smooth out transitions between curves, so each tile could have between
+ * 100% of one map applied or 25% of four maps.
+ *
+ * The curve maps define different land styles, i.e. lakes, low-lands, hills
+ * and mountain ranges, although these are dependent on the landscape style
+ * chosen as well.
+ *
+ * The level parameter dictates the resolution of the grid. A low resolution
+ * grid will result in larger continuous areas of a land style, a higher
+ * resolution grid splits the style into smaller areas.
+ *
+ * At this point in map generation, all height data has been normalized to 0
+ * to 239.
+ */
+struct control_point_t {
+	height_t x;
+	height_t y;
+};
+
+struct control_point_list_t {
+	size_t length;
+	const control_point_t *list;
+};
+
+static const control_point_t _curve_map_1[] = {
+	{ 0, 0 }, { 48, 24 }, { 192, 32 }, { 240, 96 }
+};
+
+static const control_point_t _curve_map_2[] = {
+	{ 0, 0 }, { 16, 24 }, { 128, 32 }, { 192, 64 }, { 240, 144 }
+};
+
+static const control_point_t _curve_map_3[] = {
+	{ 0, 0 }, { 16, 24 }, { 128, 64 }, { 192, 144 }, { 240, 192 }
+};
+
+static const control_point_t _curve_map_4[] = {
+	{ 0, 0 }, { 16, 24 }, { 96, 72 }, { 160, 192 }, { 220, 239 }, { 240, 239 }
+};
+
+static const control_point_list_t _curve_maps[] = {
+	{ lengthof(_curve_map_1), _curve_map_1 },
+	{ lengthof(_curve_map_2), _curve_map_2 },
+	{ lengthof(_curve_map_3), _curve_map_3 },
+	{ lengthof(_curve_map_4), _curve_map_4 },
+};
+
+static void HeightMapCurves(uint level)
+{
+	height_t ht[lengthof(_curve_maps)];
+
+	/* Set up a grid to choose curve maps based on location */
+	uint sx = Clamp(1 << level, 2, 32);
+	uint sy = Clamp(1 << level, 2, 32);
+	byte *c = (byte *)alloca(sx * sy);
+
+	for (uint i = 0; i < sx * sy; i++) {
+		c[i] = Random() % lengthof(_curve_maps);
+	}
+
+	/* Apply curves */
+	for (uint x = 0; x < _height_map.size_x; x++) {
+
+		/* Get our X grid positions and bi-linear ratio */
+		float fx = (float)(sx * x) / _height_map.size_x + 0.5f;
+		uint x1 = fx;
+		uint x2 = x1;
+		float xr = 2.0f * (fx - x1) - 1.0f;
+		xr = sin(xr * M_PI_2);
+		xr = sin(xr * M_PI_2);
+		xr = 0.5f * (xr + 1.0f);
+		float xri = 1.0f - xr;
+
+		if (x1 > 0) {
+			x1--;
+			if (x2 >= sx) x2--;
+		}
+
+		for (uint y = 0; y < _height_map.size_y; y++) {
+
+			/* Get our Y grid position and bi-linear ratio */
+			float fy = (float)(sy * y) / _height_map.size_y + 0.5f;
+			uint y1 = fy;
+			uint y2 = y1;
+			float yr = 2.0f * (fy - y1) - 1.0f;
+			yr = sin(yr * M_PI_2);
+			yr = sin(yr * M_PI_2);
+			yr = 0.5f * (yr + 1.0f);
+			float yri = 1.0f - yr;
+
+			if (y1 > 0) {
+				y1--;
+				if (y2 >= sy) y2--;
+			}
+
+			uint corner_a = c[x1 + sx * y1];
+			uint corner_b = c[x1 + sx * y2];
+			uint corner_c = c[x2 + sx * y1];
+			uint corner_d = c[x2 + sx * y2];
+
+			/* Bitmask of which curve maps are chosen, so that we do not bother
+			 * calculating a curve which won't be used. */
+			uint corner_bits = 0;
+			corner_bits |= 1 << corner_a;
+			corner_bits |= 1 << corner_b;
+			corner_bits |= 1 << corner_c;
+			corner_bits |= 1 << corner_d;
+
+			height_t *h = &_height_map.height(x, y);
+
+			/* Apply all curve maps that are used on this tile. */
+			for (uint t = 0; t < lengthof(_curve_maps); t++) {
+				if (!HasBit(corner_bits, t)) continue;
+
+				const control_point_t *cm = _curve_maps[t].list;
+				for (uint i = 0; i < _curve_maps[t].length - 1; i++) {
+					const control_point_t &p1 = cm[i];
+					const control_point_t &p2 = cm[i + 1];
+
+					if (*h >= p1.x && *h < p2.x) {
+						ht[t] = p1.y + (*h - p1.x) * (p2.y - p1.y) / (p2.x - p1.x);
+						break;
+					}
+				}
+			}
+
+			/* Apply interpolation of curve map results. */
+			*h = (ht[corner_a] * yri + ht[corner_b] * yr) * xri + (ht[corner_c] * yri + ht[corner_d] * yr) * xr;
+		}
+	}
+}
+
 /** Adjusts heights in height map to contain required amount of water tiles */
 static void HeightMapAdjustWaterLevel(amplitude_t water_percent, height_t h_max_new)
 {
@@ -728,6 +863,11 @@
 	HeightMapSmoothSlopes(roughness);
 
 	HeightMapSineTransform(12, h_max_new);
+
+	if (_settings_game.game_creation.variety > 0) {
+		HeightMapCurves(_settings_game.game_creation.variety);
+	}
+
 	HeightMapSmoothSlopes(16);
 }