From 7b288c5d2b926df3c1e5a638809de8b4a09974cb Mon Sep 17 00:00:00 2001 From: Mauro Junior <45118493+jetrotal@users.noreply.github.com> Date: Mon, 6 Jan 2025 15:30:47 -0300 Subject: [PATCH 1/4] Rewrite Map: Lower tiles - Bugfixes - Added a more robust way of identifying TILE_BLOCKS. This fixes the issue where most of lower tiles were not being aplied. - Reduced an output warning that repeated itself twice. --- src/tilemap_layer.cpp | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/tilemap_layer.cpp b/src/tilemap_layer.cpp index 5c457705d0..901e948c0c 100644 --- a/src/tilemap_layer.cpp +++ b/src/tilemap_layer.cpp @@ -722,8 +722,16 @@ void TilemapLayer::SetMapData(std::vector nmap_data) { map_data = std::move(nmap_data); } -static inline bool IsAutotileAB(int tile_id) { - return tile_id >= BLOCK_A && tile_id < BLOCK_C; +static inline bool IsTileFromBlock(int tile_id, int block) { + switch (block) { + case BLOCK_A: return tile_id >= BLOCK_A && tile_id < BLOCK_A_END; + case BLOCK_B: return tile_id >= BLOCK_B && tile_id < BLOCK_B_END; + case BLOCK_C: return tile_id >= BLOCK_C && tile_id < BLOCK_C_END; + case BLOCK_D: return tile_id >= BLOCK_D && tile_id < BLOCK_D_END; + case BLOCK_E: return tile_id >= BLOCK_E && tile_id < BLOCK_E_END; + case BLOCK_F: return tile_id >= BLOCK_F && tile_id < BLOCK_F_END; + default: return false; + } } void TilemapLayer::SetMapTileDataAt(int x, int y, int tile_id, bool disable_autotile) { @@ -732,7 +740,9 @@ void TilemapLayer::SetMapTileDataAt(int x, int y, int tile_id, bool disable_auto substitutions = Game_Map::GetTilesLayer(layer); - if (disable_autotile) { + bool is_autotile = IsTileFromBlock(tile_id, BLOCK_A) || IsTileFromBlock(tile_id, BLOCK_B) || IsTileFromBlock(tile_id, BLOCK_D); + + if (disable_autotile || !is_autotile) { RecreateTileDataAt(x, y, tile_id); } else { // Recalculate the replaced tile itself + every neighboring tile @@ -743,12 +753,7 @@ void TilemapLayer::SetMapTileDataAt(int x, int y, int tile_id, bool disable_auto }; // TODO: make it work for AB autotiles - if (IsAutotileAB(tile_id)) { - RecreateTileDataAt(x, y, tile_id); - Output::Warning("Maniac Patch: Autotiles A and B in RewriteMap are only partially supported."); - } else { - RecalculateAutotile(x, y, tile_id); - } + RecalculateAutotile(x, y, tile_id); for (const auto& adj : adjacent) { auto nx = x + adj.dx; @@ -794,8 +799,10 @@ static inline void ApplyCornerFixups(uint8_t& neighbors) { void TilemapLayer::RecalculateAutotile(int x, int y, int tile_id) { // TODO: make it work for AB autotiles - if (IsAutotileAB(tile_id)) { - Output::Warning("Maniac Patch: Autotiles A and B in RewriteMap are only partially supported."); + bool is_tileAB = IsTileFromBlock(tile_id, BLOCK_A) || IsTileFromBlock(tile_id, BLOCK_B); + if (is_tileAB) { + RecreateTileDataAt(x, y, tile_id); + Output::Warning("Maniac Patch: Autotiles AB in RewriteMap are only partially supported."); return; } From b983e207c63fceee1f317c4bcf54ea33b73021c1 Mon Sep 17 00:00:00 2001 From: Mauro Junior <45118493+jetrotal@users.noreply.github.com> Date: Mon, 6 Jan 2025 16:10:01 -0300 Subject: [PATCH 2/4] Rewrite Map: Upper tiles - Bugfixes - Fixed wrong IDs - Fixed wrong Layer assertion - Added a conditional branch to protect IDs from overflowing. --- src/spriteset_map.cpp | 3 +++ src/tilemap.h | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/spriteset_map.cpp b/src/spriteset_map.cpp index 370817e6cd..e681b343d8 100644 --- a/src/spriteset_map.cpp +++ b/src/spriteset_map.cpp @@ -180,6 +180,9 @@ void Spriteset_Map::ReplaceDownAt(int x, int y, int tile_index, bool disable_aut } void Spriteset_Map::ReplaceUpAt(int x, int y, int tile_index) { + tile_index += BLOCK_F_INDEX; + if (tile_index >= NUM_UPPER_TILES + BLOCK_F_INDEX) tile_index = 0; + auto tile_id = IndexToChipId(tile_index); tilemap->SetMapTileDataUpAt(x, y, tile_id); } diff --git a/src/tilemap.h b/src/tilemap.h index 0a79129595..d10414ae50 100644 --- a/src/tilemap.h +++ b/src/tilemap.h @@ -96,7 +96,7 @@ inline void Tilemap::SetMapDataUp(std::vector up) { } inline void Tilemap::SetMapTileDataUpAt(int x, int y, int tile_id) { - layer_down.SetMapTileDataAt(x, y, tile_id, true); + layer_up.SetMapTileDataAt(x, y, tile_id, true); } inline const std::vector& Tilemap::GetPassableDown() const { From 8bcba94d066ba4055b7ede2fe6aab6ff8cfecd2e Mon Sep 17 00:00:00 2001 From: Mauro Junior <45118493+jetrotal@users.noreply.github.com> Date: Sun, 12 Jan 2025 10:59:14 -0300 Subject: [PATCH 3/4] Rewrite Map: Tiles AB Initial Support --- src/tilemap_layer.cpp | 87 +++++++++++++++++++++++++++---------------- 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/src/tilemap_layer.cpp b/src/tilemap_layer.cpp index 901e948c0c..d371fdd18e 100644 --- a/src/tilemap_layer.cpp +++ b/src/tilemap_layer.cpp @@ -143,7 +143,7 @@ static constexpr uint8_t BlockD_Subtiles_IDS[50][2][2][2] = { // Set of neighboring autotiles -> autotile variant // Each neighbor is represented by a single bit (1 - same autotile, 0 - any other case) // The bits are ordered as follows (from most to least significant bit): NW N NE W E SW S SE -static const std::unordered_map AUTOTILE_D_VARIANTS_MAP = { +static const std::unordered_map AUTOTILE_D_VARIANTS_MAP = { //it also works with A {0b11111111, 0}, {0b01111111, 1}, {0b11011111, 2}, @@ -771,6 +771,27 @@ static inline bool IsAutotileD(int tile_id) { return tile_id >= BLOCK_D && tile_id < BLOCK_E; } +static inline bool IsSameAutotileAB(int current_tile_id, int neighbor_tile_id) { + // Special case for water tiles - allow mixing of A and B blocks + bool current_is_water = (IsTileFromBlock(current_tile_id, BLOCK_A) || + IsTileFromBlock(current_tile_id, BLOCK_B)); + bool neighbor_is_water = (IsTileFromBlock(neighbor_tile_id, BLOCK_A) || + IsTileFromBlock(neighbor_tile_id, BLOCK_B)); + + if (current_is_water && neighbor_is_water) { + return true; + } + + // For non-water tiles, keep original behavior of requiring same block + if (IsTileFromBlock(current_tile_id, BLOCK_A) && IsTileFromBlock(neighbor_tile_id, BLOCK_A)) { + return true; + } + if (IsTileFromBlock(current_tile_id, BLOCK_B) && IsTileFromBlock(neighbor_tile_id, BLOCK_B)) { + return true; + } + return false; +} + static inline bool IsSameAutotileD(int current_tile_id, int neighbor_tile_id) { return ChipIdToIndex(current_tile_id) == ChipIdToIndex(neighbor_tile_id); } @@ -798,44 +819,44 @@ static inline void ApplyCornerFixups(uint8_t& neighbors) { } void TilemapLayer::RecalculateAutotile(int x, int y, int tile_id) { - // TODO: make it work for AB autotiles - bool is_tileAB = IsTileFromBlock(tile_id, BLOCK_A) || IsTileFromBlock(tile_id, BLOCK_B); - if (is_tileAB) { - RecreateTileDataAt(x, y, tile_id); - Output::Warning("Maniac Patch: Autotiles AB in RewriteMap are only partially supported."); - return; - } - - if (!IsAutotileD(tile_id)) { - return; - } - - const int block = (tile_id - BLOCK_D) / BLOCK_D_STRIDE; - uint8_t neighbors = 0; - - // Get all neighboring tiles in a single pass static constexpr struct { int dx; int dy; uint8_t bit; } adjacent[8] = { - {-1, -1, NEIGHBOR_NW}, { 0, -1, NEIGHBOR_N}, { 1, -1, NEIGHBOR_NE}, - {-1, 0, NEIGHBOR_W }, { 1, 0, NEIGHBOR_E}, - {-1, 1, NEIGHBOR_SW}, { 0, 1, NEIGHBOR_S}, { 1, 1, NEIGHBOR_SE} + {-1, -1, NEIGHBOR_NW}, { 0, -1, NEIGHBOR_N}, { 1, -1, NEIGHBOR_NE}, + {-1, 0, NEIGHBOR_W }, { 1, 0, NEIGHBOR_E}, + {-1, 1, NEIGHBOR_SW}, { 0, 1, NEIGHBOR_S}, { 1, 1, NEIGHBOR_SE} }; - // Build the neighbors mask and fixup corners - for (const auto& adj : adjacent) { - auto nx = x + adj.dx; - auto ny = y + adj.dy; - auto adj_tile_id = IsInMapBounds(nx, ny) ? GetDataCache(nx, ny).ID : tile_id; - if (IsSameAutotileD(tile_id, adj_tile_id)) { - neighbors |= adj.bit; + auto calculateNeighbors = [&](auto isSameAutotileFn) { + uint8_t neighbors = 0; + for (const auto& adj : adjacent) { + auto nx = x + adj.dx; + auto ny = y + adj.dy; + auto adj_tile_id = IsInMapBounds(nx, ny) ? GetDataCache(nx, ny).ID : tile_id; + if (isSameAutotileFn(tile_id, adj_tile_id)) { + neighbors |= adj.bit; + } } - } - ApplyCornerFixups(neighbors); + ApplyCornerFixups(neighbors); + return neighbors; + }; - // Recalculate tile id using the neighbors -> variant map - const int new_tile_id = BLOCK_D + block * BLOCK_D_STRIDE + AUTOTILE_D_VARIANTS_MAP.at(neighbors); - RecreateTileDataAt(x, y, new_tile_id); -} + auto processBlock = [&](int blockType, int blockStride, int blockBase, auto isSameAutotileFn) { + uint8_t neighbors = calculateNeighbors(isSameAutotileFn); + int block = (tile_id - blockBase) / blockStride; + int variant = AUTOTILE_D_VARIANTS_MAP.at(neighbors); + int new_tile_id = blockBase + (block * blockStride) + variant; + RecreateTileDataAt(x, y, new_tile_id); + }; + if (IsTileFromBlock(tile_id, BLOCK_A)) { + processBlock(BLOCK_A, BLOCK_A_STRIDE, BLOCK_A, IsSameAutotileAB); + } + if (IsTileFromBlock(tile_id, BLOCK_B)) { + processBlock(BLOCK_B, BLOCK_B_STRIDE, BLOCK_B, IsSameAutotileAB); + } + if (IsTileFromBlock(tile_id, BLOCK_D)) { + processBlock(BLOCK_D, BLOCK_D_STRIDE, BLOCK_D, IsSameAutotileD); + } +} void TilemapLayer::SetPassable(std::vector npassable) { passable = std::move(npassable); From 1e3c78a4704b87eb24df8e680165cf8d1d4de555 Mon Sep 17 00:00:00 2001 From: Mauro Junior <45118493+jetrotal@users.noreply.github.com> Date: Wed, 15 Jan 2025 15:19:57 -0300 Subject: [PATCH 4/4] Rewrite Map: Lock tiles against going OOB. That solves some errors related out of bounds access. Sometimes it happens when drawing a tile, others when checking passability. It may differ from how maniacs deals with this (I suspect maniacs have an extra blank tile, with locked passability, just for OOB tiles). --- src/spriteset_map.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/spriteset_map.cpp b/src/spriteset_map.cpp index e681b343d8..50ea8a8842 100644 --- a/src/spriteset_map.cpp +++ b/src/spriteset_map.cpp @@ -175,13 +175,15 @@ void Spriteset_Map::SubstituteUp(int old_id, int new_id) { } void Spriteset_Map::ReplaceDownAt(int x, int y, int tile_index, bool disable_autotile) { + if (tile_index >= BLOCK_F_INDEX) tile_index = BLOCK_F_INDEX - 1; + auto tile_id = IndexToChipId(tile_index); tilemap->SetMapTileDataDownAt(x, y, tile_id, disable_autotile); } void Spriteset_Map::ReplaceUpAt(int x, int y, int tile_index) { tile_index += BLOCK_F_INDEX; - if (tile_index >= NUM_UPPER_TILES + BLOCK_F_INDEX) tile_index = 0; + if (tile_index >= NUM_UPPER_TILES + BLOCK_F_INDEX) tile_index = BLOCK_F_INDEX; auto tile_id = IndexToChipId(tile_index); tilemap->SetMapTileDataUpAt(x, y, tile_id);