flickerstreak@161: local ReAction = 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: flickerstreak@161: ReAction:UpdateRevision("$Revision: 154 $") flickerstreak@161: flickerstreak@161: flickerstreak@161: --[[ flickerstreak@161: Blizzard Constants: flickerstreak@161: - NUM_MULTI_CAST_BUTTONS_PER_PAGE = 4 flickerstreak@161: - NUM_MULTI_CAST_PAGES = 3 flickerstreak@161: - 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@161: returns spellIDs for all totems that fit that slot. flickerstreak@161: Note this is available in the secure environment, but because IsSpellKnown() is not, flickerstreak@161: it makes it pretty much useless. 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@161: TODO: update code to use these pretty per-slot icons, or start setting a border. 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: TODO: add an alt-button ("SHOWMULTICASTFLYOUT") statemachine to the bar to listen for alt key flickerstreak@161: presses and open/close the bar. Tapping the alt key toggles the flyout. 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@161: the summon spells is cast from elsewhere. Additionally, in the default UI Call of the Elements flickerstreak@161: always appears as the active summon when the UI is loaded, which is bad design that could be improved flickerstreak@161: upon. TODO: Store state in a config variable and restore it at load time. flickerstreak@161: flickerstreak@161: flickerstreak@161: ]]-- flickerstreak@161: 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@161: flyoutChildren = newtable() flickerstreak@161: nMultiCastSlots = self:GetAttribute("nMultiCastSlots") flickerstreak@161: baseActionID = self:GetAttribute("baseActionID") flickerstreak@161: currentMultiCastPage = currentMultiCastPage or 1 flickerstreak@161: multiCastSpellList = newtable() flickerstreak@161: for i = 1, nMultiCastSlots do flickerstreak@161: tinsert(multiCastSpellList, newtable()) flickerstreak@161: end flickerstreak@161: ]] flickerstreak@161: flickerstreak@161: local _onstate_multispellpage = -- function(self, stateid, newstate) flickerstreak@161: [[ flickerstreak@161: currentMultiCastPage = tonumber(newstate) flickerstreak@161: control:ChildUpdate() flickerstreak@161: ]] flickerstreak@161: flickerstreak@161: flickerstreak@161: -- buttons flickerstreak@161: local _childupdate = -- function(self, snippetid, message) flickerstreak@161: [[ flickerstreak@161: if self:GetAttribute("type") == "spell" then flickerstreak@161: self:SetAttribute("spell", self:GetAttribute("spell-page"..currentMultiCastPage)) flickerstreak@161: elseif self:GetAttribute("type") == "action" then flickerstreak@161: self:SetAttribute("action", self:GetAttribute("action-page"..currentMultiCastPage)) flickerstreak@161: end 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@161: local idx = self:GetAttribute("bar-idx") flickerstreak@161: if not (flyout:IsVisible() and flyoutIdx == idx) then flickerstreak@161: local arrow = owner:GetFrameRef("arrow-"..idx) flickerstreak@161: if arrow and not arrow:IsShown() then flickerstreak@161: arrow:ClearAllPoints() flickerstreak@161: arrow:SetPoint("BOTTOM",self,"TOP",0,0) -- TODO: better anchoring flickerstreak@161: arrow:Show() flickerstreak@161: arrow:RegisterAutoHide(0) flickerstreak@161: arrow:AddToAutoHide(self) flickerstreak@161: end flickerstreak@161: end flickerstreak@161: ]] flickerstreak@161: flickerstreak@161: local _onLeave = -- function(self) flickerstreak@161: -- to increase reliability (somewhat), re-register it for hide on leave flickerstreak@161: [[ flickerstreak@161: local arrow = owner:GetFrameRef("arrow-"..self:GetAttribute("bar-idx")) flickerstreak@161: if arrow then 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@161: local currentMultiCastSlot = self:GetAttribute("bar-idx") flickerstreak@161: local lastButton, lastIdx flickerstreak@161: for idx, b in ipairs(flyoutChildren) do flickerstreak@161: b:Hide() -- force the OnShow handler to run later flickerstreak@161: local spellID = multiCastSpellList[currentMultiCastSlot][idx] flickerstreak@161: if spellID then flickerstreak@161: b:SetAttribute("spell",spellID) -- does passing 0 work for no-totem? Do we have to convert to nil? flickerstreak@161: if currentMultiCastSlot == 1 then flickerstreak@161: b:SetAttribute("type","changePage") flickerstreak@161: else flickerstreak@161: b:SetAttribute("type","multispell") flickerstreak@161: local totemID = owner:GetAttribute("TOTEM_PRIORITY_"..(currentMultiCastSlot - 1)) flickerstreak@161: b:SetAttribute("action", baseActionID + (currentMultiCastPage - 1)*(nMultiCastSlots-2) + totemID) flickerstreak@161: end flickerstreak@161: b:Show() flickerstreak@161: lastButton = b flickerstreak@161: lastIdx = idx 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@161: end flickerstreak@161: flickerstreak@161: flyout:ClearAllPoints() flickerstreak@161: flyout:SetPoint("BOTTOM",self,"BOTTOM",0,0) -- TODO: better anchoring flickerstreak@161: if lastIdx then flickerstreak@161: flyout:SetHeight(lastIdx * 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@161: flyoutIdx = currentMultiCastSlot 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@161: -- with texcoord offsets flickerstreak@161: -- flickerstreak@161: local TOTEM_TEXTURE = "Interface\\Buttons\\UI-TotemBar" flickerstreak@161: local FLYOUT_UP_BUTTON_TCOORDS = { 99/128, 127/128, 84/256, 102/256 } flickerstreak@161: local FLYOUT_UP_BUTTON_HL_TCOORDS = { 72/128, 92/128, 88/256, 98/256 } flickerstreak@161: local FLYOUT_DOWN_BUTTON_TCOORDS = { 99/128, 127/128, 65/256, 83/256 } flickerstreak@161: local FLYOUT_DOWN_BUTTON_HL_TCOORDS = { 72/128, 92/128, 69/256, 79/256 } flickerstreak@161: local EMPTY_SLOT_TCOORDS = { 66/128, 96/128, 3/256, 33/256 } 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@161: local Super = ReAction.Button flickerstreak@161: local Action = ReAction.Button.Action flickerstreak@161: local MultiCast = setmetatable( { }, { __index = Action } ) flickerstreak@161: ReAction.Button.MultiCast = MultiCast flickerstreak@161: flickerstreak@161: function MultiCast:New( idx, btnConfig, bar ) flickerstreak@161: if idx < 1 or idx > NUM_MULTI_CAST_BUTTONS_PER_PAGE + 2 then flickerstreak@161: error("Multicast button index out of range") flickerstreak@161: end flickerstreak@161: flickerstreak@161: local name = format("ReAction_%s_Action_%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@161: if idx == 1 or idx == NUM_MULTI_CAST_BUTTONS_PER_PAGE + 2 then flickerstreak@161: f:SetAttribute("type","spell") flickerstreak@161: local spells = idx == 1 and TOTEM_MULTI_CAST_SUMMON_SPELLS or TOTEM_MULTI_CAST_RECALL_SPELLS 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: if i == 1 then flickerstreak@161: -- TODO: store/restore last used summon flickerstreak@161: f:SetAttribute("spell",spell) flickerstreak@161: end flickerstreak@161: end flickerstreak@161: end flickerstreak@161: else flickerstreak@161: local baseAction = barFrame:GetAttribute("baseActionID") + TOTEM_PRIORITIES[idx-1] flickerstreak@161: f:SetAttribute("type","action") flickerstreak@161: f:SetAttribute("action", baseAction) 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@161: end flickerstreak@161: f:SetAttribute("bar-idx",idx) flickerstreak@161: barFrame:SetFrameRef("slot-"..idx,f) 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@161: if idx ~= NUM_MULTI_CAST_BUTTONS_PER_PAGE + 2 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: f:EnableMouse(true) flickerstreak@161: f:RegisterForClicks("AnyUp") 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@162: self.frames.icon.SetTexCoordRaw = self.frames.icon.SetTexCoord flickerstreak@162: self.frames.icon.SetTexCoord = function( tx, ... ) flickerstreak@162: if self:GetIconTexture() == TOTEM_TEXTURE then flickerstreak@162: tx:SetTexCoordRaw(unpack(EMPTY_SLOT_TCOORDS)) flickerstreak@162: else flickerstreak@162: tx:SetTexCoordRaw(...) 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@161: -- open arrow flickerstreak@161: if idx ~= NUM_MULTI_CAST_BUTTONS_PER_PAGE + 2 then flickerstreak@161: local arrow = CreateFrame("Button", nil, f, "SecureFrameTemplate") flickerstreak@161: arrow:SetWidth(28) flickerstreak@161: arrow:SetHeight(12) flickerstreak@161: arrow:SetPoint("BOTTOM",self:GetFrame(),"TOP",0,0) -- TODO: better anchoring flickerstreak@161: arrow:SetNormalTexture(TOTEM_TEXTURE) flickerstreak@161: arrow:GetNormalTexture():SetTexCoord( unpack(FLYOUT_UP_BUTTON_TCOORDS) ) 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@161: local arrowRef = "arrow-"..idx flickerstreak@161: barFrame:SetFrameRef(arrowRef,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@161: local barFrame = self:GetFrame() 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@161: return TOTEM_TEXTURE, unpack(EMPTY_SLOT_TCOORDS) 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@161: local function ShowFlyoutTooltip(barFrame) 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: local spell = barFrame:GetAttribute("spell") flickerstreak@161: if barFrame == 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@161: GameTooltip:SetSpellByID(barFrame:GetAttribute("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@161: if spellID == 0 then flickerstreak@161: frame.icon:SetTexture(TOTEM_TEXTURE) flickerstreak@161: frame.icon:SetTexCoord( unpack(EMPTY_SLOT_TCOORDS) ) flickerstreak@161: elseif spellID then 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: flickerstreak@161: function MultiCast.SetupBarHeader( bar ) -- call this as a static method flickerstreak@161: local summon = { } flickerstreak@161: local recall = { } flickerstreak@161: local maxIdx = 1 flickerstreak@161: flickerstreak@161: for idx, spell in ipairs(TOTEM_MULTI_CAST_SUMMON_SPELLS) do flickerstreak@161: if spell and IsSpellKnown(spell) then flickerstreak@161: tinsert(summon,spell) flickerstreak@161: maxIdx = max(idx,maxIdx) flickerstreak@161: end flickerstreak@161: end flickerstreak@161: flickerstreak@161: for idx, spell in ipairs(TOTEM_MULTI_CAST_RECALL_SPELLS) do flickerstreak@161: if spell and IsSpellKnown(spell) then flickerstreak@161: tinsert(recall,spell) flickerstreak@161: maxIdx = max(idx,maxIdx) flickerstreak@161: end flickerstreak@161: end flickerstreak@161: flickerstreak@161: if #summon == 0 and #recall == 0 then flickerstreak@161: return 0 -- no multicast capability flickerstreak@161: end flickerstreak@161: flickerstreak@161: local slots = { } flickerstreak@161: flickerstreak@161: tinsert(slots, summon) flickerstreak@161: flickerstreak@161: for i = 1, NUM_MULTI_CAST_BUTTONS_PER_PAGE do flickerstreak@161: local slotSpells = { 0, GetMultiCastTotemSpells(TOTEM_PRIORITIES[i]) } flickerstreak@161: maxIdx = max(maxIdx, #slotSpells) flickerstreak@161: tinsert(slots,slotSpells) flickerstreak@161: end flickerstreak@161: flickerstreak@161: tinsert(slots, recall) flickerstreak@161: flickerstreak@161: local barFrame = bar:GetFrame() flickerstreak@161: flickerstreak@161: -- init bar secure environment flickerstreak@161: barFrame:SetAttribute("nMultiCastSlots",#slots) flickerstreak@161: barFrame:SetAttribute("baseActionID", (NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset() - 1)*NUM_ACTIONBAR_BUTTONS) flickerstreak@161: barFrame:SetAttribute("_onstate-multispellpage", _onstate_multispellpage) flickerstreak@161: barFrame:Execute(_bar_init) flickerstreak@161: flickerstreak@161: for i, p in ipairs(TOTEM_PRIORITIES) do flickerstreak@161: barFrame:SetAttribute("TOTEM_PRIORITY_"..i,p) flickerstreak@161: end flickerstreak@161: flickerstreak@161: -- create flyout container frame and close arrow flickerstreak@161: local flyout = bar._flyoutFrame flickerstreak@161: if not flyout then flickerstreak@161: flyout = CreateFrame("Frame", nil, barFrame, "SecureFrameTemplate") flickerstreak@161: bar._flyoutFrame = flyout flickerstreak@161: barFrame:SetFrameRef("flyout",flyout) flickerstreak@161: flyout.buttons = { } flickerstreak@161: flyout:Hide() flickerstreak@161: flyout:SetWidth(24) flickerstreak@161: flyout:SetHeight(1) flickerstreak@161: flyout:SetPoint("BOTTOM",barFrame,"TOP",0,0) flickerstreak@161: flickerstreak@161: local close = CreateFrame("Button", nil, flyout, "SecureFrameTemplate") flickerstreak@161: close:SetWidth(28) flickerstreak@161: close:SetHeight(12) flickerstreak@161: close:SetPoint("TOP") flickerstreak@161: close:SetNormalTexture(TOTEM_TEXTURE) flickerstreak@161: close:GetNormalTexture():SetTexCoord( unpack(FLYOUT_DOWN_BUTTON_TCOORDS) ) flickerstreak@161: close:SetHighlightTexture(TOTEM_TEXTURE) flickerstreak@161: close:GetHighlightTexture():SetTexCoord( unpack(FLYOUT_DOWN_BUTTON_HL_TCOORDS) ) flickerstreak@161: barFrame:SetFrameRef("close",close) flickerstreak@161: barFrame:WrapScript(close, "OnClick", _closeFlyout) flickerstreak@161: end flickerstreak@161: flickerstreak@161: -- create flyout buttons flickerstreak@161: for i = #flyout.buttons + 1, maxIdx do flickerstreak@161: local b = CreateFrame("Button",nil,flyout,"SecureActionButtonTemplate") flickerstreak@161: b:SetWidth(24) flickerstreak@161: b:SetHeight(24) flickerstreak@161: local prev = flyout.buttons[i-1] flickerstreak@161: b:SetPoint("BOTTOM", prev or flyout, prev and "TOP" or "BOTTOM", 0, 3) -- TODO: better anchoring flickerstreak@161: b.icon = b:CreateTexture("BACKGROUND") flickerstreak@161: b.icon:SetAllPoints() flickerstreak@161: b.icon:Show() flickerstreak@161: b:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square") flickerstreak@161: b:GetHighlightTexture():SetBlendMode("ADD") flickerstreak@161: b:RegisterForClicks("AnyUp") flickerstreak@161: b:SetScript("OnShow",UpdateFlyoutIcon) flickerstreak@161: b:SetScript("OnEnter",ShowFlyoutTooltip) flickerstreak@161: b:SetScript("OnLeave",HideFlyoutTooltip) flickerstreak@161: b:SetAttribute("index",i) flickerstreak@161: b:Show() flickerstreak@161: barFrame:WrapScript(b, "OnClick", _flyout_child_preClick, _flyout_child_postClick) flickerstreak@161: flyout.buttons[i] = b flickerstreak@161: end flickerstreak@161: flickerstreak@161: for i, b in ipairs(flyout.buttons) do flickerstreak@161: barFrame:SetFrameRef("flyout-child",b) flickerstreak@161: barFrame:Execute([[ flickerstreak@161: tinsert(flyoutChildren,self:GetFrameRef("flyout-child")) flickerstreak@161: ]]) flickerstreak@161: end flickerstreak@161: flickerstreak@161: -- transfer the table of spell IDs into the secure environment flickerstreak@161: for i, spells in ipairs(slots) do flickerstreak@161: barFrame:SetAttribute("spell-slot", i) flickerstreak@161: for j, spell in ipairs(spells) do flickerstreak@161: barFrame:SetAttribute("spell-index", j) flickerstreak@161: barFrame:SetAttribute("spell-id", spell) flickerstreak@161: barFrame:Execute([[ flickerstreak@161: multiCastSpellList[self:GetAttribute("spell-slot")][self:GetAttribute("spell-index")] = self:GetAttribute("spell-id") flickerstreak@161: ]]) flickerstreak@161: end flickerstreak@161: end flickerstreak@161: flickerstreak@161: return #slots flickerstreak@161: end flickerstreak@161: