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.
835 lines
39 KiB
835 lines
39 KiB
---@class DBMCoreNamespace
|
|
local private = select(2, ...)
|
|
|
|
local L = DBM_CORE_L
|
|
local CL = DBM_COMMON_L
|
|
|
|
local DBMScheduler = private:GetModule("DBMScheduler")
|
|
local stringUtils = private:GetPrototype("StringUtils")
|
|
local checkEntry = private:GetPrototype("TableUtils").checkEntry
|
|
|
|
---@class DBM
|
|
local DBM = private:GetPrototype("DBM")
|
|
---@class Announce
|
|
local announcePrototype = private:GetPrototype("Announce")
|
|
---@class DBMMod
|
|
local bossModPrototype = private:GetPrototype("DBMMod")
|
|
|
|
local test = private:GetPrototype("DBMTest")
|
|
|
|
local mt = {__index = announcePrototype}
|
|
|
|
---@diagnostic disable: inject-field
|
|
local frame = CreateFrame("Frame", "DBMWarning", UIParent)
|
|
local font1u = CreateFrame("Frame", "DBMWarning1Updater", UIParent)
|
|
local font2u = CreateFrame("Frame", "DBMWarning2Updater", UIParent)
|
|
local font3u = CreateFrame("Frame", "DBMWarning3Updater", UIParent)
|
|
local font1 = frame:CreateFontString("DBMWarning1", "OVERLAY", "GameFontNormal")
|
|
font1:SetWidth(1024)
|
|
font1:SetHeight(0)
|
|
font1:SetPoint("TOP", 0, 0)
|
|
local font2 = frame:CreateFontString("DBMWarning2", "OVERLAY", "GameFontNormal")
|
|
font2:SetWidth(1024)
|
|
font2:SetHeight(0)
|
|
font2:SetPoint("TOP", font1, "BOTTOM", 0, 0)
|
|
local font3 = frame:CreateFontString("DBMWarning3", "OVERLAY", "GameFontNormal")
|
|
font3:SetWidth(1024)
|
|
font3:SetHeight(0)
|
|
font3:SetPoint("TOP", font2, "BOTTOM", 0, 0)
|
|
frame:SetMovable(true)
|
|
frame:SetWidth(1)
|
|
frame:SetHeight(1)
|
|
frame:SetFrameStrata("HIGH")
|
|
frame:SetClampedToScreen(true)
|
|
frame:SetPoint("CENTER", UIParent, "CENTER", 0, 300)
|
|
font1u:Hide()
|
|
font2u:Hide()
|
|
font3u:Hide()
|
|
|
|
local font1elapsed, font2elapsed, font3elapsed
|
|
|
|
local function fontHide1()
|
|
local duration = DBM.Options.WarningDuration2
|
|
if font1elapsed > duration * 1.3 then
|
|
font1u:Hide()
|
|
font1:Hide()
|
|
if frame.font1ticker then
|
|
frame.font1ticker:Cancel()
|
|
frame.font1ticker = nil
|
|
end
|
|
elseif font1elapsed > duration then
|
|
font1elapsed = font1elapsed + 0.05
|
|
local alpha = 1 - (font1elapsed - duration) / (duration * 0.3)
|
|
font1:SetAlpha(alpha > 0 and alpha or 0)
|
|
else
|
|
font1elapsed = font1elapsed + 0.05
|
|
font1:SetAlpha(1)
|
|
end
|
|
end
|
|
|
|
local function fontHide2()
|
|
local duration = DBM.Options.WarningDuration2
|
|
if font2elapsed > duration * 1.3 then
|
|
font2u:Hide()
|
|
font2:Hide()
|
|
if frame.font2ticker then
|
|
frame.font2ticker:Cancel()
|
|
frame.font2ticker = nil
|
|
end
|
|
elseif font2elapsed > duration then
|
|
font2elapsed = font2elapsed + 0.05
|
|
local alpha = 1 - (font2elapsed - duration) / (duration * 0.3)
|
|
font2:SetAlpha(alpha > 0 and alpha or 0)
|
|
else
|
|
font2elapsed = font2elapsed + 0.05
|
|
font2:SetAlpha(1)
|
|
end
|
|
end
|
|
|
|
local function fontHide3()
|
|
local duration = DBM.Options.WarningDuration2
|
|
if font3elapsed > duration * 1.3 then
|
|
font3u:Hide()
|
|
font3:Hide()
|
|
if frame.font3ticker then
|
|
frame.font3ticker:Cancel()
|
|
frame.font3ticker = nil
|
|
end
|
|
elseif font3elapsed > duration then
|
|
font3elapsed = font3elapsed + 0.05
|
|
local alpha = 1 - (font3elapsed - duration) / (duration * 0.3)
|
|
font3:SetAlpha(alpha > 0 and alpha or 0)
|
|
else
|
|
font3elapsed = font3elapsed + 0.05
|
|
font3:SetAlpha(1)
|
|
end
|
|
end
|
|
|
|
font1u:SetScript("OnUpdate", function(self)
|
|
local diff = GetTime() - font1.lastUpdate
|
|
local origSize = DBM.Options.WarningFontSize
|
|
if diff > 0.4 then
|
|
font1:SetTextHeight(origSize)
|
|
self:Hide()
|
|
elseif diff > 0.2 then
|
|
font1:SetTextHeight(origSize * (1.5 - (diff - 0.2) * 2.5))
|
|
else
|
|
font1:SetTextHeight(origSize * (1 + diff * 2.5))
|
|
end
|
|
end)
|
|
|
|
font2u:SetScript("OnUpdate", function(self)
|
|
local diff = GetTime() - font2.lastUpdate
|
|
local origSize = DBM.Options.WarningFontSize
|
|
if diff > 0.4 then
|
|
font2:SetTextHeight(origSize)
|
|
self:Hide()
|
|
elseif diff > 0.2 then
|
|
font2:SetTextHeight(origSize * (1.5 - (diff - 0.2) * 2.5))
|
|
else
|
|
font2:SetTextHeight(origSize * (1 + diff * 2.5))
|
|
end
|
|
end)
|
|
|
|
font3u:SetScript("OnUpdate", function(self)
|
|
local diff = GetTime() - font3.lastUpdate
|
|
local origSize = DBM.Options.WarningFontSize
|
|
if diff > 0.4 then
|
|
font3:SetTextHeight(origSize)
|
|
self:Hide()
|
|
elseif diff > 0.2 then
|
|
font3:SetTextHeight(origSize * (1.5 - (diff - 0.2) * 2.5))
|
|
else
|
|
font3:SetTextHeight(origSize * (1 + diff * 2.5))
|
|
end
|
|
end)
|
|
|
|
function DBM:UpdateWarningOptions()
|
|
frame:ClearAllPoints()
|
|
frame:SetPoint(self.Options.WarningPoint, UIParent, self.Options.WarningPoint, self.Options.WarningX, self.Options.WarningY)
|
|
local font = self.Options.WarningFont == "standardFont" and private.standardFont or self.Options.WarningFont
|
|
font1:SetFont(font, self.Options.WarningFontSize, self.Options.WarningFontStyle == "None" and nil or self.Options.WarningFontStyle)
|
|
font2:SetFont(font, self.Options.WarningFontSize, self.Options.WarningFontStyle == "None" and nil or self.Options.WarningFontStyle)
|
|
font3:SetFont(font, self.Options.WarningFontSize, self.Options.WarningFontStyle == "None" and nil or self.Options.WarningFontStyle)
|
|
if self.Options.WarningFontShadow then
|
|
font1:SetShadowOffset(1, -1)
|
|
font2:SetShadowOffset(1, -1)
|
|
font3:SetShadowOffset(1, -1)
|
|
else
|
|
font1:SetShadowOffset(0, 0)
|
|
font2:SetShadowOffset(0, 0)
|
|
font3:SetShadowOffset(0, 0)
|
|
end
|
|
end
|
|
|
|
function DBM:AddWarning(text, force, announceObject)
|
|
local added = false
|
|
if not frame.font1ticker then
|
|
font1elapsed = 0
|
|
font1.lastUpdate = GetTime()
|
|
font1:SetText(text)
|
|
font1:Show()
|
|
font1u:Show()
|
|
added = true
|
|
frame.font1ticker = frame.font1ticker or C_Timer.NewTicker(0.05, fontHide1)
|
|
elseif not frame.font2ticker then
|
|
font2elapsed = 0
|
|
font2.lastUpdate = GetTime()
|
|
font2:SetText(text)
|
|
font2:Show()
|
|
font2u:Show()
|
|
added = true
|
|
frame.font2ticker = frame.font2ticker or C_Timer.NewTicker(0.05, fontHide2)
|
|
elseif not frame.font3ticker or force then
|
|
font3elapsed = 0
|
|
font3.lastUpdate = GetTime()
|
|
font3:SetText(text)
|
|
font3:Show()
|
|
font3u:Show()
|
|
fontHide3()
|
|
added = true
|
|
frame.font3ticker = frame.font3ticker or C_Timer.NewTicker(0.05, fontHide3)
|
|
end
|
|
if not added then
|
|
local prevText1 = font2:GetText()
|
|
local prevText2 = font3:GetText()
|
|
font1:SetText(prevText1)
|
|
font1elapsed = font2elapsed
|
|
font2:SetText(prevText2)
|
|
font2elapsed = font3elapsed
|
|
self:AddWarning(text, true, announceObject)
|
|
else
|
|
test:Trace(announceObject and announceObject.mod or self, "ShowAnnounce", announceObject, text)
|
|
end
|
|
end
|
|
|
|
do
|
|
local anchorFrame
|
|
local function moveEnd(self)
|
|
anchorFrame:Hide()
|
|
if anchorFrame.ticker then
|
|
anchorFrame.ticker:Cancel()
|
|
anchorFrame.ticker = nil
|
|
end
|
|
font1elapsed = self.Options.WarningDuration2
|
|
font2elapsed = self.Options.WarningDuration2
|
|
font3elapsed = self.Options.WarningDuration2
|
|
frame:SetFrameStrata("HIGH")
|
|
self:Unschedule(moveEnd)
|
|
DBT:CancelBar(L.MOVE_WARNING_BAR)
|
|
end
|
|
|
|
function DBM:MoveWarning()
|
|
if not anchorFrame then
|
|
anchorFrame = CreateFrame("Frame", nil, frame)
|
|
anchorFrame:SetWidth(32)
|
|
anchorFrame:SetHeight(32)
|
|
anchorFrame:EnableMouse(true)
|
|
anchorFrame:SetPoint("TOP", frame, "TOP", 0, 32)
|
|
anchorFrame:RegisterForDrag("LeftButton")
|
|
anchorFrame:SetClampedToScreen(true)
|
|
anchorFrame:Hide()
|
|
local texture = anchorFrame:CreateTexture()
|
|
texture:SetTexture("Interface\\Addons\\DBM-Core\\textures\\dot.blp")
|
|
texture:SetPoint("CENTER", anchorFrame, "CENTER", 0, 0)
|
|
texture:SetWidth(32)
|
|
texture:SetHeight(32)
|
|
anchorFrame:SetScript("OnDragStart", function()
|
|
frame:StartMoving()
|
|
self:Unschedule(moveEnd)
|
|
DBT:CancelBar(L.MOVE_WARNING_BAR)
|
|
end)
|
|
anchorFrame:SetScript("OnDragStop", function()
|
|
frame:StopMovingOrSizing()
|
|
local point, _, _, xOfs, yOfs = frame:GetPoint(1)
|
|
self.Options.WarningPoint = point
|
|
self.Options.WarningX = xOfs
|
|
self.Options.WarningY = yOfs
|
|
self:Schedule(15, moveEnd, self)
|
|
DBT:CreateBar(15, L.MOVE_WARNING_BAR, private.isRetail and 237538 or 136106)
|
|
end)
|
|
end
|
|
if anchorFrame:IsShown() then
|
|
moveEnd(self)
|
|
else
|
|
anchorFrame:Show()
|
|
anchorFrame.ticker = anchorFrame.ticker or C_Timer.NewTicker(5, function() self:AddWarning(L.MOVE_WARNING_MESSAGE) end)
|
|
self:AddWarning(L.MOVE_WARNING_MESSAGE)
|
|
self:Schedule(15, moveEnd, self)
|
|
DBT:CreateBar(15, L.MOVE_WARNING_BAR, private.isRetail and 237538 or 136106)
|
|
frame:Show()
|
|
frame:SetFrameStrata("TOOLTIP")
|
|
frame:SetAlpha(1)
|
|
end
|
|
end
|
|
end
|
|
|
|
local textureCode = " |T%s:12:12|t "
|
|
local textureExp = " |T(%S+......%S+):12:12|t "--Fix texture file including blank not strips(example: Interface\\Icons\\Spell_Frost_Ring of Frost). But this have limitations. Since I'm poor at regular expressions, this is not good fix. Do you have another good regular expression, tandanu?
|
|
|
|
|
|
-- TODO: is there a good reason that this is a weak table?
|
|
local cachedColorFunctions = setmetatable({}, {__mode = "kv"})
|
|
|
|
local function setText(announceType, spellId, castTime, preWarnTime, customName, originalSpellID)
|
|
local spellName
|
|
if customName then
|
|
spellName = customName
|
|
else
|
|
spellName = DBM:ParseSpellName(spellId, announceType) or CL.UNKNOWN
|
|
end
|
|
local text
|
|
if announceType == "cast" then
|
|
local spellHaste = select(4, DBM:GetSpellInfo(10059)) / 10000 -- 10059 = Stormwind Portal, should have 10000 ms cast time
|
|
local timer = (select(4, DBM:GetSpellInfo(originalSpellID or spellId)) or 1000) / spellHaste
|
|
text = L.AUTO_ANNOUNCE_TEXTS[announceType]:format(spellName, castTime or (timer / 1000))
|
|
elseif announceType == "prewarn" then
|
|
if type(preWarnTime) == "string" then
|
|
text = L.AUTO_ANNOUNCE_TEXTS[announceType]:format(spellName, preWarnTime)
|
|
else
|
|
text = L.AUTO_ANNOUNCE_TEXTS[announceType]:format(spellName, L.SEC_FMT:format(tostring(preWarnTime or 5)))
|
|
end
|
|
elseif announceType == "stage" or announceType == "prestage" then
|
|
text = L.AUTO_ANNOUNCE_TEXTS[announceType]:format(tostring(spellId))
|
|
elseif announceType == "stagechange" then
|
|
text = L.AUTO_ANNOUNCE_TEXTS.spell
|
|
else
|
|
text = L.AUTO_ANNOUNCE_TEXTS[announceType]:format(spellName)
|
|
end
|
|
return text, spellName
|
|
end
|
|
|
|
function announcePrototype:SetText(customName)
|
|
local text, spellName = setText(self.announceType, self.spellId, self.castTime, self.preWarnTime, customName)
|
|
self.text = text
|
|
self.spellName = spellName
|
|
end
|
|
|
|
---Not to be confused with SetText, which only sets the text of object.
|
|
---<br>This changes actual ID so announce callback also swaps ID for WAs
|
|
---@param altSpellId string|number
|
|
function announcePrototype:UpdateKey(altSpellId)
|
|
self.spellId = altSpellId
|
|
self.icon = DBM:ParseSpellIcon(altSpellId, self.announceType, self.icon)
|
|
if self.announceType then
|
|
--Regenerate auto localized text if it's an auto localized alert
|
|
local text, spellName = setText(self.announceType, self.spellId, self.castTime, self.preWarnTime)
|
|
self.text = text
|
|
self.spellName = spellName
|
|
else--Just regenerating spellName not message text because it's likely a custom text object such as NewSpecialWarning
|
|
self.spellName = DBM:ParseSpellName(altSpellId)
|
|
end
|
|
end
|
|
|
|
---@class Announce0: Announce
|
|
---@field Show fun(self: Announce0)
|
|
---@class Announce1: Announce
|
|
---@field Show fun(self: Announce1, arg1: string|number)
|
|
---@class Announce1Num: Announce
|
|
---@field Show fun(self: Announce1Num, arg1: number)
|
|
---@class Announce2: Announce
|
|
---@field Show fun(self: Announce2, arg1: string|number, arg2: string|number)
|
|
---@class Announce2StrNum: Announce
|
|
---@field Show fun(self: Announce2StrNum, arg1: string|number, arg2: number)
|
|
---@class Announce2NumStr: Announce
|
|
---@field Show fun(self: Announce2NumStr, arg1: number, arg2: string|number)
|
|
|
|
|
|
-- TODO: this function is an abomination, it needs to be rewritten. Also: check if these work-arounds are still necessary
|
|
function announcePrototype:Show(...) -- todo: reduce amount of unneeded strings
|
|
if not self.option or self.mod.Options[self.option] then
|
|
if DBM.Options.DontShowBossAnnounces then return end -- don't show the announces if the spam filter option is set
|
|
if DBM.Options.DontShowTargetAnnouncements and (self.announceType == "target" or self.announceType == "targetcount") and not self.noFilter then return end--don't show announces that are generic target announces
|
|
local argTable = {...}
|
|
local colorCode = ("|cff%.2x%.2x%.2x"):format(self.color.r * 255, self.color.g * 255, self.color.b * 255)
|
|
if #self.combinedtext > 0 then
|
|
--Throttle spam.
|
|
if DBM.Options.WarningAlphabetical then
|
|
table.sort(self.combinedtext)
|
|
end
|
|
local combinedText = table.concat(self.combinedtext, "<, >")
|
|
if self.combinedcount == 1 then
|
|
combinedText = combinedText .. " " .. L.GENERIC_WARNING_OTHERS
|
|
elseif self.combinedcount > 1 then
|
|
combinedText = combinedText .. " " .. L.GENERIC_WARNING_OTHERS2:format(self.combinedcount)
|
|
end
|
|
--Process
|
|
for i = 1, #argTable do
|
|
if type(argTable[i]) == "string" then
|
|
argTable[i] = combinedText
|
|
end
|
|
end
|
|
end
|
|
local announceCount
|
|
if self.announceType and (self.announceType == "count" or self.announceType == "targetcount" or self.announceType == "sooncount" or self.announceType == "incomingcount") then--Don't use find "count" here, it'll match countdown
|
|
--Stage triggers don't pass count, but they do not need to, there is a stage callback and trigger option in WA that should be used
|
|
if type(argTable[1]) == "number" then
|
|
announceCount = argTable[1]
|
|
end
|
|
end
|
|
local message = stringUtils.pformat(self.text, unpack(argTable))
|
|
local text = ("%s%s%s|r%s"):format(
|
|
(DBM.Options.WarningIconLeft and self.icon and textureCode:format(self.icon)) or "",
|
|
colorCode,
|
|
message,
|
|
(DBM.Options.WarningIconRight and self.icon and textureCode:format(self.icon)) or ""
|
|
)
|
|
self.combinedcount = 0
|
|
self.combinedtext = {}
|
|
if not cachedColorFunctions[self.color] then
|
|
local color = self.color -- upvalue for the function to colorize names, accessing self in the colorize closure is not safe as the color of the announce object might change (it would also prevent the announce from being garbage-collected but announce objects are never destroyed)
|
|
cachedColorFunctions[color] = function(cap)
|
|
cap = cap:sub(2, -2)
|
|
local noStrip = cap:match("noStrip ")
|
|
if not noStrip then
|
|
local name = cap
|
|
local playerClass, playerIcon = DBM:GetRaidClass(name)
|
|
if playerClass ~= "UNKNOWN" then
|
|
cap = DBM:GetShortServerName(cap)--Only run realm strip function if class color was valid (IE it's an actual playername)
|
|
end
|
|
local playerColor = RAID_CLASS_COLORS[playerClass] or color
|
|
if playerColor then
|
|
if playerIcon > 0 and playerIcon <= 8 then
|
|
cap = ("|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_%d:0|t"):format(playerIcon) .. ("|r|cff%.2x%.2x%.2x%s|r|cff%.2x%.2x%.2x"):format(playerColor.r * 255, playerColor.g * 255, playerColor.b * 255, cap, color.r * 255, color.g * 255, color.b * 255)
|
|
else
|
|
cap = ("|r|cff%.2x%.2x%.2x%s|r|cff%.2x%.2x%.2x"):format(playerColor.r * 255, playerColor.g * 255, playerColor.b * 255, cap, color.r * 255, color.g * 255, color.b * 255)
|
|
end
|
|
end
|
|
else
|
|
cap = cap:sub(9)
|
|
end
|
|
return cap
|
|
end
|
|
end
|
|
text = text:gsub(">.-<", cachedColorFunctions[self.color])
|
|
DBM:AddWarning(text, nil, self)
|
|
if DBM.Options.ShowWarningsInChat then
|
|
if not DBM.Options.WarningIconChat then
|
|
text = text:gsub(textureExp, "") -- textures @ chat frame can (and will) distort the font if using certain combinations of UI scale, resolution and font size TODO: is this still true as of cataclysm?
|
|
end
|
|
self.mod:AddMsg(text, nil)
|
|
end
|
|
--Message: Full message text
|
|
--Icon: Texture path/id for icon
|
|
--Type: Announce type
|
|
----Types: you, target, targetcount, targetsource, spell, ends, endtarget, fades, adds, count, stack, cast, soon, sooncount, prewarn, bait, stage, stagechange, prestage, moveto
|
|
------Personal/Role (Applies to you, or your job): you, stack, bait, moveto, fades
|
|
------General Target Messages (informative, doesn't usually apply to you): target, targetsource, targetcount
|
|
------Fight Changes (Stages, adds, boss buff/debuff, etc): stage, stagechange, prestage, adds, ends, endtarget
|
|
------General (can really apply to anything): spell, count, soon, sooncount, prewarn
|
|
--SpellId: Raw spell or encounter journal Id if available.
|
|
--Mod ID: Encounter ID as string, or a generic string for mods that don't have encounter ID (such as trash, dummy/test mods)
|
|
--boolean: Whether or not this warning is a special warning (higher priority). BW would call this "emphasized"
|
|
--announceCount: If it's a count announce, this will provide access to the number value of that count. This, along with spellId should be used instead of message text scanning for most weak auras that need to target specific count casts
|
|
DBM:FireEvent("DBM_Announce", message, self.icon, self.type, self.spellId, self.mod.id, false, announceCount)
|
|
if self.sound > 0 then--0 means muted, 1 means no voice pack support, 2 means voice pack version/support
|
|
if self.sound > 1 and DBM.Options.ChosenVoicePack2 ~= "None" and DBM.Options.VPReplacesAnnounce and not private.voiceSessionDisabled and not DBM.Options.VPDontMuteSounds and self.sound <= private.swFilterDisabled then return end
|
|
if not self.option or self.mod.Options[self.option .. "SWSound"] ~= "None" then
|
|
DBM:PlaySoundFile(DBM.Options.RaidWarningSound, nil, true)--Validate true
|
|
end
|
|
end
|
|
else
|
|
self.combinedcount = 0
|
|
self.combinedtext = {}
|
|
end
|
|
end
|
|
|
|
---Object that's used when precision isn't possible (number of targets variable or unknown)
|
|
---@param delay number
|
|
---@param ... any
|
|
function announcePrototype:CombinedShow(delay, ...)
|
|
if self.option and not self.mod.Options[self.option] then return end
|
|
if DBM.Options.DontShowBossAnnounces then return end -- don't show the announces if the spam filter option is set
|
|
if DBM.Options.DontShowTargetAnnouncements and (self.announceType == "target" or self.announceType == "targetcount") and not self.noFilter then return end--don't show announces that are generic target announces
|
|
local argTable = {...}
|
|
for i = 1, #argTable do
|
|
if type(argTable[i]) == "string" then
|
|
if #self.combinedtext < 7 then--Throttle spam. We may not need more than 6 targets..
|
|
if not checkEntry(self.combinedtext, argTable[i]) then
|
|
self.combinedtext[#self.combinedtext + 1] = argTable[i]
|
|
end
|
|
else
|
|
self.combinedcount = self.combinedcount + 1
|
|
end
|
|
end
|
|
end
|
|
DBMScheduler:Unschedule(self.Show, self.mod, self)
|
|
local id = DBMScheduler:Schedule(delay or 0.5, self.Show, self.mod, self, ...)
|
|
test:Trace(self.mod, "SchedulerHideFromTraceIfUnscheduled", id)
|
|
test:Trace(self.mod, "SetScheduleMethodName", id, self, "CombinedShow", ...)
|
|
end
|
|
|
|
---New object that allows defining count instead of scheduling for more efficient and immediate warnings when precise count is known
|
|
---@param maxTotal number
|
|
---@param ... any
|
|
function announcePrototype:PreciseShow(maxTotal, ...)
|
|
test:Trace(self.mod, "CombinedWarningPreciseShow", self, maxTotal)
|
|
if self.option and not self.mod.Options[self.option] then return end
|
|
if DBM.Options.DontShowBossAnnounces then return end -- don't show the announces if the spam filter option is set
|
|
if DBM.Options.DontShowTargetAnnouncements and (self.announceType == "target" or self.announceType == "targetcount") and not self.noFilter then return end--don't show announces that are generic target announces
|
|
local argTable = {...}
|
|
for i = 1, #argTable do
|
|
if type(argTable[i]) == "string" then
|
|
if #self.combinedtext < 7 then--Throttle spam. We may not need more than 6 targets..
|
|
if not checkEntry(self.combinedtext, argTable[i]) then
|
|
self.combinedtext[#self.combinedtext + 1] = argTable[i]
|
|
end
|
|
else
|
|
self.combinedcount = self.combinedcount + 1
|
|
end
|
|
end
|
|
end
|
|
DBMScheduler:Unschedule(self.Show, self.mod, self)
|
|
local viableTotal = DBM:NumRealAlivePlayers()
|
|
if (maxTotal <= #self.combinedtext + self.combinedcount) or (viableTotal <= #self.combinedtext + self.combinedcount) then--All targets gathered, show immediately
|
|
self:Show(...)--Does this need self or mod? will it have this bug? https://github.com/DeadlyBossMods/DBM-Unified/issues/153
|
|
test:Trace(self.mod, "CombinedWarningPreciseShowSuccess", self, maxTotal)
|
|
else--And even still, use scheduling backup in case counts still fail
|
|
local id = DBMScheduler:Schedule(1.2, self.Show, self.mod, self, ...)
|
|
test:Trace(self.mod, "SchedulerHideFromTraceIfUnscheduled", id, maxTotal)
|
|
test:Trace(self.mod, "SetScheduleMethodName", id, self, "PreciseShow", maxTotal, ...)
|
|
end
|
|
end
|
|
|
|
---@param t number
|
|
---@param ... any
|
|
function announcePrototype:Schedule(t, ...)
|
|
local id = DBMScheduler:Schedule(t, self.Show, self.mod, self, ...)
|
|
test:Trace(self.mod, "SetScheduleMethodName", id, self, "Schedule", ...)
|
|
return id
|
|
end
|
|
|
|
---@param time number?
|
|
---@param numAnnounces number?
|
|
---@param ... any
|
|
function announcePrototype:Countdown(time, numAnnounces, ...)
|
|
DBMScheduler:ScheduleCountdown(time, numAnnounces, self.Show, self.mod, self, ...)
|
|
end
|
|
|
|
function announcePrototype:Cancel(...)
|
|
return DBMScheduler:Unschedule(self.Show, self.mod, self, ...)
|
|
end
|
|
|
|
---@param name VPSound?
|
|
---@param customPath? string|number
|
|
function announcePrototype:Play(name, customPath)
|
|
local voice = DBM.Options.ChosenVoicePack2
|
|
if private.voiceSessionDisabled or voice == "None" or not DBM.Options.VPReplacesAnnounce then return end
|
|
local always = DBM.Options.AlwaysPlayVoice
|
|
if DBM.Options.DontShowTargetAnnouncements and (self.announceType == "target" or self.announceType == "targetcount") and not self.noFilter and not always then return end--don't show announces that are generic target announces
|
|
if (not DBM.Options.DontShowBossAnnounces and (not self.option or self.mod.Options[self.option]) or always) and self.sound <= private.swFilterDisabled then
|
|
--Filter tank specific voice alerts for non tanks if tank filter enabled
|
|
--But still allow AlwaysPlayVoice to play as well.
|
|
if (name == "changemt" or name == "tauntboss") and DBM.Options.FilterTankSpec and not self.mod:IsTank() and not always then return end
|
|
local path = customPath or ("Interface\\AddOns\\DBM-VP" .. voice .. "\\" .. name .. ".ogg")
|
|
DBM:PlaySoundFile(path)
|
|
end
|
|
end
|
|
|
|
---@param t number
|
|
---@param name VPSound?
|
|
---@param customPath? string|number
|
|
function announcePrototype:ScheduleVoice(t, name, customPath)
|
|
if private.voiceSessionDisabled or DBM.Options.ChosenVoicePack2 == "None" or not DBM.Options.VPReplacesAnnounce then return end
|
|
DBMScheduler:Unschedule(self.Play, self.mod, self)--Allow ScheduleVoice to be used in same way as CombinedShow
|
|
local id = DBMScheduler:Schedule(t, self.Play, self.mod, self, name, customPath)
|
|
test:Trace(self.mod, "SetScheduleMethodName", id, self, "ScheduleVoice", name, customPath)
|
|
return id
|
|
end
|
|
|
|
---Object Permits scheduling voice multiple times for same object
|
|
---@param t number
|
|
---@param name VPSound?
|
|
---@param customPath? string|number
|
|
function announcePrototype:ScheduleVoiceOverLap(t, name, customPath)
|
|
if private.voiceSessionDisabled or DBM.Options.ChosenVoicePack2 == "None" or not DBM.Options.VPReplacesAnnounce then return end
|
|
local id = DBMScheduler:Schedule(t, self.Play, self.mod, self, name, customPath)
|
|
test:Trace(self.mod, "SetScheduleMethodName", id, self, "ScheduleVoiceOverLap", name, customPath)
|
|
return id
|
|
end
|
|
|
|
function announcePrototype:CancelVoice(...)
|
|
if private.voiceSessionDisabled or DBM.Options.ChosenVoicePack2 == "None" or not DBM.Options.VPReplacesAnnounce then return end
|
|
return DBMScheduler:Unschedule(self.Play, self.mod, self, ...)
|
|
end
|
|
|
|
---old constructor (no auto-localize)
|
|
---@param text string
|
|
---@param color number? 1 = Positive Message, 2 = Normal Message, 3 - Higher Priority, 4 - Highest Priority
|
|
---@param icon number|string? Use number for spellId, -number for journalID, number as string for textureID
|
|
---@param optionDefault SpecFlags|boolean?
|
|
---@param optionName string|boolean? String for custom option name. Using false hides option completely
|
|
---@param soundOption number|boolean? 0 = No Sound, 1 = Sound with no voice pack support, >=2 = Voice pack version/support
|
|
---@param spellID number|string? Used to define a spellID used for GroupSpells and WeakAura key
|
|
---@param waCustomName any? Used to show custom name/text for Spell header (usually used when a made up SpellID is used)
|
|
function bossModPrototype:NewAnnounce(text, color, icon, optionDefault, optionName, soundOption, spellID, waCustomName)
|
|
if not text then
|
|
error("NewAnnounce: you must provide announce text", 2)
|
|
end
|
|
if type(text) == "number" then
|
|
DBM:Debug("|cffff0000NewAnnounce: Non auto localized text cannot be numbers, fix this for |r" .. text)
|
|
end
|
|
if type(optionName) == "number" then
|
|
DBM:Debug("|cffff0000NewAnnounce: Non auto localized optionNames cannot be numbers, fix this for |r" .. text)
|
|
optionName = nil
|
|
end
|
|
if soundOption and type(soundOption) == "boolean" then
|
|
soundOption = 0--No Sound
|
|
end
|
|
icon = DBM:ParseSpellIcon(icon)
|
|
---@class Announce
|
|
local obj = setmetatable(
|
|
{
|
|
objClass = "Announce",
|
|
text = self.localization.warnings[text],
|
|
combinedtext = {},
|
|
combinedcount = 0,
|
|
color = DBM.Options.WarningColors[color or 1] or DBM.Options.WarningColors[1],
|
|
sound = soundOption or 1,
|
|
mod = self,
|
|
icon = icon,
|
|
spellId = spellID,--For WeakAuras / other callbacks
|
|
},
|
|
mt
|
|
)
|
|
test:Trace(self, "NewAnnounce", obj, "untyped")
|
|
if optionName then
|
|
obj.option = optionName
|
|
self:AddBoolOption(obj.option, optionDefault, "announce", nil, nil, nil, spellID, nil, waCustomName)
|
|
elseif optionName ~= false then
|
|
obj.option = text
|
|
self:AddBoolOption(obj.option, optionDefault, "announce", nil, nil, nil, spellID, nil, waCustomName)
|
|
end
|
|
tinsert(self.announces, obj)
|
|
return obj
|
|
end
|
|
|
|
-- new constructor (partially auto-localized warnings and options, yay!)
|
|
---@return Announce|Announce0|Announce1|Announce2
|
|
local function newAnnounce(self, announceType, spellId, color, icon, optionDefault, optionName, castTime, preWarnTime, soundOption, noFilter)
|
|
if not spellId then
|
|
error("newAnnounce: you must provide spellId", 2)
|
|
end
|
|
local optionVersion, alternateSpellId
|
|
if type(optionName) == "number" then
|
|
if optionName > 10 then--Being used as spell name shortening
|
|
if DBM.Options.WarningShortText then
|
|
alternateSpellId = optionName
|
|
end
|
|
else--Being used as option version
|
|
optionVersion = optionName
|
|
end
|
|
optionName = nil
|
|
end
|
|
if soundOption and type(soundOption) == "boolean" then
|
|
soundOption = 0--No Sound
|
|
end
|
|
local text, spellName = setText(announceType, alternateSpellId or spellId, castTime, preWarnTime, nil, spellId)
|
|
icon = DBM:ParseSpellIcon(icon or spellId)
|
|
---@class Announce
|
|
local obj = setmetatable( -- todo: fix duplicate code
|
|
{
|
|
objClass = "Announce",
|
|
text = text,
|
|
combinedtext = {},
|
|
combinedcount = 0,
|
|
announceType = announceType,
|
|
color = DBM.Options.WarningColors[color or 1] or DBM.Options.WarningColors[1],
|
|
mod = self,
|
|
icon = icon,
|
|
sound = soundOption or 1,
|
|
type = announceType,
|
|
spellId = spellId,
|
|
spellName = spellName,
|
|
noFilter = noFilter,
|
|
castTime = castTime,
|
|
preWarnTime = preWarnTime,
|
|
},
|
|
mt
|
|
)
|
|
test:Trace(self, "NewAnnounce", obj, announceType)
|
|
if optionName then
|
|
obj.option = optionName
|
|
self:AddBoolOption(obj.option, optionDefault, "announce", nil, nil, nil, spellId, announceType)
|
|
elseif optionName ~= false then
|
|
obj.option = "announce" .. spellId .. announceType .. (optionVersion or "")
|
|
self:AddBoolOption(obj.option, optionDefault, "announce", nil, nil, nil, spellId, announceType)
|
|
if noFilter and announceType == "target" then
|
|
self.localization.options[obj.option] = L.AUTO_ANNOUNCE_OPTIONS["targetNF"]:format(spellId)
|
|
else
|
|
self.localization.options[obj.option] = L.AUTO_ANNOUNCE_OPTIONS[announceType]:format(spellId)
|
|
end
|
|
end
|
|
tinsert(self.announces, obj)
|
|
return obj
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce0
|
|
function bossModPrototype:NewYouAnnounce(spellId, color, ...)
|
|
---@type Announce0
|
|
return newAnnounce(self, "you", spellId, color or 1, ...)
|
|
end
|
|
|
|
---@param optionDefault SpecFlags|boolean?
|
|
function bossModPrototype:NewTargetNoFilterAnnounce(spellId, color, icon, optionDefault, optionName, castTime, preWarnTime, soundOption) -- spellId, color, icon, optionDefault, optionName, castTime, preWarnTime, soundOption, noFilter
|
|
---@type Announce1
|
|
return newAnnounce(self, "target", spellId, color or 3, icon, optionDefault, optionName, castTime, preWarnTime, soundOption, true)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce1
|
|
function bossModPrototype:NewTargetAnnounce(spellId, color, ...)
|
|
---@type Announce1
|
|
return newAnnounce(self, "target", spellId, color or 3, ...)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce2
|
|
function bossModPrototype:NewTargetSourceAnnounce(spellId, color, ...)
|
|
---@type Announce2
|
|
return newAnnounce(self, "targetsource", spellId, color or 3, ...)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce2NumStr
|
|
function bossModPrototype:NewTargetCountAnnounce(spellId, color, ...)
|
|
---@type Announce2NumStr
|
|
return newAnnounce(self, "targetcount", spellId, color or 3, ...)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce0
|
|
function bossModPrototype:NewSpellAnnounce(spellId, color, ...)
|
|
---@type Announce0
|
|
return newAnnounce(self, "spell", spellId, color or 2, ...)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce1
|
|
function bossModPrototype:NewSpellSourceAnnounce(spellId, color, ...)
|
|
---@type Announce1
|
|
return newAnnounce(self, "spellsource", spellId, color or 2, ...)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce0
|
|
function bossModPrototype:NewIncomingAnnounce(spellId, color, ...)
|
|
---@type Announce0
|
|
return newAnnounce(self, "incoming", spellId, color or 2, ...)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce1Num
|
|
function bossModPrototype:NewIncomingCountAnnounce(spellId, color, ...)
|
|
---@type Announce1Num
|
|
return newAnnounce(self, "incomingcount", spellId, color or 2, ...)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce0
|
|
function bossModPrototype:NewEndAnnounce(spellId, color, ...)
|
|
---@type Announce0
|
|
return newAnnounce(self, "ends", spellId, color or 2, ...)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce1
|
|
function bossModPrototype:NewEndTargetAnnounce(spellId, color, ...)
|
|
---@type Announce1
|
|
return newAnnounce(self, "endtarget", spellId, color or 2, ...)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce0
|
|
function bossModPrototype:NewFadesAnnounce(spellId, color, ...)
|
|
---@type Announce0
|
|
return newAnnounce(self, "fades", spellId, color or 2, ...)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce1Num
|
|
function bossModPrototype:NewAddsLeftAnnounce(spellId, color, ...)
|
|
---@type Announce1Num
|
|
return newAnnounce(self, "addsleft", spellId, color or 3, ...)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce1Num
|
|
function bossModPrototype:NewCountAnnounce(spellId, color, ...)
|
|
---@type Announce1Num
|
|
return newAnnounce(self, "count", spellId, color or 2, ...)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce2StrNum
|
|
function bossModPrototype:NewStackAnnounce(spellId, color, ...)
|
|
---@type Announce2StrNum
|
|
return newAnnounce(self, "stack", spellId, color or 2, ...)
|
|
end
|
|
|
|
---@param spellId number|string
|
|
---@param castTime number?
|
|
---@param color number?
|
|
---@param icon number|string?
|
|
---@param optionDefault SpecFlags|boolean?
|
|
---@param optionName string|number|boolean?
|
|
---@param soundOption number|boolean?
|
|
function bossModPrototype:NewCastAnnounce(spellId, color, castTime, icon, optionDefault, optionName, _, soundOption) -- spellId, color, castTime, icon, optionDefault, optionName, noArg, soundOption
|
|
---@type Announce0
|
|
return newAnnounce(self, "cast", spellId, color or 3, icon, optionDefault, optionName, castTime, nil, soundOption)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce0
|
|
function bossModPrototype:NewSoonAnnounce(spellId, color, ...)
|
|
---@type Announce0
|
|
return newAnnounce(self, "soon", spellId, color or 2, ...)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce1Num
|
|
function bossModPrototype:NewSoonCountAnnounce(spellId, color, ...)
|
|
---@type Announce1Num
|
|
return newAnnounce(self, "sooncount", spellId, color or 2, ...)
|
|
end
|
|
|
|
---This object disables sounds, it's almost always used in combation with a countdown timer. Even if not a countdown, its a text only spam not a sound spam
|
|
---@param spellId number|string
|
|
---@param castTime number?
|
|
---@param preWarnTime number?
|
|
---@param color number?
|
|
---@param icon number|string?
|
|
---@param optionDefault SpecFlags|boolean?
|
|
---@param optionName string|number|boolean?
|
|
---@param noFilter boolean?
|
|
function bossModPrototype:NewCountdownAnnounce(spellId, color, icon, optionDefault, optionName, castTime, preWarnTime, _, noFilter) -- spellId, color, icon, optionDefault, optionName, castTime, preWarnTime, soundOption, noFilter
|
|
---@type Announce1Num
|
|
return newAnnounce(self, "countdown", spellId, color or 4, icon, optionDefault, optionName, castTime, preWarnTime, 0, noFilter)
|
|
end
|
|
|
|
---@param spellId number|string
|
|
---@param time number
|
|
---@param color number?
|
|
---@param icon number|string?
|
|
---@param optionDefault SpecFlags|boolean?
|
|
---@param optionName string|number|boolean?
|
|
---@param soundOption number|boolean?
|
|
function bossModPrototype:NewPreWarnAnnounce(spellId, time, color, icon, optionDefault, optionName, _, soundOption) -- spellId, time, color, icon, optionDefault, optionName, noArg, soundOption
|
|
---@type Announce0
|
|
return newAnnounce(self, "prewarn", spellId, color or 2, icon, optionDefault, optionName, nil, time, soundOption)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce0
|
|
function bossModPrototype:NewBaitAnnounce(spellId, color, ...)
|
|
---@type Announce0
|
|
return newAnnounce(self, "bait", spellId, color or 3, ...)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce0
|
|
function bossModPrototype:NewPhaseAnnounce(stage, color, icon, ...)
|
|
---@type Announce0
|
|
return newAnnounce(self, "stage", stage, color or 2, icon or "136116", ...)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce1
|
|
function bossModPrototype:NewPhaseChangeAnnounce(color, icon, ...)
|
|
---@type Announce1
|
|
return newAnnounce(self, "stagechange", 0, color or 2, icon or "136116", ...)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce0
|
|
function bossModPrototype:NewPrePhaseAnnounce(stage, color, icon, ...)
|
|
---@type Announce0
|
|
return newAnnounce(self, "prestage", stage, color or 2, icon or "136116", ...)
|
|
end
|
|
|
|
---@overload fun(self, spellId: number|string, color: number?, icon: number|string?, optionDefault: SpecFlags|boolean?, optionName: string|number|boolean?, castTime: number?, preWarnTime: number?, soundOption: number|boolean?, noFilter: boolean?): Announce1
|
|
function bossModPrototype:NewMoveToAnnounce(spellId, color, ...)
|
|
---@type Announce1
|
|
return newAnnounce(self, "moveto", spellId, color or 3, ...)
|
|
end
|
|
|