diff --git a/data/language/english_uk.txt b/data/language/english_uk.txt index 7181e2da8d..ac2dad12af 100644 --- a/data/language/english_uk.txt +++ b/data/language/english_uk.txt @@ -2697,8 +2697,8 @@ STR_2691 :{WINDOW_COLOUR_2}Base height: STR_2692 :{WINDOW_COLOUR_2}Water level: STR_2693 :{WINDOW_COLOUR_2}Terrain: STR_2694 :Generate -STR_2695 :??? -STR_2696 :??? +STR_2695 :Random terrain +STR_2696 :Place trees STR_2697 :??? STR_2698 :??? STR_2699 :??? diff --git a/data/language/english_us.txt b/data/language/english_us.txt index feb888bde1..8da74f757e 100644 --- a/data/language/english_us.txt +++ b/data/language/english_us.txt @@ -2692,13 +2692,13 @@ STR_2686 :??? STR_2687 :??? STR_2688 :??? STR_2689 :??? -STR_2690 :??? -STR_2691 :??? -STR_2692 :??? -STR_2693 :??? -STR_2694 :??? -STR_2695 :??? -STR_2696 :??? +STR_2690 :Map Generation +STR_2691 :{WINDOW_COLOUR_2}Base height: +STR_2692 :{WINDOW_COLOUR_2}Water level: +STR_2693 :{WINDOW_COLOUR_2}Terrain: +STR_2694 :Generate +STR_2695 :Random terrain +STR_2696 :Place trees STR_2697 :??? STR_2698 :??? STR_2699 :??? diff --git a/src/windows/mapgen.c b/src/windows/mapgen.c index 28eb711cd4..461d1c5e8c 100644 --- a/src/windows/mapgen.c +++ b/src/windows/mapgen.c @@ -45,7 +45,7 @@ enum { WIDX_GENERATE, - WIDX_MAP_SIZE, + WIDX_MAP_SIZE = 7, WIDX_MAP_SIZE_UP, WIDX_MAP_SIZE_DOWN, WIDX_BASE_HEIGHT, @@ -55,7 +55,10 @@ enum { WIDX_WATER_LEVEL_UP, WIDX_WATER_LEVEL_DOWN, WIDX_FLOOR_TEXTURE, - WIDX_WALL_TEXTURE + WIDX_WALL_TEXTURE, + + WIDX_RANDOM_TERRAIN = 7, + WIDX_PLACE_TREES, }; #pragma region Widgets @@ -93,6 +96,9 @@ static rct_widget window_mapgen_random_widgets[] = { { WWT_TAB, 1, 34, 64, 17, 43, 0x2000144E, STR_NONE }, { WWT_DROPDOWN_BUTTON, 1, 104, 198, 52, 63, 2694, STR_NONE }, + + { WWT_CHECKBOX, 1, 4, 198, 52, 63, 2695, STR_NONE }, + { WWT_CHECKBOX, 1, 4, 198, 70, 81, 2696, STR_NONE }, { WIDGETS_END }, }; @@ -207,7 +213,9 @@ static uint32 window_mapgen_page_enabled_widgets[] = { (1 << WIDX_CLOSE) | (1 << WIDX_TAB_1) | (1 << WIDX_TAB_2) | - (1 << WIDX_GENERATE) + (1 << WIDX_GENERATE) | + (1 << WIDX_RANDOM_TERRAIN) | + (1 << WIDX_PLACE_TREES) }; #pragma endregion @@ -236,6 +244,8 @@ static int _baseHeight = 12; static int _waterLevel = 6; static int _floorTexture = TERRAIN_GRASS; static int _wallTexture = TERRAIN_EDGE_ROCK; +static int _randomTerrrain = 1; +static int _placeTrees = 1; rct_window *window_mapgen_open() { @@ -267,7 +277,7 @@ rct_window *window_mapgen_open() window_invalidate(w); w->widgets = window_mapgen_page_widgets[0]; w->enabled_widgets = window_mapgen_page_enabled_widgets[0]; - w->var_020 = RCT2_GLOBAL(0x00988E3C, uint32); + w->var_020 = 0xFFFFFFFF; w->event_handlers = window_mapgen_page_events[0]; w->pressed_widgets = 0; w->disabled_widgets = 0; @@ -282,6 +292,7 @@ static void window_mapgen_base_mouseup() { short widgetIndex; rct_window *w; + mapgen_settings mapgenSettings; window_widget_get_registers(w, widgetIndex); @@ -294,7 +305,13 @@ static void window_mapgen_base_mouseup() window_mapgen_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_GENERATE: - mapgen_generate_blank(_mapSize, _baseHeight + 2, _waterLevel + 2, _floorTexture, _wallTexture); + mapgenSettings.mapSize = _mapSize; + mapgenSettings.height = _baseHeight + 2; + mapgenSettings.waterLevel = _waterLevel + 2; + mapgenSettings.floor = _floorTexture; + mapgenSettings.wall = _wallTexture; + + mapgen_generate_blank(&mapgenSettings); gfx_invalidate_screen(); break; } @@ -470,6 +487,7 @@ static void window_mapgen_random_mouseup() { rct_window * w; short widgetIndex; + mapgen_settings mapgenSettings; window_widget_get_registers(w, widgetIndex); @@ -482,9 +500,22 @@ static void window_mapgen_random_mouseup() window_mapgen_set_page(w, widgetIndex - WIDX_TAB_1); break; case WIDX_GENERATE: - mapgen_generate(_mapSize); + mapgenSettings.mapSize = _mapSize; + mapgenSettings.height = _baseHeight + 2; + mapgenSettings.waterLevel = _waterLevel + 2; + mapgenSettings.floor = _randomTerrrain ? -1 : _floorTexture; + mapgenSettings.wall = _randomTerrrain ? -1 : _wallTexture; + mapgenSettings.trees = _placeTrees; + + mapgen_generate(&mapgenSettings); gfx_invalidate_screen(); break; + case WIDX_RANDOM_TERRAIN: + _randomTerrrain ^= 1; + break; + case WIDX_PLACE_TREES: + _placeTrees ^= 1; + break; } } @@ -512,6 +543,12 @@ static void window_mapgen_random_invalidate() window_init_scroll_widgets(w); } + w->pressed_widgets = 0; + if (_randomTerrrain) + w->pressed_widgets |= 1 << WIDX_RANDOM_TERRAIN; + if (_placeTrees) + w->pressed_widgets |= 1 << WIDX_PLACE_TREES; + window_mapgen_set_pressed_tab(w); window_mapgen_anchor_border_widgets(w); } diff --git a/src/world/map_helpers.c b/src/world/map_helpers.c index dad39bc54d..8e494a7b87 100644 --- a/src/world/map_helpers.c +++ b/src/world/map_helpers.c @@ -21,6 +21,9 @@ #include "map.h" #include "map_helpers.h" +/** + * Not perfect, this still leaves some particular tiles unsmoothed. + */ int map_smooth(int l, int t, int r, int b) { int i, x, y, highest, count, cornerHeights[4], doubleCorner, raisedLand = 0; @@ -138,5 +141,150 @@ int map_smooth(int l, int t, int r, int b) } } + return raisedLand; +} + +int map_get_corner_height(int x, int y, int corner) +{ + rct_map_element *mapElement = map_get_surface_element_at(x, y); + int baseHeight = mapElement->base_height; + int slope = mapElement->properties.surface.slope; + int doubleCorner = slope & 16; + if (doubleCorner) { + if (!(slope & 1)) doubleCorner = 4; + else if (!(slope & 2)) doubleCorner = 8; + else if (!(slope & 4)) doubleCorner = 1; + else if (!(slope & 8)) doubleCorner = 2; + } + + switch (corner) { + case 0: + return baseHeight + (slope & 1 ? (doubleCorner == 1 ? 4 : 2) : 0); + case 1: + return baseHeight + (slope & 8 ? (doubleCorner == 8 ? 4 : 2) : 0); + case 2: + return baseHeight + (slope & 2 ? (doubleCorner == 2 ? 4 : 2) : 0); + case 3: + return baseHeight + (slope & 4 ? (doubleCorner == 4 ? 4 : 2) : 0); + default: + return baseHeight; + } +} + +/** + * There are non-smoothed tiles with this version, but diagonal land blocks end up being wavy. + */ +int map_smooth_wavy(int l, int t, int r, int b) +{ + int i, x, y, highest, count, cornerHeights[4], doubleCorner, raisedLand = 0; + rct_map_element *mapElement; + for (y = t; y < b; y++) { + for (x = l; x < r; x++) { + mapElement = map_get_surface_element_at(x, y); + mapElement->properties.surface.slope &= ~0x1F; + + // Raise to edge height - 2 + highest = mapElement->base_height; + highest = max(highest, map_get_surface_element_at(x - 1, y + 0)->base_height); + highest = max(highest, map_get_surface_element_at(x + 1, y + 0)->base_height); + highest = max(highest, map_get_surface_element_at(x + 0, y - 1)->base_height); + highest = max(highest, map_get_surface_element_at(x + 0, y + 1)->base_height); + if (mapElement->base_height < highest - 2) { + raisedLand = 1; + mapElement->base_height = mapElement->clearance_height = highest - 2; + } + + // Check corners + doubleCorner = -1; + cornerHeights[0] = max(map_get_corner_height(x - 1, y - 1, 0), max(map_get_corner_height(x + 1, y + 0, 1), map_get_corner_height(x + 0, y + 1, 2))); + cornerHeights[1] = max(map_get_corner_height(x + 1, y - 1, 1), max(map_get_corner_height(x - 1, y + 0, 0), map_get_corner_height(x + 0, y + 1, 3))); + cornerHeights[2] = max(map_get_corner_height(x + 1, y + 1, 3), max(map_get_corner_height(x + 1, y + 0, 3), map_get_corner_height(x + 0, y - 1, 0))); + cornerHeights[3] = max(map_get_corner_height(x - 1, y + 1, 2), max(map_get_corner_height(x - 1, y + 0, 2), map_get_corner_height(x + 0, y - 1, 1))); + highest = mapElement->base_height; + for (i = 0; i < 4; i++) + highest = max(highest, cornerHeights[i]); + + if (highest >= mapElement->base_height + 4) { + count = 0; + for (i = 0; i < 4; i++) + if (cornerHeights[i] == highest) + count++; + + if (count == 1) { + if (mapElement->base_height < highest - 4) { + mapElement->base_height = mapElement->clearance_height = highest - 4; + raisedLand = 1; + } + if (cornerHeights[0] == highest && cornerHeights[2] <= cornerHeights[0] - 4) + doubleCorner = 0; + else if (cornerHeights[1] == highest && cornerHeights[3] <= cornerHeights[1] - 4) + doubleCorner = 1; + else if (cornerHeights[2] == highest && cornerHeights[0] <= cornerHeights[2] - 4) + doubleCorner = 2; + else if (cornerHeights[3] == highest && cornerHeights[1] <= cornerHeights[3] - 4) + doubleCorner = 3; + } else { + if (mapElement->base_height < highest - 2) { + mapElement->base_height = mapElement->clearance_height = highest - 2; + raisedLand = 1; + } + } + } + + if (doubleCorner != -1) { + mapElement->properties.surface.slope |= 16; + switch (doubleCorner) { + case 0: + mapElement->properties.surface.slope |= 2 | 4 | 8; + break; + case 1: + mapElement->properties.surface.slope |= 1 | 2 | 4; + break; + case 2: + mapElement->properties.surface.slope |= 1 | 2 | 8; + break; + case 3: + mapElement->properties.surface.slope |= 1 | 4 | 8; + break; + } + } else { + // Corners + if ( + map_get_corner_height(x + 1, y + 1, 3) > mapElement->base_height || + map_get_corner_height(x + 1, y + 0, 1) > mapElement->base_height || + map_get_corner_height(x + 0, y + 1, 2) > mapElement->base_height + ) + mapElement->properties.surface.slope |= 1; + + if ( + map_get_corner_height(x - 1, y + 1, 2) > mapElement->base_height || + map_get_corner_height(x - 1, y + 0, 0) > mapElement->base_height || + map_get_corner_height(x + 0, y + 1, 3) > mapElement->base_height + ) + mapElement->properties.surface.slope |= 8; + + if ( + map_get_corner_height(x + 1, y - 1, 1) > mapElement->base_height || + map_get_corner_height(x + 1, y + 0, 3) > mapElement->base_height || + map_get_corner_height(x + 0, y - 1, 0) > mapElement->base_height + ) + mapElement->properties.surface.slope |= 2; + + if ( + map_get_corner_height(x - 1, y - 1, 0) > mapElement->base_height || + map_get_corner_height(x - 1, y + 0, 2) > mapElement->base_height || + map_get_corner_height(x + 0, y - 1, 1) > mapElement->base_height + ) + mapElement->properties.surface.slope |= 4; + + // Raise + if (mapElement->properties.surface.slope == (1 | 2 | 4 | 8)) { + mapElement->properties.surface.slope &= ~0x1F; + mapElement->base_height = mapElement->clearance_height += 2; + } + } + } + } + return raisedLand; } \ No newline at end of file diff --git a/src/world/mapgen.c b/src/world/mapgen.c index 2a583fea16..8a27a6fa0b 100644 --- a/src/world/mapgen.c +++ b/src/world/mapgen.c @@ -23,9 +23,54 @@ #endif #include #include "../addresses.h" +#include "../object.h" #include "map.h" #include "map_helpers.h" #include "mapgen.h" +#include "scenery.h" + +#pragma region Random objects + +const char *GrassTrees[] = { + // Dark + "TCF ", // Caucasian Fir Tree + "TRF ", // Red Fir Tree + "TRF2 ", // Red Fir Tree + "TSP ", // Scots Pine Tree + "TMZP ", // Montezuma Pine Tree + "TAP ", // Aleppo Pine Tree + "TCRP ", // Corsican Pine Tree + "TBP ", // Black Poplar Tree + + // Light + "TCL ", // Cedar of Lebanon Tree + "TEL ", // European Larch Tree +}; + +// Trees to be placed in proximity to water +const char *GrassWaterTrees[] = { + "TWW " // Weeping Willow Tree +}; + +const char *DesertTrees[] = { + "TMP ", // Monkey-Puzzle Tree + "THL ", // Honey Locust Tree + "TH1 ", // Canary Palm Tree + "TH2 ", // Palm Tree + "TPM ", // Palm Tree + "TROPT1 ", // Tree + "TBC ", // Cactus + "TSC ", // Cactus +}; + +const char *SnowTrees[] = { + "TCFS ", // Snow-covered Caucasian Fir Tree + "TNSS ", // Snow-covered Norway Spruce Tree + "TRF3 ", // Snow-covered Red Fir Tree + "TRFS ", // Snow-covered Red Fir Tree +}; + +#pragma endregion static void mapgen_place_trees(); static void mapgen_set_water_level(int height); @@ -40,40 +85,52 @@ static uint8 *_height; const uint8 BaseTerrain[] = { TERRAIN_GRASS, TERRAIN_SAND, TERRAIN_SAND_LIGHT, TERRAIN_ICE }; -void mapgen_generate_blank(int mapSize, int height, int waterLevel, int floor, int wall) +void mapgen_generate_blank(mapgen_settings *settings) { int x, y; rct_map_element *mapElement; + map_init(settings->mapSize); + for (y = 1; y < settings->mapSize - 1; y++) { + for (x = 1; x < settings->mapSize - 1; x++) { + mapElement = map_get_surface_element_at(x, y); + map_element_set_terrain(mapElement, settings->floor); + map_element_set_terrain_edge(mapElement, settings->wall); + mapElement->base_height = settings->height; + mapElement->clearance_height = settings->height; + } + } + + mapgen_set_water_level(settings->waterLevel); +} + +void mapgen_generate(mapgen_settings *settings) +{ + int i, x, y, mapSize, floorTexture, wallTexture; + rct_map_element *mapElement; + + srand((unsigned int)time(NULL)); + + mapSize = settings->mapSize; + floorTexture = settings->floor; + wallTexture = settings->wall; + + if (floorTexture == -1) + floorTexture = BaseTerrain[rand() % countof(BaseTerrain)]; + if (wallTexture == -1) { + wallTexture = TERRAIN_EDGE_ROCK; + if (floorTexture == TERRAIN_ICE) + wallTexture = TERRAIN_EDGE_ICE; + } + map_init(mapSize); for (y = 1; y < mapSize - 1; y++) { for (x = 1; x < mapSize - 1; x++) { mapElement = map_get_surface_element_at(x, y); - map_element_set_terrain(mapElement, floor); - map_element_set_terrain_edge(mapElement, wall); - mapElement->base_height = height; - mapElement->clearance_height = height; - } - } - - mapgen_set_water_level(waterLevel); -} - -void mapgen_generate(int mapSize) -{ - int i, x, y, baseTerrain; - rct_map_element *mapElement; - - map_init(mapSize); - - srand((unsigned int)time(NULL)); - baseTerrain = BaseTerrain[rand() % countof(BaseTerrain)]; - for (y = 0; y < mapSize; y++) { - for (x = 0; x < mapSize; x++) { - mapElement = map_get_surface_element_at(x, y); - map_element_set_terrain(mapElement, baseTerrain); - if (baseTerrain == TERRAIN_ICE) - map_element_set_terrain_edge(mapElement, TERRAIN_EDGE_ICE); + map_element_set_terrain(mapElement, floorTexture); + map_element_set_terrain_edge(mapElement, wallTexture); + mapElement->base_height = settings->height; + mapElement->clearance_height = settings->height; } } @@ -98,7 +155,7 @@ void mapgen_generate(int mapSize) int border = 2 + (rand() % 24); for (i = 0; i < 128; i++) { - int radius = 4 + (rand() % 64); + int radius = 4 + (rand() % 32); mapgen_blob( border + (rand() % (_heightSize - (border * 2))), border + (rand() % (_heightSize - (border * 2))), @@ -112,11 +169,10 @@ void mapgen_generate(int mapSize) while (map_smooth(1, 1, mapSize - 1, mapSize - 1)) { } mapgen_set_water_level(6 + (rand() % 4) * 2); - // mapgen_place_trees(); -} -const uint8 GrassTrees[] = { 21, 22, 27, 31, 42, 69, 71, 82, 84, 94, 95, 109 }; -const uint8 DesertTrees[] = { 15, 16, 17, 19, 72, 81, 90, 91 }; + if (settings->trees != 0) + mapgen_place_trees(); +} static void mapgen_place_tree(int type, int x, int y) { @@ -157,9 +213,46 @@ static void mapgen_place_tree(int type, int x, int y) static void mapgen_place_trees() { - int x, y, mapSize, i, rindex, type; + int x, y, mapSize, i, j, rindex, type; rct_map_element *mapElement; + int numGrassTreeIds = 0, numDesertTreeIds = 0, numSnowTreeIds = 0; + int *grassTreeIds = (int*)malloc(countof(GrassTrees) * sizeof(int)); + int *desertTreeIds = (int*)malloc(countof(DesertTrees) * sizeof(int)); + int *snowTreeIds = (int*)malloc(countof(SnowTrees) * sizeof(int)); + + for (i = 0; i < object_entry_group_counts[OBJECT_TYPE_SMALL_SCENERY]; i++) { + rct_scenery_entry *sceneryEntry = g_smallSceneryEntries[i]; + rct_object_entry_extended *entry = &object_entry_groups[OBJECT_TYPE_SMALL_SCENERY].entries[i]; + + if (sceneryEntry == (rct_scenery_entry*)0xFFFFFFFF || sceneryEntry == NULL) + continue; + + for (j = 0; j < countof(GrassTrees); j++) + if (strncmp(GrassTrees[j], entry->name, 8) == 0) + break; + if (j != countof(GrassTrees)) { + grassTreeIds[numGrassTreeIds++] = i; + continue; + } + + for (j = 0; j < countof(DesertTrees); j++) + if (strncmp(DesertTrees[j], entry->name, 8) == 0) + break; + if (j != countof(DesertTrees)) { + desertTreeIds[numDesertTreeIds++] = i; + continue; + } + + for (j = 0; j < countof(SnowTrees); j++) + if (strncmp(SnowTrees[j], entry->name, 8) == 0) + break; + if (j != countof(SnowTrees)) { + snowTreeIds[numSnowTreeIds++] = i; + continue; + } + } + mapSize = RCT2_GLOBAL(RCT2_ADDRESS_MAP_SIZE, sint16); int availablePositionsCount = 0; @@ -197,16 +290,44 @@ static void mapgen_place_trees() for (i = 0; i < min(availablePositionsCount, 2048 + (rand() % 10000)); i++) { pos = &availablePositions[i]; - mapElement = map_get_surface_element_at(x, y); - if (map_element_get_terrain(mapElement) == TERRAIN_SAND) - type = DesertTrees[rand() % countof(DesertTrees)]; - else - type = GrassTrees[rand() % countof(GrassTrees)]; + type = -1; + mapElement = map_get_surface_element_at(pos->x, pos->y); + switch (map_element_get_terrain(mapElement)) { + case TERRAIN_GRASS: + case TERRAIN_DIRT: + case TERRAIN_GRASS_CLUMPS: + if (numGrassTreeIds == 0) + break; - mapgen_place_tree(type, pos->x, pos->y); + type = grassTreeIds[rand() % numGrassTreeIds]; + break; + + case TERRAIN_SAND: + case TERRAIN_SAND_DARK: + case TERRAIN_SAND_LIGHT: + if (numDesertTreeIds == 0) + break; + + if (rand() % 4 == 0) + type = desertTreeIds[rand() % numDesertTreeIds]; + break; + + case TERRAIN_ICE: + if (numSnowTreeIds == 0) + break; + + type = snowTreeIds[rand() % numSnowTreeIds]; + break; + } + + if (type != -1) + mapgen_place_tree(type, pos->x, pos->y); } free(availablePositions); + free(grassTreeIds); + free(desertTreeIds); + free(snowTreeIds); } static void mapgen_set_water_level(int waterLevel) diff --git a/src/world/mapgen.h b/src/world/mapgen.h index 9f98af55c1..d6771a48cd 100644 --- a/src/world/mapgen.h +++ b/src/world/mapgen.h @@ -21,7 +21,19 @@ #ifndef _MAPGEN_H_ #define _MAPGEN_H_ -void mapgen_generate_blank(int mapSize, int height, int waterLevel, int floor, int wall); -void mapgen_generate(int mapSize); +typedef struct { + // Base + int mapSize; + int height; + int waterLevel; + int floor; + int wall; + + // Features (e.g. tree, rivers, lakes etc.) + int trees; +} mapgen_settings; + +void mapgen_generate_blank(mapgen_settings *settings); +void mapgen_generate(mapgen_settings *settings); #endif \ No newline at end of file