-
Notifications
You must be signed in to change notification settings - Fork 41
/
Copy pathPlater_Auras.lua
2969 lines (2494 loc) · 123 KB
/
Plater_Auras.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
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
local Plater = _G.Plater
local DF = _G.DetailsFramework
local addonName, platerInternal = ...
local _ = nil
local IS_WOW_PROJECT_MAINLINE = WOW_PROJECT_ID == WOW_PROJECT_MAINLINE
local IS_WOW_PROJECT_NOT_MAINLINE = WOW_PROJECT_ID ~= WOW_PROJECT_MAINLINE
local IS_WOW_PROJECT_CLASSIC_ERA = WOW_PROJECT_ID == WOW_PROJECT_CLASSIC
--stop yellow lines on my editor
local tinsert = _G.tinsert
local min = _G.min
local max = _G.max
local abs = _G.abs
local G_CreateFrame = _G.CreateFrame
local CreateFrame = function (frameType , name, parent, template, id)
local frame = G_CreateFrame(frameType , name, parent, template, id)
DF:Mixin(frame, DF.FrameFunctions)
if frame.ApplyBackdrop then
NineSliceUtil.DisableSharpening(frame)
end
return frame
end
local unpack = _G.unpack
local CooldownFrame_Set = _G.CooldownFrame_Set
local GetTime = _G.GetTime
local UnitClass = _G.UnitClass
local UnitPlayerControlled = _G.UnitPlayerControlled
local UnitName = _G.UnitName
local wipe = _G.wipe
local UnitIsUnit = _G.UnitIsUnit
local UnitGUID = _G.UnitGUID
local GetSpellInfo = GetSpellInfo or function(spellID) if not spellID then return nil end local si = C_Spell.GetSpellInfo(spellID) if si then return si.name, nil, si.iconID, si.castTime, si.minRange, si.maxRange, si.spellID, si.originalIconID end end
local floor = _G.floor
local UnitAuraBySlot = _G.UnitAuraBySlot or (C_UnitAuras and (function(...) local auraData = C_UnitAuras.GetAuraDataBySlot(...); if not auraData then return nil; end; return AuraUtil.UnpackAuraData(auraData); end))
local GetAuraDataBySlot = _G.C_UnitAuras and _G.C_UnitAuras.GetAuraDataBySlot
local GetAuraSlots = _G.C_UnitAuras and _G.C_UnitAuras.GetAuraSlots
local GetAuraDataByAuraInstanceID = _G.C_UnitAuras and _G.C_UnitAuras.GetAuraDataByAuraInstanceID
local BackdropTemplateMixin = _G.BackdropTemplateMixin
local BUFF_MAX_DISPLAY_PLATER = nil -- don't limit.
local DB_AURA_GROW_DIRECTION
local DB_AURA_GROW_DIRECTION2
local DB_AURA_PADDING
local DB_AURA_SEPARATE_BUFFS
local DB_SHOW_PURGE_IN_EXTRA_ICONS
local DB_SHOW_ENRAGE_IN_EXTRA_ICONS
local DB_SHOW_MAGIC_IN_EXTRA_ICONS
local DB_DEBUFF_BANNED
local DB_AURA_SHOW_IMPORTANT
local DB_AURA_SHOW_BYPLAYER
local DB_AURA_SHOW_BYOTHERPLAYERS
local DB_BUFF_BANNED
local DB_AURA_SHOW_DISPELLABLE
local DB_AURA_SHOW_ONLY_SHORT_DISPELLABLE_ON_PLAYERS
local DB_AURA_SHOW_ENRAGE
local DB_AURA_SHOW_MAGIC
local DB_AURA_SHOW_BYUNIT
local DB_AURA_ALPHA
local DB_AURA_ENABLED
local DB_AURA_GHOSTAURA_ENABLED
local DB_TRACK_METHOD
local MEMBER_UNITID = "namePlateUnitToken"
local MEMBER_GUID = "namePlateUnitGUID"
local MEMBER_NPCID = "namePlateNpcId"
local MEMBER_QUEST = "namePlateIsQuestObjective"
local MEMBER_REACTION = "namePlateUnitReaction"
local MEMBER_RANGE = "namePlateInRange"
local MEMBER_NOCOMBAT = "namePlateNoCombat"
local MEMBER_NAME = "namePlateUnitName"
local MEMBER_NAMELOWER = "namePlateUnitNameLower"
local MEMBER_TARGET = "namePlateIsTarget"
local DebuffTypeColor = _G.DebuffTypeColor
--> As accessible translator map (where nil needs to resemble "NONE") for modding/scripting to be published in .AuraType:
local AURA_TYPES = {
[""] = "enrage",
["Magic"] = "magic",
["Poison"] = "poison",
["Curse"] = "curse",
["nil"] = "none",
}
--> Aura types for usage in AddAura / AddExtraIcon checks
local AURA_TYPE_ENRAGE = "" -- yes, 'enrage' is just empty string for Blizzard...
local AURA_TYPE_MAGIC = "Magic"
local AURA_TYPE_DISEASE = "Disease"
local AURA_TYPE_POISON = "Poison"
local AURA_TYPE_CURSE = "Curse"
local AURA_TYPE_UNKNOWN = nil
local PLATER_REFRESH_ID = 1
function Plater.IncreaseRefreshID_Auras()
PLATER_REFRESH_ID = PLATER_REFRESH_ID + 1
end
local SCRIPT_AURA_TRIGGER_CACHE = Plater.ScriptAura
--local SCRIPT_CASTBAR = Plater.ScriptCastBar
--local SCRIPT_UNIT = Plater.ScriptUnit
--caches auras for crowd control, offensives and defensives to determine the border color for special auras, if the aura is in this table, the border will be colored with the respective color
local CROWDCONTROL_AURA_IDS = {}
local OFFENSIVE_AURA_IDS = {}
local DEFENSIVE_AURA_IDS = {}
--list of auras Plater added automatically to special auras, automatic added auras passes throught black list filters while auras manually added by the user do no
local SPECIAL_AURAS_AUTO_ADDED = {}
--list of auras the user added into the track list for special auras, _MINE caches the auras where the user checked the 'Only Mine' checkbox
local SPECIAL_AURAS_USER_LIST = {}
local SPECIAL_AURAS_USER_LIST_MINE = {}
--store aura names to manually track
local MANUAL_TRACKING_BUFFS = {}
local MANUAL_TRACKING_DEBUFFS = {}
local AUTO_TRACKING_EXTRA_BUFFS = {}
local AUTO_TRACKING_EXTRA_DEBUFFS = {}
--Cache for ghost auras
--Updated on function: Plater.UpdateGhostAurasCache()
local GHOSTAURAS = {}
--extra auras namespace ~extraaura
--Plater does not have a list of saved extra auras, these are added and removed on the fly from scripts and mods
--These auras only live for its expirationTime, after that it need another Add() call to add it again
platerInternal.ExtraAuras = {
--only store unitFrames whith an extra aura
unitFramesToGUID = {}
}
local extraAuraGUIDtoUnitFrameCache = platerInternal.ExtraAuras.unitFramesToGUID
--when an extra aura is added to a nameplate, cache the unit GUID to unitFrame to use when the extra aura expired
function platerInternal.ExtraAuras.CacheGUID_to_UnitFrame(GUID, unitFrame)
extraAuraGUIDtoUnitFrameCache[GUID] = unitFrame
end
function platerInternal.ExtraAuras.GetUnitFrameFromGUID(GUID)
return extraAuraGUIDtoUnitFrameCache[GUID]
end
function platerInternal.ExtraAuras.RemoveGUIDFromUnitFrameCache(GUID)
if (extraAuraGUIDtoUnitFrameCache[GUID]) then
extraAuraGUIDtoUnitFrameCache[GUID] = nil
end
end
--not caches, these two tables hold extra icons added from platerInternal.ExtraAuras.Add()
--store {[spellId] = {[GUID] = exraAuraTable}}
local EXTRAAURAS_SPELLIDS = {}
--store {[GUID] = {[spellId] = exraAuraTable}}
local EXTRAAURAS_GUIDS = {}
--@unitGUID: to identify in which nameplate the extra aura is shown
--@spellId: of the aura
--@duration: debuff duration, start time is always the time that ExtraAuras.AddAura is called
--@borderColor: optional border color
function Plater.AddExtraAura(unitGUID, spellId, duration, borderColor, overlayColor, desaturated, showErrors)
--! if the extra aura won't show if the player isn't in combat, why add them when the player is out of combat?
local startTime = GetTime()
local expirationTime = startTime + duration
local spellName, _, spellIcon = GetSpellInfo(spellId)
if (not spellName) then
if (showErrors) then
Plater:Msg("platerInternal.ExtraAuras.Add(): invalid spellId or spell does not exists.")
end
return
end
local extraAuraTable = EXTRAAURAS_SPELLIDS[spellId] and EXTRAAURAS_SPELLIDS[spellId][unitGUID]
if (extraAuraTable) then --refresh
extraAuraTable.startTime = startTime
extraAuraTable.duration = duration
extraAuraTable.expirationTime = expirationTime
extraAuraTable.borderColor = borderColor
extraAuraTable.overlayColor = overlayColor
extraAuraTable.desaturated = desaturated
extraAuraTable.needRefresh = "refresh"
else
extraAuraTable = { --add new
startTime = startTime,
duration = duration,
expirationTime = expirationTime,
borderColor = borderColor,
overlayColor = overlayColor,
desaturated = desaturated,
spellName = spellName,
spellIcon = spellIcon,
--pre-create cache keys to avoid expensive string manipulation at runtime
spellNameExtraCache = spellName .. "_extra",
spellNameAuraCache = spellName .. "_player",
spellNameGhostAuraCache = spellName .. "_player_ghost",
needRefresh = "added",
}
end
--add the extra aura
EXTRAAURAS_SPELLIDS[spellId] = EXTRAAURAS_SPELLIDS[spellId] or {}
EXTRAAURAS_SPELLIDS[spellId][unitGUID] = extraAuraTable
EXTRAAURAS_GUIDS[unitGUID] = EXTRAAURAS_GUIDS[unitGUID] or {}
EXTRAAURAS_GUIDS[unitGUID][spellId] = extraAuraTable
return true
end
--called from Plater.ResetAuraContainer()
function platerInternal.ExtraAuras.WipeCache(unitFrame)
wipe(unitFrame.ExtraAuraCache)
end
--remove an extra aura, this is called when a extra aura expires at platerInternal.ExtraAuras.ClearExpired()
function platerInternal.ExtraAuras.Remove(spellId, unitGUID)
EXTRAAURAS_SPELLIDS[spellId][unitGUID] = nil
EXTRAAURAS_GUIDS[unitGUID][spellId] = nil
--find the unit frame and remove the icon
--! shouldn't this call Plater.ResetAuraContainer (self, resetBuffs, resetDebuffs)?
--! but calling it now might mess with regular and ghost auras already added in the pipe line
local unitFrame = platerInternal.ExtraAuras.GetUnitFrameFromGUID(unitGUID)
--unitFrame might not exist in the cache: got cleared by unit_died, or the player isn't in combat so extra auras aren't added to nameplates
if (unitFrame and unitFrame:IsShown()) then
--need to find the extra aura icon
local buffFrame1 = unitFrame.BuffFrame
for i = 1, #buffFrame1.PlaterBuffList do
local auraIconFrame = buffFrame1.PlaterBuffList[i]
if (auraIconFrame.extraAuraSpellId == spellId and auraIconFrame.spellId == spellId) then
auraIconFrame.extraAuraSpellId = nil
auraIconFrame:Hide()
else
--I guess the aura will get cleaned by regular Plater.ResetAuraContainer()
end
end
end
end
--when adding a new extra icon, attempt to get an aura icon already showing the extra aura
--! dunno if this should be require is the auras get clean up each tick? but not on retail I guess
function platerInternal.ExtraAuras.GetAuraIconByExtraAuraSpellId(unitFrame, spellId)
local buffFrame1 = unitFrame.BuffFrame
for i = 1, #buffFrame1.PlaterBuffList do
local auraIconFrame = buffFrame1.PlaterBuffList[i]
if (auraIconFrame.extraAuraSpellId == spellId and auraIconFrame.spellId == spellId) then
return auraIconFrame
end
end
end
--iterate among all extra auras currently active and remove all expired
--this is called from Tick after Plater.ShowGhostAuras(tickFrame.BuffFrame)
function platerInternal.ExtraAuras.ClearExpired()
local currentTime = GetTime()
for spellId, listOfUnitGUIDs in pairs(EXTRAAURAS_SPELLIDS) do
for unitGUID, extraAuraTable in pairs(listOfUnitGUIDs) do
if (extraAuraTable.expirationTime < currentTime) then
--this extra aura has expired
platerInternal.ExtraAuras.Remove(spellId, unitGUID)
end
end
end
end
local function CreatePlaterNamePlateAuraTooltip()
local tooltip = CreateFrame("GameTooltip", "PlaterNamePlateAuraTooltip", UIParent, "GameTooltipTemplate")
tooltip.ApplyOwnBackdrop = function(self)
self:SetBackdrop ({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Buttons\WHITE8X8]], tileSize = 0, tile = false, tileEdge = true})
self:SetBackdropColor (0.05, 0.05, 0.05, 0.8)
self:SetBackdropBorderColor (0, 0, 0, 1)
end
local function OnUpdate(self, elapsed)
self.updateTooltipTimer = (self.updateTooltipTimer or TOOLTIP_UPDATE_TIME or 0.2) - elapsed;
if self.updateTooltipTimer > 0 then
return;
end
self.updateTooltipTimer = TOOLTIP_UPDATE_TIME or 0.2
local owner = self:GetOwner();
if owner and owner.UpdateTooltip then
owner:UpdateTooltip();
end
end
tooltip:SetScript("OnUpdate", OnUpdate)
if tooltip.SetBackdrop then
return tooltip
end
-- workarounds for 9.1.5
local nineSlice = tooltip.NineSlice or tooltip
Mixin(nineSlice, BackdropTemplateMixin)
nineSlice:SetScript("OnSizeChanged", nineSlice.OnSizeChanged)
nineSlice.backdropInfo = tooltip.backdropInfo
nineSlice.backdropColor = tooltip.backdropColor
nineSlice.backdropColorAlpha = tooltip.backdropColorAlpha
nineSlice.backdropBorderColor = tooltip.backdropBorderColor
nineSlice.backdropBorderColorAlpha = tooltip.backdropBorderColorAlpha
nineSlice.backdropBorderBlendMode = tooltip.backdropBorderBlendMode
nineSlice:OnBackdropLoaded()
tooltip.SetBackdrop = function(self,...)
local nineSlice = self.NineSlice or self
nineSlice:SetBackdrop(...)
end
tooltip.SetBackdropColor = function(self,...)
local nineSlice = self.NineSlice or self
nineSlice:SetBackdropColor(...)
end
tooltip.SetBackdropBorderColor = function(self,...)
local nineSlice = self.NineSlice or self
nineSlice:SetBackdropBorderColor(...)
end
tooltip.ApplyBackdrop = function(self,...)
local nineSlice = self.NineSlice or self
nineSlice:ApplyBackdrop(...)
end
return tooltip
end
local NamePlateTooltip = _G.NamePlateTooltip -- can be removed later
local PlaterNamePlateAuraTooltip = CreatePlaterNamePlateAuraTooltip()
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> Private Aura handling
function Plater.HandlePrivateAuraAnchors(unitFrame, maxIndex)
if true then return end -- disable for now...
if not unitFrame then return end
if not C_UnitAuras or not C_UnitAuras.RemovePrivateAuraAnchor then return end
if unitFrame.privateAuraAnchors then
for index, anchorID in pairs(unitFrame.privateAuraAnchors) do
C_UnitAuras.RemovePrivateAuraAnchor(anchorID)
end
table.wipe(unitFrame.privateAuraAnchors)
else
unitFrame.privateAuraAnchors = {}
end
if not unitFrame.PlaterOnScreen then return end
--anchor building
local anchorSide = Plater.db.profile.aura_frame1_anchor.side
local rowGrowthDirectionUp = (anchorSide < 3 or anchorSide > 5)
local maxIndex = maxIndex or 2
local relIconPoint = "bottom"
local relIconPointTo = "top"
local paddingMult = 1
-- --> left to right
if (DB_AURA_GROW_DIRECTION == 3) then
relIconPoint = rowGrowthDirectionUp and "bottomleft" or "topleft"
relIconPointTo = rowGrowthDirectionUp and "topleft" or "bottomleft"
paddingMult = 1
-- <-- right to left
elseif (DB_AURA_GROW_DIRECTION == 1) then
relIconPoint = rowGrowthDirectionUp and "bottomright" or "topright"
relIconPointTo = rowGrowthDirectionUp and "topright" or "bottomright"
paddingMult = -1
end
local unit = unitFrame.IsSelf and "player" or unitFrame[MEMBER_UNITID]
for index = 1, maxIndex do
local privateAnchorArgs = {
unitToken = unit,
auraIndex = index,
parent = unitFrame,
showCountdownFrame = true,
showCountdownNumbers = true,
iconInfo = {
iconAnchor = {
point = relIconPoint,
relativeTo = unitFrame.BuffFrame,
relativePoint = relIconPointTo,
offsetX = ((Plater.db.profile.aura_width * (index - 1)) + DB_AURA_PADDING * (index -1)) * paddingMult, --((Plater.db.profile.aura_width * Plater.db.profile.ui_parent_scale_tune * (index - 1)) + DB_AURA_PADDING * (index -1)) * paddingMult,
offsetY = Plater.db.profile.aura_breakline_space,
},
iconWidth = Plater.db.profile.aura_width, -- * Plater.db.profile.ui_parent_scale_tune,
iconHeight = Plater.db.profile.aura_height, -- * Plater.db.profile.ui_parent_scale_tune,
},
durationAnchor = {
point = relIconPoint,
relativeTo = unitFrame.BuffFrame,
relativePoint = relIconPointTo,
offsetX = ((Plater.db.profile.aura_width * (index - 1)) + DB_AURA_PADDING * (index -1)) * paddingMult, --((Plater.db.profile.aura_width * Plater.db.profile.ui_parent_scale_tune * (index - 1)) + DB_AURA_PADDING * (index -1)) * paddingMult,
offsetY = Plater.db.profile.aura_breakline_space,
},
}
unitFrame.privateAuraAnchors[index] = C_UnitAuras.AddPrivateAuraAnchor(privateAnchorArgs)
end
--anchored, so we should 'show' the buffframe as anchor point. TODO: how to handle this for name only and other stuff?... might need separate anchor frame
unitFrame.BuffFrame:Show()
unitFrame.BuffFrame:SetSize(1,1)
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> UNIT_AURA event handling
local ValidateAuraForUpdate = function (unit, aura)
local needsUpdate = false
local hasBuff = false
local hasDebuff = false
if DB_AURA_GHOSTAURA_ENABLED and DB_AURA_SEPARATE_BUFFS then
if aura.isHarmful and aura.sourceUnit == "player" and GHOSTAURAS[aura.name] then
-- ensure ghost auras are updated properly
hasBuff = true
needsUpdate = true
end
end
local name, spellId = aura.name, aura.spellId
--DevTool:AddData({blacklist = (DB_BUFF_BANNED[name] or DB_BUFF_BANNED[spellId] or DB_DEBUFF_BANNED[name] or DB_DEBUFF_BANNED[spellId]) or false, spellId = spellId, name = name}, "UnitAura-Check: "..name)
hasBuff = aura.isHelpful or hasBuff
hasDebuff = aura.isHarmful or hasDebuff
local advancedBrokenFilteringTest = false
if advancedBrokenFilteringTest then
if DB_TRACK_METHOD == 0x2 then
--manual tracking
if DB_SHOW_PURGE_IN_EXTRA_ICONS or DB_SHOW_ENRAGE_IN_EXTRA_ICONS or DB_SHOW_MAGIC_IN_EXTRA_ICONS
or (aura.sourceUnit == "player" and ( MANUAL_TRACKING_BUFFS[name] or MANUAL_TRACKING_BUFFS[spellId] ) or MANUAL_TRACKING_DEBUFFS[name] or MANUAL_TRACKING_DEBUFFS[spellId] )
then -- only player buffs in manual tracking
needsUpdate = true
end
else
-- automatic tracking
--TODO: additional checks for track-list etc. possible, depending on the buff settings: if nothing like dispellable or so is ticked, we can verify this here
if not (DB_BUFF_BANNED[name] or DB_BUFF_BANNED[spellId] or DB_DEBUFF_BANNED[name] or DB_DEBUFF_BANNED[spellId]) then
--a not blocked aura is included in the update
needsUpdate = true
end
end
if SPECIAL_AURAS_AUTO_ADDED [name] or SPECIAL_AURAS_AUTO_ADDED [spellId] or SPECIAL_AURAS_USER_LIST[name] or SPECIAL_AURAS_USER_LIST[spellId] or (aura.sourceUnit == "player" and (SPECIAL_AURAS_USER_LIST_MINE[name] or SPECIAL_AURAS_USER_LIST_MINE[spellId])) then
--or DB_SHOW_PURGE_IN_EXTRA_ICONS or DB_SHOW_ENRAGE_IN_EXTRA_ICONS or DB_SHOW_MAGIC_IN_EXTRA_ICONS
--include buff special at all times
needsUpdate = true
end
else
needsUpdate = hasBuff or hasDebuff or false
end
--DevTool:AddData({needsUpdate=needsUpdate, hasBuff=hasBuff, hasDebuff=hasDebuff}, "Plater_UNIT_AURA return")
return needsUpdate, hasBuff, hasDebuff
end
--[[
UNIT_AURA Payload:
- unit -- the unit the aura update is applied to
- isFullUpdate -- if there needs to be a full aura update (potentially empty updatedAuras)
- updatedAuras { -- the table of updated aura information for this unit/event
[n] {
canApplyAura,
debuffType,
isBossAura,
isFromPlayerOrPlayerPet,
isHarmful,
isHelpful,
isNameplateOnly,
isRaid,
name,
nameplateShowAll,
nameplateShowPersonal,
shouldNeverShow,
sourceUnit,
spellId,
}
In 10.0:
- unit
- UnitAuraUpdateInfo = {
addedAuras = AuraInstanceInfo[]?,
updatedAuraInstanceIDs = number[]?
removedAuraInstanceIDs = number[]?
isFullUpdate = boolean?,
}
with AuraInstanceInfo = {
--FULL UnitAura return values plus:
auraInstanceID = number,
-- "Magic" | "Curse" | "Disease" | "Poison"
dispelName = string,
}
]]--
local UnitAuraEventHandlerData = {}
local UnitAuraEventHandlerValidUnits = {} -- units on screen. set via Plater.RemoveFromAuraUpdate and Plater.AddToAuraUpdate from NAME_PLATE_UNIT_REMOVED and NAME_PLATE_UNIT_ADDED events
local UnitAuraCacheData = {} --new unit aura event info data cache
local UpdateUnitAuraCacheData
local UnitAuraEventHandlerFrame = CreateFrame ("frame") --private
local UnitAuraEventHandler = function (_, event, arg1, arg2, arg3, ...)
Plater.StartLogPerformanceCore("Plater-Core", "Events", event)
--DevTool:AddData({event = event, arg1 = arg1, arg2 = arg2}, "Plater_UnitAuraEventHandler - " .. (event or "N/A"))
if event == "UNIT_AURA" then
local unit, updatedAuras = arg1, arg2
--DevTool:AddData({unit = unit, updatedAuras = updatedAuras, valid = UnitAuraEventHandlerValidUnits[unit]}, "Plater_UNIT_AURA - " .. unit)
if unit and UnitAuraEventHandlerValidUnits[unit] then
UpdateUnitAuraCacheData(unit, updatedAuras)
end
end
Plater.EndLogPerformanceCore("Plater-Core", "Events", event)
end
UnitAuraEventHandlerFrame:SetScript ("OnEvent", UnitAuraEventHandler)
UnitAuraEventHandlerFrame:RegisterEvent ("UNIT_AURA")
function Plater.RemoveFromAuraUpdate (unit)
if not unit then return end
UnitAuraEventHandlerValidUnits[unit] = nil
UnitAuraCacheData[unit] = nil
UnitAuraEventHandlerData[unit] = nil
end
function Plater.AddToAuraUpdate (unit)
if not unit then return end
UnitAuraEventHandlerValidUnits[unit] = true
UnitAuraEventHandlerData[unit] = { hasBuff = true, hasDebuff = true } --update at least once
UpdateUnitAuraCacheData(unit, nil)
end
UpdateUnitAuraCacheData = function (unit, updatedAuras)
--DevTool:AddData({unit = unit, updatedAuras = updatedAuras}, "Plater_UpdateUnitAuraCacheData - " .. (unit or "N/A"))
local unitCacheData = UnitAuraCacheData[unit]
if not unitCacheData then
unitCacheData = {}
unitCacheData.buffs = {}
unitCacheData.debuffs = {}
UnitAuraCacheData[unit] = unitCacheData
end
if updatedAuras == nil or updatedAuras.isFullUpdate then
UnitAuraCacheData[unit] = {}
UnitAuraCacheData[unit].buffs = {}
UnitAuraCacheData[unit].debuffs = {}
UnitAuraCacheData[unit].buffsChanged = true
UnitAuraCacheData[unit].debuffsChanged = true
UnitAuraCacheData[unit].isFullUpdateHelp = (updatedAuras == nil and true) or updatedAuras.isFullUpdate or false
UnitAuraCacheData[unit].isFullUpdateHarm = (updatedAuras == nil and true) or updatedAuras.isFullUpdate or false
UnitAuraEventHandlerData[unit] = { hasBuff = true, hasDebuff = true }
return
end
unitCacheData.tbd = {}
for _, aura in ipairs(updatedAuras.addedAuras or {}) do
local needsUpdate = ValidateAuraForUpdate(unit, aura)
if needsUpdate then
if aura.isHarmful then
unitCacheData.debuffs[aura.auraInstanceID] = aura
unitCacheData.debuffsChanged = true
elseif aura.isHelpful then
unitCacheData.buffs[aura.auraInstanceID] = aura
unitCacheData.buffsChanged = true
end
end
end
for _, auraInstanceID in ipairs(updatedAuras.updatedAuraInstanceIDs or {}) do
if unitCacheData.debuffs[auraInstanceID] ~= nil then
unitCacheData.debuffs[auraInstanceID].requriresUpdate = true
unitCacheData.debuffsChanged = true
elseif unitCacheData.buffs[auraInstanceID] ~= nil then
unitCacheData.buffs[auraInstanceID].requriresUpdate = true
unitCacheData.buffsChanged = true
else
unitCacheData.tbd[auraInstanceID] = true
unitCacheData.tbdChanged = true
end
end
for _, auraInstanceID in ipairs(updatedAuras.removedAuraInstanceIDs or {}) do
if unitCacheData.debuffs[auraInstanceID] ~= nil then
unitCacheData.debuffs[auraInstanceID] = nil
unitCacheData.debuffsChanged = true
elseif unitCacheData.buffs[auraInstanceID] ~= nil then
unitCacheData.buffs[auraInstanceID] = nil
unitCacheData.buffsChanged = true
else
unitCacheData.tbd[auraInstanceID] = true
unitCacheData.tbdChanged = true
end
end
if unitCacheData.tbdChanged then
for index, _ in pairs(unitCacheData.tbd) do
local aura = C_UnitAuras.GetAuraDataByAuraInstanceID(unit, index)
if aura then
if aura.isHarmful then
unitCacheData.debuffs[aura.auraInstanceID] = aura
unitCacheData.debuffsChanged = true
elseif aura.isHelpful then
unitCacheData.buffs[aura.auraInstanceID] = aura
unitCacheData.buffsChanged = true
end
end
end
end
unitCacheData.tbd = nil
unitCacheData.tbdChanged = nil
local existingData = UnitAuraEventHandlerData[unit] or { hasBuff = false, hasDebuff = false }
local hasDebuff = existingData.hasDebuff or unitCacheData.debuffsChanged or not DB_AURA_SEPARATE_BUFFS or false
local hasBuff = existingData.hasBuff or unitCacheData.buffsChanged or not DB_AURA_SEPARATE_BUFFS or false
UnitAuraEventHandlerData[unit] = { hasBuff = hasBuff, hasDebuff = hasDebuff }
end
local function getUnitAuras(unit, filter)
if not unit then return end
local isHarmful = string.find(filter or "HARMFUL", "HARMFUL") and true or false
local isHelpful = string.find(filter or "HELPFUL", "HELPFUL") and true or false
local unitCacheData = UnitAuraCacheData[unit]
--DevTool:AddData({unitCacheData, filter = filter, isFullUpdateHarm = unitCacheData.isFullUpdateHarm, isFullUpdateHelp = unitCacheData.isFullUpdateHelp, update = ((isHarmful and not unitCacheData.isFullUpdateHarm) or (isHelpful and not unitCacheData.isFullUpdateHelp))}, "getUnitAuras - " .. unit)
if unitCacheData and ((isHarmful and not unitCacheData.isFullUpdateHarm) or (isHelpful and not unitCacheData.isFullUpdateHelp)) then --new aura event
Plater.StartLogPerformanceCore("Plater-Core", "Update", "UpdateAuras - getUnitAuras - short")
-- debuffs
if unitCacheData.debuffsChanged then
local tmpDebuffs = {}
for auraInstanceID, aura in pairs (unitCacheData.debuffs) do
if aura.requriresUpdate then
tmpDebuffs[auraInstanceID] = GetAuraDataByAuraInstanceID(unit, auraInstanceID)
else
tmpDebuffs[auraInstanceID] = aura
end
end
unitCacheData.debuffs = tmpDebuffs
unitCacheData.debuffsChanged = false
end
-- buffs
if unitCacheData.buffsChanged then
local tmpBuffs = {}
for auraInstanceID, aura in pairs (unitCacheData.buffs) do
if aura.requriresUpdate then
tmpBuffs[auraInstanceID] = GetAuraDataByAuraInstanceID(unit, auraInstanceID)
else
tmpBuffs[auraInstanceID] = aura
end
end
unitCacheData.buffs = tmpBuffs
unitCacheData.buffsChanged = false
end
Plater.EndLogPerformanceCore("Plater-Core", "Update", "UpdateAuras - getUnitAuras - short")
return unitCacheData
end
if not filter then return end --old code requires this.
unitCacheData = unitCacheData or {debuffs = {}, buffs = {}}
UnitAuraCacheData[unit] = unitCacheData
-- full updates and old way here
local filterCache = (isHarmful and unitCacheData.debuffs) or (isHelpful and unitCacheData.buffs) or nil
if not filterCache then return end
Plater.StartLogPerformanceCore("Plater-Core", "Update", "UpdateAuras - getUnitAuras - long")
local continuationToken
local debuffIndex = 0
repeat -- until continuationToken == nil
local slots = { GetAuraSlots(unit, filter, BUFF_MAX_DISPLAY_PLATER, continuationToken) }
continuationToken = slots[1]
local numSlots = #slots
for i=2, numSlots do
local slot = slots[i]
local aura = GetAuraDataBySlot(unit, slot)
if aura then
--DevTool:AddData({unit = unit, aura = aura, slot = slot}, "GetAuraDataBySlot")
filterCache[aura.auraInstanceID] = aura
end
end
until continuationToken == nil
Plater.EndLogPerformanceCore("Plater-Core", "Update", "UpdateAuras - getUnitAuras - long")
-- done the update?
if unitCacheData.isFullUpdateHelp and isHelpful then unitCacheData.isFullUpdateHelp = false end
if unitCacheData.isFullUpdateHarm and isHarmful then unitCacheData.isFullUpdateHarm = false end
return unitCacheData
end
--[[
returns the following structure from cached / quick-updated values:
auras = {
[<aura-ID>] = {
applications,
auraInstanceID,
canApplyAura,
dispelName,
duration,
expirationTime,
icon,
isBossAura,
isFromPlayerOrPlayerPet,
isHarmful,
isHelpful,
isNameplateOnly,
isRaid,
isStealable,
name,
nameplateShowAll,
nameplateShowPersonal,
points,
sourceUnit,
spellId,
timeMod,
},
}
--]]
function Plater.GetUnitAurasForUnitID(unitID)
if not unitID or not UnitAuraCacheData[unitID] then
return nil
end
local allAuras = {}
local aurasHelp = getUnitAuras(unitID, "HELPFUL") or {buffs = {}}
local aurasHarm = getUnitAuras(unitID, "HARMFUL") or {debuffs = {}}
DF.table.copy(allAuras, aurasHelp.buffs)
DF.table.copy(allAuras, aurasHarm.debuffs)
return allAuras
end
function Plater.GetUnitAuras(unitFrame)
return Plater.GetUnitAurasForUnitID(unitFrame.namePlateUnitToken)
end
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> aura buffs and debuffs ~aura ~buffs ~debuffs ~auras
--> show the tooltip in the aura icon
function Plater.OnEnterAura (iconFrame) --private
PlaterNamePlateAuraTooltip:SetOwner (iconFrame, "ANCHOR_LEFT")
if PlaterNamePlateAuraTooltip.SetUnitBuffByAuraInstanceID then
if(iconFrame.spellId and not iconFrame.auraInstanceID) then
PlaterNamePlateAuraTooltip:SetSpellByID(iconFrame.spellId)
elseif(iconFrame.auraInstanceID) then
local setFunction = iconFrame.isBuff and NamePlateTooltip.SetUnitBuffByAuraInstanceID or NamePlateTooltip.SetUnitDebuffByAuraInstanceID
setFunction(PlaterNamePlateAuraTooltip, iconFrame:GetParent().unit, iconFrame:GetID(), iconFrame.filter)
end
else
PlaterNamePlateAuraTooltip:SetSpellByID(iconFrame.spellId)
--PlaterNamePlateAuraTooltip:SetUnitAura (iconFrame:GetParent().unit, iconFrame:GetID(), iconFrame.filter)
end
PlaterNamePlateAuraTooltip:ApplyOwnBackdrop()
iconFrame.UpdateTooltip = Plater.OnEnterAura
end
function Plater.OnLeaveAura (iconFrame) --private
PlaterNamePlateAuraTooltip:Hide()
if NamePlateTooltip:IsForbidden() then return end
NamePlateTooltip:Hide() -- backwards compatibility for mods (should be removed later)
end
--called from the options panel, request a refresh on all auras shown
function Plater.RefreshAuras() --private
for _, plateFrame in ipairs (Plater.GetAllShownPlates()) do
if plateFrame.unitFrame.PlaterOnScreen then -- only for visible
UnitAuraEventHandlerData[plateFrame.unitFrame.unit] = { hasBuff = true, hasDebuff = true } -- ensure aura update
Plater.NameplateTick (plateFrame.OnTickFrame, 1)
end
end
if Plater.Masque then
Plater.Masque.AuraFrame1:ReSkin()
Plater.Masque.AuraFrame2:ReSkin()
Plater.Masque.BuffSpecial:ReSkin()
Plater.Masque.BossModIconFrame:ReSkin()
end
end
--stack auras with the same name and change the stack text above the icon to indicate how many auras with the same name the unit has
--self is BuffFrame
function Plater.ConsolidateAuraIcons (self)
--get the table where all icon frames are stored in
local iconFrameContainer = self.PlaterBuffList
--get the amount of auras shown in the frame, this variable should be always reliable
local amountFramesShown = self.amountAurasShown
--store icon frames with the same name
local aurasDuplicated = {}
for i = 1, amountFramesShown do
local iconFrame = iconFrameContainer [i]
local icon = iconFrame.texture
local spellName = iconFrame.SpellName
local index = spellName .. (icon or spellName or math.random(1, 1000000))
if (aurasDuplicated [index]) then
tinsert (aurasDuplicated [index], {iconFrame, iconFrame.RemainingTime})
else
aurasDuplicated [index] = {
{iconFrame, iconFrame.RemainingTime}
}
end
end
for index, iconFramesTable in pairs (aurasDuplicated) do
--how many auras with the same name the unit has
local amountOfSimilarAuras = #iconFramesTable
if (amountOfSimilarAuras > 1) then
--sort order: the aura with the least time left is shown by default
if (Plater.db.profile.aura_consolidate_timeleft_lower) then
table.sort (iconFramesTable, DF.SortOrder2R)
else
table.sort (iconFramesTable, DF.SortOrder2)
end
local totalStacks = 0
--hide all auras except for the first occurrence of this aura
for i = 1, amountOfSimilarAuras do
local iconFrame = iconFramesTable [i][1]
if i == 1 then --ensure the sorted icon is always shown
iconFrame:Show()
iconFrame.InUse = true
else --hide other icons
iconFrame.ShowAnimation:Stop()
iconFrame:Hide()
iconFrame.InUse = false
end
totalStacks = totalStacks + (iconFrame.Stacks > 0 and iconFrame.Stacks or 1)
--decrease the amount of auras shown on the buff frame
self.amountAurasShown = self.amountAurasShown - 1
end
--set the stack amount number to indicate how many auras similar to this the unit has
local stackLabel = iconFramesTable [1][1].StackText
stackLabel:SetText (totalStacks)
stackLabel:Show()
end
end
end
--sort aura icons according to this function. default is time remaining hight to low (l->r) with 0-duration on the left
function Plater.AuraIconsSortFunction (aura1, aura2)
return (aura1.Duration == 0 and 99999999 or aura1.RemainingTime or 0) < (aura2.Duration == 0 and 99999999 or aura2.RemainingTime or 0)
--return (aura1.Duration == 0 and 99999999 or aura1.RemainingTime or 0) > (aura2.Duration == 0 and 99999999 or aura2.RemainingTime or 0)
end
--update the ghost auras
--this function is guaranteed to run after all auras been processed
function Plater.ShowGhostAuras(buffFrame)
if (DB_AURA_GHOSTAURA_ENABLED) then
local unitFrame = buffFrame.unitFrame
if ((unitFrame.namePlateUnitReaction < 5) and unitFrame.InCombat and not unitFrame.IsSelf and not unitFrame.isPerformanceUnit and InCombatLockdown()) then
local nameplateAuraCache = unitFrame.AuraCache --active auras currently shown in the nameplate
local nameplateGhostAuraCache = unitFrame.GhostAuraCache --active ghost auras currently shown in the nameplate
for spellName, spellTable in pairs(GHOSTAURAS) do
if (not nameplateAuraCache[spellName.."_player"]) then
if (not nameplateGhostAuraCache[spellName.."_player_ghost"]) then --the ghost aura isn't in the nameplate
--add the extra icon
local spellIcon, spellId = spellTable[1], spellTable[2]
local auraIconFrame = Plater.GetAuraIcon(buffFrame) -- show on debuff frame
auraIconFrame.InUse = true --don't play animation
Plater.AddAura(buffFrame, auraIconFrame, -1, spellName.."_player_ghost", spellIcon, 1, "DEBUFF", 0, 0, "player", false, false, -spellId, false, false, false, false, "DEBUFF", 1)
auraIconFrame:EnableMouse (false) --don't use tooltips, as there is no real aura
if auraIconFrame.EnableMouseMotion then
auraIconFrame:EnableMouseMotion (false) --don't use tooltips, as there is no real aura
end
auraIconFrame.IsGhostAura = true
Plater.Auras.GhostAuras.ApplyAppearance(auraIconFrame, spellName.."_player_ghost", spellIcon, -spellId)
nameplateGhostAuraCache[spellName.."_player_ghost"] = true --this is shown as ghost aura
end
else
nameplateGhostAuraCache[spellName.."_player_ghost"] = false --this is shown as regular aura
end
end
end
end
end
--extra auras
--these are auras which some script or weakaura requested to be shown in the nameplate of an unit
--plater does not have control, does not save or has a list of extra auras, they are all external
--plater just add an icon for the spellId and show it until its duration expires
--this function is called on tick after Plater.ShowGhostAuras(tickFrame.BuffFrame) and platerInternal.ExtraAuras.ClearExpired()
function platerInternal.ExtraAuras.Show(buffFrame)
local unitFrame = buffFrame.unitFrame
--unitFrame.IsNeutralOrHostile count npcs and players
if (unitFrame.IsNeutralOrHostile and not unitFrame.IsSelf) then --and unitFrame.InCombat --removed for debug on training dummies
if (InCombatLockdown()) then
local unitGUID = unitFrame[MEMBER_GUID]
local unitExtraAurasSpellIds = EXTRAAURAS_GUIDS[unitGUID]
--does the mob shown in the nameplate has an extra aura added to it?
if (unitExtraAurasSpellIds) then
--active extra auras currently shown in the nameplate
local nameplateExtraAurasCache = unitFrame.ExtraAuraCache
--active auras currently shown in the nameplate
local nameplateAuraCache = unitFrame.AuraCache
--active ghost auras currently shown in the nameplate
local nameplateGhostAuraCache = unitFrame.GhostAuraCache
for spellId, extraAuraTable in pairs(unitExtraAurasSpellIds) do
--does this extra aura just added or got a time refresh?
local needRefresh = extraAuraTable.needRefresh
if (needRefresh or not nameplateExtraAurasCache[extraAuraTable.spellNameExtraCache]) then
if (needRefresh or not nameplateAuraCache[extraAuraTable.spellNameAuraCache]) then
if (needRefresh or not nameplateGhostAuraCache[extraAuraTable.spellNameGhostAuraCache]) then
local spellName = extraAuraTable.spellName --not in use
local spellIcon = extraAuraTable.spellIcon
local duration = extraAuraTable.duration
local startTime = extraAuraTable.startTime --not in use
local expirationTime = extraAuraTable.expirationTime
local borderColor = extraAuraTable.borderColor
local overlayColor = extraAuraTable.overlayColor
local idDesaturated = extraAuraTable.desaturated
--check if can use the same icon, this might not be required in the future
local auraIconFrame = platerInternal.ExtraAuras.GetAuraIconByExtraAuraSpellId(unitFrame, spellId)
if (not auraIconFrame) then
auraIconFrame = Plater.GetAuraIcon(buffFrame)
end
auraIconFrame.InUse = true
Plater.AddAura (buffFrame, auraIconFrame, -1, extraAuraTable.spellNameExtraCache, spellIcon, 1, "DEBUFF", duration, expirationTime, "player", false, false, spellId, false, false, true, false, "DEBUFF", 1)
--Plater.AddAura(buffFrame, auraIconFrame, -1, spellName.."_player_ghost", spellIcon, 1, "DEBUFF", 0, 0, "player", false, false, spellId, false, false, false, false, "DEBUFF", 1)
auraIconFrame.extraAuraSpellId = spellId
--set the icon and border settings
auraIconFrame.Icon:SetDesaturated(idDesaturated)
auraIconFrame.Icon:SetVertexColor(DetailsFramework:ParseColors(overlayColor))
auraIconFrame:SetBackdropBorderColor(DetailsFramework:ParseColors(borderColor))
--add the aura to 'unitFrame.ExtraAuraCache', spellNameExtraCache = spellName .. "_extra"
nameplateExtraAurasCache[extraAuraTable.spellNameExtraCache] = true
--add to extra auras cache to know which unitFrame has the extra aura by the mob guid
platerInternal.ExtraAuras.CacheGUID_to_UnitFrame(unitGUID, unitFrame)
--tag this extra aura as clean
extraAuraTable.needRefresh = false
end
end
end
end
end
end
end
end
--align the aura frame icons currently shown in buff container
--this function is called after Plater complete the aura update loop
--at this point, icons shown are reliable icons that has auras that are shown above the nameplate
--hidden icons aren't in use and should be ignored
--self is the buff container
--~align
function Plater.AlignAuraFrames (self)
if (self.isNameplate) then
local profile = Plater.db.profile
local horizontalLength = 1
local curRowLength = 0
local verticalHeight = 1
local firstIcon
if (profile.aura_consolidate) then
Plater.ConsolidateAuraIcons (self)
end
--get the table where all icon frames are stored in
local iconFrameContainer = self.PlaterBuffList
--get the amount of auras shown in the frame; iterate over all if not sorting
local amountFramesShown = #iconFrameContainer