diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..274656fa --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +/.idea \ No newline at end of file diff --git a/api/api.lua b/api/api.lua index 5a3c88d1..e734629f 100644 --- a/api/api.lua +++ b/api/api.lua @@ -1197,10 +1197,45 @@ end -- returns: array of options as strings for pfUI.gui.bar function pfUI.api.BarLayoutOptions(barsize) assert(barsize > 0 and barsize <= NUM_ACTIONBAR_BUTTONS,"BarLayoutOptions: barsize "..tostring(barsize).." is invalid") - local options = {} - for i,layout in ipairs(pfGridmath[barsize]) do - options[i] = string.format("%d x %d",layout[1],layout[2]) + local options, seen = {}, {} + + local function add(option) + if not seen[option] then + table.insert(options, option) + seen[option] = true + end end + + for i,layout in ipairs(pfGridmath[barsize]) do + add(string.format("%d x %d",layout[1],layout[2])) + end + + for _, option in ipairs({ + "1 x 12", "1 x 10", "2 x 10", "2 x 5", "3 x 10", "3 x 5", "3 x 3", + "5 x 3", "10 x 3", "5 x 2", "10 x 2", "10 x 1", "12 x 1" + }) do + add(option) + end + + table.sort(options, function(a, b) + local _, _, ac, ar = string.find(a, "(%d+)%s*x%s*(%d+)") + local _, _, bc, br = string.find(b, "(%d+)%s*x%s*(%d+)") + ac, ar = tonumber(ac) or 1, tonumber(ar) or 1 + bc, br = tonumber(bc) or 1, tonumber(br) or 1 + + local ah = ac >= ar and 0 or 1 + local bh = bc >= br and 0 or 1 + + if ah ~= bh then return ah < bh end + if ah == 0 then -- horizontal first: wider to narrower + if ac ~= bc then return ac > bc end + return ar < br + else -- vertical: taller to shorter + if ar ~= br then return ar > br end + return ac < bc + end + end) + return options end @@ -1225,15 +1260,45 @@ function pfUI.api.BarLayoutFormfactor(option) end end +local function ResolveBarLayout(barsize, formfactor, uneven) + local layout = tostring(formfactor or "") + local _, _, a, b = string.find(layout, "(%d+)%s*x%s*(%d+)") + + if a and b then + local cols, rows = tonumber(a), tonumber(b) + cols = math.min(NUM_ACTIONBAR_BUTTONS, math.max(1, cols)) + rows = math.min(NUM_ACTIONBAR_BUTTONS, math.max(1, rows)) + local orientation = string.upper(uneven or "DOWN") + local mode = (orientation == "LEFT" or orientation == "RIGHT") and "cols" or "rows" + + if mode == "rows" then + cols = math.max(1, math.min(cols, barsize)) + rows = math.max(1, math.ceil(barsize / cols)) + else + rows = math.max(1, math.min(rows, barsize)) + cols = math.max(1, math.ceil(barsize / rows)) + end + + return cols, rows, mode, orientation + end + + local index = pfUI.api.BarLayoutFormfactor(layout) + if not index then return nil end + local cols, rows = unpack(pfGridmath[barsize][index]) + return cols, rows, "rows", "DOWN" +end + -- [ Bar Layout Size ] -- -- 'bar' frame reference, -- 'barsize' integer number of buttons, -- 'formfactor' string formfactor in cols x rows, -- 'padding' the spacing between buttons -function pfUI.api.BarLayoutSize(bar,barsize,formfactor,iconsize,bordersize,padding) +function pfUI.api.BarLayoutSize(bar,barsize,formfactor,iconsize,bordersize,padding,uneven) assert(barsize > 0 and barsize <= NUM_ACTIONBAR_BUTTONS,"BarLayoutSize: barsize "..tostring(barsize).." is invalid") - local formfactor = pfUI.api.BarLayoutFormfactor(formfactor) - local cols, rows = unpack(pfGridmath[barsize][formfactor]) + local cols, rows = ResolveBarLayout(barsize, formfactor, uneven) + if not cols or not rows then + cols, rows = unpack(pfGridmath[barsize][1]) + end local width = (iconsize + bordersize*2+padding) * cols + padding local height = (iconsize + bordersize*2+padding) * rows + padding bar._size = {width,height} @@ -1248,16 +1313,71 @@ end -- 'iconsize' size of the button -- 'bordersize' default bordersize -- 'padding' the spacing between buttons -function pfUI.api.BarButtonAnchor(button,basename,buttonindex,barsize,formfactor,iconsize,bordersize,padding) +function pfUI.api.BarButtonAnchor(button,basename,buttonindex,barsize,formfactor,iconsize,bordersize,padding,uneven) assert(barsize > 0 and barsize <= NUM_ACTIONBAR_BUTTONS,"BarButtonAnchor: barsize "..tostring(barsize).." is invalid") - local formfactor = pfUI.api.BarLayoutFormfactor(formfactor) + local cols, rows, mode, orientation = ResolveBarLayout(barsize, formfactor, uneven) + if not cols or not rows then + cols, rows, mode, orientation = unpack(pfGridmath[barsize][1]), "rows", "DOWN" + end local parent = button:GetParent() - local cols, rows = unpack(pfGridmath[barsize][formfactor]) - if buttonindex == 1 then - button._anchor = {"TOPLEFT", parent, "TOPLEFT", bordersize+padding, -bordersize-padding} + local step = iconsize + bordersize*2 + padding + + local row, col + if mode == "cols" then + col = math.ceil(buttonindex / rows) + row = buttonindex - ((col-1) * rows) else - local col = buttonindex-((math.ceil(buttonindex/cols)-1)*cols) - button._anchor = col==1 and {"TOP",_G[basename..(buttonindex-cols)],"BOTTOM",0,-(bordersize*2+padding)} or {"LEFT",_G[basename..(buttonindex-1)],"RIGHT",(bordersize*2+padding),0} + row = math.ceil(buttonindex / cols) + col = buttonindex - ((row-1) * cols) + end + + -- move only the uneven remainder to the selected edge without reversing full order + if mode == "cols" and orientation == "LEFT" then + local final_col = math.ceil(barsize / rows) + local count = barsize - (final_col - 1) * rows + if count > 0 and count < rows then + if col == final_col then + col = 1 + else + col = col + 1 + end + end + elseif mode == "rows" and orientation == "UP" then + local final_row = math.ceil(barsize / cols) + local count = barsize - (final_row - 1) * cols + if count > 0 and count < cols then + if row == final_row then + row = 1 + else + row = row + 1 + end + end + end + + local x = bordersize + padding + (col - 1) * step + local y = -bordersize - padding - (row - 1) * step + + if buttonindex == 1 then + button._anchor = {"TOPLEFT", parent, "TOPLEFT", x, y} + else + button._anchor = {"TOPLEFT", parent, "TOPLEFT", x, y} + + -- center partially filled final row/column + if mode == "cols" then + local final_col = math.ceil(barsize / rows) + local count = barsize - (final_col - 1) * rows + local target_col = (orientation == "LEFT") and 1 or cols + if count > 0 and count < rows and col == target_col and row == 1 then + button._anchor[5] = button._anchor[5] - (rows - count) * step / 2 + end + else + local final_row = math.ceil(barsize / cols) + local count = barsize - (final_row - 1) * cols + local target_row = (orientation == "UP") and 1 or rows + if count > 0 and count < cols and row == target_row and col == 1 then + button._anchor[4] = button._anchor[4] + (cols - count) * step / 2 + end + end end return button._anchor end @@ -1510,4 +1630,4 @@ function pfUI.api.TryMemoizedFuncLoadstringForSpellCasts(funcOrStr) end return nil -end \ No newline at end of file +end diff --git a/api/ui-widgets.lua b/api/ui-widgets.lua index ba8dec6b..6ebf7be6 100644 --- a/api/ui-widgets.lua +++ b/api/ui-widgets.lua @@ -8,7 +8,8 @@ do -- statusbars local animate = CreateFrame("Frame", "pfStatusBarAnimation", UIParent) animate:SetScript("OnUpdate", function() - stepsize = tonumber(pfUI_config.unitframes.animation_speed) + local unitframes = pfUI_config and pfUI_config.unitframes + stepsize = tonumber(unitframes and unitframes.animation_speed) or 20 for bar in pairs(animations) do if not bar.val_ or abs(bar.val_ - bar.val) < stepsize or bar.instant then diff --git a/env/tables.lua b/env/tables.lua index 648fad0f..a8f454ce 100644 --- a/env/tables.lua +++ b/env/tables.lua @@ -33,15 +33,15 @@ CLASS_ICON_TCOORDS = CLASS_ICON_TCOORDS or { pfGridmath = { [1] = {{1,1}}, [2] = {{2,1},{1,2}}, - [3] = {{3,1},{1,3}}, + [3] = {{3,1},{2,2},{1,3}}, [4] = {{4,1},{2,2},{1,4}}, - [5] = {{5,1},{1,5}}, + [5] = {{5,1},{3,2},{2,3},{1,5}}, [6] = {{6,1},{3,2},{2,3},{1,6}}, - [7] = {{7,1},{1,7}}, - [8] = {{8,1},{4,2},{2,4},{1,8}}, - [9] = {{9,1},{3,3},{1,9}}, - [10] = {{10,1},{5,2},{2,5},{1,10}}, - [11] = {{11,1},{1,11}}, + [7] = {{7,1},{4,2},{3,3},{2,4},{1,7}}, + [8] = {{8,1},{4,2},{3,3},{2,4},{1,8}}, + [9] = {{9,1},{5,2},{3,3},{2,5},{1,9}}, + [10] = {{10,1},{5,2},{4,3},{3,4},{2,5},{1,10}}, + [11] = {{11,1},{6,2},{4,3},{3,4},{2,6},{1,11}}, [12] = {{12,1},{6,2},{4,3},{3,4},{2,6},{1,12}} } diff --git a/modules/actionbar.lua b/modules/actionbar.lua index 222b32a1..990c246a 100644 --- a/modules/actionbar.lua +++ b/modules/actionbar.lua @@ -1276,6 +1276,26 @@ pfUI:RegisterModule("actionbar", "vanilla", function () local spacing = C.bars["bar"..i].spacing local background = C.bars["bar"..i].background local formfactor = C.bars["bar"..i].formfactor + local uneven = C.bars["bar"..i].uneven + local _, _, cols, rows = string.find(tostring(formfactor or ""), "(%d+)%s*x%s*(%d+)") + cols, rows = tonumber(cols), tonumber(rows) + local is_square = cols == 3 and rows == 3 + local is_vertical = cols and cols <= 3 and not is_square + local is_horizontal = rows and rows <= 3 and not is_square + + if is_vertical then + if uneven ~= "Up" and uneven ~= "Down" then + uneven = "Up" + end + elseif is_horizontal then + if uneven ~= "Left" and uneven ~= "Right" then + uneven = "Left" + end + elseif not uneven then + uneven = "Up" + end + + C.bars["bar"..i].uneven = uneven local autohide = C.bars["bar"..i].autohide local hide_time = C.bars["bar"..i].hide_time local hide_combat = C.bars["bar"..i].hide_combat == "1" and true or nil @@ -1298,7 +1318,8 @@ pfUI:RegisterModule("actionbar", "vanilla", function () end -- the stored layout is invalid, temporary fallback - if not pfGridmath[buttons][BarLayoutFormfactor(formfactor)] then + local _, _, fcols, frows = string.find(tostring(formfactor or ""), "(%d+)%s*x%s*(%d+)") + if not ((fcols and frows) or pfGridmath[buttons][BarLayoutFormfactor(formfactor)]) then formfactor = BarLayoutOptions(buttons)[1] end @@ -1407,7 +1428,7 @@ pfUI:RegisterModule("actionbar", "vanilla", function () bars[i][j] = CreateActionButton(bars[i], i, j) bars[i][j].bar = i - BarButtonAnchor(bars[i][j], buttonbasename, j, buttons, formfactor, size, border, spacing) + BarButtonAnchor(bars[i][j], buttonbasename, j, buttons, formfactor, size, border, spacing, uneven) bars[i][j]:ClearAllPoints() bars[i][j]:SetPoint(unpack(bars[i][j]._anchor)) bars[i][j]:Show() @@ -1438,7 +1459,7 @@ pfUI:RegisterModule("actionbar", "vanilla", function () end -- adjust actionbar size - BarLayoutSize(bars[i], buttons, formfactor, size, border, spacing) + BarLayoutSize(bars[i], buttons, formfactor, size, border, spacing, uneven) bars[i]:SetWidth(bars[i]._size[1]) bars[i]:SetHeight(bars[i]._size[2]) bars[i]:ClearAllPoints() @@ -1752,4 +1773,4 @@ pfUI:RegisterModule("actionbar", "vanilla", function () return reagent_counts[reagent_slots[slot]] end end -end) \ No newline at end of file +end) diff --git a/modules/gui.lua b/modules/gui.lua index 72013edf..140f3ae4 100644 --- a/modules/gui.lua +++ b/modules/gui.lua @@ -2090,7 +2090,7 @@ pfUI:RegisterModule("gui", "vanilla:tbc", function () CreateConfig(nil, T["Offhand Bar Color"], C.unitframes, "swingtimerohcolor", "color") CreateConfig(nil, T["Ranged Bar Color"], C.unitframes, "swingtimerrangedcolor", "color") CreateConfig(nil, T["Ranged Warn Color (Hunter)"], C.unitframes, "swingtimerrangedwarncolor", "color") - CreateConfig(nil, T["Show HS/Cleave Queue Color (Warrior)"], C.unitframes, "swingtimerhsqueue", "checkbox") + CreateConfig(nil, T["Show On Next Attack Queue Color"], C.unitframes, "swingtimerhsqueue", "checkbox") CreateConfig(nil, T["Show Attack Speed"], C.unitframes, "swingtimerattackspeed", "checkbox") CreateConfig(nil, "Mark Tracking", nil, nil, "header") @@ -2609,6 +2609,21 @@ pfUI:RegisterModule("gui", "vanilla:tbc", function () local formfactors = function() return BarLayoutOptions(tonumber(C.bars["bar"..id].buttons) or id < 11 and NUM_ACTIONBAR_BUTTONS or id > 11 and NUM_SHAPESHIFT_SLOTS or NUM_PET_ACTION_SLOTS) end + local uneven_options = function() + local formfactor = tostring(C.bars["bar"..id].formfactor or "") + local _, _, cols, rows = string.find(formfactor, "(%d+)%s*x%s*(%d+)") + cols, rows = tonumber(cols), tonumber(rows) + + if cols == 3 and rows == 3 then + return { "Up", "Down", "Left", "Right" } + elseif cols and cols <= 3 then + return { "Up", "Down" } + elseif rows and rows <= 3 then + return { "Left", "Right" } + end + + return { "Up", "Down", "Left", "Right" } + end CreateGUIEntry(T["Actionbar"], caption, function() CreateConfig(U["bars"], T["Enable"], C.bars["bar"..id], "enable", "checkbox") @@ -2627,6 +2642,7 @@ pfUI:RegisterModule("gui", "vanilla:tbc", function () CreateConfig(U["bars"], T["Icon Size"], C.bars["bar"..id], "icon_size") CreateConfig(U["bars"], T["Spacing"], C.bars["bar"..id], "spacing", "dropdown", pfUI.gui.dropdowns.actionbarbuttons) CreateConfig(U["bars"], T["Layout"], C.bars["bar"..id], "formfactor", "dropdown", formfactors) + CreateConfig(U["bars"], T["Layout Uneven Orientation"], C.bars["bar"..id], "uneven", "dropdown", uneven_options) CreateConfig(U["bars"], T["Bar Background"], C.bars["bar"..id], "background", "checkbox") CreateConfig(U["bars"], T["Show Hotkey Text"], C.bars["bar"..id], "showkeybind", "checkbox") @@ -2978,4 +2994,4 @@ pfUI:RegisterModule("gui", "vanilla:tbc", function () end end) end -end) \ No newline at end of file +end) diff --git a/modules/swingtimer.lua b/modules/swingtimer.lua index d5187fd1..b8adec05 100644 --- a/modules/swingtimer.lua +++ b/modules/swingtimer.lua @@ -22,8 +22,9 @@ pfUI:RegisterModule("swingtimer", "vanilla:tbc", function () inCombat = false, pendingCastSpellId = nil, mhFrozenAt = nil, - hsQueued = false, cleaveQueued = false, + hsQueued = false, cleaveQueued = false, maulQueued = false, isWarrior = false, + isDruid = false, cachedHSSlots = {}, cachedCleaveSlots = {}, useSpellQueueEvent = false, playerGUID = nil, @@ -64,7 +65,16 @@ pfUI:RegisterModule("swingtimer", "vanilla:tbc", function () local cleaveSpellIDs = { [845] = true, [7369] = true, [11608] = true, [11609] = true, [20569] = true, - } + } + + -- Maul spell IDs (all ranks 1 to 7) + local maulSpellIDs = { + [6807] = true, + [6808] = true, + [6809] = true, + [8972] = true, + [9745] = true, [9880] = true, [9881] = true, + } -- Read config local sw_width = tonumber(C.unitframes.swingtimerwidth) or 200 @@ -506,12 +516,16 @@ pfUI:RegisterModule("swingtimer", "vanilla:tbc", function () -- HS/Cleave color local curR, curG, curB = mhDefaultR, mhDefaultG, mhDefaultB - if sw_hsqueue and S.isWarrior then - local hs, cl = IsHSOrCleaveQueued() - if cl then - curR, curG, curB = 0.2, 0.9, 0.2 - elseif hs then - curR, curG, curB = 0.9, 0.9, 0.2 + if sw_hsqueue then + if S.maulQueued and S.isDruid then + curR, curG, curB = 1.0, 0.55, 0.0 -- orange for druid maul queue + elseif S.isWarrior then + local hs, cl = IsHSOrCleaveQueued() + if cl then + curR, curG, curB = 0.2, 0.9, 0.2 + elseif hs then + curR, curG, curB = 0.9, 0.9, 0.2 + end end end @@ -692,8 +706,9 @@ pfUI:RegisterModule("swingtimer", "vanilla:tbc", function () -- Slam delays auto-attack but does NOT reset the swing timer. Ignore. S.pendingCastSpellId = nil return - elseif hsSpellIDs[spellId] or IsOnSwingSpell(spellId) then + elseif hsSpellIDs[spellId] or cleaveSpellIDs[spellId] or maulSpellIDs[spellId] or IsOnSwingSpell(spellId) then S.hsQueued = false; S.cleaveQueued = false + S.maulQueued = false ResetMH() elseif cleaveSpellIDs[spellId] then S.hsQueued = false; S.cleaveQueued = false @@ -716,11 +731,12 @@ pfUI:RegisterModule("swingtimer", "vanilla:tbc", function () -- SPELL_CAST_EVENT hook: HS/Cleave queue tracking pfUI.libdebuff_spell_cast_hooks = pfUI.libdebuff_spell_cast_hooks or {} pfUI.libdebuff_spell_cast_hooks["swingtimer"] = function(success, spellId) - if success ~= 1 then return end if hsSpellIDs[spellId] then - S.hsQueued = true; S.cleaveQueued = false + S.hsQueued = true; S.cleaveQueued = false; S.maulQueued = false elseif cleaveSpellIDs[spellId] then - S.cleaveQueued = true; S.hsQueued = false + S.cleaveQueued = true; S.hsQueued = false; S.maulQueued = false + elseif maulSpellIDs[spellId] then + S.maulQueued = true; S.hsQueued = false; S.cleaveQueued = false end end @@ -806,12 +822,14 @@ pfUI:RegisterModule("swingtimer", "vanilla:tbc", function () if eventCode == ON_SWING_QUEUED then S.useSpellQueueEvent = true if hsSpellIDs[spellId] then - S.hsQueued = true; S.cleaveQueued = false + S.hsQueued = true; S.cleaveQueued = false; S.maulQueued = false elseif cleaveSpellIDs[spellId] then - S.cleaveQueued = true; S.hsQueued = false + S.cleaveQueued = true; S.hsQueued = false; S.maulQueued = false + elseif maulSpellIDs[spellId] then + S.maulQueued = true; S.hsQueued = false; S.cleaveQueued = false end elseif eventCode == ON_SWING_QUEUE_POPPED then - S.hsQueued = false; S.cleaveQueued = false + S.hsQueued = false; S.cleaveQueued = false; S.maulQueued = false end elseif event == "START_AUTOATTACK" then @@ -823,6 +841,7 @@ pfUI:RegisterModule("swingtimer", "vanilla:tbc", function () elseif event == "PLAYER_ENTERING_WORLD" then local _, class = UnitClass("player") S.isWarrior = (class == "WARRIOR") + S.isDruid = (class == "DRUID") S.playerGUID = GetUnitGUID("player") UpdateWeaponSpeeds() RebuildQueueSlotCache() @@ -850,6 +869,7 @@ pfUI:RegisterModule("swingtimer", "vanilla:tbc", function () S.inCombat = false S.hsQueued = false S.cleaveQueued = false + S.maulQueued = false elseif event == "UNIT_DIED" then if arg1 and arg1 == S.playerGUID then @@ -897,7 +917,8 @@ pfUI:RegisterModule("swingtimer", "vanilla:tbc", function () -- Warrior HS/Cleave queue state IsHSQueued = function() return S.hsQueued end, IsCleaveQueued = function() return S.cleaveQueued end, + IsMaulQueued = function() return S.maulQueued end, } UpdateWeaponSpeeds() -end) \ No newline at end of file +end)