Skip to content

Commit

Permalink
Implement recalculating autotiles D
Browse files Browse the repository at this point in the history
  • Loading branch information
Primekick committed Nov 27, 2024
1 parent 262883d commit ca30cf7
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 16 deletions.
15 changes: 4 additions & 11 deletions src/game_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1879,17 +1879,10 @@ int Game_Map::SubstituteUp(int old_id, int new_id) {
return DoSubstitute(map_info.upper_tiles, old_id, new_id);
}

static void DoReplaceAt(std::vector<int16_t>& layer, int x, int y, int new_id, int map_width) {
auto pos = x + y * map_width;
layer[pos] = static_cast<int16_t>(new_id);
}

void Game_Map::ReplaceDownAt(int x, int y, int new_id) {
DoReplaceAt(map->lower_layer, x, y, new_id, map->width);
}

void Game_Map::ReplaceUpAt(int x, int y, int new_id) {
DoReplaceAt(map->upper_layer, x, y, new_id, map->width);
void Game_Map::ReplaceTileAt(int x, int y, int new_id, int layer) {
auto pos = x + y * map->width;
auto& layer_vec = layer >= 1 ? map->upper_layer : map->lower_layer;
layer_vec[pos] = static_cast<int16_t>(new_id);
}

std::string Game_Map::ConstructMapName(int map_id, bool is_easyrpg) {
Expand Down
3 changes: 1 addition & 2 deletions src/game_map.h
Original file line number Diff line number Diff line change
Expand Up @@ -641,8 +641,7 @@ namespace Game_Map {
Game_Vehicle* GetVehicle(Game_Vehicle::Type which);
int SubstituteDown(int old_id, int new_id);
int SubstituteUp(int old_id, int new_id);
void ReplaceDownAt(int x, int y, int new_id);
void ReplaceUpAt(int x, int y, int new_id);
void ReplaceTileAt(int x, int y, int new_id, int layer);

/**
* Checks if its possible to step onto the tile at (x,y)
Expand Down
11 changes: 11 additions & 0 deletions src/map_data.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,17 @@ static constexpr int NUM_LOWER_TILES = BLOCK_F_INDEX;
static constexpr int NUM_UPPER_TILES = BLOCK_F_TILES;
static constexpr int NUM_TILES = NUM_LOWER_TILES + NUM_UPPER_TILES;

// Bit positions for neighbors
static constexpr uint8_t NEIGHBOR_NW = 0x80; // 0b10000000
static constexpr uint8_t NEIGHBOR_N = 0x40; // 0b01000000
static constexpr uint8_t NEIGHBOR_NE = 0x20; // 0b00100000
static constexpr uint8_t NEIGHBOR_W = 0x10; // 0b00010000
static constexpr uint8_t NEIGHBOR_E = 0x08; // 0b00001000
static constexpr uint8_t NEIGHBOR_SW = 0x04; // 0b00000100
static constexpr uint8_t NEIGHBOR_S = 0x02; // 0b00000010
static constexpr uint8_t NEIGHBOR_SE = 0x01; // 0b00000001


/** Passability flags. */
namespace Passable {
enum Passable {
Expand Down
2 changes: 0 additions & 2 deletions src/spriteset_map.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,13 +176,11 @@ void Spriteset_Map::SubstituteUp(int old_id, int new_id) {

void Spriteset_Map::ReplaceDownAt(int x, int y, int tile_index, bool disable_autotile) {
auto tile_id = IndexToChipId(tile_index);
Game_Map::ReplaceDownAt(x, y, tile_id);
tilemap->SetMapTileDataDownAt(x, y, tile_id, disable_autotile);
}

void Spriteset_Map::ReplaceUpAt(int x, int y, int tile_index) {
auto tile_id = IndexToChipId(tile_index);
Game_Map::ReplaceUpAt(x, y, tile_id);
tilemap->SetMapTileDataUpAt(x, y, tile_id);
}

Expand Down
95 changes: 94 additions & 1 deletion src/tilemap_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -717,15 +717,108 @@ void TilemapLayer::SetMapData(std::vector<short> nmap_data) {
}

void TilemapLayer::SetMapTileDataAt(int x, int y, int tile_id, bool disable_autotile) {
if(!IsInMapBounds(x, y))
return;

substitutions = Game_Map::GetTilesLayer(layer);

if (disable_autotile) {
map_data[x + y * width] = static_cast<short>(tile_id);
Game_Map::ReplaceTileAt(x, y, tile_id, layer);
CreateTileCacheAt(x, y, tile_id);
} else {
// TODO: handle autotiles
// Recalculate the replaced tile itself + every neighboring tile
static constexpr struct { int dx; int dy; } adjacent[8] = {
{-1, -1}, { 0, -1}, { 1, -1},
{-1, 0}, { 1, 0},
{-1, 1}, { 0, 1}, { 1, 1}
};

RecalculateAutotile(x, y, tile_id);
for (const auto& adj : adjacent) {
auto nx = x + adj.dx;
auto ny = y + adj.dy;
if (IsInMapBounds(nx, ny)) {
RecalculateAutotile(nx, ny, GetDataCache(nx, ny).ID);
}
}
}

SetMapData(map_data);
}

static inline bool IsAutotileAB(int tile_id) {
return tile_id >= BLOCK_A && tile_id < BLOCK_C;
}

static inline bool IsAutotileD(int tile_id) {
return tile_id >= BLOCK_D && tile_id < BLOCK_E;
}

static inline bool IsSameAutotileD(int current_tile_id, int neighbor_tile_id) {
return ChipIdToIndex(current_tile_id) == ChipIdToIndex(neighbor_tile_id);
}

static inline void ApplyCornerFixups(uint8_t& neighbors) {
// Northwest corner
if ((neighbors & NEIGHBOR_NW) && (neighbors & (NEIGHBOR_N | NEIGHBOR_W)) != (NEIGHBOR_N | NEIGHBOR_W)) {
neighbors &= ~NEIGHBOR_NW;
}

// Northeast corner
if ((neighbors & NEIGHBOR_NE) && (neighbors & (NEIGHBOR_N | NEIGHBOR_E)) != (NEIGHBOR_N | NEIGHBOR_E)) {
neighbors &= ~NEIGHBOR_NE;
}

// Southwest corner
if ((neighbors & NEIGHBOR_SW) && (neighbors & (NEIGHBOR_S | NEIGHBOR_W)) != (NEIGHBOR_S | NEIGHBOR_W)) {
neighbors &= ~NEIGHBOR_SW;
}

// Southeast corner
if ((neighbors & NEIGHBOR_SE) && (neighbors & (NEIGHBOR_S | NEIGHBOR_E)) != (NEIGHBOR_S | NEIGHBOR_E)) {
neighbors &= ~NEIGHBOR_SE;
}
}

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: Command RewriteMap is 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}
};

// 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;
}
}
ApplyCornerFixups(neighbors);

// Recalculate tile id using the neighbors -> variant map
const int new_tile_id = BLOCK_D + block * BLOCK_D_STRIDE + AUTOTILE_VARIANTS_MAP.at(neighbors);
map_data[x + y * width] = static_cast<short>(new_tile_id);
Game_Map::ReplaceTileAt(x, y, new_tile_id, layer);
CreateTileCacheAt(x, y, tile_id);
}

void TilemapLayer::SetPassable(std::vector<unsigned char> npassable) {
passable = std::move(npassable);
Expand Down
6 changes: 6 additions & 0 deletions src/tilemap_layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ class TilemapLayer {
void GenerateAutotileD(short ID);
void DrawTile(Bitmap& dst, Bitmap& tile, Bitmap& tone_tile, int x, int y, int row, int col, uint32_t tone_hash, bool allow_fast_blit = true);
void DrawTileImpl(Bitmap& dst, Bitmap& tile, Bitmap& tone_tile, int x, int y, int row, int col, uint32_t tone_hash, ImageOpacity op, bool allow_fast_blit);
void RecalculateAutotile(int x, int y, int tile_id);

static const int TILES_PER_ROW = 64;

Expand Down Expand Up @@ -163,6 +164,8 @@ class TilemapLayer {
TilemapSubLayer upper_layer;

Tone tone;

bool IsInMapBounds(int x, int y) const;
};

inline BitmapRef const& TilemapLayer::GetChipset() const {
Expand Down Expand Up @@ -261,5 +264,8 @@ inline TilemapLayer::TileData& TilemapLayer::GetDataCache(int x, int y) {
return data_cache_vec[x + y * width];
}

inline bool TilemapLayer::IsInMapBounds(int x, int y) const {
return x >= 0 && x < width && y >= 0 && y < height;
}

#endif

0 comments on commit ca30cf7

Please sign in to comment.