Mercurial > wow > reaction
comparison MultiCastButton.lua @ 257:920d17851a93 stable
Merge 1.1 beta 4 to stable
| author | Flick |
|---|---|
| date | Tue, 12 Apr 2011 16:06:31 -0700 |
| parents | 65f2805957a0 |
| children | c918ff9ac787 |
comparison
equal
deleted
inserted
replaced
| 210:b2b105747466 | 257:920d17851a93 |
|---|---|
| 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 |
