1
0
mirror of https://github.com/OpenRCT2/OpenRCT2 synced 2026-01-22 14:24:33 +01:00

Replace mountain tool algorithm

Fixes #2225. The mountain tool will now stop at land edges. It also improves the performance of the mountain tool.
This commit is contained in:
jensj12
2017-07-13 16:54:37 +02:00
parent 208ffbba89
commit f35b0e6ace

View File

@@ -2093,63 +2093,192 @@ void game_command_lower_land(sint32* eax, sint32* ebx, sint32* ecx, sint32* edx,
);
}
/**
*
* rct2: 0x0068C3B2 slope 1, style 0
* rct2: 0x0068C47A slope 2, style 1
* rct2: 0x0068C222 slope 4, style 2
* rct2: 0x0068C2EA slope 8, style 3
*/
static money32 smooth_land_tile(sint32 direction, uint8 flags, sint32 x, sint32 y, sint32 targetBaseZ, sint32 minBaseZ)
static money32 smooth_land_tile(sint32 direction, uint8 flags, sint32 x, sint32 y, rct_map_element * mapElement, bool raiseLand)
{
// Check if inside map bounds
if (!map_is_location_valid(x, y)) {
return MONEY32_UNDEFINED;
}
// Get height of tile
rct_map_element *mapElement = map_get_surface_element_at(x >> 5, y >> 5);
if (mapElement == NULL)
{
log_warning("Invalid coordinates for land smoothing, x = %d, y = %d", x, y);
return MONEY32_UNDEFINED;
}
sint32 baseZ = map_element_get_corner_height(mapElement, direction);
// 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;
sint32 slope = mapElement->properties.surface.slope & MAP_ELEMENT_SLOPE_MASK;
style = map_element_lower_styles[direction][slope];
if (style & 0x20) {
targetBaseZ -= 2;
style &= ~0x20;
}
} else {
baseZ = targetBaseZ - baseZ;
if (baseZ <= minBaseZ) {
return MONEY32_UNDEFINED;
}
targetBaseZ = mapElement->base_height;
sint32 slope = mapElement->properties.surface.slope & MAP_ELEMENT_SLOPE_MASK;
style = map_element_raise_styles[direction][slope];
if ((style & 0x20) != 0) {
sint32 targetBaseZ = mapElement->base_height;
sint32 slope = mapElement->properties.surface.slope & MAP_ELEMENT_SLOPE_MASK;
if (raiseLand) {
slope = map_element_raise_styles[direction][slope];
if (slope & 0x20) {
targetBaseZ += 2;
style &= ~0x20;
slope &= ~0x20;
}
}
else {
slope = map_element_lower_styles[direction][slope];
if (slope & 0x20) {
targetBaseZ -= 2;
slope &= ~0x20;
}
}
return game_do_command(x, flags, y, targetBaseZ | (slope << 8), GAME_COMMAND_SET_LAND_HEIGHT, 0, 0);
}
return game_do_command(x, flags, y, targetBaseZ | (style << 8), GAME_COMMAND_SET_LAND_HEIGHT, 0, 0);
static money32 smooth_land_row_by_edge(sint32 flags, sint32 x, sint32 y, sint32 expectedLandHeight1, sint32 expectedLandHeight2, sint32 stepX, sint32 stepY, sint32 direction1, sint32 direction2, sint32 checkDirection1, sint32 checkDirection2, bool raiseLand)
{
bool shouldContinue = true;
sint32 landChangePerTile = raiseLand ? -2 : 2;
rct_map_element *mapElement, *nextMapElement;
money32 totalCost = 0;
money32 result;
// check if we need to start at all
if (!map_is_location_valid(x, y) || !map_is_location_valid(x + stepX, y + stepY)) {
return 0;
}
mapElement = map_get_surface_element_at(x >> 5, y >> 5);
nextMapElement = map_get_surface_element_at((x + stepX) >> 5, (y + stepY) >> 5);
if (mapElement == NULL || nextMapElement == NULL) {
return 0;
}
if (map_element_get_corner_height(mapElement, checkDirection1) != expectedLandHeight1 + (raiseLand ? -2 : 2)) {
return 0;
}
if (map_element_get_corner_height(mapElement, checkDirection2) != expectedLandHeight2 + (raiseLand ? -2 : 2)) {
return 0;
}
if (map_element_get_corner_height(mapElement, checkDirection1) != map_element_get_corner_height(nextMapElement, direction1)) {
return 0;
}
if (map_element_get_corner_height(mapElement, checkDirection2) != map_element_get_corner_height(nextMapElement, direction2)) {
return 0;
}
while (shouldContinue)
{
x += stepX;
y += stepY;
// check if we need to continue after raising the current tile
// this needs to be checked before the tile is changed
if (!map_is_location_valid(x + stepX, y + stepY)) {
shouldContinue = false;
}
else
{
mapElement = nextMapElement;
nextMapElement = map_get_surface_element_at((x + stepX) >> 5, (y + stepY) >> 5);
if (nextMapElement == NULL) {
shouldContinue = false;
}
if (map_element_get_corner_height(mapElement, direction1) + landChangePerTile != map_element_get_corner_height(mapElement, checkDirection1)) {
shouldContinue = false;
}
if (map_element_get_corner_height(mapElement, direction2) + landChangePerTile != map_element_get_corner_height(mapElement, checkDirection2)) {
if (shouldContinue)
{
//totalCost += smooth_land_row_by_corner(flags, x, y, expectedLandHeight1, stepX, stepY, direction1, checkDirection1, raiseLand, landChangePerTile);
}
shouldContinue = false;
}
if (shouldContinue && map_element_get_corner_height(mapElement, checkDirection1) != map_element_get_corner_height(nextMapElement, direction1)) {
shouldContinue = false;
}
if (shouldContinue && map_element_get_corner_height(mapElement, checkDirection2) != map_element_get_corner_height(nextMapElement, direction2)) {
shouldContinue = false;
}
}
expectedLandHeight1 += landChangePerTile;
// change land of current tile
sint32 targetBaseZ = mapElement->base_height;
sint32 slope = mapElement->properties.surface.slope & MAP_ELEMENT_SLOPE_MASK;
if (raiseLand) {
slope = map_element_raise_styles[direction1][slope];
if (slope & 0x20) {
targetBaseZ += 2;
slope &= ~0x20;
}
slope = map_element_raise_styles[direction2][slope];
if (slope & 0x20) {
targetBaseZ += 2;
slope &= ~0x20;
}
}
else
{
slope = map_element_lower_styles[direction1][slope];
if (slope & 0x20) {
targetBaseZ -= 2;
slope &= ~0x20;
}
slope = map_element_lower_styles[direction2][slope];
if (slope & 0x20) {
targetBaseZ -= 2;
slope &= ~0x20;
}
}
result = game_do_command(x, flags, y, targetBaseZ | (slope << 8), GAME_COMMAND_SET_LAND_HEIGHT, 0, 0);
if (result != MONEY32_UNDEFINED) {
totalCost += result;
}
}
return totalCost;
}
static money32 smooth_land_row_by_corner(sint32 flags, sint32 x, sint32 y, sint32 expectedLandHeight, sint32 stepX, sint32 stepY, sint32 direction, sint32 checkDirection, bool raiseLand)
{
bool shouldContinue = true;
rct_map_element *mapElement, *nextMapElement;
money32 totalCost = 0;
money32 result;
sint32 landChangePerTile;
if (stepX == 0 || stepY == 0)
{
landChangePerTile = raiseLand ? -2 : 2;
}
else
{
landChangePerTile = raiseLand ? -4 : 4;
}
// check if we need to start at all
if (!map_is_location_valid(x, y) || !map_is_location_valid(x + stepX, y + stepY)) {
return 0;
}
mapElement = map_get_surface_element_at(x >> 5, y >> 5);
nextMapElement = map_get_surface_element_at((x + stepX) >> 5, (y + stepY) >> 5);
if (mapElement == NULL || nextMapElement == NULL) {
return 0;
}
if (map_element_get_corner_height(mapElement, checkDirection) != expectedLandHeight + (raiseLand ? -2 : 2)) {
return 0;
}
if (map_element_get_corner_height(mapElement, checkDirection) != map_element_get_corner_height(nextMapElement, direction)) {
return 0;
}
while (shouldContinue)
{
x += stepX;
y += stepY;
// check if we need to continue after raising the current tile
// this needs to be checked before the tile is changed
if (!map_is_location_valid(x + stepX, y + stepY)) {
shouldContinue = false;
}
else
{
mapElement = nextMapElement;
nextMapElement = map_get_surface_element_at((x + stepX) >> 5, (y + stepY) >> 5);
if (nextMapElement == NULL) {
shouldContinue = false;
}
if (map_element_get_corner_height(mapElement, direction) + landChangePerTile != map_element_get_corner_height(mapElement, checkDirection)) {
shouldContinue = false;
}
if (shouldContinue && map_element_get_corner_height(mapElement, checkDirection) != map_element_get_corner_height(nextMapElement, direction)) {
shouldContinue = false;
}
}
if (stepX*stepY != 0) {
totalCost += smooth_land_row_by_corner(flags, x, y, expectedLandHeight + (landChangePerTile / 2), 0, stepY, direction, (checkDirection + 1)%4, raiseLand);
totalCost += smooth_land_row_by_corner(flags, x, y, expectedLandHeight + (landChangePerTile / 2), stepX, 0, direction, (checkDirection + 3)%4, raiseLand);
}
expectedLandHeight += landChangePerTile;
// change land of current tile
result = smooth_land_tile(direction, flags, x, y, mapElement, raiseLand);
if (result != MONEY32_UNDEFINED) {
totalCost += result;
}
}
return totalCost;
}
static money32 smooth_land(sint32 flags, sint32 centreX, sint32 centreY, sint32 mapLeft, sint32 mapTop, sint32 mapRight, sint32 mapBottom, sint32 command)
@@ -2160,7 +2289,8 @@ static money32 smooth_land(sint32 flags, sint32 centreX, sint32 centreY, sint32
mapRight = clamp(0, mapRight, (MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32);
mapBottom = clamp(0, mapBottom, (MAXIMUM_MAP_SIZE_TECHNICAL - 1) * 32);
sint32 commandType;
bool raiseLand = command < 0x7FFF;
sint32 commandType = raiseLand ? GAME_COMMAND_RAISE_LAND : GAME_COMMAND_LOWER_LAND;
sint32 centreZ = map_element_height(centreX, centreY);
sint32 mapLeftRight = mapLeft | (mapRight << 16);
sint32 mapTopBottom = mapTop | (mapBottom << 16);
@@ -2173,7 +2303,6 @@ static money32 smooth_land(sint32 flags, sint32 centreX, sint32 centreY, sint32
money32 totalCost = 0;
money32 result;
commandType = command < 0x7FFF ? GAME_COMMAND_RAISE_LAND : GAME_COMMAND_LOWER_LAND;
rct_map_element *mapElement = map_get_surface_element_at(mapLeft >> 5, mapTop >> 5);
if (mapElement == NULL)
@@ -2231,192 +2360,102 @@ static money32 smooth_land(sint32 flags, sint32 centreX, sint32 centreY, sint32
}
}
sint32 x = mapLeft;
sint32 y = mapTop;
sint32 size = ((mapRight - mapLeft) >> 5) + 1;
sint32 initialMinZ = -2;
// Then do the smoothing
// The coords go in circles around the selected tile(s)
for (; size <= MAXIMUM_MAP_SIZE_TECHNICAL; size += 2) {
initialMinZ += 2;
sint32 minZ = initialMinZ * 2;
x -= 32;
y -= 32;
// Corner (North-West)
mapElement = map_get_surface_element_at(mapLeft >> 5, mapTop >> 5);
if (fullTile) {
sint32 z = clamp(minHeight, map_element_get_corner_height(mapElement, 2), maxHeight);
if (!fullTile)
z = map_get_corner_height(newBaseZ, newSlope, 2);
result = smooth_land_tile(0, flags, x, y, z, minZ);
if (result != MONEY32_UNDEFINED) {
totalCost += result;
}
y += 32;
// Side (West)
for (sint32 i = 0; i < size; i++) {
sint32 y2 = clamp(mapTop, y, mapBottom);
mapElement = map_get_surface_element_at(mapLeft >> 5, y2 >> 5);
if (y >= mapTop) {
z = clamp(minHeight, map_element_get_corner_height(mapElement, 3), maxHeight);
if (!fullTile)
z = map_get_corner_height(newBaseZ, newSlope, 3);
result = smooth_land_tile((y <= mapBottom) ? 0 : 1, flags, x, y, z, minZ);
if (result != MONEY32_UNDEFINED) {
totalCost += result;
}
}
minZ -= 2;
if (y >= mapTop) {
minZ += 2;
if (y > mapBottom) {
minZ += 2;
}
}
if (y <= mapBottom) {
z = clamp(minHeight, map_element_get_corner_height(mapElement, 2), maxHeight);
if (!fullTile)
z = map_get_corner_height(newBaseZ, newSlope, 2);
result = smooth_land_tile((y >= mapTop) ? 1 : 0, flags, x, y, z, minZ);
if (result != MONEY32_UNDEFINED) {
totalCost += result;
}
}
y += 32;
}
// Corner (South-West)
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, -32, 0, 2, raiseLand);
mapElement = map_get_surface_element_at(mapLeft >> 5, mapBottom >> 5);
z = clamp(minHeight, map_element_get_corner_height(mapElement, 3), maxHeight);
if (!fullTile)
z = map_get_corner_height(newBaseZ, newSlope, 3);
result = smooth_land_tile(1, flags, x, y, z, minZ);
if (result != MONEY32_UNDEFINED) {
totalCost += result;
}
x += 32;
// Side (South)
for (sint32 i = 0; i < size; i++) {
sint32 x2 = clamp(mapLeft, x, mapRight);
mapElement = map_get_surface_element_at(x2 >> 5, mapBottom >> 5);
if (x >= mapLeft) {
z = clamp(minHeight, map_element_get_corner_height(mapElement, 0), maxHeight);
if (!fullTile)
z = map_get_corner_height(newBaseZ, newSlope, 0);
result = smooth_land_tile((x <= mapRight) ? 1 : 2, flags, x, y, z, minZ);
if (result != MONEY32_UNDEFINED) {
totalCost += result;
}
}
minZ -= 2;
if (x >= mapLeft) {
minZ += 2;
if (x > mapRight) {
minZ += 2;
}
}
if (x <= mapRight) {
z = clamp(minHeight, map_element_get_corner_height(mapElement, 3), maxHeight);
if (!fullTile)
z = map_get_corner_height(newBaseZ, newSlope, 3);
result = smooth_land_tile((x >= mapLeft) ? 2 : 1, flags, x, y, z, minZ);
if (result != MONEY32_UNDEFINED) {
totalCost += result;
}
}
x += 32;
}
// Corner (South-East)
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapBottom, z, -32, 32, 1, 3, raiseLand);
mapElement = map_get_surface_element_at(mapRight >> 5, mapBottom >> 5);
z = clamp(minHeight, map_element_get_corner_height(mapElement, 0), maxHeight);
if (!fullTile)
z = map_get_corner_height(newBaseZ, newSlope, 0);
result = smooth_land_tile(2, flags, x, y, z, minZ);
if (result != MONEY32_UNDEFINED) {
totalCost += result;
}
y -= 32;
// Side (East)
for (sint32 i = 0; i < size; i++) {
sint32 y2 = clamp(mapTop, y, mapBottom);
mapElement = map_get_surface_element_at(mapRight >> 5, y2 >> 5);
if (y <= mapBottom) {
z = clamp(minHeight, map_element_get_corner_height(mapElement, 1), maxHeight);
if (!fullTile)
z = map_get_corner_height(newBaseZ, newSlope, 1);
result = smooth_land_tile((y >= mapTop) ? 2 : 3, flags, x, y, z, minZ);
if (result != MONEY32_UNDEFINED) {
totalCost += result;
}
}
minZ -= 2;
if (y <= mapBottom) {
minZ += 2;
if (y < mapTop) {
minZ += 2;
}
}
if (y >= mapTop) {
z = clamp(minHeight, map_element_get_corner_height(mapElement, 0), maxHeight);
if (!fullTile)
z = map_get_corner_height(newBaseZ, newSlope, 0);
result = smooth_land_tile((y <= mapBottom) ? 3 : 2, flags, x, y, z, minZ);
if (result != MONEY32_UNDEFINED) {
totalCost += result;
}
}
y -= 32;
}
// Corner (North-East)
totalCost += smooth_land_row_by_corner(flags, mapRight, mapBottom, z, 32, 32, 2, 0, raiseLand);
mapElement = map_get_surface_element_at(mapRight >> 5, mapTop >> 5);
z = clamp(minHeight, map_element_get_corner_height(mapElement, 1), maxHeight);
if (!fullTile)
z = map_get_corner_height(newBaseZ, newSlope, 1);
result = smooth_land_tile(3, flags, x, y, z, minZ);
if (result != MONEY32_UNDEFINED) {
totalCost += result;
totalCost += smooth_land_row_by_corner(flags, mapRight, mapTop, z, 32, -32, 3, 1, raiseLand);
sint32 x = mapLeft;
sint32 y, z2;
for (y = mapTop; y <= mapBottom; y += 32)
{
mapElement = map_get_surface_element_at(x >> 5, y >> 5);
z = clamp(minHeight, map_element_get_corner_height(mapElement, 0), maxHeight);
z2 = clamp(minHeight, map_element_get_corner_height(mapElement, 1), maxHeight);
totalCost += smooth_land_row_by_edge(flags, x, y, z, z2, -32, 0, 0, 1, 3, 2, raiseLand);
}
x -= 32;
x = mapRight;
for (y = mapTop; y <= mapBottom; y += 32)
{
mapElement = map_get_surface_element_at(x >> 5, y >> 5);
z = clamp(minHeight, map_element_get_corner_height(mapElement, 2), maxHeight);
z2 = clamp(minHeight, map_element_get_corner_height(mapElement, 3), 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)
{
mapElement = map_get_surface_element_at(x >> 5, y >> 5);
z = clamp(minHeight, map_element_get_corner_height(mapElement, 0), maxHeight);
z2 = clamp(minHeight, map_element_get_corner_height(mapElement, 3), 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)
{
mapElement = map_get_surface_element_at(x >> 5, y >> 5);
z = clamp(minHeight, map_element_get_corner_height(mapElement, 1), maxHeight);
z2 = clamp(minHeight, map_element_get_corner_height(mapElement, 2), maxHeight);
totalCost += smooth_land_row_by_edge(flags, x, y, z, z2, 0, 32, 1, 2, 0, 3, raiseLand);
}
}
else // fullTile
{
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);
// Side (North)
for (sint32 i = 0; i < size; i++) {
sint32 x2 = clamp(mapLeft, x, mapRight);
mapElement = map_get_surface_element_at(x2 >> 5, mapTop >> 5);
if (x <= mapRight) {
z = clamp(minHeight, map_element_get_corner_height(mapElement, 2), maxHeight);
if (!fullTile)
z = map_get_corner_height(newBaseZ, newSlope, 2);
result = smooth_land_tile((x >= mapLeft) ? 3 : 0, flags, x, y, z, minZ);
if (result != MONEY32_UNDEFINED) {
totalCost += result;
}
}
minZ -= 2;
if (x <= mapRight) {
minZ += 2;
if (x < mapLeft) {
minZ += 2;
}
}
if (x >= mapLeft) {
z = clamp(minHeight, map_element_get_corner_height(mapElement, 1), maxHeight);
if (!fullTile)
z = map_get_corner_height(newBaseZ, newSlope, 1);
result = smooth_land_tile((x <= mapRight) ? 0 : 3, flags, x, y, z, minZ);
if (result != MONEY32_UNDEFINED) {
totalCost += result;
}
}
x -= 32;
switch (command & 0x7FFF) {
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);
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, 32, 1, 0, raiseLand);
z = map_get_corner_height(newBaseZ, newSlope, 1);
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, 0, 0, 1, raiseLand);
z = map_get_corner_height(newBaseZ, newSlope, 3);
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, -32, 0, 3, raiseLand);
break;
case MAP_SELECT_TYPE_CORNER_1:
z = map_get_corner_height(newBaseZ, newSlope, 1);
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, 0, 2, 1, raiseLand);
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, -32, 0, 1, raiseLand);
z = map_get_corner_height(newBaseZ, newSlope, 0);
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, 0, 1, 0, raiseLand);
z = map_get_corner_height(newBaseZ, newSlope, 2);
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, 32, 1, 2, raiseLand);
break;
case MAP_SELECT_TYPE_CORNER_2:
z = map_get_corner_height(newBaseZ, newSlope, 2);
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, 0, 1, 2, raiseLand);
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, -32, 3, 2, raiseLand);
z = map_get_corner_height(newBaseZ, newSlope, 3);
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, 0, 2, 3, raiseLand);
z = map_get_corner_height(newBaseZ, newSlope, 1);
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, 32, 2, 1, raiseLand);
break;
case MAP_SELECT_TYPE_CORNER_3:
z = map_get_corner_height(newBaseZ, newSlope, 3);
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, -32, 0, 0, 3, raiseLand);
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, 32, 2, 3, raiseLand);
z = map_get_corner_height(newBaseZ, newSlope, 2);
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 32, 0, 3, 2, raiseLand);
z = map_get_corner_height(newBaseZ, newSlope, 0);
totalCost += smooth_land_row_by_corner(flags, mapLeft, mapTop, z, 0, -32, 3, 0, raiseLand);
break;
}
}