Files
Brues 73fd6803ab Replace Babble-Spell library with Nampower GetSpellRecField
Drop the bundled Babble-Spell-2.2 localization library and resolve spell
names from their IDs via Nampower's GetSpellRecField(id, "name"). Adds a
shared MikSBT.SpellName(id, fallback) helper that returns the client's
localized name when Nampower is present and falls back to the English name
otherwise, so DEFAULT_CONFIG still builds without a Lua error when Nampower
is absent.

Also removes the now-redundant BS:GetSpellIcon fallback in the icon
resolver (the Nampower spellIconID path already covers it) and drops the
lib from the .toc.
2026-07-19 02:46:43 -05:00

2963 lines
115 KiB
Lua

-------------------------------------------------------------------------------------
-- Title: Mik's Scrolling Battle Text
-- Author: Mik
-- Maintainer: Athene
-------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------
-- 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 table_setn = table.setn
local math_ceil, math_floor, math_random = math.ceil, math.floor, math.random
local GetTime = GetTime
local getglobal = getglobal
local strlower = strlower
local tonumber = tonumber
local UnitClass = UnitClass
-- Display types for incoming, outgoing, and notification events.
MikSBT.DISPLAYTYPE_INCOMING = 1;
MikSBT.DISPLAYTYPE_OUTGOING = 2;
MikSBT.DISPLAYTYPE_NOTIFICATION = 3;
-------------------------------------------------------------------------------------
-- Public Variables.
-------------------------------------------------------------------------------------
-- Holds the current profile.
MikSBT.CurrentProfile = nil;
-------------------------------------------------------------------------------------
-- Private constants.
-------------------------------------------------------------------------------------
-- cache des icones trouvées par l'algo et des icones qu'il ne trouverait pas
if not ICON_CACHE then
ICON_CACHE = {}
end
ICON_CACHE["repousser au loin"] = "Interface\\Icons\\Inv_Gauntlets_05"
ICON_CACHE["rupture du sol"] = "Interface\\Icons\\Spell_Nature_Earthquake"
ICON_CACHE["rayon de l'oeil"] = "Interface\\Icons\\Spell_Nature_Callstorm"
ICON_CACHE["salve de traits de l'ombre"] = "Interface\\Icons\\Spell_Shadow_Shadowbolt"
ICON_CACHE["tempête d'ombre"] = "Interface\\Icons\\spell_shadow_shadowbolt"
ICON_CACHE["gaz"] = "Interface\\Icons\\Spell_Nature_Abolishmagic"
ICON_CACHE["décimer"] = "Interface\\Icons\\Inv_Shield_01"
ICON_CACHE["champ statique"] = "Interface\\Icons\\Spell_Nature_WispHeal"
ICON_CACHE["horion"] = "Interface\\Icons\\Spell_Nature_WispHeal"
ICON_CACHE["secousse"] = "Interface\\Icons\\spell_nature_earthquake"
ICON_CACHE["charge de bouclier"] = "Interface\\Icons\\ability_warrior_charge"
ICON_CACHE["eclair des arcanes"] = "Interface\\Icons\\spell_arcane_starfire"
ICON_CACHE["gémissement des âmes"] = "Interface\\Icons\\spell_nature_lightning"
ICON_CACHE["trait noir"] = "Interface\\Icons\\spell_shadow_shadowbolt"
ICON_CACHE["infusion de mana"] = "Interface\\Icons\\inv_trinket_naxxramas01"
ICON_CACHE["afflux"] = "Interface\\Icons\\Ability_Warrior_Charge"
ICON_CACHE["frappe"] = "Interface\\Icons\\Ability_Rogue_Ambush"
ICON_CACHE["souffle de lave"] = "Interface\\Icons\\Spell_Fire_WindsOfWoe"
ICON_CACHE["frappe des ailes"] = "Interface\\Icons\\Inv_Misc_Monsterscales_14"
ICON_CACHE["lance de flammes"] = "Interface\\Icons\\Ability_Throw"
ICON_CACHE["horion de l'ombre"] = "Interface\\Icons\\Spell_Shadow_Shadowbolt"
ICON_CACHE["explosion de lave"] = "Interface\\Icons\\Spell_Fire_Volcano"
ICON_CACHE["bouclier de lave"] = "Interface\\Icons\\Spell_Fire_Immolation"
ICON_CACHE["fleur de feu"] = "Interface\\Icons\\Spell_Fire_Fireball02"
ICON_CACHE["bombe"] = "Interface\\Icons\\Spell_Fire_Selfdestruct"
ICON_CACHE["explosion"] = "Interface\\Icons\\Spell_Fire_Selfdestruct"
ICON_CACHE["colère de ragnaros"] = "Interface\\Icons\\Spell_Fire_Soulburn"
ICON_CACHE["choc"] = "Interface\\Icons\\Ability_Smash"
ICON_CACHE["secousse violente"] = "Interface\\Icons\\Ability_Smash"
ICON_CACHE["nova de feu"] = "Interface\\Icons\\Spell_Fire_SealOfFire"
ICON_CACHE["marque de détonation"] = "Interface\\Icons\\spell_fire_selfdestruct"
ICON_CACHE["cône de feu"] = "Interface\\Icons\\Spell_Fire_WindsOfWoe"
ICON_CACHE["volée de boules de feu"] = "Interface\\Icons\\Spell_Fire_Flamebolt"
ICON_CACHE["vague de foudre"] = "Interface\\Icons\\Spell_Nature_Chainlightning"
ICON_CACHE["coup de queue"] = "Interface\\Icons\\Inv_Misc_MonsterScales_05"
ICON_CACHE["exploiter faiblesse"] = "Interface\\Icons\\ability_backstab"
ICON_CACHE["lancer feu liquide"] = "Interface\\Icons\\spell_fire_meteorstorm"
ICON_CACHE["poing de force"] = "Interface\\Icons\\inv_gauntlets_31"
ICON_CACHE["uppercut"] = "Interface\\Icons\\inv_gauntlets_31"
ICON_CACHE["sangsue"] = "Interface\\Icons\\ability_racial_cannibalize"
ICON_CACHE["choc de feu"] = "Interface\\Icons\\spell_fire_immolation"
ICON_CACHE["malédiction du sang"] = "Interface\\Icons\\spell_shadow_ritualofsacrifice"
ICON_CACHE["brasier"] = "Interface\\Icons\\spell_fire_incinerate"
ICON_CACHE["météore"] = "Interface\\Icons\\spell_fire_meteorstorm"
ICON_CACHE["jet de poison"] = "Interface\\Icons\\spell_nature_corrosivebreath"
ICON_CACHE["explosion d'ombre"] = "Interface\\Icons\\spell_shadow_antishadow"
ICON_CACHE["voile de l'ombre"] = "Interface\\Icons\\spell_shadow_gathershadows"
ICON_CACHE["nuée maléfique"] = "Interface\\Icons\\spell_nature_abolishmagic"
ICON_CACHE["crachat empoisonné"] = "Interface\\Icons\\spell_nature_corrosivebreath"
ICON_CACHE["piétiner"] = "Interface\\Icons\\spell_nature_natureswrath"
ICON_CACHE["spores mutées"] = "Interface\\Icons\\spell_nature_abolishmagic"
ICON_CACHE["heurtoir du vomisseur de bile"] = "Interface\\Icons\\ability_devour"
ICON_CACHE["eruption"] = "Interface\\Icons\\spell_fire_volcano"
ICON_CACHE["tempête de mana"] = "Interface\\Icons\\spell_frost_icestorm"
ICON_CACHE["frappe sacrée"] = "Interface\\Icons\\ability_thunderbolt"
ICON_CACHE["grondement du tonnerre"] = "Interface\\Icons\\spell_nature_thunderclap"
ICON_CACHE["déflagration des arcanes"] = "Interface\\Icons\\spell_shadow_deathpact"
ICON_CACHE["nuage pestilentiel"] = "Interface\\Icons\\spell_nature_abolishmagic"
ICON_CACHE["régénération de mana"] = "Interface\\Icons\\spell_nature_manaregentotem"
ICON_CACHE["potion de soins"] = "Interface\\Icons\\inv_potion_54"
ICON_CACHE["restauration du mana"] = "Interface\\Icons\\inv_potion_76"
ICON_CACHE["rune démoniaque"] = "Interface\\Icons\\inv_misc_rune_04"
ICON_CACHE["rune ténébreuse"] = "Interface\\Icons\\spell_shadow_sealofkings"
ICON_CACHE["absorption de magie"] = "Interface\\Icons\\spell_nature_astralrecalgroup"
ICON_CACHE["gel"] = "Interface\\Icons\\spell_frost_glacier"
ICON_CACHE["lésions cérébrales"] = "Interface\\Icons\\ability_gouge"
ICON_CACHE["décharge électrique"] = "Interface\\Icons\\spell_lightning_lightningbolt01"
ICON_CACHE["foudre"] = "Interface\\Icons\\spell_lightning_lightningbolt01"
ICON_CACHE["enchantement flamboyant"] = "Interface\\Icons\\spell_fire_selfdestruct"
ICON_CACHE["pierre de soins supérieure"] = "Interface\\Icons\\inv_stone_04"
ICON_CACHE["pierre de soins mineure"] = "Interface\\Icons\\inv_stone_04"
ICON_CACHE["pierre de soins inférieure"] = "Interface\\Icons\\inv_stone_04"
ICON_CACHE["pierre de soins majeure"] = "Interface\\Icons\\inv_stone_04"
ICON_CACHE["pierre de soins"] = "Interface\\Icons\\inv_stone_04"
ICON_CACHE["capturer un esprit"] = "Interface\\Icons\\Spell_Shadow_Haunting"
ICON_CACHE["récupération du mana"] = "Interface\\Icons\\inv_misc_gem_ruby_01"
ICON_CACHE["maître des éléments"] = "Interface\\Icons\\spell_fire_masterofelements"
ICON_CACHE["tribu zandalar"] = "Interface\\Icons\\inv_misc_coin_08"
ICON_CACHE["crachat venimeux"] = "Interface\\Icons\\spell_nature_corrosivebreath"
ICON_CACHE["morsure fatale"] = "Interface\\Icons\\ability_backstab"
ICON_CACHE["explosion sonore"] = "Interface\\Icons\\spell_shadow_teleport"
ICON_CACHE["flammes"] = "Interface\\Icons\\spell_fire_fire"
ICON_CACHE["effet inferno"] = "Interface\\Icons\\spell_fire_incinerate"
ICON_CACHE["flametongue attack"] = "Interface\\Icons\\Spell_Nature_GuardianWard"
ICON_CACHE["stratholme holy water"] = "Interface\\Icons\\Inv_Potion_75"
-- 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 = {}
-- 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
damageTypeFontColors = {
[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]
end
-- Number of font strings.
local NUM_FONT_STRINGS = 20;
-- Animation values for incoming and outgoing events.
local MIN_VERTICAL_SPACING = 8;
-- How long to wait before showing events so that merges may happen.
local MERGE_DELAY_TIME = 0.3;
-- How long to show stickies before they are faded.
local STICKY_FADE_IN_TIME = 0.15;
local STICKY_DISPLAY_TIME = 2;
local STICKY_FADE_OUT_TIME = 0.5;
-- The animation speed.
local ANIMATION_SPEED = 0.02;
-- Scroll directions
local SCROLL_DIRECTION_UP = 1;
local SCROLL_DIRECTION_DOWN = 2;
-- Animation Styles
local ANIMATION_STYLE_STRAIGHT = 1;
local ANIMATION_STYLE_LEFT_PARABOLA = 2;
local ANIMATION_STYLE_RIGHT_PARABOLA = 3;
-- Max number of combo points.
local MAX_COMBO_POINTS = 5;
-- Pre-built event type string lookup tables (eliminates string concatenation in CombatEventsHandler).
local DIRECTION_SCROLLAREA_KEY = {
[MikCEH.DIRECTIONTYPE_PLAYER_INCOMING] = "Incoming",
[MikCEH.DIRECTIONTYPE_PLAYER_OUTGOING] = "Outgoing",
[MikCEH.DIRECTIONTYPE_PET_OUTGOING] = "Outgoing",
[MikCEH.DIRECTIONTYPE_PET_INCOMING] = "Incoming",
};
local DIRECTION_PREFIX = {
[MikCEH.DIRECTIONTYPE_PLAYER_INCOMING] = "MSBT_EVENTTYPE_INCOMING_",
[MikCEH.DIRECTIONTYPE_PLAYER_OUTGOING] = "MSBT_EVENTTYPE_OUTGOING_",
[MikCEH.DIRECTIONTYPE_PET_OUTGOING] = "MSBT_EVENTTYPE_OUTGOING_PET_",
[MikCEH.DIRECTIONTYPE_PET_INCOMING] = "MSBT_EVENTTYPE_INCOMING_PET_",
};
-- Action type → suffix string for damage events.
local ACTION_SUFFIX = {
[MikCEH.ACTIONTYPE_MISS] = "MISS",
[MikCEH.ACTIONTYPE_DODGE] = "DODGE",
[MikCEH.ACTIONTYPE_PARRY] = "PARRY",
[MikCEH.ACTIONTYPE_BLOCK] = "BLOCK",
[MikCEH.ACTIONTYPE_RESIST] = "RESIST",
[MikCEH.ACTIONTYPE_ABSORB] = "ABSORB",
[MikCEH.ACTIONTYPE_IMMUNE] = "IMMUNE",
[MikCEH.ACTIONTYPE_EVADE] = "EVADE",
[MikCEH.ACTIONTYPE_REFLECT] = "REFLECT",
};
-- Environmental action type → localized effect name.
local ENVIRONMENTAL_NAMES = {
[MikCEH.ACTIONTYPE_DROWNING] = MikSBT.MSG_ENVIRONMENTAL_DROWNING,
[MikCEH.ACTIONTYPE_FALLING] = MikSBT.MSG_ENVIRONMENTAL_FALLING,
[MikCEH.ACTIONTYPE_FATIGUE] = MikSBT.MSG_ENVIRONMENTAL_FATIGUE,
[MikCEH.ACTIONTYPE_FIRE] = MikSBT.MSG_ENVIRONMENTAL_FIRE,
[MikCEH.ACTIONTYPE_LAVA] = MikSBT.MSG_ENVIRONMENTAL_LAVA,
[MikCEH.ACTIONTYPE_SLIME] = MikSBT.MSG_ENVIRONMENTAL_SLIME,
};
-- Class name → color prefix string for unit name coloring.
local CLASS_COLORS = {
PALADIN = "|cffF58CBA|h",
DRUID = "|cffFF7D0A|h",
HUNTER = "|cffABD473|h",
MAGE = "|cff69CCF0|h",
PRIEST = "|cffFFFFFF|h",
ROGUE = "|cffFFF569|h",
SHAMAN = "|cff0070DE|h",
WARLOCK = "|cff9482C9|h",
WARRIOR = "|cffC79C6E|h",
};
-- Notification type → suffix string.
local NOTIFICATION_SUFFIX = {
[MikCEH.NOTIFICATIONTYPE_DEBUFF] = "DEBUFF",
[MikCEH.NOTIFICATIONTYPE_BUFF] = "BUFF",
[MikCEH.NOTIFICATIONTYPE_ITEM_BUFF] = "ITEM_BUFF",
[MikCEH.NOTIFICATIONTYPE_BUFF_FADE] = "BUFF_FADE",
[MikCEH.NOTIFICATIONTYPE_COMBAT_ENTER] = "COMBAT_ENTER",
[MikCEH.NOTIFICATIONTYPE_COMBAT_LEAVE] = "COMBAT_LEAVE",
[MikCEH.NOTIFICATIONTYPE_POWER_GAIN] = "POWER_GAIN",
[MikCEH.NOTIFICATIONTYPE_POWER_LOSS] = "POWER_LOSS",
[MikCEH.NOTIFICATIONTYPE_CP_GAIN] = "CP_GAIN",
[MikCEH.NOTIFICATIONTYPE_HONOR_GAIN] = "HONOR_GAIN",
[MikCEH.NOTIFICATIONTYPE_REP_GAIN] = "REP_GAIN",
[MikCEH.NOTIFICATIONTYPE_REP_LOSS] = "REP_LOSS",
[MikCEH.NOTIFICATIONTYPE_SKILL_GAIN] = "SKILL_GAIN",
[MikCEH.NOTIFICATIONTYPE_EXPERIENCE_GAIN] = "EXPERIENCE_GAIN",
[MikCEH.NOTIFICATIONTYPE_PC_KILLING_BLOW] = "PC_KILLING_BLOW",
[MikCEH.NOTIFICATIONTYPE_NPC_KILLING_BLOW] = "NPC_KILLING_BLOW",
};
-- Pre-build all possible event type strings to avoid runtime concatenation.
-- dmgEventStr[dirType][suffix] for non-spell damage, dmgEventStrSpell[dirType][suffix] for spell damage.
local dmgEventStr = {};
local dmgEventStrSpell = {};
local healEventStr = {};
local notifEventStr = {};
do
local suffixes = {"DAMAGE", "DOT", "MISS", "DODGE", "PARRY", "BLOCK", "RESIST", "ABSORB", "IMMUNE", "EVADE", "REFLECT", "ENVIRONMENTAL"};
for dirType, prefix in pairs(DIRECTION_PREFIX) do
dmgEventStr[dirType] = {};
dmgEventStrSpell[dirType] = {};
for _, suffix in ipairs(suffixes) do
dmgEventStr[dirType][suffix] = prefix .. suffix;
dmgEventStrSpell[dirType][suffix] = prefix .. "SPELL_" .. suffix;
end
healEventStr[dirType] = {};
healEventStr[dirType]["HEAL"] = prefix .. "HEAL";
healEventStr[dirType]["HOT"] = prefix .. "HOT";
end
for notifType, suffix in pairs(NOTIFICATION_SUFFIX) do
notifEventStr[notifType] = "MSBT_EVENTTYPE_NOTIFICATION_" .. suffix;
end
notifEventStr["CP_FULL"] = "MSBT_EVENTTYPE_NOTIFICATION_CP_FULL";
end
-------------------------------------------------------------------------------------
-- Private variables.
-------------------------------------------------------------------------------------
-- Holds whether or not debugging is active.
local debugMode = false;
-- Holds the events the mod is interested in.
local listenEvents = {};
-- Holds data for each scroll area.
local scrollAreas = {};
-- Holds arrays of unmerged events, merged events to be displayed and the time since
-- the last merge for incoming and outgoing events.
local animationMergeData = {
["Incoming"] = {["TimeSinceMerge"] = 0, UnmergedEvents={}, MergedEvents={}},
["Outgoing"] = {["TimeSinceMerge"] = 0, UnmergedEvents={}, MergedEvents={}},
};
-- Holds whether or not a merge is still being processed.
local stillMerging = false;
-- Temp arrays for repositioning animation display data to avoid overlaps.
local activeNonStickies = {};
local activeStickies = {};
-- Create a new table recycler object that holds up to 40 table references
-- in primary cache.
local eventsRecycler = MikTRO:NewRecyclerObject(40);
-------------------------------------------------------------------------------------
-- Printing functions.
-------------------------------------------------------------------------------------
-- **********************************************************************************
-- Prints out the passed message to the default chat frame.
-- **********************************************************************************
function MikSBT.Print(msg, r, g, b)
-- Add the message to the default chat frame.
DEFAULT_CHAT_FRAME:AddMessage("MSBT: " .. msg, r, g, b);
end
-- **********************************************************************************
-- Prints out the passed message to the default chat frame.
-- **********************************************************************************
function MikSBT.PrintDebug(msg, r, g, b)
-- Check if debugging is active.
if (debugMode) then
-- Add the message to the default chat frame.
DEFAULT_CHAT_FRAME:AddMessage(msg, r, g, b);
end
end
-------------------------------------------------------------------------------------
-- Core event handlers.
-------------------------------------------------------------------------------------
-- **********************************************************************************
-- Registers all of the core events the mod is interested in.
-- **********************************************************************************
function MikSBT.RegisterEvents()
-- Register the events we are interested in receiving for shaman.
for k, v in listenEvents do
MSBTEventFrame:RegisterEvent(v);
end
end
-- **********************************************************************************
-- Unregisters all of the events the mod registered for.
-- **********************************************************************************
function MikSBT.UnregisterEvents()
for k, v in listenEvents do
MSBTEventFrame:UnregisterEvent(v);
end
end
-- **********************************************************************************
-- Called when the mod is loaded.
-- **********************************************************************************
function MikSBT.OnLoad()
-- Register for the ADDON_LOADED event.
MSBTEventFrame:RegisterEvent("ADDON_LOADED");
MSBTEventFrame:RegisterEvent("PLAYER_ENTERING_WORLD");
end
-- **********************************************************************************
-- Called when the events we registered for occur.
-- **********************************************************************************
function MikSBT.OnEvent()
-- When an addon is loaded.
if (event == "ADDON_LOADED") then
-- Set Game Damage font
if MikSBT_Save and MikSBT_Save.Profiles[MikSBT_Save.CurrentProfile].BlizzardFontSettings then
DAMAGE_TEXT_FONT = MikSBT.AVAILABLE_FONTS[MikSBT_Save.Profiles[MikSBT_Save.CurrentProfile].BlizzardFontSettings.Normal.FontIndex].Path or "Fonts\\FRIZQT__.TTF"
end
-- Make sure it's this addon.
if (arg1 == MikSBT.MOD_NAME) then
-- Register for the events the helper is interested in receiving.
MikSBT.RegisterEvents();
-- Initialize the mod.
MikSBT.Init();
end
end
if (event == "PLAYER_ENTERING_WORLD") then
this:UnregisterEvent("ADDON_LOADED");
this:UnregisterEvent("PLAYER_ENTERING_WORLD");
end
end
-- **********************************************************************************
-- Called when the core events frame is updated.
-- **********************************************************************************
function MikSBT.OnUpdate()
-- Loop through all of the merge data tables.
for _, mergeData in animationMergeData do
-- Add the elapsed time since the last merge.
mergeData.TimeSinceMerge = mergeData.TimeSinceMerge + arg1;
-- Once enough time has passed and all previous merges are done.
if ((mergeData.TimeSinceMerge >= MERGE_DELAY_TIME) and not stillMerging) then
-- Set the still merging flag.
stillMerging = true;
-- Merge like events.
local numEvents = table_getn(mergeData.UnmergedEvents);
MikSBT.MergeEvents(mergeData, numEvents);
-- Add the merged animation events to the animation system.
for _, animationEvent in mergeData.MergedEvents do
MikSBT.AddAnimation(animationEvent);
end
-- Clear the merged animation events array.
local numMergedEvents = table_getn(mergeData.MergedEvents);
for i = 1, numMergedEvents do
mergeData.MergedEvents[i] = nil;
end
table_setn(mergeData.MergedEvents, 0);
-- Reset the last merged time.
mergeData.TimeSinceMerge = 0;
-- Clear the still merging flag.
stillMerging = false;
end
end
end
-- **********************************************************************************
-- Called when the mod's animation frames are updated.
-- **********************************************************************************
function MikSBT.OnUpdateAnimationFrame(frame)
local scrollArea;
-- Get the frame's name.
local frameName = frame:GetName();
if (frameName == scrollAreas.Incoming.AssociatedFrame) then
scrollArea = scrollAreas.Incoming;
elseif (frameName == scrollAreas.Outgoing.AssociatedFrame) then
scrollArea = scrollAreas.Outgoing;
elseif (frameName == scrollAreas.Notification.AssociatedFrame) then
scrollArea = scrollAreas.Notification;
end
-- Flag for whether or not all the animations are inactive.
local allInactive = true;
-- Loop through all possible animation display objects for the scroll area.
for _, animDisplayInfo in scrollArea.AnimationDisplayArray do
-- Check if the animation is active.
if (animDisplayInfo.IsActive) then
-- Do the animation and clear the all inactive flag.
MikSBT.DoAnimation(animDisplayInfo, scrollArea);
allInactive = false;
end
end
-- Check if none of the animations are active and the events frame is visible. If so, hide it so
-- the OnUpdate events stop firing. This is done to keep the number of OnUpdate events down to a minimum for
-- better performance.
if (allInactive and frame:IsVisible()) then
frame:Hide();
end
end
-- **********************************************************************************
-- Called when the mod is fully loaded (variables loaded).
-- **********************************************************************************
function MikSBT.Init()
-- Register slash commands
SLASH_MSBT1 = MikSBT.COMMAND;
SlashCmdList["MSBT"] = function (params)
MikSBT.CommandHandler(params);
end
-- Check if there are no saved variables.
if (not MikSBT_Save or not MikSBT_Save.Profiles) then
MikSBT_Save = {};
MikSBT_Save.UserDisabled = false;
MikSBT_Save.CurrentProfile = MikSBT.DEFAULT_PROFILE_NAME;
MikSBT_Save.Profiles = {};
MikSBT_Save.Profiles[MikSBT_Save.CurrentProfile] = {};
-- Set all the options to their defaults.
MikSBT.ResetProfile(MikSBT_Save.CurrentProfile);
end
-- Updates profiles created by older versions.
MikSBT.UpdateProfiles();
-- Set the current profile.
MikSBT.CurrentProfile = MikSBT_Save.Profiles[MikSBT_Save.CurrentProfile];
-- Sets up all of the animation scroll areas.
MikSBT.SetupAnimationScrollAreas();
-- Reposition the animation frames to the stored offsets.
MikSBT.RepositionAnimationFrames();
-- 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
-- **********************************************************************************
-- Sets up all of the animation scroll areas.
-- **********************************************************************************
function MikSBT.SetupAnimationScrollAreas()
-- Setup a table for data for each scroll area.
scrollAreas.Incoming = {};
scrollAreas.Outgoing = {};
scrollAreas.Notification = {};
-- Setup the associated frames.
scrollAreas.Incoming.AssociatedFrame = "MSBTFrameIncoming";
scrollAreas.Outgoing.AssociatedFrame = "MSBTFrameOutgoing";
scrollAreas.Notification.AssociatedFrame = "MSBTFrameNotification";
-- Setup the display settings.
scrollAreas.Incoming.DisplaySettings = MikSBT.CurrentProfile.DisplaySettings.Incoming;
scrollAreas.Outgoing.DisplaySettings = MikSBT.CurrentProfile.DisplaySettings.Outgoing;
scrollAreas.Notification.DisplaySettings = MikSBT.CurrentProfile.DisplaySettings.Notification;
-- Loop through the scroll areas.
for _, scrollArea in scrollAreas do
-- Initialize the next available animation display object.
scrollArea.NextAnimationDisplay = 1;
-- Initialize the next available sticky animation display object.
scrollArea.FirstSticky = 11;
scrollArea.NextSticky = scrollArea.FirstSticky;
-- Create arrays for the animation display info.
scrollArea.AnimationDisplayArray = {};
-- Associate the appropriate font string object for each animation display object.
for x = 1, NUM_FONT_STRINGS do
scrollArea.AnimationDisplayArray[x] = {};
scrollArea.AnimationDisplayArray[x].FontObject = getglobal(scrollArea.AssociatedFrame .. "Text" .. x);
scrollArea.AnimationDisplayArray[x].LastUpdated = 0;
end
end
end
-- **********************************************************************************
-- Repositions the animation frames based on the stored offsets.
-- **********************************************************************************
function MikSBT.RepositionAnimationFrames()
-- Loop through each scroll area and reposition its associated frame.
for _, scrollArea in scrollAreas do
getglobal(scrollArea.AssociatedFrame):SetPoint("BOTTOM", "UIParent", "CENTER", scrollArea.DisplaySettings.FrameOffsets.x, scrollArea.DisplaySettings.FrameOffsets.y);
end
end
-- **********************************************************************************
-- Receive combat events from Mik's combat event helper.
-- **********************************************************************************
function MikSBT.CombatEventsHandler(combatEvent)
-- Get a recycled table from the events recycler object.
local animationEvent = eventsRecycler:AcquireTable();
-- Look up the scroll area and event type string via pre-built tables (no string concatenation).
local dirType = combatEvent.DirectionType;
local scrollAreaKey = DIRECTION_SCROLLAREA_KEY[dirType];
if (scrollAreaKey) then
animationEvent.ScrollArea = scrollAreas[scrollAreaKey];
end
local eventTypeString;
-- Check if it's a damage event.
if (combatEvent.EventType == MikCEH.EVENTTYPE_DAMAGE) then
local actionType = combatEvent.ActionType;
-- Check for environmental damage (sets EffectName as side effect).
local envName = ENVIRONMENTAL_NAMES[actionType];
if (envName) then
combatEvent.EffectName = envName;
eventTypeString = dmgEventStr[dirType] and dmgEventStr[dirType]["ENVIRONMENTAL"];
else
-- Determine the action suffix.
local suffix;
if (actionType == MikCEH.ACTIONTYPE_HIT) then
suffix = (combatEvent.HitType == MikCEH.HITTYPE_OVER_TIME) and "DOT" or "DAMAGE";
else
suffix = ACTION_SUFFIX[actionType];
end
-- Use spell or non-spell variant based on whether there is an effect name.
if (suffix) then
if (combatEvent.EffectName ~= nil) then
eventTypeString = dmgEventStrSpell[dirType] and dmgEventStrSpell[dirType][suffix];
else
eventTypeString = dmgEventStr[dirType] and dmgEventStr[dirType][suffix];
end
end
end
-- Heal event.
elseif (combatEvent.EventType == MikCEH.EVENTTYPE_HEAL) then
local suffix = (combatEvent.HealType == MikCEH.HEALTYPE_OVER_TIME) and "HOT" or "HEAL";
eventTypeString = healEventStr[dirType] and healEventStr[dirType][suffix];
-- Notification event.
elseif (combatEvent.EventType == MikCEH.EVENTTYPE_NOTIFICATION) then
animationEvent.ScrollArea = scrollAreas.Notification;
local notifType = combatEvent.NotificationType;
-- Special case: combo points at max.
if (notifType == MikCEH.NOTIFICATIONTYPE_CP_GAIN and combatEvent.Amount == MAX_COMBO_POINTS) then
eventTypeString = notifEventStr["CP_FULL"];
else
eventTypeString = notifEventStr[notifType];
-- Fallback for unknown notification types.
if (not eventTypeString and notifType ~= nil) then
eventTypeString = "MSBT_EVENTTYPE_NOTIFICATION_" .. notifType;
end
end
end
-- Check if the event is a crit and set the crit field appropriately
if (combatEvent.HitType == MikCEH.HITTYPE_CRIT or combatEvent.HealType == MikCEH.HEALTYPE_CRIT) then
animationEvent.IsCrit = true;
end
-- Copy the amount, effect name, and player/enemy name from the passed combat event data.
-- if combatEvent.Amount then
-- animationEvent.Amount = combatEvent.Amount * 420;
-- else
animationEvent.Amount = combatEvent.Amount;
-- 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
animationEvent.DamageType = MikCEH.GetDamageTypeString(combatEvent.DamageType);
end
-- 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";
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";
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";
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);
end
elseif (combatEvent.PartialActionType == MikCEH.PARTIALACTIONTYPE_CRUSHING) then
if (CRUSHING_TRAILER) then
animationEvent.PartialEffectText = "\124cff7D0000\124h"..CRUSHING_TRAILER.."\124h\124r";
end
elseif (combatEvent.PartialActionType == MikCEH.PARTIALACTIONTYPE_GLANCING) then
if (GLANCING_TRAILER) then
animationEvent.PartialEffectText = "\124cffFA0000\124h"..GLANCING_TRAILER.."\124h\124r";
end
elseif (combatEvent.PartialActionType == MikCEH.PARTIALACTIONTYPE_OVERHEAL) then
animationEvent.OverhealAmount = combatEvent.PartialAmount;
end
-- Check if there is profile data for the event type.
if (MikSBT.CurrentProfile.EventSettings[eventTypeString]) then
-- Set the event type, the settings for it, its unformatted text and dispatch it to the animation system.
animationEvent.EventType = eventTypeString;
animationEvent.EventSettings = MikSBT.CurrentProfile.EventSettings[animationEvent.EventType];
animationEvent.UnformattedText = animationEvent.EventSettings.Message;
-- Dispatch the event to the animation system.
MikSBT.DispatchAnimationEvent(animationEvent);
else
-- Print an error message and reclaim the animation event table to the table recycler.
MikSBT.Print("No profile data found for " .. eventTypeString, 1, 0, 0)
eventsRecycler:ReclaimTable(animationEvent);
end
end
-- **********************************************************************************
-- Receive trigger events from Mik's trigger helper.
-- **********************************************************************************
function MikSBT.TriggerHandler(triggerEvent)
-- Get a recycled table from the events recycler object.
local animationEvent = eventsRecycler:AcquireTable();
-- Check if the trigger require a stance
if (MikSBT.CurrentProfile.Triggers[triggerEvent.TriggerKey].TriggerSettings.Stance) then
local _,_,isActive = GetShapeshiftFormInfo(MikSBT.CurrentProfile.Triggers[triggerEvent.TriggerKey].TriggerSettings.Stance)
if not isActive then
-- Reclaim the animation event table to the table recycler.
eventsRecycler:ReclaimTable(animationEvent);
return
end
end
-- Check if there is profile data for the trigger.
if (MikSBT.CurrentProfile.Triggers[triggerEvent.TriggerKey].EventSettings) then
-- Set the scroll area, event settings, and output text.
animationEvent.ScrollArea = scrollAreas.Notification;
animationEvent.EventSettings = MikSBT.CurrentProfile.Triggers[triggerEvent.TriggerKey].EventSettings;
MikSBT.FormatTriggerText(animationEvent, triggerEvent);
if (MikSBT.CurrentProfile.Triggers[triggerEvent.TriggerKey].Texture) then
animationEvent.Texture = MikSBT.CurrentProfile.Triggers[triggerEvent.TriggerKey].Texture
end
-- Dispatch the event to the animation system.
MikSBT.DispatchAnimationEvent(animationEvent);
else
-- Print an error message and reclaim the animation event table to the table recycler.
MikSBT.Print("No profile data found for trigger key " .. triggerEvent.TriggerKey, 1, 0, 0);
eventsRecycler:ReclaimTable(animationEvent);
end
end
-------------------------------------------------------------------------------------
-- Utility functions.
-------------------------------------------------------------------------------------
-- **********************************************************************************
-- This function copies a table.
-- **********************************************************************************
function MikSBT.CopyTable(srcTable)
-- Create a new table.
local newTable = {};
-- Loop through all of the entires in the table.
for key, value in srcTable do
-- Check if it's a nested table.
if (type(value) == "table") then
-- Recursively call the function to copy the nested table.
value = MikSBT.CopyTable(value);
end
-- Make a copy of the value into the new table.
newTable[key] = value;
end
-- Return the new table.
return newTable;
end
-- **********************************************************************************
-- Merges like animation events.
-- **********************************************************************************
function MikSBT.MergeEvents(mergeData, numEvents)
-- Hold whether or not the event was merged.
local eventMerged = false;
-- Don't attempt to merge any more events than were available when the function was called since
-- more events may get added while the merge is taking place.
for x = 1, numEvents do
-- Loop through all of the events in the merged events array.
for _, animationEvent in mergeData.MergedEvents do
local unmergedEventMatched = false;
-- Check if the event types match and the effect names are the same.
if ((mergeData.UnmergedEvents[x].EventType == animationEvent.EventType) and
(mergeData.UnmergedEvents[x].EffectName == animationEvent.EffectName)) then
-- Check if there is no effect name
if (mergeData.UnmergedEvents[x].EffectName == nil) then
-- Check if the affected unit name is the same.
if ((mergeData.UnmergedEvents[x].Name == animationEvent.Name) and mergeData.UnmergedEvents[x].Name ~= nil) then
-- Do Merge.
eventMerged = true;
unmergedEventMatched = true;
end -- Same name.
-- There is an effect name.
else
-- Check if there is a name and replace it with the multiple targets string.
if (animationEvent.Name ~= nil) then
animationEvent.Name = MikSBT.MSG_MULTIPLE_TARGETS;
end
-- Do Merge.
eventMerged = true;
unmergedEventMatched = true;
end -- Effect name?
end -- Event types match and effect names the same.
-- Check if the event should be merged.
if (unmergedEventMatched) then
-- Set the event merged flag for the event being merged.
mergeData.UnmergedEvents[x].EventMerged = true;
-- If the events have an amount then total them.
if (animationEvent.Amount ~= nil and mergeData.UnmergedEvents[x].Amount ~= nil) then
animationEvent.Amount = tonumber(animationEvent.Amount, 10)
if animationEvent.Amount and type(animationEvent.Amount) == "number" and mergeData.UnmergedEvents[x].Amount then
animationEvent.Amount = animationEvent.Amount + mergeData.UnmergedEvents[x].Amount;
end
-- animationEvent.Amount = animationEvent.Amount + mergeData.UnmergedEvents[x].Amount;
end
-- Check if there is an overheal amount and total it.
if (mergeData.UnmergedEvents[x].OverhealAmount ~= nil) then
animationEvent.OverhealAmount = (animationEvent.OverhealAmount or 0) + mergeData.UnmergedEvents[x].OverhealAmount;
end
-- Clear any partial effect text.
animationEvent.PartialEffectText = nil;
-- Increment the number of merged events.
animationEvent.NumMerged = animationEvent.NumMerged + 1;
-- Check if the event being merged is a crit and increment the number of crits.
if (mergeData.UnmergedEvents[x].IsCrit) then
animationEvent.NumCrits = animationEvent.NumCrits + 1;
end
-- Make sure the merged event is not flagged as a crit.
animationEvent.IsCrit = false;
end -- Do Merge.
end -- Loop through all merged events.
-- If the event wasn't merged add it to the end of the merged events array.
if (not eventMerged) then
local animationEvent = mergeData.UnmergedEvents[x];
animationEvent.NumMerged = 0;
-- Check if the event is a crit and set the number of crits appropriately.
if (animationEvent.IsCrit) then
animationEvent.NumCrits = 1;
else
animationEvent.NumCrits = 0;
end
-- Add the animation event to the end of the merged events array.
table_insert(mergeData.MergedEvents, animationEvent);
end
-- Reset the event merged flag.
eventMerged = false;
end
-- Loop through each event in the merged events array.
for _, animationEvent in mergeData.MergedEvents do
-- Check if there were any events merged.
if (animationEvent.NumMerged > 0) then
-- Create trailer text with the number of merged events.
local trailerText = " [" .. 1 + animationEvent.NumMerged.. " " .. MikSBT.MSG_HITS;
-- Check if there were any crits merged.
if (animationEvent.NumCrits > 1) then
trailerText = trailerText .. ", " .. animationEvent.NumCrits .. " " .. MikSBT.MSG_CRITS;
elseif (animationEvent.NumCrits == 1) then
trailerText = trailerText .. ", " .. animationEvent.NumCrits .. " " .. MikSBT.MSG_CRIT;
end
-- Close the merge string.
trailerText = trailerText .. "]";
-- Set the animation event's merge trailer text field.
animationEvent.MergeTrailerText = trailerText;
end
end
-- Remove the processed animation events from unmerged events queue.
for x = 1, numEvents do
-- Check if the event was merged into a different animation event.
if (mergeData.UnmergedEvents[x] and mergeData.UnmergedEvents[x].EventMerged) then
-- Reclaim the animation event table to the events recycler.
eventsRecycler:ReclaimTable(mergeData.UnmergedEvents[x]);
end
mergeData.UnmergedEvents[x] = nil;
end
table_setn(mergeData.UnmergedEvents, 0);
end
-- **********************************************************************************
-- This function gets the appropriate font size for an event.
-- **********************************************************************************
function MikSBT.GetFontSize(scrollArea, eventSettings, isCrit)
-- Holds the font sizes for the event level.
local eventFontSizeNormal = 0;
local eventFontSizeCrit = 0;
-- Check if there are event settings.
if (eventSettings ~= nil) then
-- Get the font sizes for the event type.
eventFontSizeNormal = eventSettings.FontSettings.Normal.FontSize;
-- Check if there are crit font settings for the event.
if (eventSettings.FontSettings.Crit ~= nil) then
eventFontSizeCrit = eventSettings.FontSettings.Crit.FontSize;
end
end
-- Get the font sizes for the scroll area and master.
local scrollAreaFontSizeNormal = scrollArea.DisplaySettings.FontSettings.Normal.FontSize;
local scrollAreaFontSizeCrit = scrollArea.DisplaySettings.FontSettings.Crit.FontSize;
local masterFontSizeNormal = MikSBT.CurrentProfile.MasterFontSettings.Normal.FontSize;
local masterFontSizeCrit = MikSBT.CurrentProfile.MasterFontSettings.Crit.FontSize;
-- Get the normal font size for the event.
local fontSize = eventFontSizeNormal;
-- Check if the font size is inherited from the scroll area font size.
if (fontSize == 0) then
fontSize = scrollAreaFontSizeNormal;
end
-- Check if the font size for the scroll area is inherited from the master font size.
if (fontSize == 0) then
fontSize = masterFontSizeNormal;
end
-- Check if the event is a crit.
if (isCrit) then
-- Get the crit font size for the event.
fontSize = eventFontSizeCrit;
-- Check if the font size is inherited from the scroll area font size.
if (fontSize == 0) then
fontSize = scrollAreaFontSizeCrit;
end
-- Check if the font size for the scroll area is inherited from the master font size.
if (fontSize == 0) then
fontSize = masterFontSizeCrit;
end
end
-- Return the font size.
return fontSize;
end
-- **********************************************************************************
-- This function gets the appropriate font path for an event.
-- **********************************************************************************
function MikSBT.GetFontPath(scrollArea, eventSettings, isCrit)
-- Holds the font indices for the event level.
local eventFontIndexNormal = 0;
local eventFontIndexCrit = 0;
-- Check if there are event settings.
if (eventSettings ~= nil) then
-- Get the font indices for the event type.
eventFontIndexNormal = eventSettings.FontSettings.Normal.FontIndex;
-- Check if there are crit font settings for the event.
if (eventSettings.FontSettings.Crit ~= nil) then
eventFontIndexCrit = eventSettings.FontSettings.Crit.FontIndex;
end
end
-- Get the font indices for the scroll area and master.
local scrollAreaFontIndexNormal = scrollArea.DisplaySettings.FontSettings.Normal.FontIndex;
local scrollAreaFontIndexCrit = scrollArea.DisplaySettings.FontSettings.Crit.FontIndex;
local masterFontIndexNormal = MikSBT.CurrentProfile.MasterFontSettings.Normal.FontIndex;
local masterFontIndexCrit = MikSBT.CurrentProfile.MasterFontSettings.Crit.FontIndex;
-- Get the normal font index for the event.
local fontIndex = eventFontIndexNormal;
-- Check if the font index is inherited from the scroll area font index.
if (fontIndex == 0) then
fontIndex = scrollAreaFontIndexNormal;
end
-- Check if the font index for the scroll area is inherited from the master font index.
if (fontIndex == 0) then
fontIndex = masterFontIndexNormal;
end
-- Check if the event is a crit.
if (isCrit) then
-- Get the crit font index for the event.
fontIndex = eventFontIndexCrit;
-- Check if the font index is inherited from the scroll area font index.
if (fontIndex == 0) then
fontIndex = scrollAreaFontIndexCrit;
end
-- Check if the font index for the scroll area is inherited from the master font index.
if (fontIndex == 0) then
fontIndex = masterFontIndexCrit;
end
end
-- Check if the font index is invalid and set it to the default if it is.
if (MikSBT.AVAILABLE_FONTS[fontIndex] == nil) then
fontIndex = 1;
end
-- Return the font path.
return MikSBT.AVAILABLE_FONTS[fontIndex].Path;
end
-- **********************************************************************************
-- This function gets the appropriate font outline style for an event.
-- **********************************************************************************
function MikSBT.GetFontOutline(scrollArea, eventSettings, isCrit)
-- Holds the outline indices for the event level.
local eventOutlineIndexNormal = 0;
local eventOutlineIndexCrit = 0;
-- Check if there are event settings.
if (eventSettings ~= nil) then
-- Get the outline indices for the event type.
eventOutlineIndexNormal = eventSettings.FontSettings.Normal.OutlineIndex;
-- Check if there are crit font settings for the event.
if (eventSettings.FontSettings.Crit ~= nil) then
eventOutlineIndexCrit = eventSettings.FontSettings.Crit.OutlineIndex;
end
end
-- Get the outline indices for the scroll area and master.
local scrollAreaOutlineIndexNormal = scrollArea.DisplaySettings.FontSettings.Normal.OutlineIndex;
local scrollAreaOutlineIndexCrit = scrollArea.DisplaySettings.FontSettings.Crit.OutlineIndex;
local masterOutlineIndexNormal = MikSBT.CurrentProfile.MasterFontSettings.Normal.OutlineIndex;
local masterOutlineIndexCrit = MikSBT.CurrentProfile.MasterFontSettings.Crit.OutlineIndex;
-- Get the normal outline index for the event.
local outlineIndex = eventOutlineIndexNormal;
-- Check if the outline index is inherited from the scroll area outline index.
if (outlineIndex == 0) then
outlineIndex = scrollAreaOutlineIndexNormal;
end
-- Check if the outline index for the scroll area is inherited from the master outline index.
if (outlineIndex == 0) then
outlineIndex = masterOutlineIndexNormal;
end
-- Check if the event is a crit.
if (isCrit) then
-- Get the crit outline index for the event.
outlineIndex = eventOutlineIndexCrit;
-- Check if the outline index is inherited from the scroll area outline index.
if (outlineIndex == 0) then
outlineIndex = scrollAreaOutlineIndexCrit;
end
-- Check if the outline index for the scroll area is inherited from the master outline index.
if (outlineIndex == 0) then
outlineIndex = masterOutlineIndexCrit;
end
end
-- Return the outline style.
return MikSBT.AVAILABLE_OUTLINES[outlineIndex].Style;
end
-- **********************************************************************************
-- This function substitutes the codes in the unformatted text of the passed
-- animation event with the values in its fields.
-- **********************************************************************************
function MikSBT.FormatEventText(animationEvent)
local outputString = animationEvent.UnformattedText;
-- Print(animationEvent)
-- Check if there is no unformatted text and bail.
if (outputString == nil) then
return;
end
outputString = string_gsub(outputString, "-%%a", "\124cffff0000\124h-\124h\124r%%a");
if animationEvent.DamageType then
local color = GetDamageTypeColor(animationEvent.DamageType)
if color then
outputString = string_gsub(outputString, "%%a", color .. "%%a\124h\124r")
end
end
-- Substitute amount.
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);
else
outputString = string_gsub(outputString, "%%a", animationEvent.Amount);
end
end
-- Substitute enemy/player names.
local unitID, unitName = MikCEH.GetUnitIDFromName(animationEvent.Name)
animationEvent._unitID = unitID
animationEvent._unitName = unitName
if unitName and unitID and not UnitIsUnit(unitID, "player") then
outputString = string_gsub(outputString, "%%n", unitName);
else
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);
end
-- Substitute damage types.
if (animationEvent.DamageType ~= nil) then
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.
if (animationEvent.PartialEffectText ~= nil and MikSBT.CurrentProfile.ShowPartialEffects) then
outputString = outputString .. animationEvent.PartialEffectText;
end
-- Check if there is merge trailer text and append it.
if (animationEvent.MergeTrailerText ~= nil) then
outputString = outputString .. animationEvent.MergeTrailerText;
end
-- Set the event's output text.
animationEvent.Text = outputString;
end
-- **********************************************************************************
-- This function returns the output message for the passed trigger event with all
-- %1-9 codes substituted with captured data.
-- **********************************************************************************
function MikSBT.FormatTriggerText(animationEvent, triggerEvent)
local outputString = animationEvent.EventSettings.Message;
-- Check if there is no text and bail.
if (outputString == nil) then
return;
end
-- Loop through all of the captures in the trigger event.
for i = 1, triggerEvent.NumCaptures do
outputString = string_gsub(outputString , "%%" .. i, triggerEvent["CapturedData" .. i]);
end
-- Set the event's output text.
animationEvent.Text = outputString;
end
-- **********************************************************************************
-- Registers the triggers in the current profile with combat event lib.
-- **********************************************************************************
function MikSBT.RegisterTriggers()
-- Unregister all of the triggers from the combat event helper.
MikCEH.UnregisterAllTriggers();
-- Loop through all of the triggers in the current profile.
for triggerKey, triggerData in MikSBT.CurrentProfile.Triggers do
-- Make sure the trigger is being shown and register it with the combat event helper.
if (triggerData.EventSettings.Show) then
MikCEH.RegisterTrigger(triggerKey, triggerData.TriggerSettings);
end
end
end
-------------------------------------------------------------------------------------
-- Profile functions.
-------------------------------------------------------------------------------------
-- **********************************************************************************
-- Updates profiles created with older versions.
-- **********************************************************************************
function MikSBT.UpdateProfiles()
-- Loop through all the profiles.
for _, profile in MikSBT_Save.Profiles do
if not profile.CreationVersion then
-- There is an error in this Profile, we need to reset it.
MikSBT.ResetProfile(profile);
end
-- Check if the profile was created prior to version 2.0.
if (profile.CreationVersion < 2.0) then
-- Update the profile's structure.
profile.ShowOverheals = MikSBT.DEFAULT_CONFIG.ShowOverheals;
profile.ShowGameDamage = MikSBT.DEFAULT_CONFIG.ShowGameDamage;
profile.UseStickyCrits = MikSBT.DEFAULT_CONFIG.UseStickyCrits;
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_SPELL_DOT"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_SPELL_DOT"]);
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_HOT"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_HOT"]);
profile.EventSettings["MSBT_EVENTTYPE_OUTGOING_SPELL_DOT"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_OUTGOING_SPELL_DOT"]);
profile.EventSettings["MSBT_EVENTTYPE_OUTGOING_HOT"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_OUTGOING_HOT"]);
-- Update the creation version to the current version.
profile.CreationVersion = 2.0;
end
-- Check if the profile was created prior to version 2.1.
if (profile.CreationVersion < 2.1) then
-- Update the profile's structure.
-- 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
-- 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
eventType.IsSticky = MikSBT.DEFAULT_CONFIG.EventSettings[eventTypeName].IsSticky;
end
-- Add event settings for experience gains.
profile.EventSettings["MSBT_EVENTTYPE_NOTIFICATION_EXPERIENCE_GAIN"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_NOTIFICATION_EXPERIENCE_GAIN"]);
end
-- Update the creation version to the current version.
profile.CreationVersion = 2.1;
end
-- Check if the profile was created prior to version 3.0.
if (profile.CreationVersion < 3.0) then
-- Remove the low health, low mana, and execute notification event settings since they are now triggers.
profile.EventSettings["MSBT_EVENTTYPE_NOTIFICATION_LOW_HEALTH"] = nil;
profile.EventSettings["MSBT_EVENTTYPE_NOTIFICATION_LOW_MANA"] = nil;
profile.EventSettings["MSBT_EVENTTYPE_NOTIFICATION_EXECUTE"] = nil;
-- Remove the low health and low mana warning percentage info since they are now triggers.
profile.WarningPercentageHealth = nil;
profile.WarningPercentageMana = nil;
-- Update the profile's structure with triggers.
profile.Triggers = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.Triggers);
-- Add event settings for killing blows.
profile.EventSettings["MSBT_EVENTTYPE_NOTIFICATION_PC_KILLING_BLOW"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_NOTIFICATION_PC_KILLING_BLOW"]);
profile.EventSettings["MSBT_EVENTTYPE_NOTIFICATION_NPC_KILLING_BLOW"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_NOTIFICATION_NPC_KILLING_BLOW"]);
-- Update the profile's structure with suppresions.
profile.Suppressions = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.Suppressions);
-- Update the creation version to the current version.
profile.CreationVersion = 3.0;
end
-- Check if the profile was created prior to version 4.0.
if (profile.CreationVersion < 4.0) then
profile.ShowAllManaGains = false;
profile.LowHealthSound = true;
profile.LowManaSound = true;
profile.CreationVersion = 4.0;
end
-- Check if the profile was created prior to version 4.1.
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(MikSBT.SpellName(8512, "Windfury Totem"), "-", "%%-"))
end
if profile.Triggers.MSBT_TRIGGER_HAND_OF_JUSTICE then -- Delete Hand of Justice Trigger
profile.Triggers["MSBT_TRIGGER_HAND_OF_JUSTICE"] = nil;
end
profile.CreationVersion = 4.1;
end
if (profile.CreationVersion < 4.3) then
-- The pet update !
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_DAMAGE"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_DAMAGE"]);
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_MISS"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_MISS"]);
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_DODGE"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_DODGE"]);
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_PARRY"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_PARRY"]);
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_BLOCK"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_BLOCK"]);
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_ABSORB"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_ABSORB"]);
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_IMMUNE"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_IMMUNE"]);
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_SPELL_DAMAGE"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_SPELL_DAMAGE"]);
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_SPELL_DOT"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_SPELL_DOT"]);
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_SPELL_MISS"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_SPELL_MISS"]);
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_SPELL_DODGE"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_SPELL_DODGE"]);
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_SPELL_PARRY"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_SPELL_PARRY"]);
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_SPELL_BLOCK"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_SPELL_BLOCK"]);
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_SPELL_RESIST"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_SPELL_RESIST"]);
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_SPELL_ABSORB"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_SPELL_ABSORB"]);
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_SPELL_IMMUNE"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_SPELL_IMMUNE"]);
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_HEAL"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_HEAL"]);
profile.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_HOT"] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings["MSBT_EVENTTYPE_INCOMING_PET_HOT"]);
profile.CreationVersion = 4.3;
end
if (profile.CreationVersion < 4.4) then
profile.BlizzardFontSettings = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.BlizzardFontSettings);
profile.CreationVersion = 4.4;
end
if (profile.CreationVersion < 4.5) then
-- Add missing outgoing pet event settings (omitted from the 4.3 migration).
local petOutgoingEvents = {
"MSBT_EVENTTYPE_OUTGOING_PET_DAMAGE",
"MSBT_EVENTTYPE_OUTGOING_PET_MISS",
"MSBT_EVENTTYPE_OUTGOING_PET_DODGE",
"MSBT_EVENTTYPE_OUTGOING_PET_PARRY",
"MSBT_EVENTTYPE_OUTGOING_PET_BLOCK",
"MSBT_EVENTTYPE_OUTGOING_PET_ABSORB",
"MSBT_EVENTTYPE_OUTGOING_PET_IMMUNE",
"MSBT_EVENTTYPE_OUTGOING_PET_EVADE",
"MSBT_EVENTTYPE_OUTGOING_PET_SPELL_DAMAGE",
"MSBT_EVENTTYPE_OUTGOING_PET_SPELL_MISS",
"MSBT_EVENTTYPE_OUTGOING_PET_SPELL_DODGE",
"MSBT_EVENTTYPE_OUTGOING_PET_SPELL_PARRY",
"MSBT_EVENTTYPE_OUTGOING_PET_SPELL_BLOCK",
"MSBT_EVENTTYPE_OUTGOING_PET_SPELL_RESIST",
"MSBT_EVENTTYPE_OUTGOING_PET_SPELL_ABSORB",
"MSBT_EVENTTYPE_OUTGOING_PET_SPELL_IMMUNE",
"MSBT_EVENTTYPE_OUTGOING_PET_SPELL_EVADE",
};
for _, evtName in petOutgoingEvents do
if not profile.EventSettings[evtName] and MikSBT.DEFAULT_CONFIG.EventSettings[evtName] then
profile.EventSettings[evtName] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings[evtName]);
end
end
profile.CreationVersion = 4.5;
end
if (profile.CreationVersion < 4.6) then
-- Add missing incoming evade/reflect event settings (needed for Nampower events).
local incomingMissEvents = {
"MSBT_EVENTTYPE_INCOMING_EVADE",
"MSBT_EVENTTYPE_INCOMING_SPELL_EVADE",
"MSBT_EVENTTYPE_INCOMING_PET_EVADE",
"MSBT_EVENTTYPE_INCOMING_PET_SPELL_EVADE",
"MSBT_EVENTTYPE_INCOMING_PET_SPELL_REFLECT",
};
for _, evtName in incomingMissEvents do
if not profile.EventSettings[evtName] and MikSBT.DEFAULT_CONFIG.EventSettings[evtName] then
profile.EventSettings[evtName] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG.EventSettings[evtName]);
end
end
profile.CreationVersion = 4.6;
end
end
end
-- **********************************************************************************
-- Returns whether or not the passed profile exists.
-- **********************************************************************************
function MikSBT.ProfileExists(profileName)
local profileFound = false;
-- Loop through all of the profiles looking for the passed one.
for pName in MikSBT_Save.Profiles do
if (pName == profileName) then
profileFound = true;
end
end
-- Return whether or not the profile exists.
return profileFound;
end
-- **********************************************************************************
-- Resets the passed profile to its defaults.
-- **********************************************************************************
function MikSBT.ResetProfile(profileName, showOutput)
-- Get whether or not the profile exists.
local profileExists = MikSBT.ProfileExists(profileName);
-- Check if the profile exists.
if (profileExists) then
-- Reset the profile.
MikSBT_Save.Profiles[profileName] = MikSBT.CopyTable(MikSBT.DEFAULT_CONFIG);
-- Check if it's the current profile being reset.
if (profileName == MikSBT_Save.CurrentProfile) then
-- Reset the current profile pointer.
MikSBT.CurrentProfile = MikSBT_Save.Profiles[MikSBT_Save.CurrentProfile];
-- Reassociate the display settings if the scroll areas have been setup.
if (scrollAreas.Incoming ~= nil) then
-- Setup the display settings.
scrollAreas.Incoming.DisplaySettings = MikSBT.CurrentProfile.DisplaySettings.Incoming;
scrollAreas.Outgoing.DisplaySettings = MikSBT.CurrentProfile.DisplaySettings.Outgoing;
scrollAreas.Notification.DisplaySettings = MikSBT.CurrentProfile.DisplaySettings.Notification;
end
-- Reposition the animation frames.
MikSBT.RepositionAnimationFrames();
-- Register the triggers with the combat event helper.
MikSBT.RegisterTriggers();
end
-- Check if the output text is to be shown.
if (showOutput) then
-- Print the profile reset string.
MikSBT.Print(profileName .. " " .. MikSBT.MSG_PROFILE_RESET, 0, 1, 0);
end
end
end
-- **********************************************************************************
-- Selects the passed profile.
-- **********************************************************************************
function MikSBT.SelectProfile(profileName)
-- Get whether or not the profile exists.
local profileExists = MikSBT.ProfileExists(profileName);
-- Check if the profile exists.
if (profileExists) then
-- Set the current profile name to the one being selected.
MikSBT_Save.CurrentProfile = profileName;
-- Reset the current profile pointer.
MikSBT.CurrentProfile = MikSBT_Save.Profiles[MikSBT_Save.CurrentProfile];
-- Reassociate the display settings if the scroll areas have been setup.
if (scrollAreas.Incoming ~= nil) then
-- Setup the display settings.
scrollAreas.Incoming.DisplaySettings = MikSBT.CurrentProfile.DisplaySettings.Incoming;
scrollAreas.Outgoing.DisplaySettings = MikSBT.CurrentProfile.DisplaySettings.Outgoing;
scrollAreas.Notification.DisplaySettings = MikSBT.CurrentProfile.DisplaySettings.Notification;
end
-- Reposition the animation frames.
MikSBT.RepositionAnimationFrames();
-- Register the triggers with the combat event helper.
MikSBT.RegisterTriggers();
end
end
-- **********************************************************************************
-- Creates a profile for the passed name.
-- **********************************************************************************
function MikSBT.CreateProfile(profileName)
-- Get whether or not the profile exists.
local profileExists = MikSBT.ProfileExists(profileName);
-- Make sure a profile name was passed and it doesn't already exist.
if (not profileExists and profileName ~= nil and profileName ~= "") then
-- Create an array for the profile.
MikSBT_Save.Profiles[profileName] = {};
-- Reset the profile.
MikSBT.ResetProfile(profileName);
end
end
-- **********************************************************************************
-- Deletes the passed profile.
-- **********************************************************************************
function MikSBT.DeleteProfile(profileName)
-- Check if the passed profile is the default one.
if (profileName == MikSBT.DEFAULT_PROFILE_NAME) then
return;
end
-- Get whether or not the profile exists.
local profileExists = MikSBT.ProfileExists(profileName);
-- Check if the profile exists.
if (profileExists) then
-- Check if the profile being deleted is the current one.
if (profileName == MikSBT_Save.CurrentProfile) then
-- Select the default profile.
MikSBT.SelectProfile(MikSBT.DEFAULT_PROFILE_NAME);
end
-- Delete the profile.
MikSBT_Save.Profiles[profileName] = nil;
end
end
-------------------------------------------------------------------------------------
-- Option functions.
-------------------------------------------------------------------------------------
-- **********************************************************************************
-- Set the user disabled option
-- **********************************************************************************
function MikSBT.SetOptionUserDisabled(isDisabled)
MikSBT_Save.UserDisabled = isDisabled;
-- Check if the mod is being set to disabled.
if (isDisabled) then
-- Unregister all events.
MikSBT.UnregisterEvents();
MikCEH.UnregisterEvents();
-- Turn on the game's display of outgoing damage.
SetCVar("CombatDamage", 1);
-- Turn on Blizzard's default combat text.
if (SHOW_COMBAT_TEXT ~= nil) then
SHOW_COMBAT_TEXT = 1;
end
else
-- Reregister events.
MikSBT.RegisterEvents();
MikCEH.RegisterEvents();
-- Turn the game damage display on or off depending on the user setting.
MikSBT.SetOptionShowGameDamage(MikSBT.CurrentProfile.ShowGameDamage);
-- Turn off Blizzard's default combat text.
if (SHOW_COMBAT_TEXT ~= nil) then
SHOW_COMBAT_TEXT = 0;
end
-- Make sure that periodic spell effects are reported.
SetCVar("CombatLogPeriodicSpells", 1);
end
end
-- **********************************************************************************
-- Set the game damage disabled option
-- **********************************************************************************
function MikSBT.SetOptionShowGameDamage(showDamage)
MikSBT.CurrentProfile.ShowGameDamage = showDamage;
-- Check if the game damage is being set to on.
if (showDamage) then
-- Turn on the game damage display.
SetCVar("CombatDamage", 1);
else
-- Turn off the game damage display.
SetCVar("CombatDamage", 0);
end
end
-- **********************************************************************************
-- This function takes a string version of a variable path
-- and returns its value from the current profile.
-- **********************************************************************************
function MikSBT.GetOptionFromVarPath(varPath)
-- Set the current table to the current profile.
local currentTable = MikSBT.CurrentProfile;
-- Index into each sub table until we reach the actual variable wanted.
for varName in string.gfind(varPath, "[%w_]+") do
if (type(currentTable[varName]) == "table") then
currentTable = currentTable[varName];
else
return currentTable[varName];
end
end
return nil;
end
-- **********************************************************************************
-- This function takes a string version of a variable path
-- and sets its value in the current profile to the passed value.
-- **********************************************************************************
function MikSBT.SetOptionFromVarPath(varPath, newValue)
-- Sets the current table to the current profile.
local currentTable = MikSBT.CurrentProfile;
-- Index into each sub table until we reach the actual variable wanted and set it
-- to the passed value.
for varName in string.gfind(varPath, "[%w_]+") do
if (type(currentTable[varName]) == "table") then
currentTable = currentTable[varName]
else
currentTable[varName] = newValue;
end
end
end
-------------------------------------------------------------------------------------
-- Animation functions.
-------------------------------------------------------------------------------------
-- **********************************************************************************
-- Adds the passed animation event to the animation system for the appropriate scroll
-- area.
--
-- animationEvent Possible Fields:
-- ScrollArea - The scroll area to use for the event.
-- EventType - The event type.
-- EventSettings - The event settings for the event.
-- IsCrit - Whether or not the event is a crit.
-- UnformattedText - The unformatted text to display.
-- Amount - The amount of the attack, heal, gain, loss, etc.
-- EffectName - The name of the spell, ability, buff, debuff, power type, etc.
-- Name - The name of the enemy/player.
-- DamageType - The type of damage.
-- PartialEffectText - Partial effect text to be appended. (partial resists, absorbs, blocks)
-- OverhealAmount - The amount overhealed.
-- Text - The formatted text to display.
-- **********************************************************************************
function MikSBT.DispatchAnimationEvent(animationEvent)
-- Check if the event is to be shown.
if (animationEvent.EventSettings.Show and animationEvent.ScrollArea.DisplaySettings.Show) then
-- 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);
elseif (animationEvent.ScrollArea == scrollAreas.Outgoing) then
table_insert(animationMergeData.Outgoing.UnmergedEvents, animationEvent);
-- Add the event the animation system.
else
MikSBT.AddAnimation(animationEvent);
end
end
end
-- **********************************************************************************
-- Add the event to the appropriate scroll area's animation display array.
-- **********************************************************************************
function MikSBT.AddAnimation(animationEvent)
-- Format the animation event's output text.
MikSBT.FormatEventText(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
-- 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
-- Reclaim the animation event table to the events recycler and leave the function.
eventsRecycler:ReclaimTable(animationEvent);
return;
end
end
if animationEvent.EventType == "MSBT_EVENTTYPE_NOTIFICATION_POWER_GAIN" and animationEvent.EffectName ~= 0 and MikSBT.CurrentProfile.ShowAllManaGains then
return
end
-- Check if the animation event is to be displayed sticky style.
if (animationEvent.EventSettings ~= nil and animationEvent.EventSettings.IsSticky) then
animationEvent.IsSticky = true;
end
-- Color UnitName by class (reuse cached lookup from FormatEventText)
local unitID = animationEvent._unitID
local uName = animationEvent._unitName
if unitID then
local _, class = UnitClass(unitID)
local colorPrefix = class and CLASS_COLORS[class]
if colorPrefix then
animationEvent.Text = string_gsub(animationEvent.Text, animationEvent.Name, colorPrefix .. uName .. "|h|r")
end
end
-- Get the next available animation display info object for the scroll area.
local animDisplayInfo = MikSBT.GetNextAnimationDisplayInfo(animationEvent);
-- Get the scroll function to use.
animDisplayInfo.ScrollFunction = MikSBT.GetScrollFunction(animationEvent);
-- Get the starting X and Y positions.
animDisplayInfo.PositionX, animDisplayInfo.PositionY = MikSBT.GetAnimationStartingPosition(animationEvent)
-- Check if the animation event has event settings associated with it.
if (animationEvent.EventSettings ~= nil) then
-- Get the font size for the event.
animDisplayInfo.FontSize = MikSBT.GetFontSize(animationEvent.ScrollArea, animationEvent.EventSettings, animationEvent.IsCrit);
-- Get the font path for the event.
animDisplayInfo.FontPath = MikSBT.GetFontPath(animationEvent.ScrollArea, animationEvent.EventSettings, animationEvent.IsCrit);
-- Get the font outline style for the event.
animDisplayInfo.FontOutline = MikSBT.GetFontOutline(animationEvent.ScrollArea, animationEvent.EventSettings, animationEvent.IsCrit);
-- Set the r, g, and b color for the animation display object.
animDisplayInfo.ColorR = animationEvent.EventSettings.FontSettings.Color.r;
animDisplayInfo.ColorG = animationEvent.EventSettings.FontSettings.Color.g;
animDisplayInfo.ColorB = animationEvent.EventSettings.FontSettings.Color.b;
-- Override font color based on spell school damage type.
if animationEvent.DamageType and animationEvent.EventType then
local dtColor = GetDamageTypeFontColor(animationEvent.DamageType)
if dtColor then
animDisplayInfo.ColorR = dtColor.r
animDisplayInfo.ColorG = dtColor.g
animDisplayInfo.ColorB = dtColor.b
end
end
-- No event type associated with the event so get the display info from the animation event itself.
else
-- Get the font size for the event.
animDisplayInfo.FontSize = animationEvent.FontSize;
-- Get the font path for the event.
animDisplayInfo.FontPath = animationEvent.FontPath;
-- Get the font outline style for the event.
animDisplayInfo.FontOutline = animationEvent.FontOutline;
-- Set the r, g, and b color for the animation display object.
animDisplayInfo.ColorR = animationEvent.ColorR;
animDisplayInfo.ColorG = animationEvent.ColorG;
animDisplayInfo.ColorB = animationEvent.ColorB;
end
-- Set the animation display alpha, anchor point, crit flag, and sticky flag.
animDisplayInfo.Alpha = 0;
animDisplayInfo.AnchorPoint = MikSBT.AVAILABLE_TEXT_ALIGNS[animationEvent.ScrollArea.DisplaySettings.FontSettings.TextAlignIndex].AnchorPoint;
animDisplayInfo.IsCrit = animationEvent.IsCrit;
animDisplayInfo.IsSticky = animationEvent.IsSticky;
if (animationEvent.EventType == "MSBT_EVENTTYPE_OUTGOING_SPELL_RESIST") then
-- 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
PlaySoundFile("Interface\\AddOns\\MikScrollingBattleText\\sounds\\Resist.mp3");
end
end
end
end
-- Set the font object properties.
animDisplayInfo.FontObject:ClearAllPoints();
-- SetTextHeight does NOT apply if the original font size wouldn't be changed by it
-- FontSize tops out at 32 and in vanilla doesn't seem to apply at all - every different size I've tried has refused to work
-- TextHeight does scale past 32, but expectedly becomes blurry - still better than unreadable numbers on a 4k display
local fontSize = animDisplayInfo.FontSize;
if (not fontSize or fontSize < 2) then fontSize = 18; end
animDisplayInfo.FontObject:SetFont(animDisplayInfo.FontPath, fontSize-1, animDisplayInfo.FontOutline);
-- Fall back to default font if the configured font file was not found.
if (not animDisplayInfo.FontObject:GetFont()) then
animDisplayInfo.FontObject:SetFont("Fonts\\FRIZQT__.TTF", fontSize-1, animDisplayInfo.FontOutline);
end
animDisplayInfo.FontObject:SetTextColor(animDisplayInfo.ColorR, animDisplayInfo.ColorG, animDisplayInfo.ColorB);
animDisplayInfo.FontObject:SetText(animationEvent.Text);
animDisplayInfo.FontObject:SetTextHeight(animDisplayInfo.FontSize);
animDisplayInfo.FontObject:SetAlpha(animDisplayInfo.Alpha);
animDisplayInfo.FontObject:SetPoint(animDisplayInfo.AnchorPoint, animDisplayInfo.PositionX, animDisplayInfo.PositionY);
-- 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+%)", "")
-- DEFAULT_CHAT_FRAME:AddMessage(animationEvent.EffectName)
-- DEFAULT_CHAT_FRAME:AddMessage(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
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 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
if texture then
-- local frame
if not animDisplayInfo.Texture then
-- on creer une nouvelle texture
animDisplayInfo.Texture = getglobal(animationEvent.ScrollArea.AssociatedFrame):CreateTexture(nil, "ARTWORK")
end
-- on réutilise une texture existante
animDisplayInfo.Texture:ClearAllPoints();
animDisplayInfo.Texture:SetTexture(nil);
animDisplayInfo.Texture:SetTexture(texture);
animDisplayInfo.Texture:SetWidth(animDisplayInfo.FontSize);
animDisplayInfo.Texture:SetHeight(animDisplayInfo.FontSize);
animDisplayInfo.Texture:SetTexCoord(0.125, 0.875, 0.125, 0.875)
animDisplayInfo.Texture:SetPoint("RIGHT", animDisplayInfo.FontObject, "LEFT", -4, 0);
animDisplayInfo.Texture:SetDrawLayer(animDisplayInfo.IsSticky and "OVERLAY" or animDisplayInfo.IsCrit and "OVERLAY" or "ARTWORK");
elseif animDisplayInfo.Texture then
animDisplayInfo.Texture:SetTexture(nil);
end
-- Set the last update to the current time.
animDisplayInfo.LastUpdated = GetTime();
-- Set the active flag.
animDisplayInfo.IsActive = true;
-- Check if the scroll area's display frame is not visible and make it visible so the OnUpdate events start firing.
-- This is done to keep the number of OnUpdate events down to a minimum for better performance.
if (not getglobal(animationEvent.ScrollArea.AssociatedFrame):IsVisible()) then
getglobal(animationEvent.ScrollArea.AssociatedFrame):Show();
end
-- Check if it's a sticky or a crit and sticky crits are enabled.
if (animDisplayInfo.IsSticky or (animDisplayInfo.IsCrit and MikSBT.CurrentProfile.UseStickyCrits)) then
-- Make sure the stickies don't overlap.
MikSBT.RepositionStickyAnimDisplayInfo(animationEvent.ScrollArea);
else
-- Make sure there are no overlaps in the scrolling text.
MikSBT.RepositionAnimDisplayInfo(animationEvent.ScrollArea);
end
-- Reclaim the animation event table to the events recycler.
eventsRecycler:ReclaimTable(animationEvent);
end
-- **********************************************************************************
-- This function return the buff texture based on
-- it's name.
-- **********************************************************************************
function ItemLinkToName(link)
return gsub(link,"^.*%[(.*)%].*$","%1");
end
function MikSBT.FindItemIcon(item)
if ( not item ) then return; end
item = string.lower(item);
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
-- **********************************************************************************
-- This function return the buff texture based on
-- it's name.
-- **********************************************************************************
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
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
-- **********************************************************************************
-- This function return some debug stuff
-- for real.
-- **********************************************************************************
local depth = 0
local function dump(...)
local out = "";
for i = 1, arg.n, 1 do
if depth > 30 then
out = out .. "|cffffd800... Too many things here!|r"
return out;
end
local d = arg[i];
local t = type(d);
if (t == "table") then
out = out .. "{ |cff9f9f9f--[[" .. tostring(arg[i]) .. "]]|r\n";
local first = true;
if (d) then
for k, v in pairs(d) do
if (not first) then out = out .. ", \n"; end
first = false;
depth = depth + 1
out = out .. " " .. dump(k);
out = out .. " = ";
out = out .. dump(v);
end
end
out = out .. "\n}, |cff9f9f9f--[[" .. tostring(arg[i]) .. "]]|r\n";
elseif (t == "nil") then
out = out .. "|cffff7f7fnil|r";
elseif (t == "number") then
out = out .. "|cffff7fff" .. d .. "|r";
elseif (t == "string") then
out = out .. '"|cff7fd5ff' .. d .. '|r"';
elseif (t == "boolean") then
if (d) then
out = out .. "|cffff9100true|r";
else
out = out .. "|cffff9100false|r";
end
elseif (t == "function") then
out = out .. "|cff7fd5ff" .. tostring(d) .. "|r";
elseif (t == "userdata") then
out = out .. string.format("|cffffea00<%s:%s>|r", t, getmetatable(d) or "(anon)")
else
out = out .. string.upper(t) .. "??";
end
if (i < arg.n) then out = out .. ", "; end
end
return out;
end
function MikSBT.debugPrint(...)
local debugWin = 0;
local name, shown;
for i=1, NUM_CHAT_WINDOWS do
name,_,_,_,_,_,shown = GetChatWindowInfo(i);
if (string.lower(name) == "debug") then debugWin = i; break; end
end
if (debugWin == 0) then return end
local out = "";
depth = 0
for i = 1, arg.n, 1 do
if (i > 1) then out = out .. ", "; end
out = arg[i].." = "
arg[i] = getglobal(arg[i])
local t = type(arg[i]);
if (t == "string") then
out = out .. '"|cff7fd5ff'..arg[i]..'|r"';
elseif (t == "number") then
out = out .. "|cffff7fff" .. arg[i] .. "|r";
elseif (t == "boolean") then
out = out .. "|cffff9100" .. arg[i] .. "|r";
elseif (t == nil) then
out = out .. "|cffff7f7f" .. arg[i] .. "|r";
else
out = out .. dump(arg[i]);
end
end
local start = GetTime()
getglobal("ChatFrame"..debugWin):AddMessage("|cffffd800<|r\n" .. out, 1.0, 1.0, 1.0);
getglobal("ChatFrame"..debugWin):AddMessage("|cffffd800> displayed in:|r "..string.format( "%.4f",GetTime()-start).."|cffffd800s|r", 1.0, 1.0, 1.0);
end
SLASH_MSBTDUMPCMD1 = "/bump"
SlashCmdList["MSBTDUMPCMD"] = MikSBT.debugPrint
-- **********************************************************************************
-- This function gets the next animation display info object for the correct
-- scroll area.
-- **********************************************************************************
function MikSBT.GetNextAnimationDisplayInfo(animationEvent)
-- Holds the next animation display info object.
local animDisplayInfo;
-- Check if it's a sticky or it's a crit and sticky crits are enabled.
if (animationEvent.IsSticky or (animationEvent.IsCrit and MikSBT.CurrentProfile.UseStickyCrits)) then
-- Get the next available animation display object for stickies.
-- This is done so that stickies use the last set of font strings to avoid normal hits
-- overlapping them.
animDisplayInfo = animationEvent.ScrollArea.AnimationDisplayArray[animationEvent.ScrollArea.NextSticky];
-- Increment to the next sticky animation display object.
animationEvent.ScrollArea.NextSticky = animationEvent.ScrollArea.NextSticky + 1;
-- Loop back around to the first sticky animation display object.
if (animationEvent.ScrollArea.NextSticky > NUM_FONT_STRINGS) then
animationEvent.ScrollArea.NextSticky = animationEvent.ScrollArea.FirstSticky;
end
-- Not a sticky.
else
-- Get the next available animation display object.
animDisplayInfo = animationEvent.ScrollArea.AnimationDisplayArray[animationEvent.ScrollArea.NextAnimationDisplay];
-- Increment to the next animation display object.
animationEvent.ScrollArea.NextAnimationDisplay = animationEvent.ScrollArea.NextAnimationDisplay + 1;
-- Loop back around to the first animation display object.
if (animationEvent.ScrollArea.NextAnimationDisplay > (animationEvent.ScrollArea.FirstSticky - 1)) then
animationEvent.ScrollArea.NextAnimationDisplay = 1;
end
end
-- Return the animation display info object.
return animDisplayInfo;
end
-- **********************************************************************************
-- This function gets the scroll function to use for the event.
-- **********************************************************************************
function MikSBT.GetScrollFunction(animationEvent)
-- Straight animation style.
if (animationEvent.ScrollArea.DisplaySettings.AnimationStyle == ANIMATION_STYLE_STRAIGHT) then
-- Text is scrolling down.
if (animationEvent.ScrollArea.DisplaySettings.ScrollDirection == SCROLL_DIRECTION_DOWN) then
-- Return the appropriate animation function.
return MikSBT.ScrollStraightDown;
-- Text is scrolling up.
else
-- Return the appropriate animation function.
return MikSBT.ScrollStraightUp;
end
-- Left parabola animation style.
elseif (animationEvent.ScrollArea.DisplaySettings.AnimationStyle == ANIMATION_STYLE_LEFT_PARABOLA) then
-- Text is scrolling down.
if (animationEvent.ScrollArea.DisplaySettings.ScrollDirection == SCROLL_DIRECTION_DOWN) then
-- Return the appropriate animation function.
return MikSBT.ScrollLeftParabolaDown;
-- Text is scrolling up.
else
-- Return the appropriate animation function.
return MikSBT.ScrollLeftParabolaUp;
end
-- Right parabola animation style.
elseif (animationEvent.ScrollArea.DisplaySettings.AnimationStyle == ANIMATION_STYLE_RIGHT_PARABOLA) then
-- Text is scrolling down.
if (animationEvent.ScrollArea.DisplaySettings.ScrollDirection == SCROLL_DIRECTION_DOWN) then
-- Return the appropriate animation function.
return MikSBT.ScrollRightParabolaDown;
-- Text is scrolling up.
else
-- Return the appropriate animation function.
return MikSBT.ScrollRightParabolaUp;
end
end
-- Default to the straight up animation style although the function should never get
-- to this point.
return MikSBT.ScrollStraightUp;
end
-- **********************************************************************************
-- This function gets the starting animation x and and y position.
-- **********************************************************************************
function MikSBT.GetAnimationStartingPosition(animationEvent)
-- Holds where to start the animation at.
local startPositionX, startPositionY;
-- Check which animation style is selected for the scroll area.
if (animationEvent.ScrollArea.DisplaySettings.AnimationStyle == ANIMATION_STYLE_STRAIGHT or
animationEvent.ScrollArea.DisplaySettings.AnimationStyle == ANIMATION_STYLE_LEFT_PARABOLA or
animationEvent.ScrollArea.DisplaySettings.AnimationStyle == ANIMATION_STYLE_RIGHT_PARABOLA) then
-- Check if the text is scrolling down.
if (animationEvent.ScrollArea.DisplaySettings.ScrollDirection == SCROLL_DIRECTION_DOWN) then
-- Set the starting X and Y positions.
startPositionX = 0;
startPositionY = animationEvent.ScrollArea.DisplaySettings.ScrollHeight;
-- Text is scrolling up.
else
-- Set the starting X and Y positions.
startPositionX = 0;
startPositionY = 0;
end
-- Check if it's a sticky or a crit and sticky crits are enabled.
if (animationEvent.IsSticky or (animationEvent.IsCrit and MikSBT.CurrentProfile.UseStickyCrits)) then
-- Set the sticky to the center.
startPositionY = math_ceil(animationEvent.ScrollArea.DisplaySettings.ScrollHeight / 2);
end
end
-- Return the x and y starting positions.
return startPositionX, startPositionY;
end
-- **********************************************************************************
-- This function makes sure there is no overlapping text.
-- **********************************************************************************
function MikSBT.RepositionAnimDisplayInfo(scrollArea)
-- Construct a table with the non sticky animation display info in the same order the events were added.
for x = scrollArea.NextAnimationDisplay, scrollArea.FirstSticky-1 do
local animDisplayInfo = scrollArea.AnimationDisplayArray[x];
if (animDisplayInfo.IsActive) then
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);
end
end
-- Get the number of active non sticky data objects.
local numNonStickies = table_getn(activeNonStickies);
-- Animation style straight, left parabola, or right parabola.
if (scrollArea.DisplaySettings.AnimationStyle == ANIMATION_STYLE_STRAIGHT or
scrollArea.DisplaySettings.AnimationStyle == ANIMATION_STYLE_LEFT_PARABOLA or
scrollArea.DisplaySettings.AnimationStyle == ANIMATION_STYLE_RIGHT_PARABOLA) then
-- Scrolling down.
if (scrollArea.DisplaySettings.ScrollDirection == SCROLL_DIRECTION_DOWN) then
-- Check if there is more than 1 active non sticky animation.
if (numNonStickies > 1) then
-- Loop backwards through the active non sticky animation display objects and move the ones that are colliding.
for x = numNonStickies, 2, -1 do
local bottomPointCurrent = activeNonStickies[x].PositionY;
local topPointNext = activeNonStickies[x-1].PositionY + activeNonStickies[x-1].FontSize + MIN_VERTICAL_SPACING;
if (topPointNext > bottomPointCurrent) then
activeNonStickies[x-1].PositionY = bottomPointCurrent - activeNonStickies[x-1].FontSize - MIN_VERTICAL_SPACING;
end
end
end
-- Scrolling up.
else
-- Check if there is more than 1 active non sticky animation.
if (numNonStickies > 1) then
-- Loop backwards through the active non sticky animation display objects and move the ones that are colliding.
for x = numNonStickies, 2, -1 do
local topPointCurrent = activeNonStickies[x].PositionY + activeNonStickies[x].FontSize + MIN_VERTICAL_SPACING;
local bottomPointNext = activeNonStickies[x-1].PositionY;
if (bottomPointNext < topPointCurrent) then
activeNonStickies[x-1].PositionY = topPointCurrent;
end
end
end
end -- Scrolling Direction
end -- Animation Style
-- Clear the temp non stickies table.
for x = 1, numNonStickies do
activeNonStickies[x] = nil;
end
table_setn(activeNonStickies, 0);
end
-- **********************************************************************************
-- This function makes sure stickies don't overlap.
-- **********************************************************************************
function MikSBT.RepositionStickyAnimDisplayInfo(scrollArea)
-- Construct a table with the sticky animation display info in the same order the events were added.
for x = scrollArea.NextSticky, NUM_FONT_STRINGS do
local animDisplayInfo = scrollArea.AnimationDisplayArray[x];
if (animDisplayInfo.IsActive) then
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);
end
end
-- Get the number of active stickies.
local numStickies = table_getn(activeStickies);
-- Animation style straight, left parabola, or right parabola.
if (scrollArea.DisplaySettings.AnimationStyle == ANIMATION_STYLE_STRAIGHT or
scrollArea.DisplaySettings.AnimationStyle == ANIMATION_STYLE_LEFT_PARABOLA or
scrollArea.DisplaySettings.AnimationStyle == ANIMATION_STYLE_RIGHT_PARABOLA) then
-- Scrolling down.
if (scrollArea.DisplaySettings.ScrollDirection == SCROLL_DIRECTION_DOWN) then
-- Check if there is more than 1 active sticky animation.
if (numStickies > 1) then
-- Get the middle active sticky.
local middleSticky = math_floor((numStickies + 1) / 2);
-- Set the middle sticky to the center of the scroll area.
activeStickies[middleSticky].PositionY = scrollArea.DisplaySettings.ScrollHeight / 2;
-- Loop backwards from the middle sticky.
for x = middleSticky - 1, 1, -1 do
activeStickies[x].PositionY = activeStickies[x+1].PositionY - activeStickies[x].FontSize - MIN_VERTICAL_SPACING;
end
-- Loop forwards from the middle sticky.
for x = middleSticky + 1, numStickies do
activeStickies[x].PositionY = activeStickies[x-1].PositionY + activeStickies[x-1].FontSize + MIN_VERTICAL_SPACING;
end
end
-- Scrolling up.
else
-- Check if there is more than 1 active sticky animation.
if (numStickies > 1) then
-- Get the middle active sticky.
local middleSticky = math_ceil((numStickies + 1) / 2);
-- Set the middle sticky to the center of the scroll area.
activeStickies[middleSticky].PositionY = scrollArea.DisplaySettings.ScrollHeight / 2;
-- Loop backwards from the middle sticky.
for x = middleSticky - 1, 1, -1 do
activeStickies[x].PositionY = activeStickies[x+1].PositionY + activeStickies[x+1].FontSize + MIN_VERTICAL_SPACING;
end
-- Loop forwards from the middle sticky.
for x = middleSticky + 1, numStickies do
activeStickies[x].PositionY = activeStickies[x-1].PositionY - activeStickies[x].FontSize - MIN_VERTICAL_SPACING;
end
end
end -- Scrolling Direction
end -- Animation Style
-- Clear the temp stickies table.
for x = 1, numStickies do
activeStickies[x] = nil;
end
table_setn(activeStickies, 0);
end
-- **********************************************************************************
-- Animates the passed animation display data.
-- **********************************************************************************
function MikSBT.DoAnimation(animDisplayInfo, scrollArea)
-- Get the current and elapsed times since the last update.
local currentTime = GetTime();
local elapsedTime = currentTime - animDisplayInfo.LastUpdated;
-- Check if it's a sticky or a crit and sticky crits are enabled.
if (animDisplayInfo.IsSticky or (animDisplayInfo.IsCrit and MikSBT.CurrentProfile.UseStickyCrits)) then
-- Move the sticky in case it was repositioned.
animDisplayInfo.FontObject:SetPoint(animDisplayInfo.AnchorPoint, animDisplayInfo.PositionX, animDisplayInfo.PositionY);
if (elapsedTime <= STICKY_FADE_IN_TIME) then
local textDelta = animDisplayInfo.FontSize * (1 - (elapsedTime / STICKY_FADE_IN_TIME));
animDisplayInfo.FontObject:SetTextHeight(animDisplayInfo.FontSize + textDelta);
animDisplayInfo.FontObject:SetAlpha(1);
if animDisplayInfo.Texture then
animDisplayInfo.Texture:SetAlpha(1);
animDisplayInfo.Texture:SetWidth(animDisplayInfo.FontSize + textDelta);
animDisplayInfo.Texture:SetHeight(animDisplayInfo.FontSize + textDelta);
end
return;
elseif (elapsedTime <= (STICKY_DISPLAY_TIME + STICKY_FADE_IN_TIME)) then
animDisplayInfo.FontObject:SetTextHeight(animDisplayInfo.FontSize);
if animDisplayInfo.Texture then
animDisplayInfo.Texture:SetWidth(animDisplayInfo.FontSize);
animDisplayInfo.Texture:SetHeight(animDisplayInfo.FontSize);
end
if animDisplayInfo.IsCrit then
if not animDisplayInfo.timeLastJiggled then
animDisplayInfo.originalPositionX = animDisplayInfo.PositionX
animDisplayInfo.originalPositionY = animDisplayInfo.PositionY
animDisplayInfo.DecalageX = 0
animDisplayInfo.DecalageY = 0
animDisplayInfo.timeLastJiggled = 0
end
if animDisplayInfo.PositionY ~= (animDisplayInfo.originalPositionY + animDisplayInfo.DecalageY) then
animDisplayInfo.originalPositionX = animDisplayInfo.PositionX
animDisplayInfo.originalPositionY = animDisplayInfo.PositionY
end
if (elapsedTime - animDisplayInfo.timeLastJiggled > 0.05) then
animDisplayInfo.DecalageX = math_random(-1, 1);
animDisplayInfo.DecalageY = math_random(-1, 1);
animDisplayInfo.PositionX = animDisplayInfo.originalPositionX + animDisplayInfo.DecalageX;
animDisplayInfo.PositionY = animDisplayInfo.originalPositionY + animDisplayInfo.DecalageY;
animDisplayInfo.timeLastJiggled = elapsedTime;
animDisplayInfo.FontObject:SetPoint(animDisplayInfo.AnchorPoint, animDisplayInfo.PositionX, animDisplayInfo.PositionY);
end
end
return;
elseif (elapsedTime <= (STICKY_DISPLAY_TIME + STICKY_FADE_IN_TIME + STICKY_FADE_OUT_TIME)) then
local alpha = 1 - ((elapsedTime - STICKY_DISPLAY_TIME - STICKY_FADE_IN_TIME) / STICKY_FADE_OUT_TIME);
animDisplayInfo.timeLastJiggled = nil
animDisplayInfo.FontObject:SetAlpha(alpha);
if animDisplayInfo.Texture then
animDisplayInfo.Texture:SetAlpha(alpha);
end
return;
end
-- Deactivate the animation.
animDisplayInfo.FontObject:SetAlpha(0);
if animDisplayInfo.Texture then
animDisplayInfo.Texture:SetAlpha(0);
end
animDisplayInfo.IsActive = false;
return;
end
-- Check if it's time to update.
if (elapsedTime > ANIMATION_SPEED) then
-- Scroll the text and set its alpha appropriately.
local animationComplete = animDisplayInfo.ScrollFunction(animDisplayInfo, scrollArea);
-- Check if the animation is complete.
if (animationComplete) then
-- Set the font object's alpha.
animDisplayInfo.FontObject:SetAlpha(0);
if animDisplayInfo.Texture then
animDisplayInfo.Texture:SetAlpha(0);
end
-- Deactive the animation.
animDisplayInfo.IsActive = false;
else
-- Move the font object and set its alpha.
animDisplayInfo.FontObject:SetPoint(animDisplayInfo.AnchorPoint, animDisplayInfo.PositionX, animDisplayInfo.PositionY);
animDisplayInfo.FontObject:SetAlpha(animDisplayInfo.Alpha);
if animDisplayInfo.Texture then
animDisplayInfo.Texture:SetAlpha(animDisplayInfo.Alpha);
end
end
-- Set the last update to the current time.
animDisplayInfo.LastUpdated = currentTime;
end -- Time for update.
end
-- **********************************************************************************
-- Moves the passed animation display info upward and fades the alpha correctly.
-- Returns true when the animation is complete.
-- **********************************************************************************
function MikSBT.ScrollUp(animDisplayInfo, scrollArea)
-- Calculate the animation midpoint.
local animationMidPoint = scrollArea.DisplaySettings.ScrollHeight / 2;
-- Increment the Y position by the step.
animDisplayInfo.PositionY = animDisplayInfo.PositionY + MikSBT.CurrentProfile.AnimationStep;
-- Start fading out the text on the upper half.
if (animDisplayInfo.PositionY >= animationMidPoint) then
animDisplayInfo.Alpha = 1 - ((animDisplayInfo.PositionY - animationMidPoint) / animationMidPoint);
else
animDisplayInfo.Alpha = 1;
end
-- Check if the animation is complete.
if (animDisplayInfo.PositionY >= scrollArea.DisplaySettings.ScrollHeight) then
-- Return true to indicated the animation is complete.
return true;
end
-- Return false to indicate the animation is NOT complete.
return false;
end
-- **********************************************************************************
-- Moves the passed animation display info downward and fades the alpha correctly.
-- Returns true when the animation is complete.
-- **********************************************************************************
function MikSBT.ScrollDown(animDisplayInfo, scrollArea)
-- Calculate the animation midpoint.
local animationMidPoint = scrollArea.DisplaySettings.ScrollHeight / 2;
-- Decrement the Y position by the step.
animDisplayInfo.PositionY = animDisplayInfo.PositionY - MikSBT.CurrentProfile.AnimationStep;
-- Start fading out the text on the bottom half.
if (animDisplayInfo.PositionY < animationMidPoint) then
animDisplayInfo.Alpha = 1 - ((animationMidPoint - animDisplayInfo.PositionY) / animationMidPoint);
else
animDisplayInfo.Alpha = 1;
end
-- Check if the animation is complete.
if (animDisplayInfo.PositionY <= 0) then
-- Return true to indicated the animation is complete.
return true;
end
-- Return false to indicate the animation is NOT complete.
return false;
end
-- **********************************************************************************
-- Moves the passed animation display info straight up. Returns true when the
-- animation is complete.
-- **********************************************************************************
function MikSBT.ScrollStraightUp(animDisplayInfo, scrollArea)
-- Returns the result of the scroll up function.
return MikSBT.ScrollUp(animDisplayInfo, scrollArea);
end
-- **********************************************************************************
-- Moves the passed animation display info straight down. Returns true when the
-- animation is complete.
-- **********************************************************************************
function MikSBT.ScrollStraightDown(animDisplayInfo, scrollArea)
-- Returns the result of the scroll down function.
return MikSBT.ScrollDown(animDisplayInfo, scrollArea);
end
-- **********************************************************************************
-- Moves the passed animation display info in a left parabolic shape going upwards.
-- Returns true when the animation is complete.
-- **********************************************************************************
function MikSBT.ScrollLeftParabolaUp(animDisplayInfo, scrollArea)
-- Scroll the animation display info straight up and set the alpha correctly.
local animationComplete = MikSBT.ScrollUp(animDisplayInfo, scrollArea);
-- Calculate the animation midpoint.
local animationMidPoint = scrollArea.DisplaySettings.ScrollHeight / 2;
-- Calculate the new x position based on equation of a parabola.
-- Equation of a parabola at vertex 0,0: x = y^2 / 4a
local parabolaMaxX = math_ceil((animationMidPoint * animationMidPoint) / (4 * animationMidPoint));
local parabolaY = animDisplayInfo.PositionY - animationMidPoint;
local newX = parabolaMaxX - math_ceil((parabolaY * parabolaY) / (4 * animationMidPoint));
-- Set the new x position.
animDisplayInfo.PositionX = newX * -1;
-- Return whether or not the animation is complete.
return animationComplete;
end
-- **********************************************************************************
-- Moves the passed animation display info in a left parabolic shape going downwards.
-- Returns true when the animation is complete.
-- **********************************************************************************
function MikSBT.ScrollLeftParabolaDown(animDisplayInfo, scrollArea)
-- Scroll the animation display info straight up and set the alpha correctly.
local animationComplete = MikSBT.ScrollDown(animDisplayInfo, scrollArea);
-- Calculate the animation midpoint.
local animationMidPoint = scrollArea.DisplaySettings.ScrollHeight / 2;
-- Calculate the new x position based on equation of a parabola.
-- Equation of a parabola at vertex 0,0: x = y^2 / 4a
local parabolaMaxX = math_ceil((animationMidPoint * animationMidPoint) / (4 * animationMidPoint));
local parabolaY = animDisplayInfo.PositionY - animationMidPoint;
local newX = parabolaMaxX - math_ceil((parabolaY * parabolaY) / (4 * animationMidPoint));
-- Set the new x position.
animDisplayInfo.PositionX = newX * -1;
-- Return whether or not the animation is complete.
return animationComplete;
end
-- **********************************************************************************
-- Moves the passed animation display info in a right parabolic shape going upwards.
-- Returns true when the animation is complete.
-- **********************************************************************************
function MikSBT.ScrollRightParabolaUp(animDisplayInfo, scrollArea)
-- Scroll the animation display info straight up and set the alpha correctly.
local animationComplete = MikSBT.ScrollUp(animDisplayInfo, scrollArea);
-- Calculate the animation midpoint.
local animationMidPoint = scrollArea.DisplaySettings.ScrollHeight / 2;
-- Calculate the new x position based on equation of a parabola.
-- Equation of a parabola at vertex 0,0: x = y^2 / 4a
local parabolaMaxX = math_ceil((animationMidPoint * animationMidPoint) / (4 * animationMidPoint));
local parabolaY = animDisplayInfo.PositionY - animationMidPoint;
local newX = parabolaMaxX - math_ceil((parabolaY * parabolaY) / (4 * animationMidPoint));
-- Set the new x position.
animDisplayInfo.PositionX = newX;
-- Return whether or not the animation is complete.
return animationComplete;
end
-- **********************************************************************************
-- Moves the passed animation display info in a right parabolic shape going downwards.
-- Returns true when the animation is complete.
-- **********************************************************************************
function MikSBT.ScrollRightParabolaDown(animDisplayInfo, scrollArea)
-- Scroll the animation display info straight up and set the alpha correctly.
local animationComplete = MikSBT.ScrollDown(animDisplayInfo, scrollArea);
-- Calculate the animation midpoint.
local animationMidPoint = scrollArea.DisplaySettings.ScrollHeight / 2;
-- Calculate the new x position based on equation of a parabola.
-- Equation of a parabola at vertex 0,0: x = y^2 / 4a
local parabolaMaxX = math_ceil((animationMidPoint * animationMidPoint) / (4 * animationMidPoint));
local parabolaY = animDisplayInfo.PositionY - animationMidPoint;
local newX = parabolaMaxX - math_ceil((parabolaY * parabolaY) / (4 * animationMidPoint));
-- Set the new x position.
animDisplayInfo.PositionX = newX;
-- Return whether or not the animation is complete.
return animationComplete;
end
-------------------------------------------------------------------------------------
-- Command handler functions.
-------------------------------------------------------------------------------------
-- **********************************************************************************
-- Returns the current and remaining parameters from the passed string.
-- **********************************************************************************
function MikSBT.GetNextParameter(paramString)
local remainingParams;
local currentParam = paramString;
-- Look for a space.
local idx = string_find(paramString, " ");
if (idx) then
-- Get the current and remaing parameters.
currentParam = string.sub(paramString, 1, idx-1);
remainingParams = string.sub(paramString, idx+1);
end
-- Return the current parameter and the remaining ones.
return currentParam, remainingParams;
end
-- **********************************************************************************
-- Called to handle commands.
-- **********************************************************************************
function MikSBT.CommandHandler(params)
-- Get the parameter.
local currentParam;
local remainingParams;
currentParam, remainingParams = MikSBT.GetNextParameter(params);
-- Flag for whether or not to show usage info.
local showUsage = true;
-- Make sure there is a current parameter and lower case it.
if (currentParam) then
currentParam = string.lower(currentParam);
end
-- Look for the recognized parameters.
if (currentParam == "") then
-- Check if the load on demand options are not loaded.
if (not IsAddOnLoaded(MikSBT.MOD_NAME .. "Options")) then
-- Load the on demand options.
UIParentLoadAddOn(MikSBT.MOD_NAME .. "Options");
end
-- Make sure the on demand options actually loaded.
if (IsAddOnLoaded(MikSBT.MOD_NAME .. "Options")) then
-- Make sure the scroll area mover control frame is not shown.
if (not MSBTScrollAreaMoverControlFrame:IsShown()) then
-- Show the options interface.
MSBTFrameOptions:Show();
end
end
-- Don't show the usage info.
showUsage = false;
elseif (currentParam == MikSBT.COMMAND_RESET) then
-- Reset the current profile.
MikSBT.ResetProfile(MikSBT_Save.CurrentProfile, true);
-- Don't show the usage info.
showUsage = false;
elseif (currentParam == MikSBT.COMMAND_DISABLE) then
-- Set the user disabled option.
MikSBT.SetOptionUserDisabled(true);
-- Output an informative message.
MikSBT.Print(MikSBT.MSG_DISABLE, 1,1,1);
-- Don't show the usage info.
showUsage = false;
elseif (currentParam == MikSBT.COMMAND_ENABLE) then
-- Unset the user disabled option.
MikSBT.SetOptionUserDisabled(false);
-- Output an informative message.
MikSBT.Print(MikSBT.MSG_ENABLE, 1,1,1);
-- Don't show the usage info.
showUsage = false;
elseif (currentParam == MikSBT.COMMAND_SHOWVER) then
-- Output the current version number.
MikSBT.Print(MikSBT.VERSION_STRING, 1,1,1);
-- Don't show the usage info.
showUsage = false;
elseif (currentParam == MikSBT.COMMAND_STATS) then
-- Print out stats about the event recycler.
eventsRecycler:PrintStats();
-- Don't show the usage info.
showUsage = false;
elseif (currentParam == MikSBT.COMMAND_SEARCH) then
-- Check if there is any filter text
if (remainingParams and remainingParams ~= "") then
-- Turn on event searching mode.
MikCEH.EnableEventSearching(remainingParams);
MikSBT.Print(MikSBT.MSG_SEARCH_ENABLE .. remainingParams, 1, 1, 1);
else
-- Turn off event searching mode.
MikCEH.DisableEventSearching();
MikSBT.Print(MikSBT.MSG_SEARCH_DISABLE, 1, 1, 1);
end
-- Don't show the usage info.
showUsage = false;
elseif (currentParam == MikSBT.COMMAND_DEBUG) then
-- Toggle the debug mode option.
debugMode = not debugMode;
-- Check if debug mode was enabled or disabled and output the appropriate message.
if (debugMode) then
MikSBT.Print(MikSBT.MSG_DEBUG_ENABLE, 1,1,1);
else
MikSBT.Print(MikSBT.MSG_DEBUG_DISABLE, 1,1,1);
end
-- Don't show the usage info.
showUsage = false;
end
-- Check if the usage information should be shown.
if (showUsage) then
MikSBT.PrintUsage();
end
end
-- **********************************************************************************
-- Prints out the usage information.
-- **********************************************************************************
function MikSBT.PrintUsage()
-- Loop through all of the entries in the command usage list.
for _, msg in MikSBT.COMMAND_USAGE do
MikSBT.Print(msg, 1, 1, 1);
end
end
-------------------------------------------------------------------------------------
-- Public Access functions.
-------------------------------------------------------------------------------------
-- **********************************************************************************
-- Displays the passed message in the passed scroll area with the passed parameters.
-- This function is for easy displaying of messages from external sources.
-- **********************************************************************************
function MikSBT.DisplayMessage(message, displayType, isSticky, colorR, colorG, colorB, fontSize, fontIndex, outlineIndex, Texture)
-- Check if no message was passed and do nothing.
if (message == nil) then
return;
end
-- Check if the passed display type is invalid..
if (displayType ~= MikSBT.DISPLAYTYPE_INCOMING and displayType ~= MikSBT.DISPLAYTYPE_OUTGOING and
displayType ~= MikSBT.DISPLAYTYPE_NOTIFICATION) then
-- Set the display type to the default.
displayType = MikSBT.DISPLAYTYPE_NOTIFICATION;
end
-- Get the correct scroll area for the event.
local scrollArea;
if (displayType == MikSBT.DISPLAYTYPE_INCOMING) then
scrollArea = scrollAreas.Incoming;
elseif (displayType == MikSBT.DISPLAYTYPE_OUTGOING) then
scrollArea = scrollAreas.Outgoing;
elseif (displayType == MikSBT.DISPLAYTYPE_NOTIFICATION) then
scrollArea = scrollAreas.Notification;
end
-- Check if the scroll area is hiding events.
if (not scrollArea.DisplaySettings.Show) then
return;
end
-- Check if the red color value is invalid.
if (colorR == nil or colorR < 0 or colorR > 255) then
-- Set the red color value to the default.
colorR = 255;
end
-- Check if the green color value is invalid.
if (colorG == nil or colorG < 0 or colorG > 255) then
-- Set the green color value to the default.
colorG = 255;
end
-- Check if the blue color value is invalid.
if (colorB == nil or colorB < 0 or colorB > 255) then
-- Set the blue color value to the default.
colorB = 255;
end
-- Check if the font size is invalid.
if (fontSize == nil or fontSize < 12 or fontSize > 64) then
fontSize = MikSBT.GetFontSize(scrollArea, nil, false);
end
local fontPath;
-- Check if the font index in invalid.
if (fontIndex == nil or MikSBT.AVAILABLE_FONTS[fontIndex] == nil) then
fontPath = MikSBT.GetFontPath(scrollArea, nil, false);
-- Font index is valid.
else
fontPath = MikSBT.AVAILABLE_FONTS[fontIndex].Path;
end
local fontOutline;
-- Check if the outline index in invalid.
if (outlineIndex == nil or MikSBT.AVAILABLE_OUTLINES[outlineIndex] == nil) then
fontOutline = MikSBT.GetFontOutline(scrollArea, nil, false);
else
fontOutline = MikSBT.AVAILABLE_OUTLINES[outlineIndex].Style;
end
-- Get a recycled table from the events recycler object.
local animationEvent = eventsRecycler:AcquireTable();
-- Populate the animation event data.
animationEvent.ScrollArea = scrollArea;
animationEvent.IsCrit = false;
animationEvent.IsSticky = isSticky;
animationEvent.Text = message;
animationEvent.FontSize = fontSize;
animationEvent.FontPath = fontPath;
animationEvent.FontOutline = fontOutline;
animationEvent.ColorR = colorR;
animationEvent.ColorG = colorG;
animationEvent.ColorB = colorB;
animationEvent.Texture = Texture;
-- Add the event to the animation system.
MikSBT.AddAnimation(animationEvent);
end