Files
yaze/docs/DUNGEON_EDITOR_GUIDE.md
scawful c512dd7f35 refactor: Replace deprecated dungeon editor guide with updated documentation
- Deleted the old D1-dungeon-editor-guide.md and F1-dungeon-editor-guide.md files to streamline documentation.
- Introduced a new DUNGEON_EDITOR_GUIDE.md that consolidates features, architecture, and usage instructions for the Dungeon Editor.
- Updated the development guide to include new naming conventions and clarified rendering processes.
- Enhanced the overall structure and content of the documentation to reflect the current production-ready status of the Dungeon Editor.
2025-10-09 20:48:07 -04:00

579 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# YAZE Dungeon Editor: Complete Guide
**Last Updated**: October 9, 2025
**Status**: PRODUCTION READY - Core features stable, tested, and functional
---
## Table of Contents
- [Overview](#overview)
- [Current Status](#current-status)
- [Architecture](#architecture)
- [Quick Start](#quick-start)
- [Core Features](#core-features)
- [Technical Details](#technical-details)
- [Testing](#testing)
- [Troubleshooting](#troubleshooting)
- [ROM Internals](#rom-internals)
- [Reference](#reference)
---
## Overview
The Dungeon Editor uses a modern card-based architecture for editing dungeon rooms in The Legend of Zelda: A Link to the Past. The editor features lazy loading, per-room settings, and a component-based design for maximum flexibility.
### Key Capabilities
- **Visual room editing** with 512x512 canvas
- **Object placement** with pattern-based rendering
- **Live palette editing** with instant preview
- **Independent dockable UI cards**
- **Multi-room editing** support
- **Automatic graphics loading**
- **Per-room layer visibility** settings
- **Command-line quick testing** support
---
## Current Status
### ✅ Production Ready Features
- Core rendering pipeline (floor, walls, objects, sprites)
- Object drawing via ObjectDrawer with pattern-based rendering
- Live palette editing with HSV picker
- Per-room background buffers (no shared state corruption)
- Independent dockable card system
- Cross-editor navigation (overworld ↔ dungeon)
- Error recovery system
- Test suite (29/29 tests passing - 100%)
### 🔧 Recently Fixed Issues
1. **Object Visibility** ✅ FIXED
- **Problem**: Objects drawn to bitmaps but not visible on canvas
- **Root Cause**: Textures not updated after `RenderObjectsToBackground()`
- **Fix**: Added texture UPDATE commands after object rendering
2. **Property Change Re-rendering** ✅ FIXED
- **Problem**: Changing blockset/palette didn't trigger re-render
- **Fix**: Added change detection and automatic re-rendering
3. **One-Time Rendering** ✅ FIXED
- **Problem**: Objects only rendered once, never updated
- **Fix**: Removed restrictive rendering checks
4. **Per-Room Layer Settings** ✅ IMPLEMENTED
- Each room now has independent BG1/BG2 visibility settings
- Layer type controls (Normal, Translucent, Addition, Dark, Off)
5. **Canvas Context Menu** ✅ IMPLEMENTED
- Dungeon-specific options (Place Object, Delete Selected, Toggle Layers, Re-render)
- Dynamic menu based on current selection
---
## Architecture
### Component Hierarchy
```
DungeonEditorV2 (Coordinator)
├── Dungeon Controls (Collapsible panel)
│ └── Card visibility toggles
├── Independent Cards (all fully dockable)
│ ├── Rooms List Card (filterable, searchable)
│ ├── Room Matrix Card (16x19 grid, 296 rooms)
│ ├── Entrances List Card (entrance configuration)
│ ├── Room Graphics Card (blockset graphics display)
│ ├── Object Editor Card (unified object placement)
│ ├── Palette Editor Card (90-color palette editing)
│ └── Room Cards (dynamic, auto-dock together)
└── Per-Room Rendering
└── Room
├── bg1_buffer_ (BackgroundBuffer)
├── bg2_buffer_ (BackgroundBuffer)
└── DungeonCanvasViewer
```
### Card-Based Architecture Benefits
- ✅ Full freedom to drag, dock, resize
- ✅ No layout constraints or inheritance
- ✅ Can be arranged however user wants
- ✅ Session-aware card titles
- ✅ ImGui handles all docking logic
- ✅ Independent lifetime (close Dungeon Controls, rooms stay open)
---
## Quick Start
### Launch from Command Line
```bash
# Open specific room
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0"
# Compare multiple rooms
./yaze --rom_file=zelda3.sfc --editor=Dungeon --cards="Room 0,Room 1,Room 105"
# Full workspace
./yaze --rom_file=zelda3.sfc --editor=Dungeon \
--cards="Rooms List,Room Matrix,Object Editor,Palette Editor"
# Debug mode with logging
./yaze --debug --log_file=debug.log --rom_file=zelda3.sfc --editor=Dungeon
```
### From GUI
1. Launch YAZE
2. Load ROM (File → Open ROM or drag & drop)
3. Open Dungeon Editor (Tools → Dungeon Editor)
4. Toggle cards via "Dungeon Controls" checkboxes
5. Click room in list/matrix to open
---
## Core Features
### 1. Rooms List Card
```
Features:
- Filter/search functionality (ICON_MD_SEARCH)
- Format: [HEX_ID] Room Name
- Click to open room card
- Double-click for instant focus
- Shows all 296 rooms (0x00-0x127)
```
### 2. Room Matrix Card (Visual Navigation)
```
Layout:
- 16 columns × 19 rows = 304 cells
- Displays all 296 rooms (0x00-0x127)
- 24px cells with 1px spacing (optimized)
- Window size: 440x520
Visual Features:
- Deterministic HSV colors (no loading needed)
- Light green outline: Currently selected room
- Green outline: Open rooms
- Gray outline: Inactive rooms
Performance:
- Before: 2-4 seconds (lazy loading 296 rooms)
- After: < 50ms (pure math, no I/O)
```
### 3. Entrances List Card
```
Configuration UI (ZScream Parity):
- Entrance ID, Room ID, Dungeon ID
- Blockset, Music, Floor
- Player Position (X, Y)
- Camera Trigger (X, Y)
- Scroll Position (X, Y)
- Exit value
- Camera Boundaries (quadrant & full room)
List Features:
- Format: [HEX_ID] Entrance Name -> Room Name
- Shows entrance-to-room relationship
- Click to select and open associated room
```
### 4. Object Editor Card (Unified)
```
Improved UX:
- Mode controls at top: None | Place | Select | Delete
- Current object info always visible
- 2 tabs:
- Browser: Object selection with previews
- Preview: Emulator rendering with controls
Object Browser:
- Categorized objects (Floor/Wall/Special)
- 32x32 preview icons
- Filter/search functionality
- Shows object ID and type
```
### 5. Palette Editor Card
```
Features:
- Palette selector dropdown (20 dungeon palettes)
- 90-color grid (15 per row)
- Visual selection with yellow border
- Tooltips: color index, SNES BGR555, RGB values
- HSV color wheel picker
- Live RGB display (0-255)
- SNES format display (15-bit BGR555)
- Reset button
Live Updates:
- Edit palette → all open rooms re-render automatically
- Callback system decouples palette editor from rooms
```
### 6. Room Cards (Auto-Loading)
```
Features:
- Auto-loads graphics when properties change
- Simple status indicator (✓ Loaded / ⏳ Not Loaded)
- Auto-saves with main Save command
- Per-room layer controls (BG1/BG2 visibility, BG2 layer type)
Docking Behavior:
- ImGuiWindowClass for automatic tab grouping
- New room cards auto-dock with existing rooms
- Can be undocked independently
- Maintains session state
```
### 7. Canvas Context Menu (NEW)
```
Dungeon-Specific Options:
- Place Object (Ctrl+P)
- Delete Selected (Del) - conditional on selection
- Toggle BG1 (1)
- Toggle BG2 (2)
- Re-render Room (Ctrl+R)
Integration:
- Dynamic menu based on current state
- Consistent with overworld editor UX
```
---
## Technical Details
### Rendering Pipeline
```
1. Room::CopyRoomGraphicsToBuffer()
→ Loads tile graphics into current_gfx16_ [128×N indexed pixels]
2. BackgroundBuffer::DrawFloor()
→ Fills tilemap buffer with floor tile IDs
3. BackgroundBuffer::DrawBackground()
→ Renders floor tiles to bitmap (512×512 indexed surface)
4. Room::SetPalette()
→ Apply 90-color dungeon palette to SDL surface
5. Room::RenderObjectsToBackground()
→ ObjectDrawer writes wall/object tiles to BG1/BG2 buffers
6. gfx::Arena::QueueTextureCommand(UPDATE, &bitmap)
→ CRITICAL: Update textures after object rendering
7. gfx::Arena::ProcessTextureQueue(renderer)
→ Process queued texture operations
8. DungeonCanvasViewer::DrawRoomBackgroundLayers()
→ Draw textures to canvas with ImGui::Image()
```
### Critical Fix: Texture Update After Object Rendering
**Problem**: Objects were drawn to bitmaps but textures were never updated.
**Solution** (in `room.cc`):
```cpp
void Room::RenderRoomGraphics() {
// 1. Draw floor and background
bg1_buffer_.DrawFloor(...);
bg1_buffer_.DrawBackground(...);
// 2. Apply palette and create initial textures
bg1_bmp.SetPalette(bg1_palette);
gfx::Arena::Get().QueueTextureCommand(CREATE, &bg1_bmp);
// 3. Render objects to bitmaps
RenderObjectsToBackground();
// 4. CRITICAL FIX: Update textures with new bitmap data
gfx::Arena::Get().QueueTextureCommand(UPDATE, &bg1_bmp);
gfx::Arena::Get().QueueTextureCommand(UPDATE, &bg2_bmp);
}
```
### SNES Graphics Format
**8-bit Indexed Color (3BPP for dungeons)**:
```cpp
// Each pixel is a palette index (0-7)
// RGB color comes from applying dungeon palette
bg1_bmp.SetPalette(dungeon_pal_group[palette_id]); // 90 colors
Renderer::Get().RenderBitmap(&bitmap); // indexed → RGB
```
**Color Format: 15-bit BGR555**
```
Bits: 0BBB BBGG GGGR RRRR
││││ ││││ ││││ ││││
│└──┴─┘└──┴─┘└──┴─┘
│ Blue Green Red
└─ Unused (always 0)
Each channel: 0-31 (5 bits)
Total colors: 32,768 (2^15)
```
**Palette Organization**:
- 20 total palettes (one per dungeon color scheme)
- 90 colors per palette (full SNES BG palette)
- ROM address: `kDungeonMainPalettes` (0xDD734)
### Critical Math Formulas
**Tile Position in Tilesheet (128px wide)**:
```cpp
int tile_x = (tile_id % 16) * 8;
int tile_y = (tile_id / 16) * 8;
int pixel_offset = (tile_y * 128) + tile_x;
```
**Tile Position in Canvas (512×512)**:
```cpp
int canvas_x = (tile_col * 8);
int canvas_y = (tile_row * 8);
int pixel_offset = (canvas_y * 512) + canvas_x;
// CRITICAL: For NxN tiles, advance by (tile_row * 8 * width)
int dest_offset = (yy * 8 * 512) + (xx * 8); // NOT just (yy * 512)!
```
**Palette Index Calculation**:
```cpp
// 3BPP: 8 colors per subpalette
int final_index = pixel_value + (palette_id * 8);
// NOT 4BPP (× 16)!
// int final_index = pixel_value + (palette_id << 4); // WRONG
```
### Per-Room Buffers
**Problem**: Multiple rooms shared `gfx::Arena::Get().bg1()` and corrupted each other.
**Solution**: Each `Room` has its own buffers:
```cpp
// In room.h
gfx::BackgroundBuffer bg1_buffer_;
gfx::BackgroundBuffer bg2_buffer_;
// In room.cc
bg1_buffer_.DrawFloor(...);
bg1_buffer_.DrawBackground(std::span<uint8_t>(current_gfx16_));
Renderer::Get().RenderBitmap(&bg1_buffer_.bitmap());
```
---
## Testing
### Test Suite Status
| Test Type | Total | Passing | Pass Rate |
| ----------------- | ----- | ------- | --------- |
| **Unit Tests** | 14 | 14 | 100% ✅ |
| **Integration** | 14 | 14 | 100% ✅ |
| **E2E Tests** | 1 | 1 | 100% ✅ |
| **TOTAL** | **29**| **29** | **100%** ✅ |
### Running Tests
```bash
# Build tests (mac-ai preset)
cmake --preset mac-ai -B build_ai
cmake --build build_ai --target yaze_test
# Run all dungeon tests
./build_ai/bin/yaze_test --gtest_filter="*Dungeon*"
# Run E2E tests with GUI (normal speed)
./build_ai/bin/yaze_test --ui --show-gui --normal --gtest_filter="*DungeonEditorSmokeTest*"
# Run E2E tests in slow-motion (cinematic mode)
./build_ai/bin/yaze_test --ui --show-gui --cinematic --gtest_filter="*DungeonEditorSmokeTest*"
# Run all tests with fast execution
./build_ai/bin/yaze_test --ui --fast
```
### Test Speed Modes (NEW)
```bash
--fast # Run tests as fast as possible (teleport mouse, skip delays)
--normal # Run tests at human watchable speed (for debugging)
--cinematic # Run tests in slow-motion with pauses (for demos/tutorials)
```
---
## Troubleshooting
### Common Issues & Fixes
#### Issue 1: Objects Not Visible
**Symptom**: Floor/walls render but objects invisible
**Fix**: ✅ RESOLVED - Texture update after object rendering now working
#### Issue 2: Wrong Colors
**Symptom**: Colors don't match expected palette
**Fix**: Use `SetPalette()` not `SetPaletteWithTransparent()` for dungeons
**Reason**:
```cpp
// WRONG (extracts only 8 colors):
bitmap.SetPaletteWithTransparent(palette);
// CORRECT (applies full 90-color palette):
bitmap.SetPalette(palette);
```
#### Issue 3: Bitmap Stretched/Corrupted
**Symptom**: Graphics only in top portion, repeated/stretched
**Fix**: Wrong offset in DrawBackground()
```cpp
// WRONG:
int offset = (yy * 512) + (xx * 8); // Only advances 512 per row
// CORRECT:
int offset = (yy * 8 * 512) + (xx * 8); // Advances 4096 per row
```
#### Issue 4: Room Properties Don't Update
**Symptom**: Changing blockset/palette has no effect
**Fix**: ✅ RESOLVED - Property change detection now working
---
## ROM Internals
### Object Encoding
Dungeon objects are stored in one of three formats:
#### Type 1: Standard Objects (ID 0x00-0xFF)
```
Format: xxxxxxss yyyyyyss iiiiiiii
Use: Common geometry like walls and floors
```
#### Type 2: Large Coordinate Objects (ID 0x100-0x1FF)
```
Format: 111111xx xxxxyyyy yyiiiiii
Use: More complex or interactive structures
```
#### Type 3: Special Objects (ID 0x200-0x27F)
```
Format: xxxxxxii yyyyyyii 11111iii
Use: Critical gameplay elements (chests, switches, bosses)
```
### Core Data Tables in ROM
- **`bank_01.asm`**:
- **`DrawObjects` (0x018000)**: Master tables mapping object ID → drawing routine
- **`LoadAndBuildRoom` (0x01873A)**: Primary routine that reads and draws a room
- **`rooms.asm`**:
- **`RoomData_ObjectDataPointers` (0x1F8000)**: Table of 3-byte pointers to object data for each of 296 rooms
### Key ROM Addresses
```cpp
constexpr int dungeons_palettes = 0xDD734;
constexpr int room_object_pointer = 0x874C; // Long pointer
constexpr int kRoomHeaderPointer = 0xB5DD; // LONG
constexpr int tile_address = 0x001B52;
constexpr int tile_address_floor = 0x001B5A;
constexpr int torch_data = 0x2736A;
constexpr int blocks_pointer1 = 0x15AFA;
constexpr int pit_pointer = 0x394AB;
constexpr int doorPointers = 0xF83C0;
```
---
## Reference
### File Organization
**Core Rendering**:
- `src/app/zelda3/dungeon/room.{h,cc}` - Room state, buffers
- `src/app/gfx/background_buffer.{h,cc}` - Tile → bitmap drawing
- `src/app/core/renderer.cc` - Bitmap → texture conversion
- `src/app/editor/dungeon/dungeon_canvas_viewer.cc` - Canvas display
**Object Drawing**:
- `src/app/zelda3/dungeon/object_drawer.{h,cc}` - Native C++ patterns
- `src/app/gui/widgets/dungeon_object_emulator_preview.{h,cc}` - Research tool
**Editor UI**:
- `src/app/editor/dungeon/dungeon_editor_v2.{h,cc}` - Main coordinator
- `src/app/gui/widgets/editor_card.{h,cc}` - Independent card system
- `src/app/editor/dungeon/dungeon_object_interaction.{h,cc}` - Object selection
**Palette System**:
- `src/app/gfx/snes_palette.{h,cc}` - Palette loading
- `src/app/gui/widgets/palette_editor_widget.{h,cc}` - Visual editor
- `src/app/gfx/bitmap.cc` - SetPalette() implementation
### Quick Reference: Key Functions
```cpp
// Load dungeon palette
auto& pal_group = rom->palette_group().dungeon_main;
auto palette = pal_group[palette_id]; // NOT .palette(id)!
// Apply palette to bitmap
bitmap.SetPalette(palette); // NOT SetPaletteWithTransparent()!
// Create texture from indexed bitmap
Renderer::Get().RenderBitmap(&bitmap); // NOT UpdateBitmap()!
// Tile sheet offset (128px wide)
int offset = (tile_id / 16) * 8 * 128 + (tile_id % 16) * 8;
// Canvas offset (512px wide)
int offset = (tile_row * 8 * 512) + (tile_col * 8);
// Palette offset (3BPP)
int offset = palette_id * 8; // NOT << 4 !
```
### Performance Metrics
**Matrix Loading**:
- Load time: < 50ms (pure calculation, no I/O)
- Memory allocations: ~20 per matrix draw (cached colors)
- Frame drops: None
**Room Loading**:
- Lazy loading: Rooms loaded on-demand
- Graphics caching: Reused across room switches
- Texture batching: Up to 8 textures processed per frame
---
## Summary
The Dungeon Editor is production-ready with all core features implemented and tested. Recent fixes ensure objects render correctly, property changes trigger re-renders, and the context menu provides dungeon-specific functionality. The card-based architecture provides maximum flexibility while maintaining stability.
### Critical Points
1. **Texture Update**: Always call UPDATE after modifying bitmap data
2. **Per-Room Buffers**: Each room has independent bg1/bg2 buffers
3. **Property Changes**: Automatically detected and trigger re-renders
4. **Palette Format**: Use SetPalette() for full 90-color dungeon palettes
5. **Context Menu**: Dungeon-specific options available via right-click
---
**For detailed debugging**: See `QUICK-DEBUG-REFERENCE.txt` for command-line shortcuts.