483 lines
14 KiB
Markdown
483 lines
14 KiB
Markdown
# YAZE Dungeon System - Complete Technical Reference
|
|
|
|
Comprehensive reference for AI agents working on the YAZE Dungeon editing system.
|
|
|
|
---
|
|
|
|
## 1. Architecture Overview
|
|
|
|
### File Structure
|
|
```
|
|
src/zelda3/dungeon/
|
|
├── dungeon.h/cc # Main Dungeon class
|
|
├── room.h/cc # Room class (1,337 lines)
|
|
├── room_object.h/cc # RoomObject encoding (633+249 lines)
|
|
├── object_drawer.h/cc # Object rendering (210+972 lines)
|
|
├── object_parser.h/cc # ROM tile parsing (172+387 lines)
|
|
├── room_entrance.h # Entrance data (367 lines)
|
|
├── dungeon_rom_addresses.h # ROM address constants (108 lines)
|
|
|
|
src/app/editor/dungeon/
|
|
├── dungeon_editor_v2.h/cc # Main editor (card-based)
|
|
├── dungeon_room_loader.h/cc # ROM data loading
|
|
├── dungeon_room_selector.h/cc # Room selection UI
|
|
├── dungeon_canvas_viewer.h/cc # Canvas rendering
|
|
├── dungeon_object_selector.h/cc # Object palette
|
|
├── dungeon_object_interaction.h/cc # Mouse interactions
|
|
```
|
|
|
|
### Data Model
|
|
```
|
|
Dungeon
|
|
└── rooms_[296]
|
|
└── Room
|
|
├── tile_objects_[] (RoomObject instances)
|
|
├── sprites_[]
|
|
├── chests_in_room_[]
|
|
├── z3_staircases_[]
|
|
├── bg1_buffer_ (512x512 pixels)
|
|
├── bg2_buffer_ (512x512 pixels)
|
|
└── current_gfx16_[] (16KB graphics)
|
|
```
|
|
|
|
---
|
|
|
|
## 2. Room Structure
|
|
|
|
### Room Count & Organization
|
|
- **Total Rooms:** 296 (indices 0x00-0x127)
|
|
- **Canvas Size:** 512x512 pixels (64x64 tiles)
|
|
- **Layers:** BG1, BG2, BG3
|
|
|
|
### Room Properties
|
|
```cpp
|
|
// room.h
|
|
int room_id_; // Room index (0-295)
|
|
uint8_t blockset; // Graphics blockset ID
|
|
uint8_t spriteset; // Sprite set ID
|
|
uint8_t palette; // Palette ID (0-63)
|
|
uint8_t layout; // Layout template (0-7)
|
|
uint8_t floor1, floor2; // Floor graphics (nibbles)
|
|
uint16_t message_id_; // Associated message
|
|
|
|
// Behavioral
|
|
CollisionKey collision_type; // Collision enum
|
|
EffectKey effect_type; // Visual effect enum
|
|
TagKey tag1, tag2; // Special condition tags
|
|
LayerMergeType layer_merge; // BG1/BG2 blend mode
|
|
```
|
|
|
|
### Layer Merge Types
|
|
```cpp
|
|
enum LayerMergeType {
|
|
LayerMerge00 = 0x00, // Off - Layer 2 invisible
|
|
LayerMerge01 = 0x01, // Parallax scrolling
|
|
LayerMerge02 = 0x02, // Dark overlay
|
|
LayerMerge03 = 0x03, // On top (translucent)
|
|
LayerMerge04 = 0x04, // Translucent blend
|
|
LayerMerge05 = 0x05, // Addition blend
|
|
LayerMerge06 = 0x06, // Normal overlay
|
|
LayerMerge07 = 0x07, // Transparent
|
|
LayerMerge08 = 0x08, // Dark room effect
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 3. Object Encoding System
|
|
|
|
### 3-Byte Object Format
|
|
|
|
Objects are stored as 3 bytes in ROM with three distinct encoding types:
|
|
|
|
#### Type 1: Standard Objects (ID 0x00-0xFF)
|
|
```
|
|
Byte format: xxxxxxss | yyyyyyss | iiiiiiii
|
|
b1 b2 b3
|
|
|
|
Decoding:
|
|
x = (b1 & 0xFC) >> 2 // 6 bits (0-63)
|
|
y = (b2 & 0xFC) >> 2 // 6 bits (0-63)
|
|
size = ((b1 & 0x03) << 2) | (b2 & 0x03) // 4 bits (0-15)
|
|
id = b3 // 8 bits
|
|
```
|
|
|
|
#### Type 2: Extended Objects (ID 0x100-0x1FF)
|
|
```
|
|
Indicator: b1 >= 0xFC
|
|
|
|
Byte format: 111111xx | xxxxyyyy | yyiiiiii
|
|
b1 b2 b3
|
|
|
|
Decoding:
|
|
id = (b3 & 0x3F) | 0x100
|
|
x = ((b2 & 0xF0) >> 4) | ((b1 & 0x03) << 4)
|
|
y = ((b2 & 0x0F) << 2) | ((b3 & 0xC0) >> 6)
|
|
size = 0 // No size parameter
|
|
```
|
|
|
|
#### Type 3: Rare Objects (ID 0xF00-0xFFF)
|
|
```
|
|
Indicator: b3 >= 0xF8
|
|
|
|
Byte format: xxxxxxii | yyyyyyii | 11111iii
|
|
b1 b2 b3
|
|
|
|
Decoding:
|
|
id = (b3 << 4) | 0x80 | ((b2 & 0x03) << 2) | (b1 & 0x03)
|
|
x = (b1 & 0xFC) >> 2
|
|
y = (b2 & 0xFC) >> 2
|
|
size = ((b1 & 0x03) << 2) | (b2 & 0x03)
|
|
```
|
|
|
|
### Object Categories
|
|
|
|
| Type | ID Range | Examples |
|
|
|------|----------|----------|
|
|
| Type 1 | 0x00-0xFF | Walls, floors, decorations |
|
|
| Type 2 | 0x100-0x1FF | Corners, stairs, furniture |
|
|
| Type 3 | 0xF00-0xFFF | Chests, pipes, special objects |
|
|
|
|
---
|
|
|
|
## 4. ObjectDrawer Rendering System
|
|
|
|
### Class Structure
|
|
```cpp
|
|
// object_drawer.h
|
|
class ObjectDrawer {
|
|
// Entry point
|
|
absl::Status DrawObject(const RoomObject& object,
|
|
gfx::BackgroundBuffer& bg1,
|
|
gfx::BackgroundBuffer& bg2,
|
|
const gfx::PaletteGroup& palette_group);
|
|
|
|
// Data
|
|
Rom* rom_;
|
|
const uint8_t* room_gfx_buffer_; // current_gfx16_
|
|
std::unordered_map<int16_t, int> object_to_routine_map_;
|
|
std::vector<DrawRoutine> draw_routines_;
|
|
};
|
|
```
|
|
|
|
### Draw Routine Status
|
|
|
|
| # | Routine Name | Status | Lines |
|
|
|---|--------------|--------|-------|
|
|
| 0 | DrawRightwards2x2_1to15or32 | COMPLETE | 302-321 |
|
|
| 1 | DrawRightwards2x4_1to15or26 | COMPLETE | 323-348 |
|
|
| 2 | DrawRightwards2x4spaced4_1to16 | COMPLETE | 350-373 |
|
|
| 3 | DrawRightwards2x4spaced4_BothBG | **STUB** | 375-381 |
|
|
| 4 | DrawRightwards2x2_1to16 | COMPLETE | 383-399 |
|
|
| 5 | DrawDiagonalAcute_1to16 | **UNCLEAR** | 401-417 |
|
|
| 6 | DrawDiagonalGrave_1to16 | **UNCLEAR** | 419-435 |
|
|
| 7 | DrawDownwards2x2_1to15or32 | COMPLETE | 689-708 |
|
|
| 8 | DrawDownwards4x2_1to15or26 | COMPLETE | 710-753 |
|
|
| 9 | DrawDownwards4x2_BothBG | **STUB** | 755-761 |
|
|
| 10 | DrawDownwardsDecor4x2spaced4 | COMPLETE | 763-782 |
|
|
| 11 | DrawDownwards2x2_1to16 | COMPLETE | 784-799 |
|
|
| 12 | DrawDownwardsHasEdge1x1 | COMPLETE | 801-813 |
|
|
| 13 | DrawDownwardsEdge1x1 | COMPLETE | 815-827 |
|
|
| 14 | DrawDownwardsLeftCorners | COMPLETE | 829-842 |
|
|
| 15 | DrawDownwardsRightCorners | COMPLETE | 844-857 |
|
|
| 16 | DrawRightwards4x4_1to16 | COMPLETE | 534-550 |
|
|
| - | CustomDraw | **STUB** | 524-532 |
|
|
| - | DrawDoorSwitcherer | **STUB** | 566-575 |
|
|
|
|
### INCOMPLETE: BothBG Routines (4 locations)
|
|
|
|
These routines should draw to BOTH BG1 and BG2 but currently only call single-layer version:
|
|
|
|
```cpp
|
|
// Line 375-381: DrawRightwards2x4spaced4_1to16_BothBG
|
|
// Line 437-442: DrawDiagonalAcute_1to16_BothBG
|
|
// Line 444-449: DrawDiagonalGrave_1to16_BothBG
|
|
// Line 755-761: DrawDownwards4x2_1to16_BothBG
|
|
|
|
// Current (WRONG):
|
|
void DrawRightwards2x4spaced4_1to16_BothBG(
|
|
const RoomObject& obj, gfx::BackgroundBuffer& bg, // Only 1 buffer!
|
|
std::span<const gfx::TileInfo> tiles) {
|
|
// Just calls single-layer version - misses BG2
|
|
DrawRightwards2x4spaced4_1to16(obj, bg, tiles);
|
|
}
|
|
|
|
// Should be:
|
|
void DrawRightwards2x4spaced4_1to16_BothBG(
|
|
const RoomObject& obj,
|
|
gfx::BackgroundBuffer& bg1, // Both buffers
|
|
gfx::BackgroundBuffer& bg2,
|
|
std::span<const gfx::TileInfo> tiles) {
|
|
DrawRightwards2x4spaced4_1to16(obj, bg1, tiles);
|
|
DrawRightwards2x4spaced4_1to16(obj, bg2, tiles);
|
|
}
|
|
```
|
|
|
|
### UNCLEAR: Diagonal Routines
|
|
|
|
```cpp
|
|
// Lines 401-417, 419-435
|
|
// Issues:
|
|
// - Hardcoded +6 and 5 iterations (why?)
|
|
// - Coordinate formula may produce negative Y
|
|
// - Only uses 4 tiles from larger span
|
|
// - No bounds checking
|
|
|
|
for (int s = 0; s < size + 6; s++) { // Why +6?
|
|
for (int i = 0; i < 5; i++) { // Why 5?
|
|
WriteTile8(bg, obj.x_ + s, obj.y_ + (i - s), tiles[i % 4]);
|
|
// ^^ (i - s) can be negative when s > i
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 5. Tile Rendering Pipeline
|
|
|
|
### WriteTile8() - Tile to Pixel Conversion
|
|
```cpp
|
|
// object_drawer.cc lines 863-883
|
|
void WriteTile8(gfx::BackgroundBuffer& bg, int tile_x, int tile_y,
|
|
const gfx::TileInfo& tile_info) {
|
|
// tile coords → pixel coords: tile_x * 8, tile_y * 8
|
|
DrawTileToBitmap(bitmap, tile_info, tile_x * 8, tile_y * 8, room_gfx_buffer_);
|
|
}
|
|
```
|
|
|
|
### DrawTileToBitmap() - Pixel Rendering
|
|
```cpp
|
|
// object_drawer.cc lines 890-970
|
|
// Key steps:
|
|
// 1. Graphics sheet lookup: tile_info.id_ → (sheet_x, sheet_y)
|
|
// 2. Palette offset: (palette & 0x0F) * 8
|
|
// 3. Per-pixel with mirroring support
|
|
// 4. Color 0 = transparent (skipped)
|
|
|
|
int tile_sheet_x = (tile_info.id_ % 16) * 8; // 0-127 pixels
|
|
int tile_sheet_y = (tile_info.id_ / 16) * 8; // 0-127 pixels
|
|
uint8_t palette_offset = (tile_info.palette_ & 0x0F) * 8;
|
|
|
|
for (int py = 0; py < 8; py++) {
|
|
for (int px = 0; px < 8; px++) {
|
|
int src_x = tile_info.horizontal_mirror_ ? (7 - px) : px;
|
|
int src_y = tile_info.vertical_mirror_ ? (7 - py) : py;
|
|
// Read pixel, apply palette, write to bitmap
|
|
}
|
|
}
|
|
```
|
|
|
|
### Palette Application (CRITICAL)
|
|
```cpp
|
|
// object_drawer.cc lines 71-115
|
|
// Palette must be applied AFTER drawing, BEFORE SDL sync
|
|
|
|
// 1. Draw all objects (writes palette indices 0-255)
|
|
for (auto& obj : objects) {
|
|
DrawObject(obj, bg1, bg2, palette_group);
|
|
}
|
|
|
|
// 2. Apply dungeon palette to convert indices → RGB
|
|
bg1_bmp.SetPalette(dungeon_palette);
|
|
bg2_bmp.SetPalette(dungeon_palette);
|
|
|
|
// 3. Sync to SDL surfaces
|
|
SDL_LockSurface(bg1_bmp.surface());
|
|
memcpy(bg1_bmp.surface()->pixels, bg1_bmp.mutable_data().data(), ...);
|
|
SDL_UnlockSurface(bg1_bmp.surface());
|
|
```
|
|
|
|
---
|
|
|
|
## 6. ROM Addresses
|
|
|
|
### Room Data
|
|
```cpp
|
|
kRoomObjectLayoutPointer = 0x882D // Layout pointer table
|
|
kRoomObjectPointer = 0x874C // Object data pointer
|
|
kRoomHeaderPointer = 0xB5DD // Room headers (3-byte long)
|
|
kRoomHeaderPointerBank = 0xB5E7 // Bank byte
|
|
```
|
|
|
|
### Palette & Graphics
|
|
```cpp
|
|
kDungeonsMainBgPalettePointers = 0xDEC4B
|
|
kDungeonsPalettes = 0xDD734
|
|
kGfxGroupsPointer = 0x6237
|
|
kTileAddress = 0x1B52 // Main tile graphics
|
|
kTileAddressFloor = 0x1B5A // Floor tile graphics
|
|
```
|
|
|
|
### Object Subtypes
|
|
```cpp
|
|
kRoomObjectSubtype1 = 0x0F8000 // Standard objects
|
|
kRoomObjectSubtype2 = 0x0F83F0 // Extended objects
|
|
kRoomObjectSubtype3 = 0x0F84F0 // Rare objects
|
|
kRoomObjectTileAddress = 0x091B52 // Tile data
|
|
```
|
|
|
|
### Special Objects
|
|
```cpp
|
|
kBlocksPointer[1-4] = 0x15AFA-0x15B0F
|
|
kChestsDataPointer1 = 0xEBFB
|
|
kTorchData = 0x2736A
|
|
kPitPointer = 0x394AB
|
|
kDoorPointers = 0xF83C0
|
|
```
|
|
|
|
---
|
|
|
|
## 7. TODOs in room_object.h (30+ items)
|
|
|
|
### Unknown Objects Needing Verification
|
|
|
|
| Line | ID | Description |
|
|
|------|-----|-------------|
|
|
| 234 | 0x35 | "WEIRD DOOR" - needs investigation |
|
|
| 252-255 | 0x49-0x4C | "Unknown" Type 1 objects |
|
|
| 350-353 | 0xC4-0xC7 | "Diagonal layer 2 mask B" - needs verify |
|
|
| 392-395 | 0xDE-0xE1 | "Moving wall flag" - WTF IS THIS? |
|
|
| 466-476 | Type 2 | Multiple "Unknown" objects |
|
|
| 480 | 0x30 | "Intraroom stairs north B" - verify layer |
|
|
| 486 | 0x36 | "Water ladder (south)" - needs verify |
|
|
| 512-584 | Type 3 | Multiple "Unknown" objects |
|
|
|
|
---
|
|
|
|
## 8. DungeonEditorV2 Architecture
|
|
|
|
### Card-Based Component System
|
|
```cpp
|
|
DungeonEditorV2 (Coordinator)
|
|
├── DungeonRoomLoader // ROM data loading
|
|
├── DungeonRoomSelector // Room list/selection
|
|
├── DungeonCanvasViewer // 512x512 canvas
|
|
├── DungeonObjectSelector // Object palette
|
|
├── DungeonObjectInteraction // Mouse handling
|
|
├── ObjectEditorCard // Property editing
|
|
└── PaletteEditorWidget // Color editing
|
|
```
|
|
|
|
### Card Types
|
|
```cpp
|
|
show_control_panel_ // Room/entrance selection
|
|
show_room_selector_ // Room list
|
|
show_room_matrix_ // 16x19 visual layout
|
|
show_entrances_list_ // Entrance/spawn list
|
|
show_room_graphics_ // Blockset/palette
|
|
show_object_editor_ // Object placement
|
|
show_palette_editor_ // Palette colors
|
|
show_debug_controls_ // Debug options
|
|
```
|
|
|
|
### Undo/Redo System
|
|
```cpp
|
|
// Per-room object snapshots
|
|
std::unordered_map<int, std::vector<std::vector<RoomObject>>> undo_history_;
|
|
std::unordered_map<int, std::vector<std::vector<RoomObject>>> redo_history_;
|
|
```
|
|
|
|
---
|
|
|
|
## 9. Room Loading Flow
|
|
|
|
```
|
|
LoadRoomFromRom(room_id)
|
|
│
|
|
├── Resolve room header pointer (0xB5DD + room_id * 3)
|
|
│
|
|
├── Parse header bytes:
|
|
│ ├── BG2 type, collision, light flag
|
|
│ ├── Palette, blockset, spriteset
|
|
│ ├── Effect type, tags
|
|
│ └── Staircase data
|
|
│
|
|
├── Load graphics sheets (16 blocks)
|
|
│
|
|
└── LoadObjects()
|
|
│
|
|
├── Read floor/layout header (2 bytes)
|
|
│
|
|
├── Parse object stream:
|
|
│ ├── 3 bytes per object
|
|
│ ├── 0xFF 0xFF = layer boundary
|
|
│ └── 0xF0 0xFF = door section
|
|
│
|
|
└── Handle special objects:
|
|
├── Staircases
|
|
├── Chests
|
|
├── Doors
|
|
├── Torches
|
|
└── Blocks
|
|
```
|
|
|
|
---
|
|
|
|
## 10. Rendering Pipeline
|
|
|
|
```
|
|
1. LoadRoomGraphics()
|
|
└── Build graphics sheet list from blockset
|
|
|
|
2. CopyRoomGraphicsToBuffer()
|
|
└── Copy ROM sheets → current_gfx16_[]
|
|
|
|
3. RenderRoomGraphics()
|
|
├── Check dirty flags
|
|
├── LoadLayoutTilesToBuffer()
|
|
├── Draw floor to bg1/bg2 buffers
|
|
└── RenderObjectsToBackground()
|
|
└── ObjectDrawer::DrawObjectList()
|
|
|
|
4. Present (Canvas Viewer)
|
|
├── Process deferred texture queue
|
|
├── Create/update GPU textures
|
|
└── Render to ImGui canvas
|
|
```
|
|
|
|
---
|
|
|
|
## 11. Known Issues Summary
|
|
|
|
### BothBG Support (4 stubs)
|
|
- Line 380: `DrawRightwards2x4spaced4_1to16_BothBG`
|
|
- Line 441: `DrawDiagonalAcute_1to16_BothBG`
|
|
- Line 448: `DrawDiagonalGrave_1to16_BothBG`
|
|
- Line 760: `DrawDownwards4x2_1to16_BothBG`
|
|
|
|
**Fix:** Change signature to accept both `bg1` and `bg2` buffers.
|
|
|
|
### Diagonal Logic (2 routines)
|
|
- Lines 401-435: Hardcoded constants, potential negative coords
|
|
- **Needs:** Game verification or ZScream reference
|
|
|
|
### Custom/Door Stubs (2 routines)
|
|
- Line 524-532: `CustomDraw` - only draws first tile
|
|
- Line 566-575: `DrawDoorSwitcherer` - only draws first tile
|
|
|
|
### Object Names (30+ unknowns)
|
|
- Multiple objects need in-game verification
|
|
- See section 7 for full list
|
|
|
|
---
|
|
|
|
## 12. Testing
|
|
|
|
### Run Dungeon Tests
|
|
```bash
|
|
# Unit tests
|
|
ctest --test-dir build -R "dungeon\|Dungeon" -V
|
|
|
|
# E2E tests
|
|
ctest --test-dir build -R "DungeonEditor" -V
|
|
```
|
|
|
|
### E2E Test Files
|
|
- `test/e2e/dungeon_editor_smoke_test.cc`
|
|
- `test/e2e/dungeon_canvas_interaction_test.cc`
|
|
- `test/e2e/dungeon_layer_rendering_test.cc`
|
|
- `test/e2e/dungeon_object_drawing_test.cc`
|
|
|
|
### Test Design Doc
|
|
`docs/internal/testing/dungeon-gui-test-design.md` (1000+ lines)
|