--[[ Name: SpellStatus-1.0 Revision: $Rev: 14589 $ Author(s): Nightdew (denzsolnightdew@gmail.com) Website: http://www.wowace.com/index.php/SpellStatus-1.0 Documentation: http://www.wowace.com/index.php/SpellStatus-1.0 SVN: http://svn.wowace.com/root/trunk/SpellStatusLib/SpellStatus-1.0 Description: Status library that simplifies retrieving spell status information from the player Dependencies: AceLibrary, AceDebug-2.0, AceEvent-2.0, AceHook-2.1, Deformat-2.0, Gratuity-2.0, SpellCache-1.0, (optional) SpellStatus-AimedShot-1.0 ]] local MAJOR_VERSION = "SpellStatus-1.0" local MINOR_VERSION = "$Revision: 14589 $" if (not AceLibrary) then error(MAJOR_VERSION .. " requires AceLibrary.") end if (not AceLibrary:IsNewVersion(MAJOR_VERSION, MINOR_VERSION)) then return end local function CheckDependency(dependencies) for index, value in ipairs(dependencies) do if (not AceLibrary:HasInstance(value)) then error(format("%s requires %s to function properly", MAJOR_VERSION, value)) end end end local dependencyLibraries = { "AceDebug-2.0", "AceEvent-2.0", "AceHook-2.1", "Deformat-2.0", "Gratuity-2.0", "SpellCache-1.0" } CheckDependency(dependencyLibraries) local deformat = AceLibrary("Deformat-2.0") local gratuity = AceLibrary("Gratuity-2.0") local spellcache = AceLibrary("SpellCache-1.0") --Create Library Object local SpellStatus = {} --Embed all needed mixins into the Library Object SpellStatus AceLibrary("AceDebug-2.0"):embed(SpellStatus) AceLibrary("AceEvent-2.0"):embed(SpellStatus) AceLibrary("AceHook-2.1"):embed(SpellStatus) -- -- Local functions not to be called from outside -- local function InitializeHooks(self) self:Hook("CastSpell") self:Hook("CastSpellByName") self:Hook("UseAction") self:Hook("CastShapeshiftForm") self:Hook("UseInventoryItem") self:Hook("UseContainerItem") self:Hook("ToggleGameMenu") self:Hook("SpellStopCasting") end local function InitializeEventRegisters(self) self:RegisterEvent("SPELLCAST_START") self:RegisterEvent("SPELLCAST_STOP") self:RegisterEvent("SPELLCAST_INTERRUPTED") self:RegisterEvent("SPELLCAST_FAILED") self:RegisterEvent("SPELLCAST_DELAYED") self:RegisterEvent("SPELLCAST_CHANNEL_START") self:RegisterEvent("SPELLCAST_CHANNEL_STOP") self:RegisterEvent("SPELLCAST_CHANNEL_UPDATE") self:RegisterEvent("UI_ERROR_MESSAGE") self:RegisterEvent("CHAT_MSG_SPELL_FAILED_LOCALPLAYER") self:RegisterEvent("CHAT_MSG_SPELL_AURA_GONE_SELF") self:RegisterEvent("CHAT_MSG_COMBAT_SELF_HITS") self:RegisterEvent("CHAT_MSG_COMBAT_SELF_MISSES") self:RegisterEvent("CHAT_MSG_SPELL_SELF_DAMAGE") --SPELLLOGSELFOTHER --Used to determine if we are wanding. self:RegisterEvent("START_AUTOREPEAT_SPELL") self:RegisterEvent("STOP_AUTOREPEAT_SPELL") self:RegisterEvent("PLAYER_ENTER_COMBAT") self:RegisterEvent("PLAYER_LEAVE_COMBAT") self:RegisterEvent("PLAYER_REGEN_DISABLED") self:RegisterEvent("PLAYER_REGEN_ENABLED") self:RegisterEvent("SpellCache_Updated") end local function ResetActiveVariables(self) self:LevelDebug(2, "ResetActiveVariables") --Active Spell self.vars.ActiveId = nil self.vars.ActiveName = nil self.vars.ActiveRank = nil self.vars.ActiveFullName = nil self.vars.ActiveCastStartTime = nil self.vars.ActiveCastStopTime = nil self.vars.ActiveCastDuration = nil --Casting self.vars.ActiveCastDelay = nil self.vars.ActiveCastDelayTotal = nil --Channeling self.vars.ActiveAction = nil self.vars.ActiveCastDisruption = nil self.vars.ActiveCastDisruptionTotal = nil --NextMelee self.vars.NextMeleeId = nil self.vars.NextMeleeName = nil self.vars.NextMeleeRank = nil self.vars.NextMeleeFullName = nil end local function ResetVariables(self) self:LevelDebug(2, "ResetVariables") --Spell Attempted to cast self.vars.AttemptId = nil self.vars.AttemptName = nil self.vars.AttemptRank = nil self.vars.AttemptFullName = nil ResetActiveVariables(self) --UI_ERROR_MESSAGE self.vars.UIEM_Message = nil --CHATMSGSPELLFAILEDLOCALPLAYER self.vars.CMSFLP_SpellName = nil self.vars.CMSFLP_Message = nil end local function InitializeVariables(self) self:LevelDebug(2, "InitializeVariables") self.vars = {} --True while data is assigned until fail or stop or start and no SpellId self.vars.Using = false --True while data is assigned until fail or stop or start and SpellId self.vars.Preparing = false --Only true for spell with a casting time self.vars.Casting = false --Only true for channeling spells self.vars.Channeling = false --Only true for spells that are next melee self.vars.NextMeleeing = false --Whether or not the character is auto repeating self.vars.AutoRepeating = false --Are we attacking currently self.vars.Attacking = false --Are we currently in combat self.vars.Combating = false self.vars.Targeting = false --True when hooked into ToggleGameMenu self.vars.CancelTargeting = false self.vars.CancelCasting = false self.vars.CancelChanneling = false ResetVariables(self) end -- -- Activate method -- local function activate(self, oldLib, oldDeactivate) --self:SetDebugging(true) --self:SetDebugLevel(3) self:LevelDebug(2, "SpellStatus - activate") if (oldLib) then oldLib:UnregisterAllEvents() oldLib:UnhookAll() end --Default code to clean up the oldlib if (oldDeactivate) then oldDeactivate(oldLib) end InitializeVariables(self) InitializeHooks(self) InitializeEventRegisters(self) end function SpellStatus:Report() self:LevelDebug(3, format("Using: %s", tostring(self.vars.Using)), format("Preparing: %s", tostring(self.vars.Preparing)), format("Casting: %s", tostring(self.vars.Casting)), format("Channeling: %s", tostring(self.vars.Channeling)), format("SpellName: %s", tostring(self:GetActiveSpellName())) ) end function SpellStatus:IsUsing() return self.vars.Using == true end function SpellStatus:IsPreparing() return self.vars.Preparing == true end function SpellStatus:IsCasting() return self.vars.Casting == true end function SpellStatus:IsChanneling() return self.vars.Channeling == true end function SpellStatus:IsNextMeleeing() return self.vars.NextMeleeing == true end function SpellStatus:IsCastingOrChanneling() return self:IsCasting() or self:IsChanneling() end function SpellStatus:IsPreparingOrCastingOrChanneling() return self:IsPreparing() or self:IsCasting() or self:IsChanneling() end function SpellStatus:IsAutoRepeating() return self.vars.AutoRepeating == true end function SpellStatus:IsWanding() return ((self.vars.AutoRepeating == true) and HasWandEquipped()) and true or false end function SpellStatus:IsAttacking() return self.vars.Attacking == true end function SpellStatus:IsCombating() return self.vars.Combating == true end function SpellStatus:IsActiveSpell(spellId, spellName) local sId, sName = self:GetActiveSpellData() self:LevelDebug(2, "IsActiveSpell", spellId, sId, spellName, sName) --sId and spellId might be nil return (sId == spellId) and (sName == spellName) end function SpellStatus:GetActiveSpellData() return self.vars.ActiveId, self.vars.ActiveName, self.vars.ActiveRank, self.vars.ActiveFullName, self.vars.ActiveCastStartTime, self.vars.ActiveCastStopTime, self.vars.ActiveCastDuration, self.vars.ActiveAction end function SpellStatus:GetActiveSpellName() local _, spellName = self:GetActiveSpellData() return spellName end function SpellStatus:GetNextMeleeSpellData() return self.vars.NextMeleeId, self.vars.NextMeleeName, self.vars.NextMeleeRank, self.vars.NextMeleeFullName end -- -- Function Hooking -- function SpellStatus:SpellCache_Updated() end local function AssignNextMeleeSpellData(self, spellId, spellName, spellRank, spellFullName) if (not spellId) then return false end gratuity:SetSpell(spellId, BOOKTYPE_SPELL) if (gratuity:Find(SPELL_ON_NEXT_SWING, 2, 3, false, true, true) == nil) then return false end self:LevelDebug(1, "AssignNextMeleeSpellData", spellId, spellName, spellRank, spellFullName) self.vars.NextMeleeing = true self.vars.NextMeleeId = spellId self.vars.NextMeleeName = spellName self.vars.NextMeleeRank = spellRank self.vars.NextMeleeFullName = spellFullName return true end local function AssignSpellData(self, spellId, spellName, spellRank, spellFullName) --self:LevelDebug(1, "AssignSpellData", spellId, spellName, spellRank, spellFullName) --If Next Melee spell stop dont continue if (AssignNextMeleeSpellData(self, spellId, spellName, spellRank, spellFullName)) then return end if (self.vars.Preparing or self.vars.Casting or self.vars.Channeling) then return end if (self.vars.Using and (spellName == self.vars.ActiveName)) then return end self:LevelDebug(1, "AssignSpellData", spellId, spellName, spellRank, spellFullName) ResetVariables(self) self.vars.ActiveId = spellId self.vars.ActiveName = spellName self.vars.ActiveRank = spellRank self.vars.ActiveFullName = spellFullName self.vars.Preparing = spellId ~= nil self.vars.Using = not self.vars.Preparing --Only preparing if you have a spellId if (self.vars.Preparing) then local aimedShot = AceLibrary:HasInstance("SpellStatus-AimedShot-1.0") and AceLibrary("SpellStatus-AimedShot-1.0") or nil if (aimedShot and aimedShot:Active()) then local aimedShotId, aimedShotDuration = aimedShot:MatchSpellId(spellId) if (aimedShotId) then self:SPELLCAST_START(spellName, aimedShotDuration) end end end end local function TriggerFailureEvent(self, overrideHasMessage) overrideHasMessage = overrideHasMessage == true --self:LevelDebug(2, "TriggerFailureEvent", overrideHasMessage) local hasMessage = self.vars.UIEM_Message or self.vars.CMSFLP_Message self:LevelDebug(2, "TriggerFailureEvent", hasMessage, overrideHasMessage) if (not (hasMessage or overrideHasMessage)) then return false end self:LevelDebug(2, "TriggerFailureEvent", overrideHasMessage) local isActiveSpell = false local sId, sName, sRank, sFullName if (self.vars.AttemptName) then sId = self.vars.AttemptId sName = self.vars.AttemptName sRank = self.vars.AttemptRank sFullName = self.vars.AttemptFullName self:LevelDebug(2, "TriggerFailureEvent Attempt", sId, sName, sRank, sFullName) self.vars.Using = false self.vars.Preparing = false self.vars.NextMeleeing = false --Necessary because the error might happen through multiple paths self.vars.AttemptCastFailure = true elseif (self.vars.CMSFLP_SpellName and (self.vars.CMSFLP_SpellName == self.vars.NextMeleeName)) then sId = self.vars.NextMeleeId sName = self.vars.NextMeleeName sRank = self.vars.NextMeleeRank sFullName = self.vars.NextMeleeFullName self:LevelDebug(2, "TriggerFailureEvent NextMelee", sId, sName, sRank, sFullName) self.vars.NextMeleeing = false elseif (overrideHasMessage or (self.vars.CMSFLP_SpellName and (self.vars.CMSFLP_SpellName == self.vars.ActiveName))) then isActiveSpell = true sId = self.vars.ActiveId sName = self.vars.ActiveName sRank = self.vars.ActiveRank sFullName = self.vars.ActiveFullName self:LevelDebug(2, "TriggerFailureEvent Active", sId, sName, sRank, sFullName) self.vars.Using = false self.vars.Preparing = false self.vars.Casting = false self.vars.Channeling = false else --must have been unrelated return false end self:TriggerEvent("SpellStatus_SpellCastFailure", sId, sName, sRank, sFullName, isActiveSpell, self.vars.UIEM_Message, self.vars.CMSFLP_SpellName, self.vars.CMSFLP_Message ) self.vars.UIEM_Message = nil self.vars.CMSFLP_SpellName = nil self.vars.CMSFLP_Message = nil return true end function ResetCastOriginal(self, sId, sName, sRank, sFullName) --setup variables if failing immediately self.vars.AttemptId = sId self.vars.AttemptName = sName self.vars.AttemptRank = sRank self.vars.AttemptFullName = sFullName --reset Error Message self.vars.UIEM_Message = nil self.vars.CMSFLP_SpellName = nil self.vars.CMSFLP_Message = nil self.vars.AttemptCastFailure = false end function CastOriginal(self, methodName, param1, param2, param3, sId, sName, sRank, sFullName) self:LevelDebug(1, ">>>> CastOriginal", methodName, param1, param2, param3, sId, sName, sRank, sFullName) ResetCastOriginal(self, sId, sName, sRank, sFullName) self:LevelDebug(3, "-> CastOriginal") self.hooks[methodName](param1, param2, param3) self:LevelDebug(3, "<- CastOriginal") TriggerFailureEvent(self) if (not self.vars.AttemptCastFailure) then AssignSpellData(self, sId, sName, sRank, sFullName) end ResetCastOriginal(self, nil, nil, nil, nil) self:LevelDebug(1, "<<<< CastOriginal", methodName, param1, param2, param3, sId, sName, sRank, sFullName) end function SpellStatus:CastSpellByName(spellName, onSelf) self:LevelDebug(2, ">>>> CastSpellByName", spellName, onSelf) local sName, sId, sRank, sFullName sName, sRank, sId, sFullName = spellcache:GetSpellData(spellName) CastOriginal(self, "CastSpellByName", spellName, onSelf, nil, sId, sName, sRank, sFullName) self:LevelDebug(2, "<<<< CastSpellByName2", spellName, onSelf) end function SpellStatus:CastSpell(spellId, spellbookType) self:LevelDebug(2, ">>>> CastSpell", spellId, spellbookType) local sId, sName, sRank, sFullName if (spellbookType == BOOKTYPE_SPELL) then sName, sRank, sId, sFullName = spellcache:GetSpellData(spellId) end CastOriginal(self, "CastSpell", spellId, spellbookType, nil, sId, sName, sRank, sFullName) self:LevelDebug(2, "<<<< CastSpell", spellId, spellbookType) end --When using UseAction through clicking an actiobar button, we need to get in local function GetGratuitySpellData(self, slotId) local spellName = gratuity:GetLine(1) local spellRank = gratuity:GetLine(1, true) --empty slot? if (not spellName) then return end self:LevelDebug(3, "GetGratuitySpellData", spellName, spellRank) local sName, sRank, sId, sFullName = spellcache:GetSpellData(spellName, spellRank) if (sName) then return sId, sName, sRank, sFullName else return nil, spellName, nil, nil end end function SpellStatus:UseAction(slotId, checkCursor, onSelf) self:LevelDebug(1, ">>>> UseAction", slotId, checkCursor, onSelf) local actionText = GetActionText(slotId) local isMacro = actionText ~= nil --self:LevelDebug(2, "UseAction", actionText, tostring(isMacro)) if (isMacro) then self.hooks["UseAction"](slotId, checkCursor, onSelf) else gratuity:SetAction(slotId) local sId, sName, sRank, sFullName = GetGratuitySpellData(self, slotId) if (sName) then CastOriginal(self, "UseAction", slotId, checkCursor, onSelf, sId, sName, sRank, sFullName) else self.hooks["UseAction"](slotId, checkCursor, onSelf) end end self:LevelDebug(1, "<<<< UseAction", slotId, checkCursor, onSelf) end function SpellStatus:CastShapeshiftForm(index) self:LevelDebug(2, ">>>> CastShapeshiftForm", index) gratuity:SetShapeshift(index) local sId, sName, sRank, sFullName = GetGratuitySpellData(self, slotId) if (sName) then CastOriginal(self, "CastShapeshiftForm", index, nil, nil, sId, sName, sRank, sFullName) else self.hooks["CastShapeshiftForm"](index) end self:LevelDebug(2, "<<<< CastShapeshiftForm", index) end local function GetItemLinkData(slotId, bagId) local itemLink = nil if (bagId == nil) then itemLink = GetInventoryItemLink("player", slotId) else itemLink = GetContainerItemLink(bagId, slotId) end if (itemLink) then local _, _, itemName = string.find(itemLink, "^.*%[(.*)%].*$") return itemName, itemLink end end function SpellStatus:UseInventoryItem(slotId) self:LevelDebug(2, ">>>> UseInventoryItem", slotId) local itemName, itemLink = GetItemLinkData(slotId) if (itemLink) then CastOriginal(self, "UseInventoryItem", slotId, nil, nil, nil, itemName, nil, itemLink) else self.hooks["UseInventoryItem"](slotId) end self:LevelDebug(2, "<<<< UseInventoryItem", slotId) end function SpellStatus:UseContainerItem(bagId, slotId) self:LevelDebug(2, ">>>> UseContainerItem", bagId, slotId) local itemName, itemLink = GetItemLinkData(slotId, bagId) if (itemLink) then CastOriginal(self, "UseContainerItem", bagId, slotId, nil, nil, itemName, nil, itemLink) else self.hooks["UseContainerItem"](bagId, slotId) end self:LevelDebug(2, "<<<< UseContainerItem", bagId, slotId) end function SpellStatus:ToggleGameMenu() --self:LevelDebug(3, "ToggleGameMenu1", SpellIsTargeting()) --If these hook is called and targeting, targeting will always be killed first self.vars.CancelTargeting = SpellIsTargeting() self.vars.CancelCasting = self.vars.Casting self.vars.CancelChanneling = self.vars.Channeling self.hooks["ToggleGameMenu"]() self.vars.CancelTargeting = false self.vars.CancelCasting = false self.vars.CancelChanneling = false --self:LevelDebug(3, "ToggleGameMenu2", SpellIsTargeting()) end function SpellStatus:SpellStopCasting() self:LevelDebug(2, ">>>> SpellStopCasting") if (self:IsCastingOrChanneling()) then self.vars.SpellStopCastingActiveName = self.vars.ActiveName end self.hooks["SpellStopCasting"]() self:LevelDebug(2, "<<<< SpellStopCasting") end function SpellStatus:SPELLCAST_START(spellName, duration) self:LevelDebug(1, "SPELLCAST_START", spellName, duration) self.vars.SpellStopCastingActiveName = nil self.vars.Using = false self.vars.Preparing = false self.vars.Casting = true if (self.vars.ActiveName ~= spellName) then ResetActiveVariables(self) self.vars.ActiveName = spellName self.vars.ActiveFullName = spellName end self.vars.ActiveCastStartTime = GetTime() self.vars.ActiveCastDuration = duration self.vars.ActiveCastStopTime = self.vars.ActiveCastStartTime + (self.vars.ActiveCastDuration/1000) self.vars.ActiveCastDelay = nil self.vars.ActiveCastDelayTotal = nil self:TriggerEvent( "SpellStatus_SpellCastCastingStart", self.vars.ActiveId, self.vars.ActiveName, self.vars.ActiveRank, self.vars.ActiveFullName, self.vars.ActiveCastStartTime, self.vars.ActiveCastStopTime, self.vars.ActiveCastDuration ) end function SpellStatus:SPELLCAST_DELAYED(delay) self:LevelDebug(1, "SPELLCAST_DELAYED", delay) if (self.vars.ActiveCastStopTime == nil) then return end self.vars.ActiveCastDelay = delay/1000 self.vars.ActiveCastDelayTotal = (self.vars.ActiveCastDelayTotal or 0) + self.vars.ActiveCastDelay self.vars.ActiveCastStopTime = self.vars.ActiveCastStopTime + self.vars.ActiveCastDelay self:TriggerEvent( "SpellStatus_SpellCastCastingChange", self.vars.ActiveId, self.vars.ActiveName, self.vars.ActiveRank, self.vars.ActiveFullName, self.vars.ActiveCastStartTime, self.vars.ActiveCastStopTime, self.vars.ActiveCastDuration, self.vars.ActiveCastDelay, self.vars.ActiveCastDelayTotal ) end function SpellStatus:SPELLCAST_STOP() self:LevelDebug(1, "SPELLCAST_STOP") --If canceling targeting.. ignore stop! if (self.vars.CancelTargeting or self.vars.CancelCasting or self.vars.CancelChanneling) then return end --if (self.vars.ActiveId == nil) then if (self.vars.ActiveName == nil) then return end --Player probably moved initiating a client and server side SPELLCAST_STOP if (not( self.vars.Using or self.vars.Preparing or self.vars.Casting or self.vars.Channeling or self.vars.AutoRepeating )) then return end if (TriggerFailureEvent(self)) then return end self.vars.Using = false self.vars.Preparing = false --Second SPELLCAST_STOP received from server after SpellStopCasting was called if ((not self.vars.Casting) and self.vars.SpellStopCastingActiveName and (self.vars.ActiveName == self.vars.SpellStopCastingActiveName)) then self.vars.SpellStopCastingActiveName = nil return end local castingInstant = (not self.vars.Casting) and (not self.vars.Channeling) self.vars.Casting = false --Generate Finish Event if not channeling if (not self.vars.Channeling) then local sD = castingInstant and self or self.vars.ActiveCastingData self.vars.ActiveCastStartTime = self.vars.ActiveCastStartTime or GetTime() self.vars.ActiveCastStopTime = GetTime() self:TriggerEvent( castingInstant and "SpellStatus_SpellCastInstant" or "SpellStatus_SpellCastCastingFinish", self.vars.ActiveId, self.vars.ActiveName, self.vars.ActiveRank, self.vars.ActiveFullName, self.vars.ActiveCastStartTime, self.vars.ActiveCastStopTime, self.vars.ActiveCastDuration, self.vars.ActiveCastDelayTotal ) --We cant reset because maybe we have a failure!!! --ResetActiveVariables(self) end end function SpellStatus:SPELLCAST_CHANNEL_START(duration, action) self:LevelDebug(2, "SPELLCAST_CHANNEL_START", duration, action) self.vars.SpellStopCastingActiveName = nil self.vars.Using = false self.vars.Preparing = false self.vars.Channeling = true local sD = self.vars.ActiveChannelingData self.vars.ActiveCastStartTime = GetTime() self.vars.ActiveCastDuration = duration / 1000 self.vars.ActiveCastStopTime = self.vars.ActiveCastStartTime + self.vars.ActiveCastDuration self.vars.ActiveAction = action self.vars.ActiveId = self.vars.ActiveId self.vars.ActiveName = self.vars.ActiveName self.vars.ActiveRank = self.vars.ActiveRank self.vars.ActiveFullName = self.vars.ActiveFullName self.vars.ActiveCastDisruption = nil self.vars.ActiveCastDisruptionTotal = nil self:TriggerEvent( "SpellStatus_SpellCastChannelingStart", self.vars.ActiveId, self.vars.ActiveName, self.vars.ActiveRank, self.vars.ActiveFullName, self.vars.ActiveCastStartTime, self.vars.ActiveCastStopTime, self.vars.ActiveCastDuration, self.vars.ActiveAction ) end --Any changes while channeling will come through this event function SpellStatus:SPELLCAST_CHANNEL_UPDATE(duration) self:LevelDebug(2, "SPELLCAST_CHANNEL_UPDATE", duration) local sD = self.vars.ActiveChannelingData local timeStamp = GetTime() --New Duration local spellCastDuration = duration / 1000 self:LevelDebug(3, "spellCastDuration", spellCastDuration) --Store old stop time local spellCastStopTime = self.vars.ActiveCastStopTime --Time Passed since Channeling local spellCastTimePassed = timeStamp - self.vars.ActiveCastStartTime self:LevelDebug(3, "spellCastTimePassed", spellCastTimePassed) --What channel time did we loose self.vars.ActiveCastDisruption = self.vars.ActiveCastDuration - spellCastDuration - spellCastTimePassed self.vars.ActiveCastDisruptionTotal = (self.vars.ActiveCastDisruptionTotal or 0) + self.vars.ActiveCastDisruption self.vars.ActiveCastStopTime = timeStamp + spellCastDuration self:TriggerEvent( "SpellStatus_SpellCastChannelingChange", self.vars.ActiveId, self.vars.ActiveName, self.vars.ActiveRank, self.vars.ActiveFullName, self.vars.ActiveCastStartTime, self.vars.ActiveCastStopTime, self.vars.ActiveCastDuration, self.vars.ActiveAction, self.vars.ActiveCastDisruption, self.vars.ActiveCastDisruptionTotal ) end function SpellStatus:SPELLCAST_CHANNEL_STOP() self:LevelDebug(2, "SPELLCAST_CHANNEL_STOP") self.vars.Using = false self.vars.Preparing = false self.vars.Channeling = false self.vars.ActiveCastStopTime = GetTime() self:TriggerEvent( "SpellStatus_SpellCastChannelingFinish", self.vars.ActiveId, self.vars.ActiveName, self.vars.ActiveRank, self.vars.ActiveFullName, self.vars.ActiveCastStartTime, self.vars.ActiveCastStopTime, self.vars.ActiveCastDuration, self.vars.ActiveAction, self.vars.ActiveCastDisruptionTotal ) end --Always thrown before CHAT_MSG_SPELL_FAILED_LOCALPLAYER which might be thrown function SpellStatus:UI_ERROR_MESSAGE(message) self:LevelDebug(2, "UI_ERROR_MESSAGE", message) self.vars.UIEM_Message = message end local FAILUREMESSAGE = { SPELLFAILCASTSELF, SPELLFAILPERFORMSELF } function SpellStatus:CHAT_MSG_SPELL_FAILED_LOCALPLAYER(message) self:LevelDebug(2, "CHAT_MSG_SPELL_FAILED_LOCALPLAYER", message) table.foreach(FAILUREMESSAGE, function(key, value) local spellName, spellFailureMessage = deformat:Deformat(message, value) if (spellName) then self.vars.CMSFLP_SpellName = spellName self.vars.CMSFLP_Message = spellFailureMessage end --if returning ~= nil the foreach will stop. return spellName end ) end local CHAT_MSG_SPELL_SELF_DAMAGEMESSAGES = { IMMUNESPELLSELFOTHER, SIMPLECASTSELFOTHER, SIMPLEPERFORMSELFOTHER, SPELLBLOCKEDSELFOTHER, SPELLDODGEDSELFOTHER, SPELLEVADEDSELFOTHER, SPELLIMMUNESELFOTHER, SPELLINTERRUPTSELFOTHER, SPELLLOGABSORBSELFOTHER, SPELLLOGABSORBSELFSELF, SPELLLOGCRITSCHOOLSELFOTHER, SPELLLOGCRITSCHOOLSELFSELF, SPELLLOGCRITSELFOTHER, SPELLLOGSCHOOLSELFOTHER, SPELLLOGSCHOOLSELFSELF, SPELLLOGSELFOTHER, SPELLMISSSELFOTHER, SPELLPARRIEDSELFOTHER, SPELLREFLECTSELFOTHER, SPELLRESISTSELFOTHER, SPELLRESISTSELFSELF, SPELLTERSEPERFORM_SELF, SPELLTERSE_SELF } local CHAT_MSG_SPELL_SELF_DAMAGEMESSAGETRAILERS = { "", ABSORB_TRAILER, BLOCK_TRAILER, CRUSHING_TRAILER, GLANCING_TRAILER, RESIST_TRAILER, VULNERABLE_TRAILER } function ParseCHAT_MSG_SPELL_SELF_DAMAGE(self, message, damageMessage, damageMessageTrailer) local spellName = deformat:Deformat(message, damageMessage..damageMessageTrailer) --self:LevelDebug(2, "ParseCHAT_MSG_SPELL_SELF_DAMAGE", self.vars.NextMeleeing, self.vars.NextMeleeName, spellName) if (spellName and (spellName == self.vars.NextMeleeName)) then self.vars.NextMeleeing = false self:TriggerEvent( "SpellStatus_SpellCastInstant", self.vars.NextMeleeId, self.vars.NextMeleeName, self.vars.NextMeleeRank, self.vars.NextMeleeFullName ) end end function SpellStatus:CHAT_MSG_SPELL_SELF_DAMAGE(message) self:LevelDebug(2, "CHAT_MSG_SPELL_SELF_DAMAGE", message) if (not self.vars.NextMeleeing) then return end table.foreach( CHAT_MSG_SPELL_SELF_DAMAGEMESSAGES, function(_, damageMessage) table.foreach( CHAT_MSG_SPELL_SELF_DAMAGEMESSAGETRAILERS, function(_, damageMessageTrailer) ParseCHAT_MSG_SPELL_SELF_DAMAGE(self, message, damageMessage, damageMessageTrailer) if (not self.vars.NextMeleeing) then return true end end ) if (not self.vars.NextMeleeing) then return true end end ) end function SpellStatus:CHAT_MSG_SPELL_AURA_GONE_SELF(message) self:LevelDebug(2, "CHAT_MSG_SPELL_AURA_GONE_SELF", message) if (not(self.vars.Using or self.vars.Preparing or self.vars.Casting or self.vars.Channeling)) then return end local spellName = deformat:Deformat(message, AURAREMOVEDSELF) if ((not spellName) or (spellName ~= self.vars.ActiveName)) then return end self.vars.Using = false self.vars.Preparing = false self.vars.Casting = false self.vars.Channeling = false self:TriggerEvent( "SpellStatus_SpellCastCancelAura", self.vars.ActiveId, self.vars.ActiveName, self.vars.ActiveRank, self.vars.ActiveFullName or self.vars.ActiveName, GetTime() ) end --Failed is called only when it was semi possible to cast the spell function SpellStatus:SPELLCAST_INTERRUPTED() self:LevelDebug(2, "SPELLCAST_INTERRUPTED") TriggerFailureEvent(self, true) end function SpellStatus:SPELLCAST_FAILED() self:LevelDebug(2, "SPELLCAST_FAILED") TriggerFailureEvent(self, true) end function SpellStatus:START_AUTOREPEAT_SPELL() self:LevelDebug(2, "START_AUTOREPEAT_SPELL") self.vars.AutoRepeating = true end function SpellStatus:STOP_AUTOREPEAT_SPELL() self:LevelDebug(2, "STOP_AUTOREPEAT_SPELL") self.vars.AutoRepeating = false end function SpellStatus:PLAYER_ENTER_COMBAT() self:LevelDebug(2, "PLAYER_ENTER_COMBAT") self.vars.Attacking = true end function SpellStatus:PLAYER_LEAVE_COMBAT() self:LevelDebug(2, "PLAYER_LEAVE_COMBAT") self.vars.Attacking = false end function SpellStatus:PLAYER_REGEN_DISABLED() self:LevelDebug(2, "PLAYER_REGEN_DISABLED") self.vars.Combating = true end function SpellStatus:PLAYER_REGEN_ENABLED() self:LevelDebug(2, "PLAYER_REGEN_ENABLED") self.vars.Combating = false end function SpellStatus:CHAT_MSG_COMBAT_SELF_HITS(chatMessage) self:LevelDebug(2, "CHAT_MSG_COMBAT_SELF_HITS", chatMessage) end function SpellStatus:CHAT_MSG_COMBAT_SELF_MISSES(chatMessage) self:LevelDebug(2, "CHAT_MSG_COMBAT_SELF_MISSES", chatMessage) end -- -- Final Registration of the library. -- AceLibrary:Register(SpellStatus, MAJOR_VERSION, MINOR_VERSION, activate) SpellStatus = AceLibrary(MAJOR_VERSION)