Add Comprehensive Guide for Yaze Dungeon Editor

- Introduced a new documentation file detailing the Yaze Dungeon Editor, covering its architecture, core components, coordinate system, object rendering system, user interface, and integration with ZScream.
- Included sections on usage examples, testing and validation strategies, and future enhancements to provide a complete overview of the editor's capabilities.
- Removed the outdated dungeon object rendering refactor documentation to streamline resources and focus on the comprehensive guide.
- Enhanced clarity and organization of the documentation to support both novice and expert users in effectively utilizing the dungeon editing tools.
This commit is contained in:
scawful
2025-09-24 23:00:44 -04:00
parent ccd4e8cf4b
commit 04909dc3d0
4 changed files with 585 additions and 326 deletions

View File

@@ -0,0 +1,360 @@
# Yaze Dungeon Editor - Comprehensive Guide
## Overview
The Yaze Dungeon Editor is a comprehensive tool for editing Zelda 3: A Link to the Past dungeon rooms, objects, sprites, items, entrances, doors, and chests. It provides an integrated editing experience with real-time rendering, coordinate system management, and advanced features for dungeon modification.
## Architecture
### Core Components
#### 1. DungeonEditorSystem
- **Purpose**: Central coordinator for all dungeon editing operations
- **Location**: `src/app/zelda3/dungeon/dungeon_editor_system.h/cc`
- **Features**:
- Room management (loading, saving, creating, deleting)
- Sprite management (enemies, NPCs, interactive objects)
- Item management (keys, hearts, rupees, etc.)
- Entrance/exit management (room connections)
- Door management (locked doors, key requirements)
- Chest management (treasure placement)
- Undo/redo system
- Event callbacks for real-time updates
#### 2. DungeonObjectEditor
- **Purpose**: Specialized editor for room objects (walls, floors, decorations)
- **Location**: `src/app/zelda3/dungeon/dungeon_object_editor.h/cc`
- **Features**:
- Object placement and editing
- Layer management (BG1, BG2, BG3)
- Object size editing with scroll wheel
- Collision detection and validation
- Selection and multi-selection
- Grid snapping
- Real-time preview
#### 3. ObjectRenderer
- **Purpose**: High-performance rendering system for dungeon objects
- **Location**: `src/app/zelda3/dungeon/object_renderer.h/cc`
- **Features**:
- Graphics cache for performance optimization
- Memory pool management
- Performance monitoring and statistics
- Object parsing from ROM data
- Palette support and color management
- Batch rendering for efficiency
#### 4. DungeonEditor (UI Layer)
- **Purpose**: User interface and interaction handling
- **Location**: `src/app/editor/dungeon/dungeon_editor.h/cc`
- **Features**:
- Integrated tabbed interface
- Canvas-based room editing
- Coordinate system management
- Object preview system
- Real-time rendering
- Compact editing panels
## Coordinate System
### Room Coordinates vs Canvas Coordinates
The dungeon editor uses a two-tier coordinate system:
1. **Room Coordinates**: 16x16 tile units (as used in the ROM)
2. **Canvas Coordinates**: Pixel coordinates for rendering
#### Conversion Functions
```cpp
// Convert room coordinates to canvas coordinates
std::pair<int, int> RoomToCanvasCoordinates(int room_x, int room_y) const {
return {room_x * 16, room_y * 16};
}
// Convert canvas coordinates to room coordinates
std::pair<int, int> CanvasToRoomCoordinates(int canvas_x, int canvas_y) const {
return {canvas_x / 16, canvas_y / 16};
}
// Check if coordinates are within canvas bounds
bool IsWithinCanvasBounds(int canvas_x, int canvas_y, int margin = 32) const;
```
### Coordinate System Features
- **Automatic Bounds Checking**: Objects outside visible canvas area are culled
- **Scrolling Support**: Canvas handles scrolling internally with proper coordinate transformation
- **Grid Alignment**: 16x16 pixel grid for precise object placement
- **Margin Support**: Configurable margins for partial object visibility
## Object Rendering System
### Object Types
The system supports three main object subtypes based on ROM structure:
1. **Subtype 1** (0x00-0xFF): Standard room objects (walls, floors, decorations)
2. **Subtype 2** (0x100-0x1FF): Interactive objects (doors, switches, chests)
3. **Subtype 3** (0x200+): Special objects (stairs, warps, bosses)
### Rendering Pipeline
1. **Object Loading**: Objects are loaded from ROM data using `LoadObjects()`
2. **Tile Parsing**: Object tiles are parsed using `ObjectParser`
3. **Graphics Caching**: Frequently used graphics are cached for performance
4. **Palette Application**: SNES palettes are applied to object graphics
5. **Canvas Rendering**: Objects are rendered to canvas with proper coordinate transformation
### Performance Optimizations
- **Graphics Cache**: Reduces redundant graphics sheet loading
- **Memory Pool**: Efficient memory allocation for rendering
- **Batch Rendering**: Multiple objects rendered in single pass
- **Bounds Culling**: Objects outside visible area are skipped
- **Cache Invalidation**: Smart cache management based on palette changes
## User Interface
### Integrated Editing Panels
The dungeon editor features a consolidated interface with:
#### Main Canvas
- **Room Visualization**: Real-time room rendering with background layers
- **Object Display**: Objects rendered with proper positioning and sizing
- **Interactive Editing**: Click-to-select, drag-to-move, scroll-to-resize
- **Grid Overlay**: Optional grid display for precise positioning
- **Coordinate Display**: Real-time coordinate information
#### Compact Editing Panels
1. **Object Editor**
- Mode selection (Select, Insert, Edit, Delete)
- Layer management (BG1, BG2, BG3)
- Object type selection
- Size editing with scroll wheel
- Configuration options (snap to grid, show grid)
2. **Sprite Editor**
- Sprite placement and management
- Enemy and NPC configuration
- Layer assignment
- Quick sprite addition
3. **Item Editor**
- Item placement (keys, hearts, rupees)
- Hidden item configuration
- Item type selection
- Room assignment
4. **Entrance Editor**
- Room connection management
- Bidirectional connection support
- Position configuration
- Connection validation
5. **Door Editor**
- Door placement and configuration
- Lock status management
- Key requirement setup
- Direction and target room assignment
6. **Chest Editor**
- Treasure chest placement
- Item and quantity configuration
- Big chest support
- Opened status tracking
7. **Properties Editor**
- Room metadata management
- Dungeon settings
- Music and ambient sound configuration
- Boss room and save room flags
### Object Preview System
- **Real-time Preview**: Objects are previewed in the canvas as they're selected
- **Centered Display**: Preview objects are centered in the canvas for optimal viewing
- **Palette Support**: Previews use current palette settings
- **Information Display**: Object properties are shown in preview window
## Integration with ZScream
The dungeon editor is designed to be compatible with ZScream C# patterns:
### Room Loading
- Uses same room loading patterns as ZScream
- Compatible with ZScream room data structures
- Supports ZScream room naming conventions
### Object Parsing
- Follows ZScream object parsing logic
- Compatible with ZScream object type definitions
- Supports ZScream size encoding
### Coordinate System
- Matches ZScream coordinate conventions
- Uses same tile size calculations
- Compatible with ZScream positioning logic
## Testing and Validation
### Integration Tests
The system includes comprehensive integration tests:
1. **Basic Object Rendering**: Tests fundamental object rendering functionality
2. **Multi-Palette Rendering**: Tests rendering with different palettes
3. **Real Room Object Rendering**: Tests with actual ROM room data
4. **Disassembly Room Validation**: Tests specific rooms from disassembly
5. **Performance Testing**: Measures rendering performance and memory usage
6. **Cache Effectiveness**: Tests graphics cache performance
7. **Error Handling**: Tests error conditions and edge cases
### Test Data
Tests use real ROM data from `build/bin/zelda3.sfc`:
- **Room 0x0000**: Ganon's room (from disassembly)
- **Room 0x0002, 0x0012**: Sewer rooms (from disassembly)
- **Room 0x0020**: Agahnim's tower (from disassembly)
- **Additional rooms**: 0x0001, 0x0010, 0x0033, 0x005A
### Performance Benchmarks
- **Rendering Time**: < 500ms for 100 objects
- **Memory Usage**: < 100MB for large object sets
- **Cache Hit Rate**: Optimized for frequent object access
- **Coordinate Conversion**: O(1) coordinate transformation
## Usage Examples
### Basic Object Editing
```cpp
// Load a room
auto room_result = dungeon_editor_system_->GetRoom(0x0000);
// Add an object
auto status = object_editor_->InsertObject(5, 5, 0x10, 0x12, 0);
// Parameters: x, y, object_type, size, layer
// Render objects
auto result = object_renderer_->RenderObjects(objects, palette);
```
### Coordinate Conversion
```cpp
// Convert room coordinates to canvas coordinates
auto [canvas_x, canvas_y] = RoomToCanvasCoordinates(room_x, room_y);
// Check if coordinates are within bounds
if (IsWithinCanvasBounds(canvas_x, canvas_y)) {
// Render object at this position
}
```
### Object Preview
```cpp
// Create preview object
auto preview_object = zelda3::RoomObject(id, 8, 8, 0x12, 0);
preview_object.set_rom(rom_);
preview_object.EnsureTilesLoaded();
// Render preview
auto result = object_renderer_->RenderObject(preview_object, palette);
```
## Configuration Options
### Editor Configuration
```cpp
struct EditorConfig {
bool snap_to_grid = true;
int grid_size = 16;
bool show_grid = true;
bool show_preview = true;
bool auto_save = false;
int auto_save_interval = 300;
bool validate_objects = true;
bool show_collision_bounds = false;
};
```
### Performance Configuration
```cpp
// Object renderer settings
object_renderer_->SetCacheSize(100);
object_renderer_->EnablePerformanceMonitoring(true);
// Canvas settings
canvas_.SetCanvasSize(ImVec2(512, 512));
canvas_.set_draggable(true);
```
## Troubleshooting
### Common Issues
1. **Objects Not Displaying**
- Check if ROM is loaded
- Verify object tiles are loaded with `EnsureTilesLoaded()`
- Check coordinate bounds with `IsWithinCanvasBounds()`
2. **Coordinate Misalignment**
- Use coordinate conversion functions
- Check canvas scrolling settings
- Verify grid alignment
3. **Performance Issues**
- Enable graphics caching
- Check memory usage with `GetMemoryUsage()`
- Monitor performance stats with `GetPerformanceStats()`
4. **Preview Not Showing**
- Verify object is within canvas bounds
- Check palette is properly set
- Ensure object has valid tiles
### Debug Information
The system provides comprehensive debug information:
- Object count and statistics
- Cache hit/miss rates
- Memory usage tracking
- Performance metrics
- Coordinate system validation
## Future Enhancements
### Planned Features
1. **Advanced Object Editing**
- Multi-object selection and manipulation
- Object grouping and layers
- Advanced collision detection
2. **Enhanced Rendering**
- Real-time lighting effects
- Animation support
- Advanced shader effects
3. **Improved UX**
- Keyboard shortcuts
- Context menus
- Undo/redo visualization
4. **Integration Features**
- ZScream project import/export
- Collaborative editing
- Version control integration
## Conclusion
The Yaze Dungeon Editor provides a comprehensive, high-performance solution for editing Zelda 3 dungeon rooms. With its integrated interface, robust coordinate system, and advanced rendering capabilities, it offers both novice and expert users the tools needed to create and modify dungeon content effectively.
The system's compatibility with ZScream patterns and comprehensive testing ensure reliability and consistency with existing tools, while its modern architecture provides a foundation for future enhancements and features.

View File

@@ -1,175 +0,0 @@
# Dungeon Object Rendering Refactor
## Overview
This document describes the comprehensive refactoring of the dungeon object rendering system in YAZE, replacing the SNES emulation approach with direct ROM parsing for better performance, reliability, and maintainability.
## Problem Statement
The original dungeon object rendering system had several issues:
1. **SNES Emulation Complexity**: Used full SNES CPU emulation to render objects, which was slow and error-prone
2. **Poor Performance**: Emulating thousands of CPU instructions for simple object rendering
3. **Maintenance Issues**: Complex emulation code was difficult to debug and maintain
4. **Limited Testing**: Hard to test without real ROM files and complex emulation state
5. **Architectural Problems**: Tight coupling between rendering and emulation systems
## Solution Architecture
### New Components
#### 1. ObjectParser (`src/app/zelda3/dungeon/object_parser.h/cc`)
- **Purpose**: Direct ROM parsing for object data without emulation
- **Features**:
- Parses all three object subtypes (0x00-0xFF, 0x100-0x1FF, 0x200+)
- Extracts tile data directly from ROM tables
- Provides object size and orientation information
- Handles object routine information
#### 2. ObjectRenderer (`src/app/zelda3/dungeon/object_renderer.h/cc`)
- **Purpose**: High-performance object rendering using parsed data
- **Features**:
- Renders single objects or multiple objects
- Supports size and orientation variations
- Provides object previews for UI
- Direct bitmap generation without emulation
#### 3. Enhanced RoomObject (`src/app/zelda3/dungeon/room_object.h/cc`)
- **Purpose**: Improved object representation with better tile loading
- **Features**:
- Uses ObjectParser for tile loading
- Fallback to legacy method for compatibility
- Better error handling and validation
#### 4. Simplified Test Framework (`test/test_dungeon_objects.h/cc`)
- **Purpose**: Comprehensive testing without real ROM files
- **Features**:
- Extended MockRom class for testing
- Simplified test structure using test folder prefix
- Performance benchmarks
- Modular component testing
## Implementation Details
### Object Parsing
The new system directly parses object data from ROM tables:
```cpp
// Old approach: SNES emulation
snes_.cpu().ExecuteInstruction(opcode); // Thousands of instructions
// New approach: Direct parsing
auto tiles = parser.ParseObject(object_id); // Direct ROM access
```
### Object Rendering
Objects are now rendered directly to bitmaps:
```cpp
// Create object with tiles
auto object = RoomObject(id, x, y, size, layer);
object.set_rom(rom);
object.EnsureTilesLoaded(); // Uses ObjectParser
// Render to bitmap
auto bitmap = renderer.RenderObject(object, palette);
```
### Testing Strategy
The new system includes comprehensive testing:
1. **Unit Tests**: Individual component testing
2. **Integration Tests**: End-to-end pipeline testing
3. **Mock Data**: Testing without real ROM files
4. **Performance Tests**: Benchmarking improvements
## Performance Improvements
### Before (SNES Emulation)
- **Object Rendering**: ~1000+ CPU instructions per object
- **Memory Usage**: Full SNES memory state (128KB+)
- **Complexity**: O(n) where n = instruction count
- **Debugging**: Difficult due to emulation state
### After (Direct Parsing)
- **Object Rendering**: ~10-20 ROM reads per object
- **Memory Usage**: Minimal (just parsed data)
- **Complexity**: O(1) for most operations
- **Debugging**: Simple, direct code paths
## API Changes
### DungeonEditor
```cpp
// Old
// zelda3::DungeonObjectRenderer object_renderer_;
// New
zelda3::ObjectRenderer object_renderer_;
```
### RoomObject
```cpp
// New methods
void EnsureTilesLoaded(); // Uses ObjectParser
absl::Status LoadTilesWithParser(); // Direct parsing
```
## Testing
### Running Tests
```bash
# Build and run all tests
cd build
make yaze_test
./yaze_test
# Run specific test suites
./yaze_test --gtest_filter="*ObjectParser*"
./yaze_test --gtest_filter="*ObjectRenderer*"
./yaze_test --gtest_filter="*TestDungeonObjects*"
```
### Test Coverage
- **ObjectParser**: 100% method coverage
- **ObjectRenderer**: 100% method coverage
- **Integration Tests**: Complete pipeline coverage
- **Mock Data**: Realistic test scenarios
## Migration Guide
### For Developers
1. **Replace Old Renderer**: Use `ObjectRenderer` instead of `DungeonObjectRenderer`
2. **Update Object Loading**: Use `EnsureTilesLoaded()` for automatic tile loading
3. **Add Error Handling**: Check return values from new methods
4. **Update Tests**: Use new mock framework for testing
### For Users
- **No Breaking Changes**: All existing functionality preserved
- **Better Performance**: Faster object rendering
- **More Reliable**: Fewer emulation-related bugs
- **Better UI**: Improved object previews
## Future Improvements
1. **Caching**: Add tile data caching for repeated objects
2. **Batch Rendering**: Render multiple objects in single operation
3. **GPU Acceleration**: Use GPU for large-scale rendering
4. **Real-time Preview**: Live object preview in editor
5. **Animation Support**: Animated object rendering
## Conclusion
The refactored dungeon object rendering system provides:
- **10x Performance Improvement**: Direct parsing vs emulation
- **Better Maintainability**: Cleaner, more focused code
- **Comprehensive Testing**: Full test coverage with mocks
- **Future-Proof Architecture**: Extensible design for new features
This refactoring establishes a solid foundation for future dungeon editing features while maintaining backward compatibility and improving overall system reliability.

View File

@@ -10,12 +10,11 @@
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/rom.h"
#include "app/zelda3/dungeon/room.h"
#include "app/zelda3/dungeon/dungeon_editor_system.h"
#include "app/zelda3/dungeon/dungeon_object_editor.h"
#include "app/zelda3/dungeon/object_renderer.h"
#include "app/zelda3/dungeon/room.h"
#include "imgui/imgui.h"
#include "imgui_memory_editor.h"
#include "util/hex.h"
namespace yaze::editor {
@@ -45,7 +44,8 @@ constexpr ImGuiTableFlags kDungeonObjectTableFlags =
void DungeonEditor::Initialize() {
if (rom_ && !dungeon_editor_system_) {
dungeon_editor_system_ = std::make_unique<zelda3::DungeonEditorSystem>(rom_);
dungeon_editor_system_ =
std::make_unique<zelda3::DungeonEditorSystem>(rom_);
object_editor_ = std::make_shared<zelda3::DungeonObjectEditor>(rom_);
}
}
@@ -91,7 +91,7 @@ absl::Status DungeonEditor::Load() {
gfx::CreatePaletteGroupFromLargePalette(full_palette_));
CalculateUsageStats();
// Initialize the new editor system
if (dungeon_editor_system_) {
auto status = dungeon_editor_system_->Initialize();
@@ -99,7 +99,7 @@ absl::Status DungeonEditor::Load() {
return status;
}
}
is_loaded_ = true;
return absl::OkStatus();
}
@@ -115,7 +115,7 @@ absl::Status DungeonEditor::Update() {
status_ = UpdateDungeonRoomView();
ImGui::EndTabItem();
}
if (ImGui::BeginTabItem("Usage Statistics")) {
if (is_loaded_) {
DrawUsageStats();
@@ -258,8 +258,9 @@ void DungeonEditor::DrawToolset() {
if (BeginTable("DWToolset", 16, ImGuiTableFlags_SizingFixedFit,
ImVec2(0, 0))) {
static std::array<const char *, 16> tool_names = {
"Undo", "Redo", "Separator", "Any", "BG1", "BG2", "BG3",
"Separator", "Object", "Sprite", "Item", "Entrance", "Door", "Chest", "Block", "Palette"};
"Undo", "Redo", "Separator", "Any", "BG1", "BG2",
"BG3", "Separator", "Object", "Sprite", "Item", "Entrance",
"Door", "Chest", "Block", "Palette"};
std::ranges::for_each(tool_names,
[](const char *name) { TableSetupColumn(name); });
@@ -663,9 +664,9 @@ void DungeonEditor::DrawDungeonCanvas(int room_id) {
// Render regular room objects
for (const auto &object : rooms_[room_id].GetTileObjects()) {
// Convert room coordinates to canvas coordinates
int canvas_x = object.x_ * 16;
int canvas_y = object.y_ * 16;
// Convert room coordinates to canvas coordinates using helper function
auto [canvas_x, canvas_y] =
RoomToCanvasCoordinates(object.x_, object.y_);
if (show_objects) {
// Draw object outline - use size_ to determine dimensions
@@ -682,7 +683,8 @@ void DungeonEditor::DrawDungeonCanvas(int room_id) {
outline_height = (height_bits + 1) * 16;
}
canvas_.DrawOutline(object.x_, object.y_, outline_width,
// Use canvas coordinates for outline
canvas_.DrawOutline(canvas_x, canvas_y, outline_width,
outline_height);
}
@@ -709,9 +711,8 @@ void DungeonEditor::RenderObjectInCanvas(const zelda3::RoomObject &object,
mutable_object.set_rom(rom_);
mutable_object.EnsureTilesLoaded();
// Check if tiles were loaded successfully using the new method
auto tiles_result = mutable_object.GetTiles();
if (!tiles_result.ok() || tiles_result->empty()) {
// Check if tiles were loaded successfully
if (mutable_object.tiles().empty()) {
return; // Skip objects without tiles
}
@@ -722,14 +723,20 @@ void DungeonEditor::RenderObjectInCanvas(const zelda3::RoomObject &object,
(palette_hash << 6) + (palette_hash >> 2);
}
// Convert room coordinates to canvas coordinates using helper function
auto [canvas_x, canvas_y] = RoomToCanvasCoordinates(object.x_, object.y_);
// Check if object is within canvas bounds (accounting for scrolling)
if (!IsWithinCanvasBounds(canvas_x, canvas_y, 32)) {
return; // Skip objects outside visible area
}
// Check cache first
for (auto &cached : object_render_cache_) {
if (cached.object_id == object.id_ && cached.object_x == object.x_ &&
cached.object_y == object.y_ && cached.object_size == object.size_ &&
cached.palette_hash == palette_hash && cached.is_valid) {
// Use cached bitmap
int canvas_x = object.x_ * 16;
int canvas_y = object.y_ * 16;
// Use cached bitmap - canvas handles scrolling internally
canvas_.DrawBitmap(cached.rendered_bitmap, canvas_x, canvas_y, 1.0f, 255);
return;
}
@@ -749,12 +756,8 @@ void DungeonEditor::RenderObjectInCanvas(const zelda3::RoomObject &object,
// Render the bitmap to a texture so it can be drawn
core::Renderer::Get().RenderBitmap(&object_bitmap);
// Convert room coordinates to canvas coordinates
// Room coordinates are in 16x16 tile units, canvas coordinates are in pixels
int canvas_x = object.x_ * 16;
int canvas_y = object.y_ * 16;
// Draw the object bitmap to the canvas immediately
// Draw the object bitmap to the canvas
// Canvas will handle scrolling and coordinate transformation
canvas_.DrawBitmap(object_bitmap, canvas_x, canvas_y, 1.0f, 255);
// Cache the rendered bitmap (create a copy for caching)
@@ -790,8 +793,14 @@ void DungeonEditor::RenderLayoutObjects(const zelda3::RoomLayout &layout,
// This provides a visual representation of the room's structure
for (const auto &layout_obj : layout.GetObjects()) {
int canvas_x = layout_obj.x() * 16;
int canvas_y = layout_obj.y() * 16;
// Convert room coordinates to canvas coordinates using helper function
auto [canvas_x, canvas_y] =
RoomToCanvasCoordinates(layout_obj.x(), layout_obj.y());
// Check if layout object is within canvas bounds
if (!IsWithinCanvasBounds(canvas_x, canvas_y, 16)) {
continue; // Skip objects outside visible area
}
// Choose color based on object type
gfx::SnesColor color;
@@ -895,7 +904,8 @@ void DungeonEditor::DrawObjectRenderer() {
selected_object = i;
// Create a test object and render it
auto test_object = zelda3::RoomObject(i, 0, 0, 0x12, 0);
auto test_object =
zelda3::RoomObject(i, 8, 8, 0x12, 0); // Center in canvas
test_object.set_rom(rom_);
test_object.EnsureTilesLoaded();
@@ -907,6 +917,8 @@ void DungeonEditor::DrawObjectRenderer() {
auto result = object_renderer_.GetObjectPreview(test_object, palette);
if (result.ok()) {
object_loaded_ = true;
preview_object_ = test_object; // Store for rendering
preview_palette_ = palette;
}
}
i += 1;
@@ -922,6 +934,24 @@ void DungeonEditor::DrawObjectRenderer() {
object_canvas_.DrawContextMenu();
object_canvas_.DrawTileSelector(32);
object_canvas_.DrawGrid(32.0f);
// Render object preview if available
if (object_loaded_ && preview_object_.id_ >= 0) {
// Render preview object at center of canvas
int preview_x = 8 * 16; // Center horizontally (8 tiles * 16 pixels)
int preview_y = 8 * 16; // Center vertically
auto preview_result =
object_renderer_.RenderObject(preview_object_, preview_palette_);
if (preview_result.ok()) {
auto preview_bitmap = std::move(preview_result.value());
preview_bitmap.SetPalette(preview_palette_);
core::Renderer::Get().RenderBitmap(&preview_bitmap);
object_canvas_.DrawBitmap(preview_bitmap, preview_x, preview_y, 1.0f,
255);
}
}
object_canvas_.DrawOverlay();
EndChild();
@@ -930,7 +960,10 @@ void DungeonEditor::DrawObjectRenderer() {
if (object_loaded_) {
ImGui::Begin("Object Preview", &object_loaded_, 0);
ImGui::Text("Object rendered successfully using improved renderer!");
ImGui::Text("Object ID: 0x%02X", preview_object_.id_);
ImGui::Text("Position: (%d, %d)", preview_object_.x_, preview_object_.y_);
ImGui::Text("Size: 0x%02X", preview_object_.size_);
ImGui::Text("Layer: %d", static_cast<int>(preview_object_.layer_));
ImGui::End();
}
}
@@ -1165,7 +1198,8 @@ void DungeonEditor::DrawUsageGrid() {
Text("Floor1: %#02x", room.floor1);
Text("Floor2: %#02x", room.floor2);
Text("Message ID: %#04x", room.message_id_);
Text("Size: %#016llx", room_sizes_[row * squaresWide + col]);
Text("Size: %#016llx", static_cast<unsigned long long>(
room_sizes_[row * squaresWide + col]));
Text("Size Pointer: %#016llx",
room_size_pointers_[row * squaresWide + col]);
ImGui::EndTooltip();
@@ -1189,9 +1223,8 @@ void DungeonEditor::DrawObjectEditor() {
// Display current editing mode
auto mode = object_editor_->GetMode();
const char* mode_names[] = {
"Select", "Insert", "Delete", "Edit", "Layer", "Preview"
};
const char *mode_names[] = {"Select", "Insert", "Delete",
"Edit", "Layer", "Preview"};
ImGui::Text("Mode: %s", mode_names[static_cast<int>(mode)]);
// Mode selection
@@ -1236,7 +1269,7 @@ void DungeonEditor::DrawObjectEditor() {
// Object count and selection info
ImGui::Separator();
ImGui::Text("Objects: %zu", object_editor_->GetObjectCount());
auto selection = object_editor_->GetSelection();
if (!selection.selected_objects.empty()) {
ImGui::Text("Selected: %zu objects", selection.selected_objects.size());
@@ -1244,8 +1277,10 @@ void DungeonEditor::DrawObjectEditor() {
// Undo/Redo status
ImGui::Separator();
ImGui::Text("Undo: %s", object_editor_->CanUndo() ? "Available" : "Not Available");
ImGui::Text("Redo: %s", object_editor_->CanRedo() ? "Available" : "Not Available");
ImGui::Text("Undo: %s",
object_editor_->CanUndo() ? "Available" : "Not Available");
ImGui::Text("Redo: %s",
object_editor_->CanRedo() ? "Available" : "Not Available");
}
void DungeonEditor::DrawSpriteEditor() {
@@ -1260,18 +1295,18 @@ void DungeonEditor::DrawSpriteEditor() {
// Display current room sprites
auto current_room = dungeon_editor_system_->GetCurrentRoom();
auto sprites_result = dungeon_editor_system_->GetSpritesByRoom(current_room);
if (sprites_result.ok()) {
auto sprites = sprites_result.value();
ImGui::Text("Sprites in room %d: %zu", current_room, sprites.size());
for (const auto& sprite : sprites) {
ImGui::Text("ID: %d, Type: %d, Position: (%d, %d)",
sprite.sprite_id, static_cast<int>(sprite.type),
sprite.x, sprite.y);
for (const auto &sprite : sprites) {
ImGui::Text("ID: %d, Type: %d, Position: (%d, %d)", sprite.sprite_id,
static_cast<int>(sprite.type), sprite.x, sprite.y);
}
} else {
ImGui::Text("Error loading sprites: %s", sprites_result.status().message().data());
ImGui::Text("Error loading sprites: %s",
sprites_result.status().message().data());
}
// Sprite placement controls
@@ -1294,7 +1329,7 @@ void DungeonEditor::DrawSpriteEditor() {
sprite_data.x = new_sprite_x;
sprite_data.y = new_sprite_y;
sprite_data.layer = new_sprite_layer;
auto status = dungeon_editor_system_->AddSprite(sprite_data);
if (!status.ok()) {
ImGui::Text("Error adding sprite: %s", status.message().data());
@@ -1314,18 +1349,19 @@ void DungeonEditor::DrawItemEditor() {
// Display current room items
auto current_room = dungeon_editor_system_->GetCurrentRoom();
auto items_result = dungeon_editor_system_->GetItemsByRoom(current_room);
if (items_result.ok()) {
auto items = items_result.value();
ImGui::Text("Items in room %d: %zu", current_room, items.size());
for (const auto& item : items) {
ImGui::Text("ID: %d, Type: %d, Position: (%d, %d), Hidden: %s",
item.item_id, static_cast<int>(item.type),
item.x, item.y, item.is_hidden ? "Yes" : "No");
for (const auto &item : items) {
ImGui::Text("ID: %d, Type: %d, Position: (%d, %d), Hidden: %s",
item.item_id, static_cast<int>(item.type), item.x, item.y,
item.is_hidden ? "Yes" : "No");
}
} else {
ImGui::Text("Error loading items: %s", items_result.status().message().data());
ImGui::Text("Error loading items: %s",
items_result.status().message().data());
}
// Item placement controls
@@ -1349,7 +1385,7 @@ void DungeonEditor::DrawItemEditor() {
item_data.y = new_item_y;
item_data.room_id = current_room;
item_data.is_hidden = new_item_hidden;
auto status = dungeon_editor_system_->AddItem(item_data);
if (!status.ok()) {
ImGui::Text("Error adding item: %s", status.message().data());
@@ -1368,19 +1404,22 @@ void DungeonEditor::DrawEntranceEditor() {
// Display current room entrances
auto current_room = dungeon_editor_system_->GetCurrentRoom();
auto entrances_result = dungeon_editor_system_->GetEntrancesByRoom(current_room);
auto entrances_result =
dungeon_editor_system_->GetEntrancesByRoom(current_room);
if (entrances_result.ok()) {
auto entrances = entrances_result.value();
ImGui::Text("Entrances in room %d: %zu", current_room, entrances.size());
for (const auto& entrance : entrances) {
ImGui::Text("ID: %d, Type: %d, Target Room: %d, Target Position: (%d, %d)",
entrance.entrance_id, static_cast<int>(entrance.type),
entrance.target_room_id, entrance.target_x, entrance.target_y);
for (const auto &entrance : entrances) {
ImGui::Text(
"ID: %d, Type: %d, Target Room: %d, Target Position: (%d, %d)",
entrance.entrance_id, static_cast<int>(entrance.type),
entrance.target_room_id, entrance.target_x, entrance.target_y);
}
} else {
ImGui::Text("Error loading entrances: %s", entrances_result.status().message().data());
ImGui::Text("Error loading entrances: %s",
entrances_result.status().message().data());
}
// Entrance creation controls
@@ -1399,9 +1438,8 @@ void DungeonEditor::DrawEntranceEditor() {
ImGui::InputInt("Target Y", &target_y);
if (ImGui::Button("Connect Rooms")) {
auto status = dungeon_editor_system_->ConnectRooms(current_room, target_room_id,
source_x, source_y,
target_x, target_y);
auto status = dungeon_editor_system_->ConnectRooms(
current_room, target_room_id, source_x, source_y, target_x, target_y);
if (!status.ok()) {
ImGui::Text("Error connecting rooms: %s", status.message().data());
}
@@ -1420,17 +1458,19 @@ void DungeonEditor::DrawDoorEditor() {
// Display current room doors
auto current_room = dungeon_editor_system_->GetCurrentRoom();
auto doors_result = dungeon_editor_system_->GetDoorsByRoom(current_room);
if (doors_result.ok()) {
auto doors = doors_result.value();
ImGui::Text("Doors in room %d: %zu", current_room, doors.size());
for (const auto& door : doors) {
ImGui::Text("ID: %d, Position: (%d, %d), Direction: %d, Target Room: %d",
door.door_id, door.x, door.y, door.direction, door.target_room_id);
for (const auto &door : doors) {
ImGui::Text("ID: %d, Position: (%d, %d), Direction: %d, Target Room: %d",
door.door_id, door.x, door.y, door.direction,
door.target_room_id);
}
} else {
ImGui::Text("Error loading doors: %s", doors_result.status().message().data());
ImGui::Text("Error loading doors: %s",
doors_result.status().message().data());
}
// Door creation controls
@@ -1468,7 +1508,7 @@ void DungeonEditor::DrawDoorEditor() {
door_data.is_locked = door_locked;
door_data.requires_key = door_requires_key;
door_data.key_type = door_key_type;
auto status = dungeon_editor_system_->AddDoor(door_data);
if (!status.ok()) {
ImGui::Text("Error adding door: %s", status.message().data());
@@ -1488,19 +1528,20 @@ void DungeonEditor::DrawChestEditor() {
// Display current room chests
auto current_room = dungeon_editor_system_->GetCurrentRoom();
auto chests_result = dungeon_editor_system_->GetChestsByRoom(current_room);
if (chests_result.ok()) {
auto chests = chests_result.value();
ImGui::Text("Chests in room %d: %zu", current_room, chests.size());
for (const auto& chest : chests) {
ImGui::Text("ID: %d, Position: (%d, %d), Big: %s, Item: %d, Quantity: %d",
chest.chest_id, chest.x, chest.y,
chest.is_big_chest ? "Yes" : "No",
chest.item_id, chest.item_quantity);
for (const auto &chest : chests) {
ImGui::Text("ID: %d, Position: (%d, %d), Big: %s, Item: %d, Quantity: %d",
chest.chest_id, chest.x, chest.y,
chest.is_big_chest ? "Yes" : "No", chest.item_id,
chest.item_quantity);
}
} else {
ImGui::Text("Error loading chests: %s", chests_result.status().message().data());
ImGui::Text("Error loading chests: %s",
chests_result.status().message().data());
}
// Chest creation controls
@@ -1526,7 +1567,7 @@ void DungeonEditor::DrawChestEditor() {
chest_data.is_big_chest = chest_big;
chest_data.item_id = chest_item_id;
chest_data.item_quantity = chest_item_quantity;
auto status = dungeon_editor_system_->AddChest(chest_data);
if (!status.ok()) {
ImGui::Text("Error adding chest: %s", status.message().data());
@@ -1542,49 +1583,48 @@ void DungeonEditor::DrawIntegratedEditingPanels() {
// Create a tabbed interface for different editing modes
if (ImGui::BeginTabBar("##EditingPanels")) {
// Object Editor Tab
if (ImGui::BeginTabItem("Objects")) {
DrawCompactObjectEditor();
ImGui::EndTabItem();
}
// Sprite Editor Tab
if (ImGui::BeginTabItem("Sprites")) {
DrawCompactSpriteEditor();
ImGui::EndTabItem();
}
// Item Editor Tab
if (ImGui::BeginTabItem("Items")) {
DrawCompactItemEditor();
ImGui::EndTabItem();
}
// Entrance Editor Tab
if (ImGui::BeginTabItem("Entrances")) {
DrawCompactEntranceEditor();
ImGui::EndTabItem();
}
// Door Editor Tab
if (ImGui::BeginTabItem("Doors")) {
DrawCompactDoorEditor();
ImGui::EndTabItem();
}
// Chest Editor Tab
if (ImGui::BeginTabItem("Chests")) {
DrawCompactChestEditor();
ImGui::EndTabItem();
}
// Properties Tab
if (ImGui::BeginTabItem("Properties")) {
DrawCompactPropertiesEditor();
ImGui::EndTabItem();
}
ImGui::EndTabBar();
}
}
@@ -1595,17 +1635,19 @@ void DungeonEditor::DrawCompactObjectEditor() {
// Display current editing mode
auto mode = object_editor_->GetMode();
const char* mode_names[] = {
"Select", "Insert", "Delete", "Edit", "Layer", "Preview"
};
const char *mode_names[] = {"Select", "Insert", "Delete",
"Edit", "Layer", "Preview"};
ImGui::Text("Mode: %s", mode_names[static_cast<int>(mode)]);
// Compact mode selection
if (ImGui::Button("Select")) object_editor_->SetMode(zelda3::DungeonObjectEditor::Mode::kSelect);
if (ImGui::Button("Select"))
object_editor_->SetMode(zelda3::DungeonObjectEditor::Mode::kSelect);
ImGui::SameLine();
if (ImGui::Button("Insert")) object_editor_->SetMode(zelda3::DungeonObjectEditor::Mode::kInsert);
if (ImGui::Button("Insert"))
object_editor_->SetMode(zelda3::DungeonObjectEditor::Mode::kInsert);
ImGui::SameLine();
if (ImGui::Button("Edit")) object_editor_->SetMode(zelda3::DungeonObjectEditor::Mode::kEdit);
if (ImGui::Button("Edit"))
object_editor_->SetMode(zelda3::DungeonObjectEditor::Mode::kEdit);
// Layer and object type selection
int current_layer = object_editor_->GetCurrentLayer();
@@ -1633,7 +1675,7 @@ void DungeonEditor::DrawCompactObjectEditor() {
// Object count and selection info
ImGui::Separator();
ImGui::Text("Objects: %zu", object_editor_->GetObjectCount());
auto selection = object_editor_->GetSelection();
if (!selection.selected_objects.empty()) {
ImGui::Text("Selected: %zu", selection.selected_objects.size());
@@ -1657,18 +1699,17 @@ void DungeonEditor::DrawCompactSpriteEditor() {
// Display current room sprites
auto current_room = dungeon_editor_system_->GetCurrentRoom();
auto sprites_result = dungeon_editor_system_->GetSpritesByRoom(current_room);
if (sprites_result.ok()) {
auto sprites = sprites_result.value();
ImGui::Text("Sprites in room %d: %zu", current_room, sprites.size());
// Show first few sprites in compact format
int display_count = std::min(3, static_cast<int>(sprites.size()));
for (int i = 0; i < display_count; ++i) {
const auto& sprite = sprites[i];
ImGui::Text("ID:%d Type:%d (%d,%d)",
sprite.sprite_id, static_cast<int>(sprite.type),
sprite.x, sprite.y);
const auto &sprite = sprites[i];
ImGui::Text("ID:%d Type:%d (%d,%d)", sprite.sprite_id,
static_cast<int>(sprite.type), sprite.x, sprite.y);
}
if (sprites.size() > 3) {
ImGui::Text("... and %zu more", sprites.size() - 3);
@@ -1680,15 +1721,15 @@ void DungeonEditor::DrawCompactSpriteEditor() {
// Quick sprite placement
ImGui::Separator();
ImGui::Text("Quick Add Sprite");
static int new_sprite_id = 0;
static int new_sprite_x = 0;
static int new_sprite_y = 0;
ImGui::InputInt("ID", &new_sprite_id);
ImGui::InputInt("X", &new_sprite_x);
ImGui::InputInt("Y", &new_sprite_y);
if (ImGui::Button("Add Sprite")) {
zelda3::DungeonEditorSystem::SpriteData sprite_data;
sprite_data.sprite_id = new_sprite_id;
@@ -1696,7 +1737,7 @@ void DungeonEditor::DrawCompactSpriteEditor() {
sprite_data.x = new_sprite_x;
sprite_data.y = new_sprite_y;
sprite_data.layer = 0;
auto status = dungeon_editor_system_->AddSprite(sprite_data);
if (!status.ok()) {
ImGui::Text("Error adding sprite");
@@ -1711,18 +1752,17 @@ void DungeonEditor::DrawCompactItemEditor() {
// Display current room items
auto current_room = dungeon_editor_system_->GetCurrentRoom();
auto items_result = dungeon_editor_system_->GetItemsByRoom(current_room);
if (items_result.ok()) {
auto items = items_result.value();
ImGui::Text("Items in room %d: %zu", current_room, items.size());
// Show first few items in compact format
int display_count = std::min(3, static_cast<int>(items.size()));
for (int i = 0; i < display_count; ++i) {
const auto& item = items[i];
ImGui::Text("ID:%d Type:%d (%d,%d)",
item.item_id, static_cast<int>(item.type),
item.x, item.y);
const auto &item = items[i];
ImGui::Text("ID:%d Type:%d (%d,%d)", item.item_id,
static_cast<int>(item.type), item.x, item.y);
}
if (items.size() > 3) {
ImGui::Text("... and %zu more", items.size() - 3);
@@ -1734,15 +1774,15 @@ void DungeonEditor::DrawCompactItemEditor() {
// Quick item placement
ImGui::Separator();
ImGui::Text("Quick Add Item");
static int new_item_id = 0;
static int new_item_x = 0;
static int new_item_y = 0;
ImGui::InputInt("ID", &new_item_id);
ImGui::InputInt("X", &new_item_x);
ImGui::InputInt("Y", &new_item_y);
if (ImGui::Button("Add Item")) {
zelda3::DungeonEditorSystem::ItemData item_data;
item_data.item_id = new_item_id;
@@ -1751,7 +1791,7 @@ void DungeonEditor::DrawCompactItemEditor() {
item_data.y = new_item_y;
item_data.room_id = current_room;
item_data.is_hidden = false;
auto status = dungeon_editor_system_->AddItem(item_data);
if (!status.ok()) {
ImGui::Text("Error adding item");
@@ -1765,16 +1805,17 @@ void DungeonEditor::DrawCompactEntranceEditor() {
// Display current room entrances
auto current_room = dungeon_editor_system_->GetCurrentRoom();
auto entrances_result = dungeon_editor_system_->GetEntrancesByRoom(current_room);
auto entrances_result =
dungeon_editor_system_->GetEntrancesByRoom(current_room);
if (entrances_result.ok()) {
auto entrances = entrances_result.value();
ImGui::Text("Entrances: %zu", entrances.size());
for (const auto& entrance : entrances) {
ImGui::Text("ID:%d -> Room:%d (%d,%d)",
entrance.entrance_id, entrance.target_room_id,
entrance.target_x, entrance.target_y);
for (const auto &entrance : entrances) {
ImGui::Text("ID:%d -> Room:%d (%d,%d)", entrance.entrance_id,
entrance.target_room_id, entrance.target_x,
entrance.target_y);
}
} else {
ImGui::Text("Error loading entrances");
@@ -1783,23 +1824,22 @@ void DungeonEditor::DrawCompactEntranceEditor() {
// Quick room connection
ImGui::Separator();
ImGui::Text("Connect Rooms");
static int target_room_id = 0;
static int source_x = 0;
static int source_y = 0;
static int target_x = 0;
static int target_y = 0;
ImGui::InputInt("Target Room", &target_room_id);
ImGui::InputInt("Source X", &source_x);
ImGui::InputInt("Source Y", &source_y);
ImGui::InputInt("Target X", &target_x);
ImGui::InputInt("Target Y", &target_y);
if (ImGui::Button("Connect")) {
auto status = dungeon_editor_system_->ConnectRooms(current_room, target_room_id,
source_x, source_y,
target_x, target_y);
auto status = dungeon_editor_system_->ConnectRooms(
current_room, target_room_id, source_x, source_y, target_x, target_y);
if (!status.ok()) {
ImGui::Text("Error connecting rooms");
}
@@ -1813,14 +1853,14 @@ void DungeonEditor::DrawCompactDoorEditor() {
// Display current room doors
auto current_room = dungeon_editor_system_->GetCurrentRoom();
auto doors_result = dungeon_editor_system_->GetDoorsByRoom(current_room);
if (doors_result.ok()) {
auto doors = doors_result.value();
ImGui::Text("Doors: %zu", doors.size());
for (const auto& door : doors) {
ImGui::Text("ID:%d (%d,%d) -> Room:%d",
door.door_id, door.x, door.y, door.target_room_id);
for (const auto &door : doors) {
ImGui::Text("ID:%d (%d,%d) -> Room:%d", door.door_id, door.x, door.y,
door.target_room_id);
}
} else {
ImGui::Text("Error loading doors");
@@ -1829,17 +1869,17 @@ void DungeonEditor::DrawCompactDoorEditor() {
// Quick door creation
ImGui::Separator();
ImGui::Text("Add Door");
static int door_x = 0;
static int door_y = 0;
static int door_direction = 0;
static int door_target_room = 0;
ImGui::InputInt("X", &door_x);
ImGui::InputInt("Y", &door_y);
ImGui::SliderInt("Dir", &door_direction, 0, 3);
ImGui::InputInt("Target", &door_target_room);
if (ImGui::Button("Add Door")) {
zelda3::DungeonEditorSystem::DoorData door_data;
door_data.room_id = current_room;
@@ -1852,7 +1892,7 @@ void DungeonEditor::DrawCompactDoorEditor() {
door_data.is_locked = false;
door_data.requires_key = false;
door_data.key_type = 0;
auto status = dungeon_editor_system_->AddDoor(door_data);
if (!status.ok()) {
ImGui::Text("Error adding door");
@@ -1867,14 +1907,14 @@ void DungeonEditor::DrawCompactChestEditor() {
// Display current room chests
auto current_room = dungeon_editor_system_->GetCurrentRoom();
auto chests_result = dungeon_editor_system_->GetChestsByRoom(current_room);
if (chests_result.ok()) {
auto chests = chests_result.value();
ImGui::Text("Chests: %zu", chests.size());
for (const auto& chest : chests) {
ImGui::Text("ID:%d (%d,%d) Item:%d",
chest.chest_id, chest.x, chest.y, chest.item_id);
for (const auto &chest : chests) {
ImGui::Text("ID:%d (%d,%d) Item:%d", chest.chest_id, chest.x, chest.y,
chest.item_id);
}
} else {
ImGui::Text("Error loading chests");
@@ -1883,17 +1923,17 @@ void DungeonEditor::DrawCompactChestEditor() {
// Quick chest creation
ImGui::Separator();
ImGui::Text("Add Chest");
static int chest_x = 0;
static int chest_y = 0;
static int chest_item_id = 0;
static bool chest_big = false;
ImGui::InputInt("X", &chest_x);
ImGui::InputInt("Y", &chest_y);
ImGui::InputInt("Item ID", &chest_item_id);
ImGui::Checkbox("Big", &chest_big);
if (ImGui::Button("Add Chest")) {
zelda3::DungeonEditorSystem::ChestData chest_data;
chest_data.room_id = current_room;
@@ -1902,7 +1942,7 @@ void DungeonEditor::DrawCompactChestEditor() {
chest_data.is_big_chest = chest_big;
chest_data.item_id = chest_item_id;
chest_data.item_quantity = 1;
auto status = dungeon_editor_system_->AddChest(chest_data);
if (!status.ok()) {
ImGui::Text("Error adding chest");
@@ -1915,11 +1955,12 @@ void DungeonEditor::DrawCompactPropertiesEditor() {
ImGui::Separator();
auto current_room = dungeon_editor_system_->GetCurrentRoom();
auto properties_result = dungeon_editor_system_->GetRoomProperties(current_room);
auto properties_result =
dungeon_editor_system_->GetRoomProperties(current_room);
if (properties_result.ok()) {
auto properties = properties_result.value();
static char room_name[128] = {0};
static int dungeon_id = 0;
static int floor_level = 0;
@@ -1951,8 +1992,9 @@ void DungeonEditor::DrawCompactPropertiesEditor() {
new_properties.is_boss_room = is_boss_room;
new_properties.is_save_room = is_save_room;
new_properties.music_id = music_id;
auto status = dungeon_editor_system_->SetRoomProperties(current_room, new_properties);
auto status = dungeon_editor_system_->SetRoomProperties(current_room,
new_properties);
if (!status.ok()) {
ImGui::Text("Error saving properties");
}
@@ -1964,7 +2006,7 @@ void DungeonEditor::DrawCompactPropertiesEditor() {
// Dungeon settings summary
ImGui::Separator();
ImGui::Text("Dungeon Settings");
auto dungeon_settings_result = dungeon_editor_system_->GetDungeonSettings();
if (dungeon_settings_result.ok()) {
auto settings = dungeon_settings_result.value();
@@ -1975,4 +2017,27 @@ void DungeonEditor::DrawCompactPropertiesEditor() {
}
}
// Coordinate conversion helper functions
std::pair<int, int> DungeonEditor::RoomToCanvasCoordinates(int room_x,
int room_y) const {
// Convert room coordinates (16x16 tile units) to canvas coordinates (pixels)
return {room_x * 16, room_y * 16};
}
std::pair<int, int> DungeonEditor::CanvasToRoomCoordinates(int canvas_x,
int canvas_y) const {
// Convert canvas coordinates (pixels) to room coordinates (16x16 tile units)
return {canvas_x / 16, canvas_y / 16};
}
bool DungeonEditor::IsWithinCanvasBounds(int canvas_x, int canvas_y,
int margin) const {
// Check if coordinates are within canvas bounds with optional margin
auto canvas_width = canvas_.width();
auto canvas_height = canvas_.height();
return (canvas_x >= -margin && canvas_y >= -margin &&
canvas_x <= canvas_width + margin &&
canvas_y <= canvas_height + margin);
}
} // namespace yaze::editor

View File

@@ -42,7 +42,7 @@ constexpr ImGuiTableFlags kDungeonTableFlags =
class DungeonEditor : public Editor {
public:
explicit DungeonEditor(Rom* rom = nullptr)
: rom_(rom), object_renderer_(rom) {
: rom_(rom), object_renderer_(rom), preview_object_(0, 0, 0, 0, 0) {
type_ = EditorType::kDungeon;
// Initialize the new dungeon editor system
if (rom) {
@@ -111,6 +111,11 @@ class DungeonEditor : public Editor {
int canvas_y);
void RenderLayoutObjects(const zelda3::RoomLayout& layout,
const gfx::SnesPalette& palette);
// Coordinate conversion helpers
std::pair<int, int> RoomToCanvasCoordinates(int room_x, int room_y) const;
std::pair<int, int> CanvasToRoomCoordinates(int canvas_x, int canvas_y) const;
bool IsWithinCanvasBounds(int canvas_x, int canvas_y, int margin = 32) const;
// Object rendering cache to avoid re-rendering the same objects
struct ObjectRenderCache {
@@ -123,6 +128,10 @@ class DungeonEditor : public Editor {
std::vector<ObjectRenderCache> object_render_cache_;
uint64_t last_palette_hash_ = 0;
// Object preview system
zelda3::RoomObject preview_object_;
gfx::SnesPalette preview_palette_;
void CalculateUsageStats();
void DrawUsageStats();