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.

288 lines
12 KiB

local mod = DBM:NewMod("MPlusAffixes", "DBM-Affixes")
local L = mod:GetLocalizedStrings()
mod:SetRevision("20240423222539")
--mod:SetModelID(47785)
mod:SetZone(DBM_DISABLE_ZONE_DETECTION)--Stays active in all zones for zone change handlers, but registers events based on dungeon ids
mod.isTrashMod = true
mod.isTrashModBossFightAllowed = true
mod:RegisterEvents(
"ZONE_CHANGED_NEW_AREA",
"LOADING_SCREEN_DISABLED"
)
--TODO, fine tune tank stacks/throttle?
--TODO, when Season 4 starts, prune season 3 IDs, and add WW Season 1 ids
--[[
(ability.id = 240446 or ability.id = 409492) and type = "begincast"
or (ability.id = 408556 or ability.id = 408801) and type = "applydebuff"
or type = "dungeonencounterstart" or type = "dungeonencounterend"
or (source.type = "NPC" and source.firstSeen = timestamp) and (source.name = "Afflicted Soul") or (target.type = "NPC" and target.firstSeen = timestamp) and (target.name = "Afflicted Soul")
--]]
local warnExplosion = mod:NewCastAnnounce(240446, 4)
local warnIncorporeal = mod:NewCastAnnounce(408801, 4)
local warnAfflictedCry = mod:NewCastAnnounce(409492, 4, nil, nil, "Healer|RemoveMagic|RemoveCurse|RemoveDisease|RemovePoison", 2, nil, 14)--Flagged to only warn players who actually have literally any skill to deal with spirits, else alert is just extra noise to some rogue or warrior with no skills for mechanic
local warnDestabalize = mod:NewCastAnnounce(408805, 4, nil, nil, false)
local warnSpitefulFixate = mod:NewYouAnnounce(350209, 4)
local specWarnQuake = mod:NewSpecialWarningMoveAway(240447, nil, nil, nil, 1, 2)
local specWarnSpitefulFixate = mod:NewSpecialWarningYou(350209, false, nil, 2, 1, 2)
local specWarnEntangled = mod:NewSpecialWarningYou(408556, nil, nil, nil, 1, 14)
local specWarnGTFO = mod:NewSpecialWarningGTFO(209862, nil, nil, nil, 1, 8)--Volcanic and Sanguine
local timerQuakingCD = mod:NewNextTimer(20, 240447, nil, nil, nil, 3)
local timerEntangledCD = mod:NewCDTimer(30, 408556, nil, nil, nil, 3, 396347, nil, nil, 2, 3, nil, nil, nil, true)
local timerAfflictedCD = mod:NewCDTimer(30, 409492, nil, nil, nil, 5, 2, DBM_COMMON_L.HEALER_ICON, nil, mod:IsHealer() and 3 or nil, 3)--Timer is still on for all, cause knowing when they spawn still informs decisions like running ahead or pulling
local timerIncorporealCD = mod:NewCDTimer(45, 408801, nil, nil, nil, 5, nil, nil, nil, 3, 3)
mod:AddNamePlateOption("NPSanguine", 226510, "Tank")
--Antispam IDs for this mod: 1 run away, 2 dodge, 3 dispel, 4 incoming damage, 5 you/role, 6 misc, 7 gtfo, 8 personal aggregated alert
local incorporealCounting = false
local incorpDetected = false
local afflictedCounting = false
local afflictedDetected = false
local function checkEntangled(self)
if timerEntangledCD:GetRemaining() > 0 then
--Timer exists, do nothing
return
end
timerEntangledCD:Start(25)
self:Schedule(30, checkEntangled, self)
end
local function checkAfflicted(self)
if timerAfflictedCD:GetRemaining() > 0 then
--Timer exists, do nothing
return
end
timerAfflictedCD:Start(20)
self:Schedule(30, checkAfflicted, self)
end
local function checkIncorp(self)
if timerIncorporealCD:GetRemaining() > 0 then
--Timer exists, do nothing
return
end
timerIncorporealCD:Start(35)
self:Schedule(45, checkIncorp, self)
end
--UGLY function to detect this because there isn't a good API for this.
--player regen was very unreliable due to fact it only fires for self
--This wastes cpu time being an infinite loop though but probably no more so than any WA doing this
local function checkForCombat(self)
local combatFound = self:GroupInCombat()
if incorpDetected then
if combatFound and not incorporealCounting then
incorporealCounting = true
timerIncorporealCD:Resume()
local incorpRemaining = timerIncorporealCD:GetRemaining()
if incorpRemaining and incorpRemaining > 0 then--Shouldn't be 0, unless a player clicked it off, in which case we can't reschedule
self:Unschedule(checkIncorp)
self:Schedule(incorpRemaining+10, checkIncorp, self)
DBM:Debug("Experimental reschedule of checkIncorp running")
end
elseif not combatFound and incorporealCounting then
incorporealCounting = false
timerIncorporealCD:Pause()
self:Unschedule(checkIncorp)--Soon as a pause happens this can no longer be trusted
end
end
if afflictedDetected then
if combatFound and not afflictedCounting then
afflictedCounting = true
timerAfflictedCD:Resume()
local afflictRemaining = timerAfflictedCD:GetRemaining()
if afflictRemaining and afflictRemaining > 0 then--Shouldn't be 0, unless a player clicked it off, in which case we can't reschedule
self:Unschedule(checkAfflicted)
self:Schedule(afflictRemaining+10, checkAfflicted, self)
DBM:Debug("Experimental reschedule of checkAfflicted running")
end
elseif not combatFound and afflictedCounting then
afflictedCounting = false
timerAfflictedCD:Pause()
self:Unschedule(checkAfflicted)--Soon as a pause happens this can no longer be trusted
end
end
self:Schedule(0.25, checkForCombat, self)
end
do
local validZones
--Upcoming Seasons
if (C_MythicPlus.GetCurrentSeason() or 0) == 13 then--War Within Season 1
--2652, 2662, 2660, 2669, 670, 1822, 2286, 2290
validZones = {[2652]=true, [2662]=true, [2660]=true, [2669]=true, [670]=true, [1822]=true, [2286]=true, [2290]=true}
elseif (C_MythicPlus.GetCurrentSeason() or 0) == 14 then--War Within Season 2
--2651, 2649, 2648, 2661, ?, ?, ?, ?
validZones = {[2651]=true, [2649]=true, [2648]=true, [2661]=true}
--Current Season (latest LIVE season put in else so if api fails, it just always returns latest)
else--DF Season 4 (12)
--2516, 2526, 2515, 2521, 2527, 2519, 2451, 2520
validZones = {[2516]=true, [2526]=true, [2515]=true, [2521]=true, [2527]=true, [2519]=true, [2451]=true, [2520]=true}
end
local eventsRegistered = false
function mod:DelayedZoneCheck(force)
local currentZone = DBM:GetCurrentArea() or 0
if not force and validZones[currentZone] and not eventsRegistered then
eventsRegistered = true
self:RegisterShortTermEvents(
"SPELL_CAST_START 240446 409492 408805",
-- "SPELL_CAST_SUCCESS",
"SPELL_AURA_APPLIED 240447 226510 226512 350209 408556 408801",
-- "SPELL_AURA_APPLIED_DOSE",
"SPELL_AURA_REMOVED 226510",
-- "SPELL_DAMAGE 209862",
-- "SPELL_MISSED 209862",
"CHALLENGE_MODE_COMPLETED"
)
if self.Options.NPSanguine then
DBM:FireEvent("BossMod_EnableHostileNameplates")
end
DBM:Debug("Registering M+ events")
elseif force or (not validZones[currentZone] and eventsRegistered) then
eventsRegistered = false
afflictedCounting = false
incorporealCounting = false
incorpDetected = false
afflictedDetected = false
self:UnregisterShortTermEvents()
self:Unschedule(checkForCombat)
self:Unschedule(checkEntangled)
self:Unschedule(checkAfflicted)
self:Stop()
if self.Options.NPSanguine then
DBM.Nameplate:Hide(true, nil, nil, nil, true, true)
end
DBM:Debug("Unregistering M+ events")
end
end
function mod:LOADING_SCREEN_DISABLED()
self:UnscheduleMethod("DelayedZoneCheck")
--Checks Delayed 1 second after core checks to prevent race condition of checking before core did and updated cached ID
self:ScheduleMethod(2, "DelayedZoneCheck")
self:ScheduleMethod(6, "DelayedZoneCheck")
end
mod.OnInitialize = mod.LOADING_SCREEN_DISABLED
mod.ZONE_CHANGED_NEW_AREA = mod.LOADING_SCREEN_DISABLED
function mod:CHALLENGE_MODE_COMPLETED()
--This basically force unloads things even when in a dungeon, so it's not countdown affixes that are disabled
self:DelayedZoneCheck(true)
end
end
function mod:SPELL_CAST_START(args)
if not self.Options.Enabled then return end
local spellId = args.spellId
if spellId == 240446 and self:AntiSpam(3, "aff1") then
warnExplosion:Show()
elseif spellId == 409492 and self:AntiSpam(3, "aff2") then
warnAfflictedCry:Show()
warnAfflictedCry:Play("helpspirit")
if not afflictedDetected then
afflictedDetected = true
end
--This one is interesting cause it runs every 30 seconds, sometimes skips a cast and goes 60, but also pauses out of combat
afflictedCounting = true
timerAfflictedCD:Start()
self:Unschedule(checkForCombat)
self:Unschedule(checkAfflicted)
checkForCombat(self)
self:Schedule(40, checkAfflicted, self)
elseif spellId == 408805 and self:AntiSpam(3, "aff3") then
warnDestabalize:Show()
end
end
--[[
function mod:SPELL_CAST_SUCCESS(args)
if not self.Options.Enabled then return end
local spellId = args.spellId
if spellId == 373370 then
timerNightmareCloudCD:Start(30.5, args.sourceGUID)
end
end
--]]
function mod:SPELL_AURA_APPLIED(args)
if not self.Options.Enabled then return end
local spellId = args.spellId
if spellId == 240447 then
if self:AntiSpam(3, "aff5") then
timerQuakingCD:Start()
end
if args:IsPlayer() then
specWarnQuake:Show()
specWarnQuake:Play("range5")
end
elseif spellId == 226512 and args:IsPlayer() and self:AntiSpam(3, "aff4") then--Sanguine Ichor on player
specWarnGTFO:Show(args.spellName)
specWarnGTFO:Play("watchfeet")
elseif spellId == 226510 then--Sanguine Ichor on mob
if self.Options.NPSanguine then
DBM.Nameplate:Show(true, args.destGUID, spellId)
end
elseif spellId == 350209 and args:IsPlayer() and self:AntiSpam(3, "aff5") then
if self.Options.Specwarn350209you then
specWarnSpitefulFixate:Show()
specWarnSpitefulFixate:Play("targetyou")
else
warnSpitefulFixate:Show()
end
elseif spellId == 408556 then
if self:AntiSpam(20, "aff6") then
timerEntangledCD:Start(30)
--Entangled check runs every 30 seconds, and if conditions aren't met for it activating it skips and goes into next 30 second CD
--This checks if it was cast (by seeing if timer exists) if not, it starts next timer for next possible cast
self:Unschedule(checkEntangled)
self:Schedule(35, checkEntangled, self)
end
if args:IsPlayer() then
specWarnEntangled:Show()
specWarnEntangled:Play("breakvine")--breakvine
end
elseif spellId == 408801 and self:AntiSpam(25, "aff7") then
if not incorpDetected then
incorpDetected = true
end
--This one is interesting cause it runs every 45 seconds, sometimes skips a cast and goes 90, but also pauses out of combat
incorporealCounting = true
timerIncorporealCD:Start()
self:Unschedule(checkForCombat)
self:Unschedule(checkIncorp)
checkForCombat(self)
self:Schedule(50, checkIncorp, self)
end
end
--mod.SPELL_AURA_APPLIED_DOSE = mod.SPELL_AURA_APPLIED
function mod:SPELL_AURA_REMOVED(args)
if not self.Options.Enabled then return end
local spellId = args.spellId
if spellId == 226510 then--Sanguine Ichor on mob
if self.Options.NPSanguine then
DBM.Nameplate:Hide(true, args.destGUID, spellId)
end
end
end
--[[
function mod:SPELL_DAMAGE(_, _, _, _, destGUID, _, _, _, spellId, spellName)
if spellId == 209862 and destGUID == UnitGUID("player") and self:AntiSpam(3, "aff7") then
specWarnGTFO:Show(spellName)
specWarnGTFO:Play("watchfeet")
end
end
mod.SPELL_MISSED = mod.SPELL_DAMAGE
--]]
--<610.64 01:20:34> [CHAT_MSG_MONSTER_YELL] Marked by lightning!#Raszageth###Global Affix Stalker##0#0##0#3611#nil#0#false#false#false#false", -- [3882]
--<614.44 01:20:38> [CLEU] SPELL_AURA_APPLIED#Creature-0-3023-1477-12533-199388-00007705B2#Raszageth#Player-3726-0C073FB8#Onlysummonz-Khaz'goroth#396364#Mark of Wind#DEBUFF#nil", -- [3912]