mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2026-01-25 15:54:31 +01:00
249 lines
6.7 KiB
C++
249 lines
6.7 KiB
C++
/*****************************************************************************
|
|
* Copyright (c) 2014-2018 OpenRCT2 developers
|
|
*
|
|
* For a complete list of all authors, please refer to contributors.md
|
|
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
|
*
|
|
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
|
*****************************************************************************/
|
|
|
|
#include "Surface.h"
|
|
|
|
#include "../Context.h"
|
|
#include "../object/ObjectManager.h"
|
|
#include "../object/TerrainSurfaceObject.h"
|
|
#include "../scenario/Scenario.h"
|
|
#include "Location.hpp"
|
|
#include "Map.h"
|
|
|
|
uint32_t SurfaceElement::GetSurfaceStyle() const
|
|
{
|
|
uint32_t retVal = (terrain >> 5) & 7;
|
|
if (type & 1)
|
|
retVal |= (1 << 3);
|
|
return retVal;
|
|
}
|
|
|
|
uint32_t SurfaceElement::GetEdgeStyle() const
|
|
{
|
|
uint32_t terrain_edge = (slope >> 5) & 7;
|
|
if (type & 128)
|
|
terrain_edge |= (1 << 3);
|
|
return terrain_edge;
|
|
}
|
|
|
|
void SurfaceElement::SetSurfaceStyle(uint32_t newStyle)
|
|
{
|
|
// Bit 3 for terrain is stored in element.type bit 0
|
|
if (newStyle & 8)
|
|
type |= 1;
|
|
else
|
|
type &= ~1;
|
|
|
|
// Bits 0, 1, 2 for terrain are stored in element.terrain bit 5, 6, 7
|
|
terrain &= ~0xE0;
|
|
terrain |= (newStyle & 7) << 5;
|
|
}
|
|
|
|
void SurfaceElement::SetEdgeStyle(uint32_t newStyle)
|
|
{
|
|
// Bit 3 for terrain is stored in element.type bit 7
|
|
if (newStyle & 8)
|
|
type |= 128;
|
|
else
|
|
type &= ~128;
|
|
|
|
// Bits 0, 1, 2 for terrain are stored in element.slope bit 5, 6, 7
|
|
slope &= ~TILE_ELEMENT_SURFACE_EDGE_STYLE_MASK;
|
|
slope |= (newStyle & 7) << 5;
|
|
}
|
|
|
|
uint32_t SurfaceElement::GetWaterHeight() const
|
|
{
|
|
return terrain & TILE_ELEMENT_SURFACE_WATER_HEIGHT_MASK;
|
|
}
|
|
|
|
void SurfaceElement::SetWaterHeight(uint32_t newWaterHeight)
|
|
{
|
|
newWaterHeight &= 0x1F;
|
|
terrain &= ~TILE_ELEMENT_SURFACE_WATER_HEIGHT_MASK;
|
|
terrain |= newWaterHeight;
|
|
}
|
|
|
|
bool SurfaceElement::CanGrassGrow() const
|
|
{
|
|
auto surfaceStyle = GetSurfaceStyle();
|
|
auto& objMgr = OpenRCT2::GetContext()->GetObjectManager();
|
|
auto obj = objMgr.GetLoadedObject(OBJECT_TYPE_TERRAIN_SURFACE, surfaceStyle);
|
|
if (obj != nullptr)
|
|
{
|
|
auto surfaceObject = static_cast<TerrainSurfaceObject*>(obj);
|
|
if (surfaceObject->Flags & TERRAIN_SURFACE_FLAGS::CAN_GROW)
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint8_t SurfaceElement::GetGrassLength() const
|
|
{
|
|
return grass_length;
|
|
}
|
|
|
|
void SurfaceElement::SetGrassLength(uint8_t newLength)
|
|
{
|
|
grass_length = newLength;
|
|
}
|
|
|
|
void SurfaceElement::SetGrassLengthAndInvalidate(uint8_t length, CoordsXY coords)
|
|
{
|
|
uint8_t oldLength = grass_length & 0x7;
|
|
uint8_t newLength = length & 0x7;
|
|
|
|
grass_length = length;
|
|
|
|
if (newLength == oldLength)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// If the new grass length won't result in an actual visual change
|
|
// then skip invalidating the tile, no point
|
|
if (((oldLength > 0 && oldLength < 4) && (newLength > 0 && newLength < 4))
|
|
|| ((oldLength > 3 && oldLength < 7) && (newLength > 3 && newLength < 7)))
|
|
{
|
|
return;
|
|
}
|
|
|
|
int32_t z = base_height * 8;
|
|
map_invalidate_tile(coords.x, coords.y, z, z + 16);
|
|
}
|
|
|
|
/**
|
|
*
|
|
* rct2: 0x006647A1
|
|
*/
|
|
void SurfaceElement::UpdateGrassLength(CoordsXY coords)
|
|
{
|
|
// Check if tile is grass
|
|
if (!CanGrassGrow())
|
|
return;
|
|
|
|
uint8_t grassLengthTmp = grass_length & 7;
|
|
|
|
// Check if grass is underwater or outside park
|
|
uint32_t waterHeight = GetWaterHeight() * 2;
|
|
if (waterHeight > base_height || !map_is_location_in_park(coords))
|
|
{
|
|
if (grassLengthTmp != GRASS_LENGTH_CLEAR_0)
|
|
SetGrassLengthAndInvalidate(GRASS_LENGTH_CLEAR_0, coords);
|
|
|
|
return;
|
|
}
|
|
|
|
// Grass can't grow any further than CLUMPS_2 but this code also cuts grass
|
|
// if there is an object placed on top of it.
|
|
|
|
int32_t z0 = base_height;
|
|
int32_t z1 = base_height + 2;
|
|
if (slope & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT)
|
|
z1 += 2;
|
|
|
|
// Check objects above grass
|
|
TileElement* tileElementAbove = (TileElement*)this;
|
|
for (;;)
|
|
{
|
|
if (tileElementAbove->flags & TILE_ELEMENT_FLAG_LAST_TILE)
|
|
{
|
|
// Grow grass
|
|
|
|
// Check interim grass lengths
|
|
uint8_t lengthNibble = (GetGrassLength() & 0xF0) >> 4;
|
|
if (lengthNibble < 0xF)
|
|
{
|
|
grass_length += 0x10;
|
|
}
|
|
else
|
|
{
|
|
// Zeros the length nibble
|
|
grass_length += 0x10;
|
|
grass_length ^= 8;
|
|
if (grass_length & 8)
|
|
{
|
|
// Random growth rate (length nibble)
|
|
grass_length |= scenario_rand() & 0x70;
|
|
}
|
|
else
|
|
{
|
|
// Increase length if not at max length
|
|
if (grassLengthTmp != GRASS_LENGTH_CLUMPS_2)
|
|
SetGrassLengthAndInvalidate(grassLengthTmp + 1, coords);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
tileElementAbove++;
|
|
if (tileElementAbove->GetType() == TILE_ELEMENT_TYPE_WALL)
|
|
continue;
|
|
// Grass should not be affected by ghost elements.
|
|
if (tileElementAbove->IsGhost())
|
|
continue;
|
|
if (z0 >= tileElementAbove->clearance_height)
|
|
continue;
|
|
if (z1 < tileElementAbove->base_height)
|
|
continue;
|
|
|
|
if (grassLengthTmp != GRASS_LENGTH_CLEAR_0)
|
|
SetGrassLengthAndInvalidate(GRASS_LENGTH_CLEAR_0, coords);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
uint8_t SurfaceElement::GetOwnership() const
|
|
{
|
|
return (ownership & TILE_ELEMENT_SURFACE_OWNERSHIP_MASK);
|
|
}
|
|
|
|
void SurfaceElement::SetOwnership(uint8_t newOwnership)
|
|
{
|
|
ownership &= ~TILE_ELEMENT_SURFACE_OWNERSHIP_MASK;
|
|
ownership |= (newOwnership & TILE_ELEMENT_SURFACE_OWNERSHIP_MASK);
|
|
}
|
|
|
|
uint8_t SurfaceElement::GetParkFences() const
|
|
{
|
|
return (ownership & TILE_ELEMENT_SURFACE_PARK_FENCE_MASK);
|
|
}
|
|
|
|
void SurfaceElement::SetParkFences(uint8_t newParkFences)
|
|
{
|
|
ownership &= ~TILE_ELEMENT_SURFACE_PARK_FENCE_MASK;
|
|
ownership |= (newParkFences & TILE_ELEMENT_SURFACE_PARK_FENCE_MASK);
|
|
}
|
|
|
|
uint8_t SurfaceElement::GetSlope() const
|
|
{
|
|
return (slope & TILE_ELEMENT_SURFACE_SLOPE_MASK);
|
|
}
|
|
|
|
void SurfaceElement::SetSlope(uint8_t newSlope)
|
|
{
|
|
slope &= ~TILE_ELEMENT_SURFACE_SLOPE_MASK;
|
|
slope |= (newSlope & TILE_ELEMENT_SURFACE_SLOPE_MASK);
|
|
}
|
|
|
|
bool SurfaceElement::HasTrackThatNeedsWater() const
|
|
{
|
|
return (type & SURFACE_ELEMENT_HAS_TRACK_THAT_NEEDS_WATER) != 0;
|
|
}
|
|
|
|
void SurfaceElement::SetHasTrackThatNeedsWater(bool on)
|
|
{
|
|
type &= ~SURFACE_ELEMENT_HAS_TRACK_THAT_NEEDS_WATER;
|
|
if (on)
|
|
type |= SURFACE_ELEMENT_HAS_TRACK_THAT_NEEDS_WATER;
|
|
}
|