From 1c0c9dc019561f3d6b96dc751bdb203ee91314f4 Mon Sep 17 00:00:00 2001 From: Brues <5278969+brues-code@users.noreply.github.com> Date: Mon, 20 Jul 2026 16:54:27 -0500 Subject: [PATCH] Replace librange's position scan with ClassicAPI's UnitInRange librange was a per-frame position scanner: it swept party/raid unit tokens, cached each one's distance via UnitPosition, and answered range queries from that cache. All of it existed only because 1.12 had no cheap way to check an arbitrary unit's distance. ClassicAPI's UnitInRange does exactly that C-side (fixed 40y healing range, position miss reported via the second return), so the whole library collapses to a direct call. Wins from dropping the cache: - No staleness. The scanner's zone-death and roster-reindex bugs simply can't exist without a cache to go stale, so this supersedes the keep-alive fixes from 756e8840. - All classes get target-frame range fading. The old target path faked a 40y check via IsActionInRange on a healing spell found on the action bar, so classes without such a spell (GetRangeSlot returned nil) never had a working target range check. The rangecheck == "0" master switch used to be enforced by hiding the scanner; with no scanner, move that gate into pfUI.api.UnitInRange so disabling the check still means nothing fades. Threshold is now 40y (the ClassicAPI constant) rather than the old 45y. Drop the now-dead rangechecki (Range Check Interval) setting, its GUI row, and migration. --- api/api.lua | 5 ++ api/config.lua | 8 -- libs/librange.lua | 194 ++++------------------------------------------ modules/gui.lua | 1 - 4 files changed, 19 insertions(+), 189 deletions(-) diff --git a/api/api.lua b/api/api.lua index 44539a98..26c9092d 100644 --- a/api/api.lua +++ b/api/api.lua @@ -143,6 +143,11 @@ function pfUI.api.UnitInRange(unit) return 1 end + -- master switch: with the 40y check off, a visible unit beyond interact + -- range counts as in range (nothing fades). Invisible units already + -- returned nil above, matching the pre-collapse behavior. + if C.unitframes.rangecheck == "0" then return 1 end + -- UnitXP precise mode: skip librange entirely, use direct distance check if C.unitframes.rangecheck_mode == "unitxp" and _G.UnitXP then local threshold = tonumber(C.unitframes.rangecheck_distance) or 40 diff --git a/api/config.lua b/api/config.lua index 09bd22b5..7987b824 100644 --- a/api/config.lua +++ b/api/config.lua @@ -232,7 +232,6 @@ function pfUI:LoadConfig() pfUI:UpdateConfig("unitframes", nil, "druidmanaspace", "-3") pfUI:UpdateConfig("unitframes", nil, "druidmanatexture", "Interface\\AddOns\\pfUI\\img\\bar") - pfUI:UpdateConfig("unitframes", nil, "rangechecki", "4") pfUI:UpdateConfig("unitframes", nil, "combowidth", "6") pfUI:UpdateConfig("unitframes", nil, "comboheight", "6") pfUI:UpdateConfig("unitframes", nil, "swingtimerwidth", "200") @@ -1110,13 +1109,6 @@ function pfUI:MigrateConfig() end end - -- migrating rangecheck interval (> 3.2.2) - if checkversion(3, 2, 2) then - if tonumber(pfUI_config.unitframes.rangechecki) <= 1 then - pfUI_config.unitframes.rangechecki = "2" - end - end - -- migrating legacy buff/debuff naming (> 3.5.0) if checkversion(3, 5, 0) then local unitframes = { "player", "target", "focus", "group", "grouptarget", "grouppet", "raid", "ttarget", "pet", "ptarget", "fallback" } diff --git a/libs/librange.lua b/libs/librange.lua index 71f11fe4..3692470a 100644 --- a/libs/librange.lua +++ b/libs/librange.lua @@ -2,194 +2,28 @@ setfenv(1, pfUI:GetEnvironment()) --[[ librange ]]-- --- A pfUI library that detects and caches distance to units. +-- A thin wrapper over ClassicAPI's UnitInRange: a fixed 40y healing-range +-- check computed C-side from unit positions, valid for any unit. There is +-- no cache or scan loop -- the check is cheap enough to run per query, +-- which also sidesteps the staleness a cached scan hit on zone changes and +-- roster re-indexing. -- -- librange:UnitInSpellRange(unit) -- Returns `1` if the unit is within range, `nil` otherwise. --- --- Requires SuperWoW's UnitPosition for the friendly scan path. Target --- range still works via IsActionInRange (vanilla-native) for any class --- with a known 40y healing spell on the action bar. if pfUI.api.librange then return end -local _, class = UnitClass("player") -local librange = CreateFrame("Frame", "pfRangecheck", UIParent) - --- 40y spells per class. Only consulted to find an action-bar slot for the --- IsActionInRange target-range path; the party/raid scan uses UnitPosition. -local spells = { - ["PALADIN"] = { - "Interface\\Icons\\Spell_Holy_FlashHeal", - "Interface\\Icons\\Spell_Holy_HolyBolt", - }, - ["PRIEST"] = { - "Interface\\Icons\\Spell_Holy_FlashHeal", - "Interface\\Icons\\Spell_Holy_LesserHeal", - "Interface\\Icons\\Spell_Holy_Heal", - "Interface\\Icons\\Spell_Holy_GreaterHeal", - "Interface\\Icons\\Spell_Holy_Renew", - }, - ["DRUID"] = { - "Interface\\Icons\\Spell_Nature_HealingTouch", - "Interface\\Icons\\Spell_Nature_ResistNature", - "Interface\\Icons\\Spell_Nature_Rejuvenation", - }, - ["SHAMAN"] = { - "Interface\\Icons\\Spell_Nature_MagicImmunity", - "Interface\\Icons\\Spell_Nature_HealingWaveLesser", - "Interface\\Icons\\Spell_Nature_HealingWaveGreater", - }, -} - --- friendly units the scan loop iterates -local units = {} -table.insert(units, "pet") -for i=1,4 do table.insert(units, "party" .. i) end -for i=1,4 do table.insert(units, "partypet" .. i) end -for i=1,40 do table.insert(units, "raid" .. i) end -for i=1,40 do table.insert(units, "raidpet" .. i) end -local numunits = table.getn(units) - -local unitcache = {} -local unitdata = {} -local librange_isLoggingOut = false -librange.id = 1 - -librange:Hide() -librange:RegisterEvent("ACTIONBAR_SLOT_CHANGED") -librange:RegisterEvent("PLAYER_ENTERING_WORLD") -librange:RegisterEvent("PLAYER_LOGOUT") -librange:RegisterEvent("PLAYER_LEAVING_WORLD") -librange:RegisterEvent("RAID_ROSTER_UPDATE") -librange:RegisterEvent("PARTY_MEMBERS_CHANGED") -librange:SetScript("OnEvent", function() - if event == "PLAYER_LOGOUT" then - librange_isLoggingOut = true - this:Hide() - return - end - - if event == "PLAYER_LEAVING_WORLD" then - this:Hide() - return - end - - if event == "RAID_ROSTER_UPDATE" or event == "PARTY_MEMBERS_CHANGED" then - -- Roster re-index: unitN tokens now map to different players, so the - -- token->realunit cache is stale. Clear it and restart the sweep from - -- the top so shifted/joined slots are re-evaluated within one pass - -- instead of inheriting the previous occupant's cached range. - for k in pairs(unitcache) do unitcache[k] = nil end - this.id = 1 - return - end - - if pfUI_config.unitframes.rangecheck == "0" then - this:Hide() - return - end - - this.interval = tonumber(C.unitframes.rangechecki)/numunits - - if event == "ACTIONBAR_SLOT_CHANGED" or event == "PLAYER_ENTERING_WORLD" then - librange.slot = this:GetRangeSlot() - if UnitPosition then this:Show() end - end -end) - -librange:SetScript("OnUpdate", function() - if librange_isLoggingOut then return end - - if (this.tick or 1) > GetTime() then return end - this.tick = GetTime() + this.interval - - while not this:NeedRangeScan(units[this.id]) and this.id <= numunits do - this.id = this.id + 1 - end - - if this.id <= numunits then - local unit = units[this.id] - if not UnitIsUnit("target", unit) then - local x1, y1, z1 = UnitPosition("player") - local x2, y2, z2 = UnitPosition(unit) - if x1 and x2 then - local distance = ((x2 - x1)^2 + (y2 - y1)^2 + (z2 - z1)^2)^.5 - unitdata[unit] = distance < 45 and 1 or 0 - end - end - this.id = this.id + 1 - else - this.id = 1 - end -end) - -function librange:NeedRangeScan(unit) - if not UnitExists(unit) then return nil end - if not UnitIsVisible(unit) then return nil end - if CheckInteractDistance(unit, 4) then return nil end - return true -end - -function librange:GetRealUnit(unit) - if unitdata[unit] then return unit end - - if unitcache[unit] and UnitIsUnit(unitcache[unit], unit) then - return unitcache[unit] - end - - for id, realunit in pairs(units) do - if UnitIsUnit(realunit, unit) then - unitcache[unit] = realunit - return realunit - end - end - - return unit -end - -function librange:GetRangeSlot() - if not spells[class] then return nil end - for i=1,120 do - -- Resolve the slot to a spellID for both spell and macro actions; the old - -- `not GetActionText` macro-filter missed macros that cast a 40y heal but - -- displayed a non-spell icon. C_Spell.GetSpellTexture(spellID) gives the - -- spell's *intrinsic* icon, which is what we match against. - local kind, id = GetActionInfo(i) - local spellID - if kind == "spell" then - spellID = id - elseif kind == "macro" then - local _, _, sid = GetMacroSpell(id) - spellID = sid - end - if spellID then - local texture = C_Spell.GetSpellTexture(spellID) - if texture then - for _, check in pairs(spells[class]) do - if check == texture then return i end - end - end - end - end - return nil -end +local librange = {} function librange:UnitInSpellRange(unit) - if UnitIsUnit("target", unit) then - if not librange.slot then return nil end - return IsActionInRange(librange.slot) == 1 and 1 or nil - end - - local unit = librange:GetRealUnit(unit) - - if unitdata[unit] and unitdata[unit] == 1 then - return 1 - elseif not unitdata[unit] then - return 1 - else - return nil - end + -- _G-qualified: bare `UnitInRange` resolves to pfUI.api.UnitInRange inside + -- the pfUI environment (which calls us), so this must reach ClassicAPI's + -- global directly or it recurses. + local inRange, checked = _G.UnitInRange(unit) + -- position miss (e.g. a unit outside the client's sync range): we can't + -- tell, so default to in-range -- matches the old cache's nil behavior. + if not checked then return 1 end + return inRange and 1 or nil end -- add librange to pfUI API diff --git a/modules/gui.lua b/modules/gui.lua index 98923e00..8b214b4e 100644 --- a/modules/gui.lua +++ b/modules/gui.lua @@ -2119,7 +2119,6 @@ pfUI:RegisterModule("gui", function () CreateConfig(nil, T["Enable 40y-Range Check"], C.unitframes, "rangecheck", "checkbox", nil, nil, nil, nil) CreateConfig(nil, T["Range Check Mode"], C.unitframes, "rangecheck_mode", "dropdown", pfUI.gui.dropdowns.uf_rangecheck_mode, nil, nil, nil) CreateConfig(nil, T["UnitXP Range Threshold (yards)"], C.unitframes, "rangecheck_distance", nil, nil, nil, nil, nil) - CreateConfig(nil, T["Range Check Interval"], C.unitframes, "rangechecki", "dropdown", pfUI.gui.dropdowns.uf_rangecheckinterval, nil, nil, nil) CreateConfig(nil, T["Use Raid Frames To Display Group Members"], C.unitframes, "raidforgroup", "checkbox") CreateConfig(nil, T["Always Show Self In Raid Frames"], C.unitframes, "selfinraid", "checkbox") CreateConfig(nil, T["Show Self In Group Frames"], C.unitframes, "selfingroup", "checkbox")