dll updates

This commit is contained in:
Jrc13245
2026-02-10 15:56:04 -05:00
parent cfdab18f86
commit 53d82a0ad1
3 changed files with 1267 additions and 292 deletions
File diff suppressed because it is too large Load Diff
@@ -8,6 +8,14 @@
-- Public Constants.
-------------------------------------------------------------------------------------
-- Upvalue caches for frequently called functions.
local string_find, string_gsub = string.find, string.gsub
local table_insert, table_getn, table_remove = table.insert, table.getn, table.remove
local GetTime = GetTime
local getglobal = getglobal
local strlower = strlower
local tonumber = tonumber
-- Display types for incoming, outgoing, and notification events.
MikSBT.DISPLAYTYPE_INCOMING = 1;
MikSBT.DISPLAYTYPE_OUTGOING = 2;
@@ -118,6 +126,9 @@ ICON_CACHE["effet inferno"] = "Interface\\Icons\\spell_fire_incinerate"
-- Cache des spells dont l'icone n'a pas été trouvée
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")
@@ -290,7 +301,7 @@ function MikSBT.OnUpdate()
stillMerging = true;
-- Merge like events.
local numEvents = table.getn(mergeData.UnmergedEvents);
local numEvents = table_getn(mergeData.UnmergedEvents);
MikSBT.MergeEvents(mergeData, numEvents);
-- Add the merged animation events to the animation system.
@@ -299,9 +310,9 @@ function MikSBT.OnUpdate()
end
-- Clear the merged animation events array.
local numMergedEvents = table.getn(mergeData.MergedEvents);
local numMergedEvents = table_getn(mergeData.MergedEvents);
for i = 1, numMergedEvents do
table.remove(mergeData.MergedEvents, 1);
table_remove(mergeData.MergedEvents, 1);
end
@@ -391,6 +402,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
@@ -596,6 +614,7 @@ function MikSBT.CombatEventsHandler(combatEvent)
-- end
animationEvent.EffectName = combatEvent.EffectName;
animationEvent.Name = combatEvent.Name;
animationEvent.SpellId = combatEvent.SpellId;
-- Check if there is a damage type and get the appropriate string.
if (combatEvent.DamageType ~= nil) then
@@ -605,19 +624,19 @@ function MikSBT.CombatEventsHandler(combatEvent)
-- If there are partial action types set the partial effect text appropriately.
if (combatEvent.PartialActionType == MikCEH.PARTIALACTIONTYPE_ABSORB) then
if (ABSORB_TRAILER) then
animationEvent.PartialEffectText = "\124cffFAFA00\124h"..string.gsub(ABSORB_TRAILER, "%%d", combatEvent.PartialAmount).."\124h\124r";
animationEvent.PartialEffectText = "\124cffFAFA00\124h"..string_gsub(ABSORB_TRAILER, "%%d", combatEvent.PartialAmount).."\124h\124r";
end
elseif (combatEvent.PartialActionType == MikCEH.PARTIALACTIONTYPE_BLOCK) then
if (BLOCK_TRAILER) then
animationEvent.PartialEffectText = "\124cff7F00FF\124h"..string.gsub(BLOCK_TRAILER, "%%d", combatEvent.PartialAmount).."\124h\124r";
animationEvent.PartialEffectText = "\124cff7F00FF\124h"..string_gsub(BLOCK_TRAILER, "%%d", combatEvent.PartialAmount).."\124h\124r";
end
elseif (combatEvent.PartialActionType == MikCEH.PARTIALACTIONTYPE_RESIST) then
if (RESIST_TRAILER) then
animationEvent.PartialEffectText = " \124cff81007F\124h"..string.gsub(RESIST_TRAILER, "%%d", combatEvent.PartialAmount).."\124h\124r";
animationEvent.PartialEffectText = " \124cff81007F\124h"..string_gsub(RESIST_TRAILER, "%%d", combatEvent.PartialAmount).."\124h\124r";
end
elseif (combatEvent.PartialActionType == MikCEH.PARTIALACTIONTYPE_VULNERABLE) then
if (MikSBT.MSG_VULERNABLE_TRAILER) then
animationEvent.PartialEffectText = string.gsub(MikSBT.MSG_VULERNABLE_TRAILER, "%%d", combatEvent.PartialAmount);
animationEvent.PartialEffectText = string_gsub(MikSBT.MSG_VULERNABLE_TRAILER, "%%d", combatEvent.PartialAmount);
end
elseif (combatEvent.PartialActionType == MikCEH.PARTIALACTIONTYPE_CRUSHING) then
if (CRUSHING_TRAILER) then
@@ -809,7 +828,7 @@ function MikSBT.MergeEvents(mergeData, numEvents)
end
-- Add the animation event to the end of the merged events array.
table.insert(mergeData.MergedEvents, animationEvent);
table_insert(mergeData.MergedEvents, animationEvent);
end
-- Reset the event merged flag.
@@ -849,7 +868,7 @@ function MikSBT.MergeEvents(mergeData, numEvents)
end
-- Remove the event from the unmerged events array.
table.remove(mergeData.UnmergedEvents, 1);
table_remove(mergeData.UnmergedEvents, 1);
end
end
@@ -1057,23 +1076,23 @@ function MikSBT.FormatEventText(animationEvent)
return;
end
outputString = string.gsub(outputString, "-%%a", "\124cffff0000\124h-\124h\124r%%a");
outputString = string_gsub(outputString, "-%%a", "\124cffff0000\124h-\124h\124r%%a");
if (animationEvent.DamageType ~= nil) then
if animationEvent.DamageType == SPELL_SCHOOL1_CAP then
outputString = string.gsub(outputString, "%%a", "\124cffF6F99E\124h%%a\124h\124r");
outputString = string_gsub(outputString, "%%a", "\124cffF6F99E\124h%%a\124h\124r");
elseif animationEvent.DamageType == SPELL_SCHOOL2_CAP then
outputString = string.gsub(outputString, "%%a", "\124cffFF8080\124h%%a\124h\124r");
outputString = string_gsub(outputString, "%%a", "\124cffFF8080\124h%%a\124h\124r");
elseif animationEvent.DamageType == SPELL_SCHOOL3_CAP then
outputString = string.gsub(outputString, "%%a", "\124cff80FF80\124h%%a\124h\124r");
outputString = string_gsub(outputString, "%%a", "\124cff80FF80\124h%%a\124h\124r");
elseif animationEvent.DamageType == SPELL_SCHOOL4_CAP then
outputString = string.gsub(outputString, "%%a", "\124cff8080FF\124h%%a\124h\124r");
outputString = string_gsub(outputString, "%%a", "\124cff8080FF\124h%%a\124h\124r");
elseif animationEvent.DamageType == SPELL_SCHOOL5_CAP then
outputString = string.gsub(outputString, "%%a", "\124cffA000A0\124h%%a\124h\124r");
outputString = string_gsub(outputString, "%%a", "\124cffA000A0\124h%%a\124h\124r");
elseif animationEvent.DamageType == SPELL_SCHOOL6_CAP then
outputString = string.gsub(outputString, "%%a", "\124cffFFB9FF\124h%%a\124h\124r");
outputString = string_gsub(outputString, "%%a", "\124cffFFB9FF\124h%%a\124h\124r");
elseif animationEvent.DamageType == "Inconnu" then
outputString = string.gsub(outputString, "%%a", "\124cffFFB9FF\124h%%a\124h\124r");
outputString = string_gsub(outputString, "%%a", "\124cffFFB9FF\124h%%a\124h\124r");
-- DEFAULT_CHAT_FRAME:AddMessage(animationEvent.DamageType)
end
end
@@ -1082,30 +1101,30 @@ function MikSBT.FormatEventText(animationEvent)
if (animationEvent.Amount ~= nil) then
-- Check if there is overheal info
if (animationEvent.OverhealAmount ~= nil and MikSBT.CurrentProfile.ShowOverheals) then
local overhealTrailer = string.gsub(MikSBT.MSG_OVERHEAL_TRAILER, "%%d", animationEvent.OverhealAmount);
outputString = string.gsub(outputString, "%%a", (animationEvent.Amount - animationEvent.OverhealAmount) .. overhealTrailer);
local overhealTrailer = string_gsub(MikSBT.MSG_OVERHEAL_TRAILER, "%%d", animationEvent.OverhealAmount);
outputString = string_gsub(outputString, "%%a", (animationEvent.Amount - animationEvent.OverhealAmount) .. overhealTrailer);
else
outputString = string.gsub(outputString, "%%a", animationEvent.Amount);
outputString = string_gsub(outputString, "%%a", animationEvent.Amount);
end
end
-- Substitute enemy/player names.
local unitID, unitName = MikCEH.GetUnitIDFromName(animationEvent.Name)
if unitName and unitID and not UnitIsUnit(unitID, "player") then
outputString = string.gsub(outputString, "%%n", unitName);
outputString = string_gsub(outputString, "%%n", unitName);
else
outputString = string.gsub(outputString, "%(%%n%)", "");
outputString = string.gsub(outputString, "%%n", "");
outputString = string_gsub(outputString, "%(%%n%)", "");
outputString = string_gsub(outputString, "%%n", "");
end
-- Substitute effect names.
if (animationEvent.EffectName ~= nil) then
outputString = string.gsub(outputString, "%%s", animationEvent.EffectName);
outputString = string_gsub(outputString, "%%s", animationEvent.EffectName);
end
-- Substitute damage types.
if (animationEvent.DamageType ~= nil) then
outputString = string.gsub(outputString, "%%t", animationEvent.DamageType);
outputString = string_gsub(outputString, "%%t", animationEvent.DamageType);
end
-- Check if there is partial effect text and the show partial effects flag is on and append it.
@@ -1138,7 +1157,7 @@ function MikSBT.FormatTriggerText(animationEvent, triggerEvent)
-- Loop through all of the captures in the trigger event.
for i = 1, triggerEvent.NumCaptures do
outputString = string.gsub(outputString , "%%" .. i, triggerEvent["CapturedData" .. i]);
outputString = string_gsub(outputString , "%%" .. i, triggerEvent["CapturedData" .. i]);
end
-- Set the event's output text.
@@ -1203,17 +1222,17 @@ function MikSBT.UpdateProfiles()
-- Loop through all of the event settings.
for eventTypeName, eventType in profile.EventSettings do
-- Check for the event types that can't be crits and remove the crit settings table.
if (string.find(eventTypeName, "MISS") or string.find(eventTypeName, "DODGE") or string.find(eventTypeName, "PARRY") or
string.find(eventTypeName, "BLOCK") or string.find(eventTypeName, "ABSORB") or string.find(eventTypeName, "IMMUNE") or
string.find(eventTypeName, "DOT") or string.find(eventTypeName, "RESIST") or string.find(eventTypeName, "HOT") or
string.find(eventTypeName, "ENVIRONMENTAL") or string.find(eventTypeName, "EVADE") or string.find(eventTypeName, "NOTIFICATION")) then
if (string_find(eventTypeName, "MISS") or string_find(eventTypeName, "DODGE") or string_find(eventTypeName, "PARRY") or
string_find(eventTypeName, "BLOCK") or string_find(eventTypeName, "ABSORB") or string_find(eventTypeName, "IMMUNE") or
string_find(eventTypeName, "DOT") or string_find(eventTypeName, "RESIST") or string_find(eventTypeName, "HOT") or
string_find(eventTypeName, "ENVIRONMENTAL") or string_find(eventTypeName, "EVADE") or string_find(eventTypeName, "NOTIFICATION")) then
-- Remove the crit settings table.
eventType.FontSettings.Crit = nil;
end
-- Check if the event type is a notification and add the IsSticky flag.
if (string.find(eventTypeName, "NOTIFICATION")) then
if (string_find(eventTypeName, "NOTIFICATION")) then
eventType.IsSticky = MikSBT.DEFAULT_CONFIG.EventSettings[eventTypeName].IsSticky;
end
@@ -1266,7 +1285,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(BS["Windfury Totem"], "-", "%%-"))
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
@@ -1576,9 +1595,9 @@ function MikSBT.DispatchAnimationEvent(animationEvent)
-- If it's an incoming or outgoing event add it to the associated scroll area's events array
-- for potential merging.
if (animationEvent.ScrollArea == scrollAreas.Incoming) then
table.insert(animationMergeData.Incoming.UnmergedEvents, animationEvent);
table_insert(animationMergeData.Incoming.UnmergedEvents, animationEvent);
elseif (animationEvent.ScrollArea == scrollAreas.Outgoing) then
table.insert(animationMergeData.Outgoing.UnmergedEvents, animationEvent);
table_insert(animationMergeData.Outgoing.UnmergedEvents, animationEvent);
-- Add the event the animation system.
else
@@ -1598,11 +1617,11 @@ function MikSBT.AddAnimation(animationEvent)
-- Loop through all of the suppression entries and look for a match.
for _, suppressionSettings in MikSBT.CurrentProfile.Suppressions do
-- Check if the suppression's search pattern is a match.
if (suppressionSettings.Enabled and (string.find(animationEvent.Text, suppressionSettings.SearchPattern))) then
if (suppressionSettings.Enabled and (string_find(animationEvent.Text, suppressionSettings.SearchPattern))) then
-- Reclaim the animation event table to the events recycler and leave the function.
eventsRecycler:ReclaimTable(animationEvent);
return;
elseif (suppressionSettings.Enabled and animationEvent.EffectName and string.find(animationEvent.EffectName, suppressionSettings.SearchPattern)) then
elseif (suppressionSettings.Enabled and animationEvent.EffectName and string_find(animationEvent.EffectName, suppressionSettings.SearchPattern)) then
-- Reclaim the animation event table to the events recycler and leave the function.
eventsRecycler:ReclaimTable(animationEvent);
return;
@@ -1624,23 +1643,23 @@ function MikSBT.AddAnimation(animationEvent)
if unitID then
local _, Class = UnitClass(unitID)
if Class and Class == "PALADIN" then
animationEvent.Text = string.gsub(animationEvent.Text, animationEvent.Name, "|cffF58CBA|h"..uName.."|h|r")
animationEvent.Text = string_gsub(animationEvent.Text, animationEvent.Name, "|cffF58CBA|h"..uName.."|h|r")
elseif Class and Class == "DRUID" then
animationEvent.Text = string.gsub(animationEvent.Text, animationEvent.Name, "|cffFF7D0A|h"..uName.."|h|r")
animationEvent.Text = string_gsub(animationEvent.Text, animationEvent.Name, "|cffFF7D0A|h"..uName.."|h|r")
elseif Class and Class == "HUNTER" then
animationEvent.Text = string.gsub(animationEvent.Text, animationEvent.Name, "|cffABD473|h"..uName.."|h|r")
animationEvent.Text = string_gsub(animationEvent.Text, animationEvent.Name, "|cffABD473|h"..uName.."|h|r")
elseif Class and Class == "MAGE" then
animationEvent.Text = string.gsub(animationEvent.Text, animationEvent.Name, "|cff69CCF0|h"..uName.."|h|r")
animationEvent.Text = string_gsub(animationEvent.Text, animationEvent.Name, "|cff69CCF0|h"..uName.."|h|r")
elseif Class and Class == "PRIEST" then
animationEvent.Text = string.gsub(animationEvent.Text, animationEvent.Name, "|cffFFFFFF|h"..uName.."|h|r")
animationEvent.Text = string_gsub(animationEvent.Text, animationEvent.Name, "|cffFFFFFF|h"..uName.."|h|r")
elseif Class and Class == "ROGUE" then
animationEvent.Text = string.gsub(animationEvent.Text, animationEvent.Name, "|cffFFF569|h"..uName.."|h|r")
animationEvent.Text = string_gsub(animationEvent.Text, animationEvent.Name, "|cffFFF569|h"..uName.."|h|r")
elseif Class and Class == "SHAMAN" then
animationEvent.Text = string.gsub(animationEvent.Text, animationEvent.Name, "|cff0070DE|h"..uName.."|h|r")
animationEvent.Text = string_gsub(animationEvent.Text, animationEvent.Name, "|cff0070DE|h"..uName.."|h|r")
elseif Class and Class == "WARLOCK" then
animationEvent.Text = string.gsub(animationEvent.Text, animationEvent.Name, "|cff9482C9|h"..uName.."|h|r")
animationEvent.Text = string_gsub(animationEvent.Text, animationEvent.Name, "|cff9482C9|h"..uName.."|h|r")
elseif Class and Class == "WARRIOR" then
animationEvent.Text = string.gsub(animationEvent.Text, animationEvent.Name, "|cffC79C6E|h"..uName.."|h|r")
animationEvent.Text = string_gsub(animationEvent.Text, animationEvent.Name, "|cffC79C6E|h"..uName.."|h|r")
end
end
@@ -1698,7 +1717,7 @@ function MikSBT.AddAnimation(animationEvent)
-- play sound if enabled
if MikSBT.CurrentProfile.ResistSound then
if animationEvent.EffectName then
if (string.find(animationEvent.EffectName, "Cone of Cold") or string.find(animationEvent.EffectName, "Frost Nova")) then
if (string_find(animationEvent.EffectName, "Cone of Cold") or string_find(animationEvent.EffectName, "Frost Nova")) then
PlaySoundFile("Interface\\AddOns\\MikScrollingBattleText\\sounds\\Resist.mp3");
end
end
@@ -1720,8 +1739,26 @@ function MikSBT.AddAnimation(animationEvent)
-- Athene Edit - Spell Icon
local texture
if animationEvent.Texture then texture = animationEvent.Texture end
-- SpellId-based icon lookup (fast path, bypasses Babble-Spell and tooltip scanning).
if not texture and animationEvent.SpellId then
local sid = animationEvent.SpellId
if spellIdIconCache[sid] then
texture = spellIdIconCache[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 name = string_gsub(animationEvent.EffectName, " %(%d+%)", "")
-- DEFAULT_CHAT_FRAME:AddMessage(animationEvent.EffectName)
-- DEFAULT_CHAT_FRAME:AddMessage(name)
-- texture = BS:GetSpellIcon(name)
@@ -2135,19 +2172,19 @@ function MikSBT.RepositionAnimDisplayInfo(scrollArea)
for x = scrollArea.NextAnimationDisplay, scrollArea.FirstSticky-1 do
local animDisplayInfo = scrollArea.AnimationDisplayArray[x];
if (animDisplayInfo.IsActive) then
table.insert(activeNonStickies, animDisplayInfo);
table_insert(activeNonStickies, animDisplayInfo);
end
end
for x = 1, scrollArea.NextAnimationDisplay-1 do
local animDisplayInfo = scrollArea.AnimationDisplayArray[x];
if (animDisplayInfo.IsActive) then
table.insert(activeNonStickies, animDisplayInfo);
table_insert(activeNonStickies, animDisplayInfo);
end
end
-- Get the number of active non sticky data objects.
local numNonStickies = table.getn(activeNonStickies);
local numNonStickies = table_getn(activeNonStickies);
-- Animation style straight, left parabola, or right parabola.
if (scrollArea.DisplaySettings.AnimationStyle == ANIMATION_STYLE_STRAIGHT or
@@ -2189,7 +2226,7 @@ function MikSBT.RepositionAnimDisplayInfo(scrollArea)
-- Clear the temp non stickies table.
for x = 1, numNonStickies do
table.remove(activeNonStickies, 1);
table_remove(activeNonStickies, 1);
end
end
@@ -2202,19 +2239,19 @@ function MikSBT.RepositionStickyAnimDisplayInfo(scrollArea)
for x = scrollArea.NextSticky, NUM_FONT_STRINGS do
local animDisplayInfo = scrollArea.AnimationDisplayArray[x];
if (animDisplayInfo.IsActive) then
table.insert(activeStickies, animDisplayInfo);
table_insert(activeStickies, animDisplayInfo);
end
end
for x = scrollArea.FirstSticky, scrollArea.NextSticky-1 do
local animDisplayInfo = scrollArea.AnimationDisplayArray[x];
if (animDisplayInfo.IsActive) then
table.insert(activeStickies, animDisplayInfo);
table_insert(activeStickies, animDisplayInfo);
end
end
-- Get the number of active stickies.
local numStickies = table.getn(activeStickies);
local numStickies = table_getn(activeStickies);
-- Animation style straight, left parabola, or right parabola.
@@ -2271,7 +2308,7 @@ function MikSBT.RepositionStickyAnimDisplayInfo(scrollArea)
-- Clear the temp stickies table.
for x = 1, numStickies do
table.remove(activeStickies, 1);
table_remove(activeStickies, 1);
end
end
@@ -2570,7 +2607,7 @@ function MikSBT.GetNextParameter(paramString)
local currentParam = paramString;
-- Look for a space.
local idx = string.find(paramString, " ");
local idx = string_find(paramString, " ");
if (idx) then
-- Get the current and remaing parameters.
+139
View File
@@ -0,0 +1,139 @@
# Mik's Scrolling Battle Text (Vanilla)
A World of Warcraft 1.12 (Vanilla) addon that displays scrolling combat text around the player's character model. Originally by **Mik**, maintained by **Athene**.
**Interface:** 11200 | **Version:** 4.43
## Features
- Scrolling combat text across three display areas: **Incoming** (right), **Outgoing** (left), and **Notification** (center)
- Damage, heals, DoTs, HoTs, misses, dodges, parries, blocks, resists, absorbs, reflects, and more
- Buff/debuff gain and fade notifications
- Power gains and losses (mana, rage, energy)
- Combo points, honor, reputation, skill, and experience gains
- Killing blow notifications (PC and NPC)
- Environmental damage (drowning, falling, fire, lava, fatigue, slime)
- Spell icons displayed alongside scrolling text
- Class-colored unit names
- Configurable profiles with 100+ event settings
- Customizable animation styles (Straight, Left Parabola, Right Parabola) and scroll directions (Up, Down)
- Sticky text for crits and important events
- Event merging to reduce clutter in heavy combat
- Event suppression via configurable search patterns
- Health/mana threshold triggers with per-class filtering
- Search pattern triggers for custom event matching
- Four locales: English, French, German, Russian
## Addon Packages
### MikScrollingBattleText (Core)
The main addon. Parses combat log events, formats text, and animates scrolling messages.
### MikScrollingBattleTextOptions (Options UI)
Load-on-demand options panel. Only loaded when the user opens settings via `/msbt`.
## Installation
Copy both `MikScrollingBattleText/` and `MikScrollingBattleTextOptions/` into your `Interface/AddOns/` directory.
```
Interface/
AddOns/
MikScrollingBattleText/
MikScrollingBattleTextOptions/
```
## Slash Commands
`/msbt` — Open the options UI
| Command | Description |
|---|---|
| `/msbt reset` | Reset the current profile to defaults |
| `/msbt disable` | Disable the addon |
| `/msbt enable` | Enable the addon |
| `/msbt version` | Show the current version |
| `/msbt stats` | Report table recycling statistics |
| `/msbt search <filter>` | Set a filter for searching event types |
| `/msbt debug` | Toggle debug mode |
| `/msbt help` | Show command usage |
## Nampower / SuperWoW / UnitXP SP3 Integration
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
- **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
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**: 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
## Architecture
### Global Namespaces
| Namespace | Purpose |
|---|---|
| `MikSBT` | Main addon: display, animation, profiles, event routing, slash commands |
| `MikCEH` | Combat Event Helper: parses combat events into structured data |
| `MikTRO` | Table Recycler Object: memory pool for table reuse |
### Event Processing Pipeline
```
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)
-> Queues animation event
-> MikSBT.OnUpdateAnimationFrame() renders scrolling text
```
### Bundled Libraries
- **AceLibrary** — Addon framework skeleton
- **AceLocale-2.2** — Localization management
- **Babble-Spell-2.2** — Spell name database for icon lookups (~10K lines)
### Saved Variables
`MikSBT_Save` — Stores named profiles containing event settings, triggers, suppressions, display positions, and font settings. Profiles support versioning and migration between addon versions.
## Development
- No build step — files are loaded directly by the WoW client via TOC manifests
- No test framework — testing is done manually in the WoW client
- The Lua environment is WoW's embedded Lua 5.0/5.1 (not standard Lua 5.4)
- IDE warnings about "undefined globals" (`CreateFrame`, `GetLocale`, `DEFAULT_CHAT_FRAME`, `arg1`, `event`, etc.) are false positives — these are provided by the WoW runtime
- The `wow-api-type-definitions/` submodule provides WoW API type stubs for IDE autocompletion (sumneko.lua language server)
## Optional Dependency
- **SW_FixLogStrings** — Fixes combat log string formatting issues on some server implementations
## License
This addon is distributed for use with World of Warcraft 1.12 private servers. Original work by Mik, community maintained.