diff --git a/src/app/editor/overworld_editor.cc b/src/app/editor/overworld_editor.cc index f11c115e..a8d3d1b9 100644 --- a/src/app/editor/overworld_editor.cc +++ b/src/app/editor/overworld_editor.cc @@ -79,8 +79,9 @@ absl::Status OverworldEditor::Update() { absl::Status OverworldEditor::DrawToolset() { static bool show_gfx_group = false; static bool show_tile16 = false; + static bool show_properties = false; - if (ImGui::BeginTable("OWToolset", 19, kToolsetTableFlags, ImVec2(0, 0))) { + if (ImGui::BeginTable("OWToolset", 20, kToolsetTableFlags, ImVec2(0, 0))) { for (const auto &name : kToolsetColumnNames) ImGui::TableSetupColumn(name.data()); @@ -170,6 +171,9 @@ absl::Status OverworldEditor::DrawToolset() { TableNextColumn(); // Experimental ImGui::Checkbox("Experimental", &show_experimental); + TableNextColumn(); + ImGui::Checkbox("Properties", &show_properties); + ImGui::EndTable(); } @@ -189,9 +193,47 @@ absl::Status OverworldEditor::DrawToolset() { RETURN_IF_ERROR(gfx_group_editor_.Update()) ImGui::End(); } + + if (show_properties) { + ImGui::Begin("Properties", &show_properties); + DrawOverworldProperties(); + ImGui::End(); + } + return absl::OkStatus(); } +void OverworldEditor::DrawOverworldProperties() { + static bool init_properties = false; + static gui::Canvas properties_canvas; + + if (!init_properties) { + for (int i = 0; i < 0x40; i++) { + std::string area_graphics_str = absl::StrFormat( + "0x%02hX", overworld_.overworld_map(i).area_graphics()); + properties_canvas.mutable_labels(0)->push_back(area_graphics_str); + } + for (int i = 0; i < 0x40; i++) { + std::string area_palette_str = absl::StrFormat( + "0x%02hX", overworld_.overworld_map(i).area_palette()); + properties_canvas.mutable_labels(1)->push_back(area_palette_str); + } + init_properties = true; + } + + if (ImGui::Button("Area Graphics")) { + properties_canvas.set_current_labels(0); + } + + ImGui::SameLine(); + + if (ImGui::Button("Area Palette")) { + properties_canvas.set_current_labels(1); + } + + properties_canvas.UpdateInfoGrid(ImVec2(512, 512), 16, 1.0f, 64); +} + // ---------------------------------------------------------------------------- void OverworldEditor::DrawOverworldMapSettings() { @@ -280,6 +322,18 @@ void OverworldEditor::DrawOverworldEntrances(ImVec2 canvas_p0, } } +void OverworldEditor::DrawOverworldExits(ImVec2 canvas_p0, ImVec2 scrolling) { + for (auto &each : overworld_.Exits()) { + if (each.map_id_ < 0x40 + (current_world_ * 0x40) && + each.map_id_ >= (current_world_ * 0x40)) { + ow_map_canvas_.DrawRect(each.x_, each.y_, 16, 16, + ImVec4(255, 255, 255, 150)); + std::string str = absl::StrFormat("%#x", each.entrance_id_); + ow_map_canvas_.DrawText(str, each.x_ - 4, each.y_ - 2); + } + } +} + bool OverworldEditor::IsMouseHoveringOverEntrance( const zelda3::OverworldEntrance &entrance, ImVec2 canvas_p0, ImVec2 scrolling) { @@ -420,32 +474,33 @@ void OverworldEditor::CheckForOverworldEdits() { void OverworldEditor::CheckForCurrentMap() { // DetermineActiveMap(ImGui::GetIO().MousePos); // 4096x4096, 512x512 maps and some are larges maps 1024x1024 - auto current_map_x = current_map_ % 8; - auto current_map_y = current_map_ / 8; - auto large_map_size = 1024; - auto map_size = 512; - auto mouse_position = ImGui::GetIO().MousePos; - - // Assuming each small map is 256x256 pixels (adjust as needed) constexpr int small_map_size = 512; + auto large_map_size = 1024; + + const auto canvas_zero_point = ow_map_canvas_.zero_point(); // Calculate which small map the mouse is currently over - int map_x = mouse_position.x / small_map_size; - int map_y = mouse_position.y / small_map_size; + int map_x = (mouse_position.x - canvas_zero_point.x) / small_map_size; + int map_y = (mouse_position.y - canvas_zero_point.y) / small_map_size; // Calculate the index of the map in the `maps_bmp_` vector current_map_ = map_x + map_y * 8; + auto current_map_x = current_map_ % 8; + auto current_map_y = current_map_ / 8; + if (overworld_.overworld_map(current_map_).IsLargeMap()) { - // Draw an outline around the current map - ow_map_canvas_.DrawOutline(current_map_x * large_map_size, - current_map_y * large_map_size, large_map_size, + int parent_id = overworld_.overworld_map(current_map_).Parent(); + int parent_map_x = parent_id % 8; + int parent_map_y = parent_id / 8; + ow_map_canvas_.DrawOutline(parent_map_x * small_map_size, + parent_map_x * small_map_size, large_map_size, large_map_size); } else { - // Draw an outline around the current map - ow_map_canvas_.DrawOutline(current_map_x * map_size, - current_map_y * map_size, map_size, map_size); + ow_map_canvas_.DrawOutline(current_map_x * small_map_size, + current_map_y * small_map_size, small_map_size, + small_map_size); } static int prev_map_; @@ -480,6 +535,8 @@ void OverworldEditor::DrawOverworldCanvas() { DrawOverworldMaps(); DrawOverworldEntrances(ow_map_canvas_.zero_point(), ow_map_canvas_.Scrolling()); + DrawOverworldExits(ow_map_canvas_.zero_point(), + ow_map_canvas_.Scrolling()); if (flags()->kDrawOverworldSprites) { DrawOverworldSprites(); } diff --git a/src/app/editor/overworld_editor.h b/src/app/editor/overworld_editor.h index 1665e941..6a17b90b 100644 --- a/src/app/editor/overworld_editor.h +++ b/src/app/editor/overworld_editor.h @@ -39,7 +39,8 @@ static constexpr absl::string_view kToolsetColumnNames[] = { "#undoTool", "#redoTool", "#drawTool", "#separator2", "#zoomOutTool", "#zoomInTool", "#separator", "#history", "#entranceTool", "#exitTool", "#itemTool", "#spriteTool", - "#transportTool", "#musicTool", "#separator3", "#tilemapTool"}; + "#transportTool", "#musicTool", "#separator3", "#tilemapTool", + "propertiesTool"}; constexpr ImGuiTableFlags kOWMapFlags = ImGuiTableFlags_Borders; constexpr ImGuiTableFlags kToolsetTableFlags = ImGuiTableFlags_SizingFixedFit; @@ -91,7 +92,10 @@ class OverworldEditor : public Editor, absl::Status DrawToolset(); void DrawOverworldMapSettings(); + void DrawOverworldProperties(); + void DrawOverworldEntrances(ImVec2 canvas_p, ImVec2 scrolling); + void DrawOverworldExits(ImVec2 zero, ImVec2 scrolling); void DrawOverworldMaps(); void DrawOverworldSprites(); diff --git a/src/app/zelda3/overworld.cc b/src/app/zelda3/overworld.cc index 71e15f94..9193d045 100644 --- a/src/app/zelda3/overworld.cc +++ b/src/app/zelda3/overworld.cc @@ -10,8 +10,8 @@ #include "absl/container/flat_hash_map.h" #include "absl/status/status.h" -#include "app/core/constants.h" #include "app/core/common.h" +#include "app/core/constants.h" #include "app/gfx/bitmap.h" #include "app/gfx/compression.h" #include "app/gfx/snes_tile.h" @@ -140,6 +140,7 @@ absl::Status Overworld::Load(ROM &rom) { FetchLargeMaps(); LoadEntrances(); + LoadExits(); RETURN_IF_ERROR(LoadOverworldMaps()) if (flags()->kDrawOverworldSprites) { LoadSprites(); @@ -893,6 +894,60 @@ void Overworld::LoadEntrances() { } } +void Overworld::LoadExits() { + const int NumberOfOverworldExits = 0x4F; + std::vector exits; + for (int i = 0; i < NumberOfOverworldExits; i++) { + auto rom_data = rom()->data(); + ushort exitRoomID = (ushort)((rom_data[OWExitRoomId + (i * 2) + 1] << 8) + + rom_data[OWExitRoomId + (i * 2)]); + ushort exitMapID = rom_data[OWExitMapId + i]; + ushort exitVRAM = (ushort)((rom_data[OWExitVram + (i * 2) + 1] << 8) + + rom_data[OWExitVram + (i * 2)]); + ushort exitYScroll = (ushort)((rom_data[OWExitYScroll + (i * 2) + 1] << 8) + + rom_data[OWExitYScroll + (i * 2)]); + ushort exitXScroll = (ushort)((rom_data[OWExitXScroll + (i * 2) + 1] << 8) + + rom_data[OWExitXScroll + (i * 2)]); + ushort py = (ushort)((rom_data[OWExitYPlayer + (i * 2) + 1] << 8) + + rom_data[OWExitYPlayer + (i * 2)]); + ushort px = (ushort)((rom_data[OWExitXPlayer + (i * 2) + 1] << 8) + + rom_data[OWExitXPlayer + (i * 2)]); + ushort exitYCamera = (ushort)((rom_data[OWExitYCamera + (i * 2) + 1] << 8) + + rom_data[OWExitYCamera + (i * 2)]); + ushort exitXCamera = (ushort)((rom_data[OWExitXCamera + (i * 2) + 1] << 8) + + rom_data[OWExitXCamera + (i * 2)]); + ushort exitScrollModY = rom_data[OWExitUnk1 + i]; + ushort exitScrollModX = rom_data[OWExitUnk2 + i]; + ushort exitDoorType1 = + (ushort)((rom_data[OWExitDoorType1 + (i * 2) + 1] << 8) + + rom_data[OWExitDoorType1 + (i * 2)]); + ushort exitDoorType2 = + (ushort)((rom_data[OWExitDoorType2 + (i * 2) + 1] << 8) + + rom_data[OWExitDoorType2 + (i * 2)]); + OverworldExit exit(exitRoomID, exitMapID, exitVRAM, exitYScroll, + exitXScroll, py, px, exitYCamera, exitXCamera, + exitScrollModY, exitScrollModX, exitDoorType1, + exitDoorType2); + + std::cout << "Exit: " << i << " RoomID: " << exitRoomID + << " MapID: " << exitMapID << " VRAM: " << exitVRAM + << " YScroll: " << exitYScroll << " XScroll: " << exitXScroll + << " YPlayer: " << py << " XPlayer: " << px + << " YCamera: " << exitYCamera << " XCamera: " << exitXCamera + << " ScrollModY: " << exitScrollModY + << " ScrollModX: " << exitScrollModX + << " DoorType1: " << exitDoorType1 + << " DoorType2: " << exitDoorType2 << std::endl; + + if (px == 0xFFFF && py == 0xFFFF) { + exit.deleted = true; + } + + exits.push_back(exit); + } + all_exits_ = exits; +} + void Overworld::LoadSprites() { for (int i = 0; i < 3; i++) { all_sprites_.emplace_back(); diff --git a/src/app/zelda3/overworld.h b/src/app/zelda3/overworld.h index c3ac3613..7909c425 100644 --- a/src/app/zelda3/overworld.h +++ b/src/app/zelda3/overworld.h @@ -60,6 +60,141 @@ constexpr int OWExitUnk1Whirlpool = 0x16BF5; // JP = ;016E91 constexpr int OWExitUnk2Whirlpool = 0x16C17; // JP = ;016EB3 constexpr int OWWhirlpoolPosition = 0x16CF8; // JP = ;016F94 +class OverworldExit { + public: + int x_; + int y_; + ushort room_id_; + ushort map_pos_; + uchar entrance_id_; + uchar area_x_; + uchar area_y_; + short map_id_; + bool is_hole_ = false; + bool deleted = false; + + OverworldExit(ushort roomID, uchar mapID, ushort vramLocation, ushort yScroll, + ushort xScroll, ushort playerY, ushort playerX, ushort cameraY, + ushort cameraX, uchar scrollModY, uchar scrollModX, + ushort doorType1, ushort doorType2) + : x_(playerX), + y_(playerY), + map_pos_(vramLocation), + entrance_id_(0), + area_x_(0), + area_y_(0), + map_id_(mapID), + is_hole_(false) { + int mapX = (map_id_ - ((map_id_ / 8) * 8)); + int mapY = (map_id_ / 8); + + area_x_ = (uchar)((std::abs(x_ - (mapX * 512)) / 16)); + area_y_ = (uchar)((std::abs(y_ - (mapY * 512)) / 16)); + + if (doorType1 != 0) { + int p = (doorType1 & 0x7FFF) >> 1; + entrance_id_ = (uchar)(p % 64); + area_y_ = (uchar)(p >> 6); + } + + if (doorType2 != 0) { + int p = (doorType2 & 0x7FFF) >> 1; + entrance_id_ = (uchar)(p % 64); + area_y_ = (uchar)(p >> 6); + } + + if (map_id_ >= 64) { + map_id_ -= 64; + } + + mapX = (map_id_ - ((map_id_ / 8) * 8)); + mapY = (map_id_ / 8); + + area_x_ = (uchar)((std::abs(x_ - (mapX * 512)) / 16)); + area_y_ = (uchar)((std::abs(y_ - (mapY * 512)) / 16)); + + map_pos_ = (ushort)((((area_y_) << 6) | (area_x_ & 0x3F)) << 1); + } + + // void updateMapStuff(uchar mapID, Overworld overworld) { + // map_id_ = mapID; + + // int large = 256; + // int mapid = mapID; + + // if (mapID < 128) { + // large = overworld.AllMaps[mapID].LargeMap ? 768 : 256; + // if (overworld.AllMaps[mapID].ParentID != mapID) { + // mapid = overworld.AllMaps[mapID].ParentID; + // } + // } + + // int mapX = mapID - ((mapID / 8) * 8); + // int mapY = mapID / 8; + + // area_x_ = (uchar)((std::abs(x_ - (mapX * 512)) / 16)); + // area_y_ = (uchar)((std::abs(y_ - (mapY * 512)) / 16)); + + // if (mapID >= 64) { + // mapID -= 64; + // } + + // int mapx = (mapID & 7) << 9; + // int mapy = (mapID & 56) << 6; + + // if (IsAutomatic) { + // 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; + // } + + // cameraX = playerX + 0x07; + // cameraY = playerY + 0x1F; + + // if (cameraX < mapx + 127) { + // cameraX = mapx + 127; + // } + + // if (cameraY < mapy + 111) { + // cameraY = mapy + 111; + // } + + // if (cameraX > mapx + 127 + large) { + // cameraX = mapx + 127 + large; + // } + + // if (cameraY > mapy + 143 + large) { + // cameraY = mapy + 143 + large; + // } + // } + + // short vramXScroll = (short)(x_ - mapx); + // short vramYScroll = (short)(y_ - mapy); + + // map_pos_ = + // (ushort)(((vramYScroll & 0xFFF0) << 3) | ((vramXScroll & 0xFFF0) >> + // 3)); + + // std::cout << "Exit: " << room_id_ << " MapId: " << std::hex << mapid + // << " X: " << static_cast(area_x_) + // << " Y: " << static_cast(area_y_) << std::endl; + // } +}; + class OverworldEntrance { public: int x_; @@ -199,6 +334,7 @@ class Overworld : public SharedROM, public core::ExperimentFlags { return overworld_maps_[current_map_].AreaGraphics(); } auto &Entrances() { return all_entrances_; } + auto &Exits() { return all_exits_; } auto AreaPalette() const { return overworld_maps_[current_map_].AreaPalette(); } @@ -236,6 +372,7 @@ class Overworld : public SharedROM, public core::ExperimentFlags { absl::Status DecompressProtoMapTiles(const std::string &filename); void FetchLargeMaps(); void LoadEntrances(); + void LoadExits(); void LoadSprites(); void LoadSpritesFromMap(int spriteStart, int spriteCount, int spriteIndex); @@ -253,6 +390,7 @@ class Overworld : public SharedROM, public core::ExperimentFlags { std::vector overworld_maps_; std::vector all_entrances_; std::vector all_holes_; + std::vector all_exits_; std::vector> all_sprites_; absl::flat_hash_map proto_map_data_; diff --git a/src/app/zelda3/overworld_map.h b/src/app/zelda3/overworld_map.h index d830d561..dddf2e38 100644 --- a/src/app/zelda3/overworld_map.h +++ b/src/app/zelda3/overworld_map.h @@ -39,6 +39,12 @@ class OverworldMap { auto IsInitialized() const { return initialized_; } auto Parent() const { return parent_; } + 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 mutable_area_graphics() { return &area_graphics_; } auto mutable_area_palette() { return &area_palette_; } auto mutable_sprite_graphics(int i) { return &sprite_graphics_[i]; }