dpslog: WotLK CLEU parity — overkill, overheal, spell names, unit names, GetSpellInfo
Full WotLK 3.3.5 COMBAT_LOG_EVENT_UNFILTERED layout parity: Event structure: - Base: subevent, sourceGUID, sourceName, destGUID, destName - Spell prefix: spellId, spellName, spellSchool - All suffix field orders match WotLK (blocked before absorbed, etc.) New fields: - overkill (-1 if alive) on all _DAMAGE events - overheal on SPELL_HEAL and SPELL_PERIODIC_HEAL - sourceName/destName via getNameByGUID (name cache 0x55F080) - spellName via SpellRec+0x1E0+locale*4 (verified from SuperWoW) - glancing/crushing as separate booleans (was packed flags) - amountMissed field (0 — downstream hook lacks amount) New hooks: - ProcessStandardPowerGainMessage (0x62CA00) for SPELL_ENERGIZE with actual amount and powerType from SMSG_SPELLENERGIZELOG (opcode 0x151) - Periodic energize: divide by power display factor (rage/10, happiness/1000) Leech/drain reclassification: - Drain Life routed through SPELL_PERIODIC_LEECH (aura 53 check in spell DB) - SPELL_HEAL suppressed for leech spells (covered by leech event) - Power leech (aura 64): gainMultiplier read as f32, >0 = LEECH, ==0 = DRAIN - _LEECH/_DRAIN suffix: amount, powerType(-2=health), extraAmount Lua API: - GetSpellInfo(spellId) -> name, rank, icon, castTime, minRange, maxRange, spellId Matches WotLK returns. DBC addresses verified from nampower + Ghidra: SpellRange.dbc (0xC0D79C), SpellIcon.dbc (0xC0D7EC), SpellCastTimes.dbc (0xC0D878). SpellRec offsets from nampower struct. Reference docs: - WOTLK_CLEU_SPEC.md: full suffix spec from Blizzard_CombatLog.lua + Skada - WARDEN_CONCERNS.md: hook risk analysis and remaining parity gaps
This commit is contained in:
@@ -0,0 +1,141 @@
|
||||
# DPSLog Warden Concerns
|
||||
|
||||
Hooks and patterns that could trigger Warden false positives. Our addon is legitimate
|
||||
(combat log enhancement), but we should understand the detection surface to reduce
|
||||
headaches for server admins reviewing client modifications.
|
||||
|
||||
## What Warden Covers
|
||||
|
||||
Warden is Blizzard's anti-cheat system. On vanilla private servers, implementations vary
|
||||
(some use stock mangos Warden, some extend it, some disable it). The main detection
|
||||
vectors are:
|
||||
|
||||
1. **Memory scanning**: Warden reads specific addresses and compares against known-good
|
||||
values. Patched bytes at scanned addresses trigger detection.
|
||||
2. **Module scanning**: Warden enumerates loaded DLLs and checks against blocklists
|
||||
(by name or hash). Our DLL loads via injection, so it shows up in the module list.
|
||||
3. **Window enumeration**: Checks for known cheat tool windows (not relevant to us).
|
||||
4. **Driver checks**: Checks for known cheat drivers (not relevant).
|
||||
5. **Timing checks**: Detects speed hacks via tick count validation (not relevant).
|
||||
|
||||
### What We Should Avoid
|
||||
|
||||
- **Patching packet handler dispatch tables**: The opcode-to-handler mapping tables are
|
||||
commonly scanned. Modifying entries there is high risk.
|
||||
- **Patching Warden itself**: Obvious detection surface; we don't touch it.
|
||||
- **Known DLL names**: Some servers blocklist specific DLL names. Our DLL name
|
||||
("weirdutils") is not on any known list.
|
||||
- **Hooking at addresses that overlap with known Warden scan points**: If Warden reads
|
||||
the first N bytes of a function and we've overwritten them with a JMP, it detects the
|
||||
modification. We use zhook's Detour which patches the function prologue.
|
||||
|
||||
### What's Generally Safe
|
||||
|
||||
- **Calling game functions** via indirect CALL: No memory modification, just normal
|
||||
function calls. GetSpellNameById, getNameByGUID, etc.
|
||||
- **Reading game memory**: Simple pointer dereferences. Descriptor reads, object manager
|
||||
traversal, aura slot reads.
|
||||
- **Hooking internal processing functions** (not packet handlers): These are deep inside
|
||||
the call chain, less likely to be scanned.
|
||||
- **Registering custom Lua events**: We modify the event count but don't patch any
|
||||
Warden-monitored jump tables.
|
||||
|
||||
## Current Hook Strategy
|
||||
|
||||
We hook **downstream processing functions** (0x62xxxx range) rather than packet handlers
|
||||
(0x5Exxxx range) where possible. Downstream hooks are lower risk because:
|
||||
- They're internal client functions, not dispatch table entries
|
||||
- Warden scans typically check packet handler jump tables
|
||||
- The downstream functions are called after packet validation
|
||||
|
||||
## Hooks That Are Packet Handlers (Higher Risk)
|
||||
|
||||
| Address | Function | Why packet-level |
|
||||
|---------|----------|-----------------|
|
||||
| 0x5E85E0 | SpellNonMeleeDmgLogHandler | Downstream loses school field; no viable alternative |
|
||||
| 0x626DD0 | PeriodicAuraLogHandler | IS the handler; no downstream exists |
|
||||
| 0x6255B0 | MeleeDispatcher | Shared handler for opcode range 0x143-0x14A |
|
||||
| 0x6E7640 | SpellStartHandler | SPELL_CAST_START/SUCCESS |
|
||||
| 0x6E7330 | CastResultHandler | SPELL_CAST_FAILED |
|
||||
|
||||
**Mitigation**: These are all registered via `setClientIndexedPointers` (0x5AB650), not
|
||||
in a flat dispatch table. Warden typically scans flat opcode tables, not the indexed
|
||||
handler registration system. Risk is moderate, not high.
|
||||
|
||||
## Hooks That Are Safe (Downstream)
|
||||
|
||||
| Address | Function | Subevent |
|
||||
|---------|----------|----------|
|
||||
| 0x62C770 | ProcessSpellPowerDrainMessage | SPELL_HEAL |
|
||||
| 0x62BAB0 | ProcessSpellCombatResult | SPELL_MISSED |
|
||||
| 0x62CA20 | ProcessSpellDrainEffectMessage | DAMAGE_SHIELD |
|
||||
| 0x626A10 | DisplaySpellInterruptMessage | SPELL_INTERRUPT |
|
||||
| 0x62CBE0 | ProcessInstaKillSpellMessage | SPELL_INSTAKILL |
|
||||
| 0x62D9F0 | (extra attacks handler) | SPELL_EXTRA_ATTACKS |
|
||||
| 0x62AAC0 | ProcessEnvironmentalDamage | ENVIRONMENTAL_DAMAGE |
|
||||
| 0x628890 | PartyKillLogHandler | PARTY_KILL |
|
||||
| 0x605860 | HandleUnitDeath | UNIT_DIED |
|
||||
| 0x6123F0 | SetSpellTarget | SPELL_AURA_APPLIED |
|
||||
| 0x612320 | CastSpell (aura remove) | SPELL_AURA_REMOVED |
|
||||
| 0x628C20 | ProcessMultipleSpellInterrupts | SPELL_DISPEL_FAILED |
|
||||
| 0x6E75F0 | HandleSpellInterruptUpdate | SPELL_CAST_FAILED (others) |
|
||||
| 0x62CA00 | ProcessStandardPowerGainMessage | SPELL_ENERGIZE |
|
||||
|
||||
## Event Registration
|
||||
|
||||
We register COMBAT_LOG_EVENT at slot 549 by hooking FrameScript_CreateEvents (0x703D90)
|
||||
and bumping the event count. This modifies the event table size but doesn't patch any
|
||||
Warden-monitored code.
|
||||
|
||||
## Memory Reads
|
||||
|
||||
All descriptor reads (health, power, aura slots) use `readMem` which is a simple
|
||||
pointer dereference -- no API hooking involved.
|
||||
|
||||
## GetSpellNameById (0x6264B0) / getNameByGUID (0x55F080)
|
||||
|
||||
Called via `hook.call` (indirect CALL instruction). Not hooked/patched -- we just call
|
||||
them as normal functions. No Warden concern.
|
||||
|
||||
## Remaining WotLK Parity Gaps
|
||||
|
||||
Features not yet implemented, with notes on Warden implications:
|
||||
|
||||
### amountMissed (partial absorb/block/resist on misses)
|
||||
|
||||
The downstream hook ProcessSpellCombatResult (0x62BAB0) doesn't receive the miss amount.
|
||||
Options:
|
||||
1. Hook SMSG_SPELLLOGMISS packet handler (0x5E7E00) -- **higher Warden risk**
|
||||
2. Access the CDS buffer from the downstream hook -- needs investigation, the buffer
|
||||
may still be readable if the packet handler hasn't cleaned it up
|
||||
3. Track state: when we see a partial absorb/block in a damage event that also has a
|
||||
miss component, carry the amount forward
|
||||
|
||||
**Recommendation**: Option 2 first (no new hooks). If CDS is stale, option 3.
|
||||
|
||||
### sourceFlags / destFlags (COMBATLOG_OBJECT_* bitfield)
|
||||
|
||||
Constructable from GUID type bits and descriptor flags. No new hooks needed -- pure
|
||||
memory reads. Requires defining the WotLK COMBATLOG_OBJECT_* constants and mapping
|
||||
vanilla unit types to them.
|
||||
|
||||
### Boolean 1/nil (critical, glancing, crushing)
|
||||
|
||||
SignalEventParam always pushes numbers via `%d`. To push nil for false booleans, we'd
|
||||
need to bypass SignalEventParam and push args directly onto the Lua stack using
|
||||
`lua_pushnil` / `lua_pushnumber`. Requires finding the Lua state pointer and calling
|
||||
Lua C API functions directly. No Warden concern (just function calls), but invasive
|
||||
change to the event-push mechanism.
|
||||
|
||||
### Interrupter / Dispeller / Damage Shield spell IDs
|
||||
|
||||
Solvable with a cast tracking ring buffer -- no new hooks. On SPELL_CAST_SUCCESS, record
|
||||
(casterGUID, spellId, timestamp) for interrupt-type, dispel-type, and damage-shield-type
|
||||
spells. When the corresponding event fires, look up the most recent matching cast by the
|
||||
same caster. Pure memory tracking, no Warden concern.
|
||||
|
||||
### SPELL_ENERGIZE amount (RESOLVED)
|
||||
|
||||
Now hooked via ProcessStandardPowerGainMessage (0x62CA00), downstream of
|
||||
SMSG_SPELLENERGIZELOG (opcode 0x151). Receives spellId, powerType, and display amount
|
||||
directly. Safe downstream hook -- no Warden concern.
|
||||
@@ -0,0 +1,201 @@
|
||||
# WotLK 3.3.5 COMBAT_LOG_EVENT_UNFILTERED — Full Arg Specification
|
||||
|
||||
Sources:
|
||||
- `Blizzard_CombatLog.lua` from tekkub/wow-ui-source 3.3.5 branch (authoritative)
|
||||
- Validated against `bkader/Skada-WoTLK` parser (Core/Functions.lua suffix tables)
|
||||
|
||||
## Base Args (all events)
|
||||
|
||||
| Pos | Field | Type |
|
||||
|-----|-------|------|
|
||||
| 1 | timestamp | number |
|
||||
| 2 | subevent | string |
|
||||
| 3 | sourceGUID | string |
|
||||
| 4 | sourceName | string |
|
||||
| 5 | sourceFlags | number |
|
||||
| 6 | destGUID | string |
|
||||
| 7 | destName | string |
|
||||
| 8 | destFlags | number |
|
||||
|
||||
After base args, events have prefix args then suffix args.
|
||||
|
||||
## Prefixes
|
||||
|
||||
**Spell prefix** (SPELL_, RANGE_, SPELL_PERIODIC_):
|
||||
- spellId (number)
|
||||
- spellName (string)
|
||||
- spellSchool (number)
|
||||
|
||||
**Environmental prefix** (ENVIRONMENTAL_):
|
||||
- environmentalType (string) — "Drowning", "Falling", "Fatigue", "Fire", "Lava", "Slime"
|
||||
|
||||
**Swing prefix** (SWING_):
|
||||
- (none)
|
||||
|
||||
**Inline prefix** (DAMAGE_SHIELD, DAMAGE_SHIELD_MISSED, DAMAGE_SPLIT):
|
||||
- spellId, spellName, spellSchool inlined before suffix (not a standard prefix)
|
||||
|
||||
## Suffixes
|
||||
|
||||
### _DAMAGE
|
||||
Used by: SPELL_DAMAGE, RANGE_DAMAGE, SPELL_PERIODIC_DAMAGE, DAMAGE_SHIELD, DAMAGE_SPLIT,
|
||||
SWING_DAMAGE, ENVIRONMENTAL_DAMAGE
|
||||
|
||||
| # | Field | Type | Notes |
|
||||
|---|-------|------|-------|
|
||||
| 1 | amount | number | damage dealt |
|
||||
| 2 | overkill | number | -1 if target alive |
|
||||
| 3 | school | number | damage school bitmask |
|
||||
| 4 | resisted | number | 0 if none |
|
||||
| 5 | blocked | number | 0 if none |
|
||||
| 6 | absorbed | number | 0 if none |
|
||||
| 7 | critical | 1/nil | |
|
||||
| 8 | glancing | 1/nil | |
|
||||
| 9 | crushing | 1/nil | |
|
||||
|
||||
### _MISSED
|
||||
Used by: SPELL_MISSED, RANGE_MISSED, SPELL_PERIODIC_MISSED, DAMAGE_SHIELD_MISSED,
|
||||
SWING_MISSED
|
||||
|
||||
| # | Field | Type | Notes |
|
||||
|---|-------|------|-------|
|
||||
| 1 | missType | string | ABSORB, BLOCK, DEFLECT, DODGE, EVADE, IMMUNE, MISS, PARRY, REFLECT, RESIST |
|
||||
| 2 | amountMissed | number/nil | only for ABSORB, BLOCK, RESIST |
|
||||
|
||||
Note: isOffHand was added in a later client version (post-3.3.5), not present in 3.3.5.
|
||||
|
||||
### _HEAL
|
||||
Used by: SPELL_HEAL, SPELL_PERIODIC_HEAL
|
||||
|
||||
| # | Field | Type | Notes |
|
||||
|---|-------|------|-------|
|
||||
| 1 | amount | number | total heal |
|
||||
| 2 | overhealing | number | overheal amount |
|
||||
| 3 | absorbed | number | heal absorb (e.g. Necrotic Aura) |
|
||||
| 4 | critical | 1/nil | |
|
||||
|
||||
### _ENERGIZE
|
||||
Used by: SPELL_ENERGIZE, SPELL_PERIODIC_ENERGIZE
|
||||
|
||||
| # | Field | Type | Notes |
|
||||
|---|-------|------|-------|
|
||||
| 1 | amount | number | power gained |
|
||||
| 2 | powerType | number | 0=mana, 1=rage, 2=focus, 3=energy, 4=combo, 5=runes, 6=runic |
|
||||
|
||||
### _DRAIN
|
||||
Used by: SPELL_DRAIN, SPELL_PERIODIC_DRAIN
|
||||
|
||||
| # | Field | Type | Notes |
|
||||
|---|-------|------|-------|
|
||||
| 1 | amount | number | amount drained |
|
||||
| 2 | powerType | number | power type |
|
||||
| 3 | extraAmount | number | amount gained by drainer (typically 0) |
|
||||
|
||||
### _LEECH
|
||||
Used by: SPELL_LEECH, SPELL_PERIODIC_LEECH
|
||||
|
||||
| # | Field | Type | Notes |
|
||||
|---|-------|------|-------|
|
||||
| 1 | amount | number | amount leeched |
|
||||
| 2 | powerType | number | -2=health, 0=mana, etc. |
|
||||
| 3 | extraAmount | number | amount gained by leecher |
|
||||
|
||||
### _INTERRUPT
|
||||
Used by: SPELL_INTERRUPT
|
||||
|
||||
| # | Field | Type | Notes |
|
||||
|---|-------|------|-------|
|
||||
| 1 | extraSpellId | number | interrupted spell |
|
||||
| 2 | extraSpellName | string | |
|
||||
| 3 | extraSchool | number | |
|
||||
|
||||
### _DISPEL
|
||||
Used by: SPELL_DISPEL
|
||||
|
||||
| # | Field | Type | Notes |
|
||||
|---|-------|------|-------|
|
||||
| 1 | extraSpellId | number | dispelled aura |
|
||||
| 2 | extraSpellName | string | |
|
||||
| 3 | extraSchool | number | |
|
||||
| 4 | auraType | string | "BUFF" or "DEBUFF" |
|
||||
|
||||
### _STOLEN
|
||||
Used by: SPELL_STOLEN (Spellsteal)
|
||||
|
||||
Same as _DISPEL: extraSpellId, extraSpellName, extraSchool, auraType
|
||||
|
||||
### _DISPEL_FAILED
|
||||
Used by: SPELL_DISPEL_FAILED
|
||||
|
||||
| # | Field | Type | Notes |
|
||||
|---|-------|------|-------|
|
||||
| 1 | extraSpellId | number | aura that resisted |
|
||||
| 2 | extraSpellName | string | |
|
||||
| 3 | extraSchool | number | |
|
||||
|
||||
### _EXTRA_ATTACKS
|
||||
Used by: SPELL_EXTRA_ATTACKS
|
||||
|
||||
| # | Field | Type | Notes |
|
||||
|---|-------|------|-------|
|
||||
| 1 | amount | number | number of extra attacks |
|
||||
|
||||
### _AURA_APPLIED / _AURA_REMOVED / _AURA_REFRESH
|
||||
Used by: SPELL_AURA_APPLIED, SPELL_AURA_REMOVED, SPELL_AURA_REFRESH
|
||||
|
||||
| # | Field | Type | Notes |
|
||||
|---|-------|------|-------|
|
||||
| 1 | auraType | string | "BUFF" or "DEBUFF" |
|
||||
|
||||
### _AURA_APPLIED_DOSE / _AURA_REMOVED_DOSE
|
||||
|
||||
| # | Field | Type | Notes |
|
||||
|---|-------|------|-------|
|
||||
| 1 | auraType | string | "BUFF" or "DEBUFF" |
|
||||
| 2 | amount | number | stack count |
|
||||
|
||||
### _AURA_BROKEN
|
||||
|
||||
| # | Field | Type | Notes |
|
||||
|---|-------|------|-------|
|
||||
| 1 | auraType | string | "BUFF" or "DEBUFF" |
|
||||
|
||||
### _AURA_BROKEN_SPELL
|
||||
|
||||
| # | Field | Type | Notes |
|
||||
|---|-------|------|-------|
|
||||
| 1 | extraSpellId | number | breaking spell |
|
||||
| 2 | extraSpellName | string | |
|
||||
| 3 | extraSchool | number | |
|
||||
| 4 | auraType | string | "BUFF" or "DEBUFF" |
|
||||
|
||||
### No-suffix events
|
||||
_CAST_START, _CAST_SUCCESS, _INSTAKILL, _SUMMON, _RESURRECT, _CREATE:
|
||||
(no suffix args — spell prefix only)
|
||||
|
||||
### _CAST_FAILED
|
||||
|
||||
| # | Field | Type | Notes |
|
||||
|---|-------|------|-------|
|
||||
| 1 | failedType | string | localized failure reason |
|
||||
|
||||
### UNIT_DIED, PARTY_KILL, UNIT_DESTROYED
|
||||
(base args only, no prefix, no suffix)
|
||||
|
||||
## Power Types
|
||||
0=Mana, 1=Rage, 2=Focus, 3=Energy, 4=Combo Points, 5=Runes, 6=Runic Power, -2=Health
|
||||
|
||||
## School Bitmask
|
||||
1=Physical, 2=Holy, 4=Fire, 8=Nature, 16=Frost, 32=Shadow, 64=Arcane
|
||||
|
||||
## Notes
|
||||
- overkill added in WotLK (not in TBC)
|
||||
- overhealing on _HEAL added in 3.2/3.3
|
||||
- absorbed on _HEAL (heal absorb) added in 3.3
|
||||
- Boolean fields are 1 or nil, not true/false
|
||||
- DAMAGE_SPLIT exists as a distinct subevent, uses inline prefix + _DAMAGE suffix
|
||||
- DAMAGE_SHIELD and DAMAGE_SHIELD_MISSED use inline prefix (not standard spell prefix)
|
||||
- spellName is part of the prefix; our vanilla DLL omits it (addon can look up from spellId)
|
||||
- hideCaster was added in 4.1.0 (Cata), NOT present in WotLK 3.3.5
|
||||
- Skada aliases DAMAGE_SHIELD and DAMAGE_SPLIT to SPELL_DAMAGE layout (prefix+suffix)
|
||||
- Skada reads _AURA_APPLIED/REMOVED/REFRESH with (auraType, amount) — amount is nil for non-dose
|
||||
@@ -1,42 +1,29 @@
|
||||
-- DPS Log Tracker — popup checklist for verifying all 35 COMBAT_LOG_EVENT subevents
|
||||
-- DPS Log Tracker -- WotLK CLEU parity for vanilla 1.12.1
|
||||
--
|
||||
-- All events share: arg1=subevent, arg2=sourceGUID, arg3=destGUID
|
||||
-- Remaining args are subevent-specific (see layouts below).
|
||||
-- Event layout matches WotLK 3.3.5 COMBAT_LOG_EVENT_UNFILTERED (see WOTLK_CLEU_SPEC.md).
|
||||
-- Base: arg1=subevent, arg2=sourceGUID, arg3=sourceName, arg4=destGUID, arg5=destName
|
||||
-- Spell prefix: spellId, spellName, spellSchool
|
||||
-- Env prefix: envType (string)
|
||||
-- Swing prefix: (none)
|
||||
|
||||
-- ============================================================================
|
||||
-- Reference tables
|
||||
-- Reference
|
||||
-- ============================================================================
|
||||
|
||||
-- Spell school bitmask (matches WotLK):
|
||||
-- 0x01 = Physical, 0x02 = Holy, 0x04 = Fire, 0x08 = Nature,
|
||||
-- 0x10 = Frost, 0x20 = Shadow, 0x40 = Arcane
|
||||
-- Common combos: 0x03 = Holystrike, 0x05 = Flamestrike, 0x1C = Elemental,
|
||||
-- 0x7E = Magic (all non-physical), 0x7F = Chaos (all 7)
|
||||
-- School bitmask: 1=Physical, 2=Holy, 4=Fire, 8=Nature, 16=Frost, 32=Shadow, 64=Arcane
|
||||
-- Power types: 0=Mana, 1=Rage, 2=Focus, 3=Energy, 4=Combo, -2=Health
|
||||
-- Miss types: MISS, DODGE, PARRY, BLOCK, EVADE, IMMUNE, DEFLECT, RESIST, ABSORB, REFLECT
|
||||
-- Aura types: BUFF, DEBUFF
|
||||
-- Env types: EXHAUSTED, DROWNING, FALLING, LAVA, SLIME, FIRE
|
||||
|
||||
-- Power types: 0=Mana, 1=Rage, 2=Focus, 3=Energy, 4=Combo Points
|
||||
local powerNames = { [0]="Mana", "Rage", "Focus", "Energy", "Combo Points" }
|
||||
|
||||
-- Miss types (string): "MISS", "DODGE", "PARRY", "BLOCK", "EVADE", "IMMUNE",
|
||||
-- "DEFLECT", "RESIST", "ABSORB", "REFLECT"
|
||||
|
||||
-- Aura types (string): "BUFF", "DEBUFF"
|
||||
|
||||
-- Environmental types (string): "EXHAUSTED", "DROWNING", "FALLING", "LAVA",
|
||||
-- "SLIME", "FIRE"
|
||||
|
||||
-- SWING_DAMAGE arg10 flags bitmask:
|
||||
-- bit 0 (value 1) = glancing hit
|
||||
-- bit 1 (value 2) = crushing blow
|
||||
-- bit 2 (value 4) = offhand swing
|
||||
-- Extract: glancing = (math.mod(flags, 2) == 1)
|
||||
-- crushing = (math.mod(math.floor(flags / 2), 2) == 1)
|
||||
-- offhand = (math.mod(math.floor(flags / 4), 2) == 1)
|
||||
-- Combined: offhand glancing = 5, offhand crushing = 6, etc.
|
||||
|
||||
-- ============================================================================
|
||||
-- Helpers
|
||||
-- ============================================================================
|
||||
|
||||
local format = string.format
|
||||
|
||||
local function schoolName(school)
|
||||
if school == 1 then return "Physical"
|
||||
elseif school == 2 then return "Holy"
|
||||
@@ -53,68 +40,118 @@ local function s(v)
|
||||
return tostring(v)
|
||||
end
|
||||
|
||||
local function displayName(name, guid)
|
||||
if name and name ~= "" then return name end
|
||||
return guid or "?"
|
||||
end
|
||||
|
||||
-- ============================================================================
|
||||
-- Subevent definitions — ordered list of all 34
|
||||
-- Arg position constants -- WotLK CLEU parity
|
||||
-- ============================================================================
|
||||
|
||||
-- Each entry: { displayName, subevent, [variantField], [variantValue] }
|
||||
-- variantField: which arg to check for the variant (nil = any occurrence)
|
||||
-- variantValue: specific value to match (string or number)
|
||||
-- Base: arg1=subevent, arg2=srcGUID, arg3=srcName, arg4=dstGUID, arg5=dstName
|
||||
-- Spell prefix events: arg6=spellId, arg7=spellName, arg8=spellSchool
|
||||
-- Suffix starts at arg9 for spell-prefix events
|
||||
-- Swing events: suffix starts at arg6 (no prefix)
|
||||
-- Env events: arg6=envType, suffix starts at arg7
|
||||
|
||||
-- _DAMAGE suffix (9 fields): amount, overkill, school, resisted, blocked, absorbed, critical, glancing, crushing
|
||||
-- Spell damage: arg6-8=prefix, arg9-17=suffix
|
||||
-- Swing damage: arg6-14=suffix
|
||||
-- Env damage: arg6=envType, arg7-15=suffix
|
||||
|
||||
-- _MISSED suffix: missType, amountMissed
|
||||
-- Spell missed: arg6-8=prefix, arg9=missType, arg10=amountMissed
|
||||
-- Swing missed: arg6=missType, arg7=amountMissed
|
||||
|
||||
-- _HEAL suffix: amount, overheal, absorbed, critical
|
||||
-- arg6-8=prefix, arg9-12=suffix
|
||||
|
||||
-- _ENERGIZE suffix: amount, powerType
|
||||
-- arg6-8=prefix, arg9-10=suffix
|
||||
|
||||
-- _LEECH/_DRAIN suffix: amount, powerType, extraAmount
|
||||
-- arg6-8=prefix, arg9-11=suffix
|
||||
|
||||
-- _EXTRA_ATTACKS suffix: amount
|
||||
-- arg6-8=prefix, arg9=amount
|
||||
|
||||
-- _INTERRUPT/_DISPEL_FAILED suffix: extraSpellId, extraSpellName, extraSchool
|
||||
-- arg6-8=prefix, arg9-11=suffix
|
||||
|
||||
-- _DISPEL/_STOLEN/_AURA_BROKEN_SPELL suffix: extraSpellId, extraSpellName, extraSchool, auraType
|
||||
-- arg6-8=prefix, arg9-12=suffix
|
||||
|
||||
-- _AURA_APPLIED/REMOVED/REFRESH/BROKEN: auraType
|
||||
-- arg6-8=prefix, arg9=auraType
|
||||
|
||||
-- _AURA_DOSE: auraType, amount
|
||||
-- arg6-8=prefix, arg9=auraType, arg10=amount
|
||||
|
||||
-- _CAST_FAILED: failedType
|
||||
-- arg6-8=prefix, arg9=failedType
|
||||
|
||||
-- ============================================================================
|
||||
-- Subevent definitions -- variant checklist
|
||||
-- ============================================================================
|
||||
|
||||
-- { displayName, subevent, [variantField], [variantValue] }
|
||||
local SUBEVENTS = {
|
||||
-- Damage
|
||||
-- Damage (spell prefix: arg9=amount ... arg15=critical, arg16=glancing, arg17=crushing)
|
||||
{ "SPELL_DAMAGE", "SPELL_DAMAGE" },
|
||||
{ "SPELL_DAMAGE (crit)", "SPELL_DAMAGE", 10, 1 },
|
||||
{ "SPELL_DAMAGE (crit)", "SPELL_DAMAGE", 15, 1 },
|
||||
{ "RANGE_DAMAGE", "RANGE_DAMAGE" },
|
||||
{ "RANGE_DAMAGE (crit)", "RANGE_DAMAGE", 10, 1 },
|
||||
{ "RANGE_DAMAGE (crit)", "RANGE_DAMAGE", 15, 1 },
|
||||
{ "SPELL_PERIODIC_DAMAGE", "SPELL_PERIODIC_DAMAGE" },
|
||||
{ "DAMAGE_SHIELD", "DAMAGE_SHIELD" },
|
||||
{ "DAMAGE_SPLIT", "DAMAGE_SPLIT" },
|
||||
-- Swing damage: arg6=amount ... arg12=critical, arg13=glancing, arg14=crushing
|
||||
{ "SWING_DAMAGE", "SWING_DAMAGE" },
|
||||
{ "SWING_DAMAGE (crit)", "SWING_DAMAGE", 9, 1 },
|
||||
{ "SWING_DAMAGE (glancing)", "SWING_DAMAGE", 10, 1 }, -- flags bit 0
|
||||
{ "SWING_DAMAGE (crushing)", "SWING_DAMAGE", 10, 2 }, -- flags bit 1
|
||||
{ "SWING_DAMAGE (offhand)", "SWING_DAMAGE", 10, 4 }, -- flags bit 2
|
||||
{ "ENVIRONMENTAL (DROWNING)", "ENVIRONMENTAL_DAMAGE", 4, "DROWNING" },
|
||||
{ "ENVIRONMENTAL (FALLING)", "ENVIRONMENTAL_DAMAGE", 4, "FALLING" },
|
||||
{ "ENVIRONMENTAL (LAVA)", "ENVIRONMENTAL_DAMAGE", 4, "LAVA" },
|
||||
{ "ENVIRONMENTAL (SLIME)", "ENVIRONMENTAL_DAMAGE", 4, "SLIME" },
|
||||
{ "ENVIRONMENTAL (FIRE)", "ENVIRONMENTAL_DAMAGE", 4, "FIRE" },
|
||||
{ "ENVIRONMENTAL (EXHAUSTED)", "ENVIRONMENTAL_DAMAGE", 4, "EXHAUSTED" }, -- twow does not implement fatigue damage
|
||||
-- Misses — swing
|
||||
{ "SWING_MISSED (MISS)", "SWING_MISSED", 4, "MISS" },
|
||||
{ "SWING_MISSED (DODGE)", "SWING_MISSED", 4, "DODGE" },
|
||||
{ "SWING_MISSED (PARRY)", "SWING_MISSED", 4, "PARRY" },
|
||||
{ "SWING_MISSED (BLOCK)", "SWING_MISSED", 4, "BLOCK" },
|
||||
{ "SWING_MISSED (EVADE)", "SWING_MISSED", 4, "EVADE" },
|
||||
{ "SWING_MISSED (IMMUNE)", "SWING_MISSED", 4, "IMMUNE" },
|
||||
{ "SWING_MISSED (RESIST)", "SWING_MISSED", 4, "RESIST" },
|
||||
{ "SWING_MISSED (ABSORB)", "SWING_MISSED", 4, "ABSORB" },
|
||||
-- Misses — spell/range
|
||||
{ "SPELL_MISSED (MISS)", "SPELL_MISSED", 6, "MISS" },
|
||||
{ "SPELL_MISSED (DODGE)", "SPELL_MISSED", 6, "DODGE" },
|
||||
{ "SPELL_MISSED (PARRY)", "SPELL_MISSED", 6, "PARRY" },
|
||||
{ "SPELL_MISSED (IMMUNE)", "SPELL_MISSED", 6, "IMMUNE" },
|
||||
{ "SPELL_MISSED (RESIST)", "SPELL_MISSED", 6, "RESIST" },
|
||||
{ "SPELL_MISSED (ABSORB)", "SPELL_MISSED", 6, "ABSORB" },
|
||||
{ "SPELL_MISSED (DEFLECT)", "SPELL_MISSED", 6, "DEFLECT" }, -- dead in vanilla, kept for completeness
|
||||
{ "SPELL_MISSED (REFLECT)", "SPELL_MISSED", 6, "REFLECT" },
|
||||
{ "SWING_DAMAGE (crit)", "SWING_DAMAGE", 12, 1 },
|
||||
{ "SWING_DAMAGE (glancing)", "SWING_DAMAGE", 13, 1 },
|
||||
{ "SWING_DAMAGE (crushing)", "SWING_DAMAGE", 14, 1 },
|
||||
-- Env damage: arg6=envType, arg7=amount ... arg15=crushing
|
||||
{ "ENVIRONMENTAL (DROWNING)", "ENVIRONMENTAL_DAMAGE", 6, "DROWNING" },
|
||||
{ "ENVIRONMENTAL (FALLING)", "ENVIRONMENTAL_DAMAGE", 6, "FALLING" },
|
||||
{ "ENVIRONMENTAL (LAVA)", "ENVIRONMENTAL_DAMAGE", 6, "LAVA" },
|
||||
{ "ENVIRONMENTAL (SLIME)", "ENVIRONMENTAL_DAMAGE", 6, "SLIME" },
|
||||
{ "ENVIRONMENTAL (FIRE)", "ENVIRONMENTAL_DAMAGE", 6, "FIRE" },
|
||||
{ "ENVIRONMENTAL (EXHAUSTED)", "ENVIRONMENTAL_DAMAGE", 6, "EXHAUSTED" }, -- twow: no fatigue
|
||||
-- Misses -- swing: arg6=missType
|
||||
{ "SWING_MISSED (MISS)", "SWING_MISSED", 6, "MISS" },
|
||||
{ "SWING_MISSED (DODGE)", "SWING_MISSED", 6, "DODGE" },
|
||||
{ "SWING_MISSED (PARRY)", "SWING_MISSED", 6, "PARRY" },
|
||||
{ "SWING_MISSED (BLOCK)", "SWING_MISSED", 6, "BLOCK" },
|
||||
{ "SWING_MISSED (EVADE)", "SWING_MISSED", 6, "EVADE" },
|
||||
{ "SWING_MISSED (IMMUNE)", "SWING_MISSED", 6, "IMMUNE" },
|
||||
{ "SWING_MISSED (RESIST)", "SWING_MISSED", 6, "RESIST" },
|
||||
{ "SWING_MISSED (ABSORB)", "SWING_MISSED", 6, "ABSORB" },
|
||||
-- Misses -- spell: arg9=missType (after prefix)
|
||||
{ "SPELL_MISSED (MISS)", "SPELL_MISSED", 9, "MISS" },
|
||||
{ "SPELL_MISSED (DODGE)", "SPELL_MISSED", 9, "DODGE" },
|
||||
{ "SPELL_MISSED (PARRY)", "SPELL_MISSED", 9, "PARRY" },
|
||||
{ "SPELL_MISSED (IMMUNE)", "SPELL_MISSED", 9, "IMMUNE" },
|
||||
{ "SPELL_MISSED (RESIST)", "SPELL_MISSED", 9, "RESIST" },
|
||||
{ "SPELL_MISSED (ABSORB)", "SPELL_MISSED", 9, "ABSORB" },
|
||||
{ "SPELL_MISSED (DEFLECT)", "SPELL_MISSED", 9, "DEFLECT" }, -- dead in vanilla
|
||||
{ "SPELL_MISSED (REFLECT)", "SPELL_MISSED", 9, "REFLECT" },
|
||||
{ "RANGE_MISSED", "RANGE_MISSED" },
|
||||
{ "SPELL_PERIODIC_MISSED", "SPELL_PERIODIC_MISSED" },
|
||||
{ "DAMAGE_SHIELD_MISSED", "DAMAGE_SHIELD_MISSED" },
|
||||
-- Heals
|
||||
-- Heals: arg9=amount, arg10=overheal, arg11=absorbed, arg12=critical
|
||||
{ "SPELL_HEAL", "SPELL_HEAL" },
|
||||
{ "SPELL_HEAL (crit)", "SPELL_HEAL", 8, 1 },
|
||||
{ "SPELL_HEAL (crit)", "SPELL_HEAL", 12, 1 },
|
||||
{ "SPELL_PERIODIC_HEAL", "SPELL_PERIODIC_HEAL" },
|
||||
-- Power
|
||||
{ "SPELL_ENERGIZE (Mana)", "SPELL_ENERGIZE" }, -- amount=0 from hook, can't distinguish
|
||||
-- Power: arg9=amount, arg10=powerType
|
||||
{ "SPELL_ENERGIZE", "SPELL_ENERGIZE" },
|
||||
{ "SPELL_PERIODIC_ENERGIZE", "SPELL_PERIODIC_ENERGIZE" },
|
||||
{ "SPELL_PERIODIC_DRAIN", "SPELL_PERIODIC_DRAIN" },
|
||||
{ "SPELL_PERIODIC_LEECH", "SPELL_PERIODIC_LEECH" },
|
||||
-- Auras
|
||||
{ "SPELL_AURA_APPLIED (BUFF)", "SPELL_AURA_APPLIED", 6, "BUFF" },
|
||||
{ "SPELL_AURA_APPLIED (DEBUFF)", "SPELL_AURA_APPLIED", 6, "DEBUFF" },
|
||||
{ "SPELL_AURA_REMOVED (BUFF)", "SPELL_AURA_REMOVED", 6, "BUFF" },
|
||||
{ "SPELL_AURA_REMOVED (DEBUFF)", "SPELL_AURA_REMOVED", 6, "DEBUFF" },
|
||||
-- Auras: arg9=auraType
|
||||
{ "SPELL_AURA_APPLIED (BUFF)", "SPELL_AURA_APPLIED", 9, "BUFF" },
|
||||
{ "SPELL_AURA_APPLIED (DEBUFF)", "SPELL_AURA_APPLIED", 9, "DEBUFF" },
|
||||
{ "SPELL_AURA_REMOVED (BUFF)", "SPELL_AURA_REMOVED", 9, "BUFF" },
|
||||
{ "SPELL_AURA_REMOVED (DEBUFF)", "SPELL_AURA_REMOVED", 9, "DEBUFF" },
|
||||
{ "SPELL_AURA_APPLIED_DOSE", "SPELL_AURA_APPLIED_DOSE" },
|
||||
{ "SPELL_AURA_REMOVED_DOSE", "SPELL_AURA_REMOVED_DOSE" },
|
||||
{ "SPELL_AURA_REFRESH", "SPELL_AURA_REFRESH" },
|
||||
@@ -124,7 +161,7 @@ local SUBEVENTS = {
|
||||
{ "SPELL_CAST_START", "SPELL_CAST_START" },
|
||||
{ "SPELL_CAST_SUCCESS", "SPELL_CAST_SUCCESS" },
|
||||
{ "SPELL_CAST_FAILED", "SPELL_CAST_FAILED" },
|
||||
-- Misc spell
|
||||
-- Misc
|
||||
{ "SPELL_INTERRUPT", "SPELL_INTERRUPT" },
|
||||
{ "SPELL_DISPEL", "SPELL_DISPEL" },
|
||||
{ "SPELL_DISPEL_FAILED", "SPELL_DISPEL_FAILED" },
|
||||
@@ -140,9 +177,7 @@ local SUBEVENTS = {
|
||||
|
||||
local NUM_SUBEVENTS = table.getn(SUBEVENTS)
|
||||
|
||||
-- Build reverse lookup: for each subevent name, list all indices that match it
|
||||
-- (one subevent may have multiple variant entries)
|
||||
local subeventIndices = {} -- [subeventName] = { idx1, idx2, ... }
|
||||
local subeventIndices = {}
|
||||
for i = 1, NUM_SUBEVENTS do
|
||||
local name = SUBEVENTS[i][2]
|
||||
if not subeventIndices[name] then
|
||||
@@ -151,254 +186,295 @@ for i = 1, NUM_SUBEVENTS do
|
||||
table.insert(subeventIndices[name], i)
|
||||
end
|
||||
|
||||
-- Tracking state
|
||||
local seen = {} -- [index] = true
|
||||
local seenArgs = {} -- [index] = formatted arg string
|
||||
local seen = {}
|
||||
local seenArgs = {}
|
||||
local seenCount = 0
|
||||
|
||||
-- ============================================================================
|
||||
-- Arg formatters per subevent (return a short summary string)
|
||||
-- Arg formatters (summary string for tracker display)
|
||||
-- ============================================================================
|
||||
-- Formatters receive args starting from arg6 (after base: sub, srcGUID, srcName, dstGUID, dstName)
|
||||
|
||||
local function fmtSpellDmg(spellId, amount, school, resisted, absorbed, blocked, critical)
|
||||
-- Spell-prefix damage: spellId, spellName, spellSchool, amount, overkill, school, resisted, blocked, absorbed, critical, glancing, crushing
|
||||
local function fmtSpellDmg(spellId, spellName, spellSchool, amount, overkill, school, resisted, blocked, absorbed, critical, glancing, crushing)
|
||||
local c = (critical == 1) and " CRIT" or ""
|
||||
return format("spell=%s amt=%s %s res=%s abs=%s blk=%s%s",
|
||||
s(spellId), s(amount), schoolName(school or 0), s(resisted), s(absorbed), s(blocked), c)
|
||||
local ok = (overkill and overkill >= 0) and format(" OK=%s", s(overkill)) or ""
|
||||
local g = (glancing == 1) and " GLANC" or ""
|
||||
local cr = (crushing == 1) and " CRUSH" or ""
|
||||
return format("[%s] %s amt=%s %s res=%s blk=%s abs=%s%s%s%s%s",
|
||||
s(spellId), s(spellName), s(amount), schoolName(school or 0), s(resisted), s(blocked), s(absorbed), c, ok, g, cr)
|
||||
end
|
||||
|
||||
local function fmtSwingDmg(amount, school, resisted, absorbed, blocked, critical, flags)
|
||||
-- Swing damage: amount, overkill, school, resisted, blocked, absorbed, critical, glancing, crushing
|
||||
local function fmtSwingDmg(amount, overkill, school, resisted, blocked, absorbed, critical, glancing, crushing)
|
||||
local c = (critical == 1) and " CRIT" or ""
|
||||
local f = ""
|
||||
if flags and flags > 0 then
|
||||
if math.mod(flags, 2) == 1 then f = f .. " GLANC" end
|
||||
if math.mod(math.floor(flags / 2), 2) == 1 then f = f .. " CRUSH" end
|
||||
if math.mod(math.floor(flags / 4), 2) == 1 then f = f .. " OH" end
|
||||
end
|
||||
return format("amt=%s %s res=%s abs=%s blk=%s%s%s",
|
||||
s(amount), schoolName(school or 0), s(resisted), s(absorbed), s(blocked), c, f)
|
||||
local ok = (overkill and overkill >= 0) and format(" OK=%s", s(overkill)) or ""
|
||||
local g = (glancing == 1) and " GLANC" or ""
|
||||
local cr = (crushing == 1) and " CRUSH" or ""
|
||||
return format("amt=%s %s res=%s blk=%s abs=%s%s%s%s%s",
|
||||
s(amount), schoolName(school or 0), s(resisted), s(blocked), s(absorbed), c, ok, g, cr)
|
||||
end
|
||||
|
||||
-- Spell prefix helper
|
||||
local function spellPrefix(spellId, spellName, spellSchool)
|
||||
return format("[%s] %s %s", s(spellId), s(spellName), schoolName(spellSchool or 0))
|
||||
end
|
||||
|
||||
local argFormatters = {}
|
||||
|
||||
-- Pattern A — numeric args
|
||||
argFormatters["SPELL_DAMAGE"] = function(a4,a5,a6,a7,a8,a9,a10) return fmtSpellDmg(a4,a5,a6,a7,a8,a9,a10) end
|
||||
argFormatters["RANGE_DAMAGE"] = function(a4,a5,a6,a7,a8,a9,a10) return fmtSpellDmg(a4,a5,a6,a7,a8,a9,a10) end
|
||||
argFormatters["SPELL_PERIODIC_DAMAGE"] = function(a4,a5,a6,a7,a8,a9,a10) return fmtSpellDmg(a4,a5,a6,a7,a8,a9,a10) end
|
||||
argFormatters["DAMAGE_SHIELD"] = function(a4,a5,a6,a7,a8,a9,a10) return fmtSpellDmg(a4,a5,a6,a7,a8,a9,a10) end
|
||||
argFormatters["SWING_DAMAGE"] = function(a4,a5,a6,a7,a8,a9,a10) return fmtSwingDmg(a4,a5,a6,a7,a8,a9,a10) end
|
||||
argFormatters["SPELL_HEAL"] = function(a4,a5,a6,a7,a8)
|
||||
local c = (a8 == 1) and " CRIT" or ""
|
||||
return format("spell=%s amt=%s oh=%s abs=%s%s", s(a4), s(a5), s(a6), s(a7), c)
|
||||
end
|
||||
argFormatters["SPELL_PERIODIC_HEAL"] = function(a4,a5) return format("spell=%s amt=%s", s(a4), s(a5)) end
|
||||
argFormatters["SPELL_ENERGIZE"] = function(a4,a5,a6)
|
||||
return format("spell=%s amt=%s type=%s", s(a4), s(a5), powerNames[a6] or s(a6))
|
||||
end
|
||||
argFormatters["SPELL_PERIODIC_ENERGIZE"] = function(a4,a5,a6,a7)
|
||||
return format("spell=%s amt=%s over=%s type=%s", s(a4), s(a5), s(a6), powerNames[a7] or s(a7))
|
||||
end
|
||||
argFormatters["SPELL_PERIODIC_LEECH"] = function(a4,a5,a6,a7,a8,a9)
|
||||
return format("spell=%s amt=%s %s gain=%s abs=%s res=%s", s(a4), s(a5), schoolName(a6 or 0), s(a7), s(a8), s(a9))
|
||||
end
|
||||
argFormatters["SPELL_PERIODIC_DRAIN"] = function(a4,a5,a6,a7)
|
||||
return format("spell=%s amt=%s type=%s extra=%s", s(a4), s(a5), powerNames[a6] or s(a6), s(a7))
|
||||
end
|
||||
argFormatters["SPELL_EXTRA_ATTACKS"] = function(a4,a5,a6)
|
||||
return format("spell=%s school=%s amt=%s", s(a4), s(a5), s(a6))
|
||||
-- Spell-prefix damage (args start at a6=spellId)
|
||||
argFormatters["SPELL_DAMAGE"] = function(...) return fmtSpellDmg(arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],arg[7],arg[8],arg[9],arg[10],arg[11],arg[12]) end
|
||||
argFormatters["RANGE_DAMAGE"] = argFormatters["SPELL_DAMAGE"]
|
||||
argFormatters["SPELL_PERIODIC_DAMAGE"] = argFormatters["SPELL_DAMAGE"]
|
||||
argFormatters["DAMAGE_SHIELD"] = argFormatters["SPELL_DAMAGE"]
|
||||
argFormatters["DAMAGE_SPLIT"] = argFormatters["SPELL_DAMAGE"]
|
||||
|
||||
-- Swing damage (no prefix, args start at a6=amount)
|
||||
argFormatters["SWING_DAMAGE"] = function(...) return fmtSwingDmg(arg[1],arg[2],arg[3],arg[4],arg[5],arg[6],arg[7],arg[8],arg[9]) end
|
||||
|
||||
-- Env damage: a6=envType, a7-a15=damage suffix
|
||||
argFormatters["ENVIRONMENTAL_DAMAGE"] = function(...)
|
||||
local ok = (arg[3] and arg[3] >= 0) and format(" OK=%s", s(arg[3])) or ""
|
||||
return format("env=%s amt=%s %s abs=%s%s", s(arg[1]), s(arg[2]), schoolName(arg[4] or 0), s(arg[7]), ok)
|
||||
end
|
||||
|
||||
-- Pattern B — swing missed
|
||||
argFormatters["SWING_MISSED"] = function(a4) return s(a4) end
|
||||
-- Swing missed: a6=missType, a7=amountMissed
|
||||
argFormatters["SWING_MISSED"] = function(...) return format("%s amt=%s", s(arg[1]), s(arg[2])) end
|
||||
|
||||
-- Pattern C — spell + string
|
||||
argFormatters["SPELL_MISSED"] = function(a4,a5,a6) return format("spell=%s %s %s", s(a4), schoolName(a5 or 0), s(a6)) end
|
||||
argFormatters["SPELL_PERIODIC_MISSED"] = function(a4,a5,a6) return format("spell=%s %s %s", s(a4), schoolName(a5 or 0), s(a6)) end
|
||||
argFormatters["DAMAGE_SHIELD_MISSED"] = function(a4,a5,a6) return format("spell=%s %s %s", s(a4), schoolName(a5 or 0), s(a6)) end
|
||||
argFormatters["RANGE_MISSED"] = function(a4,a5,a6) return format("spell=%s %s %s", s(a4), schoolName(a5 or 0), s(a6)) end
|
||||
argFormatters["SPELL_AURA_APPLIED"] = function(a4,a5,a6) return format("spell=%s %s %s", s(a4), schoolName(a5 or 0), s(a6)) end
|
||||
argFormatters["SPELL_AURA_REMOVED"] = function(a4,a5,a6) return format("spell=%s %s %s", s(a4), schoolName(a5 or 0), s(a6)) end
|
||||
argFormatters["SPELL_AURA_REFRESH"] = function(a4,a5,a6) return format("spell=%s %s %s", s(a4), schoolName(a5 or 0), s(a6)) end
|
||||
argFormatters["SPELL_AURA_BROKEN"] = function(a4,a5,a6) return format("spell=%s %s %s", s(a4), schoolName(a5 or 0), s(a6)) end
|
||||
argFormatters["SPELL_CAST_FAILED"] = function(a4,a5,a6) return format("spell=%s %s reason=%s", s(a4), schoolName(a5 or 0), s(a6)) end
|
||||
-- Spell missed: a6-a8=prefix, a9=missType, a10=amountMissed
|
||||
argFormatters["SPELL_MISSED"] = function(...) return format("%s %s amt=%s", spellPrefix(arg[1],arg[2],arg[3]), s(arg[4]), s(arg[5])) end
|
||||
argFormatters["SPELL_PERIODIC_MISSED"] = argFormatters["SPELL_MISSED"]
|
||||
argFormatters["DAMAGE_SHIELD_MISSED"] = argFormatters["SPELL_MISSED"]
|
||||
argFormatters["RANGE_MISSED"] = argFormatters["SPELL_MISSED"]
|
||||
|
||||
-- Pattern D — spell only
|
||||
argFormatters["SPELL_CAST_START"] = function(a4,a5) return format("spell=%s %s", s(a4), schoolName(a5 or 0)) end
|
||||
argFormatters["SPELL_CAST_SUCCESS"] = function(a4,a5) return format("spell=%s %s", s(a4), schoolName(a5 or 0)) end
|
||||
argFormatters["SPELL_SUMMON"] = function(a4,a5) return format("spell=%s %s", s(a4), schoolName(a5 or 0)) end
|
||||
argFormatters["SPELL_RESURRECT"] = function(a4,a5) return format("spell=%s %s", s(a4), schoolName(a5 or 0)) end
|
||||
argFormatters["SPELL_INSTAKILL"] = function(a4,a5) return format("spell=%s %s", s(a4), schoolName(a5 or 0)) end
|
||||
|
||||
-- Pattern E — base (no extra args)
|
||||
argFormatters["UNIT_DIED"] = function() return "" end
|
||||
argFormatters["PARTY_KILL"] = function() return "" end
|
||||
|
||||
-- Pattern F — spell + string + amount
|
||||
argFormatters["SPELL_AURA_APPLIED_DOSE"] = function(a4,a5,a6,a7) return format("spell=%s %s %s x%s", s(a4), schoolName(a5 or 0), s(a6), s(a7)) end
|
||||
argFormatters["SPELL_AURA_REMOVED_DOSE"] = function(a4,a5,a6,a7) return format("spell=%s %s %s x%s", s(a4), schoolName(a5 or 0), s(a6), s(a7)) end
|
||||
|
||||
-- Pattern G — dispel/interrupt
|
||||
argFormatters["SPELL_DISPEL"] = function(a4,a5,a6,a7,a8)
|
||||
return format("spell=%s extra=%s %s %s", s(a4), s(a6), schoolName(a7 or 0), s(a8))
|
||||
-- Spell heal: a6-a8=prefix, a9=amount, a10=overheal, a11=absorbed, a12=critical
|
||||
argFormatters["SPELL_HEAL"] = function(...)
|
||||
local c = (arg[7] == 1) and " CRIT" or ""
|
||||
local oh = (arg[5] and arg[5] > 0) and format(" OH=%s", s(arg[5])) or ""
|
||||
return format("%s amt=%s abs=%s%s%s", spellPrefix(arg[1],arg[2],arg[3]), s(arg[4]), s(arg[6]), c, oh)
|
||||
end
|
||||
argFormatters["SPELL_INTERRUPT"] = function(a4,a5,a6,a7)
|
||||
return format("spell=%s extra=%s %s", s(a4), s(a6), schoolName(a7 or 0))
|
||||
argFormatters["SPELL_PERIODIC_HEAL"] = argFormatters["SPELL_HEAL"]
|
||||
|
||||
-- Spell energize: a6-a8=prefix, a9=amount, a10=powerType
|
||||
argFormatters["SPELL_ENERGIZE"] = function(...)
|
||||
return format("%s amt=%s type=%s", spellPrefix(arg[1],arg[2],arg[3]), s(arg[4]), powerNames[arg[5]] or s(arg[5]))
|
||||
end
|
||||
argFormatters["SPELL_AURA_BROKEN_SPELL"] = function(a4,a5,a6,a7,a8)
|
||||
return format("spell=%s extra=%s %s %s", s(a4), s(a6), schoolName(a7 or 0), s(a8))
|
||||
argFormatters["SPELL_PERIODIC_ENERGIZE"] = argFormatters["SPELL_ENERGIZE"]
|
||||
|
||||
-- Spell leech/drain: a6-a8=prefix, a9=amount, a10=powerType, a11=extraAmount
|
||||
argFormatters["SPELL_PERIODIC_LEECH"] = function(...)
|
||||
return format("%s amt=%s type=%s gain=%s", spellPrefix(arg[1],arg[2],arg[3]), s(arg[4]), s(arg[5]), s(arg[6]))
|
||||
end
|
||||
argFormatters["SPELL_PERIODIC_DRAIN"] = argFormatters["SPELL_PERIODIC_LEECH"]
|
||||
|
||||
-- Extra attacks: a6-a8=prefix, a9=amount
|
||||
argFormatters["SPELL_EXTRA_ATTACKS"] = function(...)
|
||||
return format("%s amt=%s", spellPrefix(arg[1],arg[2],arg[3]), s(arg[4]))
|
||||
end
|
||||
|
||||
-- Pattern H — environmental
|
||||
argFormatters["ENVIRONMENTAL_DAMAGE"] = function(a4,a5,a6,a7)
|
||||
return format("env=%s amt=%s %s abs=%s", s(a4), s(a5), schoolName(a6 or 0), s(a7))
|
||||
end
|
||||
-- Aura applied/removed/refresh/broken: a6-a8=prefix, a9=auraType
|
||||
argFormatters["SPELL_AURA_APPLIED"] = function(...) return format("%s %s", spellPrefix(arg[1],arg[2],arg[3]), s(arg[4])) end
|
||||
argFormatters["SPELL_AURA_REMOVED"] = argFormatters["SPELL_AURA_APPLIED"]
|
||||
argFormatters["SPELL_AURA_REFRESH"] = argFormatters["SPELL_AURA_APPLIED"]
|
||||
argFormatters["SPELL_AURA_BROKEN"] = argFormatters["SPELL_AURA_APPLIED"]
|
||||
|
||||
-- New subevents
|
||||
argFormatters["DAMAGE_SPLIT"] = function(a4,a5,a6,a7,a8,a9,a10) return fmtSpellDmg(a4,a5,a6,a7,a8,a9,a10) end
|
||||
argFormatters["SPELL_DISPEL_FAILED"] = function(a4,a5) return format("spell=%s %s", s(a4), schoolName(a5 or 0)) end
|
||||
-- Aura dose: a6-a8=prefix, a9=auraType, a10=amount
|
||||
argFormatters["SPELL_AURA_APPLIED_DOSE"] = function(...) return format("%s %s x%s", spellPrefix(arg[1],arg[2],arg[3]), s(arg[4]), s(arg[5])) end
|
||||
argFormatters["SPELL_AURA_REMOVED_DOSE"] = argFormatters["SPELL_AURA_APPLIED_DOSE"]
|
||||
|
||||
-- Cast: a6-a8=prefix only
|
||||
argFormatters["SPELL_CAST_START"] = function(...) return spellPrefix(arg[1],arg[2],arg[3]) end
|
||||
argFormatters["SPELL_CAST_SUCCESS"] = argFormatters["SPELL_CAST_START"]
|
||||
argFormatters["SPELL_SUMMON"] = argFormatters["SPELL_CAST_START"]
|
||||
argFormatters["SPELL_RESURRECT"] = argFormatters["SPELL_CAST_START"]
|
||||
argFormatters["SPELL_INSTAKILL"] = argFormatters["SPELL_CAST_START"]
|
||||
|
||||
-- Cast failed: a6-a8=prefix, a9=failedType
|
||||
argFormatters["SPELL_CAST_FAILED"] = function(...) return format("%s reason=%s", spellPrefix(arg[1],arg[2],arg[3]), s(arg[4])) end
|
||||
|
||||
-- Interrupt/dispel_failed: a6-a8=prefix, a9=extraSpellId, a10=extraSpellName, a11=extraSchool
|
||||
argFormatters["SPELL_INTERRUPT"] = function(...)
|
||||
return format("%s -> extra=[%s] %s %s", spellPrefix(arg[1],arg[2],arg[3]), s(arg[4]), s(arg[5]), schoolName(arg[6] or 0))
|
||||
end
|
||||
argFormatters["SPELL_DISPEL_FAILED"] = argFormatters["SPELL_INTERRUPT"]
|
||||
|
||||
-- Dispel/stolen/aura_broken_spell: a6-a8=prefix, a9=extraSpellId, a10=extraSpellName, a11=extraSchool, a12=auraType
|
||||
argFormatters["SPELL_DISPEL"] = function(...)
|
||||
return format("%s -> extra=[%s] %s %s %s", spellPrefix(arg[1],arg[2],arg[3]), s(arg[4]), s(arg[5]), schoolName(arg[6] or 0), s(arg[7]))
|
||||
end
|
||||
argFormatters["SPELL_AURA_BROKEN_SPELL"] = argFormatters["SPELL_DISPEL"]
|
||||
|
||||
-- Base (no args)
|
||||
argFormatters["UNIT_DIED"] = function() return "" end
|
||||
argFormatters["PARTY_KILL"] = function() return "" end
|
||||
argFormatters["UNIT_DESTROYED"] = function() return "" end
|
||||
|
||||
-- ============================================================================
|
||||
-- Chat output handlers (preserved from original)
|
||||
-- Chat output handlers
|
||||
-- ============================================================================
|
||||
-- All chatHandlers receive: (src, srcName, dst, dstName, ...) where ... starts at arg6
|
||||
|
||||
local chatHandlers = {}
|
||||
|
||||
chatHandlers["SPELL_DAMAGE"] = function(src, dst, spellId, amount, school, resisted, absorbed, blocked, critical)
|
||||
-- Spell damage: src, srcName, dst, dstName, spellId, spellName, spellSchool, amount, overkill, school, resisted, blocked, absorbed, critical
|
||||
chatHandlers["SPELL_DAMAGE"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, amount, overkill, school, resisted, blocked, absorbed, critical)
|
||||
local crit = critical == 1 and " (CRIT)" or ""
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff0000SPELL_DAMAGE|r %s -> %s: spell %d for %d %s%s", src, dst, spellId, amount, schoolName(school), crit))
|
||||
local ok = (overkill and overkill >= 0) and format(" (OK %d)", overkill) or ""
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff0000SPELL_DAMAGE|r %s -> %s: %s[%d] for %d %s%s%s", displayName(srcName, src), displayName(dstName, dst), spellName or "", spellId, amount, schoolName(school), crit, ok))
|
||||
end
|
||||
chatHandlers["RANGE_DAMAGE"] = function(src, dst, spellId, amount, school, resisted, absorbed, blocked, critical)
|
||||
local crit = critical == 1 and " (CRIT)" or ""
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff0000RANGE_DAMAGE|r %s -> %s: spell %d for %d %s%s", src, dst, spellId, amount, schoolName(school), crit))
|
||||
chatHandlers["RANGE_DAMAGE"] = chatHandlers["SPELL_DAMAGE"]
|
||||
chatHandlers["DAMAGE_SPLIT"] = chatHandlers["SPELL_DAMAGE"]
|
||||
|
||||
chatHandlers["SPELL_PERIODIC_DAMAGE"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, amount, overkill, school)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffcc0000SPELL_PERIODIC_DAMAGE|r %s -> %s: %s[%d] for %d %s", displayName(srcName, src), displayName(dstName, dst), spellName or "", spellId, amount, schoolName(school)))
|
||||
end
|
||||
chatHandlers["SWING_DAMAGE"] = function(src, dst, amount, school, resisted, absorbed, blocked, critical, flags)
|
||||
|
||||
chatHandlers["DAMAGE_SHIELD"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, amount, overkill, school)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff4400DAMAGE_SHIELD|r %s -> %s: %d %s", displayName(srcName, src), displayName(dstName, dst), amount, schoolName(school)))
|
||||
end
|
||||
|
||||
-- Swing damage: src, srcName, dst, dstName, amount, overkill, school, resisted, blocked, absorbed, critical, glancing, crushing
|
||||
chatHandlers["SWING_DAMAGE"] = function(src, srcName, dst, dstName, amount, overkill, school, resisted, blocked, absorbed, critical, glancing, crushing)
|
||||
local extra = ""
|
||||
if critical == 1 then extra = extra .. " CRIT" end
|
||||
if flags then
|
||||
if math.mod(flags, 2) == 1 then extra = extra .. " GLANCING" end
|
||||
if math.mod(math.floor(flags / 2), 2) == 1 then extra = extra .. " CRUSHING" end
|
||||
if math.mod(math.floor(flags / 4), 2) == 1 then extra = extra .. " (OH)" end
|
||||
end
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff0000SWING_DAMAGE|r %s -> %s: %d%s", src, dst, amount, extra))
|
||||
if overkill and overkill >= 0 then extra = extra .. format(" OK=%d", overkill) end
|
||||
if glancing == 1 then extra = extra .. " GLANCING" end
|
||||
if crushing == 1 then extra = extra .. " CRUSHING" end
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff0000SWING_DAMAGE|r %s -> %s: %d%s", displayName(srcName, src), displayName(dstName, dst), amount, extra))
|
||||
end
|
||||
chatHandlers["SWING_MISSED"] = function(src, dst, missType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffaaaaaaSWING_MISSED|r %s -> %s: %s", src, dst, missType))
|
||||
|
||||
-- Swing missed: src, srcName, dst, dstName, missType, amountMissed
|
||||
chatHandlers["SWING_MISSED"] = function(src, srcName, dst, dstName, missType, amountMissed)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffaaaaaaSWING_MISSED|r %s -> %s: %s", displayName(srcName, src), displayName(dstName, dst), missType))
|
||||
end
|
||||
chatHandlers["SPELL_MISSED"] = function(src, dst, spellId, spellSchool, missType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffaaaaaaSPELL_MISSED|r %s -> %s: spell %d %s", src, dst, spellId, missType))
|
||||
|
||||
-- Spell missed: src, srcName, dst, dstName, spellId, spellName, spellSchool, missType, amountMissed
|
||||
chatHandlers["SPELL_MISSED"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, missType, amountMissed)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffaaaaaaSPELL_MISSED|r %s -> %s: %s[%d] %s", displayName(srcName, src), displayName(dstName, dst), spellName or "", spellId, missType))
|
||||
end
|
||||
chatHandlers["RANGE_MISSED"] = function(src, dst, spellId, spellSchool, missType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cfffaaaaaaRANGE_MISSED|r %s -> %s: spell %d %s", src, dst, spellId, missType))
|
||||
chatHandlers["RANGE_MISSED"] = chatHandlers["SPELL_MISSED"]
|
||||
chatHandlers["SPELL_PERIODIC_MISSED"] = chatHandlers["SPELL_MISSED"]
|
||||
chatHandlers["DAMAGE_SHIELD_MISSED"] = chatHandlers["SPELL_MISSED"]
|
||||
|
||||
-- Env damage: src, srcName, dst, dstName, envType, amount, overkill, school, resisted, blocked, absorbed
|
||||
chatHandlers["ENVIRONMENTAL_DAMAGE"] = function(src, srcName, dst, dstName, envType, amount)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff8800ENVIRONMENTAL_DAMAGE|r %s: %s for %d", displayName(dstName, dst), envType, amount))
|
||||
end
|
||||
chatHandlers["ENVIRONMENTAL_DAMAGE"] = function(src, dst, envType, amount, school, absorbed)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff8800ENVIRONMENTAL_DAMAGE|r %s: %s for %d", dst, envType, amount))
|
||||
end
|
||||
chatHandlers["DAMAGE_SHIELD"] = function(src, dst, spellId, amount, school)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff4400DAMAGE_SHIELD|r %s -> %s: %d %s", src, dst, amount, schoolName(school)))
|
||||
end
|
||||
chatHandlers["SPELL_HEAL"] = function(src, dst, spellId, amount, overheal, absorbed, critical)
|
||||
|
||||
-- Spell heal: src, srcName, dst, dstName, spellId, spellName, spellSchool, amount, overheal, absorbed, critical
|
||||
chatHandlers["SPELL_HEAL"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, amount, overheal, absorbed, critical)
|
||||
local crit = critical == 1 and " (CRIT)" or ""
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff00ff00SPELL_HEAL|r %s -> %s: spell %d for %d%s", src, dst, spellId, amount, crit))
|
||||
local oh = (overheal and overheal > 0) and format(" (OH %d)", overheal) or ""
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff00ff00SPELL_HEAL|r %s -> %s: %s[%d] for %d%s%s", displayName(srcName, src), displayName(dstName, dst), spellName or "", spellId, amount, crit, oh))
|
||||
end
|
||||
chatHandlers["SPELL_PERIODIC_HEAL"] = function(src, dst, spellId, amount)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff00cc00SPELL_PERIODIC_HEAL|r %s -> %s: spell %d for %d", src, dst, spellId, amount))
|
||||
chatHandlers["SPELL_PERIODIC_HEAL"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, amount, overheal)
|
||||
local oh = (overheal and overheal > 0) and format(" (OH %d)", overheal) or ""
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff00cc00SPELL_PERIODIC_HEAL|r %s -> %s: %s[%d] for %d%s", displayName(srcName, src), displayName(dstName, dst), spellName or "", spellId, amount, oh))
|
||||
end
|
||||
chatHandlers["SPELL_PERIODIC_DAMAGE"] = function(src, dst, spellId, amount, school)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffcc0000SPELL_PERIODIC_DAMAGE|r %s -> %s: spell %d for %d %s", src, dst, spellId, amount, schoolName(school)))
|
||||
|
||||
-- Energize: src, srcName, dst, dstName, spellId, spellName, spellSchool, amount, powerType
|
||||
chatHandlers["SPELL_ENERGIZE"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, amount, powerType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff4488ffSPELL_ENERGIZE|r %s -> %s: %s[%d] +%d %s", displayName(srcName, src), displayName(dstName, dst), spellName or "", spellId, amount, powerNames[powerType] or "?"))
|
||||
end
|
||||
chatHandlers["SPELL_ENERGIZE"] = function(src, dst, spellId, amount, powerType)
|
||||
local pname = powerNames[powerType] or "Unknown"
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff4488ffSPELL_ENERGIZE|r %s -> %s: spell %d +%d %s", src, dst, spellId, amount, pname))
|
||||
chatHandlers["SPELL_PERIODIC_ENERGIZE"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, amount, powerType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff4488ffSPELL_PERIODIC_ENERGIZE|r %s -> %s: %s[%d] +%d %s", displayName(srcName, src), displayName(dstName, dst), spellName or "", spellId, amount, powerNames[powerType] or "?"))
|
||||
end
|
||||
chatHandlers["SPELL_PERIODIC_ENERGIZE"] = function(src, dst, spellId, amount, overEnergize, powerType)
|
||||
local pname = powerNames[powerType] or "Unknown"
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff4488ffSPELL_PERIODIC_ENERGIZE|r %s -> %s: spell %d +%d %s", src, dst, spellId, amount, pname))
|
||||
|
||||
-- Leech: src, srcName, dst, dstName, spellId, spellName, spellSchool, amount, powerType, extraAmount
|
||||
chatHandlers["SPELL_PERIODIC_LEECH"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, amount, powerType, extraAmount)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff8844ffSPELL_PERIODIC_LEECH|r %s -> %s: %s[%d] drained %d, gained %d", displayName(srcName, src), displayName(dstName, dst), spellName or "", spellId, amount, extraAmount or 0))
|
||||
end
|
||||
chatHandlers["SPELL_PERIODIC_LEECH"] = function(src, dst, spellId, amount, school, gained)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff8844ffSPELL_PERIODIC_LEECH|r %s -> %s: spell %d drained %d, gained %d", src, dst, spellId, amount, gained or 0))
|
||||
|
||||
-- Drain: src, srcName, dst, dstName, spellId, spellName, spellSchool, amount, powerType, extraAmount
|
||||
chatHandlers["SPELL_PERIODIC_DRAIN"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, amount, powerType, extraAmount)
|
||||
local pname = powerNames[powerType] or "?"
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff8844ffSPELL_PERIODIC_DRAIN|r %s -> %s: %s[%d] drained %d %s", displayName(srcName, src), displayName(dstName, dst), spellName or "", spellId, amount, pname))
|
||||
end
|
||||
chatHandlers["SPELL_PERIODIC_DRAIN"] = function(src, dst, spellId, amount, powerType, extraAmount)
|
||||
local pname = powerNames[powerType] or "Unknown"
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff8844ffSPELL_PERIODIC_DRAIN|r %s -> %s: spell %d drained %d %s, gained %d", src, dst, spellId, amount, pname, extraAmount or 0))
|
||||
|
||||
-- Aura: src, srcName, dst, dstName, spellId, spellName, spellSchool, auraType
|
||||
chatHandlers["SPELL_AURA_APPLIED"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, auraType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff8888ffSPELL_AURA_APPLIED|r %s: %s[%d] (%s)", displayName(dstName, dst), spellName or "", spellId, auraType))
|
||||
end
|
||||
chatHandlers["SPELL_AURA_APPLIED"] = function(src, dst, spellId, spellSchool, auraType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff8888ffSPELL_AURA_APPLIED|r %s: spell %d (%s, %s)", dst, spellId, auraType, schoolName(spellSchool)))
|
||||
chatHandlers["SPELL_AURA_REMOVED"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, auraType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff6666ccSPELL_AURA_REMOVED|r %s: %s[%d] (%s)", displayName(dstName, dst), spellName or "", spellId, auraType))
|
||||
end
|
||||
chatHandlers["SPELL_AURA_REMOVED"] = function(src, dst, spellId, spellSchool, auraType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff6666ccSPELL_AURA_REMOVED|r %s: spell %d (%s)", dst, spellId, auraType))
|
||||
chatHandlers["SPELL_AURA_REFRESH"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, auraType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff7777ddSPELL_AURA_REFRESH|r %s: %s[%d] (%s)", displayName(dstName, dst), spellName or "", spellId, auraType))
|
||||
end
|
||||
chatHandlers["SPELL_AURA_APPLIED_DOSE"] = function(src, dst, spellId, spellSchool, auraType, stacks)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff8888ffSPELL_AURA_APPLIED_DOSE|r %s: spell %d x%d (%s)", dst, spellId, stacks, auraType))
|
||||
chatHandlers["SPELL_AURA_BROKEN"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, auraType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff8800SPELL_AURA_BROKEN|r %s -> %s: %s[%d] broken (%s)", displayName(srcName, src), displayName(dstName, dst), spellName or "", spellId, auraType))
|
||||
end
|
||||
chatHandlers["SPELL_AURA_REMOVED_DOSE"] = function(src, dst, spellId, spellSchool, auraType, stacks)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff6666ccSPELL_AURA_REMOVED_DOSE|r %s: spell %d x%d (%s)", dst, spellId, stacks, auraType))
|
||||
chatHandlers["SPELL_AURA_APPLIED_DOSE"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, auraType, stacks)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff8888ffSPELL_AURA_APPLIED_DOSE|r %s: %s[%d] x%d (%s)", displayName(dstName, dst), spellName or "", spellId, stacks, auraType))
|
||||
end
|
||||
chatHandlers["SPELL_AURA_REFRESH"] = function(src, dst, spellId, spellSchool, auraType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff7777ddSPELL_AURA_REFRESH|r %s: spell %d (%s)", dst, spellId, auraType))
|
||||
chatHandlers["SPELL_AURA_REMOVED_DOSE"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, auraType, stacks)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff6666ccSPELL_AURA_REMOVED_DOSE|r %s: %s[%d] x%d (%s)", displayName(dstName, dst), spellName or "", spellId, stacks, auraType))
|
||||
end
|
||||
chatHandlers["SPELL_AURA_BROKEN"] = function(src, dst, spellId, spellSchool, auraType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff8800SPELL_AURA_BROKEN|r %s -> %s: spell %d broken by melee (%s)", src, dst, spellId, auraType))
|
||||
chatHandlers["SPELL_AURA_BROKEN_SPELL"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, extraSpellId, extraSpellName, extraSchool, auraType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff8800SPELL_AURA_BROKEN_SPELL|r %s -> %s: %s[%d] broken by %s[%d]", displayName(srcName, src), displayName(dstName, dst), spellName or "", spellId, extraSpellName or "", extraSpellId))
|
||||
end
|
||||
chatHandlers["SPELL_AURA_BROKEN_SPELL"] = function(src, dst, spellId, spellSchool, extraSpellId, extraSchool, auraType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff8800SPELL_AURA_BROKEN_SPELL|r %s -> %s: spell %d broken by spell %d (%s)", src, dst, spellId, extraSpellId, auraType))
|
||||
|
||||
-- Cast
|
||||
chatHandlers["SPELL_CAST_START"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffffff00SPELL_CAST_START|r %s: %s[%d]", displayName(srcName, src), spellName or "", spellId))
|
||||
end
|
||||
chatHandlers["SPELL_CAST_START"] = function(src, dst, spellId, spellSchool)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffffff00SPELL_CAST_START|r %s: spell %d (%s)", src, spellId, schoolName(spellSchool)))
|
||||
chatHandlers["SPELL_CAST_SUCCESS"] = function(src, srcName, dst, dstName, spellId, spellName)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffffff00SPELL_CAST_SUCCESS|r %s: %s[%d]", displayName(srcName, src), spellName or "", spellId))
|
||||
end
|
||||
chatHandlers["SPELL_CAST_SUCCESS"] = function(src, dst, spellId)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffffff00SPELL_CAST_SUCCESS|r %s: spell %d", src, spellId))
|
||||
chatHandlers["SPELL_CAST_FAILED"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, failedType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff4444SPELL_CAST_FAILED|r %s: %s[%d] (%s)", displayName(srcName, src), spellName or "", spellId, failedType))
|
||||
end
|
||||
chatHandlers["SPELL_CAST_FAILED"] = function(src, dst, spellId, spellSchool, failedType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff4444SPELL_CAST_FAILED|r %s: spell %d (%s)", src, spellId, failedType))
|
||||
|
||||
-- Interrupt: src, srcName, dst, dstName, spellId, spellName, spellSchool, extraSpellId, extraSpellName, extraSchool
|
||||
chatHandlers["SPELL_INTERRUPT"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, extraSpellId, extraSpellName, extraSchool)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff8800SPELL_INTERRUPT|r %s interrupted %s: %s[%d]", displayName(srcName, src), displayName(dstName, dst), extraSpellName or "", extraSpellId))
|
||||
end
|
||||
chatHandlers["SPELL_INTERRUPT"] = function(src, dst, spellId, spellSchool, extraSpellId, extraSchool)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff8800SPELL_INTERRUPT|r %s interrupted %s: spell %d (%s)", src, dst, extraSpellId, schoolName(extraSchool)))
|
||||
|
||||
-- Dispel: src, srcName, dst, dstName, spellId, spellName, spellSchool, extraSpellId, extraSpellName, extraSchool, auraType
|
||||
chatHandlers["SPELL_DISPEL"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, extraSpellId, extraSpellName, extraSchool, auraType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff88ffffSPELL_DISPEL|r %s dispelled %s: %s[%d] (%s)", displayName(srcName, src), displayName(dstName, dst), extraSpellName or "", extraSpellId, auraType))
|
||||
end
|
||||
chatHandlers["SPELL_DISPEL"] = function(src, dst, spellId, spellSchool, extraSpellId, extraSchool, auraType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff88ffffSPELL_DISPEL|r %s dispelled %s: spell %d (%s, %s)", src, dst, extraSpellId, auraType, schoolName(extraSchool)))
|
||||
|
||||
-- Dispel failed: src, srcName, dst, dstName, spellId, spellName, spellSchool, extraSpellId, extraSpellName, extraSchool
|
||||
chatHandlers["SPELL_DISPEL_FAILED"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, extraSpellId, extraSpellName, extraSchool)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff888888SPELL_DISPEL_FAILED|r %s -> %s: %s[%d] resisted", displayName(srcName, src), displayName(dstName, dst), extraSpellName or "", extraSpellId))
|
||||
end
|
||||
chatHandlers["SPELL_EXTRA_ATTACKS"] = function(src, dst, spellId)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff8800SPELL_EXTRA_ATTACKS|r %s: spell %d", src, spellId))
|
||||
|
||||
-- Extra attacks: src, srcName, dst, dstName, spellId, spellName, spellSchool, amount
|
||||
chatHandlers["SPELL_EXTRA_ATTACKS"] = function(src, srcName, dst, dstName, spellId, spellName, spellSchool, amount)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff8800SPELL_EXTRA_ATTACKS|r %s: %s[%d] x%d", displayName(srcName, src), spellName or "", spellId, amount))
|
||||
end
|
||||
chatHandlers["SPELL_SUMMON"] = function(src, dst, spellId)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff88ff88SPELL_SUMMON|r %s summoned %s: spell %d", src, dst, spellId))
|
||||
|
||||
-- Summon/resurrect/instakill
|
||||
chatHandlers["SPELL_SUMMON"] = function(src, srcName, dst, dstName, spellId, spellName)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff88ff88SPELL_SUMMON|r %s summoned %s: %s[%d]", displayName(srcName, src), displayName(dstName, dst), spellName or "", spellId))
|
||||
end
|
||||
chatHandlers["SPELL_RESURRECT"] = function(src, dst, spellId)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff88ff88SPELL_RESURRECT|r %s resurrected %s: spell %d", src, dst, spellId))
|
||||
chatHandlers["SPELL_RESURRECT"] = function(src, srcName, dst, dstName, spellId, spellName)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff88ff88SPELL_RESURRECT|r %s resurrected %s: %s[%d]", displayName(srcName, src), displayName(dstName, dst), spellName or "", spellId))
|
||||
end
|
||||
chatHandlers["SPELL_INSTAKILL"] = function(src, dst, spellId)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff0000SPELL_INSTAKILL|r %s killed %s: spell %d", src, dst, spellId))
|
||||
chatHandlers["SPELL_INSTAKILL"] = function(src, srcName, dst, dstName, spellId, spellName)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff0000SPELL_INSTAKILL|r %s killed %s: %s[%d]", displayName(srcName, src), displayName(dstName, dst), spellName or "", spellId))
|
||||
end
|
||||
chatHandlers["UNIT_DIED"] = function(src, dst)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff888888UNIT_DIED|r %s", dst))
|
||||
|
||||
-- Deaths
|
||||
chatHandlers["UNIT_DIED"] = function(src, srcName, dst, dstName)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff888888UNIT_DIED|r %s", displayName(dstName, dst)))
|
||||
end
|
||||
chatHandlers["PARTY_KILL"] = function(src, dst)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff8800PARTY_KILL|r %s killed %s", src, dst))
|
||||
chatHandlers["PARTY_KILL"] = function(src, srcName, dst, dstName)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff8800PARTY_KILL|r %s killed %s", displayName(srcName, src), displayName(dstName, dst)))
|
||||
end
|
||||
chatHandlers["SPELL_PERIODIC_MISSED"] = function(src, dst, spellId, spellSchool, missType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffaaaaaaSPELL_PERIODIC_MISSED|r %s -> %s: spell %d %s", src, dst, spellId, missType))
|
||||
end
|
||||
chatHandlers["DAMAGE_SHIELD_MISSED"] = function(src, dst, spellId, spellSchool, missType)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffaaaaaaDAMAGE_SHIELD_MISSED|r %s -> %s: spell %d %s", src, dst, spellId, missType))
|
||||
end
|
||||
chatHandlers["DAMAGE_SPLIT"] = function(src, dst, spellId, amount, school)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cffff6600DAMAGE_SPLIT|r %s -> %s: spell %d for %d %s", src, dst, spellId, amount, schoolName(school)))
|
||||
end
|
||||
chatHandlers["SPELL_DISPEL_FAILED"] = function(src, dst, spellId, spellSchool)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff888888SPELL_DISPEL_FAILED|r %s -> %s: spell %d resisted", src, dst, spellId))
|
||||
end
|
||||
chatHandlers["UNIT_DESTROYED"] = function(src, dst)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff666666UNIT_DESTROYED|r %s", dst))
|
||||
chatHandlers["UNIT_DESTROYED"] = function(src, srcName, dst, dstName)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(format("|cff666666UNIT_DESTROYED|r %s", displayName(dstName, dst)))
|
||||
end
|
||||
|
||||
-- ============================================================================
|
||||
-- UI — Draggable popup tracker
|
||||
-- UI -- Draggable popup tracker
|
||||
-- ============================================================================
|
||||
|
||||
local ROW_HEIGHT = 14
|
||||
local FRAME_WIDTH = 420
|
||||
local VISIBLE_ROWS = 25
|
||||
local FRAME_HEIGHT = ROW_HEIGHT * VISIBLE_ROWS + 52 -- title + counter + padding
|
||||
local FRAME_HEIGHT = ROW_HEIGHT * VISIBLE_ROWS + 52
|
||||
|
||||
local main = CreateFrame("Frame", "DPSLogTracker", UIParent)
|
||||
main:SetWidth(FRAME_WIDTH)
|
||||
@@ -419,18 +495,15 @@ main:SetScript("OnDragStop", function() main:StopMovingOrSizing() end)
|
||||
main:SetFrameStrata("DIALOG")
|
||||
main:SetClampedToScreen(true)
|
||||
|
||||
-- Title
|
||||
local title = main:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
|
||||
title:SetPoint("TOPLEFT", main, "TOPLEFT", 12, -10)
|
||||
title:SetText("DPS Log Tracker")
|
||||
title:SetTextColor(1, 0.82, 0)
|
||||
|
||||
-- Close button
|
||||
local closeBtn = CreateFrame("Button", nil, main, "UIPanelCloseButton")
|
||||
closeBtn:SetPoint("TOPRIGHT", main, "TOPRIGHT", -2, -2)
|
||||
closeBtn:SetScript("OnClick", function() main:Hide() end)
|
||||
|
||||
-- Counter
|
||||
local counter = main:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
counter:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -2)
|
||||
counter:SetTextColor(0.7, 0.7, 0.7)
|
||||
@@ -440,29 +513,24 @@ local function updateCounter()
|
||||
end
|
||||
updateCounter()
|
||||
|
||||
-- Scroll frame
|
||||
local scrollFrame = CreateFrame("ScrollFrame", "DPSLogTrackerScroll", main, "FauxScrollFrameTemplate")
|
||||
scrollFrame:SetPoint("TOPLEFT", main, "TOPLEFT", 8, -48)
|
||||
scrollFrame:SetPoint("BOTTOMRIGHT", main, "BOTTOMRIGHT", -28, 8)
|
||||
|
||||
-- Row frames
|
||||
local rows = {}
|
||||
for i = 1, VISIBLE_ROWS do
|
||||
local row = CreateFrame("Frame", nil, main)
|
||||
row:SetWidth(FRAME_WIDTH - 40)
|
||||
row:SetHeight(ROW_HEIGHT)
|
||||
row:SetPoint("TOPLEFT", scrollFrame, "TOPLEFT", 2, -((i - 1) * ROW_HEIGHT))
|
||||
|
||||
local check = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
|
||||
check:SetPoint("LEFT", row, "LEFT", 0, 0)
|
||||
check:SetWidth(20)
|
||||
check:SetJustifyH("LEFT")
|
||||
|
||||
local nameStr = row:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
|
||||
nameStr:SetPoint("LEFT", check, "RIGHT", 0, 0)
|
||||
nameStr:SetWidth(FRAME_WIDTH - 60)
|
||||
nameStr:SetJustifyH("LEFT")
|
||||
|
||||
row.check = check
|
||||
row.nameStr = nameStr
|
||||
rows[i] = row
|
||||
@@ -475,18 +543,18 @@ local function updateRows()
|
||||
local row = rows[i]
|
||||
if idx <= NUM_SUBEVENTS then
|
||||
row:Show()
|
||||
local displayName = SUBEVENTS[idx][1]
|
||||
local displayN = SUBEVENTS[idx][1]
|
||||
if seen[idx] then
|
||||
row.check:SetText("|cff00ff00x|r")
|
||||
local argStr = seenArgs[idx]
|
||||
if argStr and argStr ~= "" then
|
||||
row.nameStr:SetText("|cffffffff" .. displayName .. "|r |cffaaaaaa" .. argStr .. "|r")
|
||||
row.nameStr:SetText("|cffffffff" .. displayN .. "|r |cffaaaaaa" .. argStr .. "|r")
|
||||
else
|
||||
row.nameStr:SetText("|cffffffff" .. displayName .. "|r")
|
||||
row.nameStr:SetText("|cffffffff" .. displayN .. "|r")
|
||||
end
|
||||
else
|
||||
row.check:SetText("|cff666666-|r")
|
||||
row.nameStr:SetText("|cff666666" .. displayName .. "|r")
|
||||
row.nameStr:SetText("|cff666666" .. displayN .. "|r")
|
||||
end
|
||||
else
|
||||
row:Hide()
|
||||
@@ -501,7 +569,6 @@ end)
|
||||
|
||||
updateRows()
|
||||
|
||||
-- Reset button
|
||||
local resetBtn = CreateFrame("Button", nil, main, "UIPanelButtonTemplate")
|
||||
resetBtn:SetWidth(60)
|
||||
resetBtn:SetHeight(18)
|
||||
@@ -522,27 +589,17 @@ end)
|
||||
-- Event dispatcher
|
||||
-- ============================================================================
|
||||
|
||||
local function print(msg)
|
||||
DEFAULT_CHAT_FRAME:AddMessage(msg)
|
||||
end
|
||||
|
||||
-- Expand args table to handle up to 17 args (spell damage has the most: 5 base + 3 prefix + 9 suffix)
|
||||
local eventFrame = CreateFrame("Frame")
|
||||
eventFrame:RegisterEvent("COMBAT_LOG_EVENT")
|
||||
|
||||
eventFrame:SetScript("OnEvent", function()
|
||||
-- if event == "PLAYER_LOGIN" then
|
||||
-- eventFrame:RegisterEvent("COMBAT_LOG_EVENT")
|
||||
-- DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00DPSLog|r registered COMBAT_LOG_EVENT")
|
||||
-- return
|
||||
-- end
|
||||
|
||||
local subevent = arg1
|
||||
if not subevent then return end
|
||||
|
||||
-- Collect args into a table for variant field lookup
|
||||
local args = { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10 }
|
||||
local args = { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
|
||||
arg11, arg12, arg13, arg14, arg15, arg16, arg17 }
|
||||
|
||||
-- Check all variant entries for this subevent
|
||||
local indices = subeventIndices[subevent]
|
||||
if not indices then return end
|
||||
|
||||
@@ -555,18 +612,7 @@ eventFrame:SetScript("OnEvent", function()
|
||||
|
||||
local matches = true
|
||||
if varField then
|
||||
-- Check specific arg matches variant value
|
||||
-- For swing flags (bitfield), check if the bit is set
|
||||
if subevent == "SWING_DAMAGE" and varField == 10 then
|
||||
-- flags is arg10, check bitfield
|
||||
local flags = args[varField] or 0
|
||||
if varValue == 1 then matches = (math.mod(flags, 2) == 1) -- glancing
|
||||
elseif varValue == 2 then matches = (math.mod(math.floor(flags / 2), 2) == 1) -- crushing
|
||||
elseif varValue == 4 then matches = (math.mod(math.floor(flags / 4), 2) == 1) -- offhand
|
||||
else matches = false end
|
||||
else
|
||||
matches = (args[varField] == varValue)
|
||||
end
|
||||
matches = (args[varField] == varValue)
|
||||
end
|
||||
|
||||
if matches then
|
||||
@@ -574,16 +620,18 @@ eventFrame:SetScript("OnEvent", function()
|
||||
seenCount = seenCount + 1
|
||||
local formatter = argFormatters[subevent]
|
||||
if formatter then
|
||||
seenArgs[idx] = formatter(arg4, arg5, arg6, arg7, arg8, arg9, arg10)
|
||||
-- Pass args starting from arg6 (after base: sub, srcGUID, srcName, dstGUID, dstName)
|
||||
seenArgs[idx] = formatter(arg6, arg7, arg8, arg9, arg10, arg11, arg12,
|
||||
arg13, arg14, arg15, arg16, arg17)
|
||||
else
|
||||
seenArgs[idx] = ""
|
||||
end
|
||||
any_new = true
|
||||
|
||||
-- Chat output (first occurrence of this variant)
|
||||
local chatHandler = chatHandlers[subevent]
|
||||
if chatHandler then
|
||||
chatHandler(arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
|
||||
chatHandler(arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10,
|
||||
arg11, arg12, arg13, arg14, arg15, arg16, arg17)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -608,4 +656,4 @@ SlashCmdList["DPSLOG"] = function(msg)
|
||||
end
|
||||
end
|
||||
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00DPSLog Tracker|r loaded — /dpslog to toggle window")
|
||||
DEFAULT_CHAT_FRAME:AddMessage("|cff00ff00DPSLog Tracker|r loaded -- /dpslog to toggle window")
|
||||
|
||||
+504
-157
File diff suppressed because it is too large
Load Diff
@@ -109,6 +109,9 @@ fn registerLuaFunctions() void {
|
||||
if (build_opts.transform44) {
|
||||
registerFunction("SetWeatherOverride", @intFromPtr(&transform44.luaSetWeatherOverride));
|
||||
}
|
||||
if (build_opts.dpslog) {
|
||||
registerFunction("GetSpellInfo", @intFromPtr(&dpslog.luaGetSpellInfo));
|
||||
}
|
||||
if (build_opts.addonperf) {
|
||||
registerFunction("GetAddOnMemoryUsage", @intFromPtr(&addonperf.luaGetAddOnMemoryUsage));
|
||||
registerFunction("UpdateAddOnMemoryUsage", @intFromPtr(&addonperf.luaUpdateAddOnMemoryUsage));
|
||||
|
||||
@@ -162,6 +162,12 @@ pub const FN_CVAR_LOOKUP: usize = 0x0063DEC0;
|
||||
/// SceneEnd: __thiscall(device). Per-frame hook point.
|
||||
pub const FN_SCENE_END: usize = 0x5A17A0;
|
||||
|
||||
/// RetrieveNPCDataFromCache — __thiscall(ECX=cache_obj), 6 stack params, RET 0x18.
|
||||
/// Returns char* name or 0 on miss.
|
||||
pub const FN_NAME_CACHE_LOOKUP: usize = 0x0055F080;
|
||||
/// Name cache object pointer (global at 0xC0E228).
|
||||
pub const NAME_CACHE_OBJ: usize = 0x00C0E228;
|
||||
|
||||
// =============================================================================
|
||||
// D3D9 / GxDevice
|
||||
// =============================================================================
|
||||
|
||||
+16
@@ -224,6 +224,22 @@ pub fn getLocalPlayer() u32 {
|
||||
return getObjectByGUID(guid);
|
||||
}
|
||||
|
||||
/// Resolve a GUID to a unit/NPC name via the name cache.
|
||||
/// Returns the name string or "" if not cached.
|
||||
pub fn getNameByGUID(guid: u64) [*:0]const u8 {
|
||||
if (guid == 0) return "";
|
||||
const lo: u32 = @truncate(guid);
|
||||
const hi: u32 = @truncate(guid >> 32);
|
||||
var name_buf: [2]u32 = .{ 0, 0 };
|
||||
const result = hook.call(fn (u32, u32, u32, u32, u32, u32, u32) callconv(hook.cc.thiscall) u32, o.FN_NAME_CACHE_LOOKUP, .{
|
||||
o.NAME_CACHE_OBJ, lo, hi, @intFromPtr(&name_buf), 0, 0, 0,
|
||||
});
|
||||
if (result != 0) {
|
||||
return @ptrFromInt(result);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/// Get the current target's GUID.
|
||||
pub fn getTargetGUID() u64 {
|
||||
return unitGUID("target");
|
||||
|
||||
Reference in New Issue
Block a user