diff --git a/include/ui/game_screen.hpp b/include/ui/game_screen.hpp index d2b303c8..8c57b1c1 100644 --- a/include/ui/game_screen.hpp +++ b/include/ui/game_screen.hpp @@ -112,6 +112,9 @@ private: bool pendingSeparateBags = true; bool pendingAutoLoot = false; bool pendingUseOriginalSoundtrack = true; + bool pendingShowActionBar2 = true; // Show second action bar above main bar + float pendingActionBar2OffsetX = 0.0f; // Horizontal offset from default center position + float pendingActionBar2OffsetY = 0.0f; // Vertical offset from default (above bar 1) int pendingGroundClutterDensity = 100; int pendingAntiAliasing = 0; // 0=Off, 1=2x, 2=4x, 3=8x bool pendingNormalMapping = true; // on by default diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 8e5bc602..dbdf296c 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -8086,6 +8086,9 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { LOG_WARNING("PLAYER_BYTES_2 (CREATE): raw=0x", std::hex, val, std::dec, " bankBagSlots=", static_cast(bankBagSlots)); inventory.setPurchasedBankBagSlots(bankBagSlots); + // Byte 3 (bits 24-31): REST_STATE — bit 0 set means in inn/city + uint8_t restStateByte = static_cast((val >> 24) & 0xFF); + isResting_ = (restStateByte & 0x01) != 0; } // Do not synthesize quest-log entries from raw update-field slots. // Slot layouts differ on some classic-family realms and can produce @@ -8354,6 +8357,7 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { bool slotsChanged = false; const uint16_t ufPlayerXp = fieldIndex(UF::PLAYER_XP); const uint16_t ufPlayerNextXp = fieldIndex(UF::PLAYER_NEXT_LEVEL_XP); + const uint16_t ufPlayerRestedXpV = fieldIndex(UF::PLAYER_REST_STATE_EXPERIENCE); const uint16_t ufPlayerLevel = fieldIndex(UF::UNIT_FIELD_LEVEL); const uint16_t ufCoinage = fieldIndex(UF::PLAYER_FIELD_COINAGE); const uint16_t ufPlayerFlags = fieldIndex(UF::PLAYER_FLAGS); @@ -8368,6 +8372,9 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { playerNextLevelXp_ = val; LOG_DEBUG("Next level XP updated: ", val); } + else if (ufPlayerRestedXpV != 0xFFFF && key == ufPlayerRestedXpV) { + playerRestedXp_ = val; + } else if (key == ufPlayerLevel) { serverPlayerLevel_ = val; LOG_DEBUG("Level updated: ", val); @@ -8390,6 +8397,9 @@ void GameHandler::handleUpdateObject(network::Packet& packet) { LOG_WARNING("PLAYER_BYTES_2 (VALUES): raw=0x", std::hex, val, std::dec, " bankBagSlots=", static_cast(bankBagSlots)); inventory.setPurchasedBankBagSlots(bankBagSlots); + // Byte 3 (bits 24-31): REST_STATE — bit 0 set means in inn/city + uint8_t restStateByte = static_cast((val >> 24) & 0xFF); + isResting_ = (restStateByte & 0x01) != 0; } else if (key == ufPlayerFlags) { constexpr uint32_t PLAYER_FLAGS_GHOST = 0x00000010; diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 2e11cfe9..f55e293f 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -4137,13 +4137,14 @@ void GameScreen::renderActionBar(game::GameHandler& gameHandler) { }; // Bar 2 (slots 12-23) — only show if at least one slot is populated - { + if (pendingShowActionBar2) { bool bar2HasContent = false; for (int i = 0; i < game::GameHandler::SLOTS_PER_BAR; ++i) if (!bar[game::GameHandler::SLOTS_PER_BAR + i].isEmpty()) { bar2HasContent = true; break; } - float bar2Y = barY - barH - 2.0f; - ImGui::SetNextWindowPos(ImVec2(barX, bar2Y), ImGuiCond_Always); + float bar2X = barX + pendingActionBar2OffsetX; + float bar2Y = barY - barH - 2.0f + pendingActionBar2OffsetY; + ImGui::SetNextWindowPos(ImVec2(bar2X, bar2Y), ImGuiCond_Always); ImGui::SetNextWindowSize(ImVec2(barW, barH), ImGuiCond_Always); ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 4.0f); ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(padding, padding)); @@ -7995,6 +7996,44 @@ void GameScreen::renderSettingsWindow() { ImGui::EndTabItem(); } + // ============================================================ + // INTERFACE TAB + // ============================================================ + if (ImGui::BeginTabItem("Interface")) { + ImGui::Spacing(); + ImGui::BeginChild("InterfaceSettings", ImVec2(0, 360), true); + + ImGui::SeparatorText("Action Bars"); + ImGui::Spacing(); + + if (ImGui::Checkbox("Show Second Action Bar", &pendingShowActionBar2)) { + saveSettings(); + } + ImGui::SameLine(); + ImGui::TextDisabled("(Shift+1 through Shift+=)"); + + if (pendingShowActionBar2) { + ImGui::Spacing(); + ImGui::TextUnformatted("Second Bar Position Offset"); + ImGui::SetNextItemWidth(160.0f); + if (ImGui::SliderFloat("Horizontal##bar2x", &pendingActionBar2OffsetX, -600.0f, 600.0f, "%.0f px")) { + saveSettings(); + } + ImGui::SetNextItemWidth(160.0f); + if (ImGui::SliderFloat("Vertical##bar2y", &pendingActionBar2OffsetY, -400.0f, 400.0f, "%.0f px")) { + saveSettings(); + } + if (ImGui::Button("Reset Position##bar2")) { + pendingActionBar2OffsetX = 0.0f; + pendingActionBar2OffsetY = 0.0f; + saveSettings(); + } + } + + ImGui::EndChild(); + ImGui::EndTabItem(); + } + // ============================================================ // AUDIO TAB // ============================================================ @@ -9054,6 +9093,9 @@ void GameScreen::saveSettings() { out << "minimap_square=" << (pendingMinimapSquare ? 1 : 0) << "\n"; out << "minimap_npc_dots=" << (pendingMinimapNpcDots ? 1 : 0) << "\n"; out << "separate_bags=" << (pendingSeparateBags ? 1 : 0) << "\n"; + out << "show_action_bar2=" << (pendingShowActionBar2 ? 1 : 0) << "\n"; + out << "action_bar2_offset_x=" << pendingActionBar2OffsetX << "\n"; + out << "action_bar2_offset_y=" << pendingActionBar2OffsetY << "\n"; // Audio out << "sound_muted=" << (soundMuted_ ? 1 : 0) << "\n"; @@ -9143,6 +9185,12 @@ void GameScreen::loadSettings() { } else if (key == "separate_bags") { pendingSeparateBags = (std::stoi(val) != 0); inventoryScreen.setSeparateBags(pendingSeparateBags); + } else if (key == "show_action_bar2") { + pendingShowActionBar2 = (std::stoi(val) != 0); + } else if (key == "action_bar2_offset_x") { + pendingActionBar2OffsetX = std::clamp(std::stof(val), -600.0f, 600.0f); + } else if (key == "action_bar2_offset_y") { + pendingActionBar2OffsetY = std::clamp(std::stof(val), -400.0f, 400.0f); } // Audio else if (key == "sound_muted") {