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