-- 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);