-
Notifications
You must be signed in to change notification settings - Fork 2
/
core.lua
234 lines (201 loc) · 5.71 KB
/
core.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
local _, ns = ...
ns['__LibSpellbook-1.0'] = {}
ns = ns['__LibSpellbook-1.0']
local MAJOR, MINOR = 'LibSpellbook-1.0', 30
assert(LibStub, MAJOR .. ' requires LibStub.')
local lib = LibStub:NewLibrary(MAJOR, MINOR)
if not lib then return end
ns.lib = lib
-- TODO: expose spells through the lib object?
local spells = {
book = {}, -- spellId = bookType
byId = {}, -- spellId = spellName
byName = {}, -- spellName = { id1 = true, id2 = true, ... }
lastSeen = {}, -- spellId = generation
}
local book, byId, byName, lastSeen = spells.book, spells.byId, spells.byName, spells.lastSeen
ns.spells = spells
local event_meta = {
__call = function(handlers, ...)
for _, handler in next, handlers do
handler(...)
end
end
}
lib.callbacks = lib.callbacks or LibStub('CallbackHandler-1.0'):New(lib)
if lib.events then
lib.events:UnregisterAllEvents()
for event in next, lib.events do
if event == event:upper() then
lib.events[event] = nil
end
end
else
lib.events = CreateFrame('Frame')
lib.events:SetScript('OnEvent', function(self, event, ...)
self[event](event, ...)
end)
end
-- incremented on every spells scan
ns.generation = 0
--- Register an event handler.
-- @function LibSpellbook:RegisterEvent
-- @param string the event for which the handler is to be registered
-- @param function the event handler
-- @param boolean indicates if the event should be registered for the player only
function lib:RegisterEvent(event, handler, isUnitEvent)
if type(handler) ~= 'function' then
error(('Attempted to register event [%s] with a non-function handler.'):format(event))
end
local events = self.events
local current = events[event]
if not current then
events[event] = handler
else
if type(current) == 'function' then
if current ~= handler then
events[event] = setmetatable({current, handler}, event_meta)
end
else
for _, func in next, current do
if func == handler then return end
end
current[#current + 1] = handler
end
end
local isRegistered, unit = events:IsEventRegistered(event)
if not isRegistered or not isUnitEvent and unit then
if isUnitEvent then
events:RegisterUnitEvent(event, 'player')
else
events:RegisterEvent(event)
end
end
end
--- Unregister an event handler.
-- @function LibSpellbook:UnregisterEvent
-- @param string the event for which the handler is to be unregistered
-- @param function the event handler to be unregistered
function lib:UnregisterEvent(event, handler)
if not handler then return end
local current = self.events[event]
local cleanUp = false
if (type(current) == 'table') then
for i, func in next, current do
if func == handler then
current[i] = nil
break
end
end
if not next(current) then
cleanUp = true
end
end
if cleanUp or current == handler then
self.events:UnregisterEvent(event)
self.events[event] = nil
end
end
local function Resolve(spell)
local type = type(spell)
if type == 'number' then
return spell
elseif type == 'string' then
local ids = byName[spell]
if ids then
return next(ids)
else
return tonumber(spell:match('spell:(%d+)'))
end
end
end
--- Return all associated spell identifiers.
-- @function LibSpellbook:GetAllIds
-- @param string|number a spell name, link or identifier
-- @return table a table of associated spell identifiers
function lib:GetAllIds(spell)
local id = Resolve(spell)
local name = id and byId[id]
return name and byName[name]
end
--- Return the associated spell type.
-- @function LibSpellbook:GetBookType
-- @param string|number a spell name, link or identifier
-- @return ?string the associated spell type
function lib:GetBookType(spell)
local id = Resolve(spell)
return id and book[id]
end
--- Return whether spells have already been scanned
-- @function LibSpellbook:HasSpells
-- @return boolean
function lib:HasSpells()
return next(byId) and ns.generation > 0
end
--- Return whether the spell is known to the player or their pet.
-- If `bookType` is specified, it has to match it.
-- @function LibSpellbook:IsKnown
-- @param string|number a spell name, link or identifier
-- @param ?string a spell type
-- @return ?boolean
function lib:IsKnown(spell, bookType)
local id = Resolve(spell)
if id and byId[id] then
return not bookType or bookType == book[id]
end
-- fallback for class traits (we might dump this whole thing after all)
return IsPlayerSpell(spell)
end
local function iterator(bookType, id)
local name
repeat
id, name = next(byId, id)
if id and book[id] == bookType then
return id, name
end
until not id
end
--- Iterate over all spells.
-- @function LibSpellbook:IterateSpells
-- @param ?string the spell type over which to iterate
-- @return iterator returns a spell identifier and name on every iteration
-- @usage
-- for id, name in LibSpellbook:IterateSpells() do
-- -- do something
-- end
function lib:IterateSpells(bookType)
if bookType then
return iterator, bookType
end
return next, byId
end
function ns.FoundSpell(id, name, bookType)
if not (id and name) then return end
local isNew = not lastSeen[id]
if byName[name] then
byName[name][id] = true
else
byName[name] = { [id] = true }
end
byId[id] = name
book[id] = bookType
lastSeen[id] = ns.generation
if isNew then
-- print('|cff00ff00LSB: spell added|r', id, name, bookType)
lib.callbacks:Fire('LibSpellbook_Spell_Added', id, bookType, name)
end
return isNew
end
function ns.CleanUp(id)
local name = byId[id]
local bookType = book[id]
byName[name][id] = nil
if not next(byName[name]) then
byName[name] = nil
end
byId[id] = nil
book[id] = nil
lastSeen[id] = nil
-- print('|cffff0000LSB: spell removed|r', id, name, bookType)
lib.callbacks:Fire('LibSpellbook_Spell_Removed', id, bookType, name)
end