forked from Tercioo/Plater-Nameplates
/
Plater_Auras.lua
2708 lines (2259 loc) · 111 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
--preparation for 10.0
local IS_NEW_UNIT_AURA_AVAILABLE = C_UnitAuras and C_UnitAuras.GetAuraDataBySlot and true
--stop yellow lines on my editor
local tinsert = _G.tinsert
local min = _G.min
local max = _G.max
local abs = _G.abs
local CreateFrame = _G.CreateFrame
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 = _G.GetSpellInfo
local floor = _G.floor
local UnitAuraSlots = _G.UnitAuraSlots
local UnitAuraBySlot = _G.UnitAuraBySlot
local UnitAura = _G.UnitAura
local GetAuraDataBySlot = _G.C_UnitAuras and _G.C_UnitAuras.GetAuraDataBySlot
local BackdropTemplateMixin = _G.BackdropTemplateMixin
local BUFF_MAX_DISPLAY = _G.BUFF_MAX_DISPLAY
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 MEMBER_UNITID = "namePlateUnitToken"
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
-- support for LibClassicDurations from https://github.com/rgd87/LibClassicDurations by d87
local LCD = LibStub:GetLibrary("LibClassicDurations", true)
if IS_WOW_PROJECT_CLASSIC_ERA and LCD then
LCD:Register(Plater)
LCD.RegisterCallback(Plater, "UNIT_BUFF", function(event, unit)end)
UnitAura = LCD.UnitAuraWithBuffs
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()
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> 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
--ViragDevTool_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
--ViragDevTool_AddData({needsUpdate=needsUpdate, hasBuff=hasBuff, hasDebuff=hasDebuff}, "Plater_UNIT_AURA return")
return needsUpdate, hasBuff, hasDebuff
end
local UnitAuraEventHandlerValidation = function (unit, isFullUpdate, updatedAuras)
--ViragDevTool_AddData({unit = unit, isFullUpdate = isFullUpdate, updatedAuras = updatedAuras}, "Plater_UNIT_AURA")
if isFullUpdate ~= false or not updatedAuras then
return true, true, true --update all
end
local needsUpdate = false
local hasBuff = false
local hasDebuff = false
for _, aura in pairs(updatedAuras) do
local nU, hB, hD = ValidateAuraForUpdate(unit, aura)
needsUpdate = needsUpdate or nU
hasBuff = hasBuff or hB
hasDebuff = hasDebuff or hD
end
--resets buffs and debuffs if not using aura frame 2 (for now, until partial clear is implemented)
hasBuff = not DB_AURA_SEPARATE_BUFFS and hasDebuff or hasBuff
hasDebuff = not DB_AURA_SEPARATE_BUFFS and hasBuff or hasDebuff
--ViragDevTool_AddData({unit = unit, 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)
if event == "UNIT_AURA" then
if IS_NEW_UNIT_AURA_AVAILABLE then --for 10.0
local unit, updatedAuras = arg1, arg2
--ViragDevTool_AddData({unit = unit, updatedAuras = updatedAuras}, "Plater_UNIT_AURA")
if unit and UnitAuraEventHandlerValidUnits[unit] then
UpdateUnitAuraCacheData(unit, updatedAuras)
end
else --old code
local unit, isFullUpdate, updatedAuras = arg1, arg2, arg3
if unit and UnitAuraEventHandlerValidUnits[unit] then
local needsUpdate, hasBuff, hasDebuff = UnitAuraEventHandlerValidation(unit, isFullUpdate, updatedAuras)
--ViragDevTool_AddData({unit = unit, isFullUpdate = isFullUpdate, updatedAuras = updatedAuras, needsUpdate = needsUpdate, hasBuff = hasBuff, hasDebuff = hasDebuff, existing=CopyTable(UnitAuraEventHandlerData[unit] or {})}, "Plater_UNIT_AURA_AFTER")
if needsUpdate then
local existingData = UnitAuraEventHandlerData[unit] or { hasBuff = false, hasDebuff = false }
UnitAuraEventHandlerData[unit] = { hasBuff = existingData.hasBuff or hasBuff, hasDebuff = existingData.hasDebuff or hasDebuff }
end
end
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
end
function Plater.AddToAuraUpdate (unit)
if not unit then return end
UnitAuraEventHandlerValidUnits[unit] = true
UnitAuraEventHandlerData[unit] = { hasBuff = true, hasDebuff = true } --update at least once
if IS_NEW_UNIT_AURA_AVAILABLE then
UpdateUnitAuraCacheData(unit, nil)
end
end
UpdateUnitAuraCacheData = function (unit, updatedAuras)
local unitCacheData = UnitAuraCacheData[unit]
if not unitCacheData then
unitCacheData = {}
unitCacheData.buffs = {}
unitCacheData.debuffs = {}
UnitAuraCacheData[unit] = unitCacheData
end
if updatedAuras == nil or updatedAuras.isFullUpdate or unitCacheData.isFullUpdate then
UnitAuraCacheData[unit] = {}
UnitAuraCacheData[unit].buffs = {}
UnitAuraCacheData[unit].debuffs = {}
UnitAuraCacheData[unit].isFullUpdate = true
UnitAuraEventHandlerData[unit] = { hasBuff = true, hasDebuff = true }
return
end
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
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
end
end
local existingData = UnitAuraEventHandlerData[unit] or { hasBuff = false, hasDebuff = false }
UnitAuraEventHandlerData[unit] = { hasBuff = existingData.hasBuff or unitCacheData.buffsChanged, hasDebuff = existingData.hasDebuff or unitCacheData.debuffsChanged }
end
local function getUnitAuras(unit, filter)
if not unit then return end
local unitCacheData = UnitAuraCacheData[unit]
if unitCacheData and not unitCacheData.isFullUpdate then --new aura event
-- debuffs
if unitCacheData.debuffsChanged then
local tmpDebuffs = {}
for auraInstanceID, aura in pairs (unitCacheData.debuffs) do
if aura.requriresUpdate then
tmpDebuffs[auraInstanceID] = C_UnitAuras.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] = C_UnitAuras.GetAuraDataByAuraInstanceID(unit, auraInstanceID)
else
tmpBuffs[auraInstanceID] = aura
end
end
unitCacheData.buffs = tmpBuffs
unitCacheData.buffsChanged = false
end
return unitCacheData
end
if not filter then return end --old code requires this.
unitCacheData = unitCacheData or {debuffs = {}, buffs = {}}
-- full updates and old way here
local isHarmful = string.find(filter, "HARMFUL") and true or false
local isHelpful = string.find(filter, "HELPFUL") and true or false
local filterCache = (isHarmful and unitCacheData.debuffs) or (isHelpful and unitCacheData.buffs) or nil
if not filterCache then return end
local continuationToken
local debuffIndex = 0
repeat -- until continuationToken == nil
local numSlots = 0
local slots
if IS_WOW_PROJECT_MAINLINE then
slots = { UnitAuraSlots(unit, filter, BUFF_MAX_DISPLAY, continuationToken) }
continuationToken = slots[1]
numSlots = #slots
else
numSlots = BUFF_MAX_DISPLAY + 1
end
for i=2, numSlots do
if IS_NEW_UNIT_AURA_AVAILABLE then
local slot = slots[i]
local aura = C_UnitAuras.GetAuraDataBySlot(unit, slot)
if aura then
filterCache[aura.auraInstanceID] = aura
end
else
local name, icon, applications, dispelName, duration, expirationTime, sourceUnit, isStealable, nameplateShowPersonal, spellId, canApplyAura, isBossAura, isFromPlayerOrPlayerPet, nameplateShowAll, timeMod, applications
if IS_WOW_PROJECT_MAINLINE then
local slot = slots[i]
name, icon, applications, dispelName, duration, expirationTime, sourceUnit, isStealable, nameplateShowPersonal, spellId, canApplyAura, isBossAura, isFromPlayerOrPlayerPet, nameplateShowAll, timeMod, applications = UnitAuraBySlot(unit, slot)
else
name, icon, applications, dispelName, duration, expirationTime, sourceUnit, isStealable, nameplateShowPersonal, spellId, canApplyAura, isBossAura, isFromPlayerOrPlayerPet, nameplateShowAll, timeMod, applications = UnitAura(unit, i-1, filter)
if not name then
break
end
end
debuffIndex = debuffIndex + 1
filterCache[debuffIndex] = {
applications = applications,
auraInstanceID = debuffIndex,
canApplyAura = canApplyAura,
dispelName = dispelName,
duration = duration,
expirationTime = expirationTime,
icon = icon,
isBossAura = isBossAura,
isFromPlayerOrPlayerPet = isFromPlayerOrPlayerPet,
isHarmful = isHarmful,
isHelpful = isHelpful,
isNameplateOnly = false,
isRaid = false,
isStealable = isStealable,
name = name,
nameplateShowAll = nameplateShowAll,
nameplateShowPersonal = nameplateShowPersonal,
points = {},
sourceUnit = sourceUnit,
spellId = spellId,
timeMod = timeMod,
}
end
end
until continuationToken == nil
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 IS_NEW_UNIT_AURA_AVAILABLE or 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: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
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
local totalStacks = iconFramesTable [1][1].Stacks > 0 and iconFramesTable [1][1].Stacks or 1
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
--hide all auras except for the first occurrence of this aura
for i = 2, amountOfSimilarAuras do
local iconFrame = iconFramesTable [i][1]
iconFrame.ShowAnimation:Stop()
iconFrame:Hide()
iconFrame.InUse = false
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
if ((buffFrame.unitFrame.namePlateUnitReaction < 5) and buffFrame.unitFrame.InCombat and not buffFrame.unitFrame.IsSelf and InCombatLockdown()) then
local nameplateAuraCache = buffFrame.unitFrame.AuraCache --active auras currently shown in the nameplate
local nameplateGhostAuraCache = buffFrame.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
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
if (profile.aura_sort) then
local iconFrameContainerCopy = {}
local index = 0
for _, icon in pairs(iconFrameContainer) do
if icon:IsShown() then
index = index + 1
iconFrameContainerCopy[index] = icon
end
end
iconFrameContainer = iconFrameContainerCopy
table.sort (iconFrameContainer, Plater.AuraIconsSortFunction)
--when sorted, this is reliable
amountFramesShown = index
end
local growDirection
local anchorSide
local auras_per_row
--> get the grow direction for the buff frame
if (self.Name == "Main") then
growDirection = DB_AURA_GROW_DIRECTION
anchorSide = profile.aura_frame1_anchor.side
auras_per_row = profile.auras_per_row_amount
elseif (self.Name == "Secondary") then
growDirection = DB_AURA_GROW_DIRECTION2
anchorSide = profile.aura_frame2_anchor.side
auras_per_row = profile.auras_per_row_amount2
else
return
end
if (growDirection ~= 2) then --it's growing to left or right
--debug where the buffFrame anchors are
--self:SetSize (5, 5)
--self:SetBackdrop ({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1})
--self:SetBackdropBorderColor (1, 0, 0, 1)
local aurasPerRow = (not profile.auras_per_row_auto and floor(auras_per_row) or Plater.MaxAurasPerRow)
local curAurasRowCount = aurasPerRow + 1
local rowGrowthDirectionUp = (anchorSide < 3 or anchorSide > 5)
local lineBreakMult = rowGrowthDirectionUp and 1 or -1