diff --git a/src/windows/editor_object_selection.c b/src/windows/editor_object_selection.c index ea33086ac2..64fc266563 100644 --- a/src/windows/editor_object_selection.c +++ b/src/windows/editor_object_selection.c @@ -145,6 +145,25 @@ static int get_object_from_object_selection(uint8 object_type, int y, uint8 *obj static void sub_6D33E2(); static void editor_load_selected_objects(); +static void window_editor_object_selection_select_default_objects() +{ + int i; + rct_object_entry *entry; + + if (RCT2_GLOBAL(0x00F433F7, uint16) == 0) + return; + + entry = (rct_object_entry*)0x0098DAE5; + for (i = 0; i < 22; i++) + RCT2_CALLPROC_X(0x006AB54F, 0, 7, 0, 0, 0, 0, (int)entry); + + // Add snow and ice theme + unsigned char myEntry[16] = { + 0x87, 0x8F, 0x18, 0x0A, 0x53, 0x43, 0x47, 0x53, 0x4E, 0x4F, 0x57, 0x20, 0xBF, 0x74, 0x51, 0x25 + }; + RCT2_CALLPROC_X(0x006AB54F, 0, 7, 0, 0, 0, 0, (int)myEntry); +} + /** * * rct2: 0x006AA64E @@ -160,6 +179,10 @@ void window_editor_object_selection_open() RCT2_CALLPROC_EBPSAFE(0x006AB211); RCT2_CALLPROC_EBPSAFE(0x006AA770); + // Not really where its called, but easy way to change default objects for now + if (RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_FLAGS, uint8) & SCREEN_FLAGS_SCENARIO_EDITOR) + window_editor_object_selection_select_default_objects(); + window = window_create( RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_WIDTH, uint16) / 2 - 300, max(RCT2_GLOBAL(RCT2_ADDRESS_SCREEN_HEIGHT, uint16) / 2 - 200, 28), diff --git a/src/world/map.c b/src/world/map.c index 29f883d71f..8eb938467b 100644 --- a/src/world/map.c +++ b/src/world/map.c @@ -133,7 +133,7 @@ void map_element_set_terrain(rct_map_element *element, int terrain) // Bits 0, 1, 2 for terrain are stored in element.terrain bit 5, 6, 7 element->properties.surface.terrain &= ~0xE0; - element->properties.surface.terrain = (terrain & 7) << 5; + element->properties.surface.terrain |= (terrain & 7) << 5; } void map_element_set_terrain_edge(rct_map_element *element, int terrain) @@ -186,6 +186,7 @@ void map_init(int size) map_element->properties.surface.slope = 0; map_element->properties.surface.grass_length = 1; map_element->properties.surface.ownership = 0; + map_element->properties.surface.terrain = 0; map_element_set_terrain(map_element, TERRAIN_GRASS); map_element_set_terrain_edge(map_element, TERRAIN_EDGE_ROCK); diff --git a/src/world/mapgen.c b/src/world/mapgen.c index 4b7fd2a8b7..558cd0837c 100644 --- a/src/world/mapgen.c +++ b/src/world/mapgen.c @@ -84,6 +84,8 @@ static void mapgen_blob(int cx, int cy, int size, int height); static void mapgen_smooth_height(int iterations); static void mapgen_set_height(); +static void mapgen_simplex(); + static int _heightSize; static uint8 *_height; @@ -166,17 +168,22 @@ void mapgen_generate(mapgen_settings *settings) _height = (uint8*)malloc(_heightSize * _heightSize * sizeof(uint8)); memset(_height, 0, _heightSize * _heightSize * sizeof(uint8)); - // Keep overwriting the map with rough cicular blobs of different sizes and heights. - // This procedural method can produce intersecting contour like land and lakes. - // Large blobs, general shape of map - mapgen_blobs(6, _heightSize / 2, _heightSize * 4, 2, 16); - // Medium blobs - mapgen_blobs(12, _heightSize / 16, _heightSize / 8, 2, 18); - // Small blobs, small hills and lakes - mapgen_blobs(32, _heightSize / 32, _heightSize / 16, 2, 18); + if (1) { + mapgen_simplex(); + mapgen_smooth_height(2 + (rand() % 6)); + } else { + // Keep overwriting the map with rough cicular blobs of different sizes and heights. + // This procedural method can produce intersecting contour like land and lakes. + // Large blobs, general shape of map + mapgen_blobs(6, _heightSize / 2, _heightSize * 4, 4, 16); + // Medium blobs + mapgen_blobs(12, _heightSize / 16, _heightSize / 8, 4, 18); + // Small blobs, small hills and lakes + mapgen_blobs(32, _heightSize / 32, _heightSize / 16, 4, 18); - // Smooth the land so that their aren't cliffs round every blob. - mapgen_smooth_height(2); + // Smooth the land so that their aren't cliffs round every blob. + mapgen_smooth_height(2); + } // Set the game map to the height map mapgen_set_height(); @@ -186,7 +193,29 @@ void mapgen_generate(mapgen_settings *settings) while (map_smooth(1, 1, mapSize - 1, mapSize - 1)) { } // Add the water - mapgen_set_water_level(6 + (rand() % 4) * 2); + int waterLevel = 6 + (rand() % 8) * 2; + mapgen_set_water_level(waterLevel); + + // Add sandy beaches + int beachTexture = floorTexture; + if (settings->floor == -1 && floorTexture == TERRAIN_GRASS) { + switch (rand() % 4) { + case 0: + beachTexture = TERRAIN_SAND; + break; + case 1: + beachTexture = TERRAIN_SAND_LIGHT; + break; + } + } + for (y = 1; y < mapSize - 1; y++) { + for (x = 1; x < mapSize - 1; x++) { + mapElement = map_get_surface_element_at(x, y); + + if (mapElement->base_height < waterLevel + 6) + map_element_set_terrain(mapElement, beachTexture); + } + } // Place the trees if (settings->trees != 0) @@ -290,8 +319,11 @@ static void mapgen_place_trees() } // Place trees + float treeToLandRatio = (10 + (rand() % 30)) / 100.0f; + int numTrees = max(4, (int)(availablePositionsCount * treeToLandRatio)); + mapSize = RCT2_GLOBAL(RCT2_ADDRESS_MAP_SIZE, sint16); - for (i = 0; i < min(availablePositionsCount, 2048 + (rand() % 10000)); i++) { + for (i = 0; i < numTrees; i++) { pos = &availablePositions[i]; type = -1; @@ -549,9 +581,9 @@ static void mapgen_smooth_height(int iterations) int i, x, y, xx, yy, avg; int arraySize = _heightSize * _heightSize * sizeof(uint8); uint8 *copyHeight = malloc(arraySize); - memcpy(copyHeight, _height, arraySize); for (i = 0; i < iterations; i++) { + memcpy(copyHeight, _height, arraySize); for (y = 1; y < _heightSize - 1; y++) { for (x = 1; x < _heightSize - 1; x++) { avg = 0; @@ -563,6 +595,8 @@ static void mapgen_smooth_height(int iterations) } } } + + free(copyHeight); } /** @@ -587,7 +621,7 @@ static void mapgen_set_height() uint8 baseHeight = (q00 + q01 + q10 + q11) / 4; mapElement = map_get_surface_element_at(x, y); - mapElement->base_height = baseHeight * 2; + mapElement->base_height = max(2, baseHeight * 2); mapElement->clearance_height = mapElement->base_height; if (q00 > baseHeight) @@ -600,4 +634,145 @@ static void mapgen_set_height() mapElement->properties.surface.slope |= 1; } } -} \ No newline at end of file +} + +#pragma region Noise + +/** + * Simplex Noise Algorithm with Fractional Brownian Motion + * Based on: + * - https://code.google.com/p/simplexnoise/ + * - https://code.google.com/p/fractalterraingeneration/wiki/Fractional_Brownian_Motion + */ + +static float generate(float x, float y); +static int fast_floor(float x); +static float grad(int hash, float x, float y); + +static uint8 perm[512]; + +static void noise_rand() +{ + for (int i = 0; i < countof(perm); i++) + perm[i] = rand() & 0xFF; +} + +static float fractal_noise(int x, int y, float frequency, int octaves, float lacunarity, float persistence) +{ + float total = 0.0f; + float amplitude = persistence; + for (int i = 0; i < octaves; i++) { + total += generate(x * frequency, y * frequency) * amplitude; + frequency *= lacunarity; + amplitude *= persistence; + } + return total; +} + +static float generate(float x, float y) +{ + const float F2 = 0.366025403f; // F2 = 0.5*(sqrt(3.0)-1.0) + const float G2 = 0.211324865f; // G2 = (3.0-Math.sqrt(3.0))/6.0 + + float n0, n1, n2; // Noise contributions from the three corners + + // Skew the input space to determine which simplex cell we're in + float s = (x + y) * F2; // Hairy factor for 2D + float xs = x + s; + float ys = y + s; + int i = fast_floor(xs); + int j = fast_floor(ys); + + float t = (float)(i + j) * G2; + float X0 = i - t; // Unskew the cell origin back to (x,y) space + float Y0 = j - t; + float x0 = x - X0; // The x,y distances from the cell origin + float y0 = y - Y0; + + // For the 2D case, the simplex shape is an equilateral triangle. + // Determine which simplex we are in. + int i1, j1; // Offsets for second (middle) corner of simplex in (i,j) coords + if (x0 > y0) { i1 = 1; j1 = 0; } // lower triangle, XY order: (0,0)->(1,0)->(1,1) + else { i1 = 0; j1 = 1; } // upper triangle, YX order: (0,0)->(0,1)->(1,1) + + // A step of (1,0) in (i,j) means a step of (1-c,-c) in (x,y), and + // a step of (0,1) in (i,j) means a step of (-c,1-c) in (x,y), where + // c = (3-sqrt(3))/6 + + float x1 = x0 - i1 + G2; // Offsets for middle corner in (x,y) unskewed coords + float y1 = y0 - j1 + G2; + float x2 = x0 - 1.0f + 2.0f * G2; // Offsets for last corner in (x,y) unskewed coords + float y2 = y0 - 1.0f + 2.0f * G2; + + // Wrap the integer indices at 256, to avoid indexing perm[] out of bounds + int ii = i % 256; + int jj = j % 256; + + // Calculate the contribution from the three corners + float t0 = 0.5f - x0 * x0 - y0 * y0; + if (t0 < 0.0f) n0 = 0.0f; + else { + t0 *= t0; + n0 = t0 * t0 * grad(perm[ii + perm[jj]], x0, y0); + } + + float t1 = 0.5f - x1 * x1 - y1 * y1; + if (t1 < 0.0f) n1 = 0.0f; + else { + t1 *= t1; + n1 = t1 * t1 * grad(perm[ii + i1 + perm[jj + j1]], x1, y1); + } + + float t2 = 0.5f - x2 * x2 - y2 * y2; + if (t2 < 0.0f) n2 = 0.0f; + else { + t2 *= t2; + n2 = t2 * t2 * grad(perm[ii + 1 + perm[jj + 1]], x2, y2); + } + + // Add contributions from each corner to get the final noise value. + // The result is scaled to return values in the interval [-1,1]. + return 40.0f * (n0 + n1 + n2); // TODO: The scale factor is preliminary! +} + +static int fast_floor(float x) +{ + return (x > 0) ? ((int)x) : (((int)x) - 1); +} + +static int mod(int x, int m) +{ + int a = x % m; + return a < 0 ? a + m : a; +} + +static float grad(int hash, float x, float y) +{ + int h = hash & 7; // Convert low 3 bits of hash code + float u = h < 4 ? x : y; // into 8 simple gradient directions, + float v = h < 4 ? y : x; // and compute the dot product with (x,y). + return ((h & 1) != 0 ? -u : u) + ((h & 2) != 0 ? -2.0f * v : 2.0f * v); +} + +static void mapgen_simplex() +{ + int x, y; + + float freq = 1.75f * (1.0f / _heightSize); + int octaves = 6; + + int low = rand() % 4; + int high = 12 + (rand() % (32 - 12)); + + noise_rand(); + for (y = 0; y < _heightSize; y++) { + for (x = 0; x < _heightSize; x++) { + float noiseValue = clamp(-1.0f, fractal_noise(x, y, freq, octaves, 2.0f, 0.65f), 1.0f); + float normalisedNoiseValue = (noiseValue + 1.0f) / 2.0f; + + set_height(x, y, low + (int)(normalisedNoiseValue * high)); + } + } +} + +#pragma endregion \ No newline at end of file