diff --git a/src/openrct2/rct2/S6Importer.cpp b/src/openrct2/rct2/S6Importer.cpp index 836f109f59..73856af713 100644 --- a/src/openrct2/rct2/S6Importer.cpp +++ b/src/openrct2/rct2/S6Importer.cpp @@ -218,7 +218,7 @@ public: scenario_rand_seed(_s6.scenario_srand_0, _s6.scenario_srand_1); ImportTileElements(); - ImportSprites(); + ImportEntities(); gInitialCash = _s6.initial_cash; gBankLoan = _s6.current_loan; @@ -1321,153 +1321,19 @@ public: } } - void ImportSprites() + void ImportEntities() { for (int32_t i = 0; i < RCT2_MAX_SPRITES; i++) { - auto src = &_s6.sprites[i]; - auto dst = GetEntity(i); - ImportSprite(reinterpret_cast(dst), src); - } - RebuildEntityLists(); - } - - void ImportSprite(rct_sprite* dst, const RCT2Sprite* src) - { - std::memset(&dst->pad_00, 0, sizeof(rct_sprite)); - switch (src->unknown.sprite_identifier) - { - case RCT12SpriteIdentifier::Null: - ImportSpriteCommonProperties(reinterpret_cast(dst), &src->unknown); - break; - case RCT12SpriteIdentifier::Vehicle: - ImportSpriteVehicle(&dst->vehicle, &src->vehicle); - break; - case RCT12SpriteIdentifier::Peep: - ImportSpritePeep(&dst->peep, &src->peep); - break; - case RCT12SpriteIdentifier::Misc: - ImportSpriteMisc(&dst->misc, &src->unknown); - break; - case RCT12SpriteIdentifier::Litter: - ImportSpriteLitter(&dst->litter, &src->litter); - break; - default: - ImportSpriteCommonProperties(reinterpret_cast(dst), reinterpret_cast(src)); - log_warning("Sprite identifier %d can not be imported.", src->unknown.sprite_identifier); - break; + ImportEntity(_s6.sprites[i].unknown); } } - void ImportSpriteVehicle(Vehicle* dst, const RCT2SpriteVehicle* src) + template void ImportEntity(const RCT12SpriteBase& src); + + void ImportEntityPeep(Peep* dst, const RCT2SpritePeep* src) { - const auto& ride = _s6.rides[src->ride]; - - ImportSpriteCommonProperties(static_cast(dst), src); - dst->SubType = Vehicle::Type(src->type); - dst->vehicle_sprite_type = src->vehicle_sprite_type; - dst->bank_rotation = src->bank_rotation; - dst->remaining_distance = src->remaining_distance; - dst->velocity = src->velocity; - dst->acceleration = src->acceleration; - dst->ride = src->ride; - dst->vehicle_type = src->vehicle_type; - dst->colours = src->colours; - dst->track_progress = src->track_progress; - dst->TrackLocation = { src->track_x, src->track_y, src->track_z }; - if (src->boat_location.isNull() || static_cast(ride.mode) != RideMode::BoatHire - || src->status != static_cast(Vehicle::Status::TravellingBoat)) - { - dst->BoatLocation.setNull(); - dst->SetTrackDirection(src->GetTrackDirection()); - dst->SetTrackType(src->GetTrackType()); - // RotationControlToggle and Booster are saved as the same track piece ID - // Which one the vehicle is using must be determined - if (GetRideTypeDescriptor(ride.type).HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE)) - { - dst->SetTrackType(RCT12FlatTrackTypeToOpenRCT2(src->GetTrackType())); - } - else if (src->GetTrackType() == TrackElemType::RotationControlToggleAlias) - { - // Merging hacks mean the track type that's appropriate for the ride type is not necessarily the track type the - // ride is on. It's possible to create unwanted behavior if a user layers spinning control track on top of - // booster track but this is unlikely since only two rides have spinning control track - by default they load as - // booster - TileElement* tileElement2 = map_get_track_element_at_of_type_seq( - dst->TrackLocation, TrackElemType::RotationControlToggle, 0); - - if (tileElement2 != nullptr) - dst->SetTrackType(TrackElemType::RotationControlToggle); - } - } - else - { - dst->BoatLocation = TileCoordsXY{ src->boat_location.x, src->boat_location.y }.ToCoordsXY(); - dst->SetTrackDirection(0); - dst->SetTrackType(0); - } - - dst->next_vehicle_on_train = src->next_vehicle_on_train; - dst->prev_vehicle_on_ride = src->prev_vehicle_on_ride; - dst->next_vehicle_on_ride = src->next_vehicle_on_ride; - dst->var_44 = src->var_44; - dst->mass = src->mass; - dst->update_flags = src->update_flags; - dst->SwingSprite = src->SwingSprite; - dst->current_station = src->current_station; - dst->current_time = src->current_time; - dst->crash_z = src->crash_z; - - Vehicle::Status statusSrc = Vehicle::Status::MovingToEndOfStation; - if (src->status <= static_cast(Vehicle::Status::StoppedByBlockBrakes)) - { - statusSrc = static_cast(src->status); - } - - dst->status = statusSrc; - dst->sub_state = src->sub_state; - for (size_t i = 0; i < std::size(src->peep); i++) - { - dst->peep[i] = src->peep[i]; - dst->peep_tshirt_colours[i] = src->peep_tshirt_colours[i]; - } - dst->num_seats = src->num_seats; - dst->num_peeps = src->num_peeps; - dst->next_free_seat = src->next_free_seat; - dst->restraints_position = src->restraints_position; - dst->crash_x = src->crash_x; - dst->sound2_flags = src->sound2_flags; - dst->spin_sprite = src->spin_sprite; - dst->sound1_id = static_cast(src->sound1_id); - dst->sound1_volume = src->sound1_volume; - dst->sound2_id = static_cast(src->sound2_id); - dst->sound2_volume = src->sound2_volume; - dst->sound_vector_factor = src->sound_vector_factor; - dst->time_waiting = src->time_waiting; - dst->speed = src->speed; - dst->powered_acceleration = src->powered_acceleration; - dst->dodgems_collision_direction = src->dodgems_collision_direction; - dst->animation_frame = src->animation_frame; - dst->var_C8 = src->var_C8; - dst->var_CA = src->var_CA; - dst->scream_sound_id = static_cast(src->scream_sound_id); - dst->TrackSubposition = VehicleTrackSubposition{ src->TrackSubposition }; - dst->var_CE = src->var_CE; - dst->var_CF = src->var_CF; - dst->lost_time_out = src->lost_time_out; - dst->vertical_drop_countdown = src->vertical_drop_countdown; - dst->var_D3 = src->var_D3; - dst->mini_golf_current_animation = src->mini_golf_current_animation; - dst->mini_golf_flags = src->mini_golf_flags; - dst->ride_subtype = RCTEntryIndexToOpenRCT2EntryIndex(src->ride_subtype); - dst->colours_extended = src->colours_extended; - dst->seat_rotation = src->seat_rotation; - dst->target_seat_rotation = src->target_seat_rotation; - } - - void ImportSpritePeep(Peep* dst, const RCT2SpritePeep* src) - { - ImportSpriteCommonProperties(static_cast(dst), src); + ImportEntityCommonProperties(static_cast(dst), src); if (is_user_string_id(src->name_string_idx)) { dst->SetName(GetUserString(src->name_string_idx)); @@ -1578,107 +1444,6 @@ public: dst->FavouriteRideRating = src->favourite_ride_rating; } - void ImportSpriteMisc(MiscEntity* cdst, const RCT12SpriteBase* csrc) - { - ImportSpriteCommonProperties(cdst, csrc); - switch (RCT12MiscEntityType(csrc->type)) - { - case RCT12MiscEntityType::SteamParticle: - { - auto src = static_cast(csrc); - auto dst = static_cast(cdst); - dst->time_to_move = src->time_to_move; - dst->frame = src->frame; - break; - } - case RCT12MiscEntityType::MoneyEffect: - { - auto src = static_cast(csrc); - auto dst = static_cast(cdst); - dst->MoveDelay = src->move_delay; - dst->NumMovements = src->num_movements; - dst->Vertical = src->vertical; - dst->Value = src->value; - dst->OffsetX = src->offset_x; - dst->Wiggle = src->wiggle; - break; - } - case RCT12MiscEntityType::CrashedVehicleParticle: - { - auto src = static_cast(csrc); - auto dst = static_cast(cdst); - dst->frame = src->frame; - dst->time_to_live = src->time_to_live; - dst->frame = src->frame; - dst->colour[0] = src->colour[0]; - dst->colour[1] = src->colour[1]; - dst->crashed_sprite_base = src->crashed_sprite_base; - dst->velocity_x = src->velocity_x; - dst->velocity_y = src->velocity_y; - dst->velocity_z = src->velocity_z; - dst->acceleration_x = src->acceleration_x; - dst->acceleration_y = src->acceleration_y; - dst->acceleration_z = src->acceleration_z; - break; - } - case RCT12MiscEntityType::ExplosionCloud: - case RCT12MiscEntityType::ExplosionFlare: - case RCT12MiscEntityType::CrashSplash: - { - auto src = static_cast(csrc); - auto dst = static_cast(cdst); - dst->frame = src->frame; - break; - } - case RCT12MiscEntityType::JumpingFountainWater: - case RCT12MiscEntityType::JumpingFountainSnow: - { - auto* src = static_cast(csrc); - auto* dst = static_cast(cdst); - dst->NumTicksAlive = src->num_ticks_alive; - dst->frame = src->frame; - dst->FountainFlags = src->fountain_flags; - dst->TargetX = src->target_x; - dst->TargetY = src->target_y; - dst->Iteration = src->iteration; - dst->FountainType = RCT12MiscEntityType(src->type) == RCT12MiscEntityType::JumpingFountainSnow - ? JumpingFountainType::Snow - : JumpingFountainType::Water; - break; - } - case RCT12MiscEntityType::Balloon: - { - auto src = static_cast(csrc); - auto dst = static_cast(cdst); - dst->popped = src->popped; - dst->time_to_move = src->time_to_move; - dst->frame = src->frame; - dst->colour = src->colour; - break; - } - case RCT12MiscEntityType::Duck: - { - auto src = static_cast(csrc); - auto dst = static_cast(cdst); - dst->frame = src->frame; - dst->target_x = src->target_x; - dst->target_y = src->target_y; - dst->state = static_cast(src->state); - break; - } - default: - log_warning("Misc. sprite type %d can not be imported.", csrc->type); - break; - } - } - - void ImportSpriteLitter(Litter* dst, const RCT12SpriteLitter* src) - { - ImportSpriteCommonProperties(dst, src); - dst->SubType = LitterType(src->type); - dst->creationTick = src->creationTick; - } - constexpr EntityType GetEntityTypeFromRCT2Sprite(const RCT12SpriteBase* src) { EntityType output = EntityType::Null; @@ -1742,7 +1507,7 @@ public: return output; } - void ImportSpriteCommonProperties(SpriteBase* dst, const RCT12SpriteBase* src) + void ImportEntityCommonProperties(SpriteBase* dst, const RCT12SpriteBase* src) { dst->Type = GetEntityTypeFromRCT2Sprite(src); dst->sprite_height_negative = src->sprite_height_negative; @@ -1760,6 +1525,8 @@ public: dst->sprite_direction = src->sprite_direction; } + void ImportEntity(const RCT12SpriteBase& src); + std::string GetUserString(rct_string_id stringId) { const auto originalString = _s6.custom_strings[(stringId - USER_STRING_START) % 1024]; @@ -1793,6 +1560,288 @@ public: } }; +template<> void S6Importer::ImportEntity(const RCT12SpriteBase& baseSrc) +{ + auto dst = CreateEntityAt(baseSrc.sprite_index); + auto src = static_cast(&baseSrc); + const auto& ride = _s6.rides[src->ride]; + + ImportEntityCommonProperties(dst, src); + dst->SubType = Vehicle::Type(src->type); + dst->vehicle_sprite_type = src->vehicle_sprite_type; + dst->bank_rotation = src->bank_rotation; + dst->remaining_distance = src->remaining_distance; + dst->velocity = src->velocity; + dst->acceleration = src->acceleration; + dst->ride = src->ride; + dst->vehicle_type = src->vehicle_type; + dst->colours = src->colours; + dst->track_progress = src->track_progress; + dst->TrackLocation = { src->track_x, src->track_y, src->track_z }; + if (src->boat_location.isNull() || static_cast(ride.mode) != RideMode::BoatHire + || src->status != static_cast(Vehicle::Status::TravellingBoat)) + { + dst->BoatLocation.setNull(); + dst->SetTrackDirection(src->GetTrackDirection()); + dst->SetTrackType(src->GetTrackType()); + // RotationControlToggle and Booster are saved as the same track piece ID + // Which one the vehicle is using must be determined + if (GetRideTypeDescriptor(ride.type).HasFlag(RIDE_TYPE_FLAG_FLAT_RIDE)) + { + dst->SetTrackType(RCT12FlatTrackTypeToOpenRCT2(src->GetTrackType())); + } + else if (src->GetTrackType() == TrackElemType::RotationControlToggleAlias) + { + // Merging hacks mean the track type that's appropriate for the ride type is not necessarily the track type the + // ride is on. It's possible to create unwanted behavior if a user layers spinning control track on top of + // booster track but this is unlikely since only two rides have spinning control track - by default they load as + // booster + TileElement* tileElement2 = map_get_track_element_at_of_type_seq( + dst->TrackLocation, TrackElemType::RotationControlToggle, 0); + + if (tileElement2 != nullptr) + dst->SetTrackType(TrackElemType::RotationControlToggle); + } + } + else + { + dst->BoatLocation = TileCoordsXY{ src->boat_location.x, src->boat_location.y }.ToCoordsXY(); + dst->SetTrackDirection(0); + dst->SetTrackType(0); + } + + dst->next_vehicle_on_train = src->next_vehicle_on_train; + dst->prev_vehicle_on_ride = src->prev_vehicle_on_ride; + dst->next_vehicle_on_ride = src->next_vehicle_on_ride; + dst->var_44 = src->var_44; + dst->mass = src->mass; + dst->update_flags = src->update_flags; + dst->SwingSprite = src->SwingSprite; + dst->current_station = src->current_station; + dst->current_time = src->current_time; + dst->crash_z = src->crash_z; + + Vehicle::Status statusSrc = Vehicle::Status::MovingToEndOfStation; + if (src->status <= static_cast(Vehicle::Status::StoppedByBlockBrakes)) + { + statusSrc = static_cast(src->status); + } + + dst->status = statusSrc; + dst->sub_state = src->sub_state; + for (size_t i = 0; i < std::size(src->peep); i++) + { + dst->peep[i] = src->peep[i]; + dst->peep_tshirt_colours[i] = src->peep_tshirt_colours[i]; + } + dst->num_seats = src->num_seats; + dst->num_peeps = src->num_peeps; + dst->next_free_seat = src->next_free_seat; + dst->restraints_position = src->restraints_position; + dst->crash_x = src->crash_x; + dst->sound2_flags = src->sound2_flags; + dst->spin_sprite = src->spin_sprite; + dst->sound1_id = static_cast(src->sound1_id); + dst->sound1_volume = src->sound1_volume; + dst->sound2_id = static_cast(src->sound2_id); + dst->sound2_volume = src->sound2_volume; + dst->sound_vector_factor = src->sound_vector_factor; + dst->time_waiting = src->time_waiting; + dst->speed = src->speed; + dst->powered_acceleration = src->powered_acceleration; + dst->dodgems_collision_direction = src->dodgems_collision_direction; + dst->animation_frame = src->animation_frame; + dst->var_C8 = src->var_C8; + dst->var_CA = src->var_CA; + dst->scream_sound_id = static_cast(src->scream_sound_id); + dst->TrackSubposition = VehicleTrackSubposition{ src->TrackSubposition }; + dst->var_CE = src->var_CE; + dst->var_CF = src->var_CF; + dst->lost_time_out = src->lost_time_out; + dst->vertical_drop_countdown = src->vertical_drop_countdown; + dst->var_D3 = src->var_D3; + dst->mini_golf_current_animation = src->mini_golf_current_animation; + dst->mini_golf_flags = src->mini_golf_flags; + dst->ride_subtype = RCTEntryIndexToOpenRCT2EntryIndex(src->ride_subtype); + dst->colours_extended = src->colours_extended; + dst->seat_rotation = src->seat_rotation; + dst->target_seat_rotation = src->target_seat_rotation; +} + +template<> void S6Importer::ImportEntity(const RCT12SpriteBase& baseSrc) +{ + auto dst = CreateEntityAt(baseSrc.sprite_index); + auto src = static_cast(&baseSrc); + ImportEntityPeep(dst, src); +} + +template<> void S6Importer::ImportEntity(const RCT12SpriteBase& baseSrc) +{ + auto dst = CreateEntityAt(baseSrc.sprite_index); + auto src = static_cast(&baseSrc); + ImportEntityPeep(dst, src); +} + +template<> void S6Importer::ImportEntity(const RCT12SpriteBase& baseSrc) +{ + auto dst = CreateEntityAt(baseSrc.sprite_index); + auto src = static_cast(&baseSrc); + ImportEntityCommonProperties(dst, src); + dst->time_to_move = src->time_to_move; + dst->frame = src->frame; +} + +template<> void S6Importer::ImportEntity(const RCT12SpriteBase& baseSrc) +{ + auto dst = CreateEntityAt(baseSrc.sprite_index); + auto src = static_cast(&baseSrc); + ImportEntityCommonProperties(dst, src); + dst->MoveDelay = src->move_delay; + dst->NumMovements = src->num_movements; + dst->Vertical = src->vertical; + dst->Value = src->value; + dst->OffsetX = src->offset_x; + dst->Wiggle = src->wiggle; +} + +template<> void S6Importer::ImportEntity(const RCT12SpriteBase& baseSrc) +{ + auto dst = CreateEntityAt(baseSrc.sprite_index); + auto src = static_cast(&baseSrc); + ImportEntityCommonProperties(dst, src); + dst->frame = src->frame; + dst->time_to_live = src->time_to_live; + dst->frame = src->frame; + dst->colour[0] = src->colour[0]; + dst->colour[1] = src->colour[1]; + dst->crashed_sprite_base = src->crashed_sprite_base; + dst->velocity_x = src->velocity_x; + dst->velocity_y = src->velocity_y; + dst->velocity_z = src->velocity_z; + dst->acceleration_x = src->acceleration_x; + dst->acceleration_y = src->acceleration_y; + dst->acceleration_z = src->acceleration_z; +} + +template<> void S6Importer::ImportEntity(const RCT12SpriteBase& baseSrc) +{ + auto dst = CreateEntityAt(baseSrc.sprite_index); + auto src = static_cast(&baseSrc); + ImportEntityCommonProperties(dst, src); + dst->frame = src->frame; +} + +template<> void S6Importer::ImportEntity(const RCT12SpriteBase& baseSrc) +{ + auto dst = CreateEntityAt(baseSrc.sprite_index); + auto src = static_cast(&baseSrc); + ImportEntityCommonProperties(dst, src); + dst->frame = src->frame; +} + +template<> void S6Importer::ImportEntity(const RCT12SpriteBase& baseSrc) +{ + auto dst = CreateEntityAt(baseSrc.sprite_index); + auto src = static_cast(&baseSrc); + ImportEntityCommonProperties(dst, src); + dst->frame = src->frame; +} + +template<> void S6Importer::ImportEntity(const RCT12SpriteBase& baseSrc) +{ + auto dst = CreateEntityAt(baseSrc.sprite_index); + auto src = static_cast(&baseSrc); + ImportEntityCommonProperties(dst, src); + dst->NumTicksAlive = src->num_ticks_alive; + dst->frame = src->frame; + dst->FountainFlags = src->fountain_flags; + dst->TargetX = src->target_x; + dst->TargetY = src->target_y; + dst->Iteration = src->iteration; + dst->FountainType = RCT12MiscEntityType(src->type) == RCT12MiscEntityType::JumpingFountainSnow ? JumpingFountainType::Snow + : JumpingFountainType::Water; +} + +template<> void S6Importer::ImportEntity(const RCT12SpriteBase& baseSrc) +{ + auto dst = CreateEntityAt(baseSrc.sprite_index); + auto src = static_cast(&baseSrc); + ImportEntityCommonProperties(dst, src); + dst->popped = src->popped; + dst->time_to_move = src->time_to_move; + dst->frame = src->frame; + dst->colour = src->colour; +} + +template<> void S6Importer::ImportEntity(const RCT12SpriteBase& baseSrc) +{ + auto dst = CreateEntityAt(baseSrc.sprite_index); + auto src = static_cast(&baseSrc); + ImportEntityCommonProperties(dst, src); + dst->frame = src->frame; + dst->target_x = src->target_x; + dst->target_y = src->target_y; + dst->state = static_cast(src->state); +} + +template<> void S6Importer::ImportEntity(const RCT12SpriteBase& baseSrc) +{ + auto dst = CreateEntityAt(baseSrc.sprite_index); + auto src = static_cast(&baseSrc); + ImportEntityCommonProperties(dst, src); + dst->SubType = LitterType(src->type); + dst->creationTick = src->creationTick; +} + +void S6Importer::ImportEntity(const RCT12SpriteBase& src) +{ + switch (GetEntityTypeFromRCT2Sprite(&src)) + { + case EntityType::Vehicle: + ImportEntity(src); + break; + case EntityType::Guest: + ImportEntity(src); + break; + case EntityType::Staff: + ImportEntity(src); + break; + case EntityType::SteamParticle: + ImportEntity(src); + break; + case EntityType::MoneyEffect: + ImportEntity(src); + break; + case EntityType::CrashedVehicleParticle: + ImportEntity(src); + break; + case EntityType::ExplosionCloud: + ImportEntity(src); + break; + case EntityType::ExplosionFlare: + ImportEntity(src); + break; + case EntityType::CrashSplash: + ImportEntity(src); + break; + case EntityType::JumpingFountain: + ImportEntity(src); + break; + case EntityType::Balloon: + ImportEntity(src); + break; + case EntityType::Duck: + ImportEntity(src); + break; + case EntityType::Litter: + ImportEntity(src); + break; + default: + // Null elements do not need imported + break; + } +} + std::unique_ptr ParkImporter::CreateS6(IObjectRepository& objectRepository) { return std::make_unique(objectRepository); diff --git a/src/openrct2/world/EntityList.h b/src/openrct2/world/EntityList.h index 481db15cb4..2c43d33c9f 100644 --- a/src/openrct2/world/EntityList.h +++ b/src/openrct2/world/EntityList.h @@ -28,7 +28,6 @@ const std::list& GetEntityList(const EntityType id); uint16_t GetEntityListCount(EntityType list); uint16_t GetMiscEntityCount(); uint16_t GetNumFreeEntities(); -void RebuildEntityLists(); const std::vector& GetEntityTileList(const CoordsXY& spritePos); template class EntityTileIterator diff --git a/src/openrct2/world/Sprite.cpp b/src/openrct2/world/Sprite.cpp index 74e5114d7f..593b605d41 100644 --- a/src/openrct2/world/Sprite.cpp +++ b/src/openrct2/world/Sprite.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include static rct_sprite _spriteList[MAX_ENTITIES]; @@ -200,28 +201,21 @@ void SpriteBase::Invalidate() viewports_invalidate(sprite_left, sprite_top, sprite_right, sprite_bottom, maxZoom); } -void RebuildEntityLists() +static void ResetEntityLists() { for (auto& list : gEntityLists) { list.clear(); } +} +static void ResetFreeIds() +{ _freeIdList.clear(); - for (auto& ent : _spriteList) - { - if (ent.misc.Type == EntityType::Null) - { - _freeIdList.push_back(ent.misc.sprite_index); - } - else - { - gEntityLists[EnumValue(ent.misc.Type)].push_back(ent.misc.sprite_index); - } - } + _freeIdList.resize(MAX_ENTITIES); // List needs to be back to front to simplify removing - std::sort(std::begin(_freeIdList), std::end(_freeIdList), std::greater()); + std::iota(std::rbegin(_freeIdList), std::rend(_freeIdList), 0); } const std::list& GetEntityList(const EntityType id) @@ -250,7 +244,8 @@ void reset_sprite_list() _spriteFlashingList[i] = false; } - RebuildEntityLists(); + ResetEntityLists(); + ResetFreeIds(); reset_sprite_spatial_index(); } @@ -412,6 +407,27 @@ uint16_t GetMiscEntityCount() return count; } +static void PrepareNewEntity(SpriteBase* base, const EntityType type) +{ + // Need to reset all sprite data, as the uninitialised values + // may contain garbage and cause a desync later on. + sprite_reset(base); + + base->Type = type; + AddToEntityList(base); + + base->x = LOCATION_NULL; + base->y = LOCATION_NULL; + base->z = 0; + base->sprite_width = 0x10; + base->sprite_height_negative = 0x14; + base->sprite_height_positive = 0x8; + base->flags = 0; + base->sprite_left = LOCATION_NULL; + + SpriteSpatialInsert(base, { LOCATION_NULL, 0 }); +} + rct_sprite* create_sprite(EntityType type) { if (_freeIdList.size() == 0) @@ -439,27 +455,30 @@ rct_sprite* create_sprite(EntityType type) } _freeIdList.pop_back(); - // Need to reset all sprite data, as the uninitialised values - // may contain garbage and cause a desync later on. - sprite_reset(sprite); - - sprite->Type = type; - AddToEntityList(sprite); - - sprite->x = LOCATION_NULL; - sprite->y = LOCATION_NULL; - sprite->z = 0; - sprite->sprite_width = 0x10; - sprite->sprite_height_negative = 0x14; - sprite->sprite_height_positive = 0x8; - sprite->flags = 0; - sprite->sprite_left = LOCATION_NULL; - - SpriteSpatialInsert(sprite, { LOCATION_NULL, 0 }); + PrepareNewEntity(sprite, type); return reinterpret_cast(sprite); } +SpriteBase* CreateEntityAt(const uint16_t index, const EntityType type) +{ + auto id = std::lower_bound(std::rbegin(_freeIdList), std::rend(_freeIdList), index); + if (id == std::rend(_freeIdList) || *id != index) + { + return nullptr; + } + + auto* entity = GetEntity(index); + if (entity == nullptr) + { + return nullptr; + } + + _freeIdList.erase(std::next(id).base()); + + PrepareNewEntity(entity, type); + return entity; +} /** * * rct2: 0x00673200 diff --git a/src/openrct2/world/Sprite.h b/src/openrct2/world/Sprite.h index 406b83f0e8..1d94d4b1c9 100644 --- a/src/openrct2/world/Sprite.h +++ b/src/openrct2/world/Sprite.h @@ -194,6 +194,14 @@ template T* CreateEntity() { return reinterpret_cast(create_sprite(T::cEntityType)); } + +// Use only with imports that must happen at a specified index +SpriteBase* CreateEntityAt(const uint16_t index, const EntityType type); +// Use only with imports that must happen at a specified index +template T* CreateEntityAt(const uint16_t index) +{ + return static_cast(CreateEntityAt(index, T::cEntityType)); +} void reset_sprite_list(); void reset_sprite_spatial_index(); void sprite_clear_all_unused();