-
Notifications
You must be signed in to change notification settings - Fork 1
/
Afflicted.lua
679 lines (574 loc) · 25.2 KB
/
Afflicted.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
---@diagnostic disable: undefined-global
--[[
Afflicted 3, Shadow of US-Mal'Ganis
]]
---@class AceAddonModules
---@field Bars AfflictedBars
---@field Icons AfflictedIcons
---@class Afflicted: Frame, AceAddon
---@field modules AceAddonModules
Afflicted = LibStub("AceAddon-3.0"):NewAddon("Afflicted", "AceEvent-3.0")
local L = AfflictedLocals
local instanceType, arenaBracket
local summonedTotems = {}
local summonedObjects = {}
local playerGUID;
local COMBATLOG_OBJECT_REACTION_HOSTILE = _G["COMBATLOG_OBJECT_REACTION_HOSTILE"]
function Afflicted:OnInitialize()
self.defaults = {
profile = {
showAnchors = true,
showIcons = false,
targetOnly = false,
cooldownMessage = L["READY *spell (*target)"],
barWidth = 180,
barNameOnly = false,
barName = "BantoBar",
fontSize = 12,
fontName = "Friz Quadrata TT",
spellVersion = 0,
inside = {["arena"] = true, ["pvp"] = true},
anchors = {},
spells = {},
arenas = {[2] = {}, [3] = {}, [5] = {}},
anchorDefault = {
enabled = true,
announce = false,
growUp = false,
scale = 1.0,
xOff = 0,
yOff = 0,
maxRows = 20,
fadeTime = 0.5,
icon = "LEFT",
redirect = "",
display = "bars",
startMessage = "USED *spell (*target)",
endMessage = "FADED *spell (*target)",
announceColor = { r = 1.0, g = 1.0, b = 1.0 },
announceDest = "1",
},
},
}
-- Load default anchors
local anchor = self.defaults.profile.anchorDefault
self.defaults.profile.anchors.interrupts = CopyTable(anchor)
self.defaults.profile.anchors.interrupts.text = "Interrupts"
self.defaults.profile.anchors.cooldowns = CopyTable(anchor)
self.defaults.profile.anchors.cooldowns.text = "Cooldowns"
self.defaults.profile.anchors.spells = CopyTable(anchor)
self.defaults.profile.anchors.spells.text = "Spells"
self.defaults.profile.anchors.buffs = CopyTable(anchor)
self.defaults.profile.anchors.buffs.text = "Buffs"
self.defaults.profile.anchors.defenses = CopyTable(anchor)
self.defaults.profile.anchors.defenses.text = "Defenses"
self.defaults.profile.anchors.damage = CopyTable(anchor)
self.defaults.profile.anchors.damage.text = "Damage"
-- Initialize DB
self.db = LibStub:GetLibrary("AceDB-3.0"):New("AfflictedDB", self.defaults, true)
self.db.RegisterCallback(self, "OnProfileChanged", "Reload")
self.db.RegisterCallback(self, "OnProfileCopied", "Reload")
self.db.RegisterCallback(self, "OnProfileReset", "Reload")
self.db.RegisterCallback(self, "OnDatabaseShutdown", "OnDatabaseShutdown")
-- Setup our spell cache
self.writeQueue = {}
self.spells = setmetatable({}, {
__index = function(tbl, index)
if ( not index ) then return end
-- No data found, don't try and load this index again
if ( not Afflicted.db.profile.spells[index] ) then
tbl[index] = false
return false
end
local spell = loadstring("return " .. Afflicted.db.profile.spells[index])()
if ( type(spell) == "table" ) then
---@class spell
---@field cooldown number
---@field duration number
---@field name string
---@field cooldownName string
---@field configName string
---@field disabled boolean
---@field cdDisabled boolean
---@field anchor boolean
---@field cdAnchor number
spell.cooldown = spell.cooldown or 0
spell.duration = spell.duration or 0
spell.name = spell.name and L.spells[spell.name]
spell.cooldownName = spell.cooldownName and L.spells[spell.cooldownName]
spell.configName = spell.configName and L.spells[spell.configName]
end
tbl[index] = spell
return spell
end
})
-- Load spell database
local spells = AfflictedSpells:GetData()
-- Fresh DB, copy it all in
if ( self.db.profile.spellVersion == 0 ) then
self.db.profile.spellVersion = AfflictedSpells.version
self.db.profile.spells = CopyTable(spells)
-- Spell database changed
elseif( self.db.profile.spellVersion < AfflictedSpells.version ) then
self.db.profile.spellVersion = AfflictedSpells.version
-- Remove old spells
for spellID in pairs(self.db.profile.spells) do
if ( type(spellID) == "number" and spells[spellID] == false ) then
self.db.profile.spells[spellID] = nil
end
end
-- Load new or changed spells in
for spellID, data in pairs(spells) do
-- No spell of this type exists, add a new one
if ( not self.db.profile.spells[spellID] ) then
self.db.profile.spells[spellID] = data
-- We already have this, so selective merge
elseif( type(data) == "string" and self.db.profile.spells[spellID] ~= data ) then
local spell = loadstring("return" .. data)()
local oldSpell = self.spells[spellID]
oldSpell.duration = spell.duration
oldSpell.cooldown = spell.cooldown
oldSpell.type = spell.type
oldSpell.class = spell.class
self.writeQueue[spellID] = true
end
end
end
self:HelpFrame()
-- Load display libraries
self.bars = self.modules.Bars:LoadVisual()
self.icons = self.modules.Icons:LoadVisual()
self:RegisterEvent("ZONE_CHANGED_NEW_AREA")
end
-- Quick function to get the linked spells easily and such
function Afflicted:GetSpell(spellID, spellName)
if ( self.spells[spellName] ) then
return self.spells[spellName]
elseif( tonumber(self.spells[spellID]) ) then
return self.spells[self.spells[spellID]]
end
return self.spells[spellID]
end
local eventRegistered =
{
["UNIT_DIED"] = true,
["PARTY_KILL"] = true,
["SPELL_SUMMON"] = true,
["SPELL_CREATE"] = true,
["SPELL_CAST_SUCCESS"] = true,
["SPELL_AURA_REMOVED"] = true,
}
function Afflicted:COMBAT_LOG_EVENT_UNFILTERED(event, timestamp, eventType, sourceGUID, sourceName, sourceFlags, destGUID, destName, destFlags, ...)
if ( not eventRegistered[eventType] ) then
return
end
-- Enemy buff faded
if ( eventType == "SPELL_AURA_REMOVED" and bit.band(sourceFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) == COMBATLOG_OBJECT_REACTION_HOSTILE ) then
local spellID, spellName, spellSchool, auraType = ...
local spell = self:GetSpell(spellID, spellName)
if ( auraType == "BUFF" and spell and spell.type == "buff" ) then
self:AbilityEarlyFade(sourceGUID, sourceName, spell, spellID)
end
-- Spell casted succesfully
elseif( eventType == "SPELL_CAST_SUCCESS" and bit.band(sourceFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) == COMBATLOG_OBJECT_REACTION_HOSTILE ) then
local spellID, spellName, spellSchool, auraType = ...
local spell = self:GetSpell(spellID, spellName)
if ( spell and spell.resets ) then
self:ResetCooldowns(sourceGUID, spell.resets)
end
-- Totems and traps will be handled in SPELL_SUMMON and SPELL_CREATE, don't trigger them here
if ( spell and ( spell.type == "totem" or spell.type == "trap" ) ) then
return
end
self:AbilityTriggered(sourceGUID, sourceName, spell, spellID)
-- Check for something being summoned (Pets, totems)
elseif( eventType == "SPELL_SUMMON" and bit.band(sourceFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) == COMBATLOG_OBJECT_REACTION_HOSTILE ) then
local spellID, spellName, spellSchool = ...
-- Fixes an issue with totems not being removed when they get redropped
local id = sourceGUID .. (AfflictedSpells:GetTotemClass(spellName) or spellName)
local spell = self:GetSpell(spellID, spellName)
if ( spell and spell.type == "totem" ) then
-- We already had a totem of this timer up, remove the previous one first
if ( summonedTotems[id] ) then
self[self.db.profile.anchors[spell.anchor].display]:RemoveTimerByID(summonedTotems[id])
end
-- Set it as summoned so the totem specifically dying removes its timers
summonedObjects[destGUID] = sourceGUID .. spellID
-- Now trigger
self:AbilityTriggered(sourceGUID, sourceName, spell, spellID)
end
-- Set this as the active totem of that type down
summonedTotems[id] = sourceGUID .. spellID
-- Check for something being created (Traps, ect)
elseif( eventType == "SPELL_CREATE" and bit.band(sourceFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) == COMBATLOG_OBJECT_REACTION_HOSTILE ) then
local spellID, spellName, spellSchool = ...
local spell = self:GetSpell(spellID, spellName)
if ( spell and spell.type == "trap" ) then
-- Set it as summoned so the totem specifically dying removes its timers
summonedObjects[destGUID] = sourceGUID .. spellID
self:AbilityTriggered(sourceGUID, sourceName, spell, spellID)
end
-- Check if we should clear timers
elseif( ( eventType == "PARTY_KILL" or ( instanceType ~= "arena" and eventType == "UNIT_DIED" ) ) and bit.band(destFlags, COMBATLOG_OBJECT_REACTION_HOSTILE) == COMBATLOG_OBJECT_REACTION_HOSTILE ) then
-- If this is a summoned object (trap/totem) that was specifically killed, remove its timer
if ( summonedObjects[destGUID] ) then
self.bars:RemoveTimerByID(summonedObjects[destGUID])
self.icons:RemoveTimerByID(summonedObjects[destGUID])
return
end
-- If the player has any totems, kill them off with the player
local offset = string.len(destGUID)
for guid in pairs(summonedTotems) do
if ( string.sub(guid, 0, offset) == destGUID ) then
self.bars:UnitDied(guid)
self.icons:UnitDied(guid)
summonedTotems[guid] = nil
end
end
self.bars:UnitDied(destGUID)
self.icons:UnitDied(destGUID)
end
end
local SpellCastSucceededList =
{
[GetSpellInfo(51514)] = 51514,
[GetSpellInfo(17928)] = 17928,
[GetSpellInfo(61384)] = 61384,
[GetSpellInfo(50796)] = 50796,
[GetSpellInfo(53007)] = 53007,
}
function Afflicted:UNIT_SPELLCAST_SUCCEEDED(event, unitToken, spellName, spellRank, ...)
local sourceGUID = UnitGUID(unitToken);
local sourceName = UnitName(unitToken);
local reaction = UnitReaction(unitToken, "player");
local spellId = SpellCastSucceededList[spellName];
if not spellId or sourceGUID == playerGUID or reaction > 3 then return;end
local spell = self:GetSpell(spellId, spellName);
if ( spell and spell.resets ) then
self:ResetCooldowns(sourceGUID, spell.resets);
end
self:AbilityTriggered(sourceGUID, sourceName, spell, spellId);
end
-- Reset spells
function Afflicted:ResetCooldowns(sourceGUID, resets)
for spellID in pairs(resets) do
local spellData = Afflicted.spells[spellID]
if ( spellData and spellData.cdAnchor ) then
self[self.db.profile.anchors[spellData.cdAnchor].display]:RemoveTimerByID(sourceGUID .. spellID .. "CD")
end
end
end
-- Timer started
function Afflicted:AbilityTriggered(sourceGUID, sourceName, spellData, spellID)
-- No data found, it's disabled, or it's not in our interest cause it's not focus/target
if ( not spellData or ( self.db.profile.targetOnly and UnitGUID("target") ~= sourceGUID and UnitGUID("focus") ~= sourceGUID ) ) then
return
end
-- Grab spell info
local spellName, _, spellIcon = GetSpellInfo(spellID)
-- We're in an arena, and we don't want this spell enabled in the bracket
if ( arenaBracket and ( self.db.profile.arenas[arenaBracket][spellID] or self.db.profile.arenas[arenaBracket][spellName] ) ) then
return
end
-- Start duration timer (if any)
if ( not spellData.disabled and spellData.anchor and spellData.duration > 0 ) then
self:CreateTimer(sourceGUID, sourceName, spellData.anchor, spellData.repeating, false, spellData.duration, spellID, spellData.name or spellName, spellIcon)
-- Announce timer used
self:Announce(spellData, self.db.profile.anchors[spellData.anchor], "startMessage", spellID, spellData.name or spellName, sourceName)
end
-- Start CD timer
if ( not spellData.cdDisabled and spellData.cdAnchor and spellData.cooldown > 0 ) then
self:CreateTimer(sourceGUID, sourceName, spellData.cdAnchor, false, true, spellData.cooldown, spellID, spellData.cooldownName or spellName, spellIcon)
-- Only announce that a cooldown was used if we didn't announce a duration, it's implied that the cooldown started.
if ( spellData.disabled or not spellData.anchor or spellData.duration == 0 ) then
self:Announce(spellData, self.db.profile.anchors[spellData.cdAnchor], "startMessage", spellID, spellData.cooldownName or spellName, sourceName)
end
end
end
-- Spell faded early, so announce that
function Afflicted:AbilityEarlyFade(sourceGUID, sourceName, spellData, spellID, spellName, announce)
if ( spellData and not spellData.disabled and spellData.type == "buff" ) then
local removed = self[self.db.profile.anchors[spellData.anchor].display]:RemoveTimerByID(sourceGUID .. spellID)
if ( removed and announce ) then
self:Announce(spellData, self.db.profile.anchors[spellData.anchor], "endMessage", spellID, spellName, sourceName)
end
end
end
-- Timer faded naturally
function Afflicted:AbilityEnded(sourceGUID, sourceName, spellData, spellID, spellName, isCooldown)
if ( spellData ) then
if ( not isCooldown and not spellData.disabled ) then
self:Announce(spellData, self.db.profile.anchors[spellData.anchor], "endMessage", spellID, spellData.name or spellName, sourceName)
elseif( isCooldown and not spellData.cdDisabled ) then
self:Announce(spellData, self.db.profile.anchors[spellData.cdAnchor], "cooldownMessage", spellID, spellData.cooldownName or spellName, sourceName)
end
end
end
-- Create a timer and shunt it to the correct display
function Afflicted:CreateTimer(sourceGUID, sourceName, anchorName, repeating, isCooldown, duration, spellID, spellName, spellIcon)
local anchor = self.db.profile.anchors[anchorName]
if ( anchor ) then
self[anchor.display]:CreateTimer(sourceGUID, sourceName, anchorName, repeating, isCooldown, duration, spellID, spellName, spellIcon)
end
end
-- Announce something
function Afflicted:Announce(spellData, anchor, key, spellID, spellName, sourceName, isCooldown)
local msg
if ( key == "cooldownMessage" and ( spellData.custom or ( anchor.enabled and anchor.announce ) ) ) then
msg = self.db.profile.cooldownMessage
elseif( spellData.custom ) then
msg = spellData[key]
elseif( anchor and anchor.enabled and anchor.announce ) then
msg = anchor[key] or ""
end
if ( not msg or msg == "" ) then
return
end
msg = string.gsub(msg, "*spell", spellName)
msg = string.gsub(msg, "*target", self:StripServer(sourceName))
self:SendMessage(msg, anchor.announceDest, anchor.announceColor, spellID)
end
-- Database is getting ready to be written, we need to convert any changed data back into text
function Afflicted:OnDatabaseShutdown()
for id in pairs(self.writeQueue) do
-- We currently have a table, meaning we can write it out as a string
if ( type(self.spells[id]) == "table" ) then
local data = ""
for key, value in pairs(self.spells[id]) do
local text = ""
-- Right now, the tables in the spells are resets which is a number indexed table
if ( type(value) == "table" ) then
for _, subValue in pairs(value) do
text = string.format("%s%s;", text, subValue)
end
text = string.format("{%s}", text)
elseif( type(value) == "string" ) then
text = string.format("'%s'", value)
else
text = tostring(value)
end
data = string.format("%s%s=%s;", data, key, text)
end
self.db.profile.spells[id] = string.format("{%s}", data)
-- We have a linked spell setup (spellID -> spellID)
elseif( type(self.spells[id]) == "number" ) then
self.db.profile.spells[id] = self.spells[id]
-- Nothing found, so reset the value
else
self.db.profile.spells[id] = nil
end
self.writeQueue[id] = nil
end
end
-- Find the current arena bracket we are in
function Afflicted:SaveArenaBracket()
arenaBracket = nil
for i=1, MAX_BATTLEFIELD_QUEUES do
local status, _, _, _, _, teamSize = GetBattlefieldStatus(i)
if ( status == "active" and teamSize > 0 ) then
arenaBracket = teamSize
break
end
end
end
-- Couldn't find data on the arena bracket we were in, so keep checking up UBS until we find it (or, we leave the arena)
function Afflicted:UPDATE_BATTLEFIELD_STATUS()
self:SaveArenaBracket()
if ( arenaBracket ) then
self:UnregisterEvent("UPDATE_BATTLEFIELD_STATUS")
end
end
-- Enabling Afflicted based on zone type
function Afflicted:ZONE_CHANGED_NEW_AREA()
local type = select(2, IsInInstance())
playerGUID = UnitGUID("player");
if ( type ~= instanceType ) then
-- Clear anchors because we changed zones
for key, data in pairs(self.db.profile.anchors) do
self[data.display]:ClearTimers(key)
end
-- Reset bracket
arenaBracket = nil
-- Monitor spells?
if ( self.db.profile.inside[type] ) then
-- Find arena bracket
if ( type == "arena" ) then
self:SaveArenaBracket()
if ( not arenaBracket ) then
self:RegisterEvent("UPDATE_BATTLEFIELD_STATUS")
end
end
-- Reset our summoned stuff since we don't care about anything before inside
for k in pairs(summonedObjects) do summonedObjects[k] = nil end
for k in pairs(summonedTotems) do summonedTotems[k] = nil end
self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
self:RegisterEvent("UNIT_SPELLCAST_SUCCEEDED")
else
self:UnregisterEvent("COMBAT_LOG_EVENT_UNFILTERED")
self:UnregisterEvent("UPDATE_BATTLEFIELD_STATUS")
self:UnregisterEvent("UNIT_SPELLCAST_SUCCEEDED")
end
end
instanceType = type
end
function Afflicted:ReloadEnabled()
instanceType = nil
self:ZONE_CHANGED_NEW_AREA()
end
function Afflicted:Reload()
if ( self.icons ) then
self.icons:ReloadVisual()
end
if ( self.bars ) then
self.bars:ReloadVisual()
end
self:HelpFrame()
end
-- Strips server name
function Afflicted:StripServer(text)
local name, server = string.match(text, "(.-)%-(.*)$")
if ( not name and not server ) then
return text
end
return name
end
function Afflicted:WrapIcon(msg, spellID)
if ( not self.db.profile.showIcons or not spellID ) then
return msg
end
-- Make sure we have a valid icon
local icon = select(3, GetSpellInfo(spellID))
if ( not icon ) then
return msg
end
return string.format("|T%s:0:0|t %s", icon, msg)
end
local chatFrames = {}
local _G = getfenv(0)
function Afflicted:SendMessage(msg, dest, color, spellID)
-- We're not showing anything
if ( dest == "none" ) then
return
-- We're undergrouped, so redirect it to our fake alert frame
elseif( dest == "rw" and GetNumRaidMembers() == 0 and GetNumPartyMembers() == 0 ) then
dest = "rwframe"
-- We're grouped, in a raid and not leader or assist
elseif( dest == "rw" and not IsRaidLeader() and not IsRaidOfficer() and GetNumRaidMembers() > 0 ) then
dest = "party"
end
-- Strip out any () leftover from no name being given
---@diagnostic disable-next-line: undefined-field
msg = string.trim(string.gsub(msg, "%(%)", ""))
-- Chat frame
if ( tonumber(dest) ) then
if ( not chatFrames[dest] ) then
chatFrames[dest] = _G["ChatFrame" .. dest]
end
local frame = chatFrames[dest] or DEFAULT_CHAT_FRAME
frame:AddMessage(string.format("|cff33ff99Afflicted|r|cffffffff:|r %s", self:WrapIcon(msg, spellID)), color.r, color.g, color.b)
-- Raid warning announcement to raid/party
elseif( dest == "rw" ) then
SendChatMessage(msg, "RAID_WARNING")
-- Raid warning frame, will not send it out to the party
elseif( dest == "rwframe" ) then
if ( not self.alertFrame ) then
self.alertFrame = CreateFrame("MessageFrame", nil, UIParent)
self.alertFrame:SetInsertMode("TOP")
self.alertFrame:SetFrameStrata("HIGH")
self.alertFrame:SetWidth(UIParent:GetWidth())
self.alertFrame:SetHeight(60)
self.alertFrame:SetFadeDuration(0.5)
self.alertFrame:SetTimeVisible(2)
self.alertFrame:SetFont((GameFontNormal:GetFont()), 20, "OUTLINE")
self.alertFrame:SetPoint("CENTER", 0, 60)
end
self.alertFrame:AddMessage(self:WrapIcon(msg, spellID), color.r, color.g, color.b)
-- Party chat
elseif( dest == "party" ) then
SendChatMessage(msg, "PARTY")
-- Combat text
elseif( dest == "ct" ) then
self:CombatText(self:WrapIcon(msg, spellID), color)
end
end
function Afflicted:CombatText(text, color, spellID)
-- SCT
if ( IsAddOnLoaded("sct") ) then
SCT:DisplayText(text, color, nil, "event", 1)
-- MSBT
elseif( IsAddOnLoaded("MikScrollingBattleText") ) then
MikSBT.DisplayMessage(text, MikSBT.DISPLAYTYPE_NOTIFICATION, false, color.r * 255, color.g * 255, color.b * 255)
-- Blizzard Combat Text
elseif( IsAddOnLoaded("Blizzard_CombatText") ) then
-- Haven't cached the movement function yet
if ( not COMBAT_TEXT_SCROLL_FUNCTION ) then
CombatText_UpdateDisplayedMessages()
end
CombatText_AddMessage(text, COMBAT_TEXT_SCROLL_FUNCTION, color.r, color.g, color.b)
end
end
function Afflicted:Print(msg)
DEFAULT_CHAT_FRAME:AddMessage("|cff33ff99Afflicted3|r: " .. msg)
end
function Afflicted:HelpFrame()
if ( self.helpFrame ) then
if ( Afflicted.db.profile.showAnchors ) then
self.helpFrame:Show()
else
self.helpFrame:Hide()
end
return
elseif( not Afflicted.db.profile.showAnchors ) then
return
end
local frame = CreateFrame("Frame", nil, UIParent)
frame:SetClampedToScreen(true)
frame:SetFrameStrata("LOW")
frame:SetWidth(300)
frame:SetHeight(100)
frame:RegisterForDrag("LeftButton")
frame:EnableMouse(true)
frame:SetMovable(true)
frame:SetScript("OnDragStart", function(self)
self:StartMoving()
end)
frame:SetScript("OnDragStop", function(self)
self:StopMovingOrSizing()
end)
frame:SetBackdrop({
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
edgeSize = 26,
insets = {left = 9, right = 9, top = 9, bottom = 9},
})
frame:SetBackdropColor(0, 0, 0, 0.85)
frame:SetPoint("CENTER", UIParent, "CENTER", 0, 225)
frame.titleBar = frame:CreateTexture(nil, "ARTWORK")
frame.titleBar:SetTexture("Interface\\DialogFrame\\UI-DialogBox-Header")
frame.titleBar:SetPoint("TOP", 0, 8)
frame.titleBar:SetWidth(200)
frame.titleBar:SetHeight(45)
frame.title = frame:CreateFontString(nil, "ARTWORK", "GameFontNormal")
frame.title:SetPoint("TOP", 0, 0)
frame.title:SetText("Afflicted")
frame.text = frame:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
frame.text:SetText(L["The black anchor boxes are used to move timer anchors around for Afflicted. Type /afflicted ui and check \"Show timer anchors\" or click \"Hide anchors\" below to hide them."])
frame.text:SetPoint("TOPLEFT", 12, -22)
frame.text:SetWidth(frame:GetWidth() - 20)
frame.text:SetJustifyH("LEFT")
frame.lock = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
frame.lock:SetText(L["Hide anchors"])
frame.lock:SetHeight(20)
frame.lock:SetWidth(100)
frame.lock:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 6, 8)
frame.lock:SetScript("OnEnter", OnEnter)
frame.lock:SetScript("OnLeave", OnLeave)
frame.lock.tooltipText = L["Hides the drag anchors in Afflicted."]
frame.lock:SetScript("OnClick", function(self)
Afflicted.db.profile.showAnchors = false
Afflicted:Reload()
---@diagnostic disable-next-line: undefined-field
LibStub("AceConfigRegistry-3.0"):NotifyChange("Afflicted")
end)
self.helpFrame = frame
end