From 0e5c82e2d4c774cd6b98a4b21b460cbd41aa6355 Mon Sep 17 00:00:00 2001 From: Michael Steenbeek Date: Fri, 12 Jan 2024 08:14:11 +0100 Subject: [PATCH] Close #18632: Display land ownership on the water (#21150) Co-authored-by: pfroud --- distribution/changelog.txt | 1 + src/openrct2-ui/windows/LandRights.cpp | 37 +++--- .../paint/tile_element/Paint.Surface.cpp | 116 +++++++++++++----- src/openrct2/world/Map.cpp | 15 ++- src/openrct2/world/Map.h | 2 + 5 files changed, 123 insertions(+), 48 deletions(-) diff --git a/distribution/changelog.txt b/distribution/changelog.txt index 4b3ed66cb7..4e59e05d85 100644 --- a/distribution/changelog.txt +++ b/distribution/changelog.txt @@ -1,5 +1,6 @@ 0.4.8 (in development) ------------------------------------------------------------------------ +- Improved: [#18632] Land ownership and construction rights are now shown on top of the water. - Fix: [#21145] [Plugin] setInterval/setTimeout handle conflict. - Fix: [#21158] [Plugin] Potential crash using setInterval/setTimeout within the callback. - Fix: [#21178] Inca Lost City’s scenario description incorrectly states there are height restrictions. diff --git a/src/openrct2-ui/windows/LandRights.cpp b/src/openrct2-ui/windows/LandRights.cpp index eb697e85c7..bc7bf57314 100644 --- a/src/openrct2-ui/windows/LandRights.cpp +++ b/src/openrct2-ui/windows/LandRights.cpp @@ -240,9 +240,9 @@ public: MapInvalidateSelectionRect(); gMapSelectFlags &= ~MAP_SELECT_FLAG_ENABLE; - auto mapTile = ScreenGetMapXY(screenCoords, nullptr); - - if (!mapTile.has_value()) + auto info = GetMapCoordinatesFromPos( + screenCoords, EnumsToFlags(ViewportInteractionItem::Terrain, ViewportInteractionItem::Water)); + if (info.SpriteType == ViewportInteractionItem::None) { if (_landRightsCost != MONEY64_UNDEFINED) { @@ -251,6 +251,7 @@ public: } return; } + auto mapTile = info.Loc; uint8_t state_changed = 0; @@ -260,9 +261,9 @@ public: state_changed++; } - if (gMapSelectType != MAP_SELECT_TYPE_FULL) + if (gMapSelectType != MAP_SELECT_TYPE_FULL_LAND_RIGHTS) { - gMapSelectType = MAP_SELECT_TYPE_FULL; + gMapSelectType = MAP_SELECT_TYPE_FULL_LAND_RIGHTS; state_changed++; } @@ -273,34 +274,34 @@ public: int16_t tool_length = (tool_size - 1) * 32; // Move to tool bottom left - mapTile->x -= (tool_size - 1) * 16; - mapTile->y -= (tool_size - 1) * 16; - mapTile = mapTile->ToTileStart(); + mapTile.x -= (tool_size - 1) * 16; + mapTile.y -= (tool_size - 1) * 16; + mapTile = mapTile.ToTileStart(); - if (gMapSelectPositionA.x != mapTile->x) + if (gMapSelectPositionA.x != mapTile.x) { - gMapSelectPositionA.x = mapTile->x; + gMapSelectPositionA.x = mapTile.x; state_changed++; } - if (gMapSelectPositionA.y != mapTile->y) + if (gMapSelectPositionA.y != mapTile.y) { - gMapSelectPositionA.y = mapTile->y; + gMapSelectPositionA.y = mapTile.y; state_changed++; } - mapTile->x += tool_length; - mapTile->y += tool_length; + mapTile.x += tool_length; + mapTile.y += tool_length; - if (gMapSelectPositionB.x != mapTile->x) + if (gMapSelectPositionB.x != mapTile.x) { - gMapSelectPositionB.x = mapTile->x; + gMapSelectPositionB.x = mapTile.x; state_changed++; } - if (gMapSelectPositionB.y != mapTile->y) + if (gMapSelectPositionB.y != mapTile.y) { - gMapSelectPositionB.y = mapTile->y; + gMapSelectPositionB.y = mapTile.y; state_changed++; } diff --git a/src/openrct2/paint/tile_element/Paint.Surface.cpp b/src/openrct2/paint/tile_element/Paint.Surface.cpp index e78eff5b20..f5748e413c 100644 --- a/src/openrct2/paint/tile_element/Paint.Surface.cpp +++ b/src/openrct2/paint/tile_element/Paint.Surface.cpp @@ -980,6 +980,75 @@ static void PaintPatrolArea(PaintSession& session, const SurfaceElement& element } } +static void PaintSurfaceLandOwnership( + PaintSession& session, const SurfaceElement& tileElement, uint16_t height, uint8_t surfaceShape) +{ + auto [aboveWaterHeight, aboveWaterSurfaceShape] = SurfaceGetHeightAboveWater(tileElement, height, surfaceShape); + + // Loc660E9A: + if (tileElement.GetOwnership() & OWNERSHIP_OWNED) + { + assert(static_cast(surfaceShape) < std::size(Byte97B444)); + assert(static_cast(aboveWaterSurfaceShape) < std::size(Byte97B444)); + + // Paint outline on top of surface (can be underwater) + PaintAttachToPreviousPS(session, ImageId(SPR_TERRAIN_SELECTION_SQUARE + Byte97B444[surfaceShape]), 0, 0); + + const auto tileIsUnderWater = height != aboveWaterHeight || surfaceShape != aboveWaterSurfaceShape; + if (tileIsUnderWater) + { + PaintStruct* backup = session.LastPS; + PaintAddImageAsParent( + session, ImageId(SPR_TERRAIN_SELECTION_SQUARE + Byte97B444[aboveWaterSurfaceShape]), { 0, 0, aboveWaterHeight }, + { 32, 32, 1 }); + session.LastPS = backup; + } + } + else if (tileElement.GetOwnership() & OWNERSHIP_AVAILABLE) + { + const auto pos = CoordsXYZ(session.MapPosition.x + 16, session.MapPosition.y + 16, aboveWaterHeight); + const auto height2 = TileElementHeight(pos, aboveWaterSurfaceShape); + + PaintStruct* backup = session.LastPS; + PaintAddImageAsParent(session, ImageId(SPR_LAND_OWNERSHIP_AVAILABLE), { 16, 16, height2 }, { 1, 1, 0 }); + session.LastPS = backup; + } +} + +static void PaintSurfaceConstructionRights( + PaintSession& session, const SurfaceElement& tileElement, uint16_t height, uint8_t surfaceShape) +{ + auto [aboveWaterHeight, aboveWaterSurfaceShape] = SurfaceGetHeightAboveWater(tileElement, height, surfaceShape); + + if (tileElement.GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED) + { + assert(static_cast(surfaceShape) < std::size(Byte97B444)); + assert(static_cast(aboveWaterSurfaceShape) < std::size(Byte97B444)); + + // Paint outline on top of surface (can be underwater) + PaintAttachToPreviousPS(session, ImageId(SPR_TERRAIN_SELECTION_DOTTED + Byte97B444[surfaceShape]), 0, 0); + + const auto tileIsUnderWater = height != aboveWaterHeight || surfaceShape != aboveWaterSurfaceShape; + if (tileIsUnderWater) + { + PaintStruct* backup = session.LastPS; + PaintAddImageAsParent( + session, ImageId(SPR_TERRAIN_SELECTION_DOTTED + Byte97B444[aboveWaterSurfaceShape]), { 0, 0, aboveWaterHeight }, + { 32, 32, 1 }); + session.LastPS = backup; + } + } + else if (tileElement.GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE) + { + const auto pos = CoordsXYZ(session.MapPosition.x + 16, session.MapPosition.y + 16, aboveWaterHeight); + const auto height2 = TileElementHeight(pos, aboveWaterSurfaceShape); + + PaintStruct* backup = session.LastPS; + PaintAddImageAsParent(session, ImageId(SPR_LAND_CONSTRUCTION_RIGHTS_AVAILABLE), { 16, 16, height2 }, { 1, 1, 0 }); + session.LastPS = backup; + } +} + /** * rct2: 0x0066062C */ @@ -1134,38 +1203,12 @@ void PaintSurface(PaintSession& session, uint8_t direction, uint16_t height, con if (session.ViewFlags & VIEWPORT_FLAG_LAND_OWNERSHIP) { - // Loc660E9A: - if (tileElement.GetOwnership() & OWNERSHIP_OWNED) - { - assert(surfaceShape < std::size(Byte97B444)); - PaintAttachToPreviousPS(session, ImageId(SPR_TERRAIN_SELECTION_SQUARE + Byte97B444[surfaceShape]), 0, 0); - } - else if (tileElement.GetOwnership() & OWNERSHIP_AVAILABLE) - { - const CoordsXY& pos = session.MapPosition; - const int32_t height2 = (TileElementHeight({ pos.x + 16, pos.y + 16 })) + 3; - PaintStruct* backup = session.LastPS; - PaintAddImageAsParent(session, ImageId(SPR_LAND_OWNERSHIP_AVAILABLE), { 16, 16, height2 }, { 1, 1, 0 }); - session.LastPS = backup; - } + PaintSurfaceLandOwnership(session, tileElement, height, surfaceShape); } if (session.ViewFlags & VIEWPORT_FLAG_CONSTRUCTION_RIGHTS && !(tileElement.GetOwnership() & OWNERSHIP_OWNED)) { - if (tileElement.GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_OWNED) - { - assert(surfaceShape < std::size(Byte97B444)); - PaintAttachToPreviousPS(session, ImageId(SPR_TERRAIN_SELECTION_DOTTED + Byte97B444[surfaceShape]), 0, 0); - } - else if (tileElement.GetOwnership() & OWNERSHIP_CONSTRUCTION_RIGHTS_AVAILABLE) - { - const CoordsXY& pos = session.MapPosition; - const int32_t height2 = TileElementHeight({ pos.x + 16, pos.y + 16 }); - PaintStruct* backup = session.LastPS; - PaintAddImageAsParent( - session, ImageId(SPR_LAND_CONSTRUCTION_RIGHTS_AVAILABLE), { 16, 16, height2 + 3 }, { 1, 1, 0 }); - session.LastPS = backup; - } + PaintSurfaceConstructionRights(session, tileElement, height, surfaceShape); } // ebx[0] = esi; @@ -1214,6 +1257,23 @@ void PaintSurface(PaintSession& session, uint8_t direction, uint16_t height, con const auto image_id = ImageId(SPR_TERRAIN_SELECTION_CORNER + Byte97B444[surfaceShape], fpId); PaintAttachToPreviousPS(session, image_id, 0, 0); } + else if (mapSelectionType == MAP_SELECT_TYPE_FULL_LAND_RIGHTS) + { + auto [waterHeight, waterSurfaceShape] = SurfaceGetHeightAboveWater(tileElement, height, surfaceShape); + + const auto fpId = FilterPaletteID::PaletteGlassLightPurple; + const auto imageId1 = ImageId(SPR_TERRAIN_SELECTION_CORNER + Byte97B444[surfaceShape], fpId); + PaintAttachToPreviousPS(session, imageId1, 0, 0); + + const bool isUnderWater = (surfaceShape != waterSurfaceShape || height != waterHeight); + if (isUnderWater) + { + const auto imageId2 = ImageId(SPR_TERRAIN_SELECTION_CORNER + Byte97B444[waterSurfaceShape], fpId); + PaintStruct* backup = session.LastPS; + PaintAddImageAsParent(session, imageId2, { 0, 0, waterHeight }, { 32, 32, 1 }); + session.LastPS = backup; + } + } else { // The water tool should draw its grid _on_ the water, rather than on the surface under water. diff --git a/src/openrct2/world/Map.cpp b/src/openrct2/world/Map.cpp index ded620d96e..20334b09d2 100644 --- a/src/openrct2/world/Map.cpp +++ b/src/openrct2/world/Map.cpp @@ -536,9 +536,20 @@ int16_t TileElementHeight(const CoordsXY& loc) return MINIMUM_LAND_HEIGHT_BIG; } - uint16_t height = surfaceElement->GetBaseZ(); + auto height = surfaceElement->GetBaseZ(); + auto slope = surfaceElement->GetSlope(); + + return TileElementHeight(CoordsXYZ{ loc, height }, slope); +} + +int16_t TileElementHeight(const CoordsXYZ& loc, uint8_t slope) +{ + // Off the map + if (!MapIsLocationValid(loc)) + return MINIMUM_LAND_HEIGHT_BIG; + + auto height = loc.z; - uint32_t slope = surfaceElement->GetSlope(); uint8_t extra_height = (slope & TILE_ELEMENT_SLOPE_DOUBLE_HEIGHT) >> 4; // 0x10 is the 5th bit - sets slope to double height // Remove the extra height bit slope &= TILE_ELEMENT_SLOPE_ALL_CORNERS_UP; diff --git a/src/openrct2/world/Map.h b/src/openrct2/world/Map.h index a93ee7ce4c..e58906f1b6 100644 --- a/src/openrct2/world/Map.h +++ b/src/openrct2/world/Map.h @@ -79,6 +79,7 @@ enum MAP_SELECT_TYPE_CORNER_3, MAP_SELECT_TYPE_FULL, MAP_SELECT_TYPE_FULL_WATER, + MAP_SELECT_TYPE_FULL_LAND_RIGHTS, MAP_SELECT_TYPE_QUARTER_0, MAP_SELECT_TYPE_QUARTER_1, MAP_SELECT_TYPE_QUARTER_2, @@ -191,6 +192,7 @@ void MapInvalidateMapSelectionTiles(); void MapInvalidateSelectionRect(); bool MapCheckCapacityAndReorganise(const CoordsXY& loc, size_t numElements = 1); int16_t TileElementHeight(const CoordsXY& loc); +int16_t TileElementHeight(const CoordsXYZ& loc, uint8_t slope); int16_t TileElementWaterHeight(const CoordsXY& loc); void TileElementRemove(TileElement* tileElement); TileElement* TileElementInsert(const CoordsXYZ& loc, int32_t occupiedQuadrants, TileElementType type);