move zelda3 directory to src from app

This commit is contained in:
scawful
2025-10-12 22:01:52 -04:00
parent 9c89ad5843
commit dc9b9d5d10
108 changed files with 2071 additions and 2072 deletions

View File

@@ -13,7 +13,7 @@
#include "util/platform_paths.h"
#include "app/gui/icons.h"
#include "util/log.h"
#include "app/zelda3/zelda3_labels.h"
#include "zelda3/zelda3_labels.h"
#include "imgui/imgui.h"
#include "yaze_config.h"

View File

@@ -5,8 +5,8 @@
#include "app/gfx/snes_palette.h"
#include "app/gui/input.h"
#include "app/rom.h"
#include "app/zelda3/dungeon/room.h"
#include "app/zelda3/sprite/sprite.h"
#include "zelda3/dungeon/room.h"
#include "zelda3/sprite/sprite.h"
#include "imgui/imgui.h"
#include "util/log.h"

View File

@@ -5,7 +5,7 @@
#include "app/gui/canvas.h"
#include "app/rom.h"
#include "app/zelda3/dungeon/room.h"
#include "zelda3/dungeon/room.h"
#include "app/gfx/snes_palette.h"
#include "dungeon_object_interaction.h"
#include "imgui/imgui.h"

View File

@@ -6,7 +6,7 @@
#include "absl/strings/str_format.h"
#include "app/gfx/arena.h"
#include "app/gfx/snes_palette.h"
#include "app/zelda3/dungeon/room.h"
#include "zelda3/dungeon/room.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "imgui/imgui.h"

View File

@@ -15,8 +15,8 @@
#include "dungeon_room_loader.h"
#include "object_editor_card.h"
#include "app/gui/editor_card_manager.h"
#include "app/zelda3/dungeon/room.h"
#include "app/zelda3/dungeon/room_entrance.h"
#include "zelda3/dungeon/room.h"
#include "zelda3/dungeon/room_entrance.h"
#include "app/gui/editor_layout.h"
#include "app/gui/widgets/dungeon_object_emulator_preview.h"
#include "app/gui/widgets/palette_editor_widget.h"

View File

@@ -6,8 +6,8 @@
#include "imgui/imgui.h"
#include "app/gui/canvas.h"
#include "app/zelda3/dungeon/room.h"
#include "app/zelda3/dungeon/room_object.h"
#include "zelda3/dungeon/room.h"
#include "zelda3/dungeon/room_object.h"
namespace yaze {
namespace editor {

View File

@@ -10,9 +10,9 @@
#include "app/gui/canvas.h"
#include "app/gui/modules/asset_browser.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 "zelda3/dungeon/room.h"
#include "zelda3/dungeon/dungeon_editor_system.h"
#include "zelda3/dungeon/dungeon_object_editor.h"
#include "imgui/imgui.h"
namespace yaze::editor {

View File

@@ -4,8 +4,8 @@
#include "app/gui/canvas.h"
#include "app/rom.h"
// object_renderer.h removed - using ObjectDrawer for production rendering
#include "app/zelda3/dungeon/dungeon_object_editor.h"
#include "app/zelda3/dungeon/dungeon_editor_system.h"
#include "zelda3/dungeon/dungeon_object_editor.h"
#include "zelda3/dungeon/dungeon_editor_system.h"
#include "app/gfx/snes_palette.h"
#include "imgui/imgui.h"

View File

@@ -8,7 +8,7 @@
#include "app/gfx/performance/performance_profiler.h"
#include "app/gfx/snes_palette.h"
#include "app/zelda3/dungeon/room.h"
#include "zelda3/dungeon/room.h"
#include "util/log.h"
namespace yaze::editor {

View File

@@ -6,8 +6,8 @@
#include "absl/status/status.h"
#include "app/rom.h"
#include "app/zelda3/dungeon/room.h"
#include "app/zelda3/dungeon/room_entrance.h"
#include "zelda3/dungeon/room.h"
#include "zelda3/dungeon/room_entrance.h"
namespace yaze {
namespace editor {

View File

@@ -1,8 +1,8 @@
#include "dungeon_room_selector.h"
#include "app/gui/input.h"
#include "app/zelda3/dungeon/room.h"
#include "app/zelda3/dungeon/room_entrance.h"
#include "zelda3/dungeon/room.h"
#include "zelda3/dungeon/room_entrance.h"
#include "imgui/imgui.h"
#include "util/hex.h"

View File

@@ -4,8 +4,8 @@
#include <functional>
#include "imgui/imgui.h"
#include "app/rom.h"
#include "app/zelda3/dungeon/room_entrance.h"
#include "app/zelda3/dungeon/room.h"
#include "zelda3/dungeon/room_entrance.h"
#include "zelda3/dungeon/room.h"
namespace yaze {
namespace editor {

View File

@@ -2,7 +2,7 @@
#define YAZE_APP_EDITOR_DUNGEON_DUNGEON_USAGE_TRACKER_H
#include "absl/container/flat_hash_map.h"
#include "app/zelda3/dungeon/room.h"
#include "zelda3/dungeon/room.h"
namespace yaze {
namespace editor {

View File

@@ -11,7 +11,7 @@
#include "app/gui/editor_layout.h"
#include "app/gui/widgets/dungeon_object_emulator_preview.h"
#include "app/rom.h"
#include "app/zelda3/dungeon/room_object.h"
#include "zelda3/dungeon/room_object.h"
namespace yaze {
namespace editor {

View File

@@ -37,7 +37,7 @@
#include "app/gui/theme_manager.h"
#include "app/rom.h"
#include "app/test/test_manager.h"
#include "app/zelda3/overworld/overworld_map.h"
#include "zelda3/overworld/overworld_map.h"
#ifdef YAZE_ENABLE_TESTING
#include "app/test/e2e_test_suite.h"
#include "app/test/integrated_test_suite.h"

View File

@@ -13,7 +13,7 @@
#include "app/gui/editor_layout.h"
#include "app/gui/modules/asset_browser.h"
#include "app/rom.h"
#include "app/zelda3/overworld/overworld.h"
#include "zelda3/overworld/overworld.h"
#include "imgui/imgui.h"
#include "imgui_memory_editor.h"

View File

@@ -11,8 +11,8 @@
#include "app/gfx/tilemap.h"
#include "app/gui/canvas.h"
#include "app/rom.h"
#include "app/zelda3/screen/dungeon_map.h"
#include "app/zelda3/screen/inventory.h"
#include "zelda3/screen/dungeon_map.h"
#include "zelda3/screen/inventory.h"
#include "app/gui/editor_layout.h"
#include "imgui/imgui.h"

View File

@@ -7,7 +7,7 @@
#include "app/gui/editor_card_manager.h"
#include "app/gui/editor_layout.h"
#include "app/rom.h"
#include "app/zelda3/music/tracker.h"
#include "zelda3/music/tracker.h"
#include "imgui/imgui.h"
namespace yaze {

View File

@@ -1,11 +1,11 @@
#ifndef YAZE_APP_EDITOR_OVERWORLD_ENTITY_H
#define YAZE_APP_EDITOR_OVERWORLD_ENTITY_H
#include "app/zelda3/common.h"
#include "app/zelda3/overworld/overworld_entrance.h"
#include "app/zelda3/overworld/overworld_exit.h"
#include "app/zelda3/overworld/overworld_item.h"
#include "app/zelda3/sprite/sprite.h"
#include "zelda3/common.h"
#include "zelda3/overworld/overworld_entrance.h"
#include "zelda3/overworld/overworld_exit.h"
#include "zelda3/overworld/overworld_item.h"
#include "zelda3/sprite/sprite.h"
#include "imgui/imgui.h"
namespace yaze {

View File

@@ -7,7 +7,7 @@
#include "app/gui/color.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/zelda3/overworld/overworld_map.h"
#include "zelda3/overworld/overworld_map.h"
#include "imgui/imgui.h"
namespace yaze {

View File

@@ -3,7 +3,7 @@
#include <functional>
#include "app/zelda3/overworld/overworld.h"
#include "zelda3/overworld/overworld.h"
#include "app/rom.h"
#include "app/gui/canvas.h"

View File

@@ -31,9 +31,9 @@
#include "app/gui/style.h"
#include "app/gui/ui_helpers.h"
#include "app/rom.h"
#include "app/zelda3/common.h"
#include "app/zelda3/overworld/overworld.h"
#include "app/zelda3/overworld/overworld_map.h"
#include "zelda3/common.h"
#include "zelda3/overworld/overworld.h"
#include "zelda3/overworld/overworld_map.h"
#include "imgui/imgui.h"
#include "imgui_memory_editor.h"
#include "util/file_util.h"

View File

@@ -1,382 +1,382 @@
#ifndef YAZE_APP_EDITOR_OVERWORLDEDITOR_H
#define YAZE_APP_EDITOR_OVERWORLDEDITOR_H
#include "absl/status/status.h"
#include "app/editor/editor.h"
#include "app/editor/graphics/gfx_group_editor.h"
#include "app/editor/palette/palette_editor.h"
#include "app/gui/editor_card_manager.h"
#include "app/editor/overworld/tile16_editor.h"
#include "app/editor/overworld/map_properties.h"
#include "app/editor/overworld/overworld_entity_renderer.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/tilemap.h"
#include "app/gui/canvas.h"
#include "app/gui/widgets/tile_selector_widget.h"
#include "app/gui/input.h"
#include "app/rom.h"
#include "app/zelda3/overworld/overworld.h"
#include "imgui/imgui.h"
#include <mutex>
namespace yaze {
namespace editor {
constexpr unsigned int k4BPP = 4;
constexpr unsigned int kByteSize = 3;
constexpr unsigned int kMessageIdSize = 5;
constexpr unsigned int kNumSheetsToLoad = 223;
constexpr unsigned int kOverworldMapSize = 0x200;
constexpr ImVec2 kOverworldCanvasSize(kOverworldMapSize * 8,
kOverworldMapSize * 8);
constexpr ImVec2 kCurrentGfxCanvasSize(0x100 + 1, 0x10 * 0x40 + 1);
constexpr ImVec2 kBlocksetCanvasSize(0x100 + 1, 0x4000 + 1);
constexpr ImVec2 kGraphicsBinCanvasSize(0x100 + 1, kNumSheetsToLoad * 0x40 + 1);
constexpr ImGuiTableFlags kOWMapFlags =
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp;
constexpr absl::string_view kWorldList =
"Light World\0Dark World\0Extra World\0";
constexpr absl::string_view kGamePartComboString = "Part 0\0Part 1\0Part 2\0";
constexpr absl::string_view kOWMapTable = "#MapSettingsTable";
/**
* @class OverworldEditor
* @brief Manipulates the Overworld and OverworldMap data in a Rom.
*
* The `OverworldEditor` class is responsible for managing the editing and
* manipulation of the overworld in a game. The user can drag and drop tiles,
* modify OverworldEntrance, OverworldExit, Sprite, and OverworldItem
* as well as change the gfx and palettes used in each overworld map.
*
* The Overworld itself is a series of bitmap images which exist inside each
* OverworldMap object. The drawing of the overworld is done using the Canvas
* class in conjunction with these underlying Bitmap objects.
*
* Provides access to the GfxGroupEditor and Tile16Editor through popup windows.
*
*/
class OverworldEditor : public Editor, public gfx::GfxContext {
public:
explicit OverworldEditor(Rom* rom) : rom_(rom) {
type_ = EditorType::kOverworld;
gfx_group_editor_.set_rom(rom);
// MapPropertiesSystem will be initialized after maps_bmp_ and canvas are ready
}
void Initialize(gfx::IRenderer* renderer, Rom* rom);
void Initialize() override;
absl::Status Load() override;
absl::Status Update() final;
absl::Status Undo() override { return absl::UnimplementedError("Undo"); }
absl::Status Redo() override { return absl::UnimplementedError("Redo"); }
absl::Status Cut() override { return absl::UnimplementedError("Cut"); }
absl::Status Copy() override;
absl::Status Paste() override;
absl::Status Find() override { return absl::UnimplementedError("Find"); }
absl::Status Save() override;
absl::Status Clear() override;
zelda3::Overworld& overworld() { return overworld_; }
/**
* @brief Apply ZSCustomOverworld ASM patch to upgrade ROM version
*/
absl::Status ApplyZSCustomOverworldASM(int target_version);
/**
* @brief Update ROM version markers and feature flags after ASM patching
*/
absl::Status UpdateROMVersionMarkers(int target_version);
int jump_to_tab() { return jump_to_tab_; }
int jump_to_tab_ = -1;
// ROM state methods (from Editor base class)
bool IsRomLoaded() const override { return rom_ && rom_->is_loaded(); }
std::string GetRomStatus() const override {
if (!rom_) return "No ROM loaded";
if (!rom_->is_loaded()) return "ROM failed to load";
return absl::StrFormat("ROM loaded: %s", rom_->title());
}
// Jump-to functionality
void set_current_map(int map_id) {
if (map_id >= 0 && map_id < zelda3::kNumOverworldMaps) {
current_map_ = map_id;
current_world_ = map_id / 0x40; // Calculate which world the map belongs to
}
}
/**
* @brief Load the Bitmap objects for each OverworldMap.
*
* Calls the Overworld class to load the image data and palettes from the Rom,
* then renders the area graphics and tile16 blockset Bitmap objects before
* assembling the OverworldMap Bitmap objects.
*/
absl::Status LoadGraphics();
private:
void DrawFullscreenCanvas();
void DrawToolset();
void RefreshChildMap(int map_index);
void RefreshOverworldMap();
void RefreshOverworldMapOnDemand(int map_index);
void RefreshChildMapOnDemand(int map_index);
void RefreshMultiAreaMapsSafely(int map_index, zelda3::OverworldMap* map);
absl::Status RefreshMapPalette();
void RefreshMapProperties();
absl::Status RefreshTile16Blockset();
void ForceRefreshGraphics(int map_index);
void RefreshSiblingMapGraphics(int map_index, bool include_self = false);
void DrawOverworldMaps();
void DrawOverworldEdits();
void RenderUpdatedMapBitmap(const ImVec2& click_position,
const std::vector<uint8_t>& tile_data);
/**
* @brief Check for changes to the overworld map.
*
* This function either draws the tile painter with the current tile16 or
* group of tile16 data with ow_map_canvas_ and DrawOverworldEdits or it
* checks for left mouse button click/drag to select a tile16 or group of
* tile16 data from the overworld map canvas. Similar to ZScream selection.
*/
void CheckForOverworldEdits();
/**
* @brief Draw and create the tile16 IDs that are currently selected.
*/
void CheckForSelectRectangle();
// Selected tile IDs for rectangle operations (moved from local static)
std::vector<int> selected_tile16_ids_;
/**
* @brief Check for changes to the overworld map. Calls RefreshOverworldMap
* and RefreshTile16Blockset on the current map if it is modified and is
* actively being edited.
*/
absl::Status CheckForCurrentMap();
void CheckForMousePan();
void DrawOverworldCanvas();
absl::Status DrawTile16Selector();
void DrawTile8Selector();
absl::Status DrawAreaGraphics();
void UpdateBlocksetSelectorState();
absl::Status LoadSpriteGraphics();
/**
* @brief Create textures for deferred map bitmaps on demand
*
* This method should be called periodically to create textures for maps
* that are needed but haven't had their textures created yet. This allows
* for smooth loading without blocking the main thread during ROM loading.
*/
void ProcessDeferredTextures();
/**
* @brief Ensure a specific map has its texture created
*
* Call this when a map becomes visible or is about to be rendered.
* It will create the texture if it doesn't exist yet.
*/
void EnsureMapTexture(int map_index);
void DrawOverworldProperties();
void HandleMapInteraction();
// SetupOverworldCanvasContextMenu removed (Phase 3B) - now handled by MapPropertiesSystem
// Canvas pan/zoom helpers (Overworld Refactoring)
void HandleOverworldPan();
void HandleOverworldZoom();
void ResetOverworldView();
void CenterOverworldView();
// Canvas Automation API integration (Phase 4)
void SetupCanvasAutomation();
gui::Canvas* GetOverworldCanvas() { return &ow_map_canvas_; }
// Tile operations for automation callbacks
bool AutomationSetTile(int x, int y, int tile_id);
int AutomationGetTile(int x, int y);
/**
* @brief Scroll the blockset canvas to show the current selected tile16
*/
void ScrollBlocksetCanvasToCurrentTile();
// Scratch space canvas methods
absl::Status DrawScratchSpace();
absl::Status SaveCurrentSelectionToScratch(int slot);
absl::Status LoadScratchToSelection(int slot);
absl::Status ClearScratchSpace(int slot);
void DrawScratchSpaceEdits();
void DrawScratchSpacePattern();
void DrawScratchSpaceSelection();
void UpdateScratchBitmapTile(int tile_x, int tile_y, int tile_id, int slot = -1);
absl::Status UpdateUsageStats();
void DrawUsageGrid();
void DrawDebugWindow();
enum class EditingMode {
MOUSE, // Navigation, selection, entity management via context menu
DRAW_TILE // Tile painting mode
};
EditingMode current_mode = EditingMode::DRAW_TILE;
EditingMode previous_mode = EditingMode::DRAW_TILE;
// Entity editing state (managed via context menu now)
enum class EntityEditMode {
NONE,
ENTRANCES,
EXITS,
ITEMS,
SPRITES,
TRANSPORTS,
MUSIC
};
EntityEditMode entity_edit_mode_ = EntityEditMode::NONE;
enum OverworldProperty {
LW_AREA_GFX,
DW_AREA_GFX,
LW_AREA_PAL,
DW_AREA_PAL,
LW_SPR_GFX_PART1,
LW_SPR_GFX_PART2,
DW_SPR_GFX_PART1,
DW_SPR_GFX_PART2,
LW_SPR_PAL_PART1,
LW_SPR_PAL_PART2,
DW_SPR_PAL_PART1,
DW_SPR_PAL_PART2,
};
int current_world_ = 0;
int current_map_ = 0;
int current_parent_ = 0;
int current_entrance_id_ = 0;
int current_exit_id_ = 0;
int current_item_id_ = 0;
int current_sprite_id_ = 0;
int current_blockset_ = 0;
int game_state_ = 1;
int current_tile16_ = 0;
int selected_entrance_ = 0;
int selected_usage_map_ = 0xFFFF;
bool all_gfx_loaded_ = false;
bool map_blockset_loaded_ = false;
bool selected_tile_loaded_ = false;
bool show_tile16_editor_ = false;
bool show_gfx_group_editor_ = false;
bool show_properties_editor_ = false;
bool overworld_canvas_fullscreen_ = false;
bool middle_mouse_dragging_ = false;
bool is_dragging_entity_ = false;
bool dragged_entity_free_movement_ = false;
bool current_map_lock_ = false;
bool show_custom_bg_color_editor_ = false;
bool show_overlay_editor_ = false;
bool use_area_specific_bg_color_ = false;
bool show_map_properties_panel_ = false;
bool show_overlay_preview_ = false;
// Card visibility states - Start hidden to prevent crash
bool show_overworld_canvas_ = true;
bool show_tile16_selector_ = false;
bool show_tile8_selector_ = false;
bool show_area_gfx_ = false;
bool show_scratch_ = false;
bool show_gfx_groups_ = false;
bool show_usage_stats_ = false;
bool show_v3_settings_ = false;
// Map properties system for UI organization
std::unique_ptr<MapPropertiesSystem> map_properties_system_;
std::unique_ptr<OverworldEntityRenderer> entity_renderer_;
// Scratch space for large layouts
// Scratch space canvas for tile16 drawing (like a mini overworld)
struct ScratchSpaceSlot {
gfx::Bitmap scratch_bitmap;
std::array<std::array<int, 32>, 32> tile_data; // 32x32 grid of tile16 IDs
bool in_use = false;
std::string name = "Empty";
int width = 16; // Default 16x16 tiles
int height = 16;
// Independent selection system for scratch space
std::vector<ImVec2> selected_tiles;
std::vector<ImVec2> selected_points;
bool select_rect_active = false;
};
std::array<ScratchSpaceSlot, 4> scratch_spaces_;
int current_scratch_slot_ = 0;
gfx::Tilemap tile16_blockset_;
Rom* rom_;
gfx::IRenderer* renderer_;
Tile16Editor tile16_editor_{rom_, &tile16_blockset_};
GfxGroupEditor gfx_group_editor_;
PaletteEditor palette_editor_;
gfx::SnesPalette palette_;
gfx::Bitmap selected_tile_bmp_;
gfx::Bitmap tile16_blockset_bmp_;
gfx::Bitmap current_gfx_bmp_;
gfx::Bitmap all_gfx_bmp;
std::array<gfx::Bitmap, zelda3::kNumOverworldMaps> maps_bmp_;
gfx::BitmapTable current_graphics_set_;
std::vector<gfx::Bitmap> sprite_previews_;
// Deferred texture creation for performance optimization
// Deferred texture management now handled by gfx::Arena::Get()
zelda3::Overworld overworld_{rom_};
zelda3::OverworldBlockset refresh_blockset_;
zelda3::Sprite current_sprite_;
zelda3::OverworldEntrance current_entrance_;
zelda3::OverworldExit current_exit_;
zelda3::OverworldItem current_item_;
zelda3::OverworldEntranceTileTypes entrance_tiletypes_ = {};
zelda3::GameEntity* current_entity_ = nullptr;
zelda3::GameEntity* dragged_entity_ = nullptr;
gui::Canvas ow_map_canvas_{"OwMap", kOverworldCanvasSize,
gui::CanvasGridSize::k64x64};
gui::Canvas current_gfx_canvas_{"CurrentGfx", kCurrentGfxCanvasSize,
gui::CanvasGridSize::k32x32};
gui::Canvas blockset_canvas_{"OwBlockset", kBlocksetCanvasSize,
gui::CanvasGridSize::k32x32};
std::unique_ptr<gui::TileSelectorWidget> blockset_selector_;
gui::Canvas graphics_bin_canvas_{"GraphicsBin", kGraphicsBinCanvasSize,
gui::CanvasGridSize::k16x16};
gui::Canvas properties_canvas_;
gui::Canvas scratch_canvas_{"ScratchSpace", ImVec2(320, 480), gui::CanvasGridSize::k32x32};
absl::Status status_;
};
} // namespace editor
} // namespace yaze
#endif
#ifndef YAZE_APP_EDITOR_OVERWORLDEDITOR_H
#define YAZE_APP_EDITOR_OVERWORLDEDITOR_H
#include "absl/status/status.h"
#include "app/editor/editor.h"
#include "app/editor/graphics/gfx_group_editor.h"
#include "app/editor/palette/palette_editor.h"
#include "app/gui/editor_card_manager.h"
#include "app/editor/overworld/tile16_editor.h"
#include "app/editor/overworld/map_properties.h"
#include "app/editor/overworld/overworld_entity_renderer.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/tilemap.h"
#include "app/gui/canvas.h"
#include "app/gui/widgets/tile_selector_widget.h"
#include "app/gui/input.h"
#include "app/rom.h"
#include "zelda3/overworld/overworld.h"
#include "imgui/imgui.h"
#include <mutex>
namespace yaze {
namespace editor {
constexpr unsigned int k4BPP = 4;
constexpr unsigned int kByteSize = 3;
constexpr unsigned int kMessageIdSize = 5;
constexpr unsigned int kNumSheetsToLoad = 223;
constexpr unsigned int kOverworldMapSize = 0x200;
constexpr ImVec2 kOverworldCanvasSize(kOverworldMapSize * 8,
kOverworldMapSize * 8);
constexpr ImVec2 kCurrentGfxCanvasSize(0x100 + 1, 0x10 * 0x40 + 1);
constexpr ImVec2 kBlocksetCanvasSize(0x100 + 1, 0x4000 + 1);
constexpr ImVec2 kGraphicsBinCanvasSize(0x100 + 1, kNumSheetsToLoad * 0x40 + 1);
constexpr ImGuiTableFlags kOWMapFlags =
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingStretchProp;
constexpr absl::string_view kWorldList =
"Light World\0Dark World\0Extra World\0";
constexpr absl::string_view kGamePartComboString = "Part 0\0Part 1\0Part 2\0";
constexpr absl::string_view kOWMapTable = "#MapSettingsTable";
/**
* @class OverworldEditor
* @brief Manipulates the Overworld and OverworldMap data in a Rom.
*
* The `OverworldEditor` class is responsible for managing the editing and
* manipulation of the overworld in a game. The user can drag and drop tiles,
* modify OverworldEntrance, OverworldExit, Sprite, and OverworldItem
* as well as change the gfx and palettes used in each overworld map.
*
* The Overworld itself is a series of bitmap images which exist inside each
* OverworldMap object. The drawing of the overworld is done using the Canvas
* class in conjunction with these underlying Bitmap objects.
*
* Provides access to the GfxGroupEditor and Tile16Editor through popup windows.
*
*/
class OverworldEditor : public Editor, public gfx::GfxContext {
public:
explicit OverworldEditor(Rom* rom) : rom_(rom) {
type_ = EditorType::kOverworld;
gfx_group_editor_.set_rom(rom);
// MapPropertiesSystem will be initialized after maps_bmp_ and canvas are ready
}
void Initialize(gfx::IRenderer* renderer, Rom* rom);
void Initialize() override;
absl::Status Load() override;
absl::Status Update() final;
absl::Status Undo() override { return absl::UnimplementedError("Undo"); }
absl::Status Redo() override { return absl::UnimplementedError("Redo"); }
absl::Status Cut() override { return absl::UnimplementedError("Cut"); }
absl::Status Copy() override;
absl::Status Paste() override;
absl::Status Find() override { return absl::UnimplementedError("Find"); }
absl::Status Save() override;
absl::Status Clear() override;
zelda3::Overworld& overworld() { return overworld_; }
/**
* @brief Apply ZSCustomOverworld ASM patch to upgrade ROM version
*/
absl::Status ApplyZSCustomOverworldASM(int target_version);
/**
* @brief Update ROM version markers and feature flags after ASM patching
*/
absl::Status UpdateROMVersionMarkers(int target_version);
int jump_to_tab() { return jump_to_tab_; }
int jump_to_tab_ = -1;
// ROM state methods (from Editor base class)
bool IsRomLoaded() const override { return rom_ && rom_->is_loaded(); }
std::string GetRomStatus() const override {
if (!rom_) return "No ROM loaded";
if (!rom_->is_loaded()) return "ROM failed to load";
return absl::StrFormat("ROM loaded: %s", rom_->title());
}
// Jump-to functionality
void set_current_map(int map_id) {
if (map_id >= 0 && map_id < zelda3::kNumOverworldMaps) {
current_map_ = map_id;
current_world_ = map_id / 0x40; // Calculate which world the map belongs to
}
}
/**
* @brief Load the Bitmap objects for each OverworldMap.
*
* Calls the Overworld class to load the image data and palettes from the Rom,
* then renders the area graphics and tile16 blockset Bitmap objects before
* assembling the OverworldMap Bitmap objects.
*/
absl::Status LoadGraphics();
private:
void DrawFullscreenCanvas();
void DrawToolset();
void RefreshChildMap(int map_index);
void RefreshOverworldMap();
void RefreshOverworldMapOnDemand(int map_index);
void RefreshChildMapOnDemand(int map_index);
void RefreshMultiAreaMapsSafely(int map_index, zelda3::OverworldMap* map);
absl::Status RefreshMapPalette();
void RefreshMapProperties();
absl::Status RefreshTile16Blockset();
void ForceRefreshGraphics(int map_index);
void RefreshSiblingMapGraphics(int map_index, bool include_self = false);
void DrawOverworldMaps();
void DrawOverworldEdits();
void RenderUpdatedMapBitmap(const ImVec2& click_position,
const std::vector<uint8_t>& tile_data);
/**
* @brief Check for changes to the overworld map.
*
* This function either draws the tile painter with the current tile16 or
* group of tile16 data with ow_map_canvas_ and DrawOverworldEdits or it
* checks for left mouse button click/drag to select a tile16 or group of
* tile16 data from the overworld map canvas. Similar to ZScream selection.
*/
void CheckForOverworldEdits();
/**
* @brief Draw and create the tile16 IDs that are currently selected.
*/
void CheckForSelectRectangle();
// Selected tile IDs for rectangle operations (moved from local static)
std::vector<int> selected_tile16_ids_;
/**
* @brief Check for changes to the overworld map. Calls RefreshOverworldMap
* and RefreshTile16Blockset on the current map if it is modified and is
* actively being edited.
*/
absl::Status CheckForCurrentMap();
void CheckForMousePan();
void DrawOverworldCanvas();
absl::Status DrawTile16Selector();
void DrawTile8Selector();
absl::Status DrawAreaGraphics();
void UpdateBlocksetSelectorState();
absl::Status LoadSpriteGraphics();
/**
* @brief Create textures for deferred map bitmaps on demand
*
* This method should be called periodically to create textures for maps
* that are needed but haven't had their textures created yet. This allows
* for smooth loading without blocking the main thread during ROM loading.
*/
void ProcessDeferredTextures();
/**
* @brief Ensure a specific map has its texture created
*
* Call this when a map becomes visible or is about to be rendered.
* It will create the texture if it doesn't exist yet.
*/
void EnsureMapTexture(int map_index);
void DrawOverworldProperties();
void HandleMapInteraction();
// SetupOverworldCanvasContextMenu removed (Phase 3B) - now handled by MapPropertiesSystem
// Canvas pan/zoom helpers (Overworld Refactoring)
void HandleOverworldPan();
void HandleOverworldZoom();
void ResetOverworldView();
void CenterOverworldView();
// Canvas Automation API integration (Phase 4)
void SetupCanvasAutomation();
gui::Canvas* GetOverworldCanvas() { return &ow_map_canvas_; }
// Tile operations for automation callbacks
bool AutomationSetTile(int x, int y, int tile_id);
int AutomationGetTile(int x, int y);
/**
* @brief Scroll the blockset canvas to show the current selected tile16
*/
void ScrollBlocksetCanvasToCurrentTile();
// Scratch space canvas methods
absl::Status DrawScratchSpace();
absl::Status SaveCurrentSelectionToScratch(int slot);
absl::Status LoadScratchToSelection(int slot);
absl::Status ClearScratchSpace(int slot);
void DrawScratchSpaceEdits();
void DrawScratchSpacePattern();
void DrawScratchSpaceSelection();
void UpdateScratchBitmapTile(int tile_x, int tile_y, int tile_id, int slot = -1);
absl::Status UpdateUsageStats();
void DrawUsageGrid();
void DrawDebugWindow();
enum class EditingMode {
MOUSE, // Navigation, selection, entity management via context menu
DRAW_TILE // Tile painting mode
};
EditingMode current_mode = EditingMode::DRAW_TILE;
EditingMode previous_mode = EditingMode::DRAW_TILE;
// Entity editing state (managed via context menu now)
enum class EntityEditMode {
NONE,
ENTRANCES,
EXITS,
ITEMS,
SPRITES,
TRANSPORTS,
MUSIC
};
EntityEditMode entity_edit_mode_ = EntityEditMode::NONE;
enum OverworldProperty {
LW_AREA_GFX,
DW_AREA_GFX,
LW_AREA_PAL,
DW_AREA_PAL,
LW_SPR_GFX_PART1,
LW_SPR_GFX_PART2,
DW_SPR_GFX_PART1,
DW_SPR_GFX_PART2,
LW_SPR_PAL_PART1,
LW_SPR_PAL_PART2,
DW_SPR_PAL_PART1,
DW_SPR_PAL_PART2,
};
int current_world_ = 0;
int current_map_ = 0;
int current_parent_ = 0;
int current_entrance_id_ = 0;
int current_exit_id_ = 0;
int current_item_id_ = 0;
int current_sprite_id_ = 0;
int current_blockset_ = 0;
int game_state_ = 1;
int current_tile16_ = 0;
int selected_entrance_ = 0;
int selected_usage_map_ = 0xFFFF;
bool all_gfx_loaded_ = false;
bool map_blockset_loaded_ = false;
bool selected_tile_loaded_ = false;
bool show_tile16_editor_ = false;
bool show_gfx_group_editor_ = false;
bool show_properties_editor_ = false;
bool overworld_canvas_fullscreen_ = false;
bool middle_mouse_dragging_ = false;
bool is_dragging_entity_ = false;
bool dragged_entity_free_movement_ = false;
bool current_map_lock_ = false;
bool show_custom_bg_color_editor_ = false;
bool show_overlay_editor_ = false;
bool use_area_specific_bg_color_ = false;
bool show_map_properties_panel_ = false;
bool show_overlay_preview_ = false;
// Card visibility states - Start hidden to prevent crash
bool show_overworld_canvas_ = true;
bool show_tile16_selector_ = false;
bool show_tile8_selector_ = false;
bool show_area_gfx_ = false;
bool show_scratch_ = false;
bool show_gfx_groups_ = false;
bool show_usage_stats_ = false;
bool show_v3_settings_ = false;
// Map properties system for UI organization
std::unique_ptr<MapPropertiesSystem> map_properties_system_;
std::unique_ptr<OverworldEntityRenderer> entity_renderer_;
// Scratch space for large layouts
// Scratch space canvas for tile16 drawing (like a mini overworld)
struct ScratchSpaceSlot {
gfx::Bitmap scratch_bitmap;
std::array<std::array<int, 32>, 32> tile_data; // 32x32 grid of tile16 IDs
bool in_use = false;
std::string name = "Empty";
int width = 16; // Default 16x16 tiles
int height = 16;
// Independent selection system for scratch space
std::vector<ImVec2> selected_tiles;
std::vector<ImVec2> selected_points;
bool select_rect_active = false;
};
std::array<ScratchSpaceSlot, 4> scratch_spaces_;
int current_scratch_slot_ = 0;
gfx::Tilemap tile16_blockset_;
Rom* rom_;
gfx::IRenderer* renderer_;
Tile16Editor tile16_editor_{rom_, &tile16_blockset_};
GfxGroupEditor gfx_group_editor_;
PaletteEditor palette_editor_;
gfx::SnesPalette palette_;
gfx::Bitmap selected_tile_bmp_;
gfx::Bitmap tile16_blockset_bmp_;
gfx::Bitmap current_gfx_bmp_;
gfx::Bitmap all_gfx_bmp;
std::array<gfx::Bitmap, zelda3::kNumOverworldMaps> maps_bmp_;
gfx::BitmapTable current_graphics_set_;
std::vector<gfx::Bitmap> sprite_previews_;
// Deferred texture creation for performance optimization
// Deferred texture management now handled by gfx::Arena::Get()
zelda3::Overworld overworld_{rom_};
zelda3::OverworldBlockset refresh_blockset_;
zelda3::Sprite current_sprite_;
zelda3::OverworldEntrance current_entrance_;
zelda3::OverworldExit current_exit_;
zelda3::OverworldItem current_item_;
zelda3::OverworldEntranceTileTypes entrance_tiletypes_ = {};
zelda3::GameEntity* current_entity_ = nullptr;
zelda3::GameEntity* dragged_entity_ = nullptr;
gui::Canvas ow_map_canvas_{"OwMap", kOverworldCanvasSize,
gui::CanvasGridSize::k64x64};
gui::Canvas current_gfx_canvas_{"CurrentGfx", kCurrentGfxCanvasSize,
gui::CanvasGridSize::k32x32};
gui::Canvas blockset_canvas_{"OwBlockset", kBlocksetCanvasSize,
gui::CanvasGridSize::k32x32};
std::unique_ptr<gui::TileSelectorWidget> blockset_selector_;
gui::Canvas graphics_bin_canvas_{"GraphicsBin", kGraphicsBinCanvasSize,
gui::CanvasGridSize::k16x16};
gui::Canvas properties_canvas_;
gui::Canvas scratch_canvas_{"ScratchSpace", ImVec2(320, 480), gui::CanvasGridSize::k32x32};
absl::Status status_;
};
} // namespace editor
} // namespace yaze
#endif

View File

@@ -4,7 +4,7 @@
#include "app/core/features.h"
#include "app/editor/overworld/entity.h"
#include "app/gui/canvas.h"
#include "app/zelda3/common.h"
#include "zelda3/common.h"
#include "util/hex.h"
#include "imgui/imgui.h"

View File

@@ -5,8 +5,8 @@
#include "app/gfx/bitmap.h"
#include "app/gui/canvas.h"
#include "app/zelda3/common.h"
#include "app/zelda3/overworld/overworld.h"
#include "zelda3/common.h"
#include "zelda3/overworld/overworld.h"
#include "imgui/imgui.h"
namespace yaze {

View File

@@ -27,9 +27,9 @@
#include "app/gui/input.h"
#include "app/gui/style.h"
#include "app/rom.h"
#include "app/zelda3/common.h"
#include "app/zelda3/overworld/overworld.h"
#include "app/zelda3/overworld/overworld_map.h"
#include "zelda3/common.h"
#include "zelda3/overworld/overworld.h"
#include "zelda3/overworld/overworld_map.h"
#include "imgui/imgui.h"
#include "imgui_memory_editor.h"
#include "util/hex.h"

View File

@@ -12,7 +12,7 @@
#include "app/gui/input.h"
#include "app/gui/style.h"
#include "app/rom.h"
#include "app/zelda3/overworld/overworld.h"
#include "zelda3/overworld/overworld.h"
#include "imgui/imgui.h"
#include "util/hex.h"
#include "util/log.h"

View File

@@ -7,7 +7,7 @@
#include "app/gfx/arena.h"
#include "app/gui/icons.h"
#include "app/gui/input.h"
#include "app/zelda3/sprite/sprite.h"
#include "zelda3/sprite/sprite.h"
#include "util/hex.h"
namespace yaze {

View File

@@ -252,12 +252,11 @@ absl::Status PaletteManager::SaveGroup(const std::string& group_name) {
auto color_it = modified_colors_[group_name].find(palette_idx);
if (color_it != modified_colors_[group_name].end()) {
for (int color_idx : color_it->second) {
// Calculate ROM address
uint32_t address =
GetPaletteAddress(group_name, palette_idx, color_idx);
// Calculate ROM address using the helper function
uint32_t address = GetPaletteAddress(group_name, palette_idx, color_idx);
// Write color to ROM
RETURN_IF_ERROR(rom_->WriteColor(address, (*palette)[color_idx]));
// Write color to ROM - write the 16-bit SNES color value
rom_->WriteShort(address, (*palette)[color_idx].snes());
}
}
}

View File

@@ -1,8 +1,8 @@
#include "app/gui/widgets/dungeon_object_emulator_preview.h"
#include "app/gfx/backend/irenderer.h"
#include "app/zelda3/dungeon/room.h"
#include "app/zelda3/dungeon/room_object.h"
#include "zelda3/dungeon/room.h"
#include "zelda3/dungeon/room_object.h"
#include "app/gui/widgets/widget_auto_register.h"
#include "app/core/window.h"
#include <cstdio>

View File

@@ -7,7 +7,7 @@
#include "absl/strings/str_format.h"
#include "app/test/test_manager.h"
#include "app/rom.h"
#include "app/zelda3/overworld/overworld.h"
#include "zelda3/overworld/overworld.h"
#include "app/editor/overworld/tile16_editor.h"
#include "app/gui/icons.h"

View File

@@ -1,443 +0,0 @@
#ifndef YAZE_APP_ZELDA3_COMMON_H
#define YAZE_APP_ZELDA3_COMMON_H
#include <cstdint>
#include <string>
namespace yaze {
/**
* @namespace yaze::zelda3
* @brief Zelda 3 specific classes and functions.
*/
namespace zelda3 {
/**
* @class GameEntity
* @brief Base class for all overworld and dungeon entities.
*/
class GameEntity {
public:
enum EntityType {
kEntrance = 0,
kExit = 1,
kItem = 2,
kSprite = 3,
kTransport = 4,
kMusic = 5,
kTilemap = 6,
kProperties = 7,
kDungeonSprite = 8,
} entity_type_;
int x_ = 0;
int y_ = 0;
int game_x_ = 0;
int game_y_ = 0;
int entity_id_ = 0;
uint16_t map_id_ = 0;
auto set_x(int x) { x_ = x; }
auto set_y(int y) { y_ = y; }
GameEntity() = default;
virtual void UpdateMapProperties(uint16_t map_id) = 0;
};
constexpr const char* kEntranceNames[] = {
"Link's House Intro",
"Link's House Post-intro",
"Sanctuary",
"Hyrule Castle West",
"Hyrule Castle Central",
"Hyrule Castle East",
"Death Mountain Express (Lower)",
"Death Mountain Express (Upper)",
"Eastern Palace",
"Desert Palace Central",
"Desert Palace East",
"Desert Palace West",
"Desert Palace Boss Lair",
"Kakariko Elder's House West",
"Kakariko Elder's House East",
"Kakariko Angry Bros West",
"Kakariko Angry Bros East",
"Mad Batter Lair",
"Under Lumberjacks' Weird Tree",
"Death Mountain Maze 0000",
"Death Mountain Maze 0001",
"Turtle Rock Mountainface 1",
"Death Mountain Cape Heart Piece Cave (Lower)",
"Death Mountain Cape Heart Piece Cave (Upper)",
"Turtle Rock Mountainface 2",
"Turtle Rock Mountainface 3",
"Death Mountain Maze 0002",
"Death Mountain Maze 0003",
"Death Mountain Maze 0004",
"Death Mountain Maze 0005",
"Death Mountain Maze 0006",
"Death Mountain Maze 0007",
"Death Mountain Maze 0008",
"Spectacle Rock Maze 1",
"Spectacle Rock Maze 2",
"Spectacle Rock Maze 3",
"Hyrule Castle Tower",
"Swamp Palace",
"Palace of Darkness",
"Misery Mire",
"Skull Woods 1",
"Skull Woods 2",
"Skull Woods Big Chest",
"Skull Woods Boss Lair",
"Lost Woods Thieves' Lair",
"Ice Palace",
"Death Mountain Escape West",
"Death Mountain Escape East",
"Death Mountain Elder's Cave (Lower)",
"Death Mountain Elder's Cave (Upper)",
"Hyrule Castle Secret Cellar",
"Tower of Hera",
"Thieves's Town",
"Turtle Rock Main",
"Ganon's Pyramid Sanctum (Lower)",
"Ganon's Tower",
"Fairy Cave 1",
"Kakariko Western Well",
"Death Mountain Maze 0009",
"Death Mountain Maze 0010",
"Treasure Shell Game 1",
"Storyteller Cave 1",
"Snitch House 1",
"Snitch House 2",
"SickBoy House",
"Byrna Gauntlet",
"Kakariko Pub South",
"Kakariko Pub North",
"Kakariko Inn",
"Sahasrahlah's Disco Infernum",
"Kakariko's Lame Shop",
"Village of Outcasts Chest Game",
"Village of Outcasts Orphanage",
"Kakariko Library",
"Kakariko Storage Shed",
"Kakariko Sweeper Lady's House",
"Potion Shop",
"Aginah's Desert Cottage",
"Watergate",
"Death Mountain Maze 0011",
"Fairy Cave 2",
"Refill Cave 0001",
"Refill Cave 0002",
"The Bomb \"Shop\"",
"Village of Outcasts Retirement Center",
"Fairy Cave 3",
"Good Bee Cave",
"General Store 1",
"General Store 2",
"Archery Game",
"Storyteller Cave 2",
"Hall of the Invisibility Cape",
"Pond of Wishing",
"Pond of Happiness",
"Fairy Cave 4",
"Swamp of Evil Heart Piece Hall",
"General Store 3",
"Blind's Old Hideout",
"Storyteller Cave 3",
"Warped Pond of Wishing",
"Chez Smithies",
"Fortune Teller 1",
"Fortune Teller 2",
"Chest Shell Game 2",
"Storyteller Cave 4",
"Storyteller Cave 5",
"Storyteller Cave 6",
"Village House 1",
"Thief Hideout 1",
"Thief Hideout 2",
"Heart Piece Cave 1",
"Thief Hideout 3",
"Refill Cave 3",
"Fairy Cave 5",
"Heart Piece Cave 2",
"Hyrule Castle Prison",
"Hyrule Castle Throne Room",
"Hyrule Tower Agahnim's Sanctum",
"Skull Woods 3 (Drop In)",
"Skull Woods 4 (Drop In)",
"Skull Woods 5 (Drop In)",
"Skull Woods 6 (Drop In)",
"Lost Woods Thieves' Hideout (Drop In)",
"Ganon's Pyramid Sanctum (Upper)",
"Fairy Cave 6 (Drop In)",
"Hyrule Castle Secret Cellar (Drop In)",
"Mad Batter Lair (Drop In)",
"Under Lumberjacks' Weird Tree (Drop In)",
"Kakariko Western Well (Drop In)",
"Hyrule Sewers Goodies Room (Drop In)",
"Chris Houlihan Room (Drop In)",
"Heart Piece Cave 3 (Drop In)",
"Ice Rod Cave"};
static const std::string TileTypeNames[] = {
"$00 Nothing (standard floor)",
"$01 Collision",
"$02 Collision",
"$03 Collision",
"$04 Collision",
"$05 Nothing (unused?)",
"$06 Nothing (unused?)",
"$07 Nothing (unused?)",
"$08 Deep water",
"$09 Shallow water",
"$0A Unknown? Possibly unused",
"$0B Collision (different in Overworld and unknown)",
"$0C Overlay mask",
"$0D Spike floor",
"$0E GT ice",
"$0F Ice palace ice",
"$10 Slope ◤",
"$11 Slope ◥",
"$12 Slope ◣",
"$13 Slope ◢",
"$14 Nothing (unused?)",
"$15 Nothing (unused?)",
"$16 Nothing (unused?)",
"$17 Nothing (unused?)",
"$18 Slope ◤",
"$19 Slope ◥",
"$1A Slope ◣",
"$1B Slope ◢",
"$1C Layer 2 overlay",
"$1D North single-layer auto stairs",
"$1E North layer-swap auto stairs",
"$1F North layer-swap auto stairs",
"$20 Pit",
"$21 Nothing (unused?)",
"$22 Manual stairs",
"$23 Pot switch",
"$24 Pressure switch",
"$25 Nothing (unused but referenced by somaria blocks)",
"$26 Collision (near stairs?)",
"$27 Brazier/Fence/Statue/Block/General hookable things",
"$28 North ledge",
"$29 South ledge",
"$2A East ledge",
"$2B West ledge",
"$2C ◤ ledge",
"$2D ◣ ledge",
"$2E ◥ ledge",
"$2F ◢ ledge",
"$30 Straight inter-room stairs south/up 0",
"$31 Straight inter-room stairs south/up 1",
"$32 Straight inter-room stairs south/up 2",
"$33 Straight inter-room stairs south/up 3",
"$34 Straight inter-room stairs north/down 0",
"$35 Straight inter-room stairs north/down 1",
"$36 Straight inter-room stairs north/down 2",
"$37 Straight inter-room stairs north/down 3",
"$38 Straight inter-room stairs north/down edge",
"$39 Straight inter-room stairs south/up edge",
"$3A Star tile (inactive on load)",
"$3B Star tile (active on load)",
"$3C Nothing (unused?)",
"$3D South single-layer auto stairs",
"$3E South layer-swap auto stairs",
"$3F South layer-swap auto stairs",
"$40 Thick grass",
"$41 Nothing (unused?)",
"$42 Gravestone / Tower of hera ledge shadows??",
"$43 Skull Woods entrance/Hera columns???",
"$44 Spike",
"$45 Nothing (unused?)",
"$46 Desert Tablet",
"$47 Nothing (unused?)",
"$48 Diggable ground",
"$49 Nothing (unused?)",
"$4A Diggable ground",
"$4B Warp tile",
"$4C Nothing (unused?) | Something unknown in overworld",
"$4D Nothing (unused?) | Something unknown in overworld",
"$4E Square corners in EP overworld",
"$4F Square corners in EP overworld",
"$50 Green bush",
"$51 Dark bush",
"$52 Gray rock",
"$53 Black rock",
"$54 Hint tile/Sign",
"$55 Big gray rock",
"$56 Big black rock",
"$57 Bonk rocks",
"$58 Chest 0",
"$59 Chest 1",
"$5A Chest 2",
"$5B Chest 3",
"$5C Chest 4",
"$5D Chest 5",
"$5E Spiral stairs",
"$5F Spiral stairs",
"$60 Rupee tile",
"$61 Nothing (unused?)",
"$62 Bombable floor",
"$63 Minigame chest",
"$64 Nothing (unused?)",
"$65 Nothing (unused?)",
"$66 Crystal peg down",
"$67 Crystal peg up",
"$68 Upwards conveyor",
"$69 Downwards conveyor",
"$6A Leftwards conveyor",
"$6B Rightwards conveyor",
"$6C North vines",
"$6D South vines",
"$6E West vines",
"$6F East vines",
"$70 Pot/Hammer peg/Push block 00",
"$71 Pot/Hammer peg/Push block 01",
"$72 Pot/Hammer peg/Push block 02",
"$73 Pot/Hammer peg/Push block 03",
"$74 Pot/Hammer peg/Push block 04",
"$75 Pot/Hammer peg/Push block 05",
"$76 Pot/Hammer peg/Push block 06",
"$77 Pot/Hammer peg/Push block 07",
"$78 Pot/Hammer peg/Push block 08",
"$79 Pot/Hammer peg/Push block 09",
"$7A Pot/Hammer peg/Push block 0A",
"$7B Pot/Hammer peg/Push block 0B",
"$7C Pot/Hammer peg/Push block 0C",
"$7D Pot/Hammer peg/Push block 0D",
"$7E Pot/Hammer peg/Push block 0E",
"$7F Pot/Hammer peg/Push block 0F",
"$80 North/South door",
"$81 East/West door",
"$82 North/South shutter door",
"$83 East/West shutter door",
"$84 North/South layer 2 door",
"$85 East/West layer 2 door",
"$86 North/South layer 2 shutter door",
"$87 East/West layer 2 shutter door",
"$88 Some type of door (?)",
"$89 East/West transport door",
"$8A Some type of door (?)",
"$8B Some type of door (?)",
"$8C Some type of door (?)",
"$8D Some type of door (?)",
"$8E Entrance door",
"$8F Entrance door",
"$90 Layer toggle shutter door (?)",
"$91 Layer toggle shutter door (?)",
"$92 Layer toggle shutter door (?)",
"$93 Layer toggle shutter door (?)",
"$94 Layer toggle shutter door (?)",
"$95 Layer toggle shutter door (?)",
"$96 Layer toggle shutter door (?)",
"$97 Layer toggle shutter door (?)",
"$98 Layer+Dungeon toggle shutter door (?)",
"$99 Layer+Dungeon toggle shutter door (?)",
"$9A Layer+Dungeon toggle shutter door (?)",
"$9B Layer+Dungeon toggle shutter door (?)",
"$9C Layer+Dungeon toggle shutter door (?)",
"$9D Layer+Dungeon toggle shutter door (?)",
"$9E Layer+Dungeon toggle shutter door (?)",
"$9F Layer+Dungeon toggle shutter door (?)",
"$A0 North/South Dungeon swap door",
"$A1 Dungeon toggle door (?)",
"$A2 Dungeon toggle door (?)",
"$A3 Dungeon toggle door (?)",
"$A4 Dungeon toggle door (?)",
"$A5 Dungeon toggle door (?)",
"$A6 Nothing (unused?)",
"$A7 Nothing (unused?)",
"$A8 Layer+Dungeon toggle shutter door (?)",
"$A9 Layer+Dungeon toggle shutter door (?)",
"$AA Layer+Dungeon toggle shutter door (?)",
"$AB Layer+Dungeon toggle shutter door (?)",
"$AC Layer+Dungeon toggle shutter door (?)",
"$AD Layer+Dungeon toggle shutter door (?)",
"$AE Layer+Dungeon toggle shutter door (?)",
"$AF Layer+Dungeon toggle shutter door (?)",
"$B0 Somaria ─",
"$B1 Somaria │",
"$B2 Somaria ┌",
"$B3 Somaria └",
"$B4 Somaria ┐",
"$B5 Somaria ┘",
"$B6 Somaria ⍰ 1 way",
"$B7 Somaria ┬",
"$B8 Somaria ┴",
"$B9 Somaria ├",
"$BA Somaria ┤",
"$BB Somaria ┼",
"$BC Somaria ⍰ 2 way",
"$BD Somaria ┼ crossover",
"$BE Pipe entrance",
"$BF Nothing (unused?)",
"$C0 Torch 00",
"$C1 Torch 01",
"$C2 Torch 02",
"$C3 Torch 03",
"$C4 Torch 04",
"$C5 Torch 05",
"$C6 Torch 06",
"$C7 Torch 07",
"$C8 Torch 08",
"$C9 Torch 09",
"$CA Torch 0A",
"$CB Torch 0B",
"$CC Torch 0C",
"$CD Torch 0D",
"$CE Torch 0E",
"$CF Torch 0F",
"$D0 Nothing (unused?)",
"$D1 Nothing (unused?)",
"$D2 Nothing (unused?)",
"$D3 Nothing (unused?)",
"$D4 Nothing (unused?)",
"$D5 Nothing (unused?)",
"$D6 Nothing (unused?)",
"$D7 Nothing (unused?)",
"$D8 Nothing (unused?)",
"$D9 Nothing (unused?)",
"$DA Nothing (unused?)",
"$DB Nothing (unused?)",
"$DC Nothing (unused?)",
"$DD Nothing (unused?)",
"$DE Nothing (unused?)",
"$DF Nothing (unused?)",
"$E0 Nothing (unused?)",
"$E1 Nothing (unused?)",
"$E2 Nothing (unused?)",
"$E3 Nothing (unused?)",
"$E4 Nothing (unused?)",
"$E5 Nothing (unused?)",
"$E6 Nothing (unused?)",
"$E7 Nothing (unused?)",
"$E8 Nothing (unused?)",
"$E9 Nothing (unused?)",
"$EA Nothing (unused?)",
"$EB Nothing (unused?)",
"$EC Nothing (unused?)",
"$ED Nothing (unused?)",
"$EE Nothing (unused?)",
"$EF Nothing (unused?)",
"$F0 Door 0 bottom",
"$F1 Door 1 bottom",
"$F2 Door 2 bottom",
"$F3 Door 3 bottom",
"$F4 Door X bottom? (unused?)",
"$F5 Door X bottom? (unused?)",
"$F6 Door X bottom? (unused?)",
"$F7 Door X bottom? (unused?)",
"$F8 Door 0 top",
"$F9 Door 1 top",
"$FA Door 2 top",
"$FB Door 3 top",
"$FC Door X top? (unused?)",
"$FD Door X top? (unused?)",
"$FE Door X top? (unused?)",
"$FF Door X top? (unused?)"};
} // namespace zelda3
} // namespace yaze
#endif // YAZE_APP_ZELDA3_COMMON_H

View File

@@ -1,816 +0,0 @@
#include "dungeon_editor_system.h"
#include <algorithm>
#include <chrono>
#include "absl/strings/str_format.h"
namespace yaze {
namespace zelda3 {
DungeonEditorSystem::DungeonEditorSystem(Rom* rom) : rom_(rom) {}
absl::Status DungeonEditorSystem::Initialize() {
if (rom_ == nullptr) {
return absl::InvalidArgumentError("ROM is null");
}
// Initialize default dungeon settings
dungeon_settings_.dungeon_id = 0;
dungeon_settings_.name = "Default Dungeon";
dungeon_settings_.description = "A dungeon created with the editor";
dungeon_settings_.total_rooms = 0;
dungeon_settings_.starting_room_id = 0;
dungeon_settings_.boss_room_id = 0;
dungeon_settings_.music_theme_id = 0;
dungeon_settings_.color_palette_id = 0;
dungeon_settings_.has_map = true;
dungeon_settings_.has_compass = true;
dungeon_settings_.has_big_key = true;
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::LoadDungeon(int dungeon_id) {
// TODO: Implement actual dungeon loading from ROM
editor_state_.current_room_id = 0;
editor_state_.is_dirty = false;
editor_state_.auto_save_enabled = true;
editor_state_.last_save_time = std::chrono::steady_clock::now();
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::SaveDungeon() {
// TODO: Implement actual dungeon saving to ROM
editor_state_.is_dirty = false;
editor_state_.last_save_time = std::chrono::steady_clock::now();
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::SaveRoom(int room_id) {
// TODO: Implement actual room saving to ROM
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::ReloadRoom(int room_id) {
// TODO: Implement actual room reloading from ROM
return absl::OkStatus();
}
void DungeonEditorSystem::SetEditorMode(EditorMode mode) {
editor_state_.current_mode = mode;
}
DungeonEditorSystem::EditorMode DungeonEditorSystem::GetEditorMode() const {
return editor_state_.current_mode;
}
absl::Status DungeonEditorSystem::SetCurrentRoom(int room_id) {
if (room_id < 0 || room_id >= NumberOfRooms) {
return absl::InvalidArgumentError("Invalid room ID");
}
editor_state_.current_room_id = room_id;
return absl::OkStatus();
}
int DungeonEditorSystem::GetCurrentRoom() const {
return editor_state_.current_room_id;
}
absl::StatusOr<Room> DungeonEditorSystem::GetRoom(int room_id) {
if (room_id < 0 || room_id >= NumberOfRooms) {
return absl::InvalidArgumentError("Invalid room ID");
}
// TODO: Load room from ROM or return cached room
return Room(room_id, rom_);
}
absl::Status DungeonEditorSystem::CreateRoom(int room_id, const std::string& name) {
// TODO: Implement room creation
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::DeleteRoom(int room_id) {
// TODO: Implement room deletion
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::DuplicateRoom(int source_room_id, int target_room_id) {
// TODO: Implement room duplication
return absl::OkStatus();
}
std::shared_ptr<DungeonObjectEditor> DungeonEditorSystem::GetObjectEditor() {
if (!object_editor_) {
object_editor_ = std::make_shared<DungeonObjectEditor>(rom_);
}
return object_editor_;
}
absl::Status DungeonEditorSystem::SetObjectEditorMode() {
editor_state_.current_mode = EditorMode::kObjects;
return absl::OkStatus();
}
// Sprite management
absl::Status DungeonEditorSystem::AddSprite(const SpriteData& sprite_data) {
int sprite_id = GenerateSpriteId();
sprites_[sprite_id] = sprite_data;
sprites_[sprite_id].sprite_id = sprite_id;
if (sprite_changed_callback_) {
sprite_changed_callback_(sprite_id);
}
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::RemoveSprite(int sprite_id) {
auto it = sprites_.find(sprite_id);
if (it == sprites_.end()) {
return absl::NotFoundError("Sprite not found");
}
sprites_.erase(it);
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::UpdateSprite(int sprite_id, const SpriteData& sprite_data) {
auto it = sprites_.find(sprite_id);
if (it == sprites_.end()) {
return absl::NotFoundError("Sprite not found");
}
it->second = sprite_data;
it->second.sprite_id = sprite_id;
if (sprite_changed_callback_) {
sprite_changed_callback_(sprite_id);
}
return absl::OkStatus();
}
absl::StatusOr<DungeonEditorSystem::SpriteData> DungeonEditorSystem::GetSprite(int sprite_id) {
auto it = sprites_.find(sprite_id);
if (it == sprites_.end()) {
return absl::NotFoundError("Sprite not found");
}
return it->second;
}
absl::StatusOr<std::vector<DungeonEditorSystem::SpriteData>> DungeonEditorSystem::GetSpritesByRoom(int room_id) {
std::vector<SpriteData> room_sprites;
for (const auto& [id, sprite] : sprites_) {
if (sprite.x >= 0 && sprite.y >= 0) { // Simple room assignment logic
room_sprites.push_back(sprite);
}
}
return room_sprites;
}
absl::StatusOr<std::vector<DungeonEditorSystem::SpriteData>> DungeonEditorSystem::GetSpritesByType(SpriteType type) {
std::vector<SpriteData> typed_sprites;
for (const auto& [id, sprite] : sprites_) {
if (sprite.type == type) {
typed_sprites.push_back(sprite);
}
}
return typed_sprites;
}
absl::Status DungeonEditorSystem::MoveSprite(int sprite_id, int new_x, int new_y) {
auto it = sprites_.find(sprite_id);
if (it == sprites_.end()) {
return absl::NotFoundError("Sprite not found");
}
it->second.x = new_x;
it->second.y = new_y;
if (sprite_changed_callback_) {
sprite_changed_callback_(sprite_id);
}
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::SetSpriteActive(int sprite_id, bool active) {
auto it = sprites_.find(sprite_id);
if (it == sprites_.end()) {
return absl::NotFoundError("Sprite not found");
}
it->second.is_active = active;
if (sprite_changed_callback_) {
sprite_changed_callback_(sprite_id);
}
return absl::OkStatus();
}
// Item management
absl::Status DungeonEditorSystem::AddItem(const ItemData& item_data) {
int item_id = GenerateItemId();
items_[item_id] = item_data;
items_[item_id].item_id = item_id;
if (item_changed_callback_) {
item_changed_callback_(item_id);
}
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::RemoveItem(int item_id) {
auto it = items_.find(item_id);
if (it == items_.end()) {
return absl::NotFoundError("Item not found");
}
items_.erase(it);
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::UpdateItem(int item_id, const ItemData& item_data) {
auto it = items_.find(item_id);
if (it == items_.end()) {
return absl::NotFoundError("Item not found");
}
it->second = item_data;
it->second.item_id = item_id;
if (item_changed_callback_) {
item_changed_callback_(item_id);
}
return absl::OkStatus();
}
absl::StatusOr<DungeonEditorSystem::ItemData> DungeonEditorSystem::GetItem(int item_id) {
auto it = items_.find(item_id);
if (it == items_.end()) {
return absl::NotFoundError("Item not found");
}
return it->second;
}
absl::StatusOr<std::vector<DungeonEditorSystem::ItemData>> DungeonEditorSystem::GetItemsByRoom(int room_id) {
std::vector<ItemData> room_items;
for (const auto& [id, item] : items_) {
if (item.room_id == room_id) {
room_items.push_back(item);
}
}
return room_items;
}
absl::StatusOr<std::vector<DungeonEditorSystem::ItemData>> DungeonEditorSystem::GetItemsByType(ItemType type) {
std::vector<ItemData> typed_items;
for (const auto& [id, item] : items_) {
if (item.type == type) {
typed_items.push_back(item);
}
}
return typed_items;
}
absl::Status DungeonEditorSystem::MoveItem(int item_id, int new_x, int new_y) {
auto it = items_.find(item_id);
if (it == items_.end()) {
return absl::NotFoundError("Item not found");
}
it->second.x = new_x;
it->second.y = new_y;
if (item_changed_callback_) {
item_changed_callback_(item_id);
}
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::SetItemHidden(int item_id, bool hidden) {
auto it = items_.find(item_id);
if (it == items_.end()) {
return absl::NotFoundError("Item not found");
}
it->second.is_hidden = hidden;
if (item_changed_callback_) {
item_changed_callback_(item_id);
}
return absl::OkStatus();
}
// Entrance/exit management
absl::Status DungeonEditorSystem::AddEntrance(const EntranceData& entrance_data) {
int entrance_id = GenerateEntranceId();
entrances_[entrance_id] = entrance_data;
entrances_[entrance_id].entrance_id = entrance_id;
if (entrance_changed_callback_) {
entrance_changed_callback_(entrance_id);
}
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::RemoveEntrance(int entrance_id) {
auto it = entrances_.find(entrance_id);
if (it == entrances_.end()) {
return absl::NotFoundError("Entrance not found");
}
entrances_.erase(it);
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::UpdateEntrance(int entrance_id, const EntranceData& entrance_data) {
auto it = entrances_.find(entrance_id);
if (it == entrances_.end()) {
return absl::NotFoundError("Entrance not found");
}
it->second = entrance_data;
it->second.entrance_id = entrance_id;
if (entrance_changed_callback_) {
entrance_changed_callback_(entrance_id);
}
return absl::OkStatus();
}
absl::StatusOr<DungeonEditorSystem::EntranceData> DungeonEditorSystem::GetEntrance(int entrance_id) {
auto it = entrances_.find(entrance_id);
if (it == entrances_.end()) {
return absl::NotFoundError("Entrance not found");
}
return it->second;
}
absl::StatusOr<std::vector<DungeonEditorSystem::EntranceData>> DungeonEditorSystem::GetEntrancesByRoom(int room_id) {
std::vector<EntranceData> room_entrances;
for (const auto& [id, entrance] : entrances_) {
if (entrance.source_room_id == room_id || entrance.target_room_id == room_id) {
room_entrances.push_back(entrance);
}
}
return room_entrances;
}
absl::StatusOr<std::vector<DungeonEditorSystem::EntranceData>> DungeonEditorSystem::GetEntrancesByType(EntranceType type) {
std::vector<EntranceData> typed_entrances;
for (const auto& [id, entrance] : entrances_) {
if (entrance.type == type) {
typed_entrances.push_back(entrance);
}
}
return typed_entrances;
}
absl::Status DungeonEditorSystem::ConnectRooms(int room1_id, int room2_id, int x1, int y1, int x2, int y2) {
EntranceData entrance_data;
entrance_data.source_room_id = room1_id;
entrance_data.target_room_id = room2_id;
entrance_data.source_x = x1;
entrance_data.source_y = y1;
entrance_data.target_x = x2;
entrance_data.target_y = y2;
entrance_data.type = EntranceType::kNormal;
entrance_data.is_bidirectional = true;
return AddEntrance(entrance_data);
}
absl::Status DungeonEditorSystem::DisconnectRooms(int room1_id, int room2_id) {
// Find and remove entrance between rooms
for (auto it = entrances_.begin(); it != entrances_.end();) {
const auto& entrance = it->second;
if ((entrance.source_room_id == room1_id && entrance.target_room_id == room2_id) ||
(entrance.source_room_id == room2_id && entrance.target_room_id == room1_id)) {
it = entrances_.erase(it);
} else {
++it;
}
}
return absl::OkStatus();
}
// Door management
absl::Status DungeonEditorSystem::AddDoor(const DoorData& door_data) {
int door_id = GenerateDoorId();
doors_[door_id] = door_data;
doors_[door_id].door_id = door_id;
if (door_changed_callback_) {
door_changed_callback_(door_id);
}
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::RemoveDoor(int door_id) {
auto it = doors_.find(door_id);
if (it == doors_.end()) {
return absl::NotFoundError("Door not found");
}
doors_.erase(it);
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::UpdateDoor(int door_id, const DoorData& door_data) {
auto it = doors_.find(door_id);
if (it == doors_.end()) {
return absl::NotFoundError("Door not found");
}
it->second = door_data;
it->second.door_id = door_id;
if (door_changed_callback_) {
door_changed_callback_(door_id);
}
return absl::OkStatus();
}
absl::StatusOr<DungeonEditorSystem::DoorData> DungeonEditorSystem::GetDoor(int door_id) {
auto it = doors_.find(door_id);
if (it == doors_.end()) {
return absl::NotFoundError("Door not found");
}
return it->second;
}
absl::StatusOr<std::vector<DungeonEditorSystem::DoorData>> DungeonEditorSystem::GetDoorsByRoom(int room_id) {
std::vector<DoorData> room_doors;
for (const auto& [id, door] : doors_) {
if (door.room_id == room_id) {
room_doors.push_back(door);
}
}
return room_doors;
}
absl::Status DungeonEditorSystem::SetDoorLocked(int door_id, bool locked) {
auto it = doors_.find(door_id);
if (it == doors_.end()) {
return absl::NotFoundError("Door not found");
}
it->second.is_locked = locked;
if (door_changed_callback_) {
door_changed_callback_(door_id);
}
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::SetDoorKeyRequirement(int door_id, bool requires_key, int key_type) {
auto it = doors_.find(door_id);
if (it == doors_.end()) {
return absl::NotFoundError("Door not found");
}
it->second.requires_key = requires_key;
it->second.key_type = key_type;
if (door_changed_callback_) {
door_changed_callback_(door_id);
}
return absl::OkStatus();
}
// Chest management
absl::Status DungeonEditorSystem::AddChest(const ChestData& chest_data) {
int chest_id = GenerateChestId();
chests_[chest_id] = chest_data;
chests_[chest_id].chest_id = chest_id;
if (chest_changed_callback_) {
chest_changed_callback_(chest_id);
}
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::RemoveChest(int chest_id) {
auto it = chests_.find(chest_id);
if (it == chests_.end()) {
return absl::NotFoundError("Chest not found");
}
chests_.erase(it);
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::UpdateChest(int chest_id, const ChestData& chest_data) {
auto it = chests_.find(chest_id);
if (it == chests_.end()) {
return absl::NotFoundError("Chest not found");
}
it->second = chest_data;
it->second.chest_id = chest_id;
if (chest_changed_callback_) {
chest_changed_callback_(chest_id);
}
return absl::OkStatus();
}
absl::StatusOr<DungeonEditorSystem::ChestData> DungeonEditorSystem::GetChest(int chest_id) {
auto it = chests_.find(chest_id);
if (it == chests_.end()) {
return absl::NotFoundError("Chest not found");
}
return it->second;
}
absl::StatusOr<std::vector<DungeonEditorSystem::ChestData>> DungeonEditorSystem::GetChestsByRoom(int room_id) {
std::vector<ChestData> room_chests;
for (const auto& [id, chest] : chests_) {
if (chest.room_id == room_id) {
room_chests.push_back(chest);
}
}
return room_chests;
}
absl::Status DungeonEditorSystem::SetChestItem(int chest_id, int item_id, int quantity) {
auto it = chests_.find(chest_id);
if (it == chests_.end()) {
return absl::NotFoundError("Chest not found");
}
it->second.item_id = item_id;
it->second.item_quantity = quantity;
if (chest_changed_callback_) {
chest_changed_callback_(chest_id);
}
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::SetChestOpened(int chest_id, bool opened) {
auto it = chests_.find(chest_id);
if (it == chests_.end()) {
return absl::NotFoundError("Chest not found");
}
it->second.is_opened = opened;
if (chest_changed_callback_) {
chest_changed_callback_(chest_id);
}
return absl::OkStatus();
}
// Room properties and metadata
absl::Status DungeonEditorSystem::SetRoomProperties(int room_id, const RoomProperties& properties) {
room_properties_[room_id] = properties;
if (room_changed_callback_) {
room_changed_callback_(room_id);
}
return absl::OkStatus();
}
absl::StatusOr<DungeonEditorSystem::RoomProperties> DungeonEditorSystem::GetRoomProperties(int room_id) {
auto it = room_properties_.find(room_id);
if (it == room_properties_.end()) {
// Return default properties
RoomProperties default_properties;
default_properties.room_id = room_id;
default_properties.name = absl::StrFormat("Room %d", room_id);
default_properties.description = "";
default_properties.dungeon_id = 0;
default_properties.floor_level = 0;
default_properties.is_boss_room = false;
default_properties.is_save_room = false;
default_properties.is_shop_room = false;
default_properties.music_id = 0;
default_properties.ambient_sound_id = 0;
return default_properties;
}
return it->second;
}
// Dungeon-wide settings
absl::Status DungeonEditorSystem::SetDungeonSettings(const DungeonSettings& settings) {
dungeon_settings_ = settings;
return absl::OkStatus();
}
absl::StatusOr<DungeonEditorSystem::DungeonSettings> DungeonEditorSystem::GetDungeonSettings() {
return dungeon_settings_;
}
// Validation and error checking
absl::Status DungeonEditorSystem::ValidateRoom(int room_id) {
// TODO: Implement room validation
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::ValidateDungeon() {
// TODO: Implement dungeon validation
return absl::OkStatus();
}
std::vector<std::string> DungeonEditorSystem::GetValidationErrors(int room_id) {
// TODO: Implement validation error collection
return {};
}
std::vector<std::string> DungeonEditorSystem::GetDungeonValidationErrors() {
// TODO: Implement dungeon validation error collection
return {};
}
// Rendering and preview
absl::StatusOr<gfx::Bitmap> DungeonEditorSystem::RenderRoom(int room_id) {
// TODO: Implement room rendering
return gfx::Bitmap();
}
absl::StatusOr<gfx::Bitmap> DungeonEditorSystem::RenderRoomPreview(int room_id, EditorMode mode) {
// TODO: Implement room preview rendering
return gfx::Bitmap();
}
absl::StatusOr<gfx::Bitmap> DungeonEditorSystem::RenderDungeonMap() {
// TODO: Implement dungeon map rendering
return gfx::Bitmap();
}
// Import/Export functionality
absl::Status DungeonEditorSystem::ImportRoomFromFile(const std::string& file_path, int room_id) {
// TODO: Implement room import
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::ExportRoomToFile(int room_id, const std::string& file_path) {
// TODO: Implement room export
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::ImportDungeonFromFile(const std::string& file_path) {
// TODO: Implement dungeon import
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::ExportDungeonToFile(const std::string& file_path) {
// TODO: Implement dungeon export
return absl::OkStatus();
}
// Undo/Redo system
absl::Status DungeonEditorSystem::Undo() {
if (!CanUndo()) {
return absl::FailedPreconditionError("Nothing to undo");
}
// TODO: Implement undo functionality
return absl::OkStatus();
}
absl::Status DungeonEditorSystem::Redo() {
if (!CanRedo()) {
return absl::FailedPreconditionError("Nothing to redo");
}
// TODO: Implement redo functionality
return absl::OkStatus();
}
bool DungeonEditorSystem::CanUndo() const {
return !undo_history_.empty();
}
bool DungeonEditorSystem::CanRedo() const {
return !redo_history_.empty();
}
void DungeonEditorSystem::ClearHistory() {
undo_history_.clear();
redo_history_.clear();
}
// Event callbacks
void DungeonEditorSystem::SetRoomChangedCallback(RoomChangedCallback callback) {
room_changed_callback_ = callback;
}
void DungeonEditorSystem::SetSpriteChangedCallback(SpriteChangedCallback callback) {
sprite_changed_callback_ = callback;
}
void DungeonEditorSystem::SetItemChangedCallback(ItemChangedCallback callback) {
item_changed_callback_ = callback;
}
void DungeonEditorSystem::SetEntranceChangedCallback(EntranceChangedCallback callback) {
entrance_changed_callback_ = callback;
}
void DungeonEditorSystem::SetDoorChangedCallback(DoorChangedCallback callback) {
door_changed_callback_ = callback;
}
void DungeonEditorSystem::SetChestChangedCallback(ChestChangedCallback callback) {
chest_changed_callback_ = callback;
}
void DungeonEditorSystem::SetModeChangedCallback(ModeChangedCallback callback) {
mode_changed_callback_ = callback;
}
void DungeonEditorSystem::SetValidationCallback(ValidationCallback callback) {
validation_callback_ = callback;
}
// Helper methods
int DungeonEditorSystem::GenerateSpriteId() {
return next_sprite_id_++;
}
int DungeonEditorSystem::GenerateItemId() {
return next_item_id_++;
}
int DungeonEditorSystem::GenerateEntranceId() {
return next_entrance_id_++;
}
int DungeonEditorSystem::GenerateDoorId() {
return next_door_id_++;
}
int DungeonEditorSystem::GenerateChestId() {
return next_chest_id_++;
}
Rom* DungeonEditorSystem::GetROM() const {
return rom_;
}
bool DungeonEditorSystem::IsDirty() const {
return editor_state_.is_dirty;
}
void DungeonEditorSystem::SetROM(Rom* rom) {
rom_ = rom;
// Update object editor with new ROM if it exists
if (object_editor_) {
object_editor_->SetROM(rom);
}
}
// Factory function
std::unique_ptr<DungeonEditorSystem> CreateDungeonEditorSystem(Rom* rom) {
return std::make_unique<DungeonEditorSystem>(rom);
}
} // namespace zelda3
} // namespace yaze

View File

@@ -1,492 +0,0 @@
#ifndef YAZE_APP_ZELDA3_DUNGEON_DUNGEON_EDITOR_SYSTEM_H
#define YAZE_APP_ZELDA3_DUNGEON_DUNGEON_EDITOR_SYSTEM_H
#include <memory>
#include <vector>
#include <unordered_map>
#include <functional>
#include <optional>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/window.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/rom.h"
#include "app/zelda3/dungeon/room_object.h"
#include "app/zelda3/dungeon/room.h"
#include "app/zelda3/sprite/sprite.h"
#include "dungeon_object_editor.h"
namespace yaze {
namespace zelda3 {
/**
* @brief Comprehensive dungeon editing system
*
* This class provides a complete dungeon editing solution including:
* - Object editing (walls, floors, decorations)
* - Sprite management (enemies, NPCs, interactive elements)
* - Item placement and management
* - Entrance/exit data editing
* - Door configuration
* - Chest and treasure management
* - Room properties and metadata
* - Dungeon-wide settings
*/
class DungeonEditorSystem {
public:
// Editor modes
enum class EditorMode {
kObjects, // Object editing mode
kSprites, // Sprite editing mode
kItems, // Item placement mode
kEntrances, // Entrance/exit editing mode
kDoors, // Door configuration mode
kChests, // Chest management mode
kProperties, // Room properties mode
kGlobal // Dungeon-wide settings mode
};
// Sprite types and categories
enum class SpriteType {
kEnemy, // Hostile entities
kNPC, // Non-player characters
kInteractive, // Interactive objects
kDecoration, // Decorative sprites
kBoss, // Boss entities
kSpecial // Special purpose sprites
};
// Item types
enum class ItemType {
kWeapon, // Swords, bows, etc.
kTool, // Hookshot, bombs, etc.
kKey, // Keys and key items
kHeart, // Heart containers and pieces
kRupee, // Currency
kBottle, // Bottles and contents
kUpgrade, // Capacity upgrades
kSpecial // Special items
};
// Entrance/exit types
enum class EntranceType {
kNormal, // Standard room entrance
kStairs, // Staircase connection
kDoor, // Door connection
kCave, // Cave entrance
kWarp, // Warp/teleport
kBoss, // Boss room entrance
kSpecial // Special entrance type
};
// Editor state
struct EditorState {
EditorMode current_mode = EditorMode::kObjects;
int current_room_id = 0;
bool is_dirty = false; // Has unsaved changes
bool auto_save_enabled = true;
std::chrono::steady_clock::time_point last_save_time;
};
// Sprite editing data
struct SpriteData {
int sprite_id;
std::string name;
DungeonEditorSystem::SpriteType type;
int x, y;
int layer;
std::unordered_map<std::string, std::string> properties;
bool is_active = true;
};
// Item placement data
struct ItemData {
int item_id;
DungeonEditorSystem::ItemType type;
std::string name;
int x, y;
int room_id;
bool is_hidden = false;
std::unordered_map<std::string, std::string> properties;
};
// Entrance/exit data
struct EntranceData {
int entrance_id;
DungeonEditorSystem::EntranceType type;
std::string name;
int source_room_id;
int target_room_id;
int source_x, source_y;
int target_x, target_y;
bool is_bidirectional = true;
std::unordered_map<std::string, std::string> properties;
};
// Door configuration data
struct DoorData {
int door_id;
std::string name;
int room_id;
int x, y;
int direction; // 0=up, 1=right, 2=down, 3=left
int target_room_id;
int target_x, target_y;
bool requires_key = false;
int key_type = 0;
bool is_locked = false;
std::unordered_map<std::string, std::string> properties;
};
// Chest data
struct ChestData {
int chest_id;
int room_id;
int x, y;
bool is_big_chest = false;
int item_id;
int item_quantity = 1;
bool is_opened = false;
std::unordered_map<std::string, std::string> properties;
};
explicit DungeonEditorSystem(Rom* rom);
~DungeonEditorSystem() = default;
// System initialization and management
absl::Status Initialize();
absl::Status LoadDungeon(int dungeon_id);
absl::Status SaveDungeon();
absl::Status SaveRoom(int room_id);
absl::Status ReloadRoom(int room_id);
// Mode management
void SetEditorMode(EditorMode mode);
EditorMode GetEditorMode() const;
// Room management
absl::Status SetCurrentRoom(int room_id);
int GetCurrentRoom() const;
absl::StatusOr<Room> GetRoom(int room_id);
absl::Status CreateRoom(int room_id, const std::string& name = "");
absl::Status DeleteRoom(int room_id);
absl::Status DuplicateRoom(int source_room_id, int target_room_id);
// Object editing (delegated to DungeonObjectEditor)
std::shared_ptr<DungeonObjectEditor> GetObjectEditor();
absl::Status SetObjectEditorMode();
// Sprite management
absl::Status AddSprite(const SpriteData& sprite_data);
absl::Status RemoveSprite(int sprite_id);
absl::Status UpdateSprite(int sprite_id, const SpriteData& sprite_data);
absl::StatusOr<SpriteData> GetSprite(int sprite_id);
absl::StatusOr<std::vector<SpriteData>> GetSpritesByRoom(int room_id);
absl::StatusOr<std::vector<SpriteData>> GetSpritesByType(DungeonEditorSystem::SpriteType type);
absl::Status MoveSprite(int sprite_id, int new_x, int new_y);
absl::Status SetSpriteActive(int sprite_id, bool active);
// Item management
absl::Status AddItem(const ItemData& item_data);
absl::Status RemoveItem(int item_id);
absl::Status UpdateItem(int item_id, const ItemData& item_data);
absl::StatusOr<ItemData> GetItem(int item_id);
absl::StatusOr<std::vector<ItemData>> GetItemsByRoom(int room_id);
absl::StatusOr<std::vector<ItemData>> GetItemsByType(DungeonEditorSystem::ItemType type);
absl::Status MoveItem(int item_id, int new_x, int new_y);
absl::Status SetItemHidden(int item_id, bool hidden);
// Entrance/exit management
absl::Status AddEntrance(const EntranceData& entrance_data);
absl::Status RemoveEntrance(int entrance_id);
absl::Status UpdateEntrance(int entrance_id, const EntranceData& entrance_data);
absl::StatusOr<EntranceData> GetEntrance(int entrance_id);
absl::StatusOr<std::vector<EntranceData>> GetEntrancesByRoom(int room_id);
absl::StatusOr<std::vector<EntranceData>> GetEntrancesByType(DungeonEditorSystem::EntranceType type);
absl::Status ConnectRooms(int room1_id, int room2_id, int x1, int y1, int x2, int y2);
absl::Status DisconnectRooms(int room1_id, int room2_id);
// Door management
absl::Status AddDoor(const DoorData& door_data);
absl::Status RemoveDoor(int door_id);
absl::Status UpdateDoor(int door_id, const DoorData& door_data);
absl::StatusOr<DoorData> GetDoor(int door_id);
absl::StatusOr<std::vector<DoorData>> GetDoorsByRoom(int room_id);
absl::Status SetDoorLocked(int door_id, bool locked);
absl::Status SetDoorKeyRequirement(int door_id, bool requires_key, int key_type);
// Chest management
absl::Status AddChest(const ChestData& chest_data);
absl::Status RemoveChest(int chest_id);
absl::Status UpdateChest(int chest_id, const ChestData& chest_data);
absl::StatusOr<ChestData> GetChest(int chest_id);
absl::StatusOr<std::vector<ChestData>> GetChestsByRoom(int room_id);
absl::Status SetChestItem(int chest_id, int item_id, int quantity);
absl::Status SetChestOpened(int chest_id, bool opened);
// Room properties and metadata
struct RoomProperties {
int room_id;
std::string name;
std::string description;
int dungeon_id;
int floor_level;
bool is_boss_room = false;
bool is_save_room = false;
bool is_shop_room = false;
int music_id = 0;
int ambient_sound_id = 0;
std::unordered_map<std::string, std::string> custom_properties;
};
absl::Status SetRoomProperties(int room_id, const RoomProperties& properties);
absl::StatusOr<RoomProperties> GetRoomProperties(int room_id);
// Dungeon-wide settings
struct DungeonSettings {
int dungeon_id;
std::string name;
std::string description;
int total_rooms;
int starting_room_id;
int boss_room_id;
int music_theme_id;
int color_palette_id;
bool has_map = true;
bool has_compass = true;
bool has_big_key = true;
std::unordered_map<std::string, std::string> custom_settings;
};
absl::Status SetDungeonSettings(const DungeonSettings& settings);
absl::StatusOr<DungeonSettings> GetDungeonSettings();
// Validation and error checking
absl::Status ValidateRoom(int room_id);
absl::Status ValidateDungeon();
std::vector<std::string> GetValidationErrors(int room_id);
std::vector<std::string> GetDungeonValidationErrors();
// Rendering and preview
absl::StatusOr<gfx::Bitmap> RenderRoom(int room_id);
absl::StatusOr<gfx::Bitmap> RenderRoomPreview(int room_id, EditorMode mode);
absl::StatusOr<gfx::Bitmap> RenderDungeonMap();
// Import/Export functionality
absl::Status ImportRoomFromFile(const std::string& file_path, int room_id);
absl::Status ExportRoomToFile(int room_id, const std::string& file_path);
absl::Status ImportDungeonFromFile(const std::string& file_path);
absl::Status ExportDungeonToFile(const std::string& file_path);
// Undo/Redo system
absl::Status Undo();
absl::Status Redo();
bool CanUndo() const;
bool CanRedo() const;
void ClearHistory();
// Event callbacks
using RoomChangedCallback = std::function<void(int room_id)>;
using SpriteChangedCallback = std::function<void(int sprite_id)>;
using ItemChangedCallback = std::function<void(int item_id)>;
using EntranceChangedCallback = std::function<void(int entrance_id)>;
using DoorChangedCallback = std::function<void(int door_id)>;
using ChestChangedCallback = std::function<void(int chest_id)>;
using ModeChangedCallback = std::function<void(EditorMode mode)>;
using ValidationCallback = std::function<void(const std::vector<std::string>& errors)>;
void SetRoomChangedCallback(RoomChangedCallback callback);
void SetSpriteChangedCallback(SpriteChangedCallback callback);
void SetItemChangedCallback(ItemChangedCallback callback);
void SetEntranceChangedCallback(EntranceChangedCallback callback);
void SetDoorChangedCallback(DoorChangedCallback callback);
void SetChestChangedCallback(ChestChangedCallback callback);
void SetModeChangedCallback(ModeChangedCallback callback);
void SetValidationCallback(ValidationCallback callback);
// Getters
EditorState GetEditorState() const;
Rom* GetROM() const;
bool IsDirty() const;
bool HasUnsavedChanges() const;
// ROM management
void SetROM(Rom* rom);
private:
// Internal helper methods
absl::Status InitializeObjectEditor();
absl::Status InitializeSpriteSystem();
absl::Status InitializeItemSystem();
absl::Status InitializeEntranceSystem();
absl::Status InitializeDoorSystem();
absl::Status InitializeChestSystem();
// Data management
absl::Status LoadRoomData(int room_id);
absl::Status SaveRoomData(int room_id);
absl::Status LoadSpriteData();
absl::Status SaveSpriteData();
absl::Status LoadItemData();
absl::Status SaveItemData();
absl::Status LoadEntranceData();
absl::Status SaveEntranceData();
absl::Status LoadDoorData();
absl::Status SaveDoorData();
absl::Status LoadChestData();
absl::Status SaveChestData();
// Validation helpers
absl::Status ValidateSprite(const SpriteData& sprite);
absl::Status ValidateItem(const ItemData& item);
absl::Status ValidateEntrance(const EntranceData& entrance);
absl::Status ValidateDoor(const DoorData& door);
absl::Status ValidateChest(const ChestData& chest);
// ID generation
int GenerateSpriteId();
int GenerateItemId();
int GenerateEntranceId();
int GenerateDoorId();
int GenerateChestId();
// Member variables
Rom* rom_;
std::shared_ptr<DungeonObjectEditor> object_editor_;
EditorState editor_state_;
DungeonSettings dungeon_settings_;
// Data storage
std::unordered_map<int, Room> rooms_;
std::unordered_map<int, SpriteData> sprites_;
std::unordered_map<int, ItemData> items_;
std::unordered_map<int, EntranceData> entrances_;
std::unordered_map<int, DoorData> doors_;
std::unordered_map<int, ChestData> chests_;
std::unordered_map<int, RoomProperties> room_properties_;
// ID counters
int next_sprite_id_ = 1;
int next_item_id_ = 1;
int next_entrance_id_ = 1;
int next_door_id_ = 1;
int next_chest_id_ = 1;
// Event callbacks
RoomChangedCallback room_changed_callback_;
SpriteChangedCallback sprite_changed_callback_;
ItemChangedCallback item_changed_callback_;
EntranceChangedCallback entrance_changed_callback_;
DoorChangedCallback door_changed_callback_;
ChestChangedCallback chest_changed_callback_;
ModeChangedCallback mode_changed_callback_;
ValidationCallback validation_callback_;
// Undo/Redo system
struct UndoPoint {
EditorState state;
std::unordered_map<int, Room> rooms;
std::unordered_map<int, SpriteData> sprites;
std::unordered_map<int, ItemData> items;
std::unordered_map<int, EntranceData> entrances;
std::unordered_map<int, DoorData> doors;
std::unordered_map<int, ChestData> chests;
std::chrono::steady_clock::time_point timestamp;
};
std::vector<UndoPoint> undo_history_;
std::vector<UndoPoint> redo_history_;
static constexpr size_t kMaxUndoHistory = 100;
};
/**
* @brief Factory function to create dungeon editor system
*/
std::unique_ptr<DungeonEditorSystem> CreateDungeonEditorSystem(Rom* rom);
/**
* @brief Sprite type utilities
*/
namespace SpriteTypes {
/**
* @brief Get sprite information by ID
*/
struct SpriteInfo {
int id;
std::string name;
DungeonEditorSystem::SpriteType type;
std::string description;
int default_layer;
std::vector<std::pair<std::string, std::string>> default_properties;
bool is_interactive;
bool is_hostile;
int difficulty_rating;
};
absl::StatusOr<SpriteInfo> GetSpriteInfo(int sprite_id);
std::vector<SpriteInfo> GetAllSpriteInfos();
std::vector<SpriteInfo> GetSpritesByType(DungeonEditorSystem::SpriteType type);
absl::StatusOr<std::string> GetSpriteCategory(int sprite_id);
} // namespace SpriteTypes
/**
* @brief Item type utilities
*/
namespace ItemTypes {
/**
* @brief Get item information by ID
*/
struct ItemInfo {
int id;
std::string name;
DungeonEditorSystem::ItemType type;
std::string description;
int rarity;
int value;
std::vector<std::pair<std::string, std::string>> default_properties;
bool is_stackable;
int max_stack_size;
};
absl::StatusOr<ItemInfo> GetItemInfo(int item_id);
std::vector<ItemInfo> GetAllItemInfos();
std::vector<ItemInfo> GetItemsByType(DungeonEditorSystem::ItemType type);
absl::StatusOr<std::string> GetItemCategory(int item_id);
} // namespace ItemTypes
/**
* @brief Entrance type utilities
*/
namespace EntranceTypes {
/**
* @brief Get entrance information by ID
*/
struct EntranceInfo {
int id;
std::string name;
DungeonEditorSystem::EntranceType type;
std::string description;
std::vector<std::pair<std::string, std::string>> default_properties;
bool requires_key;
int key_type;
bool is_bidirectional;
};
absl::StatusOr<EntranceInfo> GetEntranceInfo(int entrance_id);
std::vector<EntranceInfo> GetAllEntranceInfos();
std::vector<EntranceInfo> GetEntrancesByType(DungeonEditorSystem::EntranceType type);
} // namespace EntranceTypes
} // namespace zelda3
} // namespace yaze
#endif // YAZE_APP_ZELDA3_DUNGEON_DUNGEON_EDITOR_SYSTEM_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,345 +0,0 @@
#ifndef YAZE_APP_ZELDA3_DUNGEON_DUNGEON_OBJECT_EDITOR_H
#define YAZE_APP_ZELDA3_DUNGEON_DUNGEON_OBJECT_EDITOR_H
#include <functional>
#include <memory>
#include <optional>
#include <unordered_map>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/core/window.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/rom.h"
#include "app/zelda3/dungeon/room.h"
#include "app/zelda3/dungeon/room_object.h"
namespace yaze {
namespace zelda3 {
/**
* @brief Interactive dungeon object editor with scroll wheel support
*
* This class provides a comprehensive object editing system for dungeon rooms,
* including:
* - Object insertion and deletion
* - Object size editing with scroll wheel
* - Object position editing with mouse
* - Layer management
* - Real-time preview and validation
* - Undo/redo functionality
* - Object property editing
*/
class DungeonObjectEditor {
public:
// Editor modes
enum class Mode {
kSelect, // Select and move objects
kInsert, // Insert new objects
kDelete, // Delete objects
kEdit, // Edit object properties
kLayer, // Layer management
kPreview // Preview mode
};
// Object selection state
struct SelectionState {
std::vector<size_t> selected_objects; // Indices of selected objects
bool is_multi_select = false;
bool is_dragging = false;
int drag_start_x = 0;
int drag_start_y = 0;
};
// Object editing state
struct EditingState {
Mode current_mode = Mode::kSelect;
int current_layer = 0;
int current_object_type = 0x10; // Default to wall
int scroll_wheel_delta = 0;
bool is_editing_size = false;
bool is_editing_position = false;
int preview_x = 0;
int preview_y = 0;
int preview_size = 0x12; // Default size
};
// Editor configuration
struct EditorConfig {
bool snap_to_grid = true;
int grid_size = 16; // 16x16 pixel grid
bool show_grid = true;
bool show_preview = true;
bool auto_save = false;
int auto_save_interval = 300; // 5 minutes
bool validate_objects = true;
bool show_collision_bounds = false;
// Phase 4: Visual feedback settings
bool show_selection_highlight = true;
bool show_layer_colors = true;
bool show_property_panel = true;
uint32_t selection_color = 0xFFFFFF00; // Yellow
uint32_t layer0_color = 0xFFFF0000; // Red tint
uint32_t layer1_color = 0xFF00FF00; // Green tint
uint32_t layer2_color = 0xFF0000FF; // Blue tint
};
// Undo/Redo system
struct UndoPoint {
std::vector<RoomObject> objects;
SelectionState selection;
EditingState editing;
std::chrono::steady_clock::time_point timestamp;
};
explicit DungeonObjectEditor(Rom* rom);
~DungeonObjectEditor() = default;
// Core editing operations
absl::Status LoadRoom(int room_id);
absl::Status SaveRoom();
absl::Status ClearRoom();
// Object manipulation
absl::Status InsertObject(int x, int y, int object_type, int size = 0x12,
int layer = 0);
absl::Status DeleteObject(size_t object_index);
absl::Status DeleteSelectedObjects();
absl::Status MoveObject(size_t object_index, int new_x, int new_y);
absl::Status ResizeObject(size_t object_index, int new_size);
absl::Status ChangeObjectType(size_t object_index, int new_type);
absl::Status ChangeObjectLayer(size_t object_index, int new_layer);
// Selection management
absl::Status SelectObject(int screen_x, int screen_y);
absl::Status SelectObjects(int start_x, int start_y, int end_x, int end_y);
absl::Status ClearSelection();
absl::Status AddToSelection(size_t object_index);
absl::Status RemoveFromSelection(size_t object_index);
// Mouse and scroll wheel handling
absl::Status HandleMouseClick(int x, int y, bool left_button,
bool right_button, bool shift_pressed);
absl::Status HandleMouseDrag(int start_x, int start_y, int current_x,
int current_y);
absl::Status HandleMouseRelease(int x, int y); // Phase 4: End drag operations
absl::Status HandleScrollWheel(int delta, int x, int y, bool ctrl_pressed);
absl::Status HandleKeyPress(int key_code, bool ctrl_pressed,
bool shift_pressed);
// Mode management
void SetMode(Mode mode);
Mode GetMode() const { return editing_state_.current_mode; }
// Layer management
void SetCurrentLayer(int layer);
int GetCurrentLayer() const { return editing_state_.current_layer; }
absl::StatusOr<std::vector<RoomObject>> GetObjectsByLayer(int layer);
absl::Status MoveObjectToLayer(size_t object_index, int layer);
// Object type management
void SetCurrentObjectType(int object_type);
int GetCurrentObjectType() const {
return editing_state_.current_object_type;
}
absl::StatusOr<std::vector<int>> GetAvailableObjectTypes();
absl::Status ValidateObjectType(int object_type);
// Rendering and preview
absl::StatusOr<gfx::Bitmap> RenderPreview(int x, int y);
void SetPreviewPosition(int x, int y);
void UpdatePreview();
// Phase 4: Visual feedback and GUI
void RenderSelectionHighlight(gfx::Bitmap& canvas);
void RenderLayerVisualization(gfx::Bitmap& canvas);
void RenderObjectPropertyPanel(); // ImGui panel
void RenderLayerControls(); // ImGui controls
absl::Status HandleDragOperation(int current_x, int current_y);
// Undo/Redo functionality
absl::Status Undo();
absl::Status Redo();
bool CanUndo() const;
bool CanRedo() const;
void ClearHistory();
// Configuration
void SetROM(Rom* rom);
void SetConfig(const EditorConfig& config);
EditorConfig GetConfig() const { return config_; }
void SetSnapToGrid(bool enabled);
void SetGridSize(int size);
void SetShowGrid(bool enabled);
// Validation and error checking
absl::Status ValidateRoom();
absl::Status ValidateObject(const RoomObject& object);
std::vector<std::string> GetValidationErrors();
// Event callbacks
using ObjectChangedCallback =
std::function<void(size_t object_index, const RoomObject& object)>;
using RoomChangedCallback = std::function<void()>;
using SelectionChangedCallback = std::function<void(const SelectionState&)>;
void SetObjectChangedCallback(ObjectChangedCallback callback);
void SetRoomChangedCallback(RoomChangedCallback callback);
void SetSelectionChangedCallback(SelectionChangedCallback callback);
// Getters
const Room& GetRoom() const { return *current_room_; }
Room* GetMutableRoom() { return current_room_.get(); }
const SelectionState& GetSelection() const { return selection_state_; }
const EditingState& GetEditingState() const { return editing_state_; }
size_t GetObjectCount() const {
return current_room_ ? current_room_->GetTileObjects().size() : 0;
}
const std::vector<RoomObject>& GetObjects() const {
return current_room_ ? current_room_->GetTileObjects() : empty_objects_;
}
private:
// Internal helper methods
absl::Status InitializeEditor();
absl::Status CreateUndoPoint();
absl::Status ApplyUndoPoint(const UndoPoint& undo_point);
// Coordinate conversion
std::pair<int, int> ScreenToRoomCoordinates(int screen_x, int screen_y);
std::pair<int, int> RoomToScreenCoordinates(int room_x, int room_y);
int SnapToGrid(int coordinate);
// Object finding and collision detection
std::optional<size_t> FindObjectAt(int room_x, int room_y);
std::vector<size_t> FindObjectsInArea(int start_x, int start_y, int end_x,
int end_y);
bool IsObjectAtPosition(const RoomObject& object, int x, int y);
bool ObjectsCollide(const RoomObject& obj1, const RoomObject& obj2);
// Preview and rendering helpers
absl::StatusOr<gfx::Bitmap> RenderObjectPreview(int object_type, int x, int y,
int size);
void UpdatePreviewObject();
absl::Status ValidatePreviewPosition(int x, int y);
// Size editing with scroll wheel
absl::Status HandleSizeEdit(int delta, int x, int y);
int GetNextSize(int current_size, int delta);
int GetPreviousSize(int current_size, int delta);
bool IsValidSize(int size);
// Member variables
Rom* rom_;
std::unique_ptr<Room> current_room_;
SelectionState selection_state_;
EditingState editing_state_;
EditorConfig config_;
std::vector<UndoPoint> undo_history_;
std::vector<UndoPoint> redo_history_;
static constexpr size_t kMaxUndoHistory = 50;
// Preview system
std::optional<RoomObject> preview_object_;
bool preview_visible_ = false;
// Event callbacks
ObjectChangedCallback object_changed_callback_;
RoomChangedCallback room_changed_callback_;
SelectionChangedCallback selection_changed_callback_;
// Constants
static constexpr int kMinObjectSize = 0x00;
static constexpr int kMaxObjectSize = 0xFF;
static constexpr int kDefaultObjectSize = 0x12;
static constexpr int kMinLayer = 0;
static constexpr int kMaxLayer = 2;
// Empty objects vector for const getter
std::vector<RoomObject> empty_objects_;
};
/**
* @brief Factory function to create dungeon object editor
*/
std::unique_ptr<DungeonObjectEditor> CreateDungeonObjectEditor(Rom* rom);
/**
* @brief Object type categories for easier selection
*/
namespace ObjectCategories {
struct ObjectCategory {
std::string name;
std::vector<int> object_ids;
std::string description;
};
/**
* @brief Get all available object categories
*/
std::vector<ObjectCategory> GetObjectCategories();
/**
* @brief Get objects in a specific category
*/
absl::StatusOr<std::vector<int>> GetObjectsInCategory(
const std::string& category_name);
/**
* @brief Get category for a specific object
*/
absl::StatusOr<std::string> GetObjectCategory(int object_id);
/**
* @brief Get object information
*/
struct ObjectInfo {
int id;
std::string name;
std::string description;
std::vector<std::pair<int, int>> valid_sizes;
std::vector<int> valid_layers;
bool is_interactive;
bool is_collidable;
};
absl::StatusOr<ObjectInfo> GetObjectInfo(int object_id);
} // namespace ObjectCategories
/**
* @brief Scroll wheel behavior configuration
*/
struct ScrollWheelConfig {
bool enabled = true;
int sensitivity = 1; // How much size changes per scroll
int min_size = 0x00;
int max_size = 0xFF;
bool wrap_around = false; // Wrap from max to min
bool smooth_scrolling = true;
int smooth_factor = 2; // Divide delta by this for smoother scrolling
};
/**
* @brief Mouse interaction configuration
*/
struct MouseConfig {
bool left_click_select = true;
bool right_click_context = true;
bool middle_click_drag = false;
bool drag_to_select = true;
bool snap_drag_to_grid = true;
int double_click_threshold = 500; // milliseconds
int drag_threshold = 5; // pixels before drag starts
};
} // namespace zelda3
} // namespace yaze
#endif // YAZE_APP_ZELDA3_DUNGEON_DUNGEON_OBJECT_EDITOR_H

View File

@@ -1,107 +0,0 @@
#ifndef YAZE_APP_ZELDA3_DUNGEON_ROM_ADDRESSES_H
#define YAZE_APP_ZELDA3_DUNGEON_ROM_ADDRESSES_H
#include <cstdint>
#include <array> // Added for std::array
namespace yaze {
namespace zelda3 {
// ============================================================================
// Dungeon ROM Address Constants
// ============================================================================
// This file contains all ROM addresses for dungeon-related data in ALTTP.
// Organized by category for readability and maintainability.
// === Room Data Pointers ===
constexpr int kRoomObjectLayoutPointer = 0x882D; // Layout pointer table
constexpr int kRoomObjectPointer = 0x874C; // Object data pointer (Long)
constexpr int kRoomHeaderPointer = 0xB5DD; // Room header pointer (LONG)
constexpr int kRoomHeaderPointerBank = 0xB5E7; // Room header bank byte
// === Palette Data ===
constexpr int kDungeonsMainBgPalettePointers = 0xDEC4B; // JP Same
constexpr int kDungeonsPalettes = 0xDD734; // Dungeon palette data
// === Item & Sprite Data ===
constexpr int kRoomItemsPointers = 0xDB69; // JP 0xDB67
constexpr int kRoomsSpritePointer = 0x4C298; // JP Same (2-byte bank 09D62E)
constexpr int kSpriteBlocksetPointer = 0x5B57; // Sprite graphics pointer
// === Graphics Data ===
constexpr int kGfxGroupsPointer = 0x6237; // Graphics group table
constexpr int kTileAddress = 0x001B52; // Main tile graphics
constexpr int kTileAddressFloor = 0x001B5A; // Floor tile graphics
// === Block Data ===
constexpr int kBlocksLength = 0x8896; // Word value
constexpr int kBlocksPointer1 = 0x15AFA; // Block data pointer 1
constexpr int kBlocksPointer2 = 0x15B01; // Block data pointer 2
constexpr int kBlocksPointer3 = 0x15B08; // Block data pointer 3
constexpr int kBlocksPointer4 = 0x15B0F; // Block data pointer 4
// === Chests ===
constexpr int kChestsLengthPointer = 0xEBF6; // Chest count pointer
constexpr int kChestsDataPointer1 = 0xEBFB; // Chest data start
// === Torches ===
constexpr int kTorchData = 0x2736A; // JP 0x2704A
constexpr int kTorchesLengthPointer = 0x88C1; // Torch count pointer
// === Pits & Warps ===
constexpr int kPitPointer = 0x394AB; // Pit/hole data
constexpr int kPitCount = 0x394A6; // Number of pits
// === Doors ===
constexpr int kDoorPointers = 0xF83C0; // Door data table
constexpr int kDoorGfxUp = 0x4D9E; // Door graphics (up)
constexpr int kDoorGfxDown = 0x4E06; // Door graphics (down)
constexpr int kDoorGfxCaveExitDown = 0x4E06; // Cave exit door
constexpr int kDoorGfxLeft = 0x4E66; // Door graphics (left)
constexpr int kDoorGfxRight = 0x4EC6; // Door graphics (right)
constexpr int kDoorPosUp = 0x197E; // Door position (up)
constexpr int kDoorPosDown = 0x1996; // Door position (down)
constexpr int kDoorPosLeft = 0x19AE; // Door position (left)
constexpr int kDoorPosRight = 0x19C6; // Door position (right)
// === Sprites ===
constexpr int kSpritesData = 0x4D8B0; // Sprite data start
constexpr int kSpritesDataEmptyRoom = 0x4D8AE; // Empty room sprite marker
constexpr int kSpritesEndData = 0x4EC9E; // Sprite data end
constexpr int kDungeonSpritePointers = 0x090000; // Dungeon sprite pointer table
// === Messages ===
constexpr int kMessagesIdDungeon = 0x3F61D; // Dungeon message IDs
// === Room Metadata ===
constexpr int kNumberOfRooms = 296; // Total dungeon rooms (0x00-0x127)
// Stair objects (special handling)
constexpr uint16_t kStairsObjects[] = {0x139, 0x138, 0x13B, 0x12E, 0x12D};
// === Layout Pointers (referenced in comments) ===
// Layout00 ptr: 0x47EF04
// Layout01 ptr: 0xAFEF04
// Layout02 ptr: 0xF0EF04
// Layout03 ptr: 0x4CF004
// Layout04 ptr: 0xA8F004
// Layout05 ptr: 0xECF004
// Layout06 ptr: 0x48F104
// Layout07 ptr: 0xA4F104
// === Notes ===
// - Layout arrays are NOT exactly the same as rooms
// - Object array is terminated by 0xFFFF (no layers)
// - In normal room, 0xFFFF goes to next layer (layers 0, 1, 2)
// Static pointers for the 8 predefined room layouts
static const std::array<int, 8> kRoomLayoutPointers = {
0x47EF04, 0xAFEF04, 0xF0EF04, 0x4CF004,
0xA8F004, 0xECF004, 0x48F104, 0xA4F104,
};
} // namespace zelda3
} // namespace yaze
#endif // YAZE_APP_ZELDA3_DUNGEON_ROM_ADDRESSES_H

View File

@@ -1,833 +0,0 @@
#include "object_drawer.h"
#include <cstdio>
#include "absl/strings/str_format.h"
#include "app/gfx/snes_tile.h"
#include "util/log.h"
namespace yaze {
namespace zelda3 {
ObjectDrawer::ObjectDrawer(Rom* rom, const uint8_t* room_gfx_buffer)
: rom_(rom), room_gfx_buffer_(room_gfx_buffer) {
InitializeDrawRoutines();
}
absl::Status ObjectDrawer::DrawObject(const RoomObject& object,
gfx::BackgroundBuffer& bg1,
gfx::BackgroundBuffer& bg2,
const gfx::PaletteGroup& palette_group) {
LOG_DEBUG("ObjectDrawer", "Drawing object 0x%02X at (%d,%d) size=%d tiles=%zu",
object.id_, object.x_, object.y_, object.size_, object.tiles().size());
if (!rom_ || !rom_->is_loaded()) {
return absl::FailedPreconditionError("ROM not loaded");
}
if (!routines_initialized_) {
return absl::FailedPreconditionError("Draw routines not initialized");
}
// Ensure object has tiles loaded
auto mutable_obj = const_cast<RoomObject&>(object);
mutable_obj.set_rom(rom_);
mutable_obj.EnsureTilesLoaded();
// Select buffer based on layer
auto& target_bg = (object.layer_ == RoomObject::LayerType::BG2) ? bg2 : bg1;
// Skip objects that don't have tiles loaded
if (mutable_obj.tiles().empty()) {
return absl::OkStatus();
}
// Look up draw routine for this object
int routine_id = GetDrawRoutineId(object.id_);
LOG_DEBUG("ObjectDrawer", "Object %04X -> routine_id=%d", object.id_, routine_id);
if (routine_id < 0 || routine_id >= static_cast<int>(draw_routines_.size())) {
LOG_DEBUG("ObjectDrawer", "Using fallback 1x1 drawing for object %04X", object.id_);
// Fallback to simple 1x1 drawing using first 8x8 tile
if (!mutable_obj.tiles().empty()) {
const auto& tile_info = mutable_obj.tiles()[0];
WriteTile8(target_bg, object.x_, object.y_, tile_info);
}
return absl::OkStatus();
}
LOG_DEBUG("ObjectDrawer", "Executing draw routine %d for object %04X", routine_id, object.id_);
// Execute the appropriate draw routine
draw_routines_[routine_id](this, object, target_bg, mutable_obj.tiles());
return absl::OkStatus();
}
absl::Status ObjectDrawer::DrawObjectList(
const std::vector<RoomObject>& objects,
gfx::BackgroundBuffer& bg1,
gfx::BackgroundBuffer& bg2,
const gfx::PaletteGroup& palette_group) {
for (const auto& object : objects) {
DrawObject(object, bg1, bg2, palette_group);
}
// CRITICAL: Sync bitmap data to SDL surfaces after all objects are drawn
// ObjectDrawer writes directly to bitmap.mutable_data(), but textures are created from SDL surfaces
auto& bg1_bmp = bg1.bitmap();
auto& bg2_bmp = bg2.bitmap();
if (bg1_bmp.modified() && bg1_bmp.surface() && bg1_bmp.mutable_data().size() > 0) {
SDL_LockSurface(bg1_bmp.surface());
memcpy(bg1_bmp.surface()->pixels, bg1_bmp.mutable_data().data(), bg1_bmp.mutable_data().size());
SDL_UnlockSurface(bg1_bmp.surface());
printf("[ObjectDrawer] Synced BG1 bitmap data to SDL surface (%zu bytes)\n", bg1_bmp.mutable_data().size());
}
if (bg2_bmp.modified() && bg2_bmp.surface() && bg2_bmp.mutable_data().size() > 0) {
SDL_LockSurface(bg2_bmp.surface());
memcpy(bg2_bmp.surface()->pixels, bg2_bmp.mutable_data().data(), bg2_bmp.mutable_data().size());
SDL_UnlockSurface(bg2_bmp.surface());
printf("[ObjectDrawer] Synced BG2 bitmap data to SDL surface (%zu bytes)\n", bg2_bmp.mutable_data().size());
}
return absl::OkStatus();
}
// ============================================================================
// Draw Routine Registry Initialization
// ============================================================================
void ObjectDrawer::InitializeDrawRoutines() {
// This function maps object IDs to their corresponding draw routines.
// The mapping is based on ZScream's DungeonObjectData.cs and the game's assembly code.
// The order of functions in draw_routines_ MUST match the indices used here.
object_to_routine_map_.clear();
draw_routines_.clear();
// Subtype 1 Object Mappings (Horizontal)
object_to_routine_map_[0x00] = 0;
for (int id = 0x01; id <= 0x02; id++) { object_to_routine_map_[id] = 1; }
for (int id = 0x03; id <= 0x04; id++) { object_to_routine_map_[id] = 2; }
for (int id = 0x05; id <= 0x06; id++) { object_to_routine_map_[id] = 3; }
for (int id = 0x07; id <= 0x08; id++) { object_to_routine_map_[id] = 4; }
object_to_routine_map_[0x09] = 5;
for (int id = 0x0A; id <= 0x0B; id++) { object_to_routine_map_[id] = 6; }
// Subtype 1 Object Mappings (Vertical)
object_to_routine_map_[0x60] = 7;
for (int id = 0x61; id <= 0x62; id++) { object_to_routine_map_[id] = 8; }
for (int id = 0x63; id <= 0x64; id++) { object_to_routine_map_[id] = 9; }
for (int id = 0x65; id <= 0x66; id++) { object_to_routine_map_[id] = 10; }
for (int id = 0x67; id <= 0x68; id++) { object_to_routine_map_[id] = 11; }
object_to_routine_map_[0x69] = 12;
for (int id = 0x6A; id <= 0x6B; id++) { object_to_routine_map_[id] = 13; }
object_to_routine_map_[0x6C] = 14;
object_to_routine_map_[0x6D] = 15;
// Subtype 2 Object Mappings
for (int id = 0x100; id <= 0x10F; id++) { object_to_routine_map_[id] = 16; }
// Additional object mappings from logs
object_to_routine_map_[0x33] = 16;
object_to_routine_map_[0xC6] = 7; // Vertical draw
// Initialize draw routine function array in the correct order
draw_routines_.reserve(35);
// Routine 0
draw_routines_.push_back([](ObjectDrawer* self, const RoomObject& obj, gfx::BackgroundBuffer& bg, std::span<const gfx::TileInfo> tiles) {
self->DrawRightwards2x2_1to15or32(obj, bg, tiles);
});
// Routine 1
draw_routines_.push_back([](ObjectDrawer* self, const RoomObject& obj, gfx::BackgroundBuffer& bg, std::span<const gfx::TileInfo> tiles) {
self->DrawRightwards2x4_1to15or26(obj, bg, tiles);
});
// Routine 2
draw_routines_.push_back([](ObjectDrawer* self, const RoomObject& obj, gfx::BackgroundBuffer& bg, std::span<const gfx::TileInfo> tiles) {
self->DrawRightwards2x4spaced4_1to16(obj, bg, tiles);
});
// Routine 3
draw_routines_.push_back([](ObjectDrawer* self, const RoomObject& obj, gfx::BackgroundBuffer& bg, std::span<const gfx::TileInfo> tiles) {
self->DrawRightwards2x4spaced4_1to16_BothBG(obj, bg, tiles);
});
// Routine 4
draw_routines_.push_back([](ObjectDrawer* self, const RoomObject& obj, gfx::BackgroundBuffer& bg, std::span<const gfx::TileInfo> tiles) {
self->DrawRightwards2x2_1to16(obj, bg, tiles);
});
// Routine 5
draw_routines_.push_back([](ObjectDrawer* self, const RoomObject& obj, gfx::BackgroundBuffer& bg, std::span<const gfx::TileInfo> tiles) {
self->DrawDiagonalAcute_1to16(obj, bg, tiles);
});
// Routine 6
draw_routines_.push_back([](ObjectDrawer* self, const RoomObject& obj, gfx::BackgroundBuffer& bg, std::span<const gfx::TileInfo> tiles) {
self->DrawDiagonalGrave_1to16(obj, bg, tiles);
});
// Routine 7
draw_routines_.push_back([](ObjectDrawer* self, const RoomObject& obj, gfx::BackgroundBuffer& bg, std::span<const gfx::TileInfo> tiles) {
self->DrawDownwards2x2_1to15or32(obj, bg, tiles);
});
// Routine 8
draw_routines_.push_back([](ObjectDrawer* self, const RoomObject& obj, gfx::BackgroundBuffer& bg, std::span<const gfx::TileInfo> tiles) {
self->DrawDownwards4x2_1to15or26(obj, bg, tiles);
});
// Routine 9
draw_routines_.push_back([](ObjectDrawer* self, const RoomObject& obj, gfx::BackgroundBuffer& bg, std::span<const gfx::TileInfo> tiles) {
self->DrawDownwards4x2_1to16_BothBG(obj, bg, tiles);
});
// Routine 10
draw_routines_.push_back([](ObjectDrawer* self, const RoomObject& obj, gfx::BackgroundBuffer& bg, std::span<const gfx::TileInfo> tiles) {
self->DrawDownwardsDecor4x2spaced4_1to16(obj, bg, tiles);
});
// Routine 11
draw_routines_.push_back([](ObjectDrawer* self, const RoomObject& obj, gfx::BackgroundBuffer& bg, std::span<const gfx::TileInfo> tiles) {
self->DrawDownwards2x2_1to16(obj, bg, tiles);
});
// Routine 12
draw_routines_.push_back([](ObjectDrawer* self, const RoomObject& obj, gfx::BackgroundBuffer& bg, std::span<const gfx::TileInfo> tiles) {
self->DrawDownwardsHasEdge1x1_1to16_plus3(obj, bg, tiles);
});
// Routine 13
draw_routines_.push_back([](ObjectDrawer* self, const RoomObject& obj, gfx::BackgroundBuffer& bg, std::span<const gfx::TileInfo> tiles) {
self->DrawDownwardsEdge1x1_1to16(obj, bg, tiles);
});
// Routine 14
draw_routines_.push_back([](ObjectDrawer* self, const RoomObject& obj, gfx::BackgroundBuffer& bg, std::span<const gfx::TileInfo> tiles) {
self->DrawDownwardsLeftCorners2x1_1to16_plus12(obj, bg, tiles);
});
// Routine 15
draw_routines_.push_back([](ObjectDrawer* self, const RoomObject& obj, gfx::BackgroundBuffer& bg, std::span<const gfx::TileInfo> tiles) {
self->DrawDownwardsRightCorners2x1_1to16_plus12(obj, bg, tiles);
});
// Routine 16
draw_routines_.push_back([](ObjectDrawer* self, const RoomObject& obj, gfx::BackgroundBuffer& bg, std::span<const gfx::TileInfo> tiles) {
self->DrawRightwards4x4_1to16(obj, bg, tiles);
});
routines_initialized_ = true;
}
int ObjectDrawer::GetDrawRoutineId(int16_t object_id) const {
auto it = object_to_routine_map_.find(object_id);
if (it != object_to_routine_map_.end()) {
return it->second;
}
// Default to simple 1x1 solid for unmapped objects
return -1;
}
// ============================================================================
// Draw Routine Implementations (Based on ZScream patterns)
// ============================================================================
void ObjectDrawer::DrawRightwards2x2_1to15or32(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Draws 2x2 tiles rightward (object 0x00)
// Size byte determines how many times to repeat (1-15 or 32)
int size = obj.size_;
if (size == 0) size = 32; // Special case for object 0x00
for (int s = 0; s < size; s++) {
if (tiles.size() >= 4) {
// Draw 2x2 pattern using 8x8 tiles from the span
WriteTile8(bg, obj.x_ + (s * 2), obj.y_, tiles[0]); // Top-left
WriteTile8(bg, obj.x_ + (s * 2) + 1, obj.y_, tiles[1]); // Top-right
WriteTile8(bg, obj.x_ + (s * 2), obj.y_ + 1, tiles[2]); // Bottom-left
WriteTile8(bg, obj.x_ + (s * 2) + 1, obj.y_ + 1, tiles[3]); // Bottom-right
}
}
}
void ObjectDrawer::DrawRightwards2x4_1to15or26(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Draws 2x4 tiles rightward (objects 0x01-0x02)
int size = obj.size_;
if (size == 0) size = 26; // Special case
for (int s = 0; s < size; s++) {
if (tiles.size() >= 4) {
// For 2x4, we'll use the same tile pattern repeated
WriteTile8(bg, obj.x_ + (s * 2), obj.y_, tiles[0]); // Top-left
WriteTile8(bg, obj.x_ + (s * 2) + 1, obj.y_, tiles[1]); // Top-right
WriteTile8(bg, obj.x_ + (s * 2), obj.y_ + 1, tiles[2]); // Mid-left
WriteTile8(bg, obj.x_ + (s * 2) + 1, obj.y_ + 1, tiles[3]); // Mid-right
WriteTile8(bg, obj.x_ + (s * 2), obj.y_ + 2, tiles[0]); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 2) + 1, obj.y_ + 2, tiles[1]); // Bottom-right (repeat)
WriteTile8(bg, obj.x_ + (s * 2), obj.y_ + 3, tiles[2]); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 2) + 1, obj.y_ + 3, tiles[3]); // Bottom-right (repeat)
}
}
}
void ObjectDrawer::DrawRightwards2x4spaced4_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Draws 2x4 tiles rightward with spacing (objects 0x03-0x04)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 4) {
// Draw 2x4 pattern with spacing using 8x8 tiles from span
WriteTile8(bg, obj.x_ + (s * 6), obj.y_, tiles[0]); // Top-left
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_, tiles[1]); // Top-right
WriteTile8(bg, obj.x_ + (s * 6), obj.y_ + 1, tiles[2]); // Mid-left
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_ + 1, tiles[3]); // Mid-right
WriteTile8(bg, obj.x_ + (s * 6), obj.y_ + 2, tiles[0]); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_ + 2, tiles[1]); // Bottom-right (repeat)
WriteTile8(bg, obj.x_ + (s * 6), obj.y_ + 3, tiles[2]); // Bottom-left (repeat)
WriteTile8(bg, obj.x_ + (s * 6) + 1, obj.y_ + 3, tiles[3]); // Bottom-right (repeat)
}
}
}
void ObjectDrawer::DrawRightwards2x4spaced4_1to16_BothBG(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Same as above but draws to both BG1 and BG2 (objects 0x05-0x06)
DrawRightwards2x4spaced4_1to16(obj, bg, tiles);
// Note: BothBG would require access to both buffers - simplified for now
}
void ObjectDrawer::DrawRightwards2x2_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Draws 2x2 tiles rightward (objects 0x07-0x08)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 4) {
// Draw 2x2 pattern using 8x8 tiles from span
WriteTile8(bg, obj.x_ + (s * 2), obj.y_, tiles[0]); // Top-left
WriteTile8(bg, obj.x_ + (s * 2) + 1, obj.y_, tiles[1]); // Top-right
WriteTile8(bg, obj.x_ + (s * 2), obj.y_ + 1, tiles[2]); // Bottom-left
WriteTile8(bg, obj.x_ + (s * 2) + 1, obj.y_ + 1, tiles[3]); // Bottom-right
}
}
}
void ObjectDrawer::DrawDiagonalAcute_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Diagonal line going down-right (/) (object 0x09)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size + 6; s++) {
if (tiles.size() >= 4) {
// Use first tile span for diagonal pattern
for (int i = 0; i < 5; i++) {
// Cycle through the 4 tiles in the span
const gfx::TileInfo& tile_info = tiles[i % 4];
WriteTile8(bg, obj.x_ + s, obj.y_ + (i - s), tile_info);
}
}
}
}
void ObjectDrawer::DrawDiagonalGrave_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Diagonal line going down-left (\) (objects 0x0A-0x0B)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size + 6; s++) {
if (tiles.size() >= 4) {
// Use first tile span for diagonal pattern
for (int i = 0; i < 5; i++) {
// Cycle through the 4 tiles in the span
const gfx::TileInfo& tile_info = tiles[i % 4];
WriteTile8(bg, obj.x_ + s, obj.y_ + (i + s), tile_info);
}
}
}
}
void ObjectDrawer::DrawDiagonalAcute_1to16_BothBG(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Diagonal acute for both BG layers (objects 0x15-0x1F)
DrawDiagonalAcute_1to16(obj, bg, tiles);
}
void ObjectDrawer::DrawDiagonalGrave_1to16_BothBG(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Diagonal grave for both BG layers (objects 0x16-0x20)
DrawDiagonalGrave_1to16(obj, bg, tiles);
}
void ObjectDrawer::DrawRightwards1x2_1to16_plus2(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: 1x2 tiles rightward with +2 offset (object 0x21)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 2) {
// Use first tile span for 1x2 pattern
WriteTile8(bg, obj.x_ + s + 2, obj.y_, tiles[0]);
WriteTile8(bg, obj.x_ + s + 2, obj.y_ + 1, tiles[1]);
}
}
}
void ObjectDrawer::DrawRightwardsHasEdge1x1_1to16_plus3(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: 1x1 tiles with edge detection +3 offset (object 0x22)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 1) {
// Use first 8x8 tile from span
WriteTile8(bg, obj.x_ + s + 3, obj.y_, tiles[0]);
}
}
}
void ObjectDrawer::DrawRightwardsHasEdge1x1_1to16_plus2(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: 1x1 tiles with edge detection +2 offset (objects 0x23-0x2E)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 1) {
// Use first 8x8 tile from span
WriteTile8(bg, obj.x_ + s + 2, obj.y_, tiles[0]);
}
}
}
void ObjectDrawer::DrawRightwardsTopCorners1x2_1to16_plus13(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Top corner 1x2 tiles with +13 offset (object 0x2F)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 2) {
// Use first tile span for 1x2 pattern
WriteTile8(bg, obj.x_ + s + 13, obj.y_, tiles[0]);
WriteTile8(bg, obj.x_ + s + 13, obj.y_ + 1, tiles[1]);
}
}
}
void ObjectDrawer::DrawRightwardsBottomCorners1x2_1to16_plus13(const RoomObject& obj, gfx::BackgroundBuffer& bg,
const std::span<const gfx::TileInfo> tiles) {
// Pattern: Bottom corner 1x2 tiles with +13 offset (object 0x30)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 2) {
// Use first tile span for 1x2 pattern
WriteTile8(bg, obj.x_ + s + 13, obj.y_ + 1, tiles[0]);
WriteTile8(bg, obj.x_ + s + 13, obj.y_ + 2, tiles[1]);
}
}
}
void ObjectDrawer::CustomDraw(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Custom draw routine (objects 0x31-0x32)
// For now, fall back to simple 1x1
if (tiles.size() >= 1) {
// Use first 8x8 tile from span
WriteTile8(bg, obj.x_, obj.y_, tiles[0]);
}
}
void ObjectDrawer::DrawRightwards4x4_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: 4x4 block rightward (object 0x33)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 16) {
// Draw 4x4 pattern using 8x8 tiles from span
for (int y = 0; y < 4; ++y) {
for (int x = 0; x < 4; ++x) {
WriteTile8(bg, obj.x_ + (s * 4) + x, obj.y_ + y, tiles[y * 4 + x]);
}
}
}
}
}
void ObjectDrawer::DrawRightwards1x1Solid_1to16_plus3(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: 1x1 solid tiles +3 offset (object 0x34)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 1) {
// Use first 8x8 tile from span
WriteTile8(bg, obj.x_ + s + 3, obj.y_, tiles[0]);
}
}
}
void ObjectDrawer::DrawDoorSwitcherer(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Door switcher (object 0x35)
// Special door logic - simplified for now
if (tiles.size() >= 1) {
// Use first 8x8 tile from span
WriteTile8(bg, obj.x_, obj.y_, tiles[0]);
}
}
void ObjectDrawer::DrawRightwardsDecor4x4spaced2_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: 4x4 decoration with spacing (objects 0x36-0x37)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 16) {
// Draw 4x4 pattern with spacing using 8x8 tiles from span
for (int y = 0; y < 4; ++y) {
for (int x = 0; x < 4; ++x) {
WriteTile8(bg, obj.x_ + (s * 6) + x, obj.y_ + y, tiles[y * 4 + x]);
}
}
}
}
}
void ObjectDrawer::DrawRightwardsStatue2x3spaced2_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: 2x3 statue with spacing (object 0x38)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 6) {
// Draw 2x3 pattern using 8x8 tiles from span
for (int y = 0; y < 3; ++y) {
for (int x = 0; x < 2; ++x) {
WriteTile8(bg, obj.x_ + (s * 4) + x, obj.y_ + y, tiles[y * 2 + x]);
}
}
}
}
}
void ObjectDrawer::DrawRightwardsPillar2x4spaced4_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: 2x4 pillar with spacing (objects 0x39, 0x3D)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 8) {
// Draw 2x4 pattern using 8x8 tiles from span
for (int y = 0; y < 4; ++y) {
for (int x = 0; x < 2; ++x) {
WriteTile8(bg, obj.x_ + (s * 6) + x, obj.y_ + y, tiles[y * 2 + x]);
}
}
}
}
}
void ObjectDrawer::DrawRightwardsDecor4x3spaced4_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: 4x3 decoration with spacing (objects 0x3A-0x3B)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 12) {
// Draw 4x3 pattern using 8x8 tiles from span
for (int y = 0; y < 3; ++y) {
for (int x = 0; x < 4; ++x) {
WriteTile8(bg, obj.x_ + (s * 6) + x, obj.y_ + y, tiles[y * 4 + x]);
}
}
}
}
}
void ObjectDrawer::DrawRightwardsDoubled2x2spaced2_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Doubled 2x2 with spacing (object 0x3C)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 8) {
// Draw doubled 2x2 pattern using 8x8 tiles from span
for (int y = 0; y < 2; ++y) {
for (int x = 0; x < 4; ++x) { // Draw a 4x2 area
WriteTile8(bg, obj.x_ + (s * 6) + x, obj.y_ + y, tiles[y * 4 + x]);
}
}
}
}
}
void ObjectDrawer::DrawRightwardsDecor2x2spaced12_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: 2x2 decoration with large spacing (object 0x3E)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 4) {
// Draw 2x2 decoration with 12-tile spacing using 8x8 tiles from span
WriteTile8(bg, obj.x_ + (s * 14), obj.y_, tiles[0]); // Top-left
WriteTile8(bg, obj.x_ + (s * 14) + 1, obj.y_, tiles[1]); // Top-right
WriteTile8(bg, obj.x_ + (s * 14), obj.y_ + 1, tiles[2]); // Bottom-left
WriteTile8(bg, obj.x_ + (s * 14) + 1, obj.y_ + 1, tiles[3]); // Bottom-right
}
}
}
// ============================================================================
// Downwards Draw Routines (Missing Implementation)
// ============================================================================
void ObjectDrawer::DrawDownwards2x2_1to15or32(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Draws 2x2 tiles downward (object 0x60)
// Size byte determines how many times to repeat (1-15 or 32)
int size = obj.size_;
if (size == 0) size = 32; // Special case for object 0x60
for (int s = 0; s < size; s++) {
if (tiles.size() >= 4) {
// Draw 2x2 pattern using 8x8 tiles from the span
WriteTile8(bg, obj.x_, obj.y_ + (s * 2), tiles[0]); // Top-left
WriteTile8(bg, obj.x_ + 1, obj.y_ + (s * 2), tiles[1]); // Top-right
WriteTile8(bg, obj.x_, obj.y_ + (s * 2) + 1, tiles[2]); // Bottom-left
WriteTile8(bg, obj.x_ + 1, obj.y_ + (s * 2) + 1, tiles[3]); // Bottom-right
}
}
}
void ObjectDrawer::DrawDownwards4x2_1to15or26(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Draws 4x2 tiles downward (objects 0x61-0x62)
int size = obj.size_;
if (size == 0) size = 26; // Special case
LOG_DEBUG("ObjectDrawer", "DrawDownwards4x2_1to15or26: obj=%04X tiles=%zu size=%d",
obj.id_, tiles.size(), size);
for (int s = 0; s < size; s++) {
if (tiles.size() >= 8) {
// Draw 4x2 pattern using 8 tiles from span
// Top row: tiles 0,1,2,3
WriteTile8(bg, obj.x_, obj.y_ + (s * 2), tiles[0]); // Top-left
WriteTile8(bg, obj.x_ + 1, obj.y_ + (s * 2), tiles[1]); // Top-right
WriteTile8(bg, obj.x_ + 2, obj.y_ + (s * 2), tiles[2]); // Top-middle-left
WriteTile8(bg, obj.x_ + 3, obj.y_ + (s * 2), tiles[3]); // Top-middle-right
// Bottom row: tiles 4,5,6,7
WriteTile8(bg, obj.x_, obj.y_ + (s * 2) + 1, tiles[4]); // Bottom-left
WriteTile8(bg, obj.x_ + 1, obj.y_ + (s * 2) + 1, tiles[5]); // Bottom-right
WriteTile8(bg, obj.x_ + 2, obj.y_ + (s * 2) + 1, tiles[6]); // Bottom-middle-left
WriteTile8(bg, obj.x_ + 3, obj.y_ + (s * 2) + 1, tiles[7]); // Bottom-middle-right
} else if (tiles.size() >= 4) {
// Fallback: use only first 4 tiles and repeat
WriteTile8(bg, obj.x_, obj.y_ + (s * 2), tiles[0]);
WriteTile8(bg, obj.x_ + 1, obj.y_ + (s * 2), tiles[1]);
WriteTile8(bg, obj.x_ + 2, obj.y_ + (s * 2), tiles[0]);
WriteTile8(bg, obj.x_ + 3, obj.y_ + (s * 2), tiles[1]);
WriteTile8(bg, obj.x_, obj.y_ + (s * 2) + 1, tiles[2]);
WriteTile8(bg, obj.x_ + 1, obj.y_ + (s * 2) + 1, tiles[3]);
WriteTile8(bg, obj.x_ + 2, obj.y_ + (s * 2) + 1, tiles[2]);
WriteTile8(bg, obj.x_ + 3, obj.y_ + (s * 2) + 1, tiles[3]);
}
}
}
void ObjectDrawer::DrawDownwards4x2_1to16_BothBG(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Same as above but draws to both BG1 and BG2 (objects 0x63-0x64)
DrawDownwards4x2_1to15or26(obj, bg, tiles);
// Note: BothBG would require access to both buffers - simplified for now
}
void ObjectDrawer::DrawDownwardsDecor4x2spaced4_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Draws 4x2 decoration downward with spacing (objects 0x65-0x66)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 8) {
// Draw 4x2 pattern with spacing using 8x8 tiles from span
WriteTile8(bg, obj.x_, obj.y_ + (s * 6), tiles[0]);
WriteTile8(bg, obj.x_ + 1, obj.y_ + (s * 6), tiles[1]);
WriteTile8(bg, obj.x_ + 2, obj.y_ + (s * 6), tiles[2]);
WriteTile8(bg, obj.x_ + 3, obj.y_ + (s * 6), tiles[3]);
WriteTile8(bg, obj.x_, obj.y_ + (s * 6) + 1, tiles[4]);
WriteTile8(bg, obj.x_ + 1, obj.y_ + (s * 6) + 1, tiles[5]);
WriteTile8(bg, obj.x_ + 2, obj.y_ + (s * 6) + 1, tiles[6]);
WriteTile8(bg, obj.x_ + 3, obj.y_ + (s * 6) + 1, tiles[7]);
}
}
}
void ObjectDrawer::DrawDownwards2x2_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Draws 2x2 tiles downward (objects 0x67-0x68)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 4) {
// Draw 2x2 pattern using 8x8 tiles from span
WriteTile8(bg, obj.x_, obj.y_ + (s * 2), tiles[0]);
WriteTile8(bg, obj.x_ + 1, obj.y_ + (s * 2), tiles[1]);
WriteTile8(bg, obj.x_, obj.y_ + (s * 2) + 1, tiles[2]);
WriteTile8(bg, obj.x_ + 1, obj.y_ + (s * 2) + 1, tiles[3]);
}
}
}
void ObjectDrawer::DrawDownwardsHasEdge1x1_1to16_plus3(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: 1x1 tiles with edge detection +3 offset downward (object 0x69)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 1) {
// Use first 8x8 tile from span
WriteTile8(bg, obj.x_ + 3, obj.y_ + s, tiles[0]);
}
}
}
void ObjectDrawer::DrawDownwardsEdge1x1_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: 1x1 edge tiles downward (objects 0x6A-0x6B)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 1) {
// Use first 8x8 tile from span
WriteTile8(bg, obj.x_, obj.y_ + s, tiles[0]);
}
}
}
void ObjectDrawer::DrawDownwardsLeftCorners2x1_1to16_plus12(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Left corner 2x1 tiles with +12 offset downward (object 0x6C)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 2) {
// Use first tile span for 2x1 pattern
WriteTile8(bg, obj.x_ + 12, obj.y_ + s, tiles[0]);
WriteTile8(bg, obj.x_ + 12 + 1, obj.y_ + s, tiles[1]);
}
}
}
void ObjectDrawer::DrawDownwardsRightCorners2x1_1to16_plus12(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles) {
// Pattern: Right corner 2x1 tiles with +12 offset downward (object 0x6D)
int size = obj.size_ & 0x0F;
for (int s = 0; s < size; s++) {
if (tiles.size() >= 2) {
// Use first tile span for 2x1 pattern
WriteTile8(bg, obj.x_ + 12, obj.y_ + s, tiles[0]);
WriteTile8(bg, obj.x_ + 12 + 1, obj.y_ + s, tiles[1]);
}
}
}
// ============================================================================
// Utility Methods
// ============================================================================
void ObjectDrawer::WriteTile8(gfx::BackgroundBuffer& bg, int tile_x, int tile_y,
const gfx::TileInfo& tile_info) {
// Draw directly to bitmap instead of tile buffer to avoid being overwritten
auto& bitmap = bg.bitmap();
if (!bitmap.is_active() || bitmap.width() == 0) {
return; // Bitmap not ready
}
// The room-specific graphics buffer (current_gfx16_) contains the assembled
// tile graphics for the current room. Object tile IDs are relative to this buffer.
const uint8_t* gfx_data = room_gfx_buffer_;
if (!gfx_data) {
LOG_DEBUG("ObjectDrawer", "ERROR: No graphics data available");
return;
}
// Draw single 8x8 tile directly to bitmap
DrawTileToBitmap(bitmap, tile_info, tile_x * 8, tile_y * 8, gfx_data);
}
bool ObjectDrawer::IsValidTilePosition(int tile_x, int tile_y) const {
return tile_x >= 0 && tile_x < kMaxTilesX &&
tile_y >= 0 && tile_y < kMaxTilesY;
}
void ObjectDrawer::DrawTileToBitmap(gfx::Bitmap& bitmap, const gfx::TileInfo& tile_info,
int pixel_x, int pixel_y, const uint8_t* tiledata) {
// Draw an 8x8 tile directly to bitmap at pixel coordinates
if (!tiledata) return;
// DEBUG: Check if bitmap is valid
if (!bitmap.is_active() || bitmap.width() == 0 || bitmap.height() == 0) {
LOG_DEBUG("ObjectDrawer", "ERROR: Invalid bitmap - active=%d, size=%dx%d",
bitmap.is_active(), bitmap.width(), bitmap.height());
return;
}
// Calculate tile position in graphics sheet (128 pixels wide, 16 tiles per row)
int tile_sheet_x = (tile_info.id_ % 16) * 8; // 16 tiles per row
int tile_sheet_y = (tile_info.id_ / 16) * 8; // Each row is 16 tiles
// Palettes are 3bpp (8 colors). Convert palette index to base color offset.
uint8_t palette_offset = (tile_info.palette_ & 0x0F) * 8;
// DEBUG: Log tile info for first few tiles
static int debug_tile_count = 0;
if (debug_tile_count < 5) {
printf("[ObjectDrawer] DrawTile: id=0x%03X pos=(%d,%d) sheet=(%d,%d) pal=%d mirror=(h:%d,v:%d)\n",
tile_info.id_, pixel_x, pixel_y, tile_sheet_x, tile_sheet_y,
tile_info.palette_, tile_info.horizontal_mirror_, tile_info.vertical_mirror_);
debug_tile_count++;
}
// Draw 8x8 pixels
int pixels_written = 0;
int pixels_transparent = 0;
for (int py = 0; py < 8; py++) {
for (int px = 0; px < 8; px++) {
// Apply mirroring
int src_x = tile_info.horizontal_mirror_ ? (7 - px) : px;
int src_y = tile_info.vertical_mirror_ ? (7 - py) : py;
// Read pixel from graphics sheet
int src_index = (tile_sheet_y + src_y) * 128 + (tile_sheet_x + src_x);
uint8_t pixel_index = tiledata[src_index];
if (pixel_index == 0) {
pixels_transparent++;
continue;
}
uint8_t final_color = pixel_index + palette_offset;
int dest_x = pixel_x + px;
int dest_y = pixel_y + py;
if (dest_x >= 0 && dest_x < bitmap.width() && dest_y >= 0 && dest_y < bitmap.height()) {
int dest_index = dest_y * bitmap.width() + dest_x;
if (dest_index >= 0 && dest_index < static_cast<int>(bitmap.mutable_data().size())) {
bitmap.mutable_data()[dest_index] = final_color;
pixels_written++;
}
}
}
}
// Mark bitmap as modified if we wrote any pixels
if (pixels_written > 0) {
bitmap.set_modified(true);
}
// DEBUG: Log pixel writing stats for first few tiles
if (debug_tile_count < 5) {
printf("[ObjectDrawer] Tile 0x%03X: wrote %d pixels, %d transparent, src_index_range=[%d,%d]\n",
tile_info.id_, pixels_written, pixels_transparent,
(tile_sheet_y * 128 + tile_sheet_x),
(tile_sheet_y + 7) * 128 + (tile_sheet_x + 7));
}
}
} // namespace zelda3
} // namespace yaze

View File

@@ -1,183 +0,0 @@
#ifndef YAZE_APP_ZELDA3_DUNGEON_OBJECT_DRAWER_H
#define YAZE_APP_ZELDA3_DUNGEON_OBJECT_DRAWER_H
#include <vector>
#include <unordered_map>
#include <functional>
#include "absl/status/status.h"
#include "app/gfx/background_buffer.h"
#include "app/gfx/snes_tile.h"
#include "app/gfx/snes_palette.h"
#include "app/rom.h"
#include "app/zelda3/dungeon/room_object.h"
namespace yaze {
namespace zelda3 {
/**
* @brief Draws dungeon objects to background buffers using game patterns
*
* This class interprets object IDs and draws them to BG1/BG2 buffers
* using the patterns extracted from the game's drawing routines.
* Based on ZScream's DungeonObjectData.cs and Subtype1_Draw.cs patterns.
*
* Architecture:
* 1. Load tile data from ROM for the object
* 2. Look up draw routine ID from object ID mapping
* 3. Execute appropriate draw routine with size/orientation
* 4. Write tiles to BackgroundBuffer according to pattern
* 5. Handle palette coordination and graphics sheet access
*/
class ObjectDrawer {
public:
explicit ObjectDrawer(Rom* rom, const uint8_t* room_gfx_buffer = nullptr);
/**
* @brief Draw a room object to background buffers
* @param object The object to draw
* @param bg1 Background layer 1 buffer
* @param bg2 Background layer 2 buffer
* @param palette_group Current palette group for color mapping
* @return Status of the drawing operation
*/
absl::Status DrawObject(const RoomObject& object,
gfx::BackgroundBuffer& bg1,
gfx::BackgroundBuffer& bg2,
const gfx::PaletteGroup& palette_group);
/**
* @brief Draw all objects in a room
* @param objects Vector of room objects
* @param bg1 Background layer 1 buffer
* @param bg2 Background layer 2 buffer
* @param palette_group Current palette group for color mapping
* @return Status of the drawing operation
*/
absl::Status DrawObjectList(const std::vector<RoomObject>& objects,
gfx::BackgroundBuffer& bg1,
gfx::BackgroundBuffer& bg2,
const gfx::PaletteGroup& palette_group);
/**
* @brief Get draw routine ID for an object
* @param object_id The object ID to look up
* @return Draw routine ID (0-24) based on ZScream mapping
*/
int GetDrawRoutineId(int16_t object_id) const;
/**
* @brief Initialize draw routine registry
* Must be called before drawing objects
*/
void InitializeDrawRoutines();
/**
* @brief Draw a single tile directly to bitmap
* @param bitmap Target bitmap to draw to
* @param tile_info Tile information (ID, palette, mirroring)
* @param pixel_x X pixel coordinate in bitmap
* @param pixel_y Y pixel coordinate in bitmap
* @param tiledata Source graphics data from ROM
*/
void DrawTileToBitmap(gfx::Bitmap& bitmap, const gfx::TileInfo& tile_info,
int pixel_x, int pixel_y, const uint8_t* tiledata);
private:
// Draw routine function type
using DrawRoutine = std::function<void(ObjectDrawer*, const RoomObject&, gfx::BackgroundBuffer&,
std::span<const gfx::TileInfo>)>;
// Core draw routines (based on ZScream's subtype1_routines table)
void DrawRightwards2x2_1to15or32(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawRightwards2x4_1to15or26(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawRightwards2x4spaced4_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawRightwards2x4spaced4_1to16_BothBG(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawRightwards2x2_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawDiagonalAcute_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawDiagonalGrave_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawDiagonalAcute_1to16_BothBG(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawDiagonalGrave_1to16_BothBG(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawRightwards1x2_1to16_plus2(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawRightwardsHasEdge1x1_1to16_plus3(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawRightwardsHasEdge1x1_1to16_plus2(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawRightwardsTopCorners1x2_1to16_plus13(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawRightwardsBottomCorners1x2_1to16_plus13(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void CustomDraw(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawRightwards4x4_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawRightwards1x1Solid_1to16_plus3(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawDoorSwitcherer(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawRightwardsDecor4x4spaced2_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawRightwardsStatue2x3spaced2_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawRightwardsPillar2x4spaced4_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawRightwardsDecor4x3spaced4_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawRightwardsDoubled2x2spaced2_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawRightwardsDecor2x2spaced12_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
// Downwards draw routines (missing implementation)
void DrawDownwards2x2_1to15or32(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawDownwards4x2_1to15or26(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawDownwards4x2_1to16_BothBG(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawDownwardsDecor4x2spaced4_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawDownwards2x2_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawDownwardsHasEdge1x1_1to16_plus3(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawDownwardsEdge1x1_1to16(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawDownwardsLeftCorners2x1_1to16_plus12(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
void DrawDownwardsRightCorners2x1_1to16_plus12(const RoomObject& obj, gfx::BackgroundBuffer& bg,
std::span<const gfx::TileInfo> tiles);
// Utility methods
void WriteTile8(gfx::BackgroundBuffer& bg, int tile_x, int tile_y,
const gfx::TileInfo& tile_info);
bool IsValidTilePosition(int tile_x, int tile_y) const;
// Draw routine registry
std::unordered_map<int16_t, int> object_to_routine_map_;
std::vector<DrawRoutine> draw_routines_;
bool routines_initialized_ = false;
Rom* rom_;
const uint8_t* room_gfx_buffer_; // Room-specific graphics buffer (current_gfx16_)
// Canvas dimensions in tiles (64x64 = 512x512 pixels)
static constexpr int kMaxTilesX = 64;
static constexpr int kMaxTilesY = 64;
};
} // namespace zelda3
} // namespace yaze
#endif // YAZE_APP_ZELDA3_DUNGEON_OBJECT_DRAWER_H

View File

@@ -1,394 +0,0 @@
#include "object_parser.h"
#include <algorithm>
#include <cstring>
#include "absl/strings/str_format.h"
#include "app/zelda3/dungeon/room_object.h"
#include "util/log.h"
// ROM addresses for object data (PC addresses, not SNES)
static constexpr int kRoomObjectSubtype1 = 0x0A8000;
static constexpr int kRoomObjectSubtype2 = 0x0A9000;
static constexpr int kRoomObjectSubtype3 = 0x0AA000;
static constexpr int kRoomObjectTileAddress = 0x0AB000;
namespace yaze {
namespace zelda3 {
absl::StatusOr<std::vector<gfx::TileInfo>> ObjectParser::ParseObject(int16_t object_id) {
if (rom_ == nullptr) {
return absl::InvalidArgumentError("ROM is null");
}
int subtype = DetermineSubtype(object_id);
switch (subtype) {
case 1:
return ParseSubtype1(object_id);
case 2:
return ParseSubtype2(object_id);
case 3:
return ParseSubtype3(object_id);
default:
return absl::InvalidArgumentError(
absl::StrFormat("Invalid object subtype for ID: %#04x", object_id));
}
}
absl::StatusOr<ObjectRoutineInfo> ObjectParser::ParseObjectRoutine(int16_t object_id) {
if (rom_ == nullptr) {
return absl::InvalidArgumentError("ROM is null");
}
auto subtype_info = GetObjectSubtype(object_id);
if (!subtype_info.ok()) {
return subtype_info.status();
}
ObjectRoutineInfo routine_info;
routine_info.routine_ptr = subtype_info->routine_ptr;
routine_info.tile_ptr = subtype_info->subtype_ptr;
routine_info.tile_count = subtype_info->max_tile_count;
routine_info.is_repeatable = true;
routine_info.is_orientation_dependent = true;
return routine_info;
}
absl::StatusOr<ObjectSubtypeInfo> ObjectParser::GetObjectSubtype(int16_t object_id) {
ObjectSubtypeInfo info;
info.subtype = DetermineSubtype(object_id);
switch (info.subtype) {
case 1: {
int index = object_id & 0xFF;
info.subtype_ptr = kRoomObjectSubtype1 + (index * 2);
info.routine_ptr = kRoomObjectSubtype1 + 0x200 + (index * 2);
info.max_tile_count = 8; // Most subtype 1 objects use 8 tiles
break;
}
case 2: {
int index = object_id & 0x7F;
info.subtype_ptr = kRoomObjectSubtype2 + (index * 2);
info.routine_ptr = kRoomObjectSubtype2 + 0x80 + (index * 2);
info.max_tile_count = 8;
break;
}
case 3: {
int index = object_id & 0xFF;
info.subtype_ptr = kRoomObjectSubtype3 + (index * 2);
info.routine_ptr = kRoomObjectSubtype3 + 0x100 + (index * 2);
info.max_tile_count = 8;
break;
}
default:
return absl::InvalidArgumentError(
absl::StrFormat("Invalid object subtype for ID: %#04x", object_id));
}
return info;
}
absl::StatusOr<ObjectSizeInfo> ObjectParser::ParseObjectSize(int16_t object_id, uint8_t size_byte) {
ObjectSizeInfo info;
// Extract size bits (0-3 for X, 4-7 for Y)
int size_x = size_byte & 0x03;
int size_y = (size_byte >> 2) & 0x03;
info.width_tiles = (size_x + 1) * 2; // Convert to tile count
info.height_tiles = (size_y + 1) * 2;
// Determine orientation based on object ID and size
// This is a heuristic based on the object naming patterns
if (object_id >= 0x80 && object_id <= 0xFF) {
// Objects 0x80-0xFF are typically vertical
info.is_horizontal = false;
} else {
// Objects 0x00-0x7F are typically horizontal
info.is_horizontal = true;
}
// Determine if object is repeatable
info.is_repeatable = (size_byte != 0);
info.repeat_count = size_byte == 0 ? 32 : size_byte;
return info;
}
absl::StatusOr<std::vector<gfx::TileInfo>> ObjectParser::ParseSubtype1(int16_t object_id) {
int index = object_id & 0xFF;
int tile_ptr = kRoomObjectSubtype1 + (index * 2);
if (tile_ptr + 1 >= (int)rom_->size()) {
return absl::OutOfRangeError(
absl::StrFormat("Tile pointer out of range: %#06x", tile_ptr));
}
// Read tile data pointer
uint8_t low = rom_->data()[tile_ptr];
uint8_t high = rom_->data()[tile_ptr + 1];
int tile_data_ptr = kRoomObjectTileAddress + ((high << 8) | low);
// Read 8 tiles (most subtype 1 objects use 8 tiles)
return ReadTileData(tile_data_ptr, 8);
}
absl::StatusOr<std::vector<gfx::TileInfo>> ObjectParser::ParseSubtype2(int16_t object_id) {
int index = object_id & 0x7F;
int tile_ptr = kRoomObjectSubtype2 + (index * 2);
if (tile_ptr + 1 >= (int)rom_->size()) {
return absl::OutOfRangeError(
absl::StrFormat("Tile pointer out of range: %#06x", tile_ptr));
}
// Read tile data pointer
uint8_t low = rom_->data()[tile_ptr];
uint8_t high = rom_->data()[tile_ptr + 1];
int tile_data_ptr = kRoomObjectTileAddress + ((high << 8) | low);
// Read 8 tiles
return ReadTileData(tile_data_ptr, 8);
}
absl::StatusOr<std::vector<gfx::TileInfo>> ObjectParser::ParseSubtype3(int16_t object_id) {
int index = object_id & 0xFF;
int tile_ptr = kRoomObjectSubtype3 + (index * 2);
if (tile_ptr + 1 >= (int)rom_->size()) {
return absl::OutOfRangeError(
absl::StrFormat("Tile pointer out of range: %#06x", tile_ptr));
}
// Read tile data pointer
uint8_t low = rom_->data()[tile_ptr];
uint8_t high = rom_->data()[tile_ptr + 1];
int tile_data_ptr = kRoomObjectTileAddress + ((high << 8) | low);
// Read 8 tiles
return ReadTileData(tile_data_ptr, 8);
}
absl::StatusOr<std::vector<gfx::TileInfo>> ObjectParser::ReadTileData(int address, int tile_count) {
// Each tile is stored as a 16-bit word (2 bytes), not 8 bytes!
// ZScream: tiles.Add(new Tile(ROM.DATA[pos + ((i * 2))], ROM.DATA[pos + ((i * 2)) + 1]));
if (address < 0 || address + (tile_count * 2) >= (int)rom_->size()) {
return absl::OutOfRangeError(
absl::StrFormat("Tile data address out of range: %#06x", address));
}
std::vector<gfx::TileInfo> tiles;
tiles.reserve(tile_count);
// DEBUG: Log first tile read
static int debug_read_count = 0;
bool should_log = (debug_read_count < 3);
for (int i = 0; i < tile_count; i++) {
int tile_offset = address + (i * 2); // 2 bytes per tile word
// Read 1 word (2 bytes) per tile - this is the SNES tile format
uint16_t tile_word = rom_->data()[tile_offset] | (rom_->data()[tile_offset + 1] << 8);
auto tile_info = gfx::WordToTileInfo(tile_word);
tiles.push_back(tile_info);
// DEBUG: Log first few tiles
if (should_log && i < 4) {
printf("[ObjectParser] ReadTile[%d]: addr=0x%06X word=0x%04X → id=0x%03X pal=%d mirror=(h:%d,v:%d)\n",
i, tile_offset, tile_word, tile_info.id_, tile_info.palette_,
tile_info.horizontal_mirror_, tile_info.vertical_mirror_);
}
}
if (should_log) {
printf("[ObjectParser] ReadTileData: addr=0x%06X count=%d → loaded %zu tiles\n",
address, tile_count, tiles.size());
debug_read_count++;
}
return tiles;
}
int ObjectParser::DetermineSubtype(int16_t object_id) const {
if (object_id >= 0x200) {
return 3;
} else if (object_id >= 0x100) {
return 2;
} else {
return 1;
}
}
ObjectDrawInfo ObjectParser::GetObjectDrawInfo(int16_t object_id) const {
ObjectDrawInfo info;
// Map object ID to draw routine based on ZScream's subtype1_routines table
// This is based on the DungeonObjectData.cs mapping from ZScream
if (object_id == 0x00) {
info.draw_routine_id = 0; // RoomDraw_Rightwards2x2_1to15or32
info.routine_name = "Rightwards2x2_1to15or32";
info.tile_count = 4;
info.is_horizontal = true;
}
else if (object_id >= 0x01 && object_id <= 0x02) {
info.draw_routine_id = 1; // RoomDraw_Rightwards2x4_1to15or26
info.routine_name = "Rightwards2x4_1to15or26";
info.tile_count = 8;
info.is_horizontal = true;
}
else if (object_id >= 0x03 && object_id <= 0x04) {
info.draw_routine_id = 2; // RoomDraw_Rightwards2x4spaced4_1to16
info.routine_name = "Rightwards2x4spaced4_1to16";
info.tile_count = 8;
info.is_horizontal = true;
}
else if (object_id >= 0x05 && object_id <= 0x06) {
info.draw_routine_id = 3; // RoomDraw_Rightwards2x4spaced4_1to16_BothBG
info.routine_name = "Rightwards2x4spaced4_1to16_BothBG";
info.tile_count = 8;
info.is_horizontal = true;
info.both_layers = true;
}
else if (object_id >= 0x07 && object_id <= 0x08) {
info.draw_routine_id = 4; // RoomDraw_Rightwards2x2_1to16
info.routine_name = "Rightwards2x2_1to16";
info.tile_count = 4;
info.is_horizontal = true;
}
else if (object_id == 0x09) {
info.draw_routine_id = 5; // RoomDraw_DiagonalAcute_1to16
info.routine_name = "DiagonalAcute_1to16";
info.tile_count = 5;
info.is_horizontal = false;
}
else if (object_id >= 0x0A && object_id <= 0x0B) {
info.draw_routine_id = 6; // RoomDraw_DiagonalGrave_1to16
info.routine_name = "DiagonalGrave_1to16";
info.tile_count = 5;
info.is_horizontal = false;
}
else if (object_id >= 0x15 && object_id <= 0x1F) {
info.draw_routine_id = 7; // RoomDraw_DiagonalAcute_1to16_BothBG
info.routine_name = "DiagonalAcute_1to16_BothBG";
info.tile_count = 5;
info.is_horizontal = false;
info.both_layers = true;
}
else if (object_id >= 0x16 && object_id <= 0x20) {
info.draw_routine_id = 8; // RoomDraw_DiagonalGrave_1to16_BothBG
info.routine_name = "DiagonalGrave_1to16_BothBG";
info.tile_count = 5;
info.is_horizontal = false;
info.both_layers = true;
}
else if (object_id == 0x21) {
info.draw_routine_id = 9; // RoomDraw_Rightwards1x2_1to16_plus2
info.routine_name = "Rightwards1x2_1to16_plus2";
info.tile_count = 2;
info.is_horizontal = true;
}
else if (object_id == 0x22) {
info.draw_routine_id = 10; // RoomDraw_RightwardsHasEdge1x1_1to16_plus3
info.routine_name = "RightwardsHasEdge1x1_1to16_plus3";
info.tile_count = 1;
info.is_horizontal = true;
}
else if (object_id >= 0x23 && object_id <= 0x2E) {
info.draw_routine_id = 11; // RoomDraw_RightwardsHasEdge1x1_1to16_plus2
info.routine_name = "RightwardsHasEdge1x1_1to16_plus2";
info.tile_count = 1;
info.is_horizontal = true;
}
else if (object_id == 0x2F) {
info.draw_routine_id = 12; // RoomDraw_RightwardsTopCorners1x2_1to16_plus13
info.routine_name = "RightwardsTopCorners1x2_1to16_plus13";
info.tile_count = 2;
info.is_horizontal = true;
}
else if (object_id == 0x30) {
info.draw_routine_id = 13; // RoomDraw_RightwardsBottomCorners1x2_1to16_plus13
info.routine_name = "RightwardsBottomCorners1x2_1to16_plus13";
info.tile_count = 2;
info.is_horizontal = true;
}
else if (object_id >= 0x31 && object_id <= 0x32) {
info.draw_routine_id = 14; // CustomDraw
info.routine_name = "CustomDraw";
info.tile_count = 1;
}
else if (object_id == 0x33) {
info.draw_routine_id = 15; // RoomDraw_Rightwards4x4_1to16
info.routine_name = "Rightwards4x4_1to16";
info.tile_count = 16;
info.is_horizontal = true;
}
else if (object_id == 0x34) {
info.draw_routine_id = 16; // RoomDraw_Rightwards1x1Solid_1to16_plus3
info.routine_name = "Rightwards1x1Solid_1to16_plus3";
info.tile_count = 1;
info.is_horizontal = true;
}
else if (object_id == 0x35) {
info.draw_routine_id = 17; // RoomDraw_DoorSwitcherer
info.routine_name = "DoorSwitcherer";
info.tile_count = 1;
}
else if (object_id >= 0x36 && object_id <= 0x37) {
info.draw_routine_id = 18; // RoomDraw_RightwardsDecor4x4spaced2_1to16
info.routine_name = "RightwardsDecor4x4spaced2_1to16";
info.tile_count = 16;
info.is_horizontal = true;
}
else if (object_id == 0x38) {
info.draw_routine_id = 19; // RoomDraw_RightwardsStatue2x3spaced2_1to16
info.routine_name = "RightwardsStatue2x3spaced2_1to16";
info.tile_count = 6;
info.is_horizontal = true;
}
else if (object_id == 0x39 || object_id == 0x3D) {
info.draw_routine_id = 20; // RoomDraw_RightwardsPillar2x4spaced4_1to16
info.routine_name = "RightwardsPillar2x4spaced4_1to16";
info.tile_count = 8;
info.is_horizontal = true;
}
else if (object_id >= 0x3A && object_id <= 0x3B) {
info.draw_routine_id = 21; // RoomDraw_RightwardsDecor4x3spaced4_1to16
info.routine_name = "RightwardsDecor4x3spaced4_1to16";
info.tile_count = 12;
info.is_horizontal = true;
}
else if (object_id == 0x3C) {
info.draw_routine_id = 22; // RoomDraw_RightwardsDoubled2x2spaced2_1to16
info.routine_name = "RightwardsDoubled2x2spaced2_1to16";
info.tile_count = 8;
info.is_horizontal = true;
}
else if (object_id == 0x3E) {
info.draw_routine_id = 23; // RoomDraw_RightwardsDecor2x2spaced12_1to16
info.routine_name = "RightwardsDecor2x2spaced12_1to16";
info.tile_count = 4;
info.is_horizontal = true;
}
else if (object_id >= 0x3F && object_id <= 0x40) {
info.draw_routine_id = 24; // RoomDraw_RightwardsHasEdge1x1_1to16_plus2 (variant)
info.routine_name = "RightwardsHasEdge1x1_1to16_plus2_variant";
info.tile_count = 1;
info.is_horizontal = true;
}
else {
// Default to simple 1x1 solid for unmapped objects
info.draw_routine_id = 16; // Use solid block routine
info.routine_name = "DefaultSolid";
info.tile_count = 1;
info.is_horizontal = true;
}
return info;
}
} // namespace zelda3
} // namespace yaze

View File

@@ -1,172 +0,0 @@
#ifndef YAZE_APP_ZELDA3_DUNGEON_OBJECT_PARSER_H
#define YAZE_APP_ZELDA3_DUNGEON_OBJECT_PARSER_H
#include <cstdint>
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/gfx/snes_tile.h"
#include "app/rom.h"
namespace yaze {
namespace zelda3 {
/**
* @brief Object routine information
*/
struct ObjectRoutineInfo {
uint32_t routine_ptr;
uint32_t tile_ptr;
int tile_count;
bool is_repeatable;
bool is_orientation_dependent;
ObjectRoutineInfo()
: routine_ptr(0),
tile_ptr(0),
tile_count(0),
is_repeatable(false),
is_orientation_dependent(false) {}
};
/**
* @brief Object subtype information
*/
struct ObjectSubtypeInfo {
int subtype;
uint32_t subtype_ptr;
uint32_t routine_ptr;
int max_tile_count;
ObjectSubtypeInfo()
: subtype(0), subtype_ptr(0), routine_ptr(0), max_tile_count(0) {}
};
/**
* @brief Object size and orientation information
*/
struct ObjectSizeInfo {
int width_tiles;
int height_tiles;
bool is_horizontal;
bool is_repeatable;
int repeat_count;
ObjectSizeInfo()
: width_tiles(0),
height_tiles(0),
is_horizontal(true),
is_repeatable(false),
repeat_count(1) {}
};
/**
* @brief Draw routine information for object rendering
*/
struct ObjectDrawInfo {
int draw_routine_id; // Which drawing pattern to use (0-24)
int tile_count; // How many tiles this object has
bool is_horizontal; // Orientation
bool is_vertical;
bool both_layers; // Draw to both BG1 and BG2
std::string routine_name; // Human-readable routine name
ObjectDrawInfo()
: draw_routine_id(0),
tile_count(1),
is_horizontal(true),
is_vertical(false),
both_layers(false),
routine_name("Unknown") {}
};
/**
* @brief Direct ROM parser for dungeon objects
*
* This class replaces the SNES emulation approach with direct ROM parsing,
* providing better performance and reliability for object rendering.
*/
class ObjectParser {
public:
explicit ObjectParser(Rom* rom) : rom_(rom) {}
/**
* @brief Parse object data directly from ROM
*
* @param object_id The object ID to parse
* @return StatusOr containing the parsed tile data
*/
absl::StatusOr<std::vector<gfx::TileInfo>> ParseObject(int16_t object_id);
/**
* @brief Parse object routine data
*
* @param object_id The object ID
* @return StatusOr containing routine information
*/
absl::StatusOr<ObjectRoutineInfo> ParseObjectRoutine(int16_t object_id);
/**
* @brief Get object subtype information
*
* @param object_id The object ID
* @return StatusOr containing subtype information
*/
absl::StatusOr<ObjectSubtypeInfo> GetObjectSubtype(int16_t object_id);
/**
* @brief Parse object size and orientation
*
* @param object_id The object ID
* @param size_byte The size byte from object data
* @return StatusOr containing size and orientation info
*/
absl::StatusOr<ObjectSizeInfo> ParseObjectSize(int16_t object_id,
uint8_t size_byte);
/**
* @brief Determine object subtype from ID
*/
int DetermineSubtype(int16_t object_id) const;
/**
* @brief Get draw routine information for an object
* @param object_id The object ID to look up
* @return ObjectDrawInfo containing draw routine details
*/
ObjectDrawInfo GetObjectDrawInfo(int16_t object_id) const;
private:
/**
* @brief Parse subtype 1 objects (0x00-0xFF)
*/
absl::StatusOr<std::vector<gfx::TileInfo>> ParseSubtype1(int16_t object_id);
/**
* @brief Parse subtype 2 objects (0x100-0x1FF)
*/
absl::StatusOr<std::vector<gfx::TileInfo>> ParseSubtype2(int16_t object_id);
/**
* @brief Parse subtype 3 objects (0x200+)
*/
absl::StatusOr<std::vector<gfx::TileInfo>> ParseSubtype3(int16_t object_id);
/**
* @brief Read tile data from ROM
*
* @param address The address to read from
* @param tile_count Number of tiles to read
* @return StatusOr containing tile data
*/
absl::StatusOr<std::vector<gfx::TileInfo>> ReadTileData(int address,
int tile_count);
Rom* rom_;
};
} // namespace zelda3
} // namespace yaze
#endif // YAZE_APP_ZELDA3_DUNGEON_OBJECT_PARSER_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,856 +0,0 @@
#ifndef YAZE_APP_ZELDA3_DUNGEON_ROOM_H
#define YAZE_APP_ZELDA3_DUNGEON_ROOM_H
#include <yaze.h>
#include <cstdint>
#include <string_view>
#include <vector>
#include "app/rom.h"
#include "app/gfx/background_buffer.h"
#include "app/zelda3/dungeon/dungeon_rom_addresses.h"
#include "app/zelda3/dungeon/room_object.h"
#include "app/zelda3/dungeon/room_layout.h"
#include "app/zelda3/sprite/sprite.h"
namespace yaze {
namespace zelda3 {
// ROM addresses moved to dungeon_rom_addresses.h for better organization
// Use kPrefixedNames for new code (clean naming convention)
// Legacy aliases for backward compatibility (gradual migration)
constexpr int room_object_layout_pointer = kRoomObjectLayoutPointer;
constexpr int room_object_pointer = kRoomObjectPointer;
constexpr int dungeons_main_bg_palette_pointers = kDungeonsMainBgPalettePointers;
constexpr int dungeons_palettes = kDungeonsPalettes;
constexpr int room_items_pointers = kRoomItemsPointers;
constexpr int rooms_sprite_pointer = kRoomsSpritePointer;
constexpr int gfx_groups_pointer = kGfxGroupsPointer;
constexpr int chests_length_pointer = kChestsLengthPointer;
constexpr int chests_data_pointer1 = kChestsDataPointer1;
constexpr int messages_id_dungeon = kMessagesIdDungeon;
constexpr int blocks_length = kBlocksLength;
constexpr int blocks_pointer1 = kBlocksPointer1;
constexpr int blocks_pointer2 = kBlocksPointer2;
constexpr int blocks_pointer3 = kBlocksPointer3;
constexpr int blocks_pointer4 = kBlocksPointer4;
constexpr int torch_data = kTorchData;
constexpr int torches_length_pointer = kTorchesLengthPointer;
constexpr int sprite_blockset_pointer = kSpriteBlocksetPointer;
constexpr int sprites_data = kSpritesData;
constexpr int sprites_data_empty_room = kSpritesDataEmptyRoom;
constexpr int sprites_end_data = kSpritesEndData;
constexpr int pit_pointer = kPitPointer;
constexpr int pit_count = kPitCount;
constexpr int doorPointers = kDoorPointers;
constexpr int door_gfx_up = kDoorGfxUp;
constexpr int door_gfx_down = kDoorGfxDown;
constexpr int door_gfx_cavexit_down = kDoorGfxCaveExitDown;
constexpr int door_gfx_left = kDoorGfxLeft;
constexpr int door_gfx_right = kDoorGfxRight;
constexpr int door_pos_up = kDoorPosUp;
constexpr int door_pos_down = kDoorPosDown;
constexpr int door_pos_left = kDoorPosLeft;
constexpr int door_pos_right = kDoorPosRight;
constexpr int dungeon_spr_ptrs = kDungeonSpritePointers;
constexpr int tile_address = kTileAddress;
constexpr int tile_address_floor = kTileAddressFloor;
constexpr int NumberOfRooms = kNumberOfRooms;
constexpr uint16_t stairsObjects[] = {0x139, 0x138, 0x13B, 0x12E, 0x12D};
// TODO: Gradually migrate all code to use kPrefixedNames directly
// Then remove these legacy aliases
struct LayerMergeType {
uint8_t ID;
std::string Name;
bool Layer2OnTop;
bool Layer2Translucent;
bool Layer2Visible;
LayerMergeType() = default;
LayerMergeType(uint8_t id, std::string name, bool see, bool top, bool trans) {
ID = id;
Name = name;
Layer2OnTop = top;
Layer2Translucent = trans;
Layer2Visible = see;
}
};
const static LayerMergeType LayerMerge00{0x00, "Off", true, false, false};
const static LayerMergeType LayerMerge01{0x01, "Parallax", true, false, false};
const static LayerMergeType LayerMerge02{0x02, "Dark", true, true, true};
const static LayerMergeType LayerMerge03{0x03, "On top", true, true, false};
const static LayerMergeType LayerMerge04{0x04, "Translucent", true, true, true};
const static LayerMergeType LayerMerge05{0x05, "Addition", true, true, true};
const static LayerMergeType LayerMerge06{0x06, "Normal", true, false, false};
const static LayerMergeType LayerMerge07{0x07, "Transparent", true, true, true};
const static LayerMergeType LayerMerge08{0x08, "Dark room", true, true, true};
const static LayerMergeType kLayerMergeTypeList[] = {
LayerMerge00, LayerMerge01, LayerMerge02, LayerMerge03, LayerMerge04,
LayerMerge05, LayerMerge06, LayerMerge07, LayerMerge08};
enum CollisionKey {
One_Collision,
Both,
Both_With_Scroll,
Moving_Floor_Collision,
Moving_Water_Collision,
};
enum EffectKey {
Effect_Nothing,
One,
Moving_Floor,
Moving_Water,
Four,
Red_Flashes,
Torch_Show_Floor,
Ganon_Room,
};
enum TagKey {
Nothing,
NW_Kill_Enemy_to_Open,
NE_Kill_Enemy_to_Open,
SW_Kill_Enemy_to_Open,
SE_Kill_Enemy_to_Open,
W_Kill_Enemy_to_Open,
E_Kill_Enemy_to_Open,
N_Kill_Enemy_to_Open,
S_Kill_Enemy_to_Open,
Clear_Quadrant_to_Open,
Clear_Room_to_Open,
NW_Push_Block_to_Open,
NE_Push_Block_to_Open,
SW_Push_Block_to_Open,
SE_Push_Block_to_Open,
W_Push_Block_to_Open,
E_Push_Block_to_Open,
N_Push_Block_to_Open,
S_Push_Block_to_Open,
Push_Block_to_Open,
Pull_Lever_to_Open,
Clear_Level_to_Open,
Switch_Open_Door_Hold,
Switch_Open_Door_Toggle,
Turn_off_Water,
Turn_on_Water,
Water_Gate,
Water_Twin,
Secret_Wall_Right,
Secret_Wall_Left,
Crash1,
Crash2,
Pull_Switch_to_bomb_Wall,
Holes_0,
Open_Chest_Activate_Holes_0,
Holes_1,
Holes_2,
Kill_Enemy_to_clear_level,
SE_Kill_Enemy_to_Move_Block,
Trigger_activated_Chest,
Pull_lever_to_Bomb_Wall,
NW_Kill_Enemy_for_Chest,
NE_Kill_Enemy_for_Chest,
SW_Kill_Enemy_for_Chest,
SE_Kill_Enemy_for_Chest,
W_Kill_Enemy_for_Chest,
E_Kill_Enemy_for_Chest,
N_Kill_Enemy_for_Chest,
S_Kill_Enemy_for_Chest,
Clear_Quadrant_for_Chest,
Clear_Room_for_Chest,
Light_Torches_to_Open,
Holes_3,
Holes_4,
Holes_5,
Holes_6,
Agahnim_Room,
Holes_7,
Holes_8,
Open_Chest_for_Holes_8,
Push_Block_for_Chest,
Kill_to_open_Ganon_Door,
Light_Torches_to_get_Chest,
Kill_boss_Again
};
class Room {
public:
Room() = default;
Room(int room_id, Rom* rom) : room_id_(room_id), rom_(rom) {}
void LoadRoomGraphics(uint8_t entrance_blockset = 0xFF);
void CopyRoomGraphicsToBuffer();
// LoadGraphicsSheetsIntoArena() removed - per-room graphics instead
void RenderRoomGraphics();
void RenderObjectsToBackground();
void LoadAnimatedGraphics();
void LoadObjects();
void LoadSprites();
void LoadChests();
void LoadDoors();
void LoadTorches();
void LoadBlocks();
void LoadPits();
void LoadRoomLayout();
void LoadLayoutTilesToBuffer();
// Public getters and manipulators for sprites
const std::vector<zelda3::Sprite>& GetSprites() const { return sprites_; }
std::vector<zelda3::Sprite>& GetSprites() { return sprites_; }
// Public getters and manipulators for chests
const std::vector<chest_data>& GetChests() const { return chests_in_room_; }
std::vector<chest_data>& GetChests() { return chests_in_room_; }
// Public getters and manipulators for stairs
const std::vector<staircase>& GetStairs() const { return z3_staircases_; }
std::vector<staircase>& GetStairs() { return z3_staircases_; }
const RoomLayout& GetLayout() const { return layout_; }
// Public getters and manipulators for tile objects
const std::vector<RoomObject>& GetTileObjects() const {
return tile_objects_;
}
std::vector<RoomObject>& GetTileObjects() { return tile_objects_; }
// Methods for modifying tile objects
void ClearTileObjects() { tile_objects_.clear(); }
void AddTileObject(const RoomObject& object) {
tile_objects_.push_back(object);
MarkObjectsDirty();
}
// Enhanced object manipulation (Phase 3)
absl::Status AddObject(const RoomObject& object);
absl::Status RemoveObject(size_t index);
absl::Status UpdateObject(size_t index, const RoomObject& object);
absl::StatusOr<size_t> FindObjectAt(int x, int y, int layer) const;
bool ValidateObject(const RoomObject& object) const;
// Performance optimization: Mark objects as dirty when modified
void MarkObjectsDirty() { objects_dirty_ = true; textures_dirty_ = true; }
void MarkGraphicsDirty() { graphics_dirty_ = true; textures_dirty_ = true; }
void MarkLayoutDirty() { layout_dirty_ = true; textures_dirty_ = true; }
void RemoveTileObject(size_t index) {
if (index < tile_objects_.size()) {
tile_objects_.erase(tile_objects_.begin() + index);
MarkObjectsDirty();
}
}
size_t GetTileObjectCount() const { return tile_objects_.size(); }
RoomObject& GetTileObject(size_t index) { return tile_objects_[index]; }
const RoomObject& GetTileObject(size_t index) const {
return tile_objects_[index];
}
// For undo/redo functionality
void SetTileObjects(const std::vector<RoomObject>& objects) {
tile_objects_ = objects;
MarkObjectsDirty();
}
// Public setters for LoadRoomFromRom function
void SetBg2(background2 bg2) { bg2_ = bg2; }
void SetCollision(CollisionKey collision) { collision_ = collision; }
void SetIsLight(bool is_light) { is_light_ = is_light; }
void SetPalette(uint8_t palette) {
if (this->palette != palette) {
this->palette = palette;
MarkGraphicsDirty();
}
}
void SetBlockset(uint8_t blockset) {
if (this->blockset != blockset) {
this->blockset = blockset;
MarkGraphicsDirty();
}
}
void SetSpriteset(uint8_t spriteset) {
if (this->spriteset != spriteset) {
this->spriteset = spriteset;
MarkGraphicsDirty();
}
}
void SetEffect(EffectKey effect) {
if (effect_ != effect) {
effect_ = effect;
MarkObjectsDirty();
}
}
void SetTag1(TagKey tag1) {
if (tag1_ != tag1) {
tag1_ = tag1;
MarkObjectsDirty();
}
}
void SetTag2(TagKey tag2) {
if (tag2_ != tag2) {
tag2_ = tag2;
MarkObjectsDirty();
}
}
void SetStaircasePlane(int index, uint8_t plane) {
if (index >= 0 && index < 4) staircase_plane_[index] = plane;
}
void SetHolewarp(uint8_t holewarp) { this->holewarp = holewarp; }
void SetStaircaseRoom(int index, uint8_t room) {
if (index >= 0 && index < 4) staircase_rooms_[index] = room;
}
// SetFloor1/SetFloor2 removed - use set_floor1()/set_floor2() instead (defined above)
void SetMessageId(uint16_t message_id) { message_id_ = message_id; }
// Getters for LoadRoomFromRom function
bool IsLight() const { return is_light_; }
// Additional setters for LoadRoomFromRom function
void SetMessageIdDirect(uint16_t message_id) { message_id_ = message_id; }
void SetLayer2Mode(uint8_t mode) { layer2_mode_ = mode; }
void SetLayerMerging(LayerMergeType merging) { layer_merging_ = merging; }
void SetIsDark(bool is_dark) { is_dark_ = is_dark; }
void SetPaletteDirect(uint8_t palette) { palette_ = palette; }
void SetBackgroundTileset(uint8_t tileset) { background_tileset_ = tileset; }
void SetSpriteTileset(uint8_t tileset) { sprite_tileset_ = tileset; }
void SetLayer2Behavior(uint8_t behavior) { layer2_behavior_ = behavior; }
void SetTag1Direct(TagKey tag1) { tag1_ = tag1; }
void SetTag2Direct(TagKey tag2) { tag2_ = tag2; }
void SetPitsTargetLayer(uint8_t layer) { pits_.target_layer = layer; }
void SetStair1TargetLayer(uint8_t layer) { stair1_.target_layer = layer; }
void SetStair2TargetLayer(uint8_t layer) { stair2_.target_layer = layer; }
void SetStair3TargetLayer(uint8_t layer) { stair3_.target_layer = layer; }
void SetStair4TargetLayer(uint8_t layer) { stair4_.target_layer = layer; }
void SetPitsTarget(uint8_t target) { pits_.target = target; }
void SetStair1Target(uint8_t target) { stair1_.target = target; }
void SetStair2Target(uint8_t target) { stair2_.target = target; }
void SetStair3Target(uint8_t target) { stair3_.target = target; }
void SetStair4Target(uint8_t target) { stair4_.target = target; }
// Loaded state
bool IsLoaded() const { return is_loaded_; }
void SetLoaded(bool loaded) { is_loaded_ = loaded; }
// Read-only accessors for metadata
EffectKey effect() const { return effect_; }
TagKey tag1() const { return tag1_; }
TagKey tag2() const { return tag2_; }
CollisionKey collision() const { return collision_; }
const LayerMergeType& layer_merging() const { return layer_merging_; }
int id() const { return room_id_; }
uint8_t blockset = 0;
uint8_t spriteset = 0;
uint8_t palette = 0;
uint8_t layout = 0;
uint8_t holewarp = 0;
// NOTE: floor1/floor2 removed - use floor1() and floor2() accessors instead
// Floor graphics are now private (floor1_graphics_, floor2_graphics_)
uint16_t message_id_ = 0;
// Floor graphics accessors (use these instead of direct members!)
uint8_t floor1() const { return floor1_graphics_; }
uint8_t floor2() const { return floor2_graphics_; }
void set_floor1(uint8_t value) {
if (floor1_graphics_ != value) {
floor1_graphics_ = value;
MarkGraphicsDirty();
}
}
void set_floor2(uint8_t value) {
if (floor2_graphics_ != value) {
floor2_graphics_ = value;
MarkGraphicsDirty();
}
}
// Enhanced object parsing methods
void ParseObjectsFromLocation(int objects_location);
void HandleSpecialObjects(short oid, uint8_t posX, uint8_t posY,
int& nbr_of_staircase);
// Object saving (Phase 1, Task 1.3)
absl::Status SaveObjects();
std::vector<uint8_t> EncodeObjects() const;
auto blocks() const { return blocks_; }
auto& mutable_blocks() { return blocks_; }
auto rom() { return rom_; }
auto mutable_rom() { return rom_; }
const std::array<uint8_t, 0x4000>& get_gfx_buffer() const { return current_gfx16_; }
// Per-room background buffers (not shared via arena!)
auto& bg1_buffer() { return bg1_buffer_; }
auto& bg2_buffer() { return bg2_buffer_; }
const auto& bg1_buffer() const { return bg1_buffer_; }
const auto& bg2_buffer() const { return bg2_buffer_; }
private:
Rom* rom_;
std::array<uint8_t, 0x4000> current_gfx16_;
// Each room has its OWN background buffers and bitmaps
gfx::BackgroundBuffer bg1_buffer_{512, 512};
gfx::BackgroundBuffer bg2_buffer_{512, 512};
bool is_light_;
bool is_loaded_ = false;
bool is_dark_;
bool is_floor_ = true;
// Performance optimization: Cache room properties to avoid unnecessary re-renders
uint8_t cached_blockset_ = 0xFF;
uint8_t cached_spriteset_ = 0xFF;
uint8_t cached_palette_ = 0xFF;
uint8_t cached_layout_ = 0xFF;
uint8_t cached_floor1_graphics_ = 0xFF;
uint8_t cached_floor2_graphics_ = 0xFF;
uint8_t cached_effect_ = 0xFF;
TagKey cached_tag1_ = TagKey::Nothing;
TagKey cached_tag2_ = TagKey::Nothing;
// Dirty flags for selective rendering
bool graphics_dirty_ = true;
bool objects_dirty_ = true;
bool layout_dirty_ = true;
bool textures_dirty_ = true;
int room_id_;
int animated_frame_;
uint8_t staircase_plane_[4];
uint8_t staircase_rooms_[4];
uint8_t background_tileset_;
uint8_t sprite_tileset_;
uint8_t layer2_behavior_;
uint8_t palette_;
uint8_t floor1_graphics_;
uint8_t floor2_graphics_;
uint8_t layer2_mode_;
std::array<uint8_t, 16> blocks_;
std::array<chest, 16> chest_list_;
std::vector<RoomObject> tile_objects_;
// TODO: add separate door objects list when door section (F0 FF) is parsed
std::vector<zelda3::Sprite> sprites_;
std::vector<staircase> z3_staircases_;
std::vector<chest_data> chests_in_room_;
RoomLayout layout_;
LayerMergeType layer_merging_;
CollisionKey collision_;
EffectKey effect_;
TagKey tag1_;
TagKey tag2_;
background2 bg2_;
destination pits_;
destination stair1_;
destination stair2_;
destination stair3_;
destination stair4_;
};
// Loads a room from the ROM.
Room LoadRoomFromRom(Rom* rom, int room_id);
struct RoomSize {
int64_t room_size_pointer;
int64_t room_size;
};
// Calculates the size of a room in the ROM.
RoomSize CalculateRoomSize(Rom* rom, int room_id);
static const std::string RoomEffect[] = {"Nothing",
"Nothing",
"Moving Floor",
"Moving Water",
"Trinexx Shell",
"Red Flashes",
"Light Torch to See Floor",
"Ganon's Darkness"};
constexpr std::string_view kRoomNames[] = {
"Ganon",
"Hyrule Castle (North Corridor)",
"Behind Sanctuary (Switch)",
"Houlihan",
"Turtle Rock (Crysta-Roller)",
"Empty",
"Swamp Palace (Arrghus[Boss])",
"Tower of Hera (Moldorm[Boss])",
"Cave (Healing Fairy)",
"Palace of Darkness",
"Palace of Darkness (Stalfos Trap)",
"Palace of Darkness (Turtle)",
"Ganon's Tower (Entrance)",
"Ganon's Tower (Agahnim2[Boss])",
"Ice Palace (Entrance )",
"Empty Clone ",
"Ganon Evacuation Route",
"Hyrule Castle (Bombable Stock )",
"Sanctuary",
"Turtle Rock (Hokku-Bokku Key 2)",
"Turtle Rock (Big Key )",
"Turtle Rock",
"Swamp Palace (Swimming Treadmill)",
"Tower of Hera (Moldorm Fall )",
"Cave",
"Palace of Darkness (Dark Maze)",
"Palace of Darkness (Big Chest )",
"Palace of Darkness (Mimics / Moving Wall )",
"Ganon's Tower (Ice Armos)",
"Ganon's Tower (Final Hallway)",
"Ice Palace (Bomb Floor / Bari )",
"Ice Palace (Pengator / Big Key )",
"Agahnim's Tower (Agahnim[Boss])",
"Hyrule Castle (Key-rat )",
"Hyrule Castle (Sewer Text Trigger )",
"Turtle Rock (West Exit to Balcony)",
"Turtle Rock (Double Hokku-Bokku / Big chest )",
"Empty Clone ",
"Swamp Palace (Statue )",
"Tower of Hera (Big Chest)",
"Swamp Palace (Entrance )",
"Skull Woods (Mothula[Boss])",
"Palace of Darkness (Big Hub )",
"Palace of Darkness (Map Chest / Fairy )",
"Cave",
"Empty Clone ",
"Ice Palace (Compass )",
"Cave (Kakariko Well HP)",
"Agahnim's Tower (Maiden Sacrifice Chamber)",
"Tower of Hera (Hardhat Beetles )",
"Hyrule Castle (Sewer Key Chest )",
"Desert Palace (Lanmolas[Boss])",
"Swamp Palace (Push Block Puzzle / Pre-Big Key )",
"Swamp Palace (Big Key / BS )",
"Swamp Palace (Big Chest )",
"Swamp Palace (Map Chest / Water Fill )",
"Swamp Palace (Key Pot )",
"Skull Woods (Gibdo Key / Mothula Hole )",
"Palace of Darkness (Bombable Floor )",
"Palace of Darkness (Spike Block / Conveyor )",
"Cave",
"Ganon's Tower (Torch 2)",
"Ice Palace (Stalfos Knights / Conveyor Hellway)",
"Ice Palace (Map Chest )",
"Agahnim's Tower (Final Bridge )",
"Hyrule Castle (First Dark )",
"Hyrule Castle (6 Ropes )",
"Desert Palace (Torch Puzzle / Moving Wall )",
"Thieves Town (Big Chest )",
"Thieves Town (Jail Cells )",
"Swamp Palace (Compass Chest )",
"Empty Clone ",
"Empty Clone ",
"Skull Woods (Gibdo Torch Puzzle )",
"Palace of Darkness (Entrance )",
"Palace of Darkness (Warps / South Mimics )",
"Ganon's Tower (Mini-Helmasaur Conveyor )",
"Ganon's Tower (Moldorm )",
"Ice Palace (Bomb-Jump )",
"Ice Palace Clone (Fairy )",
"Hyrule Castle (West Corridor)",
"Hyrule Castle (Throne )",
"Hyrule Castle (East Corridor)",
"Desert Palace (Popos 2 / Beamos Hellway )",
"Swamp Palace (Upstairs Pits )",
"Castle Secret Entrance / Uncle Death ",
"Skull Woods (Key Pot / Trap )",
"Skull Woods (Big Key )",
"Skull Woods (Big Chest )",
"Skull Woods (Final Section Entrance )",
"Palace of Darkness (Helmasaur King[Boss])",
"Ganon's Tower (Spike Pit )",
"Ganon's Tower (Ganon-Ball Z)",
"Ganon's Tower (Gauntlet 1/2/3)",
"Ice Palace (Lonely Firebar)",
"Ice Palace (Hidden Chest / Spike Floor )",
"Hyrule Castle (West Entrance )",
"Hyrule Castle (Main Entrance )",
"Hyrule Castle (East Entrance )",
"Desert Palace (Final Section Entrance )",
"Thieves Town (West Attic )",
"Thieves Town (East Attic )",
"Swamp Palace (Hidden Chest / Hidden Door )",
"Skull Woods (Compass Chest )",
"Skull Woods (Key Chest / Trap )",
"Empty Clone ",
"Palace of Darkness (Rupee )",
"Ganon's Tower (Mimics s)",
"Ganon's Tower (Lanmolas )",
"Ganon's Tower (Gauntlet 4/5)",
"Ice Palace (Pengators )",
"Empty Clone ",
"Hyrule Castle (Small Corridor to Jail Cells)",
"Hyrule Castle (Boomerang Chest )",
"Hyrule Castle (Map Chest )",
"Desert Palace (Big Chest )",
"Desert Palace (Map Chest )",
"Desert Palace (Big Key Chest )",
"Swamp Palace (Water Drain )",
"Tower of Hera (Entrance )",
"Empty Clone ",
"Empty Clone ",
"Empty Clone ",
"Ganon's Tower",
"Ganon's Tower (East Side Collapsing Bridge / Exploding Wall )",
"Ganon's Tower (Winder / Warp Maze )",
"Ice Palace (Hidden Chest / Bombable Floor )",
"Ice Palace ( Big Spike Traps )",
"Hyrule Castle (Jail Cell )",
"Hyrule Castle",
"Hyrule Castle (Basement Chasm )",
"Desert Palace (West Entrance )",
"Desert Palace (Main Entrance )",
"Desert Palace (East Entrance )",
"Empty Clone ",
"Tower of Hera (Tile )",
"Empty Clone ",
"Eastern Palace (Fairy )",
"Empty Clone ",
"Ganon's Tower (Block Puzzle / Spike Skip / Map Chest )",
"Ganon's Tower (East and West Downstairs / Big Chest )",
"Ganon's Tower (Tile / Torch Puzzle )",
"Ice Palace",
"Empty Clone ",
"Misery Mire (Vitreous[Boss])",
"Misery Mire (Final Switch )",
"Misery Mire (Dark Bomb Wall / Switches )",
"Misery Mire (Dark Cane Floor Switch Puzzle )",
"Empty Clone ",
"Ganon's Tower (Final Collapsing Bridge )",
"Ganon's Tower (Torches 1 )",
"Misery Mire (Torch Puzzle / Moving Wall )",
"Misery Mire (Entrance )",
"Eastern Palace (Eyegore Key )",
"Empty Clone ",
"Ganon's Tower (Many Spikes / Warp Maze )",
"Ganon's Tower (Invisible Floor Maze )",
"Ganon's Tower (Compass Chest / Invisible Floor )",
"Ice Palace (Big Chest )",
"Ice Palace",
"Misery Mire (Pre-Vitreous )",
"Misery Mire (Fish )",
"Misery Mire (Bridge Key Chest )",
"Misery Mire",
"Turtle Rock (Trinexx[Boss])",
"Ganon's Tower (Wizzrobes s)",
"Ganon's Tower (Moldorm Fall )",
"Tower of Hera (Fairy )",
"Eastern Palace (Stalfos Spawn )",
"Eastern Palace (Big Chest )",
"Eastern Palace (Map Chest )",
"Thieves Town (Moving Spikes / Key Pot )",
"Thieves Town (Blind The Thief[Boss])",
"Empty Clone ",
"Ice Palace",
"Ice Palace (Ice Bridge )",
"Agahnim's Tower (Circle of Pots)",
"Misery Mire (Hourglass )",
"Misery Mire (Slug )",
"Misery Mire (Spike Key Chest )",
"Turtle Rock (Pre-Trinexx )",
"Turtle Rock (Dark Maze)",
"Turtle Rock (Chain Chomps )",
"Turtle Rock (Map Chest / Key Chest / Roller )",
"Eastern Palace (Big Key )",
"Eastern Palace (Lobby Cannonballs )",
"Eastern Palace (Dark Antifairy / Key Pot )",
"Thieves Town (Hellway)",
"Thieves Town (Conveyor Toilet)",
"Empty Clone ",
"Ice Palace (Block Puzzle )",
"Ice Palace Clone (Switch )",
"Agahnim's Tower (Dark Bridge )",
"Misery Mire (Compass Chest / Tile )",
"Misery Mire (Big Hub )",
"Misery Mire (Big Chest )",
"Turtle Rock (Final Crystal Switch Puzzle )",
"Turtle Rock (Laser Bridge)",
"Turtle Rock",
"Turtle Rock (Torch Puzzle)",
"Eastern Palace (Armos Knights[Boss])",
"Eastern Palace (Entrance )",
"??",
"Thieves Town (North West Entrance )",
"Thieves Town (North East Entrance )",
"Empty Clone ",
"Ice Palace (Hole to Kholdstare )",
"Empty Clone ",
"Agahnim's Tower (Dark Maze)",
"Misery Mire (Conveyor Slug / Big Key )",
"Misery Mire (Mire02 / Wizzrobes )",
"Empty Clone ",
"Empty Clone ",
"Turtle Rock (Laser Key )",
"Turtle Rock (Entrance )",
"Empty Clone ",
"Eastern Palace (Zeldagamer / Pre-Armos Knights )",
"Eastern Palace (Canonball ",
"Eastern Palace",
"Thieves Town (Main (South West) Entrance )",
"Thieves Town (South East Entrance )",
"Empty Clone ",
"Ice Palace (Kholdstare[Boss])",
"Cave",
"Agahnim's Tower (Entrance )",
"Cave (Lost Woods HP)",
"Cave (Lumberjack's Tree HP)",
"Cave (1/2 Magic)",
"Cave (Lost Old Man Final Cave)",
"Cave (Lost Old Man Final Cave)",
"Cave",
"Cave",
"Cave",
"Empty Clone ",
"Cave (Spectacle Rock HP)",
"Cave",
"Empty Clone ",
"Cave",
"Cave (Spiral Cave)",
"Cave (Crystal Switch / 5 Chests )",
"Cave (Lost Old Man Starting Cave)",
"Cave (Lost Old Man Starting Cave)",
"House",
"House (Old Woman (Sahasrahla's Wife?))",
"House (Angry Brothers)",
"House (Angry Brothers)",
"Empty Clone ",
"Empty Clone ",
"Cave",
"Cave",
"Cave",
"Cave",
"Empty Clone ",
"Cave",
"Cave",
"Cave",
"Chest Minigame",
"Houses",
"Sick Boy house",
"Tavern",
"Link's House",
"Sarashrala Hut",
"Chest Minigame",
"Library",
"Chicken House",
"Witch Shop",
"A Aginah's Cave",
"Dam",
"Mimic Cave",
"Mire Shed",
"Cave",
"Shop",
"Shop",
"Archery Minigame",
"DW Church/Shop",
"Grave Cave",
"Fairy Fountain",
"Fairy Upgrade",
"Pyramid Fairy",
"Spike Cave",
"Chest Minigame",
"Blind Hut",
"Bonzai Cave",
"Circle of bush Cave",
"Big Bomb Shop, C-House",
"Blind Hut 2",
"Hype Cave",
"Shop",
"Ice Cave",
"Smith",
"Fortune Teller",
"MiniMoldorm Cave",
"Under Rock Caves",
"Smith",
"Cave",
"Mazeblock Cave",
"Smith Peg Cave"};
static const std::string RoomTag[] = {"Nothing",
"NW Kill Enemy to Open",
"NE Kill Enemy to Open",
"SW Kill Enemy to Open",
"SE Kill Enemy to Open",
"W Kill Enemy to Open",
"E Kill Enemy to Open",
"N Kill Enemy to Open",
"S Kill Enemy to Open",
"Clear Quadrant to Open",
"Clear Full Tile to Open",
"NW Push Block to Open",
"NE Push Block to Open",
"SW Push Block to Open",
"SE Push Block to Open",
"W Push Block to Open",
"E Push Block to Open",
"N Push Block to Open",
"S Push Block to Open",
"Push Block to Open",
"Pull Lever to Open",
"Collect Prize to Open",
"Hold Switch Open Door",
"Toggle Switch to Open Door",
"Turn off Water",
"Turn on Water",
"Water Gate",
"Water Twin",
"Moving Wall Right",
"Moving Wall Left",
"Crash",
"Crash",
"Push Switch Exploding Wall",
"Holes 0",
"Open Chest (Holes 0)",
"Holes 1",
"Holes 2",
"Defeat Boss for Dungeon Prize",
"SE Kill Enemy to Push Block",
"Trigger Switch Chest",
"Pull Lever Exploding Wall",
"NW Kill Enemy for Chest",
"NE Kill Enemy for Chest",
"SW Kill Enemy for Chest",
"SE Kill Enemy for Chest",
"W Kill Enemy for Chest",
"E Kill Enemy for Chest",
"N Kill Enemy for Chest",
"S Kill Enemy for Chest",
"Clear Quadrant for Chest",
"Clear Full Tile for Chest",
"Light Torches to Open",
"Holes 3",
"Holes 4",
"Holes 5",
"Holes 6",
"Agahnim Room",
"Holes 7",
"Holes 8",
"Open Chest for Holes 8",
"Push Block for Chest",
"Clear Room for Triforce Door",
"Light Torches for Chest",
"Kill Boss Again"};
} // namespace zelda3
} // namespace yaze
#endif

View File

@@ -1,366 +0,0 @@
#ifndef YAZE_APP_ZELDA3_DUNGEON_ROOM_ENTRANCE_H
#define YAZE_APP_ZELDA3_DUNGEON_ROOM_ENTRANCE_H
#include <cstdint>
#include "app/rom.h"
namespace yaze {
namespace zelda3 {
// 0x14577 word value for each room
constexpr int kEntranceRoom = 0x14813;
// 8 bytes per room, HU, FU, HD, FD, HL, FL, HR, FR
constexpr int kEntranceScrollEdge = 0x1491D; // 0x14681
constexpr int kEntranceYScroll = 0x14D45; // 0x14AA9 2 bytes each room
constexpr int kEntranceXScroll = 0x14E4F; // 0x14BB3 2 bytes
constexpr int kEntranceYPosition = 0x14F59; // 0x14CBD 2bytes
constexpr int kEntranceXPosition = 0x15063; // 0x14DC7 2bytes
constexpr int kEntranceCameraYTrigger = 0x1516D; // 0x14ED1 2bytes
constexpr int kEntranceCameraXTrigger = 0x15277; // 0x14FDB 2bytes
constexpr int kEntranceBlockset = 0x15381; // 0x150E5 1byte
constexpr int kEntranceFloor = 0x15406; // 0x1516A 1byte
constexpr int kEntranceDungeon = 0x1548B; // 0x151EF 1byte (dungeon id)
constexpr int kEntranceDoor = 0x15510; // 0x15274 1byte
// 1 byte, ---b ---a b = bg2, a = need to check
constexpr int kEntranceLadderBG = 0x15595; // 0x152F9
constexpr int kEntrancescrolling = 0x1561A; // 0x1537E 1byte --h- --v-
constexpr int kEntranceScrollQuadrant = 0x1569F; // 0x15403 1byte
constexpr int kEntranceExit = 0x15724; // 0x15488 2byte word
constexpr int kEntranceMusic = 0x1582E; // 0x15592
// word value for each room
constexpr int kStartingEntranceroom = 0x15B6E; // 0x158D2
// 8 bytes per room, HU, FU, HD, FD, HL, FL, HR, FR
constexpr int kStartingEntranceScrollEdge = 0x15B7C; // 0x158E0
constexpr int kStartingEntranceYScroll = 0x15BB4; // 0x14AA9 //2bytes each room
constexpr int kStartingEntranceXScroll = 0x15BC2; // 0x14BB3 //2bytes
constexpr int kStartingEntranceYPosition = 0x15BD0; // 0x14CBD 2bytes
constexpr int kStartingEntranceXPosition = 0x15BDE; // 0x14DC7 2bytes
constexpr int kStartingEntranceCameraYTrigger = 0x15BEC; // 0x14ED1 2bytes
constexpr int kStartingEntranceCameraXTrigger = 0x15BFA; // 0x14FDB 2bytes
constexpr int kStartingEntranceBlockset = 0x15C08; // 0x150E5 1byte
constexpr int kStartingEntranceFloor = 0x15C0F; // 0x1516A 1byte
constexpr int kStartingEntranceDungeon = 0x15C16; // 0x151EF 1byte (dungeon id)
constexpr int kStartingEntranceDoor = 0x15C2B; // 0x15274 1byte
// 1 byte, ---b ---a b = bg2, a = need to check
constexpr int kStartingEntranceLadderBG = 0x15C1D; // 0x152F9
// 1byte --h- --v-
constexpr int kStartingEntrancescrolling = 0x15C24; // 0x1537E
constexpr int kStartingEntranceScrollQuadrant = 0x15C2B; // 0x15403 1byte
constexpr int kStartingEntranceexit = 0x15C32; // 0x15488 //2byte word
constexpr int kStartingEntrancemusic = 0x15C4E; // 0x15592
constexpr int kStartingEntranceentrance = 0x15C40;
constexpr int items_data_start = 0xDDE9; // save purpose
constexpr int items_data_end = 0xE6B2; // save purpose
constexpr int initial_equipement = 0x271A6;
// item id you get instead if you already have that item
constexpr int chests_backupitems = 0x3B528;
constexpr int chests_yoffset = 0x4836C;
constexpr int chests_xoffset = 0x4836C + (76 * 1);
constexpr int chests_itemsgfx = 0x4836C + (76 * 2);
constexpr int chests_itemswide = 0x4836C + (76 * 3);
constexpr int chests_itemsproperties = 0x4836C + (76 * 4);
constexpr int chests_sramaddress = 0x4836C + (76 * 5);
constexpr int chests_sramvalue = 0x4836C + (76 * 7);
constexpr int chests_msgid = 0x442DD;
constexpr int dungeons_startrooms = 0x7939;
constexpr int dungeons_endrooms = 0x792D;
constexpr int dungeons_bossrooms = 0x10954; // short value
// Bed Related Values (Starting location)
constexpr int bedPositionX = 0x039A37; // short value
constexpr int bedPositionY = 0x039A32; // short value
// short value (on 2 different bytes)
constexpr int bedPositionResetXLow = 0x02DE53;
constexpr int bedPositionResetXHigh = 0x02DE58;
// short value (on 2 different bytes)
constexpr int bedPositionResetYLow = 0x02DE5D;
constexpr int bedPositionResetYHigh = 0x02DE62;
constexpr int bedSheetPositionX = 0x0480BD; // short value
constexpr int bedSheetPositionY = 0x0480B8; // short value
/**
* @brief Dungeon Room Entrance or Spawn Point
*/
class RoomEntrance {
public:
RoomEntrance() = default;
RoomEntrance(Rom *rom, uint8_t entrance_id, bool is_spawn_point = false)
: entrance_id_(entrance_id) {
room_ = static_cast<short>(
(rom->data()[kEntranceRoom + (entrance_id * 2) + 1] << 8) +
rom->data()[kEntranceRoom + (entrance_id * 2)]);
y_position_ = static_cast<uint16_t>(
(rom->data()[kEntranceYPosition + (entrance_id * 2) + 1] << 8) +
rom->data()[kEntranceYPosition + (entrance_id * 2)]);
x_position_ = static_cast<uint16_t>(
(rom->data()[kEntranceXPosition + (entrance_id * 2) + 1] << 8) +
rom->data()[kEntranceXPosition + (entrance_id * 2)]);
camera_x_ = static_cast<uint16_t>(
(rom->data()[kEntranceXScroll + (entrance_id * 2) + 1] << 8) +
rom->data()[kEntranceXScroll + (entrance_id * 2)]);
camera_y_ = static_cast<uint16_t>(
(rom->data()[kEntranceYScroll + (entrance_id * 2) + 1] << 8) +
rom->data()[kEntranceYScroll + (entrance_id * 2)]);
camera_trigger_y_ = static_cast<uint16_t>(
(rom->data()[(kEntranceCameraYTrigger + (entrance_id * 2)) + 1] << 8) +
rom->data()[kEntranceCameraYTrigger + (entrance_id * 2)]);
camera_trigger_x_ = static_cast<uint16_t>(
(rom->data()[(kEntranceCameraXTrigger + (entrance_id * 2)) + 1] << 8) +
rom->data()[kEntranceCameraXTrigger + (entrance_id * 2)]);
blockset_ = rom->data()[kEntranceBlockset + entrance_id];
music_ = rom->data()[kEntranceMusic + entrance_id];
dungeon_id_ = rom->data()[kEntranceDungeon + entrance_id];
floor_ = rom->data()[kEntranceFloor + entrance_id];
door_ = rom->data()[kEntranceDoor + entrance_id];
ladder_bg_ = rom->data()[kEntranceLadderBG + entrance_id];
scrolling_ = rom->data()[kEntrancescrolling + entrance_id];
scroll_quadrant_ = rom->data()[kEntranceScrollQuadrant + entrance_id];
exit_ = static_cast<short>(
(rom->data()[kEntranceExit + (entrance_id * 2) + 1] << 8) +
rom->data()[kEntranceExit + (entrance_id * 2)]);
camera_boundary_qn_ =
rom->data()[kEntranceScrollEdge + 0 + (entrance_id * 8)];
camera_boundary_fn_ =
rom->data()[kEntranceScrollEdge + 1 + (entrance_id * 8)];
camera_boundary_qs_ =
rom->data()[kEntranceScrollEdge + 2 + (entrance_id * 8)];
camera_boundary_fs_ =
rom->data()[kEntranceScrollEdge + 3 + (entrance_id * 8)];
camera_boundary_qw_ =
rom->data()[kEntranceScrollEdge + 4 + (entrance_id * 8)];
camera_boundary_fw_ =
rom->data()[kEntranceScrollEdge + 5 + (entrance_id * 8)];
camera_boundary_qe_ =
rom->data()[kEntranceScrollEdge + 6 + (entrance_id * 8)];
camera_boundary_fe_ =
rom->data()[kEntranceScrollEdge + 7 + (entrance_id * 8)];
if (is_spawn_point) {
room_ = static_cast<short>(
(rom->data()[kStartingEntranceroom + (entrance_id * 2) + 1] << 8) +
rom->data()[kStartingEntranceroom + (entrance_id * 2)]);
y_position_ = static_cast<uint16_t>(
(rom->data()[kStartingEntranceYPosition + (entrance_id * 2) + 1]
<< 8) +
rom->data()[kStartingEntranceYPosition + (entrance_id * 2)]);
x_position_ = static_cast<uint16_t>(
(rom->data()[kStartingEntranceXPosition + (entrance_id * 2) + 1]
<< 8) +
rom->data()[kStartingEntranceXPosition + (entrance_id * 2)]);
camera_x_ = static_cast<uint16_t>(
(rom->data()[kStartingEntranceXScroll + (entrance_id * 2) + 1] << 8) +
rom->data()[kStartingEntranceXScroll + (entrance_id * 2)]);
camera_y_ = static_cast<uint16_t>(
(rom->data()[kStartingEntranceYScroll + (entrance_id * 2) + 1] << 8) +
rom->data()[kStartingEntranceYScroll + (entrance_id * 2)]);
camera_trigger_y_ = static_cast<uint16_t>(
(rom->data()[kStartingEntranceCameraYTrigger + (entrance_id * 2) + 1]
<< 8) +
rom->data()[kStartingEntranceCameraYTrigger + (entrance_id * 2)]);
camera_trigger_x_ = static_cast<uint16_t>(
(rom->data()[kStartingEntranceCameraXTrigger + (entrance_id * 2) + 1]
<< 8) +
rom->data()[kStartingEntranceCameraXTrigger + (entrance_id * 2)]);
blockset_ = rom->data()[kStartingEntranceBlockset + entrance_id];
music_ = rom->data()[kStartingEntrancemusic + entrance_id];
dungeon_id_ = rom->data()[kStartingEntranceDungeon + entrance_id];
floor_ = rom->data()[kStartingEntranceFloor + entrance_id];
door_ = rom->data()[kStartingEntranceDoor + entrance_id];
ladder_bg_ = rom->data()[kStartingEntranceLadderBG + entrance_id];
scrolling_ = rom->data()[kStartingEntrancescrolling + entrance_id];
scroll_quadrant_ =
rom->data()[kStartingEntranceScrollQuadrant + entrance_id];
exit_ = static_cast<short>(
((rom->data()[kStartingEntranceexit + (entrance_id * 2) + 1] & 0x01)
<< 8) +
rom->data()[kStartingEntranceexit + (entrance_id * 2)]);
camera_boundary_qn_ =
rom->data()[kStartingEntranceScrollEdge + 0 + (entrance_id * 8)];
camera_boundary_fn_ =
rom->data()[kStartingEntranceScrollEdge + 1 + (entrance_id * 8)];
camera_boundary_qs_ =
rom->data()[kStartingEntranceScrollEdge + 2 + (entrance_id * 8)];
camera_boundary_fs_ =
rom->data()[kStartingEntranceScrollEdge + 3 + (entrance_id * 8)];
camera_boundary_qw_ =
rom->data()[kStartingEntranceScrollEdge + 4 + (entrance_id * 8)];
camera_boundary_fw_ =
rom->data()[kStartingEntranceScrollEdge + 5 + (entrance_id * 8)];
camera_boundary_qe_ =
rom->data()[kStartingEntranceScrollEdge + 6 + (entrance_id * 8)];
camera_boundary_fe_ =
rom->data()[kStartingEntranceScrollEdge + 7 + (entrance_id * 8)];
}
}
absl::Status Save(Rom *rom, int entrance_id, bool is_spawn_point = false) {
if (!is_spawn_point) {
RETURN_IF_ERROR(
rom->WriteShort(kEntranceYPosition + (entrance_id * 2), y_position_));
RETURN_IF_ERROR(
rom->WriteShort(kEntranceXPosition + (entrance_id * 2), x_position_));
RETURN_IF_ERROR(
rom->WriteShort(kEntranceYScroll + (entrance_id * 2), camera_y_));
RETURN_IF_ERROR(
rom->WriteShort(kEntranceXScroll + (entrance_id * 2), camera_x_));
RETURN_IF_ERROR(rom->WriteShort(
kEntranceCameraXTrigger + (entrance_id * 2), camera_trigger_x_));
RETURN_IF_ERROR(rom->WriteShort(
kEntranceCameraYTrigger + (entrance_id * 2), camera_trigger_y_));
RETURN_IF_ERROR(
rom->WriteShort(kEntranceExit + (entrance_id * 2), exit_));
RETURN_IF_ERROR(rom->WriteByte(kEntranceBlockset + entrance_id,
(uint8_t)(blockset_ & 0xFF)));
RETURN_IF_ERROR(rom->WriteByte(kEntranceMusic + entrance_id,
(uint8_t)(music_ & 0xFF)));
RETURN_IF_ERROR(rom->WriteByte(kEntranceDungeon + entrance_id,
(uint8_t)(dungeon_id_ & 0xFF)));
RETURN_IF_ERROR(
rom->WriteByte(kEntranceDoor + entrance_id, (uint8_t)(door_ & 0xFF)));
RETURN_IF_ERROR(rom->WriteByte(kEntranceFloor + entrance_id,
(uint8_t)(floor_ & 0xFF)));
RETURN_IF_ERROR(rom->WriteByte(kEntranceLadderBG + entrance_id,
(uint8_t)(ladder_bg_ & 0xFF)));
RETURN_IF_ERROR(rom->WriteByte(kEntrancescrolling + entrance_id,
(uint8_t)(scrolling_ & 0xFF)));
RETURN_IF_ERROR(rom->WriteByte(kEntranceScrollQuadrant + entrance_id,
(uint8_t)(scroll_quadrant_ & 0xFF)));
RETURN_IF_ERROR(rom->WriteByte(
kEntranceScrollEdge + 0 + (entrance_id * 8), camera_boundary_qn_));
RETURN_IF_ERROR(rom->WriteByte(
kEntranceScrollEdge + 1 + (entrance_id * 8), camera_boundary_fn_));
RETURN_IF_ERROR(rom->WriteByte(
kEntranceScrollEdge + 2 + (entrance_id * 8), camera_boundary_qs_));
RETURN_IF_ERROR(rom->WriteByte(
kEntranceScrollEdge + 3 + (entrance_id * 8), camera_boundary_fs_));
RETURN_IF_ERROR(rom->WriteByte(
kEntranceScrollEdge + 4 + (entrance_id * 8), camera_boundary_qw_));
RETURN_IF_ERROR(rom->WriteByte(
kEntranceScrollEdge + 5 + (entrance_id * 8), camera_boundary_fw_));
RETURN_IF_ERROR(rom->WriteByte(
kEntranceScrollEdge + 6 + (entrance_id * 8), camera_boundary_qe_));
RETURN_IF_ERROR(rom->WriteByte(
kEntranceScrollEdge + 7 + (entrance_id * 8), camera_boundary_fe_));
} else {
RETURN_IF_ERROR(
rom->WriteShort(kStartingEntranceroom + (entrance_id * 2), room_));
RETURN_IF_ERROR(rom->WriteShort(
kStartingEntranceYPosition + (entrance_id * 2), y_position_));
RETURN_IF_ERROR(rom->WriteShort(
kStartingEntranceXPosition + (entrance_id * 2), x_position_));
RETURN_IF_ERROR(rom->WriteShort(
kStartingEntranceYScroll + (entrance_id * 2), camera_y_));
RETURN_IF_ERROR(rom->WriteShort(
kStartingEntranceXScroll + (entrance_id * 2), camera_x_));
RETURN_IF_ERROR(
rom->WriteShort(kStartingEntranceCameraXTrigger + (entrance_id * 2),
camera_trigger_x_));
RETURN_IF_ERROR(
rom->WriteShort(kStartingEntranceCameraYTrigger + (entrance_id * 2),
camera_trigger_y_));
RETURN_IF_ERROR(
rom->WriteShort(kStartingEntranceexit + (entrance_id * 2), exit_));
RETURN_IF_ERROR(rom->WriteByte(kStartingEntranceBlockset + entrance_id,
(uint8_t)(blockset_ & 0xFF)));
RETURN_IF_ERROR(rom->WriteByte(kStartingEntrancemusic + entrance_id,
(uint8_t)(music_ & 0xFF)));
RETURN_IF_ERROR(rom->WriteByte(kStartingEntranceDungeon + entrance_id,
(uint8_t)(dungeon_id_ & 0xFF)));
RETURN_IF_ERROR(rom->WriteByte(kStartingEntranceDoor + entrance_id,
(uint8_t)(door_ & 0xFF)));
RETURN_IF_ERROR(rom->WriteByte(kStartingEntranceFloor + entrance_id,
(uint8_t)(floor_ & 0xFF)));
RETURN_IF_ERROR(rom->WriteByte(kStartingEntranceLadderBG + entrance_id,
(uint8_t)(ladder_bg_ & 0xFF)));
RETURN_IF_ERROR(rom->WriteByte(kStartingEntrancescrolling + entrance_id,
(uint8_t)(scrolling_ & 0xFF)));
RETURN_IF_ERROR(
rom->WriteByte(kStartingEntranceScrollQuadrant + entrance_id,
(uint8_t)(scroll_quadrant_ & 0xFF)));
RETURN_IF_ERROR(
rom->WriteByte(kStartingEntranceScrollEdge + 0 + (entrance_id * 8),
camera_boundary_qn_));
RETURN_IF_ERROR(
rom->WriteByte(kStartingEntranceScrollEdge + 1 + (entrance_id * 8),
camera_boundary_fn_));
RETURN_IF_ERROR(
rom->WriteByte(kStartingEntranceScrollEdge + 2 + (entrance_id * 8),
camera_boundary_qs_));
RETURN_IF_ERROR(
rom->WriteByte(kStartingEntranceScrollEdge + 3 + (entrance_id * 8),
camera_boundary_fs_));
RETURN_IF_ERROR(
rom->WriteByte(kStartingEntranceScrollEdge + 4 + (entrance_id * 8),
camera_boundary_qw_));
RETURN_IF_ERROR(
rom->WriteByte(kStartingEntranceScrollEdge + 5 + (entrance_id * 8),
camera_boundary_fw_));
RETURN_IF_ERROR(
rom->WriteByte(kStartingEntranceScrollEdge + 6 + (entrance_id * 8),
camera_boundary_qe_));
RETURN_IF_ERROR(
rom->WriteByte(kStartingEntranceScrollEdge + 7 + (entrance_id * 8),
camera_boundary_fe_));
}
return absl::OkStatus();
}
uint16_t entrance_id_;
uint16_t x_position_;
uint16_t y_position_;
uint16_t camera_x_;
uint16_t camera_y_;
uint16_t camera_trigger_x_;
uint16_t camera_trigger_y_;
int16_t room_;
uint8_t blockset_;
uint8_t floor_;
uint8_t dungeon_id_;
uint8_t ladder_bg_;
uint8_t scrolling_;
uint8_t scroll_quadrant_;
int16_t exit_;
uint8_t music_;
uint8_t door_;
uint8_t camera_boundary_qn_;
uint8_t camera_boundary_fn_;
uint8_t camera_boundary_qs_;
uint8_t camera_boundary_fs_;
uint8_t camera_boundary_qw_;
uint8_t camera_boundary_fw_;
uint8_t camera_boundary_qe_;
uint8_t camera_boundary_fe_;
};
} // namespace zelda3
} // namespace yaze
#endif // YAZE_APP_ZELDA3_DUNGEON_ROOM_ENTRANCE_H

View File

@@ -1,76 +0,0 @@
#include "room_layout.h"
#include "absl/strings/str_format.h"
#include "app/snes.h"
#include "app/zelda3/dungeon/dungeon_rom_addresses.h"
namespace yaze::zelda3 {
namespace {
constexpr uint16_t kLayerTerminator = 0xFFFF;
uint16_t ReadWord(const Rom* rom, int pc_addr) {
const auto& data = rom->data();
return static_cast<uint16_t>(data[pc_addr] | (data[pc_addr + 1] << 8));
}
}
absl::StatusOr<int> RoomLayout::GetLayoutAddress(int layout_id) const {
if (!rom_ || !rom_->is_loaded()) {
return absl::FailedPreconditionError("ROM not loaded");
}
if (layout_id < 0 || layout_id >= static_cast<int>(kRoomLayoutPointers.size())) {
return absl::InvalidArgumentError(absl::StrFormat("Invalid layout id %d", layout_id));
}
uint32_t snes_addr = kRoomLayoutPointers[layout_id];
int pc_addr = SnesToPc(static_cast<int>(snes_addr));
if (pc_addr < 0 || pc_addr >= static_cast<int>(rom_->size())) {
return absl::OutOfRangeError(absl::StrFormat("Layout pointer %d out of range", layout_id));
}
return pc_addr;
}
absl::Status RoomLayout::LoadLayout(int layout_id) {
objects_.clear();
auto addr_result = GetLayoutAddress(layout_id);
if (!addr_result.ok()) {
return addr_result.status();
}
int pos = addr_result.value();
const auto& rom_data = rom_->data();
int layer = 0;
while (pos + 2 < static_cast<int>(rom_->size())) {
uint8_t b1 = rom_data[pos];
uint8_t b2 = rom_data[pos + 1];
if (b1 == 0xFF && b2 == 0xFF) {
layer++;
pos += 2;
if (layer >= 3) {
break;
}
continue;
}
if (pos + 2 >= static_cast<int>(rom_->size())) {
break;
}
uint8_t b3 = rom_data[pos + 2];
pos += 3;
RoomObject obj = RoomObject::DecodeObjectFromBytes(b1, b2, b3, static_cast<uint8_t>(layer));
obj.set_rom(rom_);
obj.EnsureTilesLoaded();
objects_.push_back(obj);
}
return absl::OkStatus();
}
} // namespace yaze::zelda3

View File

@@ -1,33 +0,0 @@
#ifndef YAZE_APP_ZELDA3_DUNGEON_ROOM_LAYOUT_H
#define YAZE_APP_ZELDA3_DUNGEON_ROOM_LAYOUT_H
#include <vector>
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "app/rom.h"
#include "app/zelda3/dungeon/room_object.h"
namespace yaze::zelda3 {
class RoomLayout {
public:
RoomLayout() = default;
explicit RoomLayout(Rom* rom) : rom_(rom) {}
void set_rom(Rom* rom) { rom_ = rom; }
absl::Status LoadLayout(int layout_id);
const std::vector<RoomObject>& GetObjects() const { return objects_; }
private:
absl::StatusOr<int> GetLayoutAddress(int layout_id) const;
Rom* rom_ = nullptr;
std::vector<RoomObject> objects_;
};
} // namespace yaze::zelda3
#endif // YAZE_APP_ZELDA3_DUNGEON_ROOM_LAYOUT_H

View File

@@ -1,242 +0,0 @@
#include "room_object.h"
#include "absl/status/status.h"
#include "app/zelda3/dungeon/object_parser.h"
#include "util/log.h"
namespace yaze {
namespace zelda3 {
namespace {
struct SubtypeTableInfo {
int base_ptr; // base address of subtype table in ROM (PC)
int index_mask; // mask to apply to object id for index
SubtypeTableInfo(int base, int mask) : base_ptr(base), index_mask(mask) {}
};
SubtypeTableInfo GetSubtypeTable(int object_id) {
// Heuristic: 0x00-0xFF => subtype1, 0x100-0x1FF => subtype2, >=0x200 => subtype3
if (object_id >= 0x200) {
return SubtypeTableInfo(kRoomObjectSubtype3, 0xFF);
} else if (object_id >= 0x100) {
return SubtypeTableInfo(kRoomObjectSubtype2, 0x7F);
} else {
return SubtypeTableInfo(kRoomObjectSubtype1, 0xFF);
}
}
} // namespace
ObjectOption operator|(ObjectOption lhs, ObjectOption rhs) {
return static_cast<ObjectOption>(static_cast<int>(lhs) |
static_cast<int>(rhs));
}
ObjectOption operator&(ObjectOption lhs, ObjectOption rhs) {
return static_cast<ObjectOption>(static_cast<int>(lhs) &
static_cast<int>(rhs));
}
ObjectOption operator^(ObjectOption lhs, ObjectOption rhs) {
return static_cast<ObjectOption>(static_cast<int>(lhs) ^
static_cast<int>(rhs));
}
ObjectOption operator~(ObjectOption option) {
return static_cast<ObjectOption>(~static_cast<int>(option));
}
// NOTE: DrawTile was legacy ZScream code that is no longer used.
// Modern rendering uses ObjectDrawer which draws directly to BackgroundBuffer bitmaps.
void RoomObject::EnsureTilesLoaded() {
if (tiles_loaded_) {
return;
}
if (rom_ == nullptr) {
return;
}
// Try the new parser first - this is more efficient and accurate
auto parser_status = LoadTilesWithParser();
if (parser_status.ok()) {
tiles_loaded_ = true;
return;
}
// Fallback to legacy method for compatibility with enhanced validation
auto rom_data = rom_->data();
// Determine which subtype table to use and compute the tile data offset.
SubtypeTableInfo sti = GetSubtypeTable(id_);
int index = (id_ & sti.index_mask);
int tile_ptr = sti.base_ptr + (index * 2);
// Enhanced bounds checking
if (tile_ptr < 0 || tile_ptr + 1 >= (int)rom_->size()) {
// Log error but don't crash
return;
}
int tile_rel = (int16_t)((rom_data[tile_ptr + 1] << 8) + rom_data[tile_ptr]);
int pos = kRoomObjectTileAddress + tile_rel;
tile_data_ptr_ = pos;
// Enhanced bounds checking for tile data
if (pos < 0 || pos + 7 >= (int)rom_->size()) {
// Log error but don't crash
return;
}
// Read tile data with validation
uint16_t w0 = (uint16_t)(rom_data[pos] | (rom_data[pos + 1] << 8));
uint16_t w1 = (uint16_t)(rom_data[pos + 2] | (rom_data[pos + 3] << 8));
uint16_t w2 = (uint16_t)(rom_data[pos + 4] | (rom_data[pos + 5] << 8));
uint16_t w3 = (uint16_t)(rom_data[pos + 6] | (rom_data[pos + 7] << 8));
tiles_.clear();
tiles_.push_back(gfx::WordToTileInfo(w0));
tiles_.push_back(gfx::WordToTileInfo(w1));
tiles_.push_back(gfx::WordToTileInfo(w2));
tiles_.push_back(gfx::WordToTileInfo(w3));
tile_count_ = 1;
tiles_loaded_ = true;
}
absl::Status RoomObject::LoadTilesWithParser() {
if (rom_ == nullptr) {
return absl::InvalidArgumentError("ROM is null");
}
ObjectParser parser(rom_);
auto result = parser.ParseObject(id_);
if (!result.ok()) {
return result.status();
}
tiles_ = std::move(result.value());
tile_count_ = tiles_.size();
return absl::OkStatus();
}
absl::StatusOr<std::span<const gfx::TileInfo>> RoomObject::GetTiles() const {
if (!tiles_loaded_) {
const_cast<RoomObject*>(this)->EnsureTilesLoaded();
}
if (tiles_.empty()) {
return absl::FailedPreconditionError("No tiles loaded for object");
}
return std::span<const gfx::TileInfo>(tiles_.data(), tiles_.size());
}
absl::StatusOr<const gfx::TileInfo*> RoomObject::GetTile(int index) const {
if (!tiles_loaded_) {
const_cast<RoomObject*>(this)->EnsureTilesLoaded();
}
if (index < 0 || index >= static_cast<int>(tiles_.size())) {
return absl::OutOfRangeError(
absl::StrFormat("Tile index %d out of range (0-%d)", index, tiles_.size() - 1));
}
return &tiles_[index];
}
int RoomObject::GetTileCount() const {
if (!tiles_loaded_) {
const_cast<RoomObject*>(this)->EnsureTilesLoaded();
}
return tile_count_;
}
// ============================================================================
// Object Encoding/Decoding Implementation (Phase 1, Task 1.1)
// ============================================================================
int RoomObject::DetermineObjectType(uint8_t /* b1 */, uint8_t b3) {
// Type 3: Objects with ID >= 0xF00
// These have b3 >= 0xF8 (top nibble is 0xF)
if (b3 >= 0xF8) {
return 3;
}
// Type 1: Standard objects (ID 0x00-0xFF) - check this first
// Type 2: Objects with ID >= 0x100 (these have b1 >= 0xFC)
// We'll handle Type 2 in the decoding logic after Type 1
return 1;
}
RoomObject RoomObject::DecodeObjectFromBytes(uint8_t b1, uint8_t b2, uint8_t b3,
uint8_t layer) {
uint8_t x = 0;
uint8_t y = 0;
uint8_t size = 0;
uint16_t id = 0;
// Follow ZScream's parsing logic exactly
if (b3 >= 0xF8) {
// Type 3: xxxxxxii yyyyyyii 11111iii
// ZScream: oid = (ushort)((b3 << 4) | 0x80 + (((b2 & 0x03) << 2) + ((b1 & 0x03))));
id = (static_cast<uint16_t>(b3) << 4) | 0x80 |
((static_cast<uint16_t>(b2 & 0x03) << 2) + (b1 & 0x03));
x = (b1 & 0xFC) >> 2;
y = (b2 & 0xFC) >> 2;
size = ((b1 & 0x03) << 2) | (b2 & 0x03);
LOG_DEBUG("ObjectParser", "Type3: b1=%02X b2=%02X b3=%02X -> id=%04X x=%d y=%d size=%d",
b1, b2, b3, id, x, y, size);
} else {
// Type 1: xxxxxxss yyyyyyss iiiiiiii
id = b3;
x = (b1 & 0xFC) >> 2;
y = (b2 & 0xFC) >> 2;
size = ((b1 & 0x03) << 2) | (b2 & 0x03);
// Check for Type 2 override: 111111xx xxxxyyyy yyiiiiii
if (b1 >= 0xFC) {
id = (b3 & 0x3F) | 0x100;
x = ((b2 & 0xF0) >> 4) | ((b1 & 0x03) << 4);
y = ((b2 & 0x0F) << 2) | ((b3 & 0xC0) >> 6);
size = 0;
LOG_DEBUG("ObjectParser", "Type2: b1=%02X b2=%02X b3=%02X -> id=%04X x=%d y=%d size=%d",
b1, b2, b3, id, x, y, size);
} else {
LOG_DEBUG("ObjectParser", "Type1: b1=%02X b2=%02X b3=%02X -> id=%04X x=%d y=%d size=%d",
b1, b2, b3, id, x, y, size);
}
}
return RoomObject(static_cast<int16_t>(id), x, y, size, layer);
}
RoomObject::ObjectBytes RoomObject::EncodeObjectToBytes() const {
ObjectBytes bytes;
// Determine type based on object ID
if (id_ >= 0x100 && id_ < 0x200) {
// Type 2: 111111xx xxxxyyyy yyiiiiii
bytes.b1 = 0xFC | ((x_ & 0x30) >> 4);
bytes.b2 = ((x_ & 0x0F) << 4) | ((y_ & 0x3C) >> 2);
bytes.b3 = ((y_ & 0x03) << 6) | (id_ & 0x3F);
} else if (id_ >= 0xF00) {
// Type 3: xxxxxxii yyyyyyii 11111iii
bytes.b1 = (x_ << 2) | (id_ & 0x03);
bytes.b2 = (y_ << 2) | ((id_ >> 2) & 0x03);
bytes.b3 = (id_ >> 4) & 0xFF;
} else {
// Type 1: xxxxxxss yyyyyyss iiiiiiii
uint8_t clamped_size = size_ > 15 ? 15 : size_;
bytes.b1 = (x_ << 2) | ((clamped_size >> 2) & 0x03);
bytes.b2 = (y_ << 2) | (clamped_size & 0x03);
bytes.b3 = static_cast<uint8_t>(id_);
}
return bytes;
}
} // namespace zelda3
} // namespace yaze

View File

@@ -1,631 +0,0 @@
#ifndef YAZE_APP_ZELDA3_DUNGEON_ROOM_OBJECT_H
#define YAZE_APP_ZELDA3_DUNGEON_ROOM_OBJECT_H
#include <cstdint>
#include <string>
#include <vector>
#include "app/gfx/snes_tile.h"
#include "app/rom.h"
#include "app/zelda3/dungeon/object_parser.h"
namespace yaze {
namespace zelda3 {
enum class SpecialObjectType { Chest, BigChest, InterroomStairs };
enum Sorting {
All = 0,
Wall = 1,
Horizontal = 2,
Vertical = 4,
NonScalable = 8,
Dungeons = 16,
Floors = 32,
SortStairs = 64
};
enum class ObjectOption {
Nothing = 0,
Door = 1,
Chest = 2,
Block = 4,
Torch = 8,
Bgr = 16,
Stairs = 32
};
ObjectOption operator|(ObjectOption lhs, ObjectOption rhs);
ObjectOption operator&(ObjectOption lhs, ObjectOption rhs);
ObjectOption operator^(ObjectOption lhs, ObjectOption rhs);
ObjectOption operator~(ObjectOption option);
constexpr int kRoomObjectSubtype1 = 0x8000; // JP = Same
constexpr int kRoomObjectSubtype2 = 0x83F0; // JP = Same
constexpr int kRoomObjectSubtype3 = 0x84F0; // JP = Same
constexpr int kRoomObjectTileAddress = 0x1B52; // JP = Same
constexpr int kRoomObjectTileAddressFloor = 0x1B5A; // JP = Same
class RoomObject {
public:
enum LayerType { BG1 = 0, BG2 = 1, BG3 = 2 };
RoomObject(int16_t id, uint8_t x, uint8_t y, uint8_t size, uint8_t layer = 0)
: id_(id),
x_(x),
y_(y),
size_(size),
layer_(static_cast<LayerType>(layer)),
nx_(x),
ny_(y),
ox_(x),
oy_(y),
width_(16),
height_(16),
rom_(nullptr) {}
void set_rom(Rom* rom) { rom_ = rom; }
auto rom() { return rom_; }
auto mutable_rom() { return rom_; }
// Position setters and getters
void set_x(uint8_t x) { x_ = x; }
void set_y(uint8_t y) { y_ = y; }
void set_size(uint8_t size) { size_ = size; }
uint8_t x() const { return x_; }
uint8_t y() const { return y_; }
uint8_t size() const { return size_; }
// Ensures tiles_ is populated with a basic set based on ROM tables so we can
// preview/draw objects without needing full emulator execution.
void EnsureTilesLoaded();
// Load tiles using the new ObjectParser
absl::Status LoadTilesWithParser();
// Getter for tiles
const std::vector<gfx::TileInfo>& tiles() const { return tiles_; }
std::vector<gfx::TileInfo>& mutable_tiles() { return tiles_; }
// Get tile data through Arena system - returns references, not copies
absl::StatusOr<std::span<const gfx::TileInfo>> GetTiles() const;
// Get individual tile by index - uses Arena lookup
absl::StatusOr<const gfx::TileInfo*> GetTile(int index) const;
// Get tile count without loading all tiles
int GetTileCount() const;
// ============================================================================
// Object Encoding/Decoding (Phase 1, Task 1.1)
// ============================================================================
// 3-byte object encoding structure
struct ObjectBytes {
uint8_t b1;
uint8_t b2;
uint8_t b3;
};
// Decode object from 3-byte ROM format
// Type1: xxxxxxss yyyyyyss iiiiiiii (ID 0x00-0xFF)
// Type2: 111111xx xxxxyyyy yyiiiiii (ID 0x100-0x1FF)
// Type3: xxxxxxii yyyyyyii 11111iii (ID 0xF00-0xFFF)
static RoomObject DecodeObjectFromBytes(uint8_t b1, uint8_t b2, uint8_t b3,
uint8_t layer);
// Encode object to 3-byte ROM format
ObjectBytes EncodeObjectToBytes() const;
// Determine object type from bytes (1, 2, or 3)
static int DetermineObjectType(uint8_t b1, uint8_t b3);
// Get layer from LayerType enum
uint8_t GetLayerValue() const { return static_cast<uint8_t>(layer_); }
// ============================================================================
// NOTE: Legacy ZScream methods removed. Modern rendering uses:
// - ObjectParser for loading tiles from ROM
// - ObjectDrawer for rendering tiles to BackgroundBuffer
auto options() const { return options_; }
void set_options(ObjectOption options) { options_ = options; }
bool all_bgs_ = false;
bool lit_ = false;
int16_t id_;
uint8_t x_;
uint8_t y_;
uint8_t size_;
uint8_t nx_;
uint8_t ny_;
uint8_t ox_;
uint8_t oy_;
uint8_t z_ = 0;
uint8_t previous_size_ = 0;
// Size nibble bits captured from object encoding (0..3 each) for heuristic
// orientation and sizing decisions.
uint8_t size_x_bits_ = 0;
uint8_t size_y_bits_ = 0;
int width_;
int height_;
int offset_x_ = 0;
int offset_y_ = 0;
std::string name_;
std::vector<uint8_t> preview_object_data_;
// Tile data storage - using Arena system for efficient memory management
// Instead of copying Tile16 vectors, we store references to Arena-managed data
mutable std::vector<gfx::TileInfo> tiles_; // Individual tiles like ZScream
mutable bool tiles_loaded_ = false;
mutable int tile_count_ = 0;
mutable int tile_data_ptr_ = -1; // Pointer to tile data in ROM
LayerType layer_;
ObjectOption options_ = ObjectOption::Nothing;
Rom* rom_;
};
// NOTE: Legacy Subtype1, Subtype2, Subtype3 classes removed.
// These were ported from ZScream but are no longer used.
// Modern code uses: ObjectParser + ObjectDrawer + ObjectRenderer
constexpr static inline const char* Type1RoomObjectNames[] = {
"Ceiling ↔",
"Wall (top, north) ↔",
"Wall (top, south) ↔",
"Wall (bottom, north) ↔",
"Wall (bottom, south) ↔",
"Wall columns (north) ↔",
"Wall columns (south) ↔",
"Deep wall (north) ↔",
"Deep wall (south) ↔",
"Diagonal wall A ◤ (top) ↔",
"Diagonal wall A ◣ (top) ↔",
"Diagonal wall A ◥ (top) ↔",
"Diagonal wall A ◢ (top) ↔",
"Diagonal wall B ◤ (top) ↔",
"Diagonal wall B ◣ (top) ↔",
"Diagonal wall B ◥ (top) ↔",
"Diagonal wall B ◢ (top) ↔",
"Diagonal wall C ◤ (top) ↔",
"Diagonal wall C ◣ (top) ↔",
"Diagonal wall C ◥ (top) ↔",
"Diagonal wall C ◢ (top) ↔",
"Diagonal wall A ◤ (bottom) ↔",
"Diagonal wall A ◣ (bottom) ↔",
"Diagonal wall A ◥ (bottom) ↔",
"Diagonal wall A ◢ (bottom) ↔",
"Diagonal wall B ◤ (bottom) ↔",
"Diagonal wall B ◣ (bottom) ↔",
"Diagonal wall B ◥ (bottom) ↔",
"Diagonal wall B ◢ (bottom) ↔",
"Diagonal wall C ◤ (bottom) ↔",
"Diagonal wall C ◣ (bottom) ↔",
"Diagonal wall C ◥ (bottom) ↔",
"Diagonal wall C ◢ (bottom) ↔",
"Platform stairs ↔",
"Rail ↔",
"Pit edge ┏━┓ A (north) ↔",
"Pit edge ┏━┓ B (north) ↔",
"Pit edge ┏━┓ C (north) ↔",
"Pit edge ┏━┓ D (north) ↔",
"Pit edge ┏━┓ E (north) ↔",
"Pit edge ┗━┛ (south) ↔",
"Pit edge ━━━ (south) ↔",
"Pit edge ━━━ (north) ↔",
"Pit edge ━━┛ (south) ↔",
"Pit edge ┗━━ (south) ↔",
"Pit edge ━━┓ (north) ↔",
"Pit edge ┏━━ (north) ↔",
"Rail wall (north) ↔",
"Rail wall (south) ↔",
"Nothing",
"Nothing",
"Carpet ↔",
"Carpet trim ↔",
"Weird door", // TODO: WEIRD DOOR OBJECT NEEDS INVESTIGATION
"Drapes (north) ↔",
"Drapes (west, odd) ↔",
"Statues ↔",
"Columns ↔",
"Wall decors (north) ↔",
"Wall decors (south) ↔",
"Chairs in pairs ↔",
"Tall torches ↔",
"Supports (north) ↔",
"Water edge ┏━┓ (concave) ↔",
"Water edge ┗━┛ (concave) ↔",
"Water edge ┏━┓ (convex) ↔",
"Water edge ┗━┛ (convex) ↔",
"Water edge ┏━┛ (concave) ↔",
"Water edge ┗━┓ (concave) ↔",
"Water edge ┗━┓ (convex) ↔",
"Water edge ┏━┛ (convex) ↔",
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Supports (south) ↔",
"Bar ↔",
"Shelf A ↔",
"Shelf B ↔",
"Shelf C ↔",
"Somaria path ↔",
"Cannon hole A (north) ↔",
"Cannon hole A (south) ↔",
"Pipe path ↔",
"Nothing",
"Wall torches (north) ↔",
"Wall torches (south) ↔",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
"Cannon hole B (north) ↔",
"Cannon hole B (south) ↔",
"Thick rail ↔",
"Blocks ↔",
"Long rail ↔",
"Ceiling ↕",
"Wall (top, west) ↕",
"Wall (top, east) ↕",
"Wall (bottom, west) ↕",
"Wall (bottom, east) ↕",
"Wall columns (west) ↕",
"Wall columns (east) ↕",
"Deep wall (west) ↕",
"Deep wall (east) ↕",
"Rail ↕",
"Pit edge (west) ↕",
"Pit edge (east) ↕",
"Rail wall (west) ↕",
"Rail wall (east) ↕",
"Nothing",
"Nothing",
"Carpet ↕",
"Carpet trim ↕",
"Nothing",
"Drapes (west) ↕",
"Drapes (east) ↕",
"Columns ↕",
"Wall decors (west) ↕",
"Wall decors (east) ↕",
"Supports (west) ↕",
"Water edge (west) ↕",
"Water edge (east) ↕",
"Supports (east) ↕",
"Somaria path ↕",
"Pipe path ↕",
"Nothing",
"Wall torches (west) ↕",
"Wall torches (east) ↕",
"Wall decors tight A (west) ↕",
"Wall decors tight A (east) ↕",
"Wall decors tight B (west) ↕",
"Wall decors tight B (east) ↕",
"Cannon hole (west) ↕",
"Cannon hole (east) ↕",
"Tall torches ↕",
"Thick rail ↕",
"Blocks ↕",
"Long rail ↕",
"Jump ledge (west) ↕",
"Jump ledge (east) ↕",
"Rug trim (west) ↕",
"Rug trim (east) ↕",
"Bar ↕",
"Wall flair (west) ↕",
"Wall flair (east) ↕",
"Blue pegs ↕",
"Orange pegs ↕",
"Invisible floor ↕",
"Fake pots ↕",
"Hammer pegs ↕",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
"Diagonal ceiling A ◤",
"Diagonal ceiling A ◣",
"Diagonal ceiling A ◥",
"Diagonal ceiling A ◢",
"Pit ⇲",
"Diagonal layer 2 mask A ◤",
"Diagonal layer 2 mask A ◣",
"Diagonal layer 2 mask A ◥",
"Diagonal layer 2 mask A ◢",
"Diagonal layer 2 mask B ◤", // TODO: VERIFY
"Diagonal layer 2 mask B ◣", // TODO: VERIFY
"Diagonal layer 2 mask B ◥", // TODO: VERIFY
"Diagonal layer 2 mask B ◢", // TODO: VERIFY
"Nothing",
"Nothing",
"Nothing",
"Jump ledge (north) ↔",
"Jump ledge (south) ↔",
"Rug ↔",
"Rug trim (north) ↔",
"Rug trim (south) ↔",
"Archery game curtains ↔",
"Wall flair (north) ↔",
"Wall flair (south) ↔",
"Blue pegs ↔",
"Orange pegs ↔",
"Invisible floor ↔",
"Fake pressure plates ↔",
"Fake pots ↔",
"Hammer pegs ↔",
"Nothing",
"Nothing",
"Ceiling (large) ⇲",
"Chest platform (tall) ⇲",
"Layer 2 pit mask (large) ⇲",
"Layer 2 pit mask (medium) ⇲",
"Floor 1 ⇲",
"Floor 3 ⇲",
"Layer 2 mask (large) ⇲",
"Floor 4 ⇲",
"Water floor ⇲ ",
"Flood water (medium) ⇲ ",
"Conveyor floor ⇲ ",
"Nothing",
"Nothing",
"Moving wall (west) ⇲",
"Moving wall (east) ⇲",
"Nothing",
"Nothing",
"Icy floor A ⇲",
"Icy floor B ⇲",
"Moving wall flag", // TODO: WTF IS THIS?
"Moving wall flag", // TODO: WTF IS THIS?
"Moving wall flag", // TODO: WTF IS THIS?
"Moving wall flag", // TODO: WTF IS THIS?
"Layer 2 mask (medium) ⇲",
"Flood water (large) ⇲",
"Layer 2 swim mask ⇲",
"Flood water B (large) ⇲",
"Floor 2 ⇲",
"Chest platform (short) ⇲",
"Table / rock ⇲",
"Spike blocks ⇲",
"Spiked floor ⇲",
"Floor 7 ⇲",
"Tiled floor ⇲",
"Rupee floor ⇲",
"Conveyor upwards ⇲",
"Conveyor downwards ⇲",
"Conveyor leftwards ⇲",
"Conveyor rightwards ⇲",
"Heavy current water ⇲",
"Floor 10 ⇲",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
"Nothing",
};
constexpr static inline const char* Type2RoomObjectNames[] = {
"Corner (top, concave) ▛",
"Corner (top, concave) ▙",
"Corner (top, concave) ▜",
"Corner (top, concave) ▟",
"Corner (top, convex) ▟",
"Corner (top, convex) ▜",
"Corner (top, convex) ▙",
"Corner (top, convex) ▛",
"Corner (bottom, concave) ▛",
"Corner (bottom, concave) ▙",
"Corner (bottom, concave) ▜",
"Corner (bottom, concave) ▟",
"Corner (bottom, convex) ▟",
"Corner (bottom, convex) ▜",
"Corner (bottom, convex) ▙",
"Corner (bottom, convex) ▛",
"Kinked corner north (bottom) ▜",
"Kinked corner south (bottom) ▟",
"Kinked corner north (bottom) ▛",
"Kinked corner south (bottom) ▙",
"Kinked corner west (bottom) ▙",
"Kinked corner west (bottom) ▛",
"Kinked corner east (bottom) ▟",
"Kinked corner east (bottom) ▜",
"Deep corner (concave) ▛",
"Deep corner (concave) ▙",
"Deep corner (concave) ▜",
"Deep corner (concave) ▟",
"Large brazier",
"Statue",
"Star tile (disabled)",
"Star tile (enabled)",
"Small torch (lit)",
"Barrel",
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Table",
"Fairy statue",
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Chair",
"Bed",
"Fireplace",
"Mario portrait",
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Interroom stairs (up)",
"Interroom stairs (down)",
"Interroom stairs B (down)",
"Intraroom stairs north B", // TODO: VERIFY LAYER HANDLING
"Intraroom stairs north (separate layers)",
"Intraroom stairs north (merged layers)",
"Intraroom stairs north (swim layer)",
"Block",
"Water ladder (north)",
"Water ladder (south)", // TODO: NEEDS IN GAME VERIFICATION
"Dam floodgate",
"Interroom spiral stairs up (top)",
"Interroom spiral stairs down (top)",
"Interroom spiral stairs up (bottom)",
"Interroom spiral stairs down (bottom)",
"Sanctuary wall (north)",
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Pew",
"Magic bat altar",
};
constexpr static inline const char* Type3RoomObjectNames[] = {
"Waterfall face (empty)",
"Waterfall face (short)",
"Waterfall face (long)",
"Somaria path endpoint",
"Somaria path intersection ╋",
"Somaria path corner ┏",
"Somaria path corner ┗",
"Somaria path corner ┓",
"Somaria path corner ┛",
"Somaria path intersection ┳",
"Somaria path intersection ┻",
"Somaria path intersection ┣",
"Somaria path intersection ┫",
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Somaria path 2-way endpoint",
"Somaria path crossover",
"Babasu hole (north)",
"Babasu hole (south)",
"9 blue rupees",
"Telepathy tile",
"Warp door", // TODO: NEEDS IN GAME VERIFICATION THAT THIS IS USELESS
"Kholdstare's shell",
"Hammer peg",
"Prison cell",
"Big key lock",
"Chest",
"Chest (open)",
"Intraroom stairs south", // TODO: VERIFY LAYER HANDLING
"Intraroom stairs south (separate layers)",
"Intraroom stairs south (merged layers)",
"Interroom straight stairs up (north, top)",
"Interroom straight stairs down (north, top)",
"Interroom straight stairs up (south, top)",
"Interroom straight stairs down (south, top)",
"Deep corner (convex) ▟",
"Deep corner (convex) ▜",
"Deep corner (convex) ▙",
"Deep corner (convex) ▛",
"Interroom straight stairs up (north, bottom)",
"Interroom straight stairs down (north, bottom)",
"Interroom straight stairs up (south, bottom)",
"Interroom straight stairs down (south, bottom)",
"Lamp cones",
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Liftable large block",
"Agahnim's altar",
"Agahnim's boss room",
"Pot",
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Big chest",
"Big chest (open)",
"Intraroom stairs south (swim layer)",
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Pipe end (south)",
"Pipe end (north)",
"Pipe end (east)",
"Pipe end (west)",
"Pipe corner ▛",
"Pipe corner ▙",
"Pipe corner ▜",
"Pipe corner ▟",
"Pipe-rock intersection ⯊",
"Pipe-rock intersection ⯋",
"Pipe-rock intersection ◖",
"Pipe-rock intersection ◗",
"Pipe crossover",
"Bombable floor",
"Fake bombable floor",
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Warp tile",
"Tool rack",
"Furnace",
"Tub (wide)",
"Anvil",
"Warp tile (disabled)",
"Pressure plate",
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Blue peg",
"Orange peg",
"Fortune teller room",
"Unknown", // TODO: NEEDS IN GAME CHECKING
"Bar corner ▛",
"Bar corner ▙",
"Bar corner ▜",
"Bar corner ▟",
"Decorative bowl",
"Tub (tall)",
"Bookcase",
"Range",
"Suitcase",
"Bar bottles",
"Arrow game hole (west)",
"Arrow game hole (east)",
"Vitreous goo gfx",
"Fake pressure plate",
"Medusa head",
"4-way shooter block",
"Pit",
"Wall crack (north)",
"Wall crack (south)",
"Wall crack (west)",
"Wall crack (east)",
"Large decor",
"Water grate (north)",
"Water grate (south)",
"Water grate (west)",
"Water grate (east)",
"Window sunlight",
"Floor sunlight",
"Trinexx's shell",
"Layer 2 mask (full)",
"Boss entrance",
"Minigame chest",
"Ganon door",
"Triforce wall ornament",
"Triforce floor tiles",
"Freezor hole",
"Pile of bones",
"Vitreous goo damage",
"Arrow tile ↑",
"Arrow tile ↓",
"Arrow tile →",
"Nothing",
};
} // namespace zelda3
} // namespace yaze
#endif // YAZE_APP_ZELDA3_DUNGEON_ROOM_OBJECT_H

File diff suppressed because it is too large Load Diff

View File

@@ -1,283 +0,0 @@
#ifndef YAZE_APP_ZELDA3_TRACKER_H
#define YAZE_APP_ZELDA3_TRACKER_H
#include <vector>
#include "app/rom.h"
#include "util/macro.h"
namespace yaze {
namespace zelda3 {
/**
* @namespace yaze::zelda3::music
* @brief Contains classes and functions for handling music data in Zelda 3.
*
* Based off of the Hyrule Magic tracker code, this system is designed to parse
* the game's complex, pointer-based music format into an editable in-memory
* representation and then serialize it back into a binary format that the
* SNES Audio Processing Unit (APU) can understand.
*/
namespace music {
// Length of each SPC700 command opcode (0xE0-0xFF). Used for parsing.
constexpr char op_len[32] = {1, 1, 2, 3, 0, 1, 2, 1, 2, 1, 1, 3, 0, 1, 2, 3,
1, 3, 3, 0, 1, 3, 0, 3, 3, 3, 1, 2, 0, 0, 0, 0};
// Default ROM offsets for specific sound banks.
static int sbank_ofs[] = {0xc8000, 0, 0xd8000, 0};
// Filter coefficients for BRR sample decoding.
constexpr char fil1[4] = {0, 15, 61, 115};
constexpr char fil2[4] = {0, 4, 5, 6};
constexpr char fil3[4] = {0, 0, 15, 13};
constexpr int kOverworldMusicBank = 0x0D0000;
constexpr int kDungeonMusicBank = 0x0D8000;
using text_buf_ty = char[512];
/**
* @struct SongSpcBlock
* @brief Represents a block of binary data destined for the APU (SPC700) RAM.
* This is the intermediate format used before writing data back to the ROM.
*/
struct SongSpcBlock {
unsigned short start; // The starting address of this block in the virtual SPC memory space.
unsigned short len; // Length of the data buffer.
unsigned short relnum; // Number of relocation entries.
unsigned short relsz; // Allocated size of the relocation table.
unsigned short *relocs; // Table of offsets within 'buf' that are pointers and need to be relocated.
unsigned short bank; // The target sound bank.
unsigned short addr; // The final, relocated address of this block.
unsigned char *buf; // The raw binary data for this block.
int flag; // Flags for managing the block's state.
};
/**
* @struct SongRange
* @brief A metadata structure to keep track of parsed sections of the song data.
* Used to avoid re-parsing the same data from the ROM multiple times.
*/
struct SongRange {
unsigned short start; // Start address of this range in the ROM.
unsigned short end; // End address of this range in the ROM.
short first; // Index of the first SpcCommand in this range.
short inst; // Instance count, for tracking usage.
short bank; // The ROM bank this range was loaded from.
unsigned char endtime; // Default time/duration for this block.
unsigned char filler;
int editor; // Window handle for an associated editor, if any.
};
/**
* @struct SongPart
* @brief Represents one of the 8 channels (tracks) in a song.
*/
struct SongPart {
uint8_t flag; // State flags for parsing and saving.
uint8_t inst; // Instance count.
short tbl[8]; // Pointers to the first SpcCommand for each of the 8 channels.
unsigned short addr; // The address of this part's data in the ROM.
};
/**
* @struct Song
* @brief Represents a complete song, which is a collection of SongParts.
*/
struct Song {
unsigned char flag; // State flags.
unsigned char inst; // Instance count.
SongPart **tbl; // Table of pointers to the song's parts.
short numparts; // Number of parts in the song.
short lopst; // Loop start point.
unsigned short addr; // Address of the song's main data table in the ROM.
bool in_use; // Flag indicating if the song is currently loaded.
};
/**
* @struct ZeldaWave
* @brief Represents a decoded instrument sample (a waveform).
*/
struct ZeldaWave {
int lopst; // Loop start point within the sample data.
int end; // End of the sample data.
short lflag; // Loop flag.
short copy; // Index of another wave this is a copy of, to save memory.
short *buf; // The buffer containing the decoded PCM sample data.
};
/**
* @struct SampleEdit
* @brief A state structure for a GUI sample editor.
*/
struct SampleEdit {
unsigned short flag;
unsigned short init;
unsigned short editsamp;
int width;
int height;
int pageh;
int pagev;
int zoom;
int scroll;
int page;
/// Left hand sample selection point
int sell;
/// Right hand sample selection point
int selr;
int editinst;
ZeldaWave *zw;
};
/**
* @struct ZeldaInstrument
* @brief Defines an instrument for a song, mapping to a sample and ADSR settings.
*/
struct ZeldaInstrument {
unsigned char samp; // Index of the sample (ZeldaWave) to use.
unsigned char ad; // Attack & Decay rates.
unsigned char sr; // Sustain Rate.
unsigned char gain; // Sustain Level & Gain.
unsigned char multhi; // Pitch multiplier (high byte).
unsigned char multlo; // Pitch multiplier (low byte).
};
/**
* @struct ZeldaSfxInstrument
* @brief Defines an instrument for a sound effect.
*/
struct ZeldaSfxInstrument {
unsigned char voll;
unsigned char volr;
short freq;
unsigned char samp;
unsigned char ad;
unsigned char sr;
unsigned char gain;
unsigned char multhi;
};
/**
* @struct SpcCommand
* @brief The core data structure representing a single command in a music track.
* A song track is a doubly-linked list of these commands.
*/
struct SpcCommand {
unsigned short addr; // The ROM address this command was loaded from.
short next; // Index of the next command in the list.
short prev; // Index of the previous command in the list.
unsigned char flag; // Bitfield for command properties (e.g., has duration).
unsigned char cmd; // The actual command/opcode.
unsigned char p1; // Parameter 1.
unsigned char p2; // Parameter 2.
unsigned char p3; // Parameter 3.
unsigned char b1; // Note duration or first byte of a multi-byte duration.
unsigned char b2; // Second byte of a multi-byte duration.
unsigned char tim2; // Calculated time/duration component.
unsigned short tim; // Calculated time/duration component.
};
class Tracker {
public:
SongSpcBlock *AllocSpcBlock(int len, int bank);
unsigned char *GetSpcAddr(Rom &rom, unsigned short addr, short bank);
short AllocSpcCommand();
short GetBlockTime(Rom &rom, short num, short prevtime);
short SaveSpcCommand(Rom &rom, short num, short songtime, short endtr);
short LoadSpcCommand(Rom &rom, unsigned short addr, short bank, int t);
void SaveSongs(Rom &rom);
void LoadSongs(Rom &rom);
int WriteSpcData(Rom &rom, void *buf, int len, int addr, int spc, int limit);
void EditTrack(Rom &rom, short i);
void NewSR(Rom &rom, int bank);
private:
// A "modified" flag
int modf;
int mark_sr;
int mark_start;
int mark_end;
int mark_first;
int mark_last;
int numwave;
int numinst;
int numsndinst;
int sndinit = 0;
int sndlen1;
int sndlen2;
int m_ofs;
int w_modf;
int ss_num;
int ss_size;
char op_len[32];
char *snddat1;
char *snddat2; // more music stuff.
unsigned short ss_next = 0;
unsigned short spclen;
unsigned short numseg;
short spcbank;
short lastsr;
short ss_lasttime;
short srnum;
short srsize;
short numsong[3]; // ditto
short m_size;
short m_free;
short m_modf; // ???
short m_loaded;
short t_loaded;
short t_modf;
short withhead;
size_t t_number;
std::vector<Song> songs;
SongPart *sp_mark;
SongRange *song_range_;
SpcCommand *current_spc_command_;
const SpcCommand& GetSpcCommand(short index) const {
return current_spc_command_[index];
}
SongSpcBlock **ssblt;
ZeldaWave *waves;
ZeldaInstrument *insts;
ZeldaSfxInstrument *sndinsts;
};
} // namespace music
} // namespace zelda3
} // namespace yaze
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -1,384 +0,0 @@
#ifndef YAZE_APP_DATA_OVERWORLD_H
#define YAZE_APP_DATA_OVERWORLD_H
#include <array>
#include <vector>
#include <mutex>
#include "absl/status/status.h"
#include "app/gfx/snes_tile.h"
#include "app/rom.h"
#include "app/zelda3/overworld/overworld_entrance.h"
#include "app/zelda3/overworld/overworld_exit.h"
#include "app/zelda3/overworld/overworld_item.h"
#include "app/zelda3/overworld/overworld_map.h"
#include "app/zelda3/sprite/sprite.h"
namespace yaze {
namespace zelda3 {
constexpr int GravesYTilePos = 0x49968; // short (0x0F entries)
constexpr int GravesXTilePos = 0x49986; // short (0x0F entries)
constexpr int GravesTilemapPos = 0x499A4; // short (0x0F entries)
constexpr int GravesGFX = 0x499C2; // short (0x0F entries)
constexpr int GravesXPos = 0x4994A; // short (0x0F entries)
constexpr int GravesYLine = 0x4993A; // short (0x08 entries)
constexpr int GravesCountOnY = 0x499E0; // Byte 0x09 entries
constexpr int GraveLinkSpecialHole = 0x46DD9; // short
constexpr int GraveLinkSpecialStairs = 0x46DE0; // short
constexpr int kOverworldMapPaletteIds = 0x7D1C;
constexpr int kOverworldSpritePaletteIds = 0x7B41;
constexpr int kOverworldSpritePaletteGroup = 0x75580;
constexpr int kOverworldSpriteset = 0x7A41;
constexpr int kOverworldSpecialGfxGroup = 0x16821;
constexpr int kOverworldSpecialPalGroup = 0x16831;
constexpr int kOverworldSpritesBeginning = 0x4C881;
constexpr int kOverworldSpritesAgahnim = 0x4CA21;
constexpr int kOverworldSpritesZelda = 0x4C901;
constexpr int kAreaGfxIdPtr = 0x7C9C;
constexpr int kOverworldMessageIds = 0x3F51D;
constexpr int kOverworldMusicBeginning = 0x14303;
constexpr int kOverworldMusicZelda = 0x14303 + 0x40;
constexpr int kOverworldMusicMasterSword = 0x14303 + 0x80;
constexpr int kOverworldMusicAgahnim = 0x14303 + 0xC0;
constexpr int kOverworldMusicDarkWorld = 0x14403;
constexpr int kOverworldEntranceAllowedTilesLeft = 0xDB8C1;
constexpr int kOverworldEntranceAllowedTilesRight = 0xDB917;
// 0x00 = small maps, 0x20 = large maps
constexpr int kOverworldMapSize = 0x12844;
// 0x01 = small maps, 0x03 = large maps
constexpr int kOverworldMapSizeHighByte = 0x12884;
// relative to the WORLD + 0x200 per map
// large map that are not == parent id = same position as their parent!
// eg for X position small maps :
// 0000, 0200, 0400, 0600, 0800, 0A00, 0C00, 0E00
// all Large map would be :
// 0000, 0000, 0400, 0400, 0800, 0800, 0C00, 0C00
constexpr int kOverworldMapParentId = 0x125EC;
constexpr int kOverworldTransitionPositionY = 0x128C4;
constexpr int kOverworldTransitionPositionX = 0x12944;
constexpr int kOverworldScreenSize = 0x1788D;
constexpr int kOverworldScreenSizeForLoading = 0x4C635;
constexpr int kOverworldScreenTileMapChangeByScreen1 = 0x12634;
constexpr int kOverworldScreenTileMapChangeByScreen2 = 0x126B4;
constexpr int kOverworldScreenTileMapChangeByScreen3 = 0x12734;
constexpr int kOverworldScreenTileMapChangeByScreen4 = 0x127B4;
constexpr int kOverworldMapDataOverflow = 0x130000;
constexpr int kTransitionTargetNorth = 0x13EE2;
constexpr int kTransitionTargetWest = 0x13F62;
constexpr int overworldCustomMosaicASM = 0x1301D0;
constexpr int overworldCustomMosaicArray = 0x1301F0;
// Expanded tile16 and tile32
constexpr int kMap16TilesExpanded = 0x1E8000;
constexpr int kMap32TileTRExpanded = 0x020000;
constexpr int kMap32TileBLExpanded = 0x1F0000;
constexpr int kMap32TileBRExpanded = 0x1F8000;
constexpr int kMap32TileCountExpanded = 0x0067E0;
constexpr int kMap32ExpandedFlagPos = 0x01772E; // 0x04
constexpr int kMap16ExpandedFlagPos = 0x02FD28; // 0x0F
constexpr int kOverworldEntranceExpandedFlagPos = 0x0DB895; // 0xB8
constexpr int overworldSpritesBeginingExpanded = 0x141438;
constexpr int overworldSpritesZeldaExpanded = 0x141578;
constexpr int overworldSpritesAgahnimExpanded = 0x1416B8;
constexpr int overworldSpritesDataStartExpanded = 0x04C881;
constexpr int overworldSpecialSpriteGFXGroupExpandedTemp = 0x0166E1;
constexpr int overworldSpecialSpritePaletteExpandedTemp = 0x016701;
constexpr int ExpandedOverlaySpace = 0x120000;
constexpr int overworldTilesType = 0x071459;
constexpr int overworldMessages = 0x03F51D;
constexpr int overworldMessagesExpanded = 0x1417F8;
constexpr int overworldItemsPointers = 0x0DC2F9;
constexpr int overworldItemsAddress = 0x0DC8B9; // 1BC2F9
constexpr int overworldItemsAddressBank = 0x0DC8BF;
constexpr int overworldItemsEndData = 0x0DC89C; // 0DC89E
constexpr int overworldBombDoorItemLocationsNew = 0x012644;
constexpr int overworldItemsPointersNew = 0x012784;
constexpr int overworldItemsStartDataNew = 0x0DC2F9;
constexpr int kOverworldCompressedMapPos = 0x058000;
constexpr int kOverworldCompressedOverflowPos = 0x137FFF;
constexpr int kNumTileTypes = 0x200;
constexpr int kMap16Tiles = 0x78000;
constexpr int kNumOverworldMaps = 160;
constexpr int kNumTile16Individual = 4096;
constexpr int Map32PerScreen = 256;
constexpr int NumberOfMap16 = 3752; // 4096
constexpr int NumberOfMap16Ex = 4096; // 4096
constexpr int LimitOfMap32 = 8864;
constexpr int NumberOfOWSprites = 352;
constexpr int NumberOfMap32 = Map32PerScreen * kNumOverworldMaps;
constexpr int kNumMapsPerWorld = 0x40;
/**
* @brief Represents the full Overworld data, light and dark world.
*
* This class is responsible for loading and saving the overworld data,
* as well as creating the tilesets and tilemaps for the overworld.
*/
class Overworld {
public:
Overworld(Rom *rom) : rom_(rom) {}
absl::Status Load(Rom *rom);
absl::Status LoadOverworldMaps();
void LoadTileTypes();
absl::Status LoadEntrances();
absl::Status LoadHoles();
absl::Status LoadExits();
absl::Status LoadItems();
absl::Status LoadSprites();
absl::Status LoadSpritesFromMap(int sprite_start, int sprite_count,
int sprite_index);
/**
* @brief Build a map on-demand if it hasn't been built yet
*
* This method checks if the specified map needs to be built and builds it
* if necessary. Used for lazy loading optimization.
*/
absl::Status EnsureMapBuilt(int map_index);
absl::Status Save(Rom *rom);
absl::Status SaveOverworldMaps();
absl::Status SaveLargeMaps();
absl::Status SaveLargeMapsExpanded();
absl::Status SaveSmallAreaTransitions(int i, int parent_x_pos, int parent_y_pos,
int transition_target_north, int transition_target_west,
int transition_pos_x, int transition_pos_y,
int screen_change_1, int screen_change_2,
int screen_change_3, int screen_change_4);
absl::Status SaveLargeAreaTransitions(int i, int parent_x_pos, int parent_y_pos,
int transition_target_north, int transition_target_west,
int transition_pos_x, int transition_pos_y,
int screen_change_1, int screen_change_2,
int screen_change_3, int screen_change_4);
absl::Status SaveWideAreaTransitions(int i, int parent_x_pos, int parent_y_pos,
int transition_target_north, int transition_target_west,
int transition_pos_x, int transition_pos_y,
int screen_change_1, int screen_change_2,
int screen_change_3, int screen_change_4);
absl::Status SaveTallAreaTransitions(int i, int parent_x_pos, int parent_y_pos,
int transition_target_north, int transition_target_west,
int transition_pos_x, int transition_pos_y,
int screen_change_1, int screen_change_2,
int screen_change_3, int screen_change_4);
absl::Status SaveEntrances();
absl::Status SaveExits();
absl::Status SaveItems();
absl::Status SaveMapOverlays();
absl::Status SaveOverworldTilesType();
absl::Status SaveCustomOverworldASM(bool enable_bg_color, bool enable_main_palette,
bool enable_mosaic, bool enable_gfx_groups,
bool enable_subscreen_overlay, bool enable_animated);
absl::Status SaveAreaSpecificBGColors();
absl::Status CreateTile32Tilemap();
absl::Status SaveMap16Expanded();
absl::Status SaveMap16Tiles();
absl::Status SaveMap32Expanded();
absl::Status SaveMap32Tiles();
absl::Status SaveMapProperties();
absl::Status SaveMusic();
absl::Status SaveAreaSizes();
void AssignMapSizes(std::vector<OverworldMap>& maps);
/**
* @brief Configure a multi-area map structure (Large/Wide/Tall)
* @param parent_index The parent map index
* @param size The area size to configure
* @return Status of the configuration
*
* Properly sets up sibling relationships and updates ROM data for v3+.
*/
absl::Status ConfigureMultiAreaMap(int parent_index, AreaSizeEnum size);
auto rom() const { return rom_; }
auto mutable_rom() { return rom_; }
void Destroy() {
for (auto &map : overworld_maps_) {
map.Destroy();
}
overworld_maps_.clear();
all_entrances_.clear();
all_exits_.clear();
all_items_.clear();
for (auto &sprites : all_sprites_) {
sprites.clear();
}
tiles16_.clear();
tiles32_.clear();
tiles32_unique_.clear();
is_loaded_ = false;
}
int GetTileFromPosition(ImVec2 position) const {
if (current_world_ == 0) {
return map_tiles_.light_world[position.x][position.y];
} else if (current_world_ == 1) {
return map_tiles_.dark_world[position.x][position.y];
} else {
return map_tiles_.special_world[position.x][position.y];
}
}
OverworldBlockset &GetMapTiles(int world_type) {
switch (world_type) {
case 0:
return map_tiles_.light_world;
case 1:
return map_tiles_.dark_world;
case 2:
return map_tiles_.special_world;
default:
return map_tiles_.light_world;
}
}
auto overworld_maps() const { return overworld_maps_; }
auto overworld_map(int i) const { return &overworld_maps_[i]; }
auto mutable_overworld_map(int i) { return &overworld_maps_[i]; }
auto exits() const { return &all_exits_; }
auto mutable_exits() { return &all_exits_; }
std::vector<gfx::Tile16> tiles16() const { return tiles16_; }
auto tiles32_unique() const { return tiles32_unique_; }
auto mutable_tiles16() { return &tiles16_; }
auto sprites(int state) const { return all_sprites_[state]; }
auto mutable_sprites(int state) { return &all_sprites_[state]; }
auto current_graphics() const {
return overworld_maps_[current_map_].current_graphics();
}
const std::vector<OverworldEntrance> &entrances() const { return all_entrances_; }
auto &entrances() { return all_entrances_; }
auto mutable_entrances() { return &all_entrances_; }
const std::vector<OverworldEntrance> &holes() const { return all_holes_; }
auto &holes() { return all_holes_; }
auto mutable_holes() { return &all_holes_; }
auto deleted_entrances() const { return deleted_entrances_; }
auto mutable_deleted_entrances() { return &deleted_entrances_; }
auto current_area_palette() const {
return overworld_maps_[current_map_].current_palette();
}
auto current_map_bitmap_data() const {
return overworld_maps_[current_map_].bitmap_data();
}
auto tile16_blockset_data() const {
return overworld_maps_[current_map_].current_tile16_blockset();
}
auto is_loaded() const { return is_loaded_; }
auto expanded_tile16() const { return expanded_tile16_; }
auto expanded_tile32() const { return expanded_tile32_; }
auto expanded_entrances() const { return expanded_entrances_; }
void set_current_map(int i) { current_map_ = i; }
void set_current_world(int world) { current_world_ = world; }
uint16_t GetTile(int x, int y) const {
if (current_world_ == 0) {
return map_tiles_.light_world[y][x];
} else if (current_world_ == 1) {
return map_tiles_.dark_world[y][x];
} else {
return map_tiles_.special_world[y][x];
}
}
void SetTile(int x, int y, uint16_t tile_id) {
if (current_world_ == 0) {
map_tiles_.light_world[y][x] = tile_id;
} else if (current_world_ == 1) {
map_tiles_.dark_world[y][x] = tile_id;
} else {
map_tiles_.special_world[y][x] = tile_id;
}
}
auto map_tiles() const { return map_tiles_; }
auto mutable_map_tiles() { return &map_tiles_; }
auto all_items() const { return all_items_; }
auto mutable_all_items() { return &all_items_; }
auto all_tiles_types() const { return all_tiles_types_; }
auto mutable_all_tiles_types() { return &all_tiles_types_; }
auto all_sprites() const { return all_sprites_; }
private:
enum Dimension {
map32TilesTL = 0,
map32TilesTR = 1,
map32TilesBL = 2,
map32TilesBR = 3
};
void FetchLargeMaps();
absl::StatusOr<uint16_t> GetTile16ForTile32(int index, int quadrant,
int dimension,
const uint32_t *map32address);
absl::Status AssembleMap32Tiles();
absl::Status AssembleMap16Tiles();
void AssignWorldTiles(int x, int y, int sx, int sy, int tpos,
OverworldBlockset &world);
void OrganizeMapTiles(std::vector<uint8_t> &bytes,
std::vector<uint8_t> &bytes2, int i, int sx, int sy,
int &ttpos);
void DecompressAllMapTiles();
absl::Status DecompressAllMapTilesParallel();
Rom *rom_;
bool is_loaded_ = false;
bool expanded_tile16_ = false;
bool expanded_tile32_ = false;
bool expanded_entrances_ = false;
int game_state_ = 0;
int current_map_ = 0;
int current_world_ = 0;
OverworldMapTiles map_tiles_;
// Thread safety for parallel operations
mutable std::mutex map_tiles_mutex_;
std::vector<OverworldMap> overworld_maps_;
std::vector<OverworldEntrance> all_entrances_;
std::vector<OverworldEntrance> all_holes_;
std::vector<OverworldExit> all_exits_;
std::vector<OverworldItem> all_items_;
std::vector<gfx::Tile16> tiles16_;
std::vector<gfx::Tile32> tiles32_;
std::vector<gfx::Tile32> tiles32_unique_;
std::vector<uint16_t> tiles32_list_;
std::vector<uint64_t> deleted_entrances_;
std::array<uint8_t, kNumOverworldMaps> map_parent_ = {0};
std::array<uint8_t, kNumTileTypes> all_tiles_types_ = {0};
std::array<std::vector<Sprite>, 3> all_sprites_;
std::array<std::vector<uint8_t>, kNumOverworldMaps> map_data_p1;
std::array<std::vector<uint8_t>, kNumOverworldMaps> map_data_p2;
std::array<int, kNumOverworldMaps> map_pointers1_id;
std::array<int, kNumOverworldMaps> map_pointers2_id;
std::array<int, kNumOverworldMaps> map_pointers1;
std::array<int, kNumOverworldMaps> map_pointers2;
};
} // namespace zelda3
} // namespace yaze
#endif

View File

@@ -1,144 +0,0 @@
#ifndef YAZE_APP_ZELDA3_OVERWORLD_ENTRANCE_H
#define YAZE_APP_ZELDA3_OVERWORLD_ENTRANCE_H
#include <cstdint>
#include "app/rom.h"
#include "app/zelda3/common.h"
#include "util/macro.h"
namespace yaze {
namespace zelda3 {
// EXPANDED to 0x78000 to 0x7A000
constexpr int kEntranceRoomEXP = 0x078000;
constexpr int kEntranceScrollEdgeEXP = 0x078200;
constexpr int kEntranceCameraYEXP = 0x078A00;
constexpr int kEntranceCameraXEXP = 0x078C00;
constexpr int kEntranceYPositionEXP = 0x078E00;
constexpr int kEntranceXPositionEXP = 0x079000;
constexpr int kEntranceCameraYTriggerEXP = 0x079200;
constexpr int kEntranceCameraXTriggerEXP = 0x079400;
constexpr int kEntranceBlocksetEXP = 0x079600;
constexpr int kEntranceFloorEXP = 0x079700;
constexpr int kEntranceDungeonEXP = 0x079800;
constexpr int kEntranceDoorEXP = 0x079900;
constexpr int kEntranceLadderBgEXP = 0x079A00;
constexpr int kEntranceScrollingEXP = 0x079B00;
constexpr int kEntranceScrollQuadrantEXP = 0x079C00;
constexpr int kEntranceExitEXP = 0x079D00;
constexpr int kEntranceMusicEXP = 0x079F00;
constexpr int kEntranceExtraEXP = 0x07A000;
constexpr int kEntranceTotalEXP = 0xFF;
constexpr int kEntranceTotal = 0x84;
constexpr int kEntranceLinkSpawn = 0x00;
constexpr int kEntranceNorthTavern = 0x43;
constexpr int kEntranceEXP = 0x07F000;
constexpr int kEntranceCameraY = 0x014D45; // 0x14AA9 // 2bytes each room
constexpr int kEntranceCameraX = 0x014E4F; // 0x14BB3 // 2bytes
constexpr int kNumOverworldEntrances = 129;
constexpr int kNumOverworldHoles = 0x13;
constexpr int kOverworldEntranceMap = 0xDB96F;
constexpr int kOverworldEntrancePos = 0xDBA71;
constexpr int kOverworldEntranceEntranceId = 0xDBB73;
constexpr int kOverworldEntranceMapExpanded = 0x0DB55F;
constexpr int kOverworldEntrancePosExpanded = 0x0DB35F;
constexpr int kOverworldEntranceEntranceIdExpanded = 0x0DB75F;
// (0x13 entries, 2 bytes each) modified(less 0x400)
// map16 coordinates for each hole
constexpr int kOverworldHolePos = 0xDB800;
// (0x13 entries, 2 bytes each) corresponding
// area numbers for each hole
constexpr int kOverworldHoleArea = 0xDB826;
//(0x13 entries, 1 byte each) corresponding entrance numbers
constexpr int kOverworldHoleEntrance = 0xDB84C;
// OWEntrances Expansion
// Instructions for editors
// if byte at (PC) address 0xDB895 == B8 then it is vanilla
// Load normal overworld entrances data
// Otherwise load from the expanded space
// When saving just save in expanded space 256 values for each
// (PC Addresses) - you can find snes address at the orgs below
// 0x0DB35F = (short) Map16 tile address (mapPos in ZS)
// 0x0DB55F = (short) Screen ID (MapID in ZS)
// 0x0DB75F = (byte) Entrance leading to (EntranceID in ZS)
// *Important* the Screen ID now also require bit 0x8000 (15) being set to tell
// entrance is a hole
class OverworldEntrance : public GameEntity {
public:
uint16_t map_pos_;
uint8_t entrance_id_;
uint8_t area_x_;
uint8_t area_y_;
bool is_hole_ = false;
bool deleted = false;
OverworldEntrance() = default;
OverworldEntrance(int x, int y, uint8_t entrance_id, short map_id,
uint16_t map_pos, bool hole)
: map_pos_(map_pos), entrance_id_(entrance_id), is_hole_(hole) {
x_ = x;
y_ = y;
map_id_ = map_id;
entity_id_ = entrance_id;
entity_type_ = kEntrance;
int mapX = (map_id_ - ((map_id_ / 8) * 8));
int mapY = (map_id_ / 8);
area_x_ = (uint8_t)((std::abs(x - (mapX * 512)) / 16));
area_y_ = (uint8_t)((std::abs(y - (mapY * 512)) / 16));
}
void UpdateMapProperties(uint16_t map_id) override {
map_id_ = map_id;
if (map_id_ >= 64) {
map_id_ -= 64;
}
int mapX = (map_id_ - ((map_id_ / 8) * 8));
int mapY = (map_id_ / 8);
area_x_ = (uint8_t)((std::abs(x_ - (mapX * 512)) / 16));
area_y_ = (uint8_t)((std::abs(y_ - (mapY * 512)) / 16));
map_pos_ = (uint16_t)((((area_y_) << 6) | (area_x_ & 0x3F)) << 1);
}
};
constexpr int kEntranceTileTypePtrLow = 0xDB8BF;
constexpr int kEntranceTileTypePtrHigh = 0xDB917;
constexpr int kNumEntranceTileTypes = 0x2C;
struct OverworldEntranceTileTypes {
std::array<uint16_t, kNumEntranceTileTypes> low;
std::array<uint16_t, kNumEntranceTileTypes> high;
};
inline absl::StatusOr<OverworldEntranceTileTypes> LoadEntranceTileTypes(
Rom *rom) {
OverworldEntranceTileTypes tiletypes;
for (int i = 0; i < kNumEntranceTileTypes; i++) {
ASSIGN_OR_RETURN(auto value_low,
rom->ReadWord(kEntranceTileTypePtrLow + i));
tiletypes.low[i] = value_low;
ASSIGN_OR_RETURN(auto value_high,
rom->ReadWord(kEntranceTileTypePtrHigh + i));
tiletypes.high[i] = value_high;
}
return tiletypes;
}
} // namespace zelda3
} // namespace yaze
#endif

View File

@@ -1,203 +0,0 @@
#ifndef YAZE_APP_ZELDA3_OVERWORLD_EXIT_H
#define YAZE_APP_ZELDA3_OVERWORLD_EXIT_H
#include <cstdint>
#include <iostream>
#include "app/zelda3/common.h"
namespace yaze {
namespace zelda3 {
constexpr int kNumOverworldExits = 0x4F;
constexpr int OWExitRoomId = 0x15D8A; // 0x15E07 Credits sequences
// 105C2 Ending maps
// 105E2 Sprite Group Table for Ending
constexpr int OWExitMapId = 0x15E28;
constexpr int OWExitVram = 0x15E77;
constexpr int OWExitYScroll = 0x15F15;
constexpr int OWExitXScroll = 0x15FB3;
constexpr int OWExitYPlayer = 0x16051;
constexpr int OWExitXPlayer = 0x160EF;
constexpr int OWExitYCamera = 0x1618D;
constexpr int OWExitXCamera = 0x1622B;
constexpr int OWExitDoorPosition = 0x15724;
constexpr int OWExitUnk1 = 0x162C9;
constexpr int OWExitUnk2 = 0x16318;
constexpr int OWExitDoorType1 = 0x16367;
constexpr int OWExitDoorType2 = 0x16405;
constexpr int OWExitMapIdWhirlpool = 0x16AE5; // JP = ;016849
constexpr int OWExitVramWhirlpool = 0x16B07; // JP = ;01686B
constexpr int OWExitYScrollWhirlpool = 0x16B29; // JP = ;01688D
constexpr int OWExitXScrollWhirlpool = 0x16B4B; // JP = ;016DE7
constexpr int OWExitYPlayerWhirlpool = 0x16B6D; // JP = ;016E09
constexpr int OWExitXPlayerWhirlpool = 0x16B8F; // JP = ;016E2B
constexpr int OWExitYCameraWhirlpool = 0x16BB1; // JP = ;016E4D
constexpr int OWExitXCameraWhirlpool = 0x16BD3; // JP = ;016E6F
constexpr int OWExitUnk1Whirlpool = 0x16BF5; // JP = ;016E91
constexpr int OWExitUnk2Whirlpool = 0x16C17; // JP = ;016EB3
constexpr int OWWhirlpoolPosition = 0x16CF8; // JP = ;016F94
class OverworldExit : public GameEntity {
public:
uint16_t y_scroll_;
uint16_t x_scroll_;
uint8_t y_player_;
uint8_t x_player_;
uint8_t y_camera_;
uint8_t x_camera_;
uint8_t scroll_mod_y_;
uint8_t scroll_mod_x_;
uint16_t door_type_1_;
uint16_t door_type_2_;
uint16_t room_id_;
uint16_t map_pos_; // Position in the vram
uint8_t entrance_id_;
uint8_t area_x_;
uint8_t area_y_;
bool is_hole_ = false;
bool deleted_ = false;
bool is_automatic_ = false;
bool large_map_ = false;
OverworldExit() = default;
OverworldExit(uint16_t room_id, uint8_t map_id, uint16_t vram_location,
uint16_t y_scroll, uint16_t x_scroll, uint16_t player_y,
uint16_t player_x, uint16_t camera_y, uint16_t camera_x,
uint8_t scroll_mod_y, uint8_t scroll_mod_x,
uint16_t door_type_1, uint16_t door_type_2,
bool deleted = false)
: map_pos_(vram_location),
entrance_id_(0),
area_x_(0),
area_y_(0),
room_id_(room_id),
y_scroll_(y_scroll),
x_scroll_(x_scroll),
y_player_(player_y),
x_player_(player_x),
y_camera_(camera_y),
x_camera_(camera_x),
scroll_mod_y_(scroll_mod_y),
scroll_mod_x_(scroll_mod_x),
door_type_1_(door_type_1),
door_type_2_(door_type_2),
is_hole_(false),
deleted_(deleted) {
// Initialize entity variables
x_ = player_x;
y_ = player_y;
map_id_ = map_id;
entity_type_ = kExit;
int mapX = (map_id_ - ((map_id_ / 8) * 8));
int mapY = (map_id_ / 8);
area_x_ = (uint8_t)((std::abs(x_ - (mapX * 512)) / 16));
area_y_ = (uint8_t)((std::abs(y_ - (mapY * 512)) / 16));
if (door_type_1 != 0) {
int p = (door_type_1 & 0x7FFF) >> 1;
entrance_id_ = (uint8_t)(p % 64);
area_y_ = (uint8_t)(p >> 6);
}
if (door_type_2 != 0) {
int p = (door_type_2 & 0x7FFF) >> 1;
entrance_id_ = (uint8_t)(p % 64);
area_y_ = (uint8_t)(p >> 6);
}
if (map_id_ >= 64) {
map_id_ -= 64;
}
mapX = (map_id_ - ((map_id_ / 8) * 8));
mapY = (map_id_ / 8);
area_x_ = (uint8_t)((std::abs(x_ - (mapX * 512)) / 16));
area_y_ = (uint8_t)((std::abs(y_ - (mapY * 512)) / 16));
map_pos_ = (uint16_t)((((area_y_) << 6) | (area_x_ & 0x3F)) << 1);
}
// Overworld overworld
void UpdateMapProperties(uint16_t map_id) override {
map_id_ = map_id;
int large = 256;
int mapid = map_id;
if (map_id < 128) {
large = large_map_ ? 768 : 256;
// if (overworld.overworld_map(map_id)->Parent() != map_id) {
// mapid = overworld.overworld_map(map_id)->Parent();
// }
}
int mapX = map_id - ((map_id / 8) * 8);
int mapY = map_id / 8;
area_x_ = (uint8_t)((std::abs(x_ - (mapX * 512)) / 16));
area_y_ = (uint8_t)((std::abs(y_ - (mapY * 512)) / 16));
if (map_id >= 64) {
map_id -= 64;
}
int mapx = (map_id & 7) << 9;
int mapy = (map_id & 56) << 6;
if (is_automatic_) {
x_ = x_ - 120;
y_ = y_ - 80;
if (x_ < mapx) {
x_ = mapx;
}
if (y_ < mapy) {
y_ = mapy;
}
if (x_ > mapx + large) {
x_ = mapx + large;
}
if (y_ > mapy + large + 32) {
y_ = mapy + large + 32;
}
x_camera_ = x_player_ + 0x07;
y_camera_ = y_player_ + 0x1F;
if (x_camera_ < mapx + 127) {
x_camera_ = mapx + 127;
}
if (y_camera_ < mapy + 111) {
y_camera_ = mapy + 111;
}
if (x_camera_ > mapx + 127 + large) {
x_camera_ = mapx + 127 + large;
}
if (y_camera_ > mapy + 143 + large) {
y_camera_ = mapy + 143 + large;
}
}
short vram_x_scroll = (short)(x_ - mapx);
short vram_y_scroll = (short)(y_ - mapy);
map_pos_ = (uint16_t)(((vram_y_scroll & 0xFFF0) << 3) |
((vram_x_scroll & 0xFFF0) >> 3));
}
};
} // namespace zelda3
} // namespace yaze
#endif // YAZE_APP_ZELDA3_OVERWORLD_EXIT_H_

View File

@@ -1,120 +0,0 @@
#ifndef YAZE_APP_ZELDA3_OVERWORLD_ITEM_H_
#define YAZE_APP_ZELDA3_OVERWORLD_ITEM_H_
#include <algorithm>
#include <cstdint>
#include <iomanip>
#include <iostream>
#include <string>
#include <vector>
#include "app/zelda3/common.h"
namespace yaze {
namespace zelda3 {
constexpr int kNumOverworldMapItemPointers = 0x80;
constexpr int kOverworldItemsPointers = 0xDC2F9;
constexpr int kOverworldItemsAddress = 0xDC8B9; // 1BC2F9
constexpr int kOverworldItemsBank = 0xDC8BF;
constexpr int kOverworldItemsEndData = 0xDC89C; // 0DC89E
constexpr int kOverworldBombDoorItemLocationsNew = 0x012644;
constexpr int kOverworldItemsPointersNew = 0x012784;
constexpr int kOverworldItemsStartDataNew = 0x0DC2F9;
class OverworldItem : public GameEntity {
public:
OverworldItem() = default;
OverworldItem(uint8_t id, uint16_t room_map_id, int x, int y, bool bg2)
: bg2_(bg2), id_(id), room_map_id_(room_map_id) {
x_ = x;
y_ = y;
map_id_ = room_map_id;
entity_id_ = id;
entity_type_ = kItem;
int map_x = room_map_id - ((room_map_id / 8) * 8);
int map_y = room_map_id / 8;
game_x_ = static_cast<uint8_t>(std::abs(x - (map_x * 512)) / 16);
game_y_ = static_cast<uint8_t>(std::abs(y - (map_y * 512)) / 16);
}
void UpdateMapProperties(uint16_t room_map_id) override {
room_map_id_ = room_map_id;
if (room_map_id_ >= 64) {
room_map_id_ -= 64;
}
int map_x = room_map_id_ - ((room_map_id_ / 8) * 8);
int map_y = room_map_id_ / 8;
game_x_ = static_cast<uint8_t>(std::abs(x_ - (map_x * 512)) / 16);
game_y_ = static_cast<uint8_t>(std::abs(y_ - (map_y * 512)) / 16);
}
bool bg2_ = false;
uint8_t id_;
uint8_t game_x_;
uint8_t game_y_;
uint16_t room_map_id_;
int unique_id = 0;
bool deleted = false;
};
inline bool CompareOverworldItems(const std::vector<OverworldItem>& items1,
const std::vector<OverworldItem>& items2) {
if (items1.size() != items2.size()) {
return false;
}
const auto is_same_item = [](const OverworldItem& a, const OverworldItem& b) {
return a.x_ == b.x_ && a.y_ == b.y_ && a.id_ == b.id_;
};
return std::all_of(items1.begin(), items1.end(),
[&](const OverworldItem& it) {
return std::any_of(items2.begin(), items2.end(),
[&](const OverworldItem& other) {
return is_same_item(it, other);
});
});
}
const std::vector<std::string> kSecretItemNames = {
"Nothing", // 0
"Green Rupee", // 1
"Rock hoarder", // 2
"Bee", // 3
"Health pack", // 4
"Bomb", // 5
"Heart ", // 6
"Blue Rupee", // 7
"Key", // 8
"Arrow", // 9
"Bomb", // 10
"Heart", // 11
"Magic", // 12
"Full Magic", // 13
"Cucco", // 14
"Green Soldier", // 15
"Bush Stal", // 16
"Blue Soldier", // 17
"Landmine", // 18
"Heart", // 19
"Fairy", // 20
"Heart", // 21
"Nothing ", // 22
"Hole", // 23
"Warp", // 24
"Staircase", // 25
"Bombable", // 26
"Switch" // 27
};
} // namespace zelda3
} // namespace yaze
#endif // YAZE_APP_ZELDA3_OVERWORLD_ITEM_H_

File diff suppressed because it is too large Load Diff

View File

@@ -1,317 +0,0 @@
#ifndef YAZE_APP_ZELDA3_OVERWORLD_MAP_H
#define YAZE_APP_ZELDA3_OVERWORLD_MAP_H
#include <array>
#include <cstddef>
#include <cstdint>
#include <vector>
#include "absl/status/status.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/rom.h"
namespace yaze {
namespace zelda3 {
static constexpr int kTileOffsets[] = {0, 8, 4096, 4104};
// 1 byte, not 0 if enabled
// vanilla, v2, v3
constexpr int OverworldCustomASMHasBeenApplied = 0x140145;
// 2 bytes for each overworld area (0x140)
constexpr int OverworldCustomAreaSpecificBGPalette = 0x140000;
// 1 byte, not 0 if enabled
constexpr int OverworldCustomAreaSpecificBGEnabled = 0x140140;
// Additional v3 constants
constexpr int OverworldCustomSubscreenOverlayArray = 0x140340; // 2 bytes for each overworld area (0x140)
constexpr int OverworldCustomSubscreenOverlayEnabled = 0x140144; // 1 byte, not 0 if enabled
constexpr int OverworldCustomAnimatedGFXArray = 0x1402A0; // 1 byte for each overworld area (0xA0)
constexpr int OverworldCustomAnimatedGFXEnabled = 0x140143; // 1 byte, not 0 if enabled
constexpr int OverworldCustomTileGFXGroupArray = 0x140480; // 8 bytes for each overworld area (0x500)
constexpr int OverworldCustomTileGFXGroupEnabled = 0x140148; // 1 byte, not 0 if enabled
constexpr int OverworldCustomMosaicArray = 0x140200; // 1 byte for each overworld area (0xA0)
constexpr int OverworldCustomMosaicEnabled = 0x140142; // 1 byte, not 0 if enabled
// Vanilla overlay constants
constexpr int kOverlayPointers = 0x77664; // 2 bytes for each overworld area (0x100)
constexpr int kOverlayPointersBank = 0x0E; // Bank for overlay pointers
constexpr int kOverlayData1 = 0x77676; // Check for custom overlay code
constexpr int kOverlayData2 = 0x77677; // Custom overlay data pointer
constexpr int kOverlayCodeStart = 0x77657; // Start of overlay code
// 1 byte for each overworld area (0xA0)
constexpr int OverworldCustomMainPaletteArray = 0x140160;
// 1 byte, not 0 if enabled
constexpr int OverworldCustomMainPaletteEnabled = 0x140141;
// v3 expanded constants
constexpr int kOverworldMessagesExpanded = 0x1417F8;
constexpr int kOverworldMapParentIdExpanded = 0x140998;
constexpr int kOverworldTransitionPositionYExpanded = 0x140F38;
constexpr int kOverworldTransitionPositionXExpanded = 0x141078;
constexpr int kOverworldScreenTileMapChangeByScreen1Expanded = 0x140A38;
constexpr int kOverworldScreenTileMapChangeByScreen2Expanded = 0x140B78;
constexpr int kOverworldScreenTileMapChangeByScreen3Expanded = 0x140CB8;
constexpr int kOverworldScreenTileMapChangeByScreen4Expanded = 0x140DF8;
constexpr int kOverworldSpecialSpriteGFXGroup = 0x016811;
constexpr int kOverworldSpecialGFXGroup = 0x016821;
constexpr int kOverworldSpecialPALGroup = 0x016831;
constexpr int kOverworldSpecialSpritePalette = 0x016841;
constexpr int kOverworldPalettesScreenToSetNew = 0x4C635;
constexpr int kOverworldSpecialSpriteGfxGroupExpandedTemp = 0x0166E1;
constexpr int kOverworldSpecialSpritePaletteExpandedTemp = 0x016701;
constexpr int transition_target_northExpanded = 0x1411B8;
constexpr int transition_target_westExpanded = 0x1412F8;
constexpr int kDarkWorldMapIdStart = 0x40;
constexpr int kSpecialWorldMapIdStart = 0x80;
/**
* @brief Represents tile32 data for the overworld.
*/
using OverworldBlockset = std::vector<std::vector<uint16_t>>;
/**
* @brief Overworld map tile32 data.
*/
typedef struct OverworldMapTiles {
OverworldBlockset light_world; // 64 maps
OverworldBlockset dark_world; // 64 maps
OverworldBlockset special_world; // 32 maps
} OverworldMapTiles;
enum class AreaSizeEnum {
SmallArea = 0,
LargeArea = 1,
WideArea = 2,
TallArea = 3,
};
/**
* @brief Represents a single Overworld map screen.
*/
class OverworldMap : public gfx::GfxContext {
public:
OverworldMap() = default;
OverworldMap(int index, Rom* rom);
absl::Status BuildMap(int count, int game_state, int world,
std::vector<gfx::Tile16>& tiles16,
OverworldBlockset& world_blockset);
void LoadAreaGraphics();
absl::Status LoadPalette();
absl::Status LoadOverlay();
absl::Status LoadVanillaOverlayData();
absl::Status BuildTileset();
absl::Status BuildTiles16Gfx(std::vector<gfx::Tile16>& tiles16, int count);
absl::Status BuildBitmap(OverworldBlockset& world_blockset);
void DrawAnimatedTiles();
auto current_tile16_blockset() const { return current_blockset_; }
auto current_graphics() const { return current_gfx_; }
auto current_palette() const { return current_palette_; }
auto bitmap_data() const { return bitmap_data_; }
auto is_large_map() const { return large_map_; }
auto is_initialized() const { return initialized_; }
auto is_built() const { return built_; }
auto parent() const { return parent_; }
auto mutable_mosaic() { return &mosaic_; }
auto mutable_current_palette() { return &current_palette_; }
void SetNotBuilt() { built_ = false; }
auto area_graphics() const { return area_graphics_; }
auto area_palette() const { return area_palette_; }
auto sprite_graphics(int i) const { return sprite_graphics_[i]; }
auto sprite_palette(int i) const { return sprite_palette_[i]; }
auto message_id() const { return message_id_; }
auto area_music(int i) const { return area_music_[i]; }
auto static_graphics(int i) const { return static_graphics_[i]; }
auto large_index() const { return large_index_; }
auto area_size() const { return area_size_; }
auto main_palette() const { return main_palette_; }
void set_main_palette(uint8_t palette) { main_palette_ = palette; }
auto area_specific_bg_color() const { return area_specific_bg_color_; }
void set_area_specific_bg_color(uint16_t color) {
area_specific_bg_color_ = color;
}
auto subscreen_overlay() const { return subscreen_overlay_; }
void set_subscreen_overlay(uint16_t overlay) { subscreen_overlay_ = overlay; }
auto animated_gfx() const { return animated_gfx_; }
void set_animated_gfx(uint8_t gfx) { animated_gfx_ = gfx; }
auto custom_tileset(int index) const { return custom_gfx_ids_[index]; }
// Overlay accessors (interactive overlays)
auto overlay_id() const { return overlay_id_; }
auto has_overlay() const { return has_overlay_; }
const auto& overlay_data() const { return overlay_data_; }
// Mosaic expanded accessors
const std::array<bool, 4>& mosaic_expanded() const { return mosaic_expanded_; }
void set_mosaic_expanded(int index, bool value) { mosaic_expanded_[index] = value; }
void set_custom_tileset(int index, uint8_t value) { custom_gfx_ids_[index] = value; }
auto mutable_current_graphics() { return &current_gfx_; }
auto mutable_area_graphics() { return &area_graphics_; }
auto mutable_area_palette() { return &area_palette_; }
auto mutable_sprite_graphics(int i) { return &sprite_graphics_[i]; }
auto mutable_sprite_palette(int i) { return &sprite_palette_[i]; }
auto mutable_message_id() { return &message_id_; }
auto mutable_main_palette() { return &main_palette_; }
auto mutable_animated_gfx() { return &animated_gfx_; }
auto mutable_subscreen_overlay() { return &subscreen_overlay_; }
auto mutable_area_music(int i) { return &area_music_[i]; }
auto mutable_static_graphics(int i) { return &static_graphics_[i]; }
auto set_area_graphics(uint8_t value) { area_graphics_ = value; }
auto set_area_palette(uint8_t value) { area_palette_ = value; }
auto set_sprite_graphics(int i, uint8_t value) {
sprite_graphics_[i] = value;
}
auto set_sprite_palette(int i, uint8_t value) { sprite_palette_[i] = value; }
auto set_message_id(uint16_t value) { message_id_ = value; }
uint8_t* mutable_custom_tileset(int index) { return &custom_gfx_ids_[index]; }
void SetAsLargeMap(int parent_index, int quadrant) {
parent_ = parent_index;
large_index_ = quadrant;
large_map_ = true;
area_size_ = AreaSizeEnum::LargeArea;
}
void SetAsSmallMap(int index = -1) {
if (index != -1)
parent_ = index;
else
parent_ = index_;
large_index_ = 0;
large_map_ = false;
area_size_ = AreaSizeEnum::SmallArea;
}
void SetAreaSize(AreaSizeEnum size) {
area_size_ = size;
large_map_ = (size == AreaSizeEnum::LargeArea);
}
void SetParent(int parent_index) {
parent_ = parent_index;
}
void Destroy() {
current_blockset_.clear();
current_gfx_.clear();
bitmap_data_.clear();
map_tiles_.light_world.clear();
map_tiles_.dark_world.clear();
map_tiles_.special_world.clear();
built_ = false;
initialized_ = false;
large_map_ = false;
mosaic_ = false;
index_ = 0;
parent_ = 0;
large_index_ = 0;
world_ = 0;
game_state_ = 0;
main_gfx_id_ = 0;
message_id_ = 0;
area_graphics_ = 0;
area_palette_ = 0;
main_palette_ = 0;
animated_gfx_ = 0;
subscreen_overlay_ = 0;
area_specific_bg_color_ = 0;
custom_gfx_ids_.fill(0);
sprite_graphics_.fill(0);
sprite_palette_.fill(0);
area_music_.fill(0);
static_graphics_.fill(0);
mosaic_expanded_.fill(false);
area_size_ = AreaSizeEnum::SmallArea;
overlay_id_ = 0;
has_overlay_ = false;
overlay_data_.clear();
}
private:
void LoadAreaInfo();
void LoadCustomOverworldData();
void SetupCustomTileset(uint8_t asm_version);
void LoadMainBlocksetId();
void LoadSpritesBlocksets();
void LoadMainBlocksets();
void LoadAreaGraphicsBlocksets();
void LoadDeathMountainGFX();
void ProcessGraphicsBuffer(int index, int static_graphics_offset, int size,
uint8_t* all_gfx);
absl::StatusOr<gfx::SnesPalette> GetPalette(const gfx::PaletteGroup& group,
int index, int previous_index,
int limit);
Rom* rom_;
bool built_ = false;
bool large_map_ = false;
bool initialized_ = false;
bool mosaic_ = false;
int index_ = 0; // Map index
int parent_ = 0; // Parent map index
int large_index_ = 0; // Quadrant ID [0-3]
int world_ = 0; // World ID [0-2]
int game_state_ = 0; // Game state [0-2]
int main_gfx_id_ = 0; // Main Gfx ID
AreaSizeEnum area_size_ = AreaSizeEnum::SmallArea; // Area size for v3
uint16_t message_id_ = 0;
uint8_t area_graphics_ = 0;
uint8_t area_palette_ = 0;
uint8_t main_palette_ = 0; // Custom Overworld Main Palette ID
uint8_t animated_gfx_ = 0; // Custom Overworld Animated ID
uint16_t subscreen_overlay_ = 0; // Custom Overworld Subscreen Overlay ID
uint16_t area_specific_bg_color_ =
0; // Custom Overworld Area-Specific Background Color
std::array<uint8_t, 8> custom_gfx_ids_;
std::array<uint8_t, 3> sprite_graphics_;
std::array<uint8_t, 3> sprite_palette_;
std::array<uint8_t, 4> area_music_;
std::array<uint8_t, 16> static_graphics_;
std::array<bool, 4> mosaic_expanded_;
// Overlay support (interactive overlays that reveal holes/change elements)
uint16_t overlay_id_ = 0;
bool has_overlay_ = false;
std::vector<uint8_t> overlay_data_;
std::vector<uint8_t> current_blockset_;
std::vector<uint8_t> current_gfx_;
std::vector<uint8_t> bitmap_data_;
OverworldMapTiles map_tiles_;
gfx::SnesPalette current_palette_;
};
} // namespace zelda3
} // namespace yaze
#endif

View File

@@ -1,201 +0,0 @@
#include "dungeon_map.h"
#include <fstream>
#include <vector>
#include "util/file_util.h"
#include "app/core/window.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_tile.h"
#include "app/gfx/tilemap.h"
#include "app/gfx/backend/irenderer.h"
#include "app/snes.h"
#include "util/hex.h"
namespace yaze::zelda3 {
absl::StatusOr<std::vector<DungeonMap>> LoadDungeonMaps(
Rom &rom, DungeonMapLabels &dungeon_map_labels) {
std::vector<DungeonMap> dungeon_maps;
std::vector<std::array<uint8_t, kNumRooms>> current_floor_rooms_d;
std::vector<std::array<uint8_t, kNumRooms>> current_floor_gfx_d;
int total_floors_d;
uint8_t nbr_floor_d;
uint8_t nbr_basement_d;
for (int d = 0; d < kNumDungeons; d++) {
current_floor_rooms_d.clear();
current_floor_gfx_d.clear();
ASSIGN_OR_RETURN(int ptr, rom.ReadWord(kDungeonMapRoomsPtr + (d * 2)));
ASSIGN_OR_RETURN(int ptr_gfx, rom.ReadWord(kDungeonMapGfxPtr + (d * 2)));
ptr |= 0x0A0000; // Add bank to the short ptr
ptr_gfx |= 0x0A0000; // Add bank to the short ptr
int pc_ptr = SnesToPc(ptr); // Contains data for the next 25 rooms
int pc_ptr_gfx = SnesToPc(ptr_gfx); // Contains data for the next 25 rooms
ASSIGN_OR_RETURN(uint16_t boss_room_d,
rom.ReadWord(kDungeonMapBossRooms + (d * 2)));
ASSIGN_OR_RETURN(nbr_basement_d, rom.ReadByte(kDungeonMapFloors + (d * 2)));
nbr_basement_d &= 0x0F;
ASSIGN_OR_RETURN(nbr_floor_d, rom.ReadByte(kDungeonMapFloors + (d * 2)));
nbr_floor_d &= 0xF0;
nbr_floor_d = nbr_floor_d >> 4;
total_floors_d = nbr_basement_d + nbr_floor_d;
// for each floor in the dungeon
for (int i = 0; i < total_floors_d; i++) {
dungeon_map_labels[d].emplace_back();
std::array<uint8_t, kNumRooms> rdata;
std::array<uint8_t, kNumRooms> gdata;
// for each room on the floor
for (int j = 0; j < kNumRooms; j++) {
gdata[j] = 0xFF;
rdata[j] = rom.data()[pc_ptr + j + (i * kNumRooms)]; // Set the rooms
gdata[j] = rdata[j] == 0x0F ? 0xFF : rom.data()[pc_ptr_gfx++];
std::string label = util::HexByte(rdata[j]);
dungeon_map_labels[d][i][j] = label;
}
current_floor_gfx_d.push_back(gdata); // Add new floor gfx data
current_floor_rooms_d.push_back(rdata); // Add new floor data
}
dungeon_maps.emplace_back(boss_room_d, nbr_floor_d, nbr_basement_d,
current_floor_rooms_d, current_floor_gfx_d);
}
return dungeon_maps;
}
absl::Status SaveDungeonMaps(Rom &rom, std::vector<DungeonMap> &dungeon_maps) {
for (int d = 0; d < kNumDungeons; d++) {
int ptr = kDungeonMapRoomsPtr + (d * 2);
int ptr_gfx = kDungeonMapGfxPtr + (d * 2);
int pc_ptr = SnesToPc(ptr);
int pc_ptr_gfx = SnesToPc(ptr_gfx);
const int nbr_floors = dungeon_maps[d].nbr_of_floor;
const int nbr_basements = dungeon_maps[d].nbr_of_basement;
for (int i = 0; i < nbr_floors + nbr_basements; i++) {
for (int j = 0; j < kNumRooms; j++) {
RETURN_IF_ERROR(rom.WriteByte(pc_ptr + j + (i * kNumRooms),
dungeon_maps[d].floor_rooms[i][j]));
RETURN_IF_ERROR(rom.WriteByte(pc_ptr_gfx + j + (i * kNumRooms),
dungeon_maps[d].floor_gfx[i][j]));
pc_ptr_gfx++;
}
}
}
return absl::OkStatus();
}
absl::Status LoadDungeonMapTile16(gfx::Tilemap &tile16_blockset, Rom &rom,
const std::vector<uint8_t> &gfx_data,
bool bin_mode) {
tile16_blockset.tile_size = {16, 16};
tile16_blockset.map_size = {186, 186};
tile16_blockset.atlas.Create(256, 192, 8,
std::vector<uint8_t>(256 * 192, 0x00));
for (int i = 0; i < kNumDungeonMapTile16; i++) {
int addr = kDungeonMapTile16;
if (rom.data()[kDungeonMapExpCheck] != 0xB9) {
addr = kDungeonMapTile16Expanded;
}
ASSIGN_OR_RETURN(auto tl, rom.ReadWord(addr + (i * 8)));
gfx::TileInfo t1 = gfx::WordToTileInfo(tl); // Top left
ASSIGN_OR_RETURN(auto tr, rom.ReadWord(addr + 2 + (i * 8)));
gfx::TileInfo t2 = gfx::WordToTileInfo(tr); // Top right
ASSIGN_OR_RETURN(auto bl, rom.ReadWord(addr + 4 + (i * 8)));
gfx::TileInfo t3 = gfx::WordToTileInfo(bl); // Bottom left
ASSIGN_OR_RETURN(auto br, rom.ReadWord(addr + 6 + (i * 8)));
gfx::TileInfo t4 = gfx::WordToTileInfo(br); // Bottom right
int sheet_offset = 212;
if (bin_mode) {
sheet_offset = 0;
}
ComposeTile16(tile16_blockset, gfx_data, t1, t2, t3, t4, sheet_offset);
}
tile16_blockset.atlas.SetPalette(*rom.mutable_dungeon_palette(3));
// TODO: Queue texture for later rendering.
// core::Renderer::Get().RenderBitmap(&tile16_blockset.atlas);
return absl::OkStatus();
}
absl::Status SaveDungeonMapTile16(gfx::Tilemap &tile16_blockset, Rom &rom) {
for (int i = 0; i < kNumDungeonMapTile16; i++) {
int addr = kDungeonMapTile16;
if (rom.data()[kDungeonMapExpCheck] != 0xB9) {
addr = kDungeonMapTile16Expanded;
}
gfx::TileInfo t1 = tile16_blockset.tile_info[i][0];
gfx::TileInfo t2 = tile16_blockset.tile_info[i][1];
gfx::TileInfo t3 = tile16_blockset.tile_info[i][2];
gfx::TileInfo t4 = tile16_blockset.tile_info[i][3];
auto tl = gfx::TileInfoToWord(t1);
RETURN_IF_ERROR(rom.WriteWord(addr + (i * 8), tl));
auto tr = gfx::TileInfoToWord(t2);
RETURN_IF_ERROR(rom.WriteWord(addr + 2 + (i * 8), tr));
auto bl = gfx::TileInfoToWord(t3);
RETURN_IF_ERROR(rom.WriteWord(addr + 4 + (i * 8), bl));
auto br = gfx::TileInfoToWord(t4);
RETURN_IF_ERROR(rom.WriteWord(addr + 6 + (i * 8), br));
}
return absl::OkStatus();
}
absl::Status LoadDungeonMapGfxFromBinary(Rom &rom,
gfx::Tilemap &tile16_blockset,
std::array<gfx::Bitmap, 4> &sheets,
std::vector<uint8_t> &gfx_bin_data) {
std::string bin_file = util::FileDialogWrapper::ShowOpenFileDialog();
if (bin_file.empty()) {
return absl::InternalError("No file selected");
}
std::ifstream file(bin_file, std::ios::binary);
if (!file.is_open()) {
return absl::InternalError("Failed to open file");
}
// Read the gfx data into a buffer
std::vector<uint8_t> bin_data((std::istreambuf_iterator<char>(file)),
std::istreambuf_iterator<char>());
auto converted_bin = gfx::SnesTo8bppSheet(bin_data, 4, 4);
gfx_bin_data = converted_bin;
if (LoadDungeonMapTile16(tile16_blockset, rom, converted_bin, true).ok()) {
std::vector<std::vector<uint8_t>> gfx_sheets;
for (int i = 0; i < 4; i++) {
gfx_sheets.emplace_back(converted_bin.begin() + (i * 0x1000),
converted_bin.begin() + ((i + 1) * 0x1000));
sheets[i] = gfx::Bitmap(128, 32, 8, gfx_sheets[i]);
sheets[i].SetPalette(*rom.mutable_dungeon_palette(3));
// TODO: Queue texture for later rendering.
// core::Renderer::Get().RenderBitmap(&sheets[i]);
}
}
file.close();
return absl::OkStatus();
}
} // namespace yaze::zelda3

View File

@@ -1,114 +0,0 @@
#ifndef YAZE_APP_ZELDA3_SCREEN_DUNGEON_MAP_H
#define YAZE_APP_ZELDA3_SCREEN_DUNGEON_MAP_H
#include <array>
#include <vector>
#include "absl/status/status.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/tilemap.h"
#include "app/rom.h"
namespace yaze::zelda3 {
constexpr int kDungeonMapRoomsPtr = 0x57605; // 14 pointers of map data
constexpr int kDungeonMapFloors = 0x575D9; // 14 words values
constexpr int kDungeonMapGfxPtr = 0x57BE4; // 14 pointers of gfx data
// data start for floors/gfx MUST skip 575D9 to 57621 (pointers)
constexpr int kDungeonMapDataStart = 0x57039;
// IF Byte = 0xB9 dungeon maps are not expanded
constexpr int kDungeonMapExpCheck = 0x56652; // $0A:E652
constexpr int kDungeonMapTile16 = 0x57009; // $0A:F009
constexpr int kDungeonMapTile16Expanded = 0x109010; // $21:9010
// 14 words values 0x000F = no boss
constexpr int kDungeonMapBossRooms = 0x56807;
constexpr int kTriforceVertices = 0x04FFD2; // group of 3, X, Y ,Z
constexpr int kTriforceFaces = 0x04FFE4; // group of 5
constexpr int kCrystalVertices = 0x04FF98;
constexpr int kNumDungeons = 14;
constexpr int kNumRooms = 25;
constexpr int kNumDungeonMapTile16 = 186;
/**
* @brief DungeonMap represents the map menu for a dungeon.
*/
struct DungeonMap {
unsigned short boss_room = 0xFFFF;
unsigned char nbr_of_floor = 0;
unsigned char nbr_of_basement = 0;
std::vector<std::array<uint8_t, kNumRooms>> floor_rooms;
std::vector<std::array<uint8_t, kNumRooms>> floor_gfx;
DungeonMap(unsigned short boss_room, unsigned char nbr_of_floor,
unsigned char nbr_of_basement,
const std::vector<std::array<uint8_t, kNumRooms>> &floor_rooms,
const std::vector<std::array<uint8_t, kNumRooms>> &floor_gfx)
: boss_room(boss_room),
nbr_of_floor(nbr_of_floor),
nbr_of_basement(nbr_of_basement),
floor_rooms(floor_rooms),
floor_gfx(floor_gfx) {}
};
using DungeonMapLabels =
std::array<std::vector<std::array<std::string, kNumRooms>>, kNumDungeons>;
/**
* @brief Load the dungeon maps from the ROM.
*
* @param rom
* @param dungeon_map_labels
* @return absl::StatusOr<std::vector<DungeonMap>>
*/
absl::StatusOr<std::vector<DungeonMap>> LoadDungeonMaps(
Rom &rom, DungeonMapLabels &dungeon_map_labels);
/**
* @brief Save the dungeon maps to the ROM.
*
* @param rom
* @param dungeon_maps
*/
absl::Status SaveDungeonMaps(Rom &rom, std::vector<DungeonMap> &dungeon_maps);
/**
* @brief Load the dungeon map tile16 from the ROM.
*
* @param tile16_blockset
* @param rom
* @param gfx_data
* @param bin_mode
*/
absl::Status LoadDungeonMapTile16(gfx::Tilemap &tile16_blockset, Rom &rom,
const std::vector<uint8_t> &gfx_data,
bool bin_mode);
/**
* @brief Save the dungeon map tile16 to the ROM.
*
* @param tile16_blockset
* @param rom
*/
absl::Status SaveDungeonMapTile16(gfx::Tilemap &tile16_blockset, Rom &rom);
/**
* @brief Load the dungeon map gfx from binary.
*
* @param rom
* @param tile16_blockset
* @param sheets
* @param gfx_bin_data
*/
absl::Status LoadDungeonMapGfxFromBinary(Rom &rom,
gfx::Tilemap &tile16_blockset,
std::array<gfx::Bitmap, 4> &sheets,
std::vector<uint8_t> &gfx_bin_data);
} // namespace yaze::zelda3
#endif // YAZE_APP_ZELDA3_SCREEN_DUNGEON_MAP_H

View File

@@ -1,97 +0,0 @@
#include "inventory.h"
#include "app/gfx/backend/irenderer.h"
#include "app/core/window.h"
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_tile.h"
#include "app/rom.h"
namespace yaze {
namespace zelda3 {
absl::Status Inventory::Create() {
data_.reserve(256 * 256);
for (int i = 0; i < 256 * 256; i++) {
data_.push_back(0xFF);
}
RETURN_IF_ERROR(BuildTileset())
for (int i = 0; i < 0x500; i += 0x08) {
ASSIGN_OR_RETURN(auto t1, rom()->ReadWord(i + kBowItemPos));
ASSIGN_OR_RETURN(auto t2, rom()->ReadWord(i + kBowItemPos + 0x02));
ASSIGN_OR_RETURN(auto t3, rom()->ReadWord(i + kBowItemPos + 0x04));
ASSIGN_OR_RETURN(auto t4, rom()->ReadWord(i + kBowItemPos + 0x06));
tiles_.push_back(gfx::GetTilesInfo(t1));
tiles_.push_back(gfx::GetTilesInfo(t2));
tiles_.push_back(gfx::GetTilesInfo(t3));
tiles_.push_back(gfx::GetTilesInfo(t4));
}
const int offsets[] = {0x00, 0x08, 0x800, 0x808};
auto xx = 0;
auto yy = 0;
int i = 0;
for (const auto& tile : tiles_) {
int offset = offsets[i];
for (auto y = 0; y < 0x08; ++y) {
for (auto x = 0; x < 0x08; ++x) {
int mx = x;
int my = y;
if (tile.horizontal_mirror_ != 0) {
mx = 0x07 - x;
}
if (tile.vertical_mirror_ != 0) {
my = 0x07 - y;
}
int xpos = ((tile.id_ % 0x10) * 0x08);
int ypos = (((tile.id_ / 0x10)) * 0x400);
int source = ypos + xpos + (x + (y * 0x80));
auto destination = xx + yy + offset + (mx + (my * 0x100));
data_[destination] = (test_[source] & 0x0F) + tile.palette_ * 0x08;
}
}
if (i == 4) {
i = 0;
xx += 0x10;
if (xx >= 0x100) {
yy += 0x1000;
xx = 0;
}
} else {
i++;
}
}
bitmap_.Create(256, 256, 8, data_);
bitmap_.SetPalette(palette_);
// TODO: Queue texture for later rendering.
// Renderer::Get().RenderBitmap(&bitmap_);
return absl::OkStatus();
}
absl::Status Inventory::BuildTileset() {
tilesheets_.reserve(6 * 0x2000);
for (int i = 0; i < 6 * 0x2000; i++) tilesheets_.push_back(0xFF);
ASSIGN_OR_RETURN(tilesheets_, Load2BppGraphics(*rom()));
std::vector<uint8_t> test;
for (int i = 0; i < 0x4000; i++) {
test_.push_back(tilesheets_[i]);
}
for (int i = 0x8000; i < +0x8000 + 0x2000; i++) {
test_.push_back(tilesheets_[i]);
}
tilesheets_bmp_.Create(128, 0x130, 64, test_);
auto hud_pal_group = rom()->palette_group().hud;
palette_ = hud_pal_group[0];
tilesheets_bmp_.SetPalette(palette_);
// TODO: Queue texture for later rendering.
// Renderer::Get().RenderBitmap(&tilesheets_bmp_);
return absl::OkStatus();
}
} // namespace zelda3
} // namespace yaze

View File

@@ -1,46 +0,0 @@
#ifndef YAZE_APP_ZELDA3_INVENTORY_H
#define YAZE_APP_ZELDA3_INVENTORY_H
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_palette.h"
#include "app/gfx/snes_tile.h"
#include "app/gui/canvas.h"
#include "app/rom.h"
namespace yaze {
namespace zelda3 {
constexpr int kInventoryStart = 0x6564A;
constexpr int kBowItemPos = 0x6F631;
class Inventory {
public:
absl::Status Create();
auto &bitmap() { return bitmap_; }
auto &tilesheet() { return tilesheets_bmp_; }
auto &palette() { return palette_; }
void LoadRom(Rom *rom) { rom_ = rom; }
auto rom() { return rom_; }
private:
absl::Status BuildTileset();
std::vector<uint8_t> data_;
gfx::Bitmap bitmap_;
std::vector<uint8_t> tilesheets_;
std::vector<uint8_t> test_;
gfx::Bitmap tilesheets_bmp_;
gfx::SnesPalette palette_;
Rom *rom_;
gui::Canvas canvas_;
std::vector<gfx::TileInfo> tiles_;
};
} // namespace zelda3
} // namespace yaze
#endif // YAZE_APP_ZELDA3_INVENTORY_H

View File

@@ -1,124 +0,0 @@
#include "title_screen.h"
#include <cstdint>
#include "app/gfx/bitmap.h"
#include "app/rom.h"
#include "app/snes.h"
namespace yaze {
namespace zelda3 {
void TitleScreen::Create() {
tiles8Bitmap.Create(128, 512, 8, std::vector<uint8_t>(0x20000));
tilesBG1Bitmap.Create(256, 256, 8, std::vector<uint8_t>(0x80000));
tilesBG2Bitmap.Create(256, 256, 8, std::vector<uint8_t>(0x80000));
oamBGBitmap.Create(256, 256, 8, std::vector<uint8_t>(0x80000));
BuildTileset();
LoadTitleScreen();
}
void TitleScreen::BuildTileset() {
uint8_t staticgfx[16] = {0};
// Main Blocksets
// TODO: get the gfx from the GFX class rather than the rom.
// for (int i = 0; i < 8; i++) {
// staticgfx[i] = GfxGroups.mainGfx[titleScreenTilesGFX][i];
// }
staticgfx[8] = 115 + 0;
// staticgfx[9] = (GfxGroups.spriteGfx[titleScreenSpritesGFX][3] + 115);
staticgfx[10] = 115 + 6;
staticgfx[11] = 115 + 7;
// staticgfx[12] = (GfxGroups.spriteGfx[titleScreenSpritesGFX][0] + 115);
staticgfx[13] = 112;
staticgfx[14] = 112;
staticgfx[15] = 112;
// Loaded gfx for the current screen (empty at this point)
uint8_t* currentmapgfx8Data = tiles8Bitmap.mutable_data().data();
// All gfx of the game pack of 2048 bytes (4bpp)
uint8_t* allgfxData = nullptr;
for (int i = 0; i < 16; i++) {
for (int j = 0; j < 2048; j++) {
uint8_t mapByte = allgfxData[j + (staticgfx[i] * 2048)];
switch (i) {
case 0:
case 3:
case 4:
case 5:
mapByte += 0x88;
break;
}
currentmapgfx8Data[(i * 2048) + j] = mapByte; // Upload used gfx data
}
}
}
void TitleScreen::LoadTitleScreen() {
int pos =
(rom_[0x138C + 3] << 16) + (rom_[0x1383 + 3] << 8) + rom_[0x137A + 3];
for (int i = 0; i < 1024; i++) {
tilesBG1Buffer[i] = 492;
tilesBG2Buffer[i] = 492;
}
pos = SnesToPc(pos);
while ((rom_[pos] & 0x80) != 0x80) {
int dest_addr = pos; // $03 and $04
pos += 2;
short length = pos;
bool increment64 = (length & 0x8000) == 0x8000;
bool fixsource = (length & 0x4000) == 0x4000;
pos += 2;
length = (short)((length & 0x07FF));
int j = 0;
int jj = 0;
int posB = pos;
while (j < (length / 2) + 1) {
uint16_t tiledata = (uint16_t)pos;
if (dest_addr >= 0x1000) {
// destAddr -= 0x1000;
if (dest_addr < 0x2000) {
tilesBG1Buffer[dest_addr - 0x1000] = tiledata;
}
} else {
if (dest_addr < 0x1000) {
tilesBG2Buffer[dest_addr] = tiledata;
}
}
if (increment64) {
dest_addr += 32;
} else {
dest_addr++;
}
if (!fixsource) {
pos += 2;
}
jj += 2;
j++;
}
if (fixsource) {
pos += 2;
} else {
pos = posB + jj;
}
}
pal_selected_ = 2;
}
} // namespace zelda3
} // namespace yaze

View File

@@ -1,76 +0,0 @@
#ifndef YAZE_APP_ZELDA3_SCREEN_H
#define YAZE_APP_ZELDA3_SCREEN_H
#include "app/gfx/bitmap.h"
#include "app/gfx/snes_tile.h"
#include "app/rom.h"
namespace yaze {
namespace zelda3 {
class TitleScreen {
public:
void Create();
private:
void BuildTileset();
void LoadTitleScreen();
int sword_x_ = 0;
int mx_click_ = 0;
int my_click_ = 0;
int mx_dist_ = 0;
int my_dist_ = 0;
int last_x_ = 0;
int last_y_ = 0;
int x_in_ = 0;
int y_in_ = 0;
int dungmap_selected_tile_ = 0;
int dungmap_selected_ = 0;
int selected_palette_ = 0;
int total_floors_ = 0;
int current_floor_ = 0;
int num_basement_ = 0;
int num_floor_ = 0;
int selected_map_tile = 0;
int current_floor_rooms; // [1][];
int current_floor_gfx; // [1][];
int copied_data_rooms; // 25
int copied_data_gfx; // 25
int pal_selected_;
int addresses[7] = {0x53de4, 0x53e2c, 0x53e08, 0x53e50,
0x53e74, 0x53e98, 0x53ebc};
int addressesgfx[7] = {0x53ee0, 0x53f04, 0x53ef2, 0x53f16,
0x53f28, 0x53f3a, 0x53f4c};
uint16_t bossRoom = 0x000F;
uint16_t selected_tile = 0;
uint16_t tilesBG1Buffer[0x1000]; // 0x1000
uint16_t tilesBG2Buffer[0x1000]; // 0x1000
uint8_t mapdata; // 64 * 64
uint8_t dwmapdata; // 64 * 64
bool mDown = false;
bool swordSelected = false;
bool darkWorld = false;
bool currentDungeonChanged = false;
bool editedFromEditor = false;
bool mouseDown = false;
bool mdown = false;
Rom rom_;
gfx::OamTile oam_data[10];
gfx::OamTile selected_oam_tile;
gfx::OamTile last_selected_oam_tile;
gfx::Bitmap tilesBG1Bitmap; // 0x80000
gfx::Bitmap tilesBG2Bitmap; // 0x80000
gfx::Bitmap oamBGBitmap; // 0x80000
gfx::Bitmap tiles8Bitmap; // 0x20000
};
} // namespace zelda3
} // namespace yaze
#endif // YAZE_APP_ZELDA3_SCREEN_H

View File

@@ -1,41 +0,0 @@
#ifndef YAZE_APP_ZELDA3_SPRITE_OVERLORD_H
#define YAZE_APP_ZELDA3_SPRITE_OVERLORD_H
#include <string>
namespace yaze {
namespace zelda3 {
static const std::string kOverlordNames[] = {
"Overlord_SpritePositionTarget",
"Overlord_AllDirectionMetalBallFactory",
"Overlord_CascadeMetalBallFactory",
"Overlord_StalfosFactory",
"Overlord_StalfosTrap",
"Overlord_SnakeTrap",
"Overlord_MovingFloor",
"Overlord_ZolFactory",
"Overlord_WallMasterFactory",
"Overlord_CrumbleTilePath 1",
"Overlord_CrumbleTilePath 2",
"Overlord_CrumbleTilePath 3",
"Overlord_CrumbleTilePath 4",
"Overlord_CrumbleTilePath 5",
"Overlord_CrumbleTilePath 6",
"Overlord_PirogusuFactory 1",
"Overlord_PirogusuFactory 2",
"Overlord_PirogusuFactory 3",
"Overlord_PirogusuFactory 4",
"Overlord_FlyingTileFactory",
"Overlord_WizzrobeFactory",
"Overlord_ZoroFactory",
"Overlord_StalfosTrapTriggerWindow",
"Overlord_RedStalfosTrap",
"Overlord_ArmosCoordinator",
"Overlord_BombTrap",
};
}
} // namespace yaze
#endif // YAZE_APP_ZELDA3_SPRITE_OVERLORD_H

View File

@@ -1,932 +0,0 @@
#include "sprite.h"
#include <cstdint>
#include <iostream>
namespace yaze {
namespace zelda3 {
void Sprite::UpdateMapProperties(uint16_t map_id) {
map_x_ = x_;
map_y_ = y_;
name_ = kSpriteDefaultNames[id_];
}
void Sprite::UpdateCoordinates(int map_x, int map_y) {
map_x_ = map_x;
map_y_ = map_y;
}
void Sprite::Draw() {
uint8_t x = nx_;
uint8_t y = ny_;
if (overlord_ == 0x07) {
if (id_ == 0x1A) {
DrawSpriteTile((x * 16), (y * 16), 14, 6, 11); // bomb
} else if (id_ == 0x05) {
DrawSpriteTile((x * 16), (y * 16) - 12, 12, 16, 12, false, true);
DrawSpriteTile((x * 16), (y * 16), 0, 16, 12, false, true);
} else if (id_ == 0x06) {
DrawSpriteTile((x * 16), (y * 16), 10, 26, 14, true, true, 2,
2); // snek
} else if (id_ == 0x09) {
DrawSpriteTile((x * 16), (y * 16), 6, 26, 14);
DrawSpriteTile((x * 16) + 8, (y * 16) + 8, 8, 26, 14);
DrawSpriteTile((x * 16), (y * 16) + 16, 10, 27, 14, false, false, 1, 1);
} else if (id_ == 0x14) {
DrawSpriteTile((x * 16), (y * 16) + 8, 12, 06, 12, false, false, 2,
1); // shadow tile
DrawSpriteTile((x * 16), (y * 16) - 8, 3, 29, 8, false, false, 1,
1); // tile
DrawSpriteTile((x * 16) + 8, (y * 16) - 8, 3, 29, 8, true, false, 1,
1); // tile
DrawSpriteTile((x * 16), (y * 16), 3, 29, 8, false, true, 1,
1); // tile
DrawSpriteTile((x * 16) + 8, (y * 16), 3, 29, 8, true, true, 1,
1); // tile
} else {
DrawSpriteTile((x * 16), (y * 16), 4, 4, 5);
}
if (nx_ != x || ny_ != y) {
bounding_box_.x = (lower_x_ + (nx_ * 16));
bounding_box_.y = (lower_y_ + (ny_ * 16));
bounding_box_.w = width_;
bounding_box_.h = height_;
} else {
bounding_box_.x = (lower_x_ + (x * 16));
bounding_box_.y = (lower_y_ + (y * 16));
bounding_box_.w = width_;
bounding_box_.h = height_;
}
return;
}
if (id_ == 0x00) {
DrawSpriteTile((x * 16), (y * 16), 4, 28, 10);
} else if (id_ == 0x01) {
DrawSpriteTile((x * 16) - 8, (y * 16), 6, 24, 12, false, false, 2, 2);
DrawSpriteTile((x * 16) + 8, (y * 16), 6, 24, 12, true, false, 2, 2);
} else if (id_ == 0x02) {
DrawSpriteTile((x * 16), (y * 16), 0, 16, 10);
} else if (id_ == 0x04) {
uint8_t p = 3;
DrawSpriteTile((x * 16), (y * 16), 14, 28, p);
DrawSpriteTile((x * 16), (y * 16), 14, 30, p);
} else if (id_ == 0x05) {
uint8_t p = 3;
DrawSpriteTile((x * 16), (y * 16), 14, 28, p);
DrawSpriteTile((x * 16), (y * 16), 14, 30, p);
} else if (id_ == 0x06) {
uint8_t p = 3;
DrawSpriteTile((x * 16), (y * 16), 14, 28, p);
DrawSpriteTile((x * 16), (y * 16), 14, 30, p);
} else if (id_ == 0x07) {
uint8_t p = 3;
DrawSpriteTile((x * 16), (y * 16), 14, 28, p);
DrawSpriteTile((x * 16), (y * 16), 14, 30, p);
} else if (id_ == 0x08) {
DrawSpriteTile((x * 16), (y * 16), 0, 24, 6);
DrawSpriteTile((x * 16) + 4, (y * 16) + 6, 0, 24, 6, false, false, 1, 1);
} else if (id_ == 0x09) {
DrawSpriteTile((x * 16) - 22, (y * 16) - 24, 12, 24, 12, false, false, 2,
2); // Moldorm tail
DrawSpriteTile((x * 16) - 16, (y * 16) - 20, 8, 24, 12, false, false, 2,
2); // Moldorm b2
DrawSpriteTile((x * 16) - 12, (y * 16) - 16, 4, 24, 12, false, false, 4,
4); // Moldorm b
DrawSpriteTile((x * 16), (y * 16), 0, 24, 12, false, false, 4,
4); // Moldorm head
DrawSpriteTile((x * 16) + 20, (y * 16) + 12, 8, 26, 14, false, false, 2,
2); // Moldorm eye
DrawSpriteTile((x * 16) + 12, (y * 16) + 20, 8, 26, 14, false, false, 2,
2); // Moldorm eye
} else if (id_ == 0x0A) {
DrawSpriteTile((x * 16), (y * 16), 0, 24, 8);
DrawSpriteTile((x * 16) + 4, (y * 16) + 6, 0, 24, 8, false, false, 1, 1);
} else if (id_ == 0x0B) {
DrawSpriteTile((x * 16), (y * 16), 10, 30, 10);
} else if (id_ == 0x0C) {
DrawSpriteTile((x * 16), (y * 16), 0, 24, 8);
DrawSpriteTile((x * 16) + 4, (y * 16) + 6, 0, 24, 8, false, false, 1, 1);
} else if (id_ == 0x0D) {
DrawSpriteTile((x * 16), (y * 16), 14, 28, 12);
} else if (id_ == 0x0E) {
DrawSpriteTile((x * 16), (y * 16), 8, 18, 10, false, false, 3, 2);
} else if (id_ == 0x0F) {
DrawSpriteTile((x * 16), (y * 16), 14, 24, 8, false, false, 2, 3);
DrawSpriteTile((x * 16) + 16, (y * 16), 30, 8, 8, true, false, 1, 3);
} else if (id_ == 0x10) {
DrawSpriteTile((x * 16), (y * 16), 12, 31, 8, false, false, 1, 1);
} else if (id_ == 0x11) {
DrawSpriteTile((x * 16), (y * 16) + 16, 6, 16, 8, false, false, 2,
2); // Feet
DrawSpriteTile((x * 16) - 8, (y * 16) + 8, 4, 18, 8, false, false, 2,
2); // Body1
DrawSpriteTile((x * 16) + 8, (y * 16) + 8, 4, 18, 8, true, false, 2,
2); // Body2
DrawSpriteTile((x * 16), (y * 16), 0, 16, 8, false, false, 2,
2); // Head
} else if (id_ == 0x12) {
DrawSpriteTile((x * 16), (y * 16) + 8, 8, 26, 8);
DrawSpriteTile((x * 16), (y * 16), 6, 24, 8);
} else if (id_ == 0x13) {
DrawSpriteTile((x * 16), (y * 16), 4, 22, 2);
} else if (id_ == 0x15) {
// Antifairy
DrawSpriteTile((x * 16) + 2, (y * 16) + 8, 3, 30, 5, false, false, 1, 1);
DrawSpriteTile((x * 16) + 8, (y * 16) + 2, 3, 30, 5, false, false, 1, 1);
DrawSpriteTile((x * 16) + 14, (y * 16) + 8, 3, 30, 5, false, false, 1, 1);
DrawSpriteTile((x * 16) + 8, (y * 16) + 14, 3, 30, 5, false, false, 1, 1);
DrawSpriteTile((x * 16) + 8, (y * 16) + 8, 1, 30, 5, false, false, 1,
1); // Middle
} else if (id_ == 0x16) {
DrawSpriteTile((x * 16), (y * 16) + 8, 2, 26, 2);
DrawSpriteTile((x * 16), (y * 16), 0, 26, 2);
} else if (id_ == 0x17) // Bush hoarder
{
DrawSpriteTile((x * 16), (y * 16), 8, 30, 10);
} else if (id_ == 0x18) // Mini moldorm
{
DrawSpriteTile((x * 16) + 13, (y * 16) + 17, 13, 21, 8, false, false, 1,
1); // Tail
DrawSpriteTile((x * 16) + 5, (y * 16) + 8, 2, 22, 8); // Body
DrawSpriteTile((x * 16), (y * 16), 0, 22, 8); // Head
DrawSpriteTile((x * 16), (y * 16) - 4, 13, 20, 8, false, false, 1,
1); // Eyes
DrawSpriteTile((x * 16) - 4, (y * 16), 13, 20, 8, false, false, 1,
1); // Eyes
} else if (id_ == 0x19) // Poe - ghost
{
DrawSpriteTile((x * 16), (y * 16), 6, 31, 2); //
} else if (id_ == 0x1A) // Smith
{
// DrawSpriteTile((x*16), (y *16), 2, 4, 10,true); // Smitty
// DrawSpriteTile((x*16)+12, (y *16) - 7, 0, 6, 10); // Hammer
DrawSpriteTile((x * 16), (y * 16), 4, 22, 10);
} else if (id_ == 0x1C) // Statue
{
DrawSpriteTile((x * 16), (y * 16) + 8, 0, 28, 15);
DrawSpriteTile((x * 16), (y * 16), 2, 28, 15, false, false, 1, 1);
DrawSpriteTile((x * 16) + 8, (y * 16), 2, 28, 15, true, false, 1, 1);
} else if (id_ == 0x1E) // Crystal switch
{
DrawSpriteTile((x * 16), (y * 16), 4, 30, 5);
} else if (id_ == 0x1F) // Sick kid
{
DrawSpriteTile((x * 16) - 8, (y * 16) + 8, 10, 16, 14);
DrawSpriteTile((x * 16) + 16 - 8, (y * 16) + 8, 10, 16, 14, true);
DrawSpriteTile((x * 16) - 8, (y * 16) + 16, 10, 16, 14, false, true, 2, 2);
DrawSpriteTile((x * 16) + 16 - 8, (y * 16) + 16, 10, 16, 14, true, true, 2,
2);
DrawSpriteTile((x * 16), (y * 16) - 4, 14, 16, 10);
} else if (id_ == 0x20) {
DrawSpriteTile((x * 16), (y * 16), 2, 24, 7);
} else if (id_ == 0x21) // Push switch
{
DrawSpriteTile((x * 16) + 4, (y * 16) + 20, 13, 29, 3, false, false, 1, 1);
DrawSpriteTile((x * 16) + 4, (y * 16) + 28, 12, 29, 3, false, false, 1, 1);
DrawSpriteTile((x * 16), (y * 16) + 8, 10, 28, 3);
} else if (id_ == 0x22) // Rope
{
DrawSpriteTile((x * 16), (y * 16), 8, 26, 5);
} else if (id_ == 0x23) // Red bari
{
DrawSpriteTile((x * 16), (y * 16), 2, 18, 4, false, false, 1, 2);
DrawSpriteTile((x * 16) + 8, (y * 16), 2, 18, 4, true, false, 1, 2);
} else if (id_ == 0x24) // Blue bari
{
DrawSpriteTile((x * 16), (y * 16), 2, 18, 6, false, false, 1, 2);
DrawSpriteTile((x * 16) + 8, (y * 16), 2, 18, 6, true, false, 1, 2);
} else if (id_ == 0x25) // Talking tree?
{
// TODO: Add something here?
} else if (id_ == 0x26) // Hardhat beetle
{
if ((x & 0x01) == 0x00) {
DrawSpriteTile((x * 16), (y * 16), 4, 20, 8);
DrawSpriteTile((x * 16), (y * 16) - 6, 0, 20, 8);
} else {
DrawSpriteTile((x * 16), (y * 16), 4, 20, 10);
DrawSpriteTile((x * 16), (y * 16) - 6, 0, 20, 10);
}
} else if (id_ == 0x27) // Deadrock
{
DrawSpriteTile((x * 16), (y * 16), 2, 30, 10);
} else if (id_ == 0x28) // Npcs
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
} else if (id_ == 0x29) // Npcs
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
} else if (id_ == 0x2A) // Npcs
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
} else if (id_ == 0x2B) // ???
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
} else if (id_ == 0x2C) // Lumberjack
{
DrawSpriteTile((x * 16) - 24, (y * 16) + 12, 6, 26, 12, true); // Body
DrawSpriteTile((x * 16) - 24, (y * 16), 8, 26, 12, true); // Head
DrawSpriteTile((x * 16) - 14, (y * 16) + 12, 14, 27, 10, false, false, 1,
1); // Saw left edge
DrawSpriteTile((x * 16) - 6, (y * 16) + 12, 15, 27, 10, false, false, 1,
1); // Saw left edge
DrawSpriteTile((x * 16) + 2, (y * 16) + 12, 15, 27, 10, false, false, 1,
1); // Saw left edge
DrawSpriteTile((x * 16) + 10, (y * 16) + 12, 15, 27, 10, false, false, 1,
1); // Saw left edge
DrawSpriteTile((x * 16) + 18, (y * 16) + 12, 15, 27, 10, false, false, 1,
1); // Saw left edge
DrawSpriteTile((x * 16) + 26, (y * 16) + 12, 15, 27, 10, false, false, 1,
1); // Saw left edge
DrawSpriteTile((x * 16) + 34, (y * 16) + 12, 14, 27, 10, true, false, 1,
1); // Saw left edge
DrawSpriteTile((x * 16) + 40, (y * 16) + 12, 4, 26, 12); // Body
DrawSpriteTile((x * 16) + 40, (y * 16), 8, 26, 12); // Head
} else if (id_ == 0x2D) // Npcs
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
} else if (id_ == 0x2E) // Npcs
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
} else if (id_ == 0x2F) // Npcs
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
} else if (id_ == 0x30) // Npcs
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
} else if (id_ == 0x31) // Npcs
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
} else if (id_ == 0x32) // Npcs
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
}
/*
else if (id_== 0x33) // Pull for rupees
{
}
*/
else if (id_ == 0x34) // Npcs
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
} else if (id_ == 0x35) // Npcs
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
} else if (id_ == 0x36) // Npcs
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
}
/*
else if (id_== 0x37) // Waterfall
{
DrawSpriteTile((x*16), (y *16), 14, 6, 10);
}
*/
else if (id_ == 0x38) // Arrowtarget
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
} else if (id_ == 0x39) // Npcs
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
} else if (id_ == 0x3A) // Npcs
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
} else if (id_ == 0x3B) // Dash item
{
} else if (id_ == 0x3C) // Npcs
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
} else if (id_ == 0x3D) // Npcs
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
} else if (id_ == 0x3E) // Npcs
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
} else if (id_ == 0x3F) // Npcs
{
DrawSpriteTile((x * 16), (y * 16), 14, 22, 10);
} else if (id_ == 0x40) // Lightning lock (agah tower)
{
DrawSpriteTile((x * 16) - 24, (y * 16), 10, 28, 2, false, false, 1, 2);
DrawSpriteTile((x * 16) - 16, (y * 16), 6, 30, 2);
DrawSpriteTile((x * 16), (y * 16), 8, 30, 2);
DrawSpriteTile((x * 16) + 16, (y * 16), 6, 30, 2);
DrawSpriteTile((x * 16) + 24, (y * 16), 10, 28, 2, false, false, 1, 2);
} else if (id_ == 0x41) // Blue soldier
{
DrawSpriteTile((x * 16) - 4, (y * 16) + 8, 6, 20, 10);
DrawSpriteTile((x * 16) + 12, (y * 16) + 8, 6, 20, 10, true, false, 1, 2);
DrawSpriteTile((x * 16), (y * 16), 0, 20, 10);
DrawSpriteTile((x * 16) + 12, (y * 16) + 8, 13, 22, 10, false, false, 1,
2); // Shield
DrawSpriteTile((x * 16) - 4, (y * 16) + 16, 14, 22, 10, false, true, 1,
2); // Sword
} else if (id_ == 0x42) // Green soldier
{
DrawSpriteTile((x * 16) - 4, (y * 16) + 8, 6, 20, 12);
DrawSpriteTile((x * 16) + 12, (y * 16) + 8, 6, 20, 12, true, false, 1, 2);
DrawSpriteTile((x * 16), (y * 16), 0, 20, 12);
DrawSpriteTile((x * 16) + 12, (y * 16) + 8, 13, 22, 12, false, false, 1,
2); // Shield
DrawSpriteTile((x * 16) - 4, (y * 16) + 16, 14, 22, 12, false, true, 1,
2); // Sword
} else if (id_ == 0x43) // Red spear soldier
{
DrawSpriteTile((x * 16) - 4, (y * 16) + 8, 6, 20, 8);
DrawSpriteTile((x * 16) + 12, (y * 16) + 8, 6, 20, 8, true, false, 1, 2);
DrawSpriteTile((x * 16), (y * 16), 0, 20, 8);
DrawSpriteTile((x * 16) + 12, (y * 16) + 8, 13, 22, 8, false, false, 1,
2); // Shield
DrawSpriteTile((x * 16) - 4, (y * 16) + 16, 11, 22, 8, false, true, 1,
2); // Spear
} else if (id_ == 0x44) // Sword blue holding up
{
DrawSpriteTile((x * 16) + 4, (y * 16) + 8, 6, 16, 10);
DrawSpriteTile((x * 16) - 4, (y * 16) + 8, 6, 20, 10, false, false, 1, 2);
DrawSpriteTile((x * 16), (y * 16), 0, 16, 10); // Head
DrawSpriteTile((x * 16) + 12, (y * 16) + 8, 14, 22, 10, false, true, 1,
2); // Sword
} else if (id_ == 0x45) // Green spear soldier
{
DrawSpriteTile((x * 16) - 4, (y * 16) + 8, 6, 20, 12);
DrawSpriteTile((x * 16) + 12, (y * 16) + 8, 6, 20, 12, true, false, 1, 2);
DrawSpriteTile((x * 16), (y * 16), 0, 20, 12);
DrawSpriteTile((x * 16) + 12, (y * 16) + 8, 13, 22, 12, false, false, 1,
2); // Shield
DrawSpriteTile((x * 16) - 4, (y * 16) + 16, 11, 22, 12, false, true, 1,
2); // Spear
} else if (id_ == 0x46) // Blue archer
{
DrawSpriteTile((x * 16) - 4, (y * 16) + 8, 6, 20, 10);
DrawSpriteTile((x * 16) + 12, (y * 16) + 8, 6, 20, 10, true, false, 1, 2);
DrawSpriteTile((x * 16), (y * 16), 0, 20, 10); // Head
DrawSpriteTile((x * 16), (y * 16) + 16, 10, 16, 10, false, false, 1,
1); // Bow1
DrawSpriteTile((x * 16) + 8, (y * 16) + 16, 10, 16, 10, true, false, 1,
1); // Bow2
} else if (id_ == 0x47) // Green archer
{
DrawSpriteTile((x * 16), (y * 16) + 8, 14, 16, 12);
DrawSpriteTile((x * 16), (y * 16), 0, 20, 12);
DrawSpriteTile((x * 16), (y * 16) + 16, 10, 16, 12, false, false, 1,
1); // Bow1
DrawSpriteTile((x * 16) + 8, (y * 16) + 16, 10, 16, 12, true, false, 1,
1); // Bow2
} else if (id_ == 0x48) // Javelin soldier red
{
DrawSpriteTile((x * 16) + 4, (y * 16) + 8, 6, 16, 8);
DrawSpriteTile((x * 16) - 4, (y * 16) + 8, 6, 20, 8, false, false, 1, 2);
DrawSpriteTile((x * 16), (y * 16), 0, 16, 8); // Head
DrawSpriteTile((x * 16) + 12, (y * 16) + 8, 11, 22, 8, false, true, 1,
2); // Sword
} else if (id_ == 0x49) // Javelin soldier red from bush
{
DrawSpriteTile((x * 16) + 4, (y * 16) + 8, 6, 16, 8);
DrawSpriteTile((x * 16) - 4, (y * 16) + 8, 6, 20, 8, false, false, 1, 2);
DrawSpriteTile((x * 16), (y * 16), 0, 18, 8); // Head
DrawSpriteTile((x * 16), (y * 16) + 24, 0, 20, 2);
DrawSpriteTile((x * 16) + 12, (y * 16) + 8, 11, 22, 8, false, true, 1,
2); // Sword
} else if (id_ == 0x4A) // Red bomb soldier
{
DrawSpriteTile((x * 16) + 4, (y * 16) + 8, 6, 16, 8);
DrawSpriteTile((x * 16) - 4, (y * 16) + 8, 6, 20, 8, false, false, 1, 2);
DrawSpriteTile((x * 16), (y * 16), 0, 16, 8); // Head
DrawSpriteTile((x * 16) + 8, (y * 16) - 8, 14, 22, 11); // Bomb
} else if (id_ == 0x4B) // Green soldier recruit
{
// 0,4
DrawSpriteTile((x * 16), (y * 16), 6, 24, 12);
DrawSpriteTile((x * 16), (y * 16) - 10, 0, 20, 12);
} else if (id_ == 0x4C) // Jazzhand
{
DrawSpriteTile((x * 16), (y * 16), 0, 26, 14, false, false, 6, 2);
} else if (id_ == 0x4D) // Rabit??
{
DrawSpriteTile((x * 16), (y * 16), 0, 26, 12, false, false, 6, 2);
} else if (id_ == 0x4E) // Popo1
{
DrawSpriteTile((x * 16), (y * 16), 0, 20, 10);
} else if (id_ == 0x4F) // Popo2
{
DrawSpriteTile((x * 16), (y * 16), 2, 20, 10);
} else if (id_ == 0x50) // Canon ball
{
DrawSpriteTile((x * 16), (y * 16), 0, 24, 10);
} else if (id_ == 0x51) // Armos
{
DrawSpriteTile((x * 16), (y * 16), 0, 28, 11, false, false, 2, 4);
} else if (id_ == 0x53) // Armos Knight
{
DrawSpriteTile((x * 16), (y * 16), 0, 28, 10, false, false, 4, 4);
} else if (id_ == 0x54) {
DrawSpriteTile((x * 16), (y * 16), 2, 28, 12);
DrawSpriteTile((x * 16) + 8, (y * 16) + 10, 6, 28, 12);
DrawSpriteTile((x * 16) + 16, (y * 16) + 18, 10, 28, 12);
} else if (id_ == 0x55) // Fireball Zora
{
DrawSpriteTile((x * 16), (y * 16), 4, 26, 11);
} else if (id_ == 0x56) // Zora
{
DrawSpriteTile((x * 16), (y * 16), 10, 20, 2);
DrawSpriteTile((x * 16), (y * 16) + 8, 8, 30, 2);
} else if (id_ == 0x57) // Desert Rocks
{
DrawSpriteTile((x * 16), (y * 16), 14, 24, 2, false, false, 2, 4);
DrawSpriteTile((x * 16) + 16, (y * 16), 14, 24, 2, true, false, 2, 4);
} else if (id_ == 0x58) // Crab
{
DrawSpriteTile((x * 16), (y * 16), 14, 24, 12);
DrawSpriteTile((x * 16) + 16, (y * 16), 14, 24, 12, true);
} else if (id_ == 0x5B) // Spark
{
DrawSpriteTile((x * 16), (y * 16), 8, 18, 4);
} else if (id_ == 0x5C) // Spark
{
DrawSpriteTile((x * 16), (y * 16), 8, 18, 4, true);
} else if (id_ == 0x5D) // Roller vertical1
{
// Subset3
if (((y * 16) & 0x10) == 0x10) {
DrawSpriteTile((x * 16), (y * 16), 8, 24, 11);
for (int i = 0; i < 7; i++) {
DrawSpriteTile((x * 16) + 8 + (i * 16), (y * 16), 9, 24, 11);
}
DrawSpriteTile((x * 16) + (16 * 7), (y * 16), 8, 24, 11, true);
} else {
DrawSpriteTile((x * 16), (y * 16), 8, 24, 11);
DrawSpriteTile((x * 16) + 16, (y * 16), 9, 24, 11);
DrawSpriteTile((x * 16) + 32, (y * 16), 9, 24, 11);
DrawSpriteTile((x * 16) + 48, (y * 16), 8, 24, 11, true);
}
} else if (id_ == 0x5E) // Roller vertical2
{
// Subset3
if (((y * 16) & 0x10) == 0x10) {
DrawSpriteTile((x * 16), (y * 16), 8, 24, 11);
for (int i = 0; i < 7; i++) {
DrawSpriteTile((x * 16) + 8 + (i * 16), (y * 16), 9, 24, 11);
}
DrawSpriteTile((x * 16) + (16 * 7), (y * 16), 8, 24, 11, true);
} else {
DrawSpriteTile((x * 16), (y * 16), 8, 24, 11);
DrawSpriteTile((x * 16) + 16, (y * 16), 9, 24, 11);
DrawSpriteTile((x * 16) + 32, (y * 16), 9, 24, 11);
DrawSpriteTile((x * 16) + 48, (y * 16), 8, 24, 11, true);
}
} else if (id_ == 0x5F) // Roller horizontal
{
if (((x * 16) & 0x10) == 0x10) {
DrawSpriteTile((x * 16), (y * 16), 14, 24, 11);
DrawSpriteTile((x * 16), (y * 16) + 16, 14, 25, 11);
DrawSpriteTile((x * 16), (y * 16) + 32, 14, 25, 11);
DrawSpriteTile((x * 16), (y * 16) + 48, 14, 24, 11, false, true);
} else {
for (int i = 0; i < 7; i++) {
DrawSpriteTile((x * 16), (y * 16) + i * 16, 14, 25, 11);
}
DrawSpriteTile((x * 16), (y * 16), 14, 24, 11);
DrawSpriteTile((x * 16), (y * 16) + (7 * 16), 14, 24, 11, false, true);
}
} else if (id_ == 0x60) // Roller horizontal2 (right to left)
{
// Subset3
if (((x * 16) & 0x10) == 0x10) {
DrawSpriteTile((x * 16), (y * 16), 14, 24, 11);
DrawSpriteTile((x * 16), (y * 16) + 16, 14, 25, 11);
DrawSpriteTile((x * 16), (y * 16) + 32, 14, 25, 11);
DrawSpriteTile((x * 16), (y * 16) + 48, 14, 24, 11, false, true);
} else {
for (int i = 0; i < 7; i++) {
DrawSpriteTile((x * 16), (y * 16) + i * 16, 14, 25, 11);
}
DrawSpriteTile((x * 16), (y * 16), 14, 24, 11);
DrawSpriteTile((x * 16), (y * 16) + (7 * 16), 14, 24, 11, false, true);
}
} else if (id_ == 0x61) // Beamos
{
DrawSpriteTile((x * 16), (y * 16) - 16, 8, 20, 14, false, false, 2, 4);
DrawSpriteTile((x * 16) + 4, (y * 16) - 8, 10, 20, 14, false, false, 1, 1);
} else if (id_ == 0x63) // Devalant non-shooter
{
DrawSpriteTile((x * 16) - 8, (y * 16) - 8, 2, 16, 2);
DrawSpriteTile((x * 16) + 8, (y * 16) - 8, 2, 16, 2, true);
DrawSpriteTile((x * 16) - 8, (y * 16) + 8, 2, 16, 2, false, true);
DrawSpriteTile((x * 16) + 8, (y * 16) + 8, 2, 16, 2, true, true);
DrawSpriteTile((x * 16), (y * 16), 0, 16, 10);
} else if (id_ == 0x64) // Devalant non-shooter
{
DrawSpriteTile((x * 16) - 8, (y * 16) - 8, 2, 16, 2);
DrawSpriteTile((x * 16) + 8, (y * 16) - 8, 2, 16, 2, true);
DrawSpriteTile((x * 16) - 8, (y * 16) + 8, 2, 16, 2, false, true);
DrawSpriteTile((x * 16) + 8, (y * 16) + 8, 2, 16, 2, true, true);
DrawSpriteTile((x * 16), (y * 16), 0, 16, 8);
} else if (id_ == 0x66) // Moving wall canon right
{
DrawSpriteTile((x * 16), (y * 16), 14, 16, 14, true);
} else if (id_ == 0x67) // Moving wall canon right
{
DrawSpriteTile((x * 16), (y * 16), 14, 16, 14);
} else if (id_ == 0x68) // Moving wall canon right
{
DrawSpriteTile((x * 16), (y * 16), 12, 16, 14);
} else if (id_ == 0x69) // Moving wall canon right
{
DrawSpriteTile((x * 16), (y * 16), 12, 16, 14, false, true);
} else if (id_ == 0x6A) // Chainball soldier
{
DrawSpriteTile((x * 16) + 4, (y * 16) + 8, 6, 16, 14);
DrawSpriteTile((x * 16) - 4, (y * 16) + 8, 6, 20, 14, false, false, 1, 2);
DrawSpriteTile((x * 16), (y * 16), 0, 16, 14); // Head
DrawSpriteTile((x * 16) + 12, (y * 16) - 16, 10, 18, 14); // Ball
} else if (id_ == 0x6B) // Cannon soldier
{
DrawSpriteTile((x * 16) + 4, (y * 16) + 8, 6, 16, 14);
DrawSpriteTile((x * 16) - 4, (y * 16) + 8, 6, 20, 14, false, false, 1, 2);
DrawSpriteTile((x * 16), (y * 16), 0, 16, 14); // Head
DrawSpriteTile((x * 16) + 12, (y * 16) + 8, 4, 18, 14); // Cannon
} else if (id_ == 0x6C) // Mirror portal
{
// Useless
} else if (id_ == 0x6D) // Rat
{
DrawSpriteTile((x * 16), (y * 16), 14, 24, 5);
} else if (id_ == 0x6E) // Rope
{
DrawSpriteTile((x * 16), (y * 16), 10, 26, 5);
} else if (id_ == 0x6F) {
DrawSpriteTile((x * 16), (y * 16), 4, 24, 10);
} else if (id_ == 0x70) // Helma fireball
{
DrawSpriteTile((x * 16), (y * 16), 10, 28, 4);
} else if (id_ == 0x71) // Leever
{
DrawSpriteTile((x * 16), (y * 16), 6, 16, 4);
} else if (id_ == 0x73) // Uncle priest
{
} else if (id_ == 0x79) // Bee
{
DrawSpriteTile((x * 16), (y * 16), 4, 14, 11, false, false, 1, 1);
} else if (id_ == 0x7C) // Skull head
{
DrawSpriteTile((x * 16), (y * 16), 0, 16, 10);
} else if (id_ == 0x7D) // Big spike
{
DrawSpriteTile((x * 16), (y * 16), 4, 28, 11);
DrawSpriteTile((x * 16) + 16, (y * 16), 4, 28, 11, true);
DrawSpriteTile((x * 16), (y * 16) + 16, 4, 28, 11, false, true);
DrawSpriteTile((x * 16) + 16, (y * 16) + 16, 4, 28, 11, true, true);
} else if (id_ == 0x7E) // Guruguru clockwise
{
DrawSpriteTile((x * 16), (y * 16) - 14, 8, 18, 4);
DrawSpriteTile((x * 16), (y * 16) - 28, 8, 18, 4);
DrawSpriteTile((x * 16), (y * 16) - 42, 8, 18, 4);
DrawSpriteTile((x * 16), (y * 16) - 56, 8, 18, 4);
} else if (id_ == 0x7F) // Guruguru Counterclockwise
{
DrawSpriteTile((x * 16), (y * 16) - 14, 8, 18, 4);
DrawSpriteTile((x * 16), (y * 16) - 28, 8, 18, 4);
DrawSpriteTile((x * 16), (y * 16) - 42, 8, 18, 4);
DrawSpriteTile((x * 16), (y * 16) - 56, 8, 18, 4);
} else if (id_ == 0x80) // Winder (moving firebar)
{
DrawSpriteTile((x * 16), (y * 16), 8, 18, 4);
DrawSpriteTile((x * 16) - 14, (y * 16), 8, 18, 4);
DrawSpriteTile((x * 16) - 28, (y * 16), 8, 18, 4);
DrawSpriteTile((x * 16) - 42, (y * 16), 8, 18, 4);
DrawSpriteTile((x * 16) - 56, (y * 16), 8, 18, 4);
} else if (id_ == 0x81) // Water tektite
{
DrawSpriteTile((x * 16), (y * 16), 0, 24, 11);
} else if (id_ == 0x82) // circle antifairy
{
// Antifairy top
DrawSpriteTile((x * 16 + 2) - 4, (y * 16 + 8) - 16, 3, 30, 5, false, false,
1, 1);
DrawSpriteTile((x * 16 + 8) - 4, (y * 16 + 2) - 16, 3, 30, 5, false, false,
1, 1);
DrawSpriteTile((x * 16 + 14) - 4, (y * 16 + 8) - 16, 3, 30, 5, false, false,
1, 1);
DrawSpriteTile((x * 16 + 8) - 4, (y * 16 + 14) - 16, 3, 30, 5, false, false,
1, 1);
DrawSpriteTile((x * 16 + 8) - 4, (y * 16 + 8) - 16, 1, 30, 5, false, false,
1, 1); // Middle
// Left
DrawSpriteTile((x * 16 + 2) - 16, (y * 16 + 8) - 4, 3, 30, 5, false, false,
1, 1);
DrawSpriteTile((x * 16 + 8) - 16, (y * 16 + 2) - 4, 3, 30, 5, false, false,
1, 1);
DrawSpriteTile((x * 16 + 14) - 16, (y * 16 + 8) - 4, 3, 30, 5, false, false,
1, 1);
DrawSpriteTile((x * 16 + 8) - 16, (y * 16 + 14) - 4, 3, 30, 5, false, false,
1, 1);
DrawSpriteTile((x * 16 + 8) - 16, (y * 16 + 8) - 4, 1, 30, 5, false, false,
1, 1); // Middle
DrawSpriteTile((x * 16 + 2) - 4, (y * 16 + 8) + 8, 3, 30, 5, false, false,
1, 1);
DrawSpriteTile((x * 16 + 8) - 4, (y * 16 + 2) + 8, 3, 30, 5, false, false,
1, 1);
DrawSpriteTile((x * 16 + 14) - 4, (y * 16 + 8) + 8, 3, 30, 5, false, false,
1, 1);
DrawSpriteTile((x * 16 + 8) - 4, (y * 16 + 14) + 8, 3, 30, 5, false, false,
1, 1);
DrawSpriteTile((x * 16 + 8) - 4, (y * 16 + 8) + 8, 1, 30, 5, false, false,
1, 1); // Middle
// Left
DrawSpriteTile((x * 16 + 2) + 8, (y * 16 + 8) - 4, 3, 30, 5, false, false,
1, 1);
DrawSpriteTile((x * 16 + 8) + 8, (y * 16 + 2) - 4, 3, 30, 5, false, false,
1, 1);
DrawSpriteTile((x * 16 + 14) + 8, (y * 16 + 8) - 4, 3, 30, 5, false, false,
1, 1);
DrawSpriteTile((x * 16 + 8) + 8, (y * 16 + 14) - 4, 3, 30, 5, false, false,
1, 1);
DrawSpriteTile((x * 16 + 8) + 8, (y * 16 + 8) - 4, 1, 30, 5, false, false,
1, 1); // Middle
} else if (id_ == 0x83) // Green eyegore
{
DrawSpriteTile((x * 16), (y * 16), 12, 24, 14, false, false, 2, 3);
DrawSpriteTile((x * 16) + 16, (y * 16), 12, 24, 14, true, false, 1, 3);
} else if (id_ == 0x84) // Red eyegore
{
DrawSpriteTile((x * 16), (y * 16), 12, 24, 8, false, false, 2, 3);
DrawSpriteTile((x * 16) + 16, (y * 16), 12, 24, 8, true, false, 1, 3);
} else if (id_ == 0x85) // Yellow stalfos
{
DrawSpriteTile((x * 16), (y * 16), 10, 16, 11);
DrawSpriteTile((x * 16), (y * 16) - 12, 0, 16, 11); // Head
} else if (id_ == 0x86) // Kodongo
{
DrawSpriteTile((x * 16), (y * 16), 4, 26, 14);
} else if (id_ == 0x88) // Mothula
{
DrawSpriteTile((x * 16), (y * 16), 8, 24, 14, false, false, 2, 4);
DrawSpriteTile((x * 16) + 16, (y * 16), 8, 24, 14, true, false, 2, 4);
} else if (id_ == 0x8A) // Spike
{
DrawSpriteTile((x * 16), (y * 16), 6, 30, 15);
} else if (id_ == 0x8B) // Gibdo
{
DrawSpriteTile((x * 16), (y * 16), 10, 24, 14);
DrawSpriteTile((x * 16), (y * 16) - 8, 0, 24, 14);
} else if (id_ == 0x8C) // Arrghus
{
DrawSpriteTile((x * 16), (y * 16), 0, 24, 14, false, false, 2, 4);
DrawSpriteTile((x * 16) + 16, (y * 16), 0, 24, 14, true, false, 2, 4);
} else if (id_ == 0x8D) // Arrghus spawn
{
DrawSpriteTile((x * 16), (y * 16), 6, 24, 14);
} else if (id_ == 0x8E) // Terrorpin
{
DrawSpriteTile((x * 16), (y * 16), 14, 24, 12);
}
if (id_ == 0x8F) // Slime
{
DrawSpriteTile((x * 16), (y * 16), 0, 20, 12);
} else if (id_ == 0x90) // Wall master
{
DrawSpriteTile((x * 16), (y * 16), 6, 26, 12);
DrawSpriteTile((x * 16) + 16, (y * 16), 15, 26, 12, false, false, 1, 1);
DrawSpriteTile((x * 16) + 16, (y * 16) + 8, 9, 26, 12, false, false, 1, 2);
DrawSpriteTile((x * 16), (y * 16) + 16, 10, 27, 12, false, false, 1, 1);
DrawSpriteTile((x * 16) + 8, (y * 16) + 16, 8, 27, 12, false, false, 1, 1);
} else if (id_ == 0x91) // Stalfos knight
{
DrawSpriteTile((x * 16) - 2, (y * 16) + 12, 4, 22, 12, false, false, 1, 2);
DrawSpriteTile((x * 16) + 10, (y * 16) + 12, 4, 22, 12, true, false, 1, 2);
DrawSpriteTile((x * 16) - 4, (y * 16) + 4, 1, 22, 12);
DrawSpriteTile((x * 16) + 12, (y * 16) + 4, 3, 22, 12, false, false, 1, 2);
DrawSpriteTile((x * 16), (y * 16) - 8, 6, 20, 12);
} else if (id_ == 0x92) // Helmaking
{
DrawSpriteTile((x * 16), (y * 16) + 32, 14, 26, 14);
DrawSpriteTile((x * 16) + 16, (y * 16) + 32, 0, 28, 14);
DrawSpriteTile((x * 16) + 32, (y * 16) + 32, 14, 26, 14, true);
DrawSpriteTile((x * 16), (y * 16) + 16 + 32, 2, 28, 14);
DrawSpriteTile((x * 16) + 16, (y * 16) + 16 + 32, 4, 28, 14);
DrawSpriteTile((x * 16) + 32, (y * 16) + 16 + 32, 2, 28, 14, true);
DrawSpriteTile((x * 16) + 8, (y * 16) + 32 + 32, 6, 28, 14);
DrawSpriteTile((x * 16) + 24, (y * 16) + 32 + 32, 6, 28, 14, true);
} else if (id_ == 0x93) // Bumper
{
DrawSpriteTile((x * 16), (y * 16), 12, 30, 7);
DrawSpriteTile((x * 16) + 16, (y * 16), 12, 30, 7, true);
DrawSpriteTile((x * 16) + 16, (y * 16) + 16, 12, 30, 7, true, true);
DrawSpriteTile((x * 16), (y * 16) + 16, 12, 30, 7, false, true);
} else if (id_ == 0x95) // Right laser eye
{
DrawSpriteTile((x * 16), (y * 16), 9, 28, 3, true, false, 1, 2);
DrawSpriteTile((x * 16), (y * 16) + 16, 9, 28, 3, true, true, 1, 1);
} else if (id_ == 0x96) // Left laser eye
{
DrawSpriteTile((x * 16) + 8, (y * 16) - 4, 9, 28, 3, false, false, 1, 2);
DrawSpriteTile((x * 16) + 8, (y * 16) + 12, 9, 28, 3, false, true, 1, 1);
} else if (id_ == 0x97) // Right laser eye
{
DrawSpriteTile((x * 16), (y * 16), 6, 28, 3, false, false, 2, 1);
DrawSpriteTile((x * 16) + 16, (y * 16), 6, 28, 3, true, false, 1, 1);
} else if (id_ == 0x98) // Right laser eye
{
DrawSpriteTile((x * 16), (y * 16), 6, 28, 3, false, true, 2, 1);
DrawSpriteTile((x * 16) + 16, (y * 16), 6, 28, 3, true, true, 1, 1);
} else if (id_ == 0x99) {
DrawSpriteTile((x * 16), (y * 16), 6, 24, 12);
DrawSpriteTile((x * 16), (y * 16) - 8, 0, 24, 12);
} else if (id_ == 0x9A) // Water bubble kyameron
{
DrawSpriteTile((x * 16), (y * 16), 10, 24, 6);
} else if (id_ == 0x9B) // Water bubble kyameron
{
DrawSpriteTile((x * 16), (y * 16), 6, 24, 11);
DrawSpriteTile((x * 16), (y * 16) - 8, 2, 27, 11, false, false, 2, 1);
} else if (id_ == 0x9C) // Water bubble kyameron
{
DrawSpriteTile((x * 16), (y * 16), 12, 22, 11);
DrawSpriteTile((x * 16) + 16, (y * 16), 13, 22, 11, false, false, 1, 2);
} else if (id_ == 0x9D) // Water bubble kyameron
{
DrawSpriteTile((x * 16), (y * 16), 14, 21, 11);
DrawSpriteTile((x * 16), (y * 16) - 16, 14, 20, 11, false, false, 2, 1);
} else if (id_ == 0xA1) {
DrawSpriteTile((x * 16) - 8, (y * 16) + 8, 6, 26, 14);
DrawSpriteTile((x * 16) + 8, (y * 16) + 8, 6, 26, 14, true);
} else if (id_ == 0xA2) {
DrawSpriteTile((x * 16), (y * 16) + 8, 0, 24, 14, false, false, 4, 4);
} else if (id_ == 0xA5) {
DrawSpriteTile((x * 16), (y * 16), 0, 26, 10, false, false, 3, 2);
DrawSpriteTile((x * 16) + 4, (y * 16) - 8, 0, 24, 10);
} else if (id_ == 0xA6) {
DrawSpriteTile((x * 16), (y * 16), 0, 26, 8, false, false, 3, 2);
DrawSpriteTile((x * 16) + 4, (y * 16) - 8, 0, 24, 8);
} else if (id_ == 0xA7) {
DrawSpriteTile((x * 16), (y * 16) + 12, 12, 16, 10);
DrawSpriteTile((x * 16), (y * 16), 0, 16, 10);
} else if (id_ == 0xAC) {
DrawSpriteTile((x * 16), (y * 16), 5, 14, 4);
} else if (id_ == 0xAD) {
DrawSpriteTile((x * 16), (y * 16) + 8, 14, 10, 10);
DrawSpriteTile((x * 16), (y * 16), 12, 10, 10);
} else if (id_ == 0xBA) {
DrawSpriteTile((x * 16), (y * 16), 14, 14, 6);
} else if (id_ == 0xC1 || id_ == 0x7A) {
DrawSpriteTile((x * 16), (y * 16) - 16, 2, 24, 12, false, false, 2, 4);
DrawSpriteTile((x * 16) + 16, (y * 16) - 16, 2, 24, 12, true, false, 2, 4);
} else if (id_ == 0xC3) {
DrawSpriteTile((x * 16), (y * 16), 10, 14, 12);
} else if (id_ == 0xC4) {
DrawSpriteTile((x * 16), (y * 16), 0, 18, 14);
DrawSpriteTile((x * 16), (y * 16) - 8, 0, 16, 14);
} else if (id_ == 0xC5) {
} else if (id_ == 0xC6) {
DrawSpriteTile((x * 16) + 4, (y * 16) + 14, 3, 30, 14, false, false, 1, 1);
DrawSpriteTile((x * 16) + 14, (y * 16) + 4, 3, 30, 14, false, false, 1, 1);
DrawSpriteTile((x * 16) + 4, (y * 16) + 2, 1, 31, 14, false, false, 1, 1);
DrawSpriteTile((x * 16) - 6, (y * 16) + 4, 3, 30, 14, false, false, 1, 1);
DrawSpriteTile((x * 16) + 4, (y * 16) - 6, 3, 30, 14, false, false, 1, 1);
} else if (id_ == 0xC7) {
DrawSpriteTile((x * 16), (y * 16), 0, 26, 4);
DrawSpriteTile((x * 16), (y * 16) - 10, 0, 26, 4);
DrawSpriteTile((x * 16), (y * 16) - 20, 0, 26, 4);
DrawSpriteTile((x * 16), (y * 16) - 30, 2, 26, 4);
} else if (id_ == 0xC8) {
DrawSpriteTile((x * 16), (y * 16), 12, 24, 12, false, false, 2, 3);
DrawSpriteTile((x * 16) + 16, (y * 16), 12, 24, 12, true, false, 1, 3);
} else if (id_ == 0xC9) {
DrawSpriteTile((x * 16), (y * 16), 8, 28, 8, false);
DrawSpriteTile((x * 16) + 16, (y * 16), 8, 28, 8, true);
} else if (id_ == 0xCA) {
DrawSpriteTile((x * 16), (y * 16), 8, 10, 10);
} else if (id_ == 0xD0) {
DrawSpriteTile((x * 16), (y * 16), 7, 14, 11, false, false, 3, 2);
DrawSpriteTile((x * 16), (y * 16) - 10, 8, 12, 11);
} else if (id_ == 0xD1) {
DrawSpriteTile((x * 16) + 2, (y * 16) + 8, 7, 13, 11, true, true, 1, 1);
DrawSpriteTile((x * 16) + 8, (y * 16) + 2, 7, 13, 11, true, false, 1, 1);
DrawSpriteTile((x * 16) + 14, (y * 16) + 8, 7, 13, 11, true, true, 1, 1);
DrawSpriteTile((x * 16) + 8, (y * 16) + 14, 7, 13, 11, false, true, 1, 1);
DrawSpriteTile((x * 16) + 8, (y * 16) + 8, 7, 13, 11, false, false, 1,
1); // Middle
// 6,7 / 13
} else if (id_ == 0xD4) {
DrawSpriteTile((x * 16) - 4, (y * 16), 0, 7, 7, false, false, 1, 1);
DrawSpriteTile((x * 16) + 4, (y * 16), 0, 7, 7, true, false, 1, 1);
} else if (id_ == 0xE3) // Fairy
{
DrawSpriteTile((x * 16), (y * 16), 10, 14, 10);
} else if (id_ == 0xE4) // Key
{
DrawSpriteTile((x * 16), (y * 16), 11, 06, 11, false, false, 1, 2);
} else if (id_ == 0xE7) // Mushroom
{
DrawSpriteTile((x * 16), (y * 16), 14, 30, 16);
} else if (id_ == 0xE8) // Fake ms
{
DrawSpriteTile((x * 16) + 4, (y * 16), 4, 31, 10, false, false, 1, 1);
DrawSpriteTile((x * 16) + 4, (y * 16) + 8, 5, 31, 10, false, false, 1, 1);
} else if (id_ == 0xEB) {
DrawSpriteTile((x * 16), (y * 16), 0, 14, 5);
} else if (id_ == 0xF2) {
DrawSpriteTile((x * 16), (y * 16) - 16, 12, 24, 2, false, false, 2, 4);
DrawSpriteTile((x * 16) + 16, (y * 16) - 16, 12, 24, 2, true, false, 2, 4);
} else if (id_ == 0xF4) {
DrawSpriteTile((x * 16), (y * 16), 12, 28, 5, false, false, 4, 4);
} else {
DrawSpriteTile((x * 16), (y * 16), 4, 4, 5);
}
bounding_box_.x = (lower_x_ + (x * 16));
bounding_box_.y = (lower_y_ + (y * 16));
bounding_box_.w = width_;
bounding_box_.h = height_;
}
void Sprite::DrawSpriteTile(int x, int y, int srcx, int srcy, int pal,
bool mirror_x, bool mirror_y, int sizex,
int sizey) {
if (current_gfx_.empty()) {
return;
}
// Validate input parameters
if (sizex <= 0 || sizey <= 0) {
return;
}
if (srcx < 0 || srcy < 0 || pal < 0) {
return;
}
x += 16;
y += 16;
int drawid_ = (srcx + (srcy * 16)) + 512;
// Validate drawid_ is within reasonable bounds
if (drawid_ < 0 || drawid_ > 4096) {
return;
}
for (auto yl = 0; yl < sizey * 8; yl++) {
for (auto xl = 0; xl < (sizex * 8) / 2; xl++) {
int mx = xl;
int my = yl;
if (mirror_x) {
mx = (((sizex * 8) / 2) - 1) - xl;
}
if (mirror_y) {
my = ((sizey * 8) - 1) - yl;
}
// Formula information to get tile index position in the array
//((ID / nbrofXtiles) * (imgwidth/2) + (ID - ((ID/16)*16) ))
int tx = ((drawid_ / 0x10) * 0x400) +
((drawid_ - ((drawid_ / 0x10) * 0x10)) * 8);
// Validate graphics buffer access
int gfx_index = tx + (yl * 0x80) + xl;
if (gfx_index < 0 || gfx_index >= static_cast<int>(current_gfx_.size())) {
continue; // Skip this pixel if out of bounds
}
auto pixel = current_gfx_[gfx_index];
// nx,ny = object position, xx,yy = tile position, xl,yl = pixel
// position
int index = (x) + (y * 64) + (mx + (my * 0x80));
// Validate preview buffer access
if (index >= 0 && index < static_cast<int>(preview_gfx_.size()) &&
index <= 4096) {
preview_gfx_[index] = (uint8_t)((pixel & 0x0F) + 112 + (pal * 8));
}
}
}
}
} // namespace zelda3
} // namespace yaze

View File

@@ -1,392 +0,0 @@
#ifndef YAZE_APP_ZELDA3_SPRITE_H
#define YAZE_APP_ZELDA3_SPRITE_H
#include <SDL.h>
#include <cstdint>
#include <string>
#include <vector>
#include "app/zelda3/common.h"
#include "app/zelda3/sprite/overlord.h"
namespace yaze {
namespace zelda3 {
static const std::string kSpriteDefaultNames[]{
"00 Raven",
"01 Vulture",
"02 Flying Stalfos Head",
"03 No Pointer (Empty",
"04 Pull Switch (good",
"05 Pull Switch (unused",
"06 Pull Switch (bad",
"07 Pull Switch (unused",
"08 Octorock (one way",
"09 Moldorm (Boss",
"0A Octorock (four way",
"0B Chicken",
"0C Octorock (?",
"0D Buzzblock",
"0E Snapdragon",
"0F Octoballoon",
"10 Octoballon Hatchlings",
"11 Hinox",
"12 Moblin",
"13 Mini Helmasaure",
"14 Gargoyle's Domain Gate",
"15 Antifairy",
"16 Sahasrahla / Aginah",
"17 Bush Hoarder",
"18 Mini Moldorm",
"19 Poe",
"1A Dwarves",
"1B Arrow in wall",
"1C Statue",
"1D Weathervane",
"1E Crystal Switch",
"1F Bug-Catching Kid",
"20 Sluggula",
"21 Push Switch",
"22 Ropa",
"23 Red Bari",
"24 Blue Bari",
"25 Talking Tree",
"26 Hardhat Beetle",
"27 Deadrock",
"28 Storytellers",
"29 Blind Hideout attendant",
"2A Sweeping Lady",
"2B Storytellers",
"2C Lumberjacks",
"2D Telepathic Stones",
"2E Multipurpose Sprite",
"2F Race Npc",
"30 Person?",
"31 Fortune Teller",
"32 Angry Brothers",
"33 Pull for items",
"34 Scared Girl",
"35 Innkeeper",
"36 Witch",
"37 Waterfall",
"38 Arrow Target",
"39 Average Middle",
"3A Half Magic Bat",
"3B Dash Item",
"3C Village Kid",
"3D Signs? Chicken lady also showed up / Scared ladies outside houses.",
"3E Rock Hoarder",
"3F Tutorial Soldier",
"40 Lightning Lock",
"41 Blue Sword Soldier / Used by guards to detect player",
"42 Green Sword Soldier",
"43 Red Spear Soldier",
"44 Assault Sword Soldier",
"45 Green Spear Soldier",
"46 Blue Archer",
"47 Green Archer",
"48 Red Javelin Soldier",
"49 Red Javelin Soldier 2",
"4A Red Bomb Soldiers",
"4B Green Soldier Recruits",
"4C Geldman",
"4D Rabbit",
"4E Popo",
"4F Popo 2",
"50 Cannon Balls",
"51 Armos",
"52 Giant Zora",
"53 Armos Knights (Boss",
"54 Lanmolas (Boss",
"55 Fireball Zora",
"56 Walking Zora",
"57 Desert Palace Barriers",
"58 Crab",
"59 Bird",
"5A Squirrel",
"5B Spark (Left to Right",
"5C Spark (Right to Left",
"5D Roller (vertical moving",
"5E Roller (vertical moving",
"5F Roller",
"60 Roller (horizontal moving",
"61 Beamos",
"62 Master Sword",
"63 Devalant (Non",
"64 Devalant (Shooter",
"65 Shooting Gallery Proprietor",
"66 Moving Cannon Ball Shooters (Right",
"67 Moving Cannon Ball Shooters (Left",
"68 Moving Cannon Ball Shooters (Down",
"69 Moving Cannon Ball Shooters (Up",
"6A Ball N' Chain Trooper",
"6B Cannon Soldier",
"6C Mirror Portal",
"6D Rat",
"6E Rope",
"6F Keese",
"70 Helmasaur King Fireball",
"71 Leever",
"72 Activator for the ponds (where you throw in items",
"73 Uncle / Priest",
"74 Running Man",
"75 Bottle Salesman",
"76 Princess Zelda",
"77 Antifairy (Alternate",
"78 Village Elder",
"79 Bee",
"7A Agahnim",
"7B Agahnim Energy Ball",
"7C Hyu",
"7D Big Spike Trap",
"7E Guruguru Bar (Clockwise",
"7F Guruguru Bar (Counter Clockwise",
"80 Winder",
"81 Water Tektite",
"82 Antifairy Circle",
"83 Green Eyegore",
"84 Red Eyegore",
"85 Yellow Stalfos",
"86 Kodongos",
"87 Flames",
"88 Mothula (Boss",
"89 Mothula's Beam",
"8A Spike Trap",
"8B Gibdo",
"8C Arrghus (Boss",
"8D Arrghus spawn",
"8E Terrorpin",
"8F Slime",
"90 Wallmaster",
"91 Stalfos Knight",
"92 Helmasaur King",
"93 Bumper",
"94 Swimmers",
"95 Eye Laser (Right",
"96 Eye Laser (Left",
"97 Eye Laser (Down",
"98 Eye Laser (Up",
"99 Pengator",
"9A Kyameron",
"9B Wizzrobe",
"9C Tadpoles",
"9D Tadpoles",
"9E Ostrich (Haunted Grove",
"9F Flute",
"A0 Birds (Haunted Grove",
"A1 Freezor",
"A2 Kholdstare (Boss",
"A3 Kholdstare's Shell",
"A4 Falling Ice",
"A5 Zazak Fireball",
"A6 Red Zazak",
"A7 Stalfos",
"A8 Bomber Flying Creatures from Darkworld",
"A9 Bomber Flying Creatures from Darkworld",
"AA Pikit",
"AB Maiden",
"AC Apple",
"AD Lost Old Man",
"AE Down Pipe",
"AF Up Pipe",
"B0 Right Pip",
"B1 Left Pipe",
"B2 Good bee again?",
"B3 Hylian Inscription",
"B4 Thief?s chest (not the one that follows you",
"B5 Bomb Salesman",
"B6 Kiki",
"B7 Maiden following you in Blind Dungeon",
"B8 Monologue Testing Sprite",
"B9 Feuding Friends on Death Mountain",
"BA Whirlpool",
"BB Salesman / chestgame guy / 300 rupee giver guy / Chest game thief",
"BC Drunk in the inn",
"BD Vitreous (Large Eyeball",
"BE Vitreous (Small Eyeball",
"BF Vitreous' Lightning",
"C0 Monster in Lake of Ill Omen / Quake Medallion",
"C1 Agahnim teleporting Zelda to dark world",
"C2 Boulders",
"C3 Gibo",
"C4 Thief",
"C5 Medusa",
"C6 Four Way Fireball Spitters (spit when you use your sword",
"C7 Hokku",
"C8 Big Fairy who heals you",
"C9 Tektite",
"CA Chain Chomp",
"CB Trinexx",
"CC Another part of trinexx",
"CD Yet another part of trinexx",
"CE Blind The Thief (Boss)",
"CF Swamola",
"D0 Lynel",
"D1 Bunny Beam",
"D2 Flopping fish",
"D3 Stal",
"D4 Landmine",
"D5 Digging Game Proprietor",
"D6 Ganon",
"D7 Copy of Ganon",
"D8 Heart",
"D9 Green Rupee",
"DA Blue Rupee",
"DB Red Rupee",
"DC Bomb Refill (1)",
"DD Bomb Refill (4)",
"DE Bomb Refill (8)",
"DF Small Magic Refill",
"E0 Full Magic Refill",
"E1 Arrow Refill (5)",
"E2 Arrow Refill (10)",
"E3 Fairy",
"E4 Key",
"E5 Big Key",
"E6 Shield",
"E7 Mushroom",
"E8 Fake Master Sword",
"E9 Magic Shop dude / His items",
"EA Heart Container",
"EB Heart Piece",
"EC Bushes",
"ED Cane Of Somaria Platform",
"EE Mantle",
"EF Cane of Somaria Platform (Unused)",
"F0 Cane of Somaria Platform (Unused)",
"F1 Cane of Somaria Platform (Unused)",
"F2 Medallion Tablet",
"F3",
"F4 Falling Rocks",
"F5",
"F6",
"F7",
"F8",
"F9",
"FA",
"FB",
"FC",
"FD",
"FE",
"FF",
};
/**
* @class Sprite
* @brief A class for managing sprites in the overworld and underworld.
*/
class Sprite : public GameEntity {
public:
Sprite() = default;
Sprite(const std::vector<uint8_t>& src, uint8_t overworld_map_id, uint8_t id,
uint8_t x, uint8_t y, int map_x, int map_y)
: map_id_(static_cast<int>(overworld_map_id)),
id_(id),
nx_(x),
ny_(y),
map_x_(map_x),
map_y_(map_y),
current_gfx_(src) {
entity_type_ = zelda3::GameEntity::EntityType::kSprite;
entity_id_ = id;
x_ = map_x_;
y_ = map_y_;
overworld_ = true;
name_ = kSpriteDefaultNames[id];
preview_gfx_.resize(64 * 64, 0xFF);
}
Sprite(uint8_t id, uint8_t x, uint8_t y, uint8_t subtype, uint8_t layer)
: id_(id), nx_(x), ny_(y), subtype_(subtype), layer_(layer) {
x_ = x;
y_ = y;
name_ = kSpriteDefaultNames[id];
if (((subtype & 0x07) == 0x07) && id > 0 && id <= 0x1A) {
name_ = kOverlordNames[id - 1];
overlord_ = 1;
}
}
void InitSprite(const std::vector<uint8_t>& src, uint8_t overworld_map_id,
uint8_t id, uint8_t x, uint8_t y, int map_x, int map_y) {
current_gfx_ = src;
overworld_ = true;
map_id_ = static_cast<int>(overworld_map_id);
id_ = id;
entity_type_ = zelda3::GameEntity::EntityType::kSprite;
entity_id_ = id;
x_ = map_x_;
y_ = map_y_;
nx_ = x;
ny_ = y;
name_ = kSpriteDefaultNames[id];
map_x_ = map_x;
map_y_ = map_y;
preview_gfx_.resize(64 * 64, 0xFF);
}
void Draw();
void DrawSpriteTile(int x, int y, int srcx, int srcy, int pal,
bool mirror_x = false, bool mirror_y = false,
int sizex = 2, int sizey = 2);
void UpdateMapProperties(uint16_t map_id) override;
void UpdateCoordinates(int map_x, int map_y);
auto preview_graphics() const { return &preview_gfx_; }
auto id() const { return id_; }
auto set_id(uint8_t id) { id_ = id; }
auto x() const { return x_; }
auto y() const { return y_; }
auto nx() const { return nx_; }
auto ny() const { return ny_; }
auto map_id() const { return map_id_; }
auto map_x() const { return map_x_; }
auto map_y() const { return map_y_; }
auto game_state() const { return game_state_; }
auto layer() const { return layer_; }
auto subtype() const { return subtype_; }
auto width() const { return width_; }
auto height() const { return height_; }
auto name() { return name_; }
auto deleted() const { return deleted_; }
auto set_deleted(bool deleted) { deleted_ = deleted; }
auto set_key_drop(int key) { key_drop_ = key; }
private:
uint8_t map_id_;
uint8_t game_state_;
uint8_t id_;
uint8_t nx_;
uint8_t ny_;
uint8_t overlord_ = 0;
uint8_t lower_x_ = 32;
uint8_t lower_y_ = 32;
uint8_t higher_x_ = 0;
uint8_t higher_y_ = 0;
int width_ = 16;
int height_ = 16;
int map_x_ = 0;
int map_y_ = 0;
int layer_ = 0;
int subtype_ = 0;
int key_drop_ = 0;
bool deleted_ = false;
bool overworld_;
std::string name_;
std::vector<uint8_t> preview_gfx_;
std::vector<uint8_t> current_gfx_;
SDL_Rect bounding_box_;
};
} // namespace zelda3
} // namespace yaze
#endif

View File

@@ -1,151 +0,0 @@
#include "sprite_builder.h"
#include <sstream>
#include <string>
namespace yaze {
namespace zelda3 {
SpriteBuilder SpriteBuilder::Create(const std::string& spriteName) {
SpriteBuilder builder;
return builder;
}
SpriteBuilder& SpriteBuilder::SetProperty(const std::string& propertyName,
const std::string& value) {
return *this;
}
SpriteBuilder& SpriteBuilder::SetProperty(const std::string& propertyName,
int value) {
return *this;
}
SpriteBuilder& SpriteBuilder::SetProperty(const std::string& propertyName,
bool value) {
return *this;
}
SpriteBuilder& SpriteBuilder::AddAction(const SpriteAction& action) {
return *this;
}
SpriteBuilder& SpriteBuilder::SetGlobalAction(const SpriteAction& action) {
return *this;
}
SpriteBuilder& SpriteBuilder::AddFunction(const SpriteAction& function) {
return *this;
}
SpriteBuilder& SpriteBuilder::AddFunction(const std::string& asmCode) {
return *this;
}
std::string SpriteBuilder::BuildProperties() const {
std::stringstream ss;
// Build the properties
for (int i = 0; i < 27; ++i) {
std::string property = "00";
if (!properties[i].empty()) property = properties[i];
ss << kSpriteProperties[i] << " = $" << property << std::endl;
}
return ss.str();
}
std::string SpriteBuilder::Build() const {
std::stringstream ss;
ss << BuildProperties();
return ss.str();
}
// ============================================================================
SpriteAction SpriteAction::Create(const std::string& actionName) {
SpriteAction action;
return action;
}
SpriteAction SpriteAction::Create() {
SpriteAction action;
return action;
}
SpriteAction& SpriteAction::AddInstruction(
const SpriteInstruction& instruction) {
return *this;
}
SpriteAction& SpriteAction::AddCustomInstruction(const std::string& asmCode) {
return *this;
}
SpriteAction& SpriteAction::SetNextAction(const std::string& nextActionName) {
return *this;
}
std::string SpriteAction::GetConfiguration() const { return ""; }
// ============================================================================
SpriteInstruction SpriteInstruction::PlayAnimation(int startFrame, int endFrame,
int speed) {
SpriteInstruction instruction;
return instruction;
}
SpriteInstruction SpriteInstruction::ApplySpeedTowardsPlayer(int speed) {
SpriteInstruction instruction;
instruction.SetConfiguration("JSL Sprite_ApplySpeedTowardsPlayer");
return instruction;
}
SpriteInstruction SpriteInstruction::CheckDamageFromPlayer() {
SpriteInstruction instruction;
return instruction;
}
SpriteInstruction SpriteInstruction::MoveXyz() {
SpriteInstruction instruction;
return instruction;
}
SpriteInstruction SpriteInstruction::BounceFromTileCollision() {
SpriteInstruction instruction;
return instruction;
}
SpriteInstruction SpriteInstruction::SetTimer(int timerId, int value) {
SpriteInstruction instruction;
return instruction;
}
SpriteInstruction SpriteInstruction::Custom(const std::string& asmCode) {
SpriteInstruction instruction;
return instruction;
}
SpriteInstruction SpriteInstruction::BehaveAsBarrier() {
SpriteInstruction instruction;
return instruction;
}
SpriteInstruction SpriteInstruction::JumpToFunction(
const std::string& functionName) {
SpriteInstruction instruction;
return instruction;
}
} // namespace zelda3
} // namespace yaze

View File

@@ -1,129 +0,0 @@
#ifndef YAZE_APP_ZELDA3_SPRITE_SPRITE_BUILDER_H_
#define YAZE_APP_ZELDA3_SPRITE_SPRITE_BUILDER_H_
#include <array>
#include <string>
#include <vector>
namespace yaze {
namespace zelda3 {
class SpriteInstruction {
public:
// Predefined instructions
static SpriteInstruction PlayAnimation(int startFrame, int endFrame,
int speed);
static SpriteInstruction ApplySpeedTowardsPlayer(int speed);
static SpriteInstruction CheckDamageFromPlayer();
static SpriteInstruction MoveXyz();
static SpriteInstruction BounceFromTileCollision();
static SpriteInstruction SetTimer(int timerId, int value);
static SpriteInstruction BehaveAsBarrier();
static SpriteInstruction JumpToFunction(const std::string& functionName);
// Custom instruction
static SpriteInstruction Custom(const std::string& asmCode);
// Get the instruction configuration
std::string GetConfiguration() const { return instruction_; }
void SetConfiguration(const std::string& instruction) {
instruction_ = instruction;
}
private:
std::string instruction_;
};
class SpriteAction {
public:
// Factory method to create a new action
static SpriteAction Create(const std::string& actionName);
// Anonymously create an action
static SpriteAction Create();
// Add a predefined instruction to the action
SpriteAction& AddInstruction(const SpriteInstruction& instruction);
// Add custom raw ASM instruction to the action
SpriteAction& AddCustomInstruction(const std::string& asmCode);
// Set the next action to jump to
SpriteAction& SetNextAction(const std::string& nextActionName);
// Get the action configuration
std::string GetConfiguration() const;
private:
std::string name;
std::vector<std::string> instructions;
std::string nextAction;
};
// Array of sprite property names
constexpr const char* kSpriteProperties[] = {"!SPRID",
"!NbrTiles",
"!Harmless",
"!HVelocity",
"!Health",
"!Damage",
"!DeathAnimation",
"!ImperviousAll",
"!SmallShadow",
"!Shadow",
"!Palette",
"!Hitbox",
"!Persist",
"!Statis",
"!CollisionLayer",
"!CanFall",
"!DeflectArrow",
"!WaterSprite",
"!Blockable",
"!Prize",
"!Sound",
"!Interaction",
"!Statue",
"!DeflectProjectiles",
"!ImperviousArrow",
"!ImpervSwordHammer",
"!Boss"};
class SpriteBuilder {
public:
// Factory method to create a new sprite
static SpriteBuilder Create(const std::string& spriteName);
// Set sprite properties
SpriteBuilder& SetProperty(const std::string& propertyName,
const std::string& value);
SpriteBuilder& SetProperty(const std::string& propertyName, int value);
SpriteBuilder& SetProperty(const std::string& propertyName, bool value);
// Add an action to the sprite
SpriteBuilder& AddAction(const SpriteAction& action);
// Set global action to the sprite (always runs)
SpriteBuilder& SetGlobalAction(const SpriteAction& action);
// Add a function to be called from anywhere in the sprite code
SpriteBuilder& AddFunction(const std::string& asmCode);
SpriteBuilder& AddFunction(const SpriteAction& action);
std::string BuildProperties() const;
// Build and get the sprite configuration
std::string Build() const;
private:
std::string name;
std::array<std::string, 27> properties;
std::vector<SpriteAction> actions;
SpriteAction globalAction;
std::vector<SpriteAction> functions;
};
} // namespace zelda3
} // namespace yaze
#endif // YAZE_APP_ZELDA3_SPRITE_SPRITE_BUILDER_H_

View File

@@ -1,372 +0,0 @@
#include "app/zelda3/zelda3_labels.h"
#include "app/zelda3/common.h"
#include "app/zelda3/dungeon/room.h"
#include "app/zelda3/sprite/sprite.h"
#include "app/zelda3/sprite/overlord.h"
namespace yaze {
namespace zelda3 {
// Room names - reuse existing kRoomNames array
const std::vector<std::string>& Zelda3Labels::GetRoomNames() {
static std::vector<std::string> room_names;
if (room_names.empty()) {
for (const auto& name : kRoomNames) {
room_names.emplace_back(name);
}
}
return room_names;
}
// Entrance names - reuse existing kEntranceNames array
const std::vector<std::string>& Zelda3Labels::GetEntranceNames() {
static std::vector<std::string> entrance_names;
if (entrance_names.empty()) {
for (const auto& name : kEntranceNames) {
entrance_names.emplace_back(name);
}
}
return entrance_names;
}
// Sprite names - reuse existing kSpriteDefaultNames array
const std::vector<std::string>& Zelda3Labels::GetSpriteNames() {
static std::vector<std::string> sprite_names;
if (sprite_names.empty()) {
for (const auto& name : kSpriteDefaultNames) {
sprite_names.push_back(name);
}
}
return sprite_names;
}
// Overlord names - reuse existing kOverlordNames array
const std::vector<std::string>& Zelda3Labels::GetOverlordNames() {
static std::vector<std::string> overlord_names;
if (overlord_names.empty()) {
for (const auto& name : kOverlordNames) {
overlord_names.push_back(name);
}
}
return overlord_names;
}
// Overworld map names (64 Light World + 64 Dark World + 32 Special Areas)
const std::vector<std::string>& Zelda3Labels::GetOverworldMapNames() {
static const std::vector<std::string> map_names = {
// Light World (0x00-0x3F)
"Lost Woods", "Master Sword Pedestal", "Castle Courtyard", "Link's House",
"Eastern Palace", "Desert Palace", "Hyrule Castle", "Witch's Hut",
"Kakariko Village", "Death Mountain", "Tower of Hera", "Spectacle Rock",
"Graveyard", "Sanctuary", "Lake Hylia", "Desert of Mystery",
"Eastern Ruins", "Zora's Domain", "Catfish", "Dam",
"Potion Shop", "Kakariko Well", "Blacksmith", "Sick Kid",
"Library", "Mushroom", "Magic Bat", "Fairy Fountain",
"Fortune Teller", "Lake Shop", "Bomb Shop", "Cave 45",
"Checkerboard Cave", "Mini Moldorm Cave", "Ice Rod Cave", "Bonk Rocks",
"Bottle Merchant", "Sahasrahla's Hut", "Chicken House", "Aginah's Cave",
"Dam Exterior", "Mimic Cave Exterior", "Waterfall Fairy", "Pyramid",
"Fat Fairy", "Spike Cave", "Hookshot Cave", "Graveyard Ledge",
"Dark Lumberjacks", "Bumper Cave", "Skull Woods 1", "Skull Woods 2",
"Skull Woods 3", "Skull Woods 4", "Skull Woods 5", "Skull Woods 6",
"Skull Woods 7", "Skull Woods 8", "Ice Palace Exterior", "Misery Mire Exterior",
"Palace of Darkness Exterior", "Swamp Palace Exterior", "Turtle Rock Exterior", "Thieves' Town Exterior",
// Dark World (0x40-0x7F)
"Dark Woods", "Dark Chapel", "Dark Castle", "Dark Shields",
"Dark Palace", "Dark Desert", "Dark Castle Gate", "Dark Witch",
"Dark Village", "Dark Mountain", "Dark Tower", "Dark Rocks",
"Dark Graveyard", "Dark Sanctuary", "Dark Lake", "Dark Desert South",
"Dark Eastern", "Dark Zora", "Dark Catfish", "Dark Dam",
"Dark Shop", "Dark Well", "Dark Blacksmith", "Dark Sick Kid",
"Dark Library", "Dark Mushroom", "Dark Bat", "Dark Fountain",
"Dark Fortune", "Dark Lake Shop", "Dark Bomb Shop", "Dark Cave 45",
"Dark Checker", "Dark Mini Moldorm", "Dark Ice Rod", "Dark Bonk",
"Dark Bottle", "Dark Sahasrahla", "Dark Chicken", "Dark Aginah",
"Dark Dam Exit", "Dark Mimic Exit", "Dark Waterfall", "Pyramid Top",
"Dark Fat Fairy", "Dark Spike Cave", "Dark Hookshot", "Dark Graveyard Ledge",
"Lumberjack House", "Dark Bumper", "Skull Woods A", "Skull Woods B",
"Skull Woods C", "Skull Woods D", "Skull Woods E", "Skull Woods F",
"Skull Woods G", "Skull Woods H", "Ice Palace Entry", "Misery Mire Entry",
"Palace of Darkness Entry", "Swamp Palace Entry", "Turtle Rock Entry", "Thieves' Town Entry",
// Special Areas (0x80-0x9F)
"Special Area 1", "Special Area 2", "Special Area 3", "Special Area 4",
"Special Area 5", "Special Area 6", "Special Area 7", "Special Area 8",
"Special Area 9", "Special Area 10", "Special Area 11", "Special Area 12",
"Special Area 13", "Special Area 14", "Special Area 15", "Special Area 16",
"Special Area 17", "Special Area 18", "Special Area 19", "Special Area 20",
"Special Area 21", "Special Area 22", "Special Area 23", "Special Area 24",
"Special Area 25", "Special Area 26", "Special Area 27", "Special Area 28",
"Special Area 29", "Special Area 30", "Special Area 31", "Special Area 32"
};
return map_names;
}
// Item names (complete item list)
const std::vector<std::string>& Zelda3Labels::GetItemNames() {
static const std::vector<std::string> item_names = {
"None", "Fighter Sword", "Master Sword", "Tempered Sword",
"Golden Sword", "Fighter Shield", "Fire Shield", "Mirror Shield",
"Fire Rod", "Ice Rod", "Hammer", "Hookshot",
"Bow", "Boomerang", "Powder", "Bee Badge",
"Bombos Medallion", "Ether Medallion", "Quake Medallion", "Lamp",
"Shovel", "Flute", "Somaria Cane", "Bottle",
"Heart Piece", "Byrna Cane", "Cape", "Mirror",
"Power Glove", "Titan Mitt", "Book of Mudora", "Zora Flippers",
"Moon Pearl", "Crystal", "Bug Net", "Blue Mail",
"Red Mail", "Key", "Compass", "Heart Container",
"Bomb", "3 Bombs", "Mushroom", "Red Boomerang",
"Red Potion", "Green Potion", "Blue Potion", "Red Potion (Refill)",
"Green Potion (Refill)", "Blue Potion (Refill)", "10 Bombs", "Big Key",
"Map", "1 Rupee", "5 Rupees", "20 Rupees",
"Pendant of Courage", "Pendant of Wisdom", "Pendant of Power", "Bow and Arrows",
"Silver Arrows Upgrade", "Bee", "Fairy", "Heart Container (Boss)",
"Heart", "1 Arrow", "10 Arrows", "Magic",
"Small Magic", "300 Rupees", "20 Rupees (Green)", "100 Rupees",
"50 Rupees", "Heart Container (Sanctuary)", "Arrow Refill (5)", "Arrow Refill (10)",
"Bomb Refill (1)", "Bomb Refill (4)", "Bomb Refill (8)", "Blue Shield (Refill)",
"Magic Upgrade (1/2)", "Magic Upgrade (1/4)", "Programmable Item 1", "Programmable Item 2",
"Programmable Item 3", "Silvers", "Rupoor", "Null Item",
"Red Clock", "Blue Clock", "Green Clock", "Progressive Sword",
"Progressive Shield", "Progressive Armor", "Progressive Lifting Glove", "RNG Item (Single)",
"RNG Item (Multi)", "Progressive Bow", "Progressive Bow (Alt)", "Aga Pendant",
"Pendant (Green)", "Blue Pendant", "Good Bee", "Tossed Key"
};
return item_names;
}
// Music track names
const std::vector<std::string>& Zelda3Labels::GetMusicTrackNames() {
static const std::vector<std::string> music_names = {
"Nothing", "Light World", "Beginning", "Rabbit",
"Forest", "Intro", "Town", "Warp",
"Dark World", "Master Sword", "File Select", "Soldier",
"Boss", "Dark World Death Mountain", "Minigame", "Skull Woods",
"Indoor", "Cave 1", "Zelda's Rescue", "Crystal",
"Shop", "Cave 2", "Game Over", "Boss Victory",
"Sanctuary", "Boss Victory (Short)", "Dark World Woods", "Pendant",
"Ganon's Message", "Hyrule Castle", "Light World Death Mountain", "Eastern Palace",
"Desert Palace", "Agahnim's Theme", "Damp Dungeon", "Ganon Reveals",
"Confrontation", "Ganon's Theme", "Triforce", "Credits",
"Unused", "Unused", "Unused", "Unused",
"Unused", "Unused", "Unused", "Unused"
};
return music_names;
}
// Graphics sheet names
const std::vector<std::string>& Zelda3Labels::GetGraphicsSheetNames() {
static const std::vector<std::string> gfx_names = {
"Sprite Sheet 0", "Sprite Sheet 1", "Sprite Sheet 2", "Sprite Sheet 3",
"Sprite Sheet 4", "Sprite Sheet 5", "Sprite Sheet 6", "Sprite Sheet 7",
"Sprite Sheet 8", "Sprite Sheet 9", "Sprite Sheet A", "Sprite Sheet B",
"Sprite Sheet C", "Sprite Sheet D", "Sprite Sheet E", "Sprite Sheet F",
"Link's Sprites", "Sword Sprites", "Shield Sprites", "Common Sprites",
"Boss Sprites", "NPC Sprites", "Enemy Sprites 1", "Enemy Sprites 2",
"Item Sprites", "Dungeon Objects", "Overworld Objects", "Interface",
"Font", "Credits", "Unused", "Unused"
};
return gfx_names;
}
// Room object names - these are large, so we'll delegate to a helper
namespace {
std::vector<std::string> ConvertArrayToVector(const char** array, size_t size) {
std::vector<std::string> result;
result.reserve(size);
for (size_t i = 0; i < size; ++i) {
result.emplace_back(array[i]);
}
return result;
}
}
const std::vector<std::string>& Zelda3Labels::GetType1RoomObjectNames() {
static const std::vector<std::string> names = []() {
std::vector<std::string> result;
// Note: Type1RoomObjectNames is constexpr, we need to count its size
// For now, we'll add known objects. In full implementation,
// we'd import from room_object.h
result = {
"Ceiling ↔", "Wall (top, north) ↔", "Wall (top, south) ↔",
"Wall (bottom, north) ↔", "Wall (bottom, south) ↔", "Wall columns (north) ↔",
"Wall columns (south) ↔", "Deep wall (north) ↔", "Deep wall (south) ↔",
"Diagonal wall A ◤ (top) ↔", "Diagonal wall A ◣ (top) ↔",
"Diagonal wall A ◥ (top) ↔", "Diagonal wall A ◢ (top) ↔",
// ... Add all Type1 objects here
};
return result;
}();
return names;
}
const std::vector<std::string>& Zelda3Labels::GetType2RoomObjectNames() {
static const std::vector<std::string> names = []() {
std::vector<std::string> result;
// Add Type2 room objects
result = {"Type2 Object 1", "Type2 Object 2" /* ... */};
return result;
}();
return names;
}
const std::vector<std::string>& Zelda3Labels::GetType3RoomObjectNames() {
static const std::vector<std::string> names = []() {
std::vector<std::string> result;
// Add Type3 room objects
result = {"Type3 Object 1", "Type3 Object 2" /* ... */};
return result;
}();
return names;
}
// Room effect names - reuse existing RoomEffect array
const std::vector<std::string>& Zelda3Labels::GetRoomEffectNames() {
static std::vector<std::string> effect_names;
if (effect_names.empty()) {
for (const auto& name : RoomEffect) {
effect_names.push_back(name);
}
}
return effect_names;
}
// Room tag names
const std::vector<std::string>& Zelda3Labels::GetRoomTagNames() {
static const std::vector<std::string> tag_names = {
"No Tag", "NW", "NE", "SW", "SE",
"West", "East", "North", "South",
"Entrance", "Treasure", "Boss", "Dark"
};
return tag_names;
}
// Tile type names
const std::vector<std::string>& Zelda3Labels::GetTileTypeNames() {
static const std::vector<std::string> tile_names = {
"Nothing (standard floor)", "Nothing (unused?)", "Collision",
"Collision (unknown types)", "Collision", "Collision (unused?)",
"Collision", "Collision", "Deep water", "Shallow water",
"Unknown (near water/pit edges)", "Collision (water/pit edges)",
"Overlay mask", "Spike floor", "GT ice", "Ice palace ice",
"Slope ◤", "Slope ◥", "Slope ◣", "Slope ◢",
"Nothing (unused?)", "Nothing (unused?)", "Nothing (unused?)",
"Slope ◤", "Slope ◥", "Slope ◣", "Slope ◢",
"Layer swap", "Pit", "Manual stairs", "Pot switch", "Pressure switch",
"Blocks switch (chest, PoD, walls)", "Layer toggle", "Layer 2 overlay",
"North single-layer auto stairs", "North layer swap auto stairs",
"South single-layer auto stairs", "South layer swap auto stairs",
"North/south layer swap auto stairs", "North/south single-layer auto stairs",
"West single-layer auto stairs", "West layer swap auto stairs",
"East single-layer auto stairs", "East layer swap auto stairs",
"East/west layer swap auto stairs", "East/west single-layer auto stairs",
"Nothing (stairs edge)", "Straight inter-room stairs south/up",
"Straight inter-room stairs north/down", "Straight inter-room stairs south/down 2",
"Straight inter-room stairs north/up 2", "Star tile (inactive on GBA)",
"Collision (near stairs)", "Warp tile", "Square corners ⌜⌝⌞⌟",
"Thick corner ⌜", "Thick corner ⌝", "Thick corner ⌞", "Thick corner ⌟",
"Roof/grass tiles?", "Spike floor"
};
return tile_names;
}
// Convert all labels to structured map for project embedding
std::unordered_map<std::string, std::unordered_map<std::string, std::string>>
Zelda3Labels::ToResourceLabels() {
std::unordered_map<std::string, std::unordered_map<std::string, std::string>> labels;
// Rooms
const auto& rooms = GetRoomNames();
for (size_t i = 0; i < rooms.size(); ++i) {
labels["room"][std::to_string(i)] = rooms[i];
}
// Entrances
const auto& entrances = GetEntranceNames();
for (size_t i = 0; i < entrances.size(); ++i) {
labels["entrance"][std::to_string(i)] = entrances[i];
}
// Sprites
const auto& sprites = GetSpriteNames();
for (size_t i = 0; i < sprites.size(); ++i) {
labels["sprite"][std::to_string(i)] = sprites[i];
}
// Overlords
const auto& overlords = GetOverlordNames();
for (size_t i = 0; i < overlords.size(); ++i) {
labels["overlord"][std::to_string(i)] = overlords[i];
}
// Overworld maps
const auto& maps = GetOverworldMapNames();
for (size_t i = 0; i < maps.size(); ++i) {
labels["overworld_map"][std::to_string(i)] = maps[i];
}
// Items
const auto& items = GetItemNames();
for (size_t i = 0; i < items.size(); ++i) {
labels["item"][std::to_string(i)] = items[i];
}
// Music tracks
const auto& music = GetMusicTrackNames();
for (size_t i = 0; i < music.size(); ++i) {
labels["music"][std::to_string(i)] = music[i];
}
// Graphics sheets
const auto& gfx = GetGraphicsSheetNames();
for (size_t i = 0; i < gfx.size(); ++i) {
labels["graphics"][std::to_string(i)] = gfx[i];
}
// Room effects
const auto& effects = GetRoomEffectNames();
for (size_t i = 0; i < effects.size(); ++i) {
labels["room_effect"][std::to_string(i)] = effects[i];
}
// Room tags
const auto& tags = GetRoomTagNames();
for (size_t i = 0; i < tags.size(); ++i) {
labels["room_tag"][std::to_string(i)] = tags[i];
}
// Tile types
const auto& tiles = GetTileTypeNames();
for (size_t i = 0; i < tiles.size(); ++i) {
labels["tile_type"][std::to_string(i)] = tiles[i];
}
return labels;
}
// Get a label by resource type and ID
std::string Zelda3Labels::GetLabel(const std::string& resource_type, int id,
const std::string& default_value) {
static auto labels = ToResourceLabels();
auto type_it = labels.find(resource_type);
if (type_it == labels.end()) {
return default_value.empty()
? resource_type + "_" + std::to_string(id)
: default_value;
}
auto label_it = type_it->second.find(std::to_string(id));
if (label_it == type_it->second.end()) {
return default_value.empty()
? resource_type + "_" + std::to_string(id)
: default_value;
}
return label_it->second;
}
} // namespace zelda3
} // namespace yaze

View File

@@ -1,79 +0,0 @@
#ifndef YAZE_APP_ZELDA3_ZELDA3_LABELS_H
#define YAZE_APP_ZELDA3_ZELDA3_LABELS_H
#include <map>
#include <string>
#include <unordered_map>
#include <vector>
namespace yaze {
namespace zelda3 {
/**
* @struct Zelda3Labels
* @brief Centralized default labels for all Zelda 3 resources
*
* This structure contains all the default names/labels for various game resources.
* These labels are embedded directly into the project file format and are always
* available to the AI agents (Ollama/Gemini) without requiring external files.
*/
struct Zelda3Labels {
// Dungeon/Room names (296 rooms total)
static const std::vector<std::string>& GetRoomNames();
// Entrance names (133 entrances)
static const std::vector<std::string>& GetEntranceNames();
// Sprite names (256 sprites)
static const std::vector<std::string>& GetSpriteNames();
// Overlord names (14 overlords)
static const std::vector<std::string>& GetOverlordNames();
// Overworld map names (160 maps: 64 light world + 64 dark world + 32 special)
static const std::vector<std::string>& GetOverworldMapNames();
// Item names (all collectible items)
static const std::vector<std::string>& GetItemNames();
// Music track names
static const std::vector<std::string>& GetMusicTrackNames();
// Graphics sheet names
static const std::vector<std::string>& GetGraphicsSheetNames();
// Room object type names
static const std::vector<std::string>& GetType1RoomObjectNames();
static const std::vector<std::string>& GetType2RoomObjectNames();
static const std::vector<std::string>& GetType3RoomObjectNames();
// Room effect names
static const std::vector<std::string>& GetRoomEffectNames();
// Room tag names
static const std::vector<std::string>& GetRoomTagNames();
// Tile type names
static const std::vector<std::string>& GetTileTypeNames();
/**
* @brief Convert all labels to a structured map for project embedding
* @return Map of resource type -> (id -> name) for all resources
*/
static std::unordered_map<std::string, std::unordered_map<std::string, std::string>> ToResourceLabels();
/**
* @brief Get a label by resource type and ID
* @param resource_type The type of resource (e.g., "room", "entrance", "sprite")
* @param id The numeric ID of the resource
* @param default_value Fallback value if label not found
* @return The label string
*/
static std::string GetLabel(const std::string& resource_type, int id,
const std::string& default_value = "");
};
} // namespace zelda3
} // namespace yaze
#endif // YAZE_APP_ZELDA3_ZELDA3_LABELS_H

View File

@@ -1,69 +0,0 @@
set(
YAZE_APP_ZELDA3_SRC
app/zelda3/dungeon/dungeon_editor_system.cc
app/zelda3/dungeon/dungeon_object_editor.cc
app/zelda3/dungeon/object_drawer.cc
app/zelda3/dungeon/object_parser.cc
app/zelda3/dungeon/room.cc
app/zelda3/dungeon/room_layout.cc
app/zelda3/dungeon/room_object.cc
app/zelda3/music/tracker.cc
app/zelda3/overworld/overworld.cc
app/zelda3/overworld/overworld_map.cc
app/zelda3/screen/dungeon_map.cc
app/zelda3/screen/inventory.cc
app/zelda3/screen/title_screen.cc
app/zelda3/sprite/sprite.cc
app/zelda3/sprite/sprite_builder.cc
app/zelda3/zelda3_labels.cc
)
# ==============================================================================
# Yaze Zelda3 Library
# ==============================================================================
# This library contains all Zelda3-specific game logic:
# - Overworld system (maps, tiles, sprites)
# - Dungeon system (rooms, objects, sprites)
# - Screen modules (title, inventory, dungeon map)
# - Sprite management
# - Music/tracker system
#
# Dependencies: yaze_gfx, yaze_util
# ==============================================================================
add_library(yaze_zelda3 STATIC ${YAZE_APP_ZELDA3_SRC})
target_precompile_headers(yaze_zelda3 PRIVATE
"$<$<COMPILE_LANGUAGE:CXX>:${CMAKE_SOURCE_DIR}/src/yaze_pch.h>"
)
target_include_directories(yaze_zelda3 PUBLIC
${CMAKE_SOURCE_DIR}/src
${CMAKE_SOURCE_DIR}/src/lib
${CMAKE_SOURCE_DIR}/incl
${PROJECT_BINARY_DIR}
)
target_link_libraries(yaze_zelda3 PUBLIC
yaze_gfx
yaze_util
yaze_common
${ABSL_TARGETS}
)
set_target_properties(yaze_zelda3 PROPERTIES
POSITION_INDEPENDENT_CODE ON
ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/lib"
)
# Platform-specific compile definitions
if(UNIX AND NOT APPLE)
target_compile_definitions(yaze_zelda3 PRIVATE linux stricmp=strcasecmp)
elseif(APPLE)
target_compile_definitions(yaze_zelda3 PRIVATE MACOS)
elseif(WIN32)
target_compile_definitions(yaze_zelda3 PRIVATE WINDOWS)
endif()
message(STATUS "✓ yaze_zelda3 library configured")