From def000a7c48e0296c1a39162561d7ec16359066d Mon Sep 17 00:00:00 2001 From: Meow <30401521+me0wg4ming@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:20:31 +0100 Subject: [PATCH] Refactoring: Nampower Integration & SuperWoW Cleanup * Replaced SuperWoW GUID with native Turtle WoW GUID in focus system * Added memoization API for `/pfcast` and `/castfocus` performance boost * Moved Druid mana bar from superwow.lua to nampower.lua * Cleaned up superwow.lua - removed duplicate/migrated code * Updated GUI: "Druid Settings" replaces "SuperWoW Settings" header * Focus system now standalone - no SuperWoW dependency required --- api/api.lua | 30 +++ modules/focus.lua | 153 ++++++++++++-- modules/gui.lua | 4 +- modules/mouseover.lua | 2 +- modules/nampower.lua | 302 +++++++++++++++++++++++++++ modules/superwow.lua | 474 ------------------------------------------ 6 files changed, 477 insertions(+), 488 deletions(-) diff --git a/api/api.lua b/api/api.lua index d86733a5..a29f6686 100644 --- a/api/api.lua +++ b/api/api.lua @@ -1466,3 +1466,33 @@ function pfUI.api.GetNoNameObject(frame, objtype, layer, arg1, arg2) end end end + +-- [ TryMemoizedFuncLoadstringForSpellCasts ] +-- Memoizes lua function strings for spell casts to improve performance. +-- Supports both string functions and direct function values. +-- 'funcOrStr' [function|string] Either a function or a lua string to execute +-- return: [function|nil] The function to execute, or nil on error +local memoizedFuncs = {} +function pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(funcOrStr) + -- If it's already a function, return it directly + if type(funcOrStr) == "function" then + return funcOrStr + end + + -- If it's a string, try to memoize it + if type(funcOrStr) == "string" then + -- Check if we've already compiled this string + if memoizedFuncs[funcOrStr] then + return memoizedFuncs[funcOrStr] + end + + -- Try to compile the string + local func = loadstring(funcOrStr) + if func then + memoizedFuncs[funcOrStr] = func + return func + end + end + + return nil +end diff --git a/modules/focus.lua b/modules/focus.lua index a2e24582..8e86eeaf 100644 --- a/modules/focus.lua +++ b/modules/focus.lua @@ -17,17 +17,48 @@ end) -- register focus emulation commands for vanilla if pfUI.client > 11200 then return end + SLASH_PFFOCUS1, SLASH_PFFOCUS2 = '/focus', '/pffocus' function SlashCmdList.PFFOCUS(msg) if not pfUI.uf or not pfUI.uf.focus then return end - if msg ~= "" then - pfUI.uf.focus.unitname = strlower(msg) - elseif UnitName("target") then - pfUI.uf.focus.unitname = strlower(UnitName("target")) - else + -- Try GUID-based focus (Turtle WoW native) + local unitstr = msg ~= "" and msg or "target" + local _, guid = nil, nil + + if UnitExists then + if msg ~= "" then + -- When msg is provided, we need to target by name first to get GUID + -- Save this for later - for now just use name-based + pfUI.uf.focus.unitname = strlower(msg) + pfUI.uf.focus.label = nil + pfUI.uf.focus.id = nil + else + -- Get GUID from current target + _, guid = UnitExists("target") + end + end + + if guid then + -- GUID-based focus (works with unitframes API) pfUI.uf.focus.unitname = nil - pfUI.uf.focus.label = nil + pfUI.uf.focus.label = guid + pfUI.uf.focus.id = "" + + -- Update focustarget frame + if pfUI.uf.focustarget then + pfUI.uf.focustarget.unitname = nil + pfUI.uf.focustarget.label = guid .. "target" + pfUI.uf.focustarget.id = "" + end + elseif msg == "" and not guid then + -- No target and no msg - clear focus + if UnitName("target") then + pfUI.uf.focus.unitname = strlower(UnitName("target")) + else + pfUI.uf.focus.unitname = nil + pfUI.uf.focus.label = nil + end end end @@ -53,6 +84,71 @@ function SlashCmdList.PFCASTFOCUS(msg) return end + local func = pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(msg) + + -- Check if we have GUID-based focus + local focusGUID = pfUI.uf.focus.label + local hasGUID = focusGUID and focusGUID ~= "" + + -- Nampower path with GUID targeting (if available and we have GUID focus) + if hasGUID and CastSpellByNameNoQueue then + local _, currentGUID = nil, nil + if UnitExists then + _, currentGUID = UnitExists("target") + end + local player = UnitIsUnit("target", "player") + + -- Target focus by GUID + if TargetByGUID then + TargetByGUID(focusGUID) + else + -- Fallback: use GUID as unitstring (works on Turtle) + TargetUnit(focusGUID) + end + + -- Verify we actually targeted the focus + local _, targetGUID = nil, nil + if UnitExists then + _, targetGUID = UnitExists("target") + end + + if targetGUID ~= focusGUID then + -- Restore original target + if currentGUID and TargetByGUID then + TargetByGUID(currentGUID) + elseif currentGUID then + TargetUnit(currentGUID) + elseif player then + TargetUnit("player") + else + TargetLastTarget() + end + UIErrorsFrame:AddMessage(SPELL_FAILED_BAD_TARGETS, 1, 0, 0) + return + end + + -- Execute the spell cast + if func then + func() + else + CastSpellByNameNoQueue(msg) + end + + -- Restore original target + if currentGUID and TargetByGUID then + TargetByGUID(currentGUID) + elseif currentGUID then + TargetUnit(currentGUID) + elseif player then + TargetUnit("player") + else + TargetLastTarget() + end + + return + end + + -- Fallback: Classic target-swapping method (name-based or GUID-based) local skiptarget = false local player = UnitIsUnit("target", "player") local unitname = "" @@ -77,11 +173,14 @@ function SlashCmdList.PFCASTFOCUS(msg) end end - local func = loadstring(msg or "") if func then func() else - CastSpellByName(msg) + if CastSpellByNameNoQueue then + CastSpellByNameNoQueue(msg) + else + CastSpellByName(msg) + end end if skiptarget == false then @@ -98,9 +197,39 @@ SLASH_PFSWAPFOCUS1, SLASH_PFSWAPFOCUS2 = '/swapfocus', '/pfswapfocus' function SlashCmdList.PFSWAPFOCUS(msg) if not pfUI.uf or not pfUI.uf.focus then return end - local oldunit = UnitExists("target") and strlower(UnitName("target")) - if oldunit and pfUI.uf.focus.unitname then - TargetByName(pfUI.uf.focus.unitname) - pfUI.uf.focus.unitname = oldunit + -- Try GUID-based swap + local _, guid = nil, nil + if UnitExists then + _, guid = UnitExists("target") + end + + if guid then + -- Save old focus GUID + local oldlabel = pfUI.uf.focus.label or "" + local oldid = pfUI.uf.focus.id or "" + + -- Set new focus to current target + pfUI.uf.focus.unitname = nil + pfUI.uf.focus.label = guid + pfUI.uf.focus.id = "" + + -- Update focustarget + if pfUI.uf.focustarget then + pfUI.uf.focustarget.unitname = nil + pfUI.uf.focustarget.label = guid .. "target" + pfUI.uf.focustarget.id = "" + end + + -- Target old focus + if oldlabel and oldlabel ~= "" then + TargetUnit(oldlabel .. oldid) + end + else + -- Fallback: name-based swap + local oldunit = UnitExists("target") and strlower(UnitName("target")) + if oldunit and pfUI.uf.focus.unitname then + TargetByName(pfUI.uf.focus.unitname) + pfUI.uf.focus.unitname = oldunit + end end end diff --git a/modules/gui.lua b/modules/gui.lua index 0a2c7a8c..4c926fc6 100644 --- a/modules/gui.lua +++ b/modules/gui.lua @@ -1983,10 +1983,12 @@ pfUI:RegisterModule("gui", "vanilla:tbc", function () CreateConfig(nil, T["Energy Color"], C.unitframes, "energycolor", "color") CreateConfig(nil, T["Focus Color"], C.unitframes, "focuscolor", "color") - CreateConfig(nil, T["SuperWoW Settings"], nil, nil, "header") + CreateConfig(nil, T["Druid Settings"], nil, nil, "header") CreateConfig(nil, T["Show Druid Mana Bar"], C.unitframes, "druidmanabar", "checkbox", nil, nil, nil, nil, "vanilla" ) CreateConfig(nil, T["Druid Mana Bar Height"], C.unitframes, "druidmanaheight", nil, nil, nil, nil, nil, "vanilla" ) CreateConfig(nil, T["Druid Mana Bar Text"], C.unitframes, "druidmanatext", "checkbox", nil, nil, nil, nil, "vanilla" ) + + CreateConfig(nil, T["SuperWoW Settings"], nil, nil, "header") CreateConfig(nil, T["Track Group on Minimap"], C.unitframes, "track_group", "checkbox", nil, nil, nil, nil, "vanilla" ) CreateConfig(nil, T["Nampower Settings"], nil, nil, "header") diff --git a/modules/mouseover.lua b/modules/mouseover.lua index 918a3976..c33a6b5b 100644 --- a/modules/mouseover.lua +++ b/modules/mouseover.lua @@ -34,7 +34,7 @@ pfUI:RegisterModule("mouseover", "vanilla", function () _G.SLASH_PFCAST1, _G.SLASH_PFCAST2 = "/pfcast", "/pfmouse" function SlashCmdList.PFCAST(msg) local restore_target = true - local func = loadstring(msg or "") + local func = pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(msg) local unit = "mouseover" if not UnitExists(unit) then diff --git a/modules/nampower.lua b/modules/nampower.lua index a3b171df..2c38c365 100644 --- a/modules/nampower.lua +++ b/modules/nampower.lua @@ -481,4 +481,306 @@ pfUI:RegisterModule("nampower", "vanilla", function () -- NOTE: HoT Detection (AURA_CAST events) removed - OnHotApplied callback was never implemented in libpredict -- NOTE: Swing Timer removed - GetSwingTimers() was never called anywhere + + -- Druid Secondary Mana Bar + -- Shows base mana when druid is in shapeshift form (Bear/Cat uses Rage/Energy) + -- Uses Nampower's GetUnitField to get base mana values + -- Controlled by "Show Druid Mana Bar" setting in GUI + local _, playerClass = UnitClass("player") + + if GetUnitField and pfUI.uf and pfUI.uf.player and playerClass == "DRUID" and pfUI_config.unitframes.druidmanabar == "1" then + local rawborder, default_border = GetBorderSize("unitframes") + local config = pfUI.uf.player.config + + -- Create secondary mana bar below the power bar + local playerMana = CreateFrame("StatusBar", "pfPlayerSecondaryMana", pfUI.uf.player) + playerMana:SetFrameStrata(pfUI.uf.player:GetFrameStrata()) + playerMana:SetFrameLevel(pfUI.uf.player:GetFrameLevel() + 5) + playerMana:SetStatusBarTexture(pfUI.media[config.pbartexture]) + + -- Mana color + local manacolor = config.defcolor == "0" and config.manacolor or C.unitframes.manacolor + local r, g, b, a = pfUI.api.strsplit(",", manacolor) + playerMana:SetStatusBarColor(r, g, b, a) + + -- Use SAME size as normal power bar + local width = config.pwidth ~= "-1" and config.pwidth or config.width + local height = config.pheight + playerMana:SetWidth(width) + playerMana:SetHeight(height) + playerMana:SetPoint("TOPLEFT", pfUI.uf.player.power, "BOTTOMLEFT", 0, -2*default_border - (config.pspace or 0)) + playerMana:SetPoint("TOPRIGHT", pfUI.uf.player.power, "BOTTOMRIGHT", 0, -2*default_border - (config.pspace or 0)) + playerMana:Hide() + + CreateBackdrop(playerMana) + CreateBackdropShadow(playerMana) + + -- Text overlay - same font settings as power bar + local fontname = pfUI.font_unit + local fontsize = tonumber(pfUI_config.global.font_unit_size) + local fontstyle = pfUI_config.global.font_unit_style + + if config.customfont == "1" then + fontname = pfUI.media[config.customfont_name] + fontsize = tonumber(config.customfont_size) + fontstyle = config.customfont_style + end + + playerMana.text = playerMana:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") + playerMana.text:SetFontObject(GameFontWhite) + playerMana.text:SetFont(fontname, fontsize, fontstyle) + playerMana.text:SetPoint("CENTER", playerMana, "CENTER", 0, 0) + + -- Set text color like normal power bar (mana = type 0) + local tr, tg, tb = 1, 1, 1 + if config["powercolor"] == "1" then + tr = ManaBarColor[0].r + tg = ManaBarColor[0].g + tb = ManaBarColor[0].b + end + if C.unitframes.pastel == "1" then + tr, tg, tb = (tr + .75) * .5, (tg + .75) * .5, (tb + .75) * .5 + end + playerMana.text:SetTextColor(tr, tg, tb, 1) + + -- Update function + local function UpdatePlayerSecondaryMana() + local powerType = UnitPowerType("player") + + -- Only show when NOT using mana (i.e., in Bear/Cat form) + if powerType == 0 then + playerMana:Hide() + return + end + + -- Get base mana using Nampower's GetUnitField + local baseMana, baseMaxMana + local _, guid = UnitExists("player") + + if guid then + baseMana = GetUnitField(guid, "power1") + baseMaxMana = GetUnitField(guid, "maxPower1") + end + + -- Round down power values (Nampower can return decimals) + if baseMana then baseMana = math.floor(baseMana) end + if baseMaxMana then baseMaxMana = math.floor(baseMaxMana) end + + if type(baseMana) ~= "number" or type(baseMaxMana) ~= "number" or baseMaxMana == 0 then + playerMana:Hide() + return + end + + -- Update bar + playerMana:SetMinMaxValues(0, baseMaxMana) + playerMana:SetValue(baseMana) + + -- Update text based on power text config + local textConfig = config.txtpowercenter or config.txtpowerright or config.txtpowerleft + + if not textConfig or textConfig == "" or textConfig == "none" then + playerMana.text:SetText("") + elseif textConfig == "power" then + playerMana.text:SetText(Abbreviate(baseMana)) + elseif textConfig == "powermax" then + playerMana.text:SetText(Abbreviate(baseMaxMana)) + elseif textConfig == "powerperc" then + local perc = math.ceil(baseMana / baseMaxMana * 100) + playerMana.text:SetText(perc) + elseif textConfig == "powermiss" then + local miss = math.ceil(baseMana - baseMaxMana) + playerMana.text:SetText(miss == 0 and "0" or Abbreviate(miss)) + elseif textConfig == "powerdyn" then + local perc = math.ceil(baseMana / baseMaxMana * 100) + if perc == 100 then + playerMana.text:SetText(Abbreviate(baseMana)) + else + playerMana.text:SetText(string.format("%s - %s%%", Abbreviate(baseMana), perc)) + end + elseif textConfig == "powerminmax" then + playerMana.text:SetText(string.format("%s/%s", Abbreviate(baseMana), Abbreviate(baseMaxMana))) + else + -- Default: show dynamic + local perc = math.ceil(baseMana / baseMaxMana * 100) + if perc == 100 then + playerMana.text:SetText(Abbreviate(baseMana)) + else + playerMana.text:SetText(string.format("%s - %s%%", Abbreviate(baseMana), perc)) + end + end + + playerMana:Show() + end + + -- Register events + playerMana:RegisterEvent("UNIT_MANA") + playerMana:RegisterEvent("UNIT_MAXMANA") + playerMana:RegisterEvent("UNIT_DISPLAYPOWER") + playerMana:RegisterEvent("UPDATE_SHAPESHIFT_FORM") + playerMana:RegisterEvent("PLAYER_LOGOUT") + playerMana:SetScript("OnEvent", function() + if event == "PLAYER_LOGOUT" then + this:UnregisterAllEvents() + this:SetScript("OnEvent", nil) + return + end + if arg1 == nil or arg1 == "player" then + UpdatePlayerSecondaryMana() + end + end) + + -- Initial update + UpdatePlayerSecondaryMana() + end + + -- Target Secondary Mana Bar (shows when target is a druid in shapeshift form) + if GetUnitField and pfUI.uf and pfUI.uf.target and pfUI_config.unitframes.druidmanabar == "1" then + local rawborder, default_border = GetBorderSize("unitframes") + local config = pfUI.uf.target.config + + -- Create secondary mana bar below the power bar + local targetMana = CreateFrame("StatusBar", "pfTargetSecondaryMana", pfUI.uf.target) + targetMana:SetFrameStrata(pfUI.uf.target:GetFrameStrata()) + targetMana:SetFrameLevel(pfUI.uf.target:GetFrameLevel() + 5) + targetMana:SetStatusBarTexture(pfUI.media[config.pbartexture]) + + -- Mana color + local manacolor = config.defcolor == "0" and config.manacolor or C.unitframes.manacolor + local r, g, b, a = pfUI.api.strsplit(",", manacolor) + targetMana:SetStatusBarColor(r, g, b, a) + + -- Use SAME size as normal power bar + local width = config.pwidth ~= "-1" and config.pwidth or config.width + local height = config.pheight + targetMana:SetWidth(width) + targetMana:SetHeight(height) + targetMana:SetPoint("TOPLEFT", pfUI.uf.target.power, "BOTTOMLEFT", 0, -2*default_border - (config.pspace or 0)) + targetMana:SetPoint("TOPRIGHT", pfUI.uf.target.power, "BOTTOMRIGHT", 0, -2*default_border - (config.pspace or 0)) + targetMana:Hide() + + CreateBackdrop(targetMana) + CreateBackdropShadow(targetMana) + + -- Text overlay + local fontname = pfUI.font_unit + local fontsize = tonumber(pfUI_config.global.font_unit_size) + local fontstyle = pfUI_config.global.font_unit_style + + if config.customfont == "1" then + fontname = pfUI.media[config.customfont_name] + fontsize = tonumber(config.customfont_size) + fontstyle = config.customfont_style + end + + targetMana.text = targetMana:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") + targetMana.text:SetFontObject(GameFontWhite) + targetMana.text:SetFont(fontname, fontsize, fontstyle) + targetMana.text:SetPoint("CENTER", targetMana, "CENTER", 0, 0) + + -- Set text color + local tr, tg, tb = 1, 1, 1 + if config["powercolor"] == "1" then + tr = ManaBarColor[0].r + tg = ManaBarColor[0].g + tb = ManaBarColor[0].b + end + if C.unitframes.pastel == "1" then + tr, tg, tb = (tr + .75) * .5, (tg + .75) * .5, (tb + .75) * .5 + end + targetMana.text:SetTextColor(tr, tg, tb, 1) + + -- Update function + local function UpdateTargetSecondaryMana() + if not UnitExists("target") then + targetMana:Hide() + return + end + + local powerType = UnitPowerType("target") + + -- Only show if target is NOT using mana + if powerType == 0 then + targetMana:Hide() + return + end + + -- Get base mana using Nampower's GetUnitField + local baseMana, baseMaxMana + local _, guid = UnitExists("target") + + if guid then + baseMana = GetUnitField(guid, "power1") + baseMaxMana = GetUnitField(guid, "maxPower1") + end + + -- Round down power values + if baseMana then baseMana = math.floor(baseMana) end + if baseMaxMana then baseMaxMana = math.floor(baseMaxMana) end + + if type(baseMana) ~= "number" or type(baseMaxMana) ~= "number" or baseMaxMana == 0 then + targetMana:Hide() + return + end + + -- Update bar + targetMana:SetMinMaxValues(0, baseMaxMana) + targetMana:SetValue(baseMana) + + -- Update text + local textConfig = config.txtpowercenter or config.txtpowerright or config.txtpowerleft + + if not textConfig or textConfig == "" or textConfig == "none" then + targetMana.text:SetText("") + elseif textConfig == "power" then + targetMana.text:SetText(Abbreviate(baseMana)) + elseif textConfig == "powermax" then + targetMana.text:SetText(Abbreviate(baseMaxMana)) + elseif textConfig == "powerperc" then + local perc = math.ceil(baseMana / baseMaxMana * 100) + targetMana.text:SetText(perc) + elseif textConfig == "powermiss" then + local miss = math.ceil(baseMana - baseMaxMana) + targetMana.text:SetText(miss == 0 and "0" or Abbreviate(miss)) + elseif textConfig == "powerdyn" then + local perc = math.ceil(baseMana / baseMaxMana * 100) + if perc == 100 then + targetMana.text:SetText(Abbreviate(baseMana)) + else + targetMana.text:SetText(string.format("%s - %s%%", Abbreviate(baseMana), perc)) + end + elseif textConfig == "powerminmax" then + targetMana.text:SetText(string.format("%s/%s", Abbreviate(baseMana), Abbreviate(baseMaxMana))) + else + -- Default: show dynamic + local perc = math.ceil(baseMana / baseMaxMana * 100) + if perc == 100 then + targetMana.text:SetText(Abbreviate(baseMana)) + else + targetMana.text:SetText(string.format("%s - %s%%", Abbreviate(baseMana), perc)) + end + end + + targetMana:Show() + end + + -- Register events + targetMana:RegisterEvent("UNIT_MANA") + targetMana:RegisterEvent("UNIT_MAXMANA") + targetMana:RegisterEvent("UNIT_DISPLAYPOWER") + targetMana:RegisterEvent("PLAYER_TARGET_CHANGED") + targetMana:RegisterEvent("PLAYER_LOGOUT") + targetMana:SetScript("OnEvent", function() + if event == "PLAYER_LOGOUT" then + this:UnregisterAllEvents() + this:SetScript("OnEvent", nil) + return + end + if event == "PLAYER_TARGET_CHANGED" or arg1 == nil or arg1 == "target" then + UpdateTargetSecondaryMana() + end + end) + + -- Initial update + UpdateTargetSecondaryMana() + end end) \ No newline at end of file diff --git a/modules/superwow.lua b/modules/superwow.lua index 0d2569e1..71d2f8c4 100644 --- a/modules/superwow.lua +++ b/modules/superwow.lua @@ -23,14 +23,6 @@ SlashCmdList["PFDLLSTATUS"] = function() chat:AddMessage(" |cffff0000Nampower|r: Not detected") end - -- UnitXP - local hasUnitXP = pcall(UnitXP, "nop", "nop") - if hasUnitXP then - chat:AddMessage(" |cff00ff00UnitXP_SP3|r: Detected") - else - chat:AddMessage(" |cffff0000UnitXP_SP3|r: Not detected") - end - -- Check if castbar exists for indicator positioning if pfUI.castbar and pfUI.castbar.player then chat:AddMessage(" |cff00ff00Castbar|r: Available for indicator anchoring") @@ -41,42 +33,11 @@ SlashCmdList["PFDLLSTATUS"] = function() -- Check indicator frames if pfUI.uf and pfUI.uf.target then chat:AddMessage(" |cff00ff00Target frame|r: exists") - if pfUI.uf.target.behindIndicator then - chat:AddMessage(" |cff00ff00Behind indicator|r: created") - else - chat:AddMessage(" |cffff0000Behind indicator|r: NOT created") - end - if pfUI.uf.target.losIndicator then - chat:AddMessage(" |cff00ff00LOS indicator|r: created") - else - chat:AddMessage(" |cffff0000LOS indicator|r: NOT created") - end else chat:AddMessage(" |cffff0000Target frame|r: NOT found") end end --- UnitXP Behind/LOS test command -SLASH_PFBEHIND1 = "/pfbehind" -SlashCmdList["PFBEHIND"] = function() - local chat = DEFAULT_CHAT_FRAME - if not UnitExists("target") then - chat:AddMessage("|cff33ffccpfUI|r: No target") - return - end - - local hasUnitXP = pcall(UnitXP, "nop", "nop") - if not hasUnitXP then - chat:AddMessage("|cff33ffccpfUI|r: UnitXP not available") - return - end - - local successB, behind = pcall(UnitXP, "behind", "player", "target") - local successL, inSight = pcall(UnitXP, "inSight", "player", "target") - - chat:AddMessage("|cff33ffccpfUI|r: Behind=" .. tostring(behind) .. " LOS=" .. tostring(inSight)) -end - pfUI:RegisterModule("superwow", "vanilla", function () if SetAutoloot and SpellInfo and not SUPERWOW_VERSION then -- Turn every enchanting link that we create in the enchanting frame, @@ -122,426 +83,6 @@ pfUI:RegisterModule("superwow", "vanilla", function () end) end - -- Add native mouseover support - if SUPERWOW_VERSION and pfUI.uf and pfUI.uf.mouseover then - _G.SlashCmdList.PFCAST = function(msg) - local func = loadstring(msg or "") - local unit = "mouseover" - - if not UnitExists(unit) then - local frame = GetMouseFocus() - if frame.label and frame.id then - unit = frame.label .. frame.id - elseif UnitExists("target") then - unit = "target" - elseif GetCVar("autoSelfCast") == "1" then - unit = "player" - else - return - end - end - - if func then - -- set mouseover to target for script if needed - local switch_target = not UnitIsUnit("target", unit) - if switch_target then TargetUnit(unit) end - func() - if switch_target then TargetLastTarget() end - else - -- write temporary unit name - pfUI.uf.mouseover.unit = unit - - -- cast spell to unitstr - CastSpellByName(msg, unit) - - -- remove temporary mouseover unit - pfUI.uf.mouseover.unit = nil - end - end - end - - -- Add support for druid mana bars - -- Uses Nampower's GetUnitField to get base mana when in shapeshift form - local hasNampower = (GetUnitField ~= nil) - - -- Add support for player secondary power bar (shows base mana when in shapeshift form) - -- Works with SuperWoW OR Nampower - both extend UnitMana() to return base mana as second value - -- Only for Druids (only class that can shapeshift in Vanilla) - -- Controlled by "Show Druid Mana Bar" setting - local _, playerClass = UnitClass("player") - if hasNampower and pfUI.uf and pfUI.uf.player and playerClass == "DRUID" and pfUI_config.unitframes.druidmanabar == "1" then - local rawborder, default_border = GetBorderSize("unitframes") - local config = pfUI.uf.player.config - - -- Create secondary mana bar below the power bar - local playerMana = CreateFrame("StatusBar", "pfPlayerSecondaryMana", pfUI.uf.player) - playerMana:SetFrameStrata(pfUI.uf.player:GetFrameStrata()) - playerMana:SetFrameLevel(pfUI.uf.player:GetFrameLevel() + 5) - playerMana:SetStatusBarTexture(pfUI.media[config.pbartexture]) - - -- Mana color - local manacolor = config.defcolor == "0" and config.manacolor or C.unitframes.manacolor - local r, g, b, a = pfUI.api.strsplit(",", manacolor) - playerMana:SetStatusBarColor(r, g, b, a) - - -- Use SAME size as normal power bar (pwidth/pheight from config) - local width = config.pwidth ~= "-1" and config.pwidth or config.width - local height = config.pheight - playerMana:SetWidth(width) - playerMana:SetHeight(height) - playerMana:SetPoint("TOPLEFT", pfUI.uf.player.power, "BOTTOMLEFT", 0, -2*default_border - (config.pspace or 0)) - playerMana:SetPoint("TOPRIGHT", pfUI.uf.player.power, "BOTTOMRIGHT", 0, -2*default_border - (config.pspace or 0)) - playerMana:Hide() - - CreateBackdrop(playerMana) - CreateBackdropShadow(playerMana) - - -- Text overlay - same font settings as power bar - local fontname = pfUI.font_unit - local fontsize = tonumber(pfUI_config.global.font_unit_size) - local fontstyle = pfUI_config.global.font_unit_style - - if config.customfont == "1" then - fontname = pfUI.media[config.customfont_name] - fontsize = tonumber(config.customfont_size) - fontstyle = config.customfont_style - end - - playerMana.text = playerMana:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") - playerMana.text:SetFontObject(GameFontWhite) - playerMana.text:SetFont(fontname, fontsize, fontstyle) - playerMana.text:SetPoint("CENTER", playerMana, "CENTER", 0, 0) - - -- Set text color like normal power bar (mana = type 0) - local tr, tg, tb = 1, 1, 1 - if config["powercolor"] == "1" then - tr = ManaBarColor[0].r - tg = ManaBarColor[0].g - tb = ManaBarColor[0].b - end - if C.unitframes.pastel == "1" then - tr, tg, tb = (tr + .75) * .5, (tg + .75) * .5, (tb + .75) * .5 - end - playerMana.text:SetTextColor(tr, tg, tb, 1) - - -- Update function - local function UpdatePlayerSecondaryMana() - local powerType = UnitPowerType("player") - - -- Only show when NOT using mana (i.e., in Bear/Cat form) - if powerType == 0 then - playerMana:Hide() - return - end - - -- Get base mana using Nampower's GetUnitField - local baseMana, baseMaxMana - - if GetUnitField then - local _, guid = UnitExists("player") - if guid then - baseMana = GetUnitField(guid, "power1") - baseMaxMana = GetUnitField(guid, "maxPower1") - end - end - - -- Round down power values (Nampower can return decimals, especially for rage) - if baseMana then baseMana = math.floor(baseMana) end - if baseMaxMana then baseMaxMana = math.floor(baseMaxMana) end - - if type(baseMana) ~= "number" or type(baseMaxMana) ~= "number" or baseMaxMana == 0 then - playerMana:Hide() - return - end - - -- Update bar - playerMana:SetMinMaxValues(0, baseMaxMana) - playerMana:SetValue(baseMana) - - -- Update text based on power text config (uses same setting as normal power bar) - -- Check txtpowercenter first (centered text), then txtpowerright, then txtpowerleft - local textConfig = config.txtpowercenter or config.txtpowerright or config.txtpowerleft - - if not textConfig or textConfig == "" or textConfig == "none" then - -- No text configured - hide text - playerMana.text:SetText("") - elseif textConfig == "power" then - playerMana.text:SetText(Abbreviate(baseMana)) - elseif textConfig == "powermax" then - playerMana.text:SetText(Abbreviate(baseMaxMana)) - elseif textConfig == "powerperc" then - local perc = math.ceil(baseMana / baseMaxMana * 100) - playerMana.text:SetText(perc) - elseif textConfig == "powermiss" then - local miss = math.ceil(baseMana - baseMaxMana) - playerMana.text:SetText(miss == 0 and "0" or Abbreviate(miss)) - elseif textConfig == "powerdyn" then - local perc = math.ceil(baseMana / baseMaxMana * 100) - if perc == 100 then - playerMana.text:SetText(Abbreviate(baseMana)) - else - playerMana.text:SetText(string.format("%s - %s%%", Abbreviate(baseMana), perc)) - end - elseif textConfig == "powerminmax" then - playerMana.text:SetText(string.format("%s/%s", Abbreviate(baseMana), Abbreviate(baseMaxMana))) - else - -- Default: show dynamic (value + percentage if not full) - local perc = math.ceil(baseMana / baseMaxMana * 100) - if perc == 100 then - playerMana.text:SetText(Abbreviate(baseMana)) - else - playerMana.text:SetText(string.format("%s - %s%%", Abbreviate(baseMana), perc)) - end - end - - playerMana:Show() - end - - -- Register events - playerMana:RegisterEvent("UNIT_MANA") - playerMana:RegisterEvent("UNIT_MAXMANA") - playerMana:RegisterEvent("UNIT_DISPLAYPOWER") - playerMana:RegisterEvent("UPDATE_SHAPESHIFT_FORM") - playerMana:RegisterEvent("PLAYER_LOGOUT") - playerMana:SetScript("OnEvent", function() - -- Handle shutdown to prevent crash 132 - if event == "PLAYER_LOGOUT" then - this:UnregisterAllEvents() - this:SetScript("OnEvent", nil) - return - end - if arg1 == nil or arg1 == "player" then - UpdatePlayerSecondaryMana() - end - end) - - -- Initial update - UpdatePlayerSecondaryMana() - - -- Store reference - pfUI.uf.player.secondaryMana = playerMana - end - - -- Add support for target secondary power bar (shows base mana when target is in shapeshift form) - -- Works with SuperWoW OR Nampower - both extend UnitMana() to return base mana as second value - -- Controlled by "Show Druid Mana Bar" setting - available for ALL classes - if hasNampower and pfUI.uf and pfUI.uf.target and pfUI_config.unitframes.druidmanabar == "1" then - local rawborder, default_border = GetBorderSize("unitframes") - local config = pfUI.uf.target.config - - -- Create secondary mana bar below the power bar - local targetMana = CreateFrame("StatusBar", "pfTargetSecondaryMana", pfUI.uf.target) - targetMana:SetFrameStrata(pfUI.uf.target:GetFrameStrata()) - targetMana:SetFrameLevel(pfUI.uf.target:GetFrameLevel() + 5) - targetMana:SetStatusBarTexture(pfUI.media[config.pbartexture]) - - -- Mana color - local manacolor = config.defcolor == "0" and config.manacolor or C.unitframes.manacolor - local r, g, b, a = pfUI.api.strsplit(",", manacolor) - targetMana:SetStatusBarColor(r, g, b, a) - - -- Use SAME size as normal power bar (pwidth/pheight from config) - local width = config.pwidth ~= "-1" and config.pwidth or config.width - local height = config.pheight -- Same height as power bar! - targetMana:SetWidth(width) - targetMana:SetHeight(height) - targetMana:SetPoint("TOPLEFT", pfUI.uf.target.power, "BOTTOMLEFT", 0, -2*default_border - (config.pspace or 0)) - targetMana:SetPoint("TOPRIGHT", pfUI.uf.target.power, "BOTTOMRIGHT", 0, -2*default_border - (config.pspace or 0)) - targetMana:Hide() - - CreateBackdrop(targetMana) - CreateBackdropShadow(targetMana) - - -- Text overlay - same font settings as power bar - local fontname = pfUI.font_unit - local fontsize = tonumber(pfUI_config.global.font_unit_size) - local fontstyle = pfUI_config.global.font_unit_style - - if config.customfont == "1" then - fontname = pfUI.media[config.customfont_name] - fontsize = tonumber(config.customfont_size) - fontstyle = config.customfont_style - end - - targetMana.text = targetMana:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall") - targetMana.text:SetFontObject(GameFontWhite) - targetMana.text:SetFont(fontname, fontsize, fontstyle) - targetMana.text:SetPoint("CENTER", targetMana, "CENTER", 0, 0) - - -- Set text color like normal power bar (mana = type 0) - -- Uses powercolor setting and pastel effect - local function UpdateTextColor() - local tr, tg, tb = 1, 1, 1 - if config["powercolor"] == "1" then - tr = ManaBarColor[0].r - tg = ManaBarColor[0].g - tb = ManaBarColor[0].b - end - if C.unitframes.pastel == "1" then - tr, tg, tb = (tr + .75) * .5, (tg + .75) * .5, (tb + .75) * .5 - end - targetMana.text:SetTextColor(tr, tg, tb, 1) - end - UpdateTextColor() - - -- Update function - local function UpdateTargetSecondaryMana() - if not UnitExists("target") then - targetMana:Hide() - return - end - - local powerType = UnitPowerType("target") - - -- Only show if target is NOT using mana (i.e., in shapeshift form with energy/rage) - if powerType == 0 then - -- Target is using mana as primary power - no need for secondary bar - targetMana:Hide() - return - end - - -- Get base mana using Nampower's GetUnitField - local baseMana, baseMaxMana - - if GetUnitField then - local _, guid = UnitExists("target") - if guid then - baseMana = GetUnitField(guid, "power1") - baseMaxMana = GetUnitField(guid, "maxPower1") - end - end - - -- Round down power values (Nampower can return decimals, especially for rage) - if baseMana then baseMana = math.floor(baseMana) end - if baseMaxMana then baseMaxMana = math.floor(baseMaxMana) end - - -- Check if we got valid mana values - if type(baseMana) ~= "number" or type(baseMaxMana) ~= "number" or baseMaxMana == 0 then - targetMana:Hide() - return - end - - -- Update bar - targetMana:SetMinMaxValues(0, baseMaxMana) - targetMana:SetValue(baseMana) - - -- Update text based on power text config (uses same setting as normal power bar) - local textConfig = config.txtpowercenter or config.txtpowerright or config.txtpowerleft - - if not textConfig or textConfig == "" or textConfig == "none" then - targetMana.text:SetText("") - elseif textConfig == "power" then - targetMana.text:SetText(Abbreviate(baseMana)) - elseif textConfig == "powermax" then - targetMana.text:SetText(Abbreviate(baseMaxMana)) - elseif textConfig == "powerperc" then - local perc = math.ceil(baseMana / baseMaxMana * 100) - targetMana.text:SetText(perc) - elseif textConfig == "powermiss" then - local miss = math.ceil(baseMana - baseMaxMana) - targetMana.text:SetText(miss == 0 and "0" or Abbreviate(miss)) - elseif textConfig == "powerdyn" then - local perc = math.ceil(baseMana / baseMaxMana * 100) - if perc == 100 then - targetMana.text:SetText(Abbreviate(baseMana)) - else - targetMana.text:SetText(string.format("%s - %s%%", Abbreviate(baseMana), perc)) - end - elseif textConfig == "powerminmax" then - targetMana.text:SetText(string.format("%s/%s", Abbreviate(baseMana), Abbreviate(baseMaxMana))) - else - local perc = math.ceil(baseMana / baseMaxMana * 100) - if perc == 100 then - targetMana.text:SetText(Abbreviate(baseMana)) - else - targetMana.text:SetText(string.format("%s - %s%%", Abbreviate(baseMana), perc)) - end - end - - targetMana:Show() - end - - -- Register events - targetMana:RegisterEvent("PLAYER_TARGET_CHANGED") - targetMana:RegisterEvent("UNIT_MANA") - targetMana:RegisterEvent("UNIT_MAXMANA") - targetMana:RegisterEvent("UNIT_DISPLAYPOWER") - targetMana:RegisterEvent("PLAYER_LOGOUT") - targetMana:SetScript("OnEvent", function() - -- Handle shutdown to prevent crash 132 - if event == "PLAYER_LOGOUT" then - this:UnregisterAllEvents() - this:SetScript("OnEvent", nil) - return - end - if event == "PLAYER_TARGET_CHANGED" then - UpdateTargetSecondaryMana() - elseif arg1 == "target" then - UpdateTargetSecondaryMana() - end - end) - - -- Store reference - pfUI.uf.target.secondaryMana = targetMana - end - - - -- Add support for guid based focus frame - if SUPERWOW_VERSION and pfUI.uf and pfUI.uf.focus then - local focus = function(unitstr) - -- try to read target's unit guid - local _, guid = UnitExists(unitstr) - - if guid and pfUI.uf.focus then - -- update focus frame - pfUI.uf.focus.unitname = nil - pfUI.uf.focus.label = guid - pfUI.uf.focus.id = "" - - -- update focustarget frame - pfUI.uf.focustarget.unitname = nil - pfUI.uf.focustarget.label = guid .. "target" - pfUI.uf.focustarget.id = "" - end - - return guid - end - - -- extend the builtin /focus slash command - local legacyfocus = SlashCmdList.PFFOCUS - function SlashCmdList.PFFOCUS(msg) - -- try to perform guid based focus - local guid = focus("target") - - -- run old focus emulation - if not guid then legacyfocus(msg) end - end - - -- extend the builtin /swapfocus slash command - local legacyswapfocus = SlashCmdList.PFSWAPFOCUS - function SlashCmdList.PFSWAPFOCUS(msg) - -- save previous focus values - local oldlabel = pfUI.uf.focus.label or "" - local oldid = pfUI.uf.focus.id or "" - - -- try to perform guid based focus - local guid = focus("target") - - -- target old focus - if guid and oldlabel and oldid then - TargetUnit(oldlabel..oldid) - end - - -- run old focus emulation - if not guid then legacyswapfocus(msg) end - end - end - - -- NOTE: SuperWoW libdebuff enhancement removed. - -- UNIT_CASTEVENT fires before resist/miss/dodge events arrive, - -- which breaks the failed spell detection system in libdebuff. - -- DoT timers use the standard hook-based fallback instead. - -- TrackUnit API for adding group members to minimap -- Tracks friendly units on the minimap for easier group coordination if TrackUnit and C.unitframes.track_group == "1" then @@ -607,21 +148,6 @@ pfUI:RegisterModule("superwow", "vanilla", function () end end - -- Enhanced SpellInfo API wrapper - if SpellInfo then - pfUI.api.GetSpellInfo = function(spellId) - local name, rank, texture, minRange, maxRange = SpellInfo(spellId) - return { - name = name, - rank = rank, - texture = texture, - minRange = minRange, - maxRange = maxRange, - spellId = spellId - } - end - end - -- Clickthrough Mode API -- Allows clicking through corpses to loot underneath if Clickthrough then