-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathInit.lua
903 lines (830 loc) · 29.3 KB
/
Init.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
local AddOnName, Namespace = ...
---@class Private
local Private = Namespace
local LibStub = LibStub
local AceAddon = LibStub("AceAddon-3.0")
local LSM = LibStub("LibSharedMedia-3.0")
local CallbackHandler = LibStub("CallbackHandler-1.0")
local concat = table.concat
local CreateFrame = CreateFrame
local getmetatable, setmetatable = getmetatable, setmetatable
local pairs = pairs
local random = math.random
local type = type
Private.L = LibStub("AceLocale-3.0"):GetLocale(AddOnName)
---@alias CombatLogEventType
---| "SCC" SPELL_CAST_SUCCESS
---| "SCS" SPELL_CAST_START
---| "SAA" SPELL_AURA_APPLIED
---| "SAR" SPELL_AURA_REMOVED
---@alias AssignmentType
---| "CombatLogEventAssignment"
---| "TimedAssignment"
---| "PhasedAssignment"
---@alias AssigneeType
---| "Everyone"
---| "Role"
---| "GroupNumber"
---| "Tanks"
---| "Class"
---| "Individual"
---| "Spec"
---| "Type"
---@alias AssignmentSortType
---| "Alphabetical"
---| "First Appearance"
---| "Role > Alphabetical"
---| "Role > First Appearance"
---@alias SpellIDIndex
---| integer
local byteToBase64 = {
[0] = "a",
"b",
"c",
"d",
"e",
"f",
"g",
"h",
"i",
"j",
"k",
"l",
"m",
"n",
"o",
"p",
"q",
"r",
"s",
"t",
"u",
"v",
"w",
"x",
"y",
"z",
"A",
"B",
"C",
"D",
"E",
"F",
"G",
"H",
"I",
"J",
"K",
"L",
"M",
"N",
"O",
"P",
"Q",
"R",
"S",
"T",
"U",
"V",
"W",
"X",
"Y",
"Z",
"0",
"1",
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"(",
")",
}
local function GenerateUniqueID()
local s = {}
for i = 1, 16 do
s[i] = byteToBase64[random(0, 63)]
end
return concat(s)
end
Private.GenerateUniqueID = GenerateUniqueID
local assignmentIDCounter = 0
Private.classes = {}
-- Abstract base class for assignments.
---@class Assignment
---@field uniqueID integer Incremented each time a new assignment is created used to distinguish in-memory assignments.
---@field assignee string Who to assign the assignment to, AssigneeType.
---@field text string Text to display for the assignment. If empty, the spell name is used.
---@field spellInfo SpellInfo The spell info for the assignment.
---@field targetName string The target's name if the assignment has a '@'.
---@field cooldownDuration? number
Private.classes.Assignment = {
uniqueID = 0,
assignee = "",
text = "",
spellInfo = { name = "", iconID = 0, originalIconID = 0, castTime = 0, minRange = 0, maxRange = 0, spellID = 0 },
targetName = "",
}
-- An assignment based on a combat log event.
---@class CombatLogEventAssignment : Assignment
---@field combatLogEventType CombatLogEventType The type of combat log even the assignment is triggered by.
---@field combatLogEventSpellID integer The spell for the event.
---@field spellCount integer The number of times the combat log event must occur before the assignment is triggered.
---@field time number The time from the combat log event to trigger the assignment.
---@field phase integer The boss phase that the combatLogEventSpellID and spellCount are located in.
---@field bossPhaseOrderIndex integer The index into the ordered boss phase table.
Private.classes.CombatLogEventAssignment = setmetatable({
combatLogEventType = "SCS",
combatLogEventSpellID = 0,
spellCount = 1,
time = 0.0,
phase = 0,
bossPhaseOrderIndex = 0,
}, { __index = Private.classes.Assignment })
Private.classes.CombatLogEventAssignment.__index = Private.classes.CombatLogEventAssignment
-- An assignment based on time from the boss being pulled.
---@class TimedAssignment : Assignment
---@field time number The length of time from the beginning of the fight to when this assignment is triggered.
Private.classes.TimedAssignment = setmetatable({
time = 0.0,
}, { __index = Private.classes.Assignment })
Private.classes.TimedAssignment.__index = Private.classes.TimedAssignment
-- An assignment dependent only upon a boss phase. Currently half-implemented.
---@class PhasedAssignment : Assignment
---@field phase integer The boss phase this assignment is triggered by.
---@field time number The time from the start of the phase to trigger the assignment.
Private.classes.PhasedAssignment = setmetatable({
phase = 1,
time = 0.0,
}, { __index = Private.classes.Assignment })
Private.classes.PhasedAssignment.__index = Private.classes.PhasedAssignment
-- Wrapper around an assignment with additional info about where to draw the assignment on the timeline.
---@class TimelineAssignment
---@field assignment Assignment The assignment.
---@field startTime number Time used to place the assignment on the timeline.
---@field order number The row of the assignment in the timeline.
Private.classes.TimelineAssignment = {}
Private.classes.TimelineAssignment.__index = Private.classes.TimelineAssignment
-- A raid or dungeon with a specific instanceID.
---@class RaidInstance
---@field name string The name of the raid or dungeon.
---@field journalInstanceID number The journal instance ID of the raid or dungeon. All bosses share the same JournalInstanceID.
---@field instanceID number The instance ID for the zone. All bosses share the same instanceID.
---@field bosses table<integer, Boss> List of bosses for the instance.
---@field icon integer Button image 2 from EJ_GetInstanceInfo.
Private.classes.RaidInstance = {
name = "",
journalInstanceID = 0,
instanceID = 0,
bosses = {},
icon = 0,
}
-- A raid or dungeon boss containing abilities, phases, etc.
---@class Boss
---@field name string Name of the boss.
---@field bossID table<integer,integer> ID of the boss or bosses.
---@field journalEncounterID integer Journal encounter ID of the boss encounter.
---@field dungeonEncounterID integer Dungeon encounter ID of the boss encounter.
---@field instanceID number The instance ID for the zone the boss is located in.
---@field phases table<integer, BossPhase> A list of phases and their durations.
---@field abilities table<SpellIDIndex, BossAbility> A list of abilities where the keys are spell IDs.
---@field sortedAbilityIDs table<integer, integer> An ordered list of abilities sorted by first appearance.
---@field abilityInstances table<integer, BossAbilityInstance> Data about a single instance of a boss ability stored in a boss ability frame in the timeline.
---@field treatAsSinglePhase boolean|nil If specified, the boss phases will be merged into one phase.
---@field icon integer Icon image from EJ_GetCreatureInfo.
Private.classes.Boss = {
name = "",
bossID = {},
journalEncounterID = 0,
dungeonEncounterID = 0,
instanceID = 0,
phases = {},
abilities = {},
sortedAbilityIDs = {},
abilityInstances = {},
treatAsSinglePhase = nil,
icon = 0,
}
-- A stage/phase in a boss encounter.
---@class BossPhase
---@field duration number The duration of the boss phase.
---@field defaultDuration number The default duration of the boss phase.
---@field count number The number of times the boss phase occurs.
---@field defaultCount number The default number of times the boss phase occurs.
---@field repeatAfter number|nil Which phase this phase repeats after.
---@field name string|nil If specified, the phase will be displayed on the timeline under this name. Otherwise hidden.
---@field fixedDuration boolean|nil If specified, the duration is not editable.
Private.classes.BossPhase = {
duration = 0.0,
defaultDuration = 0.0,
count = 1,
defaultCount = 1,
repeatAfter = nil,
name = nil,
fixedDuration = nil,
}
-- A spell that a boss casts including when the spell is cast.
---@class BossAbility
---@field phases table<number, BossAbilityPhase> Describes at which times in which phases the ability occurs in.
---@field eventTriggers table<SpellIDIndex, EventTrigger>|nil Other boss abilities that trigger the ability.
---@field duration number Usually how long the ability effect lasts.
---@field castTime number The actual cast time of the ability.
Private.classes.BossAbility = {
phases = {},
eventTriggers = nil,
duration = 0.0,
castTime = 0.0,
}
-- A phase in which a boss ability is triggered/cast at least once. May also repeat.
---@class BossAbilityPhase
---@field castTimes table<integer, number> An ordered list of cast times, where the actual cast time is the running sum.
---@field repeatInterval number|nil If defined, the ability will repeat at this interval starting from the last cast time.
---@field signifiesPhaseStart boolean|nil If defined, first cast denotes the start of the phase it occurs in.
---@field signifiesPhaseEnd boolean|nil If defined, last cast completion denotes the end of the phase it occurs in.
Private.classes.BossAbilityPhase = {
castTimes = {},
repeatInterval = nil,
signifiesPhaseStart = nil,
signifiesPhaseEnd = nil,
}
-- Defines a boss ability that triggers another boss ability. May also repeat.
---@class EventTrigger
---@field combatLogEventType CombatLogEventType The combat log event type that acts as a trigger.
---@field castTimes table<integer, number> An ordered list of cast times, where the actual cast time is the running sum.
---@field repeatCriteria EventTriggerRepeatCriteria|nil Describes criteria for the ability to repeat.
Private.classes.EventTrigger = {
combatLogEventType = "SCS",
castTimes = {},
repeatCriteria = nil,
}
-- A set of cast times to repeat until the phase ends. The triggering boss ability must be defined in the phase to trigger the repeat.
---@class EventTriggerRepeatCriteria
---@field spellCount integer The number of times the other ability must have been cast before the ability begins repeating.
---@field castTimes table<integer, number> An ordered list of cast times, where the actual cast time is the running sum.
Private.classes.EventTriggerRepeatCriteria = {
spellCount = 0,
castTimes = {},
}
-- Data about a single instance of a boss ability stored in a boss ability frame in the timeline. The instance may have
-- a different castStart, castEnd, or effectEnd if the boss phase duration or boss phase count are different from
-- default.
---@class BossAbilityInstance
---@field bossAbilitySpellID integer The SpellID of the boss ability.
---@field bossAbilityInstanceIndex integer The occurrence number of this instance out of all boss ability instances.
---@field bossAbilityOrderIndex integer The index of the ability in the boss's sortedAbilityIDs.
---@field bossPhaseOrderIndex integer The index of boss phase in the boss phase order (not the boss phase).
---@field bossPhaseDuration number The duration of the boss phase.
---@field bossPhaseName string|nil If defined, the name of the start of the phase.
---@field nextBossPhaseName string|nil If defined, the name of the start of the next phase.
---@field spellOccurrence integer The number of times the spell has already been cast prior to this instance (+1).
---@field bossPhaseIndex integer The phase the ability instance is cast in.
---@field castStart number The cast time from the start of the encounter.
---@field castEnd number The cast start plus the cast time.
---@field effectEnd number The cast end plus the ability duration.
---@field frameLevel integer Frame level to use for the ability instance on the timeline.
---@field relativeCastTime number|nil If defined, the cast time from the trigger cast time.
---@field combatLogEventType CombatLogEventType|nil If defined, the combat log event type that acts as a trigger.
---@field triggerSpellID number|nil If defined, the spellID of the boss ability that triggers the event trigger.
---@field spellCount number|nil If defined, the spell count of the boss ability that triggers the event trigger.
---@field repeatInstance number|nil If defined, the number of times the set of repeat criteria cast times has been completed.
---@field repeatCastIndex number|nil If defined, the index of the cast time in the repeat criteria.
---@field signifiesPhaseStart boolean|nil If defined, first cast denotes start of the phase it occurs in.
---@field signifiesPhaseEnd boolean|nil If defined, last cast completion denotes end of the phase it occurs in.
---@field overlaps {heightMultiplier:number, offset:number}|nil A height and offset multiplier to use if perfectly overlapping with another cast of the same ability.
Private.classes.BossAbilityInstance = {
bossAbilitySpellID = 0,
bossAbilityInstanceIndex = 0,
bossAbilityOrderIndex = 0,
bossPhaseIndex = 0,
bossPhaseOrderIndex = 0,
bossPhaseDuration = 0.0,
bossPhaseName = nil,
nextBossPhaseName = nil,
spellOccurrence = 0,
castStart = 0.0,
castEnd = 0.0,
effectEnd = 0.0,
frameLevel = 0,
relativeCastTime = nil,
combatLogEventType = nil,
triggerSpellID = nil,
spellCount = nil,
repeatInstance = nil,
repeatCastIndex = nil,
signifiesPhaseStart = nil,
signifiesPhaseEnd = nil,
overlaps = nil,
}
-- An entry in a roster, only used in gui.
---@class RosterEntry
---@field class string
---@field classColoredName string
---@field role RaidGroupRole
Private.classes.RosterEntry = {
class = "",
role = "",
classColoredName = "",
}
-- A plan for a boss encounter.
---@class Plan
---@field ID string Uniquely generated ID used when updating assignments received from other characters.
---@field isPrimaryPlan boolean Whether the plan has priority over other plans for the same boss.
---@field name string Name of the plan.
---@field dungeonEncounterID integer Dungeon encounter ID for the boss the plan is associated with.
---@field instanceID integer Instance ID for the boss the plan is associated with.
---@field content table<integer, string> Miscellaneous text that other addons or WeakAuras can use for the encounter.
---@field assignments table<integer, Assignment> Assignments for the plan.
---@field roster table<string, RosterEntry> Roster for the plan.
---@field collapsed table<string, boolean> Which assignees are collapsed in the assignment timeline.
---@field customPhaseDurations table<integer, number> Overridden boss phase durations.
---@field customPhaseCounts table<integer, number> Overridden boss phase counts.
---@field remindersEnabled boolean Whether reminders are enabled for the plan.
Private.classes.Plan = {
ID = "",
isPrimaryPlan = false,
name = "",
dungeonEncounterID = 0,
instanceID = 0,
content = {},
assignments = {},
roster = {},
collapsed = {},
customPhaseDurations = {},
customPhaseCounts = {},
remindersEnabled = true,
}
---@class SerializedPlan
---@field [1] string ID
---@field [2] string name
---@field [3] integer dungeonEncounterID
---@field [4] integer instanceID
---@field [5] table<integer, SerializedAssignment> assignments
---@field [6] table<string, SerializedRosterEntry> roster
---@field [7] table<integer, string> content
---@class SerializedAssignment
---@field [1] string assignee
---@field [2] number spellInfo.spellID
---@field [3] string text
---@field [4] string targetName
---@field [5] number time
---@field [6] CombatLogEventType|nil combatLogEventType
---@field [7] integer|nil combatLogEventSpellID
---@field [8] integer|nil spellCount
---@field [9] integer|nil phase
---@field [10] integer|nil bossPhaseOrderIndex
---@class SerializedRosterEntry
---@field [1] string name
---@field [2] string class
---@field [3] RaidGroupRole role
---@field [4] string classColoredName
---@class AdditionalMessageBoxButtonData Data needed to add additional buttons an EPMessageBox widget.
---@field beforeButtonIndex integer The child widget index (of EPMessageBox) to insert the button before, at the time of insertion.
---@field buttonText string Button text of the button in the message box.
---@field callback fun()|nil Function executed when the button is clicked.
---@class MessageBoxData Data needed to construct an EPMessageBox widget.
---@field ID string Unique ID to distinguish message boxes in the queue.
---@field isCommunication boolean True if the message box data was constructed from Communications.lua.
---@field title string Title of the message box.
---@field message string Content of the message box.
---@field acceptButtonText string Accept button text of the message box.
---@field acceptButtonCallback fun() Function executed when the accept button is clicked.
---@field rejectButtonText string Reject button text of the message box.
---@field rejectButtonCallback fun()|nil Function executed when the reject button is clicked.
---@field buttonsToAdd table<integer, AdditionalMessageBoxButtonData> Additional buttons to add to the message box.
--- Copies a table
---@generic T
---@param inTable T A table with any keys and values of type T
---@return T
function Private.DeepCopy(inTable)
local copy = {}
if type(inTable) == "table" then
for k, v in pairs(inTable) do
if k ~= "__index" then
copy[k] = Private.DeepCopy(v)
end
end
else
copy = inTable
end
return copy
end
--- Collects all valid fields recursively from the inheritance chain.
---@param classTable table The class table to collect fields from
---@param validFields table The table to populate with valid fields
local function CollectValidFields(classTable, validFields, visited)
for k, _ in pairs(classTable) do
if k ~= "__index" then
validFields[k] = true
end
end
local mt = getmetatable(classTable)
if mt and mt.__index and type(mt.__index) == "table" then
if not visited[mt.__index] then
visited[mt.__index] = true
CollectValidFields(mt.__index, validFields, visited)
end
end
end
--- Removes invalid fields not present in the inheritance chain of the class.
---@param classTable table The target class table
---@param o table The object to clean up
local function RemoveInvalidFields(classTable, o)
local validFields = {}
local visited = {}
CollectValidFields(classTable, validFields, visited)
for k, _ in pairs(o) do
if k ~= "__index" and k ~= "New" then
if not validFields[k] then
o[k] = nil
end
end
end
end
-- Creates a new instance of a table, copying fields that don't exist in the destination table.
---@generic T : table
---@param classTable T
---@param o table|nil
---@return T
local function CreateNewInstance(classTable, o)
o = o or {}
for key, value in pairs(Private.DeepCopy(classTable)) do
if o[key] == nil then
o[key] = value
end
end
setmetatable(o, classTable)
return o
end
---@param o any
---@return Assignment
function Private.classes.Assignment:New(o)
local instance = CreateNewInstance(self, o)
assignmentIDCounter = assignmentIDCounter + 1
instance.uniqueID = assignmentIDCounter
return instance
end
---@param o any
---@param removeInvalidFields boolean|nil
---@return CombatLogEventAssignment
function Private.classes.CombatLogEventAssignment:New(o, removeInvalidFields)
o = o or Private.classes.Assignment:New(o)
local instance = CreateNewInstance(self, o)
if removeInvalidFields then
RemoveInvalidFields(self, instance)
end
return instance
end
---@param o any
---@param removeInvalidFields boolean|nil
---@return TimedAssignment
function Private.classes.TimedAssignment:New(o, removeInvalidFields)
o = o or Private.classes.Assignment:New(o)
local instance = CreateNewInstance(self, o)
if removeInvalidFields then
RemoveInvalidFields(self, instance)
end
return instance
end
---@param o any
---@param removeInvalidFields boolean|nil
---@return PhasedAssignment
function Private.classes.PhasedAssignment:New(o, removeInvalidFields)
o = o or Private.classes.Assignment:New(o)
local instance = CreateNewInstance(self, o)
if removeInvalidFields then
RemoveInvalidFields(self, instance)
end
return instance
end
-- Copies an assignment with a new uniqueID.
---@param assignmentToCopy Assignment
---@return Assignment
function Private.DuplicateAssignment(assignmentToCopy)
local newAssignment = Private.classes.Assignment:New()
local newId = newAssignment.uniqueID
for key, value in pairs(Private.DeepCopy(assignmentToCopy)) do
newAssignment[key] = value
end
newAssignment.uniqueID = newId
setmetatable(newAssignment, getmetatable(assignmentToCopy))
return newAssignment
end
-- Creates a timeline assignment from an assignment.
---@param assignment Assignment
---@return TimelineAssignment
function Private.classes.TimelineAssignment:New(assignment)
assignment = assignment or Private.classes.Assignment:New(assignment)
local timelineAssignment = {
assignment = assignment,
startTime = 0,
order = 0,
}
setmetatable(timelineAssignment, self)
return timelineAssignment
end
---@param o any
---@return RaidInstance
function Private.classes.RaidInstance:New(o)
return CreateNewInstance(self, o)
end
---@param o any
---@return Boss
function Private.classes.Boss:New(o)
return CreateNewInstance(self, o)
end
---@param o any
---@return BossAbility
function Private.classes.BossAbility:New(o)
return CreateNewInstance(self, o)
end
---@param o any
---@return BossAbilityPhase
function Private.classes.BossAbilityPhase:New(o)
return CreateNewInstance(self, o)
end
---@param o any
---@return EventTrigger
function Private.classes.EventTrigger:New(o)
return CreateNewInstance(self, o)
end
---@param o any
---@return BossPhase
function Private.classes.BossPhase:New(o)
return CreateNewInstance(self, o)
end
---@param o any
---@param name string
---@param existingID string|nil
---@return Plan
function Private.classes.Plan:New(o, name, existingID)
local instance = CreateNewInstance(self, o)
instance.name = name
if existingID then
instance.ID = existingID
else
instance.ID = GenerateUniqueID()
end
return instance
end
-- Copies an assignment with a new uniqueID.
---@param planToCopy Plan
---@param name string
---@return Plan
function Private.DuplicatePlan(planToCopy, name)
local newPlan = Private.classes.Plan:New({}, "")
local newId = newPlan.ID
for key, value in pairs(Private.DeepCopy(planToCopy)) do
newPlan[key] = value
end
newPlan.name = name
newPlan.ID = newId
setmetatable(newPlan, getmetatable(planToCopy))
return newPlan
end
---@param o any
---@return RosterEntry
function Private.classes.RosterEntry:New(o)
return CreateNewInstance(self, o)
end
---@alias RaidGroupRole
---| "role:damager"
---| "role:healer"
---| "role:tank"
---| ""
---@alias ScrollKeyBinding
---| "MouseScroll"
---| "Alt-MouseScroll"
---| "Ctrl-MouseScroll"
---| "Shift-MouseScroll"
---@alias MouseButtonKeyBinding
---| "LeftButton"
---| "Alt-LeftButton"
---| "Ctrl-LeftButton"
---| "Shift-LeftButton"
---| "MiddleButton"
---| "Alt-MiddleButton"
---| "Ctrl-MiddleButton"
---| "Shift-MiddleButton"
---| "RightButton"
---| "Alt-RightButton"
---| "Ctrl-RightButton"
---| "Shift-RightButton"
---@alias AnchorPoint
---| "TOPLEFT"
---| "TOP"
---| "TOPRIGHT"
---| "RIGHT"
---| "BOTTOMRIGHT"
---| "BOTTOM"
---| "LEFT"
---| "BOTTOMLEFT"
---| "CENTER"
---@class KeyBindings
---@field pan MouseButtonKeyBinding
---@field zoom ScrollKeyBinding
---@field scroll ScrollKeyBinding
---@field editAssignment MouseButtonKeyBinding
---@field newAssignment MouseButtonKeyBinding
---@field duplicateAssignment MouseButtonKeyBinding
---@class Preferences
---@field lastOpenTab string
---@field keyBindings KeyBindings
---@field assignmentSortType AssignmentSortType
---@field timelineRows {numberOfAssignmentsToShow: integer, numberOfBossAbilitiesToShow: integer}
---@field zoomCenteredOnCursor boolean
---@field reminder ReminderPreferences
---@field showSpellCooldownDuration boolean
---@field minimap {hide: boolean}
---@class ReminderTextToSpeechPreferences
---@field enableAtAdvanceNotice boolean
---@field enableAtTime boolean
---@field voiceID integer
---@field volume number
---@class ReminderSoundPreferences
---@field enableAtAdvanceNotice boolean
---@field enableAtTime boolean
---@field advanceNoticeSound string
---@field atSound string
---@class GenericReminderPreferences
---@field enabled boolean
---@field font string,
---@field fontSize integer
---@field fontOutline ""|"MONOCHROME"|"OUTLINE"|"THICKOUTLINE"
---@field point AnchorPoint
---@field relativeTo string
---@field relativePoint AnchorPoint
---@field x number
---@field y number
---@field alpha number
---@class ProgressBarPreferences : GenericReminderPreferences
---@field textAlignment "LEFT"|"CENTER"|"RIGHT"
---@field texture string
---@field iconPosition "LEFT"|"RIGHT"
---@field width number
---@field durationAlignment "LEFT"|"CENTER"|"RIGHT"
---@field fill boolean
---@field showBorder boolean
---@field showIconBorder boolean
---@field color {[1]:number, [2]:number, [3]:number, [4]:number}
---@field backgroundColor {[1]:number, [2]:number, [3]:number, [4]:number}
---@field spacing integer
---@class MessagePreferences : GenericReminderPreferences
---@field showOnlyAtExpiration boolean
---@field textColor {[1]:number, [2]:number, [3]:number, [4]:number}
---@class ReminderPreferences
---@field enabled boolean
---@field onlyShowMe boolean
---@field removeDueToPhaseChange boolean
---@field cancelIfAlreadyCasted boolean
---@field advanceNotice number
---@field glowTargetFrame boolean
---@field progressBars ProgressBarPreferences
---@field messages MessagePreferences
---@field textToSpeech ReminderTextToSpeechPreferences
---@field sound ReminderSoundPreferences
local playerClass = select(2, UnitClass("player"))
local ccA, ccR, ccB, _ = GetClassColor(playerClass)
local defaults = {
---@class DefaultProfile
---@field activeBossAbilities table<integer, table<integer, boolean>> Boss abilities to show on the timeline.
---@field plans table<string, Plan> All plans.
---@field sharedRoster table<string, RosterEntry> A roster that is persistent across plans.
---@field lastOpenPlan string The last open plan.
---@field recentSpellAssignments table<integer, DropdownItemData> Recently assigned spells (up to 10).
---@field trustedCharacters table<integer, string> Characters that may bypass the import warning.
---@field windowSize {x: number, y: number}|nil Size of main frame when the addon was closed last.
---@field minimizeFramePosition {x: number, y: number}|nil Position of the minimize frame.
---@field cooldownOverrides table<integer, number> Cooldown duration overrides for spells.
---@field activeText table<integer, string> External text send by the group leader on encounter start.
---@field preferences Preferences Settings.
profile = {
activeBossAbilities = {},
plans = {},
sharedRoster = {},
lastOpenPlan = "",
recentSpellAssignments = {},
trustedCharacters = {},
cooldownOverrides = {},
activeText = {},
preferences = {
lastOpenTab = Private.L["Cooldown Overrides"],
keyBindings = {
pan = "RightButton",
zoom = "Ctrl-MouseScroll",
scroll = "MouseScroll",
editAssignment = "LeftButton",
newAssignment = "LeftButton",
duplicateAssignment = "Ctrl-LeftButton",
},
assignmentSortType = "First Appearance",
timelineRows = {
numberOfAssignmentsToShow = 8,
numberOfBossAbilitiesToShow = 8,
},
zoomCenteredOnCursor = true,
showSpellCooldownDuration = true,
minimap = {
hide = false,
},
reminder = {
enabled = true,
onlyShowMe = true,
cancelIfAlreadyCasted = true,
removeDueToPhaseChange = true,
advanceNotice = 10.0,
glowTargetFrame = true,
messages = {
enabled = true,
font = "Interface\\Addons\\EncounterPlanner\\Media\\Fonts\\PTSansNarrow-Bold.ttf",
fontSize = 24,
fontOutline = "",
point = "BOTTOM",
relativeTo = "UIParent",
relativePoint = "CENTER",
x = 0,
y = 385,
alpha = 1.0,
showOnlyAtExpiration = true,
textColor = { 1, 0.82, 0, 0.95 },
},
progressBars = {
enabled = true,
textAlignment = "LEFT",
font = "Interface\\Addons\\EncounterPlanner\\Media\\Fonts\\PTSansNarrow-Bold.ttf",
fontSize = 16,
fontOutline = "",
point = "BOTTOMRIGHT",
relativeTo = "UIParent",
relativePoint = "CENTER",
x = -200,
y = 0,
alpha = 0.90,
texture = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\Statusbar_Clean",
iconPosition = "LEFT",
width = 200,
durationAlignment = "RIGHT",
fill = false,
showBorder = false,
showIconBorder = false,
color = { ccA, ccR, ccB, 0.90 },
backgroundColor = { 10.0 / 255.0, 10.0 / 255.0, 10.0 / 255.0, 0.25 },
spacing = 0,
},
textToSpeech = {
enableAtAdvanceNotice = false,
enableAtTime = false,
voiceID = 0,
volume = 100,
},
sound = {
advanceNoticeSound = "",
atSound = "",
enableAtAdvanceNotice = false,
enableAtTime = false,
},
},
},
},
}
local callbackTarget = {}
---@param name string
---@param func function
function callbackTarget.RegisterCallback(target, name, func) end
---@param name string
function callbackTarget.UnregisterCallback(target, name) end
Private.addOn = AceAddon:NewAddon(AddOnName, "AceConsole-3.0", "AceEvent-3.0", "AceComm-3.0")
Private.addOn.defaults = defaults
---@type AceDBObject-3.0
Private.addOn.db = nil
Private.addOn.optionsModule = Private.addOn:NewModule("Options") --[[@as OptionsModule]]
Private.callbackTarget = callbackTarget
Private.callbackHandler = CallbackHandler:New(Private.callbackTarget)
Private.raidInstances = {} --[[@as table<integer, RaidInstance>]]
Private.interfaceUpdater = {}
Private.bossUtilities = {}
Private.utilities = {}
Private.mainFrame = nil --[[@as EPMainFrame]]
Private.assignmentEditor = nil --[[@as EPAssignmentEditor]]
Private.rosterEditor = nil --[[@as EPRosterEditor]]
Private.importEditBox = nil --[[@as EPEditBox]]
Private.exportEditBox = nil --[[@as EPEditBox]]
Private.optionsMenu = nil --[[@as EPOptions]]
Private.messageAnchor = nil --[[@as EPReminderMessage]]
Private.progressBarAnchor = nil --[[@as EPProgressBar]]
Private.menuButtonContainer = nil --[[@as EPContainer]]
Private.messageContainer = nil --[[@as EPContainer]]
Private.progressBarContainer = nil --[[@as EPContainer]]
Private.phaseLengthEditor = nil --[[@as EPPhaseLengthEditor]]
Private.newPlanDialog = nil --[[@as EPNewPlanDialog]]
Private.externalTextEditor = nil --[[@as EPEditBox]]
Private.tooltip = CreateFrame("GameTooltip", "EncounterPlannerTooltip", UIParent, "GameTooltipTemplate")
Private.tests = {}
LSM:Register(
"font",
"PT Sans Narrow",
"Interface\\Addons\\EncounterPlanner\\Media\\Fonts\\PTSansNarrow-Bold.ttf",
bit.bor(LSM.LOCALE_BIT_western, LSM.LOCALE_BIT_ruRU)
)