From c1e69ce03e8dc3c394e5cfbb85ad3ee63c204253 Mon Sep 17 00:00:00 2001 From: scawful Date: Thu, 9 Oct 2025 21:43:27 -0400 Subject: [PATCH] feat: Add Debug Controls Card to Dungeon Editor V2 - Introduced a new Debug Controls card in the Dungeon Editor V2, allowing runtime control over debug logging and log levels. - Implemented UI elements for enabling/disabling debug logs, selecting log levels, and managing room rendering and texture processing. - Updated the DungeonEditorV2 class to include visibility management for the Debug Controls card and integrated it into the control panel. - Enhanced logging functionality in the LogManager to support runtime adjustments for debug logging. --- src/app/editor/dungeon/dungeon_editor_v2.cc | 159 +++++++++++++++++++- src/app/editor/dungeon/dungeon_editor_v2.h | 2 + src/util/log.h | 13 ++ 3 files changed, 173 insertions(+), 1 deletion(-) diff --git a/src/app/editor/dungeon/dungeon_editor_v2.cc b/src/app/editor/dungeon/dungeon_editor_v2.cc index 4efbca84..fb9a9f7a 100644 --- a/src/app/editor/dungeon/dungeon_editor_v2.cc +++ b/src/app/editor/dungeon/dungeon_editor_v2.cc @@ -98,6 +98,16 @@ void DungeonEditorV2::Initialize(gfx::IRenderer* renderer, Rom* rom) { .visibility_flag = &show_palette_editor_, .priority = 70 }); + + card_manager.RegisterCard({ + .card_id = "dungeon.debug_controls", + .display_name = "Debug Controls", + .icon = ICON_MD_BUG_REPORT, + .category = "Dungeon", + .shortcut_hint = "Ctrl+Shift+B", + .visibility_flag = &show_debug_controls_, + .priority = 80 + }); } void DungeonEditorV2::Initialize() {} @@ -304,6 +314,10 @@ void DungeonEditorV2::DrawControlPanel() { ImGui::TableNextColumn(); ImGui::Checkbox("Palette", &show_palette_editor_); + ImGui::TableNextRow(); + ImGui::TableNextColumn(); + ImGui::Checkbox("Debug", &show_debug_controls_); + ImGui::EndTable(); } @@ -367,7 +381,12 @@ void DungeonEditorV2::DrawLayout() { // Card handles its own closing via &show_palette_editor_ in constructor } - // 6. Active Room Cards (independent, dockable, tracked for jump-to) + // 7. Debug Controls Card (independent, dockable) + if (show_debug_controls_) { + DrawDebugControlsCard(); + } + + // 8. Active Room Cards (independent, dockable, tracked for jump-to) for (int i = 0; i < active_rooms_.Size; i++) { int room_id = active_rooms_[i]; bool open = true; @@ -930,6 +949,144 @@ void DungeonEditorV2::DrawRoomGraphicsCard() { graphics_card.End(); } +void DungeonEditorV2::DrawDebugControlsCard() { + gui::EditorCard debug_card( + MakeCardTitle("Debug Controls").c_str(), + ICON_MD_BUG_REPORT, &show_debug_controls_); + + debug_card.SetDefaultSize(350, 500); + + if (debug_card.Begin()) { + ImGui::TextWrapped("Runtime debug controls for development"); + ImGui::Separator(); + + // ===== LOGGING CONTROLS ===== + ImGui::SeparatorText(ICON_MD_TERMINAL " Logging"); + + bool debug_enabled = util::LogManager::instance().IsDebugEnabled(); + if (ImGui::Checkbox("Enable DEBUG Logs", &debug_enabled)) { + if (debug_enabled) { + util::LogManager::instance().EnableDebugLogging(); + LOG_INFO("DebugControls", "DEBUG logging ENABLED"); + } else { + util::LogManager::instance().DisableDebugLogging(); + LOG_INFO("DebugControls", "DEBUG logging DISABLED"); + } + } + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Toggle LOG_DEBUG visibility\nShortcut: Ctrl+Shift+D"); + } + + // Log level selector + const char* log_levels[] = {"DEBUG", "INFO", "WARNING", "ERROR", "FATAL"}; + int current_level = static_cast(util::LogManager::instance().GetLogLevel()); + if (ImGui::Combo("Log Level", ¤t_level, log_levels, 5)) { + util::LogManager::instance().SetLogLevel(static_cast(current_level)); + LOG_INFO("DebugControls", "Log level set to %s", log_levels[current_level]); + } + + ImGui::Separator(); + + // ===== ROOM RENDERING CONTROLS ===== + ImGui::SeparatorText(ICON_MD_IMAGE " Rendering"); + + if (current_room_id_ >= 0 && current_room_id_ < static_cast(rooms_.size())) { + auto& room = rooms_[current_room_id_]; + + ImGui::Text("Current Room: %03X", current_room_id_); + ImGui::Text("Objects: %zu", room.GetTileObjects().size()); + ImGui::Text("Sprites: %zu", room.GetSprites().size()); + + if (ImGui::Button(ICON_MD_REFRESH " Force Re-render", ImVec2(-FLT_MIN, 0))) { + room.LoadRoomGraphics(room.blockset); + room.LoadObjects(); + room.RenderRoomGraphics(); + LOG_INFO("DebugControls", "Forced re-render of room %03X", current_room_id_); + } + + if (ImGui::Button(ICON_MD_CLEANING_SERVICES " Clear Room Buffers", ImVec2(-FLT_MIN, 0))) { + room.ClearTileObjects(); + LOG_INFO("DebugControls", "Cleared room %03X buffers", current_room_id_); + } + + ImGui::Separator(); + + // Floor graphics override + ImGui::Text("Floor Graphics Override:"); + uint8_t floor1 = room.floor1(); + uint8_t floor2 = room.floor2(); + static uint8_t floor_min = 0; + static uint8_t floor_max = 15; + if (ImGui::SliderScalar("Floor1", ImGuiDataType_U8, &floor1, &floor_min, &floor_max)) { + room.set_floor1(floor1); + if (room.rom() && room.rom()->is_loaded()) { + room.RenderRoomGraphics(); + } + } + if (ImGui::SliderScalar("Floor2", ImGuiDataType_U8, &floor2, &floor_min, &floor_max)) { + room.set_floor2(floor2); + if (room.rom() && room.rom()->is_loaded()) { + room.RenderRoomGraphics(); + } + } + } else { + ImGui::TextDisabled("No room selected"); + } + + ImGui::Separator(); + + // ===== TEXTURE CONTROLS ===== + ImGui::SeparatorText(ICON_MD_TEXTURE " Textures"); + + if (ImGui::Button(ICON_MD_DELETE_SWEEP " Process Texture Queue", ImVec2(-FLT_MIN, 0))) { + gfx::Arena::Get().ProcessTextureQueue(renderer_); + LOG_INFO("DebugControls", "Manually processed texture queue"); + } + + // Texture stats + ImGui::Text("Arena Graphics Sheets: %zu", gfx::Arena::Get().gfx_sheets().size()); + + ImGui::Separator(); + + // ===== MEMORY CONTROLS ===== + ImGui::SeparatorText(ICON_MD_MEMORY " Memory"); + + size_t active_rooms_count = active_rooms_.Size; + ImGui::Text("Active Rooms: %zu", active_rooms_count); + ImGui::Text("Estimated Memory: ~%zu MB", active_rooms_count * 2); // 2MB per room + + if (ImGui::Button(ICON_MD_CLOSE " Close All Rooms", ImVec2(-FLT_MIN, 0))) { + active_rooms_.clear(); + room_cards_.clear(); + LOG_INFO("DebugControls", "Closed all room cards"); + } + + ImGui::Separator(); + + // ===== QUICK ACTIONS ===== + ImGui::SeparatorText(ICON_MD_FLASH_ON " Quick Actions"); + + if (ImGui::Button(ICON_MD_SAVE " Save All Rooms", ImVec2(-FLT_MIN, 0))) { + auto status = Save(); + if (status.ok()) { + LOG_INFO("DebugControls", "Saved all rooms"); + } else { + LOG_ERROR("DebugControls", "Save failed: %s", status.message().data()); + } + } + + if (ImGui::Button(ICON_MD_REPLAY " Reload Current Room", ImVec2(-FLT_MIN, 0))) { + if (current_room_id_ >= 0 && current_room_id_ < static_cast(rooms_.size())) { + auto status = room_loader_.LoadRoom(current_room_id_, rooms_[current_room_id_]); + if (status.ok()) { + LOG_INFO("DebugControls", "Reloaded room %03X", current_room_id_); + } + } + } + } + debug_card.End(); +} + void DungeonEditorV2::ProcessDeferredTextures() { // Process queued texture commands via Arena's deferred system // This is critical for ensuring textures are actually created and updated diff --git a/src/app/editor/dungeon/dungeon_editor_v2.h b/src/app/editor/dungeon/dungeon_editor_v2.h index 2530722c..397e1667 100644 --- a/src/app/editor/dungeon/dungeon_editor_v2.h +++ b/src/app/editor/dungeon/dungeon_editor_v2.h @@ -94,6 +94,7 @@ class DungeonEditorV2 : public Editor { bool show_room_graphics_ = false; // Room graphics card bool show_object_editor_ = false; // Object editor card bool show_palette_editor_ = false; // Palette editor card + bool show_debug_controls_ = false; // Debug controls card bool show_control_panel_ = true; // Control panel (visible by default) private: @@ -106,6 +107,7 @@ class DungeonEditorV2 : public Editor { void DrawRoomsListCard(); void DrawEntrancesListCard(); void DrawRoomGraphicsCard(); + void DrawDebugControlsCard(); void DrawControlPanel(); // Texture processing (critical for rendering) diff --git a/src/util/log.h b/src/util/log.h index 136991a3..8d6f5e99 100644 --- a/src/util/log.h +++ b/src/util/log.h @@ -62,6 +62,19 @@ class LogManager { void log(LogLevel level, absl::string_view category, absl::string_view message); + /** + * @brief Runtime log level control (for debug card) + */ + void SetLogLevel(LogLevel level) { min_level_.store(level); } + LogLevel GetLogLevel() const { return min_level_.load(); } + + /** + * @brief Toggle debug logging on/off at runtime + */ + void EnableDebugLogging() { min_level_.store(LogLevel::YAZE_DEBUG); } + void DisableDebugLogging() { min_level_.store(LogLevel::INFO); } + bool IsDebugEnabled() const { return min_level_.load() == LogLevel::YAZE_DEBUG; } + private: LogManager(); ~LogManager();