diff --git a/src/game_interpreter.cpp b/src/game_interpreter.cpp index d903697b8f..cb829dc6f0 100644 --- a/src/game_interpreter.cpp +++ b/src/game_interpreter.cpp @@ -4547,12 +4547,52 @@ bool Game_Interpreter::CommandManiacKeyInputProcEx(lcf::rpg::EventCommand const& return true; } -bool Game_Interpreter::CommandManiacRewriteMap(lcf::rpg::EventCommand const&) { +bool Game_Interpreter::CommandManiacRewriteMap(lcf::rpg::EventCommand const& com) { if (!Player::IsPatchManiac()) { return true; } - Output::Warning("Maniac Patch: Command RewriteMap not supported"); + int mode = com.parameters[0]; + bool is_replace_range = com.parameters[1] != 0; + bool is_upper_layer = com.parameters[2] != 0; + + int tile_index = ValueOrVariableBitfield(mode, 0, com.parameters[3]); + int x_start = ValueOrVariableBitfield(mode, 1, com.parameters[4]); + int y_start = ValueOrVariableBitfield(mode, 2, com.parameters[5]); + int width = ValueOrVariableBitfield(mode, 3, com.parameters[6]); + int height = ValueOrVariableBitfield(mode, 4, com.parameters[7]); + + bool disable_autotile = com.parameters[8] != 0; + + Output::Debug("RewriteMap | Mode: {}, SingleRange: {}, Layer: {}, IdOrArrayStart: {}, LeftEnd: {}, TopValue: {}, Width: {}, Height: {}, Autotile: {}", + mode, + is_replace_range, + is_upper_layer, + tile_index, + x_start, + y_start, + width, + height, + disable_autotile); + + Scene_Map* scene = (Scene_Map*)Scene::Find(Scene::Map).get(); + if (!scene) + return true; + + if (is_upper_layer) { + for (auto y = y_start; y < y_start + height; ++y) { + for (auto x = x_start; x < x_start + width; ++x) { + scene->spriteset->ReplaceUpAt(x, y, tile_index); + } + } + } else { + for (auto y = y_start; y < y_start + height; ++y) { + for (auto x = x_start; x < x_start + width; ++x) { + scene->spriteset->ReplaceDownAt(x, y, tile_index, disable_autotile); + } + } + } + return true; } diff --git a/src/spriteset_map.cpp b/src/spriteset_map.cpp index c8be227b4e..4d1a0331b6 100644 --- a/src/spriteset_map.cpp +++ b/src/spriteset_map.cpp @@ -30,6 +30,7 @@ #include "bitmap.h" #include "player.h" #include "drawable_list.h" +#include "map_data.h" Spriteset_Map::Spriteset_Map() { panorama = std::make_unique(); @@ -173,6 +174,18 @@ 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); +} + bool Spriteset_Map::RequireClear(DrawableList& drawable_list) { if (drawable_list.empty()) { return true; diff --git a/src/spriteset_map.h b/src/spriteset_map.h index b9dddc31ec..dc176cf7a0 100644 --- a/src/spriteset_map.h +++ b/src/spriteset_map.h @@ -71,6 +71,16 @@ class Spriteset_Map { */ void SubstituteUp(int old_id, int new_id); + /** + * Replaces a single tile in lower layer at the given coordinates. + */ + void ReplaceDownAt(int x, int y, int tile_index, bool disable_autotile); + + /** + * Replaces a single tile in upper layer at the given coordinates. + */ + void ReplaceUpAt(int x, int y, int tile_index); + /** * @return true if we should clear the screen before drawing the map */ diff --git a/src/tilemap.h b/src/tilemap.h index ee5a4c78f6..0a79129595 100644 --- a/src/tilemap.h +++ b/src/tilemap.h @@ -36,9 +36,11 @@ class Tilemap { const std::vector& GetMapDataDown() const; void SetMapDataDown(std::vector down); + void SetMapTileDataDownAt(int x, int y, int tile_id, bool disable_autotile); const std::vector& GetMapDataUp() const; void SetMapDataUp(std::vector up); + void SetMapTileDataUpAt(int x, int y, int tile_id); const std::vector& GetPassableUp() const; void SetPassableUp(std::vector up); @@ -81,6 +83,10 @@ inline void Tilemap::SetMapDataDown(std::vector down) { layer_down.SetMapData(std::move(down)); } +inline void Tilemap::SetMapTileDataDownAt(int x, int y, int tile_id, bool disable_autotile) { + layer_down.SetMapTileDataAt(x, y, tile_id, disable_autotile); +} + inline const std::vector& Tilemap::GetMapDataUp() const { return layer_up.GetMapData(); } @@ -89,6 +95,10 @@ inline void Tilemap::SetMapDataUp(std::vector up) { layer_up.SetMapData(std::move(up)); } +inline void Tilemap::SetMapTileDataUpAt(int x, int y, int tile_id) { + layer_down.SetMapTileDataAt(x, y, tile_id, true); +} + inline const std::vector& Tilemap::GetPassableDown() const { return layer_down.GetPassable(); } diff --git a/src/tilemap_layer.cpp b/src/tilemap_layer.cpp index df8c62c436..18cc53c671 100644 --- a/src/tilemap_layer.cpp +++ b/src/tilemap_layer.cpp @@ -376,37 +376,39 @@ void TilemapLayer::CreateTileCache(const std::vector& nmap_data) { data_cache_vec.resize(width * height); for (int x = 0; x < width; x++) { for (int y = 0; y < height; y++) { - TileData tile; - - // Get the tile ID - tile.ID = nmap_data[x + y * width]; - - tile.z = TileBelow; - - // Calculate the tile Z - if (!passable.empty()) { - if (tile.ID >= BLOCK_F) { // Upper layer - if ((passable[substitutions[tile.ID - BLOCK_F]] & Passable::Above) != 0) - tile.z = TileAbove + 1; // Upper sublayer - else - tile.z = TileBelow + 1; // Lower sublayer - - } else { // Lower layer - int chip_index = - tile.ID >= BLOCK_E ? substitutions[tile.ID - BLOCK_E] + 18 : - tile.ID >= BLOCK_D ? (tile.ID - BLOCK_D) / 50 + 6 : - tile.ID >= BLOCK_C ? (tile.ID - BLOCK_C) / 50 + 3 : - tile.ID / 1000; - if ((passable[chip_index] & (Passable::Wall | Passable::Above)) != 0) - tile.z = TileAbove; // Upper sublayer - else - tile.z = TileBelow; // Lower sublayer + auto tile_id = nmap_data[x + y * width]; + CreateTileCacheAt(x, y, tile_id); + } + } +} + +void TilemapLayer::CreateTileCacheAt(int x, int y, int tile_id) { + TileData tile; + tile.ID = static_cast(tile_id); + tile.z = TileBelow; + + // Calculate the tile Z + if (!passable.empty()) { + if (tile.ID >= BLOCK_F) { // Upper layer + if ((passable[substitutions[tile.ID - BLOCK_F]] & Passable::Above) != 0) + tile.z = TileAbove + 1; // Upper sublayer + else + tile.z = TileBelow + 1; // Lower sublayer + + } else { // Lower layer + int chip_index = + tile.ID >= BLOCK_E ? substitutions[tile.ID - BLOCK_E] + 18 : + tile.ID >= BLOCK_D ? (tile.ID - BLOCK_D) / 50 + 6 : + tile.ID >= BLOCK_C ? (tile.ID - BLOCK_C) / 50 + 3 : + tile.ID / 1000; + if ((passable[chip_index] & (Passable::Wall | Passable::Above)) != 0) + tile.z = TileAbove; // Upper sublayer + else + tile.z = TileBelow; // Lower sublayer - } - } - GetDataCache(x, y) = tile; } } + GetDataCache(x, y) = tile; } void TilemapLayer::GenerateAutotileAB(short ID, short animID) { @@ -661,6 +663,17 @@ void TilemapLayer::SetMapData(std::vector nmap_data) { map_data = std::move(nmap_data); } +void TilemapLayer::SetMapTileDataAt(int x, int y, int tile_id, bool disable_autotile) { + substitutions = Game_Map::GetTilesLayer(layer); + + if (disable_autotile) { + CreateTileCacheAt(x, y, tile_id); + } else { + // TODO: handle autotiles + } +} + + void TilemapLayer::SetPassable(std::vector npassable) { passable = std::move(npassable); diff --git a/src/tilemap_layer.h b/src/tilemap_layer.h index f4cf47a599..08a2601e4a 100644 --- a/src/tilemap_layer.h +++ b/src/tilemap_layer.h @@ -66,6 +66,7 @@ class TilemapLayer { void SetChipset(BitmapRef const& nchipset); const std::vector& GetMapData() const; void SetMapData(std::vector nmap_data); + void SetMapTileDataAt(int x, int y, int tile_id, bool disable_autotile); const std::vector& GetPassable() const; void SetPassable(std::vector npassable); bool IsVisible() const; @@ -115,6 +116,7 @@ class TilemapLayer { bool fast_blit = false; void CreateTileCache(const std::vector& nmap_data); + void CreateTileCacheAt(int x, int y, int tile_id); void GenerateAutotileAB(short ID, short animID); 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);