Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Rewrite map fixes #3324

Merged
merged 4 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/spriteset_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,11 +175,16 @@ 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 = BLOCK_F_INDEX;

auto tile_id = IndexToChipId(tile_index);
tilemap->SetMapTileDataUpAt(x, y, tile_id);
}
Expand Down
2 changes: 1 addition & 1 deletion src/tilemap.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,7 @@ inline void Tilemap::SetMapDataUp(std::vector<short> 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<unsigned char>& Tilemap::GetPassableDown() const {
Expand Down
108 changes: 68 additions & 40 deletions src/tilemap_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@
// 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<uint8_t, int> AUTOTILE_D_VARIANTS_MAP = {
static const std::unordered_map<uint8_t, int> AUTOTILE_D_VARIANTS_MAP = { //it also works with A
{0b11111111, 0},
{0b01111111, 1},
{0b11011111, 2},
Expand Down Expand Up @@ -722,8 +722,16 @@
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) {
Expand All @@ -732,7 +740,9 @@

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
Expand All @@ -743,12 +753,7 @@
};

// 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;
Expand All @@ -766,6 +771,27 @@
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);
}
Expand Down Expand Up @@ -793,42 +819,44 @@
}

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.");
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) {

Check warning on line 842 in src/tilemap_layer.cpp

View workflow job for this annotation

GitHub Actions / ubuntu:22.04

unused parameter 'blockType' [-Wunused-parameter]
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<unsigned char> npassable) {
passable = std::move(npassable);

Expand Down
Loading