Vampify v0.4.0

This commit is contained in:
2026-08-30 15:37:26 +02:00
parent ad50ebe636
commit d3ad691770
14 changed files with 271 additions and 41 deletions
+71 -5
View File
@@ -319,6 +319,28 @@ local lastHp, lastAt, lastExpected = nil, nil, nil
local nextCheck = 0
local WATCH_INTERVAL = 15 -- slow on purpose: findings are read by a human, not a loop
-- ---- the zone, for the per-hit export ---------------------------------------------------------
--
-- Cached rather than read per hit. GetRealZoneText() is cheap but it RETURNS A STRING, and a
-- string per damage event is exactly the per-frame allocation this project forbids in hot paths
-- (30-60 hits/sec during an AoE pull). The zone changes on a loading screen and nowhere else, so
-- it is refreshed from the two events that already fire there.
--
-- WHY IT IS RECORDED AT ALL: an offline analysis could not answer "use only the Shadowfang Keep
-- data" -- the export had no way to tell one grinding session's zone from another's, and mob
-- level (and therefore hit size, and therefore how often the per-source floor of 1 dominates the
-- percentage) varies enormously between them. See docs/VAMPIRISM-MODELL-STAND §4.
--
-- Pipes are stripped because "|" is this format's field separator; no real zone name contains one,
-- but a parser that splits on "|" must not be able to be broken by a name at all.
local zoneName = "?"
local function refreshZone()
local z = GetRealZoneText and GetRealZoneText()
if not z or z == "" then z = GetZoneText and GetZoneText() end
if not z or z == "" then zoneName = "?" return end
zoneName = string.gsub(z, "|", "")
end
-- print_ already prefixes "Vampify", so this must not repeat it -- the first field build did, and
-- the result read "Vampify Vampify watch:".
--
@@ -485,6 +507,13 @@ if VampifyDamage.onDamage then
pred_heal = healFloat,
acc_total = VampifyState.acc.total,
crit = isCrit,
-- The three fields the offline analysis was missing (docs/VAMPIRISM-MODELL-STAND
-- §4). pcts hands over the LIVE list -- PH.recordHit serialises it immediately
-- rather than holding the reference, which matters because this very table is
-- refilled in place on every gear change (see recompute above).
pcts = sourcePercents,
zone = zoneName,
cast = VampifyDamage.castSeenFor and VampifyDamage.castSeenFor(spellId, GetTime()),
}, UnitHealth and UnitHealth("player"), GetTime(), UnitHealthMax and UnitHealthMax("player"))
end
@@ -823,6 +852,9 @@ if CreateFrame then
f:RegisterEvent("PLAYER_CAMPING")
f:RegisterEvent("PLAYER_QUITING")
f:RegisterEvent("LOGOUT_CANCEL")
-- Fires on every loading screen and on every zone border crossed on foot -- the only two ways
-- the cached zone name above can go stale.
f:RegisterEvent("ZONE_CHANGED_NEW_AREA")
-- The running count of combat seconds the "no damage events at all" verdict is given from.
-- Declared out here rather than beside the OnUpdate that uses it because the combat-end branch
@@ -860,8 +892,13 @@ if CreateFrame then
end
return
end
if event == "ZONE_CHANGED_NEW_AREA" then
refreshZone()
return
end
if event == "PLAYER_LOGIN" or event == "PLAYER_ENTERING_WORLD" then
if event == "PLAYER_ENTERING_WORLD" then wireSession() end
refreshZone()
recompute()
syncPerHit()
if VampifyMinimap and VampifyMinimap.update then VampifyMinimap.update() end
@@ -1090,6 +1127,8 @@ local function help()
print_("/vf pos -- move the display back to its default position")
print_("/vf lock -- toggle frame dragging")
print_("/vf fct / sct -- toggle scrolling combat text (either engine)")
print_("/vf sct duration <s> -- SCT fade duration, 0.5-4.0 seconds")
print_("/vf sct shadow on|off -- SCT text shadow")
print_("/vf sctmove -- move our own SCT anchor (options window has the same button)")
print_("/vf options -- open the options window")
print_("/vf source add <pct> -- add a manual source, if tooltip detection fails")
@@ -1163,12 +1202,39 @@ SlashCmdList["VAMPIFY"] = function(msg)
elseif cmd == "fct" or cmd == "sct" then
local cfg = VampifyConfig.get()
if not cfg or not cfg.sct then print_("config not loaded yet.") return end
cfg.sct.enabled = not cfg.sct.enabled
if cfg.sct.enabled and cfg.sct.mode == "blizzard" and SHOW_COMBAT_TEXT ~= "1" then
print_("scrolling combat text ON -- but Blizzard's own combat text is disabled in its"
.." options, so nothing will show in blizzard mode.")
-- Bare "/vf sct" / "/vf fct" keeps its original meaning (toggle enabled); "sct <sub> <val>"
-- is the same sub/val dispatch pattern /vf source and /vf exclude already use above. %S*
-- rather than %d* for val: duration needs a decimal ("1.5"), shadow needs a word (on/off).
local _, _, sub, val = string.find(rest, "^(%a*)%s*(%S*)")
if sub == "" then
cfg.sct.enabled = not cfg.sct.enabled
if cfg.sct.enabled and cfg.sct.mode == "blizzard" and SHOW_COMBAT_TEXT ~= "1" then
print_("scrolling combat text ON -- but Blizzard's own combat text is disabled in its"
.." options, so nothing will show in blizzard mode.")
else
print_(cfg.sct.enabled and "scrolling combat text ON." or "scrolling combat text OFF.")
end
elseif sub == "duration" then
local s = tonumber(val)
if not s or s < 0.5 or s > 4.0 then
print_("/vf sct duration <seconds> -- 0.5 to 4.0, e.g. /vf sct duration 1.5")
else
cfg.sct.duration = s
print_("SCT fade duration set to "..s.."s.")
end
elseif sub == "shadow" then
if val == "on" then
cfg.sct.shadow = true
elseif val == "off" then
cfg.sct.shadow = false
else
print_("/vf sct shadow on|off")
return
end
if VampifySCT and VampifySCT.applyFont then VampifySCT.applyFont() end
print_(cfg.sct.shadow and "SCT text shadow ON." or "SCT text shadow OFF.")
else
print_(cfg.sct.enabled and "scrolling combat text ON." or "scrolling combat text OFF.")
help()
end
elseif cmd == "sctmove" then
if not (VampifySCT and VampifySCT.setMoveMode and VampifySCT.isMoveMode) then