2 Commits
Author SHA1 Message Date
Brues 496979f486 Add BigWigs packager config to bundle both addons in one zip
Add .pkgmeta (package-as MikScrollingBattleText, move-folders pulling both addon folders to top-level siblings) and a tag-triggered GitHub Actions workflow using BigWigsMods/packager that attaches the single zip to an auto-created GitHub release. Switch both TOCs' Version to the @project-version@ substitution token so the packager stamps the git tag version.
2026-06-23 13:26:13 -05:00
Brues a5e46a62b8 Change nampower school table to sequential index keys
nampowerSchoolToType now uses sequential keys (0=Physical, then implicit 1-6 for Holy/Fire/Nature/Frost/Shadow/Arcane) instead of the previous power-of-two bitmask keys (0,1,2,4,8,16,32).
2026-06-23 13:25:13 -05:00
8 changed files with 11263 additions and 543 deletions
Binary file not shown.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+170 -70
View File
@@ -1,4 +1,4 @@
-------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------
-- Title: Mik's Scrolling Battle Text
-- Author: Mik
-- Maintainer: Athene
@@ -133,22 +133,36 @@ NO_ICON_CACHE = {}
-- Cache for spellId-based icon lookups (bypasses Babble-Spell and tooltip scanning).
local spellIdIconCache = {}
-- Babble-Spell pour les icones des spells de classe
local BS = AceLibrary("Babble-Spell-2.2")
-- Damage type → color prefix lookup (lazily initialized after globals are available).
local damageTypeColors
local function GetDamageTypeColor(dt)
if not damageTypeColors then
damageTypeColors = {
[SPELL_SCHOOL1_CAP] = "\124cffF6F99E\124h",
[SPELL_SCHOOL2_CAP] = "\124cffFF4444\124h",
[SPELL_SCHOOL3_CAP] = "\124cff44FF44\124h",
[SPELL_SCHOOL4_CAP] = "\124cff4488FF\124h",
[SPELL_SCHOOL5_CAP] = "\124cff6600CC\124h",
[SPELL_SCHOOL6_CAP] = "\124cffCC66FF\124h",
["Inconnu"] = "\124cffCC66FF\124h",
}
end
return damageTypeColors[dt]
end
-- Damage type → {r, g, b} float lookup for SetTextColor (lazily initialized).
local damageTypeFontColors
local function GetDamageTypeFontColor(dt)
if not damageTypeFontColors then
local function CreateSpellColor(r, g, b)
local color = CreateColor(r, g, b)
color.colorStr = color:GenerateHexColorMarkup() .. "\124h"
return color
end
damageTypeFontColors = {
[SPELL_SCHOOL1_CAP] = CreateSpellColor(0.965, 0.976, 0.620), -- Holy
[SPELL_SCHOOL2_CAP] = CreateSpellColor(0.933, 0.490, 0.501), -- Fire
[SPELL_SCHOOL3_CAP] = CreateSpellColor(0.631, 0.858, 0.396), -- Nature
[SPELL_SCHOOL4_CAP] = CreateSpellColor(0.462, 0.792, 0.929), -- Frost
[SPELL_SCHOOL5_CAP] = CreateSpellColor(0.356, 0.290, 0.596), -- Shadow
[SPELL_SCHOOL6_CAP] = CreateSpellColor(0.956, 0.658, 0.894), -- Arcane
[SPELL_SCHOOL2_CAP] = { r=1.0, g=0.27, b=0.27 }, -- Fire (red)
[SPELL_SCHOOL3_CAP] = { r=0.27, g=1.0, b=0.27 }, -- Nature (green)
[SPELL_SCHOOL4_CAP] = { r=0.27, g=0.53, b=1.0 }, -- Frost (blue)
[SPELL_SCHOOL5_CAP] = { r=0.4, g=0.0, b=0.8 }, -- Shadow (deep purple)
[SPELL_SCHOOL6_CAP] = { r=0.8, g=0.4, b=1.0 }, -- Arcane (purple/pink)
}
end
return damageTypeFontColors[dt]
@@ -524,6 +538,13 @@ function MikSBT.Init()
-- Register the triggers with the combat event helper.
MikSBT.RegisterTriggers();
-- Enable Nampower CVars for enhanced combat events when available.
if MikCEH.hasNampower then
pcall(SetCVar, "NP_EnableAutoAttackEvents", "1")
pcall(SetCVar, "NP_EnableSpellHealEvents", "1")
pcall(SetCVar, "NP_EnableSpellEnergizeEvents", "1")
end
-- Disable or enable the mod depending on the saved setting.
MikSBT.SetOptionUserDisabled(MikSBT_Save.UserDisabled)
end
@@ -1126,9 +1147,9 @@ function MikSBT.FormatEventText(animationEvent)
outputString = string_gsub(outputString, "-%%a", "\124cffff0000\124h-\124h\124r%%a");
if animationEvent.DamageType then
local color = GetDamageTypeFontColor(animationEvent.DamageType)
local color = GetDamageTypeColor(animationEvent.DamageType)
if color then
outputString = string_gsub(outputString, "%%a", color.colorStr .. "%%a\124h\124r")
outputString = string_gsub(outputString, "%%a", color .. "%%a\124h\124r")
end
end
@@ -1322,7 +1343,7 @@ function MikSBT.UpdateProfiles()
if (profile.CreationVersion < 4.1) then
if profile.Triggers.MSBT_TRIGGER_WINDFURY then -- Fix Windfury Trigger
profile.Triggers.MSBT_TRIGGER_WINDFURY.TriggerSettings.SearchPatterns[1] = string.format(AURAADDEDSELFHELPFUL, string_gsub(C_Spell.GetSpellName(8512) or "", "-", "%%-")) -- Windfury Totem (ID verified vs Spell.dbc)
profile.Triggers.MSBT_TRIGGER_WINDFURY.TriggerSettings.SearchPatterns[1] = string.format(AURAADDEDSELFHELPFUL, string_gsub(BS["Windfury Totem"], "-", "%%-"))
end
if profile.Triggers.MSBT_TRIGGER_HAND_OF_JUSTICE then -- Delete Hand of Justice Trigger
@@ -1396,7 +1417,7 @@ function MikSBT.UpdateProfiles()
end
if (profile.CreationVersion < 4.6) then
-- Add missing incoming evade/reflect event settings (needed for combat log miss events).
-- Add missing incoming evade/reflect event settings (needed for Nampower events).
local incomingMissEvents = {
"MSBT_EVENTTYPE_INCOMING_EVADE",
"MSBT_EVENTTYPE_INCOMING_SPELL_EVADE",
@@ -1765,7 +1786,9 @@ function MikSBT.AddAnimation(animationEvent)
if animationEvent.DamageType and animationEvent.EventType then
local dtColor = GetDamageTypeFontColor(animationEvent.DamageType)
if dtColor then
animDisplayInfo.ColorR, animDisplayInfo.ColorG, animDisplayInfo.ColorB = dtColor:GetRGB()
animDisplayInfo.ColorR = dtColor.r
animDisplayInfo.ColorG = dtColor.g
animDisplayInfo.ColorB = dtColor.b
end
end
@@ -1831,36 +1854,57 @@ function MikSBT.AddAnimation(animationEvent)
local sid = animationEvent.SpellId
if spellIdIconCache[sid] then
texture = spellIdIconCache[sid]
elseif GetSpellInfo then
local ok, _, _, tex = pcall(GetSpellInfo, sid)
elseif MikCEH.hasSuperWoW and SpellInfo then
local ok, _, _, tex = pcall(SpellInfo, sid)
if ok and tex then texture = tex; spellIdIconCache[sid] = tex end
elseif MikCEH.hasNampower and GetSpellRecField and GetSpellIconTexture then
local ok, iconId = pcall(GetSpellRecField, sid, "spellIconID")
if ok and iconId then
local ok2, tex = pcall(GetSpellIconTexture, iconId)
if ok2 and tex then texture = tex; spellIdIconCache[sid] = tex end
end
end
end
if animationEvent.EffectName then
local name = string_gsub(animationEvent.EffectName, " %(%d+%)", "")
local lowerName = strlower(name)
-- DEFAULT_CHAT_FRAME:AddMessage(animationEvent.EffectName)
-- DEFAULT_CHAT_FRAME:AddMessage(name)
if ICON_CACHE[lowerName] then
texture = ICON_CACHE[lowerName]
elseif not texture and MikSBT.GetAuraIcon(name) then
texture = MikSBT.GetAuraIcon(name)
ICON_CACHE[lowerName] = texture
elseif not texture and GetSpellInfo then
local ok, sid = pcall(function(n) return select(10, GetSpellInfo(n)) end, name)
if ok and sid and sid ~= 0 then
local ok2, _, _, tex = pcall(GetSpellInfo, sid)
if ok2 and tex then texture = tex; ICON_CACHE[lowerName] = tex end
end
elseif not texture then
local itemIcon = MikSBT.FindItemIcon(lowerName)
if itemIcon then
texture = itemIcon
ICON_CACHE[lowerName] = itemIcon
-- texture = BS:GetSpellIcon(name)
if ICON_CACHE[strlower(name)] then
texture = ICON_CACHE[strlower(name)]
elseif not texture and MikSBT.FindBuff(name) then
local bufftype,index,_ = MikSBT.FindBuff(name)
local icon
if bufftype == "buff" then
icon = UnitBuff("player", index)
ICON_CACHE[strlower(name)] = icon
texture = icon
elseif bufftype == "debuff" then
icon = UnitDebuff("player", index)
ICON_CACHE[strlower(name)] = icon
texture = icon
else
NO_ICON_CACHE[lowerName] = 1
texture = nil
NO_ICON_CACHE[strlower(name)] = 1
end
elseif not texture and MikCEH.hasNampower and GetSpellIdForName then
local ok, sid = pcall(GetSpellIdForName, name)
if ok and sid and sid ~= 0 then
local ok2, iconId = pcall(GetSpellRecField, sid, "spellIconID")
if ok2 and iconId then
local ok3, tex = pcall(GetSpellIconTexture, iconId)
if ok3 and tex then texture = tex; ICON_CACHE[strlower(name)] = tex end
end
end
elseif not texture and BS and BS.GetSpellIcon and BS:GetSpellIcon(name) then
texture = BS:GetSpellIcon(name)
elseif not texture and MikSBT.FindItemIcon(name) then
texture = MikSBT.FindItemIcon(name)
ICON_CACHE[strlower(name)] = MikSBT.FindItemIcon(name)
elseif not texture then
texture = nil
NO_ICON_CACHE[strlower(name)] = 1
end
end
@@ -1919,50 +1963,106 @@ function ItemLinkToName(link)
end
function MikSBT.FindItemIcon(item)
if ( not item ) then return end
if ( not item ) then return; end
item = string.lower(item);
for slot = INVSLOT_FIRST_EQUIPPED, INVSLOT_LAST_EQUIPPED + 4 do
local itemLocation = ItemLocation:CreateFromEquipmentSlot(slot)
local itemName = C_Item.GetItemName(itemLocation)
if itemName and item == string.lower(itemName) then
return C_Item.GetItemIcon(itemLocation)
end
end
for i = 0, NUM_BAG_FRAMES do
for j = 1, GetContainerNumSlots(i) do
local itemName = C_Item.GetItemName(ItemLocation:CreateFromBagAndSlot(i,j))
if itemName and item == string.lower(itemName) then
return (GetContainerItemInfo(i,j))
local link;
for i = 1,23 do
link = GetInventoryItemLink("player",i);
if ( link ) then
if ( item == string.lower(ItemLinkToName(link)) )then
return i, nil, GetInventoryItemTexture('player', i), GetInventoryItemCount('player', i);
end
end
end
local count, bag, slot, texture;
for i = 0,NUM_BAG_FRAMES do
for j = 1,MAX_CONTAINER_ITEMS do
link = GetContainerItemLink(i,j);
if ( link ) then
if ( item == string.lower(ItemLinkToName(link))) then
bag, slot = i, j;
texture, count = GetContainerItemInfo(i,j);
end
end
end
end
return texture;
end
-- **********************************************************************************
-- Returns the icon for an active buff/debuff or temporary weapon enchant whose
-- name matches, or nil. Uses ClassicAPI (C_UnitAuras / C_Item) instead of the old
-- tooltip-scanning approach.
-- This function return the buff texture based on
-- it's name.
-- **********************************************************************************
function MikSBT.GetAuraIcon(name, unit)
unit = unit or "player";
-- Buffs and debuffs (helpful then harmful) -- AuraData carries the icon directly.
local aura = C_UnitAuras.GetAuraDataBySpellName(unit, name);
if ( aura and aura.icon ) then return aura.icon end
-- Temporary weapon enchant (oils, poisons, imbues) matching this name.
local hasMain, _, _, mainID, hasOff, _, _, offID = C_Item.GetWeaponEnchantInfo();
if ( hasMain and mainID ) then
local info = C_Item.GetEnchantInfo(mainID);
if ( info and info.name == name and info.spellID ) then
return C_Spell.GetSpellTexture(info.spellID);
end
function MikSBT.FindBuff( obuff, unit, item)
local buff=strlower(obuff);
local tooltip=MSBT_Tooltip;
local textleft1=getglobal(tooltip:GetName().."TextLeft1");
if ( not unit ) then
unit ='player';
end
if ( hasOff and offID ) then
local info = C_Item.GetEnchantInfo(offID);
if ( info and info.name == name and info.spellID ) then
return C_Spell.GetSpellTexture(info.spellID);
local my, me, mc, oy, oe, oc = GetWeaponEnchantInfo();
if ( my ) then
tooltip:SetOwner(UIParent, "ANCHOR_NONE");
tooltip:SetInventoryItem( unit, 16);
for i=1, 23 do
local text = getglobal("MSBT_TooltipTextLeft"..i):GetText();
if ( not text ) then
break;
elseif (text and strlower(text) == buff) then
tooltip:Hide();
return "main",me, mc;
end
end
tooltip:Hide();
elseif ( oy ) then
tooltip:SetOwner(UIParent, "ANCHOR_NONE");
tooltip:SetInventoryItem( unit, 17);
for i=1, 23 do
local text = getglobal("MSBT_TooltipTextLeft"..i):GetText();
if ( not text ) then
break;
elseif (text and strlower(text) == buff) then
tooltip:Hide();
return "off", oe, oc;
end
end
tooltip:Hide();
end
if ( item ) then return end
tooltip:SetOwner(UIParent, "ANCHOR_NONE");
tooltip:SetTrackingSpell();
local b = textleft1:GetText();
if ( b and strlower(b) == buff ) then
tooltip:Hide();
return "track",b;
end
local c=nil;
for i=1, 32 do
tooltip:SetOwner(UIParent, "ANCHOR_NONE");
tooltip:SetUnitBuff(unit, i);
b = textleft1:GetText();
tooltip:Hide();
if ( b and strlower(b) == buff ) then
return "buff", i, b;
elseif ( c==b ) then
break;
end
--c = b;
end
c=nil;
for i=1, 16 do
tooltip:SetOwner(UIParent, "ANCHOR_NONE");
tooltip:SetUnitDebuff(unit, i);
b = textleft1:GetText();
tooltip:Hide();
if ( b and strlower(b) == buff ) then
return "debuff", i, b;
elseif ( c==b) then
break;
end
--c = b;
end
tooltip:Hide();
end
-- **********************************************************************************
@@ -3,12 +3,12 @@
## Title: Mik's Scrolling Battle Text
## Notes: Scrolls battle information around the character model.
## Notes-ruRU: Прокрутка информации боя вокруг модели персонажа.
## Dependencies: !!!ClassicAPI
## OptionalDeps: SW_FixLogStrings
## SavedVariables: MikSBT_Save
Libs\AceLibrary\AceLibrary.lua
Libs\AceLocale-2.2\AceLocale-2.2.lua
Libs\BabbleSpell-2.2\Babble-Spell-2.2.lua
MikScrollingBattleText-enUS.lua
MikScrollingBattleText-frFR.lua
@@ -11,6 +11,15 @@
</Scripts>
</Frame>
<!-- Tooltip -->
<GameTooltip name="MSBT_Tooltip" frameStrata="TOOLTIP" hidden="true" parent="UIParent" inherits="GameTooltipTemplate">
<Scripts>
<OnLoad>
this:SetOwner(WorldFrame, "ANCHOR_NONE");
</OnLoad>
</Scripts>
</GameTooltip>
<!-- Incoming Scroll Area -->
<Frame name="MSBTFrameIncoming" toplevel="true" enableMouse="false" parent="UIParent" frameStrata="HIGH" hidden="true">
<Size>
+13 -17
View File
@@ -18,12 +18,9 @@ MikSBT.WINDOW_TITLE = "Mik's Scrolling Battle Text " .. MikSBT.VERSION_STRING ..
MikSBT.COMMAND = "/msbt";
local BS = AceLibrary("Babble-Spell-2.2")
local L = AceLibrary("AceLocale-2.2"):new(MikSBT.MOD_NAME)
-- Resolves a spell's localized name from its ID via ClassicAPI (C_Spell), replacing
-- the old Babble-Spell localization library. Any rank's spell ID yields the same name.
local function SpellName(id) return C_Spell.GetSpellName(id) or "" end
-------------------------------------------------------------------------------
-- English (Default)
-------------------------------------------------------------------------------
@@ -129,7 +126,6 @@ MikSBT.AVAILABLE_FONTS = {
[30] = {Name="PT-Sans-Narrow-Regular", Path="Interface\\Addons\\MikScrollingBattleText\\Fonts\\PT-Sans-Narrow-Regular.ttf"},
[31] = {Name="Simhei", Path="Interface\\Addons\\MikScrollingBattleText\\Fonts\\simhei.ttf"},
[32] = {Name="Archangelsk", Path="Interface\\Addons\\MikScrollingBattleText\\Fonts\\archangelsk.ttf"},
[32] = {Name="Numen", Path="Interface\\Addons\\MikScrollingBattleText\\Fonts\\Numen.ttf"},
};
end
@@ -209,7 +205,7 @@ MikSBT.DEFAULT_CONFIG = {
CreationVersion = MikSBT.VERSION_NUMBER,
ShowPartialEffects = true,
ShowOverheals = true,
ShowGameDamage = false,
ShowGameDamage = true,
UseStickyCrits = true,
ShowAllManaGains = false,
LowHealthSound = true,
@@ -1138,7 +1134,7 @@ MikSBT.DEFAULT_CONFIG = {
MSBT_TRIGGER_EXECUTE = {
EventSettings = {
Show = true,
Message = SpellName(5308).."!", -- Execute
Message = BS["Execute"].."!",
IsSticky = true,
FontSettings = {
Color = {r=1, g=1, b=0},
@@ -1155,7 +1151,7 @@ MikSBT.DEFAULT_CONFIG = {
MSBT_TRIGGER_HAMMER_OF_WRATH = {
EventSettings = {
Show = true,
Message = SpellName(24275).."!", -- Hammer of Wrath
Message = BS["Hammer of Wrath"].."!",
IsSticky = true,
FontSettings = {
Color = {r=1, g=1, b=0},
@@ -1172,7 +1168,7 @@ MikSBT.DEFAULT_CONFIG = {
MSBT_TRIGGER_COUNTER_ATTACK = {
EventSettings = {
Show = true,
Message = SpellName(19306).."!", -- Counterattack
Message = BS["Counterattack"].."!",
IsSticky = true,
FontSettings = {
Color = {r=1, g=1, b=0},
@@ -1191,7 +1187,7 @@ MikSBT.DEFAULT_CONFIG = {
MSBT_TRIGGER_MONGOOSE_BITE = {
EventSettings = {
Show = true,
Message = SpellName(1495).."!", -- Mongoose Bite
Message = BS["Mongoose Bite"].."!",
IsSticky = true,
FontSettings = {
Color = {r=1, g=1, b=0},
@@ -1210,7 +1206,7 @@ MikSBT.DEFAULT_CONFIG = {
MSBT_TRIGGER_CLEARCAST = {
EventSettings = {
Show = true,
Message = SpellName(16246).."!", -- Clearcasting
Message = BS["Clearcasting"].."!",
IsSticky = true,
FontSettings = {
Color = {r=1, g=1, b=0},
@@ -1221,14 +1217,14 @@ MikSBT.DEFAULT_CONFIG = {
Classes = {MAGE=true,SHAMAN=true},
TriggerType = 6,
TriggerEvents = {"CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS"},
SearchPatterns = {string.format(AURAADDEDSELFHELPFUL, SpellName(16246))}, -- Clearcasting (ID verified vs Spell.dbc)
SearchPatterns = {string.format(AURAADDEDSELFHELPFUL, BS["Clearcasting"])},
},
Texture = "Interface\\Icons\\spell_shadow_manaburn"
},
MSBT_TRIGGER_RIPOSTE = {
EventSettings = {
Show = true,
Message = SpellName(14251).."!", -- Riposte
Message = BS["Riposte"].."!",
IsSticky = true,
FontSettings = {
Color = {r=1, g=1, b=0},
@@ -1257,14 +1253,14 @@ MikSBT.DEFAULT_CONFIG = {
TriggerSettings = {
TriggerType = 6,
TriggerEvents = {"CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS"},
SearchPatterns = {string.format(AURAADDEDSELFHELPFUL, string.gsub(SpellName(8512), "-", "%%-"))}, -- Windfury Totem (ID verified vs Spell.dbc). "Vous gagnez Totem Furie-des-vents" -- escape char !
SearchPatterns = {string.format(AURAADDEDSELFHELPFUL, string.gsub(BS["Windfury Totem"], "-", "%%-"))}, --"Vous gagnez Totem Furie-des-vents" -- escape char !
},
Texture = "Interface\\Icons\\spell_nature_cyclone"
},
MSBT_TRIGGER_NIGHTFALL = {
EventSettings = {
Show = true,
Message = SpellName(18094).."!", -- Nightfall (talent)
Message = BS["Nightfall"].."!",
IsSticky = true,
FontSettings = {
Color = {r=0.709, g=0, b=0.709},
@@ -1275,14 +1271,14 @@ MikSBT.DEFAULT_CONFIG = {
Classes = {WARLOCK=true},
TriggerType = 6,
TriggerEvents = {"CHAT_MSG_SPELL_PERIODIC_SELF_BUFFS"},
SearchPatterns = {string.format(AURAADDEDSELFHELPFUL, SpellName(17941))}, -- Shadow Trance (ID verified vs Spell.dbc)
SearchPatterns = {string.format(AURAADDEDSELFHELPFUL, BS["Shadow Trance"])},
},
Texture = "Interface\\Icons\\spell_shadow_twilight"
},
MSBT_TRIGGER_OVERPOWER = {
EventSettings = {
Show = true,
Message = SpellName(7384).."!", -- Overpower
Message = BS["Overpower"].."!",
IsSticky = true,
FontSettings = {
Color = {r=1, g=1, b=0},
+17 -17
View File
@@ -4,10 +4,6 @@ A World of Warcraft 1.12 (Vanilla) addon that displays scrolling combat text aro
**Interface:** 11200 | **Version:** 4.43
## Requirements
- **ClassicAPI** — a client-side DLL that backports modern WoW API calls to the 1.12 client (no companion addon required). MSBT relies on it for `C_Spell.GetSpellName`, `C_Item.GetItemName`/`GetItemIcon`, `ItemLocation`, and `CreateColor`/`GetRGB` — used for spell-name lookups, item-icon resolution, and damage-type text coloring. These are not optional fast paths; the addon expects them to be present.
## Features
- Scrolling combat text across three display areas: **Incoming** (right), **Outgoing** (left), and **Notification** (center)
@@ -64,28 +60,32 @@ Interface/
| `/msbt debug` | Toggle debug mode |
| `/msbt help` | Show command usage |
## ClassicAPI Combat Log Integration
## Nampower / SuperWoW / UnitXP SP3 Integration
MSBT reads structured combat data from ClassicAPI's `COMBAT_LOG_EVENT_UNFILTERED` instead of relying on string-based combat log parsing alone, for significantly better performance in heavy combat scenarios (raids, AoE, battlegrounds).
When these enhanced client mods are detected, MSBT automatically switches from string-based combat log parsing to structured event handling for significantly better performance in heavy combat scenarios (raids, AoE, battlegrounds).
### What Changes
- **ClassicAPI combat log**: Registers for the unfiltered combat log (`SPELL_DAMAGE`, `SWING_DAMAGE`, `SPELL_HEAL`, `SPELL_ENERGIZE`, `SPELL_MISSED`, `SPELL_AURA_APPLIED`, `ENVIRONMENTAL_DAMAGE`, `PARTY_KILL`, etc.) which carries pre-parsed amounts, GUIDs, crit flags, miss types and spell info. Redundant `CHAT_MSG_*` events are unregistered to avoid duplicates. No opt-in CVars are needed.
- **SpellId icons**: SpellIds from combat log events resolve icons directly through ClassicAPI's `GetSpellInfo`, bypassing Babble-Spell dictionary lookups and tooltip scanning.
### Removed: SuperWoW / UnitXP SP3 / Nampower
SuperWoW, UnitXP SP3 and Nampower integration has been removed in favor of ClassicAPI (for spell/item lookups, GUIDs and structured combat events). ClassicAPI is a hard dependency (see the `.toc`).
- **Nampower**: Registers for structured events (`SPELL_DAMAGE_EVENT_SELF`, `AUTO_ATTACK_SELF`, `SPELL_HEAL_ON_SELF`, etc.) that provide pre-parsed combat data. Redundant `CHAT_MSG_*` events are unregistered to avoid duplicates. Enables `NP_EnableAutoAttackEvents`, `NP_EnableSpellHealEvents`, and `NP_EnableSpellEnergizeEvents` CVars.
- **SuperWoW**: Provides O(1) unit lookups by name via `UnitExists(name)` (replacing O(n) raid/party iteration) and `SpellInfo(spellId)` for spell icon resolution.
- **UnitXP SP3**: Detected for compatibility; used for GUID-based unit resolution.
### Fallback
`CHAT_MSG_*` parsing stays registered for everything with no combat log equivalent (honor, reputation, skill, XP, item enchantments, some power gains/losses) and as a fallback until each combat log event actually fires.
When none of these mods are present, the addon falls back to the original string-parsing pipeline with no changes in behavior.
### Verification
```lua
-- Check detection status in-game:
/run MikSBT.Print("NP="..tostring(MikCEH.hasNampower).." SW="..tostring(MikCEH.hasSuperWoW).." UXP="..tostring(MikCEH.hasUnitXP))
```
## Performance Optimizations
- **Upvalue caching**: Frequently called standard library functions (`string.find`, `string.gsub`, `table.insert`, `table.getn`, `GetTime`, etc.) are cached as local variables
- **Dispatch table**: Combat event routing uses O(1) table lookup instead of an if/elseif chain
- **SpellId icon cache**: SpellIds from combat log events resolve icons directly through ClassicAPI and are cached, bypassing Babble-Spell dictionary lookups and tooltip scanning
- **SpellId icon cache**: When spellIds are available (via Nampower/SuperWoW), icons are resolved directly and cached, bypassing Babble-Spell dictionary lookups and tooltip scanning
- **Event throttling**: UNIT_HEALTH and UNIT_MANA trigger checks are throttled to 150ms intervals
- **Table recycling**: `MikTRO` (Table Recycler Object) pools and reuses table allocations to reduce garbage collection pressure
@@ -102,9 +102,9 @@ SuperWoW, UnitXP SP3 and Nampower integration has been removed in favor of Class
### Event Processing Pipeline
```
WoW CHAT_MSG_* events (or ClassicAPI COMBAT_LOG_EVENT_UNFILTERED)
-> MikCEH.OnEvent() / MikCEH.OnCombatLogEvent()
-> ParseCombatEvents() dispatch table (or combat log handler)
WoW CHAT_MSG_* events (or Nampower structured events)
-> MikCEH.OnEvent() / MikCEH.OnNampowerEvent()
-> ParseCombatEvents() dispatch table (or Nampower handler)
-> Creates CombatEventData tables
-> MikSBT.CombatEventsHandler()
-> Formats text with templates (%a=amount, %n=name, %s=spell, %t=type)