diff --git a/api/config.lua b/api/config.lua index 458df43f..44799d9e 100644 --- a/api/config.lua +++ b/api/config.lua @@ -228,6 +228,8 @@ function pfUI:LoadConfig() pfUI:UpdateConfig("unitframes", nil, "rangechecki", "4") pfUI:UpdateConfig("unitframes", nil, "combowidth", "6") pfUI:UpdateConfig("unitframes", nil, "comboheight", "6") + pfUI:UpdateConfig("unitframes", nil, "swingtimerwidth", "200") + pfUI:UpdateConfig("unitframes", nil, "swingtimerheight", "12") pfUI:UpdateConfig("unitframes", nil, "abbrevnum", "1") pfUI:UpdateConfig("unitframes", nil, "castbardecimals", "2") pfUI:UpdateConfig("unitframes", nil, "abbrevname", "1") diff --git a/init/modules.xml b/init/modules.xml index 198ae3b4..b516d941 100644 --- a/init/modules.xml +++ b/init/modules.xml @@ -17,6 +17,7 @@ + diff --git a/modules/gui.lua b/modules/gui.lua index 79f27d52..ae49c601 100644 --- a/modules/gui.lua +++ b/modules/gui.lua @@ -1954,6 +1954,10 @@ pfUI:RegisterModule("gui", "vanilla:tbc", function () CreateConfig(nil, T["Enable Mana Ticks"], C.unitframes.player, "manatick", "checkbox") CreateConfig(nil, T["Detect Enemy Buffs"], C.unitframes, "buffdetect", "checkbox", nil, nil, nil, nil, "vanilla" ) + CreateConfig(nil, T["Swing Timer"], nil, nil, "header") + CreateConfig(nil, T["Swing Timer Width"], C.unitframes, "swingtimerwidth") + CreateConfig(nil, T["Swing Timer Height"], C.unitframes, "swingtimerheight") + CreateConfig(U[c], T["Font Options"], nil, nil, "header") CreateConfig(nil, T["Unit Frame Text Font"], C.global, "font_unit", "dropdown", pfUI.gui.dropdowns.fonts) CreateConfig(nil, T["Unit Frame Text Size"], C.global, "font_unit_size") diff --git a/modules/swingtimer.lua b/modules/swingtimer.lua new file mode 100644 index 00000000..e49c1739 --- /dev/null +++ b/modules/swingtimer.lua @@ -0,0 +1,181 @@ +pfUI:RegisterModule("swingtimer", "vanilla:tbc", function () + local rawborder, border = GetBorderSize() + + -- Enable Nampower auto attack events + if GetCVar and SetCVar then + local success, value = pcall(GetCVar, "NP_EnableAutoAttackEvents") + if success and value == "0" then + pcall(SetCVar, "NP_EnableAutoAttackEvents", "1") + end + end + + -- HitInfo flags + local HITINFO_LEFTSWING = 4 + + -- Swing state + local swingState = { + mainhand = { speed = 0, nextSwing = 0, swinging = false }, + offhand = { speed = 0, nextSwing = 0, swinging = false } + } + + -- Create container frame + pfUI.swingtimer = CreateFrame("Frame", "pfSwingTimer", UIParent) + pfUI.swingtimer:SetFrameStrata("MEDIUM") + pfUI.swingtimer:Hide() + + local sw_width = tonumber(C.unitframes.swingtimerwidth) or 200 + local sw_height = tonumber(C.unitframes.swingtimerheight) or 12 + + -- Mainhand bar + pfUI.swingtimer.mainhand = CreateFrame("StatusBar", "pfSwingTimer", UIParent) + pfUI.swingtimer.mainhand:SetPoint("CENTER", UIParent, "CENTER", 0, -100) + pfUI.swingtimer.mainhand:SetWidth(sw_width) + pfUI.swingtimer.mainhand:SetHeight(sw_height) + pfUI.swingtimer.mainhand:SetMinMaxValues(0, 1) + pfUI.swingtimer.mainhand:SetValue(0) + pfUI.swingtimer.mainhand:SetStatusBarTexture(pfUI.media["img:bar"]) + pfUI.swingtimer.mainhand:SetStatusBarColor(0.8, 0.3, 0.3, 1) + pfUI.swingtimer.mainhand:Hide() + + pfUI.swingtimer.mainhand.text = pfUI.swingtimer.mainhand:CreateFontString("Status", "DIALOG", "GameFontNormal") + pfUI.swingtimer.mainhand.text:SetPoint("CENTER", pfUI.swingtimer.mainhand, "CENTER", 0, 0) + pfUI.swingtimer.mainhand.text:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE") + pfUI.swingtimer.mainhand.text:SetTextColor(1, 1, 1, 1) + pfUI.swingtimer.mainhand.text:SetText("") + + pfUI.swingtimer.mainhand.label = pfUI.swingtimer.mainhand:CreateFontString("Status", "DIALOG", "GameFontNormal") + pfUI.swingtimer.mainhand.label:SetPoint("RIGHT", pfUI.swingtimer.mainhand, "LEFT", -4, 0) + pfUI.swingtimer.mainhand.label:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE") + pfUI.swingtimer.mainhand.label:SetTextColor(0.8, 0.8, 0.8, 1) + pfUI.swingtimer.mainhand.label:SetText("MH") + + CreateBackdrop(pfUI.swingtimer.mainhand) + CreateBackdropShadow(pfUI.swingtimer.mainhand) + + -- Offhand bar + pfUI.swingtimer.offhand = CreateFrame("StatusBar", "pfSwingTimerOffhand", UIParent) + pfUI.swingtimer.offhand:SetPoint("TOP", pfUI.swingtimer.mainhand, "BOTTOM", 0, -4) + pfUI.swingtimer.offhand:SetWidth(sw_width) + pfUI.swingtimer.offhand:SetHeight(sw_height) + pfUI.swingtimer.offhand:SetMinMaxValues(0, 1) + pfUI.swingtimer.offhand:SetValue(0) + pfUI.swingtimer.offhand:SetStatusBarTexture(pfUI.media["img:bar"]) + pfUI.swingtimer.offhand:SetStatusBarColor(0.3, 0.8, 0.3, 1) + pfUI.swingtimer.offhand:Hide() + + pfUI.swingtimer.offhand.text = pfUI.swingtimer.offhand:CreateFontString("Status", "DIALOG", "GameFontNormal") + pfUI.swingtimer.offhand.text:SetPoint("CENTER", pfUI.swingtimer.offhand, "CENTER", 0, 0) + pfUI.swingtimer.offhand.text:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE") + pfUI.swingtimer.offhand.text:SetTextColor(1, 1, 1, 1) + pfUI.swingtimer.offhand.text:SetText("") + + pfUI.swingtimer.offhand.label = pfUI.swingtimer.offhand:CreateFontString("Status", "DIALOG", "GameFontNormal") + pfUI.swingtimer.offhand.label:SetPoint("RIGHT", pfUI.swingtimer.offhand, "LEFT", -4, 0) + pfUI.swingtimer.offhand.label:SetFont(pfUI.font_default, C.global.font_size, "OUTLINE") + pfUI.swingtimer.offhand.label:SetTextColor(0.8, 0.8, 0.8, 1) + pfUI.swingtimer.offhand.label:SetText("OH") + + CreateBackdrop(pfUI.swingtimer.offhand) + CreateBackdropShadow(pfUI.swingtimer.offhand) + + UpdateMovable(pfUI.swingtimer.mainhand) + + local function UpdateWeaponSpeeds() + if not GetUnitField then return end + + local mhSpeed = GetUnitField("player", "baseAttackTime") + local ohSpeed = GetUnitField("player", "offhandAttackTime") + + if mhSpeed and mhSpeed > 0 then + swingState.mainhand.speed = mhSpeed / 1000 + end + + if ohSpeed and ohSpeed > 0 then + swingState.offhand.speed = ohSpeed / 1000 + else + swingState.offhand.speed = 0 + end + end + + local function StartSwing(isOffhand) + local now = GetTime() + + -- always refresh speeds to catch haste buffs/debuffs + UpdateWeaponSpeeds() + + if isOffhand and swingState.offhand.speed > 0 then + swingState.offhand.nextSwing = now + swingState.offhand.speed + swingState.offhand.swinging = true + pfUI.swingtimer.offhand:Show() + else + swingState.mainhand.nextSwing = now + swingState.mainhand.speed + swingState.mainhand.swinging = true + pfUI.swingtimer.mainhand:Show() + end + + pfUI.swingtimer:Show() + end + + pfUI.swingtimer:SetScript("OnUpdate", function() + local now = GetTime() + local anyActive = false + + if swingState.mainhand.swinging then + local remaining = swingState.mainhand.nextSwing - now + + if remaining <= 0 then + swingState.mainhand.swinging = false + pfUI.swingtimer.mainhand:Hide() + else + local progress = 1 - (remaining / swingState.mainhand.speed) + pfUI.swingtimer.mainhand:SetValue(progress) + pfUI.swingtimer.mainhand.text:SetText(string.format("%.1f", remaining)) + anyActive = true + end + end + + if swingState.offhand.swinging then + local remaining = swingState.offhand.nextSwing - now + + if remaining <= 0 then + swingState.offhand.swinging = false + pfUI.swingtimer.offhand:Hide() + else + local progress = 1 - (remaining / swingState.offhand.speed) + pfUI.swingtimer.offhand:SetValue(progress) + pfUI.swingtimer.offhand.text:SetText(string.format("%.1f", remaining)) + anyActive = true + end + end + + if not anyActive then + this:Hide() + end + end) + + local events = CreateFrame("Frame") + events:RegisterEvent("AUTO_ATTACK_SELF") + events:RegisterEvent("PLAYER_ENTERING_WORLD") + events:RegisterEvent("UNIT_INVENTORY_CHANGED") + events:RegisterEvent("PLAYER_REGEN_DISABLED") + events:RegisterEvent("PLAYER_REGEN_ENABLED") + + events:SetScript("OnEvent", function() + if event == "AUTO_ATTACK_SELF" then + local hitInfo = arg4 or 0 + local isOffhand = bit.band(hitInfo, HITINFO_LEFTSWING) ~= 0 + StartSwing(isOffhand) + elseif event == "PLAYER_ENTERING_WORLD" or event == "UNIT_INVENTORY_CHANGED" then + if arg1 and arg1 ~= "player" then return end + UpdateWeaponSpeeds() + elseif event == "PLAYER_REGEN_DISABLED" then + UpdateWeaponSpeeds() + elseif event == "PLAYER_REGEN_ENABLED" then + swingState.mainhand.swinging = false + swingState.offhand.swinging = false + pfUI.swingtimer:Hide() + end + end) + + UpdateWeaponSpeeds() +end)