X-Git-Url: https://www.aleksib.fi/git/wowui.git/blobdiff_plain/c711b1a7ea9d9cef2ba53a9eb4ddcfb5405126b2..f69c4b06781d9f2e389d45e4ed725c260cbc7f13:/OmaAB/ActionBars.lua?ds=inline diff --git a/OmaAB/ActionBars.lua b/OmaAB/ActionBars.lua index f61b9c7..95d326e 100644 --- a/OmaAB/ActionBars.lua +++ b/OmaAB/ActionBars.lua @@ -1,77 +1,450 @@ -- ActionBars.lua local _; -local min = math.min; -local CreateFrame = CreateFrame; -local UnitXP, UnitXPMax, GetXPExhaustion = UnitXP, UnitXPMax, GetXPExhaustion; -local ExhaustionToolTipText = ExhaustionToolTipText; +local pairs = pairs; +local GetActionTexture = GetActionTexture; +local GetActionLossOfControlCooldown = GetActionLossOfControlCooldown; +local GetActionCooldown, GetActionCharges = GetActionCooldown, GetActionCharges; +local IsConsumableAction, IsStackableAction = IsConsumableAction, IsStackableAction; +local IsItemAction, GetActionCount = IsItemAction, GetActionCount; +local HasAction, IsUsableAction = HasAction, IsUsableAction; +local IsCurrentAction, IsAutoRepeatAction = IsCurrentAction, IsAutoRepeatAction; +local RegisterStateDriver = RegisterStateDriver; +local CooldownFrame_Set, CooldownFrame_Clear = CooldownFrame_Set, CooldownFrame_Clear; +local CTimerAfter = C_Timer.After; local GameTooltip = nil; +local COOLDOWN_TYPE_LOSS_OF_CONTROL = COOLDOWN_TYPE_LOSS_OF_CONTROL; +local COOLDOWN_TYPE_NORMAL = COOLDOWN_TYPE_NORMAL; +local CDTexture = "Interface\\Cooldown\\edge"; +local locCDTexture = "Interface\\Cooldown\\edge-LoC"; -local width = 300; - -local ActionBars = CreateFrame("Frame", "OmaActionBars"); - -local function expBar(parent) - local frame = CreateFrame("Frame", "OmaExpBar", parent); - frame:SetPoint("BOTTOM"); - frame:SetWidth(width); - frame:SetHeight(10); - frame.base = frame:CreateTexture(nil, "BACKGROUND"); - frame.base:SetAllPoints(); - frame.base:SetColorTexture(0, 0, 0, 0.5); - frame.bar = frame:CreateTexture(nil, "BORDER"); - frame.bar:SetPoint("TOPLEFT", frame.base, "TOPLEFT"); - frame.bar:SetPoint("BOTTOMLEFT", frame.base, "BOTTOMLEFT"); - frame.bar:SetColorTexture(1, 1, 1); - frame.rest = frame:CreateTexture(nil, "BORDER"); - frame.rest:SetPoint("TOPLEFT", frame.bar, "TOPRIGHT"); - frame.rest:SetPoint("BOTTOMLEFT", frame.bar, "BOTTOMRIGHT"); - frame.rest:SetColorTexture(0, 0.3, 1, 0.7); - frame.text = frame:CreateFontString(nil, "ARTWORK", "GameFontHighlight"); - frame.text:SetPoint("BOTTOM"); - frame.text:Hide(); - - local function updateXP() - local xp, xpmax = UnitXP("player"), UnitXPMax("player"); - local rested = GetXPExhaustion(); - local current, restw = xp/xpmax*width, rested/xpmax*width; - local space = width - current; - frame.bar:SetWidth(current); - if rested then - frame.rest:SetWidth(min(space, restw)); - frame.rest:Show(); - frame.text:SetFormattedText("%d / %d (+%d)", xp, xpmax, rested/2); - frame.bar:SetVertexColor(0, 0.5, 1, 0.9); +local BUTTONLOCK = false; -- change to lock button dragging + +local settings = { + ["OmaBT1"] = { + start = 1, + length = 12, + x = 1000, + y = 1000, + }, + ["OmaBT2"] = { + start = 13, + length = 12, + x = 1000, + y = 960, + }, + ["OmaBT3"] = { + start = 25, + length = 12, + x = 1000, + y = 920, + }, + ["OmaBT4"] = { + start = 37, + length = 12, + x = 1000, + y = 880, + }, + ["OmaBT5"] = { + start = 49, + length = 12, + x = 1000, + y = 840, + }, + ["OmaBT6"] = { + start = 61, + length = 12, + x = 1000, + y = 800, + }, + ["OmaBT7"] = { + start = 73, + length = 12, + x = 1000, + y = 760, + }, + ["OmaBT8"] = { + start = 85, + length = 12, + x = 1000, + y = 720, + }, + ["OmaBT9"] = { + start = 97, + length = 12, + x = 1000, + y = 680, + }, + ["OmaBT10"] = { + start = 109, + length = 12, + x = 1000, + y = 640, + }, +}; + +local buttons = {}; +local activeButtons = {}; + +local ActionBars = CreateFrame("Frame", "OmaActionBars", UIParent); +local inheritedFrames = "SecureActionButtonTemplate,SecureHandlerDragTemplate,SecureHandlerStateTemplate"; + +local numChargeCDs = 0; +local function createChargeCD(parent) + numChargeCDs = numChargeCDs + 1; + local frame = CreateFrame("Cooldown", "OmaChargeCD"..numChargeCDs, parent, "CooldownFrameTemplate"); + frame:SetHideCountdownNumbers(false); + frame:SetDrawSwipe(false); + frame:SetAllPoints(parent); + frame:SetFrameStrata("TOOLTIP"); + return frame; +end + +local function clearChargeCD(parent) + if parent.chargecd then CooldownFrame_Clear(parent.chargecd) end +end + +local function startChargeCD(parent, start, duration, modrate) + if start == 0 then + return clearChargeCD(parent); + end + parent.chargecd = parent.chargecd or createChargeCD(parent); + CooldownFrame_Set(parent.chargecd, start, duration, true, true, modrate); +end + +local redoCooldown; + +local function updateCooldown(button, slot) + -- CD update from FrameXML/ActionButton.lua + local locstart, locduration = GetActionLossOfControlCooldown(slot); + local start, duration, enable, modrate = GetActionCooldown(slot); + local charges, maxcharges, chargestart, chargeduration, chargemodrate = GetActionCharges(slot); + if (locstart + locduration) > (start + duration) then + if button.cd.currentCooldownType ~= COOLDOWN_TYPE_LOSS_OF_CONTROL then + button.cd:SetEdgeTexture(locCDTexture); + button.cd:SetSwipeColor(0.17, 0, 0); + button.cd:SetHideCountdownNumbers(true); + button.cd.currentCooldownType = COOLDOWN_TYPE_LOSS_OF_CONTROL; + end + + CooldownFrame_Set(button.cd, locstart, locduration, true, true, modrate); + clearChargeCD(button); + else + if button.cd.currentCooldownType ~= COOLDOWN_TYPE_NORMAL then + button.cd:SetEdgeTexture(CDTexture); + button.cd:SetSwipeColor(0, 0, 0); + button.cd:SetHideCountdownNumbers(false); + button.cd.currentCooldownType = COOLDOWN_TYPE_NORMAL; + end + + if locstart > 0 then + button.cd:SetScript("OnCooldownDone", redoCooldown); + end + if charges and maxcharges and maxcharges > 1 and charges < maxcharges then + startChargeCD(button, chargestart, chargeduration, chargemodrate); + else + clearChargeCD(button); + end + CooldownFrame_Set(button.cd, start, duration, enable, false, modrate); + end +end + +local function redoCooldown(cd) + local button = cd:GetParent(); + cd:SetScript("OnCooldownDone", nil); + updateCooldown(button, button.slot); +end + +local function updateCount(button, slot) + if IsConsumableAction(slot) or IsStackableAction(slot) or + (not IsItemAction(slot) and GetActionCount(slot) > 0) then + local count = GetActionCount(slot); + if count > 99 then + button.count:SetText("*"); + else + button.count:SetText(count); + end + else + local charges, maxcharges = GetActionCharges(slot); + if maxcharges > 1 then + button.count:SetText(charges); + else + button.count:SetText(""); + end + end +end + +local function updateUsable(button, slot) + local isUsable, noMana = IsUsableAction(slot); + if isUsable then + button.icon:SetVertexColor(1, 1, 1); + elseif noMana then + button.icon:SetVertexColor(0, 0.5, 1); + else + button.icon:SetVertexColor(0.4, 0.4, 0.4); + end +end + +local function updateState(button, slot) + if IsCurrentAction(slot) or IsAutoRepeatAction(slot) then + button:SetChecked(true); + else + button:SetChecked(false); + end +end + +local function updateButton(button, slot) + if HasAction(slot) then + activeButtons[slot] = button; + button.base:Show(); + button.icon:SetTexture(GetActionTexture(slot)); + updateCooldown(button, slot); + updateUsable(button, slot); + updateState(button, slot); + updateCount(button, slot); + else + activeButtons[slot] = nil; + if not button.grid then button.base:Hide() end + button.icon:SetTexture(nil); + button.cd:Hide(); + button.count:SetText(""); + end +end + +local function createActionBar(parent, name, config) + local prev; + for slot = config.start, config.start+config.length-1 do + local secure = CreateFrame("CheckButton", name..slot, parent, inheritedFrames); + secure.slot = slot; + if slot == config.start then + secure:SetPoint("TOPLEFT", parent, "BOTTOMLEFT", config.x, config.y); + else + secure:SetPoint("TOPLEFT", prev, "TOPRIGHT"); + end + secure:RegisterForClicks("AnyUp"); + if not BUTTONLOCK then + secure:RegisterForDrag("LeftButton", "RightButton"); + end + secure:SetWidth(32); + secure:SetHeight(32); + secure.base = secure:CreateTexture(nil, "BACKGROUND"); + secure.base:SetAllPoints(); + secure.base:SetColorTexture(0, 0, 0, 0.5); + secure.iconbase = secure:CreateTexture(nil, "BORDER"); + secure.iconbase:SetPoint("TOPLEFT", secure.base, "TOPLEFT", 1, -1); + secure.iconbase:SetPoint("BOTTOMRIGHT", secure.base, "BOTTOMRIGHT", -1, 1); + secure.iconbase:SetColorTexture(0, 0, 0, 0.5); + secure.iconbase:Hide(); + secure.icon = secure:CreateTexture(nil, "ARTWORK"); + secure.icon:SetPoint("TOPLEFT", secure.iconbase, "TOPLEFT"); + secure.icon:SetPoint("BOTTOMRIGHT", secure.iconbase, "BOTTOMRIGHT"); + secure.icon:SetTexCoord(0.07, 0.93, 0.07, 0.93); + secure:SetCheckedTexture("Interface\\Buttons\\CheckButtonHilight"); + secure.hotkey = secure:CreateFontString(nil, "OVERLAY", "NumberFontNormalGray"); + secure.hotkey:SetPoint("TOPRIGHT", secure, "TOPRIGHT", 2, -1); + secure.count = secure:CreateFontString(nil, "OVERLAY", "NumberFontNormal"); + secure.count:SetPoint("BOTTOMRIGHT", secure, "BOTTOMRIGHT", 2, -1); + secure.cd = CreateFrame("Cooldown", name..slot.."CD", secure, "CooldownFrameTemplate"); + secure.cd:SetAllPoints(); + secure:SetAttribute("type", "action"); + secure:SetAttribute("action", slot); + function secure:ActionChanged() + self.slot = self:GetAttribute("action"); + return updateButton(self, self.slot); + end + secure:ActionChanged(); -- initial update + -- FrameXML/SecureHandlers.lua has arguments and return value + -- args: self, button, kind, value, ... (kind, value, ... from GetCursorInfo()) + -- returns: kind, target, detail + -- or: "clear", kind, target, detail + -- used for Pickup* functions + -- some of these snippets based on LibActionButton-1.0 + secure:SetAttribute("_ondragstart", [=[ + return "action", self:GetAttribute("action"); + ]=]); + secure:SetAttribute("_onreceivedrag", [=[ + if not kind or not value then return nil end + return "action", self:GetAttribute("action"); + ]=]); + -- pre-wrapper can pass a message to post-wrapper + secure:WrapScript(secure, "OnDragStart", [=[ + local kind, value = GetActionInfo(self:GetAttribute("action")); + return "message", format("%s|%s", tostring(kind), tostring(value)); + ]=], [=[ + local kind, value = GetActionInfo(self:GetAttribute("action")); + if message ~= format("%s|%s", tostring(kind), tostring(value)) then + self:CallMethod("ActionChanged"); + end + ]=]); + secure:WrapScript(secure, "OnReceiveDrag", [=[ + local kind, value = GetActionInfo(self:GetAttribute("action")); + return "message", format("%s|%s", tostring(kind), tostring(value)); + ]=], [=[ + local kind, value = GetActionInfo(self:GetAttribute("action")); + if message ~= format("%s|%s", tostring(kind), tostring(value)) then + self:CallMethod("ActionChanged"); + end + ]=]); + secure:WrapScript(secure, "OnClick", [=[ + local kind, value = GetActionInfo(self:GetAttribute("action")); + return nil, format("%s|%s", tostring(kind), tostring(value)); + ]=], [=[ + local kind, value = GetActionInfo(self:GetAttribute("action")); + if message ~= format("%s|%s", tostring(kind), tostring(value)) then + self:CallMethod("ActionChanged"); + end + ]=]); + if slot < 13 then + -- first action bar has possible states based on vehicle/possess etc. + secure:SetAttribute("origaction", slot); + secure:SetAttribute("_onstate-possess", [=[ + if newstate == "possess" then + local slot; + if HasVehicleActionBar() then + slot = (GetVehicleBarIndex()-1)*12+self:GetAttribute("origaction"); + elseif HasOverrideActionBar() then + slot = (GetOverrideBarIndex()-1)*12+self:GetAttribute("origaction"); + elseif HasTempShapeshitftActionBar() then + slot = (GetTempShapeshiftBarIndex()-1)*12+self:GetAttribute("origaction"); + else + -- something wrong, just revert to normal + print("Possess bar index not found"); + slot = self:GetAttribute("origaction"); + end + self:SetAttribute("action", slot); + else + self:SetAttribute("action", self:GetAttribute("origaction")); + end + self:CallMethod("ActionChanged"); + ]=]); + RegisterStateDriver(secure, "possess", "[overridebar][possessbar][shapeshift] possess; normal"); else - frame.rest:Hide(); - frame.text:SetFormattedText("%d / %d", xp, xpmax); - frame.bar:SetVertexColor(0.6, 0.2, 1, 0.9); + function secure:ShowButton() if HasAction(slot) then activeButtons[slot] = self end end + function secure:HideButton() activeButtons[slot] = nil end + -- all other action bar are hidden if with overridebar or vehicleui (not shapeshift, possessbar) + -- default Bartender4 options + secure:SetAttribute("_onstate-possess", [=[ + if newstate == "possess" then + self:Hide(); + self:CallMethod("HideButton"); + else + self:Show(); + self:CallMethod("ShowButton"); + end + ]=]); + RegisterStateDriver(secure, "possess", "[overridebar][vehicleui] possess; normal"); end + buttons[slot] = secure; + prev = secure; end - updateXP(); - - frame:SetScript("OnEvent", function(self, event) - if event == "PLAYER_XP_UPDATE" or event == "PLAYER_LEVEL_UP" then - updateXP(); - end - end); - frame:RegisterEvent("PLAYER_XP_UPDATE"); - frame:RegisterEvent("PLAYER_LEVEL_UP"); - -- from FrameXML/MainMenuBar.lua - frame:SetScript("OnEnter", function(frame) frame.text:Show(); ExhaustionToolTipText(); end); - frame:SetScript("OnLeave", function(frame) frame.text:Hide(); GameTooltip:Hide(); end); end local function initialize() - -- let other addons hook this to anchor tooltip elsewhere - GameTooltip = _G["GameTooltip"]; - if UnitLevel("player") < 110 and not IsXPUserDisabled() then - expBar(UIParent); + ActionBars:SetFrameStrata("LOW"); + ActionBars:SetPoint("BOTTOMLEFT"); + ActionBars:SetWidth(1); + ActionBars:SetHeight(1); + for name, config in pairs(settings) do + createActionBar(ActionBars, name, config); end end -ActionBars:RegisterEvent("PLAYER_LOGIN"); -ActionBars:SetScript("OnEvent", function(self, event) - if event == "PLAYER_LOGIN" then +local throttleCD = false; +local function throttleCDDone() throttleCD = false end + +local events = { + ["ACTIONBAR_UPDATE_COOLDOWN"] = function() + if not throttleCD then -- only update at most once/frame + throttleCD = true; + for _, button in pairs(activeButtons) do + updateCooldown(button, button.slot); + end + CTimerAfter(0.01, throttleCDDone); -- wait one frame + end + end, + ["SPELL_UPDATE_CHARGES"] = function() + for _, button in pairs(activeButtons) do + updateCount(button, button.slot); + end + end, + ["ACTIONBAR_SLOT_CHANGED"] = function(slot) + if buttons[slot] then buttons[slot]:ActionChanged() end + end, + ["ACTIONBAR_SHOWGRID"] = function() + for _, button in pairs(buttons) do + button.grid = true; + button.iconbase:Show(); + if not activeButtons[button.slot] then button.base:Show() end + end + end, + ["ACTIONBAR_HIDEGRID"] = function() + for _, button in pairs(buttons) do + button.grid = nil; + button.iconbase:Hide(); + if not activeButtons[button.slot] then button.base:Hide() end + end + end, + ["ACTIONBAR_UPDATE_STATE"] = function() + for _, button in pairs(activeButtons) do + updateState(button, button.slot); + end + end, + ["ACTIONBAR_UPDATE_USABLE"] = function() + for _, button in pairs(activeButtons) do + updateUsable(button, button.slot); + end + end, + ["UPDATE_OVERRIDE_ACTIONBAR"] = function() + if buttons[1] then -- called before PLAYER_LOGIN + for _, button in pairs(buttons) do + updateButton(button, button.slot); + end + end + end, + ["UPDATE_BINDINGS"] = function() + end, + ["UPDATE_ALL_BUTTONS"] = function() + for _, button in pairs(buttons) do + updateButton(button, button.slot); + end + end, + ["PLAYER_LOGIN"] = function() + GameTooltip = _G["GameTooltip"]; initialize(); - end + end, +}; +events["LOSS_OF_CONTROL_ADDED"] = events["ACTIONBAR_UPDATE_COOLDOWN"]; +events["LOSS_OF_CONTROL_UPDATE"] = events["ACTIONBAR_UPDATE_COOLDOWN"]; -- TODO might change once tooltips are in +events["PLAYER_MOUNT_DISPLAY_CHANGED"] = events["ACTIONBAR_UPDATE_USABLE"]; +events["UPDATE_VEHICLE_ACTIONBAR"] = events["UPDATE_ALL_BUTTONS"]; +events["SPELL_UPDATE_ICON"] = events["UPDATE_ALL_BUTTONS"]; +events["PET_STABLE_UPDATE"] = events["UPDATE_ALL_BUTTONS"]; +events["PET_STABLE_SHOW"] = events["UPDATE_ALL_BUTTONS"]; +events["PLAYER_SPECIALIZATION_CHANGED"] = events["UPDATE_ALL_BUTTONS"]; +events["UNIT_ENTERED_VEHICLE"] = function(unit) + if unit == "player" then events["ACTIONBAR_UPDATE_STATE"]() end +end +events["UNIT_EXITED_VEHICLE"] = events["UNIT_ENTERED_VEHICLE"]; +-- TODO COMPANION_UPDATE needed? +-- TODO overlay glow ? don't exactly know what it does, proc highlight? +-- autorepeat, tooltips + +ActionBars:RegisterEvent("PLAYER_LOGIN"); +ActionBars:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN"); +ActionBars:RegisterEvent("ACTIONBAR_UPDATE_USABLE"); +ActionBars:RegisterEvent("ACTIONBAR_UPDATE_STATE"); +ActionBars:RegisterEvent("ACTIONBAR_SLOT_CHANGED"); +ActionBars:RegisterEvent("ACTIONBAR_SHOWGRID"); +ActionBars:RegisterEvent("ACTIONBAR_HIDEGRID"); +ActionBars:RegisterEvent("SPELL_UPDATE_ICON"); +ActionBars:RegisterEvent("SPELL_UPDATE_CHARGES"); +ActionBars:RegisterEvent("UPDATE_VEHICLE_ACTIONBAR"); +ActionBars:RegisterEvent("UPDATE_OVERRIDE_ACTIONBAR"); +ActionBars:RegisterEvent("PLAYER_MOUNT_DISPLAY_CHANGED"); +ActionBars:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED"); +ActionBars:RegisterEvent("UNIT_ENTERED_VEHICLE"); +ActionBars:RegisterEvent("UNIT_EXITED_VEHICLE"); +ActionBars:RegisterEvent("PET_STABLE_UPDATE"); +ActionBars:RegisterEvent("PET_STABLE_SHOW"); +ActionBars:SetScript("OnEvent", function(self, event, arg1) + events[event](arg1); end);