Mercurial > wow > reaction
comparison MultiCastButton.lua @ 245:65f2805957a0
No real reason to store some of the code in a subdirectory.
author | Flick |
---|---|
date | Sat, 26 Mar 2011 12:35:08 -0700 |
parents | classes/MultiCastButton.lua@0e20f65375d5 |
children | c918ff9ac787 |
comparison
equal
deleted
inserted
replaced
244:f255cd69e890 | 245:65f2805957a0 |
---|---|
1 local addonName, addonTable = ... | |
2 local ReAction = addonTable.ReAction | |
3 local L = ReAction.L | |
4 local _G = _G | |
5 local CreateFrame = CreateFrame | |
6 local format = string.format | |
7 local unpack = unpack | |
8 local GetCVar = GetCVar | |
9 local GameTooltip_SetDefaultAnchor = GameTooltip_SetDefaultAnchor | |
10 local CooldownFrame_SetTimer = CooldownFrame_SetTimer | |
11 local InCombatLockdown = InCombatLockdown | |
12 local IsUsableSpell = IsUsableSpell | |
13 local IsUsableAction = IsUsableAction | |
14 local IsSpellKnown = IsSpellKnown | |
15 local IsSpellInRange = IsSpellInRange | |
16 local IsActionInRange = IsActionInRange | |
17 local GetSpellInfo = GetSpellInfo | |
18 local GetSpellCooldown = GetSpellCooldown | |
19 local GetActionCooldown = GetActionCooldown | |
20 local GetSpellTexture = GetSpellTexture | |
21 local GetActionTexture = GetActionTexture | |
22 local GetMultiCastTotemSpells = GetMultiCastTotemSpells | |
23 | |
24 --[[ | |
25 Blizzard Constants: | |
26 - NUM_MULTI_CAST_BUTTONS_PER_PAGE = 4 | |
27 - NUM_MULTI_CAST_PAGES = 3 | |
28 - SHAMAN_TOTEM_PRIORITIES = { } -- sets the order of the totems | |
29 - TOTEM_MULTI_CAST_SUMMON_SPELLS = { } -- list of summon spellIDs | |
30 - TOTEM_MULTI_CAST_RECALL_SPELLS = { } -- list of recall spellIDs | |
31 | |
32 Blizzard Events: | |
33 - UPDATE_MULTI_CAST_ACTIONBAR | |
34 | |
35 Blizzard APIs: | |
36 - GetMultiCastBarOffset() : returns 6 | |
37 | |
38 - SetMultiCastSpell(actionID, spellID) (protected) OR | |
39 SetAttribute("type","multispell") | |
40 SetAttribute("action",actionID) | |
41 SetAttribute("spell",spellID) | |
42 | |
43 note: multicast actionID page is NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset(), | |
44 so that's action ID 132-144. | |
45 | |
46 - spell1, spell2, spell3, ... = GetMultiCastTotemSpells(slot) | |
47 returns spellIDs for all known totems that fit that slot. This function is available in | |
48 the secure environment. | |
49 | |
50 Blizzard textures: | |
51 All the textures for the multicast bar (arrows, empty-slot icons, etc) are part of a single | |
52 texture: each texture uses SetTexCoord() to display only a slice of the textures. I suppose | |
53 this is to slightly optimize texture load performance, but it makes the UI code more clumsy. | |
54 | |
55 Each totem button and arrow has a colored border indicating its elemental type. | |
56 | |
57 TODO: | |
58 - make whether to show the colored border configurable (looks really bad with ButtonFacade:Zoomed) | |
59 - apply ButtonFacade to the flyout buttons? Or at least zoom the textures slightly? | |
60 - use a multiplier with SetTexCoord on totem bar texture? | |
61 | |
62 Design Notes: | |
63 - Only the header has a secure context. All other frames execute in its context. | |
64 | |
65 - Each button is either type "spell" (summon/recall) or type "action" (totem action IDs are | |
66 GetBonusBarOffset()=6, 132-144) with 3 pages of 4 buttons. The paging is controlled by | |
67 the summon flyout, which is also paged with the summon spells (the recall button is not paged) | |
68 | |
69 - A spell list is updated in the secure context at setup time (TODO: redo setup when learning new | |
70 spells) with the list of spells known for each slot. | |
71 | |
72 - Each button (except recall) has an arrow button which appears on mouseover and when clicked | |
73 opens the flyout via a wrapped OnClick handler. When the flyout is open, the arrow does not | |
74 appear. | |
75 | |
76 - A single flyout with N+1 (1 slot is to select no totem for the set) flyout-buttons is a child | |
77 of the bar. Each time the flyout panel is opened, the individual buttons grab their corresponding | |
78 spell/type from the list, according to the slot which opened the flyout. Each button either sets | |
79 the current page (summon) or sets a multispell to an actionID via type="multispell". None of them | |
80 actually cast any spells (though, I suppose we could modify this so that e.g. configurable | |
81 right-click casts the spell). The flyout also has a close button which closes the flyout: the | |
82 flyout-open code positions the close button anchored to the last button in the flyout (which | |
83 changes dynamically because each slot has a different number of items in the list). | |
84 | |
85 - Multicast sets are not stances, there's no need (or ability) to handle swapping sets if one of | |
86 the summon spells is cast from elsewhere. | |
87 | |
88 - The default UI has Call of the Elements always selected on UI load. This module remembers the last | |
89 selected one and restores it. | |
90 | |
91 | |
92 ]]-- | |
93 | |
94 | |
95 -- | |
96 -- Secure snippets | |
97 -- | |
98 | |
99 -- bar | |
100 local _bar_init = -- function(self) | |
101 [[ | |
102 -- set up some globals in the secure environment | |
103 flyout = self:GetFrameRef("flyout") | |
104 flyoutSlot = nil | |
105 summonSlot = self:GetAttribute("summonSlot") | |
106 recallSlot = self:GetAttribute("recallSlot") | |
107 baseActionID = self:GetAttribute("baseActionID") | |
108 slotsPerPage = self:GetAttribute("slotsPerPage") | |
109 currentPage = currentPage or self:GetAttribute("lastSummon") or 1 | |
110 | |
111 totemIDsBySlot = newtable() | |
112 for i = 1, slotsPerPage do | |
113 totemIDsBySlot[i] = self:GetAttribute("TOTEM_PRIORITY_"..i) | |
114 end | |
115 | |
116 -- these are set up in bar:SetupBar() | |
117 flyoutChildren = flyoutChildren or newtable() | |
118 summonSpells = summonSpells or newtable() | |
119 ]] | |
120 | |
121 local _onstate_multispellpage = -- function(self, stateid, newstate) | |
122 [[ | |
123 currentPage = tonumber(newstate) | |
124 control:CallMethod("UpdateLastSummon",currentPage) | |
125 control:ChildUpdate() | |
126 ]] | |
127 | |
128 | |
129 -- buttons | |
130 local _childupdate = -- function(self, snippetid, message) | |
131 [[ | |
132 local t = self:GetAttribute("type") | |
133 self:SetAttribute(t, self:GetAttribute(t.."-page"..currentPage)) | |
134 ]] | |
135 | |
136 local _onEnter = -- function(self) | |
137 -- for whatever reason, RegisterAutoHide is unreliable | |
138 -- unless you re-anchor the frame prior to calling it. | |
139 -- Even then, it's still not terribly reliable. | |
140 [[ | |
141 local slot = self:GetAttribute("bar-idx") | |
142 local arrow = owner:GetFrameRef("arrow-"..slot) | |
143 if arrow and not arrow:IsShown() and not (flyout:IsVisible() and flyoutSlot == slot) then | |
144 arrow:ClearAllPoints() | |
145 arrow:SetPoint("BOTTOM",self,"TOP",0,0) | |
146 arrow:Show() | |
147 arrow:RegisterAutoHide(0) | |
148 arrow:AddToAutoHide(self) | |
149 end | |
150 ]] | |
151 | |
152 | |
153 -- flyout arrow | |
154 local _arrow_openFlyout = -- function(self) | |
155 [[ | |
156 local slot = self:GetAttribute("bar-idx") | |
157 local totemID = totemIDsBySlot[slot - (summonSlot or 0)] | |
158 if totemID == 0 then | |
159 totemID = "summon" | |
160 end | |
161 | |
162 local lastButton, lastPage | |
163 for page, b in ipairs(flyoutChildren) do | |
164 b:Hide() | |
165 b:SetAttribute("totemSlot",totemID) | |
166 if slot == summonSlot then | |
167 local spellID = self:GetParent():GetAttribute("spell-page"..page) | |
168 if spellID then | |
169 b:SetAttribute("type","changePage") | |
170 b:SetAttribute("spell",spellID) | |
171 b:Show() | |
172 lastButton = b | |
173 lastPage = page | |
174 end | |
175 else | |
176 local spell = select(page, 0, GetMultiCastTotemSpells(totemID) ) | |
177 if spell then | |
178 b:SetAttribute("type","multispell") | |
179 b:SetAttribute("action", baseActionID + (currentPage - 1)*slotsPerPage + totemID) | |
180 b:SetAttribute("spell", spell) | |
181 b:Show() | |
182 lastButton = b | |
183 lastPage = page | |
184 end | |
185 end | |
186 end | |
187 | |
188 local close = owner:GetFrameRef("close") | |
189 if lastButton and close then | |
190 close:ClearAllPoints() | |
191 close:SetPoint("BOTTOM",lastButton,"TOP",0,0) -- TODO: better anchoring | |
192 close:Show() | |
193 control:CallMethod("UpdateFlyoutTextures",totemID) | |
194 end | |
195 | |
196 flyout:ClearAllPoints() | |
197 flyout:SetPoint("BOTTOM",self,"BOTTOM",0,0) -- TODO: better anchoring | |
198 if lastPage then | |
199 flyout:SetHeight(lastPage * 27 + (close and close:GetHeight() or 0)) | |
200 end | |
201 flyout:Show() | |
202 flyout:RegisterAutoHide(1) -- TODO: configurable | |
203 flyout:AddToAutoHide(owner) | |
204 flyoutSlot = slot | |
205 self:Hide() | |
206 ]] | |
207 | |
208 local _closeFlyout = -- function(self) | |
209 [[ | |
210 flyout:Hide() | |
211 ]] | |
212 | |
213 | |
214 -- flyout child buttons | |
215 local _flyout_child_preClick = -- function(self, button, down) | |
216 [[ | |
217 local button = button | |
218 if self:GetAttribute("type") == "changePage" then | |
219 owner:SetAttribute("state-multispellpage",self:GetAttribute("index")) | |
220 self:GetParent():Hide() | |
221 return false | |
222 else | |
223 return nil, "close" | |
224 end | |
225 ]] | |
226 | |
227 local _flyout_child_postClick = -- function(self, message, button, down) | |
228 [[ | |
229 if message == "close" then | |
230 self:GetParent():Hide() -- hide flyout after selecting | |
231 end | |
232 ]] | |
233 | |
234 | |
235 -- | |
236 -- The Blizzard totem bar textures are all actually one big texture, | |
237 -- with texcoord offsets. Shamelessly stolen from FrameXML/MultiCastActionBarFrame.lua | |
238 -- | |
239 local TOTEM_TEXTURE = "Interface\\Buttons\\UI-TotemBar" | |
240 local FLYOUT_UP_BUTTON_HL_TCOORDS = { 72/128, 92/128, 88/256, 98/256 } | |
241 local FLYOUT_DOWN_BUTTON_HL_TCOORDS = { 72/128, 92/128, 69/256, 79/256 } | |
242 | |
243 local SLOT_EMPTY_TCOORDS = { | |
244 [EARTH_TOTEM_SLOT] = { 66/128, 96/128, 3/256, 33/256 }, | |
245 [FIRE_TOTEM_SLOT] = { 67/128, 97/128, 100/256, 130/256 }, | |
246 [WATER_TOTEM_SLOT] = { 39/128, 69/128, 209/256, 239/256 }, | |
247 [AIR_TOTEM_SLOT] = { 66/128, 96/128, 36/256, 66/256 }, | |
248 } | |
249 | |
250 local SLOT_OVERLAY_TCOORDS = { | |
251 [EARTH_TOTEM_SLOT] = { 1/128, 35/128, 172/256, 206/256 }, | |
252 [FIRE_TOTEM_SLOT] = { 36/128, 70/128, 172/256, 206/256 }, | |
253 [WATER_TOTEM_SLOT] = { 1/128, 35/128, 207/256, 240/256 }, | |
254 [AIR_TOTEM_SLOT] = { 36/128, 70/128, 137/256, 171/256 }, | |
255 } | |
256 | |
257 local FLYOUT_UP_BUTTON_TCOORDS = { | |
258 ["summon"] = { 99/128, 127/128, 84/256, 102/256 }, | |
259 [EARTH_TOTEM_SLOT] = { 99/128, 127/128, 160/256, 178/256 }, | |
260 [FIRE_TOTEM_SLOT] = { 99/128, 127/128, 122/256, 140/256 }, | |
261 [WATER_TOTEM_SLOT] = { 99/128, 127/128, 199/256, 217/256 }, | |
262 [AIR_TOTEM_SLOT] = { 99/128, 127/128, 237/256, 255/256 }, | |
263 } | |
264 | |
265 local FLYOUT_DOWN_BUTTON_TCOORDS = { | |
266 ["summon"] = { 99/128, 127/128, 65/256, 83/256 }, | |
267 [EARTH_TOTEM_SLOT] = { 99/128, 127/128, 141/256, 159/256 }, | |
268 [FIRE_TOTEM_SLOT] = { 99/128, 127/128, 103/256, 121/256 }, | |
269 [WATER_TOTEM_SLOT] = { 99/128, 127/128, 180/256, 198/256 }, | |
270 [AIR_TOTEM_SLOT] = { 99/128, 127/128, 218/256, 236/256 }, | |
271 } | |
272 | |
273 local FLYOUT_TOP_TCOORDS = { | |
274 ["summon"] = { 33/128, 65/128, 1/256, 23/256 }, | |
275 [EARTH_TOTEM_SLOT] = { 0/128, 32/128, 46/256, 68/256 }, | |
276 [FIRE_TOTEM_SLOT] = { 33/128, 65/128, 46/256, 68/256 }, | |
277 [WATER_TOTEM_SLOT] = { 0/128, 32/128, 1/256, 23/256 }, | |
278 [AIR_TOTEM_SLOT] = { 0/128, 32/128, 91/256, 113/256 }, | |
279 } | |
280 | |
281 local FLYOUT_MIDDLE_TCOORDS = { | |
282 ["summon"] = { 33/128, 65/128, 23/256, 43/256 }, | |
283 [EARTH_TOTEM_SLOT] = { 0/128, 32/128, 68/256, 88/256 }, | |
284 [FIRE_TOTEM_SLOT] = { 33/128, 65/128, 68/256, 88/256 }, | |
285 [WATER_TOTEM_SLOT] = { 0/128, 32/128, 23/256, 43/256 }, | |
286 [AIR_TOTEM_SLOT] = { 0/128, 32/128, 113/256, 133/256 }, | |
287 } | |
288 | |
289 local eventList = { | |
290 "ACTIONBAR_SLOT_CHANGED", | |
291 "ACTIONBAR_UPDATE_STATE", | |
292 "ACTIONBAR_UPDATE_USABLE", | |
293 "ACTIONBAR_UPDATE_COOLDOWN", | |
294 "UPDATE_BINDINGS", | |
295 "UPDATE_MULTI_CAST_ACTIONBAR", | |
296 } | |
297 | |
298 -- | |
299 -- MultiCast Button class | |
300 -- Inherits implementation methods from Action button class, but circumvents the constructor | |
301 -- and redefines/removes some methods. | |
302 -- | |
303 local buttonTypeID = "Totem" | |
304 local Super = ReAction.Button | |
305 local Action = ReAction.Button.Action | |
306 local MultiCast = setmetatable( | |
307 { | |
308 defaultBarConfig = { | |
309 type = buttonTypeID, | |
310 btnWidth = 36, | |
311 btnHeight = 36, | |
312 btnRows = 1, | |
313 btnColumns = 6, | |
314 spacing = 3, | |
315 buttons = { } | |
316 }, | |
317 | |
318 barType = L["Totem Bar"], | |
319 buttonTypeID = buttonTypeID | |
320 }, | |
321 { __index = Action } ) | |
322 | |
323 ReAction.Button.MultiCast = MultiCast | |
324 ReAction:RegisterBarType(MultiCast) | |
325 | |
326 function MultiCast:New( btnConfig, bar, idx ) | |
327 local maxIndex = bar.nTotemSlots or 0 | |
328 if bar.summonSlot then | |
329 maxIndex = maxIndex + 1 | |
330 end | |
331 if bar.recallSlot then | |
332 maxIndex = maxIndex + 1 | |
333 end | |
334 | |
335 if not bar.hasMulticast or idx > maxIndex then | |
336 return false | |
337 end | |
338 | |
339 if idx < 1 then | |
340 error("invalid index") | |
341 end | |
342 | |
343 local name = format("ReAction_%s_Totem_%d",bar:GetName(),idx) | |
344 | |
345 self = Super.New(self, name, btnConfig, bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" ) | |
346 | |
347 local barFrame = bar:GetFrame() | |
348 local f = self:GetFrame() | |
349 | |
350 -- attributes | |
351 local page = (idx == bar.recallSlot) and 1 or bar:GetConfig().lastSummon or 1 | |
352 if idx == bar.recallSlot or idx == bar.summonSlot then | |
353 f:SetAttribute("type","spell") | |
354 local spells = (idx == bar.summonSlot) and TOTEM_MULTI_CAST_SUMMON_SPELLS or TOTEM_MULTI_CAST_RECALL_SPELLS | |
355 f:SetAttribute("spell",spells[page]) | |
356 for i, spell in ipairs(spells) do | |
357 if spell and IsSpellKnown(spell) then | |
358 f:SetAttribute("spell-page"..i, spell) | |
359 end | |
360 end | |
361 else | |
362 local offset = bar.summonSlot and 1 or 0 | |
363 local slot = SHAMAN_TOTEM_PRIORITIES[idx - offset] | |
364 local baseAction = barFrame:GetAttribute("baseActionID") + slot | |
365 self.totemSlot = slot | |
366 f:SetAttribute("type","action") | |
367 f:SetAttribute("action", baseAction + (page - 1) * NUM_MULTI_CAST_BUTTONS_PER_PAGE) | |
368 for i = 1, NUM_MULTI_CAST_PAGES do | |
369 f:SetAttribute("action-page"..i, baseAction + (i-1) * NUM_MULTI_CAST_BUTTONS_PER_PAGE) | |
370 end | |
371 if not f.overlayTex then | |
372 local tx = f:CreateTexture("OVERLAY") | |
373 tx:SetTexture(TOTEM_TEXTURE) | |
374 tx:SetTexCoord(unpack(SLOT_OVERLAY_TCOORDS[self.totemSlot])) | |
375 tx:SetWidth(34) | |
376 tx:SetHeight(34) | |
377 tx:SetPoint("CENTER") | |
378 tx:Show() | |
379 f.overlayTex = tx | |
380 end | |
381 end | |
382 f:SetAttribute("bar-idx",idx) | |
383 | |
384 -- non secure scripts | |
385 f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end) | |
386 f:SetScript("OnEnter", function(frame) self:OnEnter() end) | |
387 f:SetScript("OnLeave", function(frame) self:OnLeave() end) | |
388 f:SetScript("OnAttributeChanged", function(frame, attr, value) self:OnAttributeChanged(attr, value) end) | |
389 f:SetScript("PostClick", function(frame, ...) self:PostClick(...) end) | |
390 | |
391 -- secure handlers | |
392 if idx ~= bar.recallSlot then | |
393 f:SetAttribute("_childupdate",_childupdate) | |
394 end | |
395 barFrame:WrapScript(f, "OnEnter", _onEnter) | |
396 | |
397 -- event registration | |
398 f:EnableMouse(true) | |
399 f:RegisterForClicks("AnyUp") | |
400 for _, evt in pairs(eventList) do | |
401 f:RegisterEvent(evt) | |
402 end | |
403 | |
404 -- Set up a proxy for the icon texture for use with ButtonFacade | |
405 local SetTexCoordRaw = self.frames.icon.SetTexCoord | |
406 self.frames.icon.SetTexCoord = function( tx, ... ) | |
407 if self:GetIconTexture() == TOTEM_TEXTURE then | |
408 SetTexCoordRaw(tx,select(2,self:GetIconTexture())) | |
409 else | |
410 SetTexCoordRaw(tx,...) | |
411 end | |
412 end | |
413 | |
414 -- attach to skinner | |
415 bar:SkinButton(self) | |
416 | |
417 f:Show() | |
418 | |
419 -- open arrow and flyout background textures | |
420 if idx ~= bar.recallSlot then | |
421 local arrow = f._arrowFrame or CreateFrame("Button", nil, f, "SecureFrameTemplate") | |
422 f._arrowFrame = arrow | |
423 arrow:SetWidth(28) | |
424 arrow:SetHeight(18) | |
425 arrow:SetPoint("BOTTOM",self:GetFrame(),"TOP",0,0) -- TODO: better anchoring | |
426 arrow:SetNormalTexture(TOTEM_TEXTURE) | |
427 local slot = self.totemSlot or "summon" | |
428 arrow:GetNormalTexture():SetTexCoord( unpack(FLYOUT_UP_BUTTON_TCOORDS[slot]) ) | |
429 arrow:SetHighlightTexture(TOTEM_TEXTURE) | |
430 arrow:GetHighlightTexture():SetTexCoord( unpack(FLYOUT_UP_BUTTON_HL_TCOORDS) ) | |
431 arrow:SetAttribute("bar-idx",idx) | |
432 arrow:Hide() | |
433 barFrame:WrapScript(arrow, "OnClick", _arrow_openFlyout) | |
434 barFrame:SetFrameRef("arrow-"..idx,arrow) | |
435 end | |
436 | |
437 self:Refresh() | |
438 | |
439 return self | |
440 end | |
441 | |
442 function MultiCast:Destroy() | |
443 local barFrame = self.bar:GetFrame() | |
444 local f = self:GetFrame() | |
445 pcall( barFrame.UnwrapScript, barFrame, f, "OnEnter" ) -- ignore errors | |
446 if f._arrowFrame then | |
447 pcall( barFrame.UnwrapScript, barFrame, f._arrowFrame,"OnClick" ) -- ignore errors | |
448 end | |
449 Super.Destroy(self) | |
450 end | |
451 | |
452 function MultiCast:Refresh() | |
453 Super.Refresh(self) | |
454 self:UpdateAction() | |
455 end | |
456 | |
457 function MultiCast:ShowGrid( show ) | |
458 end | |
459 | |
460 function MultiCast:ShowGridTemp( show ) | |
461 end | |
462 | |
463 function MultiCast:AcquireActionID() | |
464 end | |
465 | |
466 function MultiCast:ReleaseActionID() | |
467 end | |
468 | |
469 function MultiCast:UpdateShowGrid() | |
470 end | |
471 | |
472 function MultiCast:UpdateBorder() | |
473 end | |
474 | |
475 function MultiCast:UpdateMacroText() | |
476 end | |
477 | |
478 function MultiCast:UpdateCount() | |
479 end | |
480 | |
481 function MultiCast:UpdateCheckedState() | |
482 local action = self:GetActionID() | |
483 if action and IsCurrentAction(action) then | |
484 self:GetFrame():SetChecked(1) | |
485 else | |
486 self:GetFrame():SetChecked(0) | |
487 end | |
488 end | |
489 | |
490 function MultiCast:RefreshHasActionAttributes() | |
491 end | |
492 | |
493 function MultiCast:UpdateFlash() | |
494 end | |
495 | |
496 function MultiCast:GetIconTexture() | |
497 local tx | |
498 if self.spellID then | |
499 tx = GetSpellTexture(GetSpellInfo(self.spellID)) | |
500 elseif self.actionID then | |
501 tx = GetActionTexture(self.actionID) | |
502 end | |
503 if tx then | |
504 return tx | |
505 else | |
506 return TOTEM_TEXTURE, unpack(SLOT_EMPTY_TCOORDS[self.totemSlot or 1]) | |
507 end | |
508 end | |
509 | |
510 function MultiCast:UpdateAction() | |
511 local action = self:GetActionID() | |
512 if action then | |
513 if action ~= self.actionID then | |
514 self.actionID = action | |
515 self:UpdateAll() | |
516 end | |
517 else | |
518 local spellID = self:GetSpellID() | |
519 if spellID ~= self.spellID then | |
520 self.spellID = spellID | |
521 self:UpdateAll() | |
522 end | |
523 end | |
524 end | |
525 | |
526 function MultiCast:GetActionID(page) | |
527 return self:GetFrame():GetAttribute("action") | |
528 end | |
529 | |
530 function MultiCast:GetSpellID(page) | |
531 return self:GetFrame():GetAttribute("spell") | |
532 end | |
533 | |
534 function MultiCast:SetActionID( id ) | |
535 error("Can not set action ID of multicast buttons") | |
536 end | |
537 | |
538 function MultiCast:SetTooltip() | |
539 local barFrame = self:GetFrame() | |
540 if GetCVar("UberTooltips") == "1" then | |
541 GameTooltip_SetDefaultAnchor(GameTooltip, barFrame) | |
542 else | |
543 GameTooltip:SetOwner(barFrame) | |
544 end | |
545 if self.spellID then | |
546 GameTooltip:SetSpellByID(self.spellID,false,true) | |
547 elseif self.actionID then | |
548 GameTooltip:SetAction(self.actionID) | |
549 end | |
550 end | |
551 | |
552 function MultiCast:GetUsable() | |
553 if self.spellID then | |
554 return IsUsableSpell((GetSpellInfo(self.spellID))) | |
555 elseif self.actionID then | |
556 return IsUsableAction(self.actionID) | |
557 end | |
558 end | |
559 | |
560 function MultiCast:GetInRange() | |
561 if self.spellID then | |
562 return IsSpellInRange((GetSpellInfo(self.spellID))) == 0 | |
563 elseif self.actionID then | |
564 return IsActionInRange(self.actionID) == 0 | |
565 end | |
566 end | |
567 | |
568 function MultiCast:GetCooldown() | |
569 if self.spellID then | |
570 return GetSpellCooldown((GetSpellInfo(self.spellID))) | |
571 elseif self.actionID then | |
572 return GetActionCooldown(self.actionID) | |
573 else | |
574 return 0, 0, 0 | |
575 end | |
576 end | |
577 | |
578 function MultiCast:UPDATE_MULTI_CAST_ACTIONBAR() | |
579 self:UpdateAll() | |
580 end | |
581 | |
582 | |
583 -- | |
584 -- flyout setup | |
585 -- | |
586 local function ShowFlyoutTooltip(frame) | |
587 if GetCVar("UberTooltips") == "1" then | |
588 GameTooltip_SetDefaultAnchor(GameTooltip, frame) | |
589 else | |
590 GameTooltip:SetOwner(frame) | |
591 end | |
592 local spell = frame:GetAttribute("spell") | |
593 if spell == nil or spell == 0 then | |
594 GameTooltip:SetText(MULTI_CAST_TOOLTIP_NO_TOTEM, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b) | |
595 else | |
596 GameTooltip:SetSpellByID(spell,false,true) | |
597 end | |
598 end | |
599 | |
600 local function HideFlyoutTooltip() | |
601 GameTooltip:Hide() | |
602 end | |
603 | |
604 local function UpdateFlyoutIcon(frame) | |
605 local spellID = frame:GetAttribute("spell") | |
606 if spellID == 0 or spellID == nil then | |
607 frame.icon:SetTexture(TOTEM_TEXTURE) | |
608 local slot = tonumber(frame:GetAttribute("totemSlot")) or 1 | |
609 frame.icon:SetTexCoord( unpack(SLOT_EMPTY_TCOORDS[slot]) ) | |
610 else | |
611 frame.icon:SetTexture(GetSpellTexture(GetSpellInfo(spellID))) | |
612 frame.icon:SetTexCoord(0,1,0,1) | |
613 end | |
614 end | |
615 | |
616 function MultiCast:SetupBar( bar ) | |
617 Super.SetupBar(self,bar) | |
618 | |
619 local slot = 0 | |
620 local nTotemSlots = 0 | |
621 local summonSlot = nil | |
622 local recallSlot = nil | |
623 | |
624 -- figure out the capabilities of the character | |
625 for i, spell in ipairs(TOTEM_MULTI_CAST_SUMMON_SPELLS) do | |
626 if spell and IsSpellKnown(spell) then | |
627 slot = 1 | |
628 summonSlot = 1 | |
629 end | |
630 end | |
631 | |
632 for i = 1, NUM_MULTI_CAST_BUTTONS_PER_PAGE do | |
633 local totem = SHAMAN_TOTEM_PRIORITIES[i]; | |
634 if GetTotemInfo(totem) and GetMultiCastTotemSpells(totem) then | |
635 nTotemSlots = nTotemSlots + 1 | |
636 slot = slot + 1 | |
637 end | |
638 end | |
639 | |
640 slot = slot + 1 | |
641 for i, spell in ipairs(TOTEM_MULTI_CAST_RECALL_SPELLS) do | |
642 if spell and IsSpellKnown(spell) then | |
643 recallSlot = slot | |
644 end | |
645 end | |
646 | |
647 if nTotemSlots == 0 then | |
648 bar.hasMulticast = false -- no multicast capability | |
649 return | |
650 end | |
651 | |
652 bar.hasMulticast = true | |
653 bar.summonSlot = summonSlot | |
654 bar.recallSlot = recallSlot | |
655 bar.nTotemSlots = nTotemSlots | |
656 | |
657 | |
658 local f = bar:GetFrame() | |
659 | |
660 -- init bar secure environment | |
661 f:SetAttribute("lastSummon", bar:GetConfig().lastSummon) | |
662 f:SetAttribute("summonSlot", summonSlot) | |
663 f:SetAttribute("recallSlot", recallSlot) | |
664 f:SetAttribute("slotsPerPage", NUM_MULTI_CAST_BUTTONS_PER_PAGE) | |
665 f:SetAttribute("baseActionID", (NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset() - 1)*NUM_ACTIONBAR_BUTTONS) | |
666 for i, p in ipairs(SHAMAN_TOTEM_PRIORITIES) do | |
667 f:SetAttribute("TOTEM_PRIORITY_"..i,p) | |
668 end | |
669 f:SetAttribute("_onstate-multispellpage", _onstate_multispellpage) | |
670 | |
671 function f:UpdateLastSummon(value) | |
672 bar:GetConfig().lastSummon = value | |
673 end | |
674 | |
675 -- create flyout container frame and close arrow | |
676 local flyout = bar._flyoutFrame | |
677 if not flyout then | |
678 flyout = CreateFrame("Frame", nil, f, "SecureFrameTemplate") | |
679 bar._flyoutFrame = flyout | |
680 f:SetFrameRef("flyout",flyout) | |
681 flyout.buttons = { } | |
682 flyout:Hide() | |
683 flyout:SetWidth(24) | |
684 flyout:SetHeight(1) | |
685 flyout:SetPoint("BOTTOM",f,"TOP",0,0) | |
686 | |
687 local close = CreateFrame("Button", nil, flyout, "SecureFrameTemplate") | |
688 close:SetWidth(28) | |
689 close:SetHeight(18) | |
690 close:SetPoint("BOTTOM",flyout,"TOP") | |
691 close:SetNormalTexture(TOTEM_TEXTURE) | |
692 close:GetNormalTexture():SetTexCoord(unpack(FLYOUT_DOWN_BUTTON_TCOORDS["summon"])) | |
693 close:SetHighlightTexture(TOTEM_TEXTURE) | |
694 close:GetHighlightTexture():SetTexCoord( unpack(FLYOUT_DOWN_BUTTON_HL_TCOORDS) ) | |
695 f:SetFrameRef("close",close) | |
696 f:WrapScript(close, "OnClick", _closeFlyout) | |
697 close:Show() | |
698 | |
699 local midTx = flyout:CreateTexture("BACKGROUND") | |
700 midTx:SetWidth(32) | |
701 midTx:SetHeight(20) | |
702 midTx:SetPoint("BOTTOM") | |
703 midTx:SetTexture(TOTEM_TEXTURE) | |
704 midTx:SetTexCoord(unpack(FLYOUT_MIDDLE_TCOORDS["summon"])) | |
705 midTx:Show() | |
706 | |
707 local topTx = flyout:CreateTexture("BACKGROUND") | |
708 topTx:SetWidth(32) | |
709 topTx:SetHeight(20) | |
710 topTx:SetTexture(TOTEM_TEXTURE) | |
711 midTx:SetTexCoord(unpack(FLYOUT_TOP_TCOORDS["summon"])) | |
712 topTx:SetPoint("BOTTOM",midTx,"TOP",0,-10) | |
713 topTx:Show() | |
714 | |
715 function flyout:UpdateTextures(slot) | |
716 slot = slot or "summon" | |
717 close:GetNormalTexture():SetTexCoord(unpack(FLYOUT_DOWN_BUTTON_TCOORDS[slot])) | |
718 midTx:ClearAllPoints() | |
719 midTx:SetPoint("BOTTOM") | |
720 midTx:SetPoint("TOP",close,"BOTTOM",0,0) | |
721 midTx:SetTexCoord(unpack(FLYOUT_MIDDLE_TCOORDS[slot])) | |
722 topTx:SetTexCoord(unpack(FLYOUT_TOP_TCOORDS[slot])) | |
723 end | |
724 | |
725 -- create flyout buttons | |
726 for i = 1, 10 do -- maximum 9 spells + 1 empty slot | |
727 local b = CreateFrame("Button",nil,flyout,"SecureActionButtonTemplate") | |
728 b:SetWidth(24) | |
729 b:SetHeight(24) | |
730 local prev = flyout.buttons[i-1] | |
731 b:SetPoint("BOTTOM", prev or flyout, prev and "TOP" or "BOTTOM", 0, 3) -- TODO: better anchoring | |
732 b.icon = b:CreateTexture("BACKGROUND") | |
733 b.icon:SetAllPoints() | |
734 b.icon:Show() | |
735 b:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square") | |
736 b:GetHighlightTexture():SetBlendMode("ADD") | |
737 b:RegisterForClicks("AnyUp") | |
738 b:SetScript("OnShow",UpdateFlyoutIcon) | |
739 b:SetScript("OnEnter",ShowFlyoutTooltip) | |
740 b:SetScript("OnLeave",HideFlyoutTooltip) | |
741 b:SetAttribute("index",i) | |
742 f:SetAttribute("flyout-child-idx",i) | |
743 f:SetFrameRef("flyout-child",b) | |
744 f:Execute([[ | |
745 flyoutChildren = flyoutChildren or newtable() | |
746 flyoutChildren[self:GetAttribute("flyout-child-idx")] = self:GetFrameRef("flyout-child") | |
747 ]]) | |
748 f:WrapScript(b, "OnClick", _flyout_child_preClick, _flyout_child_postClick) | |
749 b:Show() | |
750 flyout.buttons[i] = b | |
751 end | |
752 end | |
753 | |
754 -- scale flyout frame | |
755 local scale = bar:GetButtonSize() / 36 | |
756 flyout:SetScale(scale) | |
757 | |
758 function f:UpdateFlyoutTextures(slot) | |
759 flyout:UpdateTextures(slot) | |
760 end | |
761 | |
762 -- re-execute setup when new spells are loaded | |
763 if not f.events_registered then | |
764 f:RegisterEvent("UPDATE_MULTI_CAST_ACTIONBAR") | |
765 f:RegisterEvent("PLAYER_ENTERING_WORLD") | |
766 -- Bar.frame does not use OnEvent | |
767 f:SetScript("OnEvent", | |
768 function() | |
769 if not InCombatLockdown() then | |
770 self:SetupBar(bar) | |
771 end | |
772 end) | |
773 f.events_registered = true | |
774 end | |
775 | |
776 | |
777 f:Execute(_bar_init) | |
778 end | |
779 |