mirror of
https://github.com/OpenRCT2/OpenRCT2
synced 2025-12-10 09:32:29 +01:00
Move EntityList and EntityRegistry into OpenRCT2 namespace (#25039)
This commit is contained in:
@@ -26,6 +26,8 @@
|
||||
static constexpr size_t kMaximumGameStateSnapshots = 32;
|
||||
static constexpr uint32_t kInvalidTick = 0xFFFFFFFF;
|
||||
|
||||
using namespace OpenRCT2;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
union EntitySnapshot
|
||||
{
|
||||
|
||||
@@ -17,158 +17,161 @@
|
||||
#include <list>
|
||||
#include <vector>
|
||||
|
||||
const std::list<EntityId>& GetEntityList(const EntityType id);
|
||||
|
||||
uint16_t GetEntityListCount(EntityType list);
|
||||
uint16_t GetMiscEntityCount();
|
||||
uint16_t GetNumFreeEntities();
|
||||
const std::vector<EntityId>& GetEntityTileList(const CoordsXY& spritePos);
|
||||
|
||||
template<typename T>
|
||||
class EntityTileIterator
|
||||
namespace OpenRCT2
|
||||
{
|
||||
private:
|
||||
std::vector<EntityId>::const_iterator iter;
|
||||
std::vector<EntityId>::const_iterator end;
|
||||
T* Entity = nullptr;
|
||||
const std::list<EntityId>& GetEntityList(const EntityType id);
|
||||
|
||||
public:
|
||||
EntityTileIterator(std::vector<EntityId>::const_iterator _iter, std::vector<EntityId>::const_iterator _end)
|
||||
: iter(_iter)
|
||||
, end(_end)
|
||||
{
|
||||
++(*this);
|
||||
}
|
||||
EntityTileIterator& operator++()
|
||||
{
|
||||
Entity = nullptr;
|
||||
uint16_t GetEntityListCount(EntityType list);
|
||||
uint16_t GetMiscEntityCount();
|
||||
uint16_t GetNumFreeEntities();
|
||||
const std::vector<EntityId>& GetEntityTileList(const CoordsXY& spritePos);
|
||||
|
||||
while (iter != end && Entity == nullptr)
|
||||
template<typename T>
|
||||
class EntityTileIterator
|
||||
{
|
||||
private:
|
||||
std::vector<EntityId>::const_iterator iter;
|
||||
std::vector<EntityId>::const_iterator end;
|
||||
T* Entity = nullptr;
|
||||
|
||||
public:
|
||||
EntityTileIterator(std::vector<EntityId>::const_iterator _iter, std::vector<EntityId>::const_iterator _end)
|
||||
: iter(_iter)
|
||||
, end(_end)
|
||||
{
|
||||
Entity = TryGetEntity<T>(*iter++);
|
||||
++(*this);
|
||||
}
|
||||
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 = EntityBase>
|
||||
class EntityTileList
|
||||
{
|
||||
private:
|
||||
const std::vector<EntityId>& vec;
|
||||
|
||||
public:
|
||||
EntityTileList(const CoordsXY& loc)
|
||||
: vec(GetEntityTileList(loc))
|
||||
{
|
||||
}
|
||||
|
||||
EntityTileIterator<T> begin()
|
||||
{
|
||||
return EntityTileIterator<T>(std::begin(vec), std::end(vec));
|
||||
}
|
||||
EntityTileIterator<T> end()
|
||||
{
|
||||
return EntityTileIterator<T>(std::end(vec), std::end(vec));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class EntityListIterator
|
||||
{
|
||||
private:
|
||||
std::list<EntityId>::const_iterator iter;
|
||||
std::list<EntityId>::const_iterator end;
|
||||
T* Entity = nullptr;
|
||||
|
||||
public:
|
||||
EntityListIterator(std::list<EntityId>::const_iterator _iter, std::list<EntityId>::const_iterator _end)
|
||||
: iter(_iter)
|
||||
, end(_end)
|
||||
{
|
||||
++(*this);
|
||||
}
|
||||
EntityListIterator& operator++()
|
||||
{
|
||||
Entity = nullptr;
|
||||
|
||||
while (iter != end && Entity == nullptr)
|
||||
EntityTileIterator& operator++()
|
||||
{
|
||||
Entity = TryGetEntity<T>(*iter++);
|
||||
Entity = nullptr;
|
||||
|
||||
while (iter != end && Entity == nullptr)
|
||||
{
|
||||
Entity = TryGetEntity<T>(*iter++);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
EntityListIterator operator++(int)
|
||||
{
|
||||
EntityListIterator retval = *this;
|
||||
++(*this);
|
||||
return *iter;
|
||||
}
|
||||
bool operator==(EntityListIterator other) const
|
||||
{
|
||||
return Entity == other.Entity;
|
||||
}
|
||||
bool operator!=(EntityListIterator 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;
|
||||
};
|
||||
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 = EntityBase>
|
||||
class EntityList
|
||||
{
|
||||
private:
|
||||
using EntityListIterator_t = EntityListIterator<T>;
|
||||
const std::list<EntityId>& vec;
|
||||
template<typename T = EntityBase>
|
||||
class EntityTileList
|
||||
{
|
||||
private:
|
||||
const std::vector<EntityId>& vec;
|
||||
|
||||
public:
|
||||
EntityList()
|
||||
: vec(GetEntityList(T::cEntityType))
|
||||
{
|
||||
}
|
||||
public:
|
||||
EntityTileList(const CoordsXY& loc)
|
||||
: vec(GetEntityTileList(loc))
|
||||
{
|
||||
}
|
||||
|
||||
EntityListIterator_t begin() const
|
||||
EntityTileIterator<T> begin()
|
||||
{
|
||||
return EntityTileIterator<T>(std::begin(vec), std::end(vec));
|
||||
}
|
||||
EntityTileIterator<T> end()
|
||||
{
|
||||
return EntityTileIterator<T>(std::end(vec), std::end(vec));
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class EntityListIterator
|
||||
{
|
||||
return EntityListIterator_t(std::cbegin(vec), std::cend(vec));
|
||||
}
|
||||
EntityListIterator_t end() const
|
||||
private:
|
||||
std::list<EntityId>::const_iterator iter;
|
||||
std::list<EntityId>::const_iterator end;
|
||||
T* Entity = nullptr;
|
||||
|
||||
public:
|
||||
EntityListIterator(std::list<EntityId>::const_iterator _iter, std::list<EntityId>::const_iterator _end)
|
||||
: iter(_iter)
|
||||
, end(_end)
|
||||
{
|
||||
++(*this);
|
||||
}
|
||||
EntityListIterator& operator++()
|
||||
{
|
||||
Entity = nullptr;
|
||||
|
||||
while (iter != end && Entity == nullptr)
|
||||
{
|
||||
Entity = TryGetEntity<T>(*iter++);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
EntityListIterator operator++(int)
|
||||
{
|
||||
EntityListIterator retval = *this;
|
||||
++(*this);
|
||||
return *iter;
|
||||
}
|
||||
bool operator==(EntityListIterator other) const
|
||||
{
|
||||
return Entity == other.Entity;
|
||||
}
|
||||
bool operator!=(EntityListIterator 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 = EntityBase>
|
||||
class EntityList
|
||||
{
|
||||
return EntityListIterator_t(std::cend(vec), std::cend(vec));
|
||||
}
|
||||
};
|
||||
private:
|
||||
using EntityListIterator_t = EntityListIterator<T>;
|
||||
const std::list<EntityId>& vec;
|
||||
|
||||
public:
|
||||
EntityList()
|
||||
: vec(GetEntityList(T::cEntityType))
|
||||
{
|
||||
}
|
||||
|
||||
EntityListIterator_t begin() const
|
||||
{
|
||||
return EntityListIterator_t(std::cbegin(vec), std::cend(vec));
|
||||
}
|
||||
EntityListIterator_t end() const
|
||||
{
|
||||
return EntityListIterator_t(std::cend(vec), std::cend(vec));
|
||||
}
|
||||
};
|
||||
} // namespace OpenRCT2
|
||||
|
||||
@@ -39,441 +39,448 @@
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
using namespace OpenRCT2;
|
||||
using namespace OpenRCT2::Core;
|
||||
|
||||
static std::array<std::list<EntityId>, EnumValue(EntityType::Count)> gEntityLists;
|
||||
static std::vector<EntityId> _freeIdList;
|
||||
|
||||
static bool _entityFlashingList[kMaxEntities];
|
||||
|
||||
static constexpr const uint32_t kSpatialIndexSize = (kMaximumMapSizeTechnical * kMaximumMapSizeTechnical) + 1;
|
||||
static constexpr uint32_t kSpatialIndexNullBucket = kSpatialIndexSize - 1;
|
||||
|
||||
static constexpr uint32_t kInvalidSpatialIndex = 0xFFFFFFFFu;
|
||||
static constexpr uint32_t kSpatialIndexDirtyMask = 1u << 31;
|
||||
|
||||
static std::array<std::vector<EntityId>, kSpatialIndexSize> gEntitySpatialIndex;
|
||||
|
||||
static void FreeEntity(EntityBase& entity);
|
||||
|
||||
static constexpr uint32_t ComputeSpatialIndex(const CoordsXY& loc)
|
||||
namespace OpenRCT2
|
||||
{
|
||||
if (loc.IsNull())
|
||||
return kSpatialIndexNullBucket;
|
||||
using namespace OpenRCT2::Core;
|
||||
|
||||
// NOTE: The input coordinate is rotated and can have negative components.
|
||||
const auto tileX = std::abs(loc.x) / kCoordsXYStep;
|
||||
const auto tileY = std::abs(loc.y) / kCoordsXYStep;
|
||||
static constexpr const uint32_t kSpatialIndexSize = (kMaximumMapSizeTechnical * kMaximumMapSizeTechnical) + 1;
|
||||
static constexpr uint32_t kSpatialIndexNullBucket = kSpatialIndexSize - 1;
|
||||
|
||||
if (tileX >= kMaximumMapSizeTechnical || tileY >= kMaximumMapSizeTechnical)
|
||||
return kSpatialIndexNullBucket;
|
||||
static constexpr uint32_t kInvalidSpatialIndex = 0xFFFFFFFFu;
|
||||
static constexpr uint32_t kSpatialIndexDirtyMask = 1u << 31;
|
||||
|
||||
return tileX * kMaximumMapSizeTechnical + tileY;
|
||||
}
|
||||
// TODO: move into GameState_t
|
||||
static std::array<std::list<EntityId>, EnumValue(EntityType::Count)> gEntityLists;
|
||||
static std::vector<EntityId> _freeIdList;
|
||||
|
||||
static constexpr uint32_t GetSpatialIndex(EntityBase& entity)
|
||||
{
|
||||
return entity.SpatialIndex & ~kSpatialIndexDirtyMask;
|
||||
}
|
||||
// TODO: move into MapWindow or GameState_t?
|
||||
static bool _entityFlashingList[kMaxEntities];
|
||||
|
||||
constexpr bool EntityTypeIsMiscEntity(const EntityType type)
|
||||
{
|
||||
switch (type)
|
||||
// TODO: move into GameState_t
|
||||
static std::array<std::vector<EntityId>, kSpatialIndexSize> gEntitySpatialIndex;
|
||||
|
||||
static void FreeEntity(EntityBase& entity);
|
||||
|
||||
static constexpr uint32_t ComputeSpatialIndex(const CoordsXY& loc)
|
||||
{
|
||||
case EntityType::SteamParticle:
|
||||
case EntityType::MoneyEffect:
|
||||
case EntityType::CrashedVehicleParticle:
|
||||
case EntityType::ExplosionCloud:
|
||||
case EntityType::CrashSplash:
|
||||
case EntityType::ExplosionFlare:
|
||||
case EntityType::JumpingFountain:
|
||||
case EntityType::Balloon:
|
||||
case EntityType::Duck:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
if (loc.IsNull())
|
||||
return kSpatialIndexNullBucket;
|
||||
|
||||
// NOTE: The input coordinate is rotated and can have negative components.
|
||||
const auto tileX = std::abs(loc.x) / kCoordsXYStep;
|
||||
const auto tileY = std::abs(loc.y) / kCoordsXYStep;
|
||||
|
||||
if (tileX >= kMaximumMapSizeTechnical || tileY >= kMaximumMapSizeTechnical)
|
||||
return kSpatialIndexNullBucket;
|
||||
|
||||
return tileX * kMaximumMapSizeTechnical + tileY;
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t GetEntityListCount(EntityType type)
|
||||
{
|
||||
return static_cast<uint16_t>(gEntityLists[EnumValue(type)].size());
|
||||
}
|
||||
|
||||
uint16_t GetNumFreeEntities()
|
||||
{
|
||||
return static_cast<uint16_t>(_freeIdList.size());
|
||||
}
|
||||
|
||||
std::string EntitiesChecksum::ToString() const
|
||||
{
|
||||
return String::StringFromHex(raw);
|
||||
}
|
||||
|
||||
EntityBase* TryGetEntity(EntityId entityIndex)
|
||||
{
|
||||
auto& gameState = getGameState();
|
||||
const auto idx = entityIndex.ToUnderlying();
|
||||
return idx >= kMaxEntities ? nullptr : &gameState.entities[idx].base;
|
||||
}
|
||||
|
||||
EntityBase* GetEntity(EntityId entityIndex)
|
||||
{
|
||||
if (entityIndex.IsNull())
|
||||
static constexpr uint32_t GetSpatialIndex(EntityBase& entity)
|
||||
{
|
||||
return nullptr;
|
||||
return entity.SpatialIndex & ~kSpatialIndexDirtyMask;
|
||||
}
|
||||
Guard::Assert(entityIndex.ToUnderlying() < kMaxEntities, "Tried getting entity %u", entityIndex.ToUnderlying());
|
||||
return TryGetEntity(entityIndex);
|
||||
}
|
||||
|
||||
const std::vector<EntityId>& GetEntityTileList(const CoordsXY& spritePos)
|
||||
{
|
||||
return gEntitySpatialIndex[ComputeSpatialIndex(spritePos)];
|
||||
}
|
||||
|
||||
static void ResetEntityLists()
|
||||
{
|
||||
for (auto& list : gEntityLists)
|
||||
constexpr bool EntityTypeIsMiscEntity(const EntityType type)
|
||||
{
|
||||
list.clear();
|
||||
}
|
||||
}
|
||||
|
||||
static void ResetFreeIds()
|
||||
{
|
||||
_freeIdList.clear();
|
||||
_freeIdList.resize(kMaxEntities);
|
||||
|
||||
// List needs to be back to front to simplify removing
|
||||
auto nextId = 0;
|
||||
std::for_each(std::rbegin(_freeIdList), std::rend(_freeIdList), [&](auto& elem) {
|
||||
elem = EntityId::FromUnderlying(nextId);
|
||||
nextId++;
|
||||
});
|
||||
}
|
||||
|
||||
const std::list<EntityId>& GetEntityList(const EntityType id)
|
||||
{
|
||||
return gEntityLists[EnumValue(id)];
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x0069EB13
|
||||
*/
|
||||
void ResetAllEntities()
|
||||
{
|
||||
// Free all associated Entity pointers prior to zeroing memory
|
||||
for (int32_t i = 0; i < kMaxEntities; ++i)
|
||||
{
|
||||
auto* spr = GetEntity(EntityId::FromUnderlying(i));
|
||||
if (spr == nullptr)
|
||||
switch (type)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
FreeEntity(*spr);
|
||||
}
|
||||
|
||||
auto& gameState = getGameState();
|
||||
std::fill(std::begin(gameState.entities), std::end(gameState.entities), Entity_t());
|
||||
OpenRCT2::RideUse::GetHistory().Clear();
|
||||
OpenRCT2::RideUse::GetTypeHistory().Clear();
|
||||
for (int32_t i = 0; i < kMaxEntities; ++i)
|
||||
{
|
||||
auto* spr = GetEntity(EntityId::FromUnderlying(i));
|
||||
if (spr == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
spr->Type = EntityType::Null;
|
||||
spr->Id = EntityId::FromUnderlying(i);
|
||||
|
||||
_entityFlashingList[i] = false;
|
||||
}
|
||||
ResetEntityLists();
|
||||
ResetFreeIds();
|
||||
ResetEntitySpatialIndices();
|
||||
}
|
||||
|
||||
static void EntitySpatialInsert(EntityBase& entity, const CoordsXY& newLoc);
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x0069EBE4
|
||||
* This function looks as though it sets some sort of order for sprites.
|
||||
* Sprites can share their position if this is the case.
|
||||
*/
|
||||
void ResetEntitySpatialIndices()
|
||||
{
|
||||
for (auto& vec : gEntitySpatialIndex)
|
||||
{
|
||||
vec.clear();
|
||||
}
|
||||
for (EntityId::UnderlyingType i = 0; i < kMaxEntities; i++)
|
||||
{
|
||||
auto* entity = GetEntity(EntityId::FromUnderlying(i));
|
||||
if (entity != nullptr && entity->Type != EntityType::Null)
|
||||
{
|
||||
EntitySpatialInsert(*entity, { entity->x, entity->y });
|
||||
case EntityType::SteamParticle:
|
||||
case EntityType::MoneyEffect:
|
||||
case EntityType::CrashedVehicleParticle:
|
||||
case EntityType::ExplosionCloud:
|
||||
case EntityType::CrashSplash:
|
||||
case EntityType::ExplosionFlare:
|
||||
case EntityType::JumpingFountain:
|
||||
case EntityType::Balloon:
|
||||
case EntityType::Duck:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
|
||||
template<typename T>
|
||||
void NetworkSerialseEntityType(DataSerialiser& ds)
|
||||
{
|
||||
for (auto* ent : EntityList<T>())
|
||||
uint16_t GetEntityListCount(EntityType type)
|
||||
{
|
||||
ent->Serialise(ds);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
void NetworkSerialiseEntityTypes(DataSerialiser& ds)
|
||||
{
|
||||
(NetworkSerialseEntityType<T>(ds), ...);
|
||||
}
|
||||
|
||||
EntitiesChecksum GetAllEntitiesChecksum()
|
||||
{
|
||||
EntitiesChecksum checksum{};
|
||||
|
||||
OpenRCT2::ChecksumStream ms(checksum.raw);
|
||||
DataSerialiser ds(true, ms);
|
||||
NetworkSerialiseEntityTypes<Guest, Staff, Vehicle, Litter>(ds);
|
||||
|
||||
return checksum;
|
||||
}
|
||||
#else
|
||||
|
||||
EntitiesChecksum GetAllEntitiesChecksum()
|
||||
{
|
||||
return EntitiesChecksum{};
|
||||
}
|
||||
|
||||
#endif // DISABLE_NETWORK
|
||||
|
||||
static void EntityReset(EntityBase& entity)
|
||||
{
|
||||
// Need to retain how the sprite is linked in lists
|
||||
auto entityIndex = entity.Id;
|
||||
_entityFlashingList[entityIndex.ToUnderlying()] = false;
|
||||
|
||||
Entity_t* tempEntity = reinterpret_cast<Entity_t*>(&entity);
|
||||
*tempEntity = Entity_t();
|
||||
|
||||
entity.Id = entityIndex;
|
||||
entity.Type = EntityType::Null;
|
||||
}
|
||||
|
||||
static constexpr uint16_t kMaxMiscEntities = 3200;
|
||||
|
||||
static void AddToEntityList(EntityBase& entity)
|
||||
{
|
||||
auto& list = gEntityLists[EnumValue(entity.Type)];
|
||||
|
||||
// Entity list is sorted by Id to prevent desyncs.
|
||||
Algorithm::sortedInsert(list, entity.Id);
|
||||
}
|
||||
|
||||
static void AddToFreeList(EntityId index)
|
||||
{
|
||||
// Free list must be in reverse sprite_index order to prevent desync issues
|
||||
_freeIdList.insert(std::upper_bound(std::rbegin(_freeIdList), std::rend(_freeIdList), index).base(), index);
|
||||
}
|
||||
|
||||
static void RemoveFromEntityList(EntityBase& entity)
|
||||
{
|
||||
auto& list = gEntityLists[EnumValue(entity.Type)];
|
||||
auto ptr = Algorithm::binaryFind(std::begin(list), std::end(list), entity.Id);
|
||||
if (ptr != std::end(list))
|
||||
{
|
||||
list.erase(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t GetMiscEntityCount()
|
||||
{
|
||||
uint16_t count = 0;
|
||||
for (auto id : { EntityType::SteamParticle, EntityType::MoneyEffect, EntityType::CrashedVehicleParticle,
|
||||
EntityType::ExplosionCloud, EntityType::CrashSplash, EntityType::ExplosionFlare,
|
||||
EntityType::JumpingFountain, EntityType::Balloon, EntityType::Duck })
|
||||
{
|
||||
count += GetEntityListCount(id);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void PrepareNewEntity(EntityBase& base, const EntityType type)
|
||||
{
|
||||
// Need to reset all sprite data, as the uninitialised values
|
||||
// may contain garbage and cause a desync later on.
|
||||
EntityReset(base);
|
||||
|
||||
base.Type = type;
|
||||
AddToEntityList(base);
|
||||
|
||||
base.x = kLocationNull;
|
||||
base.y = kLocationNull;
|
||||
base.z = 0;
|
||||
base.SpriteData.Width = 0x10;
|
||||
base.SpriteData.HeightMin = 0x14;
|
||||
base.SpriteData.HeightMax = 0x8;
|
||||
base.SpriteData.SpriteRect = {};
|
||||
base.SpatialIndex = kInvalidSpatialIndex;
|
||||
|
||||
EntitySpatialInsert(base, { kLocationNull, 0 });
|
||||
}
|
||||
|
||||
EntityBase* CreateEntity(EntityType type)
|
||||
{
|
||||
if (_freeIdList.size() == 0)
|
||||
{
|
||||
// No free sprites.
|
||||
return nullptr;
|
||||
return static_cast<uint16_t>(gEntityLists[EnumValue(type)].size());
|
||||
}
|
||||
|
||||
if (EntityTypeIsMiscEntity(type))
|
||||
uint16_t GetNumFreeEntities()
|
||||
{
|
||||
// Misc sprites are commonly used for effects, give other entity types higher priority.
|
||||
if (GetMiscEntityCount() >= kMaxMiscEntities)
|
||||
return static_cast<uint16_t>(_freeIdList.size());
|
||||
}
|
||||
|
||||
std::string EntitiesChecksum::ToString() const
|
||||
{
|
||||
return String::StringFromHex(raw);
|
||||
}
|
||||
|
||||
EntityBase* TryGetEntity(EntityId entityIndex)
|
||||
{
|
||||
auto& gameState = getGameState();
|
||||
const auto idx = entityIndex.ToUnderlying();
|
||||
return idx >= kMaxEntities ? nullptr : &gameState.entities[idx].base;
|
||||
}
|
||||
|
||||
EntityBase* GetEntity(EntityId entityIndex)
|
||||
{
|
||||
if (entityIndex.IsNull())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
Guard::Assert(entityIndex.ToUnderlying() < kMaxEntities, "Tried getting entity %u", entityIndex.ToUnderlying());
|
||||
return TryGetEntity(entityIndex);
|
||||
}
|
||||
|
||||
// If there are less than kMaxMiscEntities free slots, ensure other entities can be created.
|
||||
if (_freeIdList.size() < kMaxMiscEntities)
|
||||
const std::vector<EntityId>& GetEntityTileList(const CoordsXY& spritePos)
|
||||
{
|
||||
return gEntitySpatialIndex[ComputeSpatialIndex(spritePos)];
|
||||
}
|
||||
|
||||
static void ResetEntityLists()
|
||||
{
|
||||
for (auto& list : gEntityLists)
|
||||
{
|
||||
return nullptr;
|
||||
list.clear();
|
||||
}
|
||||
}
|
||||
|
||||
auto* entity = GetEntity(_freeIdList.back());
|
||||
if (entity == nullptr)
|
||||
static void ResetFreeIds()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
_freeIdList.pop_back();
|
||||
_freeIdList.clear();
|
||||
_freeIdList.resize(kMaxEntities);
|
||||
|
||||
PrepareNewEntity(*entity, type);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
EntityBase* CreateEntityAt(const EntityId index, const EntityType type)
|
||||
{
|
||||
auto id = Algorithm::binaryFind(std::rbegin(_freeIdList), std::rend(_freeIdList), index);
|
||||
if (id == std::rend(_freeIdList))
|
||||
{
|
||||
return nullptr;
|
||||
// List needs to be back to front to simplify removing
|
||||
auto nextId = 0;
|
||||
std::for_each(std::rbegin(_freeIdList), std::rend(_freeIdList), [&](auto& elem) {
|
||||
elem = EntityId::FromUnderlying(nextId);
|
||||
nextId++;
|
||||
});
|
||||
}
|
||||
|
||||
auto* entity = GetEntity(index);
|
||||
if (entity == nullptr)
|
||||
const std::list<EntityId>& GetEntityList(const EntityType id)
|
||||
{
|
||||
return nullptr;
|
||||
return gEntityLists[EnumValue(id)];
|
||||
}
|
||||
|
||||
_freeIdList.erase(std::next(id).base());
|
||||
|
||||
PrepareNewEntity(*entity, type);
|
||||
return entity;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void MiscUpdateAllType()
|
||||
{
|
||||
for (auto misc : EntityList<T>())
|
||||
/**
|
||||
*
|
||||
* rct2: 0x0069EB13
|
||||
*/
|
||||
void ResetAllEntities()
|
||||
{
|
||||
misc->Update();
|
||||
}
|
||||
}
|
||||
// Free all associated Entity pointers prior to zeroing memory
|
||||
for (int32_t i = 0; i < kMaxEntities; ++i)
|
||||
{
|
||||
auto* spr = GetEntity(EntityId::FromUnderlying(i));
|
||||
if (spr == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
FreeEntity(*spr);
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
void MiscUpdateAllTypes()
|
||||
{
|
||||
(MiscUpdateAllType<T>(), ...);
|
||||
}
|
||||
auto& gameState = getGameState();
|
||||
std::fill(std::begin(gameState.entities), std::end(gameState.entities), Entity_t());
|
||||
OpenRCT2::RideUse::GetHistory().Clear();
|
||||
OpenRCT2::RideUse::GetTypeHistory().Clear();
|
||||
for (int32_t i = 0; i < kMaxEntities; ++i)
|
||||
{
|
||||
auto* spr = GetEntity(EntityId::FromUnderlying(i));
|
||||
if (spr == nullptr)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
spr->Type = EntityType::Null;
|
||||
spr->Id = EntityId::FromUnderlying(i);
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x00672AA4
|
||||
*/
|
||||
void UpdateAllMiscEntities()
|
||||
{
|
||||
PROFILED_FUNCTION();
|
||||
|
||||
MiscUpdateAllTypes<
|
||||
SteamParticle, MoneyEffect, VehicleCrashParticle, ExplosionCloud, CrashSplashParticle, ExplosionFlare, JumpingFountain,
|
||||
Balloon, Duck>();
|
||||
}
|
||||
|
||||
void UpdateMoneyEffect()
|
||||
{
|
||||
MiscUpdateAllTypes<MoneyEffect>();
|
||||
}
|
||||
|
||||
// Performs a search to ensure that insert keeps next_in_quadrant in sprite_index order
|
||||
static void EntitySpatialInsert(EntityBase& entity, const CoordsXY& newLoc)
|
||||
{
|
||||
const auto newIndex = ComputeSpatialIndex(newLoc);
|
||||
|
||||
auto& spatialVector = gEntitySpatialIndex[newIndex];
|
||||
|
||||
Algorithm::sortedInsert(spatialVector, entity.Id);
|
||||
|
||||
entity.SpatialIndex = newIndex;
|
||||
}
|
||||
|
||||
static void EntitySpatialRemove(EntityBase& entity)
|
||||
{
|
||||
const auto currentIndex = GetSpatialIndex(entity);
|
||||
|
||||
auto& spatialVector = gEntitySpatialIndex[currentIndex];
|
||||
auto index = Algorithm::binaryFind(std::begin(spatialVector), std::end(spatialVector), entity.Id);
|
||||
if (index != std::end(spatialVector))
|
||||
{
|
||||
spatialVector.erase(index, index + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_WARNING("Bad sprite spatial index. Rebuilding the spatial index...");
|
||||
_entityFlashingList[i] = false;
|
||||
}
|
||||
ResetEntityLists();
|
||||
ResetFreeIds();
|
||||
ResetEntitySpatialIndices();
|
||||
}
|
||||
|
||||
entity.SpatialIndex = kInvalidSpatialIndex;
|
||||
}
|
||||
static void EntitySpatialInsert(EntityBase& entity, const CoordsXY& newLoc);
|
||||
|
||||
static void UpdateEntitySpatialIndex(EntityBase& entity)
|
||||
{
|
||||
if (entity.SpatialIndex & kSpatialIndexDirtyMask)
|
||||
/**
|
||||
*
|
||||
* rct2: 0x0069EBE4
|
||||
* This function looks as though it sets some sort of order for sprites.
|
||||
* Sprites can share their position if this is the case.
|
||||
*/
|
||||
void ResetEntitySpatialIndices()
|
||||
{
|
||||
if (entity.SpatialIndex != kInvalidSpatialIndex)
|
||||
for (auto& vec : gEntitySpatialIndex)
|
||||
{
|
||||
EntitySpatialRemove(entity);
|
||||
vec.clear();
|
||||
}
|
||||
EntitySpatialInsert(entity, { entity.x, entity.y });
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateEntitiesSpatialIndex()
|
||||
{
|
||||
for (auto& entityList : gEntityLists)
|
||||
{
|
||||
for (auto& entityId : entityList)
|
||||
for (EntityId::UnderlyingType i = 0; i < kMaxEntities; i++)
|
||||
{
|
||||
auto* entity = TryGetEntity(entityId);
|
||||
auto* entity = GetEntity(EntityId::FromUnderlying(i));
|
||||
if (entity != nullptr && entity->Type != EntityType::Null)
|
||||
{
|
||||
UpdateEntitySpatialIndex(*entity);
|
||||
EntitySpatialInsert(*entity, { entity->x, entity->y });
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef DISABLE_NETWORK
|
||||
|
||||
template<typename T>
|
||||
void NetworkSerialseEntityType(DataSerialiser& ds)
|
||||
{
|
||||
for (auto* ent : EntityList<T>())
|
||||
{
|
||||
ent->Serialise(ds);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
void NetworkSerialiseEntityTypes(DataSerialiser& ds)
|
||||
{
|
||||
(NetworkSerialseEntityType<T>(ds), ...);
|
||||
}
|
||||
|
||||
EntitiesChecksum GetAllEntitiesChecksum()
|
||||
{
|
||||
EntitiesChecksum checksum{};
|
||||
|
||||
OpenRCT2::ChecksumStream ms(checksum.raw);
|
||||
DataSerialiser ds(true, ms);
|
||||
NetworkSerialiseEntityTypes<Guest, Staff, Vehicle, Litter>(ds);
|
||||
|
||||
return checksum;
|
||||
}
|
||||
#else
|
||||
|
||||
EntitiesChecksum GetAllEntitiesChecksum()
|
||||
{
|
||||
return EntitiesChecksum{};
|
||||
}
|
||||
|
||||
#endif // DISABLE_NETWORK
|
||||
|
||||
static void EntityReset(EntityBase& entity)
|
||||
{
|
||||
// Need to retain how the sprite is linked in lists
|
||||
auto entityIndex = entity.Id;
|
||||
_entityFlashingList[entityIndex.ToUnderlying()] = false;
|
||||
|
||||
Entity_t* tempEntity = reinterpret_cast<Entity_t*>(&entity);
|
||||
*tempEntity = Entity_t();
|
||||
|
||||
entity.Id = entityIndex;
|
||||
entity.Type = EntityType::Null;
|
||||
}
|
||||
|
||||
static constexpr uint16_t kMaxMiscEntities = 3200;
|
||||
|
||||
static void AddToEntityList(EntityBase& entity)
|
||||
{
|
||||
auto& list = gEntityLists[EnumValue(entity.Type)];
|
||||
|
||||
// Entity list is sorted by Id to prevent desyncs.
|
||||
Algorithm::sortedInsert(list, entity.Id);
|
||||
}
|
||||
|
||||
static void AddToFreeList(EntityId index)
|
||||
{
|
||||
// Free list must be in reverse sprite_index order to prevent desync issues
|
||||
_freeIdList.insert(std::upper_bound(std::rbegin(_freeIdList), std::rend(_freeIdList), index).base(), index);
|
||||
}
|
||||
|
||||
static void RemoveFromEntityList(EntityBase& entity)
|
||||
{
|
||||
auto& list = gEntityLists[EnumValue(entity.Type)];
|
||||
auto ptr = Algorithm::binaryFind(std::begin(list), std::end(list), entity.Id);
|
||||
if (ptr != std::end(list))
|
||||
{
|
||||
list.erase(ptr);
|
||||
}
|
||||
}
|
||||
|
||||
uint16_t GetMiscEntityCount()
|
||||
{
|
||||
uint16_t count = 0;
|
||||
for (auto id : { EntityType::SteamParticle, EntityType::MoneyEffect, EntityType::CrashedVehicleParticle,
|
||||
EntityType::ExplosionCloud, EntityType::CrashSplash, EntityType::ExplosionFlare,
|
||||
EntityType::JumpingFountain, EntityType::Balloon, EntityType::Duck })
|
||||
{
|
||||
count += GetEntityListCount(id);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static void PrepareNewEntity(EntityBase& base, const EntityType type)
|
||||
{
|
||||
// Need to reset all sprite data, as the uninitialised values
|
||||
// may contain garbage and cause a desync later on.
|
||||
EntityReset(base);
|
||||
|
||||
base.Type = type;
|
||||
AddToEntityList(base);
|
||||
|
||||
base.x = kLocationNull;
|
||||
base.y = kLocationNull;
|
||||
base.z = 0;
|
||||
base.SpriteData.Width = 0x10;
|
||||
base.SpriteData.HeightMin = 0x14;
|
||||
base.SpriteData.HeightMax = 0x8;
|
||||
base.SpriteData.SpriteRect = {};
|
||||
base.SpatialIndex = kInvalidSpatialIndex;
|
||||
|
||||
EntitySpatialInsert(base, { kLocationNull, 0 });
|
||||
}
|
||||
|
||||
EntityBase* CreateEntity(EntityType type)
|
||||
{
|
||||
if (_freeIdList.size() == 0)
|
||||
{
|
||||
// No free sprites.
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (EntityTypeIsMiscEntity(type))
|
||||
{
|
||||
// Misc sprites are commonly used for effects, give other entity types higher priority.
|
||||
if (GetMiscEntityCount() >= kMaxMiscEntities)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// If there are less than kMaxMiscEntities free slots, ensure other entities can be created.
|
||||
if (_freeIdList.size() < kMaxMiscEntities)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
auto* entity = GetEntity(_freeIdList.back());
|
||||
if (entity == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
_freeIdList.pop_back();
|
||||
|
||||
PrepareNewEntity(*entity, type);
|
||||
|
||||
return entity;
|
||||
}
|
||||
|
||||
EntityBase* CreateEntityAt(const EntityId index, const EntityType type)
|
||||
{
|
||||
auto id = Algorithm::binaryFind(std::rbegin(_freeIdList), std::rend(_freeIdList), index);
|
||||
if (id == std::rend(_freeIdList))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* entity = GetEntity(index);
|
||||
if (entity == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
_freeIdList.erase(std::next(id).base());
|
||||
|
||||
PrepareNewEntity(*entity, type);
|
||||
return entity;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
void MiscUpdateAllType()
|
||||
{
|
||||
for (auto misc : EntityList<T>())
|
||||
{
|
||||
misc->Update();
|
||||
}
|
||||
}
|
||||
|
||||
template<typename... T>
|
||||
void MiscUpdateAllTypes()
|
||||
{
|
||||
(MiscUpdateAllType<T>(), ...);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x00672AA4
|
||||
*/
|
||||
void UpdateAllMiscEntities()
|
||||
{
|
||||
PROFILED_FUNCTION();
|
||||
|
||||
MiscUpdateAllTypes<
|
||||
SteamParticle, MoneyEffect, VehicleCrashParticle, ExplosionCloud, CrashSplashParticle, ExplosionFlare,
|
||||
JumpingFountain, Balloon, Duck>();
|
||||
}
|
||||
|
||||
void UpdateMoneyEffect()
|
||||
{
|
||||
MiscUpdateAllTypes<MoneyEffect>();
|
||||
}
|
||||
|
||||
// Performs a search to ensure that insert keeps next_in_quadrant in sprite_index order
|
||||
static void EntitySpatialInsert(EntityBase& entity, const CoordsXY& newLoc)
|
||||
{
|
||||
const auto newIndex = ComputeSpatialIndex(newLoc);
|
||||
|
||||
auto& spatialVector = gEntitySpatialIndex[newIndex];
|
||||
|
||||
Algorithm::sortedInsert(spatialVector, entity.Id);
|
||||
|
||||
entity.SpatialIndex = newIndex;
|
||||
}
|
||||
|
||||
static void EntitySpatialRemove(EntityBase& entity)
|
||||
{
|
||||
const auto currentIndex = GetSpatialIndex(entity);
|
||||
|
||||
auto& spatialVector = gEntitySpatialIndex[currentIndex];
|
||||
auto index = Algorithm::binaryFind(std::begin(spatialVector), std::end(spatialVector), entity.Id);
|
||||
if (index != std::end(spatialVector))
|
||||
{
|
||||
spatialVector.erase(index, index + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG_WARNING("Bad sprite spatial index. Rebuilding the spatial index...");
|
||||
ResetEntitySpatialIndices();
|
||||
}
|
||||
|
||||
entity.SpatialIndex = kInvalidSpatialIndex;
|
||||
}
|
||||
|
||||
static void UpdateEntitySpatialIndex(EntityBase& entity)
|
||||
{
|
||||
if (entity.SpatialIndex & kSpatialIndexDirtyMask)
|
||||
{
|
||||
if (entity.SpatialIndex != kInvalidSpatialIndex)
|
||||
{
|
||||
EntitySpatialRemove(entity);
|
||||
}
|
||||
EntitySpatialInsert(entity, { entity.x, entity.y });
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateEntitiesSpatialIndex()
|
||||
{
|
||||
for (auto& entityList : gEntityLists)
|
||||
{
|
||||
for (auto& entityId : entityList)
|
||||
{
|
||||
auto* entity = TryGetEntity(entityId);
|
||||
if (entity != nullptr && entity->Type != EntityType::Null)
|
||||
{
|
||||
UpdateEntitySpatialIndex(*entity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace OpenRCT2
|
||||
|
||||
using namespace OpenRCT2;
|
||||
|
||||
CoordsXYZ EntityBase::GetLocation() const
|
||||
{
|
||||
@@ -549,80 +556,83 @@ void EntityBase::MoveToAndUpdateSpatialIndex(const CoordsXYZ& newLocation)
|
||||
UpdateEntitySpatialIndex(*this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees any dynamically attached memory to the entity, such as peep name.
|
||||
*/
|
||||
static void FreeEntity(EntityBase& entity)
|
||||
namespace OpenRCT2
|
||||
{
|
||||
auto* guest = entity.As<Guest>();
|
||||
auto* staff = entity.As<Staff>();
|
||||
if (staff != nullptr)
|
||||
/**
|
||||
* Frees any dynamically attached memory to the entity, such as peep name.
|
||||
*/
|
||||
static void FreeEntity(EntityBase& entity)
|
||||
{
|
||||
staff->SetName({});
|
||||
staff->ClearPatrolArea();
|
||||
}
|
||||
else if (guest != nullptr)
|
||||
{
|
||||
guest->SetName({});
|
||||
guest->GuestNextInQueue = EntityId::GetNull();
|
||||
|
||||
OpenRCT2::RideUse::GetHistory().RemoveHandle(guest->Id);
|
||||
OpenRCT2::RideUse::GetTypeHistory().RemoveHandle(guest->Id);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x0069EDB6
|
||||
*/
|
||||
void EntityRemove(EntityBase* entity)
|
||||
{
|
||||
FreeEntity(*entity);
|
||||
|
||||
EntityTweener::Get().RemoveEntity(entity);
|
||||
RemoveFromEntityList(*entity); // remove from existing list
|
||||
AddToFreeList(entity->Id);
|
||||
|
||||
EntitySpatialRemove(*entity);
|
||||
EntityReset(*entity);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops through all floating entities and removes them.
|
||||
* Returns the amount of removed objects as feedback.
|
||||
*/
|
||||
uint16_t RemoveFloatingEntities()
|
||||
{
|
||||
uint16_t removed = 0;
|
||||
for (auto* balloon : EntityList<Balloon>())
|
||||
{
|
||||
EntityRemove(balloon);
|
||||
removed++;
|
||||
}
|
||||
for (auto* duck : EntityList<Duck>())
|
||||
{
|
||||
if (duck->IsFlying())
|
||||
auto* guest = entity.As<Guest>();
|
||||
auto* staff = entity.As<Staff>();
|
||||
if (staff != nullptr)
|
||||
{
|
||||
EntityRemove(duck);
|
||||
removed++;
|
||||
staff->SetName({});
|
||||
staff->ClearPatrolArea();
|
||||
}
|
||||
else if (guest != nullptr)
|
||||
{
|
||||
guest->SetName({});
|
||||
guest->GuestNextInQueue = EntityId::GetNull();
|
||||
|
||||
OpenRCT2::RideUse::GetHistory().RemoveHandle(guest->Id);
|
||||
OpenRCT2::RideUse::GetTypeHistory().RemoveHandle(guest->Id);
|
||||
}
|
||||
}
|
||||
for (auto* money : EntityList<MoneyEffect>())
|
||||
|
||||
/**
|
||||
*
|
||||
* rct2: 0x0069EDB6
|
||||
*/
|
||||
void EntityRemove(EntityBase* entity)
|
||||
{
|
||||
EntityRemove(money);
|
||||
removed++;
|
||||
FreeEntity(*entity);
|
||||
|
||||
EntityTweener::Get().RemoveEntity(entity);
|
||||
RemoveFromEntityList(*entity); // remove from existing list
|
||||
AddToFreeList(entity->Id);
|
||||
|
||||
EntitySpatialRemove(*entity);
|
||||
EntityReset(*entity);
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
void EntitySetFlashing(EntityBase* entity, bool flashing)
|
||||
{
|
||||
assert(entity->Id.ToUnderlying() < kMaxEntities);
|
||||
_entityFlashingList[entity->Id.ToUnderlying()] = flashing;
|
||||
}
|
||||
/**
|
||||
* Loops through all floating entities and removes them.
|
||||
* Returns the amount of removed objects as feedback.
|
||||
*/
|
||||
uint16_t RemoveFloatingEntities()
|
||||
{
|
||||
uint16_t removed = 0;
|
||||
for (auto* balloon : EntityList<Balloon>())
|
||||
{
|
||||
EntityRemove(balloon);
|
||||
removed++;
|
||||
}
|
||||
for (auto* duck : EntityList<Duck>())
|
||||
{
|
||||
if (duck->IsFlying())
|
||||
{
|
||||
EntityRemove(duck);
|
||||
removed++;
|
||||
}
|
||||
}
|
||||
for (auto* money : EntityList<MoneyEffect>())
|
||||
{
|
||||
EntityRemove(money);
|
||||
removed++;
|
||||
}
|
||||
return removed;
|
||||
}
|
||||
|
||||
bool EntityGetFlashing(EntityBase* entity)
|
||||
{
|
||||
assert(entity->Id.ToUnderlying() < kMaxEntities);
|
||||
return _entityFlashingList[entity->Id.ToUnderlying()];
|
||||
}
|
||||
void EntitySetFlashing(EntityBase* entity, bool flashing)
|
||||
{
|
||||
assert(entity->Id.ToUnderlying() < kMaxEntities);
|
||||
_entityFlashingList[entity->Id.ToUnderlying()] = flashing;
|
||||
}
|
||||
|
||||
bool EntityGetFlashing(EntityBase* entity)
|
||||
{
|
||||
assert(entity->Id.ToUnderlying() < kMaxEntities);
|
||||
return _entityFlashingList[entity->Id.ToUnderlying()];
|
||||
}
|
||||
} // namespace OpenRCT2
|
||||
|
||||
@@ -25,84 +25,84 @@ namespace OpenRCT2
|
||||
{
|
||||
}
|
||||
};
|
||||
} // namespace OpenRCT2
|
||||
|
||||
constexpr uint16_t kMaxEntities = 65535;
|
||||
constexpr uint16_t kMaxEntities = 65535;
|
||||
|
||||
EntityBase* GetEntity(EntityId entityId);
|
||||
EntityBase* GetEntity(EntityId entityId);
|
||||
|
||||
template<typename T>
|
||||
T* GetEntity(EntityId entityId)
|
||||
{
|
||||
auto* ent = GetEntity(entityId);
|
||||
if (ent == nullptr)
|
||||
template<typename T>
|
||||
T* GetEntity(EntityId entityId)
|
||||
{
|
||||
return nullptr;
|
||||
auto* ent = GetEntity(entityId);
|
||||
if (ent == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if constexpr (std::is_same_v<T, EntityBase>)
|
||||
{
|
||||
return ent;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ent->As<T>();
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_same_v<T, EntityBase>)
|
||||
|
||||
EntityBase* TryGetEntity(EntityId spriteIndex);
|
||||
|
||||
template<typename T>
|
||||
T* TryGetEntity(EntityId entityId)
|
||||
{
|
||||
return ent;
|
||||
auto* ent = TryGetEntity(entityId);
|
||||
if (ent == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
if constexpr (std::is_same_v<T, EntityBase>)
|
||||
{
|
||||
return ent;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ent->As<T>();
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
EntityBase* CreateEntity(EntityType type);
|
||||
|
||||
template<typename T>
|
||||
T* CreateEntity()
|
||||
{
|
||||
return ent->As<T>();
|
||||
return static_cast<T*>(CreateEntity(T::cEntityType));
|
||||
}
|
||||
}
|
||||
|
||||
EntityBase* TryGetEntity(EntityId spriteIndex);
|
||||
|
||||
template<typename T>
|
||||
T* TryGetEntity(EntityId entityId)
|
||||
{
|
||||
auto* ent = TryGetEntity(entityId);
|
||||
if (ent == nullptr)
|
||||
// Use only with imports that must happen at a specified index
|
||||
EntityBase* CreateEntityAt(const EntityId index, const EntityType type);
|
||||
// Use only with imports that must happen at a specified index
|
||||
template<typename T>
|
||||
T* CreateEntityAt(const EntityId index)
|
||||
{
|
||||
return nullptr;
|
||||
return static_cast<T*>(CreateEntityAt(index, T::cEntityType));
|
||||
}
|
||||
if constexpr (std::is_same_v<T, EntityBase>)
|
||||
{
|
||||
return ent;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ent->As<T>();
|
||||
}
|
||||
}
|
||||
|
||||
EntityBase* CreateEntity(EntityType type);
|
||||
|
||||
template<typename T>
|
||||
T* CreateEntity()
|
||||
{
|
||||
return static_cast<T*>(CreateEntity(T::cEntityType));
|
||||
}
|
||||
|
||||
// Use only with imports that must happen at a specified index
|
||||
EntityBase* CreateEntityAt(const EntityId index, const EntityType type);
|
||||
// Use only with imports that must happen at a specified index
|
||||
template<typename T>
|
||||
T* CreateEntityAt(const EntityId index)
|
||||
{
|
||||
return static_cast<T*>(CreateEntityAt(index, T::cEntityType));
|
||||
}
|
||||
|
||||
void ResetAllEntities();
|
||||
void ResetEntitySpatialIndices();
|
||||
void UpdateAllMiscEntities();
|
||||
void UpdateMoneyEffect();
|
||||
void EntityRemove(EntityBase* entity);
|
||||
uint16_t RemoveFloatingEntities();
|
||||
void UpdateEntitiesSpatialIndex();
|
||||
void ResetAllEntities();
|
||||
void ResetEntitySpatialIndices();
|
||||
void UpdateAllMiscEntities();
|
||||
void UpdateMoneyEffect();
|
||||
void EntityRemove(EntityBase* entity);
|
||||
uint16_t RemoveFloatingEntities();
|
||||
void UpdateEntitiesSpatialIndex();
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct EntitiesChecksum
|
||||
{
|
||||
std::array<std::byte, 20> raw;
|
||||
struct EntitiesChecksum
|
||||
{
|
||||
std::array<std::byte, 20> raw;
|
||||
|
||||
std::string ToString() const;
|
||||
};
|
||||
std::string ToString() const;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
EntitiesChecksum GetAllEntitiesChecksum();
|
||||
EntitiesChecksum GetAllEntitiesChecksum();
|
||||
|
||||
void EntitySetFlashing(EntityBase* entity, bool flashing);
|
||||
bool EntityGetFlashing(EntityBase* entity);
|
||||
void EntitySetFlashing(EntityBase* entity, bool flashing);
|
||||
bool EntityGetFlashing(EntityBase* entity);
|
||||
} // namespace OpenRCT2
|
||||
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include <iterator>
|
||||
|
||||
using namespace OpenRCT2;
|
||||
|
||||
static constexpr uint32_t kVehicleCrashParticleSprites[kCrashedVehicleParticleNumberTypes] = {
|
||||
SPR_VEHICLE_CRASH_PARTICLE_1, SPR_VEHICLE_CRASH_PARTICLE_2, SPR_VEHICLE_CRASH_PARTICLE_3,
|
||||
SPR_VEHICLE_CRASH_PARTICLE_4, SPR_VEHICLE_CRASH_PARTICLE_5,
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace OpenRCT2::Scripting
|
||||
|
||||
Balloon* ScBalloon::GetBalloon() const
|
||||
{
|
||||
return ::GetEntity<Balloon>(_id);
|
||||
return OpenRCT2::GetEntity<Balloon>(_id);
|
||||
}
|
||||
|
||||
uint8_t ScBalloon::colour_get() const
|
||||
|
||||
@@ -192,7 +192,7 @@ namespace OpenRCT2::Scripting
|
||||
|
||||
EntityBase* GetEntity() const
|
||||
{
|
||||
return ::GetEntity(_id);
|
||||
return OpenRCT2::GetEntity(_id);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
@@ -194,7 +194,7 @@ namespace OpenRCT2::Scripting
|
||||
|
||||
Guest* ScGuest::GetGuest() const
|
||||
{
|
||||
return ::GetEntity<Guest>(_id);
|
||||
return OpenRCT2::GetEntity<Guest>(_id);
|
||||
}
|
||||
|
||||
uint8_t ScGuest::tshirtColour_get() const
|
||||
|
||||
@@ -45,7 +45,7 @@ namespace OpenRCT2::Scripting
|
||||
|
||||
Litter* ScLitter::GetLitter() const
|
||||
{
|
||||
return ::GetEntity<Litter>(_id);
|
||||
return OpenRCT2::GetEntity<Litter>(_id);
|
||||
}
|
||||
|
||||
std::string ScLitter::litterType_get() const
|
||||
|
||||
@@ -28,7 +28,7 @@ namespace OpenRCT2::Scripting
|
||||
|
||||
MoneyEffect* ScMoneyEffect::GetMoneyEffect() const
|
||||
{
|
||||
return ::GetEntity<MoneyEffect>(_id);
|
||||
return OpenRCT2::GetEntity<MoneyEffect>(_id);
|
||||
}
|
||||
|
||||
money64 ScMoneyEffect::value_get() const
|
||||
|
||||
@@ -49,7 +49,7 @@ namespace OpenRCT2::Scripting
|
||||
|
||||
VehicleCrashParticle* ScCrashedVehicleParticle::GetCrashedVehicleParticle() const
|
||||
{
|
||||
return ::GetEntity<VehicleCrashParticle>(_id);
|
||||
return OpenRCT2::GetEntity<VehicleCrashParticle>(_id);
|
||||
}
|
||||
|
||||
void ScCrashedVehicleParticle::frame_set(uint8_t value)
|
||||
|
||||
@@ -196,7 +196,7 @@ namespace OpenRCT2::Scripting
|
||||
protected:
|
||||
Peep* GetPeep() const
|
||||
{
|
||||
return ::GetEntity<Peep>(_id);
|
||||
return OpenRCT2::GetEntity<Peep>(_id);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace OpenRCT2::Scripting
|
||||
|
||||
Staff* ScStaff::GetStaff() const
|
||||
{
|
||||
return ::GetEntity<Staff>(_id);
|
||||
return OpenRCT2::GetEntity<Staff>(_id);
|
||||
}
|
||||
|
||||
std::string ScStaff::staffType_get() const
|
||||
@@ -420,7 +420,7 @@ namespace OpenRCT2::Scripting
|
||||
|
||||
Staff* ScHandyman::GetHandyman() const
|
||||
{
|
||||
return ::GetEntity<Staff>(_id);
|
||||
return OpenRCT2::GetEntity<Staff>(_id);
|
||||
}
|
||||
|
||||
DukValue ScHandyman::lawnsMown_get() const
|
||||
@@ -501,7 +501,7 @@ namespace OpenRCT2::Scripting
|
||||
|
||||
Staff* ScMechanic::GetMechanic() const
|
||||
{
|
||||
return ::GetEntity<Staff>(_id);
|
||||
return OpenRCT2::GetEntity<Staff>(_id);
|
||||
}
|
||||
|
||||
DukValue ScMechanic::ridesFixed_get() const
|
||||
@@ -549,7 +549,7 @@ namespace OpenRCT2::Scripting
|
||||
|
||||
Staff* ScSecurity::GetSecurity() const
|
||||
{
|
||||
return ::GetEntity<Staff>(_id);
|
||||
return OpenRCT2::GetEntity<Staff>(_id);
|
||||
}
|
||||
|
||||
DukValue ScSecurity::vandalsStopped_get() const
|
||||
|
||||
Reference in New Issue
Block a user