From 370167b3a717b64b74c716da767e4a8c4b406c8d Mon Sep 17 00:00:00 2001 From: Jrc13245 Date: Sat, 27 Dec 2025 14:38:24 -0500 Subject: [PATCH] update for nampower errors --- ComboPointTracker.lua | 19 ++++---- Conditionals.lua | 29 ++++++++++-- Core.lua | 91 +++++++++++++++++++++++++---------- Init.lua | 18 ++++++- NampowerAPI.lua | 43 +++++++++++++++-- Utility.lua | 108 ++++++++++++++++++++++++++++++++++++------ 6 files changed, 250 insertions(+), 58 deletions(-) diff --git a/ComboPointTracker.lua b/ComboPointTracker.lua index 8dd05b8..1169daa 100644 --- a/ComboPointTracker.lua +++ b/ComboPointTracker.lua @@ -224,14 +224,17 @@ function CleveRoids.CalculateComboScaledDurationByID(spellID, comboPoints) baseDuration = data.base + (comboPoints - 1) * data.increment end - -- Apply talent modifiers (e.g., Taste for Blood) - additive - if CleveRoids.ApplyTalentModifier then - baseDuration = CleveRoids.ApplyTalentModifier(spellID, baseDuration) - end - - -- Apply equipment modifiers (e.g., Black Morass Idol) - multiplicative - if CleveRoids.ApplyEquipmentModifier then - baseDuration = CleveRoids.ApplyEquipmentModifier(spellID, baseDuration) + -- Apply all duration modifiers (Nampower, talents, equipment, set bonuses) + if CleveRoids.ApplyAllDurationModifiers then + baseDuration = CleveRoids.ApplyAllDurationModifiers(spellID, baseDuration) + else + -- Fallback for load order (ApplyAllDurationModifiers not yet defined) + if CleveRoids.ApplyTalentModifier then + baseDuration = CleveRoids.ApplyTalentModifier(spellID, baseDuration) + end + if CleveRoids.ApplyEquipmentModifier then + baseDuration = CleveRoids.ApplyEquipmentModifier(spellID, baseDuration) + end end return baseDuration diff --git a/Conditionals.lua b/Conditionals.lua index 3a8b633..5857362 100644 --- a/Conditionals.lua +++ b/Conditionals.lua @@ -154,6 +154,9 @@ local _equippedItemIDs = {} -- [slot] = itemID (number) local _equippedItemNames = {} -- [slot] = itemName (lowercase string) local _equipmentCacheValid = false +-- Track if we've warned about GetEquippedItems errors (warn once per session) +local _getEquippedItemsErrorWarned = false + local function BuildEquipmentCache() if _equipmentCacheValid then return end _equipmentCacheValid = true @@ -168,11 +171,28 @@ local function BuildEquipmentCache() local string_lower = string.lower -- Try Nampower v2.18+ GetEquippedItems for faster enumeration - if GetEquippedItems then - local items = GetEquippedItems("player") - if items and type(items) == "table" then + -- Requires version check because the function may exist but be broken in older versions + local API = CleveRoids.NampowerAPI + local hasValidNampower = API and API.HasMinimumVersion and API.HasMinimumVersion(2, 18, 0) + + if hasValidNampower and GetEquippedItems then + -- Use pcall to catch any internal Nampower errors and fall back gracefully + local success, result = pcall(GetEquippedItems, "player") + + if not success then + -- Log the error once per session for debugging + if not _getEquippedItemsErrorWarned then + _getEquippedItemsErrorWarned = true + local errMsg = tostring(result) + if CleveRoids.Print then + CleveRoids.Print("|cffff6600Warning:|r GetEquippedItems failed: " .. errMsg) + CleveRoids.Print("Using fallback equipment detection. Consider updating Nampower.") + end + end + -- Fall through to manual enumeration + elseif result and type(result) == "table" then local usedNampower = false - for nampowerSlot, itemInfo in pairs(items) do + for nampowerSlot, itemInfo in pairs(result) do -- Nampower uses 0-indexed slots, WoW API uses 1-indexed -- tonumber() handles both string and numeric keys from different Nampower versions local slot = tonumber(nampowerSlot) + 1 @@ -182,7 +202,6 @@ local function BuildEquipmentCache() usedNampower = true -- Get item name via Nampower API or GetItemInfo - local API = CleveRoids.NampowerAPI local itemName = API and API.GetItemName and API.GetItemName(itemInfo.itemId) if not itemName then itemName = GetItemInfo(itemInfo.itemId) diff --git a/Core.lua b/Core.lua index c63f205..9a65af1 100644 --- a/Core.lua +++ b/Core.lua @@ -3570,19 +3570,26 @@ function IsCurrentAction(slot) name = actionToCheck.spell.name..(rank and ("("..rank..")")) -- Check if this spell is currently queued or being cast via Nampower - if GetCurrentCastingInfo then - local castId, visId, autoId, casting, channeling, onswing, autoattack = GetCurrentCastingInfo() + -- Get spell ID for comparison + local spellId = actionToCheck.spell.id + if not spellId and GetSpellIdForName then + spellId = GetSpellIdForName(name) + end - -- Get spell ID for comparison - local spellId = actionToCheck.spell.id - if not spellId and GetSpellIdForName then - spellId = GetSpellIdForName(name) + if spellId then + -- Prefer GetCastInfo (Nampower 2.18+) for cleaner API + if GetCastInfo then + local info = GetCastInfo() + if info and info.spellId == spellId then + -- Spell is actively being cast/channeled + return true + end end - -- Only show glow if spell is actively casting, channeling, or queued - -- castId matches when spell is queued or being cast - -- visId matches during channeling - if spellId then + -- Also check GetCurrentCastingInfo for queued spell detection + if GetCurrentCastingInfo then + local castId, visId, autoId, casting, channeling = GetCurrentCastingInfo() + -- Show glow if actively casting/channeling this spell if (casting == 1 and castId == spellId) or (channeling == 1 and visId == spellId) then return true @@ -4196,15 +4203,30 @@ function CleveRoids.Frame:SPELLCAST_CHANNEL_START() -- Duration is captured by UNIT_CASTEVENT which fires earlier CleveRoids.CurrentSpell.type = "channeled" - -- Try to get spell name from GetCurrentCastingInfo if available - if GetCurrentCastingInfo then - local castId, visId, autoId, casting, channeling, onswing, autoattack = GetCurrentCastingInfo() + -- Try to get spell info - prefer GetCastInfo (Nampower 2.18+) for better data + local spellId = nil + if GetCastInfo then + local info = GetCastInfo() + if info and info.spellId and info.spellId > 0 then + spellId = info.spellId + -- Also capture timing data + CleveRoids.CurrentSpell.castRemainingMs = info.castRemainingMs + CleveRoids.CurrentSpell.castEndTime = info.castEndS + end + end + -- Fallback to GetCurrentCastingInfo for older Nampower + if not spellId and GetCurrentCastingInfo then + local _, visId = GetCurrentCastingInfo() if visId and visId > 0 then - CleveRoids.CurrentSpell.castingSpellId = visId - local spellName = SpellInfo(visId) - if spellName then - CleveRoids.CurrentSpell.spellName = spellName - end + spellId = visId + end + end + -- Update spell info + if spellId then + CleveRoids.CurrentSpell.castingSpellId = spellId + local spellName = SpellInfo(spellId) + if spellName then + CleveRoids.CurrentSpell.spellName = spellName end end @@ -4227,15 +4249,32 @@ function CleveRoids.Frame:SPELLCAST_START() -- Duration is captured by UNIT_CASTEVENT which fires earlier CleveRoids.CurrentSpell.type = "cast" - -- Try to get spell name from GetCurrentCastingInfo if available - if GetCurrentCastingInfo then - local castId, visId, autoId, casting, channeling, onswing, autoattack = GetCurrentCastingInfo() + -- Try to get spell info - prefer GetCastInfo (Nampower 2.18+) for better data + local spellId = nil + if GetCastInfo then + local info = GetCastInfo() + if info and info.spellId and info.spellId > 0 then + spellId = info.spellId + -- Also capture timing data + CleveRoids.CurrentSpell.castRemainingMs = info.castRemainingMs + CleveRoids.CurrentSpell.castEndTime = info.castEndS + CleveRoids.CurrentSpell.gcdRemainingMs = info.gcdRemainingMs + CleveRoids.CurrentSpell.gcdEndTime = info.gcdEndS + end + end + -- Fallback to GetCurrentCastingInfo for older Nampower + if not spellId and GetCurrentCastingInfo then + local castId = GetCurrentCastingInfo() if castId and castId > 0 then - CleveRoids.CurrentSpell.castingSpellId = castId - local spellName = SpellInfo(castId) - if spellName then - CleveRoids.CurrentSpell.spellName = spellName - end + spellId = castId + end + end + -- Update spell info + if spellId then + CleveRoids.CurrentSpell.castingSpellId = spellId + local spellName = SpellInfo(spellId) + if spellName then + CleveRoids.CurrentSpell.spellName = spellName end end diff --git a/Init.lua b/Init.lua index 1451e56..ad56807 100644 --- a/Init.lua +++ b/Init.lua @@ -79,12 +79,28 @@ CleveRoids.UpdateCastingState = function() end -- DO NOT touch channel state here - events handle it - -- Always update metadata + -- Always update metadata from GetCurrentCastingInfo (onswing/autoattack only here) CleveRoids.CurrentSpell.autoAttack = (autoattack == 1) CleveRoids.CurrentSpell.onSwingPending = (onswing == 1) CleveRoids.CurrentSpell.visualSpellId = visId CleveRoids.CurrentSpell.autoRepeatSpellId = autoId + -- Enhanced timing data from GetCastInfo (Nampower 2.18+) + if GetCastInfo then + local info = GetCastInfo() + if info then + CleveRoids.CurrentSpell.castRemainingMs = info.castRemainingMs + CleveRoids.CurrentSpell.castEndTime = info.castEndS + CleveRoids.CurrentSpell.gcdRemainingMs = info.gcdRemainingMs + CleveRoids.CurrentSpell.gcdEndTime = info.gcdEndS + else + CleveRoids.CurrentSpell.castRemainingMs = nil + CleveRoids.CurrentSpell.castEndTime = nil + CleveRoids.CurrentSpell.gcdRemainingMs = nil + CleveRoids.CurrentSpell.gcdEndTime = nil + end + end + return true end diff --git a/NampowerAPI.lua b/NampowerAPI.lua index 66adce9..386759f 100644 --- a/NampowerAPI.lua +++ b/NampowerAPI.lua @@ -32,9 +32,14 @@ - Pass 1 to get independent copy safe for storage - Without copy, table references are reused - extract values immediately! + Utility Functions (v2.22+): + - DisenchantAll - Auto-disenchant items by ID/name or quality + Settings Integration: - Reads from NampowerSettings addon when available - Falls back to CVars when addon not present + + Current version: v2.22.0 ]] local _G = _G or getfenv(0) @@ -114,6 +119,9 @@ API.features = { hasGetTrinketCooldown = (GetTrinketCooldown ~= nil), hasUseTrinket = (UseTrinket ~= nil), hasUseItemIdOrName = (UseItemIdOrName ~= nil), + + -- v2.22+ APIs (utility functions) + hasDisenchantAll = (DisenchantAll ~= nil), } -- Detect if enhanced spell functions are available (accept name/spellId:number) @@ -1337,10 +1345,13 @@ function API.GetTrinkets(copy) local _, _, name = string.find(link, "|h%[(.-)%]|h") local texture = GetInventoryItemTexture("player", slot) if itemId then + local numItemId = tonumber(itemId) + local itemLevel = API.GetItemLevel(numItemId) trinkets[index] = { - itemId = tonumber(itemId), + itemId = numItemId, trinketName = name or "Unknown", texture = texture, + itemLevel = itemLevel, bagIndex = nil, -- nil means equipped slotIndex = slot == 13 and 1 or 2, } @@ -1357,14 +1368,17 @@ function API.GetTrinkets(copy) if link then local _, _, itemId = string.find(link, "item:(%d+)") if itemId then - local invType = API.GetItemInventoryType(tonumber(itemId)) + local numItemId = tonumber(itemId) + local invType = API.GetItemInventoryType(numItemId) if invType == 12 then -- Trinket local _, _, name = string.find(link, "|h%[(.-)%]|h") local texture = GetContainerItemInfo(bag, slot) + local itemLevel = API.GetItemLevel(numItemId) trinkets[index] = { - itemId = tonumber(itemId), + itemId = numItemId, trinketName = name or "Unknown", texture = texture, + itemLevel = itemLevel, bagIndex = bag, slotIndex = slot, } @@ -1521,6 +1535,29 @@ function API.UseItemIdOrName(itemIdOrName, target) return 0 end +-------------------------------------------------------------------------------- +-- DISENCHANT API (v2.22+) +-------------------------------------------------------------------------------- + +-- Automatically disenchant items in inventory +-- Mode 1: DisenchantAll(itemIdOrName, [includeSoulbound]) - specific item by ID/name +-- Mode 2: DisenchantAll(quality, [includeSoulbound]) - "greens" or "blues" +-- includeSoulbound: pass 1 to include soulbound items (default: 0) +-- Returns: 1 if first disenchant succeeded, 0 if no items found or failed +-- +-- WARNING: This function WILL disenchant items without confirmation! +-- Quest items are always protected. Soulbound items protected by default. +-- Only affects bags 0-4 (not bank). +function API.DisenchantAll(itemIdOrNameOrQuality, includeSoulbound) + -- Use native function if available (v2.22+) + if DisenchantAll then + return DisenchantAll(itemIdOrNameOrQuality, includeSoulbound) + end + + -- No fallback available - requires Nampower 2.22+ + return 0 +end + -------------------------------------------------------------------------------- -- EVENT CONSTANTS (v2.18+, updated v2.20) -------------------------------------------------------------------------------- diff --git a/Utility.lua b/Utility.lua index 28ce31b..85c4c09 100644 --- a/Utility.lua +++ b/Utility.lua @@ -2124,21 +2124,10 @@ ev:SetScript("OnEvent", function() if not duration then duration = lib:GetDuration(spellID, casterGUID) - -- Apply talent modifiers for non-combo spells - -- (combo spells already have talent modifiers applied in CalculateComboScaledDurationByID) - if duration and CleveRoids.ApplyTalentModifier then - duration = CleveRoids.ApplyTalentModifier(spellID, duration) - end - - -- Apply equipment modifiers for non-combo spells - -- (combo spells already have equipment modifiers applied in CalculateComboScaledDurationByID) - if duration and CleveRoids.ApplyEquipmentModifier then - duration = CleveRoids.ApplyEquipmentModifier(spellID, duration) - end - - -- Apply set bonus modifiers for non-combo spells - if duration and CleveRoids.ApplySetBonusModifier then - duration = CleveRoids.ApplySetBonusModifier(spellID, duration) + -- Apply all duration modifiers (Nampower, talents, equipment, set bonuses) + -- (combo spells already have modifiers applied in CalculateComboScaledDurationByID) + if duration and CleveRoids.ApplyAllDurationModifiers then + duration = CleveRoids.ApplyAllDurationModifiers(spellID, duration) end end @@ -3289,6 +3278,95 @@ function CleveRoids.RegisterEquipmentModifier(spellID, slotID, modifierFunc) return true end +-- ============================================================================ +-- NAMPOWER SPELL MODIFIERS INTEGRATION (v2.18+) +-- ============================================================================ + +-- Try to get duration modifiers from Nampower's GetSpellModifiers +-- This function supplements (not replaces) the talent/equipment modifier system +-- Returns: Modified duration, or nil if no Nampower modifier applies +-- Parameters: +-- spellID: The spell ID +-- baseDuration: The base duration before modifiers +function CleveRoids.ApplyNampowerDurationModifier(spellID, baseDuration) + if not spellID or not baseDuration then + return nil + end + + -- Check if Nampower API is available with GetSpellModifiers + local API = CleveRoids.NampowerAPI + if not API or not GetSpellModifiers then + return nil + end + + -- MODIFIER_DURATION = 1 + local flat, percent, hasModifier = GetSpellModifiers(spellID, 1) + + if not hasModifier or (flat == 0 and percent == 0) then + return nil + end + + -- Apply flat modifier first, then percentage (same as API.GetModifiedDuration) + local modified = baseDuration + (flat or 0) + if percent and percent ~= 0 then + modified = modified * (1 + percent / 100) + end + + if CleveRoids.debug then + DEFAULT_CHAT_FRAME:AddMessage( + string.format("|cffff00ff[Nampower Duration Modifier]|r %s (ID:%d): %ds -> %ds (flat: %+d, percent: %+d%%)", + SpellInfo(spellID) or "Unknown", spellID, baseDuration, modified, + flat or 0, percent or 0) + ) + end + + return modified +end + +-- Comprehensive duration modifier function that combines all sources +-- Order of application: +-- 1. Nampower GetSpellModifiers (if available and has data) +-- 2. Manual talent modifiers (fallback/supplement) +-- 3. Equipment modifiers +-- 4. Set bonus modifiers +-- Parameters: +-- spellID: The spell ID +-- baseDuration: The base duration (after combo points if applicable) +-- Returns: Fully modified duration +function CleveRoids.ApplyAllDurationModifiers(spellID, baseDuration) + if not spellID or not baseDuration then + return baseDuration + end + + local duration = baseDuration + + -- Try Nampower's GetSpellModifiers first (includes dynamic modifiers from buffs/talents) + local nampowerDuration = CleveRoids.ApplyNampowerDurationModifier(spellID, duration) + if nampowerDuration then + -- Nampower handled the talent modifiers, now apply equipment + duration = nampowerDuration + -- Still apply equipment modifiers (Nampower may not know about specific idols) + if CleveRoids.ApplyEquipmentModifier then + duration = CleveRoids.ApplyEquipmentModifier(spellID, duration) + end + else + -- Fallback: use manual talent and equipment modifiers + if CleveRoids.ApplyTalentModifier then + duration = CleveRoids.ApplyTalentModifier(spellID, duration) + end + if CleveRoids.ApplyEquipmentModifier then + duration = CleveRoids.ApplyEquipmentModifier(spellID, duration) + end + end + + -- Always apply set bonus modifiers last + if CleveRoids.ApplySetBonusModifier then + duration = CleveRoids.ApplySetBonusModifier(spellID, duration) + end + + return duration +end + -- ============================================================================ -- SET BONUS MODIFIER SYSTEM -- ============================================================================