diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 7511907470..15431e96f9 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -19,6 +19,7 @@ - Feature: [#16731] [Plugin] New API for fetching and manipulating a staff member's patrol area. - Feature: [#16800] [Plugin] Add lift hill speed properties to API. - Feature: [#16806] Parkobj can load sprites from RCT image archives. +- Feature: [#16831] Allow ternary colours for small and large scenery objects. - Improved: [#3517] Cheats are now saved with the park. - Improved: [#10150] Ride stations are now properly checked if they’re sheltered. - Improved: [#10664, #16072] Visibility status can be modified directly in the Tile Inspector's list. diff --git a/src/openrct2-ui/windows/Scenery.cpp b/src/openrct2-ui/windows/Scenery.cpp index 6710ade397..c17319285a 100644 --- a/src/openrct2-ui/windows/Scenery.cpp +++ b/src/openrct2-ui/windows/Scenery.cpp @@ -571,6 +571,8 @@ public: widgets[WIDX_SCENERY_PRIMARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_SECONDARY_COLOUR) widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_TERTIARY_COLOUR) + widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; } else if (tabSelectedScenery.SceneryType == SCENERY_TYPE_WALL) { @@ -600,6 +602,8 @@ public: if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR)) widgets[WIDX_SCENERY_SECONDARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_TERTIARY_COLOUR)) + widgets[WIDX_SCENERY_TERTIARY_COLOUR_BUTTON].type = WindowWidgetType::ColourBtn; } } } @@ -1200,8 +1204,13 @@ private: else if (scenerySelection.SceneryType == SCENERY_TYPE_LARGE) { auto sceneryEntry = get_large_scenery_entry(scenerySelection.EntryIndex); - auto imageId = ImageId( - sceneryEntry->image + gWindowSceneryRotation, gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour); + auto imageId = ImageId(sceneryEntry->image + gWindowSceneryRotation); + if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR) + imageId = imageId.WithPrimary(gWindowSceneryPrimaryColour); + if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_SECONDARY_COLOUR) + imageId = imageId.WithSecondary(gWindowScenerySecondaryColour); + if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_TERTIARY_COLOUR) + imageId = imageId.WithTertiary(gWindowSceneryTertiaryColour); gfx_draw_sprite(&dpi, imageId, { 33, 0 }); } else if (scenerySelection.SceneryType == SCENERY_TYPE_WALL) @@ -1258,6 +1267,10 @@ private: imageId = imageId.WithSecondary(gWindowScenerySecondaryColour); } } + if (sceneryEntry->flags & SMALL_SCENERY_FLAG_HAS_TERTIARY_COLOUR) + { + imageId = imageId.WithTertiary(gWindowSceneryTertiaryColour); + } auto spriteTop = (sceneryEntry->height / 4) + 43; if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE) && sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_VOFFSET_CENTRE)) diff --git a/src/openrct2-ui/windows/TopToolbar.cpp b/src/openrct2-ui/windows/TopToolbar.cpp index 867cd4e7e0..f176e0e469 100644 --- a/src/openrct2-ui/windows/TopToolbar.cpp +++ b/src/openrct2-ui/windows/TopToolbar.cpp @@ -1036,7 +1036,7 @@ static void RepaintSceneryToolDown(const ScreenCoordsXY& windowPos, rct_widgetin uint8_t quadrant = info.Element->AsSmallScenery()->GetSceneryQuadrant(); auto repaintScenery = SmallScenerySetColourAction( { info.Loc, info.Element->GetBaseZ() }, quadrant, info.Element->AsSmallScenery()->GetEntryIndex(), - gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour); + gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); GameActions::Execute(&repaintScenery); break; @@ -1066,7 +1066,8 @@ static void RepaintSceneryToolDown(const ScreenCoordsXY& windowPos, rct_widgetin auto repaintScenery = LargeScenerySetColourAction( { info.Loc, info.Element->GetBaseZ(), info.Element->GetDirection() }, - info.Element->AsLargeScenery()->GetSequenceIndex(), gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour); + info.Element->AsLargeScenery()->GetSequenceIndex(), gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, + gWindowSceneryTertiaryColour); GameActions::Execute(&repaintScenery); break; @@ -1812,7 +1813,7 @@ static void WindowTopToolbarSceneryToolDown(const ScreenCoordsXY& windowPos, rct { auto smallSceneryPlaceAction = SmallSceneryPlaceAction( { cur_grid_x, cur_grid_y, gSceneryPlaceZ, gSceneryPlaceRotation }, quadrant, selectedScenery, - gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour); + gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); auto res = GameActions::Query(&smallSceneryPlaceAction); success = res.Error; if (res.Error == GameActions::Status::Ok) @@ -1835,7 +1836,7 @@ static void WindowTopToolbarSceneryToolDown(const ScreenCoordsXY& windowPos, rct { auto smallSceneryPlaceAction = SmallSceneryPlaceAction( { cur_grid_x, cur_grid_y, gSceneryPlaceZ, gSceneryPlaceRotation }, quadrant, selectedScenery, - gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour); + gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); smallSceneryPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { if (result->Error == GameActions::Status::Ok) @@ -1947,7 +1948,8 @@ static void WindowTopToolbarSceneryToolDown(const ScreenCoordsXY& windowPos, rct CoordsXYZD loc = { gridPos, gSceneryPlaceZ, direction }; auto sceneryPlaceAction = LargeSceneryPlaceAction( - loc, selectedScenery, gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour); + loc, selectedScenery, gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, + gWindowSceneryTertiaryColour); auto res = GameActions::Query(&sceneryPlaceAction); if (res.Error == GameActions::Status::Ok) @@ -1972,7 +1974,7 @@ static void WindowTopToolbarSceneryToolDown(const ScreenCoordsXY& windowPos, rct CoordsXYZD loc = { gridPos, gSceneryPlaceZ, direction }; auto sceneryPlaceAction = LargeSceneryPlaceAction( - loc, selectedScenery, gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour); + loc, selectedScenery, gWindowSceneryPrimaryColour, gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); sceneryPlaceAction.SetCallback([=](const GameAction* ga, const GameActions::Result* result) { if (result->Error == GameActions::Status::Ok) { @@ -2469,12 +2471,14 @@ static void TopToolbarToolUpdateWater(const ScreenCoordsXY& screenPos) * On success places ghost scenery and returns cost to place proper */ static money64 TryPlaceGhostSmallScenery( - CoordsXYZD loc, uint8_t quadrant, ObjectEntryIndex entryIndex, colour_t primaryColour, colour_t secondaryColour) + CoordsXYZD loc, uint8_t quadrant, ObjectEntryIndex entryIndex, colour_t primaryColour, colour_t secondaryColour, + colour_t tertiaryColour) { scenery_remove_ghost_tool_placement(); // 6e252b - auto smallSceneryPlaceAction = SmallSceneryPlaceAction(loc, quadrant, entryIndex, primaryColour, secondaryColour); + auto smallSceneryPlaceAction = SmallSceneryPlaceAction( + loc, quadrant, entryIndex, primaryColour, secondaryColour, tertiaryColour); smallSceneryPlaceAction.SetFlags(GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED); auto res = GameActions::Execute(&smallSceneryPlaceAction); if (res.Error != GameActions::Status::Ok) @@ -2553,12 +2557,12 @@ static money64 TryPlaceGhostWall( } static money64 TryPlaceGhostLargeScenery( - CoordsXYZD loc, ObjectEntryIndex entryIndex, colour_t primaryColour, colour_t secondaryColour) + CoordsXYZD loc, ObjectEntryIndex entryIndex, colour_t primaryColour, colour_t secondaryColour, colour_t tertiaryColour) { scenery_remove_ghost_tool_placement(); // 6e25a7 - auto sceneryPlaceAction = LargeSceneryPlaceAction(loc, entryIndex, primaryColour, secondaryColour); + auto sceneryPlaceAction = LargeSceneryPlaceAction(loc, entryIndex, primaryColour, secondaryColour, tertiaryColour); sceneryPlaceAction.SetFlags(GAME_COMMAND_FLAG_GHOST | GAME_COMMAND_FLAG_ALLOW_DURING_PAUSED | GAME_COMMAND_FLAG_NO_SPEND); auto res = GameActions::Execute(&sceneryPlaceAction); if (res.Error != GameActions::Status::Ok) @@ -2710,7 +2714,7 @@ static void TopToolbarToolUpdateScenery(const ScreenCoordsXY& screenPos) { cost = TryPlaceGhostSmallScenery( { mapTile, gSceneryPlaceZ, rotation }, quadrant, selection.EntryIndex, gWindowSceneryPrimaryColour, - gWindowScenerySecondaryColour); + gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); if (cost != MONEY64_UNDEFINED) break; @@ -2866,7 +2870,7 @@ static void TopToolbarToolUpdateScenery(const ScreenCoordsXY& screenPos) { cost = TryPlaceGhostLargeScenery( { mapTile, gSceneryPlaceZ, direction }, selection.EntryIndex, gWindowSceneryPrimaryColour, - gWindowScenerySecondaryColour); + gWindowScenerySecondaryColour, gWindowSceneryTertiaryColour); if (cost != MONEY64_UNDEFINED) break; diff --git a/src/openrct2/actions/LargeSceneryPlaceAction.cpp b/src/openrct2/actions/LargeSceneryPlaceAction.cpp index 23612ea6bd..8429700015 100644 --- a/src/openrct2/actions/LargeSceneryPlaceAction.cpp +++ b/src/openrct2/actions/LargeSceneryPlaceAction.cpp @@ -20,11 +20,12 @@ #include "../world/Surface.h" LargeSceneryPlaceAction::LargeSceneryPlaceAction( - const CoordsXYZD& loc, ObjectEntryIndex sceneryType, uint8_t primaryColour, uint8_t secondaryColour) + const CoordsXYZD& loc, ObjectEntryIndex sceneryType, uint8_t primaryColour, uint8_t secondaryColour, uint8_t tertiaryColour) : _loc(loc) , _sceneryType(sceneryType) , _primaryColour(primaryColour) , _secondaryColour(secondaryColour) + , _tertiaryColour(tertiaryColour) { } @@ -34,6 +35,7 @@ void LargeSceneryPlaceAction::AcceptParameters(GameActionParameterVisitor& visit visitor.Visit("object", _sceneryType); visitor.Visit("primaryColour", _primaryColour); visitor.Visit("secondaryColour", _secondaryColour); + visitor.Visit("tertiaryColour", _tertiaryColour); } uint16_t LargeSceneryPlaceAction::GetActionFlags() const @@ -45,7 +47,8 @@ void LargeSceneryPlaceAction::Serialise(DataSerialiser& stream) { GameAction::Serialise(stream); - stream << DS_TAG(_loc) << DS_TAG(_sceneryType) << DS_TAG(_primaryColour) << DS_TAG(_secondaryColour); + stream << DS_TAG(_loc) << DS_TAG(_sceneryType) << DS_TAG(_primaryColour) << DS_TAG(_secondaryColour) + << DS_TAG(_tertiaryColour); } GameActions::Result LargeSceneryPlaceAction::Query() const @@ -62,7 +65,7 @@ GameActions::Result LargeSceneryPlaceAction::Query() const money32 supportsCost = 0; - if (_primaryColour > TILE_ELEMENT_COLOUR_MASK || _secondaryColour > TILE_ELEMENT_COLOUR_MASK) + if (_primaryColour >= COLOUR_COUNT || _secondaryColour >= COLOUR_COUNT || _tertiaryColour >= COLOUR_COUNT) { log_error( "Invalid game command for scenery placement, primaryColour = %u, secondaryColour = %u", _primaryColour, @@ -381,6 +384,7 @@ void LargeSceneryPlaceAction::SetNewLargeSceneryElement(LargeSceneryElement& sce sceneryElement.SetSequenceIndex(tileNum); sceneryElement.SetPrimaryColour(_primaryColour); sceneryElement.SetSecondaryColour(_secondaryColour); + sceneryElement.SetTertiaryColour(_tertiaryColour); if (GetFlags() & GAME_COMMAND_FLAG_GHOST) { diff --git a/src/openrct2/actions/LargeSceneryPlaceAction.h b/src/openrct2/actions/LargeSceneryPlaceAction.h index 4eb294bd79..f26eb22686 100644 --- a/src/openrct2/actions/LargeSceneryPlaceAction.h +++ b/src/openrct2/actions/LargeSceneryPlaceAction.h @@ -27,12 +27,14 @@ private: ObjectEntryIndex _sceneryType{ OBJECT_ENTRY_INDEX_NULL }; uint8_t _primaryColour{}; uint8_t _secondaryColour{}; + uint8_t _tertiaryColour{}; public: LargeSceneryPlaceAction() = default; LargeSceneryPlaceAction( - const CoordsXYZD& loc, ObjectEntryIndex sceneryType, uint8_t primaryColour, uint8_t secondaryColour); + const CoordsXYZD& loc, ObjectEntryIndex sceneryType, uint8_t primaryColour, uint8_t secondaryColour, + uint8_t tertiaryColour); void AcceptParameters(GameActionParameterVisitor& visitor) override; diff --git a/src/openrct2/actions/LargeScenerySetColourAction.cpp b/src/openrct2/actions/LargeScenerySetColourAction.cpp index 255c20af7d..a3bbffb899 100644 --- a/src/openrct2/actions/LargeScenerySetColourAction.cpp +++ b/src/openrct2/actions/LargeScenerySetColourAction.cpp @@ -14,11 +14,12 @@ #include "../world/Scenery.h" LargeScenerySetColourAction::LargeScenerySetColourAction( - const CoordsXYZD& loc, uint8_t tileIndex, uint8_t primaryColour, uint8_t secondaryColour) + const CoordsXYZD& loc, uint8_t tileIndex, uint8_t primaryColour, uint8_t secondaryColour, uint8_t tertiaryColour) : _loc(loc) , _tileIndex(tileIndex) , _primaryColour(primaryColour) , _secondaryColour(secondaryColour) + , _tertiaryColour(tertiaryColour) { } @@ -31,7 +32,8 @@ void LargeScenerySetColourAction::Serialise(DataSerialiser& stream) { GameAction::Serialise(stream); - stream << DS_TAG(_loc) << DS_TAG(_tileIndex) << DS_TAG(_primaryColour) << DS_TAG(_secondaryColour); + stream << DS_TAG(_loc) << DS_TAG(_tileIndex) << DS_TAG(_primaryColour) << DS_TAG(_secondaryColour) + << DS_TAG(_tertiaryColour); } GameActions::Result LargeScenerySetColourAction::Query() const @@ -60,15 +62,21 @@ GameActions::Result LargeScenerySetColourAction::QueryExecute(bool isExecuting) return GameActions::Result(GameActions::Status::InvalidParameters, STR_CANT_REPAINT_THIS, STR_NONE); } - if (_primaryColour > 31) + if (_primaryColour >= COLOUR_COUNT) { log_error("Invalid primary colour: colour = %u", _primaryColour); return GameActions::Result(GameActions::Status::InvalidParameters, STR_CANT_REPAINT_THIS, STR_NONE); } - if (_secondaryColour > 31) + if (_secondaryColour >= COLOUR_COUNT) { - log_error("Invalid primary colour: colour = %u", _secondaryColour); + log_error("Invalid secondary colour: colour = %u", _secondaryColour); + return GameActions::Result(GameActions::Status::InvalidParameters, STR_CANT_REPAINT_THIS, STR_NONE); + } + + if (_tertiaryColour >= COLOUR_COUNT) + { + log_error("Invalid tertiary colour: colour = %u", _tertiaryColour); return GameActions::Result(GameActions::Status::InvalidParameters, STR_CANT_REPAINT_THIS, STR_NONE); } @@ -135,6 +143,7 @@ GameActions::Result LargeScenerySetColourAction::QueryExecute(bool isExecuting) { tileElement->SetPrimaryColour(_primaryColour); tileElement->SetSecondaryColour(_secondaryColour); + tileElement->SetTertiaryColour(_tertiaryColour); map_invalidate_tile_full(currentTile); } diff --git a/src/openrct2/actions/LargeScenerySetColourAction.h b/src/openrct2/actions/LargeScenerySetColourAction.h index d8525706d5..70481afd21 100644 --- a/src/openrct2/actions/LargeScenerySetColourAction.h +++ b/src/openrct2/actions/LargeScenerySetColourAction.h @@ -18,10 +18,12 @@ private: uint8_t _tileIndex{}; uint8_t _primaryColour{}; uint8_t _secondaryColour{}; + uint8_t _tertiaryColour{}; public: LargeScenerySetColourAction() = default; - LargeScenerySetColourAction(const CoordsXYZD& loc, uint8_t tileIndex, uint8_t primaryColour, uint8_t secondaryColour); + LargeScenerySetColourAction( + const CoordsXYZD& loc, uint8_t tileIndex, uint8_t primaryColour, uint8_t secondaryColour, uint8_t tertiaryColour); uint16_t GetActionFlags() const override; diff --git a/src/openrct2/actions/SmallSceneryPlaceAction.cpp b/src/openrct2/actions/SmallSceneryPlaceAction.cpp index 30abcbb6b4..ca7542dde8 100644 --- a/src/openrct2/actions/SmallSceneryPlaceAction.cpp +++ b/src/openrct2/actions/SmallSceneryPlaceAction.cpp @@ -29,12 +29,14 @@ #include "SmallSceneryRemoveAction.h" SmallSceneryPlaceAction::SmallSceneryPlaceAction( - const CoordsXYZD& loc, uint8_t quadrant, ObjectEntryIndex sceneryType, uint8_t primaryColour, uint8_t secondaryColour) + const CoordsXYZD& loc, uint8_t quadrant, ObjectEntryIndex sceneryType, uint8_t primaryColour, uint8_t secondaryColour, + uint8_t tertiaryColour) : _loc(loc) , _quadrant(quadrant) , _sceneryType(sceneryType) , _primaryColour(primaryColour) , _secondaryColour(secondaryColour) + , _tertiaryColour(tertiaryColour) { } @@ -419,6 +421,7 @@ GameActions::Result SmallSceneryPlaceAction::Execute() const sceneryElement->SetAge(0); sceneryElement->SetPrimaryColour(_primaryColour); sceneryElement->SetSecondaryColour(_secondaryColour); + sceneryElement->SetTertiaryColour(_tertiaryColour); sceneryElement->SetClearanceZ(sceneryElement->GetBaseZ() + sceneryEntry->height + 7); sceneryElement->SetGhost(GetFlags() & GAME_COMMAND_FLAG_GHOST); if (supportsRequired) diff --git a/src/openrct2/actions/SmallSceneryPlaceAction.h b/src/openrct2/actions/SmallSceneryPlaceAction.h index 2e40f4fe58..47877c439f 100644 --- a/src/openrct2/actions/SmallSceneryPlaceAction.h +++ b/src/openrct2/actions/SmallSceneryPlaceAction.h @@ -27,11 +27,13 @@ private: ObjectEntryIndex _sceneryType{}; uint8_t _primaryColour{}; uint8_t _secondaryColour{}; + uint8_t _tertiaryColour{}; public: SmallSceneryPlaceAction() = default; SmallSceneryPlaceAction( - const CoordsXYZD& loc, uint8_t quadrant, ObjectEntryIndex sceneryType, uint8_t primaryColour, uint8_t secondaryColour); + const CoordsXYZD& loc, uint8_t quadrant, ObjectEntryIndex sceneryType, uint8_t primaryColour, uint8_t secondaryColour, + uint8_t tertiaryColour); void AcceptParameters(GameActionParameterVisitor& visitor) override; diff --git a/src/openrct2/actions/SmallScenerySetColourAction.cpp b/src/openrct2/actions/SmallScenerySetColourAction.cpp index cafd2e53ef..b74cd14bfa 100644 --- a/src/openrct2/actions/SmallScenerySetColourAction.cpp +++ b/src/openrct2/actions/SmallScenerySetColourAction.cpp @@ -26,12 +26,14 @@ #include "../world/TileElement.h" SmallScenerySetColourAction::SmallScenerySetColourAction( - const CoordsXYZ& loc, uint8_t quadrant, ObjectEntryIndex sceneryType, uint8_t primaryColour, uint8_t secondaryColour) + const CoordsXYZ& loc, uint8_t quadrant, ObjectEntryIndex sceneryType, uint8_t primaryColour, uint8_t secondaryColour, + uint8_t tertiaryColour) : _loc(loc) , _quadrant(quadrant) , _sceneryType(sceneryType) , _primaryColour(primaryColour) , _secondaryColour(secondaryColour) + , _tertiaryColour(tertiaryColour) { } @@ -96,6 +98,7 @@ GameActions::Result SmallScenerySetColourAction::QueryExecute(bool isExecuting) { sceneryElement->SetPrimaryColour(_primaryColour); sceneryElement->SetSecondaryColour(_secondaryColour); + sceneryElement->SetTertiaryColour(_tertiaryColour); map_invalidate_tile_full(_loc); } diff --git a/src/openrct2/actions/SmallScenerySetColourAction.h b/src/openrct2/actions/SmallScenerySetColourAction.h index 8f7a9924c7..ab742ff034 100644 --- a/src/openrct2/actions/SmallScenerySetColourAction.h +++ b/src/openrct2/actions/SmallScenerySetColourAction.h @@ -19,11 +19,13 @@ private: ObjectEntryIndex _sceneryType{}; uint8_t _primaryColour{}; uint8_t _secondaryColour{}; + uint8_t _tertiaryColour{}; public: SmallScenerySetColourAction() = default; SmallScenerySetColourAction( - const CoordsXYZ& loc, uint8_t quadrant, ObjectEntryIndex sceneryType, uint8_t primaryColour, uint8_t secondaryColour); + const CoordsXYZ& loc, uint8_t quadrant, ObjectEntryIndex sceneryType, uint8_t primaryColour, uint8_t secondaryColour, + uint8_t tertiaryColour); uint16_t GetActionFlags() const override; diff --git a/src/openrct2/object/LargeSceneryObject.cpp b/src/openrct2/object/LargeSceneryObject.cpp index 0b391321b3..570a524b5d 100644 --- a/src/openrct2/object/LargeSceneryObject.cpp +++ b/src/openrct2/object/LargeSceneryObject.cpp @@ -106,7 +106,14 @@ void LargeSceneryObject::DrawPreview(rct_drawpixelinfo* dpi, int32_t width, int3 { auto screenCoords = ScreenCoordsXY{ width / 2, (height / 2) - 39 }; - const auto image = ImageId(_legacyType.image, COLOUR_BORDEAUX_RED, COLOUR_YELLOW); + auto image = ImageId(_legacyType.image); + if (_legacyType.flags & LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR) + image = image.WithPrimary(COLOUR_BORDEAUX_RED); + if (_legacyType.flags & LARGE_SCENERY_FLAG_HAS_SECONDARY_COLOUR) + image = image.WithSecondary(COLOUR_YELLOW); + if (_legacyType.flags & LARGE_SCENERY_FLAG_HAS_TERTIARY_COLOUR) + image = image.WithTertiary(COLOUR_DARK_BROWN); + gfx_draw_sprite(dpi, image, screenCoords); } @@ -143,6 +150,7 @@ void LargeSceneryObject::ReadJson(IReadObjectContext* context, json_t& root) { { "hasPrimaryColour", LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR }, { "hasSecondaryColour", LARGE_SCENERY_FLAG_HAS_SECONDARY_COLOUR }, + { "hasTertiaryColour", LARGE_SCENERY_FLAG_HAS_TERTIARY_COLOUR }, { "isAnimated", LARGE_SCENERY_FLAG_ANIMATED }, { "isPhotogenic", LARGE_SCENERY_FLAG_PHOTOGENIC }, { "isTree", LARGE_SCENERY_FLAG_IS_TREE }, diff --git a/src/openrct2/object/SmallSceneryObject.cpp b/src/openrct2/object/SmallSceneryObject.cpp index f9d704e5a3..2ec57336be 100644 --- a/src/openrct2/object/SmallSceneryObject.cpp +++ b/src/openrct2/object/SmallSceneryObject.cpp @@ -107,6 +107,10 @@ void SmallSceneryObject::DrawPreview(rct_drawpixelinfo* dpi, int32_t width, int3 imageId = imageId.WithSecondary(COLOUR_YELLOW); } } + if (_legacyType.HasFlag(SMALL_SCENERY_FLAG_HAS_TERTIARY_COLOUR)) + { + imageId = imageId.WithSecondary(COLOUR_DARK_BROWN); + } auto screenCoords = ScreenCoordsXY{ width / 2, (height / 2) + (_legacyType.height / 2) }; screenCoords.y = std::min(screenCoords.y, height - 16); @@ -260,6 +264,7 @@ void SmallSceneryObject::ReadJson(IReadObjectContext* context, json_t& root) { "supportsHavePrimaryColour", SMALL_SCENERY_FLAG_PAINT_SUPPORTS }, { "SMALL_SCENERY_FLAG27", SMALL_SCENERY_FLAG27 }, { "isTree", SMALL_SCENERY_FLAG_IS_TREE }, + { "hasTertiaryColour", SMALL_SCENERY_FLAG_HAS_TERTIARY_COLOUR }, }); // Determine shape flags from a shape string diff --git a/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp b/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp index 1825263e6b..83e881d48d 100644 --- a/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp +++ b/src/openrct2/paint/tile_element/Paint.LargeScenery.cpp @@ -374,7 +374,18 @@ void PaintLargeScenery(paint_session& session, uint8_t direction, uint16_t heigh } else { - imageTemplate = ImageId(0, tileElement.GetPrimaryColour(), tileElement.GetSecondaryColour()); + if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_PRIMARY_COLOUR) + { + imageTemplate = imageTemplate.WithPrimary(tileElement.GetPrimaryColour()); + } + if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_SECONDARY_COLOUR) + { + imageTemplate = imageTemplate.WithSecondary(tileElement.GetSecondaryColour()); + } + if (sceneryEntry->flags & LARGE_SCENERY_FLAG_HAS_TERTIARY_COLOUR) + { + imageTemplate = imageTemplate.WithTertiary(tileElement.GetTertiaryColour()); + } } auto boxlengthZ = std::min(tile->z_clearance, 128) - 3; diff --git a/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp b/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp index f0f7ca1b30..95532ec196 100644 --- a/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp +++ b/src/openrct2/paint/tile_element/Paint.SmallScenery.cpp @@ -186,12 +186,19 @@ static void PaintSmallSceneryBody( if (!(sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_VISIBLE_WHEN_ZOOMED))) { auto imageId = imageTemplate.WithIndex(baseImageIndex); - if (!imageTemplate.IsRemap() && sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR)) + if (!imageTemplate.IsRemap()) { - imageId = imageId.WithPrimary(sceneryElement.GetPrimaryColour()); - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR)) + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR)) { - imageId = imageId.WithSecondary(sceneryElement.GetSecondaryColour()); + imageId = imageId.WithPrimary(sceneryElement.GetPrimaryColour()); + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR)) + { + imageId = imageId.WithSecondary(sceneryElement.GetSecondaryColour()); + } + } + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_TERTIARY_COLOUR)) + { + imageId = imageId.WithTertiary(sceneryElement.GetTertiaryColour()); } } PaintAddImageAsParent(session, imageId, offset, boxLength, boxOffset); @@ -285,12 +292,19 @@ static void PaintSmallSceneryBody( } auto imageId = imageTemplate.WithIndex(imageIndex); - if (!imageTemplate.IsRemap() && sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR)) + if (!imageTemplate.IsRemap()) { - imageId = ImageId(imageIndex).WithPrimary(sceneryElement.GetPrimaryColour()); - if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR)) + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_PRIMARY_COLOUR)) { - imageId = imageId.WithSecondary(sceneryElement.GetSecondaryColour()); + imageId = ImageId(imageIndex).WithPrimary(sceneryElement.GetPrimaryColour()); + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_SECONDARY_COLOUR)) + { + imageId = imageId.WithSecondary(sceneryElement.GetSecondaryColour()); + } + } + if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HAS_TERTIARY_COLOUR)) + { + imageId = imageId.WithTertiary(sceneryElement.GetTertiaryColour()); } } diff --git a/src/openrct2/ride/TrackDesign.cpp b/src/openrct2/ride/TrackDesign.cpp index ff0bebeb0f..035305a266 100644 --- a/src/openrct2/ride/TrackDesign.cpp +++ b/src/openrct2/ride/TrackDesign.cpp @@ -1086,7 +1086,7 @@ static GameActions::Result TrackDesignPlaceSceneryElement( auto smallSceneryPlace = SmallSceneryPlaceAction( { mapCoord.x, mapCoord.y, z, rotation }, quadrant, entryInfo->Index, scenery.primary_colour, - scenery.secondary_colour); + scenery.secondary_colour, COLOUR_DARK_BROWN); smallSceneryPlace.SetFlags(flags); auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&smallSceneryPlace) @@ -1127,7 +1127,8 @@ static GameActions::Result TrackDesignPlaceSceneryElement( flags |= GAME_COMMAND_FLAG_REPLAY; } auto sceneryPlaceAction = LargeSceneryPlaceAction( - { mapCoord.x, mapCoord.y, z, rotation }, entryInfo->Index, scenery.primary_colour, scenery.secondary_colour); + { mapCoord.x, mapCoord.y, z, rotation }, entryInfo->Index, scenery.primary_colour, scenery.secondary_colour, + COLOUR_DARK_BROWN); sceneryPlaceAction.SetFlags(flags); auto res = flags & GAME_COMMAND_FLAG_APPLY ? GameActions::ExecuteNested(&sceneryPlaceAction) : GameActions::QueryNested(&sceneryPlaceAction); diff --git a/src/openrct2/world/LargeScenery.cpp b/src/openrct2/world/LargeScenery.cpp index 6103d5b648..cc0b0101e8 100644 --- a/src/openrct2/world/LargeScenery.cpp +++ b/src/openrct2/world/LargeScenery.cpp @@ -26,6 +26,11 @@ colour_t LargeSceneryElement::GetSecondaryColour() const return Colour[1]; } +colour_t LargeSceneryElement::GetTertiaryColour() const +{ + return Colour[2]; +} + void LargeSceneryElement::SetPrimaryColour(colour_t newColour) { assert(newColour <= 31); @@ -38,6 +43,12 @@ void LargeSceneryElement::SetSecondaryColour(colour_t newColour) Colour[1] = newColour; } +void LargeSceneryElement::SetTertiaryColour(colour_t newColour) +{ + assert(newColour <= 31); + Colour[2] = newColour; +} + Banner* LargeSceneryElement::GetBanner() const { return ::GetBanner(GetBannerIndex()); diff --git a/src/openrct2/world/Scenery.h b/src/openrct2/world/Scenery.h index aaaf3ea3b8..e5d034b1d8 100644 --- a/src/openrct2/world/Scenery.h +++ b/src/openrct2/world/Scenery.h @@ -97,6 +97,7 @@ enum LARGE_SCENERY_FLAGS LARGE_SCENERY_FLAG_ANIMATED = (1 << 3), // 0x8 LARGE_SCENERY_FLAG_PHOTOGENIC = (1 << 4), // 0x10 LARGE_SCENERY_FLAG_IS_TREE = (1 << 5), // 0x20 + LARGE_SCENERY_FLAG_HAS_TERTIARY_COLOUR = (1 << 6), // 0x40 }; enum WALL_SCENERY_FLAGS diff --git a/src/openrct2/world/SmallScenery.cpp b/src/openrct2/world/SmallScenery.cpp index 5abe1a2967..6d193215dd 100644 --- a/src/openrct2/world/SmallScenery.cpp +++ b/src/openrct2/world/SmallScenery.cpp @@ -79,36 +79,48 @@ void SmallSceneryElement::IncreaseAge(const CoordsXY& sceneryPos) colour_t SmallSceneryElement::GetPrimaryColour() const { - return colour_1 & TILE_ELEMENT_COLOUR_MASK; + return Colour[0] & TILE_ELEMENT_COLOUR_MASK; } colour_t SmallSceneryElement::GetSecondaryColour() const { - return colour_2 & TILE_ELEMENT_COLOUR_MASK; + return Colour[1] & TILE_ELEMENT_COLOUR_MASK; } -void SmallSceneryElement::SetPrimaryColour(colour_t colour) +colour_t SmallSceneryElement::GetTertiaryColour() const { - assert(colour <= 31); - colour_1 &= ~TILE_ELEMENT_COLOUR_MASK; - colour_1 |= colour; + return Colour[2] & TILE_ELEMENT_COLOUR_MASK; } -void SmallSceneryElement::SetSecondaryColour(colour_t colour) +void SmallSceneryElement::SetPrimaryColour(colour_t newColour) { - assert(colour <= 31); - colour_2 &= ~TILE_ELEMENT_COLOUR_MASK; - colour_2 |= colour; + assert(newColour <= 31); + Colour[0] &= ~TILE_ELEMENT_COLOUR_MASK; + Colour[0] |= newColour; +} + +void SmallSceneryElement::SetSecondaryColour(colour_t newColour) +{ + assert(newColour <= 31); + Colour[1] &= ~TILE_ELEMENT_COLOUR_MASK; + Colour[1] |= newColour; +} + +void SmallSceneryElement::SetTertiaryColour(colour_t newColour) +{ + assert(newColour <= 31); + Colour[2] &= ~TILE_ELEMENT_COLOUR_MASK; + Colour[2] |= newColour; } bool SmallSceneryElement::NeedsSupports() const { - return static_cast(colour_1 & MAP_ELEM_SMALL_SCENERY_COLOUR_FLAG_NEEDS_SUPPORTS); + return static_cast(Colour[0] & MAP_ELEM_SMALL_SCENERY_COLOUR_FLAG_NEEDS_SUPPORTS); } void SmallSceneryElement::SetNeedsSupports() { - colour_1 |= MAP_ELEM_SMALL_SCENERY_COLOUR_FLAG_NEEDS_SUPPORTS; + Colour[0] |= MAP_ELEM_SMALL_SCENERY_COLOUR_FLAG_NEEDS_SUPPORTS; } SmallSceneryEntry* SmallSceneryElement::GetEntry() const diff --git a/src/openrct2/world/SmallScenery.h b/src/openrct2/world/SmallScenery.h index 8a8ded3aeb..07dc66e41d 100644 --- a/src/openrct2/world/SmallScenery.h +++ b/src/openrct2/world/SmallScenery.h @@ -45,7 +45,9 @@ enum SMALL_SCENERY_FLAGS : uint32_t SMALL_SCENERY_FLAG_PAINT_SUPPORTS = (1 << 26), // 0x4000000; used for scenery items which are support structures SMALL_SCENERY_FLAG27 = (1 << 27), // 0x8000000 - SMALL_SCENERY_FLAG_IS_TREE = (1 << 28), // Added by OpenRCT2 + // Added by OpenRCT2: + SMALL_SCENERY_FLAG_IS_TREE = (1 << 28), + SMALL_SCENERY_FLAG_HAS_TERTIARY_COLOUR = (1 << 29), }; enum diff --git a/src/openrct2/world/TileElement.h b/src/openrct2/world/TileElement.h index e21ff2d912..5cd8ee71c4 100644 --- a/src/openrct2/world/TileElement.h +++ b/src/openrct2/world/TileElement.h @@ -429,11 +429,10 @@ struct SmallSceneryElement : TileElementBase private: ObjectEntryIndex entryIndex; // 5 uint8_t age; // 7 - uint8_t colour_1; // 8 - uint8_t colour_2; // 9 + uint8_t Colour[3]; // 8 #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-private-field" - uint8_t pad_0A[6]; + uint8_t pad_0B[5]; #pragma clang diagnostic pop public: @@ -449,6 +448,8 @@ public: void SetPrimaryColour(colour_t colour); colour_t GetSecondaryColour() const; void SetSecondaryColour(colour_t colour); + colour_t GetTertiaryColour() const; + void SetTertiaryColour(colour_t colour); bool NeedsSupports() const; void SetNeedsSupports(); void UpdateAge(const CoordsXY& sceneryPos); @@ -483,6 +484,8 @@ public: void SetPrimaryColour(colour_t colour); colour_t GetSecondaryColour() const; void SetSecondaryColour(colour_t colour); + colour_t GetTertiaryColour() const; + void SetTertiaryColour(colour_t colour); Banner* GetBanner() const; ::BannerIndex GetBannerIndex() const;