/** * Gets the maximum allowed height while generating a map based on * mapsize, terraintype, and the maximum height level. * @return The maximum height for the map generation. * @note Values should never be lower than 3 since the minimum snowline height is 2. */ static height_t TGPGetMaxHeight() { /** * Desired maximum height - indexed by: * - _settings_game.difficulty.terrain_type * - min(MapLogX(), MapLogY()) - MIN_MAP_SIZE_BITS * * It is indexed by map size as well as terrain type since the map size limits the height of * a usable mountain. For example, on a 64x64 map a 24 high single peak mountain (as if you * raised land 24 times in the center of the map) will leave only a ring of about 10 tiles * around the mountain to build on. On a 4096x4096 map, it won't cover any major part of the map. */ static const int max_height[5][MAX_MAP_SIZE_BITS - MIN_MAP_SIZE_BITS + 1] = { /* 64 128 256 512 1024 2048 4096 */ { 3, 3, 3, 3, 4, 5, 7 }, ///< Very flat { 5, 7, 8, 9, 14, 19, 31 }, ///< Flat { 8, 9, 10, 15, 23, 37, 61 }, ///< Hilly { 10, 11, 17, 19, 49, 63, 73 }, ///< Mountainous { 12, 19, 25, 31, 67, 75, 87 }, ///< Alpinist }; int max_height_from_table = max_height[_settings_game.difficulty.terrain_type][min(MapLogX(), MapLogY()) - MIN_MAP_SIZE_BITS]; return I2H(min(max_height_from_table, _settings_game.construction.max_heightlevel)); }
/** Applies sine wave redistribution onto height map */ static void HeightMapSineTransform(height_t h_min, height_t h_max) { height_t *h; FOR_ALL_TILES_IN_HEIGHT(h) { double fheight; if (*h < h_min) continue; /* Transform height into 0..1 space */ fheight = (double)(*h - h_min) / (double)(h_max - h_min); /* Apply sine transform depending on landscape type */ switch (_settings_game.game_creation.landscape) { case LT_TOYLAND: case LT_TEMPERATE: /* Move and scale 0..1 into -1..+1 */ fheight = 2 * fheight - 1; /* Sine transform */ fheight = sin(fheight * M_PI_2); /* Transform it back from -1..1 into 0..1 space */ fheight = 0.5 * (fheight + 1); break; case LT_ARCTIC: { /* Arctic terrain needs special height distribution. * Redistribute heights to have more tiles at highest (75%..100%) range */ double sine_upper_limit = 0.75; double linear_compression = 2; if (fheight >= sine_upper_limit) { /* Over the limit we do linear compression up */ fheight = 1.0 - (1.0 - fheight) / linear_compression; } else { double m = 1.0 - (1.0 - sine_upper_limit) / linear_compression; /* Get 0..sine_upper_limit into -1..1 */ fheight = 2.0 * fheight / sine_upper_limit - 1.0; /* Sine wave transform */ fheight = sin(fheight * M_PI_2); /* Get -1..1 back to 0..(1 - (1 - sine_upper_limit) / linear_compression) == 0.0..m */ fheight = 0.5 * (fheight + 1.0) * m; } } break; case LT_TROPIC: { /* Desert terrain needs special height distribution. * Half of tiles should be at lowest (0..25%) heights */ double sine_lower_limit = 0.5; double linear_compression = 2; if (fheight <= sine_lower_limit) { /* Under the limit we do linear compression down */ fheight = fheight / linear_compression; } else { double m = sine_lower_limit / linear_compression; /* Get sine_lower_limit..1 into -1..1 */ fheight = 2.0 * ((fheight - sine_lower_limit) / (1.0 - sine_lower_limit)) - 1.0; /* Sine wave transform */ fheight = sin(fheight * M_PI_2); /* Get -1..1 back to (sine_lower_limit / linear_compression)..1.0 */ fheight = 0.5 * ((1.0 - m) * fheight + (1.0 + m)); } } break; default: NOT_REACHED(); break; } /* Transform it back into h_min..h_max space */ *h = (height_t)(fheight * (h_max - h_min) + h_min); if (*h < 0) *h = I2H(0); if (*h >= h_max) *h = h_max - 1; } }
/** * 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. * @param level Rough indication of the size of the grid sections to style. Small level means large grid sections. */ static void HeightMapCurves(uint level) { height_t mh = TGPGetMaxHeight() - I2H(1); // height levels above sea level only /** Basically scale height X to height Y. Everything in between is interpolated. */ struct control_point_t { height_t x; ///< The height to scale from. height_t y; ///< The height to scale to. }; /* Scaled curve maps; value is in height_ts. */ #define F(fraction) ((height_t)(fraction * mh)) const control_point_t curve_map_1[] = { { F(0.0), F(0.0) }, { F(0.8), F(0.13) }, { F(1.0), F(0.4) } }; const control_point_t curve_map_2[] = { { F(0.0), F(0.0) }, { F(0.53), F(0.13) }, { F(0.8), F(0.27) }, { F(1.0), F(0.6) } }; const control_point_t curve_map_3[] = { { F(0.0), F(0.0) }, { F(0.53), F(0.27) }, { F(0.8), F(0.57) }, { F(1.0), F(0.8) } }; const control_point_t curve_map_4[] = { { F(0.0), F(0.0) }, { F(0.4), F(0.3) }, { F(0.7), F(0.8) }, { F(0.92), F(0.99) }, { F(1.0), F(0.99) } }; #undef F /** Helper structure to index the different curve maps. */ struct control_point_list_t { size_t length; ///< The length of the curve map. const control_point_t *list; ///< The actual curve map. }; 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 }, }; height_t ht[lengthof(curve_maps)]; MemSetT(ht, 0, lengthof(ht)); /* Set up a grid to choose curve maps based on location; attempt to get a somewhat square grid */ float factor = sqrt((float)_height_map.size_x / (float)_height_map.size_y); uint sx = Clamp((int)(((1 << level) * factor) + 0.5), 1, 128); uint sy = Clamp((int)(((1 << level) / factor) + 0.5), 1, 128); byte *c = AllocaM(byte, sx * sy); for (uint i = 0; i < sx * sy; i++) { c[i] = Random() % lengthof(curve_maps); } /* Apply curves */ for (int 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 + 1.0f; uint x1 = (uint)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 (int 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 + 1.0f; uint y1 = (uint)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); /* Do not touch sea level */ if (*h < I2H(1)) continue; /* Only scale above sea level */ *h -= I2H(1); /* 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; bool found = false; 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); found = true; break; } } assert(found); } /* Apply interpolation of curve map results. */ *h = (height_t)((ht[corner_a] * yri + ht[corner_b] * yr) * xri + (ht[corner_c] * yri + ht[corner_d] * yr) * xr); /* Readd sea level */ *h += I2H(1); } } }
/** * Get the amplitude associated with the currently selected * smoothness and maximum height level. * @param frequency The frequency to get the amplitudes for * @return The amplitudes to apply to the map. */ static amplitude_t GetAmplitude(int frequency) { /* Base noise amplitudes (multiplied by 1024) and indexed by "smoothness setting" and log2(frequency). * Used for maps that have their smallest side smaller than 512. */ static const amplitude_t amplitudes_small[][10] = { /* lowest frequency ...... highest (every corner) */ {60000, 2273, 4142, 2253, 421, 213, 137, 177, 37, 16}, ///< Very smooth {50000, 2273, 4142, 2253, 421, 213, 137, 177, 37, 61}, ///< Smooth {40000, 2273, 4142, 2253, 421, 213, 137, 177, 37, 91}, ///< Rough {30000, 2273, 4142, 2253, 421, 213, 137, 177, 37, 161}, ///< Very rough }; /* Base noise amplitudes (multiplied by 1024) and indexed by "smoothness setting" and log2(frequency). * Used for maps that have their smallest side equal to 512. */ static const amplitude_t amplitudes_middle[][10] = { {55000, 2273, 5142, 253, 2421, 213, 137, 177, 37, 16}, ///< Very smooth {45000, 2273, 5142, 253, 2421, 213, 137, 177, 37, 61}, ///< Smooth {35000, 2273, 5142, 253, 2421, 213, 137, 177, 37, 91}, ///< Rough {25000, 2273, 5142, 253, 2421, 213, 137, 177, 37, 161}, ///< Very rough }; /* Base noise amplitudes (multiplied by 1024) and indexed by "smoothness setting" and log2(frequency). * Used for maps that have their smallest side bigger than 512. */ static const amplitude_t amplitudes_large[][10] = { /* lowest frequency ...... highest (every corner) */ {55000, 2273, 5142, 253, 421, 2213, 137, 177, 37, 16}, ///< Very smooth {45000, 2273, 5142, 253, 421, 2213, 137, 177, 37, 61}, ///< Smooth {35000, 2273, 5142, 253, 421, 2213, 137, 177, 37, 91}, ///< Rough {25000, 2273, 5142, 253, 421, 2213, 137, 177, 37, 161}, ///< Very rough }; /* Make sure arrays cover all smoothness settings. */ assert_compile(lengthof(amplitudes_small) == TGEN_SMOOTHNESS_END); assert_compile(lengthof(amplitudes_middle) == TGEN_SMOOTHNESS_END); assert_compile(lengthof(amplitudes_large) == TGEN_SMOOTHNESS_END); /* Extrapolation factors for ranges before the table. * The extrapolation is needed to account for the higher map heights. They need larger * areas with a particular gradient so that we are able to create maps without too * many steep slopes up to the wanted height level. It's definitely not perfect since * it will bring larger rectangles with similar slopes which makes the rectangular * behaviour of TGP more noticable. However, these height differentiations cannot * happen over much smaller areas; we basically double the "range" to give a similar * slope for every doubling of map height. */ static const double extrapolation_factors[] = { 3.3, 2.8, 2.3, 1.8 }; int smoothness = _settings_game.game_creation.tgen_smoothness; int smallest_size = min(_settings_game.game_creation.map_x, _settings_game.game_creation.map_y); int index; amplitude_t amplitude; if (smallest_size < 9) { // Smallest map side is less than 2^9 == 512. index = frequency - MAX_TGP_FREQUENCIES + lengthof(amplitudes_small[0]); amplitude = amplitudes_small[smoothness][max(0, index)]; } else if (smallest_size == 9) { index = frequency - MAX_TGP_FREQUENCIES + lengthof(amplitudes_middle[0]); amplitude = amplitudes_middle[smoothness][max(0, index)]; } else { index = frequency - MAX_TGP_FREQUENCIES + lengthof(amplitudes_large[0]); amplitude = amplitudes_large[smoothness][max(0, index)]; } if (index >= 0) return amplitude; /* We need to extrapolate the amplitude. */ double extrapolation_factor = extrapolation_factors[smoothness]; int height_range = I2H(16); do { amplitude = (amplitude_t)(extrapolation_factor * (double)amplitude); height_range <<= 1; index++; } while (index < 0); return Clamp((TGPGetMaxHeight() - height_range) / height_range, 0, 1) * amplitude; }