diff --git a/src/world/map.c b/src/world/map.c index c02691b09c..0194509789 100644 --- a/src/world/map.c +++ b/src/world/map.c @@ -2051,13 +2051,473 @@ void game_command_lower_land(int* eax, int* ebx, int* ecx, int* edx, int* esi, i ); } +static int map_get_corner_height(rct_map_element *mapElement, int slopeMask) +{ + int z = mapElement->base_height; + int slope = mapElement->properties.surface.slope & MAP_ELEMENT_SLOPE_MASK; + switch (slopeMask) { + case 1: + if (slope & 1) { + z += 2; + if (slope == 27) { + z += 2; + } + } + break; + case 2: + if (slope & 2) { + z += 2; + if (slope == 23) { + z += 2; + } + } + break; + case 4: + if (slope & 4) { + z += 2; + if (slope == 30) { + z += 2; + } + } + break; + case 8: + if (slope & 8) { + z += 2; + if (slope == 29) { + z += 2; + } + } + break; + } + return z; +} + +/** + * + * rct2: 0x0068C3B2 + */ +static money32 sub_68C3B2(uint8 flags, int x, int y, uint8 targetBaseZ, uint8 minBaseZ) +{ + // Check if inside map bounds + if (x < 0 || y < 0 || x >= (256 * 32) || y >= (256 * 32)) { + return MONEY32_UNDEFINED; + } + + // Get height of tile + rct_map_element *mapElement = map_get_surface_element_at(x >> 5, y >> 5); + uint8 baseZ = map_get_corner_height(mapElement, 1); + + // Check if tile is same height as target tile + if (baseZ == targetBaseZ) { + // No need to raise or lower + return MONEY32_UNDEFINED; + } + + uint8 style; + if (targetBaseZ <= baseZ) { + baseZ = baseZ - targetBaseZ; + if (baseZ <= minBaseZ) { + return MONEY32_UNDEFINED; + } + targetBaseZ = mapElement->base_height; + int slope = mapElement->properties.surface.slope & MAP_ELEMENT_SLOPE_MASK; + style = map_element_lower_styles[0][slope]; + if (style & 0x20) { + targetBaseZ -= 2; + style &= ~0x20; + } + } else { + baseZ = targetBaseZ - baseZ; + if (baseZ <= minBaseZ) { + return MONEY32_UNDEFINED; + } + targetBaseZ = mapElement->base_height; + int slope = mapElement->properties.surface.slope & MAP_ELEMENT_SLOPE_MASK; + style = map_element_raise_styles[0][slope]; + if ((style & 0x20) != 0) { + targetBaseZ += 2; + style &= ~0x20; + } + } + + return game_do_command(x, flags, y, targetBaseZ | (style << 8), GAME_COMMAND_SET_LAND_HEIGHT, 0, 0); +} + +/** + * + * rct2: 0x0068C47A + */ +static money32 sub_68C47A(uint8 flags, int x, int y, uint8 targetBaseZ, uint8 minBaseZ) +{ + // Check if inside map bounds + if (x < 0 || y < 0 || x >= (256 * 32) || y >= (256 * 32)) { + return MONEY32_UNDEFINED; + } + + // Get height of tile + rct_map_element *mapElement = map_get_surface_element_at(x >> 5, y >> 5); + uint8 baseZ = map_get_corner_height(mapElement, 2); + + // Check if tile is same height as target tile + if (baseZ == targetBaseZ) { + // No need to raise or lower + return MONEY32_UNDEFINED; + } + + uint8 style; + if (targetBaseZ <= baseZ) { + baseZ = baseZ - targetBaseZ; + if (baseZ <= minBaseZ) { + return MONEY32_UNDEFINED; + } + targetBaseZ = mapElement->base_height; + int slope = mapElement->properties.surface.slope & MAP_ELEMENT_SLOPE_MASK; + style = map_element_lower_styles[1][slope]; + if (style & 0x20) { + targetBaseZ -= 2; + style &= ~0x20; + } + } else { + baseZ = targetBaseZ - baseZ; + if (baseZ <= minBaseZ) { + return MONEY32_UNDEFINED; + } + targetBaseZ = mapElement->base_height; + int slope = mapElement->properties.surface.slope & MAP_ELEMENT_SLOPE_MASK; + style = map_element_raise_styles[1][slope]; + if ((style & 0x20) != 0) { + targetBaseZ += 2; + style &= ~0x20; + } + } + + return game_do_command(x, flags, y, targetBaseZ | (style << 8), GAME_COMMAND_SET_LAND_HEIGHT, 0, 0); +} + +/** + * + * rct2: 0x0068C222 + */ +static money32 sub_68C222(uint8 flags, int x, int y, uint8 targetBaseZ, uint8 minBaseZ) +{ + // Check if inside map bounds + if (x < 0 || y < 0 || x >= (256 * 32) || y >= (256 * 32)) { + return MONEY32_UNDEFINED; + } + + // Get height of tile + rct_map_element *mapElement = map_get_surface_element_at(x >> 5, y >> 5); + uint8 baseZ = map_get_corner_height(mapElement, 4); + + // Check if tile is same height as target tile + if (baseZ == targetBaseZ) { + // No need to raise or lower + return MONEY32_UNDEFINED; + } + + uint8 style; + if (targetBaseZ <= baseZ) { + baseZ = baseZ - targetBaseZ; + if (baseZ <= minBaseZ) { + return MONEY32_UNDEFINED; + } + targetBaseZ = mapElement->base_height; + int slope = mapElement->properties.surface.slope & MAP_ELEMENT_SLOPE_MASK; + style = map_element_lower_styles[2][slope]; + if (style & 0x20) { + targetBaseZ -= 2; + style &= ~0x20; + } + } else { + baseZ = targetBaseZ - baseZ; + if (baseZ <= minBaseZ) { + return MONEY32_UNDEFINED; + } + targetBaseZ = mapElement->base_height; + int slope = mapElement->properties.surface.slope & MAP_ELEMENT_SLOPE_MASK; + style = map_element_raise_styles[2][slope]; + if ((style & 0x20) != 0) { + targetBaseZ += 2; + style &= ~0x20; + } + } + + return game_do_command(x, flags, y, targetBaseZ | (style << 8), GAME_COMMAND_SET_LAND_HEIGHT, 0, 0); +} + +/** + * + * rct2: 0x0068C2EA + */ +static money32 sub_68C2EA(uint8 flags, int x, int y, uint8 targetBaseZ, uint8 minBaseZ) +{ + // Check if inside map bounds + if (x < 0 || y < 0 || x >= (256 * 32) || y >= (256 * 32)) { + return MONEY32_UNDEFINED; + } + + // Get height of tile + rct_map_element *mapElement = map_get_surface_element_at(x >> 5, y >> 5); + uint8 baseZ = map_get_corner_height(mapElement, 8); + + // Check if tile is same height as target tile + if (baseZ == targetBaseZ) { + // No need to raise or lower + return MONEY32_UNDEFINED; + } + + uint8 style; + if (targetBaseZ <= baseZ) { + baseZ = baseZ - targetBaseZ; + if (baseZ <= minBaseZ) { + return MONEY32_UNDEFINED; + } + targetBaseZ = mapElement->base_height; + int slope = mapElement->properties.surface.slope & MAP_ELEMENT_SLOPE_MASK; + style = map_element_lower_styles[3][slope]; + if (style & 0x20) { + targetBaseZ -= 2; + style &= ~0x20; + } + } else { + baseZ = targetBaseZ - baseZ; + if (baseZ <= minBaseZ) { + return MONEY32_UNDEFINED; + } + targetBaseZ = mapElement->base_height; + int slope = mapElement->properties.surface.slope & MAP_ELEMENT_SLOPE_MASK; + style = map_element_raise_styles[3][slope]; + if ((style & 0x20) != 0) { + targetBaseZ += 2; + style &= ~0x20; + } + } + + return game_do_command(x, flags, y, targetBaseZ | (style << 8), GAME_COMMAND_SET_LAND_HEIGHT, 0, 0); +} + /** * * rct2: 0x0068BC01 */ void game_command_smooth_land(int* eax, int* ebx, int* ecx, int* edx, int* esi, int* edi, int* ebp) { - RCT2_CALLFUNC_X(0x0068BC01, eax, ebx, ecx, edx, esi, edi, ebp); + // RCT2_CALLFUNC_X(0x0068BC01, eax, ebx, ecx, edx, esi, edi, ebp); return; + + int flags = *ebx & 0xFF; + int centreX = *eax & 0xFFFF; + int centreY = *ecx & 0xFFFF; + int centreZ = map_element_height(centreX, centreY); + int mapLeftRight = *edx; + int mapTopBottom = *ebp; + int mapLeft = mapLeftRight & 0xFFFF; + int mapTop = mapTopBottom & 0xFFFF; + int mapRight = mapLeftRight >> 16; + int mapBottom = mapTopBottom >> 16; + + int subCommand = *edi; + int subGameCommandType = *edi == 1 ? GAME_COMMAND_RAISE_LAND : GAME_COMMAND_LOWER_LAND; + + // Play sound (only once) + if ((flags & GAME_COMMAND_FLAG_APPLY) && RCT2_GLOBAL(0x009A8C28, uint8) == 1) { + sound_play_panned(SOUND_PLACE_ITEM, 0x8001, centreX, centreY, centreZ); + } + + money32 totalCost = 0; + + // First raise / lower the centre tile + money32 result; + result = game_do_command(centreX, flags, centreY, mapLeftRight, subGameCommandType, 4, mapTopBottom); + if (result != MONEY32_UNDEFINED) { + totalCost += result; + } + + int x, y, z; + rct_map_element *mapElement; + + x = mapLeft; + y = mapTop; + mapElement = map_get_surface_element_at(x >> 5, y >> 5); + int slope = mapElement->properties.surface.slope & MAP_ELEMENT_SLOPE_MASK; + if (slope != 0) { + int command = subCommand == 0xFFFF ? GAME_COMMAND_RAISE_LAND : GAME_COMMAND_LOWER_LAND; + result = game_do_command(centreX, flags, centreY, mapLeftRight, command, 4, mapTopBottom); + if (result != MONEY32_UNDEFINED) { + totalCost += result; + } + + x = mapLeft; + y = mapTop; + mapElement = map_get_surface_element_at(x >> 5, y >> 5); + slope = mapElement->properties.surface.slope & MAP_ELEMENT_SLOPE_MASK; + + *ebx = totalCost * 4; + RCT2_GLOBAL(RCT2_ADDRESS_NEXT_EXPENDITURE_TYPE, uint8) = RCT_EXPENDITURE_TYPE_LANDSCAPING * 4; + RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_X, uint32) = centreX; + RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_Y, uint32) = centreY; + RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_Z, uint32) = centreZ; + return; + } + + x = mapLeft; + y = mapTop; + int bx = ((mapRight - mapLeft) >> 5) - 1; + sint8 byte_F1AD5C = -2; + + for (;;) { + byte_F1AD5C += 2; + bx += 2; + x -= 32; + y -= 32; + if (bx > 64) { + *ebx = totalCost * 4; + RCT2_GLOBAL(RCT2_ADDRESS_NEXT_EXPENDITURE_TYPE, uint8) = RCT_EXPENDITURE_TYPE_LANDSCAPING * 4; + RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_X, uint32) = centreX; + RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_Y, uint32) = centreY; + RCT2_GLOBAL(RCT2_ADDRESS_COMMAND_MAP_Z, uint32) = centreZ; + return; + } + + sint8 minZ = (byte_F1AD5C << 1) & 0xFF; + + // Corner (North-West) + mapElement = map_get_surface_element_at(mapLeft >> 5, mapTop >> 5); + z = map_get_corner_height(mapElement, 4); + result = sub_68C3B2(flags, x, y, z, minZ); + if (result != MONEY32_UNDEFINED) { + totalCost += result; + } + y += 32; + + // Side (West) + for (int i = 0; i < bx; i++) { + int y2 = clamp(mapTop, y, mapBottom); + mapElement = map_get_surface_element_at(mapLeft >> 5, y2 >> 5); + z = map_get_corner_height(mapElement, 4); + result = sub_68C47A(flags, x, y, z, minZ); + if (result != MONEY32_UNDEFINED) { + totalCost += result; + } + minZ -= 2; + if (y >= mapTop) { + minZ += 2; + if (y > mapBottom) { + minZ += 2; + } + } + z = map_get_corner_height(mapElement, 8); + result = sub_68C3B2(flags, x, y, z, minZ); + if (result != MONEY32_UNDEFINED) { + totalCost += result; + } + + y += 32; + } + + // Corner (South-West) + mapElement = map_get_surface_element_at(mapLeft >> 5, mapBottom >> 5); + z = map_get_corner_height(mapElement, 8); + result = sub_68C47A(flags, x, y, z, minZ); + if (result != MONEY32_UNDEFINED) { + totalCost += result; + } + x += 32; + + // Side (South) + for (int i = 0; i < bx; i++) { + int x2 = clamp(mapLeft, x, mapRight); + mapElement = map_get_surface_element_at(x2 >> 5, mapBottom >> 5); + z = map_get_corner_height(mapElement, 8); + result = sub_68C222(flags, x, y, z, minZ); + if (result != MONEY32_UNDEFINED) { + totalCost += result; + } + minZ -= 2; + if (x >= mapLeft) { + minZ += 2; + if (x > mapRight) { + minZ += 2; + } + } + z = map_get_corner_height(mapElement, 1); + result = sub_68C47A(flags, x, y, z, minZ); + if (result != MONEY32_UNDEFINED) { + totalCost += result; + } + + x += 32; + } + + // Corner (South-East) + mapElement = map_get_surface_element_at(mapRight >> 5, mapBottom >> 5); + z = map_get_corner_height(mapElement, 1); + result = sub_68C222(flags, x, y, z, minZ); + if (result != MONEY32_UNDEFINED) { + totalCost += result; + } + y -= 32; + + // Side (East) + for (int i = 0; i < bx; i++) { + int y2 = clamp(mapTop, y, mapBottom); + mapElement = map_get_surface_element_at(mapRight >> 5, y2 >> 5); + z = map_get_corner_height(mapElement, 1); + result = sub_68C2EA(flags, x, y, z, minZ); + if (result != MONEY32_UNDEFINED) { + totalCost += result; + } + minZ -= 2; + if (y <= mapBottom) { + minZ += 2; + if (y < mapTop) { + minZ += 2; + } + } + z = map_get_corner_height(mapElement, 2); + result = sub_68C222(flags, x, y, z, minZ); + if (result != MONEY32_UNDEFINED) { + totalCost += result; + } + + y -= 32; + } + + // Corner (North-East) + mapElement = map_get_surface_element_at(mapRight >> 5, mapTop >> 5); + z = map_get_corner_height(mapElement, 2); + result = sub_68C2EA(flags, x, y, z, minZ); + if (result != MONEY32_UNDEFINED) { + totalCost += result; + } + x -= 32; + + // Side (North) + for (int i = 0; i < bx; i++) { + int x2 = clamp(mapLeft, x, mapRight); + mapElement = map_get_surface_element_at(x2 >> 5, mapTop >> 5); + z = map_get_corner_height(mapElement, 2); + result = sub_68C3B2(flags, x, y, z, minZ); + if (result != MONEY32_UNDEFINED) { + totalCost += result; + } + minZ -= 2; + if (x <= mapRight) { + minZ += 2; + if (x < mapLeft) { + minZ += 2; + } + } + z = map_get_corner_height(mapElement, 4); + result = sub_68C2EA(flags, x, y, z, minZ); + if (result != MONEY32_UNDEFINED) { + totalCost += result; + } + + x -= 32; + } + } + + *ebx = totalCost; } /**