diff --git a/include/addons/addon_manager.hpp b/include/addons/addon_manager.hpp index 681d3822..be4a6a89 100644 --- a/include/addons/addon_manager.hpp +++ b/include/addons/addon_manager.hpp @@ -27,14 +27,9 @@ public: void saveAllSavedVariables(); - /// Re-initialize the Lua VM and reload all addons (used by /reload). - bool reload(); - private: LuaEngine luaEngine_; std::vector addons_; - game::GameHandler* gameHandler_ = nullptr; - std::string addonsPath_; bool loadAddon(const TocFile& addon); std::string getSavedVariablesPath(const TocFile& addon) const; diff --git a/include/audio/ui_sound_manager.hpp b/include/audio/ui_sound_manager.hpp index 7a9a66b8..6423d460 100644 --- a/include/audio/ui_sound_manager.hpp +++ b/include/audio/ui_sound_manager.hpp @@ -78,9 +78,6 @@ public: // Chat notifications void playWhisperReceived(); - // Minimap ping - void playMinimapPing(); - private: struct UISample { std::string path; @@ -129,7 +126,6 @@ private: std::vector selectTargetSounds_; std::vector deselectTargetSounds_; std::vector whisperSounds_; - std::vector minimapPingSounds_; // State tracking float volumeScale_ = 1.0f; diff --git a/include/game/game_handler.hpp b/include/game/game_handler.hpp index 7c4e0918..1c2907fd 100644 --- a/include/game/game_handler.hpp +++ b/include/game/game_handler.hpp @@ -294,14 +294,6 @@ public: return spellIconPathResolver_ ? spellIconPathResolver_(spellId) : std::string{}; } - // Random property/suffix name resolver: randomPropertyId -> suffix name (e.g., "of the Eagle") - // Positive IDs → ItemRandomProperties.dbc; negative IDs → ItemRandomSuffix.dbc (abs value) - using RandomPropertyNameResolver = std::function; - void setRandomPropertyNameResolver(RandomPropertyNameResolver r) { randomPropertyNameResolver_ = std::move(r); } - std::string getRandomPropertyName(int32_t id) const { - return randomPropertyNameResolver_ ? randomPropertyNameResolver_(id) : std::string{}; - } - // Emote animation callback: (entityGuid, animationId) using EmoteAnimCallback = std::function; void setEmoteAnimCallback(EmoteAnimCallback cb) { emoteAnimCallback_ = std::move(cb); } @@ -876,7 +868,6 @@ public: // 400ms spell-queue window: next spell to cast when current finishes uint32_t getQueuedSpellId() const { return queuedSpellId_; } - void cancelQueuedSpell() { queuedSpellId_ = 0; queuedSpellTarget_ = 0; } // Unit cast state (tracked per GUID for target frame + boss frames) struct UnitCastState { @@ -1451,7 +1442,6 @@ public: // roles bitmask: 0x02=tank, 0x04=healer, 0x08=dps; pass LFGDungeonEntry ID void lfgJoin(uint32_t dungeonId, uint8_t roles); void lfgLeave(); - void lfgSetRoles(uint8_t roles); void lfgAcceptProposal(uint32_t proposalId, bool accept); void lfgSetBootVote(bool vote); void lfgTeleport(bool toLfgDungeon = true); @@ -2662,7 +2652,6 @@ private: AddonChatCallback addonChatCallback_; AddonEventCallback addonEventCallback_; SpellIconPathResolver spellIconPathResolver_; - RandomPropertyNameResolver randomPropertyNameResolver_; EmoteAnimCallback emoteAnimCallback_; // Targeting diff --git a/include/rendering/loading_screen.hpp b/include/rendering/loading_screen.hpp index a0ed13a5..afd134b9 100644 --- a/include/rendering/loading_screen.hpp +++ b/include/rendering/loading_screen.hpp @@ -30,7 +30,6 @@ public: void setProgress(float progress) { loadProgress = progress; } void setStatus(const std::string& status) { statusText = status; } - void setZoneName(const std::string& name) { zoneName = name; } // Must be set before initialize() for Vulkan texture upload void setVkContext(VkContext* ctx) { vkCtx = ctx; } @@ -54,7 +53,6 @@ private: float loadProgress = 0.0f; std::string statusText = "Loading..."; - std::string zoneName; int imageWidth = 0; int imageHeight = 0; diff --git a/include/rendering/weather.hpp b/include/rendering/weather.hpp index 3349526f..b92c963d 100644 --- a/include/rendering/weather.hpp +++ b/include/rendering/weather.hpp @@ -28,8 +28,7 @@ public: enum class Type { NONE, RAIN, - SNOW, - STORM + SNOW }; Weather(); diff --git a/include/ui/game_screen.hpp b/include/ui/game_screen.hpp index 5391978f..cd200126 100644 --- a/include/ui/game_screen.hpp +++ b/include/ui/game_screen.hpp @@ -388,7 +388,6 @@ private: void renderBgInvitePopup(game::GameHandler& gameHandler); void renderBfMgrInvitePopup(game::GameHandler& gameHandler); void renderLfgProposalPopup(game::GameHandler& gameHandler); - void renderLfgRoleCheckPopup(game::GameHandler& gameHandler); void renderChatBubbles(game::GameHandler& gameHandler); void renderMailWindow(game::GameHandler& gameHandler); void renderMailComposeWindow(game::GameHandler& gameHandler); diff --git a/src/addons/addon_manager.cpp b/src/addons/addon_manager.cpp index e826097f..60593792 100644 --- a/src/addons/addon_manager.cpp +++ b/src/addons/addon_manager.cpp @@ -11,14 +11,12 @@ AddonManager::AddonManager() = default; AddonManager::~AddonManager() { shutdown(); } bool AddonManager::initialize(game::GameHandler* gameHandler) { - gameHandler_ = gameHandler; if (!luaEngine_.initialize()) return false; luaEngine_.setGameHandler(gameHandler); return true; } void AddonManager::scanAddons(const std::string& addonsPath) { - addonsPath_ = addonsPath; addons_.clear(); std::error_code ec; @@ -123,26 +121,6 @@ void AddonManager::saveAllSavedVariables() { } } -bool AddonManager::reload() { - LOG_INFO("AddonManager: reloading all addons..."); - saveAllSavedVariables(); - addons_.clear(); - luaEngine_.shutdown(); - - if (!luaEngine_.initialize()) { - LOG_ERROR("AddonManager: failed to reinitialize Lua VM during reload"); - return false; - } - luaEngine_.setGameHandler(gameHandler_); - - if (!addonsPath_.empty()) { - scanAddons(addonsPath_); - loadAllAddons(); - } - LOG_INFO("AddonManager: reload complete"); - return true; -} - void AddonManager::shutdown() { saveAllSavedVariables(); addons_.clear(); diff --git a/src/addons/lua_engine.cpp b/src/addons/lua_engine.cpp index e75d5359..8686ef3c 100644 --- a/src/addons/lua_engine.cpp +++ b/src/addons/lua_engine.cpp @@ -59,6 +59,15 @@ static int lua_wow_message(lua_State* L) { return lua_wow_print(L); } +// Helper: get player Unit from game handler +static game::Unit* getPlayerUnit(lua_State* L) { + auto* gh = getGameHandler(L); + if (!gh) return nullptr; + auto entity = gh->getEntityManager().getEntity(gh->getPlayerGuid()); + if (!entity) return nullptr; + return dynamic_cast(entity.get()); +} + // Helper: resolve WoW unit IDs to GUID static uint64_t resolveUnitGuid(game::GameHandler* gh, const std::string& uid) { if (uid == "player") return gh->getPlayerGuid(); @@ -379,50 +388,10 @@ static int lua_UnitAura(lua_State* L, bool wantBuff) { if (!iconPath.empty()) lua_pushstring(L, iconPath.c_str()); else lua_pushnil(L); // icon texture path lua_pushnumber(L, aura.charges); // count - // debuffType: resolve from Spell.dbc dispel type - { - uint8_t dt = gh->getSpellDispelType(aura.spellId); - switch (dt) { - case 1: lua_pushstring(L, "Magic"); break; - case 2: lua_pushstring(L, "Curse"); break; - case 3: lua_pushstring(L, "Disease"); break; - case 4: lua_pushstring(L, "Poison"); break; - default: lua_pushnil(L); break; - } - } + lua_pushnil(L); // debuffType lua_pushnumber(L, aura.maxDurationMs > 0 ? aura.maxDurationMs / 1000.0 : 0); // duration - // expirationTime: GetTime() + remaining seconds (so addons can compute countdown) - if (aura.durationMs > 0) { - uint64_t auraNowMs = static_cast( - std::chrono::duration_cast( - std::chrono::steady_clock::now().time_since_epoch()).count()); - int32_t remMs = aura.getRemainingMs(auraNowMs); - // GetTime epoch = steady_clock relative to engine start - static auto sStart = std::chrono::steady_clock::now(); - double nowSec = std::chrono::duration( - std::chrono::steady_clock::now() - sStart).count(); - lua_pushnumber(L, nowSec + remMs / 1000.0); - } else { - lua_pushnumber(L, 0); // permanent aura - } - // caster: return unit ID string if caster is known - if (aura.casterGuid != 0) { - if (aura.casterGuid == gh->getPlayerGuid()) - lua_pushstring(L, "player"); - else if (aura.casterGuid == gh->getTargetGuid()) - lua_pushstring(L, "target"); - else if (aura.casterGuid == gh->getFocusGuid()) - lua_pushstring(L, "focus"); - else if (aura.casterGuid == gh->getPetGuid()) - lua_pushstring(L, "pet"); - else { - char cBuf[32]; - snprintf(cBuf, sizeof(cBuf), "0x%016llX", (unsigned long long)aura.casterGuid); - lua_pushstring(L, cBuf); - } - } else { - lua_pushnil(L); - } + lua_pushnumber(L, 0); // expirationTime (would need absolute time) + lua_pushnil(L); // caster lua_pushboolean(L, 0); // isStealable lua_pushboolean(L, 0); // shouldConsolidate lua_pushnumber(L, aura.spellId); // spellId @@ -436,17 +405,6 @@ static int lua_UnitAura(lua_State* L, bool wantBuff) { static int lua_UnitBuff(lua_State* L) { return lua_UnitAura(L, true); } static int lua_UnitDebuff(lua_State* L) { return lua_UnitAura(L, false); } -// UnitAura(unit, index, filter) — generic aura query with filter string -// filter: "HELPFUL" = buffs, "HARMFUL" = debuffs, "PLAYER" = cast by player, -// "HELPFUL|PLAYER" = buffs cast by player, etc. -static int lua_UnitAuraGeneric(lua_State* L) { - const char* filter = luaL_optstring(L, 3, "HELPFUL"); - std::string f(filter ? filter : "HELPFUL"); - for (char& c : f) c = static_cast(std::toupper(static_cast(c))); - bool wantBuff = (f.find("HARMFUL") == std::string::npos); - return lua_UnitAura(L, wantBuff); -} - // --- Action API --- static int lua_SendChatMessage(lua_State* L) { @@ -534,20 +492,9 @@ static int lua_GetSpellCooldown(lua_State* L) { } } float cd = gh->getSpellCooldown(spellId); - // WoW returns (start, duration, enabled) where remaining = start + duration - GetTime() - // Compute start = GetTime() - elapsed, duration = total cooldown - static auto sStart = std::chrono::steady_clock::now(); - double nowSec = std::chrono::duration( - std::chrono::steady_clock::now() - sStart).count(); - if (cd > 0.01f) { - lua_pushnumber(L, nowSec); // start (approximate — we don't track exact start) - lua_pushnumber(L, cd); // duration (remaining, used as total for simplicity) - } else { - lua_pushnumber(L, 0); // not on cooldown - lua_pushnumber(L, 0); - } - lua_pushnumber(L, 1); // enabled (always 1 — spell is usable) - return 3; + lua_pushnumber(L, 0); // start time (not tracked precisely, return 0) + lua_pushnumber(L, cd); // duration remaining + return 2; } static int lua_HasTarget(lua_State* L) { @@ -694,29 +641,6 @@ static int lua_GetCurrentMapAreaID(lua_State* L) { return 1; } -// GetZoneText() / GetRealZoneText() → current zone name -static int lua_GetZoneText(lua_State* L) { - auto* gh = getGameHandler(L); - if (!gh) { lua_pushstring(L, ""); return 1; } - uint32_t zoneId = gh->getWorldStateZoneId(); - if (zoneId != 0) { - std::string name = gh->getWhoAreaName(zoneId); - if (!name.empty()) { lua_pushstring(L, name.c_str()); return 1; } - } - lua_pushstring(L, ""); - return 1; -} - -// GetSubZoneText() → subzone name (same as zone for now — server doesn't always send subzone) -static int lua_GetSubZoneText(lua_State* L) { - return lua_GetZoneText(L); // Best-effort: zone and subzone often overlap -} - -// GetMinimapZoneText() → zone name displayed near minimap -static int lua_GetMinimapZoneText(lua_State* L) { - return lua_GetZoneText(L); -} - // --- Player State API --- // These replace the hardcoded "return false" Lua stubs with real game state. @@ -785,310 +709,6 @@ static int lua_GetUnitSpeed(lua_State* L) { return 1; } -// --- Container/Bag API --- -// WoW bags: container 0 = backpack (16 slots), containers 1-4 = equipped bags - -static int lua_GetContainerNumSlots(lua_State* L) { - auto* gh = getGameHandler(L); - int container = static_cast(luaL_checknumber(L, 1)); - if (!gh) { lua_pushnumber(L, 0); return 1; } - const auto& inv = gh->getInventory(); - if (container == 0) { - lua_pushnumber(L, inv.getBackpackSize()); - } else if (container >= 1 && container <= 4) { - lua_pushnumber(L, inv.getBagSize(container - 1)); - } else { - lua_pushnumber(L, 0); - } - return 1; -} - -// GetContainerItemInfo(container, slot) → texture, count, locked, quality, readable, lootable, link -static int lua_GetContainerItemInfo(lua_State* L) { - auto* gh = getGameHandler(L); - int container = static_cast(luaL_checknumber(L, 1)); - int slot = static_cast(luaL_checknumber(L, 2)); - if (!gh) { lua_pushnil(L); return 1; } - - const auto& inv = gh->getInventory(); - const game::ItemSlot* itemSlot = nullptr; - - if (container == 0 && slot >= 1 && slot <= inv.getBackpackSize()) { - itemSlot = &inv.getBackpackSlot(slot - 1); // WoW uses 1-based - } else if (container >= 1 && container <= 4) { - int bagIdx = container - 1; - int bagSize = inv.getBagSize(bagIdx); - if (slot >= 1 && slot <= bagSize) - itemSlot = &inv.getBagSlot(bagIdx, slot - 1); - } - - if (!itemSlot || itemSlot->empty()) { lua_pushnil(L); return 1; } - - // Get item info for quality/icon - const auto* info = gh->getItemInfo(itemSlot->item.itemId); - - lua_pushnil(L); // texture (icon path — would need ItemDisplayInfo icon resolver) - lua_pushnumber(L, itemSlot->item.stackCount); // count - lua_pushboolean(L, 0); // locked - lua_pushnumber(L, info ? info->quality : 0); // quality - lua_pushboolean(L, 0); // readable - lua_pushboolean(L, 0); // lootable - // Build item link with quality color - std::string name = info ? info->name : ("Item #" + std::to_string(itemSlot->item.itemId)); - uint32_t q = info ? info->quality : 0; - static const char* kQH[] = {"9d9d9d","ffffff","1eff00","0070dd","a335ee","ff8000","e6cc80","e6cc80"}; - uint32_t qi = q < 8 ? q : 1u; - char link[256]; - snprintf(link, sizeof(link), "|cff%s|Hitem:%u:0:0:0:0:0:0:0|h[%s]|h|r", - kQH[qi], itemSlot->item.itemId, name.c_str()); - lua_pushstring(L, link); // link - return 7; -} - -// GetContainerItemLink(container, slot) → item link string -static int lua_GetContainerItemLink(lua_State* L) { - auto* gh = getGameHandler(L); - int container = static_cast(luaL_checknumber(L, 1)); - int slot = static_cast(luaL_checknumber(L, 2)); - if (!gh) { lua_pushnil(L); return 1; } - - const auto& inv = gh->getInventory(); - const game::ItemSlot* itemSlot = nullptr; - - if (container == 0 && slot >= 1 && slot <= inv.getBackpackSize()) { - itemSlot = &inv.getBackpackSlot(slot - 1); - } else if (container >= 1 && container <= 4) { - int bagIdx = container - 1; - int bagSize = inv.getBagSize(bagIdx); - if (slot >= 1 && slot <= bagSize) - itemSlot = &inv.getBagSlot(bagIdx, slot - 1); - } - - if (!itemSlot || itemSlot->empty()) { lua_pushnil(L); return 1; } - const auto* info = gh->getItemInfo(itemSlot->item.itemId); - std::string name = info ? info->name : ("Item #" + std::to_string(itemSlot->item.itemId)); - uint32_t q = info ? info->quality : 0; - char link[256]; - static const char* kQH[] = {"9d9d9d","ffffff","1eff00","0070dd","a335ee","ff8000","e6cc80","e6cc80"}; - uint32_t qi = q < 8 ? q : 1u; - snprintf(link, sizeof(link), "|cff%s|Hitem:%u:0:0:0:0:0:0:0|h[%s]|h|r", - kQH[qi], itemSlot->item.itemId, name.c_str()); - lua_pushstring(L, link); - return 1; -} - -// GetContainerNumFreeSlots(container) → numFreeSlots, bagType -static int lua_GetContainerNumFreeSlots(lua_State* L) { - auto* gh = getGameHandler(L); - int container = static_cast(luaL_checknumber(L, 1)); - if (!gh) { lua_pushnumber(L, 0); lua_pushnumber(L, 0); return 2; } - - const auto& inv = gh->getInventory(); - int freeSlots = 0; - int totalSlots = 0; - - if (container == 0) { - totalSlots = inv.getBackpackSize(); - for (int i = 0; i < totalSlots; ++i) - if (inv.getBackpackSlot(i).empty()) ++freeSlots; - } else if (container >= 1 && container <= 4) { - totalSlots = inv.getBagSize(container - 1); - for (int i = 0; i < totalSlots; ++i) - if (inv.getBagSlot(container - 1, i).empty()) ++freeSlots; - } - - lua_pushnumber(L, freeSlots); - lua_pushnumber(L, 0); // bagType (0 = normal) - return 2; -} - -// --- Equipment Slot API --- -// WoW inventory slot IDs: 1=Head,2=Neck,3=Shoulders,4=Shirt,5=Chest, -// 6=Waist,7=Legs,8=Feet,9=Wrists,10=Hands,11=Ring1,12=Ring2, -// 13=Trinket1,14=Trinket2,15=Back,16=MainHand,17=OffHand,18=Ranged,19=Tabard - -static int lua_GetInventoryItemLink(lua_State* L) { - auto* gh = getGameHandler(L); - const char* uid = luaL_optstring(L, 1, "player"); - int slotId = static_cast(luaL_checknumber(L, 2)); - if (!gh || slotId < 1 || slotId > 19) { lua_pushnil(L); return 1; } - std::string uidStr(uid); - for (char& c : uidStr) c = static_cast(std::tolower(static_cast(c))); - if (uidStr != "player") { lua_pushnil(L); return 1; } - - const auto& inv = gh->getInventory(); - const auto& slot = inv.getEquipSlot(static_cast(slotId - 1)); - if (slot.empty()) { lua_pushnil(L); return 1; } - - const auto* info = gh->getItemInfo(slot.item.itemId); - std::string name = info ? info->name : slot.item.name; - uint32_t q = info ? info->quality : static_cast(slot.item.quality); - static const char* kQH[] = {"9d9d9d","ffffff","1eff00","0070dd","a335ee","ff8000","e6cc80","e6cc80"}; - uint32_t qi = q < 8 ? q : 1u; - char link[256]; - snprintf(link, sizeof(link), "|cff%s|Hitem:%u:0:0:0:0:0:0:0|h[%s]|h|r", - kQH[qi], slot.item.itemId, name.c_str()); - lua_pushstring(L, link); - return 1; -} - -static int lua_GetInventoryItemID(lua_State* L) { - auto* gh = getGameHandler(L); - const char* uid = luaL_optstring(L, 1, "player"); - int slotId = static_cast(luaL_checknumber(L, 2)); - if (!gh || slotId < 1 || slotId > 19) { lua_pushnil(L); return 1; } - std::string uidStr(uid); - for (char& c : uidStr) c = static_cast(std::tolower(static_cast(c))); - if (uidStr != "player") { lua_pushnil(L); return 1; } - - const auto& inv = gh->getInventory(); - const auto& slot = inv.getEquipSlot(static_cast(slotId - 1)); - if (slot.empty()) { lua_pushnil(L); return 1; } - lua_pushnumber(L, slot.item.itemId); - return 1; -} - -static int lua_GetInventoryItemTexture(lua_State* L) { - auto* gh = getGameHandler(L); - const char* uid = luaL_optstring(L, 1, "player"); - int slotId = static_cast(luaL_checknumber(L, 2)); - if (!gh || slotId < 1 || slotId > 19) { lua_pushnil(L); return 1; } - std::string uidStr(uid); - for (char& c : uidStr) c = static_cast(std::tolower(static_cast(c))); - if (uidStr != "player") { lua_pushnil(L); return 1; } - - const auto& inv = gh->getInventory(); - const auto& slot = inv.getEquipSlot(static_cast(slotId - 1)); - if (slot.empty()) { lua_pushnil(L); return 1; } - // Return spell icon path for the item's on-use spell, or nil - lua_pushnil(L); - return 1; -} - -// --- Time & XP API --- - -static int lua_GetGameTime(lua_State* L) { - // Returns server game time as hours, minutes - auto* gh = getGameHandler(L); - if (gh) { - float gt = gh->getGameTime(); - int hours = static_cast(gt) % 24; - int mins = static_cast((gt - static_cast(gt)) * 60.0f); - lua_pushnumber(L, hours); - lua_pushnumber(L, mins); - } else { - lua_pushnumber(L, 12); - lua_pushnumber(L, 0); - } - return 2; -} - -static int lua_GetServerTime(lua_State* L) { - lua_pushnumber(L, static_cast(std::time(nullptr))); - return 1; -} - -static int lua_UnitXP(lua_State* L) { - const char* uid = luaL_optstring(L, 1, "player"); - auto* gh = getGameHandler(L); - if (!gh) { lua_pushnumber(L, 0); return 1; } - std::string u(uid); - for (char& c : u) c = static_cast(std::tolower(static_cast(c))); - if (u == "player") lua_pushnumber(L, gh->getPlayerXp()); - else lua_pushnumber(L, 0); - return 1; -} - -static int lua_UnitXPMax(lua_State* L) { - const char* uid = luaL_optstring(L, 1, "player"); - auto* gh = getGameHandler(L); - if (!gh) { lua_pushnumber(L, 1); return 1; } - std::string u(uid); - for (char& c : u) c = static_cast(std::tolower(static_cast(c))); - if (u == "player") { - uint32_t nlxp = gh->getPlayerNextLevelXp(); - lua_pushnumber(L, nlxp > 0 ? nlxp : 1); - } else { - lua_pushnumber(L, 1); - } - return 1; -} - -// GetXPExhaustion() → rested XP pool remaining (nil if none) -static int lua_GetXPExhaustion(lua_State* L) { - auto* gh = getGameHandler(L); - if (!gh) { lua_pushnil(L); return 1; } - uint32_t rested = gh->getPlayerRestedXp(); - if (rested > 0) lua_pushnumber(L, rested); - else lua_pushnil(L); - return 1; -} - -// GetRestState() → 1 = normal, 2 = rested -static int lua_GetRestState(lua_State* L) { - auto* gh = getGameHandler(L); - lua_pushnumber(L, (gh && gh->isPlayerResting()) ? 2 : 1); - return 1; -} - -// --- Quest Log API --- - -static int lua_GetNumQuestLogEntries(lua_State* L) { - auto* gh = getGameHandler(L); - if (!gh) { lua_pushnumber(L, 0); lua_pushnumber(L, 0); return 2; } - const auto& ql = gh->getQuestLog(); - lua_pushnumber(L, ql.size()); // numEntries - lua_pushnumber(L, 0); // numQuests (headers not tracked) - return 2; -} - -// GetQuestLogTitle(index) → title, level, suggestedGroup, isHeader, isCollapsed, isComplete, frequency, questID -static int lua_GetQuestLogTitle(lua_State* L) { - auto* gh = getGameHandler(L); - int index = static_cast(luaL_checknumber(L, 1)); - if (!gh || index < 1) { lua_pushnil(L); return 1; } - const auto& ql = gh->getQuestLog(); - if (index > static_cast(ql.size())) { lua_pushnil(L); return 1; } - const auto& q = ql[index - 1]; // 1-based - lua_pushstring(L, q.title.c_str()); // title - lua_pushnumber(L, 0); // level (not tracked) - lua_pushnumber(L, 0); // suggestedGroup - lua_pushboolean(L, 0); // isHeader - lua_pushboolean(L, 0); // isCollapsed - lua_pushboolean(L, q.complete); // isComplete - lua_pushnumber(L, 0); // frequency - lua_pushnumber(L, q.questId); // questID - return 8; -} - -// GetQuestLogQuestText(index) → description, objectives -static int lua_GetQuestLogQuestText(lua_State* L) { - auto* gh = getGameHandler(L); - int index = static_cast(luaL_checknumber(L, 1)); - if (!gh || index < 1) { lua_pushnil(L); return 1; } - const auto& ql = gh->getQuestLog(); - if (index > static_cast(ql.size())) { lua_pushnil(L); return 1; } - const auto& q = ql[index - 1]; - lua_pushstring(L, ""); // description (not stored) - lua_pushstring(L, q.objectives.c_str()); // objectives - return 2; -} - -// IsQuestComplete(questID) → boolean -static int lua_IsQuestComplete(lua_State* L) { - auto* gh = getGameHandler(L); - uint32_t questId = static_cast(luaL_checknumber(L, 1)); - if (!gh) { lua_pushboolean(L, 0); return 1; } - for (const auto& q : gh->getQuestLog()) { - if (q.questId == questId) { - lua_pushboolean(L, q.complete); - return 1; - } - } - lua_pushboolean(L, 0); - return 1; -} - // --- Additional WoW API --- static int lua_UnitAffectingCombat(lua_State* L) { @@ -1222,198 +842,6 @@ static int lua_UnitCreatureType(lua_State* L) { return 1; } -// GetPlayerInfoByGUID(guid) → localizedClass, englishClass, localizedRace, englishRace, sex, name, realm -static int lua_GetPlayerInfoByGUID(lua_State* L) { - auto* gh = getGameHandler(L); - const char* guidStr = luaL_checkstring(L, 1); - if (!gh || !guidStr) { - for (int i = 0; i < 7; i++) lua_pushnil(L); - return 7; - } - // Parse hex GUID string "0x0000000000000001" - uint64_t guid = 0; - if (guidStr[0] == '0' && (guidStr[1] == 'x' || guidStr[1] == 'X')) - guid = strtoull(guidStr + 2, nullptr, 16); - else - guid = strtoull(guidStr, nullptr, 16); - - if (guid == 0) { for (int i = 0; i < 7; i++) lua_pushnil(L); return 7; } - - // Look up entity name - std::string name = gh->lookupName(guid); - if (name.empty() && guid == gh->getPlayerGuid()) { - const auto& chars = gh->getCharacters(); - for (const auto& c : chars) - if (c.guid == guid) { name = c.name; break; } - } - - // For player GUID, return class/race if it's the local player - const char* className = "Unknown"; - const char* raceName = "Unknown"; - if (guid == gh->getPlayerGuid()) { - static const char* kClasses[] = {"","Warrior","Paladin","Hunter","Rogue","Priest", - "Death Knight","Shaman","Mage","Warlock","","Druid"}; - static const char* kRaces[] = {"","Human","Orc","Dwarf","Night Elf","Undead", - "Tauren","Gnome","Troll","","Blood Elf","Draenei"}; - uint8_t cid = gh->getPlayerClass(); - uint8_t rid = gh->getPlayerRace(); - if (cid < 12) className = kClasses[cid]; - if (rid < 12) raceName = kRaces[rid]; - } - - lua_pushstring(L, className); // 1: localizedClass - lua_pushstring(L, className); // 2: englishClass - lua_pushstring(L, raceName); // 3: localizedRace - lua_pushstring(L, raceName); // 4: englishRace - lua_pushnumber(L, 0); // 5: sex (0=unknown) - lua_pushstring(L, name.c_str()); // 6: name - lua_pushstring(L, ""); // 7: realm - return 7; -} - -// GetItemLink(itemId) → "|cFFxxxxxx|Hitem:ID:...|h[Name]|h|r" -static int lua_GetItemLink(lua_State* L) { - auto* gh = getGameHandler(L); - if (!gh) { lua_pushnil(L); return 1; } - uint32_t itemId = static_cast(luaL_checknumber(L, 1)); - if (itemId == 0) { lua_pushnil(L); return 1; } - const auto* info = gh->getItemInfo(itemId); - if (!info || info->name.empty()) { lua_pushnil(L); return 1; } - static const char* kQH[] = {"9d9d9d","ffffff","1eff00","0070dd","a335ee","ff8000","e6cc80","e6cc80"}; - uint32_t qi = info->quality < 8 ? info->quality : 1u; - char link[256]; - snprintf(link, sizeof(link), "|cff%s|Hitem:%u:0:0:0:0:0:0:0|h[%s]|h|r", - kQH[qi], itemId, info->name.c_str()); - lua_pushstring(L, link); - return 1; -} - -// GetSpellLink(spellIdOrName) → "|cFFxxxxxx|Hspell:ID|h[Name]|h|r" -static int lua_GetSpellLink(lua_State* L) { - auto* gh = getGameHandler(L); - if (!gh) { lua_pushnil(L); return 1; } - - uint32_t spellId = 0; - if (lua_isnumber(L, 1)) { - spellId = static_cast(lua_tonumber(L, 1)); - } else if (lua_isstring(L, 1)) { - const char* name = lua_tostring(L, 1); - if (!name || !*name) { lua_pushnil(L); return 1; } - std::string nameLow(name); - for (char& c : nameLow) c = static_cast(std::tolower(static_cast(c))); - for (uint32_t sid : gh->getKnownSpells()) { - std::string sn = gh->getSpellName(sid); - for (char& c : sn) c = static_cast(std::tolower(static_cast(c))); - if (sn == nameLow) { spellId = sid; break; } - } - } - if (spellId == 0) { lua_pushnil(L); return 1; } - std::string name = gh->getSpellName(spellId); - if (name.empty()) { lua_pushnil(L); return 1; } - char link[256]; - snprintf(link, sizeof(link), "|cff71d5ff|Hspell:%u|h[%s]|h|r", spellId, name.c_str()); - lua_pushstring(L, link); - return 1; -} - -// IsUsableSpell(spellIdOrName) → usable, noMana -static int lua_IsUsableSpell(lua_State* L) { - auto* gh = getGameHandler(L); - if (!gh) { lua_pushboolean(L, 0); lua_pushboolean(L, 0); return 2; } - - uint32_t spellId = 0; - if (lua_isnumber(L, 1)) { - spellId = static_cast(lua_tonumber(L, 1)); - } else if (lua_isstring(L, 1)) { - const char* name = lua_tostring(L, 1); - if (!name || !*name) { lua_pushboolean(L, 0); lua_pushboolean(L, 0); return 2; } - std::string nameLow(name); - for (char& c : nameLow) c = static_cast(std::tolower(static_cast(c))); - for (uint32_t sid : gh->getKnownSpells()) { - std::string sn = gh->getSpellName(sid); - for (char& c : sn) c = static_cast(std::tolower(static_cast(c))); - if (sn == nameLow) { spellId = sid; break; } - } - } - - if (spellId == 0 || !gh->getKnownSpells().count(spellId)) { - lua_pushboolean(L, 0); - lua_pushboolean(L, 0); - return 2; - } - - // Check if on cooldown - float cd = gh->getSpellCooldown(spellId); - bool onCooldown = (cd > 0.1f); - - lua_pushboolean(L, onCooldown ? 0 : 1); // usable (not on cooldown) - lua_pushboolean(L, 0); // noMana (can't determine without spell cost data) - return 2; -} - -// IsInInstance() → isInstance, instanceType -static int lua_IsInInstance(lua_State* L) { - auto* gh = getGameHandler(L); - if (!gh) { lua_pushboolean(L, 0); lua_pushstring(L, "none"); return 2; } - bool inInstance = gh->isInInstance(); - lua_pushboolean(L, inInstance); - lua_pushstring(L, inInstance ? "party" : "none"); // simplified: "none", "party", "raid", "pvp", "arena" - return 2; -} - -// GetInstanceInfo() → name, type, difficultyIndex, difficultyName, maxPlayers, ... -static int lua_GetInstanceInfo(lua_State* L) { - auto* gh = getGameHandler(L); - if (!gh) { - lua_pushstring(L, ""); lua_pushstring(L, "none"); lua_pushnumber(L, 0); - lua_pushstring(L, "Normal"); lua_pushnumber(L, 0); - return 5; - } - std::string mapName = gh->getMapName(gh->getCurrentMapId()); - lua_pushstring(L, mapName.c_str()); // 1: name - lua_pushstring(L, gh->isInInstance() ? "party" : "none"); // 2: instanceType - lua_pushnumber(L, gh->getInstanceDifficulty()); // 3: difficultyIndex - static const char* kDiff[] = {"Normal", "Heroic", "25 Normal", "25 Heroic"}; - uint32_t diff = gh->getInstanceDifficulty(); - lua_pushstring(L, (diff < 4) ? kDiff[diff] : "Normal"); // 4: difficultyName - lua_pushnumber(L, 5); // 5: maxPlayers (default 5-man) - return 5; -} - -// GetInstanceDifficulty() → difficulty (1=normal, 2=heroic, 3=25normal, 4=25heroic) -static int lua_GetInstanceDifficulty(lua_State* L) { - auto* gh = getGameHandler(L); - lua_pushnumber(L, gh ? (gh->getInstanceDifficulty() + 1) : 1); // WoW returns 1-based - return 1; -} - -// UnitClassification(unit) → "normal", "elite", "rareelite", "worldboss", "rare" -static int lua_UnitClassification(lua_State* L) { - auto* gh = getGameHandler(L); - if (!gh) { lua_pushstring(L, "normal"); return 1; } - const char* uid = luaL_optstring(L, 1, "target"); - std::string uidStr(uid); - for (char& c : uidStr) c = static_cast(std::tolower(static_cast(c))); - uint64_t guid = resolveUnitGuid(gh, uidStr); - if (guid == 0) { lua_pushstring(L, "normal"); return 1; } - auto entity = gh->getEntityManager().getEntity(guid); - if (!entity || entity->getType() == game::ObjectType::PLAYER) { - lua_pushstring(L, "normal"); - return 1; - } - auto unit = std::dynamic_pointer_cast(entity); - if (!unit) { lua_pushstring(L, "normal"); return 1; } - int rank = gh->getCreatureRank(unit->getEntry()); - switch (rank) { - case 1: lua_pushstring(L, "elite"); break; - case 2: lua_pushstring(L, "rareelite"); break; - case 3: lua_pushstring(L, "worldboss"); break; - case 4: lua_pushstring(L, "rare"); break; - default: lua_pushstring(L, "normal"); break; - } - return 1; -} - // --- Frame System --- // Minimal WoW-compatible frame objects with RegisterEvent/SetScript/GetScript. // Frames are Lua tables with a metatable that provides methods. @@ -1747,7 +1175,6 @@ void LuaEngine::registerCoreAPI() { {"InCombatLockdown", lua_InCombatLockdown}, {"UnitBuff", lua_UnitBuff}, {"UnitDebuff", lua_UnitDebuff}, - {"UnitAura", lua_UnitAuraGeneric}, {"GetNumAddOns", lua_GetNumAddOns}, {"GetAddOnInfo", lua_GetAddOnInfo}, {"GetSpellInfo", lua_GetSpellInfo}, @@ -1756,10 +1183,6 @@ void LuaEngine::registerCoreAPI() { {"GetLocale", lua_GetLocale}, {"GetBuildInfo", lua_GetBuildInfo}, {"GetCurrentMapAreaID", lua_GetCurrentMapAreaID}, - {"GetZoneText", lua_GetZoneText}, - {"GetRealZoneText", lua_GetZoneText}, - {"GetSubZoneText", lua_GetSubZoneText}, - {"GetMinimapZoneText", lua_GetMinimapZoneText}, // Player state (replaces hardcoded stubs) {"IsMounted", lua_IsMounted}, {"IsFlying", lua_IsFlying}, @@ -1778,35 +1201,6 @@ void LuaEngine::registerCoreAPI() { {"UnitIsFriend", lua_UnitIsFriend}, {"UnitIsEnemy", lua_UnitIsEnemy}, {"UnitCreatureType", lua_UnitCreatureType}, - {"UnitClassification", lua_UnitClassification}, - {"GetPlayerInfoByGUID", lua_GetPlayerInfoByGUID}, - {"GetItemLink", lua_GetItemLink}, - {"GetSpellLink", lua_GetSpellLink}, - {"IsUsableSpell", lua_IsUsableSpell}, - {"IsInInstance", lua_IsInInstance}, - {"GetInstanceInfo", lua_GetInstanceInfo}, - {"GetInstanceDifficulty", lua_GetInstanceDifficulty}, - // Container/bag API - {"GetContainerNumSlots", lua_GetContainerNumSlots}, - {"GetContainerItemInfo", lua_GetContainerItemInfo}, - {"GetContainerItemLink", lua_GetContainerItemLink}, - {"GetContainerNumFreeSlots", lua_GetContainerNumFreeSlots}, - // Equipment slot API - {"GetInventoryItemLink", lua_GetInventoryItemLink}, - {"GetInventoryItemID", lua_GetInventoryItemID}, - {"GetInventoryItemTexture", lua_GetInventoryItemTexture}, - // Time/XP API - {"GetGameTime", lua_GetGameTime}, - {"GetServerTime", lua_GetServerTime}, - {"UnitXP", lua_UnitXP}, - {"UnitXPMax", lua_UnitXPMax}, - {"GetXPExhaustion", lua_GetXPExhaustion}, - {"GetRestState", lua_GetRestState}, - // Quest log API - {"GetNumQuestLogEntries", lua_GetNumQuestLogEntries}, - {"GetQuestLogTitle", lua_GetQuestLogTitle}, - {"GetQuestLogQuestText", lua_GetQuestLogQuestText}, - {"IsQuestComplete", lua_IsQuestComplete}, // Utilities {"strsplit", lua_strsplit}, {"strtrim", lua_strtrim}, @@ -1988,20 +1382,6 @@ void LuaEngine::registerCoreAPI() { " SHAMAN={r=0.0,g=0.44,b=0.87}, MAGE={r=0.41,g=0.80,b=0.94},\n" " WARLOCK={r=0.58,g=0.51,b=0.79}, DRUID={r=1.0,g=0.49,b=0.04},\n" "}\n" - // Money formatting utility - "function GetCoinTextureString(copper)\n" - " if not copper or copper == 0 then return '0c' end\n" - " copper = math.floor(copper)\n" - " local g = math.floor(copper / 10000)\n" - " local s = math.floor(math.fmod(copper, 10000) / 100)\n" - " local c = math.fmod(copper, 100)\n" - " local r = ''\n" - " if g > 0 then r = r .. g .. 'g ' end\n" - " if s > 0 then r = r .. s .. 's ' end\n" - " if c > 0 or r == '' then r = r .. c .. 'c' end\n" - " return r\n" - "end\n" - "GetCoinText = GetCoinTextureString\n" ); } diff --git a/src/audio/ui_sound_manager.cpp b/src/audio/ui_sound_manager.cpp index 6518259e..8ef800f0 100644 --- a/src/audio/ui_sound_manager.cpp +++ b/src/audio/ui_sound_manager.cpp @@ -130,12 +130,6 @@ bool UiSoundManager::initialize(pipeline::AssetManager* assets) { } } - // Minimap ping sound - minimapPingSounds_.resize(1); - if (!loadSound("Sound\\Interface\\MapPing.wav", minimapPingSounds_[0], assets)) { - minimapPingSounds_ = selectTargetSounds_; // fallback to target select sound - } - LOG_INFO("UISoundManager: Window sounds - Bag: ", (bagOpenLoaded && bagCloseLoaded) ? "YES" : "NO", ", QuestLog: ", (questLogOpenLoaded && questLogCloseLoaded) ? "YES" : "NO", ", CharSheet: ", (charSheetOpenLoaded && charSheetCloseLoaded) ? "YES" : "NO"); @@ -242,8 +236,5 @@ void UiSoundManager::playTargetDeselect() { playSound(deselectTargetSounds_); } // Chat notifications void UiSoundManager::playWhisperReceived() { playSound(whisperSounds_); } -// Minimap ping -void UiSoundManager::playMinimapPing() { playSound(minimapPingSounds_); } - } // namespace audio } // namespace wowee diff --git a/src/core/application.cpp b/src/core/application.cpp index 8b4aeeb0..ce3883db 100644 --- a/src/core/application.cpp +++ b/src/core/application.cpp @@ -413,38 +413,6 @@ bool Application::initialize() { return pit->second; }); } - // Wire random property/suffix name resolver for item display - { - auto propNames = std::make_shared>(); - auto propLoaded = std::make_shared(false); - auto* amPtr = assetManager.get(); - gameHandler->setRandomPropertyNameResolver([propNames, propLoaded, amPtr](int32_t id) -> std::string { - if (!amPtr || id == 0) return {}; - if (!*propLoaded) { - *propLoaded = true; - // ItemRandomProperties.dbc: ID=0, Name=4 (string) - if (auto dbc = amPtr->loadDBC("ItemRandomProperties.dbc"); dbc && dbc->isLoaded()) { - uint32_t nameField = (dbc->getFieldCount() > 4) ? 4 : 1; - for (uint32_t r = 0; r < dbc->getRecordCount(); ++r) { - int32_t rid = static_cast(dbc->getUInt32(r, 0)); - std::string name = dbc->getString(r, nameField); - if (!name.empty() && rid > 0) (*propNames)[rid] = name; - } - } - // ItemRandomSuffix.dbc: ID=0, Name=4 (string) — stored as negative IDs - if (auto dbc = amPtr->loadDBC("ItemRandomSuffix.dbc"); dbc && dbc->isLoaded()) { - uint32_t nameField = (dbc->getFieldCount() > 4) ? 4 : 1; - for (uint32_t r = 0; r < dbc->getRecordCount(); ++r) { - int32_t rid = static_cast(dbc->getUInt32(r, 0)); - std::string name = dbc->getString(r, nameField); - if (!name.empty() && rid > 0) (*propNames)[-rid] = name; - } - } - } - auto it = propNames->find(id); - return (it != propNames->end()) ? it->second : std::string{}; - }); - } LOG_INFO("Addon system initialized, found ", addonManager_->getAddons().size(), " addon(s)"); } else { LOG_WARNING("Failed to initialize addon system"); @@ -678,15 +646,6 @@ void Application::run() { LOG_ERROR("GPU device lost — exiting application"); window->setShouldClose(true); } - - // Soft frame rate cap when vsync is off to prevent 100% CPU usage. - // Target ~240 FPS max (~4.2ms per frame); vsync handles its own pacing. - if (!window->isVsyncEnabled() && deltaTime < 0.004f) { - float sleepMs = (0.004f - deltaTime) * 1000.0f; - if (sleepMs > 0.5f) - std::this_thread::sleep_for(std::chrono::microseconds( - static_cast(sleepMs * 900.0f))); // 90% of target to account for sleep overshoot - } } } catch (...) { watchdogRunning.store(false, std::memory_order_release); @@ -4327,15 +4286,6 @@ void Application::loadOnlineWorldTerrain(uint32_t mapId, float x, float y, float window->swapBuffers(); }; - // Set zone name on loading screen from Map.dbc - if (gameHandler) { - std::string mapDisplayName = gameHandler->getMapName(mapId); - if (!mapDisplayName.empty()) - loadingScreen.setZoneName(mapDisplayName); - else - loadingScreen.setZoneName("Loading..."); - } - showProgress("Entering world...", 0.0f); // --- Clean up previous map's state on map change --- diff --git a/src/game/game_handler.cpp b/src/game/game_handler.cpp index 47f0756f..a7bbab69 100644 --- a/src/game/game_handler.cpp +++ b/src/game/game_handler.cpp @@ -784,22 +784,7 @@ void GameHandler::disconnect() { wardenLoadedModule_.reset(); pendingIncomingPackets_.clear(); pendingUpdateObjectWork_.clear(); - // Fire despawn callbacks so the renderer releases M2/character model resources. - for (const auto& [guid, entity] : entityManager.getEntities()) { - if (guid == playerGuid) continue; - if (entity->getType() == ObjectType::UNIT && creatureDespawnCallback_) - creatureDespawnCallback_(guid); - else if (entity->getType() == ObjectType::PLAYER && playerDespawnCallback_) - playerDespawnCallback_(guid); - else if (entity->getType() == ObjectType::GAMEOBJECT && gameObjectDespawnCallback_) - gameObjectDespawnCallback_(guid); - } - otherPlayerVisibleItemEntries_.clear(); - otherPlayerVisibleDirty_.clear(); - otherPlayerMoveTimeMs_.clear(); - unitCastStates_.clear(); - unitAurasCache_.clear(); - combatText.clear(); + // Clear entity state so reconnect sees fresh CREATE_OBJECT for all visible objects. entityManager.clear(); setState(WorldState::DISCONNECTED); LOG_INFO("Disconnected from world server"); @@ -1978,7 +1963,7 @@ void GameHandler::handlePacket(network::Packet& packet) { /*uint32_t itemSlot =*/ packet.readUInt32(); uint32_t itemId = packet.readUInt32(); /*uint32_t suffixFactor =*/ packet.readUInt32(); - int32_t randomProp = static_cast(packet.readUInt32()); + /*int32_t randomProp =*/ static_cast(packet.readUInt32()); uint32_t count = packet.readUInt32(); /*uint32_t totalCount =*/ packet.readUInt32(); @@ -1987,11 +1972,6 @@ void GameHandler::handlePacket(network::Packet& packet) { if (const ItemQueryResponseData* info = getItemInfo(itemId)) { // Item info already cached — emit immediately. std::string itemName = info->name.empty() ? ("item #" + std::to_string(itemId)) : info->name; - // Append random suffix name (e.g., "of the Eagle") if present - if (randomProp != 0) { - std::string suffix = getRandomPropertyName(randomProp); - if (!suffix.empty()) itemName += " " + suffix; - } uint32_t quality = info->quality; std::string link = buildItemLink(itemId, quality, itemName); std::string msg = "Received: " + link; @@ -2002,9 +1982,6 @@ void GameHandler::handlePacket(network::Packet& packet) { sfx->playLootItem(); } if (itemLootCallback_) itemLootCallback_(itemId, count, quality, itemName); - // Fire CHAT_MSG_LOOT for loot tracking addons - if (addonEventCallback_) - addonEventCallback_("CHAT_MSG_LOOT", {msg, "", std::to_string(itemId), std::to_string(count)}); } else { // Item info not yet cached; defer until SMSG_ITEM_QUERY_SINGLE_RESPONSE. pendingItemPushNotifs_.push_back({itemId, count}); @@ -2304,8 +2281,6 @@ void GameHandler::handlePacket(network::Packet& packet) { : ("Spell cast failed (error " + std::to_string(castResult) + ")"); addUIError(errMsg); if (spellCastFailedCallback_) spellCastFailedCallback_(castResultSpellId); - if (addonEventCallback_) - addonEventCallback_("UNIT_SPELLCAST_FAILED", {"player", std::to_string(castResultSpellId)}); MessageChatData msg; msg.type = ChatType::SYSTEM; msg.language = ChatLanguage::UNIVERSAL; @@ -2375,10 +2350,9 @@ void GameHandler::handlePacket(network::Packet& packet) { /*uint32_t mapId =*/ packet.readUInt32(); uint32_t slot = packet.readUInt32(); uint32_t itemId = packet.readUInt32(); - int32_t rollRandProp = 0; if (isWotLK) { /*uint32_t randSuffix =*/ packet.readUInt32(); - rollRandProp = static_cast(packet.readUInt32()); + /*uint32_t randProp =*/ packet.readUInt32(); } uint32_t countdown = packet.readUInt32(); uint8_t voteMask = packet.readUInt8(); @@ -2388,14 +2362,11 @@ void GameHandler::handlePacket(network::Packet& packet) { pendingLootRoll_.slot = slot; pendingLootRoll_.itemId = itemId; // Ensure item info is queried so the roll popup can show the name/icon. + // The popup re-reads getItemInfo() live, so the name will populate once + // SMSG_ITEM_QUERY_SINGLE_RESPONSE arrives (usually within ~100 ms). queryItemInfo(itemId, 0); auto* info = getItemInfo(itemId); - std::string rollItemName = info ? info->name : std::to_string(itemId); - if (rollRandProp != 0) { - std::string suffix = getRandomPropertyName(rollRandProp); - if (!suffix.empty()) rollItemName += " " + suffix; - } - pendingLootRoll_.itemName = rollItemName; + pendingLootRoll_.itemName = info ? info->name : std::to_string(itemId); pendingLootRoll_.itemQuality = info ? static_cast(info->quality) : 0; pendingLootRoll_.rollCountdownMs = (countdown > 0 && countdown <= 120000) ? countdown : 60000; pendingLootRoll_.voteMask = voteMask; @@ -2551,8 +2522,6 @@ void GameHandler::handlePacket(network::Packet& packet) { poi.icon = icon; poi.data = data; poi.name = std::move(name); - // Cap POI count to prevent unbounded growth from rapid gossip queries - if (gossipPois_.size() >= 200) gossipPois_.erase(gossipPois_.begin()); gossipPois_.push_back(std::move(poi)); LOG_DEBUG("SMSG_GOSSIP_POI: x=", poiX, " y=", poiY, " icon=", icon); break; @@ -3412,15 +3381,6 @@ void GameHandler::handlePacket(network::Packet& packet) { } } } - // Fire UNIT_SPELLCAST_INTERRUPTED for Lua addons - if (addonEventCallback_) { - std::string unitId; - if (failGuid == playerGuid || failGuid == 0) unitId = "player"; - else if (failGuid == targetGuid) unitId = "target"; - else if (failGuid == focusGuid) unitId = "focus"; - if (!unitId.empty()) - addonEventCallback_("UNIT_SPELLCAST_INTERRUPTED", {unitId}); - } if (failGuid == playerGuid || failGuid == 0) { // Player's own cast failed — clear gather-node loot target so the // next timed cast doesn't try to loot a stale interrupted gather node. @@ -3558,13 +3518,6 @@ void GameHandler::handlePacket(network::Packet& packet) { ping.wowY = pingX; // canonical WoW Y = west = server's posX ping.age = 0.0f; minimapPings_.push_back(ping); - // Play ping sound for other players' pings (not our own) - if (senderGuid != playerGuid) { - if (auto* renderer = core::Application::getInstance().getRenderer()) { - if (auto* sfx = renderer->getUiSoundManager()) - sfx->playMinimapPing(); - } - } break; } case Opcode::SMSG_ZONE_UNDER_ATTACK: { @@ -4192,8 +4145,6 @@ void GameHandler::handlePacket(network::Packet& packet) { addSystemChatMessage(buf); watchedFactionId_ = factionId; if (repChangeCallback_) repChangeCallback_(name, delta, standing); - if (addonEventCallback_) - addonEventCallback_("UPDATE_FACTION", {}); } LOG_DEBUG("SMSG_SET_FACTION_STANDING: faction=", factionId, " standing=", standing); } @@ -4592,7 +4543,6 @@ void GameHandler::handlePacket(network::Packet& packet) { } } LOG_INFO("SMSG_ACTION_BUTTONS: populated action bar from server"); - if (addonEventCallback_) addonEventCallback_("ACTIONBAR_SLOT_CHANGED", {}); packet.setReadPos(packet.getSize()); break; } @@ -5030,14 +4980,8 @@ void GameHandler::handlePacket(network::Packet& packet) { uint64_t progress = packet.readUInt64(); packet.readUInt32(); // elapsedTime packet.readUInt32(); // creationTime - uint64_t oldProgress = 0; - auto cpit = criteriaProgress_.find(criteriaId); - if (cpit != criteriaProgress_.end()) oldProgress = cpit->second; criteriaProgress_[criteriaId] = progress; LOG_DEBUG("SMSG_CRITERIA_UPDATE: id=", criteriaId, " progress=", progress); - // Fire addon event for achievement tracking addons - if (addonEventCallback_ && progress != oldProgress) - addonEventCallback_("CRITERIA_UPDATE", {std::to_string(criteriaId), std::to_string(progress)}); } break; } @@ -5121,27 +5065,12 @@ void GameHandler::handlePacket(network::Packet& packet) { case Opcode::SMSG_ENCHANTMENTLOG: { // uint64 targetGuid + uint64 casterGuid + uint32 spellId + uint32 displayId + uint32 animType if (packet.getSize() - packet.getReadPos() >= 28) { - uint64_t enchTargetGuid = packet.readUInt64(); - uint64_t enchCasterGuid = packet.readUInt64(); - uint32_t enchSpellId = packet.readUInt32(); + /*uint64_t targetGuid =*/ packet.readUInt64(); + /*uint64_t casterGuid =*/ packet.readUInt64(); + uint32_t spellId = packet.readUInt32(); /*uint32_t displayId =*/ packet.readUInt32(); /*uint32_t animType =*/ packet.readUInt32(); - LOG_DEBUG("SMSG_ENCHANTMENTLOG: spellId=", enchSpellId); - // Show enchant message if the player is involved - if (enchTargetGuid == playerGuid || enchCasterGuid == playerGuid) { - const std::string& enchName = getSpellName(enchSpellId); - std::string casterName = lookupName(enchCasterGuid); - if (!enchName.empty()) { - std::string msg; - if (enchCasterGuid == playerGuid) - msg = "You enchant with " + enchName + "."; - else if (!casterName.empty()) - msg = casterName + " enchants your item with " + enchName + "."; - else - msg = "Your item has been enchanted with " + enchName + "."; - addSystemChatMessage(msg); - } - } + LOG_DEBUG("SMSG_ENCHANTMENTLOG: spellId=", spellId); } break; } @@ -5334,7 +5263,6 @@ void GameHandler::handlePacket(network::Packet& packet) { } } } - if (addonEventCallback_) addonEventCallback_("QUEST_LOG_UPDATE", {}); // Re-query all nearby quest giver NPCs so markers refresh if (socket) { for (const auto& [guid, entity] : entityManager.getEntities()) { @@ -6209,23 +6137,16 @@ void GameHandler::handlePacket(network::Packet& packet) { handleAuctionCommandResult(packet); break; case Opcode::SMSG_AUCTION_OWNER_NOTIFICATION: { - // auctionId(u32) + action(u32) + error(u32) + itemEntry(u32) + randomPropertyId(u32) + ... + // auctionId(u32) + action(u32) + error(u32) + itemEntry(u32) + ... // action: 0=sold/won, 1=expired, 2=bid placed on your auction if (packet.getSize() - packet.getReadPos() >= 16) { /*uint32_t auctionId =*/ packet.readUInt32(); uint32_t action = packet.readUInt32(); /*uint32_t error =*/ packet.readUInt32(); uint32_t itemEntry = packet.readUInt32(); - int32_t ownerRandProp = 0; - if (packet.getSize() - packet.getReadPos() >= 4) - ownerRandProp = static_cast(packet.readUInt32()); ensureItemInfo(itemEntry); auto* info = getItemInfo(itemEntry); std::string rawName = info && !info->name.empty() ? info->name : ("Item #" + std::to_string(itemEntry)); - if (ownerRandProp != 0) { - std::string suffix = getRandomPropertyName(ownerRandProp); - if (!suffix.empty()) rawName += " " + suffix; - } uint32_t aucQuality = info ? info->quality : 1u; std::string itemLink = buildItemLink(itemEntry, aucQuality, rawName); if (action == 1) @@ -6239,21 +6160,14 @@ void GameHandler::handlePacket(network::Packet& packet) { break; } case Opcode::SMSG_AUCTION_BIDDER_NOTIFICATION: { - // auctionHouseId(u32) + auctionId(u32) + bidderGuid(u64) + bidAmount(u32) + outbidAmount(u32) + itemEntry(u32) + randomPropertyId(u32) + // auctionId(u32) + itemEntry(u32) + ... if (packet.getSize() - packet.getReadPos() >= 8) { - /*uint32_t auctionId =*/ packet.readUInt32(); + uint32_t auctionId = packet.readUInt32(); uint32_t itemEntry = packet.readUInt32(); - int32_t bidRandProp = 0; - // Try to read randomPropertyId if enough data remains - if (packet.getSize() - packet.getReadPos() >= 4) - bidRandProp = static_cast(packet.readUInt32()); + (void)auctionId; ensureItemInfo(itemEntry); auto* info = getItemInfo(itemEntry); std::string rawName2 = info && !info->name.empty() ? info->name : ("Item #" + std::to_string(itemEntry)); - if (bidRandProp != 0) { - std::string suffix = getRandomPropertyName(bidRandProp); - if (!suffix.empty()) rawName2 += " " + suffix; - } uint32_t bidQuality = info ? info->quality : 1u; std::string bidLink = buildItemLink(itemEntry, bidQuality, rawName2); addSystemChatMessage("You have been outbid on " + bidLink + "."); @@ -6266,14 +6180,10 @@ void GameHandler::handlePacket(network::Packet& packet) { if (packet.getSize() - packet.getReadPos() >= 12) { /*uint32_t auctionId =*/ packet.readUInt32(); uint32_t itemEntry = packet.readUInt32(); - int32_t itemRandom = static_cast(packet.readUInt32()); + /*uint32_t itemRandom =*/ packet.readUInt32(); ensureItemInfo(itemEntry); auto* info = getItemInfo(itemEntry); std::string rawName3 = info && !info->name.empty() ? info->name : ("Item #" + std::to_string(itemEntry)); - if (itemRandom != 0) { - std::string suffix = getRandomPropertyName(itemRandom); - if (!suffix.empty()) rawName3 += " " + suffix; - } uint32_t remQuality = info ? info->quality : 1u; std::string remLink = buildItemLink(itemEntry, remQuality, rawName3); addSystemChatMessage("Your auction of " + remLink + " has expired."); @@ -7392,15 +7302,6 @@ void GameHandler::handlePacket(network::Packet& packet) { } LOG_DEBUG("MSG_CHANNEL_START: caster=0x", std::hex, chanCaster, std::dec, " spell=", chanSpellId, " total=", chanTotalMs, "ms"); - // Fire UNIT_SPELLCAST_CHANNEL_START for Lua addons - if (addonEventCallback_) { - std::string unitId; - if (chanCaster == playerGuid) unitId = "player"; - else if (chanCaster == targetGuid) unitId = "target"; - else if (chanCaster == focusGuid) unitId = "focus"; - if (!unitId.empty()) - addonEventCallback_("UNIT_SPELLCAST_CHANNEL_START", {unitId, std::to_string(chanSpellId)}); - } } break; } @@ -7428,15 +7329,6 @@ void GameHandler::handlePacket(network::Packet& packet) { } LOG_DEBUG("MSG_CHANNEL_UPDATE: caster=0x", std::hex, chanCaster2, std::dec, " remaining=", chanRemainMs, "ms"); - // Fire UNIT_SPELLCAST_CHANNEL_STOP when channel ends - if (chanRemainMs == 0 && addonEventCallback_) { - std::string unitId; - if (chanCaster2 == playerGuid) unitId = "player"; - else if (chanCaster2 == targetGuid) unitId = "target"; - else if (chanCaster2 == focusGuid) unitId = "focus"; - if (!unitId.empty()) - addonEventCallback_("UNIT_SPELLCAST_CHANNEL_STOP", {unitId}); - } break; } @@ -12682,8 +12574,6 @@ void GameHandler::applyUpdateObjectBlock(const UpdateBlock& block, bool& newItem if (addonEventCallback_) addonEventCallback_("PLAYER_ALIVE", {}); if (ghostStateCallback_) ghostStateCallback_(false); } - if (addonEventCallback_) - addonEventCallback_("PLAYER_FLAGS_CHANGED", {}); } else if (ufMeleeAPV != 0xFFFF && key == ufMeleeAPV) { playerMeleeAP_ = static_cast(val); } else if (ufRangedAPV != 0xFFFF && key == ufRangedAPV) { playerRangedAP_ = static_cast(val); } @@ -12714,11 +12604,7 @@ void GameHandler::applyUpdateObjectBlock(const UpdateBlock& block, bool& newItem // Do not auto-create quests from VALUES quest-log slot fields for the // same reason as CREATE_OBJECT2 above (can be misaligned per realm). if (applyInventoryFields(block.fields)) slotsChanged = true; - if (slotsChanged) { - rebuildOnlineInventory(); - if (addonEventCallback_) - addonEventCallback_("PLAYER_EQUIPMENT_CHANGED", {}); - } + if (slotsChanged) rebuildOnlineInventory(); extractSkillFields(lastPlayerFields_); extractExploredZoneFields(lastPlayerFields_); applyQuestStateFromFields(lastPlayerFields_); @@ -12823,10 +12709,6 @@ void GameHandler::applyUpdateObjectBlock(const UpdateBlock& block, bool& newItem } if (inventoryChanged) { rebuildOnlineInventory(); - if (addonEventCallback_) { - addonEventCallback_("BAG_UPDATE", {}); - addonEventCallback_("UNIT_INVENTORY_CHANGED", {"player"}); - } } } if (block.hasMovement && entity->getType() == ObjectType::GAMEOBJECT) { @@ -13084,21 +12966,8 @@ void GameHandler::handleDestroyObject(network::Packet& packet) { // Clean up quest giver status npcQuestStatus_.erase(data.guid); - // Remove combat text entries referencing the destroyed entity so floating - // damage numbers don't linger after the source/target despawns. - combatText.erase( - std::remove_if(combatText.begin(), combatText.end(), - [&data](const CombatTextEntry& e) { - return e.dstGuid == data.guid; - }), - combatText.end()); - - // Clean up unit cast state (cast bar) for the destroyed unit - unitCastStates_.erase(data.guid); - // Clean up cached auras - unitAurasCache_.erase(data.guid); - tabCycleStale = true; + // Entity count logging disabled } void GameHandler::sendChatMessage(ChatType type, const std::string& message, const std::string& target) { @@ -17274,17 +17143,6 @@ void GameHandler::lfgLeave() { LOG_INFO("Sent CMSG_LFG_LEAVE"); } -void GameHandler::lfgSetRoles(uint8_t roles) { - if (state != WorldState::IN_WORLD || !socket) return; - const uint32_t wire = wireOpcode(Opcode::CMSG_LFG_SET_ROLES); - if (wire == 0xFFFF) return; - - network::Packet pkt(static_cast(wire)); - pkt.writeUInt8(roles); - socket->send(pkt); - LOG_INFO("Sent CMSG_LFG_SET_ROLES: roles=", static_cast(roles)); -} - void GameHandler::lfgAcceptProposal(uint32_t proposalId, bool accept) { if (!socket) return; @@ -19576,10 +19434,7 @@ void GameHandler::handleSupercededSpell(network::Packet& packet) { LOG_DEBUG("Action bar slot upgraded: spell ", oldSpellId, " -> ", newSpellId); } } - if (barChanged) { - saveCharacterConfig(); - if (addonEventCallback_) addonEventCallback_("ACTIONBAR_SLOT_CHANGED", {}); - } + if (barChanged) saveCharacterConfig(); // Show "Upgraded to X" only when the new spell wasn't already announced by the // trainer-buy handler. For non-trainer supersedes (e.g. quest rewards), the new @@ -19846,7 +19701,6 @@ void GameHandler::handleGroupList(network::Packet& packet) { const bool hasRoles = isActiveExpansion("wotlk"); // Snapshot state before reset so we can detect transitions. const uint32_t prevCount = partyData.memberCount; - const uint8_t prevLootMethod = partyData.lootMethod; const bool wasInGroup = !partyData.isEmpty(); // Reset before parsing — SMSG_GROUP_LIST is a full replacement, not a delta. // Without this, repeated GROUP_LIST packets push duplicate members. @@ -19863,14 +19717,6 @@ void GameHandler::handleGroupList(network::Packet& packet) { } else if (nowInGroup && partyData.memberCount != prevCount) { LOG_INFO("Group updated: ", partyData.memberCount, " members"); } - // Loot method change notification - if (wasInGroup && nowInGroup && partyData.lootMethod != prevLootMethod) { - static const char* kLootMethods[] = { - "Free for All", "Round Robin", "Master Looter", "Group Loot", "Need Before Greed" - }; - const char* methodName = (partyData.lootMethod < 5) ? kLootMethods[partyData.lootMethod] : "Unknown"; - addSystemChatMessage(std::string("Loot method changed to ") + methodName + "."); - } // Fire GROUP_ROSTER_UPDATE / PARTY_MEMBERS_CHANGED for Lua addons if (addonEventCallback_) { addonEventCallback_("GROUP_ROSTER_UPDATE", {}); @@ -21069,7 +20915,6 @@ void GameHandler::handleQuestPoiQueryResponse(network::Packet& packet) { poi.name = questTitle.empty() ? "Quest objective" : questTitle; LOG_DEBUG("Quest POI: questId=", questId, " mapId=", mapId, " centroid=(", poi.x, ",", poi.y, ") title=", poi.name); - if (gossipPois_.size() >= 200) gossipPois_.erase(gossipPois_.begin()); gossipPois_.push_back(std::move(poi)); } } @@ -21131,10 +20976,6 @@ void GameHandler::addQuestToLocalLogIfMissing(uint32_t questId, const std::strin entry.title = title.empty() ? ("Quest #" + std::to_string(questId)) : title; entry.objectives = objectives; questLog_.push_back(std::move(entry)); - if (addonEventCallback_) { - addonEventCallback_("QUEST_ACCEPTED", {std::to_string(questId)}); - addonEventCallback_("QUEST_LOG_UPDATE", {}); - } } bool GameHandler::resyncQuestLogFromServerSlots(bool forceQueryMetadata) { @@ -21643,7 +21484,6 @@ void GameHandler::openVendor(uint64_t npcGuid) { } void GameHandler::closeVendor() { - bool wasOpen = vendorWindowOpen; vendorWindowOpen = false; currentVendorItems = ListInventoryData{}; buybackItems_.clear(); @@ -21652,7 +21492,6 @@ void GameHandler::closeVendor() { pendingBuybackWireSlot_ = 0; pendingBuyItemId_ = 0; pendingBuyItemSlot_ = 0; - if (wasOpen && addonEventCallback_) addonEventCallback_("MERCHANT_CLOSED", {}); } void GameHandler::buyItem(uint64_t vendorGuid, uint32_t itemId, uint32_t slot, uint32_t count) { @@ -22374,7 +22213,6 @@ void GameHandler::handleListInventory(network::Packet& packet) { currentVendorItems.canRepair = savedCanRepair; vendorWindowOpen = true; gossipWindowOpen = false; // Close gossip if vendor opens - if (addonEventCallback_) addonEventCallback_("MERCHANT_SHOW", {}); // Auto-sell grey items if enabled if (autoSellGrey_ && currentVendorItems.vendorGuid != 0) { @@ -22917,24 +22755,11 @@ void GameHandler::handleXpGain(network::Packet& packet) { // but we can show combat text for XP gains addCombatText(CombatTextEntry::XP_GAIN, static_cast(data.totalXp), 0, true); - // Build XP message with source creature name when available - std::string msg; - if (data.victimGuid != 0 && data.type == 0) { - // Kill XP — resolve creature name - std::string victimName = lookupName(data.victimGuid); - if (!victimName.empty()) - msg = victimName + " dies, you gain " + std::to_string(data.totalXp) + " experience."; - else - msg = "You gain " + std::to_string(data.totalXp) + " experience."; - } else { - msg = "You gain " + std::to_string(data.totalXp) + " experience."; - } + std::string msg = "You gain " + std::to_string(data.totalXp) + " experience."; if (data.groupBonus > 0) { msg += " (+" + std::to_string(data.groupBonus) + " group bonus)"; } addSystemChatMessage(msg); - if (addonEventCallback_) - addonEventCallback_("CHAT_MSG_COMBAT_XP_GAIN", {msg, std::to_string(data.totalXp)}); } @@ -22949,8 +22774,6 @@ void GameHandler::addMoneyCopper(uint32_t amount) { msg += std::to_string(silver) + "s "; msg += std::to_string(copper) + "c."; addSystemChatMessage(msg); - if (addonEventCallback_) - addonEventCallback_("CHAT_MSG_MONEY", {msg}); } void GameHandler::addSystemChatMessage(const std::string& message) { @@ -23131,24 +22954,7 @@ void GameHandler::handleNewWorld(network::Packet& packet) { mountCallback_(0); } - // Invoke despawn callbacks for all entities before clearing, so the renderer - // can release M2 instances, character models, and associated resources. - for (const auto& [guid, entity] : entityManager.getEntities()) { - if (guid == playerGuid) continue; // skip self - if (entity->getType() == ObjectType::UNIT && creatureDespawnCallback_) { - creatureDespawnCallback_(guid); - } else if (entity->getType() == ObjectType::PLAYER && playerDespawnCallback_) { - playerDespawnCallback_(guid); - } else if (entity->getType() == ObjectType::GAMEOBJECT && gameObjectDespawnCallback_) { - gameObjectDespawnCallback_(guid); - } - } - otherPlayerVisibleItemEntries_.clear(); - otherPlayerVisibleDirty_.clear(); - otherPlayerMoveTimeMs_.clear(); - unitCastStates_.clear(); - unitAurasCache_.clear(); - combatText.clear(); + // Clear world state for the new map entityManager.clear(); hostileAttackers_.clear(); worldStates_.clear(); @@ -24449,19 +24255,7 @@ void GameHandler::extractSkillFields(const std::map& fields) } } - bool skillsChanged = (newSkills.size() != playerSkills_.size()); - if (!skillsChanged) { - for (const auto& [id, sk] : newSkills) { - auto it = playerSkills_.find(id); - if (it == playerSkills_.end() || it->second.value != sk.value) { - skillsChanged = true; - break; - } - } - } playerSkills_ = std::move(newSkills); - if (skillsChanged && addonEventCallback_) - addonEventCallback_("SKILL_LINES_CHANGED", {}); } void GameHandler::extractExploredZoneFields(const std::map& fields) { @@ -24793,13 +24587,11 @@ void GameHandler::updateAttachedTransportChildren(float /*deltaTime*/) { // ============================================================ void GameHandler::closeMailbox() { - bool wasOpen = mailboxOpen_; mailboxOpen_ = false; mailboxGuid_ = 0; mailInbox_.clear(); selectedMailIndex_ = -1; showMailCompose_ = false; - if (wasOpen && addonEventCallback_) addonEventCallback_("MAIL_CLOSED", {}); } void GameHandler::refreshMailList() { @@ -24976,7 +24768,6 @@ void GameHandler::handleShowMailbox(network::Packet& packet) { hasNewMail_ = false; selectedMailIndex_ = -1; showMailCompose_ = false; - if (addonEventCallback_) addonEventCallback_("MAIL_SHOW", {}); // Request inbox contents refreshMailList(); } @@ -25135,10 +24926,8 @@ void GameHandler::openBank(uint64_t guid) { } void GameHandler::closeBank() { - bool wasOpen = bankOpen_; bankOpen_ = false; bankerGuid_ = 0; - if (wasOpen && addonEventCallback_) addonEventCallback_("BANKFRAME_CLOSED", {}); } void GameHandler::buyBankSlot() { @@ -25169,7 +24958,6 @@ void GameHandler::handleShowBank(network::Packet& packet) { bankerGuid_ = packet.readUInt64(); bankOpen_ = true; gossipWindowOpen = false; // Close gossip when bank opens - if (addonEventCallback_) addonEventCallback_("BANKFRAME_OPENED", {}); // Bank items are already tracked via update fields (bank slot GUIDs) // Trigger rebuild to populate bank slots in inventory rebuildOnlineInventory(); @@ -25288,10 +25076,8 @@ void GameHandler::openAuctionHouse(uint64_t guid) { } void GameHandler::closeAuctionHouse() { - bool wasOpen = auctionOpen_; auctionOpen_ = false; auctioneerGuid_ = 0; - if (wasOpen && addonEventCallback_) addonEventCallback_("AUCTION_HOUSE_CLOSED", {}); } void GameHandler::auctionSearch(const std::string& name, uint8_t levelMin, uint8_t levelMax, @@ -25372,7 +25158,6 @@ void GameHandler::handleAuctionHello(network::Packet& packet) { auctionHouseId_ = data.auctionHouseId; auctionOpen_ = true; gossipWindowOpen = false; // Close gossip when auction house opens - if (addonEventCallback_) addonEventCallback_("AUCTION_HOUSE_SHOW", {}); auctionActiveTab_ = 0; auctionBrowseResults_ = AuctionListResult{}; auctionOwnerResults_ = AuctionListResult{}; @@ -25913,10 +25698,9 @@ void GameHandler::handleLootRollWon(network::Packet& packet) { /*uint32_t slot =*/ packet.readUInt32(); uint64_t winnerGuid = packet.readUInt64(); uint32_t itemId = packet.readUInt32(); - int32_t wonRandProp = 0; if (isWotLK) { /*uint32_t randSuffix =*/ packet.readUInt32(); - wonRandProp = static_cast(packet.readUInt32()); + /*uint32_t randProp =*/ packet.readUInt32(); } uint8_t rollNum = packet.readUInt8(); uint8_t rollType = packet.readUInt8(); @@ -25935,10 +25719,6 @@ void GameHandler::handleLootRollWon(network::Packet& packet) { auto* info = getItemInfo(itemId); std::string iName = info && !info->name.empty() ? info->name : std::to_string(itemId); - if (wonRandProp != 0) { - std::string suffix = getRandomPropertyName(wonRandProp); - if (!suffix.empty()) iName += " " + suffix; - } uint32_t wonItemQuality = info ? info->quality : 1u; std::string wonItemLink = buildItemLink(itemId, wonItemQuality, iName); diff --git a/src/network/world_socket.cpp b/src/network/world_socket.cpp index 7fe18709..271fc0e9 100644 --- a/src/network/world_socket.cpp +++ b/src/network/world_socket.cpp @@ -668,7 +668,7 @@ void WorldSocket::tryParsePackets() { closeSocketNoJoin(); return; } - constexpr uint16_t kMaxWorldPacketSize = 0x8000; // 32KB — allows large guild rosters, auction lists + constexpr uint16_t kMaxWorldPacketSize = 0x4000; if (size > kMaxWorldPacketSize) { LOG_ERROR("World packet framing desync: oversized packet size=", size, " rawHdr=", std::hex, diff --git a/src/rendering/lens_flare.cpp b/src/rendering/lens_flare.cpp index 3dd6b734..820641af 100644 --- a/src/rendering/lens_flare.cpp +++ b/src/rendering/lens_flare.cpp @@ -313,12 +313,8 @@ void LensFlare::render(VkCommandBuffer cmd, const Camera& camera, const glm::vec return; } - // Sun height attenuation — flare weakens when sun is near horizon (sunrise/sunset) - float sunHeight = sunDir.z; // z = up in render space; 0 = horizon, 1 = zenith - float heightFactor = glm::smoothstep(-0.05f, 0.25f, sunHeight); - // Atmospheric attenuation — fog, clouds, and weather reduce lens flare - float atmosphericFactor = heightFactor; + float atmosphericFactor = 1.0f; atmosphericFactor *= (1.0f - glm::clamp(fogDensity * 0.8f, 0.0f, 0.9f)); // Heavy fog nearly kills flare atmosphericFactor *= (1.0f - glm::clamp(cloudDensity * 0.6f, 0.0f, 0.7f)); // Clouds attenuate atmosphericFactor *= (1.0f - glm::clamp(weatherIntensity * 0.9f, 0.0f, 0.95f)); // Rain/snow heavily attenuates @@ -343,9 +339,6 @@ void LensFlare::render(VkCommandBuffer cmd, const Camera& camera, const glm::vec VkDeviceSize offset = 0; vkCmdBindVertexBuffers(cmd, 0, 1, &vertexBuffer, &offset); - // Warm tint at sunrise/sunset — shift flare color toward orange/amber when sun is low - float warmTint = 1.0f - glm::smoothstep(0.05f, 0.35f, sunHeight); - // Render each flare element for (const auto& element : flareElements) { // Calculate position along sun-to-center axis @@ -354,19 +347,12 @@ void LensFlare::render(VkCommandBuffer cmd, const Camera& camera, const glm::vec // Apply visibility, intensity, and atmospheric attenuation float brightness = element.brightness * visibility * intensityMultiplier * atmosphericFactor; - // Apply warm sunset/sunrise color shift - glm::vec3 tintedColor = element.color; - if (warmTint > 0.01f) { - glm::vec3 warmColor(1.0f, 0.6f, 0.25f); // amber/orange - tintedColor = glm::mix(tintedColor, warmColor, warmTint * 0.5f); - } - // Set push constants FlarePushConstants push{}; push.position = position; push.size = element.size; push.aspectRatio = aspectRatio; - push.colorBrightness = glm::vec4(tintedColor, brightness); + push.colorBrightness = glm::vec4(element.color, brightness); vkCmdPushConstants(cmd, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, diff --git a/src/rendering/loading_screen.cpp b/src/rendering/loading_screen.cpp index 92c1fe1c..a2e83a2b 100644 --- a/src/rendering/loading_screen.cpp +++ b/src/rendering/loading_screen.cpp @@ -261,20 +261,6 @@ void LoadingScreen::renderOverlay() { ImVec2(0, 0), ImVec2(screenW, screenH)); } - // Zone name header - if (!zoneName.empty()) { - ImFont* font = ImGui::GetFont(); - float zoneTextSize = 24.0f; - ImVec2 zoneSize = font->CalcTextSizeA(zoneTextSize, FLT_MAX, 0.0f, zoneName.c_str()); - float zoneX = (screenW - zoneSize.x) * 0.5f; - float zoneY = screenH * 0.06f - 44.0f; - ImDrawList* dl = ImGui::GetWindowDrawList(); - dl->AddText(font, zoneTextSize, ImVec2(zoneX + 2.0f, zoneY + 2.0f), - IM_COL32(0, 0, 0, 200), zoneName.c_str()); - dl->AddText(font, zoneTextSize, ImVec2(zoneX, zoneY), - IM_COL32(255, 220, 120, 255), zoneName.c_str()); - } - // Progress bar { const float barWidthFrac = 0.6f; @@ -346,22 +332,6 @@ void LoadingScreen::render() { ImVec2(0, 0), ImVec2(screenW, screenH)); } - // Zone name header (large text centered above progress bar) - if (!zoneName.empty()) { - ImFont* font = ImGui::GetFont(); - float zoneTextSize = 24.0f; - ImVec2 zoneSize = font->CalcTextSizeA(zoneTextSize, FLT_MAX, 0.0f, zoneName.c_str()); - float zoneX = (screenW - zoneSize.x) * 0.5f; - float zoneY = screenH * 0.06f - 44.0f; // above percentage text - ImDrawList* dl = ImGui::GetWindowDrawList(); - // Drop shadow - dl->AddText(font, zoneTextSize, ImVec2(zoneX + 2.0f, zoneY + 2.0f), - IM_COL32(0, 0, 0, 200), zoneName.c_str()); - // Gold text - dl->AddText(font, zoneTextSize, ImVec2(zoneX, zoneY), - IM_COL32(255, 220, 120, 255), zoneName.c_str()); - } - // Progress bar (top of screen) { const float barWidthFrac = 0.6f; diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp index 7199273d..11c37bab 100644 --- a/src/rendering/renderer.cpp +++ b/src/rendering/renderer.cpp @@ -3192,7 +3192,7 @@ void Renderer::update(float deltaTime) { // Server-driven weather (SMSG_WEATHER) — authoritative if (wType == 1) weather->setWeatherType(Weather::Type::RAIN); else if (wType == 2) weather->setWeatherType(Weather::Type::SNOW); - else if (wType == 3) weather->setWeatherType(Weather::Type::STORM); + else if (wType == 3) weather->setWeatherType(Weather::Type::RAIN); // thunderstorm — use rain particles else weather->setWeatherType(Weather::Type::NONE); weather->setIntensity(wInt); } else { diff --git a/src/rendering/sky_system.cpp b/src/rendering/sky_system.cpp index 98e27621..9509cdc2 100644 --- a/src/rendering/sky_system.cpp +++ b/src/rendering/sky_system.cpp @@ -135,14 +135,6 @@ void SkySystem::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet, // --- Clouds (DBC-driven colors + sun lighting) --- if (clouds_) { - // Sync cloud density with weather/DBC-driven cloud coverage. - // Active weather (rain/snow/storm) increases cloud density for visual consistency. - float effectiveDensity = params.cloudDensity; - if (params.weatherIntensity > 0.05f) { - float weatherBoost = params.weatherIntensity * 0.4f; // storms add up to 0.4 density - effectiveDensity = glm::min(1.0f, effectiveDensity + weatherBoost); - } - clouds_->setDensity(effectiveDensity); clouds_->render(cmd, perFrameSet, params); } diff --git a/src/rendering/weather.cpp b/src/rendering/weather.cpp index 5dc525da..fed604dc 100644 --- a/src/rendering/weather.cpp +++ b/src/rendering/weather.cpp @@ -198,10 +198,6 @@ void Weather::update(const Camera& camera, float deltaTime) { if (weatherType == Type::RAIN) { p.velocity = glm::vec3(0.0f, -50.0f, 0.0f); // Fast downward p.maxLifetime = 5.0f; - } else if (weatherType == Type::STORM) { - // Storm: faster, angled rain with wind - p.velocity = glm::vec3(15.0f, -70.0f, 8.0f); - p.maxLifetime = 3.5f; } else { // SNOW p.velocity = glm::vec3(0.0f, -5.0f, 0.0f); // Slow downward p.maxLifetime = 10.0f; @@ -249,12 +245,6 @@ void Weather::updateParticle(Particle& particle, const Camera& camera, float del particle.velocity.x = windX; particle.velocity.z = windZ; } - // Storm: gusty, turbulent wind with varying direction - if (weatherType == Type::STORM) { - float gust = std::sin(particle.lifetime * 1.5f + particle.position.x * 0.1f) * 5.0f; - particle.velocity.x = 15.0f + gust; - particle.velocity.z = 8.0f + std::cos(particle.lifetime * 2.0f) * 3.0f; - } // Update position particle.position += particle.velocity * deltaTime; @@ -285,9 +275,6 @@ void Weather::render(VkCommandBuffer cmd, VkDescriptorSet perFrameSet) { if (weatherType == Type::RAIN) { push.particleSize = 3.0f; push.particleColor = glm::vec4(0.7f, 0.8f, 0.9f, 0.6f); - } else if (weatherType == Type::STORM) { - push.particleSize = 3.5f; - push.particleColor = glm::vec4(0.6f, 0.65f, 0.75f, 0.7f); // Darker, more opaque } else { // SNOW push.particleSize = 8.0f; push.particleColor = glm::vec4(1.0f, 1.0f, 1.0f, 0.9f); diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 18d0a6ec..21e9a0b2 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -720,7 +720,6 @@ void GameScreen::render(game::GameHandler& gameHandler) { renderBgInvitePopup(gameHandler); renderBfMgrInvitePopup(gameHandler); renderLfgProposalPopup(gameHandler); - renderLfgRoleCheckPopup(gameHandler); renderGuildRoster(gameHandler); renderSocialFrame(gameHandler); renderBuffBar(gameHandler); @@ -2617,32 +2616,24 @@ void GameScreen::renderChatWindow(game::GameHandler& gameHandler) { static const std::vector kCmds = { "/afk", "/assist", "/away", - "/cancelaura", "/cancelform", "/cancellogout", "/cancelshapeshift", - "/cast", "/castsequence", "/chathelp", "/clear", "/clearfocus", - "/clearmainassist", "/clearmaintank", "/cleartarget", "/cloak", - "/combatlog", "/dance", "/dismount", "/dnd", "/do", "/duel", "/dump", + "/cancelaura", "/cancelform", "/cancelshapeshift", + "/cast", "/castsequence", "/chathelp", "/clear", "/clearfocus", "/cleartarget", + "/combatlog", "/dance", "/dismount", "/dnd", "/do", "/duel", "/e", "/emote", "/equip", "/equipset", "/focus", "/follow", "/forfeit", "/friend", - "/g", "/gdemote", "/ginvite", "/gkick", "/gleader", "/gmotd", - "/gmticket", "/gpromote", "/gquit", "/grouploot", "/groster", - "/guild", "/guildinfo", + "/g", "/ginvite", "/gmticket", "/grouploot", "/guild", "/guildinfo", "/helm", "/help", "/i", "/ignore", "/inspect", "/instance", "/invite", "/j", "/join", "/kick", "/kneel", - "/l", "/leave", "/leaveparty", "/loc", "/local", "/logout", - "/macrohelp", "/mainassist", "/maintank", "/mark", "/me", - "/notready", + "/l", "/leave", "/loc", "/local", "/logout", + "/macrohelp", "/mark", "/me", "/p", "/party", "/petaggressive", "/petattack", "/petdefensive", "/petdismiss", "/petfollow", "/pethalt", "/petpassive", "/petstay", "/played", "/pvp", - "/r", "/raid", "/raidinfo", "/raidwarning", "/random", "/ready", - "/readycheck", "/reload", "/reloadui", "/removefriend", - "/reply", "/rl", "/roll", "/run", - "/s", "/say", "/score", "/screenshot", "/script", "/setloot", - "/shout", "/sit", "/stand", + "/r", "/raid", "/raidinfo", "/raidwarning", "/random", "/reply", "/roll", "/run", + "/s", "/say", "/screenshot", "/setloot", "/shout", "/sit", "/stand", "/startattack", "/stopattack", "/stopcasting", "/stopfollow", "/stopmacro", - "/t", "/target", "/targetenemy", "/targetfriend", "/targetlast", - "/threat", "/ticket", "/time", "/trade", + "/t", "/target", "/threat", "/time", "/trade", "/unignore", "/uninvite", "/unstuck", "/use", "/w", "/whisper", "/who", "/wts", "/wtb", "/y", "/yell", "/zone" @@ -6016,30 +6007,6 @@ void GameScreen::sendChatMessage(game::GameHandler& gameHandler) { return; } - // /dump — evaluate Lua expression and print result - if ((cmdLower == "dump" || cmdLower == "print") && spacePos != std::string::npos) { - std::string expr = command.substr(spacePos + 1); - auto* am = core::Application::getInstance().getAddonManager(); - if (am && am->isInitialized()) { - // Wrap expression in print(tostring(...)) to display the value - std::string wrapped = "local __v = " + expr + - "; if type(__v) == 'table' then " - " local parts = {} " - " for k,v in pairs(__v) do parts[#parts+1] = tostring(k)..'='..tostring(v) end " - " print('{' .. table.concat(parts, ', ') .. '}') " - "else print(tostring(__v)) end"; - am->runScript(wrapped); - } else { - game::MessageChatData errMsg; - errMsg.type = game::ChatType::SYSTEM; - errMsg.language = game::ChatLanguage::UNIVERSAL; - errMsg.message = "Addon system not initialized."; - gameHandler.addLocalChatMessage(errMsg); - } - chatInputBuffer[0] = '\0'; - return; - } - // Check addon slash commands (SlashCmdList) before built-in commands { auto* am = core::Application::getInstance().getAddonManager(); @@ -6067,30 +6034,6 @@ void GameScreen::sendChatMessage(game::GameHandler& gameHandler) { return; } - // /reload or /reloadui — reload all addons (save variables, re-init Lua, re-scan .toc files) - if (cmdLower == "reload" || cmdLower == "reloadui" || cmdLower == "rl") { - auto* am = core::Application::getInstance().getAddonManager(); - if (am) { - am->reload(); - am->fireEvent("VARIABLES_LOADED"); - am->fireEvent("PLAYER_LOGIN"); - am->fireEvent("PLAYER_ENTERING_WORLD"); - game::MessageChatData rlMsg; - rlMsg.type = game::ChatType::SYSTEM; - rlMsg.language = game::ChatLanguage::UNIVERSAL; - rlMsg.message = "Interface reloaded."; - gameHandler.addLocalChatMessage(rlMsg); - } else { - game::MessageChatData rlMsg; - rlMsg.type = game::ChatType::SYSTEM; - rlMsg.language = game::ChatLanguage::UNIVERSAL; - rlMsg.message = "Addon system not available."; - gameHandler.addLocalChatMessage(rlMsg); - } - chatInputBuffer[0] = '\0'; - return; - } - // /stopmacro [conditions] // Halts execution of the current macro (remaining lines are skipped). // With a condition block, only stops if the conditions evaluate to true. @@ -7140,12 +7083,6 @@ void GameScreen::sendChatMessage(game::GameHandler& gameHandler) { return; } - if (cmdLower == "cancelqueuedspell" || cmdLower == "stopspellqueue") { - gameHandler.cancelQueuedSpell(); - chatInputBuffer[0] = '\0'; - return; - } - // /equipset [name] — equip a saved equipment set by name (partial match, case-insensitive) // /equipset — list available sets in chat if (cmdLower == "equipset") { @@ -9475,7 +9412,7 @@ void GameScreen::renderActionBar(game::GameHandler& gameHandler) { } } if (totalCount > 0) { - char countStr[16]; + char countStr[8]; snprintf(countStr, sizeof(countStr), "%d", totalCount); ImVec2 btnMax = ImGui::GetItemRectMax(); ImVec2 tsz = ImGui::CalcTextSize(countStr); @@ -14264,71 +14201,6 @@ void GameScreen::renderLfgProposalPopup(game::GameHandler& gameHandler) { ImGui::PopStyleColor(3); } -void GameScreen::renderLfgRoleCheckPopup(game::GameHandler& gameHandler) { - using LfgState = game::GameHandler::LfgState; - if (gameHandler.getLfgState() != LfgState::RoleCheck) return; - - auto* window = core::Application::getInstance().getWindow(); - float screenW = window ? static_cast(window->getWidth()) : 1280.0f; - float screenH = window ? static_cast(window->getHeight()) : 720.0f; - - ImGui::SetNextWindowPos(ImVec2(screenW / 2.0f - 160.0f, screenH / 2.0f - 80.0f), ImGuiCond_Always); - ImGui::SetNextWindowSize(ImVec2(320.0f, 0.0f), ImGuiCond_Always); - - ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.08f, 0.08f, 0.18f, 0.96f)); - ImGui::PushStyleColor(ImGuiCol_Border, ImVec4(0.3f, 0.5f, 0.9f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_TitleBgActive, ImVec4(0.1f, 0.1f, 0.3f, 1.0f)); - ImGui::PushStyleVar(ImGuiStyleVar_WindowRounding, 6.0f); - - const ImGuiWindowFlags flags = - ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse; - - if (ImGui::Begin("Role Check##LfgRoleCheck", nullptr, flags)) { - ImGui::TextColored(ImVec4(0.4f, 0.7f, 1.0f, 1.0f), "Confirm your role:"); - ImGui::Spacing(); - - // Role checkboxes - bool isTank = (lfgRoles_ & 0x02) != 0; - bool isHealer = (lfgRoles_ & 0x04) != 0; - bool isDps = (lfgRoles_ & 0x08) != 0; - - if (ImGui::Checkbox("Tank", &isTank)) lfgRoles_ = (lfgRoles_ & ~0x02) | (isTank ? 0x02 : 0); - ImGui::SameLine(120.0f); - if (ImGui::Checkbox("Healer", &isHealer)) lfgRoles_ = (lfgRoles_ & ~0x04) | (isHealer ? 0x04 : 0); - ImGui::SameLine(220.0f); - if (ImGui::Checkbox("DPS", &isDps)) lfgRoles_ = (lfgRoles_ & ~0x08) | (isDps ? 0x08 : 0); - - ImGui::Spacing(); - ImGui::Separator(); - ImGui::Spacing(); - - bool hasRole = (lfgRoles_ & 0x0E) != 0; - if (!hasRole) ImGui::BeginDisabled(); - - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.4f, 0.15f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.2f, 0.6f, 0.2f, 1.0f)); - if (ImGui::Button("Accept", ImVec2(140.0f, 28.0f))) { - gameHandler.lfgSetRoles(lfgRoles_); - } - ImGui::PopStyleColor(2); - - if (!hasRole) ImGui::EndDisabled(); - - ImGui::SameLine(); - - ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.4f, 0.15f, 0.15f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.6f, 0.2f, 0.2f, 1.0f)); - if (ImGui::Button("Leave Queue", ImVec2(140.0f, 28.0f))) { - gameHandler.lfgLeave(); - } - ImGui::PopStyleColor(2); - } - ImGui::End(); - - ImGui::PopStyleVar(); - ImGui::PopStyleColor(3); -} - void GameScreen::renderGuildRoster(game::GameHandler& gameHandler) { // Guild Roster toggle (customizable keybind) if (!chatInputActive && !ImGui::GetIO().WantTextInput && @@ -22305,12 +22177,6 @@ void GameScreen::renderAuctionHouseWindow(game::GameHandler& gameHandler) { const auto& auction = results.auctions[i]; auto* info = gameHandler.getItemInfo(auction.itemEntry); std::string name = info ? info->name : ("Item #" + std::to_string(auction.itemEntry)); - // Append random suffix name (e.g., "of the Eagle") if present - if (auction.randomPropertyId != 0) { - std::string suffix = gameHandler.getRandomPropertyName( - static_cast(auction.randomPropertyId)); - if (!suffix.empty()) name += " " + suffix; - } game::ItemQuality quality = info ? static_cast(info->quality) : game::ItemQuality::COMMON; ImVec4 qc = InventoryScreen::getQualityColor(quality); @@ -22504,11 +22370,6 @@ void GameScreen::renderAuctionHouseWindow(game::GameHandler& gameHandler) { const auto& a = results.auctions[bi]; auto* info = gameHandler.getItemInfo(a.itemEntry); std::string name = info ? info->name : ("Item #" + std::to_string(a.itemEntry)); - if (a.randomPropertyId != 0) { - std::string suffix = gameHandler.getRandomPropertyName( - static_cast(a.randomPropertyId)); - if (!suffix.empty()) name += " " + suffix; - } game::ItemQuality quality = info ? static_cast(info->quality) : game::ItemQuality::COMMON; ImVec4 bqc = InventoryScreen::getQualityColor(quality); @@ -22521,15 +22382,6 @@ void GameScreen::renderAuctionHouseWindow(game::GameHandler& gameHandler) { ImGui::SameLine(); } } - // High bidder indicator - bool isHighBidder = (a.bidderGuid != 0 && a.bidderGuid == gameHandler.getPlayerGuid()); - if (isHighBidder) { - ImGui::TextColored(ImVec4(0.2f, 0.9f, 0.2f, 1.0f), "[Winning]"); - ImGui::SameLine(); - } else if (a.bidderGuid != 0) { - ImGui::TextColored(ImVec4(0.9f, 0.3f, 0.3f, 1.0f), "[Outbid]"); - ImGui::SameLine(); - } ImGui::TextColored(bqc, "%s", name.c_str()); // Tooltip and shift-click if (ImGui::IsItemHovered() && info && info->valid) @@ -22593,11 +22445,6 @@ void GameScreen::renderAuctionHouseWindow(game::GameHandler& gameHandler) { const auto& a = results.auctions[i]; auto* info = gameHandler.getItemInfo(a.itemEntry); std::string name = info ? info->name : ("Item #" + std::to_string(a.itemEntry)); - if (a.randomPropertyId != 0) { - std::string suffix = gameHandler.getRandomPropertyName( - static_cast(a.randomPropertyId)); - if (!suffix.empty()) name += " " + suffix; - } game::ItemQuality quality = info ? static_cast(info->quality) : game::ItemQuality::COMMON; ImGui::TableNextRow(); @@ -22610,11 +22457,6 @@ void GameScreen::renderAuctionHouseWindow(game::GameHandler& gameHandler) { ImGui::SameLine(); } } - // Bid activity indicator for seller - if (a.bidderGuid != 0) { - ImGui::TextColored(ImVec4(1.0f, 0.85f, 0.2f, 1.0f), "[Bid]"); - ImGui::SameLine(); - } ImGui::TextColored(oqc, "%s", name.c_str()); if (ImGui::IsItemHovered() && info && info->valid) inventoryScreen.renderItemTooltip(*info);