Repository navigation
Expand file tree
/
Copy pathCore.lua
More file actions
256 lines (236 loc) · 10.5 KB
/
Copy pathCore.lua
File metadata and controls
256 lines (236 loc) · 10.5 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
246
247
248
249
250
251
252
253
254
255
256
-- Wick's Demons and Things
-- Core.lua: WickCore addon object, saved variables, event dispatch, slash command.
--
-- Forever build. The cooldown and proc tracker is gone with Midnight rules.
-- What remains is the warlock's loadout: the soul bar (stones, shards, the
-- create spells), the pet bar with Incubus as its own summon, the shard
-- counter, and through WickCore the talent layer, pre-pull checklist and
-- racials.
local ADDON, ns = ...
local Core = WickCore
if not Core then
-- WickCore is missing or switched off.
--
-- The TOC asks for it with OptionalDeps rather than Dependencies on
-- purpose. A hard dependency makes the client refuse to load this addon
-- at all, so nothing of ours runs and the player is told nothing beyond
-- a greyed line in the AddOns list. Loading anyway lets us say what is
-- wrong and where to get it.
--
-- One line for the lot of them, not one per addon: with the whole suite
-- installed and WickCore switched off, a line each would be a wall.
local need = _G.WicksNeedCore
if not need then
need = {}
_G.WicksNeedCore = need
local f = CreateFrame("Frame")
f:RegisterEvent("PLAYER_LOGIN")
f:SetScript("OnEvent", function()
table.sort(need)
print(("|cff4FC778Wick's Mods|r: %s %s WickCore, which is not installed or not switched on. It is in the same download as the rest of the suite: |cffD4C8A1wicksmods.com|r")
:format(table.concat(need, ", "), #need == 1 and "needs" or "need"))
end)
end
need[#need + 1] = "Wick's Demons and Things"
return
end
local D, R = Core.Dialect, Core.Restrict
local BAR = function(x, y) return { point = "CENTER", x = x, y = y, hidden = false, locked = false } end
local PROFILE_DEFAULTS = {
soul = BAR(-180, 200),
pet = BAR(180, 200),
cd = BAR(0, 156), -- kept for the UI lock/reset loops; no module behind it on Forever
shard = BAR(0, 240),
ui = { point = "CENTER", x = 0, y = 0 },
kitWindow = {},
}
local A = Core:NewAddon("WicksDemonsAndThings", {
title = "Wick's Demons and Things",
version = "0.9.0",
savedVar = "WicksDemonsSaved",
defaults = { profile = PROFILE_DEFAULTS, char = {}, global = {} },
})
WicksDemons = WicksDemons or {}
local WD = WicksDemons
ns.WD = WD
WD.A = A
WD.ADDON = ADDON
-- Runtime aliases to the WickCore profile and character tables. The module
-- files address these names; they are not saved variables of their own.
WicksDemonsDB = WicksDemonsDB or {}
WicksDemonsCharDB = WicksDemonsCharDB or {}
local _, playerClass = UnitClass("player")
WD.playerClass = playerClass
WD.isWarlock = (playerClass == "WARLOCK")
WD.SHARD_ITEM_ID = 6265
WD._listeners = {}
function WD:On(event, fn)
self._listeners[event] = self._listeners[event] or {}
table.insert(self._listeners[event], fn)
end
function WD:Emit(event, ...)
local list = self._listeners[event]
if not list then return end
for _, fn in ipairs(list) do
local ok, err = pcall(fn, ...)
if not ok then A:Print(("error in %s: %s"):format(event, tostring(err))) end
end
end
-- Spec through the trait system where it exists, the talent tabs elsewhere.
local WARLOCK_SPEC_BY_TAB = { "affliction", "demonology", "destruction" }
function WD:GetActiveSpec()
if not self.isWarlock then return nil end
if Core.Talents:IsAvailable() then
local s = Core.Talents:Summary()
if s and s.spent and #s.spent >= 3 then
local best, bestPts = nil, -1
for i, c in ipairs(s.spent) do
if (c.spent or 0) > bestPts then best, bestPts = i, c.spent or 0 end
end
return bestPts > 0 and WARLOCK_SPEC_BY_TAB[best] or nil
end
return nil
end
if not GetNumTalentTabs or not GetTalentTabInfo then return nil end
local ok, result = pcall(function()
local maxIdx, maxPts = 0, -1
for i = 1, GetNumTalentTabs() or 0 do
local _, _, points = GetTalentTabInfo(i)
if (points or 0) > maxPts then maxPts, maxIdx = points or 0, i end
end
return maxPts > 0 and WARLOCK_SPEC_BY_TAB[maxIdx] or nil
end)
return ok and result or nil
end
-- ============================================================
-- Lifecycle
-- ============================================================
function A:OnInitialize()
WicksDemonsDB = self.db.profile
WicksDemonsCharDB = self.db.char
WD.db = self.db
self.db:On("OnProfileChanged", function()
WicksDemonsDB = self.db.profile
WD:Emit("PLAYER_TALENT_UPDATE")
end)
Core.Cooldowns:New(self, { key = "cooldownBar" })
Core.Kit:New(self, {
racials = true,
checklist = {
{ label = "Demon armor", aura = { "Demon Armor", "Demon Skin", "Fel Armor" }, cast = "Demon Skin" },
{ label = "Shadow Ward known", check = function() return D.IsSpellKnown(6229) or nil end },
{ label = "Healthstone", item = 5512, min = 1, cast = "Create Healthstone" },
{ label = "Soulstone", item = 5232, min = 1, cast = "Create Soulstone" },
{ label = "Soul shards", item = 6265, min = 5 },
{ label = "Demon out", check = function() return UnitExists("pet") and true or false end },
},
})
end
function A:OnEnable()
if not WD.isWarlock then
self:Print("loaded (non-warlock: viewer mode).")
else
self:Print("loaded. /wdt for options, /wdt kit for talents and checklist.")
end
WD:Emit("LOGIN")
self:RegisterLauncher({
onClick = function(_, button)
if button == "RightButton" then self.kit:Toggle()
elseif WD.UI then WD.UI:Toggle() end
end,
tooltip = function(tt)
tt:AddLine(Core.Chrome:TitleMarkup("Wick's Demons and Things"))
tt:AddLine("Left-click: options Right-click: talents and checklist", 0.5, 0.5, 0.5)
end,
})
if self.cooldowns then self.cooldowns:Init() end
self:RegisterOptions(function(page, addon)
local O = Core.Options
local y = O:Heading(page, "Wick's Demons and Things", 0)
y = O:Note(page, "Bars are configured in the panel (/wdt). Talents, checklist and racials live in the kit (/wdt kit).", y)
y = O:Button(page, "Open panel", function() if WD.UI then WD.UI:Toggle() end end, y, 100)
y = O:Button(page, "Open kit", function() addon.kit:Toggle() end, y, 100)
if addon.cooldowns then y = addon.cooldowns:OptionRow(page, y - 6) end
y = O:ProfileSection(page, addon, y - 8)
end)
end
-- ============================================================
-- Game events
-- ============================================================
local f = CreateFrame("Frame")
WD.eventFrame = f
for _, e in ipairs({ "PLAYER_ENTERING_WORLD", "PLAYER_REGEN_DISABLED", "PLAYER_REGEN_ENABLED",
"BAG_UPDATE", "UNIT_PET", "PLAYER_TALENT_UPDATE", "CHARACTER_POINTS_CHANGED",
"SPELLS_CHANGED", "TRAIT_CONFIG_UPDATED", "ACTIVE_COMBAT_CONFIG_CHANGED" }) do
pcall(f.RegisterEvent, f, e)
end
f:SetScript("OnEvent", function(_, event, ...)
if event == "PLAYER_REGEN_DISABLED" then
WD.inCombat = true
WD:Emit("COMBAT_START")
elseif event == "PLAYER_REGEN_ENABLED" then
WD.inCombat = false
WD:Emit("COMBAT_END")
elseif event == "TRAIT_CONFIG_UPDATED" or event == "ACTIVE_COMBAT_CONFIG_CHANGED" then
WD:Emit("PLAYER_TALENT_UPDATE")
end
WD:Emit(event, ...)
end)
-- ============================================================
-- Keybinding labels
-- ============================================================
BINDING_HEADER_WICKSDEMONS = "Wick's Demons and Things"
BINDING_NAME_WICKSDEMONS_TOGGLE_SOUL = "Toggle soul bar"
BINDING_NAME_WICKSDEMONS_TOGGLE_PET = "Toggle pet bar"
BINDING_NAME_WICKSDEMONS_TOGGLE_SHARD = "Toggle shard counter"
BINDING_NAME_WICKSDEMONS_TOGGLE_KIT = "Toggle talents and checklist"
-- ============================================================
-- Slash command
-- ============================================================
A:RegisterSlash(function(_, input)
input = (input or ""):lower()
local function toggle(mod) if mod and mod.Toggle then mod:Toggle() end end
local function reset(mod) if mod and mod.ResetPosition then mod:ResetPosition() end end
if input == "" then if WD.UI then WD.UI:Toggle() end return end
if input == "kit" or input == "talents" or input == "checklist" then A.kit:Toggle() return end
if input == "cd" or input:match("^cd%s") then return A.cooldowns:Command(input:match("^%a+%s*(.*)$")) end
if input == "help" or input == "?" then
A:Print("commands")
print(" /wdt open options panel")
print(" /wdt kit talents, pre-pull checklist, racials")
print(" /wdt soul toggle soul bar")
print(" /wdt pet toggle pet bar")
print(" /wdt shard toggle shard counter")
print(" /wdt lock|unlock lock or unlock all bars")
print(" /wdt reset reset all bar positions")
print(" /wdt status print diagnostic info")
return
end
if input == "soul" then toggle(WD.SoulBar) return end
if input == "pet" then toggle(WD.PetBar) return end
if input == "shard" then toggle(WD.ShardCounter) return end
if input == "lock" or input == "unlock" then
for _, key in ipairs({ "soul", "pet", "cd", "shard" }) do
WicksDemonsDB[key] = WicksDemonsDB[key] or {}
WicksDemonsDB[key].locked = (input == "lock")
end
A:Print("bars " .. input .. "ed.")
return
end
if input == "reset" then
reset(WD.SoulBar); reset(WD.PetBar); reset(WD.ShardCounter)
A:Print("positions reset.")
return
end
if input == "status" then
A:Print(("warlock: %s spec: %s"):format(tostring(WD.isWarlock), tostring(WD:GetActiveSpec() or "(unknown)")))
for _, key in ipairs({ "soul", "pet", "shard" }) do
local cfg = WicksDemonsDB[key] or {}
print((" %s: hidden=%s locked=%s point=%s x=%d y=%d"):format(key, tostring(cfg.hidden), tostring(cfg.locked), cfg.point or "?", cfg.x or 0, cfg.y or 0))
end
if WD.ShardCounter and WD.ShardCounter.Count then print((" shards: %d"):format(WD.ShardCounter:Count())) end
A:Print("restrictions: " .. R:Summary())
return
end
A:Print("unknown command. Try /wdt help")
end, "/wdt", "/wicksdemons")