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 "Entrance.h"
11 
12 #include "../Cheats.h"
13 #include "../Context.h"
14 #include "../Game.h"
15 #include "../OpenRCT2.h"
16 #include "../actions/ParkEntranceRemoveAction.h"
17 #include "../actions/RideEntranceExitPlaceAction.h"
18 #include "../actions/RideEntranceExitRemoveAction.h"
19 #include "../localisation/StringIds.h"
20 #include "../management/Finance.h"
21 #include "../network/network.h"
22 #include "../object/FootpathObject.h"
23 #include "../object/FootpathSurfaceObject.h"
24 #include "../object/ObjectManager.h"
25 #include "../ride/Station.h"
26 #include "../ride/Track.h"
27 #include "Footpath.h"
28 #include "Map.h"
29 #include "MapAnimation.h"
30 #include "Park.h"
31 
32 #include <algorithm>
33 
34 bool gParkEntranceGhostExists = false;
35 CoordsXYZD gParkEntranceGhostPosition = { 0, 0, 0, 0 };
36 std::vector<CoordsXYZD> gParkEntrances;
37 
38 CoordsXYZD gRideEntranceExitGhostPosition;
39 StationIndex gRideEntranceExitGhostStationIndex;
40 
RideEntranceExitPlaceGhost(ride_id_t rideIndex,const CoordsXY & entranceExitCoords,Direction direction,uint8_t placeType,StationIndex stationNum)41 static money32 RideEntranceExitPlaceGhost(
42     ride_id_t rideIndex, const CoordsXY& entranceExitCoords, Direction direction, uint8_t placeType, StationIndex stationNum)
43 {
44     auto rideEntranceExitPlaceAction = RideEntranceExitPlaceAction(
45         entranceExitCoords, direction, rideIndex, stationNum, placeType == ENTRANCE_TYPE_RIDE_EXIT);
46     rideEntranceExitPlaceAction.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_GHOST);
47     auto res = GameActions::Execute(&rideEntranceExitPlaceAction);
48 
49     return res->Error == GameActions::Status::Ok ? res->Cost : MONEY32_UNDEFINED;
50 }
51 
52 /**
53  *
54  *  rct2: 0x00666F9E
55  */
park_entrance_remove_ghost()56 void park_entrance_remove_ghost()
57 {
58     if (gParkEntranceGhostExists)
59     {
60         gParkEntranceGhostExists = false;
61         auto parkEntranceRemoveAction = ParkEntranceRemoveAction(gParkEntranceGhostPosition);
62         parkEntranceRemoveAction.SetFlags(GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED);
63         GameActions::Execute(&parkEntranceRemoveAction);
64     }
65 }
66 
park_entrance_get_index(const CoordsXYZ & entrancePos)67 int32_t park_entrance_get_index(const CoordsXYZ& entrancePos)
68 {
69     int32_t i = 0;
70     for (const auto& entrance : gParkEntrances)
71     {
72         if (entrancePos == entrance)
73         {
74             return i;
75         }
76         i++;
77     }
78     return -1;
79 }
80 
reset_park_entrance()81 void reset_park_entrance()
82 {
83     gParkEntrances.clear();
84 }
85 
ride_entrance_exit_place_provisional_ghost()86 void ride_entrance_exit_place_provisional_ghost()
87 {
88     if (_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_ENTRANCE_OR_EXIT)
89     {
90         RideEntranceExitPlaceGhost(
91             _currentRideIndex, gRideEntranceExitGhostPosition, gRideEntranceExitGhostPosition.direction,
92             gRideEntranceExitPlaceType, gRideEntranceExitGhostStationIndex);
93     }
94 }
95 
ride_entrance_exit_remove_ghost()96 void ride_entrance_exit_remove_ghost()
97 {
98     if (_currentTrackSelectionFlags & TRACK_SELECTION_FLAG_ENTRANCE_OR_EXIT)
99     {
100         auto rideEntranceExitRemove = RideEntranceExitRemoveAction(
101             gRideEntranceExitGhostPosition, _currentRideIndex, gRideEntranceExitGhostStationIndex,
102             gRideEntranceExitPlaceType == ENTRANCE_TYPE_RIDE_EXIT);
103 
104         rideEntranceExitRemove.SetFlags(GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED);
105         GameActions::Execute(&rideEntranceExitRemove);
106     }
107 }
108 
109 /**
110  *
111  *  rct2: 0x006CA28C
112  */
ride_entrance_exit_place_ghost(Ride * ride,const CoordsXY & entranceExitCoords,Direction direction,int32_t placeType,StationIndex stationNum)113 money32 ride_entrance_exit_place_ghost(
114     Ride* ride, const CoordsXY& entranceExitCoords, Direction direction, int32_t placeType, StationIndex stationNum)
115 {
116     ride_construction_remove_ghosts();
117     money32 result = RideEntranceExitPlaceGhost(ride->id, entranceExitCoords, direction, placeType, stationNum);
118 
119     if (result != MONEY32_UNDEFINED)
120     {
121         _currentTrackSelectionFlags |= TRACK_SELECTION_FLAG_ENTRANCE_OR_EXIT;
122         gRideEntranceExitGhostPosition.x = entranceExitCoords.x;
123         gRideEntranceExitGhostPosition.y = entranceExitCoords.y;
124         gRideEntranceExitGhostPosition.direction = direction;
125         gRideEntranceExitGhostStationIndex = stationNum;
126     }
127     return result;
128 }
129 
130 /**
131  * Replaces the outer hedge walls for an entrance placement removal.
132  *  rct2: 0x00666D6F
133  */
maze_entrance_hedge_replacement(const CoordsXYE & entrance)134 void maze_entrance_hedge_replacement(const CoordsXYE& entrance)
135 {
136     int32_t direction = entrance.element->GetDirection();
137     auto hedgePos = entrance + CoordsDirectionDelta[direction];
138     int32_t z = entrance.element->GetBaseZ();
139     ride_id_t rideIndex = entrance.element->AsEntrance()->GetRideIndex();
140 
141     auto tileElement = map_get_first_element_at(hedgePos);
142     if (tileElement == nullptr)
143         return;
144     do
145     {
146         if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK)
147             continue;
148         if (tileElement->AsTrack()->GetRideIndex() != rideIndex)
149             continue;
150         if (tileElement->GetBaseZ() != z)
151             continue;
152         if (tileElement->AsTrack()->GetTrackType() != TrackElemType::Maze)
153             continue;
154 
155         // Each maze element is split into 4 sections with 4 different walls
156         uint8_t mazeSection = direction * 4;
157         // Add the top outer wall
158         tileElement->AsTrack()->MazeEntryAdd(1 << ((mazeSection + 9) & 0x0F));
159         // Add the bottom outer wall
160         tileElement->AsTrack()->MazeEntryAdd(1 << ((mazeSection + 12) & 0x0F));
161 
162         map_invalidate_tile({ hedgePos, tileElement->GetBaseZ(), tileElement->GetClearanceZ() });
163         return;
164     } while (!(tileElement++)->IsLastForTile());
165 }
166 
167 /**
168  * Removes the hedge walls for an entrance placement.
169  *  rct2: 0x00666CBE
170  */
maze_entrance_hedge_removal(const CoordsXYE & entrance)171 void maze_entrance_hedge_removal(const CoordsXYE& entrance)
172 {
173     int32_t direction = entrance.element->GetDirection();
174     auto hedgePos = entrance + CoordsDirectionDelta[direction];
175     int32_t z = entrance.element->GetBaseZ();
176     ride_id_t rideIndex = entrance.element->AsEntrance()->GetRideIndex();
177 
178     auto tileElement = map_get_first_element_at(hedgePos);
179     if (tileElement == nullptr)
180         return;
181     do
182     {
183         if (tileElement->GetType() != TILE_ELEMENT_TYPE_TRACK)
184             continue;
185         if (tileElement->AsTrack()->GetRideIndex() != rideIndex)
186             continue;
187         if (tileElement->GetBaseZ() != z)
188             continue;
189         if (tileElement->AsTrack()->GetTrackType() != TrackElemType::Maze)
190             continue;
191 
192         // Each maze element is split into 4 sections with 4 different walls
193         uint8_t mazeSection = direction * 4;
194         // Remove the top outer wall
195         tileElement->AsTrack()->MazeEntrySubtract(1 << ((mazeSection + 9) & 0x0F));
196         // Remove the bottom outer wall
197         tileElement->AsTrack()->MazeEntrySubtract(1 << ((mazeSection + 12) & 0x0F));
198         // Remove the intersecting wall
199         tileElement->AsTrack()->MazeEntrySubtract(1 << ((mazeSection + 10) & 0x0F));
200         // Remove the top hedge section
201         tileElement->AsTrack()->MazeEntrySubtract(1 << ((mazeSection + 11) & 0x0F));
202         // Remove the bottom hedge section
203         tileElement->AsTrack()->MazeEntrySubtract(1 << ((mazeSection + 15) & 0x0F));
204 
205         map_invalidate_tile({ hedgePos, tileElement->GetBaseZ(), tileElement->GetClearanceZ() });
206         return;
207     } while (!(tileElement++)->IsLastForTile());
208 }
209 
fix_park_entrance_locations(void)210 void fix_park_entrance_locations(void)
211 {
212     // Fix gParkEntrance locations for which the tile_element no longer exists
213     gParkEntrances.erase(
214         std::remove_if(
215             gParkEntrances.begin(), gParkEntrances.end(),
216             [](const auto& entrance) { return map_get_park_entrance_element_at(entrance, false) == nullptr; }),
217         gParkEntrances.end());
218 }
219 
GetStationIndex() const220 uint8_t EntranceElement::GetStationIndex() const
221 {
222     return StationIndex;
223 }
224 
SetStationIndex(uint8_t newStationIndex)225 void EntranceElement::SetStationIndex(uint8_t newStationIndex)
226 {
227     StationIndex = newStationIndex;
228 }
229 
GetEntranceType() const230 uint8_t EntranceElement::GetEntranceType() const
231 {
232     return entranceType;
233 }
234 
SetEntranceType(uint8_t newType)235 void EntranceElement::SetEntranceType(uint8_t newType)
236 {
237     entranceType = newType;
238 }
239 
GetRideIndex() const240 ride_id_t EntranceElement::GetRideIndex() const
241 {
242     return rideIndex;
243 }
244 
SetRideIndex(ride_id_t newRideIndex)245 void EntranceElement::SetRideIndex(ride_id_t newRideIndex)
246 {
247     rideIndex = newRideIndex;
248 }
249 
GetSequenceIndex() const250 uint8_t EntranceElement::GetSequenceIndex() const
251 {
252     return SequenceIndex & 0xF;
253 }
254 
SetSequenceIndex(uint8_t newSequenceIndex)255 void EntranceElement::SetSequenceIndex(uint8_t newSequenceIndex)
256 {
257     SequenceIndex &= ~0xF;
258     SequenceIndex |= (newSequenceIndex & 0xF);
259 }
260 
HasLegacyPathEntry() const261 bool EntranceElement::HasLegacyPathEntry() const
262 {
263     return (flags2 & ENTRANCE_ELEMENT_FLAGS2_LEGACY_PATH_ENTRY) != 0;
264 }
265 
GetLegacyPathEntryIndex() const266 ObjectEntryIndex EntranceElement::GetLegacyPathEntryIndex() const
267 {
268     if (HasLegacyPathEntry())
269         return PathType;
270 
271     return OBJECT_ENTRY_INDEX_NULL;
272 }
273 
GetLegacyPathEntry() const274 const FootpathObject* EntranceElement::GetLegacyPathEntry() const
275 {
276     auto& objMgr = OpenRCT2::GetContext()->GetObjectManager();
277     return static_cast<FootpathObject*>(objMgr.GetLoadedObject(ObjectType::Paths, GetLegacyPathEntryIndex()));
278 }
279 
SetLegacyPathEntryIndex(ObjectEntryIndex newPathType)280 void EntranceElement::SetLegacyPathEntryIndex(ObjectEntryIndex newPathType)
281 {
282     PathType = newPathType;
283     flags2 |= ENTRANCE_ELEMENT_FLAGS2_LEGACY_PATH_ENTRY;
284 }
285 
GetSurfaceEntryIndex() const286 ObjectEntryIndex EntranceElement::GetSurfaceEntryIndex() const
287 {
288     if (HasLegacyPathEntry())
289         return OBJECT_ENTRY_INDEX_NULL;
290 
291     return PathType;
292 }
293 
GetSurfaceEntry() const294 const FootpathSurfaceObject* EntranceElement::GetSurfaceEntry() const
295 {
296     auto& objMgr = OpenRCT2::GetContext()->GetObjectManager();
297     return static_cast<FootpathSurfaceObject*>(objMgr.GetLoadedObject(ObjectType::FootpathSurface, GetSurfaceEntryIndex()));
298 }
299 
SetSurfaceEntryIndex(ObjectEntryIndex newIndex)300 void EntranceElement::SetSurfaceEntryIndex(ObjectEntryIndex newIndex)
301 {
302     PathType = newIndex;
303     flags2 &= ~ENTRANCE_ELEMENT_FLAGS2_LEGACY_PATH_ENTRY;
304 }
305 
GetPathSurfaceDescriptor() const306 const PathSurfaceDescriptor* EntranceElement::GetPathSurfaceDescriptor() const
307 {
308     if (HasLegacyPathEntry())
309     {
310         const auto* legacyPathEntry = GetLegacyPathEntry();
311         if (legacyPathEntry == nullptr)
312             return nullptr;
313 
314         return &legacyPathEntry->GetPathSurfaceDescriptor();
315     }
316 
317     const auto* surfaceEntry = GetSurfaceEntry();
318     if (surfaceEntry == nullptr)
319         return nullptr;
320 
321     return &surfaceEntry->GetDescriptor();
322 }
323