Repository navigation
Expand file tree
/
Copy pathConfig.lua
More file actions
245 lines (213 loc) · 8.91 KB
/
Copy pathConfig.lua
File metadata and controls
245 lines (213 loc) · 8.91 KB
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
-- Settings storage.
--
-- One flat table. Settings were briefly split per content type (keys vs
-- raid), which cost a whole tab and bought nothing: the cast database holds
-- keystone dungeons only, so in a raid the addon has nothing to say and a
-- second set of switches just gave the player a way to change settings that
-- never fire.
--
-- Convention: nil means on, false means off. Nothing ever stores true for an
-- on-by-default option, or a fresh profile would read as "explicitly
-- enabled" and defeat later default changes.
CastAheadConfig = {}
local C = CastAheadConfig
-- Off: the heads-up before a cast reads as noise more often than as warning,
-- so the call lands when the cast starts unless the player asks for lead.
C.LEAD_DEFAULT = 0
C.LEAD_MAX = 15
-- Every nameplate addon draws a different frame, so where our icons land and
-- how big they are has to be the player's call. Sizes are in pixels at the
-- default UI scale; the nudge shifts the whole row off the anchor point.
C.ICON_DEFAULT, C.ICON_MIN, C.ICON_MAX = 26, 14, 64
C.NUDGE_MAX = 100
-- Sideways spacing: by default each icon steps far enough that the labels
-- under neighbours never touch. Fixed spacing steps by icon plus this gap
-- instead, so every row packs the same and wide labels may overlap (#20).
C.GAP_DEFAULT, C.GAP_MAX = 4, 40
-- Where the icons draw relative to everything else. Nameplates live under
-- WorldFrame, below all of UIParent, so even BACKGROUND clears every plate
-- while staying under the whole UI, the way a plate does; DIALOG and above
-- are for a Plater profile that lifts plates into UIParent (#19).
C.STRATA = { "BACKGROUND", "LOW", "MEDIUM", "HIGH", "DIALOG", "FULLSCREEN", "FULLSCREEN_DIALOG", "TOOLTIP" }
C.STRATA_DEFAULT = "BACKGROUND"
C.CENTER_DEFAULT, C.CENTER_MIN, C.CENTER_MAX = 100, 50, 250
-- The advice under an icon is sized from the icon, so one setting keeps them
-- in proportion. This is the adjustment on top of that, in percent, for a
-- player who wants tighter rows or larger words than the icon implies.
C.LABEL_DEFAULT, C.LABEL_MIN, C.LABEL_MAX = 100, 50, 200
-- The countdown drawn on the icon, and the words in the centre call, are
-- adjusted the same way: a percentage of what the thing they sit on implies.
C.TIME_DEFAULT, C.TIME_MIN, C.TIME_MAX = 100, 50, 200
C.CENTER_TEXT_DEFAULT, C.CENTER_TEXT_MIN, C.CENTER_TEXT_MAX = 100, 50, 200
function C.Get(key)
return CastAheadDB and CastAheadDB[key]
end
-- A saved variable can hold anything, so every number the drawing code reads
-- comes back through one clamp rather than being trusted where it is used.
function C.Number(key, default, low, high)
local value = tonumber(C.Get(key))
if not value then return default end
if value < low then return low end
if value > high then return high end
return value
end
function C.IconSize()
return C.Number("iconSize", C.ICON_DEFAULT, C.ICON_MIN, C.ICON_MAX)
end
function C.FixedGap()
if C.Get("fixedSpacing") ~= true then return nil end
return C.Number("iconGap", C.GAP_DEFAULT, 0, C.GAP_MAX)
end
function C.Strata()
local value = C.Get("strata")
for _, name in ipairs(C.STRATA) do
if name == value then return value end
end
return C.STRATA_DEFAULT
end
-- The game gives a played file no volume of its own, only a channel, so the
-- channel is how a player sets our level apart from Master: each one has its
-- own slider in the game's Sound options.
C.CHANNELS = { "Master", "Dialog", "SFX" }
C.CHANNEL_DEFAULT = "Master"
function C.SoundChannel()
local value = C.Get("soundChannel")
for _, name in ipairs(C.CHANNELS) do
if name == value then return value end
end
return C.CHANNEL_DEFAULT
end
function C.NudgeX()
return C.Number("nudgeX", 0, -C.NUDGE_MAX, C.NUDGE_MAX)
end
function C.NudgeY()
return C.Number("nudgeY", 0, -C.NUDGE_MAX, C.NUDGE_MAX)
end
-- Percent, so the slider reads the way a player expects.
function C.CenterScale()
return C.Number("centerScale", C.CENTER_DEFAULT, C.CENTER_MIN, C.CENTER_MAX) / 100
end
function C.LabelScale()
return C.Number("labelScale", C.LABEL_DEFAULT, C.LABEL_MIN, C.LABEL_MAX) / 100
end
function C.TimeScale()
return C.Number("timeScale", C.TIME_DEFAULT, C.TIME_MIN, C.TIME_MAX) / 100
end
-- Only the words. The block's own scale moves the icon and the countdown with
-- them, so this is for a player who wants the call readable from further away
-- without a bigger block.
function C.CenterTextScale()
return C.Number("centerTextScale", C.CENTER_TEXT_DEFAULT,
C.CENTER_TEXT_MIN, C.CENTER_TEXT_MAX) / 100
end
-- Short by default. Measured on real rows: with the long verdicts spelled
-- out, three icons stretched across the whole nameplate and the spacing
-- depended on which verdict happened to come up; short, the widest word on a
-- plate is six characters and every row packs the same. The whole word is
-- still spoken and still shown in the cast table, so nothing is lost - but
-- the switch is there for anyone who wants it on the icons too.
function C.ShortLabels()
return C.Get("fullLabels") ~= true
end
function C.CenterText()
return C.Get("centerText") == true
end
function C.Set(key, value)
CastAheadDB = CastAheadDB or {}
CastAheadDB[key] = value
end
function C.Enabled(key)
return C.Get(key) ~= false
end
function C.Dev()
return C.Get("devMode") == true
end
function C.SaveCalls()
return C.Get("saveCalls") == true
end
function C.Recording()
return C.Dev() and C.Enabled("recordAll")
end
function C.SetEnabled(key, enabled)
if enabled then
C.Set(key, nil)
else
C.Set(key, false)
end
end
-- Seconds before a predicted cast to announce it; 0 switches the heads-up
-- off. Clamped rather than rejected: a saved variable can hold anything.
C.SAVE_LEAD_MAX = 10
function C.SaveLead()
return C.Number("saveLeadSeconds", 0, 0, C.SAVE_LEAD_MAX)
end
function C.Lead()
local seconds = tonumber(C.Get("leadSeconds"))
if not seconds then return C.LEAD_DEFAULT end
if seconds < 0 then return 0 end
if seconds > C.LEAD_MAX then return C.LEAD_MAX end
return seconds
end
-- The addon was called Forecast until 2026-09-01. Its saved variables live
-- under the old global for one more load: the TOC now declares CastAheadDB,
-- so ForecastDB arrives populated only while the old file is still on disk.
-- Adopt it once, then let it go.
function C.AdoptOldName()
if CastAheadDB == nil and type(ForecastDB) == "table" then
CastAheadDB = ForecastDB
end
end
-- Run once, the moment the saved variables arrive: by PLAYER_LOGIN the
-- minimap icon has already created the table, and a fresh install would
-- read as an old profile. Three older profile shapes exist and each would
-- otherwise read as a reset: the per-context one (CastAheadDB.ctx.key), the
-- flat one from before the early warning became a number, and any profile
-- from before the centre call became opt-in, which keeps it on.
C.SCHEMA = 3
function C.Migrate()
local existing = CastAheadDB ~= nil
CastAheadDB = CastAheadDB or {}
if type(CastAheadDB.ctx) == "table" then
-- The key context was the one that ever fired; the raid one is
-- dropped along with the tab that edited it.
for key, value in pairs(CastAheadDB.ctx.key or {}) do
if CastAheadDB[key] == nil then CastAheadDB[key] = value end
end
CastAheadDB.ctx = nil
end
if CastAheadDB.leadWarning ~= nil then
-- The old boolean said only on or off, and on meant five seconds.
if CastAheadDB.leadSeconds == nil then
CastAheadDB.leadSeconds = CastAheadDB.leadWarning == true and 5 or 0
end
CastAheadDB.leadWarning = nil
end
if existing and CastAheadDB.schema == nil and CastAheadDB.centerText == nil then
CastAheadDB.centerText = true
end
for _, field in ipairs({ "sounds", "spellSounds" }) do
local picks = CastAheadDB[field]
for key, name in pairs(type(picks) == "table" and picks or {}) do
if type(name) == "string" and name:sub(1, 11) == "CastAhead: " then
picks[key] = "Cast Ahead: " .. name:sub(12)
end
end
end
for _, picks in pairs(type(CastAheadDB.saveButtons) == "table" and CastAheadDB.saveButtons or {}) do
for size, value in pairs(type(picks) == "table" and picks or {}) do
if type(value) == "number" then picks[size] = { value } end
end
end
if existing and (tonumber(CastAheadDB.schema) or 0) < C.SCHEMA and CastAheadDB.saveCalls == nil then
CastAheadDB.saveCalls = true
end
CastAheadDB.schema = C.SCHEMA
end
local RECORDED = { "journal", "probe", "phaseProbe", "sweep", "fingerprints", "diag", "combatLogOurs", "dispel" }
-- Every setting back to a new install; recorded data stays until the
-- Development tab clears it.
function C.ResetAll()
local fresh = { schema = C.SCHEMA }
for _, key in ipairs(RECORDED) do fresh[key] = CastAheadDB and CastAheadDB[key] end
CastAheadDB = fresh
end