mirror of https://github.com/OpenRCT2/OpenRCT2.git
Remove next_in_quadrant (#13754)
* Use std::vector of quadrants * Prevent ptr invalidation issues * Remove next_in_quadrant * Make review changes * Rebuild next_in_quadrant for S6Export * Fix formatting * Constexpr where possible * Increment network version and update replays
This commit is contained in:
parent
3cc283adb2
commit
eb52391b9a
|
@ -44,8 +44,8 @@ set(TITLE_SEQUENCE_SHA1 "304d13a126c15bf2c86ff13b81a2f2cc1856ac8d")
|
|||
set(OBJECTS_URL "https://github.com/OpenRCT2/objects/releases/download/v1.0.20/objects.zip")
|
||||
set(OBJECTS_SHA1 "151424d24b1d49a167932b58319bedaa6ec368e9")
|
||||
|
||||
set(REPLAYS_URL "https://github.com/OpenRCT2/replays/releases/download/v0.0.29/replays.zip")
|
||||
set(REPLAYS_SHA1 "B64135F28A79758AD9FCB611625ADDB5C89FB40B")
|
||||
set(REPLAYS_URL "https://github.com/OpenRCT2/replays/releases/download/v0.0.30/replays.zip")
|
||||
set(REPLAYS_SHA1 "FD0949B81A7A267EA3A8F96CB02C460C8C21B923")
|
||||
|
||||
option(FORCE32 "Force 32-bit build. It will add `-m32` to compiler flags.")
|
||||
option(WITH_TESTS "Build tests")
|
||||
|
|
|
@ -48,8 +48,8 @@
|
|||
<TitleSequencesSha1>304d13a126c15bf2c86ff13b81a2f2cc1856ac8d</TitleSequencesSha1>
|
||||
<ObjectsUrl>https://github.com/OpenRCT2/objects/releases/download/v1.0.20/objects.zip</ObjectsUrl>
|
||||
<ObjectsSha1>151424d24b1d49a167932b58319bedaa6ec368e9</ObjectsSha1>
|
||||
<ReplaysUrl>https://github.com/OpenRCT2/replays/releases/download/v0.0.29/replays.zip</ReplaysUrl>
|
||||
<ReplaysSha1>B64135F28A79758AD9FCB611625ADDB5C89FB40B</ReplaysSha1>
|
||||
<ReplaysUrl>https://github.com/OpenRCT2/replays/releases/download/v0.0.30/replays.zip</ReplaysUrl>
|
||||
<ReplaysSha1>FD0949B81A7A267EA3A8F96CB02C460C8C21B923</ReplaysSha1>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -200,7 +200,6 @@ struct GameStateSnapshots final : public IGameStateSnapshots
|
|||
const SpriteBase& spriteBase, const SpriteBase& spriteCmp, GameStateSpriteChange_t& changeData) const
|
||||
{
|
||||
COMPARE_FIELD(SpriteBase, sprite_identifier);
|
||||
COMPARE_FIELD(SpriteBase, next_in_quadrant);
|
||||
COMPARE_FIELD(SpriteBase, linked_list_index);
|
||||
COMPARE_FIELD(SpriteBase, sprite_index);
|
||||
COMPARE_FIELD(SpriteBase, flags);
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
// This string specifies which version of network stream current build uses.
|
||||
// It is used for making sure only compatible builds get connected, even within
|
||||
// single OpenRCT2 version.
|
||||
#define NETWORK_STREAM_VERSION "14"
|
||||
#define NETWORK_STREAM_VERSION "15"
|
||||
#define NETWORK_STREAM_ID OPENRCT2_VERSION "-" NETWORK_STREAM_VERSION
|
||||
|
||||
static Peep* _pickup_peep = nullptr;
|
||||
|
|
|
@ -981,6 +981,27 @@ void S6Exporter::RebuildEntityLinks()
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuild next_in_quadrant linked list entity indexs
|
||||
for (auto x = 0; x < 255; ++x)
|
||||
{
|
||||
for (auto y = 0; y < 255; ++y)
|
||||
{
|
||||
uint16_t previous = SPRITE_INDEX_NULL;
|
||||
for (auto* entity : EntityTileList(TileCoordsXY{ x, y }.ToCoordsXY()))
|
||||
{
|
||||
if (previous != SPRITE_INDEX_NULL)
|
||||
{
|
||||
_s6.sprites[previous].unknown.next_in_quadrant = entity->sprite_index;
|
||||
}
|
||||
previous = entity->sprite_index;
|
||||
}
|
||||
if (previous != SPRITE_INDEX_NULL)
|
||||
{
|
||||
_s6.sprites[previous].unknown.next_in_quadrant = SPRITE_INDEX_NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void S6Exporter::ExportSprite(RCT2Sprite* dst, const rct_sprite* src)
|
||||
|
@ -1046,8 +1067,8 @@ constexpr RCT12EntityLinkListOffset GetRCT2LinkListOffset(const SpriteBase* src)
|
|||
void S6Exporter::ExportSpriteCommonProperties(RCT12SpriteBase* dst, const SpriteBase* src)
|
||||
{
|
||||
dst->sprite_identifier = src->sprite_identifier;
|
||||
dst->next_in_quadrant = src->next_in_quadrant;
|
||||
dst->linked_list_type_offset = GetRCT2LinkListOffset(src);
|
||||
dst->next_in_quadrant = SPRITE_INDEX_NULL;
|
||||
dst->sprite_height_negative = src->sprite_height_negative;
|
||||
dst->sprite_index = src->sprite_index;
|
||||
dst->flags = src->flags;
|
||||
|
|
|
@ -1656,7 +1656,6 @@ public:
|
|||
void ImportSpriteCommonProperties(SpriteBase* dst, const RCT12SpriteBase* src)
|
||||
{
|
||||
dst->sprite_identifier = src->sprite_identifier;
|
||||
dst->next_in_quadrant = src->next_in_quadrant;
|
||||
dst->linked_list_index = static_cast<EntityListId>(EnumValue(src->linked_list_type_offset) >> 1);
|
||||
dst->sprite_height_negative = src->sprite_height_negative;
|
||||
dst->sprite_index = src->sprite_index;
|
||||
|
|
|
@ -397,16 +397,20 @@ CoordsXY footpath_bridge_get_info_from_pos(const ScreenCoordsXY& screenCoords, i
|
|||
*/
|
||||
void footpath_remove_litter(const CoordsXYZ& footpathPos)
|
||||
{
|
||||
auto quad = EntityTileList<Litter>(footpathPos);
|
||||
for (auto litter : quad)
|
||||
std::vector<Litter*> removals;
|
||||
for (auto litter : EntityTileList<Litter>(footpathPos))
|
||||
{
|
||||
int32_t distanceZ = abs(litter->z - footpathPos.z);
|
||||
if (distanceZ <= 32)
|
||||
{
|
||||
litter->Invalidate();
|
||||
sprite_remove(litter);
|
||||
removals.push_back(litter);
|
||||
}
|
||||
}
|
||||
for (auto* litter : removals)
|
||||
{
|
||||
litter->Invalidate();
|
||||
sprite_remove(litter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -24,13 +24,14 @@
|
|||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <iterator>
|
||||
#include <vector>
|
||||
|
||||
static rct_sprite _spriteList[MAX_SPRITES];
|
||||
static std::array<std::list<uint16_t>, EnumValue(EntityListId::Count)> gEntityLists;
|
||||
|
||||
static bool _spriteFlashingList[MAX_SPRITES];
|
||||
|
||||
uint16_t gSpriteSpatialIndex[SPATIAL_INDEX_SIZE];
|
||||
static std::array<std::vector<uint16_t>, SPATIAL_INDEX_SIZE> gSpriteSpatialIndex;
|
||||
|
||||
const rct_string_id litterNames[12] = { STR_LITTER_VOMIT,
|
||||
STR_LITTER_VOMIT,
|
||||
|
@ -45,7 +46,25 @@ const rct_string_id litterNames[12] = { STR_LITTER_VOMIT,
|
|||
STR_SHOP_ITEM_SINGULAR_EMPTY_JUICE_CUP,
|
||||
STR_SHOP_ITEM_SINGULAR_EMPTY_BOWL_BLUE };
|
||||
|
||||
static size_t GetSpatialIndexOffset(int32_t x, int32_t y);
|
||||
constexpr size_t GetSpatialIndexOffset(int32_t x, int32_t y)
|
||||
{
|
||||
size_t index = SPATIAL_INDEX_LOCATION_NULL;
|
||||
if (x != LOCATION_NULL)
|
||||
{
|
||||
x = std::clamp(x, 0, 0xFFFF);
|
||||
y = std::clamp(y, 0, 0xFFFF);
|
||||
|
||||
int16_t flooredX = floor2(x, 32);
|
||||
uint8_t tileY = y >> 5;
|
||||
index = (flooredX << 3) | tileY;
|
||||
}
|
||||
|
||||
if (index >= sizeof(gSpriteSpatialIndex))
|
||||
{
|
||||
return SPATIAL_INDEX_LOCATION_NULL;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
// Required for GetEntity to return a default
|
||||
template<> bool SpriteBase::Is<SpriteBase>() const
|
||||
|
@ -116,7 +135,7 @@ SpriteBase* get_sprite(size_t spriteIndex)
|
|||
return try_get_sprite(spriteIndex);
|
||||
}
|
||||
|
||||
uint16_t sprite_get_first_in_quadrant(const CoordsXY& spritePos)
|
||||
const std::vector<uint16_t>& GetEntityTileList(const CoordsXY& spritePos)
|
||||
{
|
||||
return gSpriteSpatialIndex[GetSpatialIndexOffset(spritePos.x, spritePos.y)];
|
||||
}
|
||||
|
@ -242,6 +261,8 @@ void reset_sprite_list()
|
|||
reset_sprite_spatial_index();
|
||||
}
|
||||
|
||||
static void SpriteSpatialInsert(SpriteBase* sprite, const CoordsXY& newLoc);
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x0069EBE4
|
||||
|
@ -250,40 +271,20 @@ void reset_sprite_list()
|
|||
*/
|
||||
void reset_sprite_spatial_index()
|
||||
{
|
||||
std::fill_n(gSpriteSpatialIndex, std::size(gSpriteSpatialIndex), SPRITE_INDEX_NULL);
|
||||
for (auto& vec : gSpriteSpatialIndex)
|
||||
{
|
||||
vec.clear();
|
||||
}
|
||||
for (size_t i = 0; i < MAX_SPRITES; i++)
|
||||
{
|
||||
auto* spr = GetEntity(i);
|
||||
if (spr != nullptr && spr->sprite_identifier != SpriteIdentifier::Null)
|
||||
{
|
||||
size_t index = GetSpatialIndexOffset(spr->x, spr->y);
|
||||
uint32_t nextSpriteId = gSpriteSpatialIndex[index];
|
||||
gSpriteSpatialIndex[index] = spr->sprite_index;
|
||||
spr->next_in_quadrant = nextSpriteId;
|
||||
SpriteSpatialInsert(spr, { spr->x, spr->y });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static size_t GetSpatialIndexOffset(int32_t x, int32_t y)
|
||||
{
|
||||
size_t index = SPATIAL_INDEX_LOCATION_NULL;
|
||||
if (x != LOCATION_NULL)
|
||||
{
|
||||
x = std::clamp(x, 0, 0xFFFF);
|
||||
y = std::clamp(y, 0, 0xFFFF);
|
||||
|
||||
int16_t flooredX = floor2(x, 32);
|
||||
uint8_t tileY = y >> 5;
|
||||
index = (flooredX << 3) | tileY;
|
||||
}
|
||||
|
||||
if (index >= sizeof(gSpriteSpatialIndex))
|
||||
{
|
||||
return SPATIAL_INDEX_LOCATION_NULL;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
|
||||
rct_sprite_checksum sprite_checksum()
|
||||
|
@ -318,15 +319,6 @@ rct_sprite_checksum sprite_checksum()
|
|||
copy.misc.sprite_left = copy.misc.sprite_right = copy.misc.sprite_top = copy.misc.sprite_bottom = 0;
|
||||
copy.misc.sprite_width = copy.misc.sprite_height_negative = copy.misc.sprite_height_positive = 0;
|
||||
|
||||
// Next in quadrant might be a misc sprite, set first non-misc sprite in quadrant.
|
||||
while (auto* nextSprite = GetEntity(copy.misc.next_in_quadrant))
|
||||
{
|
||||
if (nextSprite->sprite_identifier == SpriteIdentifier::Misc)
|
||||
copy.misc.next_in_quadrant = nextSprite->next_in_quadrant;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
if (copy.misc.Is<Peep>())
|
||||
{
|
||||
// Name is pointer and will not be the same across clients
|
||||
|
@ -365,14 +357,12 @@ static void sprite_reset(SpriteBase* sprite)
|
|||
{
|
||||
// Need to retain how the sprite is linked in lists
|
||||
auto llto = sprite->linked_list_index;
|
||||
uint16_t next_in_quadrant = sprite->next_in_quadrant;
|
||||
uint16_t sprite_index = sprite->sprite_index;
|
||||
_spriteFlashingList[sprite_index] = false;
|
||||
|
||||
std::memset(sprite, 0, sizeof(rct_sprite));
|
||||
|
||||
sprite->linked_list_index = llto;
|
||||
sprite->next_in_quadrant = next_in_quadrant;
|
||||
sprite->sprite_index = sprite_index;
|
||||
sprite->sprite_identifier = SpriteIdentifier::Null;
|
||||
}
|
||||
|
@ -388,19 +378,10 @@ void sprite_clear_all_unused()
|
|||
sprite_reset(sprite);
|
||||
sprite->linked_list_index = EntityListId::Free;
|
||||
|
||||
// sprite->next_in_quadrant will only end up as zero owing to corruption
|
||||
// most likely due to previous builds not preserving it when resetting sprites
|
||||
// We reset it to SPRITE_INDEX_NULL to prevent cycles in the sprite lists
|
||||
if (sprite->next_in_quadrant == 0)
|
||||
{
|
||||
sprite->next_in_quadrant = SPRITE_INDEX_NULL;
|
||||
}
|
||||
_spriteFlashingList[sprite->sprite_index] = false;
|
||||
}
|
||||
}
|
||||
|
||||
static void SpriteSpatialInsert(SpriteBase* sprite, const CoordsXY& newLoc);
|
||||
|
||||
static constexpr uint16_t MAX_MISC_SPRITES = 300;
|
||||
static void AddToEntityList(const EntityListId linkedListIndex, SpriteBase* entity)
|
||||
{
|
||||
|
@ -605,41 +586,25 @@ void sprite_misc_update_all()
|
|||
static void SpriteSpatialInsert(SpriteBase* sprite, const CoordsXY& newLoc)
|
||||
{
|
||||
size_t newIndex = GetSpatialIndexOffset(newLoc.x, newLoc.y);
|
||||
|
||||
auto* next = &gSpriteSpatialIndex[newIndex];
|
||||
while (sprite->sprite_index < *next && *next != SPRITE_INDEX_NULL)
|
||||
{
|
||||
auto sprite2 = GetEntity(*next);
|
||||
next = &sprite2->next_in_quadrant;
|
||||
}
|
||||
|
||||
sprite->next_in_quadrant = *next;
|
||||
*next = sprite->sprite_index;
|
||||
auto& spatialVector = gSpriteSpatialIndex[newIndex];
|
||||
auto index = std::lower_bound(std::begin(spatialVector), std::end(spatialVector), sprite->sprite_index);
|
||||
spatialVector.insert(index, sprite->sprite_index);
|
||||
}
|
||||
|
||||
static void SpriteSpatialRemove(SpriteBase* sprite)
|
||||
{
|
||||
size_t currentIndex = GetSpatialIndexOffset(sprite->x, sprite->y);
|
||||
auto* index = &gSpriteSpatialIndex[currentIndex];
|
||||
|
||||
// This indicates that the spatial index data is incorrect.
|
||||
if (*index == SPRITE_INDEX_NULL)
|
||||
auto& spatialVector = gSpriteSpatialIndex[currentIndex];
|
||||
auto index = std::lower_bound(std::begin(spatialVector), std::end(spatialVector), sprite->sprite_index);
|
||||
if (index != std::end(spatialVector) && *index == sprite->sprite_index)
|
||||
{
|
||||
spatialVector.erase(index, index + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
log_warning("Bad sprite spatial index. Rebuilding the spatial index...");
|
||||
reset_sprite_spatial_index();
|
||||
}
|
||||
|
||||
auto* sprite2 = GetEntity(*index);
|
||||
while (sprite != sprite2)
|
||||
{
|
||||
index = &sprite2->next_in_quadrant;
|
||||
if (*index == SPRITE_INDEX_NULL)
|
||||
{
|
||||
break;
|
||||
}
|
||||
sprite2 = GetEntity(*index);
|
||||
}
|
||||
*index = sprite->next_in_quadrant;
|
||||
}
|
||||
|
||||
static void SpriteSpatialMove(SpriteBase* sprite, const CoordsXY& newLoc)
|
||||
|
@ -807,17 +772,22 @@ void litter_create(const CoordsXYZD& litterPos, LitterType type)
|
|||
*/
|
||||
void litter_remove_at(const CoordsXYZ& litterPos)
|
||||
{
|
||||
std::vector<Litter*> removals;
|
||||
for (auto litter : EntityTileList<Litter>(litterPos))
|
||||
{
|
||||
if (abs(litter->z - litterPos.z) <= 16)
|
||||
{
|
||||
if (abs(litter->x - litterPos.x) <= 8 && abs(litter->y - litterPos.y) <= 8)
|
||||
{
|
||||
litter->Invalidate();
|
||||
sprite_remove(litter);
|
||||
removals.push_back(litter);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto* litter : removals)
|
||||
{
|
||||
litter->Invalidate();
|
||||
sprite_remove(litter);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -231,7 +231,6 @@ uint16_t GetEntityListCount(EntityListId list);
|
|||
|
||||
constexpr const uint32_t SPATIAL_INDEX_SIZE = (MAXIMUM_MAP_SIZE_TECHNICAL * MAXIMUM_MAP_SIZE_TECHNICAL) + 1;
|
||||
constexpr const uint32_t SPATIAL_INDEX_LOCATION_NULL = SPATIAL_INDEX_SIZE - 1;
|
||||
extern uint16_t gSpriteSpatialIndex[SPATIAL_INDEX_SIZE];
|
||||
|
||||
extern const rct_string_id litterNames[12];
|
||||
|
||||
|
@ -249,7 +248,7 @@ void litter_remove_at(const CoordsXYZ& litterPos);
|
|||
uint16_t remove_floating_sprites();
|
||||
void sprite_misc_explosion_cloud_create(const CoordsXYZ& cloudPos);
|
||||
void sprite_misc_explosion_flare_create(const CoordsXYZ& flarePos);
|
||||
uint16_t sprite_get_first_in_quadrant(const CoordsXY& spritePos);
|
||||
const std::vector<uint16_t>& GetEntityTileList(const CoordsXY& spritePos);
|
||||
|
||||
///////////////////////////////////////////////////////////////
|
||||
// Balloon
|
||||
|
@ -333,25 +332,75 @@ public:
|
|||
using iterator_category = std::forward_iterator_tag;
|
||||
};
|
||||
|
||||
template<typename T> class EntityTileIterator
|
||||
{
|
||||
private:
|
||||
std::vector<uint16_t>::const_iterator iter;
|
||||
std::vector<uint16_t>::const_iterator end;
|
||||
T* Entity = nullptr;
|
||||
|
||||
public:
|
||||
EntityTileIterator(std::vector<uint16_t>::const_iterator _iter, std::vector<uint16_t>::const_iterator _end)
|
||||
: iter(_iter)
|
||||
, end(_end)
|
||||
{
|
||||
++(*this);
|
||||
}
|
||||
EntityTileIterator& operator++()
|
||||
{
|
||||
Entity = nullptr;
|
||||
|
||||
while (iter != end && Entity == nullptr)
|
||||
{
|
||||
Entity = GetEntity<T>(*iter++);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
EntityTileIterator operator++(int)
|
||||
{
|
||||
EntityTileIterator retval = *this;
|
||||
++(*this);
|
||||
return *iter;
|
||||
}
|
||||
bool operator==(EntityTileIterator other) const
|
||||
{
|
||||
return Entity == other.Entity;
|
||||
}
|
||||
bool operator!=(EntityTileIterator other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
T* operator*()
|
||||
{
|
||||
return Entity;
|
||||
}
|
||||
// iterator traits
|
||||
using difference_type = std::ptrdiff_t;
|
||||
using value_type = T;
|
||||
using pointer = const T*;
|
||||
using reference = const T&;
|
||||
using iterator_category = std::forward_iterator_tag;
|
||||
};
|
||||
|
||||
template<typename T = SpriteBase> class EntityTileList
|
||||
{
|
||||
private:
|
||||
uint16_t FirstEntity = SPRITE_INDEX_NULL;
|
||||
using EntityTileIterator = EntityIterator<T, &SpriteBase::next_in_quadrant>;
|
||||
const std::vector<uint16_t>& vec;
|
||||
|
||||
public:
|
||||
EntityTileList(const CoordsXY& loc)
|
||||
: FirstEntity(sprite_get_first_in_quadrant(loc))
|
||||
: vec(GetEntityTileList(loc))
|
||||
{
|
||||
}
|
||||
|
||||
EntityTileIterator begin()
|
||||
EntityTileIterator<T> begin()
|
||||
{
|
||||
return EntityTileIterator(FirstEntity);
|
||||
return EntityTileIterator<T>(std::begin(vec), std::end(vec));
|
||||
}
|
||||
EntityTileIterator end()
|
||||
EntityTileIterator<T> end()
|
||||
{
|
||||
return EntityTileIterator(SPRITE_INDEX_NULL);
|
||||
return EntityTileIterator<T>(std::end(vec), std::end(vec));
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ enum class SpriteIdentifier : uint8_t;
|
|||
struct SpriteBase
|
||||
{
|
||||
SpriteIdentifier sprite_identifier;
|
||||
uint16_t next_in_quadrant;
|
||||
// Valid values are EntityListId::...
|
||||
EntityListId linked_list_index;
|
||||
// Height from centre of sprite to bottom
|
||||
|
|
|
@ -131,7 +131,6 @@ static void AdvanceGameTicks(uint32_t ticks, std::unique_ptr<IContext>& context)
|
|||
static void CompareSpriteDataCommon(const SpriteBase& left, const SpriteBase& right)
|
||||
{
|
||||
COMPARE_FIELD(sprite_identifier);
|
||||
COMPARE_FIELD(next_in_quadrant);
|
||||
COMPARE_FIELD(linked_list_index);
|
||||
COMPARE_FIELD(sprite_index);
|
||||
COMPARE_FIELD(flags);
|
||||
|
|
Loading…
Reference in New Issue