Files
pfUI/pfUI.lua
T
Brues 00292ca3b4 Focus/Nameplates now respond solely to UNIT_* events
commit 70b1e66c2d
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Sun Jul 26 18:30:18 2026 -0500

    Use SetSize/SetShown and simplify minimap/map

commit 7b6bfe9975
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Sat Jul 25 11:27:14 2026 -0500

    Include raidpet frames in /pftest test mode

    The unit-frame test toggle (showall) only previews frames that exist and
    are positioned. Raidpet frames are created when raidpet.visible == "1",
    but LayoutPets' collapse mode only positions pets whose raidpet<N> unit
    actually exists, so solo/in test mode they stayed hidden.

    Add a showall branch to LayoutPets that mirror-lays every pet cell and
    shows it (bypassing collapse and the roster gate), still guarded by the
    existing showpets check so nothing happens when raidpet is disabled. Call
    LayoutPets from the /pftest handler so the grid updates immediately on
    toggle-on and restores to the normal layout on toggle-off.

commit 4c65c38647
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Sat Jul 25 10:34:33 2026 -0500

    Remove unused RangeCache local in UnitInRange

commit 6c96bbbe6c
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Sat Jul 25 10:34:33 2026 -0500

    Use Clamp() for the two-sided clamps in ui-widgets

    Replace the hand-rolled math.min/math.max and if/elseif clamp idioms in the
    status bar and scroll frame with Clamp(). Equivalent for every value seen
    (min <= max always holds), just clearer.

commit 830e0a0be1
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Sat Jul 25 10:34:33 2026 -0500

    Drop vanilla GetItemInfo shim for C_Item.GetItemInfo

    The compat/vanilla.lua GetItemInfo override reshaped vanilla's tuple into
    retail order by inserting nil at slot 4 (itemLevel), but truncated at 8
    fields, dropping itemEquipLoc/itemTexture. ClassicAPI's global GetItemInfo
    keeps the vanilla shape (its hook only warms the item cache), so the shim
    stayed necessary but incomplete.

    Remove it and point the three callers at ClassicAPI's C_Item.GetItemInfo,
    which returns the full 18-field retail tuple. Positions 1-8 are identical
    to the old shim output, so bags/roll are unchanged; character.lua's
    itemSlot (equipLoc, field 9) was always nil under the truncating shim and
    now resolves correctly for ShaguScore.

commit c0da63657d
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Sat Jul 25 10:09:34 2026 -0500

    Various cleanup

    Removes version compatibility checks for TBC/Vanilla, consolidating code to target a single WoW version. Refactors repetitive SetWidth/SetHeight calls to SetSize for cleaner code. Removes duplicate function definitions from unitxp module and simplifies conditional show/hide operations using SetShown.

commit 11a6302c2d
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Sat Jul 25 09:53:37 2026 -0500

    Delegate pfUI.api.strsplit to ClassicAPI's strsplit

    Replace the Lua pattern-based implementation with a thin wrapper around
    ClassicAPI's C-level strsplit. Keeps the pfUI.api.strsplit entry point for
    backwards compatibility with addons that call it, while dropping the
    redundant reimplementation.

    Behavioral note: the old version used ([^delim]+) which silently collapsed
    empty fields; delegating to real strsplit now preserves them
    ("a,,b" -> "a", "", "b"). All in-repo callers split numeric color tuples,
    version strings, or build name-keyed lookup tables, none of which are
    affected by empty-field preservation.

commit 67c126eac8
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Sat Jul 25 01:01:02 2026 -0500

    Bump ClassicAPI minimum version to 1.8.0

commit a6cf157518
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Sat Jul 25 00:49:50 2026 -0500

    read spell rank via C_Spell.GetSpellSubtext

    The rank string comes from the spell subtext ("Rank N"), which ClassicAPI's
    C_Spell.GetSpellSubtext returns directly -- so drop nampower's
    GetSpellRecField(spellId, "rank") here. The existing "Rank " gsub parse is
    unchanged.

commit 4b1ba99b4f
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Sat Jul 25 00:33:20 2026 -0500

    Move player info overlay onto ClassicAPI (drop Nampower)

    The haste/spell-power overlay read both values through Nampower and disabled
    itself entirely without it. Move both to ClassicAPI so it works on any
    ClassicAPI client:

    - Spell power: GetSpellPower("net") (nampower) -> GetSpellBonusDamage(i)
      (ClassicAPI, same per-school field). Merged the build+scan into one loop;
      the default school seeds the tiebreak so equal schools still favor it.
    - Haste: GetUnitField("player", "modCastSpeed") -> UnitSpellHaste("player"),
      which returns the haste percentage directly (the (1/modCastSpeed-1)*100
      conversion is now baked in, off the same UNIT_MOD_CAST_SPEED field, signed).
    - Dropped the "if not GetUnitField then return" gate -- UpdateInfoText no
      longer touches Nampower, so the overlay runs everywhere.

commit 7df4aa6d50
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Sat Jul 25 00:32:44 2026 -0500

    Hook the real global _G.UnitHealth for feign death

    Inside a RegisterModule body, `function UnitHealth(...)` defines UnitHealth on
    the pfUI environment, not the real global -- so the feign-death real-HP fix
    only reached callers that resolve UnitHealth through pfUI's env, and missed
    _G consumers (Blizzard frames, other addons). Hook _G.UnitHealth explicitly
    (and capture oldUnitHealth from _G) so the un-gate applies everywhere.

commit b0bf2fd869
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Fri Jul 24 21:10:58 2026 -0500

    Refactor nampower module

    Simplify reactive spell storage by using spell IDs instead of texture/name tables. Update to use modern C_Spell APIs (GetSpellTexture, GetSpellName) instead of manual texture paths. Consolidate SetWidth/SetHeight calls to SetSize and use print() instead of DEFAULT_CHAT_FRAME:AddMessage().

commit 0b06961333
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Fri Jul 24 20:54:23 2026 -0500

    Use GetNamePlateForUnit for target lookups; drop dead ScanGuid block

    - Replace the three GetNamePlateForGUID(UnitGUID("target")) round-trips with
      GetNamePlateForUnit("target"), which resolves the token to a GUID internally
      -- no UnitGUID string detour, and it returns nil for no-target/no-plate so
      the UnitExists guards collapse. GetNamePlateForGUID is left for raw-GUID cases.
    - Remove the dead libunitscan.ScanGuid nameplate block (ScanGuid was deleted
      from libunitscan long ago, so the guard was never true) -- it carried the
      last GetUnitField("npcFlags") read.

commit 3e6210086e
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Fri Jul 24 20:54:23 2026 -0500

    Drop Nampower stats system and polling from unit frames

    GetUnitStats now reads health/power straight from UnitHealth/UnitPower
    (the descriptor fields the server broadcasts) -- for a real unit token
    GetUnitField read nothing different, so the whole Nampower-vs-fallback
    apparatus was measuring a distinction that no longer exists. Removed:

    - The stats system: pfUI.uf.stats, pfUIStatsFrame + UpdateStatsDisplay, the
      lastUnitStats change-cache, the /pfuistats slash command, and every counter
      increment (event/heartbeat/earlyReturn/throttledSkip/nampower/fallback).
    - All GetUnitField health reads (GetUnitStats, heal-prediction, health-gradient
      color) -- collapsed to the UnitHealth/UnitHealthMax they already fell back to.
    - The heartbeat-polling backstop and its fallbackThrottle/lastEventUpdate deps.
      Frames now refresh on events only; eventless frames still use their own tick,
      and range/aggro still run in the eventless-actions block.

commit 63001b7b0c
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Fri Jul 24 20:09:42 2026 -0500

    Move nameplates onto ClassicAPI stable nameplate tokens

    ClassicAPI now assigns retail-exact, per-plate-stable "nameplateN" tokens and
    fires vanilla UNIT_* events for them, so the nameplate module can key off the
    token instead of Nampower/SuperWoW GUID primitives:

    - Events: UNIT_AURA_GUID/UNIT_FLAGS_GUID -> UNIT_AURA/UNIT_FLAGS, matched by
      the "nameplateN" token (guarded on the token prefix, since these also fire
      for target/party/raid). Registered unconditionally -- no GetUnitField gate.
    - Health: GetUnitField(guid, "health"/"maxHealth") -> UnitHealth/UnitHealthMax
      on the plate's cached token. Same UNIT_FIELD_HEALTH the server broadcasts
      (real HP on Turtle; the ~= 100 scaled-vs-real guard is unchanged).
    - Combat/target in GetCombatStateColor: GetUnitField("flags") + HasFlag ->
      UnitAffectingCombat(token); GetUnitField("target") + SuperWoW "<guid>target"
      -> the "nameplateNtarget" chain. Dropped the now-dead HasFlag,
      UNIT_FLAG_IN_COMBAT, and guidTargetTokenCache.
    - GetCastInfo now takes the token directly; every caller already holds it, so
      the UnitTokenFromGUID(guid) round-trip is gone (unused module-wide now).

    NAME_PLATE_UNIT_ADDED caches the token as plate.nameplate.unit alongside the
    GUID; REMOVED clears both. GUID is retained only as the stable cache key
    (debuffCache/threatMemory/combatColorCache). Only GetUnitField("npcFlags")
    remains -- it has no token/vanilla equivalent.

commit ce1c49fcbb
Author: Brues <5278969+brues-code@users.noreply.github.com>
Date:   Fri Jul 24 18:20:06 2026 -0500

    Make focus/focustarget event-driven via ClassicAPI unit events

    ClassicAPI now fires UNIT_* (health/mana/aura/...) with arg1 == "focus" and
    arg1 == "focustarget", observed per-unit like target/party/raid. Both frames
    already registered those events (focus/focustarget are in pfValidUnits) and
    their OnEvent already matches arg1 == label, so the 0.2s polling ticks were
    pure workarounds for the missing events. Drop both ticks; the frames now
    refresh on-event like target, with range/glow still on the shared 0.5s state
    pass and PLAYER_FOCUS_CHANGED still driving assign/clear.
2026-07-26 18:32:36 -05:00

557 lines
18 KiB
Lua

SLASH_RELOAD1 = '/rl'
function SlashCmdList.RELOAD(msg, editbox)
ReloadUI()
end
SLASH_PFUI1 = '/pfui'
function SlashCmdList.PFUI(msg, editbox)
pfUI.gui:SetShown(not pfUI.gui:IsShown())
end
SLASH_GM1, SLASH_GM2 = '/gm', '/support'
function SlashCmdList.GM(msg, editbox)
ToggleHelpFrame(1)
end
pfUI = CreateFrame("Frame", nil, UIParent)
pfUI:RegisterEvent("ADDON_LOADED")
-- setup bootvar
pfUI.bootup = true
do
-- ClassicAPI dependency check.
-- pfUI relies pervasively on the modern C_* / SuperWoW / nameplate / focus
-- API surface that ClassicAPI polyfills, so presence is required.
local PFUI_CLASSIC_API_MIN = 10800 -- (X*10000 + Y*100 + Z)
local PFUI_CLASSIC_API_LATEST = PFUI_CLASSIC_API_MIN
local PFUI_CLASSIC_API_WEBSITE = "https://github.com/brues-code/ClassicAPI"
local PFUI_CLASSIC_API_LATEST_URL = PFUI_CLASSIC_API_WEBSITE .. "/releases/latest"
local function FormatVersion(packed)
local x = math.floor(packed / 10000)
local y = math.floor(math.mod(packed, 10000) / 100)
local z = math.mod(packed, 100)
return string.format("v%d.%d.%d", x, y, z)
end
if not CLASSIC_API_VERSION or CLASSIC_API_VERSION < PFUI_CLASSIC_API_MIN then
local minVersion = FormatVersion(PFUI_CLASSIC_API_MIN)
pfUI.disabled = true
local detail
if not CLASSIC_API_VERSION then
detail = "The ClassicAPI DLL isn't loaded. The |cff33ffcc!!!ClassicAPI|r addon ships bundled with it -- delete your |cff33ffcc!!!ClassicAPI|r folder and install the latest release from:"
else
detail = "ClassicAPI " .. minVersion .. " or newer is required. Delete your |cff33ffcc!!!ClassicAPI|r folder and reinstall the latest release from:"
end
local function ShowRequiredPopup()
StaticPopupDialogs["PFUI_CLASSICAPI_REQUIRED"] = {
text = "|cff33ffccpf|cffffffffUI|r has been disabled.\n\n" .. detail,
button1 = OKAY,
hasEditBox = 1,
editBoxWidth = 280,
timeout = 0,
whileDead = 1,
hideOnEscape = 1,
preferredIndex = 3,
OnShow = function()
local editBox = getglobal(this:GetName().."EditBox")
if editBox then
editBox:SetText(PFUI_CLASSIC_API_LATEST_URL)
editBox:HighlightText()
editBox:SetFocus()
end
end,
}
StaticPopup_Show("PFUI_CLASSICAPI_REQUIRED")
DEFAULT_CHAT_FRAME:AddMessage(
"|cff33ffccpf|cffffffffUI|r disabled: " .. detail .. " " .. PFUI_CLASSIC_API_LATEST_URL,
1, 0.3, 0.3
)
end
local loginFrame = CreateFrame("Frame")
loginFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
loginFrame:SetScript("OnEvent", function()
loginFrame:UnregisterEvent("PLAYER_ENTERING_WORLD")
ShowRequiredPopup()
end)
elseif CLASSIC_API_VERSION < PFUI_CLASSIC_API_LATEST then
EventUtil.ContinueOnPlayerLogin(function()
C_Timer.After(8, function()
DEFAULT_CHAT_FRAME:AddMessage(
"|cff33ffccpf|rUI: ClassicAPI " .. FormatVersion(PFUI_CLASSIC_API_LATEST) .. " is available — " .. PFUI_CLASSIC_API_LATEST_URL,
1, 0.85, 0.3
)
end)
end)
end
end
-- initialize saved variables
pfUI_playerDB = {}
pfUI_config = {}
pfUI_init = {}
pfUI_profiles = {}
pfUI_addon_profiles = {}
pfUI_cache = {}
pfUI_throttle = {}
-- localization
pfUI_locale = {}
pfUI_translation = {}
-- initialize default variables
pfUI.cache = {}
pfUI.module = {}
pfUI.modules = {}
pfUI.skin = {}
pfUI.skins = {}
pfUI.environment = {}
pfUI.movables = {}
pfUI.version = {}
pfUI.env = {}
if not pfUI.disabled then
pfUI.events = Mixin({}, CallbackRegistryMixin)
pfUI.events:OnLoad()
pfUI.events:SetUndefinedEventsAllowed(true)
end
-- check if macro addons are loaded (disables macrotweak/macroscan)
function pfUI:MacroAddonsLoaded()
return IsAddOnLoaded("Supermacro") or IsAddOnLoaded("SuperCleveRoidMacros") or IsAddOnLoaded("UltimaMacros")
end
-- detect current addon path
local tocs = { "", "-master", "-tbc", "-wotlk" }
for _, name in pairs(tocs) do
local current = string.format("pfUI%s", name)
local title = C_AddOns.GetAddOnName(current)
if title then
pfUI.name = current
pfUI.path = "Interface\\AddOns\\" .. current
break
end
end
-- handle/convert media dir paths
pfUI.media = setmetatable({}, { __index = function(tab,key)
local value = tostring(key)
if strfind(value, "img:") then
value = string.gsub(value, "img:", pfUI.path .. "\\img\\")
elseif strfind(value, "font:") then
value = string.gsub(value, "font:", pfUI.path .. "\\fonts\\")
else
value = string.gsub(value, "Interface\\AddOns\\pfUI\\", pfUI.path .. "\\")
end
rawset(tab,key,value)
return value
end})
-- cache client version
local _, _, _, client = GetBuildInfo()
pfUI.client = client or 11200
-- setup pfUI namespace
setmetatable(pfUI.env, {__index = getfenv(0)})
PFUI_CLASS_COLORS = setmetatable({
WARRIOR = { r = 0.78, g = 0.61, b = 0.43 },
MAGE = { r = 0.25, g = 0.78, b = 0.92 },
ROGUE = { r = 1, g = 0.96, b = 0.41 },
DRUID = { r = 1, g = 0.49, b = 0.04 },
HUNTER = { r = 0.67, g = 0.83, b = 0.45 },
SHAMAN = { r = 0, g = 0.44, b = 0.87 },
PRIEST = { r = 1, g = 1, b = 1 },
WARLOCK = { r = 0.53, g = 0.53, b = 0.93 },
PALADIN = { r = 0.96, g = 0.55, b = 0.73 },
}, { __index = function(t,k)
local unknownColor = CreateColor(0.6, 0.6, 0.6, 1)
unknownColor.colorStr = unknownColor:GenerateHexColor()
return unknownColor
end})
for _, classColor in pairs(PFUI_CLASS_COLORS) do
Mixin(classColor, ColorMixin)
classColor.a = 1
classColor.colorStr = classColor:GenerateHexColor()
end
function pfUI:UpdateFonts()
-- abort when config is not ready yet
if not pfUI_config or not pfUI_config.global then return end
-- load font configuration
local default, tooltip, unit, unit_name, combat
if pfUI_config.global.force_region == "1" and GetLocale() == "zhCN" then
-- force locale compatible fonts (zhCN 1.12)
default = "Fonts\\FZXHLJW.TTF"
tooltip = "Fonts\\FZXHLJW.TTF"
combat = "Fonts\\FZXHLJW.TTF"
unit = "Fonts\\FZXHLJW.TTF"
unit_name = "Fonts\\FZXHLJW.TTF"
elseif pfUI_config.global.force_region == "1" and GetLocale() == "zhTW" then
-- force locale compatible fonts (zhTW 1.12)
default = "Fonts\\FZXHLJW.ttf"
tooltip = "Fonts\\FZXHLJW.ttf"
combat = "Fonts\\FZXHLJW.ttf"
unit = "Fonts\\FZXHLJW.ttf"
unit_name = "Fonts\\FZXHLJW.ttf"
elseif pfUI_config.global.force_region == "1" and GetLocale() == "koKR" then
-- force locale compatible fonts (koKR)
default = "Fonts\\2002.TTF"
tooltip = "Fonts\\2002.TTF"
combat = "Fonts\\2002.TTF"
unit = "Fonts\\2002.TTF"
unit_name = "Fonts\\2002.TTF"
else
-- use default entries
default = pfUI.media[pfUI_config.global.font_default]
tooltip = pfUI.media[pfUI_config.tooltip.font_tooltip]
combat = pfUI.media[pfUI_config.global.font_combat]
unit = pfUI.media[pfUI_config.global.font_unit]
unit_name = pfUI.media[pfUI_config.global.font_unit_name]
end
-- write setting shortcuts
pfUI.font_default = default
pfUI.font_combat = combat
pfUI.font_unit = unit
pfUI.font_unit_name = unit_name
-- skip setting fonts, keep blizzard defaults
if pfUI_config.global.font_blizzard == "1" then
return
end
-- set game constants
STANDARD_TEXT_FONT = default
DAMAGE_TEXT_FONT = combat
NAMEPLATE_FONT = default
UNIT_NAME_FONT = unit_name
-- If the Invisible font is selected, suppress all combat text entirely
if string.find(combat or "", "AdobeBlank") then
SHOW_COMBAT_TEXT = "0"
end
-- set dropdown font to default size
UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT = 11
-- change default game font objects
SystemFont:SetFont(default, 15)
GameFontNormal:SetFont(default, 12)
GameFontBlack:SetFont(default, 12)
GameFontNormalSmall:SetFont(default, 11)
GameFontNormalLarge:SetFont(default, 16)
GameFontNormalHuge:SetFont(default, 20)
NumberFontNormal:SetFont(default, 14, "OUTLINE")
NumberFontNormalSmall:SetFont(default, 14, "OUTLINE")
NumberFontNormalLarge:SetFont(default, 16, "OUTLINE")
NumberFontNormalHuge:SetFont(default, 30, "OUTLINE")
QuestTitleFont:SetFont(default, 18)
QuestFont:SetFont(default, 13)
QuestFontHighlight:SetFont(default, 14)
ItemTextFontNormal:SetFont(default, 15)
MailTextFontNormal:SetFont(default, 15)
SubSpellFont:SetFont(default, 12)
DialogButtonNormalText:SetFont(default, 16)
ZoneTextFont:SetFont(default, 34, "OUTLINE")
SubZoneTextFont:SetFont(default, 24, "OUTLINE")
GameTooltipText:SetFont(tooltip, pfUI_config.tooltip.font_tooltip_size)
GameTooltipTextSmall:SetFont(tooltip, pfUI_config.tooltip.font_tooltip_size)
GameTooltipHeaderText:SetFont(tooltip, pfUI_config.tooltip.font_tooltip_size + 1)
WorldMapTextFont:SetFont(default, 102, "THICK")
InvoiceTextFontNormal:SetFont(default, 12)
InvoiceTextFontSmall:SetFont(default, 12)
CombatTextFont:SetFont(combat, 25)
ChatFontNormal:SetFont(default, 13, pfUI_config.chat.text.outline == "1" and "OUTLINE")
if TextStatusBarTextSmall then -- does not exist in koKR
TextStatusBarTextSmall:SetFont(default, 12, "NORMAL")
end
end
local translations
function pfUI:GetEnvironment()
-- load api into environment
for m, func in pairs(pfUI.api or {}) do
pfUI.env[m] = func
end
if pfUI_config and pfUI_config.global and pfUI_config.global.language and not translations then
local lang = pfUI_config and pfUI_config.global and pfUI_config.global.language and pfUI_translation[pfUI_config.global.language] and pfUI_config.global.language or GetLocale()
pfUI.env.T = setmetatable(pfUI_translation[lang] or {}, { __index = function(tab,key)
local value = tostring(key)
rawset(tab,key,value)
return value
end})
translations = true
end
pfUI.env._G = getfenv(0)
pfUI.env.C = pfUI_config
pfUI.env.pfUI_throttle = _G.pfUI_throttle
pfUI.env.L = (pfUI_locale[GetLocale()] or pfUI_locale["enUS"])
return pfUI.env
end
-- [ New Module Registry ]
-- Tracks modules introduced after initial release so existing users get
-- a one-time prompt asking if they want to enable them.
pfUI.new_modules = {}
function pfUI:RegisterNewModule(name, label, class)
table.insert(pfUI.new_modules, { name = name, label = label, class = class })
end
function pfUI:CheckNewModules()
pfUI_init.seen_modules = pfUI_init.seen_modules or {}
local pending = 0
local _, playerClass = UnitClass("player")
for _, entry in pairs(pfUI.new_modules) do
if not pfUI_init.seen_modules[entry.name] and pfUI_init["finalize"] then
if not entry.class or entry.class == playerClass then
pending = pending + 1
end
end
end
if pending == 0 then return end
local remaining = pending
local needsReload = false
local function onAnswer()
remaining = remaining - 1
if remaining <= 0 and needsReload then
ReloadUI()
end
end
for _, entry in pairs(pfUI.new_modules) do
local name = entry.name
if not pfUI_init.seen_modules[name] then
-- Skip modules restricted to a different class
if entry.class and entry.class ~= playerClass then
-- don't mark as seen - player might have a druid alt later
else
pfUI_init.seen_modules[name] = true
-- Only prompt existing users (firstrun wizard already ran)
if pfUI_init["finalize"] then
StaticPopupDialogs["PFUI_NEW_MODULE_" .. strupper(name)] = {
text = "|cff33ffccpfUI|r: New module available!\n\n|cffffffffDo you want to enable |cff33ffcc" .. (entry.label or name) .. "|r|cffffffff?\n\nYou can always change this later in the pfUI settings.|r",
button1 = "Yes",
button2 = "No",
OnAccept = function()
pfUI_config["disabled"] = pfUI_config["disabled"] or {}
pfUI_config["disabled"][name] = nil
onAnswer()
end,
OnCancel = function()
pfUI_config["disabled"] = pfUI_config["disabled"] or {}
pfUI_config["disabled"][name] = "1"
needsReload = true
onAnswer()
end,
timeout = 0,
whileDead = true,
hideOnEscape = false,
}
StaticPopup_Show("PFUI_NEW_MODULE_" .. strupper(name))
end
end -- else (class match)
end
end
end
local function BackwardsCompatRegister(func, arg3)
if arg3 and type(func) == "string" and type(arg3) == "function" and string.find(func, "vanilla") then
return arg3
end
return func
end
function pfUI:RegisterModule(name, func, arg3)
if pfUI.disabled then return end
if pfUI.module[name] then return end
func = BackwardsCompatRegister(func, arg3)
pfUI.module[name] = func
table.insert(pfUI.modules, name)
if not pfUI.bootup then
pfUI:LoadModule(name)
end
end
function pfUI:RegisterSkin(name, func, arg3)
if pfUI.disabled then return end
if pfUI.skin[name] then return end
func = BackwardsCompatRegister(func, arg3)
pfUI.skin[name] = func
table.insert(pfUI.skins, name)
if not pfUI.bootup then
pfUI:LoadSkin(name)
end
end
function pfUI:LoadModule(m)
setfenv(pfUI.module[m], pfUI:GetEnvironment())
pfUI.module[m]()
end
function pfUI:LoadSkin(s)
setfenv(pfUI.skin[s], pfUI:GetEnvironment())
pfUI.skin[s]()
end
pfUI:SetScript("OnEvent", function()
if pfUI.disabled then return end
-- make sure to initialize and set our fonts
-- each time an addon got loaded but only
-- when the config is already accessible
if not pfUI.bootup then
pfUI:UpdateFonts()
end
if arg1 == pfUI.name then
-- read pfUI version from .toc file. Source/dev installs leave Version as
-- "@project-version@" until release tooling substitutes it — mark those
-- explicitly as "dev" instead of pretending they're a real numbered build.
local raw = tostring(GetAddOnMetadata(pfUI.name, "Version"))
if strfind(raw, "@") then
pfUI.version.major, pfUI.version.minor, pfUI.version.fix = 0, 0, 0
pfUI.version.string = "dev"
else
local major, minor, fix = pfUI.api.strsplit(".", raw)
pfUI.version.major = tonumber(major) or 0
pfUI.version.minor = tonumber(minor) or 0
pfUI.version.fix = tonumber(fix) or 0
pfUI.version.string = pfUI.version.major .. "." .. pfUI.version.minor .. "." .. pfUI.version.fix
end
-- use "Modern" as default profile on a fresh install
if pfUI.api.isempty(pfUI_init) and pfUI.api.isempty(pfUI_config) then
pfUI_config = pfUI.api.CopyTable(pfUI_profiles["Modern"]) or {}
end
pfUI:LoadConfig()
pfUI:MigrateConfig()
pfUI:UpdateFonts()
-- load modules
for _, m in pairs(this.modules) do
if not ( pfUI_config["disabled"] and pfUI_config["disabled"][m] == "1" ) then
pfUI:LoadModule(m)
end
end
-- load skins
for _, s in pairs(this.skins) do
if not ( pfUI_config["disabled"] and pfUI_config["disabled"]["skin_" .. s] == "1" ) then
pfUI:LoadSkin(s)
end
end
pfUI.bootup = nil
-- Prompt existing users about newly introduced modules
pfUI:CheckNewModules()
end
end)
pfUI.backdrop = {
bgFile = "Interface\\BUTTONS\\WHITE8X8", tile = false, tileSize = 0,
edgeFile = "Interface\\BUTTONS\\WHITE8X8", edgeSize = 1,
insets = {left = -1, right = -1, top = -1, bottom = -1},
}
pfUI.backdrop_no_top = pfUI.backdrop
pfUI.backdrop_thin = {
bgFile = "Interface\\BUTTONS\\WHITE8X8", tile = false, tileSize = 0,
edgeFile = "Interface\\BUTTONS\\WHITE8X8", edgeSize = 1,
insets = {left = 0, right = 0, top = 0, bottom = 0},
}
pfUI.backdrop_hover = {
edgeFile = "Interface\\BUTTONS\\WHITE8X8", edgeSize = 24,
insets = {left = -1, right = -1, top = -1, bottom = -1},
}
pfUI.backdrop_shadow = {
edgeFile = pfUI.media["img:glow2"], edgeSize = 8,
insets = {left = 0, right = 0, top = 0, bottom = 0},
}
pfUI.backdrop_blizz_bg = {
bgFile = "Interface\\BUTTONS\\WHITE8X8", tile = true, tileSize = 8,
insets = { left = 3, right = 3, top = 3, bottom = 3 }
}
pfUI.backdrop_blizz_border = {
edgeFile = pfUI.media["img:border_blizz"], edgeSize = 6,
insets = { left = 3, right = 3, top = 3, bottom = 3 }
}
pfUI.backdrop_blizz_full = {
bgFile = "Interface\\BUTTONS\\WHITE8X8", tile = true, tileSize = 8,
edgeFile = pfUI.media["img:border_blizz"], edgeSize = 6,
insets = { left = 3, right = 3, top = 3, bottom = 3 }
}
message = function(msg)
DEFAULT_CHAT_FRAME:AddMessage("|cffcccc33INFO: |cffffff55" .. ( msg or "nil" ))
end
print = print or message
error = function(msg)
if PF_DEBUG_MODE then message(debugstack()) end
if string.find(msg, "AddOns\\pfUI") then
DEFAULT_CHAT_FRAME:AddMessage("|cffcc3333ERROR: |cffff5555".. (msg or "nil" ))
elseif not pfUI_config or (pfUI_config.global and pfUI_config.global.errors == "1") then
DEFAULT_CHAT_FRAME:AddMessage("|cffcc3333ERROR: |cffff5555".. (msg or "nil" ))
end
end
seterrorhandler(error)
function pfUI.SetupCVars()
ClearTutorials()
TutorialFrame_HideAllAlerts()
ConsoleExec("CameraDistanceMaxFactor 5")
SetCVar("autoSelfCast", "1")
SetCVar("profanityFilter", "0")
MultiActionBar_ShowAllGrids()
ALWAYS_SHOW_MULTIBARS = "1"
SHOW_BUFF_DURATIONS = "1"
QUEST_FADING_DISABLE = "1"
NAMEPLATES_ON = "1"
SIMPLE_CHAT = "0"
-- Only force combat text CVars when not using the Invisible font.
-- If AdobeBlank is selected, the player wants no combat text -- respect that.
local isInvisible = pfUI_config.global.font_combat and
string.find(pfUI_config.global.font_combat, "AdobeBlank")
if not isInvisible then
SHOW_COMBAT_TEXT = "1"
COMBAT_TEXT_SHOW_LOW_HEALTH_MANA = "1"
COMBAT_TEXT_SHOW_AURAS = "1"
COMBAT_TEXT_SHOW_AURA_FADE = "1"
COMBAT_TEXT_SHOW_COMBAT_STATE = "1"
COMBAT_TEXT_SHOW_DODGE_PARRY_MISS = "1"
COMBAT_TEXT_SHOW_RESISTANCES = "1"
COMBAT_TEXT_SHOW_REPUTATION = "1"
COMBAT_TEXT_SHOW_REACTIVES = "1"
COMBAT_TEXT_SHOW_FRIENDLY_NAMES = "1"
COMBAT_TEXT_SHOW_COMBO_POINTS = "1"
COMBAT_TEXT_SHOW_MANA = "1"
COMBAT_TEXT_FLOAT_MODE = "1"
COMBAT_TEXT_SHOW_HONOR_GAINED = "1"
end
UIParentLoadAddOn("Blizzard_CombatText")
end