From d9bbea300d3bd09fdb5b7a14391140a224aaab8e Mon Sep 17 00:00:00 2001 From: Irastris Date: Wed, 13 May 2026 00:52:02 -0400 Subject: [PATCH] Add map loader to RmlUi (#1147) * Add Warp to RmlUi * Remove ImGui map loader --- files.cmake | 3 +- include/dusk/hotkeys.h | 1 - src/dusk/imgui/ImGuiConsole.cpp | 1 - src/dusk/imgui/ImGuiMapLoader.cpp | 149 ------------- src/dusk/imgui/ImGuiMenuTools.cpp | 1 - src/dusk/imgui/ImGuiMenuTools.hpp | 14 -- src/dusk/ui/editor.cpp | 75 ++++--- src/dusk/ui/editor.hpp | 11 + src/dusk/ui/menu_bar.cpp | 5 +- src/dusk/ui/warp.cpp | 334 ++++++++++++++++++++++++++++++ src/dusk/ui/warp.hpp | 12 ++ 11 files changed, 398 insertions(+), 208 deletions(-) delete mode 100644 src/dusk/imgui/ImGuiMapLoader.cpp create mode 100644 src/dusk/ui/warp.cpp create mode 100644 src/dusk/ui/warp.hpp diff --git a/files.cmake b/files.cmake index 450fae3b49..eb8c62bee5 100644 --- a/files.cmake +++ b/files.cmake @@ -1458,7 +1458,6 @@ set(DUSK_FILES src/dusk/imgui/ImGuiHeapOverlay.cpp src/dusk/imgui/ImGuiControllerOverlay.cpp src/dusk/imgui/ImGuiStubLog.cpp - src/dusk/imgui/ImGuiMapLoader.cpp src/dusk/imgui/ImGuiSaveEditor.cpp src/dusk/imgui/ImGuiStateShare.hpp src/dusk/imgui/ImGuiStateShare.cpp @@ -1509,6 +1508,8 @@ set(DUSK_FILES src/dusk/ui/tab_bar.hpp src/dusk/ui/ui.cpp src/dusk/ui/ui.hpp + src/dusk/ui/warp.cpp + src/dusk/ui/warp.hpp src/dusk/ui/window.cpp src/dusk/ui/window.hpp src/dusk/achievements.cpp diff --git a/include/dusk/hotkeys.h b/include/dusk/hotkeys.h index c3dd354538..7a774bde80 100644 --- a/include/dusk/hotkeys.h +++ b/include/dusk/hotkeys.h @@ -14,7 +14,6 @@ constexpr const char* SHOW_DEBUG_OVERLAY = "F3"; constexpr const char* SHOW_HEAP_VIEWER = "F4"; constexpr const char* SHOW_PLAYER_INFO = "F5"; constexpr const char* SHOW_SAVE_EDITOR = "F6"; -constexpr const char* SHOW_MAP_LOADER = "F7"; constexpr const char* SHOW_STATE_SHARE = "F8"; constexpr const char* SHOW_DEBUG_CAMERA = "F9"; constexpr const char* SHOW_AUDIO_DEBUG = "F10"; diff --git a/src/dusk/imgui/ImGuiConsole.cpp b/src/dusk/imgui/ImGuiConsole.cpp index 9ba323eaad..b1349ac385 100644 --- a/src/dusk/imgui/ImGuiConsole.cpp +++ b/src/dusk/imgui/ImGuiConsole.cpp @@ -368,7 +368,6 @@ namespace dusk { m_menuTools.ShowProcessManager(); m_menuTools.ShowHeapOverlay(); m_menuTools.ShowStubLog(); - m_menuTools.ShowMapLoader(); m_menuTools.ShowBloomWindow(); m_menuTools.ShowPlayerInfo(); m_menuTools.ShowAudioDebug(); diff --git a/src/dusk/imgui/ImGuiMapLoader.cpp b/src/dusk/imgui/ImGuiMapLoader.cpp deleted file mode 100644 index cd6f292152..0000000000 --- a/src/dusk/imgui/ImGuiMapLoader.cpp +++ /dev/null @@ -1,149 +0,0 @@ -#include "d/d_com_inf_game.h" - -#include "imgui.h" -#include -#include "ImGuiConsole.hpp" -#include "ImGuiMenuTools.hpp" -#include "dusk/map_loader_definitions.h" -#include "fmt/format.h" - -namespace dusk { - void ImGuiMenuTools::ShowMapLoader() { - if (!getSettings().backend.enableAdvancedSettings || - !ImGuiConsole::CheckMenuViewToggle(ImGuiKey_F7, m_showMapLoader)) - { - return; - } - - ImGuiWindowFlags windowFlags = ImGuiWindowFlags_AlwaysAutoResize | - ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav; - - // ImGui::SetNextWindowBgAlpha(0.65f); - - if (!ImGui::Begin("Map Loader", &m_showMapLoader, windowFlags)) { - ImGui::End(); - return; - } - - ImGui::Checkbox("Show Internal Names", &m_mapLoaderInfo.showInternalNames); - - const char* previewRegion = "None"; - if (m_mapLoaderInfo.regionIdx != -1) { - previewRegion = gameRegions[m_mapLoaderInfo.regionIdx].regionName; - } - - if (ImGui::BeginCombo("Select Region", previewRegion)) { - int idx = 0; - for (const auto& region : gameRegions) { - if (ImGui::Selectable(region.regionName)) { - if (m_mapLoaderInfo.regionIdx != idx) { - m_mapLoaderInfo.mapIdx = 0; - m_mapLoaderInfo.roomNoIdx = 0; - m_mapLoaderInfo.pointNoIdx = 0; - } - m_mapLoaderInfo.regionIdx = idx; - } - idx++; - } - ImGui::EndCombo(); - } - - if (m_mapLoaderInfo.regionIdx != -1) { - const auto& region = gameRegions[m_mapLoaderInfo.regionIdx]; - - std::string previewMap = "None"; - if (m_mapLoaderInfo.mapIdx != -1) { - const auto& map = region.maps[m_mapLoaderInfo.mapIdx]; - previewMap = m_mapLoaderInfo.showInternalNames ? fmt::format("{} ({})", map.mapName, map.mapFile) : map.mapName; - } - - if (ImGui::BeginCombo("Select Map", previewMap.data())) { - int prevMapIdx = m_mapLoaderInfo.mapIdx; - for (int i = 0; i < region.maps.size(); ++i) { - const auto& map = region.maps[i]; - std::string label = m_mapLoaderInfo.showInternalNames ? fmt::format("{} ({})", map.mapName, map.mapFile) : map.mapName; - if (ImGui::Selectable(label.data())) { - m_mapLoaderInfo.mapIdx = i; - } - } - ImGui::EndCombo(); - if (m_mapLoaderInfo.mapIdx != prevMapIdx) { - m_mapLoaderInfo.roomNoIdx = 0; - m_mapLoaderInfo.pointNoIdx = 0; - } - } - } else { - ImGui::Text("No region selected."); - } - - if (m_mapLoaderInfo.regionIdx != -1 && m_mapLoaderInfo.mapIdx != -1) { - const auto& region = gameRegions[m_mapLoaderInfo.regionIdx]; - const auto& map = region.maps[m_mapLoaderInfo.mapIdx]; - const auto& room = map.mapRooms[m_mapLoaderInfo.roomNoIdx]; - - if (map.mapRooms.size() > 1) { - ImGui::Text("Selected Room: %2d", room.roomNo); - ImGui::SameLine(); - if (ImGui::Button("-###RoomNoIdxDec")) { - m_mapLoaderInfo.roomNoIdx--; - if (m_mapLoaderInfo.roomNoIdx < 0) { - m_mapLoaderInfo.roomNoIdx = map.mapRooms.size() - 1; - } - m_mapLoaderInfo.pointNoIdx = 0; - } - ImGui::SameLine(); - if (ImGui::Button("+###RoomNoIdxInc")) { - m_mapLoaderInfo.roomNoIdx++; - if (m_mapLoaderInfo.roomNoIdx >= map.mapRooms.size()) { - m_mapLoaderInfo.roomNoIdx = 0; - } - m_mapLoaderInfo.pointNoIdx = 0; - } - } - - constexpr int MAX_LAYER = 14; - - ImGui::Text("Selected Layer: %3d", m_mapLoaderInfo.layer); - ImGui::SameLine(); - if (ImGui::Button("-###layerDec")) { - m_mapLoaderInfo.layer--; - if (m_mapLoaderInfo.layer < -1) { - m_mapLoaderInfo.layer = MAX_LAYER; - } - } - ImGui::SameLine(); - if (ImGui::Button("+###layerInc")) { - m_mapLoaderInfo.layer++; - if (m_mapLoaderInfo.layer > MAX_LAYER) { - m_mapLoaderInfo.layer = -1; - } - } - - if (room.roomPoints.size() > 1) { - ImGui::Text("Selected Point: %3d", room.roomPoints[m_mapLoaderInfo.pointNoIdx]); - ImGui::SameLine(); - if (ImGui::Button("-###PointNoIdxDec")) { - m_mapLoaderInfo.pointNoIdx--; - if (m_mapLoaderInfo.pointNoIdx < 0) { - m_mapLoaderInfo.pointNoIdx = room.roomPoints.size() - 1; - } - } - ImGui::SameLine(); - if (ImGui::Button("+###PointNoIdxInc")) { - m_mapLoaderInfo.pointNoIdx++; - if (m_mapLoaderInfo.pointNoIdx >= room.roomPoints.size()) { - m_mapLoaderInfo.pointNoIdx = 0; - } - } - } - - if (ImGui::Button("Warp")) { - dComIfGp_setNextStage(map.mapFile, room.roomPoints[m_mapLoaderInfo.pointNoIdx], room.roomNo, m_mapLoaderInfo.layer); - } - } - - - ImGui::End(); - } - - } // namespace dusk diff --git a/src/dusk/imgui/ImGuiMenuTools.cpp b/src/dusk/imgui/ImGuiMenuTools.cpp index c7afea3202..3045e09df1 100644 --- a/src/dusk/imgui/ImGuiMenuTools.cpp +++ b/src/dusk/imgui/ImGuiMenuTools.cpp @@ -40,7 +40,6 @@ namespace dusk { ImGui::BeginDisabled(getSettings().game.speedrunMode); ImGui::MenuItem("Save Editor", hotkeys::SHOW_SAVE_EDITOR, &m_showSaveEditor); - ImGui::MenuItem("Map Loader", hotkeys::SHOW_MAP_LOADER, &m_showMapLoader); ImGui::MenuItem("State Share", hotkeys::SHOW_STATE_SHARE, &m_showStateShare); ImGui::EndDisabled(); diff --git a/src/dusk/imgui/ImGuiMenuTools.hpp b/src/dusk/imgui/ImGuiMenuTools.hpp index 50cc697d09..61fab81f79 100644 --- a/src/dusk/imgui/ImGuiMenuTools.hpp +++ b/src/dusk/imgui/ImGuiMenuTools.hpp @@ -21,7 +21,6 @@ namespace dusk { void ShowProcessManager(); void ShowHeapOverlay(); void ShowStubLog(); - void ShowMapLoader(); void ShowBloomWindow(); void ShowPlayerInfo(); void ShowAudioDebug(); @@ -43,22 +42,9 @@ namespace dusk { bool m_showStubLog = false; - bool m_showMapLoader = false; - bool m_showBloomWindow = false; bool m_showAudioDebug = false; - struct { - int mapIdx = -1; - int regionIdx = -1; - int roomNoIdx = 0; - int pointNoIdx = 0; - int roomNo = -1; - int pointNo = -1; - int spawnId = 0; - int layer = -1; - bool showInternalNames = false; - } m_mapLoaderInfo; bool m_showPlayerInfo = false; int m_playerInfoOverlayCorner = 1; // top-right diff --git a/src/dusk/ui/editor.cpp b/src/dusk/ui/editor.cpp index 469bb920e3..bd6dc3e067 100644 --- a/src/dusk/ui/editor.cpp +++ b/src/dusk/ui/editor.cpp @@ -26,6 +26,43 @@ #include namespace dusk::ui { + +Rml::String stage_option_label(const MapEntry& map, bool showInternalNames) { + return showInternalNames ? fmt::format("{} ({})", map.mapName, map.mapFile) : map.mapName; +} + +Rml::String stage_label_for_file(const Rml::String& stageFile, bool showInternalNames) { + for (const auto& region : gameRegions) { + for (const auto& map : region.maps) { + if (stageFile == map.mapFile) { + return stage_option_label(map, showInternalNames); + } + } + } + return stageFile; +} + +void populate_stage_picker(Pane& pane, std::function getStageFile, + std::function setStageFile, bool showInternalNames) { + pane.clear(); + for (const auto& region : gameRegions) { + pane.add_section(region.regionName); + for (const auto& map : region.maps) { + pane.add_button({ + .text = stage_option_label(map, showInternalNames), + .isSelected = + [getStageFile, stageFile = map.mapFile] { + return getStageFile() == stageFile; + }, + }) + .on_pressed([setStageFile, stageFile = map.mapFile] { + mDoAud_seStartMenu(kSoundItemChange); + setStageFile(stageFile); + }); + } + } +} + namespace { bool has_save_data() { @@ -155,44 +192,6 @@ bool parse_vec3(const Rml::String& value, float& x, float& y, float& z) { return *cursor == '\0'; } -Rml::String stage_option_label(const MapEntry& map) { - // TODO: option to show internal name? - // return fmt::format("{} ({})", map.mapName, map.mapFile); - return map.mapName; -} - -Rml::String stage_label_for_file(const Rml::String& stageFile) { - for (const auto& region : gameRegions) { - for (const auto& map : region.maps) { - if (stageFile == map.mapFile) { - return stage_option_label(map); - } - } - } - return stageFile; -} - -void populate_stage_picker(Pane& pane, std::function getStageFile, - std::function setStageFile) { - pane.clear(); - for (const auto& region : gameRegions) { - pane.add_section(region.regionName); - for (const auto& map : region.maps) { - pane.add_button({ - .text = stage_option_label(map), - .isSelected = - [getStageFile, stageFile = map.mapFile] { - return getStageFile() == stageFile; - }, - }) - .on_pressed([setStageFile, stageFile = map.mapFile] { - mDoAud_seStartMenu(kSoundItemChange); - setStageFile(stageFile); - }); - } - } -} - Rml::String get_player_name() { if (!has_save_data()) { return ""; diff --git a/src/dusk/ui/editor.hpp b/src/dusk/ui/editor.hpp index cec381e277..c2cbbcd834 100644 --- a/src/dusk/ui/editor.hpp +++ b/src/dusk/ui/editor.hpp @@ -1,8 +1,19 @@ #pragma once + #include "window.hpp" +struct MapEntry; + namespace dusk::ui { +class Pane; + +Rml::String stage_option_label(const MapEntry& map, bool showInternalNames = false); +Rml::String stage_label_for_file(const Rml::String& stageFile, bool showInternalNames = false); +void populate_stage_picker(Pane& pane, std::function getStageFile, + std::function setStageFile, + bool showInternalNames = false); + class EditorWindow : public Window { public: EditorWindow(); diff --git a/src/dusk/ui/menu_bar.cpp b/src/dusk/ui/menu_bar.cpp index 5ee7e68a88..1be5c7abb0 100644 --- a/src/dusk/ui/menu_bar.cpp +++ b/src/dusk/ui/menu_bar.cpp @@ -18,6 +18,7 @@ #include "modal.hpp" #include "settings.hpp" #include "ui.hpp" +#include "warp.hpp" #include "window.hpp" #include @@ -51,11 +52,9 @@ MenuBar::MenuBar() : Document(kDocumentSource), mRoot(mDocument->GetElementById( .autoSelect = false, }); mTabBar->add_tab("Settings", [this] { push(std::make_unique()); }); - // mTabBar->add_tab("Warp", [] { - // // TODO - // }); if (getSettings().backend.enableAdvancedSettings) { + mTabBar->add_tab("Warp", [this] { push(std::make_unique()); }); mTabBar->add_tab("Editor", [this] { push(std::make_unique()); }); } diff --git a/src/dusk/ui/warp.cpp b/src/dusk/ui/warp.cpp new file mode 100644 index 0000000000..efae7f1d65 --- /dev/null +++ b/src/dusk/ui/warp.cpp @@ -0,0 +1,334 @@ +#include "warp.hpp" + +#include "editor.hpp" +#include "pane.hpp" + +#include "dusk/map_loader_definitions.h" +#include "fmt/format.h" + +namespace dusk::ui { +namespace { + +constexpr int kMinLayer = -1; +constexpr int kMaxLayer = 14; + +struct WarpSelectionState { + int regionIdx = 0; + int mapIdx = 0; + int roomIdx = 0; + int pointIdx = 0; + int layer = -1; + bool showInternalNames = false; +}; + +WarpSelectionState& selection_state() { + static WarpSelectionState state; + return state; +} + +const RegionEntry* selected_region(const WarpSelectionState& state) { + if (state.regionIdx < 0 || state.regionIdx >= static_cast(gameRegions.size())) { + return nullptr; + } + return &gameRegions[state.regionIdx]; +} + +const MapEntry* selected_map(const WarpSelectionState& state) { + const auto* region = selected_region(state); + if (region == nullptr || state.mapIdx < 0 || state.mapIdx >= static_cast(region->maps.size())) { + return nullptr; + } + return ®ion->maps[state.mapIdx]; +} + +const RoomEntry* selected_room(const WarpSelectionState& state) { + const auto* map = selected_map(state); + if (map == nullptr || state.roomIdx < 0 || state.roomIdx >= static_cast(map->mapRooms.size())) { + return nullptr; + } + return &map->mapRooms[state.roomIdx]; +} + +const s16* selected_point(const WarpSelectionState& state) { + const auto* room = selected_room(state); + if (room == nullptr || state.pointIdx < 0 || + state.pointIdx >= static_cast(room->roomPoints.size())) + { + return nullptr; + } + return &room->roomPoints[state.pointIdx]; +} + +void clamp_indices(WarpSelectionState& state) { + if (gameRegions.empty()) { + state.regionIdx = -1; + state.mapIdx = -1; + state.roomIdx = -1; + state.pointIdx = -1; + state.layer = std::clamp(state.layer, kMinLayer, kMaxLayer); + return; + } + + state.regionIdx = std::clamp(state.regionIdx, 0, static_cast(gameRegions.size()) - 1); + const auto& region = gameRegions[state.regionIdx]; + if (region.maps.empty()) { + state.mapIdx = -1; + state.roomIdx = -1; + state.pointIdx = -1; + state.layer = std::clamp(state.layer, kMinLayer, kMaxLayer); + return; + } + + state.mapIdx = std::clamp(state.mapIdx, 0, static_cast(region.maps.size()) - 1); + const auto& map = region.maps[state.mapIdx]; + if (map.mapRooms.empty()) { + state.roomIdx = -1; + state.pointIdx = -1; + state.layer = std::clamp(state.layer, kMinLayer, kMaxLayer); + return; + } + + state.roomIdx = std::clamp(state.roomIdx, 0, static_cast(map.mapRooms.size()) - 1); + const auto& room = map.mapRooms[state.roomIdx]; + if (room.roomPoints.empty()) { + state.pointIdx = -1; + } else { + state.pointIdx = std::clamp(state.pointIdx, 0, static_cast(room.roomPoints.size()) - 1); + } + + state.layer = std::clamp(state.layer, kMinLayer, kMaxLayer); +} + +bool can_warp(const WarpSelectionState& state) { + return selected_point(state) != nullptr; +} + +void reset_selection(WarpSelectionState& state) { + state.roomIdx = 0; + state.pointIdx = 0; + state.layer = kMinLayer; + clamp_indices(state); +} + +void populate_map_picker(Pane& pane, WarpSelectionState& state) { + pane.clear(); + clamp_indices(state); + if (state.regionIdx < 0 || state.regionIdx >= static_cast(gameRegions.size())) { + return; + } + + pane.add_button({ + .text = "Show Internal Names", + .isSelected = [&state] { return state.showInternalNames; }, + }) + .on_pressed([&pane, &state] { + mDoAud_seStartMenu(kSoundItemChange); + state.showInternalNames = !state.showInternalNames; + populate_map_picker(pane, state); + }); + + pane.add_section("Maps"); + const auto& region = gameRegions[state.regionIdx]; + for (int i = 0; i < static_cast(region.maps.size()); ++i) { + pane.add_button({ + .text = stage_option_label(region.maps[i], state.showInternalNames), + .isSelected = [i, &state] { return state.mapIdx == i; }, + }) + .on_pressed([i, &state] { + mDoAud_seStartMenu(kSoundItemChange); + if (state.mapIdx != i) { + state.mapIdx = i; + reset_selection(state); + } + }); + } +} + +} // namespace + +WarpWindow::WarpWindow() { + add_tab("Warp", [this](Rml::Element* content) { + auto& leftPane = add_child(content, Pane::Type::Controlled); + auto& rightPane = add_child(content, Pane::Type::Uncontrolled); + auto& state = selection_state(); + clamp_indices(state); + + leftPane.add_section("Destination"); + leftPane.register_control( + leftPane.add_select_button({ + .key = "Region", + .getValue = + [&state] { + clamp_indices(state); + const auto* region = selected_region(state); + return region == nullptr ? Rml::String{"None"} : + Rml::String{region->regionName}; + }, + }), + rightPane, [&state](Pane& pane) { + pane.clear(); + for (int i = 0; i < static_cast(gameRegions.size()); ++i) { + pane.add_button({ + .text = gameRegions[i].regionName, + .isSelected = [i, &state] { return state.regionIdx == i; }, + }) + .on_pressed([i, &state] { + mDoAud_seStartMenu(kSoundItemChange); + if (state.regionIdx != i) { + state.regionIdx = i; + state.mapIdx = 0; + reset_selection(state); + } + }); + } + }); + + leftPane.register_control( + leftPane.add_select_button({ + .key = "Map", + .getValue = + [&state] { + clamp_indices(state); + const auto* map = selected_map(state); + return map == nullptr ? Rml::String{"None"} : + stage_option_label(*map, state.showInternalNames); + }, + }), + rightPane, [&state](Pane& pane) { populate_map_picker(pane, state); }); + + leftPane.register_control( + leftPane.add_select_button({ + .key = "Room", + .getValue = [&state] { + clamp_indices(state); + const auto* room = selected_room(state); + return room == nullptr ? Rml::String{"None"} : + fmt::format("{}", room->roomNo); + }, + .isDisabled = [&state] { + clamp_indices(state); + const auto* map = selected_map(state); + return map == nullptr || map->mapRooms.size() <= 1; + }, + }), + rightPane, [&state](Pane& pane) { + pane.clear(); + clamp_indices(state); + if (state.regionIdx < 0 || state.regionIdx >= static_cast(gameRegions.size())) { + return; + } + const auto& region = gameRegions[state.regionIdx]; + if (state.mapIdx < 0 || state.mapIdx >= static_cast(region.maps.size())) { + return; + } + const auto& map = region.maps[state.mapIdx]; + for (int i = 0; i < static_cast(map.mapRooms.size()); ++i) { + pane.add_button({ + .text = fmt::format("{}", map.mapRooms[i].roomNo), + .isSelected = [i, &state] { return state.roomIdx == i; }, + }) + .on_pressed([i, &state] { + mDoAud_seStartMenu(kSoundItemChange); + if (state.roomIdx != i) { + state.roomIdx = i; + state.pointIdx = 0; + clamp_indices(state); + } + }); + } + }); + + leftPane.register_control( + leftPane.add_select_button({ + .key = "Point", + .getValue = [&state] { + clamp_indices(state); + const auto* point = selected_point(state); + return point == nullptr ? Rml::String{"None"} : fmt::format("{}", *point); + }, + .isDisabled = [&state] { + clamp_indices(state); + const auto* room = selected_room(state); + return room == nullptr || room->roomPoints.size() <= 1; + }, + }), + rightPane, [&state](Pane& pane) { + pane.clear(); + clamp_indices(state); + if (state.regionIdx < 0 || state.regionIdx >= static_cast(gameRegions.size())) { + return; + } + const auto& region = gameRegions[state.regionIdx]; + if (state.mapIdx < 0 || state.mapIdx >= static_cast(region.maps.size())) { + return; + } + const auto& map = region.maps[state.mapIdx]; + if (state.roomIdx < 0 || state.roomIdx >= static_cast(map.mapRooms.size())) { + return; + } + const auto& room = map.mapRooms[state.roomIdx]; + for (int i = 0; i < static_cast(room.roomPoints.size()); ++i) { + pane.add_button({ + .text = fmt::format("{}", room.roomPoints[i]), + .isSelected = [i, &state] { return state.pointIdx == i; }, + }) + .on_pressed([i, &state] { + if (state.pointIdx != i) { + mDoAud_seStartMenu(kSoundItemChange); + state.pointIdx = i; + clamp_indices(state); + } + }); + } + }); + + leftPane.register_control( + leftPane.add_select_button({ + .key = "Layer", + .getValue = [&state] { return fmt::format("{}", state.layer); }, + }), + rightPane, [&state](Pane& pane) { + pane.clear(); + for (int layer = kMinLayer; layer <= kMaxLayer; ++layer) { + pane.add_button({ + .text = fmt::format("{}", layer), + .isSelected = [layer, &state] { return state.layer == layer; }, + }) + .on_pressed([layer, &state] { + if (state.layer != layer) { + mDoAud_seStartMenu(kSoundItemChange); + state.layer = layer; + } + }); + } + }); + + leftPane.add_section("Action"); + leftPane.register_control( + leftPane.add_button({ + .text = "Warp", + .isDisabled = [&state] { + clamp_indices(state); + return !can_warp(state); + }, + }) + .on_pressed([&state] { + clamp_indices(state); + if (!can_warp(state)) { + return; + } + + mDoAud_seStartMenu(kSoundClick); + const auto& region = gameRegions[state.regionIdx]; + const auto& map = region.maps[state.mapIdx]; + const auto& room = map.mapRooms[state.roomIdx]; + dComIfGp_setNextStage( map.mapFile, room.roomPoints[state.pointIdx], room.roomNo, state.layer); + }), + rightPane, [](Pane& pane) { + pane.clear(); + pane.add_text("Warp to the selected destination."); + }); + }); +} + +} // namespace dusk::ui diff --git a/src/dusk/ui/warp.hpp b/src/dusk/ui/warp.hpp new file mode 100644 index 0000000000..9031474b39 --- /dev/null +++ b/src/dusk/ui/warp.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "window.hpp" + +namespace dusk::ui { + +class WarpWindow : public Window { +public: + WarpWindow(); +}; + +} // namespace dusk::ui