Refactor map animations and dynamically create on park load (#9705)

This commit is contained in:
Ted John 2019-08-03 21:18:33 +01:00 committed by Michael Steenbeek
parent 1a6e5b5548
commit c3234b1442
12 changed files with 192 additions and 103 deletions

View File

@ -546,6 +546,7 @@ namespace OpenRCT2
gCurrentLoadedPath = path;
gFirstTimeSaving = true;
game_fix_save_vars();
AutoCreateMapAnimations();
sprite_position_tween_reset();
gScreenAge = 0;
gLastAutoSaveUpdate = AUTOSAVE_PAUSE;

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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<uint16_t> S6Exporter::AllocateUserString(const std::string_view& value)
{
auto nextId = _userStrings.size();

View File

@ -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<uint16_t> AllocateUserString(const std::string_view& value);
void ExportUserStrings();

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -25,53 +25,42 @@
#include "SmallScenery.h"
#include "Sprite.h"
#include <cstring>
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<MapAnimation> _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<MapAnimation>& 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;
}
}
}
}

View File

@ -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 <cstdint>
#include <vector>
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<MapAnimation>& GetMapAnimations();
void AutoCreateMapAnimations();