diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 266d0d9f9b..5e7d03823f 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -8,6 +8,7 @@ - Fix: [#6152] Camera and UI are no longer locked at 40 Hz, providing a smoother experience. - Fix: [#9534] Screams no longer cut-off on steep diagonal drops. - Fix: [#19450] The correct element is now auto-suggested when building a Medium Half Loop backwards. +- Fix: [#19822] Tile inspector does not deep copy banners. - Fix: [#19823] Parkobj: disallow overriding objects of different object types. - Fix: [#19878] Unresearched scenery can be placed via prebuilt rides. - Fix: [#20083] Cannot use terrain surfaces with ID > 32 and terrain edges with ID > 16. diff --git a/src/openrct2-ui/windows/TileInspector.cpp b/src/openrct2-ui/windows/TileInspector.cpp index c448098e51..7884b602c8 100644 --- a/src/openrct2-ui/windows/TileInspector.cpp +++ b/src/openrct2-ui/windows/TileInspector.cpp @@ -483,6 +483,7 @@ private: bool _applyToAll = false; bool _elementCopied = false; TileElement _copiedElement; + Banner _copiedBanner; public: void OnOpen() override @@ -1835,13 +1836,21 @@ private: { // Copy value, in case the element gets moved _copiedElement = *GetSelectedElement(); + _copiedBanner = {}; + auto bannerIndex = _copiedElement.GetBannerIndex(); + if (bannerIndex != BannerIndex::GetNull()) + { + auto banner = GetBanner(bannerIndex); + if (banner != nullptr) + _copiedBanner = *banner; + } _elementCopied = true; Invalidate(); } void PasteElement() { - auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnyPaste, 0, 0, _copiedElement); + auto modifyTile = TileModifyAction(_toolMap, TileModifyType::AnyPaste, 0, 0, _copiedElement, _copiedBanner); GameActions::Execute(&modifyTile); } diff --git a/src/openrct2/actions/TileModifyAction.cpp b/src/openrct2/actions/TileModifyAction.cpp index 72bf7d7acb..af5664afa6 100644 --- a/src/openrct2/actions/TileModifyAction.cpp +++ b/src/openrct2/actions/TileModifyAction.cpp @@ -14,12 +14,13 @@ using namespace OpenRCT2; TileModifyAction::TileModifyAction( - CoordsXY loc, TileModifyType setting, uint32_t value1, uint32_t value2, TileElement pasteElement) + CoordsXY loc, TileModifyType setting, uint32_t value1, uint32_t value2, TileElement pasteElement, Banner pasteBanner) : _loc(loc) , _setting(setting) , _value1(value1) , _value2(value2) , _pasteElement(pasteElement) + , _pasteBanner(pasteBanner) { } @@ -40,7 +41,8 @@ void TileModifyAction::Serialise(DataSerialiser& stream) { GameAction::Serialise(stream); - stream << DS_TAG(_loc) << DS_TAG(_setting) << DS_TAG(_value1) << DS_TAG(_value2) << DS_TAG(_pasteElement); + stream << DS_TAG(_loc) << DS_TAG(_setting) << DS_TAG(_value1) << DS_TAG(_value2) << DS_TAG(_pasteElement) + << DS_TAG(_pasteBanner); } GameActions::Result TileModifyAction::Query() const @@ -89,7 +91,7 @@ GameActions::Result TileModifyAction::QueryExecute(bool isExecuting) const } case TileModifyType::AnyPaste: { - res = TileInspector::PasteElementAt(_loc, _pasteElement, isExecuting); + res = TileInspector::PasteElementAt(_loc, _pasteElement, _pasteBanner, isExecuting); break; } case TileModifyType::AnySort: diff --git a/src/openrct2/actions/TileModifyAction.h b/src/openrct2/actions/TileModifyAction.h index c4c218fc76..6af9559bc0 100644 --- a/src/openrct2/actions/TileModifyAction.h +++ b/src/openrct2/actions/TileModifyAction.h @@ -49,11 +49,13 @@ private: uint32_t _value1{}; uint32_t _value2{}; TileElement _pasteElement{}; + Banner _pasteBanner{}; public: TileModifyAction() = default; TileModifyAction( - CoordsXY loc, TileModifyType setting, uint32_t value1 = 0, uint32_t value2 = 0, TileElement pasteElement = {}); + CoordsXY loc, TileModifyType setting, uint32_t value1 = 0, uint32_t value2 = 0, TileElement pasteElement = {}, + Banner _pasteBanner = {}); void AcceptParameters(GameActionParameterVisitor& visitor) override; diff --git a/src/openrct2/core/DataSerialiserTraits.h b/src/openrct2/core/DataSerialiserTraits.h index 017d7a6792..ee1663b239 100644 --- a/src/openrct2/core/DataSerialiserTraits.h +++ b/src/openrct2/core/DataSerialiserTraits.h @@ -890,3 +890,38 @@ template struct DataSerializerTraitsTWrite(msg, strlen(msg)); } }; + +template<> struct DataSerializerTraitsT +{ + static void encode(OpenRCT2::IStream* stream, const Banner& banner) + { + DataSerializerTraits().encode(stream, banner.id); + DataSerializerTraits().encode(stream, banner.type); + stream->WriteValue(banner.flags); + stream->WriteString(banner.text); + stream->WriteValue(banner.colour); + DataSerializerTraits().encode(stream, banner.ride_index); + stream->WriteValue(banner.text_colour); + DataSerializerTraits().encode(stream, banner.position); + } + + static void decode(OpenRCT2::IStream* stream, Banner& banner) + { + DataSerializerTraits().decode(stream, banner.id); + DataSerializerTraits().decode(stream, banner.type); + stream->Read(&banner.flags); + banner.text = stream->ReadStdString(); + stream->Read(&banner.colour); + DataSerializerTraits().decode(stream, banner.ride_index); + stream->Read(&banner.text_colour); + DataSerializerTraits().decode(stream, banner.position); + } + + static void log(OpenRCT2::IStream* stream, const Banner& banner) + { + char msg[128] = {}; + snprintf( + msg, sizeof(msg), "Banner(x = %d, y = %d, text = %s)", banner.position.x, banner.position.y, banner.text.c_str()); + stream->Write(msg, strlen(msg)); + } +}; \ No newline at end of file diff --git a/src/openrct2/world/TileInspector.cpp b/src/openrct2/world/TileInspector.cpp index d3a2e0a095..037e9c4a01 100644 --- a/src/openrct2/world/TileInspector.cpp +++ b/src/openrct2/world/TileInspector.cpp @@ -320,7 +320,7 @@ namespace OpenRCT2::TileInspector return GameActions::Result(); } - GameActions::Result PasteElementAt(const CoordsXY& loc, TileElement element, bool isExecuting) + GameActions::Result PasteElementAt(const CoordsXY& loc, TileElement element, Banner banner, bool isExecuting) { // Make sure there is enough space for the new element if (!MapCheckCapacityAndReorganise(loc)) @@ -330,28 +330,31 @@ namespace OpenRCT2::TileInspector auto tileLoc = TileCoordsXY(loc); - auto bannerIndex = element.GetBannerIndex(); - if (bannerIndex != BannerIndex::GetNull() && GetBanner(bannerIndex) == nullptr) - { - return GameActions::Result(GameActions::Status::Unknown, STR_NONE, STR_NONE); - } - if (isExecuting) { - // Check if the element to be pasted refers to a banner index - if (bannerIndex != BannerIndex::GetNull()) + // Check if the element to be pasted has a banner + if (element.GetBannerIndex() != BannerIndex::GetNull()) { - // The element to be pasted refers to a banner index - make a copy of it + // The element to be pasted has a banner - make a copy of it from the banner provided auto newBanner = CreateBanner(); if (newBanner == nullptr) { LOG_ERROR("No free banners available"); return GameActions::Result(GameActions::Status::Unknown, STR_TOO_MANY_BANNERS_IN_GAME, STR_NONE); } + auto newId = newBanner->id; // Copy the banners style - *newBanner = *GetBanner(bannerIndex); + *newBanner = banner; // Reset the location to the paste location newBanner->position = tileLoc; + newBanner->id = newId; + + // If the linked ride has been destroyed since copying, unlink the pasted banner + if (newBanner->flags & BANNER_FLAG_LINKED_TO_RIDE && GetRide(newBanner->ride_index) == nullptr) + { + newBanner->flags &= ~BANNER_FLAG_LINKED_TO_RIDE; + newBanner->ride_index = RideId::GetNull(); + } // Use the new banner index element.SetBannerIndex(newBanner->id); diff --git a/src/openrct2/world/TileInspector.h b/src/openrct2/world/TileInspector.h index 5e9d210616..03a7f8501a 100644 --- a/src/openrct2/world/TileInspector.h +++ b/src/openrct2/world/TileInspector.h @@ -27,7 +27,7 @@ namespace OpenRCT2::TileInspector GameActions::Result SwapElementsAt(const CoordsXY& loc, int16_t first, int16_t second, bool isExecuting); GameActions::Result RotateElementAt(const CoordsXY& loc, int32_t elementIndex, bool isExecuting); GameActions::Result ToggleInvisibilityOfElementAt(const CoordsXY& loc, int32_t elementIndex, bool isExecuting); - GameActions::Result PasteElementAt(const CoordsXY& loc, TileElement element, bool isExecuting); + GameActions::Result PasteElementAt(const CoordsXY& loc, TileElement element, Banner banner, bool isExecuting); GameActions::Result SortElementsAt(const CoordsXY& loc, bool isExecuting); GameActions::Result AnyBaseHeightOffset(const CoordsXY& loc, int16_t elementIndex, int8_t heightOffset, bool isExecuting); GameActions::Result SurfaceShowParkFences(const CoordsXY& loc, bool showFences, bool isExecuting);