From 203514abc71e8140ddb297eff90a343ec71d827f Mon Sep 17 00:00:00 2001 From: Kelsi Date: Tue, 17 Mar 2026 08:18:46 -0700 Subject: [PATCH] fix: correct minimap orientation and arrow direction, compact key ring UI Remove stray X-flip in minimap display shader that mirrored the map horizontally (West on right instead of East). Fix arrow rotation fallback path (missing negation) and add character-facing-relative arrow in rotateWithCamera mode. Compact key ring: 24px slots in 8-column grid, only show rows with items, hide when empty. Add Show Key Ring toggle in Settings with persistence. --- assets/shaders/minimap_display.frag.glsl | 2 +- assets/shaders/minimap_display.frag.spv | Bin 6552 -> 3808 bytes include/ui/game_screen.hpp | 1 + include/ui/inventory_screen.hpp | 3 + src/rendering/minimap.cpp | 7 +- src/ui/game_screen.cpp | 10 +++ src/ui/inventory_screen.cpp | 78 +++++++++++++---------- 7 files changed, 63 insertions(+), 38 deletions(-) diff --git a/assets/shaders/minimap_display.frag.glsl b/assets/shaders/minimap_display.frag.glsl index 3f4d42e4..476017b9 100644 --- a/assets/shaders/minimap_display.frag.glsl +++ b/assets/shaders/minimap_display.frag.glsl @@ -40,7 +40,7 @@ void main() { float cs = cos(push.rotation); float sn = sin(push.rotation); vec2 rotated = vec2(center.x * cs - center.y * sn, center.x * sn + center.y * cs); - vec2 mapUV = push.playerUV + vec2(-rotated.x, rotated.y) * push.zoomRadius * 2.0; + vec2 mapUV = push.playerUV + vec2(rotated.x, rotated.y) * push.zoomRadius * 2.0; vec4 mapColor = texture(uComposite, mapUV); diff --git a/assets/shaders/minimap_display.frag.spv b/assets/shaders/minimap_display.frag.spv index 5c0ac7b00a4d21d05c44ef1d7a25ad0cd1187c6d..f33deef2ea4598479ae64500b28003ff7ea911c6 100644 GIT binary patch literal 3808 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 Ob6yFS_q!wZyYoNZmUa~Y diff --git a/include/ui/game_screen.hpp b/include/ui/game_screen.hpp index a6ebf9c0..6c1244ae 100644 --- a/include/ui/game_screen.hpp +++ b/include/ui/game_screen.hpp @@ -192,6 +192,7 @@ private: bool pendingMinimapNpcDots = false; bool pendingShowLatencyMeter = true; bool pendingSeparateBags = true; + bool pendingShowKeyring = true; bool pendingAutoLoot = false; // Keybinding customization diff --git a/include/ui/inventory_screen.hpp b/include/ui/inventory_screen.hpp index 65ef41c9..b9c30c6c 100644 --- a/include/ui/inventory_screen.hpp +++ b/include/ui/inventory_screen.hpp @@ -39,6 +39,8 @@ public: bool isSeparateBags() const { return separateBags_; } void toggleCompactBags() { compactBags_ = !compactBags_; } bool isCompactBags() const { return compactBags_; } + void setShowKeyring(bool show) { showKeyring_ = show; } + bool isShowKeyring() const { return showKeyring_; } bool isBackpackOpen() const { return backpackOpen_; } bool isBagOpen(int idx) const { return idx >= 0 && idx < 4 ? bagOpen_[idx] : false; } @@ -79,6 +81,7 @@ private: bool bKeyWasDown = false; bool separateBags_ = true; bool compactBags_ = false; + bool showKeyring_ = true; bool backpackOpen_ = false; std::array bagOpen_{}; bool cKeyWasDown = false; diff --git a/src/rendering/minimap.cpp b/src/rendering/minimap.cpp index 0f44869b..f47264d0 100644 --- a/src/rendering/minimap.cpp +++ b/src/rendering/minimap.cpp @@ -513,14 +513,15 @@ void Minimap::render(VkCommandBuffer cmd, const Camera& playerCamera, float arrowRotation = 0.0f; if (!rotateWithCamera) { - // Prefer authoritative orientation if provided. This value is expected - // to already match minimap shader rotation convention. if (hasPlayerOrientation) { arrowRotation = playerOrientation; } else { glm::vec3 fwd = playerCamera.getForward(); - arrowRotation = std::atan2(-fwd.x, fwd.y); + arrowRotation = -std::atan2(-fwd.x, fwd.y); } + } else if (hasPlayerOrientation) { + // Show character facing relative to the rotated map + arrowRotation = playerOrientation + rotation; } MinimapDisplayPush push{}; diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index eab06871..64a6155e 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -15326,6 +15326,10 @@ void GameScreen::renderSettingsWindow() { inventoryScreen.setSeparateBags(pendingSeparateBags); saveSettings(); } + if (ImGui::Checkbox("Show Key Ring", &pendingShowKeyring)) { + inventoryScreen.setShowKeyring(pendingShowKeyring); + saveSettings(); + } ImGui::Spacing(); ImGui::Separator(); @@ -15341,6 +15345,8 @@ void GameScreen::renderSettingsWindow() { pendingMinimapNpcDots = false; pendingSeparateBags = true; inventoryScreen.setSeparateBags(true); + pendingShowKeyring = true; + inventoryScreen.setShowKeyring(true); uiOpacity_ = 0.65f; minimapRotate_ = false; minimapSquare_ = false; @@ -17244,6 +17250,7 @@ void GameScreen::saveSettings() { out << "show_dps_meter=" << (showDPSMeter_ ? 1 : 0) << "\n"; out << "show_cooldown_tracker=" << (showCooldownTracker_ ? 1 : 0) << "\n"; out << "separate_bags=" << (pendingSeparateBags ? 1 : 0) << "\n"; + out << "show_keyring=" << (pendingShowKeyring ? 1 : 0) << "\n"; out << "action_bar_scale=" << pendingActionBarScale << "\n"; out << "nameplate_scale=" << nameplateScale_ << "\n"; out << "show_action_bar2=" << (pendingShowActionBar2 ? 1 : 0) << "\n"; @@ -17365,6 +17372,9 @@ void GameScreen::loadSettings() { } else if (key == "separate_bags") { pendingSeparateBags = (std::stoi(val) != 0); inventoryScreen.setSeparateBags(pendingSeparateBags); + } else if (key == "show_keyring") { + pendingShowKeyring = (std::stoi(val) != 0); + inventoryScreen.setShowKeyring(pendingShowKeyring); } else if (key == "action_bar_scale") { pendingActionBarScale = std::clamp(std::stof(val), 0.5f, 1.5f); } else if (key == "nameplate_scale") { diff --git a/src/ui/inventory_screen.cpp b/src/ui/inventory_screen.cpp index 3d4b0c17..0c826c7d 100644 --- a/src/ui/inventory_screen.cpp +++ b/src/ui/inventory_screen.cpp @@ -1069,20 +1069,29 @@ void InventoryScreen::renderBagWindow(const char* title, bool& isOpen, ImGui::PopID(); } - if (bagIndex < 0) { - ImGui::Spacing(); - ImGui::Separator(); - ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.0f, 1.0f), "Keyring"); - for (int i = 0; i < inventory.getKeyringSize(); ++i) { - if (i % columns != 0) ImGui::SameLine(); - const auto& slot = inventory.getKeyringSlot(i); - char id[32]; - snprintf(id, sizeof(id), "##skr_%d", i); - ImGui::PushID(id); - // Keyring is display-only for now. - renderItemSlot(inventory, slot, slotSize, nullptr, - SlotKind::BACKPACK, -1, game::EquipSlot::NUM_SLOTS); - ImGui::PopID(); + if (bagIndex < 0 && showKeyring_) { + constexpr float keySlotSize = 24.0f; + constexpr int keyCols = 8; + // Only show rows that contain items (round up to full row) + int lastOccupied = -1; + for (int i = inventory.getKeyringSize() - 1; i >= 0; --i) { + if (!inventory.getKeyringSlot(i).empty()) { lastOccupied = i; break; } + } + int visibleSlots = (lastOccupied < 0) ? 0 : ((lastOccupied / keyCols) + 1) * keyCols; + if (visibleSlots > 0) { + ImGui::Spacing(); + ImGui::Separator(); + ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.0f, 1.0f), "Keyring"); + for (int i = 0; i < visibleSlots; ++i) { + if (i % keyCols != 0) ImGui::SameLine(); + const auto& slot = inventory.getKeyringSlot(i); + char id[32]; + snprintf(id, sizeof(id), "##skr_%d", i); + ImGui::PushID(id); + renderItemSlot(inventory, slot, keySlotSize, nullptr, + SlotKind::BACKPACK, -1, game::EquipSlot::NUM_SLOTS); + ImGui::PopID(); + } } } @@ -2042,27 +2051,28 @@ void InventoryScreen::renderBackpackPanel(game::Inventory& inventory, bool colla } } - bool keyringHasAnyItems = false; - for (int i = 0; i < inventory.getKeyringSize(); ++i) { - if (!inventory.getKeyringSlot(i).empty()) { - keyringHasAnyItems = true; - break; + if (showKeyring_) { + constexpr float keySlotSize = 24.0f; + constexpr int keyCols = 8; + int lastOccupied = -1; + for (int i = inventory.getKeyringSize() - 1; i >= 0; --i) { + if (!inventory.getKeyringSlot(i).empty()) { lastOccupied = i; break; } } - } - if (!collapseEmptySections || keyringHasAnyItems) { - ImGui::Spacing(); - ImGui::Separator(); - ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.0f, 1.0f), "Keyring"); - for (int i = 0; i < inventory.getKeyringSize(); ++i) { - if (i % columns != 0) ImGui::SameLine(); - const auto& slot = inventory.getKeyringSlot(i); - char sid[32]; - snprintf(sid, sizeof(sid), "##keyring_%d", i); - ImGui::PushID(sid); - // Keyring is display-only for now. - renderItemSlot(inventory, slot, slotSize, nullptr, - SlotKind::BACKPACK, -1, game::EquipSlot::NUM_SLOTS); - ImGui::PopID(); + int visibleSlots = (lastOccupied < 0) ? 0 : ((lastOccupied / keyCols) + 1) * keyCols; + if (visibleSlots > 0) { + ImGui::Spacing(); + ImGui::Separator(); + ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.0f, 1.0f), "Keyring"); + for (int i = 0; i < visibleSlots; ++i) { + if (i % keyCols != 0) ImGui::SameLine(); + const auto& slot = inventory.getKeyringSlot(i); + char sid[32]; + snprintf(sid, sizeof(sid), "##keyring_%d", i); + ImGui::PushID(sid); + renderItemSlot(inventory, slot, keySlotSize, nullptr, + SlotKind::BACKPACK, -1, game::EquipSlot::NUM_SLOTS); + ImGui::PopID(); + } } } }