Skip to content

Commit de9b5dd

Browse files
committed
Maniacs Patch - AB Autotiling Support
Introduces IsWaterTile and IsDeepWaterTile helpers and implements specialized autotiling logic for water and deep water tiles in RecalculateAutotile. This enables more accurate coastline and deep water boundary rendering, including diagonal and cardinal neighbor checks for seamless water transitions.
1 parent 31de2a7 commit de9b5dd

File tree

1 file changed

+120
-39
lines changed

1 file changed

+120
-39
lines changed

src/tilemap_layer.cpp

Lines changed: 120 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,10 @@ void TilemapLayer::SetMapData(std::vector<short> nmap_data) {
722722
map_data = std::move(nmap_data);
723723
}
724724

725+
static inline bool IsAutotileD(int tile_id) {
726+
return tile_id >= BLOCK_D && tile_id < BLOCK_E;
727+
}
728+
725729
static inline bool IsTileFromBlock(int tile_id, int block) {
726730
switch (block) {
727731
case BLOCK_A: return tile_id >= BLOCK_A && tile_id < BLOCK_A_END;
@@ -734,41 +738,11 @@ static inline bool IsTileFromBlock(int tile_id, int block) {
734738
}
735739
}
736740

737-
void TilemapLayer::SetMapTileDataAt(int x, int y, int tile_id, bool disable_autotile) {
738-
if(!IsInMapBounds(x, y))
739-
return;
740-
741-
substitutions = Game_Map::GetTilesLayer(layer);
742-
743-
bool is_autotile = IsTileFromBlock(tile_id, BLOCK_A) || IsTileFromBlock(tile_id, BLOCK_B) || IsTileFromBlock(tile_id, BLOCK_D);
744-
745-
if (disable_autotile || !is_autotile) {
746-
RecreateTileDataAt(x, y, tile_id);
747-
} else {
748-
// Recalculate the replaced tile itself + every neighboring tile
749-
static constexpr struct { int dx; int dy; } adjacent[8] = {
750-
{-1, -1}, { 0, -1}, { 1, -1},
751-
{-1, 0}, { 1, 0},
752-
{-1, 1}, { 0, 1}, { 1, 1}
753-
};
754-
755-
// TODO: make it work for AB autotiles
756-
RecalculateAutotile(x, y, tile_id);
757-
758-
for (const auto& adj : adjacent) {
759-
auto nx = x + adj.dx;
760-
auto ny = y + adj.dy;
761-
if (IsInMapBounds(nx, ny)) {
762-
RecalculateAutotile(nx, ny, GetDataCache(nx, ny).ID);
763-
}
764-
}
765-
}
766-
767-
SetMapData(map_data);
741+
static bool IsWaterTile(int tile_id) {
742+
return tile_id >= 0 && tile_id < 3000;
768743
}
769-
770-
static inline bool IsAutotileD(int tile_id) {
771-
return tile_id >= BLOCK_D && tile_id < BLOCK_E;
744+
static bool IsDeepWaterTile(int tile_id) {
745+
return tile_id >= 2000 && tile_id < 3000;
772746
}
773747

774748
static inline bool IsSameAutotileAB(int current_tile_id, int neighbor_tile_id) {
@@ -818,6 +792,39 @@ static inline void ApplyCornerFixups(uint8_t& neighbors) {
818792
}
819793
}
820794

795+
//Maniac Patch - Rewrite Map Main Method:
796+
void TilemapLayer::SetMapTileDataAt(int x, int y, int tile_id, bool disable_autotile) {
797+
if(!IsInMapBounds(x, y))
798+
return;
799+
800+
substitutions = Game_Map::GetTilesLayer(layer);
801+
802+
bool is_autotile = IsTileFromBlock(tile_id, BLOCK_A) || IsTileFromBlock(tile_id, BLOCK_B) || IsTileFromBlock(tile_id, BLOCK_D);
803+
804+
if (disable_autotile || !is_autotile) {
805+
RecreateTileDataAt(x, y, tile_id);
806+
} else {
807+
// Recalculate the replaced tile itself + every neighboring tile
808+
static constexpr struct { int dx; int dy; } adjacent[8] = {
809+
{-1, -1}, { 0, -1}, { 1, -1},
810+
{-1, 0}, { 1, 0},
811+
{-1, 1}, { 0, 1}, { 1, 1}
812+
};
813+
814+
RecalculateAutotile(x, y, tile_id);
815+
816+
for (const auto& adj : adjacent) {
817+
auto nx = x + adj.dx;
818+
auto ny = y + adj.dy;
819+
if (IsInMapBounds(nx, ny)) {
820+
RecalculateAutotile(nx, ny, GetDataCache(nx, ny).ID);
821+
}
822+
}
823+
}
824+
825+
SetMapData(map_data);
826+
}
827+
821828
void TilemapLayer::RecalculateAutotile(int x, int y, int tile_id) {
822829
static constexpr struct { int dx; int dy; uint8_t bit; } adjacent[8] = {
823830
{-1, -1, NEIGHBOR_NW}, { 0, -1, NEIGHBOR_N}, { 1, -1, NEIGHBOR_NE},
@@ -847,16 +854,90 @@ void TilemapLayer::RecalculateAutotile(int x, int y, int tile_id) {
847854
RecreateTileDataAt(x, y, new_tile_id);
848855
};
849856

850-
if (IsTileFromBlock(tile_id, BLOCK_A)) {
851-
processBlock(BLOCK_A, BLOCK_A_STRIDE, BLOCK_A, IsSameAutotileAB);
852-
}
853-
if (IsTileFromBlock(tile_id, BLOCK_B)) {
854-
processBlock(BLOCK_B, BLOCK_B_STRIDE, BLOCK_B, IsSameAutotileAB);
857+
if (IsWaterTile(tile_id)) {
858+
int type = tile_id / 1000;
859+
int base = type * 1000;
860+
861+
// 1. Calculate a_subtile (Coastline) - 8 neighbors
862+
// Any Water connects to Any Water (0-2999) + Animated (3000-3999)
863+
auto isWaterCompatible = [&](int /*curr*/, int neighbor) {
864+
return IsWaterTile(neighbor) || (neighbor >= 3000 && neighbor < 4000);
865+
};
866+
867+
uint8_t neighbors_a = calculateNeighbors(isWaterCompatible);
868+
int a_subtile = AUTOTILE_D_VARIANTS_MAP.at(neighbors_a);
869+
870+
// 2. Calculate b_subtile (Deep Boundary) - 4 neighbors (N, E, S, W)
871+
auto is_b_compatible = [&](int nid) {
872+
bool n_deep = IsDeepWaterTile(nid);
873+
bool n_water = IsWaterTile(nid) || (nid >= 3000 && nid < 4000);
874+
875+
if (type == 2) { // Deep
876+
// Deep connects to Deep or Land (Non-Water). Blocks on Shallow.
877+
return n_deep || !n_water;
878+
}
879+
else { // Shallow
880+
// Shallow connects to Shallow or Land. Blocks on Deep.
881+
return !n_deep;
882+
}
883+
};
884+
885+
auto check_blocked = [&](int dx, int dy) {
886+
auto nx = x + dx;
887+
auto ny = y + dy;
888+
if (!IsInMapBounds(nx, ny)) return type == 2;
889+
auto adj_id = GetDataCache(nx, ny).ID;
890+
return !is_b_compatible(adj_id);
891+
};
892+
893+
bool b_n = check_blocked(0, -1);
894+
bool b_e = check_blocked(1, 0);
895+
bool b_s = check_blocked(0, 1);
896+
bool b_w = check_blocked(-1, 0);
897+
898+
int b_subtile = 0;
899+
900+
if (type == 2) {
901+
// Handle diagonal connections for Deep Water (Type 2)
902+
// A corner is only cut (border added) if the cardinal neighbors are blocked
903+
// AND the corresponding diagonal neighbor is NOT Deep Water.
904+
// If the diagonal neighbor IS Deep Water, we maintain the connection (don't cut).
905+
906+
auto check_diag_deep = [&](int dx, int dy) {
907+
auto nx = x + dx;
908+
auto ny = y + dy;
909+
if (!IsInMapBounds(nx, ny)) return false;
910+
auto adj_id = GetDataCache(nx, ny).ID;
911+
return IsDeepWaterTile(adj_id);
912+
};
913+
914+
bool d_nw = check_diag_deep(-1, -1);
915+
bool d_ne = check_diag_deep(1, -1);
916+
bool d_sw = check_diag_deep(-1, 1);
917+
bool d_se = check_diag_deep(1, 1);
918+
919+
if (b_n && b_w && !d_nw) b_subtile |= 1; // TL
920+
if (b_n && b_e && !d_ne) b_subtile |= 2; // TR
921+
if (b_s && b_w && !d_sw) b_subtile |= 4; // BL
922+
if (b_s && b_e && !d_se) b_subtile |= 8; // BR
923+
}
924+
else {
925+
// Shallow Water (Type 0 & 1) - Standard corner logic
926+
if (b_n && b_w) b_subtile |= 1; // TL
927+
if (b_n && b_e) b_subtile |= 2; // TR
928+
if (b_s && b_w) b_subtile |= 4; // BL
929+
if (b_s && b_e) b_subtile |= 8; // BR
930+
}
931+
932+
int new_tile_id = base + (b_subtile * 50) + a_subtile;
933+
RecreateTileDataAt(x, y, new_tile_id);
855934
}
935+
856936
if (IsTileFromBlock(tile_id, BLOCK_D)) {
857937
processBlock(BLOCK_D, BLOCK_D_STRIDE, BLOCK_D, IsSameAutotileD);
858938
}
859939
}
940+
860941
void TilemapLayer::SetPassable(std::vector<unsigned char> npassable) {
861942
passable = std::move(npassable);
862943

0 commit comments

Comments
 (0)