Flick@276: local _, ns = ... Flick@276: local ReAction = ns.ReAction flickerstreak@161: local L = ReAction.L flickerstreak@161: local _G = _G flickerstreak@161: local CreateFrame = CreateFrame flickerstreak@161: local format = string.format flickerstreak@161: local unpack = unpack flickerstreak@161: local GetCVar = GetCVar flickerstreak@161: local GameTooltip_SetDefaultAnchor = GameTooltip_SetDefaultAnchor flickerstreak@161: local CooldownFrame_SetTimer = CooldownFrame_SetTimer flickerstreak@161: local InCombatLockdown = InCombatLockdown flickerstreak@161: local IsUsableSpell = IsUsableSpell flickerstreak@161: local IsUsableAction = IsUsableAction flickerstreak@161: local IsSpellKnown = IsSpellKnown flickerstreak@161: local IsSpellInRange = IsSpellInRange flickerstreak@161: local IsActionInRange = IsActionInRange flickerstreak@161: local GetSpellInfo = GetSpellInfo flickerstreak@161: local GetSpellCooldown = GetSpellCooldown flickerstreak@161: local GetActionCooldown = GetActionCooldown flickerstreak@161: local GetSpellTexture = GetSpellTexture flickerstreak@161: local GetActionTexture = GetActionTexture flickerstreak@161: local GetMultiCastTotemSpells = GetMultiCastTotemSpells flickerstreak@161: Flick@276: --@do-not-package@ flickerstreak@161: --[[ flickerstreak@161: Blizzard Constants: flickerstreak@161: - NUM_MULTI_CAST_BUTTONS_PER_PAGE = 4 flickerstreak@161: - NUM_MULTI_CAST_PAGES = 3 flickerstreak@166: - SHAMAN_TOTEM_PRIORITIES = { } -- sets the order of the totems flickerstreak@161: - TOTEM_MULTI_CAST_SUMMON_SPELLS = { } -- list of summon spellIDs flickerstreak@161: - TOTEM_MULTI_CAST_RECALL_SPELLS = { } -- list of recall spellIDs flickerstreak@161: flickerstreak@161: Blizzard Events: flickerstreak@161: - UPDATE_MULTI_CAST_ACTIONBAR flickerstreak@161: flickerstreak@161: Blizzard APIs: flickerstreak@161: - GetMultiCastBarOffset() : returns 6 flickerstreak@161: flickerstreak@161: - SetMultiCastSpell(actionID, spellID) (protected) OR flickerstreak@161: SetAttribute("type","multispell") flickerstreak@161: SetAttribute("action",actionID) flickerstreak@161: SetAttribute("spell",spellID) flickerstreak@161: flickerstreak@161: note: multicast actionID page is NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset(), flickerstreak@161: so that's action ID 132-144. flickerstreak@161: flickerstreak@161: - spell1, spell2, spell3, ... = GetMultiCastTotemSpells(slot) flickerstreak@167: returns spellIDs for all known totems that fit that slot. This function is available in flickerstreak@167: the secure environment. flickerstreak@161: flickerstreak@161: Blizzard textures: flickerstreak@161: All the textures for the multicast bar (arrows, empty-slot icons, etc) are part of a single flickerstreak@161: texture: each texture uses SetTexCoord() to display only a slice of the textures. I suppose flickerstreak@161: this is to slightly optimize texture load performance, but it makes the UI code more clumsy. flickerstreak@161: flickerstreak@161: Each totem button and arrow has a colored border indicating its elemental type. flickerstreak@174: flickerstreak@174: TODO: flickerstreak@174: - make whether to show the colored border configurable (looks really bad with ButtonFacade:Zoomed) flickerstreak@174: - apply ButtonFacade to the flyout buttons? Or at least zoom the textures slightly? flickerstreak@174: - use a multiplier with SetTexCoord on totem bar texture? flickerstreak@161: flickerstreak@161: Design Notes: flickerstreak@161: - Only the header has a secure context. All other frames execute in its context. flickerstreak@161: flickerstreak@161: - Each button is either type "spell" (summon/recall) or type "action" (totem action IDs are flickerstreak@161: GetBonusBarOffset()=6, 132-144) with 3 pages of 4 buttons. The paging is controlled by flickerstreak@161: the summon flyout, which is also paged with the summon spells (the recall button is not paged) flickerstreak@161: flickerstreak@161: - A spell list is updated in the secure context at setup time (TODO: redo setup when learning new flickerstreak@161: spells) with the list of spells known for each slot. flickerstreak@161: flickerstreak@161: - Each button (except recall) has an arrow button which appears on mouseover and when clicked flickerstreak@161: opens the flyout via a wrapped OnClick handler. When the flyout is open, the arrow does not flickerstreak@161: appear. flickerstreak@161: flickerstreak@161: - A single flyout with N+1 (1 slot is to select no totem for the set) flyout-buttons is a child flickerstreak@161: of the bar. Each time the flyout panel is opened, the individual buttons grab their corresponding flickerstreak@161: spell/type from the list, according to the slot which opened the flyout. Each button either sets flickerstreak@161: the current page (summon) or sets a multispell to an actionID via type="multispell". None of them flickerstreak@161: actually cast any spells (though, I suppose we could modify this so that e.g. configurable flickerstreak@161: right-click casts the spell). The flyout also has a close button which closes the flyout: the flickerstreak@161: flyout-open code positions the close button anchored to the last button in the flyout (which flickerstreak@161: changes dynamically because each slot has a different number of items in the list). flickerstreak@161: flickerstreak@161: - Multicast sets are not stances, there's no need (or ability) to handle swapping sets if one of flickerstreak@167: the summon spells is cast from elsewhere. flickerstreak@167: flickerstreak@167: - The default UI has Call of the Elements always selected on UI load. This module remembers the last flickerstreak@167: selected one and restores it. flickerstreak@161: flickerstreak@161: flickerstreak@161: ]]-- Flick@276: --@end-do-not-package@ flickerstreak@161: flickerstreak@161: -- flickerstreak@161: -- Secure snippets flickerstreak@161: -- flickerstreak@161: flickerstreak@161: -- bar flickerstreak@161: local _bar_init = -- function(self) flickerstreak@161: [[ flickerstreak@161: -- set up some globals in the secure environment flickerstreak@161: flyout = self:GetFrameRef("flyout") flickerstreak@167: flyoutSlot = nil flickerstreak@167: summonSlot = self:GetAttribute("summonSlot") flickerstreak@167: recallSlot = self:GetAttribute("recallSlot") flickerstreak@161: baseActionID = self:GetAttribute("baseActionID") flickerstreak@167: slotsPerPage = self:GetAttribute("slotsPerPage") flickerstreak@167: currentPage = currentPage or self:GetAttribute("lastSummon") or 1 flickerstreak@167: flickerstreak@167: totemIDsBySlot = newtable() flickerstreak@167: for i = 1, slotsPerPage do flickerstreak@167: totemIDsBySlot[i] = self:GetAttribute("TOTEM_PRIORITY_"..i) flickerstreak@161: end flickerstreak@167: Flick@234: -- these are set up in bar:SetupBar() flickerstreak@168: flyoutChildren = flyoutChildren or newtable() flickerstreak@168: summonSpells = summonSpells or newtable() flickerstreak@161: ]] flickerstreak@161: flickerstreak@161: local _onstate_multispellpage = -- function(self, stateid, newstate) flickerstreak@161: [[ flickerstreak@167: currentPage = tonumber(newstate) flickerstreak@167: control:CallMethod("UpdateLastSummon",currentPage) flickerstreak@161: control:ChildUpdate() flickerstreak@161: ]] flickerstreak@161: flickerstreak@161: flickerstreak@161: -- buttons flickerstreak@161: local _childupdate = -- function(self, snippetid, message) flickerstreak@161: [[ flickerstreak@167: local t = self:GetAttribute("type") flickerstreak@167: self:SetAttribute(t, self:GetAttribute(t.."-page"..currentPage)) flickerstreak@161: ]] flickerstreak@161: flickerstreak@161: local _onEnter = -- function(self) flickerstreak@161: -- for whatever reason, RegisterAutoHide is unreliable flickerstreak@161: -- unless you re-anchor the frame prior to calling it. flickerstreak@161: -- Even then, it's still not terribly reliable. flickerstreak@161: [[ flickerstreak@167: local slot = self:GetAttribute("bar-idx") flickerstreak@167: local arrow = owner:GetFrameRef("arrow-"..slot) flickerstreak@167: if arrow and not arrow:IsShown() and not (flyout:IsVisible() and flyoutSlot == slot) then flickerstreak@167: arrow:ClearAllPoints() flickerstreak@167: arrow:SetPoint("BOTTOM",self,"TOP",0,0) flickerstreak@167: arrow:Show() flickerstreak@161: arrow:RegisterAutoHide(0) flickerstreak@161: arrow:AddToAutoHide(self) flickerstreak@161: end flickerstreak@161: ]] flickerstreak@161: flickerstreak@161: flickerstreak@161: -- flyout arrow flickerstreak@161: local _arrow_openFlyout = -- function(self) flickerstreak@161: [[ flickerstreak@167: local slot = self:GetAttribute("bar-idx") flickerstreak@174: local totemID = totemIDsBySlot[slot - (summonSlot or 0)] flickerstreak@174: if totemID == 0 then flickerstreak@174: totemID = "summon" flickerstreak@174: end flickerstreak@174: flickerstreak@167: local lastButton, lastPage flickerstreak@167: for page, b in ipairs(flyoutChildren) do flickerstreak@167: b:Hide() flickerstreak@174: b:SetAttribute("totemSlot",totemID) flickerstreak@167: if slot == summonSlot then flickerstreak@167: local spellID = self:GetParent():GetAttribute("spell-page"..page) flickerstreak@167: if spellID then flickerstreak@161: b:SetAttribute("type","changePage") flickerstreak@167: b:SetAttribute("spell",spellID) flickerstreak@167: b:Show() flickerstreak@167: lastButton = b flickerstreak@167: lastPage = page flickerstreak@167: end flickerstreak@167: else flickerstreak@168: local spell = select(page, 0, GetMultiCastTotemSpells(totemID) ) flickerstreak@168: if spell then flickerstreak@161: b:SetAttribute("type","multispell") flickerstreak@167: b:SetAttribute("action", baseActionID + (currentPage - 1)*slotsPerPage + totemID) flickerstreak@168: b:SetAttribute("spell", spell) flickerstreak@167: b:Show() flickerstreak@167: lastButton = b flickerstreak@167: lastPage = page flickerstreak@161: end flickerstreak@161: end flickerstreak@161: end flickerstreak@161: flickerstreak@161: local close = owner:GetFrameRef("close") flickerstreak@161: if lastButton and close then flickerstreak@161: close:ClearAllPoints() flickerstreak@161: close:SetPoint("BOTTOM",lastButton,"TOP",0,0) -- TODO: better anchoring flickerstreak@161: close:Show() flickerstreak@174: control:CallMethod("UpdateFlyoutTextures",totemID) flickerstreak@161: end flickerstreak@161: flickerstreak@161: flyout:ClearAllPoints() flickerstreak@161: flyout:SetPoint("BOTTOM",self,"BOTTOM",0,0) -- TODO: better anchoring flickerstreak@167: if lastPage then flickerstreak@167: flyout:SetHeight(lastPage * 27 + (close and close:GetHeight() or 0)) flickerstreak@161: end flickerstreak@161: flyout:Show() flickerstreak@161: flyout:RegisterAutoHide(1) -- TODO: configurable flickerstreak@161: flyout:AddToAutoHide(owner) flickerstreak@167: flyoutSlot = slot flickerstreak@161: self:Hide() flickerstreak@161: ]] flickerstreak@161: flickerstreak@161: local _closeFlyout = -- function(self) flickerstreak@161: [[ flickerstreak@161: flyout:Hide() flickerstreak@161: ]] flickerstreak@161: flickerstreak@161: flickerstreak@161: -- flyout child buttons flickerstreak@161: local _flyout_child_preClick = -- function(self, button, down) flickerstreak@161: [[ flickerstreak@161: local button = button flickerstreak@161: if self:GetAttribute("type") == "changePage" then flickerstreak@161: owner:SetAttribute("state-multispellpage",self:GetAttribute("index")) flickerstreak@161: self:GetParent():Hide() flickerstreak@161: return false flickerstreak@161: else flickerstreak@161: return nil, "close" flickerstreak@161: end flickerstreak@161: ]] flickerstreak@161: flickerstreak@161: local _flyout_child_postClick = -- function(self, message, button, down) flickerstreak@161: [[ flickerstreak@161: if message == "close" then flickerstreak@161: self:GetParent():Hide() -- hide flyout after selecting flickerstreak@161: end flickerstreak@161: ]] flickerstreak@161: flickerstreak@161: flickerstreak@161: -- flickerstreak@161: -- The Blizzard totem bar textures are all actually one big texture, flickerstreak@174: -- with texcoord offsets. Shamelessly stolen from FrameXML/MultiCastActionBarFrame.lua flickerstreak@161: -- flickerstreak@161: local TOTEM_TEXTURE = "Interface\\Buttons\\UI-TotemBar" flickerstreak@161: local FLYOUT_UP_BUTTON_HL_TCOORDS = { 72/128, 92/128, 88/256, 98/256 } flickerstreak@161: local FLYOUT_DOWN_BUTTON_HL_TCOORDS = { 72/128, 92/128, 69/256, 79/256 } flickerstreak@174: flickerstreak@174: local SLOT_EMPTY_TCOORDS = { flickerstreak@174: [EARTH_TOTEM_SLOT] = { 66/128, 96/128, 3/256, 33/256 }, flickerstreak@174: [FIRE_TOTEM_SLOT] = { 67/128, 97/128, 100/256, 130/256 }, flickerstreak@174: [WATER_TOTEM_SLOT] = { 39/128, 69/128, 209/256, 239/256 }, flickerstreak@174: [AIR_TOTEM_SLOT] = { 66/128, 96/128, 36/256, 66/256 }, flickerstreak@174: } flickerstreak@174: flickerstreak@174: local SLOT_OVERLAY_TCOORDS = { flickerstreak@174: [EARTH_TOTEM_SLOT] = { 1/128, 35/128, 172/256, 206/256 }, flickerstreak@174: [FIRE_TOTEM_SLOT] = { 36/128, 70/128, 172/256, 206/256 }, flickerstreak@174: [WATER_TOTEM_SLOT] = { 1/128, 35/128, 207/256, 240/256 }, flickerstreak@174: [AIR_TOTEM_SLOT] = { 36/128, 70/128, 137/256, 171/256 }, flickerstreak@174: } flickerstreak@174: flickerstreak@174: local FLYOUT_UP_BUTTON_TCOORDS = { flickerstreak@174: ["summon"] = { 99/128, 127/128, 84/256, 102/256 }, flickerstreak@174: [EARTH_TOTEM_SLOT] = { 99/128, 127/128, 160/256, 178/256 }, flickerstreak@174: [FIRE_TOTEM_SLOT] = { 99/128, 127/128, 122/256, 140/256 }, flickerstreak@174: [WATER_TOTEM_SLOT] = { 99/128, 127/128, 199/256, 217/256 }, flickerstreak@174: [AIR_TOTEM_SLOT] = { 99/128, 127/128, 237/256, 255/256 }, flickerstreak@174: } flickerstreak@174: flickerstreak@174: local FLYOUT_DOWN_BUTTON_TCOORDS = { flickerstreak@174: ["summon"] = { 99/128, 127/128, 65/256, 83/256 }, flickerstreak@174: [EARTH_TOTEM_SLOT] = { 99/128, 127/128, 141/256, 159/256 }, flickerstreak@174: [FIRE_TOTEM_SLOT] = { 99/128, 127/128, 103/256, 121/256 }, flickerstreak@174: [WATER_TOTEM_SLOT] = { 99/128, 127/128, 180/256, 198/256 }, flickerstreak@174: [AIR_TOTEM_SLOT] = { 99/128, 127/128, 218/256, 236/256 }, flickerstreak@174: } flickerstreak@174: flickerstreak@174: local FLYOUT_TOP_TCOORDS = { flickerstreak@174: ["summon"] = { 33/128, 65/128, 1/256, 23/256 }, flickerstreak@174: [EARTH_TOTEM_SLOT] = { 0/128, 32/128, 46/256, 68/256 }, flickerstreak@174: [FIRE_TOTEM_SLOT] = { 33/128, 65/128, 46/256, 68/256 }, flickerstreak@174: [WATER_TOTEM_SLOT] = { 0/128, 32/128, 1/256, 23/256 }, flickerstreak@174: [AIR_TOTEM_SLOT] = { 0/128, 32/128, 91/256, 113/256 }, flickerstreak@174: } flickerstreak@174: flickerstreak@174: local FLYOUT_MIDDLE_TCOORDS = { flickerstreak@174: ["summon"] = { 33/128, 65/128, 23/256, 43/256 }, flickerstreak@174: [EARTH_TOTEM_SLOT] = { 0/128, 32/128, 68/256, 88/256 }, flickerstreak@174: [FIRE_TOTEM_SLOT] = { 33/128, 65/128, 68/256, 88/256 }, flickerstreak@174: [WATER_TOTEM_SLOT] = { 0/128, 32/128, 23/256, 43/256 }, flickerstreak@174: [AIR_TOTEM_SLOT] = { 0/128, 32/128, 113/256, 133/256 }, flickerstreak@174: } flickerstreak@161: flickerstreak@161: local eventList = { flickerstreak@161: "ACTIONBAR_SLOT_CHANGED", flickerstreak@161: "ACTIONBAR_UPDATE_STATE", flickerstreak@161: "ACTIONBAR_UPDATE_USABLE", flickerstreak@161: "ACTIONBAR_UPDATE_COOLDOWN", flickerstreak@162: "UPDATE_BINDINGS", flickerstreak@161: "UPDATE_MULTI_CAST_ACTIONBAR", flickerstreak@161: } flickerstreak@161: flickerstreak@161: -- flickerstreak@161: -- MultiCast Button class flickerstreak@161: -- Inherits implementation methods from Action button class, but circumvents the constructor flickerstreak@161: -- and redefines/removes some methods. flickerstreak@161: -- flickerstreak@218: local buttonTypeID = "Totem" flickerstreak@161: local Super = ReAction.Button flickerstreak@161: local Action = ReAction.Button.Action flickerstreak@218: local MultiCast = setmetatable( flickerstreak@218: { flickerstreak@218: defaultBarConfig = { flickerstreak@218: type = buttonTypeID, flickerstreak@218: btnWidth = 36, flickerstreak@218: btnHeight = 36, flickerstreak@218: btnRows = 1, flickerstreak@218: btnColumns = 6, flickerstreak@222: spacing = 3, flickerstreak@222: buttons = { } flickerstreak@218: }, flickerstreak@218: flickerstreak@218: barType = L["Totem Bar"], Flick@231: buttonTypeID = buttonTypeID flickerstreak@218: }, flickerstreak@218: { __index = Action } ) flickerstreak@223: flickerstreak@161: ReAction.Button.MultiCast = MultiCast flickerstreak@223: ReAction:RegisterBarType(MultiCast) flickerstreak@161: Flick@234: function MultiCast:New( btnConfig, bar, idx ) flickerstreak@167: local maxIndex = bar.nTotemSlots or 0 flickerstreak@167: if bar.summonSlot then flickerstreak@167: maxIndex = maxIndex + 1 flickerstreak@167: end flickerstreak@167: if bar.recallSlot then flickerstreak@167: maxIndex = maxIndex + 1 flickerstreak@161: end flickerstreak@161: flickerstreak@167: if not bar.hasMulticast or idx > maxIndex then flickerstreak@163: return false flickerstreak@163: end flickerstreak@163: flickerstreak@167: if idx < 1 then flickerstreak@167: error("invalid index") flickerstreak@167: end flickerstreak@167: flickerstreak@167: local name = format("ReAction_%s_Totem_%d",bar:GetName(),idx) flickerstreak@161: flickerstreak@161: self = Super.New(self, name, btnConfig, bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" ) flickerstreak@161: flickerstreak@161: local barFrame = bar:GetFrame() flickerstreak@161: local f = self:GetFrame() flickerstreak@161: flickerstreak@161: -- attributes flickerstreak@167: local page = (idx == bar.recallSlot) and 1 or bar:GetConfig().lastSummon or 1 flickerstreak@167: if idx == bar.recallSlot or idx == bar.summonSlot then flickerstreak@161: f:SetAttribute("type","spell") flickerstreak@167: local spells = (idx == bar.summonSlot) and TOTEM_MULTI_CAST_SUMMON_SPELLS or TOTEM_MULTI_CAST_RECALL_SPELLS flickerstreak@163: f:SetAttribute("spell",spells[page]) flickerstreak@161: for i, spell in ipairs(spells) do flickerstreak@161: if spell and IsSpellKnown(spell) then flickerstreak@161: f:SetAttribute("spell-page"..i, spell) flickerstreak@161: end flickerstreak@161: end flickerstreak@161: else flickerstreak@167: local offset = bar.summonSlot and 1 or 0 flickerstreak@167: local slot = SHAMAN_TOTEM_PRIORITIES[idx - offset] flickerstreak@167: local baseAction = barFrame:GetAttribute("baseActionID") + slot flickerstreak@174: self.totemSlot = slot flickerstreak@161: f:SetAttribute("type","action") flickerstreak@163: f:SetAttribute("action", baseAction + (page - 1) * NUM_MULTI_CAST_BUTTONS_PER_PAGE) flickerstreak@161: for i = 1, NUM_MULTI_CAST_PAGES do flickerstreak@161: f:SetAttribute("action-page"..i, baseAction + (i-1) * NUM_MULTI_CAST_BUTTONS_PER_PAGE) flickerstreak@161: end flickerstreak@174: if not f.overlayTex then flickerstreak@174: local tx = f:CreateTexture("OVERLAY") flickerstreak@174: tx:SetTexture(TOTEM_TEXTURE) flickerstreak@174: tx:SetTexCoord(unpack(SLOT_OVERLAY_TCOORDS[self.totemSlot])) flickerstreak@174: tx:SetWidth(34) flickerstreak@174: tx:SetHeight(34) flickerstreak@174: tx:SetPoint("CENTER") flickerstreak@174: tx:Show() flickerstreak@174: f.overlayTex = tx flickerstreak@174: end flickerstreak@161: end flickerstreak@161: f:SetAttribute("bar-idx",idx) flickerstreak@161: flickerstreak@161: -- non secure scripts flickerstreak@161: f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end) flickerstreak@161: f:SetScript("OnEnter", function(frame) self:OnEnter() end) flickerstreak@161: f:SetScript("OnLeave", function(frame) self:OnLeave() end) flickerstreak@161: f:SetScript("OnAttributeChanged", function(frame, attr, value) self:OnAttributeChanged(attr, value) end) flickerstreak@161: f:SetScript("PostClick", function(frame, ...) self:PostClick(...) end) flickerstreak@161: flickerstreak@161: -- secure handlers flickerstreak@167: if idx ~= bar.recallSlot then flickerstreak@161: f:SetAttribute("_childupdate",_childupdate) flickerstreak@161: end flickerstreak@161: barFrame:WrapScript(f, "OnEnter", _onEnter) flickerstreak@161: flickerstreak@161: -- event registration flickerstreak@161: for _, evt in pairs(eventList) do flickerstreak@161: f:RegisterEvent(evt) flickerstreak@161: end flickerstreak@161: flickerstreak@162: -- Set up a proxy for the icon texture for use with ButtonFacade flickerstreak@174: local SetTexCoordRaw = self.frames.icon.SetTexCoord flickerstreak@162: self.frames.icon.SetTexCoord = function( tx, ... ) flickerstreak@162: if self:GetIconTexture() == TOTEM_TEXTURE then flickerstreak@174: SetTexCoordRaw(tx,select(2,self:GetIconTexture())) flickerstreak@162: else flickerstreak@174: SetTexCoordRaw(tx,...) flickerstreak@162: end flickerstreak@162: end flickerstreak@162: flickerstreak@161: -- attach to skinner flickerstreak@161: bar:SkinButton(self) flickerstreak@161: flickerstreak@161: f:Show() flickerstreak@161: flickerstreak@174: -- open arrow and flyout background textures flickerstreak@167: if idx ~= bar.recallSlot then flickerstreak@174: local arrow = f._arrowFrame or CreateFrame("Button", nil, f, "SecureFrameTemplate") flickerstreak@174: f._arrowFrame = arrow flickerstreak@161: arrow:SetWidth(28) flickerstreak@174: arrow:SetHeight(18) flickerstreak@161: arrow:SetPoint("BOTTOM",self:GetFrame(),"TOP",0,0) -- TODO: better anchoring flickerstreak@161: arrow:SetNormalTexture(TOTEM_TEXTURE) flickerstreak@174: local slot = self.totemSlot or "summon" flickerstreak@174: arrow:GetNormalTexture():SetTexCoord( unpack(FLYOUT_UP_BUTTON_TCOORDS[slot]) ) flickerstreak@161: arrow:SetHighlightTexture(TOTEM_TEXTURE) flickerstreak@161: arrow:GetHighlightTexture():SetTexCoord( unpack(FLYOUT_UP_BUTTON_HL_TCOORDS) ) flickerstreak@161: arrow:SetAttribute("bar-idx",idx) flickerstreak@161: arrow:Hide() flickerstreak@161: barFrame:WrapScript(arrow, "OnClick", _arrow_openFlyout) flickerstreak@167: barFrame:SetFrameRef("arrow-"..idx,arrow) flickerstreak@161: end flickerstreak@161: flickerstreak@161: self:Refresh() flickerstreak@161: flickerstreak@161: return self flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:Destroy() flickerstreak@174: local barFrame = self.bar:GetFrame() flickerstreak@174: local f = self:GetFrame() flickerstreak@174: pcall( barFrame.UnwrapScript, barFrame, f, "OnEnter" ) -- ignore errors flickerstreak@174: if f._arrowFrame then flickerstreak@174: pcall( barFrame.UnwrapScript, barFrame, f._arrowFrame,"OnClick" ) -- ignore errors flickerstreak@174: end flickerstreak@161: Super.Destroy(self) flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:Refresh() flickerstreak@161: Super.Refresh(self) flickerstreak@161: self:UpdateAction() flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:ShowGrid( show ) flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:ShowGridTemp( show ) flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:AcquireActionID() flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:ReleaseActionID() flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:UpdateShowGrid() flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:UpdateBorder() flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:UpdateMacroText() flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:UpdateCount() flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:UpdateCheckedState() flickerstreak@161: local action = self:GetActionID() flickerstreak@161: if action and IsCurrentAction(action) then flickerstreak@161: self:GetFrame():SetChecked(1) flickerstreak@161: else flickerstreak@161: self:GetFrame():SetChecked(0) flickerstreak@161: end flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:RefreshHasActionAttributes() flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:UpdateFlash() flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:GetIconTexture() flickerstreak@161: local tx flickerstreak@161: if self.spellID then flickerstreak@161: tx = GetSpellTexture(GetSpellInfo(self.spellID)) flickerstreak@161: elseif self.actionID then flickerstreak@161: tx = GetActionTexture(self.actionID) flickerstreak@161: end flickerstreak@161: if tx then flickerstreak@161: return tx flickerstreak@161: else flickerstreak@174: return TOTEM_TEXTURE, unpack(SLOT_EMPTY_TCOORDS[self.totemSlot or 1]) flickerstreak@161: end flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:UpdateAction() flickerstreak@161: local action = self:GetActionID() flickerstreak@161: if action then flickerstreak@161: if action ~= self.actionID then flickerstreak@161: self.actionID = action flickerstreak@161: self:UpdateAll() flickerstreak@161: end flickerstreak@161: else flickerstreak@161: local spellID = self:GetSpellID() flickerstreak@161: if spellID ~= self.spellID then flickerstreak@161: self.spellID = spellID flickerstreak@161: self:UpdateAll() flickerstreak@161: end flickerstreak@161: end flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:GetActionID(page) flickerstreak@161: return self:GetFrame():GetAttribute("action") flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:GetSpellID(page) flickerstreak@161: return self:GetFrame():GetAttribute("spell") flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:SetActionID( id ) flickerstreak@161: error("Can not set action ID of multicast buttons") flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:SetTooltip() flickerstreak@161: local barFrame = self:GetFrame() flickerstreak@161: if GetCVar("UberTooltips") == "1" then flickerstreak@161: GameTooltip_SetDefaultAnchor(GameTooltip, barFrame) flickerstreak@161: else flickerstreak@161: GameTooltip:SetOwner(barFrame) flickerstreak@161: end flickerstreak@161: if self.spellID then flickerstreak@161: GameTooltip:SetSpellByID(self.spellID,false,true) flickerstreak@161: elseif self.actionID then flickerstreak@161: GameTooltip:SetAction(self.actionID) flickerstreak@161: end flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:GetUsable() flickerstreak@161: if self.spellID then flickerstreak@161: return IsUsableSpell((GetSpellInfo(self.spellID))) flickerstreak@161: elseif self.actionID then flickerstreak@161: return IsUsableAction(self.actionID) flickerstreak@161: end flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:GetInRange() flickerstreak@161: if self.spellID then flickerstreak@161: return IsSpellInRange((GetSpellInfo(self.spellID))) == 0 flickerstreak@161: elseif self.actionID then flickerstreak@161: return IsActionInRange(self.actionID) == 0 flickerstreak@161: end flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:GetCooldown() flickerstreak@161: if self.spellID then flickerstreak@161: return GetSpellCooldown((GetSpellInfo(self.spellID))) flickerstreak@161: elseif self.actionID then flickerstreak@161: return GetActionCooldown(self.actionID) flickerstreak@161: else flickerstreak@161: return 0, 0, 0 flickerstreak@161: end flickerstreak@161: end flickerstreak@161: flickerstreak@161: function MultiCast:UPDATE_MULTI_CAST_ACTIONBAR() flickerstreak@161: self:UpdateAll() flickerstreak@161: end flickerstreak@161: flickerstreak@161: flickerstreak@161: -- flickerstreak@161: -- flyout setup flickerstreak@161: -- flickerstreak@167: local function ShowFlyoutTooltip(frame) flickerstreak@161: if GetCVar("UberTooltips") == "1" then flickerstreak@167: GameTooltip_SetDefaultAnchor(GameTooltip, frame) flickerstreak@161: else flickerstreak@167: GameTooltip:SetOwner(frame) flickerstreak@161: end flickerstreak@167: local spell = frame:GetAttribute("spell") flickerstreak@167: if spell == nil or spell == 0 then flickerstreak@161: GameTooltip:SetText(MULTI_CAST_TOOLTIP_NO_TOTEM, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b) flickerstreak@161: else flickerstreak@167: GameTooltip:SetSpellByID(spell,false,true) flickerstreak@161: end flickerstreak@161: end flickerstreak@161: flickerstreak@161: local function HideFlyoutTooltip() flickerstreak@161: GameTooltip:Hide() flickerstreak@161: end flickerstreak@161: flickerstreak@161: local function UpdateFlyoutIcon(frame) flickerstreak@161: local spellID = frame:GetAttribute("spell") flickerstreak@167: if spellID == 0 or spellID == nil then flickerstreak@161: frame.icon:SetTexture(TOTEM_TEXTURE) flickerstreak@174: local slot = tonumber(frame:GetAttribute("totemSlot")) or 1 flickerstreak@174: frame.icon:SetTexCoord( unpack(SLOT_EMPTY_TCOORDS[slot]) ) flickerstreak@167: else flickerstreak@161: frame.icon:SetTexture(GetSpellTexture(GetSpellInfo(spellID))) flickerstreak@161: frame.icon:SetTexCoord(0,1,0,1) flickerstreak@161: end flickerstreak@161: end flickerstreak@161: Flick@234: function MultiCast:SetupBar( bar ) Flick@234: Super.SetupBar(self,bar) Flick@234: flickerstreak@167: local slot = 0 flickerstreak@167: local nTotemSlots = 0 flickerstreak@167: local summonSlot = nil flickerstreak@167: local recallSlot = nil flickerstreak@161: flickerstreak@167: -- figure out the capabilities of the character flickerstreak@167: for i, spell in ipairs(TOTEM_MULTI_CAST_SUMMON_SPELLS) do flickerstreak@161: if spell and IsSpellKnown(spell) then flickerstreak@167: slot = 1 flickerstreak@167: summonSlot = 1 flickerstreak@161: end flickerstreak@161: end flickerstreak@161: flickerstreak@167: for i = 1, NUM_MULTI_CAST_BUTTONS_PER_PAGE do flickerstreak@167: local totem = SHAMAN_TOTEM_PRIORITIES[i]; flickerstreak@167: if GetTotemInfo(totem) and GetMultiCastTotemSpells(totem) then flickerstreak@167: nTotemSlots = nTotemSlots + 1 flickerstreak@167: slot = slot + 1 flickerstreak@167: end flickerstreak@167: end flickerstreak@167: flickerstreak@167: slot = slot + 1 flickerstreak@167: for i, spell in ipairs(TOTEM_MULTI_CAST_RECALL_SPELLS) do flickerstreak@161: if spell and IsSpellKnown(spell) then flickerstreak@167: recallSlot = slot flickerstreak@161: end flickerstreak@161: end flickerstreak@161: flickerstreak@167: if nTotemSlots == 0 then flickerstreak@167: bar.hasMulticast = false -- no multicast capability flickerstreak@163: return flickerstreak@161: end flickerstreak@161: flickerstreak@167: bar.hasMulticast = true flickerstreak@167: bar.summonSlot = summonSlot flickerstreak@167: bar.recallSlot = recallSlot flickerstreak@167: bar.nTotemSlots = nTotemSlots flickerstreak@161: flickerstreak@161: flickerstreak@167: local f = bar:GetFrame() flickerstreak@161: flickerstreak@161: -- init bar secure environment flickerstreak@167: f:SetAttribute("lastSummon", bar:GetConfig().lastSummon) flickerstreak@167: f:SetAttribute("summonSlot", summonSlot) flickerstreak@167: f:SetAttribute("recallSlot", recallSlot) flickerstreak@167: f:SetAttribute("slotsPerPage", NUM_MULTI_CAST_BUTTONS_PER_PAGE) flickerstreak@167: f:SetAttribute("baseActionID", (NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset() - 1)*NUM_ACTIONBAR_BUTTONS) flickerstreak@167: for i, p in ipairs(SHAMAN_TOTEM_PRIORITIES) do flickerstreak@167: f:SetAttribute("TOTEM_PRIORITY_"..i,p) flickerstreak@167: end flickerstreak@167: f:SetAttribute("_onstate-multispellpage", _onstate_multispellpage) flickerstreak@161: flickerstreak@167: function f:UpdateLastSummon(value) flickerstreak@163: bar:GetConfig().lastSummon = value flickerstreak@163: end flickerstreak@163: flickerstreak@161: -- create flyout container frame and close arrow flickerstreak@161: local flyout = bar._flyoutFrame flickerstreak@161: if not flyout then flickerstreak@167: flyout = CreateFrame("Frame", nil, f, "SecureFrameTemplate") flickerstreak@161: bar._flyoutFrame = flyout flickerstreak@167: f:SetFrameRef("flyout",flyout) flickerstreak@161: flyout.buttons = { } flickerstreak@161: flyout:Hide() flickerstreak@161: flyout:SetWidth(24) flickerstreak@161: flyout:SetHeight(1) flickerstreak@167: flyout:SetPoint("BOTTOM",f,"TOP",0,0) flickerstreak@161: flickerstreak@161: local close = CreateFrame("Button", nil, flyout, "SecureFrameTemplate") flickerstreak@161: close:SetWidth(28) flickerstreak@174: close:SetHeight(18) flickerstreak@174: close:SetPoint("BOTTOM",flyout,"TOP") flickerstreak@161: close:SetNormalTexture(TOTEM_TEXTURE) flickerstreak@174: close:GetNormalTexture():SetTexCoord(unpack(FLYOUT_DOWN_BUTTON_TCOORDS["summon"])) flickerstreak@161: close:SetHighlightTexture(TOTEM_TEXTURE) flickerstreak@161: close:GetHighlightTexture():SetTexCoord( unpack(FLYOUT_DOWN_BUTTON_HL_TCOORDS) ) flickerstreak@167: f:SetFrameRef("close",close) flickerstreak@167: f:WrapScript(close, "OnClick", _closeFlyout) flickerstreak@174: close:Show() flickerstreak@174: flickerstreak@174: local midTx = flyout:CreateTexture("BACKGROUND") flickerstreak@174: midTx:SetWidth(32) flickerstreak@174: midTx:SetHeight(20) flickerstreak@174: midTx:SetPoint("BOTTOM") flickerstreak@174: midTx:SetTexture(TOTEM_TEXTURE) flickerstreak@174: midTx:SetTexCoord(unpack(FLYOUT_MIDDLE_TCOORDS["summon"])) flickerstreak@174: midTx:Show() flickerstreak@174: flickerstreak@174: local topTx = flyout:CreateTexture("BACKGROUND") flickerstreak@174: topTx:SetWidth(32) flickerstreak@174: topTx:SetHeight(20) flickerstreak@174: topTx:SetTexture(TOTEM_TEXTURE) flickerstreak@174: midTx:SetTexCoord(unpack(FLYOUT_TOP_TCOORDS["summon"])) flickerstreak@174: topTx:SetPoint("BOTTOM",midTx,"TOP",0,-10) flickerstreak@174: topTx:Show() flickerstreak@174: flickerstreak@174: function flyout:UpdateTextures(slot) flickerstreak@174: slot = slot or "summon" flickerstreak@174: close:GetNormalTexture():SetTexCoord(unpack(FLYOUT_DOWN_BUTTON_TCOORDS[slot])) flickerstreak@174: midTx:ClearAllPoints() flickerstreak@174: midTx:SetPoint("BOTTOM") flickerstreak@174: midTx:SetPoint("TOP",close,"BOTTOM",0,0) flickerstreak@174: midTx:SetTexCoord(unpack(FLYOUT_MIDDLE_TCOORDS[slot])) flickerstreak@174: topTx:SetTexCoord(unpack(FLYOUT_TOP_TCOORDS[slot])) flickerstreak@174: end flickerstreak@161: flickerstreak@167: -- create flyout buttons flickerstreak@167: for i = 1, 10 do -- maximum 9 spells + 1 empty slot flickerstreak@167: local b = CreateFrame("Button",nil,flyout,"SecureActionButtonTemplate") flickerstreak@167: b:SetWidth(24) flickerstreak@167: b:SetHeight(24) flickerstreak@167: local prev = flyout.buttons[i-1] flickerstreak@167: b:SetPoint("BOTTOM", prev or flyout, prev and "TOP" or "BOTTOM", 0, 3) -- TODO: better anchoring flickerstreak@167: b.icon = b:CreateTexture("BACKGROUND") flickerstreak@167: b.icon:SetAllPoints() flickerstreak@167: b.icon:Show() flickerstreak@167: b:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square") flickerstreak@167: b:GetHighlightTexture():SetBlendMode("ADD") Flick@259: b:EnableMouse(true) Flick@259: b:RegisterForClicks(bar:GetConfig().clickDown and "AnyDown" or "AnyUp") flickerstreak@167: b:SetScript("OnShow",UpdateFlyoutIcon) flickerstreak@167: b:SetScript("OnEnter",ShowFlyoutTooltip) flickerstreak@167: b:SetScript("OnLeave",HideFlyoutTooltip) flickerstreak@167: b:SetAttribute("index",i) flickerstreak@167: f:SetAttribute("flyout-child-idx",i) flickerstreak@167: f:SetFrameRef("flyout-child",b) flickerstreak@167: f:Execute([[ flickerstreak@167: flyoutChildren = flyoutChildren or newtable() flickerstreak@167: flyoutChildren[self:GetAttribute("flyout-child-idx")] = self:GetFrameRef("flyout-child") flickerstreak@161: ]]) flickerstreak@167: f:WrapScript(b, "OnClick", _flyout_child_preClick, _flyout_child_postClick) flickerstreak@167: b:Show() flickerstreak@167: flyout.buttons[i] = b flickerstreak@161: end flickerstreak@161: end flickerstreak@161: flickerstreak@196: -- scale flyout frame flickerstreak@196: local scale = bar:GetButtonSize() / 36 flickerstreak@196: flyout:SetScale(scale) flickerstreak@196: flickerstreak@174: function f:UpdateFlyoutTextures(slot) flickerstreak@174: flyout:UpdateTextures(slot) flickerstreak@174: end flickerstreak@174: Flick@234: -- re-execute setup when new spells are loaded Flick@234: if not f.events_registered then Flick@234: f:RegisterEvent("UPDATE_MULTI_CAST_ACTIONBAR") Flick@234: f:RegisterEvent("PLAYER_ENTERING_WORLD") Flick@234: -- Bar.frame does not use OnEvent Flick@234: f:SetScript("OnEvent", Flick@234: function() Flick@234: if not InCombatLockdown() then Flick@234: self:SetupBar(bar) Flick@234: end Flick@234: end) Flick@234: f.events_registered = true Flick@234: end Flick@234: Flick@234: flickerstreak@167: f:Execute(_bar_init) flickerstreak@161: end flickerstreak@161: