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.
This commit is contained in:
Kelsi 2026-03-17 08:18:46 -07:00
parent e38324619e
commit 203514abc7
7 changed files with 63 additions and 38 deletions

View file

@ -40,7 +40,7 @@ void main() {
float cs = cos(push.rotation); float cs = cos(push.rotation);
float sn = sin(push.rotation); float sn = sin(push.rotation);
vec2 rotated = vec2(center.x * cs - center.y * sn, center.x * sn + center.y * cs); 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); vec4 mapColor = texture(uComposite, mapUV);

View file

@ -192,6 +192,7 @@ private:
bool pendingMinimapNpcDots = false; bool pendingMinimapNpcDots = false;
bool pendingShowLatencyMeter = true; bool pendingShowLatencyMeter = true;
bool pendingSeparateBags = true; bool pendingSeparateBags = true;
bool pendingShowKeyring = true;
bool pendingAutoLoot = false; bool pendingAutoLoot = false;
// Keybinding customization // Keybinding customization

View file

@ -39,6 +39,8 @@ public:
bool isSeparateBags() const { return separateBags_; } bool isSeparateBags() const { return separateBags_; }
void toggleCompactBags() { compactBags_ = !compactBags_; } void toggleCompactBags() { compactBags_ = !compactBags_; }
bool isCompactBags() const { return compactBags_; } bool isCompactBags() const { return compactBags_; }
void setShowKeyring(bool show) { showKeyring_ = show; }
bool isShowKeyring() const { return showKeyring_; }
bool isBackpackOpen() const { return backpackOpen_; } bool isBackpackOpen() const { return backpackOpen_; }
bool isBagOpen(int idx) const { return idx >= 0 && idx < 4 ? bagOpen_[idx] : false; } bool isBagOpen(int idx) const { return idx >= 0 && idx < 4 ? bagOpen_[idx] : false; }
@ -79,6 +81,7 @@ private:
bool bKeyWasDown = false; bool bKeyWasDown = false;
bool separateBags_ = true; bool separateBags_ = true;
bool compactBags_ = false; bool compactBags_ = false;
bool showKeyring_ = true;
bool backpackOpen_ = false; bool backpackOpen_ = false;
std::array<bool, 4> bagOpen_{}; std::array<bool, 4> bagOpen_{};
bool cKeyWasDown = false; bool cKeyWasDown = false;

View file

@ -513,14 +513,15 @@ void Minimap::render(VkCommandBuffer cmd, const Camera& playerCamera,
float arrowRotation = 0.0f; float arrowRotation = 0.0f;
if (!rotateWithCamera) { if (!rotateWithCamera) {
// Prefer authoritative orientation if provided. This value is expected
// to already match minimap shader rotation convention.
if (hasPlayerOrientation) { if (hasPlayerOrientation) {
arrowRotation = playerOrientation; arrowRotation = playerOrientation;
} else { } else {
glm::vec3 fwd = playerCamera.getForward(); 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{}; MinimapDisplayPush push{};

View file

@ -15326,6 +15326,10 @@ void GameScreen::renderSettingsWindow() {
inventoryScreen.setSeparateBags(pendingSeparateBags); inventoryScreen.setSeparateBags(pendingSeparateBags);
saveSettings(); saveSettings();
} }
if (ImGui::Checkbox("Show Key Ring", &pendingShowKeyring)) {
inventoryScreen.setShowKeyring(pendingShowKeyring);
saveSettings();
}
ImGui::Spacing(); ImGui::Spacing();
ImGui::Separator(); ImGui::Separator();
@ -15341,6 +15345,8 @@ void GameScreen::renderSettingsWindow() {
pendingMinimapNpcDots = false; pendingMinimapNpcDots = false;
pendingSeparateBags = true; pendingSeparateBags = true;
inventoryScreen.setSeparateBags(true); inventoryScreen.setSeparateBags(true);
pendingShowKeyring = true;
inventoryScreen.setShowKeyring(true);
uiOpacity_ = 0.65f; uiOpacity_ = 0.65f;
minimapRotate_ = false; minimapRotate_ = false;
minimapSquare_ = false; minimapSquare_ = false;
@ -17244,6 +17250,7 @@ void GameScreen::saveSettings() {
out << "show_dps_meter=" << (showDPSMeter_ ? 1 : 0) << "\n"; out << "show_dps_meter=" << (showDPSMeter_ ? 1 : 0) << "\n";
out << "show_cooldown_tracker=" << (showCooldownTracker_ ? 1 : 0) << "\n"; out << "show_cooldown_tracker=" << (showCooldownTracker_ ? 1 : 0) << "\n";
out << "separate_bags=" << (pendingSeparateBags ? 1 : 0) << "\n"; out << "separate_bags=" << (pendingSeparateBags ? 1 : 0) << "\n";
out << "show_keyring=" << (pendingShowKeyring ? 1 : 0) << "\n";
out << "action_bar_scale=" << pendingActionBarScale << "\n"; out << "action_bar_scale=" << pendingActionBarScale << "\n";
out << "nameplate_scale=" << nameplateScale_ << "\n"; out << "nameplate_scale=" << nameplateScale_ << "\n";
out << "show_action_bar2=" << (pendingShowActionBar2 ? 1 : 0) << "\n"; out << "show_action_bar2=" << (pendingShowActionBar2 ? 1 : 0) << "\n";
@ -17365,6 +17372,9 @@ void GameScreen::loadSettings() {
} else if (key == "separate_bags") { } else if (key == "separate_bags") {
pendingSeparateBags = (std::stoi(val) != 0); pendingSeparateBags = (std::stoi(val) != 0);
inventoryScreen.setSeparateBags(pendingSeparateBags); inventoryScreen.setSeparateBags(pendingSeparateBags);
} else if (key == "show_keyring") {
pendingShowKeyring = (std::stoi(val) != 0);
inventoryScreen.setShowKeyring(pendingShowKeyring);
} else if (key == "action_bar_scale") { } else if (key == "action_bar_scale") {
pendingActionBarScale = std::clamp(std::stof(val), 0.5f, 1.5f); pendingActionBarScale = std::clamp(std::stof(val), 0.5f, 1.5f);
} else if (key == "nameplate_scale") { } else if (key == "nameplate_scale") {

View file

@ -1069,20 +1069,29 @@ void InventoryScreen::renderBagWindow(const char* title, bool& isOpen,
ImGui::PopID(); ImGui::PopID();
} }
if (bagIndex < 0) { if (bagIndex < 0 && showKeyring_) {
ImGui::Spacing(); constexpr float keySlotSize = 24.0f;
ImGui::Separator(); constexpr int keyCols = 8;
ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.0f, 1.0f), "Keyring"); // Only show rows that contain items (round up to full row)
for (int i = 0; i < inventory.getKeyringSize(); ++i) { int lastOccupied = -1;
if (i % columns != 0) ImGui::SameLine(); for (int i = inventory.getKeyringSize() - 1; i >= 0; --i) {
const auto& slot = inventory.getKeyringSlot(i); if (!inventory.getKeyringSlot(i).empty()) { lastOccupied = i; break; }
char id[32]; }
snprintf(id, sizeof(id), "##skr_%d", i); int visibleSlots = (lastOccupied < 0) ? 0 : ((lastOccupied / keyCols) + 1) * keyCols;
ImGui::PushID(id); if (visibleSlots > 0) {
// Keyring is display-only for now. ImGui::Spacing();
renderItemSlot(inventory, slot, slotSize, nullptr, ImGui::Separator();
SlotKind::BACKPACK, -1, game::EquipSlot::NUM_SLOTS); ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.0f, 1.0f), "Keyring");
ImGui::PopID(); 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; if (showKeyring_) {
for (int i = 0; i < inventory.getKeyringSize(); ++i) { constexpr float keySlotSize = 24.0f;
if (!inventory.getKeyringSlot(i).empty()) { constexpr int keyCols = 8;
keyringHasAnyItems = true; int lastOccupied = -1;
break; 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 (!collapseEmptySections || keyringHasAnyItems) { if (visibleSlots > 0) {
ImGui::Spacing(); ImGui::Spacing();
ImGui::Separator(); ImGui::Separator();
ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.0f, 1.0f), "Keyring"); ImGui::TextColored(ImVec4(0.8f, 0.8f, 0.0f, 1.0f), "Keyring");
for (int i = 0; i < inventory.getKeyringSize(); ++i) { for (int i = 0; i < visibleSlots; ++i) {
if (i % columns != 0) ImGui::SameLine(); if (i % keyCols != 0) ImGui::SameLine();
const auto& slot = inventory.getKeyringSlot(i); const auto& slot = inventory.getKeyringSlot(i);
char sid[32]; char sid[32];
snprintf(sid, sizeof(sid), "##keyring_%d", i); snprintf(sid, sizeof(sid), "##keyring_%d", i);
ImGui::PushID(sid); ImGui::PushID(sid);
// Keyring is display-only for now. renderItemSlot(inventory, slot, keySlotSize, nullptr,
renderItemSlot(inventory, slot, slotSize, nullptr, SlotKind::BACKPACK, -1, game::EquipSlot::NUM_SLOTS);
SlotKind::BACKPACK, -1, game::EquipSlot::NUM_SLOTS); ImGui::PopID();
ImGui::PopID(); }
} }
} }
} }