From c3234b1442a0c2b783bb471aa06c2bbbc624f549 Mon Sep 17 00:00:00 2001 From: Ted John Date: Sat, 3 Aug 2019 21:18:33 +0100 Subject: [PATCH] Refactor map animations and dynamically create on park load (#9705) --- src/openrct2/Context.cpp | 1 + .../actions/SmallSceneryPlaceAction.hpp | 2 +- src/openrct2/rct1/RCT1.h | 2 +- src/openrct2/rct1/S4Importer.cpp | 13 -- src/openrct2/rct12/RCT12.h | 9 + src/openrct2/rct2/S6Exporter.cpp | 22 +- src/openrct2/rct2/S6Exporter.h | 1 + src/openrct2/rct2/S6Importer.cpp | 9 +- src/openrct2/scenario/Scenario.h | 2 +- src/openrct2/world/Map.cpp | 2 +- src/openrct2/world/MapAnimation.cpp | 200 +++++++++++++----- src/openrct2/world/MapAnimation.h | 32 +-- 12 files changed, 192 insertions(+), 103 deletions(-) diff --git a/src/openrct2/Context.cpp b/src/openrct2/Context.cpp index 3390971050..fc568a09ee 100644 --- a/src/openrct2/Context.cpp +++ b/src/openrct2/Context.cpp @@ -546,6 +546,7 @@ namespace OpenRCT2 gCurrentLoadedPath = path; gFirstTimeSaving = true; game_fix_save_vars(); + AutoCreateMapAnimations(); sprite_position_tween_reset(); gScreenAge = 0; gLastAutoSaveUpdate = AUTOSAVE_PAUSE; diff --git a/src/openrct2/actions/SmallSceneryPlaceAction.hpp b/src/openrct2/actions/SmallSceneryPlaceAction.hpp index c616dd80e3..99f6462456 100644 --- a/src/openrct2/actions/SmallSceneryPlaceAction.hpp +++ b/src/openrct2/actions/SmallSceneryPlaceAction.hpp @@ -456,7 +456,7 @@ public: map_invalidate_tile_full(_loc.x, _loc.y); if (scenery_small_entry_has_flag(sceneryEntry, SMALL_SCENERY_FLAG_ANIMATED)) { - map_animation_create(2, _loc.x, _loc.y, sceneryElement->base_height); + map_animation_create(MAP_ANIMATION_TYPE_SMALL_SCENERY, _loc.x, _loc.y, sceneryElement->base_height); } return res; diff --git a/src/openrct2/rct1/RCT1.h b/src/openrct2/rct1/RCT1.h index d79c662de9..e6e5369320 100644 --- a/src/openrct2/rct1/RCT1.h +++ b/src/openrct2/rct1/RCT1.h @@ -675,7 +675,7 @@ struct rct1_s4 uint16_t view_y; uint8_t view_zoom; uint8_t view_rotation; - rct_map_animation map_animations[RCT1_MAX_ANIMATED_OBJECTS]; + RCT12MapAnimation map_animations[RCT1_MAX_ANIMATED_OBJECTS]; uint32_t num_map_animations; uint8_t unk_1CADBC[12]; uint16_t scrolling_text_step; diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index ded3972144..c3940367ae 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -189,7 +189,6 @@ public: ImportRideMeasurements(); ImportSprites(); ImportTileElements(); - ImportMapAnimations(); ImportPeepSpawns(); ImportFinance(); ImportResearch(); @@ -1800,18 +1799,6 @@ private: } } - void ImportMapAnimations() - { - // This is sketchy, ideally we should try to re-create them - rct_map_animation* s4Animations = _s4.map_animations; - for (size_t i = 0; i < RCT1_MAX_ANIMATED_OBJECTS; i++) - { - gAnimatedObjects[i] = s4Animations[i]; - gAnimatedObjects[i].baseZ /= 2; - } - gNumMapAnimations = _s4.num_map_animations; - } - void ImportFinance() { gParkEntranceFee = _s4.park_entrance_fee; diff --git a/src/openrct2/rct12/RCT12.h b/src/openrct2/rct12/RCT12.h index be0089df15..055c25e16d 100644 --- a/src/openrct2/rct12/RCT12.h +++ b/src/openrct2/rct12/RCT12.h @@ -540,6 +540,15 @@ struct RCT12Banner }; assert_struct_size(RCT12Banner, 8); +struct RCT12MapAnimation +{ + uint8_t baseZ; + uint8_t type; + uint16_t x; + uint16_t y; +}; +assert_struct_size(RCT12MapAnimation, 6); + #pragma pack(pop) bool is_user_string_id(rct_string_id stringId); diff --git a/src/openrct2/rct2/S6Exporter.cpp b/src/openrct2/rct2/S6Exporter.cpp index 4b8b3d267e..5d7554e24c 100644 --- a/src/openrct2/rct2/S6Exporter.cpp +++ b/src/openrct2/rct2/S6Exporter.cpp @@ -353,9 +353,8 @@ void S6Exporter::Export() _s6.saved_view_y = gSavedViewY; _s6.saved_view_zoom = gSavedViewZoom; _s6.saved_view_rotation = gSavedViewRotation; - std::memcpy(_s6.map_animations, gAnimatedObjects, sizeof(_s6.map_animations)); - _s6.num_map_animations = gNumMapAnimations; - // pad_0138B582 + + ExportMapAnimations(); _s6.ride_ratings_calc_data = gRideRatingsCalcData; ExportRideMeasurements(); @@ -1282,6 +1281,23 @@ void S6Exporter::ExportBanner(RCT12Banner& dst, const Banner& src) } } +void S6Exporter::ExportMapAnimations() +{ + const auto& mapAnimations = GetMapAnimations(); + auto numAnimations = std::min(mapAnimations.size(), std::size(_s6.map_animations)); + _s6.num_map_animations = (uint16_t)numAnimations; + for (size_t i = 0; i < numAnimations; i++) + { + const auto& src = mapAnimations[i]; + auto& dst = _s6.map_animations[i]; + + dst.type = src.type; + dst.x = src.location.x; + dst.y = src.location.y; + dst.baseZ = src.location.z; + } +} + opt::optional S6Exporter::AllocateUserString(const std::string_view& value) { auto nextId = _userStrings.size(); diff --git a/src/openrct2/rct2/S6Exporter.h b/src/openrct2/rct2/S6Exporter.h index 91f0d4f408..5aeb8497f9 100644 --- a/src/openrct2/rct2/S6Exporter.h +++ b/src/openrct2/rct2/S6Exporter.h @@ -66,6 +66,7 @@ private: void ExportRideMeasurement(RCT12RideMeasurement& dst, const RideMeasurement& src); void ExportBanners(); void ExportBanner(RCT12Banner& dst, const Banner& src); + void ExportMapAnimations(); opt::optional AllocateUserString(const std::string_view& value); void ExportUserStrings(); diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 214fe902b9..c413877193 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -382,13 +382,6 @@ public: gSavedViewZoom = _s6.saved_view_zoom; gSavedViewRotation = _s6.saved_view_rotation; - for (size_t i = 0; i < RCT2_MAX_ANIMATED_OBJECTS; i++) - { - gAnimatedObjects[i] = _s6.map_animations[i]; - } - gNumMapAnimations = _s6.num_map_animations; - // pad_0138B582 - gRideRatingsCalcData = _s6.ride_ratings_calc_data; ImportRideMeasurements(); gNextGuestNumber = _s6.next_guest_index; @@ -1545,6 +1538,7 @@ void load_from_sv6(const char* path) objectMgr.LoadObjects(result.RequiredObjects.data(), result.RequiredObjects.size()); s6Importer->Import(); game_fix_save_vars(); + AutoCreateMapAnimations(); sprite_position_tween_reset(); gScreenAge = 0; gLastAutoSaveUpdate = AUTOSAVE_PAUSE; @@ -1583,6 +1577,7 @@ void load_from_sc6(const char* path) objManager.LoadObjects(result.RequiredObjects.data(), result.RequiredObjects.size()); s6Importer->Import(); game_fix_save_vars(); + AutoCreateMapAnimations(); sprite_position_tween_reset(); return; } diff --git a/src/openrct2/scenario/Scenario.h b/src/openrct2/scenario/Scenario.h index ae37559f92..1e6d1ccc37 100644 --- a/src/openrct2/scenario/Scenario.h +++ b/src/openrct2/scenario/Scenario.h @@ -260,7 +260,7 @@ struct rct_s6_data uint16_t saved_view_y; uint8_t saved_view_zoom; uint8_t saved_view_rotation; - rct_map_animation map_animations[RCT2_MAX_ANIMATED_OBJECTS]; + RCT12MapAnimation map_animations[RCT2_MAX_ANIMATED_OBJECTS]; uint16_t num_map_animations; uint8_t pad_0138B582[2]; rct_ride_rating_calc_data ride_ratings_calc_data; diff --git a/src/openrct2/world/Map.cpp b/src/openrct2/world/Map.cpp index a374df9669..082765883c 100644 --- a/src/openrct2/world/Map.cpp +++ b/src/openrct2/world/Map.cpp @@ -333,7 +333,6 @@ BannerElement* map_get_banner_element_at(int32_t x, int32_t y, int32_t z, uint8_ */ void map_init(int32_t size) { - gNumMapAnimations = 0; gNextFreeTileElementPointerIndex = 0; for (int32_t i = 0; i < MAX_TILE_TILE_ELEMENT_POINTERS; i++) @@ -362,6 +361,7 @@ void map_init(int32_t size) gMapBaseZ = 7; map_update_tile_pointers(); map_remove_out_of_range_elements(); + AutoCreateMapAnimations(); auto intent = Intent(INTENT_ACTION_MAP); context_broadcast_intent(&intent); diff --git a/src/openrct2/world/MapAnimation.cpp b/src/openrct2/world/MapAnimation.cpp index 9269f37da9..aea7adfb70 100644 --- a/src/openrct2/world/MapAnimation.cpp +++ b/src/openrct2/world/MapAnimation.cpp @@ -25,53 +25,42 @@ #include "SmallScenery.h" #include "Sprite.h" -#include - using map_animation_invalidate_event_handler = bool (*)(int32_t x, int32_t y, int32_t baseZ); -static bool map_animation_invalidate(rct_map_animation* obj); +static std::vector _mapAnimations; -uint16_t gNumMapAnimations; -rct_map_animation gAnimatedObjects[MAX_ANIMATED_OBJECTS]; +constexpr size_t MAX_ANIMATED_OBJECTS = 2000; + +static bool InvalidateMapAnimation(const MapAnimation& obj); + +static bool DoesAnimationExist(int32_t type, const CoordsXYZ& location) +{ + for (const auto& a : _mapAnimations) + { + if (a.type == type && a.location.x == location.x && a.location.y == location.y && a.location.z == location.z) + { + // Animation already exists + return true; + } + } + return false; +} -/** - * - * rct2: 0x0068AF67 - * - * @param type (dh) - * @param x (ax) - * @param y (cx) - * @param z (dl) - */ void map_animation_create(int32_t type, int32_t x, int32_t y, int32_t z) { - rct_map_animation* aobj = &gAnimatedObjects[0]; - int32_t numAnimatedObjects = gNumMapAnimations; - if (numAnimatedObjects >= MAX_ANIMATED_OBJECTS) + auto location = CoordsXYZ{ x, y, z }; + if (!DoesAnimationExist(type, location)) { - log_error("Exceeded the maximum number of animations"); - return; + if (_mapAnimations.size() < MAX_ANIMATED_OBJECTS) + { + // Create new animation + _mapAnimations.push_back({ (uint8_t)type, location }); + } + else + { + log_error("Exceeded the maximum number of animations"); + } } - for (int32_t i = 0; i < numAnimatedObjects; i++, aobj++) - { - if (aobj->x != x) - continue; - if (aobj->y != y) - continue; - if (aobj->baseZ != z) - continue; - if (aobj->type != type) - continue; - // Animation already exists - return; - } - - // Create new animation - gNumMapAnimations++; - aobj->type = type; - aobj->x = x; - aobj->y = y; - aobj->baseZ = z; } /** @@ -80,22 +69,17 @@ void map_animation_create(int32_t type, int32_t x, int32_t y, int32_t z) */ void map_animation_invalidate_all() { - rct_map_animation* aobj = &gAnimatedObjects[0]; - int32_t numAnimatedObjects = gNumMapAnimations; - while (numAnimatedObjects > 0) + auto it = _mapAnimations.begin(); + while (it != _mapAnimations.end()) { - if (map_animation_invalidate(aobj)) + if (InvalidateMapAnimation(*it)) { - // Remove animated object - gNumMapAnimations--; - numAnimatedObjects--; - if (numAnimatedObjects > 0) - memmove(aobj, aobj + 1, numAnimatedObjects * sizeof(rct_map_animation)); + // Map animation has finished, remove it + it = _mapAnimations.erase(it); } else { - numAnimatedObjects--; - aobj++; + it++; } } } @@ -598,9 +582,117 @@ static constexpr const map_animation_invalidate_event_handler _animatedObjectEve /** * @returns true if the animation should be removed. */ -static bool map_animation_invalidate(rct_map_animation* obj) +static bool InvalidateMapAnimation(const MapAnimation& a) { - assert(obj->type < MAP_ANIMATION_TYPE_COUNT); - - return _animatedObjectEventHandlers[obj->type](obj->x, obj->y, obj->baseZ); + if (a.type < std::size(_animatedObjectEventHandlers)) + { + return _animatedObjectEventHandlers[a.type](a.location.x, a.location.y, a.location.z); + } + return true; +} + +const std::vector& GetMapAnimations() +{ + return _mapAnimations; +} + +static void ClearMapAnimations() +{ + _mapAnimations.clear(); +} + +void AutoCreateMapAnimations() +{ + ClearMapAnimations(); + + tile_element_iterator it; + tile_element_iterator_begin(&it); + while (tile_element_iterator_next(&it)) + { + auto el = it.element; + auto loc = CoordsXYZ{ it.x * 32, it.y * 32, el->base_height }; + switch (el->GetType()) + { + case TILE_ELEMENT_TYPE_BANNER: + map_animation_create(MAP_ANIMATION_TYPE_BANNER, loc.x, loc.y, loc.z); + break; + case TILE_ELEMENT_TYPE_WALL: + { + auto wallEl = el->AsWall(); + auto entry = wallEl->GetEntry(); + if (entry != nullptr + && ((entry->wall.flags2 & WALL_SCENERY_2_ANIMATED) || entry->wall.scrolling_mode != SCROLLING_MODE_NONE)) + { + map_animation_create(MAP_ANIMATION_TYPE_WALL, loc.x, loc.y, loc.z); + } + break; + } + case TILE_ELEMENT_TYPE_SMALL_SCENERY: + { + auto sceneryEl = el->AsSmallScenery(); + auto entry = sceneryEl->GetEntry(); + if (entry != nullptr && scenery_small_entry_has_flag(entry, SMALL_SCENERY_FLAG_ANIMATED)) + { + map_animation_create(MAP_ANIMATION_TYPE_SMALL_SCENERY, loc.x, loc.y, loc.z); + } + break; + } + case TILE_ELEMENT_TYPE_LARGE_SCENERY: + { + auto sceneryEl = el->AsLargeScenery(); + auto entry = sceneryEl->GetEntry(); + if (entry != nullptr && (entry->large_scenery.flags & LARGE_SCENERY_FLAG_ANIMATED)) + { + map_animation_create(MAP_ANIMATION_TYPE_LARGE_SCENERY, loc.x, loc.y, loc.z); + } + break; + } + case TILE_ELEMENT_TYPE_PATH: + { + auto path = el->AsPath(); + if (path->HasQueueBanner()) + { + map_animation_create(MAP_ANIMATION_TYPE_QUEUE_BANNER, loc.x, loc.y, loc.z); + } + break; + } + case TILE_ELEMENT_TYPE_ENTRANCE: + { + auto entrance = el->AsEntrance(); + switch (entrance->GetEntranceType()) + { + case ENTRANCE_TYPE_PARK_ENTRANCE: + if (entrance->GetSequenceIndex() == 0) + { + map_animation_create(MAP_ANIMATION_TYPE_PARK_ENTRANCE, loc.x, loc.y, loc.z); + } + break; + case ENTRANCE_TYPE_RIDE_ENTRANCE: + map_animation_create(MAP_ANIMATION_TYPE_RIDE_ENTRANCE, loc.x, loc.y, loc.z); + break; + } + break; + } + case TILE_ELEMENT_TYPE_TRACK: + { + auto track = el->AsTrack(); + switch (track->GetTrackType()) + { + case TRACK_ELEM_WATERFALL: + map_animation_create(MAP_ANIMATION_TYPE_TRACK_WATERFALL, loc.x, loc.y, loc.z); + break; + case TRACK_ELEM_RAPIDS: + map_animation_create(MAP_ANIMATION_TYPE_TRACK_RAPIDS, loc.x, loc.y, loc.z); + break; + case TRACK_ELEM_WHIRLPOOL: + map_animation_create(MAP_ANIMATION_TYPE_TRACK_WHIRLPOOL, loc.x, loc.y, loc.z); + break; + case TRACK_ELEM_SPINNING_TUNNEL: + map_animation_create(MAP_ANIMATION_TYPE_TRACK_SPINNINGTUNNEL, loc.x, loc.y, loc.z); + break; + } + break; + } + } + } } diff --git a/src/openrct2/world/MapAnimation.h b/src/openrct2/world/MapAnimation.h index 496c6b7fc5..6f8c9d64dc 100644 --- a/src/openrct2/world/MapAnimation.h +++ b/src/openrct2/world/MapAnimation.h @@ -7,25 +7,18 @@ * OpenRCT2 is licensed under the GNU General Public License version 3. *****************************************************************************/ -#ifndef _MAP_ANIMATION_H_ -#define _MAP_ANIMATION_H_ +#pragma once -#include "../common.h" +#include "Location.hpp" -#pragma pack(push, 1) -/** - * Animated object - * size: 0x06 - */ -struct rct_map_animation +#include +#include + +struct MapAnimation { - uint8_t baseZ; - uint8_t type; - uint16_t x; - uint16_t y; + uint8_t type{}; + CoordsXYZ location{}; }; -assert_struct_size(rct_map_animation, 6); -#pragma pack(pop) enum { @@ -46,12 +39,7 @@ enum MAP_ANIMATION_TYPE_COUNT }; -#define MAX_ANIMATED_OBJECTS 2000 - -extern uint16_t gNumMapAnimations; -extern rct_map_animation gAnimatedObjects[MAX_ANIMATED_OBJECTS]; - void map_animation_create(int32_t type, int32_t x, int32_t y, int32_t z); void map_animation_invalidate_all(); - -#endif +const std::vector& GetMapAnimations(); +void AutoCreateMapAnimations();