From 175394f15b98573e64242a1da977e5672f851570 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=CE=B6eh=20Matt?= <5415177+ZehMatt@users.noreply.github.com> Date: Sun, 15 Aug 2021 05:28:08 -0700 Subject: [PATCH] Fix #15193: Crash when rides/stalls are demolished --- distribution/changelog.txt | 1 + src/openrct2/actions/RideDemolishAction.cpp | 84 +-------------- src/openrct2/peep/Guest.cpp | 109 ++++++++++++++++++++ src/openrct2/peep/Peep.h | 4 + 4 files changed, 115 insertions(+), 83 deletions(-) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index c23034eb8b..f9c7ead1d9 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -13,6 +13,7 @@ - Fix: [#15148] Track Designs Manager delete confirmation window doesn't display properly. - Fix: [#15170] Plugin: incorrect label text alignment. - Fix: [#15184] Crash when hovering over water types in Object Selection. +- Fix: [#15193] Crash when rides/stalls are demolished. - Improved: [#3417] Crash dumps are now placed in their own folder. - Change: [#8601] Revert ToonTower base block fix to re-enable support blocking. - Change: [#15174] [Plugin] Deprecate the type "peep" and add support to target a specific scripting api version. diff --git a/src/openrct2/actions/RideDemolishAction.cpp b/src/openrct2/actions/RideDemolishAction.cpp index 7e5e0b89ab..1e584de91b 100644 --- a/src/openrct2/actions/RideDemolishAction.cpp +++ b/src/openrct2/actions/RideDemolishAction.cpp @@ -136,89 +136,7 @@ GameActions::Result::Ptr RideDemolishAction::DemolishRide(Ride* ride) const for (auto peep : EntityList()) { - uint8_t ride_id_bit = _rideIndex % 8; - uint8_t ride_id_offset = _rideIndex / 8; - - // clear ride from potentially being in RidesBeenOn - peep->RidesBeenOn[ride_id_offset] &= ~(1 << ride_id_bit); - if (peep->State == PeepState::Watching) - { - if (peep->CurrentRide == _rideIndex) - { - peep->CurrentRide = RIDE_ID_NULL; - if (peep->TimeToStand >= 50) - { - // make peep stop watching the ride - peep->TimeToStand = 50; - } - } - } - - // remove any free voucher for this ride from peep - if (peep->HasItem(ShopItem::Voucher)) - { - if (peep->VoucherType == VOUCHER_TYPE_RIDE_FREE && peep->VoucherRideId == _rideIndex) - { - peep->RemoveItem(ShopItem::Voucher); - } - } - - // remove any photos of this ride from peep - if (peep->HasItem(ShopItem::Photo)) - { - if (peep->Photo1RideRef == _rideIndex) - { - peep->RemoveItem(ShopItem::Photo); - } - } - if (peep->HasItem(ShopItem::Photo2)) - { - if (peep->Photo2RideRef == _rideIndex) - { - peep->RemoveItem(ShopItem::Photo2); - } - } - if (peep->HasItem(ShopItem::Photo3)) - { - if (peep->Photo3RideRef == _rideIndex) - { - peep->RemoveItem(ShopItem::Photo3); - } - } - if (peep->HasItem(ShopItem::Photo4)) - { - if (peep->Photo4RideRef == _rideIndex) - { - peep->RemoveItem(ShopItem::Photo4); - } - } - - if (peep->GuestHeadingToRideId == _rideIndex) - { - peep->GuestHeadingToRideId = RIDE_ID_NULL; - } - if (peep->FavouriteRide == _rideIndex) - { - peep->FavouriteRide = RIDE_ID_NULL; - } - - for (int32_t i = 0; i < PEEP_MAX_THOUGHTS; i++) - { - // Don't touch items after the first NONE thought as they are not valid - // fixes issues with clearing out bad thought data in multiplayer - if (peep->Thoughts[i].type == PeepThoughtType::None) - break; - - if (peep->Thoughts[i].type != PeepThoughtType::None && peep->Thoughts[i].item == _rideIndex) - { - // Clear top thought, push others up - memmove(&peep->Thoughts[i], &peep->Thoughts[i + 1], sizeof(rct_peep_thought) * (PEEP_MAX_THOUGHTS - i - 1)); - peep->Thoughts[PEEP_MAX_THOUGHTS - 1].type = PeepThoughtType::None; - peep->Thoughts[PEEP_MAX_THOUGHTS - 1].item = PEEP_THOUGHT_ITEM_NONE; - // Next iteration, check the new thought at this index - i--; - } - } + peep->RemoveRideFromMemory(_rideIndex); } MarketingCancelCampaignsForRide(_rideIndex); diff --git a/src/openrct2/peep/Guest.cpp b/src/openrct2/peep/Guest.cpp index 828082215b..2aa6787403 100644 --- a/src/openrct2/peep/Guest.cpp +++ b/src/openrct2/peep/Guest.cpp @@ -7356,3 +7356,112 @@ bool Guest::HasItem(ShopItem peepItem) const { return GetItemFlags() & EnumToFlag(peepItem); } + +static bool IsThoughtShopItemRelated(const PeepThoughtType type) +{ + switch (type) + { + case PeepThoughtType::AlreadyGot: + case PeepThoughtType::HaventFinished: + case PeepThoughtType::CantAffordItem: + return true; + default: + break; + } + return false; +} + +void Guest::RemoveRideFromMemory(ride_id_t rideId) +{ + uint8_t ride_id_bit = rideId % 8; + uint8_t ride_id_offset = rideId / 8; + + // clear ride from potentially being in RidesBeenOn + RidesBeenOn[ride_id_offset] &= ~(1 << ride_id_bit); + if (State == PeepState::Watching) + { + if (CurrentRide == rideId) + { + CurrentRide = RIDE_ID_NULL; + if (TimeToStand >= 50) + { + // make peep stop watching the ride + TimeToStand = 50; + } + } + } + + // remove any free voucher for this ride from peep + if (HasItem(ShopItem::Voucher)) + { + if (VoucherType == VOUCHER_TYPE_RIDE_FREE && VoucherRideId == rideId) + { + RemoveItem(ShopItem::Voucher); + } + } + + // remove any photos of this ride from peep + if (HasItem(ShopItem::Photo)) + { + if (Photo1RideRef == rideId) + { + RemoveItem(ShopItem::Photo); + } + } + if (HasItem(ShopItem::Photo2)) + { + if (Photo2RideRef == rideId) + { + RemoveItem(ShopItem::Photo2); + } + } + if (HasItem(ShopItem::Photo3)) + { + if (Photo3RideRef == rideId) + { + RemoveItem(ShopItem::Photo3); + } + } + if (HasItem(ShopItem::Photo4)) + { + if (Photo4RideRef == rideId) + { + RemoveItem(ShopItem::Photo4); + } + } + + if (GuestHeadingToRideId == rideId) + { + GuestHeadingToRideId = RIDE_ID_NULL; + } + if (FavouriteRide == rideId) + { + FavouriteRide = RIDE_ID_NULL; + } + + // Erase all thoughts that contain the ride. + for (auto it = std::begin(Thoughts); it != std::end(Thoughts);) + { + const auto& entry = *it; + if (entry.type == PeepThoughtType::None) + break; + + // Ride ids and shop item ids might have the same value, look only for ride thoughts. + if (IsThoughtShopItemRelated(entry.type) || entry.item != rideId) + { + it++; + continue; + } + + if (auto itNext = std::next(it); itNext != std::end(Thoughts)) + { + // Overwrite this entry by shifting all entries that follow. + std::rotate(it, itNext, std::end(Thoughts)); + } + + // Last slot is now free. + auto& lastEntry = Thoughts.back(); + lastEntry.type = PeepThoughtType::None; + lastEntry.item = PEEP_THOUGHT_ITEM_NONE; + } +} diff --git a/src/openrct2/peep/Peep.h b/src/openrct2/peep/Peep.h index 7b0d176ebe..6683f419c9 100644 --- a/src/openrct2/peep/Peep.h +++ b/src/openrct2/peep/Peep.h @@ -784,6 +784,10 @@ public: bool HasItem(ShopItem peepItem) const; void Serialise(DataSerialiser& stream); + // Removes the ride from the guests memory, this includes + // the history, thoughts, etc. + void RemoveRideFromMemory(ride_id_t rideId); + private: void UpdateRide(); void UpdateOnRide(){}; // TODO