You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1130 lines
62 KiB
1130 lines
62 KiB
|
2 years ago
|
---@class DBMCoreNamespace
|
||
|
|
local private = select(2, ...)
|
||
|
|
|
||
|
|
local L = DBM_CORE_L
|
||
|
|
|
||
|
|
local DBMScheduler = private:GetModule("DBMScheduler")
|
||
|
|
local stringUtils = private:GetPrototype("StringUtils")
|
||
|
|
local tableUtils = private:GetPrototype("TableUtils")
|
||
|
|
|
||
|
|
---@class DBM
|
||
|
|
local DBM = private:GetPrototype("DBM")
|
||
|
|
---@class Announce
|
||
|
|
local announcePrototype = private:GetPrototype("Announce")
|
||
|
|
---@class DBMMod
|
||
|
|
local bossModPrototype = private:GetPrototype("DBMMod")
|
||
|
|
|
||
|
|
local pformat = stringUtils.pformat
|
||
|
|
local removeEntry = tableUtils.removeEntry
|
||
|
|
|
||
|
|
---@class Timer
|
||
|
|
local timerPrototype = private:GetPrototype("Timer")
|
||
|
|
local mt = {__index = timerPrototype}
|
||
|
|
local countvoice1, countvoice2, countvoice3, countvoice4
|
||
|
|
local countvoice1max, countvoice2max, countvoice3max, countvoice4max = 5, 5, 5, 5
|
||
|
|
local countpath1, countpath2, countpath3, countpath4
|
||
|
|
|
||
|
|
--Merged countdown object for timers with build-in countdown
|
||
|
|
function DBM:BuildVoiceCountdownCache()
|
||
|
|
countvoice1 = self.Options.CountdownVoice
|
||
|
|
countvoice2 = self.Options.CountdownVoice2
|
||
|
|
countvoice3 = self.Options.CountdownVoice3
|
||
|
|
countvoice4 = self.Options.PullVoice
|
||
|
|
for _, count in pairs(DBM:GetCountSounds()) do
|
||
|
|
if count.value == countvoice1 then
|
||
|
|
countpath1 = count.path
|
||
|
|
countvoice1max = count.max
|
||
|
|
end
|
||
|
|
if count.value == countvoice2 then
|
||
|
|
countpath2 = count.path
|
||
|
|
countvoice2max = count.max
|
||
|
|
end
|
||
|
|
if count.value == countvoice3 then
|
||
|
|
countpath3 = count.path
|
||
|
|
countvoice3max = count.max
|
||
|
|
end
|
||
|
|
if count.value == countvoice4 then
|
||
|
|
countpath4 = count.path
|
||
|
|
countvoice4max = count.max
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
local function playCountSound(_, path, requiresCombat) -- timerId, path
|
||
|
|
if requiresCombat and not (InCombatLockdown() or UnitAffectingCombat("player")) then return end
|
||
|
|
DBM:PlaySoundFile(path)
|
||
|
|
end
|
||
|
|
|
||
|
|
local function playCountdown(timerId, timer, voice, count, requiresCombat)
|
||
|
|
if DBM.Options.DontPlayCountdowns then return end
|
||
|
|
timer = timer or 10
|
||
|
|
count = count or 4
|
||
|
|
voice = voice or 1
|
||
|
|
if timer <= count then count = math.floor(timer) end
|
||
|
|
if not countpath1 or not countpath2 or not countpath3 then
|
||
|
|
DBM:Debug("Voice cache not built at time of playCountdown. On fly caching.", 3)
|
||
|
|
DBM:BuildVoiceCountdownCache()
|
||
|
|
end
|
||
|
|
local maxCount, path
|
||
|
|
if type(voice) == "string" then
|
||
|
|
maxCount = 5--Safe to assume if it's not one of the built ins, it's likely heroes/OW, which has a max of 5
|
||
|
|
path = voice
|
||
|
|
elseif voice == 2 then
|
||
|
|
maxCount = countvoice2max or 10
|
||
|
|
path = countpath2 or "Interface\\AddOns\\DBM-Core\\Sounds\\Kolt\\"
|
||
|
|
elseif voice == 3 then
|
||
|
|
maxCount = countvoice3max or 5
|
||
|
|
path = countpath3 or "Interface\\AddOns\\DBM-Core\\Sounds\\Smooth\\"
|
||
|
|
elseif voice == 4 then
|
||
|
|
maxCount = countvoice4max or 10
|
||
|
|
path = countpath4 or "Interface\\AddOns\\DBM-Core\\Sounds\\Corsica\\"
|
||
|
|
else
|
||
|
|
maxCount = countvoice1max or 10
|
||
|
|
path = countpath1 or "Interface\\AddOns\\DBM-Core\\Sounds\\Corsica\\"
|
||
|
|
end
|
||
|
|
if not path then--Should not happen but apparently it does somehow
|
||
|
|
DBM:Debug("Voice path failed in countdownProtoType:Start.")
|
||
|
|
return
|
||
|
|
end
|
||
|
|
if count == 0 then--If a count of 0 is passed,then it's a "Countout" timer, not "Countdown"
|
||
|
|
for i = 1, timer do
|
||
|
|
if i < maxCount then
|
||
|
|
DBM:Schedule(i, playCountSound, timerId, path .. i .. ".ogg", requiresCombat)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
else
|
||
|
|
for i = count, 1, -1 do
|
||
|
|
if i <= maxCount then
|
||
|
|
DBM:Schedule(timer - i, playCountSound, timerId, path .. i .. ".ogg", requiresCombat)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
--"break" and "pull" timers have custom classifications that are straight forward and not in this table
|
||
|
|
local timerTypeSimplification = {
|
||
|
|
--All cooldown times, be they approx cd or next exact, or even AI timers, map to "CD"
|
||
|
|
["cdcount"] = "cd",
|
||
|
|
["cdsource"] = "cd",
|
||
|
|
["nextcount"] = "cd",
|
||
|
|
["nextsource"] = "cd",
|
||
|
|
["cdspecial"] = "cd",
|
||
|
|
["nextspecial"] = "cd",
|
||
|
|
["ai"] = "cd",
|
||
|
|
["adds"] = "cd",
|
||
|
|
["addscustom"] = "cd",
|
||
|
|
["cdnp"] = "cd",
|
||
|
|
["nextnp"] = "cd",
|
||
|
|
|
||
|
|
--RPs all map to "warmup"
|
||
|
|
["roleplay"] = "warmup",
|
||
|
|
["combat"] = "warmup",
|
||
|
|
|
||
|
|
--all stage types will map to "stage"
|
||
|
|
["achievement"] = "stage",
|
||
|
|
["stagecount"] = "stage",
|
||
|
|
["stagecountcycle"] = "stage",
|
||
|
|
["stagecontext"] = "stage",
|
||
|
|
["stagecontextcount"] = "stage",
|
||
|
|
["intermission"] = "stage",
|
||
|
|
["intermissioncount"] = "stage",
|
||
|
|
|
||
|
|
--Target Bars such as buff/debuff on another player, on self, or on the boss, RPs all map to "target"
|
||
|
|
["targetcount"] = "target",
|
||
|
|
["fades"] = "target",--Fades is usually used as a personal target timer. So like debuff on other player is "debuff (targetname)" but on self it's just "debuff fades"
|
||
|
|
|
||
|
|
--All cast bar types map to "cast"
|
||
|
|
["active"] = "cast",--Active bars are usually things like Whirlwind is active on the boss, or a channeled cast is being done. so effectively it's for channeled casts, as upposed to regular casts
|
||
|
|
["castsource"] = "cast",
|
||
|
|
["castcount"] = "cast",
|
||
|
|
}
|
||
|
|
|
||
|
|
--Very similar to above but more specific to key replacement and not type replacement, to match BW behavior for unification of WAs
|
||
|
|
local waKeyOverrides = {
|
||
|
|
["combat"] = "warmup",
|
||
|
|
["roleplay"] = "warmup",
|
||
|
|
["achievement"] = "stages",
|
||
|
|
["stagecount"] = "stages",
|
||
|
|
["stagecountcycle"] = "stages",
|
||
|
|
["stagecontext"] = "stages",
|
||
|
|
["stagecontextcount"] = "stages",
|
||
|
|
["intermission"] = "stages",
|
||
|
|
["intermissioncount"] = "stages",
|
||
|
|
}
|
||
|
|
|
||
|
|
function timerPrototype:Start(timer, ...)
|
||
|
|
if not self.mod.isDummyMod then--Don't apply following rulesets to pull timers and such
|
||
|
|
if DBM.Options.DontShowBossTimers and not self.mod.isTrashMod then return end
|
||
|
|
if DBM.Options.DontShowTrashTimers and self.mod.isTrashMod then return end
|
||
|
|
end
|
||
|
|
if timer and type(timer) ~= "number" then
|
||
|
|
return self:Start(nil, timer, ...) -- first argument is optional!
|
||
|
|
end
|
||
|
|
if not self.option or self.mod.Options[self.option] then
|
||
|
|
local isCountTimer = false
|
||
|
|
if self.type and (self.type == "cdcount" or self.type == "nextcount" or self.type == "stagecount" or self.type == "stagecontextcount" or self.type == "stagecountcycle" or self.type == "intermissioncount") then
|
||
|
|
isCountTimer = true
|
||
|
|
end
|
||
|
|
if isCountTimer and not self.allowdouble then--remove previous timer.
|
||
|
|
for i = #self.startedTimers, 1, -1 do
|
||
|
|
if DBM.Options.BadTimerAlert or DBM.Options.DebugMode and DBM.Options.DebugLevel > 1 then
|
||
|
|
local bar = DBT:GetBar(self.startedTimers[i])
|
||
|
|
if bar then
|
||
|
|
local remaining = ("%.1f"):format(bar.timer)
|
||
|
|
local ttext = _G[bar.frame:GetName() .. "BarName"]:GetText() or ""
|
||
|
|
ttext = ttext .. "(" .. self.id .. ")"
|
||
|
|
if bar.timer > 0.2 then
|
||
|
|
local phaseText = self.mod.vb.phase and " (" .. SCENARIO_STAGE:format(self.mod.vb.phase) .. ")" or ""
|
||
|
|
if DBM.Options.BadTimerAlert and bar.timer > 1 then--If greater than 1 seconds off, report this out of debug mode to all users
|
||
|
|
DBM:AddMsg("Timer " .. ttext .. phaseText .. " refreshed before expired. Remaining time is : " .. remaining .. ". Please report this bug", nil, nil, nil, true)
|
||
|
|
DBM:FireEvent("DBM_Debug", "Timer " .. ttext .. phaseText .. " refreshed before expired. Remaining time is : " .. remaining .. ". Please report this bug", 2)
|
||
|
|
else
|
||
|
|
DBM:Debug("Timer " .. ttext .. phaseText .. " refreshed before expired. Remaining time is : " .. remaining, 2, true)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
DBT:CancelBar(self.startedTimers[i])
|
||
|
|
DBM:Unschedule(playCountSound, self.startedTimers[i])
|
||
|
|
DBM:FireEvent("DBM_TimerStop", self.startedTimers[i])
|
||
|
|
tremove(self.startedTimers, i)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
timer = timer and ((timer > 0 and timer) or self.timer + timer) or self.timer
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
--AI timer api:
|
||
|
|
--Starting ai timer with (1) indicates it's a first timer after pull
|
||
|
|
--Starting timer with (2) or (3) indicates it's a stage 2 or stage 3 first timer
|
||
|
|
--Starting AI timer with anything above 3 indicarets it's a regular timer and to use shortest time in between two regular casts
|
||
|
|
if self.type == "ai" then--A learning timer
|
||
|
|
if not DBM.Options.AITimer then return end
|
||
|
|
if timer > 5 then--Normal behavior.
|
||
|
|
local newPhase = false
|
||
|
|
for i = 1, 5 do
|
||
|
|
--Check for any phase timers that are strings, if a string it means last cast of this ability was first case of a given stage
|
||
|
|
if self["phase" .. i .. "CastTimer"] and type(self["phase" .. i .. "CastTimer"]) == "string" then--This is first cast of spell, we need to generate self.firstPullTimer
|
||
|
|
self["phase" .. i .. "CastTimer"] = tonumber(self["phase" .. i .. "CastTimer"])
|
||
|
|
self["phase" .. i .. "CastTimer"] = GetTime() - self["phase" .. i .. "CastTimer"]--We have generated a self.phase1CastTimer! Next pull, DBM should know timer for first cast next pull. FANCY!
|
||
|
|
DBM:Debug("AI timer learned a first timer for current phase of " .. self["phase" .. i .. "CastTimer"], 2)
|
||
|
|
newPhase = true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if self.lastCast and not newPhase then--We have a GetTime() on last cast and it's not affected by a phase change
|
||
|
|
local timeLastCast = GetTime() - self.lastCast--Get time between current cast and last cast
|
||
|
|
if timeLastCast > 5 then--Prevent infinite loop cpu hang. Plus anything shorter than 5 seconds doesn't need a timer
|
||
|
|
if not self.lowestSeenCast or (self.lowestSeenCast and self.lowestSeenCast > timeLastCast) then--Always use lowest seen cast for a timer
|
||
|
|
self.lowestSeenCast = timeLastCast
|
||
|
|
DBM:Debug("AI timer learned a new lowest timer of " .. self.lowestSeenCast, 2)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
self.lastCast = GetTime()
|
||
|
|
if self.lowestSeenCast then--Always use lowest seen cast for timer
|
||
|
|
timer = self.lowestSeenCast
|
||
|
|
else
|
||
|
|
return--Don't start the bogus timer shoved into timer field in the mod
|
||
|
|
end
|
||
|
|
else--AI timer passed with 5 or less is indicating phase change, with timer as phase number
|
||
|
|
if not private.isRetail then
|
||
|
|
timer = math.floor(timer)--Floor inprecise timers in classic because combat is mostly caused by PLAYER_REGEN in dungeons
|
||
|
|
end
|
||
|
|
if self["phase" .. timer .. "CastTimer"] and type(self["phase" .. timer .. "CastTimer"]) == "number" then
|
||
|
|
--Check if timer is shorter than previous learned first timer by scanning remaining time on existing bar
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
if bar then
|
||
|
|
local remaining = ("%.1f"):format(bar.timer)
|
||
|
|
if bar.timer > 0.2 then
|
||
|
|
self["phase" .. timer .. "CastTimer"] = self["phase" .. timer .. "CastTimer"] - remaining
|
||
|
|
DBM:Debug("AI timer learned a lower first timer for current phase of " .. self["phase" .. timer .. "CastTimer"], 2)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
timer = self["phase" .. timer .. "CastTimer"]
|
||
|
|
else--No first pull timer generated yet, set it to GetTime, as a string
|
||
|
|
self["phase" .. timer .. "CastTimer"] = tostring(GetTime())
|
||
|
|
return--Don't start the x second timer
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if DBM.Options.BadTimerAlert or DBM.Options.DebugMode and DBM.Options.DebugLevel > 1 then
|
||
|
|
if not self.type or (self.type ~= "target" and self.type ~= "active" and self.type ~= "fades" and self.type ~= "ai") and not self.allowdouble then
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
if bar then
|
||
|
|
local remaining = ("%.1f"):format(bar.timer)
|
||
|
|
local ttext = _G[bar.frame:GetName() .. "BarName"]:GetText() or ""
|
||
|
|
ttext = ttext .. "(" .. self.id .. ")"
|
||
|
|
if bar.timer > 0.2 then
|
||
|
|
local phaseText = self.mod.vb.phase and " (" .. SCENARIO_STAGE:format(self.mod.vb.phase) .. ")" or ""
|
||
|
|
if DBM.Options.BadTimerAlert and bar.timer > 1 then--If greater than 1 seconds off, report this out of debug mode to all users
|
||
|
|
DBM:AddMsg("Timer " .. ttext .. phaseText .. " refreshed before expired. Remaining time is : " .. remaining .. ". Please report this bug", nil, nil, nil, true)
|
||
|
|
DBM:FireEvent("DBM_Debug", "Timer " .. ttext .. phaseText .. " refreshed before expired. Remaining time is : " .. remaining .. ". Please report this bug", 2)
|
||
|
|
else
|
||
|
|
DBM:Debug("Timer " .. ttext .. phaseText .. " refreshed before expired. Remaining time is : " .. remaining, 2, true)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
local colorId
|
||
|
|
if self.option then
|
||
|
|
colorId = self.mod.Options[self.option .. "TColor"]
|
||
|
|
elseif self.colorType and type(self.colorType) == "string" then--No option for specific timer, but another bool option given that tells us where to look for TColor (for mods such as trio boss for valentines day in events mods)
|
||
|
|
colorId = self.mod.Options[self.colorType .. "TColor"]
|
||
|
|
else--No option, or secondary option, set colorId to hardcoded color type
|
||
|
|
colorId = self.colorType or 0
|
||
|
|
end
|
||
|
|
local countVoice, countVoiceMax = 0, self.countdownMax or 4
|
||
|
|
if self.option then
|
||
|
|
countVoice = self.mod.Options[self.option .. "CVoice"]
|
||
|
|
if not self.fade and (type(countVoice) == "string" or countVoice > 0) then--Started without faded and has count voice assigned
|
||
|
|
playCountdown(id, timer, countVoice, countVoiceMax, self.requiresCombat)--timerId, timer, voice, count
|
||
|
|
end
|
||
|
|
end
|
||
|
|
local bar = DBT:CreateBar(timer, id, self.icon, self.startLarge, nil, nil, nil, colorId, nil, self.keep, self.fade, countVoice, countVoiceMax, self.simpType == "cd")
|
||
|
|
if not bar then
|
||
|
|
return false, "error" -- creating the timer failed somehow, maybe hit the hard-coded timer limit of 15
|
||
|
|
end
|
||
|
|
local msg
|
||
|
|
if self.type and not self.text then
|
||
|
|
msg = pformat(self.mod:GetLocalizedTimerText(self.type, self.spellId, self.name), ...)
|
||
|
|
else
|
||
|
|
if type(self.text) == "number" then--spellId passed in timer text, it's a timer with short text
|
||
|
|
msg = pformat(self.mod:GetLocalizedTimerText(self.type, self.text, self.name), ...)
|
||
|
|
else
|
||
|
|
msg = pformat(self.text, ...)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
msg = msg:gsub(">.-<", stringUtils.stripServerName)
|
||
|
|
bar:SetText(msg, self.inlineIcon)
|
||
|
|
--ID: Internal DBM timer ID
|
||
|
|
--msg: Timer Text (Do not use msg has an event trigger, it varies language to language or based on user timer options. Use this to DISPLAY only (such as timer replacement UI). use spellId field 99% of time
|
||
|
|
--timer: Raw timer value (number).
|
||
|
|
--Icon: Texture Path for Icon
|
||
|
|
--type: Timer type, which is one of only 7 possible types: "cd" for coolodwns, "target" for target bars such as debuff on a player, "stage" for any kind of stage timer (stage ends, next stage, or even just a warmup timer like "fight begins"), and then "cast" timer which is used for both a regular cast and a channeled cast (ie boss is casting frostbolt, or boss is channeling whirlwind). Lastly, break, pull, and berserk timers are "breaK", "pull", and "berserk" respectively
|
||
|
|
--spellId: Raw spellid if available (most timers will have spellId or EJ ID unless it's a specific timer not tied to ability such as pull or combat start or rez timers. EJ id will be in format ej%d
|
||
|
|
--colorID: Type classification (1-Add, 2-Aoe, 3-targeted ability, 4-Interrupt, 5-Role, 6-Stage, 7-User(custom))
|
||
|
|
--Mod ID: Encounter ID as string, or a generic string for mods that don't have encounter ID (such as trash, dummy/test mods)
|
||
|
|
--Keep: true or nil, whether or not to keep bar on screen when it expires (if true, timer should be retained until an actual TimerStop occurs or a new TimerStart with same barId happens (in which case you replace bar with new one)
|
||
|
|
--fade: true or nil, whether or not to fade a bar (set alpha to usersetting/2)
|
||
|
|
--spellName: Sent so users can use a spell name instead of spellId, if they choose. Mostly to be more classic wow friendly, spellID is still preferred method (even for classic)
|
||
|
|
--MobGUID if it could be parsed out of args
|
||
|
|
--timerCount if current timer is a count timer. Returns number (count value) needed to have weak auras that trigger off a specific timer count without using localized message text
|
||
|
|
local guid, timerCount
|
||
|
|
if select("#", ...) > 0 then--If timer has args
|
||
|
|
for i = 1, select("#", ...) do
|
||
|
|
local v = select(i, ...)
|
||
|
|
if DBM:IsNonPlayableGUID(v) then--Then scan them for a mob guid
|
||
|
|
guid = v--If found, guid will be passed in DBM_TimerStart callback
|
||
|
|
end
|
||
|
|
--Not most efficient way to do it, but since it's already being done for guid, it's best not to repeat the work
|
||
|
|
if isCountTimer and type(v) == "number" then
|
||
|
|
timerCount = v
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
--Mods that have specifically flagged that it's safe to assume all timers from that boss mod belong to boss1
|
||
|
|
--This check is performed secondary to args scan so that no adds guids are overwritten
|
||
|
|
if not guid and self.mod.sendMainBossGUID and not DBM.Options.DontSendBossGUIDs and (self.type == "cd" or self.type == "next" or self.type == "cdcount" or self.type == "nextcount" or self.type == "cdspecial" or self.type == "ai") then
|
||
|
|
guid = UnitGUID("boss1")
|
||
|
|
end
|
||
|
|
DBM:FireEvent("DBM_TimerStart", id, msg, timer, self.icon, self.simpType, self.waSpecialKey or self.spellId, colorId, self.mod.id, self.keep, self.fade, self.name, guid, timerCount)
|
||
|
|
--Bssically tops bar from starting if it's being put on a plater nameplate, to give plater users option to have nameplate CDs without actually using the bars
|
||
|
|
--This filter will only apply to trash mods though, boss timers will always be shown due to need to have them exist for Pause, Resume, Update, and GetTime/GetRemaining methods
|
||
|
|
if guid and (self.type == "cdnp" or self.type == "nextnp") and not (DBM.Options.DebugMode and DBM.Options.DebugLevel > 1) then
|
||
|
|
DBT:CancelBar(id)--Cancel bar without stop callback
|
||
|
|
return false, "disabled"
|
||
|
|
end
|
||
|
|
if not tContains(self.startedTimers, id) then--Make sure timer doesn't exist already before adding it
|
||
|
|
tinsert(self.startedTimers, id)
|
||
|
|
end
|
||
|
|
if not self.keep then--Don't ever remove startedTimers on a schedule, if it's a keep timer
|
||
|
|
self.mod:Unschedule(removeEntry, self.startedTimers, id)
|
||
|
|
self.mod:Schedule(timer, removeEntry, self.startedTimers, id)
|
||
|
|
end
|
||
|
|
return bar
|
||
|
|
else
|
||
|
|
return false, "disabled"
|
||
|
|
end
|
||
|
|
end
|
||
|
|
timerPrototype.Show = timerPrototype.Start
|
||
|
|
|
||
|
|
--A way to set the fade to yes or no, overriding hardcoded value in NewTimer object with temporary one
|
||
|
|
--If this method is used, it WILL persist until reload or changing it back
|
||
|
|
function timerPrototype:SetFade(fadeOn, ...)
|
||
|
|
--Done this way so SetFade can be used with :Start without needless performance cost (ie, ApplyStyle won't run unless it needs to)
|
||
|
|
if fadeOn and not self.fade then
|
||
|
|
self.fade = true--set timer object metatable, which will make sure next bar started uses fade
|
||
|
|
--Find and Update an existing bar that's already started
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
if bar and not bar.fade then
|
||
|
|
DBM:FireEvent("DBM_TimerFadeUpdate", id, self.spellId, self.mod.id, true, self.name)--Timer ID, spellId, modId, true/nil, spellName (new callback only needed if we update an existing timers fade, self.fade is passed in timer start object for new timers)
|
||
|
|
bar.fade = true--Set bar object metatable, which is copied from timer metatable at bar start only
|
||
|
|
bar:ApplyStyle()
|
||
|
|
DBM:Unschedule(playCountSound, id)--Don't even need to check option, it's faster cpu wise to just unschedule countdown either way
|
||
|
|
end
|
||
|
|
elseif not fadeOn and self.fade then
|
||
|
|
self.fade = nil--set timer object metatable, which will make sure next bar started does NOT use fade
|
||
|
|
--Find and Update an existing bar that's already started
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
if bar and bar.fade then
|
||
|
|
DBM:FireEvent("DBM_TimerFadeUpdate", id, self.spellId, self.mod.id, nil, self.name)--Timer ID, spellId, modId, true/nil, spellName (new callback only needed if we update an existing timers fade, self.fade is passed in timer start object for new timers)
|
||
|
|
bar.fade = nil--Set bar object metatable, which is copied from timer metatable at bar start only
|
||
|
|
bar:ApplyStyle()
|
||
|
|
if self.option then
|
||
|
|
local countVoice = self.mod.Options[self.option .. "CVoice"] or 0
|
||
|
|
if (type(countVoice) == "string" or countVoice > 0) then--Unfading bar, start countdown
|
||
|
|
DBM:Unschedule(playCountSound, id)
|
||
|
|
playCountdown(id, bar.timer, countVoice, self.countdownMax, self.requiresCombat)--timerId, timer, voice, count
|
||
|
|
DBM:Debug("Re-enabling a countdown on bar ID: " .. id .. " after a SetFade disable call")
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
--This version does NOT set timer object meta, only started bar meta
|
||
|
|
--Use this if you only want to alter an already STARTED temporarily
|
||
|
|
--As such it also only needs fadeOn. fadeoff isn't needed since this temp alter never affects newly started bars
|
||
|
|
function timerPrototype:SetSTFade(fadeOn, ...)
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
if bar then
|
||
|
|
if fadeOn and not bar.fade then
|
||
|
|
DBM:FireEvent("DBM_TimerFadeUpdate", id, self.spellId, self.mod.id, true, self.name)--Timer ID, spellId, modId, true/nil, spellName (new callback only needed if we update an existing timers fade, self.fade is passed in timer start object for new timers)
|
||
|
|
bar.fade = true--Set bar object metatable, which is copied from timer metatable at bar start only
|
||
|
|
bar:ApplyStyle()
|
||
|
|
DBM:Unschedule(playCountSound, id)
|
||
|
|
elseif not fadeOn and bar.fade then
|
||
|
|
DBM:FireEvent("DBM_TimerFadeUpdate", id, self.spellId, self.mod.id, nil, self.name)
|
||
|
|
bar.fade = false
|
||
|
|
bar:ApplyStyle()
|
||
|
|
if self.option then
|
||
|
|
local countVoice = self.mod.Options[self.option .. "CVoice"] or 0
|
||
|
|
if (type(countVoice) == "string" or countVoice > 0) then--Unfading bar, start countdown
|
||
|
|
DBM:Unschedule(playCountSound, id)
|
||
|
|
playCountdown(id, bar.timer, countVoice, self.countdownMax, self.requiresCombat)--timerId, timer, voice, count
|
||
|
|
DBM:Debug("Re-enabling a countdown on bar ID: " .. id .. " after a SetSTFade disable call")
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function timerPrototype:SetSTKeep(keepOn, ...)
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
if bar then
|
||
|
|
if keepOn and not bar.keep then
|
||
|
|
bar.keep = true--Set bar object metatable, which is copied from timer metatable at bar start only
|
||
|
|
bar:ApplyStyle()
|
||
|
|
elseif not keepOn and bar.keep then
|
||
|
|
DBM:FireEvent("DBM_TimerFadeUpdate", id, self.spellId, self.mod.id, nil)
|
||
|
|
bar.keep = false
|
||
|
|
bar:ApplyStyle()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function timerPrototype:DelayedStart(delay, ...)
|
||
|
|
DBMScheduler:Unschedule(self.Start, self.mod, self, ...)
|
||
|
|
DBMScheduler:Schedule(delay or 0.5, self.Start, self.mod, self, ...)
|
||
|
|
end
|
||
|
|
timerPrototype.DelayedShow = timerPrototype.DelayedStart
|
||
|
|
|
||
|
|
function timerPrototype:Schedule(t, ...)
|
||
|
|
return DBMScheduler:Schedule(t, self.Start, self.mod, self, ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
function timerPrototype:Unschedule(...)
|
||
|
|
return DBMScheduler:Unschedule(self.Start, self.mod, self, ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
--TODO, figure out why this function doesn't properly stop count timers when calling stop without count on count timers
|
||
|
|
function timerPrototype:Stop(...)
|
||
|
|
if select("#", ...) == 0 then
|
||
|
|
for i = #self.startedTimers, 1, -1 do
|
||
|
|
DBM:FireEvent("DBM_TimerStop", self.startedTimers[i])
|
||
|
|
DBT:CancelBar(self.startedTimers[i])
|
||
|
|
DBM:Unschedule(playCountSound, self.startedTimers[i])--Unschedule countdown by timerId
|
||
|
|
tremove(self.startedTimers, i)
|
||
|
|
end
|
||
|
|
else
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
for i = #self.startedTimers, 1, -1 do
|
||
|
|
if self.startedTimers[i] == id then
|
||
|
|
local guid
|
||
|
|
for j = 1, select("#", ...) do
|
||
|
|
local v = select(j, ...)
|
||
|
|
if DBM:IsNonPlayableGUID(v) then--Then scan them for a mob guid
|
||
|
|
guid = v--If found, guid will be passed in DBM_TimerStart callback
|
||
|
|
end
|
||
|
|
end
|
||
|
|
--Mods that have specifically flagged that it's safe to assume all timers from that boss mod belong to boss1
|
||
|
|
--This check is performed secondary to args scan so that no adds guids are overwritten
|
||
|
|
if not guid and self.mod.sendMainBossGUID and not DBM.Options.DontSendBossGUIDs and (self.type == "cd" or self.type == "next" or self.type == "cdcount" or self.type == "nextcount" or self.type == "cdspecial" or self.type == "ai") then
|
||
|
|
guid = UnitGUID("boss1")
|
||
|
|
end
|
||
|
|
DBM:FireEvent("DBM_TimerStop", id, guid)
|
||
|
|
DBT:CancelBar(id)
|
||
|
|
DBM:Unschedule(playCountSound, id)--Unschedule countdown by timerId
|
||
|
|
tremove(self.startedTimers, i)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
if self.type == "ai" then--A learning timer
|
||
|
|
if not DBM.Options.AITimer then return end
|
||
|
|
self.lastCast = nil
|
||
|
|
for i = 1, 4 do
|
||
|
|
--Check for any phase timers that are strings and never got a chance to become AI timers, then wipe them
|
||
|
|
if self["phase" .. i .. "CastTimer"] and type(self["phase" .. i .. "CastTimer"]) == "string" then
|
||
|
|
self["phase" .. i .. "CastTimer"] = nil
|
||
|
|
DBM:Debug("Wiping incomplete new timer of stage " .. i, 2)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
--HardStop is a method used when you want to force stop all varients of a timer by ID, period, but still pass a GUID for callbacks
|
||
|
|
--This is especially useful for count timers where guid is 2nd arg and count is 1st
|
||
|
|
--where Stop(guid) would mismatch object and not stop a bar and calling stop on every possible count is silly and stop without args wouldn't send GUID
|
||
|
|
function timerPrototype:HardStop(guid)
|
||
|
|
--Mods that have specifically flagged that it's safe to assume all timers from that boss mod belong to boss1
|
||
|
|
--This check is performed secondary to args scan so that no adds guids are overwritten
|
||
|
|
if not guid and self.mod.sendMainBossGUID and not DBM.Options.DontSendBossGUIDs and (self.type == "cd" or self.type == "next" or self.type == "cdcount" or self.type == "nextcount" or self.type == "cdspecial" or self.type == "ai") then
|
||
|
|
guid = UnitGUID("boss1")
|
||
|
|
end
|
||
|
|
for i = #self.startedTimers, 1, -1 do
|
||
|
|
DBM:FireEvent("DBM_TimerStop", self.startedTimers[i], guid)
|
||
|
|
DBT:CancelBar(self.startedTimers[i])
|
||
|
|
DBM:Unschedule(playCountSound, self.startedTimers[i])--Unschedule countdown by timerId
|
||
|
|
tremove(self.startedTimers, i)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
--In past boss mods have always had to manually call Stop just to restart a timer, to avoid triggering false debug messages
|
||
|
|
--This function should simplify boss mod creation by allowing you to "Restart" a timer with one call in mod instead of 2
|
||
|
|
function timerPrototype:Restart(timer, ...)
|
||
|
|
if self.type and (self.type == "cdcount" or self.type == "nextcount") and not self.allowdouble then
|
||
|
|
self:Stop()--Cleanup any count timers left over on a restart
|
||
|
|
else
|
||
|
|
self:Stop(...)
|
||
|
|
end
|
||
|
|
self:Unschedule(...)--Also unschedules not yet started timers that used timer:Schedule()
|
||
|
|
self:Start(timer, ...)
|
||
|
|
end
|
||
|
|
timerPrototype.Reboot = timerPrototype.Restart
|
||
|
|
|
||
|
|
function timerPrototype:Cancel(...)
|
||
|
|
self:Stop(...)
|
||
|
|
self:Unschedule(...)--Also unschedules not yet started timers that used timer:Schedule()
|
||
|
|
end
|
||
|
|
|
||
|
|
function timerPrototype:GetTime(...)
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
return bar and (bar.totalTime - bar.timer) or 0, (bar and bar.totalTime) or 0
|
||
|
|
end
|
||
|
|
|
||
|
|
function timerPrototype:GetRemaining(...)
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
return bar and bar.timer or 0
|
||
|
|
end
|
||
|
|
|
||
|
|
function timerPrototype:IsStarted(...)
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
return bar and true
|
||
|
|
end
|
||
|
|
|
||
|
|
function timerPrototype:SetTimer(timer)
|
||
|
|
self.timer = timer
|
||
|
|
end
|
||
|
|
|
||
|
|
function timerPrototype:Update(elapsed, totalTime, ...)
|
||
|
|
if DBM.Options.DontShowBossTimers and not self.mod.isTrashMod then return end
|
||
|
|
if DBM.Options.DontShowTrashTimers and self.mod.isTrashMod then return end
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
---@type DBTBar|boolean|nil
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
if not bar then
|
||
|
|
bar = self:Start(totalTime, ...)
|
||
|
|
end
|
||
|
|
if bar then -- still need to check as :Start() can return nil instead of actually starting the timer
|
||
|
|
DBM:FireEvent("DBM_TimerUpdate", id, elapsed, totalTime)
|
||
|
|
local newRemaining = totalTime - elapsed
|
||
|
|
if not bar.keep and newRemaining > 0 then
|
||
|
|
--Correct table for tracked timer objects for adjusted time, or else timers may get stuck if stop is called on them
|
||
|
|
self.mod:Unschedule(removeEntry, self.startedTimers, id)
|
||
|
|
self.mod:Schedule(newRemaining, removeEntry, self.startedTimers, id)
|
||
|
|
end
|
||
|
|
if self.option then
|
||
|
|
local countVoice = self.mod.Options[self.option .. "CVoice"] or 0
|
||
|
|
if (type(countVoice) == "string" or countVoice > 0) then
|
||
|
|
if not bar.fade then--Don't start countdown voice if it's faded bar
|
||
|
|
if newRemaining > 2 then
|
||
|
|
--Can't be called early beacuse then it won't unschedule countdown triggered by :Start if it was called
|
||
|
|
--Also doesn't need to be called early like it does in AddTime and RemoveTime since those early return
|
||
|
|
DBM:Unschedule(playCountSound, id)
|
||
|
|
playCountdown(id, newRemaining, countVoice, self.countdownMax, self.requiresCombat)--timerId, timer, voice, count
|
||
|
|
DBM:Debug("Updating a countdown after a timer Update call for timer ID:" .. id)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return DBT:UpdateBar(id, elapsed, totalTime)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function timerPrototype:AddTime(extendAmount, ...)
|
||
|
|
if DBM.Options.DontShowBossTimers and not self.mod.isTrashMod then return end
|
||
|
|
if DBM.Options.DontShowTrashTimers and self.mod.isTrashMod then return end
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
DBM:Unschedule(playCountSound, id)--Needs to be unscheduled early in case Start is called instead of Update
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
if not bar then
|
||
|
|
return self:Start(extendAmount, ...)
|
||
|
|
else
|
||
|
|
local elapsed, total = (bar.totalTime - bar.timer), bar.totalTime
|
||
|
|
if elapsed and total then
|
||
|
|
local newRemaining = (total + extendAmount) - elapsed
|
||
|
|
if not bar.keep then
|
||
|
|
--Correct table for tracked timer objects for adjusted time, or else timers may get stuck if stop is called on them
|
||
|
|
self.mod:Unschedule(removeEntry, self.startedTimers, id)
|
||
|
|
self.mod:Schedule(newRemaining, removeEntry, self.startedTimers, id)
|
||
|
|
end
|
||
|
|
if self.option then
|
||
|
|
local countVoice = self.mod.Options[self.option .. "CVoice"] or 0
|
||
|
|
if (type(countVoice) == "string" or countVoice > 0) then
|
||
|
|
if not bar.fade then--Don't start countdown voice if it's faded bar
|
||
|
|
playCountdown(id, newRemaining, countVoice, self.countdownMax, self.requiresCombat)--timerId, timer, voice, count
|
||
|
|
DBM:Debug("Updating a countdown after a timer AddTime call for timer ID:" .. id)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
DBM:FireEvent("DBM_TimerUpdate", id, elapsed, total + extendAmount)
|
||
|
|
return DBT:UpdateBar(id, elapsed, total + extendAmount)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function timerPrototype:RemoveTime(reduceAmount, ...)
|
||
|
|
if DBM.Options.DontShowBossTimers and not self.mod.isTrashMod then return end
|
||
|
|
if DBM.Options.DontShowTrashTimers and self.mod.isTrashMod then return end
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
DBM:Unschedule(playCountSound, id)--Needs to be unscheduled here, or countdown might not be canceled if removing time made it cease to have a > 0 value
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
if not bar then
|
||
|
|
return--Do nothing
|
||
|
|
else
|
||
|
|
if not bar.keep then
|
||
|
|
self.mod:Unschedule(removeEntry, self.startedTimers, id)--Needs to be unscheduled here, or the entry might just get left in table until original expire time, if new expire time is less than 0
|
||
|
|
end
|
||
|
|
local elapsed, total = (bar.totalTime - bar.timer), bar.totalTime
|
||
|
|
if elapsed and total then
|
||
|
|
local newRemaining = (total - reduceAmount) - elapsed
|
||
|
|
if newRemaining > 0 then
|
||
|
|
--Correct table for tracked timer objects for adjusted time, or else timers may get stuck if stop is called on them
|
||
|
|
if not bar.keep then
|
||
|
|
self.mod:Schedule(newRemaining, removeEntry, self.startedTimers, id)
|
||
|
|
end
|
||
|
|
if self.option and newRemaining > 2 then
|
||
|
|
local countVoice = self.mod.Options[self.option .. "CVoice"] or 0
|
||
|
|
if (type(countVoice) == "string" or countVoice > 0) then
|
||
|
|
if not bar.fade then--Don't start countdown voice if it's faded bar
|
||
|
|
if newRemaining > 2 then
|
||
|
|
playCountdown(id, newRemaining, countVoice, self.countdownMax, self.requiresCombat)--timerId, timer, voice, count
|
||
|
|
DBM:Debug("Updating a countdown after a timer RemoveTime call for timer ID:" .. id)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
DBM:FireEvent("DBM_TimerUpdate", id, elapsed, total - reduceAmount)
|
||
|
|
return DBT:UpdateBar(id, elapsed, total - reduceAmount)
|
||
|
|
else--New remaining less than 0
|
||
|
|
DBM:FireEvent("DBM_TimerStop", id)
|
||
|
|
removeEntry(self.startedTimers, id)
|
||
|
|
return DBT:CancelBar(id)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function timerPrototype:Pause(...)
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
DBM:Unschedule(playCountSound, id)--Kill countdown on pause
|
||
|
|
if bar then
|
||
|
|
if not bar.keep then
|
||
|
|
self.mod:Unschedule(removeEntry, self.startedTimers, id)--Prevent removal from startedTimers table while bar is paused
|
||
|
|
end
|
||
|
|
DBM:FireEvent("DBM_TimerPause", id)
|
||
|
|
return bar:Pause()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function timerPrototype:Resume(...)
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
if bar then
|
||
|
|
local elapsed, total = (bar.totalTime - bar.timer), bar.totalTime
|
||
|
|
if elapsed and total then
|
||
|
|
local remaining = total - elapsed
|
||
|
|
if not bar.keep then
|
||
|
|
self.mod:Schedule(remaining, removeEntry, self.startedTimers, id)--Re-schedule the auto remove entry stuff
|
||
|
|
end
|
||
|
|
--Have to check if paused bar had a countdown on resume so we can restore it
|
||
|
|
if self.option and not bar.fade then
|
||
|
|
local countVoice = self.mod.Options[self.option .. "CVoice"] or 0
|
||
|
|
if (type(countVoice) == "string" or countVoice > 0) then
|
||
|
|
playCountdown(id, remaining, countVoice, self.countdownMax, self.requiresCombat)--timerId, timer, voice, count
|
||
|
|
DBM:Debug("Updating a countdown after a timer Resume call for timer ID:" .. id)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
DBM:FireEvent("DBM_TimerResume", id)
|
||
|
|
return bar:Resume()
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function timerPrototype:UpdateIcon(icon, ...)
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
if bar then
|
||
|
|
icon = DBM:ParseSpellIcon(icon)
|
||
|
|
DBM:FireEvent("DBM_TimerUpdateIcon", id, icon)
|
||
|
|
return bar:SetIcon(icon)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
--This function changes the spellname and callback key (but not option key) of timer object
|
||
|
|
--This is needed for faction bosses where we need to swap out a spell key/name on fly after a boss is engaged
|
||
|
|
function timerPrototype:UpdateKey(altSpellId, ...)
|
||
|
|
--Check if existing bar first,
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
self.spellId = altSpellId
|
||
|
|
self.icon = DBM:ParseSpellIcon(altSpellId, self.type, self.icon)
|
||
|
|
self.name = nil--By wiping name, it becomes uncached and can get replaced by GetLocalizedTimerText in :Start
|
||
|
|
if bar then
|
||
|
|
--If a bar exists while updating key we"
|
||
|
|
--Get remainig, kill old timer, start new one with ID/name replacement applied
|
||
|
|
local remaining = bar.timer
|
||
|
|
self:Stop(...)
|
||
|
|
self:Unschedule(...)
|
||
|
|
DBM:Unschedule(playCountSound, id)
|
||
|
|
self:Start(remaining, ...)--Restart it
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function timerPrototype:UpdateInline(newInline, ...)
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
if bar then
|
||
|
|
local ttext = _G[bar.frame:GetName() .. "BarName"]:GetText() or ""
|
||
|
|
return bar:SetText(ttext, newInline or self.inlineIcon)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function timerPrototype:UpdateName(name, ...)
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
if bar then
|
||
|
|
return bar:SetText(name, self.inlineIcon)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function timerPrototype:SetColor(c, ...)
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
if bar then
|
||
|
|
return bar:SetColor(c)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
function timerPrototype:DisableEnlarge(...)
|
||
|
|
local id = self.id .. pformat((("\t%s"):rep(select("#", ...))), ...)
|
||
|
|
local bar = DBT:GetBar(id)
|
||
|
|
if bar then
|
||
|
|
bar.small = true
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
---@param optionDefault SpecFlags|boolean?
|
||
|
|
function timerPrototype:AddOption(optionDefault, optionName, colorType, countdown, spellId, optionType, waCustomName)
|
||
|
|
if optionName ~= false then
|
||
|
|
self.option = optionName or self.id
|
||
|
|
self.mod:AddBoolOption(self.option, optionDefault, "timer", nil, colorType, countdown, spellId, optionType, waCustomName)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
|
||
|
|
--If a new countdown default is added to a NewTimer object, change optionName of timer to reset a new default
|
||
|
|
function bossModPrototype:NewTimer(timer, name, icon, optionDefault, optionName, colorType, inlineIcon, keep, countdown, countdownMax, r, g, b, spellId, requiresCombat, waCustomName, customType)
|
||
|
|
if r and type(r) == "string" then
|
||
|
|
DBM:Debug("|cffff0000r probably has inline icon in it and needs to be fixed for |r" .. name .. r)
|
||
|
|
r = nil--Fix it for users
|
||
|
|
end
|
||
|
|
if inlineIcon and type(inlineIcon) == "number" then
|
||
|
|
DBM:Debug("|cffff0000spellID texture path or colorType is in inlineIcon field and needs to be fixed for |r" .. name .. inlineIcon)
|
||
|
|
inlineIcon = nil--Fix it for users
|
||
|
|
end
|
||
|
|
icon = DBM:ParseSpellIcon(icon)
|
||
|
|
local waSpecialKey, simpType
|
||
|
|
if customType then
|
||
|
|
simpType = timerTypeSimplification[customType] or customType
|
||
|
|
waSpecialKey = waKeyOverrides[customType]
|
||
|
|
end
|
||
|
|
---@class Timer
|
||
|
|
local obj = setmetatable(
|
||
|
|
{
|
||
|
|
text = self.localization.timers[name],
|
||
|
|
type = customType or "cd",--Auto assign
|
||
|
|
simpType = simpType or "cd",
|
||
|
|
waSpecialKey = waSpecialKey,
|
||
|
|
spellId = spellId,--Allows Localized timer text to still have a spellId arg weak auras can latch onto
|
||
|
|
timer = timer,
|
||
|
|
id = name,
|
||
|
|
icon = icon,
|
||
|
|
colorType = colorType or 0,
|
||
|
|
inlineIcon = inlineIcon,
|
||
|
|
keep = keep,
|
||
|
|
countdown = countdown,
|
||
|
|
countdownMax = countdownMax,
|
||
|
|
r = r,
|
||
|
|
g = g,
|
||
|
|
b = b,
|
||
|
|
requiresCombat = requiresCombat,
|
||
|
|
startedTimers = {},
|
||
|
|
mod = self,
|
||
|
|
startLarge = nil,
|
||
|
|
},
|
||
|
|
mt
|
||
|
|
)
|
||
|
|
obj:AddOption(optionDefault, optionName, colorType, countdown, spellId, nil, waCustomName)
|
||
|
|
tinsert(self.timers, obj)
|
||
|
|
return obj
|
||
|
|
end
|
||
|
|
|
||
|
|
-- new constructor for the new auto-localized timer types
|
||
|
|
-- note that the function might look unclear because it needs to handle different timer types, especially achievement timers need special treatment
|
||
|
|
-- If a new countdown is added to an existing timer that didn't have one before, use optionName (number) to force timer to reset defaults by assigning it a new variable
|
||
|
|
---@param timerType string
|
||
|
|
local function newTimer(self, timerType, timer, spellId, timerText, optionDefault, optionName, colorType, texture, inlineIcon, keep, countdown, countdownMax, r, g, b, requiresCombat)
|
||
|
|
if type(timer) == "string" and timer:match("OptionVersion") then
|
||
|
|
error("OptionVersion hack deprecated, remove it from: " .. spellId)
|
||
|
|
end
|
||
|
|
if type(colorType) == "number" and colorType > 8 then
|
||
|
|
DBM:AddMsg("|cffff0000texture is in the colorType arg for: |r" .. spellId)
|
||
|
|
end
|
||
|
|
--Use option optionName for optionVersion as well, no reason to split.
|
||
|
|
--This ensures that remaining arg positions match for auto generated and regular NewTimer
|
||
|
|
local optionVersion
|
||
|
|
if type(optionName) == "number" then
|
||
|
|
optionVersion = optionName
|
||
|
|
optionName = nil
|
||
|
|
end
|
||
|
|
local allowdouble
|
||
|
|
if type(timer) == "string" and timer:match("d%d+") then
|
||
|
|
allowdouble = true
|
||
|
|
timer = tonumber(string.sub(timer, 2))
|
||
|
|
end
|
||
|
|
local spellName, icon
|
||
|
|
spellName = DBM:ParseSpellName(spellId, timerType)
|
||
|
|
local unparsedId = spellId
|
||
|
|
if timerType == "achievement" then
|
||
|
|
icon = DBM:ParseSpellIcon(texture or spellId, timerType)
|
||
|
|
elseif timerType == "cdspecial" or timerType == "nextspecial" or timerType == "stage" or timerType == "stagecount" or timerType == "stagecountcycle" or timerType == "stagecontext" or timerType == "stagecontextcount" or timerType == "intermission" or timerType == "intermissioncount" then
|
||
|
|
icon = DBM:ParseSpellIcon(texture or spellId, timerType)
|
||
|
|
if timerType == "stage" or timerType == "stagecount" or timerType == "stagecountcycle" or timerType == "stagecontext" or timerType == "stagecontextcount" or timerType == "intermission" or timerType == "intermissioncount" then
|
||
|
|
colorType = 6
|
||
|
|
end
|
||
|
|
elseif timerType == "roleplay" then
|
||
|
|
icon = DBM:ParseSpellIcon(texture or spellId, timerType, private.isRetail and 237538 or 136106)
|
||
|
|
colorType = 6
|
||
|
|
elseif timerType == "adds" or timerType == "addscustom" then
|
||
|
|
icon = DBM:ParseSpellIcon(texture or spellId, timerType, 136116)
|
||
|
|
colorType = 1
|
||
|
|
else
|
||
|
|
icon = DBM:ParseSpellIcon(texture or spellId, timerType)
|
||
|
|
colorType = colorType or 0
|
||
|
|
end
|
||
|
|
local timerTextValue
|
||
|
|
if timerText then
|
||
|
|
--First check if it's shorttext
|
||
|
|
if DBM.Options.ShortTimerText then
|
||
|
|
--If timertext is a number, accept it as a secondary auto translate spellid
|
||
|
|
if type(timerText) == "number" then
|
||
|
|
timerTextValue = timerText
|
||
|
|
spellName = DBM:GetSpellName(timerText or 0)--Override Cached spell Name
|
||
|
|
--Interpret it literal with no restrictions, first checking mod local table, then just taking timerText directly
|
||
|
|
else
|
||
|
|
timerTextValue = self.localization.timers[timerText]--Check timers table first, otherwise accept it as literal timer text
|
||
|
|
end
|
||
|
|
else--Short text is off, we want to be more aggressive in NOT setting short text if we can help it
|
||
|
|
--Short text is off, and spellId does exist, only accept timerText if it's in mods localization tables, cause then it's not short text, it's hard localization
|
||
|
|
if spellId and type(spellId) == "number" then
|
||
|
|
--Only use timerText if it's full localized, cause that's not shorttext
|
||
|
|
timerTextValue = rawget(self.localization.timers, timerText)
|
||
|
|
else--If no spellID, then we allow hard setting timerText because it's only translation timer object has
|
||
|
|
timerTextValue = self.localization.timers[timerText]
|
||
|
|
end
|
||
|
|
end
|
||
|
|
end
|
||
|
|
local id = "Timer" .. (spellId or 0) .. timerType .. (optionVersion or "")
|
||
|
|
local simpType = timerTypeSimplification[timerType] or timerType
|
||
|
|
local waSpecialKey = waKeyOverrides[timerType]
|
||
|
|
---@class Timer
|
||
|
|
local obj = setmetatable(
|
||
|
|
{
|
||
|
|
text = timerTextValue,
|
||
|
|
type = timerType,
|
||
|
|
simpType = simpType,
|
||
|
|
waSpecialKey = waSpecialKey,--Not same as simpType, this overrides option key
|
||
|
|
spellId = spellId,
|
||
|
|
name = spellName,--If name gets stored as nil, it'll be corrected later in Timer start, if spell name returns in a later attempt
|
||
|
|
timer = timer,
|
||
|
|
id = id,
|
||
|
|
icon = icon,
|
||
|
|
colorType = colorType,
|
||
|
|
inlineIcon = inlineIcon,
|
||
|
|
keep = keep,
|
||
|
|
countdown = countdown,
|
||
|
|
countdownMax = countdownMax,
|
||
|
|
r = r,
|
||
|
|
g = g,
|
||
|
|
b = b,
|
||
|
|
requiresCombat = requiresCombat,
|
||
|
|
allowdouble = allowdouble,
|
||
|
|
startedTimers = {},
|
||
|
|
mod = self,
|
||
|
|
},
|
||
|
|
mt
|
||
|
|
)
|
||
|
|
obj:AddOption(optionDefault, optionName, colorType, countdown, spellId, timerType)
|
||
|
|
tinsert(self.timers, obj)
|
||
|
|
-- todo: move the string creation to the GUI with SetFormattedString...
|
||
|
|
if not self.localization.options[id] or self.localization.options[id] == id then
|
||
|
|
if timerType == "achievement" then
|
||
|
|
self.localization.options[id] = L.AUTO_TIMER_OPTIONS[timerType]:format((GetAchievementLink(spellId) or ""):gsub("%[(.+)%]", "%1"))
|
||
|
|
elseif timerType == "cdspecial" or timerType == "nextspecial" or timerType == "stage" or timerType == "stagecount" or timerType == "stagecountcycle" or timerType == "intermission" or timerType == "intermissioncount" or timerType == "roleplay" then--Timers without spellid, generic (do not add stagecontext here, it has spellname parsing)
|
||
|
|
self.localization.options[id] = L.AUTO_TIMER_OPTIONS[timerType]--Using more than 1 stage timer or more than 1 special timer will break this, fortunately you should NEVER use more than 1 of either in a mod
|
||
|
|
else
|
||
|
|
self.localization.options[id] = L.AUTO_TIMER_OPTIONS[timerType]:format(unparsedId)
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return obj
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewTargetTimer(...)
|
||
|
|
return newTimer(self, "target", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewTargetCountTimer(...)
|
||
|
|
return newTimer(self, "targetcount", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
--Buff/Debuff/event on boss
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewBuffActiveTimer(...)
|
||
|
|
return newTimer(self, "active", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
----Buff/Debuff on players
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewBuffFadesTimer(...)
|
||
|
|
return newTimer(self, "fades", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewCastTimer(timer, ...)
|
||
|
|
if tonumber(timer) and timer > 1000 then -- hehe :) best hack in DBM. This makes the first argument optional, so we can omit it to use the cast time from the spell id ;)
|
||
|
|
local spellId = timer
|
||
|
|
timer = select(4, DBM:GetSpellInfo(spellId)) or 1000 -- GetSpellInfo takes YOUR spell haste into account...WTF?
|
||
|
|
local spellHaste = select(4, DBM:GetSpellInfo(10059)) / 10000 -- 10059 = Stormwind Portal, should have 10000 ms cast time
|
||
|
|
timer = timer / spellHaste -- calculate the real cast time of the spell...
|
||
|
|
return self:NewCastTimer(timer / 1000, spellId, ...)
|
||
|
|
end
|
||
|
|
return newTimer(self, "cast", timer, ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewCastCountTimer(timer, ...)
|
||
|
|
if tonumber(timer) and timer > 1000 then -- hehe :) best hack in DBM. This makes the first argument optional, so we can omit it to use the cast time from the spell id ;)
|
||
|
|
local spellId = timer
|
||
|
|
timer = select(4, DBM:GetSpellInfo(spellId)) or 1000 -- GetSpellInfo takes YOUR spell haste into account...WTF?
|
||
|
|
local spellHaste = select(4, DBM:GetSpellInfo(10059)) / 10000 -- 10059 = Stormwind Portal, should have 10000 ms cast time
|
||
|
|
timer = timer / spellHaste -- calculate the real cast time of the spell...
|
||
|
|
return self:NewCastTimer(timer / 1000, spellId, ...)
|
||
|
|
end
|
||
|
|
return newTimer(self, "castcount", timer, ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewCastSourceTimer(timer, ...)
|
||
|
|
if tonumber(timer) and timer > 1000 then -- hehe :) best hack in DBM. This makes the first argument optional, so we can omit it to use the cast time from the spell id ;)
|
||
|
|
local spellId = timer
|
||
|
|
timer = select(4, DBM:GetSpellInfo(spellId)) or 1000 -- GetSpellInfo takes YOUR spell haste into account...WTF?
|
||
|
|
local spellHaste = select(4, DBM:GetSpellInfo(10059)) / 10000 -- 10059 = Stormwind Portal, should have 10000 ms cast time
|
||
|
|
timer = timer / spellHaste -- calculate the real cast time of the spell...
|
||
|
|
return self:NewCastSourceTimer(timer / 1000, spellId, ...)
|
||
|
|
end
|
||
|
|
return newTimer(self, "castsource", timer, ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewCDTimer(...)
|
||
|
|
return newTimer(self, "cd", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewCDCountTimer(...)
|
||
|
|
return newTimer(self, "cdcount", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewCDSourceTimer(...)
|
||
|
|
return newTimer(self, "cdsource", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewNextTimer(...)
|
||
|
|
return newTimer(self, "next", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewNextCountTimer(...)
|
||
|
|
return newTimer(self, "nextcount", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewNextSourceTimer(...)
|
||
|
|
return newTimer(self, "nextsource", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewAchievementTimer(...)
|
||
|
|
return newTimer(self, "achievement", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewCDSpecialTimer(...)
|
||
|
|
return newTimer(self, "cdspecial", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewNextSpecialTimer(...)
|
||
|
|
return newTimer(self, "nextspecial", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewCDComboTimer(...)
|
||
|
|
return newTimer(self, "cdcombo", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewNextComboTimer(...)
|
||
|
|
return newTimer(self, "nextcombo", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewStageTimer(...)
|
||
|
|
return newTimer(self, "stage", ...)
|
||
|
|
end
|
||
|
|
bossModPrototype.NewPhaseTimer = bossModPrototype.NewStageTimer--Deprecated naming, once all mods are converted over, NewPhaseTimer will be wiped out for NewStageTimer
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewStageCountTimer(...)
|
||
|
|
return newTimer(self, "stagecount", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
--Used mainly for compat with BW/LW timers where they use "stages" but then use the spell/journal descriptor instead of "stage d"
|
||
|
|
--Basically, it's a generic spellName timer for "stages" callback
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewStageContextTimer(...)
|
||
|
|
return newTimer(self, "stagecontext", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
--Same as NewStageContextTimer, with count
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewStageContextCountTimer(...)
|
||
|
|
return newTimer(self, "stagecontextcount", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewStageCountCycleTimer(...)
|
||
|
|
return newTimer(self, "stagecountcycle", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewIntermissionTimer(...)
|
||
|
|
return newTimer(self, "intermission", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewIntermissionCountTimer(...)
|
||
|
|
return newTimer(self, "intermissioncount", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewRPTimer(...)
|
||
|
|
return newTimer(self, "roleplay", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
function bossModPrototype:NewCombatTimer(timer)
|
||
|
|
return newTimer(self, "combat", timer, nil, nil, nil, nil, 0, "132349", nil, nil, 1, 5)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewAddsTimer(...)
|
||
|
|
return newTimer(self, "adds", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewAddsCustomTimer(...)
|
||
|
|
return newTimer(self, "addscustom", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewCDNPTimer(...)
|
||
|
|
return newTimer(self, "cdnp", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewNextNPTimer(...)
|
||
|
|
return newTimer(self, "nextnp", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewCDCountNPTimer(...)
|
||
|
|
return newTimer(self, "cdcountnp", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewNextCountNPTimer(...)
|
||
|
|
return newTimer(self, "nextcountnp", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
---@overload fun(self: DBMMod, timer: number|string, spellId: number|string?, timerText: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, colorType: number?, texture: number|string?, inlineIcon: string?, keep: boolean?, countdown: number?, countdownMax: number?, r: number?, g: number?, b: number?, requiresCombat: boolean?): Timer
|
||
|
|
function bossModPrototype:NewAITimer(...)
|
||
|
|
return newTimer(self, "ai", ...)
|
||
|
|
end
|
||
|
|
|
||
|
|
function bossModPrototype:GetLocalizedTimerText(timerType, spellId, Name)
|
||
|
|
local spellName
|
||
|
|
if Name then
|
||
|
|
spellName = Name--Pull from name stored in object
|
||
|
|
elseif spellId then
|
||
|
|
DBM:Debug("|cffff0000GetLocalizedTimerText fallback, this should not happen and is a bug. this fallback should be deleted if this message is never seen after async code is live|r")
|
||
|
|
spellName = DBM:ParseSpellName(spellId, timerType)
|
||
|
|
--Name wasn't provided, but we succeeded in getting a name, generate one into object now for caching purposes
|
||
|
|
--This would really only happen if GetSpellName failed to return spell name on first attempt (which now happens in 9.0)
|
||
|
|
if spellName then
|
||
|
|
self.name = spellName
|
||
|
|
end
|
||
|
|
end
|
||
|
|
return pformat(L.AUTO_TIMER_TEXTS[timerType], spellName)
|
||
|
|
end
|