Import RCT1 tile elements by x/y (#13740)

* Import RCT1 tile elements by x/y

* Clean up wall import

* Move tile pointers creation to a separate function and to heap

* Create class for tile pointer index
This commit is contained in:
Michael Steenbeek 2021-01-12 18:53:58 +01:00 committed by GitHub
parent e51a048add
commit 830d2426bd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 130 additions and 206 deletions

View File

@ -334,7 +334,7 @@ private:
// Avoid reusing the value used for last import
_parkValueConversionFactor = 0;
uint16_t mapSize = _s4.map_size == 0 ? 128 : _s4.map_size;
uint16_t mapSize = _s4.map_size == 0 ? RCT1_MAX_MAP_SIZE : _s4.map_size;
String::Set(gScenarioFileName, sizeof(gScenarioFileName), GetRCT1ScenarioName().c_str());
@ -1973,26 +1973,45 @@ private:
{
gMapBaseZ = 7;
for (uint32_t index = 0, dstOffset = 0; index < RCT1_MAX_TILE_ELEMENTS; index++)
// Build tile pointer cache (needed to get the first element at a certain location)
auto tilePointerIndex = TilePointerIndex<RCT12TileElement>(RCT1_MAX_MAP_SIZE, _s4.tile_elements);
TileElement* dstElement = gTileElements;
for (TileCoordsXY coords = { 0, 0 }; coords.y < MAXIMUM_MAP_SIZE_TECHNICAL; coords.y++)
{
auto src = &_s4.tile_elements[index];
auto dst = &gTileElements[index + dstOffset];
if (src->base_height == RCT12_MAX_ELEMENT_HEIGHT)
for (coords.x = 0; coords.x < MAXIMUM_MAP_SIZE_TECHNICAL; coords.x++)
{
std::memcpy(dst, src, sizeof(*src));
}
else
{
ImportTileElement(dst, src);
if (coords.x >= RCT1_MAX_MAP_SIZE || coords.y >= RCT1_MAX_MAP_SIZE)
{
dstElement->ClearAs(TILE_ELEMENT_TYPE_SURFACE);
dstElement->SetLastForTile(true);
dstElement++;
continue;
}
// This is the equivalent of map_get_first_element_at(x, y), but on S4 data.
RCT12TileElement* srcElement = tilePointerIndex.GetFirstElementAt(coords);
do
{
if (srcElement->base_height == RCT12_MAX_ELEMENT_HEIGHT)
continue;
auto numAddedElements = ImportTileElement(dstElement, srcElement);
dstElement += numAddedElements;
} while (!(srcElement++)->IsLastForTile());
// Set last element flag in case the original last element was never added
(dstElement - 1)->SetLastForTile(true);
}
}
ClearExtraTileEntries();
FixWalls();
map_update_tile_pointers();
FixEntrancePositions();
}
void ImportTileElement(TileElement* dst, const RCT12TileElement* src)
size_t ImportTileElement(TileElement* dst, const RCT12TileElement* src)
{
// Todo: allow for changing definition of OpenRCT2 tile element types - replace with a map
uint8_t tileElementType = src->GetType();
@ -2002,7 +2021,8 @@ private:
// All saved in "flags"
dst->SetOccupiedQuadrants(src->GetOccupiedQuadrants());
// Skipping IsGhost, which appears to use a different flag in RCT1.
dst->SetLastForTile(src->IsLastForTile());
// This flag will be set by the caller.
dst->SetLastForTile(false);
dst->SetBaseZ(src->base_height * RCT1_COORDS_Z_STEP);
dst->SetClearanceZ(src->clearance_height * RCT1_COORDS_Z_STEP);
@ -2023,7 +2043,7 @@ private:
dst2->SetWaterHeight(src2->GetWaterHeight());
dst2->SetHasTrackThatNeedsWater(src2->HasTrackThatNeedsWater());
break;
return 1;
}
case TILE_ELEMENT_TYPE_PATH:
{
@ -2094,7 +2114,7 @@ private:
}
dst2->SetAddition(entryIndex + 1);
}
break;
return 1;
}
case TILE_ELEMENT_TYPE_TRACK:
{
@ -2139,7 +2159,7 @@ private:
dst2->SetMazeEntry(src2->GetMazeEntry());
}
break;
return 1;
}
case TILE_ELEMENT_TYPE_SMALL_SCENERY:
{
@ -2174,7 +2194,7 @@ private:
break;
}
break;
return 1;
}
case TILE_ELEMENT_TYPE_ENTRANCE:
{
@ -2197,17 +2217,62 @@ private:
dst2->SetPathType(entryIndex & 0x7F);
}
break;
return 1;
}
case TILE_ELEMENT_TYPE_WALL:
{
auto dst2 = dst->AsWall();
auto src2 = src->AsWall();
auto slope = src2->GetRCT1Slope();
size_t numAddedElements = 0;
dst2->SetSlope(src2->GetSlope());
dst2->SetRawRCT1Data(src2->GetRawRCT1WallTypeData());
for (int32_t edge = 0; edge < 4; edge++)
{
int32_t type = src2->GetRCT1WallType(edge);
if (type == -1)
continue;
break;
colour_t colourA = RCT1::GetColour(src2->GetRCT1WallColour());
colour_t colourB = COLOUR_BLACK;
colour_t colourC = COLOUR_BLACK;
ConvertWall(type, &colourA, &colourB);
type = _wallTypeToEntryMap[type];
auto baseZ = src->base_height * RCT1_COORDS_Z_STEP;
auto clearanceZ = src->clearance_height * RCT1_COORDS_Z_STEP;
auto edgeSlope = LandSlopeToWallSlope[slope][edge & 3];
if (edgeSlope & (EDGE_SLOPE_UPWARDS | EDGE_SLOPE_DOWNWARDS))
{
clearanceZ += LAND_HEIGHT_STEP;
}
if (edgeSlope & EDGE_SLOPE_ELEVATED)
{
edgeSlope &= ~EDGE_SLOPE_ELEVATED;
baseZ += LAND_HEIGHT_STEP;
clearanceZ += LAND_HEIGHT_STEP;
}
dst->SetType(TILE_ELEMENT_TYPE_WALL);
dst->SetDirection(edge);
dst->SetBaseZ(baseZ);
dst->SetClearanceZ(clearanceZ);
// Will be set later.
dst->SetLastForTile(false);
auto* wallElement = dst->AsWall();
wallElement->SetEntryIndex(type);
wallElement->SetPrimaryColour(colourA);
wallElement->SetSecondaryColour(colourB);
wallElement->SetTertiaryColour(colourC);
wallElement->SetBannerIndex(BANNER_INDEX_NULL);
wallElement->SetAcrossTrack(false);
wallElement->SetAnimationIsBackwards(false);
wallElement->SetSlope(edgeSlope);
dst++;
numAddedElements++;
}
return numAddedElements;
}
case TILE_ELEMENT_TYPE_LARGE_SCENERY:
{
@ -2220,7 +2285,7 @@ private:
dst2->SetPrimaryColour(RCT1::GetColour(src2->GetPrimaryColour()));
dst2->SetSecondaryColour(RCT1::GetColour(src2->GetSecondaryColour()));
break;
return 1;
}
case TILE_ELEMENT_TYPE_BANNER:
{
@ -2241,11 +2306,13 @@ private:
auto dstBanner = GetBanner(index);
ImportBanner(dstBanner, srcBanner);
}
break;
return 1;
}
default:
assert(false);
}
return 0;
}
void ImportResearch()
@ -2726,141 +2793,9 @@ private:
gSavedViewRotation = _s4.view_rotation;
}
void ClearExtraTileEntries()
void ConvertWall(const int32_t& type, colour_t* colourA, colour_t* colourB)
{
// Reset the map tile pointers
std::fill(std::begin(gTileElementTilePointers), std::end(gTileElementTilePointers), nullptr);
// Get the first free map element
TileElement* nextFreeTileElement = gTileElements;
for (size_t i = 0; i < RCT1_MAX_MAP_SIZE * RCT1_MAX_MAP_SIZE; i++)
{
while (!(nextFreeTileElement++)->IsLastForTile())
;
}
TileElement* tileElement = gTileElements;
TileElement** tilePointer = gTileElementTilePointers;
// 128 rows of map data from RCT1 map
for (int32_t x = 0; x < RCT1_MAX_MAP_SIZE; x++)
{
// Assign the first half of this row
for (int32_t y = 0; y < RCT1_MAX_MAP_SIZE; y++)
{
*tilePointer++ = tileElement;
while (!(tileElement++)->IsLastForTile())
;
}
// Fill the rest of the row with blank tiles
for (int32_t y = 0; y < RCT1_MAX_MAP_SIZE; y++)
{
nextFreeTileElement->ClearAs(TILE_ELEMENT_TYPE_SURFACE);
nextFreeTileElement->SetLastForTile(true);
nextFreeTileElement->AsSurface()->SetSlope(TILE_ELEMENT_SLOPE_FLAT);
nextFreeTileElement->AsSurface()->SetSurfaceStyle(TERRAIN_GRASS);
nextFreeTileElement->AsSurface()->SetEdgeStyle(TERRAIN_EDGE_ROCK);
nextFreeTileElement->AsSurface()->SetGrassLength(GRASS_LENGTH_CLEAR_0);
nextFreeTileElement->AsSurface()->SetOwnership(OWNERSHIP_UNOWNED);
*tilePointer++ = nextFreeTileElement++;
}
}
// 128 extra rows left to fill with blank tiles
for (int32_t y = 0; y < 128 * 256; y++)
{
nextFreeTileElement->ClearAs(TILE_ELEMENT_TYPE_SURFACE);
nextFreeTileElement->SetLastForTile(true);
nextFreeTileElement->AsSurface()->SetSlope(TILE_ELEMENT_SLOPE_FLAT);
nextFreeTileElement->AsSurface()->SetSurfaceStyle(TERRAIN_GRASS);
nextFreeTileElement->AsSurface()->SetEdgeStyle(TERRAIN_EDGE_ROCK);
nextFreeTileElement->AsSurface()->SetGrassLength(GRASS_LENGTH_CLEAR_0);
nextFreeTileElement->AsSurface()->SetOwnership(OWNERSHIP_UNOWNED);
*tilePointer++ = nextFreeTileElement++;
}
gNextFreeTileElement = nextFreeTileElement;
}
void FixWalls()
{
std::vector<TileElement> wallsOnTile = {};
for (int32_t x = 0; x < RCT1_MAX_MAP_SIZE; x++)
{
for (int32_t y = 0; y < RCT1_MAX_MAP_SIZE; y++)
{
TileElement* tileElement = map_get_first_element_at(TileCoordsXY{ x, y }.ToCoordsXY());
if (tileElement == nullptr)
continue;
do
{
if (tileElement->GetType() == TILE_ELEMENT_TYPE_WALL)
{
wallsOnTile.push_back(*tileElement);
tile_element_remove(tileElement);
tileElement--;
}
} while (!(tileElement++)->IsLastForTile());
for (auto originalTileElement : wallsOnTile)
{
auto location = TileCoordsXYZ(x, y, 0).ToCoordsXYZ();
for (int32_t edge = 0; edge < 4; edge++)
{
int32_t type = originalTileElement.AsWall()->GetRCT1WallType(edge);
auto slope = originalTileElement.AsWall()->GetRCT1Slope();
if (type != -1)
{
colour_t colourA = RCT1::GetColour(originalTileElement.AsWall()->GetRCT1WallColour());
colour_t colourB = COLOUR_BLACK;
colour_t colourC = COLOUR_BLACK;
ConvertWall(&type, &colourA, &colourB);
type = _wallTypeToEntryMap[type];
auto baseZ = originalTileElement.GetBaseZ();
auto clearanceZ = originalTileElement.GetClearanceZ();
auto edgeSlope = LandSlopeToWallSlope[slope][edge & 3];
if (edgeSlope & (EDGE_SLOPE_UPWARDS | EDGE_SLOPE_DOWNWARDS))
{
clearanceZ += LAND_HEIGHT_STEP;
}
if (edgeSlope & EDGE_SLOPE_ELEVATED)
{
edgeSlope &= ~EDGE_SLOPE_ELEVATED;
baseZ += LAND_HEIGHT_STEP;
clearanceZ += LAND_HEIGHT_STEP;
}
auto element = tile_element_insert(location, originalTileElement.GetOccupiedQuadrants());
element->SetType(TILE_ELEMENT_TYPE_WALL);
element->SetDirection(edge);
element->SetBaseZ(baseZ);
element->SetClearanceZ(clearanceZ);
auto wallElement = element->AsWall();
wallElement->SetEntryIndex(type);
wallElement->SetPrimaryColour(colourA);
wallElement->SetSecondaryColour(colourB);
wallElement->SetTertiaryColour(colourC);
wallElement->SetBannerIndex(BANNER_INDEX_NULL);
wallElement->SetAcrossTrack(originalTileElement.AsWall()->IsAcrossTrack());
wallElement->SetAnimationIsBackwards(originalTileElement.AsWall()->AnimationIsBackwards());
wallElement->SetSlope(edgeSlope);
}
}
}
wallsOnTile.clear();
}
}
}
void ConvertWall(int32_t* type, colour_t* colourA, colour_t* colourB)
{
switch (*type)
switch (type)
{
case RCT1_WALL_TYPE_WOODEN_PANEL_FENCE:
*colourA = COLOUR_DARK_BROWN;
@ -3166,26 +3101,3 @@ void load_from_sc4(const utf8* path)
objectMgr.LoadObjects(result.RequiredObjects.data(), result.RequiredObjects.size());
s4Importer->Import();
}
int32_t WallElement::GetRCT1WallType(int32_t edge) const
{
uint8_t var_05 = colour_3;
uint16_t var_06 = colour_1 | (animation << 8);
int32_t typeA = (var_05 >> (edge * 2)) & 3;
int32_t typeB = (var_06 >> (edge * 4)) & 0x0F;
if (typeB != 0x0F)
{
return typeA | (typeB << 2);
}
else
{
return -1;
}
}
colour_t WallElement::GetRCT1WallColour() const
{
return (((type & 0xC0) >> 3) | ((entryIndex & 0xE0) >> 5)) & 31;
}

View File

@ -420,11 +420,6 @@ bool RCT12WallElement::AnimationIsBackwards() const
return (animation & WALL_ANIMATION_FLAG_DIRECTION_BACKWARD) != 0;
}
uint32_t RCT12WallElement::GetRawRCT1WallTypeData() const
{
return entryIndex | (colour_3 << 8) | (colour_1 << 16) | (animation << 24);
}
int32_t RCT12WallElement::GetRCT1WallType(int32_t edge) const
{
uint8_t var_05 = colour_3;
@ -448,6 +443,11 @@ colour_t RCT12WallElement::GetRCT1WallColour() const
return ((type & 0xC0) >> 3) | ((entryIndex & 0xE0) >> 5);
}
uint8_t RCT12WallElement::GetRCT1Slope() const
{
return entryIndex & 0b00011111;
}
uint8_t RCT12EntranceElement::GetEntranceType() const
{
return entranceType;

View File

@ -600,9 +600,9 @@ public:
uint8_t GetBannerIndex() const;
bool IsAcrossTrack() const;
bool AnimationIsBackwards() const;
uint32_t GetRawRCT1WallTypeData() const;
int32_t GetRCT1WallType(int32_t edge) const;
colour_t GetRCT1WallColour() const;
uint8_t GetRCT1Slope() const;
void SetEntryIndex(RCT12ObjectEntryIndex newIndex);
void SetSlope(uint8_t newslope);

View File

@ -144,6 +144,36 @@ constexpr auto SURFACE_STYLE_FLAG_RAISE_OR_LOWER_BASE_HEIGHT = 0x20;
extern const uint8_t tile_element_lower_styles[9][32];
extern const uint8_t tile_element_raise_styles[9][32];
template<typename T> class TilePointerIndex
{
std::vector<T*> TilePointers;
uint16_t MapSize;
public:
explicit TilePointerIndex(const uint16_t mapSize, T* tileElements)
{
MapSize = mapSize;
const uint16_t MaxTileElementPointers = MapSize * MapSize;
TilePointers.reserve(MaxTileElementPointers);
T* tileElement = tileElements;
for (size_t y = 0; y < MapSize; y++)
{
for (size_t x = 0; x < MapSize; x++)
{
TilePointers.emplace_back(tileElement);
while (!(tileElement++)->IsLastForTile())
;
}
}
}
T* GetFirstElementAt(TileCoordsXY coords)
{
return TilePointers[coords.x + (coords.y * MapSize)];
}
};
void map_init(int32_t size);
void map_count_remaining_land_rights();

View File

@ -527,11 +527,6 @@ public:
void SetAcrossTrack(bool acrossTrack);
bool AnimationIsBackwards() const;
void SetAnimationIsBackwards(bool isBackwards);
void SetRawRCT1Data(uint32_t rawData);
int32_t GetRCT1WallType(int32_t edge) const;
colour_t GetRCT1WallColour() const;
uint8_t GetRCT1Slope() const;
};
assert_struct_size(WallElement, 16);

View File

@ -186,16 +186,3 @@ void WallElement::SetAnimationIsBackwards(bool isBackwards)
if (isBackwards)
animation |= WALL_ANIMATION_FLAG_DIRECTION_BACKWARD;
}
void WallElement::SetRawRCT1Data(uint32_t rawData)
{
entryIndex = rawData & 0xFF;
colour_3 = (rawData >> 8) & 0xFF;
colour_1 = (rawData >> 16) & 0xFF;
animation = (rawData >> 24) & 0xFF;
}
uint8_t WallElement::GetRCT1Slope() const
{
return entryIndex & 0b00011111;
}