diff --git a/src/app/core/pipeline.cc b/src/app/core/pipeline.cc index 59b6b203..db62a346 100644 --- a/src/app/core/pipeline.cc +++ b/src/app/core/pipeline.cc @@ -77,6 +77,38 @@ void GraphicsBinCanvasPipeline(int width, int height, int tile_size, ImGui::EndChild(); } +void GraphicsManagerCanvasPipeline(int width, int height, int tile_size, + int num_sheets, int canvas_id, + bool is_loaded, + const gfx::BitmapManager& graphics_manager) { + gui::Canvas canvas; + + if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)canvas_id); + ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true, + ImGuiWindowFlags_AlwaysVerticalScrollbar)) { + canvas.DrawBackground(ImVec2(width + 1, num_sheets * height + 1)); + canvas.DrawContextMenu(); + if (is_loaded) { + for (const auto& [key, value] : graphics_manager) { + int offset = height * (key + 1); + int top_left_y = canvas.GetZeroPoint().y + 2; + if (key >= 1) { + top_left_y = canvas.GetZeroPoint().y + height * key; + } + canvas.GetDrawList()->AddImage( + (void*)value->texture(), + ImVec2(canvas.GetZeroPoint().x + 2, top_left_y), + ImVec2(canvas.GetZeroPoint().x + 0x100, + canvas.GetZeroPoint().y + offset)); + } + } + canvas.DrawTileSelector(tile_size); + canvas.DrawGrid(tile_size); + canvas.DrawOverlay(); + } + ImGui::EndChild(); +} + void ButtonPipe(absl::string_view button_text, std::function callback) { if (ImGui::Button(button_text.data())) { callback(); diff --git a/src/app/core/pipeline.h b/src/app/core/pipeline.h index 8424e0b2..fd6c261c 100644 --- a/src/app/core/pipeline.h +++ b/src/app/core/pipeline.h @@ -29,10 +29,15 @@ void GraphicsBinCanvasPipeline(int width, int height, int tile_size, void ButtonPipe(absl::string_view button_text, std::function callback); -void BitmapCanvasPipeline(gui::Canvas& canvas, const gfx::Bitmap& bitmap, int width, - int height, int tile_size, bool is_loaded, +void BitmapCanvasPipeline(gui::Canvas& canvas, const gfx::Bitmap& bitmap, + int width, int height, int tile_size, bool is_loaded, bool scrollbar, int canvas_id); +void GraphicsManagerCanvasPipeline(int width, int height, int tile_size, + int num_sheets, int canvas_id, + bool is_loaded, + const gfx::BitmapManager& graphics_manager); + void BuildAndRenderBitmapPipeline(int width, int height, int depth, Bytes data, ROM& z3_rom, gfx::Bitmap& bitmap, gfx::SNESPalette& palette); diff --git a/src/app/editor/dungeon_editor.cc b/src/app/editor/dungeon_editor.cc index 8a21d88c..a5d9d085 100644 --- a/src/app/editor/dungeon_editor.cc +++ b/src/app/editor/dungeon_editor.cc @@ -3,6 +3,7 @@ #include #include "app/core/common.h" +#include "app/core/pipeline.h" #include "app/gui/canvas.h" #include "app/gui/icons.h" #include "app/gui/input.h" @@ -26,44 +27,38 @@ absl::Status DungeonEditor::Update() { rooms_.emplace_back(zelda3::dungeon::Room(i)); rooms_[i].LoadHeader(); if (flags()->kDrawDungeonRoomGraphics) { - rooms_[i].LoadRoomGraphics(rooms_[i].blockset); + rooms_[i].LoadRoomGraphics(); + + auto blocks = rooms_[i].blocks(); + room_graphics_.emplace_back(); + int current_sheet = 0; + for (auto& block : blocks) { + room_graphics_[i].CopyBitmap(rom()->bitmap_manager().GetBitmap(block), + current_sheet); + current_sheet += 1; + } } } is_loaded_ = true; } DrawToolset(); - DrawObjectRenderer(); - ImGui::Separator(); - if (ImGui::BeginTable("#DungeonEditTable", 3, toolset_table_flags_, + if (ImGui::BeginTable("#DungeonEditTable", 3, kDungeonTableFlags, ImVec2(0, 0))) { TableSetupColumn("Room Selector"); - TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch, ImGui::GetContentRegionAvail().x); TableSetupColumn("Object Selector"); TableHeadersRow(); TableNextRow(); + TableNextColumn(); - if (rom()->isLoaded()) { - if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)9); - ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true, - ImGuiWindowFlags_AlwaysVerticalScrollbar)) { - int i = 0; - for (const auto each_room_name : zelda3::dungeon::kRoomNames) { - ImGui::Button(each_room_name.data()); - if (ImGui::IsItemClicked()) { - active_rooms_.push_back(i); - } - i += 1; - } - } - ImGui::EndChild(); - } + DrawRoomSelector(); TableNextColumn(); DrawDungeonTabView(); + TableNextColumn(); DrawTileSelector(); ImGui::EndTable(); @@ -71,33 +66,35 @@ absl::Status DungeonEditor::Update() { return absl::OkStatus(); } -// Using ImGui Custom Tabs show each individual room the user selects from the -// Buttons above to open a canvas for each individual room. +void DungeonEditor::DrawRoomSelector() { + if (rom()->isLoaded()) { + ImGui::InputInt("Room ID", (int*)¤t_room_id_, 1, 1); + + if (ImGuiID child_id = ImGui::GetID((void*)(intptr_t)9); + ImGui::BeginChild(child_id, ImGui::GetContentRegionAvail(), true, + ImGuiWindowFlags_AlwaysVerticalScrollbar)) { + int i = 0; + for (const auto each_room_name : zelda3::dungeon::kRoomNames) { + ImGui::Selectable(each_room_name.data(), current_room_id_ == i, + ImGuiSelectableFlags_AllowDoubleClick); + if (ImGui::IsItemClicked()) { + active_rooms_.push_back(i); + } + i += 1; + } + } + ImGui::EndChild(); + } +} + void DungeonEditor::DrawDungeonTabView() { static int next_tab_id = 0; - // Expose some other flags which are useful to showcase how they interact with - // Leading/Trailing tabs - static ImGuiTabBarFlags tab_bar_flags = - ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | - ImGuiTabBarFlags_FittingPolicyResizeDown | - ImGuiTabBarFlags_TabListPopupButton; - - if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyResizeDown", - &tab_bar_flags, - ImGuiTabBarFlags_FittingPolicyResizeDown)) - tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ - ImGuiTabBarFlags_FittingPolicyResizeDown); - if (ImGui::CheckboxFlags("ImGuiTabBarFlags_FittingPolicyScroll", - &tab_bar_flags, - ImGuiTabBarFlags_FittingPolicyScroll)) - tab_bar_flags &= ~(ImGuiTabBarFlags_FittingPolicyMask_ ^ - ImGuiTabBarFlags_FittingPolicyScroll); - - if (ImGui::BeginTabBar("MyTabBar", tab_bar_flags)) { + // Using ImGui Custom Tabs show each individual room the user selects from the + // Buttons above to open a canvas for each individual room. + if (ImGui::BeginTabBar("MyTabBar", kDungeonTabBarFlags)) { // TODO: Manage the room that is being added to the tab bar. - if (ImGui::TabItemButton( - "+", ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip)) { + if (ImGui::TabItemButton("##tabitem", kDungeonTabFlags)) { active_rooms_.push_back(next_tab_id++); // Add new tab } @@ -155,17 +152,21 @@ void DungeonEditor::DrawDungeonCanvas(int room_id) { } void DungeonEditor::DrawToolset() { - if (ImGui::BeginTable("DWToolset", 10, ImGuiTableFlags_SizingFixedFit, + if (ImGui::BeginTable("DWToolset", 12, ImGuiTableFlags_SizingFixedFit, ImVec2(0, 0))) { ImGui::TableSetupColumn("#undoTool"); ImGui::TableSetupColumn("#redoTool"); - ImGui::TableSetupColumn("#history"); ImGui::TableSetupColumn("#separator"); + ImGui::TableSetupColumn("#anyTool"); + ImGui::TableSetupColumn("#bg1Tool"); ImGui::TableSetupColumn("#bg2Tool"); ImGui::TableSetupColumn("#bg3Tool"); - ImGui::TableSetupColumn("#itemTool"); + ImGui::TableSetupColumn("#separator"); ImGui::TableSetupColumn("#spriteTool"); + ImGui::TableSetupColumn("#itemTool"); + ImGui::TableSetupColumn("#doorTool"); + ImGui::TableSetupColumn("#blockTool"); ImGui::TableNextColumn(); if (ImGui::Button(ICON_MD_UNDO)) { @@ -178,41 +179,80 @@ void DungeonEditor::DrawToolset() { } ImGui::TableNextColumn(); - ImGui::Button(ICON_MD_MANAGE_HISTORY); + ImGui::Text(ICON_MD_MORE_VERT); + + ImGui::TableNextColumn(); + if (ImGui::RadioButton(ICON_MD_FILTER_NONE, + background_type_ == kBackgroundAny)) { + background_type_ = kBackgroundAny; + } + + ImGui::TableNextColumn(); + if (ImGui::RadioButton(ICON_MD_FILTER_1, + background_type_ == kBackground1)) { + background_type_ = kBackground1; + } + + ImGui::TableNextColumn(); + if (ImGui::RadioButton(ICON_MD_FILTER_2, + background_type_ == kBackground2)) { + background_type_ = kBackground2; + } + + ImGui::TableNextColumn(); + if (ImGui::RadioButton(ICON_MD_FILTER_3, + background_type_ == kBackground3)) { + background_type_ = kBackground3; + } ImGui::TableNextColumn(); ImGui::Text(ICON_MD_MORE_VERT); ImGui::TableNextColumn(); - ImGui::Button(ICON_MD_FILTER_1); - - ImGui::TableNextColumn(); - ImGui::Button(ICON_MD_FILTER_2); - - ImGui::TableNextColumn(); - ImGui::Button(ICON_MD_FILTER_3); - - ImGui::TableNextColumn(); - ImGui::Button(ICON_MD_GRASS); - - ImGui::TableNextColumn(); - ImGui::Button(ICON_MD_PEST_CONTROL_RODENT); - - ImGui::TableNextColumn(); - if (ImGui::Button("Dungeon Object Renderer")) { - show_object_render_ = !show_object_render_; + if (ImGui::RadioButton(ICON_MD_PEST_CONTROL, placement_type_ == kSprite)) { + placement_type_ = kSprite; } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Sprites"); + } + + ImGui::TableNextColumn(); + if (ImGui::RadioButton(ICON_MD_GRASS, placement_type_ == kItem)) { + placement_type_ = kItem; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Items"); + } + + ImGui::TableNextColumn(); + if (ImGui::RadioButton(ICON_MD_SENSOR_DOOR, placement_type_ == kDoor)) { + placement_type_ = kDoor; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Doors"); + } + + ImGui::TableNextColumn(); + if (ImGui::RadioButton(ICON_MD_SQUARE, placement_type_ == kBlock)) { + placement_type_ = kBlock; + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Blocks"); + } + ImGui::EndTable(); } } void DungeonEditor::DrawRoomGraphics() { - room_gfx_canvas_.DrawBackground(ImVec2(256 + 1, 0x10 * 0x40 + 1)); - room_gfx_canvas_.DrawContextMenu(); - room_gfx_canvas_.DrawTileSelector(32); - room_gfx_canvas_.DrawBitmap(room_gfx_bmp_, 2, is_loaded_); - room_gfx_canvas_.DrawGrid(32.0f); - room_gfx_canvas_.DrawOverlay(); + // room_gfx_canvas_.DrawBackground(ImVec2(256 + 1, 0x10 * 0x40 + 1)); + // room_gfx_canvas_.DrawContextMenu(); + // room_gfx_canvas_.DrawTileSelector(32); + // room_gfx_canvas_.DrawBitmap(room_gfx_bmp_, 2, is_loaded_); + // room_gfx_canvas_.DrawGrid(32.0f); + // room_gfx_canvas_.DrawOverlay(); + core::GraphicsManagerCanvasPipeline(256, 0x10 * 0x40, 32, 0x10, 0, is_loaded_, + room_graphics_[current_room_id_]); } void DungeonEditor::DrawTileSelector() { @@ -226,48 +266,55 @@ void DungeonEditor::DrawTileSelector() { ImGui::EndChild(); ImGui::EndTabItem(); } + + if (ImGui::BeginTabItem("Object Renderer")) { + DrawObjectRenderer(); + ImGui::EndTabItem(); + } ImGui::EndTabBar(); } } void DungeonEditor::DrawObjectRenderer() { - if (show_object_render_) { - ImGui::Begin("Dungeon Object Renderer", &show_object_render_); + if (ImGui::BeginTable( + "DungeonObjectEditorTable", 2, + ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | + ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | + ImGuiTableFlags_BordersV, + ImVec2(0, 0))) { + ImGui::TableSetupColumn("Dungeon Objects", + ImGuiTableColumnFlags_WidthStretch, + ImGui::GetContentRegionAvail().x); + ImGui::TableSetupColumn("Canvas"); - // Create an ImGui table where the left side of the table is a matrix of - // buttons which represent each dungeon object. The right side of the table - // is a canvas which will display the selected dungeon object. The canvas - // will also have a tile selector and a grid overlay. - if (ImGui::BeginTable("DungeonObjectEditorTable", 2, - ImGuiTableFlags_SizingFixedFit, ImVec2(0, 0))) { - ImGui::TableSetupColumn("Dungeon Objects", - ImGuiTableColumnFlags_WidthFixed, 150.0f); - ImGui::TableSetupColumn("Canvas"); + ImGui::TableNextColumn(); + ImGui::BeginChild("DungeonObjectButtons", ImVec2(0, 0), true); - ImGui::TableNextColumn(); - ImGui::BeginChild("DungeonObjectButtons", ImVec2(150.0f, 0), true); - - for (const auto each : zelda3::dungeon::Type1RoomObjectNames) { - ImGui::Button(each.data()); - if (ImGui::IsItemClicked()) { - } + int selected_object = 0; + int i = 0; + for (const auto object_name : zelda3::dungeon::Type1RoomObjectNames) { + if (ImGui::Selectable(object_name.data(), selected_object == i)) { + selected_object = i; } - - ImGui::EndChild(); - - // Right side of the table - Canvas - ImGui::TableNextColumn(); - ImGui::BeginChild("DungeonObjectCanvas", ImVec2(0, 0), true); - - // TODO: Insert code to display canvas, tile selector, and grid overlay - // here - - ImGui::EndChild(); - - ImGui::EndTable(); + i += 1; } - ImGui::End(); + ImGui::EndChild(); + + // Right side of the table - Canvas + ImGui::TableNextColumn(); + ImGui::BeginChild("DungeonObjectCanvas", ImVec2(276, 0x10 * 0x40 + 1), + true); + + dungeon_object_canvas_.DrawBackground(ImVec2(256 + 1, 0x10 * 0x40 + 1)); + dungeon_object_canvas_.DrawContextMenu(); + dungeon_object_canvas_.DrawTileSelector(32); + dungeon_object_canvas_.DrawGrid(32.0f); + dungeon_object_canvas_.DrawOverlay(); + + ImGui::EndChild(); + + ImGui::EndTable(); } } diff --git a/src/app/editor/dungeon_editor.h b/src/app/editor/dungeon_editor.h index c0413dce..29882c23 100644 --- a/src/app/editor/dungeon_editor.h +++ b/src/app/editor/dungeon_editor.h @@ -15,6 +15,19 @@ namespace yaze { namespace app { namespace editor { +constexpr ImGuiTabItemFlags kDungeonTabFlags = + ImGuiTabItemFlags_Trailing | ImGuiTabItemFlags_NoTooltip; + +constexpr ImGuiTabBarFlags kDungeonTabBarFlags = + ImGuiTabBarFlags_AutoSelectNewTabs | ImGuiTabBarFlags_Reorderable | + ImGuiTabBarFlags_FittingPolicyResizeDown | + ImGuiTabBarFlags_TabListPopupButton; + +constexpr ImGuiTableFlags kDungeonTableFlags = + ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | + ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | + ImGuiTableFlags_BordersV; + class DungeonEditor : public Editor, public SharedROM, public core::ExperimentFlags { @@ -28,12 +41,13 @@ class DungeonEditor : public Editor, private: void DrawToolset(); + void DrawRoomSelector(); void DrawDungeonTabView(); void DrawDungeonCanvas(int room_id); + void DrawRoomGraphics(); void DrawTileSelector(); - void DrawObjectRenderer(); uint16_t current_room_id_ = 0; @@ -47,8 +61,25 @@ class DungeonEditor : public Editor, std::vector rooms_; zelda3::dungeon::DungeonObjectRenderer object_renderer_; + enum BackgroundType { + kNoBackground, + kBackground1, + kBackground2, + kBackground3, + kBackgroundAny, + }; + enum PlacementType { kNoType, kSprite, kItem, kDoor, kBlock }; + + int background_type_ = kNoBackground; + int placement_type_ = kNoType; + gui::Canvas canvas_; gui::Canvas room_gfx_canvas_; + + gui::Canvas dungeon_object_canvas_; + + std::vector room_graphics_; + ImGuiTableFlags toolset_table_flags_ = ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Resizable; diff --git a/src/app/editor/overworld_editor.cc b/src/app/editor/overworld_editor.cc index a010efe2..9bb48a5f 100644 --- a/src/app/editor/overworld_editor.cc +++ b/src/app/editor/overworld_editor.cc @@ -51,7 +51,7 @@ absl::Status OverworldEditor::Update() { // Draws the toolset for editing the Overworld. RETURN_IF_ERROR(DrawToolset()) - Separator(); + if (ImGui::BeginTable(kOWEditTable.data(), 2, kOWEditFlags, ImVec2(0, 0))) { TableSetupColumn("Canvas", ImGuiTableColumnFlags_WidthStretch, ImGui::GetContentRegionAvail().x); diff --git a/src/app/editor/overworld_editor.h b/src/app/editor/overworld_editor.h index ba63a41d..8a9b17ea 100644 --- a/src/app/editor/overworld_editor.h +++ b/src/app/editor/overworld_editor.h @@ -47,9 +47,10 @@ static constexpr absl::string_view kOverworldSettingsColumnNames[] = { constexpr ImGuiTableFlags kOWMapFlags = ImGuiTableFlags_Borders; constexpr ImGuiTableFlags kToolsetTableFlags = ImGuiTableFlags_SizingFixedFit; -constexpr ImGuiTableFlags kOWEditFlags = ImGuiTableFlags_Reorderable | - ImGuiTableFlags_Resizable | ImGuiTableFlags_Hideable | - ImGuiTableFlags_SizingStretchSame; +constexpr ImGuiTableFlags kOWEditFlags = + ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | + ImGuiTableFlags_Hideable | ImGuiTableFlags_BordersOuter | + ImGuiTableFlags_BordersV; constexpr absl::string_view kWorldList = "Light World\0Dark World\0Extra World\0"; diff --git a/src/app/gfx/bitmap.h b/src/app/gfx/bitmap.h index 4e3a4a24..877e0c8b 100644 --- a/src/app/gfx/bitmap.h +++ b/src/app/gfx/bitmap.h @@ -95,6 +95,7 @@ class Bitmap { int width() const { return width_; } int height() const { return height_; } + auto depth() const { return depth_; } auto size() const { return data_size_; } auto data() const { return pixel_data_; } auto at(int i) const { return pixel_data_[i]; } @@ -149,6 +150,14 @@ class BitmapManager { return bitmap; } + std::shared_ptr CopyBitmap(std::shared_ptr bitmap, + int id) { + auto copy = std::make_shared(bitmap->width(), bitmap->height(), + bitmap->depth(), bitmap->data()); + bitmap_cache_[id] = copy; + return copy; + } + std::shared_ptr operator[](int id) { auto it = bitmap_cache_.find(id); if (it != bitmap_cache_.end()) { diff --git a/src/app/zelda3/dungeon/room.cc b/src/app/zelda3/dungeon/room.cc index 8a332677..0134efb7 100644 --- a/src/app/zelda3/dungeon/room.cc +++ b/src/app/zelda3/dungeon/room.cc @@ -143,7 +143,7 @@ void Room::LoadRoomGraphics(uchar entrance_blockset) { current_gfx16_.reserve(0x4000); for (int i = 0; i < 8; i++) { - blocks[i] = mainGfx[BackgroundTileset][i]; + blocks_[i] = mainGfx[BackgroundTileset][i]; if (i >= 6 && i <= 6) { // 3-6 if (entrance_blockset != 0xFF) { @@ -151,27 +151,29 @@ void Room::LoadRoomGraphics(uchar entrance_blockset) { // TODO: Find why this is wrong - Thats because of the stairs need to // find a workaround if (roomGfx[entrance_blockset][i - 3] != 0) { - blocks[i] = roomGfx[entrance_blockset][i - 3]; + blocks_[i] = roomGfx[entrance_blockset][i - 3]; } } } } - blocks[8] = 115 + 0; // Static Sprites Blocksets (fairy,pot,ect...) - blocks[9] = 115 + 10; - blocks[10] = 115 + 6; - blocks[11] = 115 + 7; + blocks_[8] = 115 + 0; // Static Sprites Blocksets (fairy,pot,ect...) + blocks_[9] = 115 + 10; + blocks_[10] = 115 + 6; + blocks_[11] = 115 + 7; for (int i = 0; i < 4; i++) { - blocks[12 + i] = (uchar)(spriteGfx[SpriteTileset + 64][i] + 115); + blocks_[12 + i] = (uchar)(spriteGfx[SpriteTileset + 64][i] + 115); } // 12-16 sprites +} +void Room::CopyRoomGraphicsToBuffer() { auto gfx_buffer_data = rom()->graphics_buffer(); // Into "room gfx16" 16 of them int sheetPos = 0; for (int i = 0; i < 16; i++) { int d = 0; - int ioff = blocks[i] * 2048; + int ioff = blocks_[i] * 2048; while (d < 2048) { uchar mapByte = gfx_buffer_data[d + ioff]; if (i < 4) { @@ -189,8 +191,7 @@ void Room::LoadRoomGraphics(uchar entrance_blockset) { } void Room::LoadAnimatedGraphics() { - int gfx_ptr = - core::SnesToPc(rom()->version_constants().kGfxAnimatedPointer); + int gfx_ptr = core::SnesToPc(rom()->version_constants().kGfxAnimatedPointer); auto gfx_buffer_data = rom()->graphics_buffer(); auto rom_data = rom()->vector(); diff --git a/src/app/zelda3/dungeon/room.h b/src/app/zelda3/dungeon/room.h index 651d479e..4de18158 100644 --- a/src/app/zelda3/dungeon/room.h +++ b/src/app/zelda3/dungeon/room.h @@ -117,6 +117,7 @@ class Room : public SharedROM { ~Room() = default; void LoadHeader(); void LoadRoomGraphics(uchar entrance_blockset = 0xFF); + void CopyRoomGraphicsToBuffer(); void LoadAnimatedGraphics(); void LoadSprites(); @@ -125,6 +126,8 @@ class Room : public SharedROM { void LoadRoomFromROM(); + auto blocks() const { return blocks_; } + RoomObject AddObject(short oid, uint8_t x, uint8_t y, uint8_t size, uint8_t layer) { return RoomObject(oid, x, y, size, layer); @@ -164,7 +167,7 @@ class Room : public SharedROM { uchar Floor1Graphics; uchar Floor2Graphics; uchar Layer2Mode; - std::array blocks; + std::array blocks_; std::array ChestList; std::vector sprites_;