Mercurial > wow > reaction
diff classes/ReAction.lua @ 4:dfd829db3ad0
(none)
author | Flick <flickerstreak@gmail.com> |
---|---|
date | Tue, 20 Mar 2007 21:19:34 +0000 |
parents | Button.lua@8e0ff8ae4c08 |
children | f920db5fc6b1 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/classes/ReAction.lua Tue Mar 20 21:19:34 2007 +0000 @@ -0,0 +1,773 @@ +-- private constants +local namePrefix = "ReActionButton" +local _G = getfenv(0) +local ACTION_FREE = { } +local MAX_ACTIONS = 120 + +local hotKeyDefaultColor = { r=1.0, g=1.0, b=1.0, a=1.0 } +local hotKeyDisabledColor = { r=0.6, g=0.6, b=0.6, a=1.0 } +local hotKeyOutOfRangeColor = { r=1.0, g=0.2, b=0.2, a=1.0 } + +local hotKeyModifierColors = { + S = { r=0.6, g=0.6, b=1.0, a=1.0 }, -- shift + C = { r=1.0, g=0.82, b=0, a=1.0 }, -- ctrl + A = { r=0.1, g=1.0, b=0.1, a=1.0 }, -- alt +} + +-- TODO: localize these key names with GetBindingText(KEY_) +local keybindAbbreviations = { + ["Mouse Button "] = "M-", + ["Spacebar"] = "Sp", + ["Num Pad "] = "Num-", + ["Page Up"] = "PgUp", + ["Page Down"] = "PgDn", + [" Arrow"] = "", +} + +local equippedActionBorderColor = { r=0, g=1.0, b=0, a=0.35 } + +local actionUsableColor = { r=1.0, g=1.0, b=1.0, a=1.0 } +local actionNotUsableColor = { r=0.4, g=0.4, b=0.4, a=1.0 } +local actionNotEnoughManaColor = { r=0.2, g=0.2, b=0.7, a=1.0 } +local actionOutOfRangeColor = { r=1.0, g=0.2, b=0.2, a=1.0 } + +-- private variables +local kbValidate = AceLibrary("AceConsole-2.0").keybindingValidateFunc +local actionButtonTbl = { } + + + +-- ReActionButton is a class prototype object. +ReActionButton = AceLibrary("AceOO-2.0").Class("AceEvent-2.0") + +------------------- +-- Class methods +------------------- + +-- In addition to supporting the 'new' creation method (in which case an action ID must be specified directly), +-- ReActionButton supports the 'acquire'/'release' creation method, which recycles objects and manages actionID assignment. + +function ReActionButton:acquire(parent, config, barIdx) + local id = nil + for i = 1, MAX_ACTIONS do + if actionButtonTbl[i] == nil or actionButtonTbl[i].inUse == false then + id = i + break + end + end + + if id == nil then return nil end -- all buttons and action ids are in use + + local hint = config.actionIDs[barIdx] + if hint and (actionButtonTbl[hint] == nil or actionButtonTbl[hint].inUse == false) then + id = hint + end + + if actionButtonTbl[id] == nil then + actionButtonTbl[id] = { } + end + local t = actionButtonTbl[id] + + t.inUse = true + if t.button then + t.button:Configure(parent,config,barIdx) + else + t.button = self:new(parent,config,barIdx,id) + end + + -- fix screwy config with overlapping IDs + config.actionIDs[barIdx] = id + return t.button +end + +function ReActionButton:release( b ) + if b then + actionButtonTbl[b:GetActionID()].inUse = false + b:Recycle() + end +end + + + +function ReActionButton:ShowAllActionIDs() + for _, b in ipairs(actionButtonTbl) do + b:ShowActionID() + end +end + +function ReActionButton:HideAllActionIDs() + for _, b in ipairs(actionButtonTbl) do + b:HideActionID() + end +end + + +---------------------- +-- Instance methods +---------------------- +function ReActionButton.prototype:init( parentFrame, config, barIdx, id ) + ReActionButton.super.prototype.init(self) + + -- create the button widget + self.name = namePrefix..id + self.button = CreateFrame("CheckButton", self.name, parentFrame, "ReActionButtonTemplate") + + -- store references to the various sub-frames so we don't have to look it up all the time + self.frames = { + hotkey = _G[self.name.."HotKey"], + count = _G[self.name.."Count"], + cooldown = _G[self.name.."Cooldown"], +-- nCooldown = _G[self.name.."CooldownNumeric"], + macro = _G[self.name.."Name"], + icon = _G[self.name.."Icon"], + border = _G[self.name.."Border"], + normalTexture = _G[self.name.."NormalTexture"], + flash = _G[self.name.."Flash"], + actionID = _G[self.name.."ActionID"], + } + + -- provide a reference back to this object for the frame to use in event handlers + self.button.rxnBtn = self + + -- set the action ID + self:SetActionID(id) + + -- register button with ReBinder for keybinding + ReBinder:AddKeybindTarget(self.button) + + -- initialize + self:Configure(parentFrame, config, barIdx) +end + +local function tcopy(t) + local r = { } + for k, v in pairs(t) do + r[k] = (type(v) == "table" and tcopy(v) or v) + end + return r +end + +function ReActionButton.prototype:Recycle() + local b = self.button + + self:SetKeyBinding(nil) + self:UpdateDisplay() + b:UnregisterAllEvents() + b:SetParent(ReActionButtonRecycleFrame) + b:ClearAllPoints() + b:SetPoint("TOPLEFT",0,0) + b:Hide() + self.config = tcopy(self.config) -- ew, but necessary +end + +function ReActionButton.prototype:BarUnlocked() + self:ShowGridTmp() +end + +function ReActionButton.prototype:BarLocked() + self:HideGridTmp() +end + + +-- set the button location +function ReActionButton.prototype:PlaceButton(point, x, y, sz) + local b = self.button + local scale = sz / 36 + b:ClearAllPoints() + b:SetScale( scale ) + b:SetPoint(point,x/scale,y/scale) +end + + +function ReActionButton.prototype:ShowActionID() + self.frames.actionID:Show() +end + +function ReActionButton.prototype:HideActionID() + self.frames.actionID:Hide() +end + + + +-- configuration and setup +function ReActionButton.prototype:Configure( parentFrame, config, barIdx ) + self.config = config + self.barIdx = barIdx + self.showGridTmp_ = 0 + + self.button:ClearAllPoints() + self.button:SetParent(parentFrame) + + self:SetupAttributes() + self:RegisterStaticEvents() + + self:ApplyLayout() + self:ApplyStyle() + + self:UpdateDisplay() +end + +function ReActionButton.prototype:SetupAttributes() + local b = self.button + b:SetAttribute("type", "action") + b:SetAttribute("shift-type*", ATTRIBUTE_NOOP) + b:SetAttribute("checkselfcast", true) + b:SetAttribute("useparent-unit", true) +end + +function ReActionButton.prototype:RegisterStaticEvents() + self:RegisterEvent("PLAYER_ENTERING_WORLD") + self:RegisterEvent("ACTIONBAR_SLOT_CHANGED") + self:RegisterEvent("UPDATE_BINDINGS") + self:RegisterEvent("ACTIONBAR_SHOWGRID") + self:RegisterEvent("ACTIONBAR_HIDEGRID") +end + +function ReActionButton.prototype:RegisterActionEvents() + self:RegisterEvent("ACTIONBAR_UPDATE_STATE") + self:RegisterEvent("ACTIONBAR_UPDATE_USABLE") + self:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN", "ACTIONBAR_UPDATE_USABLE") + self:RegisterEvent("UPDATE_INVENTORY_ALERTS", "ACTIONBAR_UPDATE_USABLE") + self:RegisterEvent("PLAYER_AURAS_CHANGED", "ACTIONBAR_UPDATE_USABLE") + self:RegisterEvent("PLAYER_TARGET_CHANGED", "ACTIONBAR_UPDATE_USABLE") + self:RegisterEvent("UNIT_INVENTORY_CHANGED") + self:RegisterEvent("CRAFT_SHOW") + self:RegisterEvent("CRAFT_CLOSE", "CRAFT_SHOW") + self:RegisterEvent("TRADE_SKILL_SHOW", "CRAFT_SHOW") + self:RegisterEvent("TRADE_SKILL_CLOSE", "CRAFT_SHOW") + self:RegisterEvent("PLAYER_ENTER_COMBAT", "CRAFT_SHOW") + self:RegisterEvent("PLAYER_LEAVE_COMBAT") + self:RegisterEvent("START_AUTOREPEAT_SPELL") + self:RegisterEvent("STOP_AUTOREPEAT_SPELL") + + self.button:SetScript("OnUpdate", function() self:OnUpdate(arg1) end) + self.actionEventsRegistered = true +end + +function ReActionButton.prototype:UnregisterActionEvents() + self:UnregisterEvent("ACTIONBAR_UPDATE_STATE") + self:UnregisterEvent("ACTIONBAR_UPDATE_USABLE") + self:UnregisterEvent("ACTIONBAR_UPDATE_COOLDOWN") + self:UnregisterEvent("UPDATE_INVENTORY_ALERTS") + self:UnregisterEvent("PLAYER_AURAS_CHANGED") + self:UnregisterEvent("PLAYER_TARGET_CHANGED") + self:UnregisterEvent("UNIT_INVENTORY_CHANGED") + self:UnregisterEvent("CRAFT_SHOW") + self:UnregisterEvent("CRAFT_CLOSE") + self:UnregisterEvent("TRADE_SKILL_SHOW") + self:UnregisterEvent("TRADE_SKILL_CLOSE") + self:UnregisterEvent("PLAYER_ENTER_COMBAT") + self:UnregisterEvent("PLAYER_LEAVE_COMBAT") + self:UnregisterEvent("START_AUTOREPEAT_SPELL") + self:UnregisterEvent("STOP_AUTOREPEAT_SPELL") + + self.button:SetScript("OnUpdate", nil) + self.actionEventsRegistered = false +end + + +-- event handlers +function ReActionButton.prototype:ACTIONBAR_SLOT_CHANGED() + if arg1 == 0 or arg1 == self:GetActionID() then + self:UpdateDisplay() + end +end + +function ReActionButton.prototype:PLAYER_ENTERING_WORLD() + self:UpdateDisplay() +end + +function ReActionButton.prototype:UPDATE_BINDINGS() + self:UpdateDisplay() +end + +function ReActionButton.prototype:ACTIONBAR_SHOWGRID() + self:ShowGridTmp() +end + +function ReActionButton.prototype:ACTIONBAR_HIDEGRID() + self:HideGridTmp() +end + +function ReActionButton.prototype:ACTIONBAR_UPDATE_STATE() + self:UpdateCheckedState() +end + +function ReActionButton.prototype:ACTIONBAR_UPDATE_USABLE() + self:UpdateUsable() + self:UpdateCooldown() + self:ColorHotKey() +end + +function ReActionButton.prototype:UNIT_INVENTORY_CHANGED() + if arg1 == "player" then + self:UpdateDisplay() + end +end + +function ReActionButton.prototype:CRAFT_SHOW() + self:UpdateCheckedState() +end + +function ReActionButton.prototype:PLAYER_ENTER_COMBAT() + if IsAttackAction(self:GetActionID()) then + self:StartFlash() + end +end + +function ReActionButton.prototype:PLAYER_LEAVE_COMBAT() + if IsAttackAction(self:GetActionID()) then + self:StopFlash() + end +end + +function ReActionButton.prototype:START_AUTOREPEAT_SPELL() + if IsAutoRepeatAction(self:GetActionID()) then + self:StartFlash() + end +end + +function ReActionButton.prototype:STOP_AUTOREPEAT_SPELL() + if self:IsFlashing() and not IsAttackAction(self:GetActionID()) then + self:StopFlash() + end +end + + +-- OnUpdate handler +function ReActionButton.prototype:OnUpdate(elapsed) + local action = self:GetActionID() + local f = self.frames + + -- handle flashing + if self:IsFlashing() then + self.flashtime = self.flashtime - elapsed + if self.flashtime <= 0 then + local overtime = -self.flashtime + if overtime >= ATTACK_BUTTON_FLASH_TIME then + overtime = 0 + end + self.flashtime = ATTACK_BUTTON_FLASH_TIME - overtime + + if f.flash:IsVisible() then + f.flash:Hide() + else + f.flash:Show() + end + end + end + + -- Handle range indicator + if self.rangeTimer then + self.rangeTimer = self.rangeTimer - elapsed + if self.rangeTimer <= 0 then + self:ColorHotKey() + self:UpdateUsable() + self.rangeTimer = TOOLTIP_UPDATE_TIME + end + end + + -- handle toltip update + if self.tooltipTime then + self.tooltipTime = self.tooltipTime - elapsed + if self.tooltipTime <= 0 then + if GameTooltip:IsOwned(self.button) then + self:UpdateTooltip() + else + self.tooltipTime = nil + end + end + end +end + + + + +-- keybinding functions +function ReActionButton.prototype:SetKeyBinding( k ) + if k == nil or kbValidate(k) then + local current = self:GetKeyBinding() + ClearOverrideBindings(self.button) + if current then + SetBinding(current,nil) + end + if k then + SetBindingClick(k, self.name, "LeftButton") + end + end +end + +function ReActionButton.prototype:GetKeyBinding() + return GetBindingKey("CLICK "..self.name..":LeftButton") +end + +-- action ID functions +function ReActionButton.prototype:SetActionID( id ) + self.actionID = tonumber(id) -- force data integrity + self:ApplyActionID() +end + +function ReActionButton.prototype:GetActionID() + return self.actionID +end + +function ReActionButton.prototype:ApplyActionID() + local action = tonumber(self:GetActionID()) + self.button:SetAttribute("action",action) + self.frames.actionID:SetText(action or "") +end + +function ReActionButton.prototype:ShowActionID() + self.frames.actionID:Show() +end + +function ReActionButton.prototype:HideActionID() + self.frames.actionID:Hide() +end + +function ReActionButton:ShowAllActionIDs() -- class-wide function + for _, tbl in pairs(actionButtonTbl) do + if tbl.button then tbl.button:ShowActionID() end + end +end + +function ReActionButton:HideAllActionIDs() -- class-wide function + for _, tbl in pairs(actionButtonTbl) do + if tbl.button then tbl.button:HideActionID() end + end +end + + +-- action transfer functions +function ReActionButton.prototype:ShouldPickupAction(mouseButton) + return IsShiftKeyDown() and not SecureButton_GetModifiedAttribute(self.button, "type", mouseButton) +end + +function ReActionButton.prototype:ShowGridTmp() + self.showGridTmp_ = self.showGridTmp_ + 1 + self:UpdateVisibility() +end + +function ReActionButton.prototype:HideGridTmp() + self.showGridTmp_ = self.showGridTmp_ - 1 + self:UpdateVisibility() +end + +function ReActionButton.prototype:ShowGrid() + self.config.showGrid = true + self:UpdateVisibility() +end + +function ReActionButton.prototype:HideGrid() + self.config.showGrid = false + self:UpdateVisibility() +end + + + +-- layout & style functions +function ReActionButton.prototype:ApplyLayout() + local f = self.frames + + if self.config.keyBindLoc then + local h = f.hotkey + local loc = self.config.keyBindLoc + local top = string.match(loc,"TOP") + local bottom = string.match(loc, "BOTTOM") + h:ClearAllPoints() + h:SetWidth(40) + h:SetPoint(top or bottom,0,top and 2 or -2) + local j + if string.match(loc,"LEFT") then + j = "LEFT" + elseif string.match(loc,"RIGHT") then + j = "RIGHT" + else + j = "CENTER" + end + h:SetJustifyH(j) + end + + if self.config.stackCountLoc then + local c = f.count + local loc = self.config.stackCountLoc + local top = string.match(loc,"TOP") + local bottom = string.match(loc, "BOTTOM") + c:ClearAllPoints() + c:SetWidth(40) + c:SetPoint(top or bottom,0,top and 2 or -2) + local j + if string.match(loc,"LEFT") then + j = "LEFT" + elseif string.match(loc,"RIGHT") then + j = "RIGHT" + else + j = "CENTER" + end + c:SetJustifyH(j) + end + + if self.config.showKeyBind then + f.hotkey:Show() + else + f.hotkey:Hide() + end + + if self.config.showStackCount then + f.count:Show() + else + f.count:Hide() + end + +--[[ + if self.config.showNumericCooldown then + f.nCooldown:Show() + else + f.nCooldown:Hide() + end +]] + + if self.config.showMacroName then + f.macro:Show() + else + f.macro:Hide() + end +end + +function ReActionButton.prototype:ApplyStyle() + local f = self.frames + -- for now, just a static style + f.hotkey:SetFontObject(NumberFontNormal) + f.count:SetFontObject(NumberFontNormalYellow) +end + + + +-- start/stop flashing +function ReActionButton.prototype:StartFlash() + self.flashing = true + self.flashtime = 0 + self:UpdateCheckedState() +end + +function ReActionButton.prototype:StopFlash() + self.flashing = false + self.frames.flash:Hide() + self:UpdateCheckedState() +end + +function ReActionButton.prototype:IsFlashing() + return self.flashing +end + + + + + +-- set the tooltip +function ReActionButton.prototype:SetTooltip() + GameTooltip_SetDefaultAnchor(GameTooltip, self.button) + self:UpdateTooltip() +end + +function ReActionButton.prototype:ClearTooltip() + tooltipTime = nil + GameTooltip:Hide() +end + + + +-- colorize the hotkey +function ReActionButton.prototype:ColorHotKey() + local action = self:GetActionID() + local c = hotKeyDefaultColor + + if action and HasAction(action) then + if IsActionInRange(action) == 0 then + c = hotKeyOutOfRangeColor + elseif self.config.keyBindColorCode then + local modKey = string.match( self.frames.hotkey:GetText() or "", "([ACS])%-") + c = modKey and hotKeyModifierColors[modKey] or c + end + else + c = hotKeyDisabledColor + end + + self.frames.hotkey:SetTextColor(c.r, c.g, c.b) +end + + + + +-- display update functions +function ReActionButton.prototype:UpdateDisplay() + self:UpdateIcon() + self:UpdateHotkey() + self:UpdateCount() + self:UpdateMacroText() + self:UpdateUsable() + self:UpdateCooldown() + self:UpdateFlash() + self:UpdateEvents() + self:UpdateVisibility() + self:UpdateTooltip() +end + +function ReActionButton.prototype:UpdateIcon() + local f = self.frames + local b = self.button + + local action = self:GetActionID() + local texture = action and GetActionTexture(action) + + if action and texture then + f.icon:SetTexture(texture) + f.icon:Show() + self.rangeTimer = -1 + b:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2") + else + f.icon:Hide() + f.cooldown:Hide() + self.rangeTimer = nil + b:SetNormalTexture("Interface\\Buttons\\UI-Quickslot") + end + + self:UpdateCheckedState() + + -- Add a green border if action is an equipped item + if action and IsEquippedAction(action) then + local c = equippedActionBorderColor + f.border:SetVertexColor(c.r, c.g, c.b, c.a or 1) + f.border:Show() + else + f.border:Hide() + end +end + +function ReActionButton.prototype:UpdateCheckedState() + local action = self:GetActionID() + if action and (IsCurrentAction(action) or IsAutoRepeatAction(action)) then + self.button:SetChecked(1) + else + self.button:SetChecked(0) + end +end + + +function ReActionButton.prototype:UpdateHotkey() + local action = self:GetActionID() + local b = self.button + local f = self.frames + local key = self:GetKeyBinding() + local txt = GetBindingText(key, "KEY_",1) + + -- abbreviate long key names + for pat, rep in pairs(keybindAbbreviations) do + txt = string.gsub(txt,pat,rep) + end + + if txt then + f.hotkey:SetText(string.upper(txt)) + self:ColorHotKey() + else + f.hotkey:SetText("") + end +end + +function ReActionButton.prototype:UpdateCount() + local action = self:GetActionID() + if action and (IsConsumableAction(action) or IsStackableAction(action)) then + self.frames.count:SetText(GetActionCount(action)) + else + self.frames.count:SetText("") + end +end + +function ReActionButton.prototype:UpdateMacroText() + local action = self:GetActionID() + self.frames.macro:SetText(action and GetActionText(action) or "") +end + +function ReActionButton.prototype:UpdateUsable() + local f = self.frames + local action = self:GetActionID() + local isUsable, notEnoughMana + if action then + isUsable, notEnoughMana = IsUsableAction(action) + end + if isUsable then + local c = actionUsableColor + if IsActionInRange(action) == 0 then + c = actionOutOfRangeColor + else + f.normalTexture:SetVertexColor(c.r, c.g, c.b, c.a) + end + f.icon:SetVertexColor(c.r, c.g, c.b, c.a) + elseif notEnoughMana then + local c = actionNotEnoughManaColor + f.icon:SetVertexColor(c.r, c.g, c.b, c.a) + f.normalTexture:SetVertexColor(c.r, c.g, c.b, c.a) + else + local c = actionNotUsableColor + f.icon:SetVertexColor(c.r, c.g, c.b, c.a) + f.normalTexture:SetVertexColor(1.0, 1.0, 1.0) + end +end + +function ReActionButton.prototype:UpdateCooldown() + local action = self:GetActionID() + if action then + local start, duration, enable = GetActionCooldown(self:GetActionID()) + CooldownFrame_SetTimer(self.frames.cooldown, start, duration, enable) + -- do numeric cooldown stuff here + end +end + +function ReActionButton.prototype:UpdateFlash() + local b = self.button + local action = self:GetActionID() + if action and ((IsAttackAction(action) and IsCurrentAction(action)) or IsAutoRepeatAction(action)) then + self:StartFlash() + else + self:StopFlash() + end +end + +function ReActionButton.prototype:UpdateVisibility() + local action = self:GetActionID() + local b = self.button + + if b:GetAttribute("statehidden") then + b:Hide() + elseif action and HasAction(action) then + b:GetNormalTexture():SetAlpha(1.0) + b:Show() + elseif self.showGridTmp_ > 0 or self.config.showGrid then + b:GetNormalTexture():SetAlpha(0.5) + self.frames.cooldown:Hide() + b:Show() + else + b:Hide() + end +end + +function ReActionButton.prototype:UpdateEvents() + local action = self:GetActionID() + if action and HasAction(action) then + self:RegisterActionEvents() + elseif self.actionEventsRegistered then + self:UnregisterActionEvents() + end +end + +function ReActionButton.prototype:UpdateTooltip() + local action = self:GetActionID() + if GameTooltip:IsOwned(self.button) and action and GameTooltip:SetAction(action) then + self.tooltipTime = TOOLTIP_UPDATE_TIME + else + self.tooltipTime = nil + end +end + + +