OpenRCT2/src/world/map_helpers.c

322 lines
12 KiB
C

#pragma region Copyright (c) 2014-2016 OpenRCT2 Developers
/*****************************************************************************
* OpenRCT2, an open source clone of Roller Coaster Tycoon 2.
*
* OpenRCT2 is the work of many authors, a full list can be found in contributors.md
* For more information, visit https://github.com/OpenRCT2/OpenRCT2
*
* OpenRCT2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* A full copy of the GNU General Public License can be found in licence.txt
*****************************************************************************/
#pragma endregion
#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;
rct_map_element *mapElement, *mapElement2;
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] = map_get_surface_element_at(x - 1, y - 1)->base_height;
cornerHeights[1] = map_get_surface_element_at(x + 1, y - 1)->base_height;
cornerHeights[2] = map_get_surface_element_at(x + 1, y + 1)->base_height;
cornerHeights[3] = map_get_surface_element_at(x - 1, y + 1)->base_height;
highest = mapElement->base_height;
for (i = 0; i < 4; i++)
highest = max(highest, cornerHeights[i]);
if (highest >= mapElement->base_height + 4) {
count = 0;
int canCompensate = 1;
for (i = 0; i < 4; i++)
if (cornerHeights[i] == highest){
count++;
// Check if surrounding corners aren't too high. The current tile
// can't compensate for all the height differences anymore if it has
// the extra height slope.
int highestOnLowestSide;
switch (i){
case 0:
highestOnLowestSide = max(
map_get_surface_element_at(x + 1, y)->base_height,
map_get_surface_element_at(x, y + 1)->base_height);
break;
case 1:
highestOnLowestSide = max(
map_get_surface_element_at(x - 1, y)->base_height,
map_get_surface_element_at(x, y + 1)->base_height);
break;
case 2:
highestOnLowestSide = max(
map_get_surface_element_at(x - 1, y)->base_height,
map_get_surface_element_at(x, y - 1)->base_height);
break;
case 3:
highestOnLowestSide = max(
map_get_surface_element_at(x + 1, y)->base_height,
map_get_surface_element_at(x, y - 1)->base_height);
break;
}
if (highestOnLowestSide > mapElement->base_height){
mapElement->base_height = mapElement->clearance_height = highestOnLowestSide;
raisedLand = 1;
canCompensate = 0;
}
}
if (count == 1 && canCompensate) {
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
mapElement2 = map_get_surface_element_at(x + 1, y + 1);
if (mapElement2->base_height > mapElement->base_height)
mapElement->properties.surface.slope |= 1;
mapElement2 = map_get_surface_element_at(x - 1, y + 1);
if (mapElement2->base_height > mapElement->base_height)
mapElement->properties.surface.slope |= 8;
mapElement2 = map_get_surface_element_at(x + 1, y - 1);
if (mapElement2->base_height > mapElement->base_height)
mapElement->properties.surface.slope |= 2;
mapElement2 = map_get_surface_element_at(x - 1, y - 1);
if (mapElement2->base_height > mapElement->base_height)
mapElement->properties.surface.slope |= 4;
// Sides
mapElement2 = map_get_surface_element_at(x + 1, y + 0);
if (mapElement2->base_height > mapElement->base_height)
mapElement->properties.surface.slope |= 1 | 2;
mapElement2 = map_get_surface_element_at(x - 1, y + 0);
if (mapElement2->base_height > mapElement->base_height)
mapElement->properties.surface.slope |= 4 | 8;
mapElement2 = map_get_surface_element_at(x + 0, y - 1);
if (mapElement2->base_height > mapElement->base_height)
mapElement->properties.surface.slope |= 2 | 4;
mapElement2 = map_get_surface_element_at(x + 0, y + 1);
if (mapElement2->base_height > mapElement->base_height)
mapElement->properties.surface.slope |= 1 | 8;
// Raise
if (mapElement->properties.surface.slope == (1 | 2 | 4 | 8)) {
mapElement->properties.surface.slope &= ~0x1F;
mapElement->base_height = mapElement->clearance_height += 2;
}
}
}
}
return raisedLand;
}
static 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.
*/
static 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;
}