1 /*****************************************************************************
2 * Copyright (c) 2014-2020 OpenRCT2 developers
3 *
4 * For a complete list of all authors, please refer to contributors.md
5 * Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
6 *
7 * OpenRCT2 is licensed under the GNU General Public License version 3.
8 *****************************************************************************/
9
10 #include "Surface.h"
11
12 #include "../Context.h"
13 #include "../object/ObjectManager.h"
14 #include "../object/TerrainEdgeObject.h"
15 #include "../object/TerrainSurfaceObject.h"
16 #include "../scenario/Scenario.h"
17 #include "Location.hpp"
18 #include "Map.h"
19
GetSurfaceStyle() const20 uint32_t SurfaceElement::GetSurfaceStyle() const
21 {
22 return SurfaceStyle;
23 }
24
GetSurfaceStyleObject() const25 TerrainSurfaceObject* SurfaceElement::GetSurfaceStyleObject() const
26 {
27 auto& objManager = OpenRCT2::GetContext()->GetObjectManager();
28 return static_cast<TerrainSurfaceObject*>(objManager.GetLoadedObject(ObjectType::TerrainSurface, GetSurfaceStyle()));
29 }
30
GetEdgeStyle() const31 uint32_t SurfaceElement::GetEdgeStyle() const
32 {
33 return EdgeStyle;
34 }
35
GetEdgeStyleObject() const36 TerrainEdgeObject* SurfaceElement::GetEdgeStyleObject() const
37 {
38 auto& objManager = OpenRCT2::GetContext()->GetObjectManager();
39 return static_cast<TerrainEdgeObject*>(objManager.GetLoadedObject(ObjectType::TerrainEdge, GetEdgeStyle()));
40 }
41
SetSurfaceStyle(uint32_t newStyle)42 void SurfaceElement::SetSurfaceStyle(uint32_t newStyle)
43 {
44 SurfaceStyle = newStyle;
45 }
46
SetEdgeStyle(uint32_t newStyle)47 void SurfaceElement::SetEdgeStyle(uint32_t newStyle)
48 {
49 EdgeStyle = newStyle;
50 }
51
GetWaterHeight() const52 int32_t SurfaceElement::GetWaterHeight() const
53 {
54 return WaterHeight * 16;
55 }
56
SetWaterHeight(int32_t newWaterHeight)57 void SurfaceElement::SetWaterHeight(int32_t newWaterHeight)
58 {
59 WaterHeight = newWaterHeight / 16;
60 }
61
CanGrassGrow() const62 bool SurfaceElement::CanGrassGrow() const
63 {
64 auto surfaceStyle = GetSurfaceStyle();
65 auto& objMgr = OpenRCT2::GetContext()->GetObjectManager();
66 auto obj = objMgr.GetLoadedObject(ObjectType::TerrainSurface, surfaceStyle);
67 if (obj != nullptr)
68 {
69 auto surfaceObject = static_cast<TerrainSurfaceObject*>(obj);
70 if (surfaceObject->Flags & TERRAIN_SURFACE_FLAGS::CAN_GROW)
71 {
72 return true;
73 }
74 }
75 return false;
76 }
77
GetGrassLength() const78 uint8_t SurfaceElement::GetGrassLength() const
79 {
80 return GrassLength;
81 }
82
SetGrassLength(uint8_t newLength)83 void SurfaceElement::SetGrassLength(uint8_t newLength)
84 {
85 GrassLength = newLength;
86 }
87
SetGrassLengthAndInvalidate(uint8_t length,const CoordsXY & coords)88 void SurfaceElement::SetGrassLengthAndInvalidate(uint8_t length, const CoordsXY& coords)
89 {
90 uint8_t oldLength = GrassLength & 0x7;
91 uint8_t newLength = length & 0x7;
92
93 GrassLength = length;
94
95 if (newLength == oldLength)
96 {
97 return;
98 }
99
100 // If the new grass length won't result in an actual visual change
101 // then skip invalidating the tile, no point
102 if (((oldLength > 0 && oldLength < 4) && (newLength > 0 && newLength < 4))
103 || ((oldLength > 3 && oldLength < 7) && (newLength > 3 && newLength < 7)))
104 {
105 return;
106 }
107
108 int32_t z = GetBaseZ();
109 map_invalidate_tile({ coords, z, z + 16 });
110 }
111
112 /**
113 *
114 * rct2: 0x006647A1
115 */
UpdateGrassLength(const CoordsXY & coords)116 void SurfaceElement::UpdateGrassLength(const CoordsXY& coords)
117 {
118 // Check if tile is grass
119 if (!CanGrassGrow())
120 return;
121
122 uint8_t grassLengthTmp = GrassLength & 7;
123
124 // Check if grass is underwater or outside park
125 if (GetWaterHeight() > GetBaseZ() || !map_is_location_in_park(coords))
126 {
127 if (grassLengthTmp != GRASS_LENGTH_CLEAR_0)
128 SetGrassLengthAndInvalidate(GRASS_LENGTH_CLEAR_0, coords);
129
130 return;
131 }
132
133 // Grass can't grow any further than CLUMPS_2 but this code also cuts grass
134 // if there is an object placed on top of it.
135
136 int32_t baseZ = GetBaseZ();
137 int32_t clearZ = GetBaseZ() + LAND_HEIGHT_STEP;
138 if (Slope & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT)
139 clearZ += LAND_HEIGHT_STEP;
140
141 // Check objects above grass
142 TileElement* tileElementAbove = reinterpret_cast<TileElement*>(this);
143 for (;;)
144 {
145 if (tileElementAbove->IsLastForTile())
146 {
147 // Grow grass
148
149 // Check interim grass lengths
150 uint8_t lengthNibble = (GetGrassLength() & 0xF0) >> 4;
151 if (lengthNibble < 0xF)
152 {
153 GrassLength += 0x10;
154 }
155 else
156 {
157 // Zeros the length nibble
158 GrassLength += 0x10;
159 GrassLength ^= 8;
160 if (GrassLength & 8)
161 {
162 // Random growth rate (length nibble)
163 GrassLength |= scenario_rand() & 0x70;
164 }
165 else
166 {
167 // Increase length if not at max length
168 if (grassLengthTmp != GRASS_LENGTH_CLUMPS_2)
169 SetGrassLengthAndInvalidate(grassLengthTmp + 1, coords);
170 }
171 }
172 }
173 else
174 {
175 tileElementAbove++;
176 if (tileElementAbove->GetType() == TILE_ELEMENT_TYPE_WALL)
177 continue;
178 // Grass should not be affected by ghost elements.
179 if (tileElementAbove->IsGhost())
180 continue;
181 if (baseZ >= tileElementAbove->GetClearanceZ())
182 continue;
183 if (clearZ < tileElementAbove->GetBaseZ())
184 continue;
185
186 if (grassLengthTmp != GRASS_LENGTH_CLEAR_0)
187 SetGrassLengthAndInvalidate(GRASS_LENGTH_CLEAR_0, coords);
188 }
189 break;
190 }
191 }
192
GetOwnership() const193 uint8_t SurfaceElement::GetOwnership() const
194 {
195 return (Ownership & TILE_ELEMENT_SURFACE_OWNERSHIP_MASK);
196 }
197
SetOwnership(uint8_t newOwnership)198 void SurfaceElement::SetOwnership(uint8_t newOwnership)
199 {
200 Ownership &= ~TILE_ELEMENT_SURFACE_OWNERSHIP_MASK;
201 Ownership |= (newOwnership & TILE_ELEMENT_SURFACE_OWNERSHIP_MASK);
202 }
203
GetParkFences() const204 uint8_t SurfaceElement::GetParkFences() const
205 {
206 return (Ownership & TILE_ELEMENT_SURFACE_PARK_FENCE_MASK);
207 }
208
SetParkFences(uint8_t newParkFences)209 void SurfaceElement::SetParkFences(uint8_t newParkFences)
210 {
211 Ownership &= ~TILE_ELEMENT_SURFACE_PARK_FENCE_MASK;
212 Ownership |= (newParkFences & TILE_ELEMENT_SURFACE_PARK_FENCE_MASK);
213 }
214
GetSlope() const215 uint8_t SurfaceElement::GetSlope() const
216 {
217 return Slope;
218 }
219
SetSlope(uint8_t newSlope)220 void SurfaceElement::SetSlope(uint8_t newSlope)
221 {
222 Slope = newSlope;
223 }
224
HasTrackThatNeedsWater() const225 bool SurfaceElement::HasTrackThatNeedsWater() const
226 {
227 return (type & SURFACE_ELEMENT_HAS_TRACK_THAT_NEEDS_WATER) != 0;
228 }
229
SetHasTrackThatNeedsWater(bool on)230 void SurfaceElement::SetHasTrackThatNeedsWater(bool on)
231 {
232 type &= ~SURFACE_ELEMENT_HAS_TRACK_THAT_NEEDS_WATER;
233 if (on)
234 type |= SURFACE_ELEMENT_HAS_TRACK_THAT_NEEDS_WATER;
235 }
236