Files
2026-10-30 16:56:31 +00:00

152 lines
4.5 KiB
Lua

-- ScaledLifeTap 4.1 - embedded gear scanner
-- A trimmed-down replacement for BonusScanner that only totals spell damage
-- and shadow damage. It scans lazily: equipment changes just mark the cache
-- dirty, and the actual tooltip scan happens the next time Life Tap is cast.
-- No OnUpdate handler, no per-frame work.
local L = SCALEDLIFETAP_L
local Scanner = {}
ScaledLifeTap_Scanner = Scanner
-- equipment slot ids worth scanning (shirt=4 and tabard=19 never carry spell damage)
local SLOTS = { 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18 }
local MAX_AGE = 60 -- seconds; safety net for temporary enchants (wizard oil) expiring
local TIPNAME = "ScaledLifeTapScanTip"
local tip, lines
local dirty = true
local lastScan = -1000
local dmg, shadow = 0, 0
-- per-scan state
local curSet, countedSets = nil, {}
local find, sub, len, gsub = string.find, string.sub, string.len, string.gsub
local EQUIP, SET = L.EQUIP, L.SET
local EQUIP_LEN, SET_LEN = string.len(L.EQUIP), string.len(L.SET)
local function add(kind, value)
value = tonumber(value)
if not value then return end
if kind == "D" then dmg = dmg + value elseif kind == "S" then shadow = shadow + value end
end
local function trim(s)
s = gsub(s, "^%s+", "")
s = gsub(s, "%s+$", "")
s = gsub(s, "%.$", "")
return s
end
-- "Shadow Damage", "Fire Damage", ... (BonusScanner's stage1/stage2 composite match)
local function checkToken(token, value)
local kind = L.LOOKUP[token]
if kind then add(kind, value); return true end
local school
for i = 1, table.getn(L.STAGE1) do
if find(token, L.STAGE1[i][1], 1, 1) then school = L.STAGE1[i][2] end
end
if not school then return false end
for i = 1, table.getn(L.STAGE2) do
if find(token, L.STAGE2[i], 1, 1) then
if school == "SHADOW" then add("S", value) end
return true
end
end
return false
end
-- "+30 Spell Damage", "Shadow Damage +20", "+8 Spell Damage and Healing/+5 Stamina"
local function checkGeneric(line)
local found = false
while len(line) > 0 do
local part
local pos = find(line, "/", 1, true)
if pos then part, line = sub(line, 1, pos - 1), sub(line, pos + 1) else part, line = line, "" end
part = trim(part)
local _, _, value, token = find(part, "^%+(%d+)%%?(.*)$")
if not value then _, _, token, value = find(part, "^(.*)%+(%d+)%%?$") end
if token and value and checkToken(trim(token), value) then found = true end
end
return found
end
local function checkPassive(text)
for i = 1, table.getn(L.PASSIVE) do
local _, _, value = find(text, L.PASSIVE[i][1])
if value then add(L.PASSIVE[i][2], value); return end
end
checkGeneric(text)
end
local function checkOther(line)
for i = 1, table.getn(L.OTHER) do
if find(line, L.OTHER[i][1]) then add("D", L.OTHER[i][2]); return true end
end
return false
end
local function scanLine(line)
if sub(line, 1, EQUIP_LEN) == EQUIP then
checkPassive(sub(line, EQUIP_LEN + 1))
elseif sub(line, 1, SET_LEN) == SET then
-- only active set bonuses start with "Set: "; count each set once
if curSet and not countedSets[curSet] then checkPassive(sub(line, SET_LEN + 1)) end
else
local _, _, setName = find(line, L.SETNAME)
if setName then
curSet = setName
elseif not checkGeneric(line) then
checkOther(line)
end
end
end
local function ensureTooltip()
if tip then return end
tip = CreateFrame("GameTooltip", TIPNAME, nil, "GameTooltipTemplate")
lines = {}
for i = 1, 30 do lines[i] = getglobal(TIPNAME .. "TextLeft" .. i) end
end
function Scanner:Scan()
ensureTooltip()
dmg, shadow = 0, 0
for k in pairs(countedSets) do countedSets[k] = nil end
for s = 1, table.getn(SLOTS) do
tip:SetOwner(WorldFrame or UIParent, "ANCHOR_NONE")
tip:ClearLines()
curSet = nil
if tip:SetInventoryItem("player", SLOTS[s]) then
local n = tip:NumLines()
if n > 30 then n = 30 end
for i = 2, n do
local fs = lines[i]
local text = fs and fs:GetText()
if text then scanLine(text) end
end
if curSet then countedSets[curSet] = true end
end
end
tip:Hide()
dirty = false
lastScan = GetTime()
end
function Scanner:MarkDirty() dirty = true end
-- returns generic spell damage, shadow spell damage
function Scanner:Get()
if dirty or (GetTime() - lastScan) > MAX_AGE then self:Scan() end
return dmg, shadow
end
-- exposed for tests / debugging: parse arbitrary tooltip lines
function Scanner:ParseLines(list)
dmg, shadow, curSet = 0, 0, nil
for k in pairs(countedSets) do countedSets[k] = nil end
for i = 1, table.getn(list) do scanLine(list[i]) end
dirty = true
return dmg, shadow
end