diff --git a/src/openrct2/actions/BannerPlaceAction.cpp b/src/openrct2/actions/BannerPlaceAction.cpp index 474df4e56d..469a646534 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); - MapAnimation::Create(_loc); + MapAnimation::MarkTileForInvalidation(_loc); res.Cost = bannerEntry->price; return res; diff --git a/src/openrct2/actions/BannerRemoveAction.cpp b/src/openrct2/actions/BannerRemoveAction.cpp index 64639edde7..9691e6a41a 100644 --- a/src/openrct2/actions/BannerRemoveAction.cpp +++ b/src/openrct2/actions/BannerRemoveAction.cpp @@ -14,7 +14,6 @@ #include "../object/BannerSceneryEntry.h" #include "../object/ObjectEntryManager.h" #include "../world/Banner.h" -#include "../world/MapAnimation.h" #include "../world/Scenery.h" #include "../world/TileElementsView.h" #include "../world/tile_element/BannerElement.h" diff --git a/src/openrct2/actions/LargeSceneryPlaceAction.cpp b/src/openrct2/actions/LargeSceneryPlaceAction.cpp index a86ae0f13d..330d15ce01 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); } - MapAnimation::Create(curTile); + MapAnimation::MarkTileForInvalidation(curTile); MapInvalidateTileFull(curTile); if (tile.index == 0) diff --git a/src/openrct2/actions/ParkEntrancePlaceAction.cpp b/src/openrct2/actions/ParkEntrancePlaceAction.cpp index 88e16f4832..97faf61e98 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) { - MapAnimation::Create(entranceLoc); + MapAnimation::MarkTileForInvalidation(entranceLoc); } } diff --git a/src/openrct2/actions/RideEntranceExitPlaceAction.cpp b/src/openrct2/actions/RideEntranceExitPlaceAction.cpp index 313eb972c2..ae91108ab9 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; - MapAnimation::Create(_loc); + MapAnimation::MarkTileForInvalidation(_loc); } FootpathQueueChainReset(); diff --git a/src/openrct2/actions/SmallSceneryPlaceAction.cpp b/src/openrct2/actions/SmallSceneryPlaceAction.cpp index c77f5c5383..26475df0a3 100644 --- a/src/openrct2/actions/SmallSceneryPlaceAction.cpp +++ b/src/openrct2/actions/SmallSceneryPlaceAction.cpp @@ -450,9 +450,13 @@ GameActions::Result SmallSceneryPlaceAction::Execute() const res.SetData(SmallSceneryPlaceActionResult{ groundFlags, sceneryElement->GetBaseZ(), sceneryElement->GetSceneryQuadrant() }); MapInvalidateTileFull(_loc); - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ANIMATED)) + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_IS_CLOCK)) { - MapAnimation::Create(_loc); + MapAnimation::MarkTileForUpdate(_loc); + } + else if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_ANIMATED)) + { + MapAnimation::MarkTileForInvalidation(_loc); } return res; diff --git a/src/openrct2/actions/SmallScenerySetColourAction.cpp b/src/openrct2/actions/SmallScenerySetColourAction.cpp index 2ae5015e33..896e16236f 100644 --- a/src/openrct2/actions/SmallScenerySetColourAction.cpp +++ b/src/openrct2/actions/SmallScenerySetColourAction.cpp @@ -18,7 +18,6 @@ #include "../management/Finance.h" #include "../ride/Ride.h" #include "../ride/TrackDesign.h" -#include "../world/MapAnimation.h" #include "../world/Park.h" #include "../world/tile_element/SmallSceneryElement.h" diff --git a/src/openrct2/actions/TrackPlaceAction.cpp b/src/openrct2/actions/TrackPlaceAction.cpp index c291d31132..aafb50e90a 100644 --- a/src/openrct2/actions/TrackPlaceAction.cpp +++ b/src/openrct2/actions/TrackPlaceAction.cpp @@ -600,7 +600,7 @@ GameActions::Result TrackPlaceAction::Execute() const case TrackElemType::Whirlpool: [[fallthrough]]; case TrackElemType::SpinningTunnel: - MapAnimation::Create(mapLoc); + MapAnimation::MarkTileForInvalidation(mapLoc); break; case TrackElemType::Brakes: [[fallthrough]]; diff --git a/src/openrct2/actions/TrackRemoveAction.cpp b/src/openrct2/actions/TrackRemoveAction.cpp index b2bc9a3746..cfdf7cffd4 100644 --- a/src/openrct2/actions/TrackRemoveAction.cpp +++ b/src/openrct2/actions/TrackRemoveAction.cpp @@ -17,7 +17,6 @@ #include "../ride/TrackData.h" #include "../ride/TrackDesign.h" #include "../world/Footpath.h" -#include "../world/MapAnimation.h" #include "../world/tile_element/SurfaceElement.h" #include "../world/tile_element/TrackElement.h" #include "RideSetSettingAction.h" diff --git a/src/openrct2/actions/WallPlaceAction.cpp b/src/openrct2/actions/WallPlaceAction.cpp index bd21bf8ad6..5c555b3c9e 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); - MapAnimation::Create(targetLoc); + MapAnimation::MarkTileForInvalidation(targetLoc); MapInvalidateTileZoom1({ _loc, wallElement->GetBaseZ(), wallElement->GetBaseZ() + 72 }); res.Cost = wallEntry->price; diff --git a/src/openrct2/actions/WallSetColourAction.cpp b/src/openrct2/actions/WallSetColourAction.cpp index cf77121e42..a5c3f4a8c2 100644 --- a/src/openrct2/actions/WallSetColourAction.cpp +++ b/src/openrct2/actions/WallSetColourAction.cpp @@ -17,7 +17,6 @@ #include "../ride/Track.h" #include "../ride/TrackData.h" #include "../world/Banner.h" -#include "../world/MapAnimation.h" #include "../world/Scenery.h" #include "../world/tile_element/WallElement.h" diff --git a/src/openrct2/interface/Viewport.cpp b/src/openrct2/interface/Viewport.cpp index e8032e2f68..e93fff2603 100644 --- a/src/openrct2/interface/Viewport.cpp +++ b/src/openrct2/interface/Viewport.cpp @@ -1167,6 +1167,17 @@ namespace OpenRCT2 return { mapCoords->ToTileStart() }; } + [[nodiscard]] bool Viewport::ContainsTile(const TileCoordsXY coords) const noexcept + { + const auto centreCoords = coords.ToCoordsXY() + CoordsXY(16, 16); + const auto screenPos = Translate3DTo2DWithZ(rotation, CoordsXYZ{ centreCoords, 0 }); + const auto left = screenPos.x - 32; + const auto top = screenPos.y - (kMaxTileElementHeight * kCoordsZStep) - 16; + const auto right = screenPos.x + 32; + const auto bottom = screenPos.y + 16; + return !(left > viewPos.x + ViewWidth() || top > viewPos.y + ViewHeight() || right < viewPos.x || bottom < viewPos.y); + } + [[nodiscard]] ScreenCoordsXY Viewport::ScreenToViewportCoord(const ScreenCoordsXY& screenCoords) const { ScreenCoordsXY ret; @@ -2065,6 +2076,19 @@ namespace OpenRCT2 gameState.savedViewRotation = viewport->rotation; } } + + ViewportList GetVisibleViewports() noexcept + { + ViewportList viewports; + for (auto& viewport : _viewports) + { + if (viewport.isVisible) + { + viewports.push_back(&viewport); + } + }; + return viewports; + } } // namespace OpenRCT2 ZoomLevel ZoomLevel::min() diff --git a/src/openrct2/interface/Viewport.h b/src/openrct2/interface/Viewport.h index 37a65198d8..d6e23b012b 100644 --- a/src/openrct2/interface/Viewport.h +++ b/src/openrct2/interface/Viewport.h @@ -14,6 +14,7 @@ #include #include +#include #include struct PaintSession; @@ -65,6 +66,8 @@ namespace OpenRCT2 return (sPos.x >= pos.x && sPos.x < pos.x + width && sPos.y >= pos.y && sPos.y < pos.y + height); } + [[nodiscard]] bool ContainsTile(const TileCoordsXY coords) const noexcept; + [[nodiscard]] ScreenCoordsXY ScreenToViewportCoord(const ScreenCoordsXY& screenCoord) const; void Invalidate() const; @@ -227,4 +230,8 @@ namespace OpenRCT2 void ViewportSetSavedView(); VisibilityKind GetPaintStructVisibility(const PaintStruct* ps, uint32_t viewFlags); + + using ViewportList = sfl::static_vector; + + ViewportList GetVisibleViewports() noexcept; } // namespace OpenRCT2 diff --git a/src/openrct2/paint/Paint.Entity.cpp b/src/openrct2/paint/Paint.Entity.cpp index 94272905ff..dcbca1aad2 100644 --- a/src/openrct2/paint/Paint.Entity.cpp +++ b/src/openrct2/paint/Paint.Entity.cpp @@ -25,7 +25,6 @@ #include "../ride/TrackDesign.h" #include "../ride/Vehicle.h" #include "../world/Climate.h" -#include "../world/MapAnimation.h" #include "../world/Park.h" #include "Paint.h" #include "vehicle/VehiclePaint.h" diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 5ab567dfea..50b977aa3d 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -1051,7 +1051,7 @@ namespace OpenRCT2::RCT2 } tileElement->AsWall()->SetIsAnimating(true); - MapAnimation::Create(coords); + MapAnimation::MarkTileForUpdate(coords); } while (!(tileElement++)->IsLastForTile()); break; } diff --git a/src/openrct2/ride/Ride.cpp b/src/openrct2/ride/Ride.cpp index 01debe0099..f7dbedb643 100644 --- a/src/openrct2/ride/Ride.cpp +++ b/src/openrct2/ride/Ride.cpp @@ -56,7 +56,6 @@ #include "../world/Footpath.h" #include "../world/Location.hpp" #include "../world/Map.h" -#include "../world/MapAnimation.h" #include "../world/Park.h" #include "../world/Scenery.h" #include "../world/TileElementsView.h" diff --git a/src/openrct2/ride/RideConstruction.cpp b/src/openrct2/ride/RideConstruction.cpp index a13175a7b9..d73a9ab4c5 100644 --- a/src/openrct2/ride/RideConstruction.cpp +++ b/src/openrct2/ride/RideConstruction.cpp @@ -32,7 +32,6 @@ #include "../world/Footpath.h" #include "../world/Location.hpp" #include "../world/Map.h" -#include "../world/MapAnimation.h" #include "../world/Park.h" #include "../world/Scenery.h" #include "../world/TileElementsView.h" diff --git a/src/openrct2/ride/Vehicle.cpp b/src/openrct2/ride/Vehicle.cpp index fae1e3933b..8f1c41152e 100644 --- a/src/openrct2/ride/Vehicle.cpp +++ b/src/openrct2/ride/Vehicle.cpp @@ -6316,7 +6316,7 @@ static void AnimateSceneryDoor(const CoordsXYZD& doorLocation, const CoordsXYZ& door->SetIsAnimating(true); play_scenery_door_open_sound(trackLocation, door); - MapAnimation::Create(doorLocation); + MapAnimation::MarkTileForUpdate(doorLocation); } if (isLastVehicle) @@ -6326,7 +6326,7 @@ static void AnimateSceneryDoor(const CoordsXYZD& doorLocation, const CoordsXYZ& door->SetIsAnimating(true); play_scenery_door_close_sound(trackLocation, door); - MapAnimation::Create(doorLocation); + MapAnimation::MarkTileForUpdate(doorLocation); } } diff --git a/src/openrct2/world/Footpath.cpp b/src/openrct2/world/Footpath.cpp index 1df3026509..e7792bbbbe 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 - MapAnimation::Create(lastPath); + MapAnimation::MarkTileForInvalidation(lastPath); } } } diff --git a/src/openrct2/world/MapAnimation.cpp b/src/openrct2/world/MapAnimation.cpp index ad100ca9b5..99392103bc 100644 --- a/src/openrct2/world/MapAnimation.cpp +++ b/src/openrct2/world/MapAnimation.cpp @@ -36,6 +36,7 @@ #include "tile_element/WallElement.h" #include +#include #include using namespace OpenRCT2; @@ -54,6 +55,12 @@ struct TemporaryMapAnimation } }; +enum class UpdateType : uint8_t +{ + invalidate, + update, +}; + struct TileCoordsXYCmp { constexpr bool operator()(const TileCoordsXY& lhs, const TileCoordsXY& rhs) const noexcept @@ -64,11 +71,11 @@ struct TileCoordsXYCmp }; // A list of tile coordinates which contains animated tile elements. -static std::set _mapAnimations; +static std::map _mapAnimations; static std::set _temporaryMapAnimations; -static bool UpdateEntranceAnimation(const EntranceElement& entrance, const CoordsXYZ& loc, const int32_t baseZ) +static bool InvalidateEntranceAnimation(const EntranceElement& entrance, const CoordsXYZ& loc, const int32_t baseZ) { if (entrance.GetEntranceType() == ENTRANCE_TYPE_RIDE_ENTRANCE) { @@ -96,7 +103,7 @@ static bool UpdateEntranceAnimation(const EntranceElement& entrance, const Coord return false; } -static bool UpdatePathAnimation(const PathElement& path, const CoordsXYZ& loc, const int32_t baseZ) +static bool InvalidatePathAnimation(const PathElement& path, const CoordsXYZ& loc, const int32_t baseZ) { if (path.IsQueue() && path.HasQueueBanner()) { @@ -110,18 +117,20 @@ static bool UpdatePathAnimation(const PathElement& path, const CoordsXYZ& loc, c return false; } -static bool UpdateSmallSceneryAnimation(const SmallSceneryElement& scenery, const CoordsXYZ& loc, const int32_t baseZ) +static std::optional UpdateSmallSceneryAnimation( + const SmallSceneryElement& scenery, const CoordsXYZ& loc, const int32_t baseZ) { const auto entry = scenery.GetEntry(); if (entry == nullptr) { - return false; + return std::nullopt; } 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)) { + auto animationType = UpdateType ::invalidate; // Don't apply anything to peeps when the scenery is a ghost. if (!scenery.IsGhost()) { @@ -140,17 +149,18 @@ static bool UpdateSmallSceneryAnimation(const SmallSceneryElement& scenery, cons peep->Invalidate(); break; } + animationType = UpdateType ::update; } } const auto clearZ = scenery.GetClearanceZ(); ViewportsInvalidate(loc.x, loc.y, baseZ, clearZ, kMaxZoom); - return true; + return std::optional(animationType); } - return false; + return std::nullopt; } -static bool UpdateTrackAnimation(TrackElement& track, const CoordsXYZ& loc, const int32_t baseZ) +static bool InvalidateTrackAnimation(TrackElement& track, const CoordsXYZ& loc, const int32_t baseZ) { switch (track.GetTrackType()) { @@ -171,7 +181,7 @@ static bool UpdateTrackAnimation(TrackElement& track, const CoordsXYZ& loc, cons return false; } -static bool UpdateLargeSceneryAnimation(const LargeSceneryElement& scenery, const CoordsXYZ& loc, const int32_t baseZ) +static bool InvalidateLargeSceneryAnimation(const LargeSceneryElement& scenery, const CoordsXYZ& loc, const int32_t baseZ) { const auto entry = scenery.GetEntry(); if (entry != nullptr && (entry->flags & LARGE_SCENERY_FLAG_ANIMATED)) @@ -183,19 +193,19 @@ static bool UpdateLargeSceneryAnimation(const LargeSceneryElement& scenery, cons return false; } -static bool UpdateWallAnimation(WallElement& wall, const CoordsXYZ& loc, const int32_t baseZ) +static std::optional UpdateWallAnimation(WallElement& wall, const CoordsXYZ& loc, const int32_t baseZ) { const auto entry = wall.GetEntry(); if (entry == nullptr) { - return false; + return std::nullopt; } if (entry->flags & WALL_SCENERY_IS_DOOR && wall.IsAnimating()) { if (getGameState().currentTicks & 1) { - return true; + return std::optional(UpdateType::update); } bool removeAnim = true; @@ -228,32 +238,34 @@ static bool UpdateWallAnimation(WallElement& wall, const CoordsXYZ& loc, const i } } - return !removeAnim; + return removeAnim ? std::nullopt : std::optional(UpdateType::update); } else if ((entry->flags2 & WALL_SCENERY_2_ANIMATED) || entry->scrolling_mode != kScrollingModeNone) { ViewportsInvalidate(loc.x, loc.y, baseZ, baseZ + 16, kMaxZoom); - return true; + return std::optional(UpdateType::invalidate); } - return false; + return std::nullopt; } -static bool UpdateBannerAnimation([[maybe_unused]] const BannerElement& banner, const CoordsXYZ& loc, const int32_t baseZ) +static bool InvalidateBannerAnimation([[maybe_unused]] const BannerElement& banner, const CoordsXYZ& loc, const int32_t baseZ) { ViewportsInvalidate(loc.x, loc.y, baseZ, baseZ + 16, kMaxZoom); return true; } -static bool UpdateTileAnimations(const TileCoordsXY& coords) +template +static std::optional UpdateTileAnimations(const TileCoordsXY& coords) { auto tileElement = MapGetFirstElementAt(coords); if (tileElement == nullptr) { - return false; + return std::nullopt; } bool hasAnimations = false; + auto animationType = UpdateType ::invalidate; do { const auto baseZ = tileElement->GetBaseZ(); @@ -262,32 +274,67 @@ static bool UpdateTileAnimations(const TileCoordsXY& coords) switch (tileElement->GetType()) { case TileElementType::Entrance: - hasAnimations |= UpdateEntranceAnimation(*tileElement->AsEntrance(), loc, baseZ); + if constexpr (!noInvalidations) + { + hasAnimations |= InvalidateEntranceAnimation(*tileElement->AsEntrance(), loc, baseZ); + } break; case TileElementType::Path: - hasAnimations |= UpdatePathAnimation(*tileElement->AsPath(), loc, baseZ); + if constexpr (!noInvalidations) + { + hasAnimations |= InvalidatePathAnimation(*tileElement->AsPath(), loc, baseZ); + } break; case TileElementType::SmallScenery: - hasAnimations |= UpdateSmallSceneryAnimation(*tileElement->AsSmallScenery(), loc, baseZ); + { + const auto result = UpdateSmallSceneryAnimation(*tileElement->AsSmallScenery(), loc, baseZ); + if (result) + { + hasAnimations |= true; + if (result.value() == UpdateType::update) + { + animationType = UpdateType::update; + } + } break; + } case TileElementType::Track: - hasAnimations |= UpdateTrackAnimation(*tileElement->AsTrack(), loc, baseZ); + if constexpr (!noInvalidations) + { + hasAnimations |= InvalidateTrackAnimation(*tileElement->AsTrack(), loc, baseZ); + } break; case TileElementType::Banner: - hasAnimations |= UpdateBannerAnimation(*tileElement->AsBanner(), loc, baseZ); + if constexpr (!noInvalidations) + { + hasAnimations |= InvalidateBannerAnimation(*tileElement->AsBanner(), loc, baseZ); + } break; case TileElementType::LargeScenery: - hasAnimations |= UpdateLargeSceneryAnimation(*tileElement->AsLargeScenery(), loc, baseZ); + if constexpr (!noInvalidations) + { + hasAnimations |= InvalidateLargeSceneryAnimation(*tileElement->AsLargeScenery(), loc, baseZ); + } break; case TileElementType::Wall: - hasAnimations |= UpdateWallAnimation(*tileElement->AsWall(), loc, baseZ); + { + const auto result = UpdateWallAnimation(*tileElement->AsWall(), loc, baseZ); + if (result) + { + hasAnimations |= true; + if (result.value() == UpdateType::update) + { + animationType = UpdateType::update; + } + } break; + } default: break; } } while (!(tileElement++)->IsLastForTile()); - return hasAnimations; + return hasAnimations ? std::optional(animationType) : std::nullopt; } static bool UpdateOnRidePhotoAnimation(TrackElement& track, const CoordsXYZ& coords) @@ -375,12 +422,12 @@ static bool UpdateTemporaryAnimation(const TemporaryMapAnimation& animation) return isAnimating; } -static bool IsElementAnimated(const TileElementBase& element) +static std::optional IsElementAnimated(const TileElementBase& element) { switch (element.GetType()) { case TileElementType::Banner: - return true; + return std::optional(UpdateType::invalidate); case TileElementType::Wall: { const auto wall = element.AsWall(); @@ -389,11 +436,11 @@ static bool IsElementAnimated(const TileElementBase& element) { if ((entry->flags2 & WALL_SCENERY_2_ANIMATED) || entry->scrolling_mode != kScrollingModeNone) { - return true; + return std::optional(UpdateType::invalidate); } if (entry->flags & WALL_SCENERY_IS_DOOR && wall->IsAnimating()) { - return true; + return std::optional(UpdateType::update); } } break; @@ -404,7 +451,14 @@ static bool IsElementAnimated(const TileElementBase& element) const auto entry = scenery->GetEntry(); if (entry != nullptr && entry->HasFlag(SMALL_SCENERY_FLAG_ANIMATED)) { - return true; + if (entry->HasFlag(SMALL_SCENERY_FLAG_IS_CLOCK)) + { + return std::optional(UpdateType::update); + } + else + { + return std::optional(UpdateType::invalidate); + } } break; } @@ -414,7 +468,7 @@ static bool IsElementAnimated(const TileElementBase& element) const auto entry = scenery->GetEntry(); if (entry != nullptr && (entry->flags & LARGE_SCENERY_FLAG_ANIMATED)) { - return true; + return std::optional(UpdateType::invalidate); } break; } @@ -423,7 +477,7 @@ static bool IsElementAnimated(const TileElementBase& element) const auto path = element.AsPath(); if (path->HasQueueBanner()) { - return true; + return std::optional(UpdateType::invalidate); } break; } @@ -432,11 +486,11 @@ static bool IsElementAnimated(const TileElementBase& element) const auto entrance = element.AsEntrance(); if (entrance->GetEntranceType() == ENTRANCE_TYPE_PARK_ENTRANCE && entrance->GetSequenceIndex() == 0) { - return true; + return std::optional(UpdateType::invalidate); } else if (entrance->GetEntranceType() == ENTRANCE_TYPE_RIDE_ENTRANCE) { - return true; + return std::optional(UpdateType::invalidate); } break; } @@ -449,7 +503,7 @@ static bool IsElementAnimated(const TileElementBase& element) case TrackElemType::Rapids: case TrackElemType::Whirlpool: case TrackElemType::SpinningTunnel: - return true; + return std::optional(UpdateType::invalidate); default: break; } @@ -459,12 +513,17 @@ static bool IsElementAnimated(const TileElementBase& element) break; } - return false; + return std::nullopt; } -void MapAnimation::Create(const CoordsXY coords) +void MapAnimation::MarkTileForInvalidation(const CoordsXY coords) { - _mapAnimations.insert(TileCoordsXY(coords)); + _mapAnimations[TileCoordsXY(coords)]; +} + +void MapAnimation::MarkTileForUpdate(const CoordsXY coords) +{ + _mapAnimations[TileCoordsXY(coords)] = UpdateType::update; } void MapAnimation::CreateTemporary(const CoordsXYZ& coords, const TemporaryType type) @@ -478,36 +537,79 @@ void MapAnimation::MarkAllTiles() TileElementIteratorBegin(&it); while (TileElementIteratorNext(&it)) { - if (IsElementAnimated(*it.element)) + const auto isAnimated = IsElementAnimated(*it.element); + if (isAnimated) { - _mapAnimations.insert(TileCoordsXY(it.x, it.y)); + switch (*isAnimated) + { + case UpdateType::invalidate: + _mapAnimations[TileCoordsXY(it.x, it.y)]; + break; + case UpdateType::update: + _mapAnimations[TileCoordsXY(it.x, it.y)] = UpdateType::update; + break; + } } } } -template -static void UpdateAll(auto& mapAnimations) +static bool IsTileInView(const TileCoordsXY coords, const ViewportList& viewports) noexcept { - auto it = mapAnimations.begin(); - while (it != mapAnimations.end()) + for (const auto viewport : viewports) { - if (TUpdateFunction(*it)) + if (viewport->ContainsTile(coords)) { - ++it; - } - else - { - it = mapAnimations.erase(it); + return true; } } + return false; } void MapAnimation::UpdateAll() { PROFILED_FUNCTION(); - UpdateAll(_mapAnimations); - UpdateAll(_temporaryMapAnimations); + { + const auto viewports = GetVisibleViewports(); + + auto it = _mapAnimations.begin(); + while (it != _mapAnimations.end()) + { + const auto& tile = *it; + const bool tileInView = IsTileInView(tile.first, viewports); + if (!tileInView && tile.second == UpdateType::invalidate) + { + ++it; + continue; + } + + const auto result = tileInView ? UpdateTileAnimations(tile.first) : UpdateTileAnimations(tile.first); + if (result) + { + (*it).second = result.value(); + ++it; + } + else + { + it = _mapAnimations.erase(it); + } + } + } + + { + auto it = _temporaryMapAnimations.begin(); + while (it != _temporaryMapAnimations.end()) + { + if (UpdateTemporaryAnimation(*it)) + { + ++it; + } + else + { + it = _temporaryMapAnimations.erase(it); + } + } + } } void MapAnimation::ClearAll() @@ -521,10 +623,10 @@ void MapAnimation::ShiftAll(const CoordsXY amount) if (amount.x == 0 && amount.y == 0) return; - std::set newMapAnimations; + std::map newMapAnimations; for (const auto& a : _mapAnimations) { - newMapAnimations.insert(a + TileCoordsXY(amount)); + newMapAnimations[a.first + TileCoordsXY(amount)] = a.second; } _mapAnimations = std::move(newMapAnimations); diff --git a/src/openrct2/world/MapAnimation.h b/src/openrct2/world/MapAnimation.h index 8a96142018..532a009c94 100644 --- a/src/openrct2/world/MapAnimation.h +++ b/src/openrct2/world/MapAnimation.h @@ -19,7 +19,8 @@ namespace OpenRCT2::MapAnimation landEdgeDoor, }; - void Create(const CoordsXY coords); + void MarkTileForInvalidation(const CoordsXY coords); + void MarkTileForUpdate(const CoordsXY coords); void CreateTemporary(const CoordsXYZ& coords, const TemporaryType type); void MarkAllTiles(); void UpdateAll(); diff --git a/src/openrct2/world/TileInspector.cpp b/src/openrct2/world/TileInspector.cpp index 4afc32bec4..0e9226b63d 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); - MapAnimation::Create(tileLoc.ToCoordsXY()); + MapAnimation::MarkTileForUpdate(tileLoc.ToCoordsXY()); if (IsTileSelected(loc)) {