From 830d2426bd860d5a5954c593ca95c320c891792b Mon Sep 17 00:00:00 2001 From: Michael Steenbeek Date: Tue, 12 Jan 2021 18:53:58 +0100 Subject: [PATCH] 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 --- src/openrct2/rct1/S4Importer.cpp | 276 +++++++++++-------------------- src/openrct2/rct12/RCT12.cpp | 10 +- src/openrct2/rct12/RCT12.h | 2 +- src/openrct2/world/Map.h | 30 ++++ src/openrct2/world/TileElement.h | 5 - src/openrct2/world/Wall.cpp | 13 -- 6 files changed, 130 insertions(+), 206 deletions(-) diff --git a/src/openrct2/rct1/S4Importer.cpp b/src/openrct2/rct1/S4Importer.cpp index c90b684e3b..3de5df9462 100644 --- a/src/openrct2/rct1/S4Importer.cpp +++ b/src/openrct2/rct1/S4Importer.cpp @@ -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(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 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; -} diff --git a/src/openrct2/rct12/RCT12.cpp b/src/openrct2/rct12/RCT12.cpp index d8128a4925..5cfe317d38 100644 --- a/src/openrct2/rct12/RCT12.cpp +++ b/src/openrct2/rct12/RCT12.cpp @@ -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; diff --git a/src/openrct2/rct12/RCT12.h b/src/openrct2/rct12/RCT12.h index 6d09cc894d..22c556acf1 100644 --- a/src/openrct2/rct12/RCT12.h +++ b/src/openrct2/rct12/RCT12.h @@ -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); diff --git a/src/openrct2/world/Map.h b/src/openrct2/world/Map.h index 03c3ab2390..d91db66222 100644 --- a/src/openrct2/world/Map.h +++ b/src/openrct2/world/Map.h @@ -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 class TilePointerIndex +{ + std::vector 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(); diff --git a/src/openrct2/world/TileElement.h b/src/openrct2/world/TileElement.h index 3235728756..eca5089af2 100644 --- a/src/openrct2/world/TileElement.h +++ b/src/openrct2/world/TileElement.h @@ -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); diff --git a/src/openrct2/world/Wall.cpp b/src/openrct2/world/Wall.cpp index e5471cca4b..d84b9214ac 100644 --- a/src/openrct2/world/Wall.cpp +++ b/src/openrct2/world/Wall.cpp @@ -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; -}