mirror of https://github.com/OpenRCT2/OpenRCT2.git
Compare commits
5 Commits
9b2f85ffec
...
71484a11bd
Author | SHA1 | Date |
---|---|---|
spacek531 | 71484a11bd | |
Harry Hopkinson | 9266a6f0d3 | |
Michael Steenbeek | 03926c46d0 | |
Spacek531 | 8da2c36ea5 | |
Spacek531 | 9e30512cb9 |
|
@ -64,9 +64,9 @@ set(TITLE_SEQUENCE_VERSION "0.4.6")
|
|||
set(TITLE_SEQUENCE_URL "https://github.com/OpenRCT2/title-sequences/releases/download/v${TITLE_SEQUENCE_VERSION}/title-sequences.zip")
|
||||
set(TITLE_SEQUENCE_SHA1 "80fefc6ebbabc42a6f4703412daa5c62f661420d")
|
||||
|
||||
set(OBJECTS_VERSION "1.4.3")
|
||||
set(OBJECTS_VERSION "1.4.4")
|
||||
set(OBJECTS_URL "https://github.com/OpenRCT2/objects/releases/download/v${OBJECTS_VERSION}/objects.zip")
|
||||
set(OBJECTS_SHA1 "ac78210ef46465c0f51bbffd6fe21845092af48e")
|
||||
set(OBJECTS_SHA1 "4e9b5d7f85a07b90ec57fe37517e04b19ee0395c")
|
||||
|
||||
set(OPENSFX_VERSION "1.0.5")
|
||||
set(OPENSFX_URL "https://github.com/OpenRCT2/OpenSoundEffects/releases/download/v${OPENSFX_VERSION}/opensound.zip")
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
- Fix: [#21696] Fullscreen window option not correctly applied on macOS.
|
||||
- Fix: [#21787] Map generator heightmap should respect increased height limits.
|
||||
- Fix: [#21829] When creating a new scenario, the default name contains formatting codes.
|
||||
- Fix: [objects#324] Cannot build Colosseum inside a turn or helix.
|
||||
- Fix: [objects#325] Sloped castle walls are vertically offset by one pixel (original bug).
|
||||
|
||||
0.4.10 (2024-04-02)
|
||||
------------------------------------------------------------------------
|
||||
|
|
|
@ -45,8 +45,8 @@
|
|||
<LibsSha1 Condition="'$(Platform)'=='Win32'">f3636b530110c7592deab23ca0d0c1916fc7e44c</LibsSha1>
|
||||
<TitleSequencesUrl>https://github.com/OpenRCT2/title-sequences/releases/download/v0.4.6/title-sequences.zip</TitleSequencesUrl>
|
||||
<TitleSequencesSha1>80fefc6ebbabc42a6f4703412daa5c62f661420d</TitleSequencesSha1>
|
||||
<ObjectsUrl>https://github.com/OpenRCT2/objects/releases/download/v1.4.3/objects.zip</ObjectsUrl>
|
||||
<ObjectsSha1>ac78210ef46465c0f51bbffd6fe21845092af48e</ObjectsSha1>
|
||||
<ObjectsUrl>https://github.com/OpenRCT2/objects/releases/download/v1.4.4/objects.zip</ObjectsUrl>
|
||||
<ObjectsSha1>4e9b5d7f85a07b90ec57fe37517e04b19ee0395c</ObjectsSha1>
|
||||
<OpenSFXUrl>https://github.com/OpenRCT2/OpenSoundEffects/releases/download/v1.0.5/opensound.zip</OpenSFXUrl>
|
||||
<OpenSFXSha1>b1b1f1b241d2cbff63a1889c4dc5a09bdf769bfb</OpenSFXSha1>
|
||||
<OpenMSXUrl>https://github.com/OpenRCT2/OpenMusic/releases/download/v1.5/openmusic.zip</OpenMSXUrl>
|
||||
|
|
|
@ -338,6 +338,7 @@
|
|||
<ClInclude Include="object\ObjectTypes.h" />
|
||||
<ClInclude Include="object\ResourceTable.h" />
|
||||
<ClInclude Include="object\RideObject.h" />
|
||||
<ClInclude Include="object\SceneryBoundingBox.h" />
|
||||
<ClInclude Include="object\SceneryGroupEntry.h" />
|
||||
<ClInclude Include="object\SceneryGroupObject.h" />
|
||||
<ClInclude Include="object\SceneryObject.h" />
|
||||
|
@ -846,6 +847,7 @@
|
|||
<ClCompile Include="object\ObjectTypes.cpp" />
|
||||
<ClCompile Include="object\ResourceTable.cpp" />
|
||||
<ClCompile Include="object\RideObject.cpp" />
|
||||
<ClCompile Include="object\SceneryBoundingBox.cpp" />
|
||||
<ClCompile Include="object\SceneryGroupObject.cpp" />
|
||||
<ClCompile Include="object\SmallSceneryObject.cpp" />
|
||||
<ClCompile Include="object\StationObject.cpp" />
|
||||
|
@ -1072,4 +1074,4 @@
|
|||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
</Project>
|
||||
</Project>
|
|
@ -11,6 +11,7 @@
|
|||
#include "../common.h"
|
||||
#include "../interface/Cursors.h"
|
||||
#include "../world/Location.hpp"
|
||||
#include "../world/Scenery.h"
|
||||
#include "ObjectTypes.h"
|
||||
|
||||
struct LargeSceneryText;
|
||||
|
@ -23,6 +24,8 @@ struct LargeSceneryTile
|
|||
uint8_t z_clearance;
|
||||
// CCCC WWWW 0SS0 0000
|
||||
uint16_t flags;
|
||||
SceneryBoundBoxes boundBoxes = {};
|
||||
CoordsXYZ spriteOffset = {};
|
||||
};
|
||||
|
||||
enum
|
||||
|
|
|
@ -19,10 +19,54 @@
|
|||
#include "../localisation/Language.h"
|
||||
#include "../world/Banner.h"
|
||||
#include "../world/Location.hpp"
|
||||
#include "SceneryBoundingBox.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
|
||||
static DefaultBoundingBoxType boundBoxTypes[16] = {
|
||||
DefaultBoundingBoxType::FullTileBox, // 0000
|
||||
DefaultBoundingBoxType::FullTileSouthQuadrantBox, // 0001
|
||||
DefaultBoundingBoxType::FullTileWestQuadrantBox, // 0010
|
||||
DefaultBoundingBoxType::FullTileSouthwestSideBox, // 0011
|
||||
DefaultBoundingBoxType::FullTileNorthQuadrantBox, // 0100
|
||||
DefaultBoundingBoxType::FullTileBox, // 0101 (diagonal of South and North corners)
|
||||
DefaultBoundingBoxType::FullTileNorthwestSideBox, // 0110
|
||||
DefaultBoundingBoxType::FullTileBox, // 0111 (triangle of South, West, and North corners)
|
||||
DefaultBoundingBoxType::FullTileEastQuadrantBox, // 1000
|
||||
DefaultBoundingBoxType::FullTileSoutheastSideBox, // 1001
|
||||
DefaultBoundingBoxType::FullTileBox, // 1010 (diagonal of East and West corners)
|
||||
DefaultBoundingBoxType::FullTileBox, // 1011 (triangle of South, West, and East corners)
|
||||
DefaultBoundingBoxType::FullTileNortheastSideBox, // 1100
|
||||
DefaultBoundingBoxType::FullTileBox, // 1101 (triangle of South, West, and North corners)
|
||||
DefaultBoundingBoxType::FullTileBox, // 1110 (triangle of West, North, and East corners)
|
||||
DefaultBoundingBoxType::FullTileBox, // 1111
|
||||
};
|
||||
|
||||
static int32_t getBoundBoxHeight(uint8_t clearanceHeight)
|
||||
{
|
||||
return std::min<uint8_t>(clearanceHeight, 128) - 3;
|
||||
}
|
||||
|
||||
static void SetTileBoundingBox(LargeSceneryTile& tile)
|
||||
{
|
||||
if (tile.flags & 0xF00)
|
||||
{
|
||||
tile.boundBoxes = GetDefaultSceneryBoundBoxes(boundBoxTypes[(tile.flags & 0xF000) >> 12]);
|
||||
}
|
||||
else
|
||||
{
|
||||
tile.boundBoxes = GetDefaultSceneryBoundBoxes(DefaultBoundingBoxType::FullTileLargeBox);
|
||||
}
|
||||
tile.spriteOffset = GetDefaultSpriteOffset(DefaultSpriteOffsetType::LargeSceneryOffset);
|
||||
|
||||
auto clearanceHeight = getBoundBoxHeight(tile.z_clearance);
|
||||
for (uint8_t i = 0; i < NumOrthogonalDirections; i++)
|
||||
{
|
||||
tile.boundBoxes[i].length.z = clearanceHeight;
|
||||
}
|
||||
}
|
||||
|
||||
static RCTLargeSceneryText ReadLegacy3DFont(OpenRCT2::IStream& stream)
|
||||
{
|
||||
RCTLargeSceneryText _3dFontLegacy = {};
|
||||
|
@ -168,6 +212,7 @@ std::vector<LargeSceneryTile> LargeSceneryObject::ReadTiles(OpenRCT2::IStream* s
|
|||
tile.z_offset = stream->ReadValue<int16_t>();
|
||||
tile.z_clearance = stream->ReadValue<uint8_t>();
|
||||
tile.flags = stream->ReadValue<uint16_t>();
|
||||
SetTileBoundingBox(tile);
|
||||
return tile;
|
||||
};
|
||||
|
||||
|
@ -256,6 +301,15 @@ std::vector<LargeSceneryTile> LargeSceneryObject::ReadJsonTiles(json_t& jTiles)
|
|||
|
||||
auto walls = Json::GetNumber<int16_t>(jTile["walls"]);
|
||||
tile.flags |= (walls & 0xFF) << 8;
|
||||
SetTileBoundingBox(tile);
|
||||
auto jBBox = jTile["boundingBox"];
|
||||
if (!jBBox.empty())
|
||||
{
|
||||
tile.boundBoxes = ReadBoundBoxes(jBBox, tile.boundBoxes[0].length.z, false);
|
||||
}
|
||||
auto jSpriteOffset = jTile["spriteOffsetCoordinates"];
|
||||
if (!jSpriteOffset.empty())
|
||||
tile.spriteOffset = ReadSpriteOffset(jSpriteOffset);
|
||||
|
||||
tiles.push_back(std::move(tile));
|
||||
}
|
||||
|
|
|
@ -0,0 +1,261 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2024 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#include "SceneryBoundingBox.h"
|
||||
|
||||
constexpr std::array<CoordsXYZ, DefaultSpriteOffsetType::CountOffset> DefaultSpriteOffsets = {
|
||||
CoordsXYZ(7, 7, 0), // quarter tile
|
||||
CoordsXYZ(15, 15, 0), // small scenery full tile w/o VOFFSET_CENTRE
|
||||
CoordsXYZ(3, 3, 0), // small scenery halftile/VOFFSET_CENTRE
|
||||
CoordsXYZ(1, 1, 0), // small scenery VOFFSET_CENTER+NO_WALLS
|
||||
CoordsXYZ(0, 0, 0), // large scenery
|
||||
};
|
||||
|
||||
constexpr SceneryBoundBoxes QuarterTile = {
|
||||
BoundBoxXYZ({ 7, 7, 0 }, { 2, 2, 0 }),
|
||||
BoundBoxXYZ({ 7, 7, 0 }, { 2, 2, 0 }),
|
||||
BoundBoxXYZ({ 7, 7, 0 }, { 2, 2, 0 }),
|
||||
BoundBoxXYZ({ 7, 7, 0 }, { 2, 2, 0 }),
|
||||
};
|
||||
|
||||
constexpr SceneryBoundBoxes HalfTile = {
|
||||
BoundBoxXYZ({ 3, 3, 0 }, { 12, 26, 0 }),
|
||||
BoundBoxXYZ({ 3, 17, 0 }, { 26, 12, 0 }),
|
||||
BoundBoxXYZ({ 17, 3, 0 }, { 12, 26, 0 }),
|
||||
BoundBoxXYZ({ 3, 3, 0 }, { 26, 12, 0 }),
|
||||
};
|
||||
|
||||
// LargeSpecial corner/side match sub-fulltile large scenery boundboxes
|
||||
constexpr SceneryBoundBoxes FullTileNorthQuadrant = {
|
||||
BoundBoxXYZ({ 3, 3, 0 }, { 12, 12, 0 }),
|
||||
BoundBoxXYZ({ 3, 17, 0 }, { 12, 12, 0 }),
|
||||
BoundBoxXYZ({ 17, 17, 0 }, { 12, 12, 0 }),
|
||||
BoundBoxXYZ({ 17, 3, 0 }, { 12, 12, 0 }),
|
||||
};
|
||||
constexpr SceneryBoundBoxes FullTileNortheastSide = {
|
||||
BoundBoxXYZ({ 3, 3, 0 }, { 12, 28, 0 }),
|
||||
BoundBoxXYZ({ 3, 17, 0 }, { 26, 12, 0 }),
|
||||
BoundBoxXYZ({ 17, 3, 0 }, { 12, 26, 0 }),
|
||||
BoundBoxXYZ({ 3, 3, 0 }, { 28, 12, 0 }),
|
||||
};
|
||||
constexpr SceneryBoundBoxes FullTileEastQuadrant = {
|
||||
BoundBoxXYZ({ 3, 17, 0 }, { 12, 12, 0 }),
|
||||
BoundBoxXYZ({ 17, 17, 0 }, { 12, 12, 0 }),
|
||||
BoundBoxXYZ({ 17, 3, 0 }, { 12, 12, 0 }),
|
||||
BoundBoxXYZ({ 3, 3, 0 }, { 12, 12, 0 }),
|
||||
};
|
||||
constexpr SceneryBoundBoxes FullTileSoutheastSide = {
|
||||
BoundBoxXYZ({ 3, 17, 0 }, { 26, 12, 0 }),
|
||||
BoundBoxXYZ({ 17, 3, 0 }, { 12, 26, 0 }),
|
||||
BoundBoxXYZ({ 3, 3, 0 }, { 28, 12, 0 }),
|
||||
BoundBoxXYZ({ 3, 3, 0 }, { 12, 28, 0 }),
|
||||
};
|
||||
constexpr SceneryBoundBoxes FullTileSouthQuadrant = {
|
||||
BoundBoxXYZ({ 17, 17, 0 }, { 12, 12, 0 }),
|
||||
BoundBoxXYZ({ 17, 3, 0 }, { 12, 12, 0 }),
|
||||
BoundBoxXYZ({ 3, 3, 0 }, { 12, 12, 0 }),
|
||||
BoundBoxXYZ({ 3, 17, 0 }, { 12, 12, 0 }),
|
||||
};
|
||||
constexpr SceneryBoundBoxes FullTileSouthwestSide = {
|
||||
BoundBoxXYZ({ 17, 3, 0 }, { 12, 26, 0 }),
|
||||
BoundBoxXYZ({ 3, 3, 0 }, { 28, 12, 0 }),
|
||||
BoundBoxXYZ({ 3, 3, 0 }, { 12, 28, 0 }),
|
||||
BoundBoxXYZ({ 3, 17, 0 }, { 26, 12, 0 }),
|
||||
};
|
||||
constexpr SceneryBoundBoxes FullTileWestQuadrant = {
|
||||
BoundBoxXYZ({ 17, 3, 0 }, { 12, 12, 0 }),
|
||||
BoundBoxXYZ({ 3, 3, 0 }, { 12, 12, 0 }),
|
||||
BoundBoxXYZ({ 3, 17, 0 }, { 12, 12, 0 }),
|
||||
BoundBoxXYZ({ 17, 17, 0 }, { 12, 12, 0 }),
|
||||
};
|
||||
constexpr SceneryBoundBoxes FullTileNorthwestSide = {
|
||||
BoundBoxXYZ({ 3, 3, 0 }, { 28, 12, 0 }),
|
||||
BoundBoxXYZ({ 3, 3, 0 }, { 12, 28, 0 }),
|
||||
BoundBoxXYZ({ 3, 17, 0 }, { 26, 12, 0 }),
|
||||
BoundBoxXYZ({ 17, 3, 0 }, { 12, 26, 0 }),
|
||||
};
|
||||
|
||||
// LargeSpecialCenter matches large scenery with allowed walls and small scenery with SMALL_SCENERY_FLAG_VOFFSET_CENTRE
|
||||
constexpr SceneryBoundBoxes FullTile = {
|
||||
BoundBoxXYZ({ 3, 3, 0 }, { 26, 26, 0 }),
|
||||
BoundBoxXYZ({ 3, 3, 0 }, { 26, 26, 0 }),
|
||||
BoundBoxXYZ({ 3, 3, 0 }, { 26, 26, 0 }),
|
||||
BoundBoxXYZ({ 3, 3, 0 }, { 26, 26, 0 }),
|
||||
};
|
||||
|
||||
// Large matches large scenery and small scenery that do not allow walls.
|
||||
constexpr SceneryBoundBoxes FullTileLarge = {
|
||||
BoundBoxXYZ({ 1, 1, 0 }, { 30, 30, 0 }),
|
||||
BoundBoxXYZ({ 1, 1, 0 }, { 30, 30, 0 }),
|
||||
BoundBoxXYZ({ 1, 1, 0 }, { 30, 30, 0 }),
|
||||
BoundBoxXYZ({ 1, 1, 0 }, { 30, 30, 0 }),
|
||||
};
|
||||
|
||||
// Small Scenery without VOFFSET_CENTRE flag set
|
||||
constexpr SceneryBoundBoxes FullTileThin = {
|
||||
BoundBoxXYZ({ 15, 15, 0 }, { 2, 2, 0 }),
|
||||
BoundBoxXYZ({ 15, 15, 0 }, { 2, 2, 0 }),
|
||||
BoundBoxXYZ({ 15, 15, 0 }, { 2, 2, 0 }),
|
||||
BoundBoxXYZ({ 15, 15, 0 }, { 2, 2, 0 }),
|
||||
};
|
||||
|
||||
static const std::array<SceneryBoundBoxes, DefaultBoundingBoxType::CountBox> boundBoxes = {
|
||||
QuarterTile,
|
||||
HalfTile,
|
||||
FullTileNorthQuadrant,
|
||||
FullTileNortheastSide,
|
||||
FullTileEastQuadrant,
|
||||
FullTileSoutheastSide,
|
||||
FullTileSouthQuadrant,
|
||||
FullTileSouthwestSide,
|
||||
FullTileWestQuadrant,
|
||||
FullTileNorthwestSide,
|
||||
FullTile,
|
||||
FullTileLarge,
|
||||
FullTileThin,
|
||||
};
|
||||
|
||||
#pragma endregion
|
||||
|
||||
static const EnumMap<DefaultBoundingBoxType> BBoxTypeLookup{
|
||||
{ "quarterTile", DefaultBoundingBoxType::QuarterTileBox },
|
||||
{ "halfTile", DefaultBoundingBoxType::HalfTileBox },
|
||||
{ "cornerNorth", DefaultBoundingBoxType::FullTileNorthQuadrantBox },
|
||||
{ "sideNortheast", DefaultBoundingBoxType::FullTileNortheastSideBox },
|
||||
{ "cornerEast", DefaultBoundingBoxType::FullTileEastQuadrantBox },
|
||||
{ "sideSoutheast", DefaultBoundingBoxType::FullTileSoutheastSideBox },
|
||||
{ "cornerSouth", DefaultBoundingBoxType::FullTileSouthQuadrantBox },
|
||||
{ "sideSouthwest", DefaultBoundingBoxType::FullTileSouthwestSideBox },
|
||||
{ "cornerEast", DefaultBoundingBoxType::FullTileWestQuadrantBox },
|
||||
{ "sideNorthwest", DefaultBoundingBoxType::FullTileNorthwestSideBox },
|
||||
{ "fullTile", DefaultBoundingBoxType::FullTileBox },
|
||||
{ "fullTileLarge", DefaultBoundingBoxType::FullTileLargeBox },
|
||||
{ "fullTileThin", DefaultBoundingBoxType::FullTileThinBox }
|
||||
};
|
||||
|
||||
static DefaultBoundingBoxType GetBoundingBoxTypeFromString(const std::string& s)
|
||||
{
|
||||
auto result = BBoxTypeLookup.find(s);
|
||||
return (result != BBoxTypeLookup.end()) ? result->second : DefaultBoundingBoxType::FullTileBox;
|
||||
}
|
||||
|
||||
SceneryBoundBoxes GetDefaultSceneryBoundBoxes(DefaultBoundingBoxType type)
|
||||
{
|
||||
if (type >= DefaultBoundingBoxType::CountBox)
|
||||
return boundBoxes[DefaultBoundingBoxType::FullTileBox];
|
||||
return boundBoxes[type];
|
||||
}
|
||||
|
||||
static BoundBoxXYZ ReadBoundBox(json_t& box)
|
||||
{
|
||||
auto jOffset = box["offset"];
|
||||
auto jLength = box["size"];
|
||||
|
||||
Guard::Assert(
|
||||
jOffset.is_array() && jLength.is_array() && jOffset.size() >= 3 && jLength.size() >= 3,
|
||||
"ReadBoundBox expects arrays 'offset' and 'size' with 3 elements");
|
||||
BoundBoxXYZ boundBox = {
|
||||
{
|
||||
Json::GetNumber<int32_t>(jOffset[0], 0),
|
||||
Json::GetNumber<int32_t>(jOffset[1], 0),
|
||||
Json::GetNumber<int32_t>(jOffset[2], 0),
|
||||
},
|
||||
{
|
||||
Json::GetNumber<int32_t>(jLength[0], 0),
|
||||
Json::GetNumber<int32_t>(jLength[1], 0),
|
||||
Json::GetNumber<int32_t>(jLength[2], 0),
|
||||
},
|
||||
};
|
||||
return boundBox;
|
||||
}
|
||||
|
||||
// Rotates a BoundBoxXYZ clockwise 90 degrees around the vertical axis.
|
||||
static BoundBoxXYZ RotateBoundBox(BoundBoxXYZ box, CoordsXY rotationCenter)
|
||||
{
|
||||
CoordsXYZ rotatedLength = { box.length.y, box.length.x, box.length.z };
|
||||
// equations are performed in double scale to avoid decimals
|
||||
CoordsXY relativeCentroid = box.offset * 2 + box.length - rotationCenter * 2;
|
||||
CoordsXY rotatedCentroid = { relativeCentroid.y, -relativeCentroid.x };
|
||||
CoordsXY newCorner = (rotatedCentroid - rotatedLength) / 2 + rotationCenter;
|
||||
return { { newCorner.x, newCorner.y, box.offset.z }, rotatedLength };
|
||||
}
|
||||
|
||||
SceneryBoundBoxes ReadBoundBoxes(json_t& jBBox, int32_t defaultHeight, bool fullTile)
|
||||
{
|
||||
SceneryBoundBoxes boxes;
|
||||
if (jBBox.is_array())
|
||||
{
|
||||
Guard::Assert(jBBox.size() >= 4, "boundBox arrays require four elements, one for each view angle");
|
||||
// array of four bboxes
|
||||
for (uint8_t i = 0; i < NumOrthogonalDirections; i++)
|
||||
boxes[i] = ReadBoundBox(jBBox[i]);
|
||||
}
|
||||
else if (jBBox.is_object())
|
||||
{
|
||||
// single box, rotated around (16, 16) if fulltile or (8,8) if quarter tile
|
||||
CoordsXY rotationCenter = { 8, 8 };
|
||||
if (fullTile)
|
||||
{
|
||||
rotationCenter = { 16, 16 };
|
||||
}
|
||||
auto bBox = ReadBoundBox(jBBox);
|
||||
boxes[0] = bBox;
|
||||
boxes[1] = RotateBoundBox(bBox, rotationCenter);
|
||||
boxes[2] = RotateBoundBox(boxes[1], rotationCenter);
|
||||
boxes[3] = RotateBoundBox(boxes[2], rotationCenter);
|
||||
}
|
||||
else
|
||||
{
|
||||
Guard::Assert(
|
||||
jBBox.is_string(),
|
||||
"boundBox must be an array of four boundBox objects, a single boundBox object, or a string matching the "
|
||||
"DefaultBoundingBoxType enum.");
|
||||
boxes = GetDefaultSceneryBoundBoxes(GetBoundingBoxTypeFromString(Json::GetString(jBBox)));
|
||||
for (uint8_t i = 0; i < NumOrthogonalDirections; i++)
|
||||
boxes[i].length.z = defaultHeight;
|
||||
}
|
||||
return boxes;
|
||||
}
|
||||
|
||||
static const EnumMap<DefaultSpriteOffsetType> SpriteOffsetLookup{
|
||||
{ "quarterTile", DefaultSpriteOffsetType::QuarterTileOffset },
|
||||
{ "fullTileThin", DefaultSpriteOffsetType::FullTileThinOffset },
|
||||
{ "halfTile", DefaultSpriteOffsetType::FullTileOffset },
|
||||
{ "fullTile", DefaultSpriteOffsetType::FullTileOffset },
|
||||
{ "fullTileLarge", DefaultSpriteOffsetType::FullTileLargeOffset },
|
||||
{ "largeScenery", DefaultSpriteOffsetType::LargeSceneryOffset },
|
||||
};
|
||||
|
||||
static DefaultSpriteOffsetType GetSpriteOffsetTypeFromString(const std::string& s)
|
||||
{
|
||||
auto result = SpriteOffsetLookup.find(s);
|
||||
return (result != SpriteOffsetLookup.end()) ? result->second : DefaultSpriteOffsetType::LargeSceneryOffset;
|
||||
}
|
||||
|
||||
CoordsXYZ GetDefaultSpriteOffset(DefaultSpriteOffsetType type)
|
||||
{
|
||||
if (type >= DefaultSpriteOffsetType::CountOffset)
|
||||
return DefaultSpriteOffsets[DefaultSpriteOffsetType::LargeSceneryOffset];
|
||||
return DefaultSpriteOffsets[type];
|
||||
}
|
||||
|
||||
CoordsXYZ ReadSpriteOffset(json_t& jCoords)
|
||||
{
|
||||
if (jCoords.is_string())
|
||||
{
|
||||
return DefaultSpriteOffsets[GetSpriteOffsetTypeFromString(Json::GetString(jCoords))];
|
||||
}
|
||||
Guard::Assert(jCoords.is_array() && jCoords.size() >= 3, "spriteOffsetCoordinates must be an array with three elements");
|
||||
CoordsXYZ coordinates = {
|
||||
Json::GetNumber<int32_t>(jCoords[0], 0),
|
||||
Json::GetNumber<int32_t>(jCoords[1], 0),
|
||||
Json::GetNumber<int32_t>(jCoords[2], 0),
|
||||
};
|
||||
return coordinates;
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
/*****************************************************************************
|
||||
* Copyright (c) 2014-2024 OpenRCT2 developers
|
||||
*
|
||||
* For a complete list of all authors, please refer to contributors.md
|
||||
* Interested in contributing? Visit https://github.com/OpenRCT2/OpenRCT2
|
||||
*
|
||||
* OpenRCT2 is licensed under the GNU General Public License version 3.
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../core/EnumMap.hpp"
|
||||
#include "../core/Json.hpp"
|
||||
#include "../core/Memory.hpp"
|
||||
#include "../paint/Boundbox.h"
|
||||
#include "../world/Scenery.h"
|
||||
|
||||
enum DefaultBoundingBoxType : uint8_t
|
||||
{
|
||||
QuarterTileBox,
|
||||
HalfTileBox,
|
||||
FullTileNorthQuadrantBox,
|
||||
FullTileNortheastSideBox,
|
||||
FullTileEastQuadrantBox,
|
||||
FullTileSoutheastSideBox,
|
||||
FullTileSouthQuadrantBox,
|
||||
FullTileSouthwestSideBox,
|
||||
FullTileWestQuadrantBox,
|
||||
FullTileNorthwestSideBox,
|
||||
FullTileBox,
|
||||
FullTileLargeBox,
|
||||
FullTileThinBox,
|
||||
CountBox
|
||||
};
|
||||
|
||||
enum DefaultSpriteOffsetType : uint8_t
|
||||
{
|
||||
QuarterTileOffset,
|
||||
FullTileThinOffset,
|
||||
FullTileOffset,
|
||||
FullTileLargeOffset,
|
||||
LargeSceneryOffset,
|
||||
CountOffset
|
||||
};
|
||||
|
||||
SceneryBoundBoxes GetDefaultSceneryBoundBoxes(DefaultBoundingBoxType type);
|
||||
SceneryBoundBoxes ReadBoundBoxes(json_t& jBBox, int32_t defaultHeight, bool fullTile);
|
||||
CoordsXYZ GetDefaultSpriteOffset(DefaultSpriteOffsetType type);
|
||||
CoordsXYZ ReadSpriteOffset(json_t& jCoords);
|
|
@ -11,6 +11,7 @@
|
|||
|
||||
#include "../common.h"
|
||||
#include "../interface/Cursors.h"
|
||||
#include "../world/Scenery.h"
|
||||
#include "ObjectTypes.h"
|
||||
|
||||
enum SMALL_SCENERY_FLAGS : uint32_t
|
||||
|
@ -67,6 +68,8 @@ struct SmallSceneryEntry
|
|||
uint16_t animation_mask;
|
||||
uint16_t num_frames;
|
||||
ObjectEntryIndex scenery_tab_id;
|
||||
SceneryBoundBoxes boundBoxes;
|
||||
CoordsXYZ spriteOffset;
|
||||
|
||||
constexpr bool HasFlag(const uint32_t _flags) const
|
||||
{
|
||||
|
|
|
@ -19,6 +19,7 @@
|
|||
#include "../interface/Cursors.h"
|
||||
#include "../localisation/Language.h"
|
||||
#include "../world/Scenery.h"
|
||||
#include "SceneryBoundingBox.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
|
@ -35,6 +36,7 @@ void SmallSceneryObject::ReadLegacy(IReadObjectContext* context, OpenRCT2::IStre
|
|||
_legacyType.animation_mask = stream->ReadValue<uint16_t>();
|
||||
_legacyType.num_frames = stream->ReadValue<uint16_t>();
|
||||
_legacyType.scenery_tab_id = OBJECT_ENTRY_INDEX_NULL;
|
||||
SetBoundingBoxFromFlags();
|
||||
|
||||
GetStringTable().Read(context, stream, ObjectStringID::NAME);
|
||||
|
||||
|
@ -247,6 +249,17 @@ void SmallSceneryObject::ReadJson(IReadObjectContext* context, json_t& root)
|
|||
}
|
||||
}
|
||||
}
|
||||
SetBoundingBoxFromFlags();
|
||||
auto jBBox = properties["boundingBox"];
|
||||
if (!jBBox.empty())
|
||||
{
|
||||
_legacyType.boundBoxes = ReadBoundBoxes(
|
||||
jBBox, _legacyType.boundBoxes[0].length.z,
|
||||
_legacyType.HasFlag(SMALL_SCENERY_FLAG_FULL_TILE) || _legacyType.HasFlag(SMALL_SCENERY_FLAG_HALF_SPACE));
|
||||
}
|
||||
auto jSpriteOffset = properties["spriteOffsetCoordinates"];
|
||||
if (!jSpriteOffset.empty())
|
||||
_legacyType.spriteOffset = ReadSpriteOffset(jSpriteOffset);
|
||||
|
||||
auto jFrameOffsets = properties["frameOffsets"];
|
||||
if (jFrameOffsets.is_array())
|
||||
|
@ -271,3 +284,50 @@ std::vector<uint8_t> SmallSceneryObject::ReadJsonFrameOffsets(json_t& jFrameOffs
|
|||
}
|
||||
return offsets;
|
||||
}
|
||||
|
||||
static int32_t getBoundBoxHeight(uint8_t clearanceHeight)
|
||||
{
|
||||
int32_t height = clearanceHeight - 4;
|
||||
if (height > 128 || height < 0)
|
||||
{
|
||||
height = 128;
|
||||
}
|
||||
return height - 1;
|
||||
}
|
||||
|
||||
void SmallSceneryObject::SetBoundingBoxFromFlags()
|
||||
{
|
||||
DefaultBoundingBoxType boundBoxType = DefaultBoundingBoxType::QuarterTileBox;
|
||||
DefaultSpriteOffsetType spriteOffsetType = DefaultSpriteOffsetType::QuarterTileOffset;
|
||||
if (_legacyType.HasFlag(SMALL_SCENERY_FLAG_FULL_TILE))
|
||||
{
|
||||
boundBoxType = DefaultBoundingBoxType::FullTileThinBox;
|
||||
spriteOffsetType = DefaultSpriteOffsetType::FullTileThinOffset;
|
||||
if (_legacyType.HasFlag(SMALL_SCENERY_FLAG_HALF_SPACE))
|
||||
{
|
||||
spriteOffsetType = DefaultSpriteOffsetType::FullTileOffset;
|
||||
boundBoxType = DefaultBoundingBoxType::HalfTileBox;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (_legacyType.HasFlag(SMALL_SCENERY_FLAG_VOFFSET_CENTRE))
|
||||
{
|
||||
boundBoxType = DefaultBoundingBoxType::FullTileBox;
|
||||
spriteOffsetType = DefaultSpriteOffsetType::FullTileOffset;
|
||||
if (_legacyType.HasFlag(SMALL_SCENERY_FLAG_NO_WALLS))
|
||||
{
|
||||
boundBoxType = DefaultBoundingBoxType::FullTileLargeBox;
|
||||
spriteOffsetType = DefaultSpriteOffsetType::FullTileLargeOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_legacyType.spriteOffset = GetDefaultSpriteOffset(spriteOffsetType);
|
||||
_legacyType.boundBoxes = GetDefaultSceneryBoundBoxes(boundBoxType);
|
||||
|
||||
auto clearanceHeight = getBoundBoxHeight(_legacyType.height);
|
||||
for (uint8_t i = 0; i < NumOrthogonalDirections; i++)
|
||||
{
|
||||
_legacyType.boundBoxes[i].length.z = clearanceHeight;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,4 +38,5 @@ private:
|
|||
static std::vector<uint8_t> ReadFrameOffsets(OpenRCT2::IStream* stream);
|
||||
static std::vector<uint8_t> ReadJsonFrameOffsets(json_t& jFrameOffsets);
|
||||
void PerformFixes();
|
||||
void SetBoundingBoxFromFlags();
|
||||
};
|
||||
|
|
|
@ -33,28 +33,6 @@
|
|||
|
||||
using namespace OpenRCT2;
|
||||
|
||||
// clang-format off
|
||||
static constexpr BoundBoxXY LargeSceneryBoundBoxes[] = {
|
||||
{ { 3, 3 }, { 26, 26 } },
|
||||
{ { 17, 17 }, { 12, 12 } },
|
||||
{ { 17, 3 }, { 12, 12 } },
|
||||
{ { 17, 3 }, { 12, 26 } },
|
||||
{ { 3, 3 }, { 12, 12 } },
|
||||
{ { 3, 3 }, { 26, 26 } },
|
||||
{ { 3, 3 }, { 28, 12 } },
|
||||
{ { 3, 3 }, { 26, 26 } },
|
||||
{ { 3, 17 }, { 12, 12 } },
|
||||
{ { 3, 17 }, { 26, 12 } },
|
||||
{ { 3, 3 }, { 26, 26 } },
|
||||
{ { 3, 3 }, { 26, 26 } },
|
||||
{ { 3, 3 }, { 12, 28 } },
|
||||
{ { 3, 3 }, { 26, 26 } },
|
||||
{ { 3, 3 }, { 26, 26 } },
|
||||
{ { 3, 3 }, { 26, 26 } },
|
||||
{ { 1, 1 }, { 30, 30 } },
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
static void PaintLargeScenerySupports(
|
||||
PaintSession& session, uint8_t direction, uint16_t height, const LargeSceneryElement& tileElement, ImageId imageTemplate,
|
||||
const LargeSceneryTile& tile)
|
||||
|
@ -387,20 +365,13 @@ void PaintLargeScenery(PaintSession& session, uint8_t direction, uint16_t height
|
|||
}
|
||||
}
|
||||
|
||||
auto boxlengthZ = std::min<uint8_t>(tile->z_clearance, 128) - 3;
|
||||
auto flags = tile->flags;
|
||||
auto bbIndex = 16;
|
||||
if (flags & 0xF00)
|
||||
{
|
||||
flags &= 0xF000;
|
||||
flags = Numerics::rol16(flags, direction);
|
||||
bbIndex = (flags & 0xF) | (flags >> 12);
|
||||
}
|
||||
const CoordsXYZ& bbOffset = { LargeSceneryBoundBoxes[bbIndex].offset, height };
|
||||
const CoordsXYZ& bbLength = { LargeSceneryBoundBoxes[bbIndex].length, boxlengthZ };
|
||||
auto boundBox = tile->boundBoxes[direction];
|
||||
auto offset = tile->spriteOffset;
|
||||
boundBox.offset.z += height;
|
||||
offset.z += height;
|
||||
|
||||
auto imageIndex = sceneryEntry->image + 4 + (sequenceNum << 2) + direction;
|
||||
PaintAddImageAsParent(session, imageTemplate.WithIndex(imageIndex), { 0, 0, height }, { bbOffset, bbLength });
|
||||
PaintAddImageAsParent(session, imageTemplate.WithIndex(imageIndex), offset, boundBox);
|
||||
|
||||
if (sceneryEntry->scrolling_mode != SCROLLING_MODE_NONE && direction != 1 && direction != 2)
|
||||
{
|
||||
|
@ -413,7 +384,8 @@ void PaintLargeScenery(PaintSession& session, uint8_t direction, uint16_t height
|
|||
auto sequenceDirection2 = (tileElement.GetSequenceIndex() - 1) & 3;
|
||||
if (sequenceDirection2 == direction)
|
||||
{
|
||||
PaintLargeSceneryScrollingText(session, *sceneryEntry, tileElement, direction, height, bbOffset, isGhost);
|
||||
PaintLargeSceneryScrollingText(
|
||||
session, *sceneryEntry, tileElement, direction, height, boundBox.offset, isGhost);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,11 +26,11 @@
|
|||
|
||||
using namespace OpenRCT2;
|
||||
|
||||
static constexpr CoordsXY lengths[] = {
|
||||
{ 12, 26 },
|
||||
{ 26, 12 },
|
||||
{ 12, 26 },
|
||||
{ 26, 12 },
|
||||
static constexpr CoordsXY newQuadrantOffsets[NumOrthogonalDirections] = {
|
||||
{ 0, 0 },
|
||||
{ 0, 16 },
|
||||
{ 16, 16 },
|
||||
{ 16, 0 },
|
||||
};
|
||||
|
||||
static void PaintSmallScenerySupports(
|
||||
|
@ -119,63 +119,17 @@ static void PaintSmallSceneryBody(
|
|||
{
|
||||
PROFILED_FUNCTION();
|
||||
|
||||
BoundBoxXYZ boundBox = { { 0, 0, height }, { 2, 2, 0 } };
|
||||
BoundBoxXYZ boundBox = sceneryEntry->boundBoxes[direction];
|
||||
CoordsXYZ offset = sceneryEntry->spriteOffset;
|
||||
boundBox.offset.z += height;
|
||||
offset.z += height;
|
||||
|
||||
CoordsXYZ offset = { 0, 0, height };
|
||||
if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE))
|
||||
{
|
||||
if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_HALF_SPACE))
|
||||
{
|
||||
static constexpr CoordsXY sceneryHalfTileOffsets[] = {
|
||||
{ 3, 3 },
|
||||
{ 3, 17 },
|
||||
{ 17, 3 },
|
||||
{ 3, 3 },
|
||||
};
|
||||
boundBox.offset.x = sceneryHalfTileOffsets[direction].x;
|
||||
boundBox.offset.y = sceneryHalfTileOffsets[direction].y;
|
||||
boundBox.length.x = lengths[direction].x;
|
||||
boundBox.length.y = lengths[direction].y;
|
||||
offset.x = 3;
|
||||
offset.y = 3;
|
||||
}
|
||||
else
|
||||
{
|
||||
offset.x = 15;
|
||||
offset.y = 15;
|
||||
if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_VOFFSET_CENTRE))
|
||||
{
|
||||
offset.x = 3;
|
||||
offset.y = 3;
|
||||
boundBox.length.x = 26;
|
||||
boundBox.length.y = 26;
|
||||
if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_NO_WALLS))
|
||||
{
|
||||
offset.x = 1;
|
||||
offset.y = 1;
|
||||
boundBox.length.x = 30;
|
||||
boundBox.length.y = 30;
|
||||
}
|
||||
}
|
||||
boundBox.offset.x = offset.x;
|
||||
boundBox.offset.y = offset.y;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_FULL_TILE))
|
||||
{
|
||||
uint8_t quadrant = (sceneryElement.GetSceneryQuadrant() + session.CurrentRotation) & 3;
|
||||
// -1 to maintain compatibility with existing CSOs in context of issue #17616
|
||||
offset.x = SceneryQuadrantOffsets[quadrant].x - 1;
|
||||
offset.y = SceneryQuadrantOffsets[quadrant].y - 1;
|
||||
boundBox.offset.x = offset.x;
|
||||
boundBox.offset.y = offset.y;
|
||||
boundBox.offset += newQuadrantOffsets[quadrant];
|
||||
offset += newQuadrantOffsets[quadrant];
|
||||
}
|
||||
boundBox.length.z = sceneryEntry->height - 4;
|
||||
if (boundBox.length.z > 128 || boundBox.length.z < 0)
|
||||
{
|
||||
boundBox.length.z = 128;
|
||||
}
|
||||
boundBox.length.z--;
|
||||
|
||||
ImageIndex baseImageIndex = sceneryEntry->image + direction;
|
||||
if (sceneryEntry->HasFlag(SMALL_SCENERY_FLAG_CAN_WITHER))
|
||||
|
|
|
@ -1625,19 +1625,12 @@ constexpr CoordsXY defaultRightQuarterTurn3TilesBoundLengths[4][3] = {
|
|||
},
|
||||
};
|
||||
|
||||
static constexpr int8_t right_quarter_turn_3_tiles_sprite_map[] = {
|
||||
0,
|
||||
-1,
|
||||
1,
|
||||
2,
|
||||
};
|
||||
|
||||
void TrackPaintUtilRightQuarterTurn3TilesPaint(
|
||||
PaintSession& session, int8_t thickness, int16_t height, Direction direction, uint8_t trackSequence,
|
||||
const ImageId colourFlags, const uint32_t sprites[4][3], const CoordsXY offsets[4][3], const CoordsXY boundsLengths[4][3],
|
||||
const CoordsXYZ boundsOffsets[4][3])
|
||||
{
|
||||
int32_t index = right_quarter_turn_3_tiles_sprite_map[trackSequence];
|
||||
int32_t index = kRightQuarterTurn3TilesSpriteMap[trackSequence];
|
||||
if (index < 0)
|
||||
{
|
||||
return;
|
||||
|
@ -1665,7 +1658,7 @@ void TrackPaintUtilRightQuarterTurn3TilesPaint2WithHeightOffset(
|
|||
PaintSession& session, int8_t thickness, int16_t height, Direction direction, uint8_t trackSequence,
|
||||
const ImageId colourFlags, const uint32_t sprites[4][3], int32_t heightOffset)
|
||||
{
|
||||
int8_t sprite = right_quarter_turn_3_tiles_sprite_map[trackSequence];
|
||||
int8_t sprite = kRightQuarterTurn3TilesSpriteMap[trackSequence];
|
||||
if (sprite < 0)
|
||||
{
|
||||
return;
|
||||
|
@ -1753,7 +1746,7 @@ void TrackPaintUtilRightQuarterTurn3TilesPaint3(
|
|||
PaintSession& session, int16_t height, Direction direction, uint8_t trackSequence, const ImageId colourFlags,
|
||||
const SpriteBb sprites[4][3])
|
||||
{
|
||||
int8_t sprite = right_quarter_turn_3_tiles_sprite_map[trackSequence];
|
||||
int8_t sprite = kRightQuarterTurn3TilesSpriteMap[trackSequence];
|
||||
if (sprite < 0)
|
||||
{
|
||||
return;
|
||||
|
@ -1770,7 +1763,7 @@ void TrackPaintUtilRightQuarterTurn3TilesPaint4(
|
|||
PaintSession& session, int16_t height, Direction direction, uint8_t trackSequence, const ImageId colourFlags,
|
||||
const SpriteBb sprites[4][3])
|
||||
{
|
||||
int8_t sprite = right_quarter_turn_3_tiles_sprite_map[trackSequence];
|
||||
int8_t sprite = kRightQuarterTurn3TilesSpriteMap[trackSequence];
|
||||
if (sprite < 0)
|
||||
{
|
||||
return;
|
||||
|
|
|
@ -258,6 +258,8 @@ extern const CoordsXY defaultRightQuarterTurn3TilesOffsets[4][3];
|
|||
extern const CoordsXYZ defaultRightQuarterTurn3TilesBoundOffsets[4][3];
|
||||
extern const CoordsXY defaultRightQuarterTurn3TilesBoundLengths[4][3];
|
||||
|
||||
constexpr int8_t kRightQuarterTurn3TilesSpriteMap[] = { 0, -1, 1, 2 };
|
||||
|
||||
extern const CoordsXY defaultRightHelixUpSmallQuarterBoundLengths[4][3][2];
|
||||
extern const CoordsXYZ defaultRightHelixUpSmallQuarterBoundOffsets[4][3][2];
|
||||
|
||||
|
|
|
@ -1392,9 +1392,7 @@ static void PaintMiniatureRailwayTrackRightQuarterTurn3Tiles(
|
|||
miniature_railway_right_quarter_turn_3_tile_track_floor, nullptr, defaultRightQuarterTurn3TilesBoundLengths,
|
||||
miniature_railway_right_quarter_turn_3_tile_bound_offsets);
|
||||
|
||||
static constexpr int8_t _right_quarter_turn_3_tiles_sprite_map[] = { 0, -1, 1, 2 };
|
||||
|
||||
int32_t index = _right_quarter_turn_3_tiles_sprite_map[trackSequence];
|
||||
int32_t index = kRightQuarterTurn3TilesSpriteMap[trackSequence];
|
||||
|
||||
auto imageId = session.TrackColours.WithIndex(
|
||||
miniature_railway_track_pieces_flat_quarter_turn_3_tiles[direction][index]);
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#pragma once
|
||||
|
||||
#include "../common.h"
|
||||
#include "../paint/Boundbox.h"
|
||||
#include "Location.hpp"
|
||||
#include "ScenerySelection.h"
|
||||
|
||||
|
@ -18,6 +19,8 @@
|
|||
constexpr uint8_t kSceneryWitherAgeThreshold1 = 0x28;
|
||||
constexpr uint8_t kSceneryWitherAgeThreshold2 = 0x37;
|
||||
|
||||
using SceneryBoundBoxes = std::array<BoundBoxXYZ, NumOrthogonalDirections>;
|
||||
|
||||
enum
|
||||
{
|
||||
SCENERY_TYPE_SMALL,
|
||||
|
|
Loading…
Reference in New Issue