From a86482576f65b6069b8fbd8cfb76589bc7e1f522 Mon Sep 17 00:00:00 2001 From: Hielke Morsink Date: Sat, 23 Sep 2017 23:41:32 +0200 Subject: [PATCH] Land Line Tools (select land edges/row of tiles) (This commit is multiple commits squashed together, to make rebasing and merging easier. Many of the commits undid or slightly altered previous changes.) This Extends the land-tool by allowing the player to select an edge of a surface tile, and to select a row of tiles. Both work by holding down the Ctrl key (same key for keeping the same base-height for other tools). When using a single-sized tool, the selection will become the edge, and when using a selection area of 2x2 or higher, the selection becomes a row of tiles. The tables `tile_element_raise_styles` and `tile_element_lower_styles` hold the data for how slopes should change when a tile gets raised or lowered with the land tool. Each row represents a selection, and each column the slope type. Co-authored-by: Adam T <32143337+Despotico@users.noreply.github.com> --- src/openrct2-ui/windows/TopToolbar.cpp | 162 ++++++--- src/openrct2/world/Map.cpp | 475 +++++++++++++++---------- 2 files changed, 402 insertions(+), 235 deletions(-) diff --git a/src/openrct2-ui/windows/TopToolbar.cpp b/src/openrct2-ui/windows/TopToolbar.cpp index 212c1ebff8..001a33a377 100644 --- a/src/openrct2-ui/windows/TopToolbar.cpp +++ b/src/openrct2-ui/windows/TopToolbar.cpp @@ -1857,15 +1857,18 @@ static void top_toolbar_tool_update_scenery_clear(sint16 x, sint16 y){ } } -static void top_toolbar_tool_update_land_paint(sint16 x, sint16 y){ +static void top_toolbar_tool_update_land_paint(sint16 x, sint16 y) +{ map_invalidate_selection_rect(); gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; LocationXY16 mapTile = { 0 }; screen_get_map_xy(x, y, &mapTile.x, &mapTile.y, nullptr); - if (mapTile.x == LOCATION_NULL) { - if (gClearSceneryCost != MONEY32_UNDEFINED) { + if (mapTile.x == LOCATION_NULL) + { + if (gClearSceneryCost != MONEY32_UNDEFINED) + { gClearSceneryCost = MONEY32_UNDEFINED; window_invalidate_by_class(WC_CLEAR_SCENERY); } @@ -1874,12 +1877,14 @@ static void top_toolbar_tool_update_land_paint(sint16 x, sint16 y){ uint8 state_changed = 0; - if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) { + if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) + { gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; state_changed++; } - if (gMapSelectType != MAP_SELECT_TYPE_FULL) { + if (gMapSelectType != MAP_SELECT_TYPE_FULL) + { gMapSelectType = MAP_SELECT_TYPE_FULL; state_changed++; } @@ -1893,12 +1898,14 @@ static void top_toolbar_tool_update_land_paint(sint16 x, sint16 y){ mapTile.x &= 0xFFE0; mapTile.y &= 0xFFE0; - if (gMapSelectPositionA.x != mapTile.x){ + if (gMapSelectPositionA.x != mapTile.x) + { gMapSelectPositionA.x = mapTile.x; state_changed++; } - if (gMapSelectPositionA.y != mapTile.y){ + if (gMapSelectPositionA.y != mapTile.y) + { gMapSelectPositionA.y = mapTile.y; state_changed++; } @@ -1906,12 +1913,14 @@ static void top_toolbar_tool_update_land_paint(sint16 x, sint16 y){ mapTile.x += tool_length; mapTile.y += tool_length; - if (gMapSelectPositionB.x != mapTile.x){ + if (gMapSelectPositionB.x != mapTile.x) + { gMapSelectPositionB.x = mapTile.x; state_changed++; } - if (gMapSelectPositionB.y != mapTile.y){ + if (gMapSelectPositionB.y != mapTile.y) + { gMapSelectPositionB.y = mapTile.y; state_changed++; } @@ -1925,18 +1934,22 @@ static void top_toolbar_tool_update_land_paint(sint16 x, sint16 y){ * * rct2: 0x00664280 */ -static void top_toolbar_tool_update_land(sint16 x, sint16 y){ +static void top_toolbar_tool_update_land(sint16 x, sint16 y) +{ + const bool mapCtrlPressed = input_test_place_object_modifier(PLACE_OBJECT_MODIFIER_COPY_Z); + map_invalidate_selection_rect(); - if (gCurrentToolId == TOOL_UP_DOWN_ARROW){ + if (gCurrentToolId == TOOL_UP_DOWN_ARROW) + { if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) return; money32 lower_cost = selection_lower_land(0); money32 raise_cost = selection_raise_land(0); - if (gLandToolRaiseCost != raise_cost || - gLandToolLowerCost != lower_cost){ + if (gLandToolRaiseCost != raise_cost || gLandToolLowerCost != lower_cost) + { gLandToolRaiseCost = raise_cost; gLandToolLowerCost = lower_cost; window_invalidate_by_class(WC_LAND); @@ -1944,20 +1957,26 @@ static void top_toolbar_tool_update_land(sint16 x, sint16 y){ return; } - sint16 tool_size = gLandToolSize; - LocationXY16 mapTile = { x, y }; + sint16 tool_size = gLandToolSize; + LocationXY16 mapTile; + uint8 side; gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - if (tool_size == 1){ - sint32 direction; - screen_pos_to_map_pos(&mapTile.x, &mapTile.y, &direction); + if (tool_size == 1) + { + sint32 selectionType; + // Get selection type and map coordinates from mouse x,y position + mapTile = { x, y }; + screen_pos_to_map_pos(&mapTile.x, &mapTile.y, &selectionType); + screen_get_map_xy_side(x, y, &mapTile.x, &mapTile.y, &side); - if (mapTile.x == LOCATION_NULL) { + if (mapTile.x == LOCATION_NULL) + { money32 lower_cost = MONEY32_UNDEFINED; money32 raise_cost = MONEY32_UNDEFINED; - if (gLandToolRaiseCost != raise_cost || - gLandToolLowerCost != lower_cost){ + if (gLandToolRaiseCost != raise_cost || gLandToolLowerCost != lower_cost) + { gLandToolRaiseCost = raise_cost; gLandToolLowerCost = lower_cost; window_invalidate_by_class(WC_LAND); @@ -1967,33 +1986,44 @@ static void top_toolbar_tool_update_land(sint16 x, sint16 y){ uint8 state_changed = 0; - if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) { + if (!(gMapSelectFlags & MAP_SELECT_FLAG_ENABLE)) + { gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; state_changed++; } - if (gMapSelectType != direction) { - gMapSelectType = direction; + if (gMapSelectType != selectionType) + { + gMapSelectType = selectionType; state_changed++; } + if ((gMapSelectType != MAP_SELECT_TYPE_EDGE_0 + (side & 0xFF)) && mapCtrlPressed) + { + gMapSelectType = MAP_SELECT_TYPE_EDGE_0 + (side & 0xFF); + state_changed++; + } - if (gMapSelectPositionA.x != mapTile.x){ + if (gMapSelectPositionA.x != mapTile.x) + { gMapSelectPositionA.x = mapTile.x; state_changed++; } - if (gMapSelectPositionA.y != mapTile.y){ + if (gMapSelectPositionA.y != mapTile.y) + { gMapSelectPositionA.y = mapTile.y; state_changed++; } - if (gMapSelectPositionB.x != mapTile.x){ + if (gMapSelectPositionB.x != mapTile.x) + { gMapSelectPositionB.x = mapTile.x; state_changed++; } - if (gMapSelectPositionB.y != mapTile.y){ + if (gMapSelectPositionB.y != mapTile.y) + { gMapSelectPositionB.y = mapTile.y; state_changed++; } @@ -2014,7 +2044,8 @@ static void top_toolbar_tool_update_land(sint16 x, sint16 y){ return; } - screen_get_map_xy(x, y, &mapTile.x, &mapTile.y, nullptr); + // Get map coordinates and the side of the tile that is being hovered over + screen_get_map_xy_side(x, y, &mapTile.x, &mapTile.y, &side); if (mapTile.x == LOCATION_NULL) { money32 lower_cost = MONEY32_UNDEFINED; @@ -2041,30 +2072,74 @@ static void top_toolbar_tool_update_land(sint16 x, sint16 y){ state_changed++; } + if ((gMapSelectType != MAP_SELECT_TYPE_EDGE_0 + (side & 0xFF)) && mapCtrlPressed) + { + gMapSelectType = MAP_SELECT_TYPE_EDGE_0 + (side & 0xFF); + state_changed++; + } if (tool_size == 0) tool_size = 1; sint16 tool_length = (tool_size - 1) * 32; - // Move to tool bottom left - mapTile.x -= (tool_size - 1) * 16; - mapTile.y -= (tool_size - 1) * 16; - mapTile.x &= 0xFFE0; - mapTile.y &= 0xFFE0; + // Decide on shape of the brush for bigger selection size + switch (gMapSelectType) + { + case MAP_SELECT_TYPE_EDGE_0: + case MAP_SELECT_TYPE_EDGE_2: + // Line + mapTile.y -= (tool_size - 1) * 16; + mapTile.y &= 0xFFE0; + break; + case MAP_SELECT_TYPE_EDGE_1: + case MAP_SELECT_TYPE_EDGE_3: + // Line + mapTile.x -= (tool_size - 1) * 16; + mapTile.x &= 0xFFE0; + break; + default: + // Move to tool bottom left + mapTile.x -= (tool_size - 1) * 16; + mapTile.y -= (tool_size - 1) * 16; + mapTile.x &= 0xFFE0; + mapTile.y &= 0xFFE0; + break; + } - if (gMapSelectPositionA.x != mapTile.x){ + if (gMapSelectPositionA.x != mapTile.x) + { gMapSelectPositionA.x = mapTile.x; state_changed++; } - if (gMapSelectPositionA.y != mapTile.y){ + if (gMapSelectPositionA.y != mapTile.y) + { gMapSelectPositionA.y = mapTile.y; state_changed++; } - mapTile.x += tool_length; - mapTile.y += tool_length; + // Go to other side + switch (gMapSelectType) + { + case MAP_SELECT_TYPE_EDGE_0: + case MAP_SELECT_TYPE_EDGE_2: + // Line + mapTile.y += tool_length; + gMapSelectType = MAP_SELECT_TYPE_FULL; + break; + case MAP_SELECT_TYPE_EDGE_1: + case MAP_SELECT_TYPE_EDGE_3: + // Line + mapTile.x += tool_length; + gMapSelectType = MAP_SELECT_TYPE_FULL; + break; + default: + mapTile.x += tool_length; + mapTile.y += tool_length; + break; + } + if (gMapSelectPositionB.x != mapTile.x){ gMapSelectPositionB.x = mapTile.x; @@ -2425,7 +2500,7 @@ static void top_toolbar_tool_update_scenery(sint16 x, sint16 y){ money32 cost = 0; switch (scenery_type){ - case 0: + case SCENERY_TYPE_SMALL: gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; if (gWindowSceneryClusterEnabled) { gMapSelectPositionA.x = mapTile.x - (8 << 5); @@ -2487,7 +2562,7 @@ static void top_toolbar_tool_update_scenery(sint16 x, sint16 y){ gSceneryPlaceCost = cost; break; - case 1: + case SCENERY_TYPE_PATH_ITEM: gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; gMapSelectPositionA.x = mapTile.x; gMapSelectPositionA.y = mapTile.y; @@ -2516,7 +2591,7 @@ static void top_toolbar_tool_update_scenery(sint16 x, sint16 y){ gSceneryPlaceCost = cost; break; - case 2: + case SCENERY_TYPE_WALL: gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; gMapSelectPositionA.x = mapTile.x; gMapSelectPositionA.y = mapTile.y; @@ -2564,7 +2639,7 @@ static void top_toolbar_tool_update_scenery(sint16 x, sint16 y){ gSceneryPlaceCost = cost; break; - case 3: + case SCENERY_TYPE_LARGE: { scenery = get_large_scenery_entry(selected_scenery); LocationXY16* selectedTile = gMapSelectionTiles; @@ -2623,7 +2698,7 @@ static void top_toolbar_tool_update_scenery(sint16 x, sint16 y){ gSceneryPlaceCost = cost; break; } - case 4: + case SCENERY_TYPE_BANNER: gMapSelectFlags |= MAP_SELECT_FLAG_ENABLE; gMapSelectPositionA.x = mapTile.x; gMapSelectPositionA.y = mapTile.y; @@ -2798,6 +2873,7 @@ static void window_top_toolbar_land_tool_drag(sint16 x, sint16 y) sint16 tile_height = -16 / (1 << viewport->zoom); sint32 y_diff = y - gInputDragLastY; + if (y_diff <= tile_height) { gInputDragLastY += tile_height; diff --git a/src/openrct2/world/Map.cpp b/src/openrct2/world/Map.cpp index fcc105f1dd..df236b14ff 100644 --- a/src/openrct2/world/Map.cpp +++ b/src/openrct2/world/Map.cpp @@ -1385,21 +1385,36 @@ void game_command_change_surface_style( } //0x00981A1E -static constexpr const uint8 tile_element_raise_styles[5][32] = { - { 0x01, 0x1B, 0x03, 0x1B, 0x05, 0x21, 0x07, 0x21, 0x09, 0x1B, 0x0B, 0x1B, 0x0D, 0x21, 0x20, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x23, 0x18, 0x19, 0x1A, 0x3B, 0x1C, 0x29, 0x24, 0x1F }, - { 0x02, 0x03, 0x17, 0x17, 0x06, 0x07, 0x17, 0x17, 0x0A, 0x0B, 0x22, 0x22, 0x0E, 0x20, 0x22, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x37, 0x18, 0x19, 0x1A, 0x23, 0x1C, 0x28, 0x26, 0x1F }, - { 0x04, 0x05, 0x06, 0x07, 0x1E, 0x24, 0x1E, 0x24, 0x0C, 0x0D, 0x0E, 0x20, 0x1E, 0x24, 0x1E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x26, 0x18, 0x19, 0x1A, 0x21, 0x1C, 0x2C, 0x3E, 0x1F }, - { 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x20, 0x1D, 0x1D, 0x28, 0x28, 0x1D, 0x1D, 0x28, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x22, 0x18, 0x19, 0x1A, 0x29, 0x1C, 0x3D, 0x2C, 0x1F }, - { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x20, 0x21, 0x20, 0x28, 0x24, 0x20 }, +#define SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT 0x20 +// Table of pre-calculated surface slopes (32) when raising the land tile for a given selection (5) +// 0x1F = new slope +// 0x20 = base height increases +static constexpr const uint8 tile_element_raise_styles[9][32] = { + { 0x01, 0x1B, 0x03, 0x1B, 0x05, 0x21, 0x07, 0x21, 0x09, 0x1B, 0x0B, 0x1B, 0x0D, 0x21, 0x20, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x23, 0x18, 0x19, 0x1A, 0x3B, 0x1C, 0x29, 0x24, 0x1F }, // MAP_SELECT_TYPE_CORNER_0 (absolute rotation) + { 0x02, 0x03, 0x17, 0x17, 0x06, 0x07, 0x17, 0x17, 0x0A, 0x0B, 0x22, 0x22, 0x0E, 0x20, 0x22, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x37, 0x18, 0x19, 0x1A, 0x23, 0x1C, 0x28, 0x26, 0x1F }, // MAP_SELECT_TYPE_CORNER_1 + { 0x04, 0x05, 0x06, 0x07, 0x1E, 0x24, 0x1E, 0x24, 0x0C, 0x0D, 0x0E, 0x20, 0x1E, 0x24, 0x1E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x26, 0x18, 0x19, 0x1A, 0x21, 0x1C, 0x2C, 0x3E, 0x1F }, // MAP_SELECT_TYPE_CORNER_2 + { 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x20, 0x1D, 0x1D, 0x28, 0x28, 0x1D, 0x1D, 0x28, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x22, 0x18, 0x19, 0x1A, 0x29, 0x1C, 0x3D, 0x2C, 0x1F }, // MAP_SELECT_TYPE_CORNER_3 + { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x22, 0x20, 0x20, 0x20, 0x21, 0x20, 0x28, 0x24, 0x20 }, // MAP_SELECT_TYPE_FULL + { 0x0C, 0x0D, 0x0E, 0x20, 0x0C, 0x0D, 0x0E, 0x20, 0x0C, 0x0D, 0x0E, 0x20, 0x2C, 0x2C, 0x2C, 0x2C, 0x0C, 0x0D, 0x0E, 0x20, 0x0C, 0x0C, 0x0E, 0x22, 0x0C, 0x0D, 0x0E, 0x21, 0x2C, 0x2C, 0x2C, 0x2C }, // MAP_SELECT_TYPE_EDGE_0 + { 0x09, 0x09, 0x0B, 0x0B, 0x0D, 0x0D, 0x20, 0x20, 0x09, 0x29, 0x0B, 0x29, 0x0D, 0x29, 0x20, 0x29, 0x09, 0x09, 0x0B, 0x0B, 0x0D, 0x0D, 0x24, 0x22, 0x09, 0x29, 0x0B, 0x29, 0x0D, 0x29, 0x24, 0x29 }, // MAP_SELECT_TYPE_EDGE_1 + { 0x03, 0x03, 0x03, 0x23, 0x07, 0x07, 0x07, 0x23, 0x0B, 0x0B, 0x0B, 0x23, 0x20, 0x20, 0x20, 0x23, 0x03, 0x03, 0x03, 0x23, 0x07, 0x07, 0x07, 0x23, 0x0B, 0x0B, 0x0B, 0x23, 0x20, 0x28, 0x24, 0x23 }, // MAP_SELECT_TYPE_EDGE_2 + { 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x26, 0x26, 0x0E, 0x20, 0x0E, 0x20, 0x0E, 0x20, 0x26, 0x26, 0x06, 0x07, 0x06, 0x07, 0x06, 0x07, 0x26, 0x26, 0x0E, 0x20, 0x0E, 0x21, 0x0E, 0x28, 0x26, 0x26 }, // MAP_SELECT_TYPE_EDGE_3 }; //0x00981ABE -static constexpr const uint8 tile_element_lower_styles[5][32] = { - { 0x2E, 0x00, 0x2E, 0x02, 0x3E, 0x04, 0x3E, 0x06, 0x2E, 0x08, 0x2E, 0x0A, 0x3E, 0x0C, 0x3E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x06, 0x18, 0x19, 0x1A, 0x0B, 0x1C, 0x0C, 0x3E, 0x1F }, - { 0x2D, 0x2D, 0x00, 0x01, 0x2D, 0x2D, 0x04, 0x05, 0x3D, 0x3D, 0x08, 0x09, 0x3D, 0x3D, 0x0C, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x07, 0x18, 0x19, 0x1A, 0x09, 0x1C, 0x3D, 0x0C, 0x1F }, - { 0x2B, 0x3B, 0x2B, 0x3B, 0x00, 0x01, 0x02, 0x03, 0x2B, 0x3B, 0x2B, 0x3B, 0x08, 0x09, 0x0A, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x03, 0x18, 0x19, 0x1A, 0x3B, 0x1C, 0x09, 0x0E, 0x1F }, - { 0x27, 0x27, 0x37, 0x37, 0x27, 0x27, 0x37, 0x37, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x37, 0x18, 0x19, 0x1A, 0x03, 0x1C, 0x0D, 0x06, 0x1F }, - { 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x0D, 0x0E, 0x00 }, +// Basically the inverse of the table above. +// 0x1F = new slope +// 0x20 = base height increases +static constexpr const uint8 tile_element_lower_styles[9][32] = { + { 0x2E, 0x00, 0x2E, 0x02, 0x3E, 0x04, 0x3E, 0x06, 0x2E, 0x08, 0x2E, 0x0A, 0x3E, 0x0C, 0x3E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x06, 0x18, 0x19, 0x1A, 0x0B, 0x1C, 0x0C, 0x3E, 0x1F }, // MAP_SELECT_TYPE_CORNER_0 + { 0x2D, 0x2D, 0x00, 0x01, 0x2D, 0x2D, 0x04, 0x05, 0x3D, 0x3D, 0x08, 0x09, 0x3D, 0x3D, 0x0C, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x07, 0x18, 0x19, 0x1A, 0x09, 0x1C, 0x3D, 0x0C, 0x1F }, // MAP_SELECT_TYPE_CORNER_1 + { 0x2B, 0x3B, 0x2B, 0x3B, 0x00, 0x01, 0x02, 0x03, 0x2B, 0x3B, 0x2B, 0x3B, 0x08, 0x09, 0x0A, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x03, 0x18, 0x19, 0x1A, 0x3B, 0x1C, 0x09, 0x0E, 0x1F }, // MAP_SELECT_TYPE_CORNER_2 + { 0x27, 0x27, 0x37, 0x37, 0x27, 0x27, 0x37, 0x37, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x37, 0x18, 0x19, 0x1A, 0x03, 0x1C, 0x0D, 0x06, 0x1F }, // MAP_SELECT_TYPE_CORNER_3 + { 0x20, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x0D, 0x0E, 0x00 }, // MAP_SELECT_TYPE_FULL + { 0x23, 0x23, 0x23, 0x23, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, 0x23, 0x23, 0x23, 0x23, 0x00, 0x01, 0x02, 0x03, 0x00, 0x01, 0x02, 0x03, 0x00, 0x0D, 0x0E, 0x03 }, // MAP_SELECT_TYPE_EDGE_0 + { 0x26, 0x00, 0x26, 0x02, 0x26, 0x04, 0x26, 0x06, 0x00, 0x00, 0x02, 0x02, 0x04, 0x04, 0x06, 0x06, 0x26, 0x00, 0x26, 0x02, 0x26, 0x04, 0x26, 0x06, 0x00, 0x00, 0x02, 0x0B, 0x04, 0x0D, 0x06, 0x06 }, // MAP_SELECT_TYPE_EDGE_1 + { 0x2C, 0x00, 0x00, 0x00, 0x2C, 0x04, 0x04, 0x04, 0x2C, 0x08, 0x08, 0x08, 0x2C, 0x0C, 0x0C, 0x0C, 0x2C, 0x00, 0x00, 0x00, 0x2C, 0x04, 0x04, 0x07, 0x2C, 0x08, 0x08, 0x0B, 0x2C, 0x0C, 0x0C, 0x0C }, // MAP_SELECT_TYPE_EDGE_2 + { 0x29, 0x29, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x29, 0x29, 0x08, 0x09, 0x08, 0x09, 0x08, 0x09, 0x29, 0x29, 0x00, 0x01, 0x00, 0x01, 0x00, 0x07, 0x29, 0x29, 0x08, 0x09, 0x08, 0x09, 0x0E, 0x09 }, // MAP_SELECT_TYPE_EDGE_3 }; /** @@ -1461,7 +1476,7 @@ static sint32 map_get_corner_height(sint32 z, sint32 slope, sint32 direction) return z; } -static sint32 tile_element_get_corner_height(rct_tile_element *tileElement, sint32 direction) +static sint32 tile_element_get_corner_height(const rct_tile_element *tileElement, sint32 direction) { sint32 z = tileElement->base_height; sint32 slope = tileElement->properties.surface.slope & TILE_ELEMENT_SURFACE_SLOPE_MASK; @@ -1791,43 +1806,50 @@ static uint8 map_get_highest_land_height(sint32 xMin, sint32 xMax, sint32 yMin, return max_height; } -static money32 raise_land(sint32 flags, sint32 x, sint32 y, sint32 z, sint32 ax, sint32 ay, sint32 bx, sint32 by, sint32 selectionType) +static money32 raise_land(sint32 flags, sint32 x, sint32 y, sint32 z, sint32 point_a_x, sint32 point_a_y, sint32 point_b_x, sint32 point_b_y, sint32 selectionType) { money32 cost = 0; + size_t tableRow = selectionType; - if (selectionType < 0 || selectionType >= (sint32)Util::CountOf(tile_element_raise_styles)) + // The selections between MAP_SELECT_TYPE_FULL and MAP_SELECT_TYPE_EDGE_0 are not included in the tables + if (selectionType >= MAP_SELECT_TYPE_EDGE_0 && selectionType <= MAP_SELECT_TYPE_EDGE_3) + tableRow -= MAP_SELECT_TYPE_EDGE_0 - MAP_SELECT_TYPE_FULL - 1; + + if ((flags & GAME_COMMAND_FLAG_APPLY) && gGameCommandNestLevel == 1) { - log_warning("Invalid selection type %d for raising land", selectionType); - return MONEY32_UNDEFINED; - } - - if ((flags & GAME_COMMAND_FLAG_APPLY) && gGameCommandNestLevel == 1) { audio_play_sound_at_location(SOUND_PLACE_ITEM, x, y, z); } // Keep big coordinates within map boundaries - ax = std::max(32, ax); - bx = std::min(gMapSizeMaxXY, bx); - ay = std::max(32, ay); - by = std::min(gMapSizeMaxXY, by); + point_a_x = std::max(32, point_a_x); + point_b_x = std::min(gMapSizeMaxXY, point_b_x); + point_a_y = std::max(32, point_a_y); + point_b_y = std::min(gMapSizeMaxXY, point_b_y); - uint8 min_height = map_get_lowest_land_height(ax, bx, ay, by); + uint8 min_height = map_get_lowest_land_height(point_a_x, point_b_x, point_a_y, point_b_y); - for (sint32 yi = ay; yi <= by; yi += 32) { - for (sint32 xi = ax; xi <= bx; xi += 32) { - rct_tile_element *tile_element = map_get_surface_element_at({xi, yi}); - if (tile_element != nullptr) { + for (sint32 y_coord = point_a_y; y_coord <= point_b_y; y_coord += 32) + { + for (sint32 x_coord = point_a_x; x_coord <= point_b_x; x_coord += 32) + { + rct_tile_element * tile_element = map_get_surface_element_at(x_coord / 32, y_coord / 32); + if (tile_element != nullptr) + { uint8 height = tile_element->base_height; - if (height <= min_height){ - uint8 newStyle = tile_element_raise_styles[selectionType][tile_element->properties.surface.slope & TILE_ELEMENT_SURFACE_SLOPE_MASK]; - if (newStyle & 0x20) { // needs to be raised - height += 2; - newStyle &= ~0x20; - } - money32 tileCost = map_set_land_height(flags, xi, yi, height, newStyle); - if (tileCost == MONEY32_UNDEFINED) - return MONEY32_UNDEFINED; + if (height <= min_height) + { + uint8 raisedCorners = tile_element->properties.surface.slope & TILE_ELEMENT_SURFACE_SLOPE_MASK; + uint8 slope = tile_element_raise_styles[tableRow][raisedCorners]; + if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) + height += 2; + + slope &= TILE_ELEMENT_SURFACE_SLOPE_MASK; + money32 tileCost = map_set_land_height(flags, x_coord, y_coord, height, slope); + if (tileCost == MONEY32_UNDEFINED) + { + return MONEY32_UNDEFINED; + } cost += tileCost; } } @@ -1844,48 +1866,55 @@ static money32 raise_land(sint32 flags, sint32 x, sint32 y, sint32 z, sint32 ax, return cost; } -static money32 lower_land(sint32 flags, sint32 x, sint32 y, sint32 z, sint32 ax, sint32 ay, sint32 bx, sint32 by, sint32 selectionType) +static money32 lower_land(sint32 flags, sint32 x, sint32 y, sint32 z, sint32 point_a_x, sint32 point_a_y, sint32 point_b_x, sint32 point_b_y, sint32 selectionType) { money32 cost = 0; + size_t tableRow = selectionType; - if ((flags & GAME_COMMAND_FLAG_APPLY) && gGameCommandNestLevel == 1) { + // The selections between MAP_SELECT_TYPE_FULL and MAP_SELECT_TYPE_EDGE_0 are not included in the tables + if (selectionType >= MAP_SELECT_TYPE_EDGE_0 && selectionType <= MAP_SELECT_TYPE_EDGE_3) + tableRow -= MAP_SELECT_TYPE_EDGE_0 - MAP_SELECT_TYPE_FULL - 1; + + // Keep big coordinates within map boundaries + point_a_x = std::max(32, point_a_x); + point_b_x = std::min(gMapSizeMaxXY, point_b_x); + point_a_y = std::max(32, point_a_y); + point_b_y = std::min(gMapSizeMaxXY, point_b_y); + + if ((flags & GAME_COMMAND_FLAG_APPLY) && gGameCommandNestLevel == 1) + { audio_play_sound_at_location(SOUND_PLACE_ITEM, x, y, z); } - if (selectionType < 0 || selectionType >= 5) + uint8 max_height = map_get_highest_land_height(point_a_x, point_b_x, point_a_y, point_b_y); + + for (sint32 y_coord = point_a_y; y_coord <= point_b_y; y_coord += 32) { - log_warning("Improper selection type %d", selectionType); - return MONEY32_UNDEFINED; - } - - // Keep big coordinates within map boundaries - ax = std::max(32, ax); - bx = std::min(gMapSizeMaxXY, bx); - ay = std::max(32, ay); - by = std::min(gMapSizeMaxXY, by); - - uint8 max_height = map_get_highest_land_height(ax, bx, ay, by); - - for (sint32 yi = ay; yi <= by; yi += 32) { - for (sint32 xi = ax; xi <= bx; xi += 32) { - rct_tile_element *tile_element = map_get_surface_element_at({xi, yi}); - if (tile_element != nullptr) { + for (sint32 x_coord = point_a_x; x_coord <= point_b_x; x_coord += 32) + { + rct_tile_element * tile_element = map_get_surface_element_at(x_coord / 32, y_coord / 32); + if (tile_element != nullptr) + { uint8 height = tile_element->base_height; - if (tile_element->properties.surface.slope & TILE_ELEMENT_SLOPE_ALL_CORNERS_UP) + if (tile_element->properties.surface.slope & TILE_ELEMENT_SURFACE_RAISED_CORNERS_MASK) height += 2; - if (tile_element->properties.surface.slope & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) + if (tile_element->properties.surface.slope & TILE_ELEMENT_SURFACE_DIAGONAL_FLAG) height += 2; - if (height >= max_height) { - height = tile_element->base_height; - uint8 newStyle = tile_element_lower_styles[selectionType][tile_element->properties.surface.slope & TILE_ELEMENT_SURFACE_SLOPE_MASK]; - if (newStyle & 0x20) { // needs to be lowered - height -= 2; - newStyle &= ~0x20; - } - money32 tileCost = map_set_land_height(flags, xi, yi, height, newStyle); - if (tileCost == MONEY32_UNDEFINED) - return MONEY32_UNDEFINED; + if (height >= max_height) + { + height = tile_element->base_height; + uint8 currentSlope = tile_element->properties.surface.slope & TILE_ELEMENT_SURFACE_SLOPE_MASK; + uint8 newSlope = tile_element_lower_styles[tableRow][currentSlope]; + if (newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) + height -= 2; + + newSlope &= TILE_ELEMENT_SURFACE_SLOPE_MASK; + money32 tileCost = map_set_land_height(flags, x_coord, y_coord, height, newSlope); + if (tileCost == MONEY32_UNDEFINED) + { + return MONEY32_UNDEFINED; + } cost += tileCost; } } @@ -2105,16 +2134,17 @@ static money32 smooth_land_tile(sint32 direction, uint8 flags, sint32 x, sint32 sint32 slope = tileElement->properties.surface.slope & TILE_ELEMENT_SURFACE_SLOPE_MASK; if (raiseLand) { slope = tile_element_raise_styles[direction][slope]; - if (slope & 0x20) { + if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) { targetBaseZ += 2; - slope &= ~0x20; + slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; } } - else { + else + { slope = tile_element_lower_styles[direction][slope]; - if (slope & 0x20) { + if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) { targetBaseZ -= 2; - slope &= ~0x20; + slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; } } return game_do_command(x, flags, y, targetBaseZ | (slope << 8), GAME_COMMAND_SET_LAND_HEIGHT, 0, 0); @@ -2188,9 +2218,9 @@ static money32 smooth_land_row_by_edge(sint32 flags, sint32 x, sint32 y, sint32 if (raiseLand) { if (shouldContinue & 0x4) { slope = tile_element_raise_styles[direction1][slope]; - if (slope & 0x20) { + if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) { targetBaseZ += 2; - slope &= ~0x20; + slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; } } if ((shouldContinue & 0x8) && @@ -2198,9 +2228,9 @@ static money32 smooth_land_row_by_edge(sint32 flags, sint32 x, sint32 y, sint32 map_get_corner_height(targetBaseZ, slope, direction2)) { slope = tile_element_raise_styles[direction2][slope]; - if (slope & 0x20) { + if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) { targetBaseZ += 2; - slope &= ~0x20; + slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; } } } @@ -2208,9 +2238,9 @@ static money32 smooth_land_row_by_edge(sint32 flags, sint32 x, sint32 y, sint32 { if (shouldContinue & 0x4) { slope = tile_element_lower_styles[direction1][slope]; - if (slope & 0x20) { + if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) { targetBaseZ -= 2; - slope &= ~0x20; + slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; } } if ((shouldContinue & 0x8) && @@ -2218,9 +2248,9 @@ static money32 smooth_land_row_by_edge(sint32 flags, sint32 x, sint32 y, sint32 map_get_corner_height(targetBaseZ, slope, direction2)) { slope = tile_element_lower_styles[direction2][slope]; - if (slope & 0x20) { + if (slope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) { targetBaseZ -= 2; - slope &= ~0x20; + slope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; } } } @@ -2303,32 +2333,22 @@ static money32 smooth_land_row_by_corner(sint32 flags, sint32 x, sint32 y, sint3 static money32 smooth_land(sint32 flags, sint32 centreX, sint32 centreY, sint32 mapLeft, sint32 mapTop, sint32 mapRight, sint32 mapBottom, sint32 command) { + // break up information in command + const bool raiseLand = command < 0x7FFF; + const sint32 selectionType = command & 0x7FFF; + const sint32 heightOffset = raiseLand ? 2 : -2; + // Cap bounds to map mapLeft = std::max(mapLeft, 32); mapTop = std::max(mapTop, 32); mapRight = Math::Clamp(0, mapRight, (MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32); mapBottom = Math::Clamp(0, mapBottom, (MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32); - bool raiseLand = command < 0x7FFF; - sint32 commandType = raiseLand ? GAME_COMMAND_RAISE_LAND : GAME_COMMAND_LOWER_LAND; - sint32 centreZ = tile_element_height(centreX, centreY); - sint32 mapLeftRight = mapLeft | (mapRight << 16); - sint32 mapTopBottom = mapTop | (mapBottom << 16); - bool fullTile = ((command & 0x7FFF) == MAP_SELECT_TYPE_FULL); - // Play sound (only once) - if ((flags & GAME_COMMAND_FLAG_APPLY) && gGameCommandNestLevel == 1) { - audio_play_sound_at_location(SOUND_PLACE_ITEM, centreX, centreY, centreZ); - } - - money32 totalCost = 0; - money32 result; - - rct_tile_element *tileElement = map_get_surface_element_at({mapLeft, mapTop}); - if (tileElement == nullptr) + sint32 centreZ = tile_element_height(centreX, centreY); + if ((flags & GAME_COMMAND_FLAG_APPLY) && gGameCommandNestLevel == 1) { - log_warning("Invalid coordinates for land smoothing, x = %d, y = %d", mapLeft, mapTop); - return MONEY32_UNDEFINED; + audio_play_sound_at_location(SOUND_PLACE_ITEM, centreX, centreY, centreZ); } if (flags & GAME_COMMAND_FLAG_APPLY) { @@ -2339,100 +2359,91 @@ static money32 smooth_land(sint32 flags, sint32 centreX, sint32 centreY, sint32 network_set_player_last_action_coord(network_get_player_index(game_command_playerid), coord); } - uint8 maxHeight = 0; - uint8 minHeight = 0xFF; - uint32 newBaseZ = 0; - uint32 newSlope = 0; - - // Predict the land height for future use - if (fullTile) + // Do the smoothing + money32 totalCost = 0; + switch (selectionType) { - minHeight = map_get_lowest_land_height(mapLeft, mapRight, mapTop, mapBottom); - maxHeight = map_get_highest_land_height(mapLeft, mapRight, mapTop, mapBottom); - - if (commandType == GAME_COMMAND_RAISE_LAND) { - minHeight += 2; - maxHeight += 2; - } - else { - maxHeight -= 2; - minHeight -= 2; - } - } - else + case MAP_SELECT_TYPE_FULL: { - // One corner tile selected - newBaseZ = tileElement->base_height; - newSlope = tileElement->properties.surface.slope & TILE_ELEMENT_SURFACE_SLOPE_MASK; - if (commandType == GAME_COMMAND_RAISE_LAND) { - newSlope = tile_element_raise_styles[command & 0xFF][newSlope]; - if (newSlope & 0x20) { - newBaseZ += 2; - newSlope &= ~0x20; - } - } - else { - newSlope = tile_element_lower_styles[command & 0xFF][newSlope]; - if (newSlope & 0x20) { - newBaseZ -= 2; - newSlope &= ~0x20; - } - } - } + uint8 minHeight = heightOffset + map_get_lowest_land_height(mapLeft, mapRight, mapTop, mapBottom); + uint8 maxHeight = heightOffset + map_get_highest_land_height(mapLeft, mapRight, mapTop, mapBottom); - // Then do the smoothing - if (fullTile) { - // Smooth the corners - sint32 z = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 2), maxHeight); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, -32, 0, 2, raiseLand); - tileElement = map_get_surface_element_at(mapLeft >> 5, mapBottom >> 5); - z = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 3), maxHeight); - totalCost += smooth_land_row_by_corner(flags, mapLeft, mapBottom, z, -32, 32, 1, 3, raiseLand); - tileElement = map_get_surface_element_at(mapRight >> 5, mapBottom >> 5); - z = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 0), maxHeight); - totalCost += smooth_land_row_by_corner(flags, mapRight, mapBottom, z, 32, 32, 2, 0, raiseLand); - tileElement = map_get_surface_element_at(mapRight >> 5, mapTop >> 5); - z = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 1), maxHeight); - totalCost += smooth_land_row_by_corner(flags, mapRight, mapTop, z, 32, -32, 3, 1, raiseLand); + // Smooth the 4 corners + { // top-left + rct_tile_element * tileElement = map_get_surface_element_at({ mapLeft, mapTop }); + sint32 z = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 2), maxHeight); + totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, -32, 0, 2, raiseLand); + } + { // bottom-left + rct_tile_element * tileElement = map_get_surface_element_at(mapLeft >> 5, mapBottom >> 5); + sint32 z = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 3), maxHeight); + totalCost += smooth_land_row_by_corner(flags, mapLeft, mapBottom, z, -32, 32, 1, 3, raiseLand); + } + { // bottom-right + rct_tile_element * tileElement = map_get_surface_element_at(mapRight >> 5, mapBottom >> 5); + sint32 z = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 0), maxHeight); + totalCost += smooth_land_row_by_corner(flags, mapRight, mapBottom, z, 32, 32, 2, 0, raiseLand); + } + { // top-right + rct_tile_element * tileElement = map_get_surface_element_at(mapRight >> 5, mapTop >> 5); + sint32 z = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 1), maxHeight); + totalCost += smooth_land_row_by_corner(flags, mapRight, mapTop, z, 32, -32, 3, 1, raiseLand); + } // Smooth the edges - sint32 x = mapLeft; - sint32 y, z2; - for (y = mapTop; y <= mapBottom; y += 32) + rct_tile_element * tileElement = nullptr; + sint32 z1, z2; + for (sint32 y = mapTop; y <= mapBottom; y += 32) { - tileElement = map_get_surface_element_at({x, y}); - z = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 3), maxHeight); - z2 = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 2), maxHeight); - totalCost += smooth_land_row_by_edge(flags, x, y, z, z2, -32, 0, 0, 1, 3, 2, raiseLand); + tileElement = map_get_surface_element_at({ mapLeft, y }); + z1 = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 3), maxHeight); + z2 = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 2), maxHeight); + totalCost += smooth_land_row_by_edge(flags, mapLeft, y, z1, z2, -32, 0, 0, 1, 3, 2, raiseLand); + + tileElement = map_get_surface_element_at({ mapRight, y }); + z1 = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 1), maxHeight); + z2 = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 0), maxHeight); + totalCost += smooth_land_row_by_edge(flags, mapRight, y, z1, z2, 32, 0, 2, 3, 1, 0, raiseLand); } - x = mapRight; - for (y = mapTop; y <= mapBottom; y += 32) + + for (sint32 x = mapLeft; x <= mapRight; x += 32) { - tileElement = map_get_surface_element_at({x, y}); - z = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 1), maxHeight); - z2 = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 0), maxHeight); - totalCost += smooth_land_row_by_edge(flags, x, y, z, z2, 32, 0, 2, 3, 1, 0, raiseLand); - } - y = mapTop; - for (x = mapLeft; x <= mapRight; x += 32) - { - tileElement = map_get_surface_element_at({x, y}); - z = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 1), maxHeight); - z2 = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 2), maxHeight); - totalCost += smooth_land_row_by_edge(flags, x, y, z, z2, 0, -32, 0, 3, 1, 2, raiseLand); - } - y = mapBottom; - for (x = mapLeft; x <= mapRight; x += 32) - { - tileElement = map_get_surface_element_at({x, y}); - z = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 0), maxHeight); - z2 = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 3), maxHeight); - totalCost += smooth_land_row_by_edge(flags, x, y, z, z2, 0, 32, 1, 2, 0, 3, raiseLand); + tileElement = map_get_surface_element_at({ x, mapTop }); + z1 = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 1), maxHeight); + z2 = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 2), maxHeight); + totalCost += smooth_land_row_by_edge(flags, x, mapTop, z1, z2, 0, -32, 0, 3, 1, 2, raiseLand); + + tileElement = map_get_surface_element_at({ x, mapBottom }); + z1 = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 0), maxHeight); + z2 = Math::Clamp(minHeight, (uint8)tile_element_get_corner_height(tileElement, 3), maxHeight); + totalCost += smooth_land_row_by_edge(flags, x, mapBottom, z1, z2, 0, 32, 1, 2, 0, 3, raiseLand); } + break; } - else + case MAP_SELECT_TYPE_CORNER_0: + case MAP_SELECT_TYPE_CORNER_1: + case MAP_SELECT_TYPE_CORNER_2: + case MAP_SELECT_TYPE_CORNER_3: { - // One corner tile selected + rct_tile_element * tileElement = map_get_surface_element_at({ mapLeft, mapTop }); + uint8 newBaseZ = tileElement->base_height; + uint8 newSlope = tileElement->properties.surface.slope & TILE_ELEMENT_SURFACE_SLOPE_MASK; + + if (raiseLand) + { + newSlope = tile_element_raise_styles[selectionType][newSlope]; + } + else + { + newSlope = tile_element_lower_styles[selectionType][newSlope]; + } + + if (newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT) + { + newBaseZ += heightOffset; + newSlope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; + } + // Smooth the corners sint32 z = map_get_corner_height(newBaseZ, newSlope, 2); totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, -32, 0, 2, raiseLand); @@ -2444,7 +2455,8 @@ static money32 smooth_land(sint32 flags, sint32 centreX, sint32 centreY, sint32 totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, -32, 3, 1, raiseLand); // Smooth the edges - switch (command & 0x7FFF) { + switch (selectionType) + { case MAP_SELECT_TYPE_CORNER_0: z = map_get_corner_height(newBaseZ, newSlope, 0); totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, 0, 3, 0, raiseLand); @@ -2482,20 +2494,99 @@ static money32 smooth_land(sint32 flags, sint32 centreX, sint32 centreY, sint32 totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, -32, 3, 2, raiseLand); break; } + break; } + case MAP_SELECT_TYPE_EDGE_0: + case MAP_SELECT_TYPE_EDGE_1: + case MAP_SELECT_TYPE_EDGE_2: + case MAP_SELECT_TYPE_EDGE_3: + { + // TODO: Handle smoothing by edge + // Get the two corners to raise + rct_tile_element * surfaceElement = map_get_surface_element_at({ mapLeft, mapTop }); + uint8 newBaseZ = surfaceElement->base_height; + uint8 oldSlope = surfaceElement->properties.surface.slope; + uint8 newSlope = oldSlope; + sint32 rowIndex = selectionType - (MAP_SELECT_TYPE_EDGE_0 - MAP_SELECT_TYPE_FULL - 1); - // Finally raise / lower the centre tile - result = game_do_command(centreX, flags, centreY, mapLeftRight, commandType, command & 0x7FFF, mapTopBottom); - if (result != MONEY32_UNDEFINED) { - totalCost += result; - } else { + if (raiseLand) + { + newSlope = tile_element_raise_styles[rowIndex][oldSlope]; + } + else + { + newSlope = tile_element_lower_styles[rowIndex][oldSlope]; + } + + const bool changeBaseHeight = newSlope & SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; + if (changeBaseHeight) + { + newBaseZ += heightOffset; + newSlope &= ~SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT; + } + + const uint8 edge = selectionType - MAP_SELECT_TYPE_EDGE_0; + + // Table with corners for each edge selection. The first two are the selected corners, the latter two are the opposites + static constexpr uint8 cornerIndices[][4] = { + { 2, 3, 1, 0 }, // MAP_SELECT_TYPE_EDGE_0 + { 3, 0, 2, 1 }, // MAP_SELECT_TYPE_EDGE_1 + { 0, 1, 3, 2 }, // MAP_SELECT_TYPE_EDGE_2 + { 1, 2, 0, 3 }, // MAP_SELECT_TYPE_EDGE_3 + }; + // Big coordinate offsets for the neigbouring tile for the given edge selection + static constexpr sLocationXY8 stepOffsets[] = { + { -32, 0 }, + { 0, 32 }, + { 32, 0 }, + { 0, -32 }, + }; + + // Smooth higher and lower edges + uint8 c1 = cornerIndices[edge][0]; + uint8 c2 = cornerIndices[edge][1]; + uint8 c3 = cornerIndices[edge][2]; + uint8 c4 = cornerIndices[edge][3]; + uint8 z1 = map_get_corner_height(newBaseZ, newSlope, c1); + uint8 z2 = map_get_corner_height(newBaseZ, newSlope, c2); + uint8 z3 = map_get_corner_height(newBaseZ, newSlope, c3); + uint8 z4 = map_get_corner_height(newBaseZ, newSlope, c4); + // Smooth the edge at the top of the new slope + totalCost += smooth_land_row_by_edge(flags, mapLeft, mapTop, z1, z2, stepOffsets[edge].x, stepOffsets[edge].y, c3, c4, c1, c2, raiseLand); + // Smooth the edge at the bottom of the new slope + totalCost += smooth_land_row_by_edge(flags, mapLeft, mapTop, z3, z4, -stepOffsets[edge].x, -stepOffsets[edge].y, c1, c2, c3, c4, raiseLand); + + // Smooth corners + totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z1, -stepOffsets[edge].y, stepOffsets[edge].x, c2, c1, raiseLand); + totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z2, stepOffsets[edge].y, -stepOffsets[edge].x, c1, c2, raiseLand); + sint32 z = map_get_corner_height(newBaseZ, newSlope, 2); + totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, -32, 0, 2, raiseLand); + z = map_get_corner_height(newBaseZ, newSlope, 0); + totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, 32, 2, 0, raiseLand); + z = map_get_corner_height(newBaseZ, newSlope, 3); + totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, 32, 1, 3, raiseLand); + z = map_get_corner_height(newBaseZ, newSlope, 1); + totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, -32, 3, 1, raiseLand); + break; + } + } // switch selectionType + + // Raise / lower the land tool selection area + sint32 commandType = raiseLand ? GAME_COMMAND_RAISE_LAND : GAME_COMMAND_LOWER_LAND; + sint32 mapLeftRight = mapLeft | (mapRight << 16); + sint32 mapTopBottom = mapTop | (mapBottom << 16); + money32 cost = game_do_command(centreX, flags, centreY, mapLeftRight, commandType, command & 0x7FFF, mapTopBottom); + if (cost == MONEY32_UNDEFINED) + { return MONEY32_UNDEFINED; } + totalCost += cost; + gCommandExpenditureType = RCT_EXPENDITURE_TYPE_LANDSCAPING; - gCommandPosition.x = centreX; - gCommandPosition.y = centreY; - gCommandPosition.z = centreZ; + gCommandPosition.x = centreX; + gCommandPosition.y = centreY; + gCommandPosition.z = centreZ; return totalCost; }