# Dungeon Object Rendering Fix Plan

## Completed Phases

### Phase 1: BG Layer Draw Order - COMPLETED (2025-11-26)
**File:** `src/app/editor/dungeon/dungeon_canvas_viewer.cc:900-971`
**Fix:** Swapped draw order - BG2 drawn first (background), then BG1 (foreground with objects)
**Result:** Objects on BG1 no longer covered by BG2

### Phase 2: Wall Rendering Investigation - COMPLETED (2025-11-26)
**Finding:** Bitmap initialization was the issue, not draw routines
**Root Cause:** Test code was creating bitmap with `{0}` which only allocated 1 byte instead of 262144
**Verification:** Created ROM-dependent integration tests:
- `test/integration/zelda3/dungeon_graphics_transparency_test.cc`
- All 5 tests pass confirming:
  - Graphics buffer has 13% transparent pixels
  - Room graphics buffer has 31.9% transparent pixels
  - Wall objects load 8 tiles each correctly
  - BG1 has 24,000 non-zero pixels after object drawing

**Wall tiles confirmed working:** 0x090, 0x092, 0x093, 0x096, 0x098, 0x099, 0x0A2, 0x0A4, 0x0A5, 0x0AC, 0x0AD

### Phase 3: Subtype1 Tile Count Lookup Table - COMPLETED (2025-11-26)
**File:** `src/zelda3/dungeon/object_parser.cc:18-57`
**Fix:** Added `kSubtype1TileLengths[0xF8]` lookup table from ZScream's DungeonObjectData.cs
**Changes:**
- Added 248-entry tile count lookup table for Subtype 1 objects
- Modified `GetSubtype1TileCount()` helper function to use lookup table
- Updated `GetObjectSubtype()` and `ParseSubtype1()` to use dynamic tile counts
- Objects now load correct number of tiles (e.g., 0xC1 = 68 tiles, 0x33 = 16 tiles)

**Source:** [ZScream DungeonObjectData.cs](https://github.com/Zarby89/ZScreamDungeon)

## All Critical Phases Complete

**Root Cause Summary**: Multiple issues - layer draw order (FIXED), bitmap sizing (FIXED), tile counts (FIXED).

## Critical Findings

### Finding 1: Hardcoded Tile Count (ROOT CAUSE)
- **Location**: `src/zelda3/dungeon/object_parser.cc:141,160,178`
- **Issue**: `ReadTileData(tile_data_ptr, 8)` always reads 8 tiles
- **Impact**:
  - Simple objects (walls: 8 tiles) render correctly
  - Medium objects (carpets: 16 tiles) render incomplete
  - Complex objects (Agahnim's altar: 84 tiles) severely broken
- **Fix**: Use ZScream's `subtype1Lengths[]` lookup table

### Finding 2: Type 2/Type 3 Boundary Collision
- **Location**: `src/zelda3/dungeon/room_object.cc:184-190, 204-208`
- **Issue**: Type 2 objects with Y positions 3,7,11,...,63 encode to `b3 >= 0xF8`, triggering incorrect Type 3 decoding
- **Impact**: 512 object placements affected

### Finding 3: Type 2 Subtype Index Mask
- **Location**: `src/zelda3/dungeon/object_parser.cc:77, 147-148`
- **Issue**: Uses mask `0x7F` for 256 IDs, causing IDs 0x180-0x1FF to alias to 0x100-0x17F
- **Fix**: Use `object_id & 0xFF` or `object_id - 0x100`

### Finding 4: Type 3 Subtype Heuristic
- **Location**: `src/zelda3/dungeon/room_object.cc:18-28, 74`
- **Issue**: `GetSubtypeTable()` uses `id_ >= 0x200` but Type 3 IDs are 0xF00-0xFFF
- **Fix**: Change to `id_ >= 0xF00`

### Finding 5: Object ID Validation Range
- **Location**: `src/zelda3/dungeon/room.cc:966`
- **Issue**: Validates `r.id_ <= 0x3FF` but decoder can produce IDs up to 0xFFF
- **Fix**: Change to `r.id_ <= 0xFFF`

### Finding 6: tile_objects_ Not Cleared on Reload
- **Location**: `src/zelda3/dungeon/room.cc:908`
- **Issue**: Calling LoadObjects() twice causes object duplication
- **Fix**: Add `tile_objects_.clear()` at start of LoadObjects()

### Finding 7: Incomplete Draw Routine Registry
- **Location**: `src/zelda3/dungeon/object_drawer.cc:170`
- **Issue**: Reserves 35 routines but only initializes 17 (indices 0-16)
- **Impact**: Object IDs mapping to routines 17-34 fallback to 1x1 drawing

## Implementation Plan

### Phase 1: Fix BG Layer Draw Order (CRITICAL - DO FIRST)

**File:** `src/app/editor/dungeon/dungeon_canvas_viewer.cc`
**Location:** `DrawRoomBackgroundLayers()` (lines 900-968)

**Problem:** BG1 is drawn first, then BG2 is drawn ON TOP with 255 alpha, covering BG1 content.

**Fix:** Swap the draw order - draw BG2 first (background), then BG1 (foreground):

```cpp
void DungeonCanvasViewer::DrawRoomBackgroundLayers(int room_id) {
  // ... validation code ...

  // Draw BG2 FIRST (background layer - underneath)
  if (layer_settings.bg2_visible && bg2_bitmap.is_active() ...) {
    // ... existing BG2 draw code ...
  }

  // Draw BG1 SECOND (foreground layer - on top)
  if (layer_settings.bg1_visible && bg1_bitmap.is_active() ...) {
    // ... existing BG1 draw code ...
  }
}
```

### Phase 2: Investigate North/South Wall Draw Routines

**Observation:** Left/right walls (vertical, Downwards routines 0x60+) work, but up/down walls (horizontal, Rightwards routines 0x00-0x0B) don't.

**Files to check:**
- `src/zelda3/dungeon/object_drawer.cc` - Rightwards draw routines
- Object-to-routine mapping for wall IDs

**Wall Object IDs:**
- 0x00: Ceiling (routine 0 - DrawRightwards2x2_1to15or32)
- 0x01-0x02: North walls (routine 1 - DrawRightwards2x4_1to15or26)
- 0x03-0x04: South walls (routine 2 - DrawRightwards2x4spaced4_1to16)
- 0x60+: East/West walls (Downwards routines - WORKING)

**Possible issues:**
1. Object tiles not being loaded for Subtype1 0x00-0x0B
2. Draw routines have coordinate bugs
3. Objects assigned to wrong layer (BG2 instead of BG1)

### Phase 3: Subtype1 Tile Count Fix

**Files to modify:**
- `src/zelda3/dungeon/object_parser.cc`

Add ZScream's tile count lookup table:
```cpp
static constexpr uint8_t kSubtype1TileLengths[0xF8] = {
    04,08,08,08,08,08,08,04,04,05,05,05,05,05,05,05,
    05,05,05,05,05,05,05,05,05,05,05,05,05,05,05,05,
    05,09,03,03,03,03,03,03,03,03,03,03,03,03,03,06,
    06,01,01,16,01,01,16,16,06,08,12,12,04,08,04,03,
    03,03,03,03,03,03,03,00,00,08,08,04,09,16,16,16,
    01,18,18,04,01,08,08,01,01,01,01,18,18,15,04,03,
    04,08,08,08,08,08,08,04,04,03,01,01,06,06,01,01,
    16,01,01,16,16,08,16,16,04,01,01,04,01,04,01,08,
    08,12,12,12,12,18,18,08,12,04,03,03,03,01,01,06,
    08,08,04,04,16,04,04,01,01,01,01,01,01,01,01,01,
    01,01,01,01,24,01,01,01,01,01,01,01,01,01,01,01,
    01,01,16,03,03,08,08,08,04,04,16,04,04,04,01,01,
    01,68,01,01,08,08,08,08,08,08,08,01,01,28,28,01,
    01,08,08,00,00,00,00,01,08,08,08,08,21,16,04,08,
    08,08,08,08,08,08,08,08,08,01,01,01,01,01,01,01,
    01,01,01,01,01,01,01,01
};
```

### Phase 4: Object Type Detection Fixes (Deferred)

- Type 2/Type 3 boundary collision
- Type 2 index mask (0x7F vs 0xFF)
- Type 3 detection heuristic (0x200 vs 0xF00)

### Phase 5: Validation & Lifecycle Fixes (Deferred)

- Object ID validation range (0x3FF → 0xFFF)
- tile_objects_ not cleared on reload

### Phase 6: Draw Routine Completion (Deferred)

- Complete draw routine registry (routines 17-34)

## Testing Strategy

### Test Objects by Complexity
| Object ID | Tiles | Description | Expected Result |
|-----------|-------|-------------|-----------------|
| 0x000 | 4 | Ceiling | Works |
| 0x001 | 8 | Wall (north) | Works |
| 0x033 | 16 | Carpet | Should render fully |
| 0x0C1 | 68 | Chest platform | Should render fully |
| 0x215 | 80 | Prison cell | Should render fully |

### Rooms to Test
- Room 0x00 (Simple walls)
- Room with carpets
- Agahnim's tower rooms
- Fortune teller room (uses 242-tile objects)

## Files to Read Before Implementation

1. `/Users/scawful/Code/yaze/src/zelda3/dungeon/object_parser.cc` - **PRIMARY** - Find the hardcoded `8` in tile loading
2. `/Users/scawful/Code/ZScreamDungeon/ZeldaFullEditor/Data/DungeonObjectData.cs` - Verify tile table values

## Estimated Impact

- **Phase 1 alone** should fix ~90% of broken Subtype1 objects (most common type)
- Simple walls/floors already work (they use 4-8 tiles)
- Carpets (16 tiles), chest platforms (68 tiles), and complex objects will now render fully

## Risk Assessment

- **Low Risk**: Adding a lookup table is additive, doesn't change existing logic flow
- **Mitigation**: Compare visual output against ZScream for a few test rooms
