diff --git a/src/openrct2-ui/title/TitleSequencePlayer.cpp b/src/openrct2-ui/title/TitleSequencePlayer.cpp index 0f767f5f6c..5794b4ac19 100644 --- a/src/openrct2-ui/title/TitleSequencePlayer.cpp +++ b/src/openrct2-ui/title/TitleSequencePlayer.cpp @@ -328,7 +328,7 @@ namespace OpenRCT2::Title parkImporter->Import(gameState); ReportProgress(100); - MapAnimationAutoCreate(); + MapAnimation::CreateAll(); } PrepareParkForPlayback(); _initialLoadCommand = false; @@ -384,7 +384,7 @@ namespace OpenRCT2::Title parkImporter->Import(gameState); ReportProgress(100); - MapAnimationAutoCreate(); + MapAnimation::CreateAll(); } PrepareParkForPlayback(); _initialLoadCommand = false; diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp index 7aba927208..628d84d024 100644 --- a/src/openrct2/Context.cpp +++ b/src/openrct2/Context.cpp @@ -832,7 +832,7 @@ namespace OpenRCT2 gCurrentLoadedPath = path; gFirstTimeSaving = true; GameFixSaveVars(); - MapAnimationAutoCreate(); + MapAnimation::CreateAll(); EntityTweener::Get().Reset(); gScreenAge = 0; gLastAutoSaveUpdate = kAutosavePause; diff --git a/src/openrct2/GameState.cpp b/src/openrct2/GameState.cpp index 9b8c4feac0..b3632dc951 100644 --- a/src/openrct2/GameState.cpp +++ b/src/openrct2/GameState.cpp @@ -321,7 +321,7 @@ namespace OpenRCT2 RideMeasurementsUpdate(); News::UpdateCurrentItem(); - MapAnimationInvalidateAll(); + MapAnimation::UpdateAll(); VehicleSoundsUpdate(); PeepUpdateCrowdNoise(); ClimateUpdateSound(); diff --git a/src/openrct2/actions/BannerPlaceAction.cpp b/src/openrct2/actions/BannerPlaceAction.cpp index 6d6209889d..474df4e56d 100644 --- a/src/openrct2/actions/BannerPlaceAction.cpp +++ b/src/openrct2/actions/BannerPlaceAction.cpp @@ -161,7 +161,7 @@ GameActions::Result BannerPlaceAction::Execute() const bannerElement->SetGhost(GetFlags() & GAME_COMMAND_FLAG_GHOST); MapInvalidateTileFull(_loc); - MapAnimationCreate(MAP_ANIMATION_TYPE_BANNER, CoordsXYZ{ _loc, bannerElement->GetBaseZ() }); + MapAnimation::Create(_loc); res.Cost = bannerEntry->price; return res; diff --git a/src/openrct2/actions/LargeSceneryPlaceAction.cpp b/src/openrct2/actions/LargeSceneryPlaceAction.cpp index 04c41677c0..a86ae0f13d 100644 --- a/src/openrct2/actions/LargeSceneryPlaceAction.cpp +++ b/src/openrct2/actions/LargeSceneryPlaceAction.cpp @@ -314,7 +314,7 @@ GameActions::Result LargeSceneryPlaceAction::Execute() const newSceneryElement->SetBannerIndex(banner->id); } - MapAnimationCreate(MAP_ANIMATION_TYPE_LARGE_SCENERY, { curTile, zLow }); + MapAnimation::Create(curTile); MapInvalidateTileFull(curTile); if (tile.index == 0) diff --git a/src/openrct2/actions/ParkEntrancePlaceAction.cpp b/src/openrct2/actions/ParkEntrancePlaceAction.cpp index 090c5c7c49..88e16f4832 100644 --- a/src/openrct2/actions/ParkEntrancePlaceAction.cpp +++ b/src/openrct2/actions/ParkEntrancePlaceAction.cpp @@ -186,7 +186,7 @@ GameActions::Result ParkEntrancePlaceAction::Execute() const if (index == 0) { - MapAnimationCreate(MAP_ANIMATION_TYPE_PARK_ENTRANCE, { entranceLoc, zLow }); + MapAnimation::Create(entranceLoc); } } diff --git a/src/openrct2/actions/RideEntranceExitPlaceAction.cpp b/src/openrct2/actions/RideEntranceExitPlaceAction.cpp index 9d721e27fb..313eb972c2 100644 --- a/src/openrct2/actions/RideEntranceExitPlaceAction.cpp +++ b/src/openrct2/actions/RideEntranceExitPlaceAction.cpp @@ -213,7 +213,7 @@ GameActions::Result RideEntranceExitPlaceAction::Execute() const station.LastPeepInQueue = EntityId::GetNull(); station.QueueLength = 0; - MapAnimationCreate(MAP_ANIMATION_TYPE_RIDE_ENTRANCE, { _loc, z }); + MapAnimation::Create(_loc); } FootpathQueueChainReset(); diff --git a/src/openrct2/actions/SmallSceneryPlaceAction.cpp b/src/openrct2/actions/SmallSceneryPlaceAction.cpp index 318e3b0f69..c77f5c5383 100644 --- a/src/openrct2/actions/SmallSceneryPlaceAction.cpp +++ b/src/openrct2/actions/SmallSceneryPlaceAction.cpp @@ -452,7 +452,7 @@ GameActions::Result SmallSceneryPlaceAction::Execute() const MapInvalidateTileFull(_loc); if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ANIMATED)) { - MapAnimationCreate(MAP_ANIMATION_TYPE_SMALL_SCENERY, CoordsXYZ{ _loc, sceneryElement->GetBaseZ() }); + MapAnimation::Create(_loc); } return res; diff --git a/src/openrct2/actions/TrackPlaceAction.cpp b/src/openrct2/actions/TrackPlaceAction.cpp index 35b591dc51..32d74727f5 100644 --- a/src/openrct2/actions/TrackPlaceAction.cpp +++ b/src/openrct2/actions/TrackPlaceAction.cpp @@ -594,18 +594,16 @@ GameActions::Result TrackPlaceAction::Execute() const switch (_trackType) { case TrackElemType::Waterfall: - MapAnimationCreate(MAP_ANIMATION_TYPE_TRACK_WATERFALL, CoordsXYZ{ mapLoc, trackElement->GetBaseZ() }); - break; + [[fallthrough]]; case TrackElemType::Rapids: - MapAnimationCreate(MAP_ANIMATION_TYPE_TRACK_RAPIDS, CoordsXYZ{ mapLoc, trackElement->GetBaseZ() }); - break; + [[fallthrough]]; case TrackElemType::Whirlpool: - MapAnimationCreate(MAP_ANIMATION_TYPE_TRACK_WHIRLPOOL, CoordsXYZ{ mapLoc, trackElement->GetBaseZ() }); - break; + [[fallthrough]]; case TrackElemType::SpinningTunnel: - MapAnimationCreate(MAP_ANIMATION_TYPE_TRACK_SPINNINGTUNNEL, CoordsXYZ{ mapLoc, trackElement->GetBaseZ() }); + MapAnimation::Create(mapLoc); break; case TrackElemType::Brakes: + [[fallthrough]]; case TrackElemType::DiagBrakes: trackElement->SetBrakeClosed(true); break; diff --git a/src/openrct2/actions/WallPlaceAction.cpp b/src/openrct2/actions/WallPlaceAction.cpp index b9dc32b2c7..bd21bf8ad6 100644 --- a/src/openrct2/actions/WallPlaceAction.cpp +++ b/src/openrct2/actions/WallPlaceAction.cpp @@ -399,7 +399,7 @@ GameActions::Result WallPlaceAction::Execute() const wallElement->SetGhost(GetFlags() & GAME_COMMAND_FLAG_GHOST); - MapAnimationCreate(MAP_ANIMATION_TYPE_WALL, targetLoc); + MapAnimation::Create(targetLoc); MapInvalidateTileZoom1({ _loc, wallElement->GetBaseZ(), wallElement->GetBaseZ() + 72 }); res.Cost = wallEntry->price; diff --git a/src/openrct2/network/NetworkBase.cpp b/src/openrct2/network/NetworkBase.cpp index 620d52e66c..2bfa1122d5 100644 --- a/src/openrct2/network/NetworkBase.cpp +++ b/src/openrct2/network/NetworkBase.cpp @@ -2853,7 +2853,7 @@ bool NetworkBase::LoadMap(IStream* stream) importer->Import(gameState); EntityTweener::Get().Reset(); - MapAnimationAutoCreate(); + MapAnimation::CreateAll(); gLastAutoSaveUpdate = kAutosavePause; result = true; diff --git a/src/openrct2/ride/Vehicle.cpp b/src/openrct2/ride/Vehicle.cpp index eee90d79eb..faf7c6e78b 100644 --- a/src/openrct2/ride/Vehicle.cpp +++ b/src/openrct2/ride/Vehicle.cpp @@ -6313,8 +6313,9 @@ static void AnimateSceneryDoor(const CoordsXYZD& doorLocation, const CoordsXYZ& { door->SetAnimationIsBackwards(isBackwards); door->SetAnimationFrame(1); - MapAnimationCreate(MAP_ANIMATION_TYPE_WALL_DOOR, doorLocation); play_scenery_door_open_sound(trackLocation, door); + + MapAnimation::Create(doorLocation); } if (isLastVehicle) @@ -6322,6 +6323,8 @@ static void AnimateSceneryDoor(const CoordsXYZD& doorLocation, const CoordsXYZ& door->SetAnimationIsBackwards(isBackwards); door->SetAnimationFrame(6); play_scenery_door_close_sound(trackLocation, door); + + MapAnimation::Create(doorLocation); } } @@ -6409,17 +6412,6 @@ void Vehicle::UpdateGoKartAttemptSwitchLanes() } } -/** - * - * rct2: 0x006DB545 - */ -static void trigger_on_ride_photo(const CoordsXYZ& loc, TileElement* tileElement) -{ - tileElement->AsTrack()->SetPhotoTimeout(); - - MapAnimationCreate(MAP_ANIMATION_TYPE_TRACK_ONRIDEPHOTO, { loc, tileElement->GetBaseZ() }); -} - /** * * rct2: 0x006DEDE8 @@ -7105,7 +7097,8 @@ bool Vehicle::UpdateTrackMotionForwardsGetNewTrack( } if (trackType == TrackElemType::OnRidePhoto) { - trigger_on_ride_photo(TrackLocation, tileElement); + tileElement->AsTrack()->SetPhotoTimeout(); + MapAnimation::Create(TrackLocation); } if (trackType == TrackElemType::RotationControlToggle) { diff --git a/src/openrct2/world/Footpath.cpp b/src/openrct2/world/Footpath.cpp index 764d03df87..1df3026509 100644 --- a/src/openrct2/world/Footpath.cpp +++ b/src/openrct2/world/Footpath.cpp @@ -914,7 +914,7 @@ void FootpathChainRideQueue( lastPathElement->AsPath()->SetHasQueueBanner(true); lastPathElement->AsPath()->SetQueueBannerDirection(lastPathDirection); // set the ride sign direction - MapAnimationCreate(MAP_ANIMATION_TYPE_QUEUE_BANNER, { lastPath, lastPathElement->GetBaseZ() }); + MapAnimation::Create(lastPath); } } } diff --git a/src/openrct2/world/Map.cpp b/src/openrct2/world/Map.cpp index b09a2f1687..a3cd99601c 100644 --- a/src/openrct2/world/Map.cpp +++ b/src/openrct2/world/Map.cpp @@ -472,7 +472,7 @@ void MapInit(const TileCoordsXY& size) gameState.widePathTileLoopPosition = {}; gameState.mapSize = size; MapRemoveOutOfRangeElements(); - ClearMapAnimations(); + MapAnimation::ClearAll(); auto intent = Intent(INTENT_ACTION_MAP); ContextBroadcastIntent(&intent); @@ -2407,5 +2407,5 @@ void ShiftMap(const TileCoordsXY& amount) id = BannerIndex::FromUnderlying(id.ToUnderlying() + 1); } - ShiftAllMapAnimations(amountToMove); + MapAnimation::ShiftAll(amountToMove); } diff --git a/src/openrct2/world/MapAnimation.cpp b/src/openrct2/world/MapAnimation.cpp index 828f7abfc6..5951dc8f5c 100644 --- a/src/openrct2/world/MapAnimation.cpp +++ b/src/openrct2/world/MapAnimation.cpp @@ -35,185 +35,88 @@ #include "tile_element/TrackElement.h" #include "tile_element/WallElement.h" +#include +#include + using namespace OpenRCT2; -using map_animation_invalidate_event_handler = bool (*)(const CoordsXYZ& loc); - -static std::vector _mapAnimations; - -constexpr size_t kMaxAnimatedObjects = 2000; - -static bool InvalidateMapAnimation(const MapAnimation& obj); - -static bool DoesAnimationExist(int32_t type, const CoordsXYZ& location) +struct TileCoordsXYCmp { - for (const auto& a : _mapAnimations) + constexpr bool operator()(const TileCoordsXY& lhs, const TileCoordsXY& rhs) const noexcept { - if (a.type == type && a.location == location) + // NOTE: Ordering should match GetFirstElementAt which is x + (y * size) + return std::tie(lhs.y, lhs.x) < std::tie(rhs.y, rhs.x); + } +}; + +// A list of tile coordinates which contains animated tile elements. +static std::set _mapAnimations; + +static bool UpdateEntranceAnimation(const EntranceElement& entrance, const CoordsXYZ& loc, const int32_t baseZ) +{ + if (entrance.GetEntranceType() == ENTRANCE_TYPE_RIDE_ENTRANCE) + { + const auto ride = GetRide(entrance.GetRideIndex()); + if (ride != nullptr) { - // Animation already exists - return true; + const auto stationObj = ride->getStationObject(); + if (stationObj != nullptr) + { + MapInvalidateTileZoom1({ loc, baseZ + stationObj->Height + 8, baseZ + stationObj->Height + 24 }); + return true; + } } } + else if (entrance.GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE && entrance.GetSequenceIndex() == 0) + { + const int32_t direction = (entrance.GetDirection() + GetCurrentRotation()) & 3; + if (direction == TILE_ELEMENT_DIRECTION_SOUTH || direction == TILE_ELEMENT_DIRECTION_WEST) + { + MapInvalidateTileZoom1({ loc, baseZ + 32, baseZ + 64 }); + } + return true; + } + + return false; +} + +static bool UpdatePathAnimation(const PathElement& path, const CoordsXYZ& loc, const int32_t baseZ) +{ + if (path.IsQueue() && path.HasQueueBanner()) + { + const int32_t direction = (path.GetQueueBannerDirection() + GetCurrentRotation()) & 3; + if (direction == TILE_ELEMENT_DIRECTION_NORTH || direction == TILE_ELEMENT_DIRECTION_EAST) + { + MapInvalidateTileZoom1({ loc, baseZ + 16, baseZ + 30 }); + } + return true; + } return false; } -void MapAnimationCreate(int32_t type, const CoordsXYZ& loc) +static bool UpdateSmallSceneryAnimation(const SmallSceneryElement& scenery, const CoordsXYZ& loc, const int32_t baseZ) { - if (!DoesAnimationExist(type, loc)) + const auto entry = scenery.GetEntry(); + if (entry == nullptr) { - if (_mapAnimations.size() < kMaxAnimatedObjects) - { - // Create new animation - _mapAnimations.push_back({ static_cast(type), loc }); - } - else - { - LOG_ERROR("Exceeded the maximum number of animations"); - } - } -} - -/** - * - * rct2: 0x0068AFAD - */ -void MapAnimationInvalidateAll() -{ - PROFILED_FUNCTION(); - - auto it = _mapAnimations.begin(); - while (it != _mapAnimations.end()) - { - if (InvalidateMapAnimation(*it)) - { - // Map animation has finished, remove it - it = _mapAnimations.erase(it); - } - else - { - it++; - } - } -} - -/** - * - * rct2: 0x00666670 - */ -static bool MapAnimationInvalidateRideEntrance(const CoordsXYZ& loc) -{ - TileCoordsXYZ tileLoc{ loc }; - auto tileElement = MapGetFirstElementAt(loc); - if (tileElement == nullptr) - return true; - do - { - if (tileElement->BaseHeight != tileLoc.z) - continue; - if (tileElement->GetType() != TileElementType::Entrance) - continue; - if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_RIDE_ENTRANCE) - continue; - - auto ride = GetRide(tileElement->AsEntrance()->GetRideIndex()); - if (ride != nullptr) - { - auto stationObj = ride->getStationObject(); - if (stationObj != nullptr) - { - int32_t height = loc.z + stationObj->Height + 8; - MapInvalidateTileZoom1({ loc, height, height + 16 }); - } - } return false; - } while (!(tileElement++)->IsLastForTile()); + } - return true; -} - -/** - * - * rct2: 0x006A7BD4 - */ -static bool MapAnimationInvalidateQueueBanner(const CoordsXYZ& loc) -{ - TileCoordsXYZ tileLoc{ loc }; - TileElement* tileElement; - - tileElement = MapGetFirstElementAt(loc); - if (tileElement == nullptr) - return true; - do + if (entry->HasFlag( + SMALL_SCENERY_FLAG_FOUNTAIN_SPRAY_1 | SMALL_SCENERY_FLAG_FOUNTAIN_SPRAY_4 | SMALL_SCENERY_FLAG_SWAMP_GOO + | SMALL_SCENERY_FLAG_HAS_FRAME_OFFSETS | SMALL_SCENERY_FLAG_IS_CLOCK)) { - if (tileElement->BaseHeight != tileLoc.z) - continue; - if (tileElement->GetType() != TileElementType::Path) - continue; - if (!(tileElement->AsPath()->IsQueue())) - continue; - if (!tileElement->AsPath()->HasQueueBanner()) - continue; - - int32_t direction = (tileElement->AsPath()->GetQueueBannerDirection() + GetCurrentRotation()) & 3; - if (direction == TILE_ELEMENT_DIRECTION_NORTH || direction == TILE_ELEMENT_DIRECTION_EAST) + // Don't apply anything to peeps when the scenery is a ghost. + if (!scenery.IsGhost()) { - MapInvalidateTileZoom1({ loc, loc.z + 16, loc.z + 30 }); - } - return false; - } while (!(tileElement++)->IsLastForTile()); - - return true; -} - -/** - * - * rct2: 0x006E32C9 - */ -static bool MapAnimationInvalidateSmallScenery(const CoordsXYZ& loc) -{ - TileCoordsXYZ tileLoc{ loc }; - - auto tileElement = MapGetFirstElementAt(loc); - if (tileElement == nullptr) - return true; - do - { - if (tileElement->BaseHeight != tileLoc.z) - continue; - if (tileElement->GetType() != TileElementType::SmallScenery) - continue; - if (tileElement->IsGhost()) - continue; - - auto* sceneryEntry = tileElement->AsSmallScenery()->GetEntry(); - if (sceneryEntry == nullptr) - continue; - - if (sceneryEntry->HasFlag( - SMALL_SCENERY_FLAG_FOUNTAIN_SPRAY_1 | SMALL_SCENERY_FLAG_FOUNTAIN_SPRAY_4 | SMALL_SCENERY_FLAG_SWAMP_GOO - | SMALL_SCENERY_FLAG_HAS_FRAME_OFFSETS)) - { - MapInvalidateTileZoom1({ loc, loc.z, tileElement->GetClearanceZ() }); - return false; - } - - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_IS_CLOCK)) - { - // Peep, looking at scenery - if (!(getGameState().currentTicks & 0x3FF) && GameIsNotPaused()) + if (entry->HasFlag(SMALL_SCENERY_FLAG_IS_CLOCK) && !(getGameState().currentTicks & 0x3FF)) { - int32_t direction = tileElement->GetDirection(); - auto quad = EntityTileList(CoordsXY{ loc } - CoordsDirectionDelta[direction]); + const int32_t direction = scenery.GetDirection(); + auto quad = EntityTileList(CoordsXY{ loc.x, loc.y } - CoordsDirectionDelta[direction]); for (auto peep : quad) { - if (peep->State != PeepState::Walking) + if (peep->State != PeepState::Walking || peep->z != baseZ || peep->Action < PeepActionType::Idle) continue; - if (peep->z != loc.z) - continue; - if (peep->Action < PeepActionType::Idle) - continue; - peep->Action = PeepActionType::CheckTime; peep->AnimationFrameNum = 0; peep->AnimationImageIdOffset = 0; @@ -222,509 +125,302 @@ static bool MapAnimationInvalidateSmallScenery(const CoordsXYZ& loc) break; } } - MapInvalidateTileZoom1({ loc, loc.z, tileElement->GetClearanceZ() }); - return false; } + const auto clearZ = scenery.GetClearanceZ(); + MapInvalidateTileZoom1({ loc, baseZ, clearZ }); + return true; + } - } while (!(tileElement++)->IsLastForTile()); - return true; + return false; } -/** - * - * rct2: 0x00666C63 - */ -static bool MapAnimationInvalidateParkEntrance(const CoordsXYZ& loc) +static bool UpdateTrackAnimation(TrackElement& track, const CoordsXYZ& loc, const int32_t baseZ) { - TileCoordsXYZ tileLoc{ loc }; - TileElement* tileElement; - - tileElement = MapGetFirstElementAt(loc); - if (tileElement == nullptr) - return true; - do + const auto clearZ = track.GetClearanceZ(); + switch (track.GetTrackType()) { - if (tileElement->BaseHeight != tileLoc.z) - continue; - if (tileElement->GetType() != TileElementType::Entrance) - continue; - if (tileElement->AsEntrance()->GetEntranceType() != ENTRANCE_TYPE_PARK_ENTRANCE) - continue; - if (tileElement->AsEntrance()->GetSequenceIndex()) - continue; + case TrackElemType::Waterfall: + MapInvalidateTileZoom1({ loc, baseZ + 14, baseZ + 46 }); + return true; + case TrackElemType::Rapids: + case TrackElemType::Whirlpool: + MapInvalidateTileZoom1({ loc, baseZ + 14, baseZ + 18 }); + return true; + case TrackElemType::SpinningTunnel: + MapInvalidateTileZoom1({ loc, baseZ + 14, baseZ + 32 }); + return true; + case TrackElemType::OnRidePhoto: + if (track.IsTakingPhoto()) + { + MapInvalidateTileZoom1({ loc, baseZ, clearZ }); + track.DecrementPhotoTimeout(); + return true; + } + default: + break; + } - MapInvalidateTileZoom1({ loc, loc.z + 32, loc.z + 64 }); + return false; +} + +static bool UpdateLargeSceneryAnimation(const LargeSceneryElement& scenery, const CoordsXYZ& loc, const int32_t baseZ) +{ + const auto entry = scenery.GetEntry(); + if (entry != nullptr && (entry->flags & LARGE_SCENERY_FLAG_ANIMATED)) + { + MapInvalidateTileZoom1({ loc, baseZ, baseZ + 16 }); + return true; + } + + return false; +} + +static bool UpdateWallAnimation(WallElement& wall, const CoordsXYZ& loc, const int32_t baseZ) +{ + const auto entry = wall.GetEntry(); + if (entry == nullptr) + { return false; - } while (!(tileElement++)->IsLastForTile()); + } - return true; -} - -/** - * - * rct2: 0x006CE29E - */ -static bool MapAnimationInvalidateTrackWaterfall(const CoordsXYZ& loc) -{ - TileCoordsXYZ tileLoc{ loc }; - TileElement* tileElement; - - tileElement = MapGetFirstElementAt(loc); - if (tileElement == nullptr) - return true; - do + if (entry->flags & WALL_SCENERY_IS_DOOR) { - if (tileElement->BaseHeight != tileLoc.z) - continue; - if (tileElement->GetType() != TileElementType::Track) - continue; - - if (tileElement->AsTrack()->GetTrackType() == TrackElemType::Waterfall) + if (getGameState().currentTicks & 1) { - MapInvalidateTileZoom1({ loc, loc.z + 14, loc.z + 46 }); - return false; - } - } while (!(tileElement++)->IsLastForTile()); - - return true; -} - -/** - * - * rct2: 0x006CE2F3 - */ -static bool MapAnimationInvalidateTrackRapids(const CoordsXYZ& loc) -{ - TileCoordsXYZ tileLoc{ loc }; - TileElement* tileElement; - - tileElement = MapGetFirstElementAt(loc); - if (tileElement == nullptr) - return true; - do - { - if (tileElement->BaseHeight != tileLoc.z) - continue; - if (tileElement->GetType() != TileElementType::Track) - continue; - - if (tileElement->AsTrack()->GetTrackType() == TrackElemType::Rapids) - { - MapInvalidateTileZoom1({ loc, loc.z + 14, loc.z + 18 }); - return false; - } - } while (!(tileElement++)->IsLastForTile()); - - return true; -} - -/** - * - * rct2: 0x006CE39D - */ -static bool MapAnimationInvalidateTrackOnRidePhoto(const CoordsXYZ& loc) -{ - TileCoordsXYZ tileLoc{ loc }; - TileElement* tileElement; - - tileElement = MapGetFirstElementAt(loc); - if (tileElement == nullptr) - return true; - do - { - if (tileElement->BaseHeight != tileLoc.z) - continue; - if (tileElement->GetType() != TileElementType::Track) - continue; - - if (tileElement->AsTrack()->GetTrackType() == TrackElemType::OnRidePhoto) - { - MapInvalidateTileZoom1({ loc, loc.z, tileElement->GetClearanceZ() }); - if (GameIsPaused()) - { - return false; - } - if (tileElement->AsTrack()->IsTakingPhoto()) - { - tileElement->AsTrack()->DecrementPhotoTimeout(); - return false; - } - return true; } - } while (!(tileElement++)->IsLastForTile()); - return true; -} + bool removeAnim = true; -/** - * - * rct2: 0x006CE348 - */ -static bool MapAnimationInvalidateTrackWhirlpool(const CoordsXYZ& loc) -{ - TileCoordsXYZ tileLoc{ loc }; - TileElement* tileElement; - - tileElement = MapGetFirstElementAt(loc); - if (tileElement == nullptr) - return true; - do - { - if (tileElement->BaseHeight != tileLoc.z) - continue; - if (tileElement->GetType() != TileElementType::Track) - continue; - - if (tileElement->AsTrack()->GetTrackType() == TrackElemType::Whirlpool) - { - MapInvalidateTileZoom1({ loc, loc.z + 14, loc.z + 18 }); - return false; - } - } while (!(tileElement++)->IsLastForTile()); - - return true; -} - -/** - * - * rct2: 0x006CE3FA - */ -static bool MapAnimationInvalidateTrackSpinningTunnel(const CoordsXYZ& loc) -{ - TileCoordsXYZ tileLoc{ loc }; - TileElement* tileElement; - - tileElement = MapGetFirstElementAt(loc); - if (tileElement == nullptr) - return true; - do - { - if (tileElement->BaseHeight != tileLoc.z) - continue; - if (tileElement->GetType() != TileElementType::Track) - continue; - - if (tileElement->AsTrack()->GetTrackType() == TrackElemType::SpinningTunnel) - { - MapInvalidateTileZoom1({ loc, loc.z + 14, loc.z + 32 }); - return false; - } - } while (!(tileElement++)->IsLastForTile()); - - return true; -} - -/** - * - * rct2: 0x0068DF8F - */ -static bool MapAnimationInvalidateRemove([[maybe_unused]] const CoordsXYZ& loc) -{ - return true; -} - -/** - * - * rct2: 0x006BA2BB - */ -static bool MapAnimationInvalidateBanner(const CoordsXYZ& loc) -{ - TileCoordsXYZ tileLoc{ loc }; - TileElement* tileElement; - - tileElement = MapGetFirstElementAt(loc); - if (tileElement == nullptr) - return true; - do - { - if (tileElement->BaseHeight != tileLoc.z) - continue; - if (tileElement->GetType() != TileElementType::Banner) - continue; - MapInvalidateTileZoom1({ loc, loc.z, loc.z + 16 }); - return false; - } while (!(tileElement++)->IsLastForTile()); - - return true; -} - -/** - * - * rct2: 0x006B94EB - */ -static bool MapAnimationInvalidateLargeScenery(const CoordsXYZ& loc) -{ - TileCoordsXYZ tileLoc{ loc }; - TileElement* tileElement; - - bool wasInvalidated = false; - tileElement = MapGetFirstElementAt(loc); - if (tileElement == nullptr) - return true; - do - { - if (tileElement->BaseHeight != tileLoc.z) - continue; - if (tileElement->GetType() != TileElementType::LargeScenery) - continue; - - auto* sceneryEntry = tileElement->AsLargeScenery()->GetEntry(); - if (sceneryEntry != nullptr && sceneryEntry->flags & LARGE_SCENERY_FLAG_ANIMATED) - { - MapInvalidateTileZoom1({ loc, loc.z, loc.z + 16 }); - wasInvalidated = true; - } - } while (!(tileElement++)->IsLastForTile()); - - return !wasInvalidated; -} - -/** - * - * rct2: 0x006E5B50 - */ -static bool MapAnimationInvalidateWallDoor(const CoordsXYZ& loc) -{ - TileCoordsXYZ tileLoc{ loc }; - TileElement* tileElement; - - if (getGameState().currentTicks & 1) - return false; - - bool removeAnimation = true; - tileElement = MapGetFirstElementAt(loc); - if (tileElement == nullptr) - return removeAnimation; - do - { - if (tileElement->BaseHeight != tileLoc.z) - continue; - if (tileElement->GetType() != TileElementType::Wall) - continue; - - auto* wallEntry = tileElement->AsWall()->GetEntry(); - if (wallEntry == nullptr || !(wallEntry->flags & WALL_SCENERY_IS_DOOR)) - continue; - - if (GameIsPaused()) - { - return false; - } - - bool invalidate = false; - - uint8_t currentFrame = tileElement->AsWall()->GetAnimationFrame(); + const auto currentFrame = wall.GetAnimationFrame(); if (currentFrame != 0) { + auto newFrame = currentFrame; if (currentFrame == 15) { - currentFrame = 0; + newFrame = 0; } else { - removeAnimation = false; + removeAnim = false; if (currentFrame != 5) { - currentFrame++; - if (currentFrame == 13 && !(wallEntry->flags & WALL_SCENERY_LONG_DOOR_ANIMATION)) - currentFrame = 15; + newFrame++; - invalidate = true; + if (newFrame == 13 && !(entry->flags & WALL_SCENERY_LONG_DOOR_ANIMATION)) + newFrame = 15; } } + + if (currentFrame != newFrame) + { + wall.SetAnimationFrame(newFrame); + MapInvalidateTileZoom1({ loc, baseZ, baseZ + 32 }); + } } - tileElement->AsWall()->SetAnimationFrame(currentFrame); - if (invalidate) - { - MapInvalidateTileZoom1({ loc, loc.z, loc.z + 32 }); - } - } while (!(tileElement++)->IsLastForTile()); - return removeAnimation; -} - -/** - * - * rct2: 0x006E5EE4 - */ -static bool MapAnimationInvalidateWall(const CoordsXYZ& loc) -{ - TileCoordsXYZ tileLoc{ loc }; - TileElement* tileElement; - - bool wasInvalidated = false; - tileElement = MapGetFirstElementAt(loc); - if (tileElement == nullptr) - return true; - do - { - if (tileElement->BaseHeight != tileLoc.z) - continue; - if (tileElement->GetType() != TileElementType::Wall) - continue; - - auto* wallEntry = tileElement->AsWall()->GetEntry(); - - if (wallEntry == nullptr - || (!(wallEntry->flags2 & WALL_SCENERY_2_ANIMATED) && wallEntry->scrolling_mode == kScrollingModeNone)) - continue; - - MapInvalidateTileZoom1({ loc, loc.z, loc.z + 16 }); - wasInvalidated = true; - } while (!(tileElement++)->IsLastForTile()); - - return !wasInvalidated; -} - -/** - * - * rct2: 0x009819DC - */ -static constexpr map_animation_invalidate_event_handler _animatedObjectEventHandlers[MAP_ANIMATION_TYPE_COUNT] = { - MapAnimationInvalidateRideEntrance, MapAnimationInvalidateQueueBanner, MapAnimationInvalidateSmallScenery, - MapAnimationInvalidateParkEntrance, MapAnimationInvalidateTrackWaterfall, MapAnimationInvalidateTrackRapids, - MapAnimationInvalidateTrackOnRidePhoto, MapAnimationInvalidateTrackWhirlpool, MapAnimationInvalidateTrackSpinningTunnel, - MapAnimationInvalidateRemove, MapAnimationInvalidateBanner, MapAnimationInvalidateLargeScenery, - MapAnimationInvalidateWallDoor, MapAnimationInvalidateWall, -}; - -/** - * @returns true if the animation should be removed. - */ -static bool InvalidateMapAnimation(const MapAnimation& a) -{ - if (a.type < std::size(_animatedObjectEventHandlers)) - { - return _animatedObjectEventHandlers[a.type](a.location); + return !removeAnim; } + else if ((entry->flags2 & WALL_SCENERY_2_ANIMATED) || entry->scrolling_mode != kScrollingModeNone) + { + MapInvalidateTileZoom1({ loc, baseZ, baseZ + 16 }); + return true; + } + + return false; +} + +static bool UpdateBannerAnimation([[maybe_unused]] const BannerElement& banner, const CoordsXYZ& loc, const int32_t baseZ) +{ + MapInvalidateTileZoom1({ loc, baseZ, baseZ + 16 }); return true; } -const std::vector& GetMapAnimations() +static bool UpdateTileAnimations(const TileCoordsXY& coords) { - return _mapAnimations; -} - -void ClearMapAnimations() -{ - _mapAnimations.clear(); -} - -void MapAnimationAutoCreate() -{ - ClearMapAnimations(); - - TileElementIterator it; - TileElementIteratorBegin(&it); - while (TileElementIteratorNext(&it)) + auto tileElement = MapGetFirstElementAt(coords); + if (tileElement == nullptr) { - MapAnimationAutoCreateAtTileElement(TileCoordsXY(it.x, it.y), it.element); + return false; } + + bool hasAnimations = false; + do + { + const auto baseZ = tileElement->GetBaseZ(); + const CoordsXYZ loc{ coords.ToCoordsXY(), baseZ }; + + switch (tileElement->GetType()) + { + case TileElementType::Entrance: + hasAnimations |= UpdateEntranceAnimation(*tileElement->AsEntrance(), loc, baseZ); + break; + case TileElementType::Path: + hasAnimations |= UpdatePathAnimation(*tileElement->AsPath(), loc, baseZ); + break; + case TileElementType::SmallScenery: + hasAnimations |= UpdateSmallSceneryAnimation(*tileElement->AsSmallScenery(), loc, baseZ); + break; + case TileElementType::Track: + hasAnimations |= UpdateTrackAnimation(*tileElement->AsTrack(), loc, baseZ); + break; + case TileElementType::Banner: + hasAnimations |= UpdateBannerAnimation(*tileElement->AsBanner(), loc, baseZ); + break; + case TileElementType::LargeScenery: + hasAnimations |= UpdateLargeSceneryAnimation(*tileElement->AsLargeScenery(), loc, baseZ); + break; + case TileElementType::Wall: + hasAnimations |= UpdateWallAnimation(*tileElement->AsWall(), loc, baseZ); + break; + default: + break; + } + } while (!(tileElement++)->IsLastForTile()); + + return hasAnimations; } -void MapAnimationAutoCreateAtTileElement(TileCoordsXY coords, TileElement* el) +static bool IsElementAnimated(const TileElementBase& element, const bool skipDoors) { - if (el == nullptr) - { - return; - } - auto loc = CoordsXYZ{ coords.ToCoordsXY(), el->GetBaseZ() }; - switch (el->GetType()) + switch (element.GetType()) { case TileElementType::Banner: - MapAnimationCreate(MAP_ANIMATION_TYPE_BANNER, loc); - break; + return true; case TileElementType::Wall: { - auto wallEl = el->AsWall(); - auto* entry = wallEl->GetEntry(); - if (entry != nullptr && ((entry->flags2 & WALL_SCENERY_2_ANIMATED) || entry->scrolling_mode != kScrollingModeNone)) + const auto wall = element.AsWall(); + const auto entry = wall->GetEntry(); + if (entry != nullptr) { - MapAnimationCreate(MAP_ANIMATION_TYPE_WALL, loc); + if ((entry->flags2 & WALL_SCENERY_2_ANIMATED) || entry->scrolling_mode != kScrollingModeNone) + { + return true; + } + if (!skipDoors && (entry->flags & WALL_SCENERY_IS_DOOR)) + { + return true; + } } break; } case TileElementType::SmallScenery: { - auto sceneryEl = el->AsSmallScenery(); - auto* sceneryEntry = sceneryEl->GetEntry(); - if (sceneryEntry != nullptr && sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ANIMATED)) + const auto scenery = element.AsSmallScenery(); + const auto entry = scenery->GetEntry(); + if (entry != nullptr && entry->HasFlag(SMALL_SCENERY_FLAG_ANIMATED)) { - MapAnimationCreate(MAP_ANIMATION_TYPE_SMALL_SCENERY, loc); + return true; } break; } case TileElementType::LargeScenery: { - auto sceneryEl = el->AsLargeScenery(); - auto entry = sceneryEl->GetEntry(); + const auto scenery = element.AsLargeScenery(); + const auto entry = scenery->GetEntry(); if (entry != nullptr && (entry->flags & LARGE_SCENERY_FLAG_ANIMATED)) { - MapAnimationCreate(MAP_ANIMATION_TYPE_LARGE_SCENERY, loc); + return true; } break; } case TileElementType::Path: { - auto path = el->AsPath(); + const auto path = element.AsPath(); if (path->HasQueueBanner()) { - MapAnimationCreate(MAP_ANIMATION_TYPE_QUEUE_BANNER, loc); + return true; } break; } case TileElementType::Entrance: { - auto entrance = el->AsEntrance(); - switch (entrance->GetEntranceType()) + const auto entrance = element.AsEntrance(); + if (entrance->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE && entrance->GetSequenceIndex() == 0) { - case ENTRANCE_TYPE_PARK_ENTRANCE: - if (entrance->GetSequenceIndex() == 0) - { - MapAnimationCreate(MAP_ANIMATION_TYPE_PARK_ENTRANCE, loc); - } - break; - case ENTRANCE_TYPE_RIDE_ENTRANCE: - MapAnimationCreate(MAP_ANIMATION_TYPE_RIDE_ENTRANCE, loc); - break; + return true; + } + else if (entrance->GetEntranceType() == ENTRANCE_TYPE_RIDE_ENTRANCE) + { + return true; } break; } case TileElementType::Track: { - auto track = el->AsTrack(); + const auto track = element.AsTrack(); switch (track->GetTrackType()) { case TrackElemType::Waterfall: - MapAnimationCreate(MAP_ANIMATION_TYPE_TRACK_WATERFALL, loc); - break; case TrackElemType::Rapids: - MapAnimationCreate(MAP_ANIMATION_TYPE_TRACK_RAPIDS, loc); - break; case TrackElemType::Whirlpool: - MapAnimationCreate(MAP_ANIMATION_TYPE_TRACK_WHIRLPOOL, loc); - break; case TrackElemType::SpinningTunnel: - MapAnimationCreate(MAP_ANIMATION_TYPE_TRACK_SPINNINGTUNNEL, loc); - break; + case TrackElemType::OnRidePhoto: + return true; default: break; } break; } - case TileElementType::Surface: + default: break; } + + return false; +} + +void MapAnimation::Create(const CoordsXY coords) +{ + _mapAnimations.insert(TileCoordsXY(coords)); +} + +void MapAnimation::CreateAll() +{ + ClearAll(); + + TileElementIterator it; + TileElementIteratorBegin(&it); + while (TileElementIteratorNext(&it)) + { + if (IsElementAnimated(*it.element, true)) + { + _mapAnimations.insert(TileCoordsXY(it.x, it.y)); + } + } } -void ShiftAllMapAnimations(CoordsXY amount) +void MapAnimation::UpdateAll() +{ + PROFILED_FUNCTION(); + + auto it = _mapAnimations.begin(); + while (it != _mapAnimations.end()) + { + if (UpdateTileAnimations(*it)) + { + ++it; // Tile was updated, keep it + } + else + { + it = _mapAnimations.erase(it); // No updates, remove tile + } + } +} + +void MapAnimation::ClearAll() +{ + _mapAnimations.clear(); +} + +void MapAnimation::ShiftAll(const CoordsXY amount) { if (amount.x == 0 && amount.y == 0) return; - for (auto& a : _mapAnimations) + std::set newMapAnimations; + for (const auto& a : _mapAnimations) { - a.location += amount; + newMapAnimations.insert(a + TileCoordsXY(amount)); } + _mapAnimations = std::move(newMapAnimations); } diff --git a/src/openrct2/world/MapAnimation.h b/src/openrct2/world/MapAnimation.h index 9d5e5d7e6c..b7293c6570 100644 --- a/src/openrct2/world/MapAnimation.h +++ b/src/openrct2/world/MapAnimation.h @@ -11,40 +11,11 @@ #include "Location.hpp" -#include -#include - -struct TileElement; - -struct MapAnimation +namespace OpenRCT2::MapAnimation { - uint8_t type{}; - CoordsXYZ location{}; -}; - -enum -{ - MAP_ANIMATION_TYPE_RIDE_ENTRANCE, - MAP_ANIMATION_TYPE_QUEUE_BANNER, - MAP_ANIMATION_TYPE_SMALL_SCENERY, - MAP_ANIMATION_TYPE_PARK_ENTRANCE, - MAP_ANIMATION_TYPE_TRACK_WATERFALL, - MAP_ANIMATION_TYPE_TRACK_RAPIDS, - MAP_ANIMATION_TYPE_TRACK_ONRIDEPHOTO, - MAP_ANIMATION_TYPE_TRACK_WHIRLPOOL, - MAP_ANIMATION_TYPE_TRACK_SPINNINGTUNNEL, - MAP_ANIMATION_TYPE_REMOVE, - MAP_ANIMATION_TYPE_BANNER, - MAP_ANIMATION_TYPE_LARGE_SCENERY, - MAP_ANIMATION_TYPE_WALL_DOOR, - MAP_ANIMATION_TYPE_WALL, - MAP_ANIMATION_TYPE_COUNT -}; - -void MapAnimationCreate(int32_t type, const CoordsXYZ& loc); -void MapAnimationInvalidateAll(); -const std::vector& GetMapAnimations(); -void ClearMapAnimations(); -void MapAnimationAutoCreate(); -void MapAnimationAutoCreateAtTileElement(TileCoordsXY coords, TileElement* el); -void ShiftAllMapAnimations(CoordsXY amount); + void Create(const CoordsXY coords); + void CreateAll(); + void UpdateAll(); + void ClearAll(); + void ShiftAll(const CoordsXY amount); +} // namespace OpenRCT2::MapAnimation diff --git a/src/openrct2/world/TileInspector.cpp b/src/openrct2/world/TileInspector.cpp index a00898d315..4afc32bec4 100644 --- a/src/openrct2/world/TileInspector.cpp +++ b/src/openrct2/world/TileInspector.cpp @@ -349,7 +349,7 @@ namespace OpenRCT2::TileInspector *pastedElement = element; pastedElement->SetLastForTile(lastForTile); - MapAnimationAutoCreateAtTileElement(tileLoc, pastedElement); + MapAnimation::Create(tileLoc.ToCoordsXY()); if (IsTileSelected(loc)) { diff --git a/test/tests/PlayTests.cpp b/test/tests/PlayTests.cpp index 66f9fa1547..e8752f7841 100644 --- a/test/tests/PlayTests.cpp +++ b/test/tests/PlayTests.cpp @@ -60,7 +60,7 @@ static std::unique_ptr localStartGame(const std::string& parkPath) ResetAllSpriteQuadrantPlacements(); LoadPalette(); EntityTweener::Get().Reset(); - MapAnimationAutoCreate(); + MapAnimation::CreateAll(); FixInvalidVehicleSpriteSizes(); gGameSpeed = 1; diff --git a/test/tests/S6ImportExportTests.cpp b/test/tests/S6ImportExportTests.cpp index 8a89d20c15..f8cb1808ca 100644 --- a/test/tests/S6ImportExportTests.cpp +++ b/test/tests/S6ImportExportTests.cpp @@ -67,7 +67,7 @@ static void GameInit(bool retainSpatialIndices) ResetAllSpriteQuadrantPlacements(); LoadPalette(); EntityTweener::Get().Reset(); - MapAnimationAutoCreate(); + MapAnimation::CreateAll(); FixInvalidVehicleSpriteSizes(); gGameSpeed = 1;