Mercurial > wow > reaction
diff classes/MultiCastButton.lua @ 167:eab7e7642dd6
Rewrote MultiCastButton to show prior to learning a summon ability, cleaned up implementation.
NOTE: a typo fix will invalidate any existing keybindings to totem buttons.
author | Flick <flickerstreak@gmail.com> |
---|---|
date | Tue, 19 Oct 2010 16:49:40 +0000 |
parents | 8241be11dcc0 |
children | 07c76dbc0236 |
line wrap: on
line diff
--- a/classes/MultiCastButton.lua Sat Oct 16 21:53:57 2010 +0000 +++ b/classes/MultiCastButton.lua Tue Oct 19 16:49:40 2010 +0000 @@ -46,9 +46,8 @@ so that's action ID 132-144. - spell1, spell2, spell3, ... = GetMultiCastTotemSpells(slot) - returns spellIDs for all totems that fit that slot. - Note this is available in the secure environment, but because IsSpellKnown() is not, - it makes it pretty much useless. + returns spellIDs for all known totems that fit that slot. This function is available in + the secure environment. Blizzard textures: All the textures for the multicast bar (arrows, empty-slot icons, etc) are part of a single @@ -71,8 +70,6 @@ - Each button (except recall) has an arrow button which appears on mouseover and when clicked opens the flyout via a wrapped OnClick handler. When the flyout is open, the arrow does not appear. - TODO: add an alt-button ("SHOWMULTICASTFLYOUT") statemachine to the bar to listen for alt key - presses and open/close the bar. Tapping the alt key toggles the flyout. - A single flyout with N+1 (1 slot is to select no totem for the set) flyout-buttons is a child of the bar. Each time the flyout panel is opened, the individual buttons grab their corresponding @@ -84,9 +81,10 @@ changes dynamically because each slot has a different number of items in the list). - Multicast sets are not stances, there's no need (or ability) to handle swapping sets if one of - the summon spells is cast from elsewhere. Additionally, in the default UI Call of the Elements - always appears as the active summon when the UI is loaded, which is bad design that could be improved - upon. TODO: Store state in a config variable and restore it at load time. + the summon spells is cast from elsewhere. + + - The default UI has Call of the Elements always selected on UI load. This module remembers the last + selected one and restores it. ]]-- @@ -101,20 +99,25 @@ [[ -- set up some globals in the secure environment flyout = self:GetFrameRef("flyout") - flyoutChildren = newtable() - nMultiCastSlots = self:GetAttribute("nMultiCastSlots") + flyoutSlot = nil + summonSlot = self:GetAttribute("summonSlot") + recallSlot = self:GetAttribute("recallSlot") baseActionID = self:GetAttribute("baseActionID") - currentMultiCastPage = currentMultiCastPage or self:GetAttribute("lastSummon") or 1 - multiCastSpellList = newtable() - for i = 1, nMultiCastSlots do - tinsert(multiCastSpellList, newtable()) + slotsPerPage = self:GetAttribute("slotsPerPage") + currentPage = currentPage or self:GetAttribute("lastSummon") or 1 + + totemIDsBySlot = newtable() + for i = 1, slotsPerPage do + totemIDsBySlot[i] = self:GetAttribute("TOTEM_PRIORITY_"..i) end + + summonSpells = summonSpells or newtable() -- set up in bar:SetupBarHeader() ]] local _onstate_multispellpage = -- function(self, stateid, newstate) [[ - currentMultiCastPage = tonumber(newstate) - control:CallMethod("UpdateLastSummon",currentMultiCastPage) + currentPage = tonumber(newstate) + control:CallMethod("UpdateLastSummon",currentPage) control:ChildUpdate() ]] @@ -122,11 +125,8 @@ -- buttons local _childupdate = -- function(self, snippetid, message) [[ - if self:GetAttribute("type") == "spell" then - self:SetAttribute("spell", self:GetAttribute("spell-page"..currentMultiCastPage)) - elseif self:GetAttribute("type") == "action" then - self:SetAttribute("action", self:GetAttribute("action-page"..currentMultiCastPage)) - end + local t = self:GetAttribute("type") + self:SetAttribute(t, self:GetAttribute(t.."-page"..currentPage)) ]] local _onEnter = -- function(self) @@ -134,24 +134,12 @@ -- unless you re-anchor the frame prior to calling it. -- Even then, it's still not terribly reliable. [[ - local idx = self:GetAttribute("bar-idx") - if not (flyout:IsVisible() and flyoutIdx == idx) then - local arrow = owner:GetFrameRef("arrow-"..idx) - if arrow and not arrow:IsShown() then - arrow:ClearAllPoints() - arrow:SetPoint("BOTTOM",self,"TOP",0,0) -- TODO: better anchoring - arrow:Show() - arrow:RegisterAutoHide(0) - arrow:AddToAutoHide(self) - end - end -]] - -local _onLeave = -- function(self) - -- to increase reliability (somewhat), re-register it for hide on leave -[[ - local arrow = owner:GetFrameRef("arrow-"..self:GetAttribute("bar-idx")) - if arrow then + local slot = self:GetAttribute("bar-idx") + local arrow = owner:GetFrameRef("arrow-"..slot) + if arrow and not arrow:IsShown() and not (flyout:IsVisible() and flyoutSlot == slot) then + arrow:ClearAllPoints() + arrow:SetPoint("BOTTOM",self,"TOP",0,0) + arrow:Show() arrow:RegisterAutoHide(0) arrow:AddToAutoHide(self) end @@ -161,23 +149,32 @@ -- flyout arrow local _arrow_openFlyout = -- function(self) [[ - local currentMultiCastSlot = self:GetAttribute("bar-idx") - local lastButton, lastIdx - for idx, b in ipairs(flyoutChildren) do - b:Hide() -- force the OnShow handler to run later - local spellID = multiCastSpellList[currentMultiCastSlot][idx] - if spellID then - b:SetAttribute("spell",spellID) -- does passing 0 work for no-totem? Do we have to convert to nil? - if currentMultiCastSlot == 1 then + local slot = self:GetAttribute("bar-idx") + local lastButton, lastPage + for page, b in ipairs(flyoutChildren) do + b:Hide() + if slot == summonSlot then + local spellID = self:GetParent():GetAttribute("spell-page"..page) + print("got spell-page"..tostring(page).." = ".. tostring(spellID)) + if spellID then b:SetAttribute("type","changePage") - else + b:SetAttribute("spell",spellID) + b:Show() + lastButton = b + lastPage = page + end + else + local offset = summonSlot or 0 + local totemID = totemIDsBySlot[slot - offset] + local spells = newtable( 0, GetMultiCastTotemSpells(totemID) ) + if spells[page] then b:SetAttribute("type","multispell") - local totemID = owner:GetAttribute("TOTEM_PRIORITY_"..(currentMultiCastSlot - 1)) - b:SetAttribute("action", baseActionID + (currentMultiCastPage - 1)*(nMultiCastSlots-2) + totemID) + b:SetAttribute("action", baseActionID + (currentPage - 1)*slotsPerPage + totemID) + b:SetAttribute("spell", spells[page]) + b:Show() + lastButton = b + lastPage = page end - b:Show() - lastButton = b - lastIdx = idx end end @@ -190,13 +187,13 @@ flyout:ClearAllPoints() flyout:SetPoint("BOTTOM",self,"BOTTOM",0,0) -- TODO: better anchoring - if lastIdx then - flyout:SetHeight(lastIdx * 27 + (close and close:GetHeight() or 0)) + if lastPage then + flyout:SetHeight(lastPage * 27 + (close and close:GetHeight() or 0)) end flyout:Show() flyout:RegisterAutoHide(1) -- TODO: configurable flyout:AddToAutoHide(owner) - flyoutIdx = currentMultiCastSlot + flyoutSlot = slot self:Hide() ]] @@ -258,15 +255,23 @@ ReAction.Button.MultiCast = MultiCast function MultiCast:New( idx, btnConfig, bar ) - if idx < 1 or idx > NUM_MULTI_CAST_BUTTONS_PER_PAGE + 2 then - error("Multicast button index out of range") + local maxIndex = bar.nTotemSlots or 0 + if bar.summonSlot then + maxIndex = maxIndex + 1 + end + if bar.recallSlot then + maxIndex = maxIndex + 1 end - if idx > bar.nMultiCastSlots then + if not bar.hasMulticast or idx > maxIndex then return false end - local name = format("ReAction_%s_Action_%d",bar:GetName(),idx) + if idx < 1 then + error("invalid index") + end + + local name = format("ReAction_%s_Totem_%d",bar:GetName(),idx) self = Super.New(self, name, btnConfig, bar, idx, "SecureActionButtonTemplate, ActionButtonTemplate" ) @@ -274,18 +279,21 @@ local f = self:GetFrame() -- attributes - local page = (idx == NUM_MULTI_CAST_BUTTONS_PER_PAGE + 2) and 1 or (bar:GetConfig().lastSummon or 1) - if idx == 1 or idx == NUM_MULTI_CAST_BUTTONS_PER_PAGE + 2 then + local page = (idx == bar.recallSlot) and 1 or bar:GetConfig().lastSummon or 1 + if idx == bar.recallSlot or idx == bar.summonSlot then f:SetAttribute("type","spell") - local spells = idx == 1 and TOTEM_MULTI_CAST_SUMMON_SPELLS or TOTEM_MULTI_CAST_RECALL_SPELLS + local spells = (idx == bar.summonSlot) and TOTEM_MULTI_CAST_SUMMON_SPELLS or TOTEM_MULTI_CAST_RECALL_SPELLS f:SetAttribute("spell",spells[page]) for i, spell in ipairs(spells) do if spell and IsSpellKnown(spell) then + print("setting attribute spell-page"..i.." to "..spell) f:SetAttribute("spell-page"..i, spell) end end else - local baseAction = barFrame:GetAttribute("baseActionID") + SHAMAN_TOTEM_PRIORITIES[idx-1] + local offset = bar.summonSlot and 1 or 0 + local slot = SHAMAN_TOTEM_PRIORITIES[idx - offset] + local baseAction = barFrame:GetAttribute("baseActionID") + slot f:SetAttribute("type","action") f:SetAttribute("action", baseAction + (page - 1) * NUM_MULTI_CAST_BUTTONS_PER_PAGE) for i = 1, NUM_MULTI_CAST_PAGES do @@ -293,7 +301,6 @@ end end f:SetAttribute("bar-idx",idx) - barFrame:SetFrameRef("slot-"..idx,f) -- non secure scripts f:SetScript("OnEvent", function(frame, ...) self:OnEvent(...) end) @@ -303,7 +310,7 @@ f:SetScript("PostClick", function(frame, ...) self:PostClick(...) end) -- secure handlers - if idx ~= NUM_MULTI_CAST_BUTTONS_PER_PAGE + 2 then + if idx ~= bar.recallSlot then f:SetAttribute("_childupdate",_childupdate) end barFrame:WrapScript(f, "OnEnter", _onEnter) @@ -331,7 +338,7 @@ f:Show() -- open arrow - if idx ~= NUM_MULTI_CAST_BUTTONS_PER_PAGE + 2 then + if idx ~= bar.recallSlot then local arrow = CreateFrame("Button", nil, f, "SecureFrameTemplate") arrow:SetWidth(28) arrow:SetHeight(12) @@ -343,8 +350,7 @@ arrow:SetAttribute("bar-idx",idx) arrow:Hide() barFrame:WrapScript(arrow, "OnClick", _arrow_openFlyout) - local arrowRef = "arrow-"..idx - barFrame:SetFrameRef(arrowRef,arrow) + barFrame:SetFrameRef("arrow-"..idx,arrow) end self:Refresh() @@ -491,17 +497,17 @@ -- -- flyout setup -- -local function ShowFlyoutTooltip(barFrame) +local function ShowFlyoutTooltip(frame) if GetCVar("UberTooltips") == "1" then - GameTooltip_SetDefaultAnchor(GameTooltip, barFrame) + GameTooltip_SetDefaultAnchor(GameTooltip, frame) else - GameTooltip:SetOwner(barFrame) + GameTooltip:SetOwner(frame) end - local spell = barFrame:GetAttribute("spell") - if barFrame == 0 then + local spell = frame:GetAttribute("spell") + if spell == nil or spell == 0 then GameTooltip:SetText(MULTI_CAST_TOOLTIP_NO_TOTEM, HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b) else - GameTooltip:SetSpellByID(barFrame:GetAttribute("spell"),false,true) + GameTooltip:SetSpellByID(spell,false,true) end end @@ -511,79 +517,83 @@ local function UpdateFlyoutIcon(frame) local spellID = frame:GetAttribute("spell") - if spellID == 0 then + if spellID == 0 or spellID == nil then frame.icon:SetTexture(TOTEM_TEXTURE) frame.icon:SetTexCoord( unpack(EMPTY_SLOT_TCOORDS) ) - elseif spellID then + else frame.icon:SetTexture(GetSpellTexture(GetSpellInfo(spellID))) frame.icon:SetTexCoord(0,1,0,1) end end function MultiCast.SetupBarHeader( bar ) -- call this as a static method - local summon = { } - local recall = { } - local maxIdx = 1 + local slot = 0 + local nTotemSlots = 0 + local summonSlot = nil + local recallSlot = nil - for idx, spell in ipairs(TOTEM_MULTI_CAST_SUMMON_SPELLS) do + -- figure out the capabilities of the character + for i, spell in ipairs(TOTEM_MULTI_CAST_SUMMON_SPELLS) do if spell and IsSpellKnown(spell) then - tinsert(summon,spell) - maxIdx = max(idx,maxIdx) + slot = 1 + summonSlot = 1 end end - for idx, spell in ipairs(TOTEM_MULTI_CAST_RECALL_SPELLS) do + for i = 1, NUM_MULTI_CAST_BUTTONS_PER_PAGE do + local totem = SHAMAN_TOTEM_PRIORITIES[i]; + if GetTotemInfo(totem) and GetMultiCastTotemSpells(totem) then + nTotemSlots = nTotemSlots + 1 + slot = slot + 1 + end + end + + slot = slot + 1 + for i, spell in ipairs(TOTEM_MULTI_CAST_RECALL_SPELLS) do if spell and IsSpellKnown(spell) then - tinsert(recall,spell) - maxIdx = max(idx,maxIdx) + recallSlot = slot end end - if #summon == 0 and #recall == 0 then - bar.nMultiCastSlots = 0 -- no multicast capability + if nTotemSlots == 0 then + bar.hasMulticast = false -- no multicast capability return end - local slots = { } - - tinsert(slots, summon) + bar.hasMulticast = true + bar.summonSlot = summonSlot + bar.recallSlot = recallSlot + bar.nTotemSlots = nTotemSlots - for i = 1, NUM_MULTI_CAST_BUTTONS_PER_PAGE do - local slotSpells = { 0, GetMultiCastTotemSpells(SHAMAN_TOTEM_PRIORITIES[i]) } - maxIdx = max(maxIdx, #slotSpells) - tinsert(slots,slotSpells) - end - tinsert(slots, recall) - - local barFrame = bar:GetFrame() + local f = bar:GetFrame() -- init bar secure environment - barFrame:SetAttribute("lastSummon",bar:GetConfig().lastSummon) - barFrame:SetAttribute("nMultiCastSlots",#slots) - barFrame:SetAttribute("baseActionID", (NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset() - 1)*NUM_ACTIONBAR_BUTTONS) - barFrame:SetAttribute("_onstate-multispellpage", _onstate_multispellpage) - barFrame:Execute(_bar_init) + f:SetAttribute("lastSummon", bar:GetConfig().lastSummon) + f:SetAttribute("summonSlot", summonSlot) + f:SetAttribute("recallSlot", recallSlot) + f:SetAttribute("slotsPerPage", NUM_MULTI_CAST_BUTTONS_PER_PAGE) + f:SetAttribute("baseActionID", (NUM_ACTIONBAR_PAGES + GetMultiCastBarOffset() - 1)*NUM_ACTIONBAR_BUTTONS) + for i, p in ipairs(SHAMAN_TOTEM_PRIORITIES) do + f:SetAttribute("TOTEM_PRIORITY_"..i,p) + end + f:SetAttribute("_onstate-multispellpage", _onstate_multispellpage) - function barFrame:UpdateLastSummon(value) + function f:UpdateLastSummon(value) bar:GetConfig().lastSummon = value end - for i, p in ipairs(SHAMAN_TOTEM_PRIORITIES) do - barFrame:SetAttribute("TOTEM_PRIORITY_"..i,p) - end - -- create flyout container frame and close arrow local flyout = bar._flyoutFrame if not flyout then - flyout = CreateFrame("Frame", nil, barFrame, "SecureFrameTemplate") + flyout = CreateFrame("Frame", nil, f, "SecureFrameTemplate") bar._flyoutFrame = flyout - barFrame:SetFrameRef("flyout",flyout) + f:SetFrameRef("flyout",flyout) flyout.buttons = { } flyout:Hide() flyout:SetWidth(24) flyout:SetHeight(1) - flyout:SetPoint("BOTTOM",barFrame,"TOP",0,0) + flyout:SetPoint("BOTTOM",f,"TOP",0,0) local close = CreateFrame("Button", nil, flyout, "SecureFrameTemplate") close:SetWidth(28) @@ -593,51 +603,38 @@ close:GetNormalTexture():SetTexCoord( unpack(FLYOUT_DOWN_BUTTON_TCOORDS) ) close:SetHighlightTexture(TOTEM_TEXTURE) close:GetHighlightTexture():SetTexCoord( unpack(FLYOUT_DOWN_BUTTON_HL_TCOORDS) ) - barFrame:SetFrameRef("close",close) - barFrame:WrapScript(close, "OnClick", _closeFlyout) - end + f:SetFrameRef("close",close) + f:WrapScript(close, "OnClick", _closeFlyout) - -- create flyout buttons - for i = #flyout.buttons + 1, maxIdx do - local b = CreateFrame("Button",nil,flyout,"SecureActionButtonTemplate") - b:SetWidth(24) - b:SetHeight(24) - local prev = flyout.buttons[i-1] - b:SetPoint("BOTTOM", prev or flyout, prev and "TOP" or "BOTTOM", 0, 3) -- TODO: better anchoring - b.icon = b:CreateTexture("BACKGROUND") - b.icon:SetAllPoints() - b.icon:Show() - b:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square") - b:GetHighlightTexture():SetBlendMode("ADD") - b:RegisterForClicks("AnyUp") - b:SetScript("OnShow",UpdateFlyoutIcon) - b:SetScript("OnEnter",ShowFlyoutTooltip) - b:SetScript("OnLeave",HideFlyoutTooltip) - b:SetAttribute("index",i) - b:Show() - barFrame:WrapScript(b, "OnClick", _flyout_child_preClick, _flyout_child_postClick) - flyout.buttons[i] = b - end - - for i, b in ipairs(flyout.buttons) do - barFrame:SetFrameRef("flyout-child",b) - barFrame:Execute([[ - tinsert(flyoutChildren,self:GetFrameRef("flyout-child")) - ]]) - end - - -- transfer the table of spell IDs into the secure environment - for i, spells in ipairs(slots) do - barFrame:SetAttribute("spell-slot", i) - for j, spell in ipairs(spells) do - barFrame:SetAttribute("spell-index", j) - barFrame:SetAttribute("spell-id", spell) - barFrame:Execute([[ - multiCastSpellList[self:GetAttribute("spell-slot")][self:GetAttribute("spell-index")] = self:GetAttribute("spell-id") + -- create flyout buttons + for i = 1, 10 do -- maximum 9 spells + 1 empty slot + local b = CreateFrame("Button",nil,flyout,"SecureActionButtonTemplate") + b:SetWidth(24) + b:SetHeight(24) + local prev = flyout.buttons[i-1] + b:SetPoint("BOTTOM", prev or flyout, prev and "TOP" or "BOTTOM", 0, 3) -- TODO: better anchoring + b.icon = b:CreateTexture("BACKGROUND") + b.icon:SetAllPoints() + b.icon:Show() + b:SetHighlightTexture("Interface\\Buttons\\ButtonHilight-Square") + b:GetHighlightTexture():SetBlendMode("ADD") + b:RegisterForClicks("AnyUp") + b:SetScript("OnShow",UpdateFlyoutIcon) + b:SetScript("OnEnter",ShowFlyoutTooltip) + b:SetScript("OnLeave",HideFlyoutTooltip) + b:SetAttribute("index",i) + f:SetAttribute("flyout-child-idx",i) + f:SetFrameRef("flyout-child",b) + f:Execute([[ + flyoutChildren = flyoutChildren or newtable() + flyoutChildren[self:GetAttribute("flyout-child-idx")] = self:GetFrameRef("flyout-child") ]]) + f:WrapScript(b, "OnClick", _flyout_child_preClick, _flyout_child_postClick) + b:Show() + flyout.buttons[i] = b end end - bar.nMultiCastSlots = #slots + f:Execute(_bar_init) end