Files
DopingControl/scan/resist.lua
T
2026-08-03 09:47:19 +02:00

115 lines
4.6 KiB
Lua

-- DopingControl scan/resist.lua
-- Pure resistance-reconstruction math: turn item TOOLTIP lines into a
-- per-school sum, and look up a race's innate bonus. Mirrors scan/hit.lua's
-- pure half (ParseHitLine/SumGear) one level down -- the constants live in
-- data/resist.lua, this file only turns them into behavior.
--
-- NO WoW-glue function lives here on purpose: unlike scan/hit.lua/scan/
-- gear.lua/scan/aura.lua, this module never calls SetInventoryItem itself.
-- The RESIST tab's numbers are reconstructed from tooltip lines
-- scan/hit.lua's item scan ALREADY fetched (see that file's itemHit()) --
-- one more table walk over data already in hand, not a second tooltip
-- operation per item. That is the entire point of hanging resistance
-- reconstruction off the HIT tab's existing per-item cache instead of
-- building an independent RESIST scan pass: 26 players x 19 slots would be
-- ~500 extra SetInventoryItem calls per scan, and scan/hit.lua already pays
-- that cost once per unique item.
--
-- Pure Lua 5.0, no WoW API, dofile-loadable offline: everything here is
-- offline-tested (test_resist.lua).
DopingControl = DopingControl or {}
local DC = DopingControl
DC_Resist = DC_Resist or {}
local R = DC_Resist
-- ParseResistLine(text) -> kind, value | nil
-- kind : a DC.RESIST_SCHOOLS name ("Fire", "Nature", ...) or "all"
-- value : the flat resistance amount as a number
-- Returns nil (a single value) when no pattern matches -- an unrecognized
-- phrasing is ignored, never guessed at (same contract as
-- DC_Hit.ParseHitLine).
function R.ParseResistLine(text)
if type(text) ~= "string" then
return nil
end
local pats = DC.RESIST_PATTERNS
if type(pats) ~= "table" then
return nil
end
local n = table.getn(pats)
for i = 1, n do
local e = pats[i]
local _, _, cap = string.find(text, e.pattern)
if cap then
local v = tonumber(cap)
if v then
return e.kind, v
end
end
end
return nil
end
-- SumLines(lines) -> sum
-- lines : the FULL tooltip line array of ONE item, element 1 = item name
-- (same convention as scan/hit.lua's itemHit -- line 1 is never
-- a stat line, so the scan starts at 2; a caller that already
-- stripped the name may safely pass an array starting at the
-- real first stat line too, since no genuine item name matches
-- "^%+%d+ <School> Resistance" or "^Equip: %+%d+ All
-- Resistances" in the first place).
-- sum : { [UnitResistance school index] = total } -- ONLY schools
-- that actually appeared; a plain item with no resistance stat
-- returns {} (empty, not nil) -- an empty sum is a real "this
-- item carries no resistance", exactly like SumGear's zero.
-- The "All Resistances" trap this table exists to close: a naive parser
-- keyed only on the five single-school patterns would silently skip
-- "Equip: +8 All Resistances." and undercount every one of the five
-- columns by that amount. Handled here by fanning ONE matched line out
-- over every entry of DC.RESIST_SCHOOLS.
function R.SumLines(lines)
local sum = {}
lines = lines or {}
local n = table.getn(lines)
for i = 2, n do
local kind, v = R.ParseResistLine(lines[i])
if kind then
if kind == "all" then
local schools = DC.RESIST_SCHOOLS or {}
for s = 1, table.getn(schools) do
local school = schools[s].school
sum[school] = (sum[school] or 0) + v
end
else
local num = DC.RESIST_SCHOOL_NUM and DC.RESIST_SCHOOL_NUM[kind]
if num then
sum[num] = (sum[num] or 0) + v
end
end
end
end
return sum
end
-- RaceBonus(race) -> { [UnitResistance school index] = bonus }
-- ALWAYS returns a table (empty for "no bonus", an unrecognized race
-- token/display name, or nil input) so callers can fold it onto a gear sum
-- without a nil check. The returned table is a FRESH COPY, never the
-- shared DC.RACE_RESIST entry itself -- DC.RACE_RESIST aliases two
-- spellings of the same race to ONE table object, so handing that
-- reference out would let a caller's in-place addition (E.AssembleResists
-- folds this straight onto the gear sum) corrupt the shared constant for
-- every future lookup of that race.
function R.RaceBonus(race)
local out = {}
local tbl = race and DC.RACE_RESIST and DC.RACE_RESIST[race]
if tbl then
for school, v in pairs(tbl) do
out[school] = v
end
end
return out
end