---@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 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) 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) 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. --This changes actual ID so announce callback also swaps ID for WAs 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 -- 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) 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 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) DBMScheduler:Schedule(delay or 0.5, self.Show, self.mod, self, ...) end --New object that allows defining count instead of scheduling for more efficient and immediate warnings when precise count is known function announcePrototype:PreciseShow(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) or (viableTotal == #self.combinedtext) 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 else--And even still, use scheduling backup in case counts still fail DBMScheduler:Schedule(1.2, self.Show, self.mod, self, ...) end end function announcePrototype:Schedule(t, ...) return DBMScheduler:Schedule(t, self.Show, self.mod, self, ...) end 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 function announcePrototype:ScheduleVoice(t, ...) 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 return DBMScheduler:Schedule(t, self.Play, self.mod, self, ...) end --Object Permits scheduling voice multiple times for same object function announcePrototype:ScheduleVoiceOverLap(t, ...) if private.voiceSessionDisabled or DBM.Options.ChosenVoicePack2 == "None" or not DBM.Options.VPReplacesAnnounce then return end return DBMScheduler:Schedule(t, self.Play, self.mod, self, ...) 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) 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( { 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 ) 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!) 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 { 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 ) local catType = "announce"--Default to General announce if not self.NoSortAnnounce then--ALL announce objects will be assigned "announce", usually for mods that sort by phase instead --Change if Personal or Other if announceType == "target" or announceType == "targetcount" or announceType == "stack" then catType = "announceother" end end if optionName then obj.option = optionName self:AddBoolOption(obj.option, optionDefault, catType, nil, nil, nil, spellId, announceType) elseif optionName ~= false then obj.option = catType .. spellId .. announceType .. (optionVersion or "") self:AddBoolOption(obj.option, optionDefault, catType, 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, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewYouAnnounce(spellId, color, ...) 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 return newAnnounce(self, "target", spellId, color or 3, icon, optionDefault, optionName, castTime, preWarnTime, soundOption, true) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewTargetAnnounce(spellId, color, ...) return newAnnounce(self, "target", spellId, color or 3, ...) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewTargetSourceAnnounce(spellId, color, ...) return newAnnounce(self, "targetsource", spellId, color or 3, ...) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewTargetCountAnnounce(spellId, color, ...) return newAnnounce(self, "targetcount", spellId, color or 3, ...) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewSpellAnnounce(spellId, color, ...) return newAnnounce(self, "spell", spellId, color or 2, ...) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewSpellSourceAnnounce(spellId, color, ...) return newAnnounce(self, "spellsource", spellId, color or 2, ...) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewIncomingAnnounce(spellId, color, ...) return newAnnounce(self, "incoming", spellId, color or 2, ...) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewIncomingCountAnnounce(spellId, color, ...) return newAnnounce(self, "incomingcount", spellId, color or 2, ...) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewEndAnnounce(spellId, color, ...) return newAnnounce(self, "ends", spellId, color or 2, ...) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewEndTargetAnnounce(spellId, color, ...) return newAnnounce(self, "endtarget", spellId, color or 2, ...) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewFadesAnnounce(spellId, color, ...) return newAnnounce(self, "fades", spellId, color or 2, ...) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewAddsLeftAnnounce(spellId, color, ...) return newAnnounce(self, "addsleft", spellId, color or 3, ...) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewCountAnnounce(spellId, color, ...) return newAnnounce(self, "count", spellId, color or 2, ...) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewStackAnnounce(spellId, color, ...) return newAnnounce(self, "stack", spellId, color or 2, ...) end ---@param optionDefault SpecFlags|boolean? function bossModPrototype:NewCastAnnounce(spellId, color, castTime, icon, optionDefault, optionName, _, soundOption) -- spellId, color, castTime, icon, optionDefault, optionName, noArg, soundOption return newAnnounce(self, "cast", spellId, color or 3, icon, optionDefault, optionName, castTime, nil, soundOption) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewSoonAnnounce(spellId, color, ...) return newAnnounce(self, "soon", spellId, color or 2, ...) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewSoonCountAnnounce(spellId, color, ...) 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 optionDefault SpecFlags|boolean? function bossModPrototype:NewCountdownAnnounce(spellId, color, icon, optionDefault, optionName, castTime, preWarnTime, _, noFilter) -- spellId, color, icon, optionDefault, optionName, castTime, preWarnTime, soundOption, noFilter return newAnnounce(self, "countdown", spellId, color or 4, icon, optionDefault, optionName, castTime, preWarnTime, 0, noFilter) end ---@param optionDefault SpecFlags|boolean? function bossModPrototype:NewPreWarnAnnounce(spellId, time, color, icon, optionDefault, optionName, _, soundOption) -- spellId, time, color, icon, optionDefault, optionName, noArg, soundOption return newAnnounce(self, "prewarn", spellId, color or 2, icon, optionDefault, optionName, nil, time, soundOption) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewBaitAnnounce(spellId, color, ...) return newAnnounce(self, "bait", spellId, color or 3, ...) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewPhaseAnnounce(stage, color, icon, ...) return newAnnounce(self, "stage", stage, color or 2, icon or "136116", ...) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewPhaseChangeAnnounce(color, icon, ...) return newAnnounce(self, "stagechange", 0, color or 2, icon or "136116", ...) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewPrePhaseAnnounce(stage, color, icon, ...) return newAnnounce(self, "prestage", stage, color or 2, icon or "136116", ...) end ---@overload fun(self, spellId, color, icon, optionDefault: SpecFlags|boolean?, optionName, castTime, preWarnTime, soundOption, noFilter): Announce function bossModPrototype:NewMoveToAnnounce(spellId, color, ...) return newAnnounce(self, "moveto", spellId, color or 3, ...) end