Compare commits

...

37 Commits
v1.0.1 ... main

Author SHA1 Message Date
OldManAlpha
899408c4e9
Update README.md 2025-09-09 15:35:44 -07:00
OldManAlpha
8b67661ed9
Merge pull request #17 from OldManAlpha/dev
Version 1.0.5
2025-09-08 18:43:41 -07:00
OldManAlpha
7be8640a6f Version 1.0.5 2025-09-08 18:30:07 -07:00
OldManAlpha
c8fceaf243 Change variable name 2025-09-08 18:29:45 -07:00
OldManAlpha
224f329d08 Removed "None" text in spells tooltip
Also fixed issue where non-spell bindings wouldn't show on dead players
2025-09-08 18:15:43 -07:00
OldManAlpha
5e4a63d651 Small duration optimizations 2025-09-08 17:35:08 -07:00
OldManAlpha
34f549744e Change Auto Role experiment to global setting 2025-09-08 17:16:48 -07:00
OldManAlpha
e1898c1486 New style overrides 2025-09-08 16:32:37 -07:00
OldManAlpha
e3ba7e831c Add Anchor and Hide Title frame options
Frame positions are now also saved in the addon data rather than layout cache. This means disabling and re-enabling the addon will preserve your frame positions.
2025-09-08 16:10:22 -07:00
OldManAlpha
27bd99c7a8 Refactor unit tracker 2025-09-06 16:55:46 -07:00
OldManAlpha
0faa8b9184 Change file name 2025-09-06 16:54:12 -07:00
OldManAlpha
30da69e21b Add internal tracking options
Currently not configurable in-game
2025-09-06 16:52:07 -07:00
OldManAlpha
f7563933ee Update mods info 2025-09-06 16:43:47 -07:00
OldManAlpha
ad54a21b6a Add PVP Flag Protection 2025-09-06 15:53:32 -07:00
OldManAlpha
93a0594410 Add PVP Flag Icon 2025-09-05 16:21:39 -07:00
OldManAlpha
be859b3b90 Fix error with reset command 2025-09-05 16:16:11 -07:00
OldManAlpha
b44605879e Add colored debuff borders 2025-09-04 21:39:59 -07:00
OldManAlpha
5049852079 Fix losing custom units in GUID roster 2025-09-02 16:44:07 -07:00
OldManAlpha
119a2381c6
Merge pull request #14 from OldManAlpha/dev
Version 1.0.4
2025-08-29 22:21:30 -07:00
OldManAlpha
4982c62fe0 Version 1.0.4 2025-08-29 22:16:40 -07:00
OldManAlpha
b0e9d956a2 Don't request Damage-only talents 2025-08-29 22:12:59 -07:00
OldManAlpha
58fb2aeadc Prevent errors on unmodded Turtle clients 2025-08-29 22:12:07 -07:00
OldManAlpha
da225c898e
Merge pull request #13 from OldManAlpha/dev
Version 1.0.3
2025-08-29 16:10:47 -07:00
OldManAlpha
6cb3f51c57 Version 1.0.3 2025-08-29 15:49:10 -07:00
OldManAlpha
2247ba244f Reduce spell tooltip scanning 2025-08-29 15:41:46 -07:00
OldManAlpha
aaadb265a1 Fix potential errors 2025-08-29 11:24:17 -07:00
OldManAlpha
2ccf53fd86 Clarity improvements 2025-08-29 09:31:04 -07:00
OldManAlpha
c00be9fd64 Checkbox now makes sound 2025-08-28 20:03:49 -07:00
OldManAlpha
87d27f4cc9 Add experimental auto role detection 2025-08-28 20:02:32 -07:00
OldManAlpha
83736ac0c4 Some optimizations 2025-08-28 10:48:49 -07:00
OldManAlpha
14516d62db Massively improve aura update performance 2025-08-27 17:33:45 -07:00
OldManAlpha
d025a1a906 Don't update sight and distance on self
Fixes lag in certain zones with UnitXP SP3
2025-08-27 10:28:56 -07:00
OldManAlpha
3faed3a1b7 Update LFT for 1.18 2025-08-25 11:13:21 -07:00
OldManAlpha
fb2ad22973 Fix HoT cache being stored as a string 2025-08-21 20:55:59 -07:00
OldManAlpha
7a2007044f Fix background being layered above inc health bars 2025-08-21 16:18:49 -07:00
OldManAlpha
406330c255
Merge pull request #5 from OldManAlpha/dev
Version 1.0.2
2025-08-15 08:49:04 -07:00
OldManAlpha
c053d7cdae Update for TWoW 1.18/Small Changes 2025-08-15 08:43:59 -07:00
27 changed files with 800 additions and 177 deletions

View File

@ -180,6 +180,7 @@ function PTUnitFrame:UpdateAll()
self:EvaluateTarget()
self:UpdateOutline()
self:UpdateRaidMark()
self:UpdatePVP()
end
function PTUnitFrame:GetShowDistanceThreshold()
@ -322,6 +323,22 @@ function PTUnitFrame:UpdateRaidMark()
self.raidMarkIcon.frame:Show()
end
function PTUnitFrame:UpdatePVP()
if UnitIsPVP(self.unit) and (not IsInInstance() or not UnitIsVisible(self.unit)) then
local faction = UnitFactionGroup(self.unit)
if faction == "Alliance" then
self.pvpIcon.icon:SetTexture("Interface\\TargetingFrame\\UI-PVP-Alliance")
elseif faction == "Horde" then
self.pvpIcon.icon:SetTexture("Interface\\TargetingFrame\\UI-PVP-Horde")
else
self.pvpIcon.icon:SetTexture("Interface\\TargetingFrame\\UI-PVP-FFA")
end
self.pvpIcon.frame:Show()
else
self.pvpIcon.frame:Hide()
end
end
function PTUnitFrame:Flash()
local FLASH_TIME = 0.15
local START_OPACITY = self:GetProfile().FlashOpacity / 100
@ -632,7 +649,7 @@ function PTUnitFrame:SetHealthBarValue(value)
incomingHealText:SetText("")
end
elseif profile.IncomingHealDisplay == "Heal" then
incomingHealText:SetText("+"..self.incomingHealing)
incomingHealText:SetText("+"..math.ceil(incomingHealing))
local rgb = incomingDirectHealing > 0 and profile.IncomingHealText.Color or
profile.IncomingHealText.IndirectColor
if incomingDirectHealing > 0 then
@ -786,7 +803,12 @@ function PTUnitFrame:AllocateAura()
frame:SetScript("OnMouseUp", PTUnitFrame.Aura_OnMouseUp)
frame:SetScript("OnMouseDown", PTUnitFrame.Aura_OnMouseDown)
local icon = frame:CreateTexture(nil, "OVERLAY")
local icon = frame:CreateTexture(nil, "ARTWORK")
local border = frame:CreateTexture(nil, "OVERLAY")
border:SetTexture("Interface\\Buttons\\UI-Debuff-Overlays")
border:SetTexCoord(0.296875, 0.5703125, 0, 0.515625)
border:SetPoint("TOPLEFT", icon, "TOPLEFT", -0.5, 0.5)
border:SetPoint("BOTTOMRIGHT", icon, "BOTTOMRIGHT", 0.5, -0.5)
local stackText = frame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
stackText:SetTextColor(1, 1, 1)
@ -824,16 +846,18 @@ function PTUnitFrame:AllocateAura()
end
duration:SetScript("OnUpdate", nil)
end
local SetSequenceTime = duration.SetSequenceTime
local GetTime = GetTime
duration:SetScript("OnUpdateModel", function()
if this.stopping == 0 then
this:SetAlpha(0.8)
if duration.stopping == 0 then
duration:SetAlpha(0.8)
local time = GetTime()
local progress = (time - this.start) / this.duration
local progress = (time - duration.start) / duration.duration
if progress < 1.0 then
this:SetSequenceTime(0, 1000 - (progress * 1000))
local secondsPrecise = this.start - time + this.duration
SetSequenceTime(duration, 0, 1000 - (progress * 1000))
local secondsPrecise = duration.start - time + duration.duration
local seconds = math.floor(secondsPrecise)
if seconds <= (this.displayAt and this.displayAt or AURA_DURATION_TEXT_FLASH_THRESHOLD) then
if seconds <= (duration.displayAt or AURA_DURATION_TEXT_FLASH_THRESHOLD) then
if durationText.seconds ~= seconds or seconds <= AURA_DURATION_TEXT_FLASH_THRESHOLD then
-- You don't want to know why it's gotta be done like this..
-- (If you're insane and you do, it's because otherwise the text will disappear for one frame otherwise)
@ -847,13 +871,13 @@ function PTUnitFrame:AllocateAura()
return
end
durationText:SetSeconds(nil)
this:SetSequenceTime(0, 0)
SetSequenceTime(duration, 0, 0)
end
end)
return {["frame"] = frame, ["icon"] = icon, ["stackText"] = stackText, ["overlay"] = durationOverlayFrame,
return {["frame"] = frame, ["icon"] = icon, ["border"] = border, ["stackText"] = stackText, ["overlay"] = durationOverlayFrame,
["durationText"] = durationText, ["duration"] = duration, ["durationEnabled"] = true}
end
return {["frame"] = frame, ["icon"] = icon, ["stackText"] = stackText}
return {["frame"] = frame, ["icon"] = icon, ["border"] = border, ["stackText"] = stackText}
end
-- Get an icon from the available pool. Automatically inserts into the used pool.
@ -974,13 +998,13 @@ function PTUnitFrame:UpdateAuras()
local yOffset = profile.TrackedAurasAlignment == "TOP" and 0 or origSize - auraSize
for _, buff in ipairs(buffs) do
local aura = self:GetUnusedAura()
self:CreateAura(aura, buff.name, buff.index, buff.texture, buff.stacks, xOffset, -yOffset, "Buff", auraSize)
self:CreateAura(aura, buff.name, buff.index, buff.texture, buff.stacks, buff.type, xOffset, -yOffset, "Buff", auraSize)
xOffset = xOffset + auraSize + spacing
end
xOffset = 0
for _, debuff in ipairs(debuffs) do
local aura = self:GetUnusedAura()
self:CreateAura(aura, debuff.name, debuff.index, debuff.texture, debuff.stacks, xOffset, -yOffset, "Debuff", auraSize)
self:CreateAura(aura, debuff.name, debuff.index, debuff.texture, debuff.stacks, debuff.type, xOffset, -yOffset, "Debuff", auraSize)
xOffset = xOffset - auraSize - spacing
end
compost:Reclaim(buffs)
@ -1056,7 +1080,14 @@ do
PTUnitFrame.Aura_OnMouseDown = wrapButtonScript("OnMouseDown")
end
function PTUnitFrame:CreateAura(aura, name, index, texturePath, stacks, xOffset, yOffset, type, size)
local debuffTypeBorderColors = {
["Magic"] = {0.2, 0.6, 1.0},
["Curse"] = {0.6, 0.0, 1.0},
["Disease"] = {0.6, 0.4, 0},
["Poison"] = {0.0, 0.6, 0},
["Other"] = {1, 0, 0}
}
function PTUnitFrame:CreateAura(aura, name, index, texturePath, stacks, auraType, xOffset, yOffset, type, size)
local frame = aura.frame
frame:SetWidth(size)
frame:SetHeight(size)
@ -1070,7 +1101,6 @@ function PTUnitFrame:CreateAura(aura, name, index, texturePath, stacks, xOffset,
local icon = aura.icon
icon:SetAllPoints(frame)
icon:SetTexture(texturePath)
--icon:SetVertexColor(1, 0, 0)
if aura.durationEnabled then
local overlay = aura.overlay
@ -1088,6 +1118,15 @@ function PTUnitFrame:CreateAura(aura, name, index, texturePath, stacks, xOffset,
stackText:SetText(stacks)
end
if type == "Buff" then
aura.border:Hide()
else
local border = aura.border
border:Show()
local color = debuffTypeBorderColors[auraType] or debuffTypeBorderColors["Other"]
border:SetVertexColor(color[1], color[2], color[3])
end
if aura.durationEnabled then
local cache = self:GetCache()
if cache.AuraTimes[name] then
@ -1166,6 +1205,17 @@ function PTUnitFrame:Initialize()
raidMarkIcon:SetTexture("Interface\\TARGETINGFRAME\\UI-RaidTargetingIcons")
raidMarkFrame:Hide()
-- PVP Icon
local pvpFrame = CreateFrame("Frame", nil, container)
pvpFrame:SetFrameLevel(container:GetFrameLevel() + 4)
local pvpIcon = pvpFrame:CreateTexture(nil, "OVERLAY")
self.pvpIcon = {frame = pvpFrame, icon = pvpIcon}
pvpIcon:SetAlpha(profile.PVPIcon:GetAlpha())
pvpIcon:SetTexture("Interface\\TargetingFrame\\UI-PVP-Alliance")
pvpIcon:SetTexCoord(3 / 64, 39 / 64, 2 / 64, 38 / 64)
pvpFrame:Hide()
-- Health Bar Element
local healthBar = CreateFrame("StatusBar", "$parentHealthBar", container)
@ -1191,9 +1241,9 @@ function PTUnitFrame:Initialize()
self.nameText = name
name:SetAlpha(profile.NameText:GetAlpha())
local bg = healthBar:CreateTexture(nil, "BACKGROUND")
local bg = container:CreateTexture(nil, "BACKGROUND")
healthBar.background = bg
bg:SetAllPoints(true)
bg:SetAllPoints(healthBar)
bg:SetTexture(0.5, 0.5, 0.5, 0.25)
-- Incoming Text
@ -1389,6 +1439,12 @@ function PTUnitFrame:SizeElements()
local raidMarkIcon = self.raidMarkIcon.icon
raidMarkIcon:SetAllPoints(raidMarkFrame)
local pvpFrame = self.pvpIcon.frame
self:UpdateComponent(pvpFrame, profile.PVPIcon)
local pvpIcon = self.pvpIcon.icon
pvpIcon:SetAllPoints(pvpFrame)
local auraPanel = self.auraPanel
self:UpdateComponent(auraPanel, profile.AuraTracker)
@ -1460,7 +1516,7 @@ function PTUnitFrame:UpdateComponent(component, props, xOffset, yOffset)
end
function PTUnitFrame:GetCache()
return PTUnit.Get(self.unit) or PTUnit
return PTUnit.Get(self.unit)
end
function PTUnitFrame:GetAfflictedDebuffTypes()

View File

@ -132,14 +132,15 @@ end
function PTUnitFrameGroup:Initialize()
local container = CreateFrame("Frame", "PTUnitFrameGroupContainer_"..self.name, UIParent)
self.container = container
self:ApplyToplevel()
if container:GetNumPoints() == 0 then
container:SetPoint(util.GetCenterScreenPoint(0, 0))
end
container:SetBackdrop({bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background"})
container:SetBackdropColor(0, 0, 0, 0.5)
container:EnableMouse(true)
container:SetMovable(true)
container:SetUserPlaced(false)
self:ApplyToplevel()
container:ClearAllPoints()
local anchor, x, y = PuppeteerSettings.GetFramePosition(self.name)
container:SetPoint(anchor or "TOPLEFT", UIParent, "TOPLEFT", x or 100, y or -100)
container:SetBackdrop({bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background"})
container:SetBackdropColor(0, 0, 0, 0.5)
container:SetScript("OnMouseDown", function()
local button = arg1
@ -156,6 +157,7 @@ function PTUnitFrameGroup:Initialize()
if (util.GetKeyModifier() == PTOptions.FrameDrag.AltMoveKey) == PTOptions.FrameDrag.MoveAll then
container:StartMoving()
container:SetUserPlaced(false) -- StartMoving sets this and needs to be reverted
self:RemoveToplevel()
return
end
@ -187,7 +189,7 @@ function PTUnitFrameGroup:Initialize()
container:SetScript("OnMouseUp", function()
local button = arg1
if button == "RightButton" and MouseIsOver(self.header) then
if button == "RightButton" and MouseIsOver(self.header) and self.header:IsVisible() then
ContextMenu.FrameGroup = self
ContextMenu:SetToggleState(false)
ContextMenu:SetToggleState(true, container, container:GetWidth(), container:GetHeight())
@ -204,6 +206,7 @@ function PTUnitFrameGroup:Initialize()
if not container.bulkMovement then
container:StopMovingOrSizing()
self:ApplyToplevel()
util.ConvertAnchor(container, PuppeteerSettings.GetFramePosition(self.name))
return
end
@ -214,10 +217,7 @@ function PTUnitFrameGroup:Initialize()
for _, group in pairs(moveContainer.groups) do
group:ApplyToplevel()
local gc = group:GetContainer()
local xOffset = gc:GetLeft()
local yOffset = gc:GetTop() - GetScreenHeight()
gc:ClearAllPoints()
gc:SetPoint("TOPLEFT", UIParent, "TOPLEFT", xOffset, yOffset)
util.ConvertAnchor(gc, PuppeteerSettings.GetFramePosition(group.name))
end
-- Prevent container from potentially blocking mouse by setting it back to 0 size
moveContainer:SetWidth(0)
@ -240,15 +240,15 @@ function PTUnitFrameGroup:Initialize()
header:SetBackdrop({bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background"})
self:UpdateHeaderColor()
local borderFrame = CreateFrame("Frame", "$parentBorder", container)
self.borderFrame = borderFrame
borderFrame:SetPoint("CENTER", container, 0, 0)
local label = header:CreateFontString(header, "OVERLAY", "GameFontNormal")
self.label = label
label:SetPoint("CENTER", header, "CENTER", 0, 0)
label:SetText(self.name)
local borderFrame = CreateFrame("Frame", "$parentBorder", container)
self.borderFrame = borderFrame
borderFrame:SetPoint("CENTER", container, 0, 0)
self:ApplyProfile()
self:UpdateUIPositions()
@ -275,6 +275,8 @@ function PTUnitFrameGroup:UpdateUIPositions()
local profileHeight = profile:GetHeight()
local maxUnitsInAxis = profile.MaxUnitsInAxis
local orientation = profile.Orientation
local headerEnabled = not PuppeteerSettings.IsTitleHidden(self.name)
local headerHeight = headerEnabled and 20 or 0
local sortedUIs = self:GetSortedUIs()
local splitSortedUIs = {}
@ -308,7 +310,7 @@ function PTUnitFrameGroup:UpdateUIPositions()
local container = ui:GetRootContainer()
local x = orientation == "Vertical" and ((profileWidth + xSpacing) * (columnIndex - 1)) or ((profileWidth + xSpacing) * (i - 1))
local y = orientation == "Vertical" and ((profileHeight + ySpacing) * (i - 1)) or ((profileHeight + ySpacing) * (columnIndex - 1))
container:SetPoint("TOPLEFT", self.container, "TOPLEFT", x, -y - 20)
container:SetPoint("TOPLEFT", self.container, "TOPLEFT", x, -y - headerHeight)
end
end
@ -321,13 +323,18 @@ function PTUnitFrameGroup:UpdateUIPositions()
local width = orientation == "Vertical" and (profileWidth * largestRow + (xSpacing * (largestRow - 1))) or (profileWidth * largestColumn + (xSpacing * (largestColumn - 1)))
width = math.max(width, profileWidth) -- Prevent width from being 0
local height = orientation == "Vertical" and (profileHeight * largestColumn + (ySpacing * (largestColumn - 1))) or (profileHeight * largestRow + (ySpacing * (largestRow - 1)))
height = height + 20
height = height + headerHeight
self.container:SetWidth(width)
self.container:SetHeight(height)
local header = self.header
header:SetWidth(width)
header:SetHeight(20)
if headerEnabled then
header:Show()
header:SetWidth(width)
header:SetHeight(headerHeight)
else
header:Hide()
end
local borderPadding = 0
if profile.BorderStyle == "Tooltip" then

View File

@ -206,6 +206,19 @@ function PTUIProfile.SetDefaults()
["Opacity"] = 100
})
profile.PVPIcon = createSizedObject({
["Width"] = 14,
["Height"] = 14,
["AlignmentH"] = "LEFT",
["AlignmentV"] = "TOP",
["PaddingH"] = 0,
["OffsetX"] = -6,
["PaddingV"] = 0,
["OffsetY"] = 2,
["Anchor"] = "Container",
["Opacity"] = 100
})
profile.TrackAuras = true
profile.AuraTracker = createSizedObject({
["Height"] = 20,

View File

@ -75,6 +75,8 @@ function InitializeDefaultProfiles()
profile.RoleIcon.PaddingV = 0
profile.RoleIcon.OffsetY = 5
profile.RoleIcon.OffsetX = -5
profile.PVPIcon.OffsetY = -5
end
@ -107,6 +109,8 @@ function InitializeDefaultProfiles()
profile.RoleIcon.PaddingV = 0
profile.RoleIcon.OffsetY = 5
profile.RoleIcon.OffsetX = -4
profile.PVPIcon.OffsetY = -5
end
do
@ -136,6 +140,8 @@ function InitializeDefaultProfiles()
profile.RoleIcon.PaddingV = 0
profile.RoleIcon.OffsetY = 6
profile.RoleIcon.OffsetX = -5
profile.PVPIcon.OffsetY = -5
end
do
@ -247,6 +253,9 @@ function InitializeDefaultProfiles()
profile.PowerDisplay = "Hidden"
profile.PowerText.FontSize = 8
profile.Orientation = "Vertical"
profile.PVPIcon.Width = 12
profile.PVPIcon.Height = 12
end
do
@ -400,6 +409,8 @@ function InitializeDefaultProfiles()
profile.RaidMarkIcon.Width = 16
profile.RaidMarkIcon.Height = 16
profile.PVPIcon.OffsetY = -4
profile.BorderStyle = "Hidden"
end

View File

@ -262,18 +262,36 @@ function OnAddonLoaded()
-- Older versions of SuperWoW had an issue where units that aren't part of normal units wouldn't receive events,
-- so updates are done manually
local needsManualUpdates = util.SuperWoWFeatureLevel < util.SuperWoW_v1_4
local existing = {}
local nextCleanup = GetTime() + 10
customUnitUpdater:SetScript("OnUpdate", function()
if GetTime() > nextUpdate then
nextUpdate = GetTime() + 0.25
for unit, guid in pairs(CustomUnitGUIDMap) do
if needsManualUpdates or not UnitExists(guid) then
PTUnit.Get(unit):UpdateAuras()
for ui in UnitFrames(unit) do
ui:UpdateHealth()
ui:UpdatePower()
ui:UpdateAuras()
ui:UpdateIncomingHealing()
for guid, units in pairs(GUIDCustomUnitMap) do
local exists = UnitExists(guid) == 1
local needsUpdate = false
if (existing[guid] ~= nil) ~= exists then
existing[guid] = exists or nil
needsUpdate = true
end
if needsManualUpdates or needsUpdate then
PTUnit.Get(guid):UpdateAuras()
for _, unit in ipairs(units) do
for ui in UnitFrames(unit) do
ui:UpdateAll()
ui:UpdateIncomingHealing()
end
end
end
end
if GetTime() > nextCleanup then
nextCleanup = GetTime() + 10
for guid in pairs(existing) do
if not GUIDCustomUnitMap[guid] then
existing[guid] = nil
end
end
end
@ -354,6 +372,8 @@ function OnAddonLoaded()
end
end)
end
InitRoleDropdown()
SetLFTAutoRoleEnabled(PTOptions.LFTAutoRole)
@ -364,7 +384,7 @@ function OnAddonLoaded()
end
initUnitFrames()
StartDistanceScanner()
StartUnitTracker()
PuppeteerLib:RegisterEvent("Banzai_UnitGainedAggro", function(unit)
if PTGuidRoster then
@ -736,7 +756,7 @@ function CheckGroup()
if not superwow then -- If SuperWoW isn't present, the units may have shifted and thus require a full scan
PTUnit.UpdateAllUnits()
end
for _, ui in pairs(AllUnitFrames) do
for _, ui in ipairs(AllUnitFrames) do
if ui:IsShown() then
ui:UpdateRange()
ui:UpdateAuras()
@ -813,4 +833,8 @@ function EndTiming(name)
return
end
pfDebug_EndTiming(name)
end
function PrintStack()
print(debugstack(2))
end

View File

@ -1,5 +1,5 @@
## Interface: 11200
## Version: 1.0.1
## Version: 1.0.5
## Title: Puppeteer
## Notes: Unit frames addon tailored for healers
## Author: OldManAlpha, Richard Truax(i2ichardt)
@ -51,7 +51,7 @@ libs\gui\frame\SimpleDialog.lua
Puppeteer.lua
core\SpellsTooltip.lua
core\EventHandler.lua
core\DistanceScanner.lua
core\UnitTracker.lua
core\Command.lua
core\OverrideBindings.lua
core\Bindings.lua

View File

@ -133,10 +133,19 @@ function SetDefaults()
["Medium"] = 10, -- <=2 min
["Long"] = 60 * 2 -- >2 min
},
["Tracking"] = {
["EvaluateInterval"] = 1.25, -- How often everyone is fully scanned to determine if they should be closely tracked
["DistanceUpdateInterval"] = 0.1, -- How often distance tracked units are updated
["SightUpdateInterval"] = 0.1, -- How often sight tracked units are updated
["MinDistanceTracking"] = 20, -- The minimum distance to start closely tracking distance
["MaxDistanceTracking"] = 60, -- The maxmimum distance to start closely tracking distance
["MaxSightTracking"] = 80 -- The maximum distance to closely track sight
},
["CastWhen"] = "Mouse Up", -- Mouse Up, Mouse Down
["CastWhenKey"] = "Key Up", -- Key Up, Key Down
["AutoResurrect"] = Puppeteer.ResurrectionSpells[util.GetClass("player")] ~= nil,
["UseHealPredictions"] = true,
["PVPFlagProtection"] = true,
["SetMouseover"] = true,
["LFTAutoRole"] = true, -- Turtle WoW
["TestUI"] = false,
@ -284,6 +293,9 @@ function SetDefaults()
do
local defaults = {
["ShowLoadMessage"] = true,
["Experiments"] = {
["AutoRole"] = false
},
["OptionsVersion"] = OPTIONS_VERSION
}
ApplyDefaults(PTGlobalOptions, defaults)
@ -304,6 +316,10 @@ end
function TraverseOptions(location)
local path = util.SplitString(location, ".")
local currentTable = PTOptions
if path[1] == "Global" then
currentTable = PTGlobalOptions
table.remove(path, 1)
end
for i = 1, table.getn(path) - 1 do
currentTable = currentTable[path[i]]
end
@ -348,7 +364,7 @@ DefaultClassTrackedBuffs = {
"Greater Blessing of Light", "Daybreak", "Blessing of Freedom", "Hand of Freedom", "Redoubt", "Holy Shield"},
["PRIEST"] = {"Prayer of Fortitude", "Power Word: Fortitude", "Prayer of Spirit", "Divine Spirit",
"Prayer of Shadow Protection", "Shadow Protection", "Holy Champion", "Champion's Grace", "Empower Champion",
"Fear Ward", "Inner Fire", "Renew", "Greater Heal", "Lightwell Renew", "Inspiration",
"Champion's Bond", "Fear Ward", "Inner Fire", "Renew", "Greater Heal", "Lightwell Renew", "Inspiration",
"Fade", "Spirit Tap", "Enlighten", "Enlightened"},
["WARRIOR"] = {"Battle Shout"},
["DRUID"] = {"Gift of the Wild", "Mark of the Wild", "Thorns", "Rejuvenation", "Regrowth"},
@ -456,14 +472,18 @@ function GetSelectedProfile(frame)
return PTProfileManager.GetProfile(GetSelectedProfileName(frame))
end
local function validateFrameOptionsExistence(frameName)
if not PTOptions.FrameOptions[frameName] then
PTOptions.FrameOptions[frameName] = {}
end
end
function IsFrameHidden(frameName)
return PTOptions.FrameOptions[frameName] and PTOptions.FrameOptions[frameName].Hidden
end
function SetFrameHidden(frameName, hidden)
if not PTOptions.FrameOptions[frameName] then
PTOptions.FrameOptions[frameName] = {}
end
validateFrameOptionsExistence(frameName)
PTOptions.FrameOptions[frameName].Hidden = hidden
PTSettingsGui.UpdateFrameOptions()
end
@ -473,13 +493,42 @@ function IsFrameLocked(frameName)
end
function SetFrameLocked(frameName, locked)
if not PTOptions.FrameOptions[frameName] then
PTOptions.FrameOptions[frameName] = {}
end
validateFrameOptionsExistence(frameName)
PTOptions.FrameOptions[frameName].Locked = locked
local group = Puppeteer.UnitFrameGroups[frameName]
if group then
group:UpdateHeaderColor()
end
PTSettingsGui.UpdateFrameOptions()
end
function IsTitleHidden(frameName)
return PTOptions.FrameOptions[frameName] and PTOptions.FrameOptions[frameName].TitleHidden
end
function SetTitleHidden(frameName, hidden)
validateFrameOptionsExistence(frameName)
PTOptions.FrameOptions[frameName].TitleHidden = hidden
local group = Puppeteer.UnitFrameGroups[frameName]
if group then
group:UpdateUIPositions()
end
PTSettingsGui.UpdateFrameOptions()
end
function GetFramePosition(frameName)
if not (PTOptions.FrameOptions[frameName] and PTOptions.FrameOptions[frameName].Position) then
return "TOPLEFT", (GetScreenWidth() / 2), -(GetScreenHeight() / 2)
end
return unpack(PTOptions.FrameOptions[frameName].Position)
end
function SaveFramePositions()
for frameName, group in pairs(Puppeteer.UnitFrameGroups) do
if not PTOptions.FrameOptions[frameName] then
PTOptions.FrameOptions[frameName] = {}
end
local anchor, _, _, x, y = group:GetContainer():GetPoint(1)
PTOptions.FrameOptions[frameName].Position = {anchor, x, y}
end
end

View File

@ -4,7 +4,7 @@
Puppeteer, formerly HealersMate, is a unit frames addon for World of Warcraft Vanilla 1.12 that strives to be an alternative to modern WoW's VuhDo, Cell, or Healbot. Its features are tailored for healers, but can be a viable unit frames addon for any class and spec.
### Features
- See health, power, marks, incoming healing, mob aggro, and relevant buffs & debuffs of your party, raid, pets, and targets
- See health, power, marks, incoming healing, mob aggro, PvP status, and relevant buffs & debuffs of your party, raid, pets, and targets
- Bind mouse clicks, the mouse wheel, and keys to spells
- See your bound spells, their cost, and available mana while hovering over frames
- Assign roles to players
@ -23,7 +23,7 @@ Puppeteer, formerly HealersMate, is a unit frames addon for World of Warcraft Va
### Simple, Yet Advanced Bindings
<img align="right" width="36%" src="https://i.imgur.com/KoFygXv.png">
Puppeteer boasts the ability to bind mouse clicks, the mouse wheel, and keys to any combination of Ctrl/Shift/Alt modifiers. You can bind spells, macros, items, custom Lua scripts, and menus which contain multiple bindings.
Puppeteer boasts the ability to bind mouse clicks, the mouse wheel, and keys to any combination of Shift/Ctrl/Alt modifiers. You can bind spells, macros, items, custom Lua scripts, and menus which contain multiple bindings. **Use the `/pt` command to open the configuration menu.**
<p align="left">
<img src="https://i.imgur.com/iglcV7z.png" width=30% align="top">
<img src="https://i.imgur.com/7iIQTkk.png" width=30% align="top">
@ -52,14 +52,21 @@ While not required, the mods listed below will massively improve your experience
| Mod | Enhancement |
| - | - |
| SuperWoW ([GitHub](https://github.com/balakethelock/SuperWoW)) | - Shows more accurate incoming healing, and shows incoming healing from players that do not have HealComm<br>- Track the remaining duration of many buffs and HoTs on other players<br>- Allows casting on players without doing split-second target switching<br>- Lets you see accurate distance to friendly players/NPCs<br>- Lets you set units you're hovering over as your mouseover target |
| UnitXP SP3 ([GitHub](https://github.com/allfoxwy/UnitXP_SP3)) | Allows Puppeteer to show very accurate distance to both friendly players and enemies, and show if they're out of line-of-sight |
| UnitXP SP3 ([GitHub](https://github.com/jrc13245/UnitXP_SP3)) | Allows Puppeteer to show very accurate distance to both friendly players and enemies, and show if they're out of line-of-sight |
| Nampower ([GitHub](https://github.com/pepopo978/nampower)) | Drastically decreases the amount of time in between casting consecutive spells |
### Planned Features
### Roadmap of Major Planned Features
- [ ] Fully customizable unit frames
- [ ] Customizable buff/debuff tracking
- [ ] Support for non-English clients
Tentative, this could change at any time.
- [X] ~~1.0.0~~
- ~~Overhaul bindings~~
- ~~Lay out groundwork for GUI development~~
- [ ] 1.1.0
- Support non-English clients
- Add Enemy frames (SuperWoW Required)
- [ ] 1.2.0 and/or 1.3.0
- Cell-like unit frame customization
- Customizable buff/debuff tracking
### FAQ & Known Issues

View File

@ -81,6 +81,7 @@ RegisterActionBind({
return
end
RoleAssignInfo.Name = UnitName(unit)
RoleAssignInfo.Class = util.GetClass(unit)
RoleAssignInfo.ClassColor = util.GetClassColor(util.GetClass(unit), true)
RoleAssignInfo.FrameGroup = unitFrame.owningGroup
local frame = unitFrame:GetRootContainer()

View File

@ -315,6 +315,55 @@ function GenerateDefaultBindings()
end
end
PVPProtectOverrideTime = 0
PVPProtectMenu = PTGuiLib.Get("dropdown", UIParent)
PVPProtectMenu:SetOptions({
{
text = colorize("PVP Flag Protection", 0.3, 1, 0.3),
textHeight = 11,
notCheckable = true,
disabled = true
},
{
notCheckable = true,
disabled = true
},
{
text = colorize("Whoops, thanks for the save!", 0.8, 1, 0.8),
notCheckable = true,
func = function()
if UIErrorsFrame then
UIErrorsFrame:AddMessage(colorize("No problem, stay safe", 0.2, 1, 0.2))
end
end
},
{
notCheckable = true,
disabled = true
},
{
text = colorize("Disable protection for 5 min", 1, 0.8, 0.8),
notCheckable = true,
func = function()
PVPProtectOverrideTime = GetTime() + (5 * 60)
if UIErrorsFrame then
UIErrorsFrame:AddMessage(colorize("Protection disabled for 5 min", 1, 0, 0))
end
end
},
{
text = colorize("Disable protection this session", 1, 0.6, 0.6),
notCheckable = true,
func = function()
PVPProtectOverrideTime = GetTime() + (1000 * 60)
if UIErrorsFrame then
UIErrorsFrame:AddMessage(colorize("Protection disabled this session", 1, 0, 0))
end
end
}
})
local Sound_Disabled = function() end
function RunTargetedAction(binding, unit, actionFunc, mustTempTarget)
@ -347,6 +396,20 @@ function RunTargetedAction(binding, unit, actionFunc, mustTempTarget)
end
end
local targetedSpell
local targetedSpellUnit
local function targetedCastFunc()
if util.IsSuperWowPresent() then
CastSpellByName(targetedSpell, targetedSpellUnit)
else
CastSpellByName(targetedSpell)
end
end
local function setupTargetedCast(spell, unit)
targetedSpell = spell
targetedSpellUnit = unit
return targetedCastFunc
end
function RunBinding_Spell(binding, unit)
local spell = binding.Data
@ -359,13 +422,7 @@ function RunBinding_Spell(binding, unit)
end
end
RunTargetedAction(binding, unit, function()
if util.IsSuperWowPresent() then
CastSpellByName(spell, unit)
else
CastSpellByName(spell)
end
end, not util.IsSuperWowPresent())
RunTargetedAction(binding, unit, setupTargetedCast(spell, unit), not util.IsSuperWowPresent())
end
function RunBinding_Action(binding, unit, unitFrame)
@ -410,6 +467,18 @@ local preScript = "local unit = PTScriptUnit;"..
"local unitFrame = PTScriptUnitFrame;"
BindingScriptCache = {}
BindingEnvironment = setmetatable({_G = _G, api = BindingScriptAPI}, {__index = PTUnitProxy or _G})
local targetedScript
local function targetedScriptFunc()
local ok, result = pcall(targetedScript)
if not ok then
DEFAULT_CHAT_FRAME:AddMessage(colorize("[Puppeteer] Error occurred while running custom script binding:", 1, 0.5, 0.5))
DEFAULT_CHAT_FRAME:AddMessage(colorize(result, 1, 0.5, 0.5))
end
end
local function setupTargetedScript(script)
targetedScript = script
return targetedScriptFunc
end
function RunBinding_Script(binding, unit, unitFrame)
local scriptString = binding.Data
if not BindingScriptCache[scriptString] then
@ -425,13 +494,7 @@ function RunBinding_Script(binding, unit, unitFrame)
BindingEnvironment.PTScriptUnit = PTUnitProxy and PTUnitProxy.CustomUnitGUIDMap[unit] or unit
BindingEnvironment.PTScriptUnitUnresolved = unit
BindingEnvironment.PTScriptUnitFrame = unitFrame
RunTargetedAction(binding, unit, function()
local ok, result = pcall(BindingScriptCache[scriptString])
if not ok then
DEFAULT_CHAT_FRAME:AddMessage(colorize("[Puppeteer] Error occurred while running custom script binding:", 1, 0.5, 0.5))
DEFAULT_CHAT_FRAME:AddMessage(colorize(result, 1, 0.5, 0.5))
end
end)
RunTargetedAction(binding, unit, setupTargetedScript(BindingScriptCache[scriptString]))
end
MultiMenu = PTGuiLib.Get("dropdown", UIParent)
@ -554,6 +617,15 @@ function RunBinding(binding, unit, unitFrame)
local bindingType = binding.Type
if bindingType == "SPELL" then
if targetCastable then
if PTOptions.PVPFlagProtection and not IsInInstance() and UnitIsPVP(unit) and UnitIsPlayer(unit)
and not UnitIsPVP("player") and PVPProtectOverrideTime < GetTime() then
PVPProtectMenu:SetToggleState(false)
local frame = unitFrame:GetRootContainer()
PVPProtectMenu:SetToggleState(true, frame, frame:GetWidth(), frame:GetHeight())
PVPProtectMenu:SetKeepOpen(true)
PlaySound("igMainMenuOpen")
return
end
RunBinding_Spell(binding, unit)
end
elseif bindingType == "ACTION" then

View File

@ -10,13 +10,13 @@ SlashCmdList["PUPPETEER"] = function(args)
gc:ClearAllPoints()
gc:SetPoint(PTUtil.GetCenterScreenPoint(gc:GetWidth(), gc:GetHeight()))
end
PuppeteerSettings.HM_SettingsContainer:ClearAllPoints()
PuppeteerSettings.HM_SettingsContainer:SetPoint("CENTER", 0, 0)
PTSettingsGui.TabFrame:ClearAllPoints()
PTSettingsGui.TabFrame:SetPoint("CENTER", 0, 0)
DEFAULT_CHAT_FRAME:AddMessage("Reset all frame positions.")
elseif args == "check" then
Puppeteer.CheckGroup()
elseif args == "update" then
for _, ui in pairs(Puppeteer.AllUnitFrames) do
for _, ui in ipairs(Puppeteer.AllUnitFrames) do
ui:SizeElements()
ui:UpdateAll()
end
@ -28,7 +28,7 @@ SlashCmdList["PUPPETEER"] = function(args)
PTOptions.TestUI = not PTOptions.TestUI
Puppeteer.TestUI = PTOptions.TestUI
if PTOptions.TestUI then
for _, ui in pairs(Puppeteer.AllUnitFrames) do
for _, ui in ipairs(Puppeteer.AllUnitFrames) do
ui.fakeStats = ui.GenerateFakeStats()
ui:Show()
end

View File

@ -65,6 +65,9 @@ RegisterEventHandler("UNIT_AURA", function()
if not IsRelevantUnit(unit) then
return
end
if unit == "player" then
util.MarkSpellCostCacheDirty()
end
PTUnit.Get(unit):UpdateAuras()
for ui in UnitFrames(unit) do
ui:UpdateAuras()
@ -107,14 +110,27 @@ RegisterEventHandler("PLAYER_TARGET_CHANGED", function()
end)
RegisterEventHandler("SPELLS_CHANGED", function()
PuppeteerSettings.UpdateTrackedDebuffTypes()
util.MarkSpellCostCacheDirty()
end)
RegisterEventHandler("CHARACTER_POINTS_CHANGED", function()
util.MarkSpellCostCacheDirty()
end)
RegisterEventHandler("RAID_TARGET_UPDATE", function()
for _, ui in ipairs(AllUnitFrames) do
ui:UpdateRaidMark()
end
end)
RegisterEventHandler("UNIT_FACTION", function()
if not IsRelevantUnit(arg1) then
return
end
for ui in UnitFrames(arg1) do
ui:UpdatePVP()
end
end)
RegisterEventHandler("PLAYER_LOGOUT", function()
RemoveOverrideBindings()
PuppeteerSettings.SaveFramePositions()
end)
local GetKeyModifier = util.GetKeyModifier

View File

@ -91,7 +91,10 @@ AddUpdateBindingsFrame("MainMenuMicroButton")
AddUpdateBindingsFrame("QuestLogMicroButton")
AddUpdateBindingsFrame("SocialsMicroButton")
AddUpdateBindingsFrame("WorldMapMicroButton")
AddUpdateBindingsFrame("pfActionBar") -- pfUI
util.RunLater(function()
AddUpdateBindingsFrame("pfActionBar") -- pfUI
AddUpdateBindingsFrame("DFRL_HotkeyBinding") -- Dragonflight Reloaded
end)
local function StopUpdateBindingsUpdates()
if holdingFunctionsHostage then

View File

@ -3,12 +3,13 @@ local _G = getfenv(0)
local util = PTUtil
local colorize = util.Colorize
local GetColoredRoleText = util.GetColoredRoleText
local SplitString = util.SplitString
AssignedRoles = nil
LFTAutoRoleFrame = CreateFrame("Frame", "PT_LFTAutoRoleFrame")
function SetLFTAutoRoleEnabled(enabled)
if not LFT_ADDON_PREFIX then -- Must not be Turtle WoW, or LFT has changed
if not util.IsTurtleWow() then
return
end
@ -48,11 +49,11 @@ function SetLFTAutoRoleEnabled(enabled)
end
LFTAutoRoleFrame:RegisterEvent("CHAT_MSG_ADDON")
LFTAutoRoleFrame:SetScript("OnEvent", function()
if arg1 == LFT_ADDON_PREFIX then
if arg1 == (LFT_ADDON_PREFIX or "TW_LFG") then
-- After an offer is complete, it is immediately followed by rolecheck info for the tank and healer
if strfind(arg2, "S2C_ROLECHECK_INFO") then
if GetNumPartyMembers() == 4 then
local params = util.SplitString(arg2, LFT_ADDON_FIELD_DELIMITER)
local params = util.SplitString(arg2, ";")
local member = params[2]
params = util.SplitString(params[3], ":") -- get confirmed roles
if offerCompleteTime + 0.5 > GetTime() and table.getn(params) == 1 then
@ -120,15 +121,121 @@ function PruneAssignedRoles()
end
end
function SetRoleAndUpdate(name, role)
SetAssignedRole(name, role)
UpdateUnitFrameGroups()
end
function SetUnitRoleAndUpdate(unit, role)
if not SetUnitAssignedRole(unit, role) then
UpdateUnitFrameGroups()
end
end
-- Players will be considered as the role in the index if they have the highest talent points in said index.
-- Clases not listed have only DPS specs and are not bothered to be scanned.
TalentCountRoleMap = {
PRIEST = {
"Healer", "Healer", "Damage"
},
PALADIN = {
"Healer", "Tank", "Damage"
},
WARRIOR = {
"Damage", "Damage", "Tank"
},
SHAMAN = {
"Damage", "Damage", "Healer"
},
DRUID = { -- Spec #2(Feral) will be swapped to Tank if Thick Hide talent is found
"Damage", "Damage", "Healer"
}
}
local PlayerTalentData = {}
local talentScanner = CreateFrame("Frame", "PTTalentScanner")
talentScanner:RegisterEvent("CHAT_MSG_ADDON")
talentScanner:SetScript("OnEvent", function()
if arg1 == "TW_CHAT_MSG_WHISPER" then
local message = arg2
local sender = arg4
if not PlayerTalentData[sender] then
return
end
if string.find(message, "INSTalentTabInfo;", 1, true) then
-- This is sent right before receiving info for individual talents in the tree
local split = SplitString(message, ';')
local index = tonumber(split[2])
local pointsSpent = tonumber(split[4])
PlayerTalentData[sender].trees[index] = {points = pointsSpent, talents = {}}
elseif string.find(message, "INSTalentInfo;", 1, true) then
local split = SplitString(message, ';')
local tree = tonumber(split[2])
local tier = tonumber(split[5])
local column = tonumber(split[6])
local currRank = tonumber(split[7])
local cache = PlayerTalentData[sender]
local talents = cache.trees[tree].talents
talents[tier.."-"..column] = currRank
elseif string.find(message, "INSTalentEND;", 1, true) then
local data = PlayerTalentData[sender]
local trees = data.trees
local mostPoints = 0
local mostIndex = 1
for i = 1, 3 do
if trees[i].points > mostPoints then
mostPoints = trees[i].points
mostIndex = i
end
end
local class = data.class
-- Check for Druid Thick Hide talent, set as tank if they have it
if class == "DRUID" and (trees[2].talents["2-3"] or 0) > 0 then
SetRoleAndUpdate(sender, "Tank")
else
SetRoleAndUpdate(sender, mostPoints > 0 and TalentCountRoleMap[class][mostIndex] or "Damage")
end
PlayerTalentData[sender] = nil
end
end
end)
local function requestTalents(name)
SendAddonMessage("TW_CHAT_MSG_WHISPER<"..name..">", "INSShowTalents", "GUILD")
end
function AutoRole(unit)
local class = util.GetClass(unit)
if not TalentCountRoleMap[class] then
SetUnitRoleAndUpdate(unit, "Damage")
return
end
if not UnitIsConnected(unit) then -- Can't request offline player's talents
return
end
PlayerTalentData[UnitName(unit)] = {class = class, trees = {}}
requestTalents(UnitName(unit))
end
function AutoRoleByNameClass(name, class)
if not TalentCountRoleMap[class] then
SetRoleAndUpdate(name, "Damage")
return
end
PlayerTalentData[name] = {class = class, trees = {}}
requestTalents(name)
end
RoleAssignInfo = {}
do
RoleDropdown = PTGuiLib.Get("dropdown", UIParent)
function InitRoleDropdown()
local initFunc = function(self)
self.checked = (GetAssignedRole(RoleAssignInfo.Name) or "No Role") == self.role
end
@ -165,7 +272,6 @@ do
}
end
RoleDropdown = PTGuiLib.Get("dropdown", UIParent)
local options = {
{
initFunc = function(self)
@ -178,7 +284,7 @@ do
genRole("Tank"),
genRole("Healer"),
genRole("Damage"),
genRole("No Role"),
genRole("No Role"),
{
notCheckable = true,
disabled = true
@ -202,5 +308,39 @@ do
func = massRoleFunc
}
}
if PTGlobalOptions.Experiments.AutoRole then
table.insert(options, 6, {
text = colorize("Auto Detect", 1, 0.6, 0),
func = function()
if not RoleAssignInfo.FrameGroup then
return
end
AutoRoleByNameClass(RoleAssignInfo.Name, RoleAssignInfo.Class)
end
})
local lastMassRole = 0
table.insert(options, 9, {
text = "Auto Detect Unassigned",
tooltipTitle = "Auto Detect Unassigned",
tooltipText = "Automatically detect the roles of unassigned players. Only applies to players contained in this UI group.",
notCheckable = true,
textHeight = 11,
func = function()
if not RoleAssignInfo.FrameGroup then
return
end
if lastMassRole + 6 > GetTime() then
DEFAULT_CHAT_FRAME:AddMessage("Please wait a moment before requesting roles again")
return
end
lastMassRole = GetTime()
for _, ui in pairs(RoleAssignInfo.FrameGroup.uis) do
if UnitIsPlayer(ui:GetUnit()) and not GetUnitAssignedRole(ui:GetUnit()) then
AutoRole(ui:GetUnit())
end
end
end
})
end
RoleDropdown:SetOptions(options)
end

View File

@ -297,8 +297,10 @@ function ApplySpellsTooltip(attachTo, unit, owner)
powerText = colorize(powerText, powerColor)
end
local modifier = util.GetKeyModifierTypeByID(1 + (options.AbbreviatedKeys and 2 or 0) + (options.ColoredKeys and 1 or 0))
SpellsTooltip:AddDoubleLine(modifier, showPowerBar and " " or powerText, 1, 1, 1)
local modifier = GetKeyModifier()
local displayModifier = modifier ~= "None" and
util.GetKeyModifierTypeByID(1 + (options.AbbreviatedKeys and 2 or 0) + (options.ColoredKeys and 1 or 0)) or " "
SpellsTooltip:AddDoubleLine(displayModifier, showPowerBar and " " or powerText, 1, 1, 1)
local friendly = not UnitCanAttack("player", unit)
@ -315,7 +317,7 @@ function ApplySpellsTooltip(attachTo, unit, owner)
end
--StartTiming("BindingDisplays")
local entries = UpdateBindingDisplays(friendly and "Friendly" or "Hostile", GetKeyModifier())
local entries = UpdateBindingDisplays(friendly and "Friendly" or "Hostile", modifier)
--EndTiming("BindingDisplays")
for _, button in ipairs(PTOptions.Buttons) do
local focused = not CurrentlyHeldButton or button == CurrentlyHeldButton

View File

@ -1,41 +1,51 @@
PTUtil.SetEnvironment(Puppeteer)
local _G = getfenv(0)
DistanceScannerFrame = CreateFrame("Frame", "PTDistanceScannerFrame", UIParent)
UnitTrackerFrame = CreateFrame("Frame", "PTUnitTrackerFrame", UIParent)
local util = PTUtil
local GetTime = GetTime
local compost = AceLibrary("Compost-2.0")
local TRACKING_MIN_DIST = 20
local TRACKING_MAX_DIST = 60
local TRACKING_EVAL_INTERVAL = 1.25
local RANGE_MIN_DIST = 20
local RANGE_MAX_DIST = 60
local SIGHT_MAX_DIST = 80
local DISTANCE_UPDATE_INTERVAL = 0.1
local SIGHT_UPDATE_INTERVAL = 0.1
local almostAllUnits = util.CloneTable(util.AllUnits) -- Everything except the player
table.remove(almostAllUnits, util.IndexOf(almostAllUnits, "player"))
if PTUnitProxy then
PTUnitProxy.RegisterUpdateListener(function()
almostAllUnits = util.CloneTable(util.AllUnits) -- Everything except the player
table.remove(almostAllUnits, util.IndexOf(almostAllUnits, "player"))
end)
end
local AllUnits = util.AllUnits
local distanceTrackedUnits = util.CloneTable(almostAllUnits) -- Initially scan all units
local sightTrackedUnits = util.CloneTable(almostAllUnits)
local distanceTrackedUnits = {}
local sightTrackedUnits = {}
local preciseDistance = util.CanClientGetPreciseDistance()
local sightTrackingEnabled = util.CanClientSightCheck()
local nextTrackingUpdate = GetTime() + 0.5
local nextUpdate = GetTime() + 0.6
if not preciseDistance and not sightTrackingEnabled then
nextUpdate = nextUpdate + 99999999 -- Effectively disable updates
local nextEval = GetTime() + 0.5
local nextRangeUpdate = GetTime() + 0.6
local nextSightUpdate = GetTime() + 0.6
if not preciseDistance then
nextRangeUpdate = nextRangeUpdate + 99999999 -- Effectively disable updates
end
if not sightTrackingEnabled then
nextSightUpdate = nextSightUpdate + 99999999
end
local TRACKING_UPDATE_INTERVAL = 1.25
function LoadTrackingOptions()
local opts = PTOptions.Tracking
TRACKING_EVAL_INTERVAL = opts.EvaluateInterval
RANGE_MIN_DIST = opts.MinDistanceTracking
RANGE_MAX_DIST = opts.MaxDistanceTracking
SIGHT_MAX_DIST = opts.MaxSightTracking
DISTANCE_UPDATE_INTERVAL = opts.DistanceUpdateInterval
SIGHT_UPDATE_INTERVAL = opts.SightUpdateInterval
end
function RunTrackingScan()
local UnitFrames = UnitFrames
local time = GetTime()
if time > nextTrackingUpdate then
if time > nextEval then
--StartTiming("TrackingEval")
nextTrackingUpdate = time + TRACKING_UPDATE_INTERVAL
nextEval = time + TRACKING_EVAL_INTERVAL
compost:Erase(distanceTrackedUnits)
@ -46,7 +56,7 @@ function RunTrackingScan()
EvaluateTracking(guid)
end
else
for _, unit in ipairs(almostAllUnits) do
for _, unit in ipairs(AllUnits) do
EvaluateTracking(unit)
end
end
@ -60,9 +70,8 @@ function RunTrackingScan()
--EndTiming("TrackingEval")
end
--StartTiming("TrackingScan")
if time > nextUpdate then
nextUpdate = time + 0.1
if time > nextRangeUpdate then
nextRangeUpdate = time + DISTANCE_UPDATE_INTERVAL
for _, unit in ipairs(distanceTrackedUnits) do
local cache = PTUnit.Get(unit)
if cache and cache:UpdateDistance() then
@ -71,16 +80,21 @@ function RunTrackingScan()
end
end
end
end
if time > nextSightUpdate then
nextSightUpdate = time + SIGHT_UPDATE_INTERVAL
for _, unit in ipairs(sightTrackedUnits) do
local cache = PTUnit.Get(unit)
--StartTiming("SightScan")
if cache and cache:UpdateSight() then
for ui in UnitFrames(unit) do
ui:UpdateSight()
end
end
--EndTiming("SightScan")
end
end
--EndTiming("TrackingScan")
end
function EvaluateTracking(unit, update)
@ -100,11 +114,17 @@ function EvaluateTracking(unit, update)
end
end
end
if cache:UpdatePVP() then
for ui in UnitFrames(unit) do
ui:UpdatePVP()
end
end
local isTarget = UnitIsUnit(unit, "target")
if PTGuidRoster then
unit = PTGuidRoster.ResolveUnitGuid(unit)
end
if isTarget or (dist < TRACKING_MAX_DIST and dist > TRACKING_MIN_DIST) then -- Only closely track units that are close to the range threshold
-- Only closely track units that are close to the range threshold
if isTarget or (dist < RANGE_MAX_DIST and dist > RANGE_MIN_DIST) then
if not update or not util.ArrayContains(distanceTrackedUnits, unit) then
table.insert(distanceTrackedUnits, unit)
end
@ -116,6 +136,7 @@ function EvaluateTracking(unit, update)
end
end
function StartDistanceScanner()
DistanceScannerFrame:SetScript("OnUpdate", RunTrackingScan)
function StartUnitTracker()
LoadTrackingOptions()
UnitTrackerFrame:SetScript("OnUpdate", RunTrackingScan)
end

View File

@ -228,8 +228,8 @@ function CreateTab_Bindings()
end)
UniversalBindingsCheckbox = universalBindingsCheckbox
local keyLabel = CreateLabel(container, "Key")
:SetPoint("TOPLEFT", container, "TOPLEFT", 120, -125)
local keyLabel = CreateLabel(container, "Key Modifier")
:SetPoint("TOPLEFT", container, "TOPLEFT", 95, -125)
local keyDropdown = CreateDropdown(container, 150)
:SetPoint("LEFT", keyLabel, "RIGHT", 5, 0)
@ -259,8 +259,8 @@ function CreateTab_Bindings()
local addButton = PTGuiLib.Get("button", container)
:SetPoint("TOP", container, "TOP", 0, -440)
:SetSize(200, 25)
:SetText("Edit Buttons")
:ApplyTooltip("Edit what buttons you use and their names")
:SetText("Add or Remove Buttons")
:ApplyTooltip("Edit what buttons you can bind spells to")
:OnClick(function()
local editor = PTGuiLib.Get("puppeteer_button_editor", TabFrame)
:SetPoint("CENTER", TabFrame, "CENTER")
@ -393,6 +393,9 @@ function CreateTab_Options_Casting(panel)
local autoResInfo = not resSpell and "This does nothing for your class" or {"Replaces your bound spells with "..resSpell..
" when clicking on a dead ally", "All other types of binds, such as Actions, will not be replaced"}
factory:checkbox("Auto Resurrect", autoResInfo, "AutoResurrect")
factory:checkbox("PVP Flag Protection", {"Stops you from casting spells on PVP flagged players if you're not flagged",
"Attempting to cast will prompt you to make an exception",
"Only stops you from using Spell bindings"}, "PVPFlagProtection")
factory:checkbox("Target While Casting", {"Target the unit while most bindings run",
"Note that these binding types override this rule:",
"Spell - Always targets unless using SuperWoW",
@ -436,7 +439,7 @@ function CreateTab_Options_SpellsTooltip(panel)
layout:offset(0, 10)
factory:dropdown("Anchor", "Where the tooltip should be anchored", "SpellsTooltip.Anchor",
{"Top Left", "Top Right", "Bottom Left", "Bottom Right"})
factory:checkbox("Show Item Count", {"Show the amount of your bound items", colorize("Warning: This causes lag!", 1, 0.2, 0.2)},
factory:checkbox("Show Item Count", {"Show the amount of your bound items", colorize("Warning: This causes lag!", 1, 0.4, 0.4)},
"SpellsTooltip.ShowItemCount")
end
@ -477,22 +480,37 @@ function CreateTab_Options_Other(panel)
"UseHealPredictions", function() Puppeteer.UpdateAllIncomingHealing() end)
factory:checkbox("(TWoW) LFT Auto Role", {"Automatically assign roles when joining LFT groups",
"This functionality was created for 1.17.2 and may break in future updates"}, "LFTAutoRole",
"This functionality was tested for 1.18.0 and may break in future updates"}, "LFTAutoRole",
function() Puppeteer.SetLFTAutoRoleEnabled(PTOptions.LFTAutoRole) end)
end
function CreateTab_Options_Advanced(panel)
local container = panel:CreateTab("Advanced")
local layout = NewLabeledColumnLayout(container, {150, 220, 300}, -20, 10)
local layout = NewLabeledColumnLayout(container, {150, 220, 300}, 0, 10)
local factory = NewComponentFactory(container, layout)
container.factory = factory
local TEXT_WIDTH = 370
local experimentsLabel = CreateLabel(container, "Experiments")
:SetPoint("TOP", container, "TOP", 0, -20)
:SetFontSize(14)
local experimentsInfo = CreateLabel(container, "Features which are not complete and/or need more testing. Use at your own risk.")
:SetWidth(TEXT_WIDTH)
:SetPoint("TOP", experimentsLabel, "BOTTOM", 0, -5)
layout:offset(0, -70)
factory:checkbox("(TWoW) Auto Role", {"If enabled, the Role Action menu shows auto role detection options",
colorize("Using this functionality WILL cause errors and other unexpected behavior", 1, 0.4, 0.4)}, "Global.Experiments.AutoRole",
Puppeteer.InitRoleDropdown)
local scriptsLabel = CreateLabel(container, "Load & Postload Scripts")
:SetPoint("TOP", container, "TOP", 0, -105)
:SetFontSize(14)
local loadScriptInfo = CreateLabel(container, "The Load Script runs after profiles are initialized, but before UIs are created, "..
"making it good for editing profile attributes. GetProfile and CreateProfile are defined locals.")
:SetWidth(TEXT_WIDTH)
:SetPoint("TOP", container, "TOP", 0, -10)
:SetPoint("TOP", scriptsLabel, "BOTTOM", 0, -10)
local loadScriptButton = PTGuiLib.Get("button", container)
:SetPoint("TOP", loadScriptInfo, "BOTTOM", 0, -5)
:SetSize(150, 20)
@ -537,7 +555,7 @@ function CreateTab_Options_Advanced(panel)
editor:GetEditbox():SetFocus()
AddOverlayFrame(editor)
end)
local reloadInfo = CreateLabel(container, "A reload or relog is required for any changes to take effect.")
local reloadInfo = CreateLabel(container, "A reload or relog is required for any script changes to take effect.")
:SetWidth(TEXT_WIDTH)
:SetPoint("TOP", postLoadScriptButton, "BOTTOM", 0, -20)
local reloadButton = PTGuiLib.Get("button", container)
@ -581,7 +599,8 @@ function CreateTab_Options_Mods(panel)
"• Enhances spell casting by directly casting on targets rather than split-second target switching tricks\n"..
"• Allows you to see accurate distance to other friendly players and NPCs\n"..
"• Mousing over unit frames properly sets your mouseover target\n"..
"• Shows incoming healing from players that do not have HealComm and predicts more accurate numbers")
"• Shows incoming healing from players that do not have HealComm and predicts more accurate numbers\n"..
"• Add players/mobs to a separate Focus frame (By using the Focus action bind)")
:SetJustifyH("LEFT")
:SetWidth(TEXT_WIDTH)
:SetPoint("TOP", superWowDetectedLabel, "BOTTOM", 0, -10)
@ -615,7 +634,7 @@ function CreateTab_Options_Mods(panel)
:SetJustifyH("LEFT")
:SetWidth(TEXT_WIDTH)
:SetPoint("TOP", unitXPDetectedLabel, "BOTTOM", 0, -10)
local unitXPLink = CreateLinkEditbox(container, "https://github.com/allfoxwy/UnitXP_SP3")
local unitXPLink = CreateLinkEditbox(container, "https://github.com/jrc13245/UnitXP_SP3")
:SetPoint("TOP", unitXPInfo, "BOTTOM", 0, -5)
:SetSize(300, 20)
local unitXPLinkLabel = CreateLabel(container, "Link:")
@ -653,8 +672,8 @@ function CreateTab_Customize()
local frameStyleContainer = PTGuiLib.Get("container", container)
:SetSimpleBackground()
:SetPoint("TOPLEFT", container, "TOPLEFT", 5, -26)
:SetPoint("BOTTOMRIGHT", container, "TOPRIGHT", -5, -120)
local layout = NewLabeledColumnLayout(frameStyleContainer, {100, 340}, -35, 10)
:SetPoint("BOTTOMRIGHT", container, "TOPRIGHT", -5, -155)
local layout = NewLabeledColumnLayout(frameStyleContainer, {100, 340, 175}, -25, 5)
local frameSettingsText = CreateLabel(frameStyleContainer, "Frame Group Settings")
:SetPoint("TOP", frameStyleContainer, "TOP", 0, -5)
@ -662,7 +681,7 @@ function CreateTab_Customize()
local preferredFrameOrder = {"Party", "Pets", "Raid", "Raid Pets", "Target", "Focus"}
local frameDropdown = CreateLabeledDropdown(frameStyleContainer, "Select Frame", "The frame to edit the attributes of")
local frameDropdown = CreateLabeledDropdown(frameStyleContainer, "Configure Frame", "The frame to edit the attributes of")
:SetWidth(150)
:SetDynamicOptions(function(addOption, level, args)
for _, name in ipairs(preferredFrameOrder) do
@ -687,12 +706,13 @@ function CreateTab_Customize()
end,
func = function(self, gui)
StyleDropdown:UpdateText()
AnchorDropdown:UpdateText()
UpdateFrameOptions()
end
})
:SetText("Party")
FrameDropdown = frameDropdown
layout:layoutComponent(frameDropdown)
layout:column(3):layoutComponent(frameDropdown)
local GetSelectedProfileName = PuppeteerSettings.GetSelectedProfileName
local styleDropdown = CreateLabeledDropdown(frameStyleContainer, "Choose Style", "The style of the frame")
:SetWidth(150)
@ -746,7 +766,51 @@ function CreateTab_Customize()
self:SetText(GetSelectedProfileName(frameDropdown:GetText()))
end)
StyleDropdown = styleDropdown
layout:offset(0, 10):layoutComponent(styleDropdown)
layout:column(1):offset(0, -30):layoutComponent(styleDropdown)
local anchors = {"TOPLEFT", "TOP", "TOPRIGHT", "LEFT", "CENTER", "RIGHT", "BOTTOMLEFT", "BOTTOM", "BOTTOMRIGHT"}
local readableAnchorMap = {
TOPLEFT = "Top Left",
TOP = "Top",
TOPRIGHT = "Top Right",
LEFT = "Left",
CENTER = "Center",
RIGHT = "Right",
BOTTOMLEFT = "Bottom Left",
BOTTOM = "Bottom",
BOTTOMRIGHT = "Bottom Right"
}
local anchorDropdown = CreateLabeledDropdown(frameStyleContainer, "Anchor",
{"The point the frame is anchored to, affecting the direction it expands/retracts",
"Top Left: Expands right and down",
"Top: Expands equally left & right and down",
"Top Right: Expands left and down",
"Left: Expands right and equally up & down",
"Center: Expands equally in all directions",
"Right: Expands left and equally up & down",
"Bottom Left: Expands right and up",
"Bottom: Expands equally left & right and up",
"Bottom Right: Expands left and up",})
:SetWidth(150)
:SetSimpleOptions(anchors, function(option)
return {
initFunc = function(self)
self.checked = PuppeteerSettings.GetFramePosition(frameDropdown:GetText()) == option
end,
func = function(self, gui)
local group = Puppeteer.UnitFrameGroups[frameDropdown:GetText()]
util.ConvertAnchor(group:GetContainer(), option)
PuppeteerSettings.SaveFramePositions()
gui:UpdateText()
end,
text = readableAnchorMap[option]
}
end)
:SetTextUpdater(function(self)
self:SetText(readableAnchorMap[PuppeteerSettings.GetFramePosition(frameDropdown:GetText())])
end)
layout:layoutComponent(anchorDropdown)
AnchorDropdown = anchorDropdown
local lockFrameCheckbox = CreateLabeledCheckbox(frameStyleContainer, "Lock Frame", {"If checked, this frame will not be movable",
"Note: This setting is also accessible by right-clicking the group title bar"})
@ -754,9 +818,18 @@ function CreateTab_Customize()
local frameName = frameDropdown:GetText()
PuppeteerSettings.SetFrameLocked(frameName, self:GetChecked() == 1)
end)
layout:column(2):layoutComponent(lockFrameCheckbox)
layout:column(2):offset(0, -30):layoutComponent(lockFrameCheckbox)
LockFrameCheckbox = lockFrameCheckbox
local hideTitleCheckbox = CreateLabeledCheckbox(frameStyleContainer, "Hide Title", {"If checked, the title of this frame will be hidden",
colorize("Note: When you want to move the frame, you need to enable the title!", 1, 0.4, 0.4)})
:OnClick(function(self)
local frameName = frameDropdown:GetText()
PuppeteerSettings.SetTitleHidden(frameName, self:GetChecked() == 1)
end)
layout:layoutComponent(hideTitleCheckbox)
HideTitleCheckbox = hideTitleCheckbox
local hideFrameCheckbox = CreateLabeledCheckbox(frameStyleContainer, "Hide Frame", "If checked, this frame will not be visible")
:OnClick(function(self)
local frameName = frameDropdown:GetText()
@ -828,23 +901,26 @@ function CreateTab_Customize()
add(createDropdown("Health Bar Color", nil, "HealthBarColor", {"Green To Red", "Green", "Class"}), -10)
add(createDropdown("Health Bar Texture", nil, "HealthBarStyle", barStyles))
add(createDropdown("Health Display", "What kind of text is displayed as health", "HealthDisplay", {"Health", "Health/Max Health", "% Health", "Hidden"}))
add(createDropdown("Missing Health Display", "What kind of text is displayed as missing health", "MissingHealthDisplay", {"Hidden", "-Health", "-% Health"}))
add(createDropdown("Missing Health Display", "What kind of text is displayed as missing health", "MissingHealthDisplay", {"-Health", "-% Health", "Hidden"}))
add(createDropdown("Incoming Heal Display", nil, "IncomingHealDisplay", {"Overheal", "Heal", "Hidden"}))
add(createDropdown("Power Bar Texture", nil, "PowerBarStyle", barStyles))
add(createDropdown("Power Display", "What kind of text is displayed as missing", "PowerDisplay", {"Power", "Power/Max Power", "% Power", "Hidden"}))
add(createDropdown("Power Display", "What kind of text is displayed as power", "PowerDisplay", {"Power", "Power/Max Power", "% Power", "Hidden"}))
add(createDropdown("Name Text Color", "'Default' is default Blizzard yellow text", "NameText.Color", {"Class", "Default"}))
add(createDropdown("Show Debuff Colors On", nil, "ShowDebuffColorsOn", {"Health Bar", "Name", "Health", "Hidden"}))
add(createDropdown("Sort Units By", "The sorting algorithm for units in a group", "SortUnitsBy", {"ID", "Name", "Class Name"}))
add(createDropdown("Growth Direction", "Vertical grows units down, Horizontal grows units right", "Orientation", {"Vertical", "Horizontal"}))
add(createDropdown("Growth Orientation", "Vertical grows units up and down, Horizontal grows units left and right", "Orientation", {"Vertical", "Horizontal"}))
add(createDropdown("Border Style", "The border of the group", "BorderStyle", {"Tooltip", "Dialog Box", "Borderless"}))
add(createDropdown("Max Units In Axis", "The maximum number of units in the growth axis until it must shift down", "MaxUnitsInAxis", {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}))
add(createDropdown("Min Units X", "The minimum amount of unit space to take on the X-axis", "MinUnitsX", {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}))
add(createDropdown("Min Units Y", "The minimum amount of unit space to take on the Y-axis", "MinUnitsY", {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}))
add(createDropdown("Horizontal Spacing", "The number of pixels between units", "HorizontalSpacing", {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}))
add(createDropdown("Vertical Spacing", "The number of pixels between units", "VerticalSpacing", {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}))
add(createDropdown("Out of Range Opacity", "How opaque out of range players appear in %", "OutOfRangeOpacity", {0, 10, 20, 30, 40, 50, 60, 70, 80, 90, 100}))
end
function UpdateFrameOptions()
LockFrameCheckbox:SetChecked(PuppeteerSettings.IsFrameLocked(FrameDropdown:GetText()))
HideTitleCheckbox:SetChecked(PuppeteerSettings.IsTitleHidden(FrameDropdown:GetText()))
HideFrameCheckbox:SetChecked(PuppeteerSettings.IsFrameHidden(FrameDropdown:GetText()))
end

View File

@ -95,6 +95,7 @@ function PTButtonEditor:New()
end
if not obj:ContainsButton(arg1) then
obj:AddButtonLine(arg1, arg1)
PlaySound("igSpellBookOpen")
end
obj:SetAddMode(false)
local onClick = addButton:GetScript("OnClick")
@ -114,6 +115,7 @@ function PTButtonEditor:New()
if not obj:ContainsButton(arg1) then
if not obj:AreBindingsFull() then
obj:AddButtonLine(arg1, arg1)
PlaySound("igSpellBookOpen")
else
DEFAULT_CHAT_FRAME:AddMessage("You cannot add any more non-mouse buttons!")
end
@ -128,6 +130,7 @@ function PTButtonEditor:New()
if not obj:ContainsButton("MOUSEWHEELUP") then
if not obj:AreBindingsFull() then
obj:AddButtonLine("MOUSEWHEELUP", "MOUSEWHEELUP")
PlaySound("igSpellBookOpen")
else
DEFAULT_CHAT_FRAME:AddMessage("You cannot add any more non-mouse buttons!")
end
@ -136,6 +139,7 @@ function PTButtonEditor:New()
if not obj:ContainsButton("MOUSEWHEELDOWN") then
if not obj:AreBindingsFull() then
obj:AddButtonLine("MOUSEWHEELDOWN", "MOUSEWHEELDOWN")
PlaySound("igSpellBookOpen")
else
DEFAULT_CHAT_FRAME:AddMessage("You cannot add any more non-mouse buttons!")
end

View File

@ -23,8 +23,7 @@ function PTNewLoadout:New()
:SetDynamicOptions(function(addOption, level, args)
addOption("text", "<Blank>",
"dropdownText", "<Blank>",
"initFunc", args.initFunc,
"func", args.func)
"initFunc", args.initFunc)
for _, name in ipairs(Puppeteer.GetBindingLoadoutNames()) do
addOption("text", name,
"dropdownText", name,
@ -94,7 +93,6 @@ end
function PTNewLoadout:OnDispose()
self.super.OnDispose(self)
end
PTNewLoadout:CreateGetter("InheritDropdown")

View File

@ -323,7 +323,8 @@ function PTSpellLine:Update()
self:SetEnabledContent("editbox")
self:GetTypeDropdown():SetText("Macro")
self:GetContentEditbox():SetText(binding.Data or "")
self:GetContentEditbox():ApplyTooltip("Enter the name of one of your macros")
self:GetContentEditbox():ApplyTooltip("Enter the name of one of your macros",
"Note that macro binds won't target unless you enable", "\"Target While Casting\" in this binding's settings or globally")
self:ApplySearchableEditbox("MACRO", SearchMacros, 1)
elseif binding.Type == "ITEM" then
self:SetEnabledContent("editbox")

View File

@ -9,7 +9,7 @@ local turtle = PTUtil.IsTurtleWow()
local trackedCastedAuras = {
-- Druid
["Rejuvenation"] = 12,
["Regrowth"] = 21,
["Regrowth"] = turtle and 20 or 21,
["Mark of the Wild"] = 30 * 60,
["Gift of the Wild"] = 60 * 60,
["Thorns"] = 10 * 60,
@ -33,6 +33,7 @@ local trackedCastedAuras = {
["Fear Ward"] = 10 * 60,
["Champion's Grace"] = 30 * 60,
["Empower Champion"] = 10 * 60,
["Champion's Bond"] = 10 * 60,
["Spirit of Redemption"] = 10,
["Abolish Disease"] = 20,
["Inner Fire"] = 10 * 60,

View File

@ -24,12 +24,17 @@ function ResetRoster()
end
function PopulateRoster()
for _, unit in ipairs(util.AllUnits) do
for _, unit in ipairs(util.AllRealUnits) do
local exists, guid = UnitExists(unit)
if exists then
AddUnit(guid, unit)
end
end
for guid, units in pairs(PTUnitProxy.GUIDCustomUnitMap) do
for _, unit in ipairs(units) do
AddUnit(guid, unit)
end
end
end
function AddUnit(guid, unit)

View File

@ -303,6 +303,7 @@ function UpdateCacheHot(spellName, heal, targetGuid, targetName, casterGuid, cas
if not hots[spellName] then
return
end
heal = tonumber(heal) or 0
local hot = hots[spellName]
if hot["heal"] ~= heal then
local prevHeal = hot["heal"]

View File

@ -8,8 +8,10 @@ local _G = getfenv(0)
local util = PTUtil
local GetAuraInfo = util.GetAuraInfo
local AllUnits = util.AllUnits
local AllRealUnits = util.AllRealUnits
local AllUnitsSet = util.AllUnitsSet
local superwow = util.IsSuperWowPresent()
local canGetAuraIDs = util.CanClientGetAuraIDs()
local compost = AceLibrary("Compost-2.0")
@ -35,6 +37,8 @@ PTUnit.HasHealingModifier = false
-- Only used with SuperWoW, managed in AuraTracker.lua
PTUnit.AuraTimes = {} -- Key: Aura Name | Value: {"startTime", "duration"}
PTUnit.DisplayPVP = false -- This is not the real PVP status of the unit, this is affected by other conditions
PTUnit.Distance = 0
PTUnit.InSight = true
PTUnit.IsNew = false
@ -53,7 +57,7 @@ end
function UpdateGuidCaches()
local cached = PTUnit.Cached
local prevCached = PTUtil.CloneTableCompost(cached)
for _, unit in ipairs(AllUnits) do
for _, unit in ipairs(AllRealUnits) do
local exists, guid = UnitExists(unit)
if exists then
if not cached[guid] then
@ -92,7 +96,7 @@ function Get(unit)
if superwow and AllUnitsSet[unit] then
return PTUnit.Cached[PTGuidRoster.GetUnitGuid(unit)] or PTUnit
end
return PTUnit.Cached[unit]
return PTUnit.Cached[unit] or PTUnit
end
function GetAllUnits()
@ -107,6 +111,7 @@ function PTUnit:New(unit)
obj:AllocateAuras()
obj.AurasPopulated = true -- To force aura fields to generate
obj.IsNew = true
obj.IsSelf = UnitIsUnit(unit, "player")
if superwow then
obj.AuraTimes = compost:GetTable()
end
@ -128,6 +133,7 @@ end
function PTUnit:UpdateAll()
self:UpdateAuras()
self:UpdatePVP()
self:UpdateDistance()
self:UpdateSight()
end
@ -140,13 +146,29 @@ function PTUnit:CheckNew()
end
end
function PTUnit:UpdatePVP()
if not self.Unit then
return
end
local shouldDisplay = UnitIsPVP(self.Unit) and (not IsInInstance() or not UnitIsVisible(self.Unit))
if self.DisplayPVP ~= shouldDisplay then
self.DisplayPVP = shouldDisplay
return true
end
return false
end
function PTUnit:ShouldDisplayPVP()
return self.DisplayPVP
end
-- Returns true if the distance changed
function PTUnit:UpdateDistance()
if not self.Unit then
return
end
local prevDist = self.Distance
self.Distance = util.GetDistanceTo(self.Unit)
self.Distance = self.IsSelf and 0 or util.GetDistanceTo(self.Unit)
return self.Distance ~= prevDist
end
@ -161,7 +183,7 @@ function PTUnit:UpdateSight()
return
end
local wasInSight = self.InSight
self.InSight = util.IsInSight(self.Unit)
self.InSight = self.IsSelf or util.IsInSight(self.Unit)
return self.InSight ~= wasInSight
end
@ -202,25 +224,21 @@ function PTUnit:AllocateAuras()
end
function PTUnit:ClearAuras()
if not self.AurasPopulated or self.Buffs == PTUnit.Buffs then
if not self.AurasPopulated or self == PTUnit then
return
end
compost:Reclaim(self.Buffs, 1)
compost:Reclaim(self.BuffsMap, 1)
compost:Reclaim(self.BuffsIDSet)
compost:Erase(self.BuffsIDSet)
compost:Reclaim(self.Debuffs, 1)
compost:Reclaim(self.DebuffsMap, 1)
compost:Reclaim(self.DebuffsIDSet)
compost:Reclaim(self.TypedDebuffs)
compost:Reclaim(self.AfflictedDebuffTypes)
compost:Erase(self.DebuffsIDSet)
compost:Erase(self.TypedDebuffs)
compost:Erase(self.AfflictedDebuffTypes)
self.Buffs = compost:GetTable()
self.BuffsMap = compost:GetTable()
self.BuffsIDSet = compost:GetTable()
self.Debuffs = compost:GetTable()
self.DebuffsMap = compost:GetTable()
self.DebuffsIDSet = compost:GetTable()
self.TypedDebuffs = compost:GetTable()
self.AfflictedDebuffTypes = compost:GetTable()
self.HasHealingModifier = false
self.AurasPopulated = false
end
@ -238,9 +256,6 @@ function PTUnit:UpdateAuras()
return
end
local PT = Puppeteer
-- Track player buffs
local buffs = self.Buffs
local buffsMap = self.BuffsMap
local buffsIDSet = self.BuffsIDSet
@ -249,7 +264,7 @@ function PTUnit:UpdateAuras()
if not texture then
break
end
local name, type = GetAuraInfo(unit, "Buff", index)
local name, type = GetAuraInfo(unit, index, "Buff", id)
if PuppeteerSettings.TrackedHealingBuffs[name] then
self.HasHealingModifier = true
end
@ -265,7 +280,6 @@ function PTUnit:UpdateAuras()
end
local afflictedDebuffTypes = self.AfflictedDebuffTypes
-- Track player debuffs
local debuffs = self.Debuffs
local debuffsMap = self.DebuffsMap
local debuffsIDSet = self.DebuffsIDSet
@ -276,7 +290,7 @@ function PTUnit:UpdateAuras()
break
end
type = type or ""
local name = GetAuraInfo(unit, "Debuff", index)
local name = GetAuraInfo(unit, index, "Debuff", id)
if PuppeteerSettings.TrackedHealingDebuffs[name] then
self.HasHealingModifier = true
end
@ -304,14 +318,14 @@ function PTUnit:HasBuff(name)
return self.BuffsMap[name] ~= nil
end
-- SuperWoW only
-- SuperWoW/Turtle WoW only
function PTUnit:HasBuffID(id)
return self.BuffsIDSet[id] ~= nil
end
-- Looks for ID if SuperWoW is present, otherwise searches by name
-- Looks for ID if SuperWoW/Turtle WoW is present, otherwise searches by name
function PTUnit:HasBuffIDOrName(id, name)
if superwow then
if canGetAuraIDs then
return self:HasBuffID(id)
end
return self:HasBuff(name)
@ -321,14 +335,14 @@ function PTUnit:HasDebuff(name)
return self.DebuffsMap[name] ~= nil
end
-- SuperWoW only
-- SuperWoW/Turtle WoW only
function PTUnit:HasDebuffID(id)
return self.DebuffsIDSet[id] ~= nil
end
-- Looks for ID if SuperWoW is present, otherwise searches by name
-- Looks for ID if SuperWoW/Turtle WoW is present, otherwise searches by name
function PTUnit:HasDebuffIDOrName(id, name)
if superwow then
if canGetAuraIDs then
return self:HasDebuffID(id)
end
return self:HasDebuff(name)
@ -363,6 +377,9 @@ function PTUnit:GetDebuffs(name)
end
function PTUnit:GetAuraTimeRemaining(name)
if not superwow then
return
end
local auraTime = self.AuraTimes[name]
if not auraTime then
return

View File

@ -407,6 +407,8 @@ function CreateUnitProxies()
UnitProxy("FollowUnit", _G.FollowUnit, nil)
UnitProxy("AssistUnit", _G.AssistUnit, nil)
UnitProxy("UnitAffectingCombat", _G.UnitAffectingCombat, false)
UnitProxy("UnitIsPVP", _G.UnitIsPVP, false)
UnitProxy("UnitFactionGroup", _G.UnitFactionGroup, nil)
DoubleUnitProxy("UnitIsFriend", _G.UnitIsFriend, false)
DoubleUnitProxy("UnitIsEnemy", _G.UnitIsEnemy, false)
DoubleUnitProxy("UnitIsUnit", _G.UnitIsUnit, false)

View File

@ -35,7 +35,7 @@ if SUPERWOW_VERSION then
end
Nampower = QueueSpellByName ~= nil
TurtleWow = true
TurtleWow = TURTLE_WOW_VERSION ~= nil
PowerColors = {
["mana"] = {0.1, 0.25, 1}, --{r = 0, g = 0, b = 0.882}, Not accurate, changed color to make brighter
@ -498,14 +498,14 @@ function ExtractSpellRank(spellname)
end
-- Thanks again ChatGPT
local tooltipResources = {"Mana", "Rage", "Energy"}
local tooltipResources = {["Mana"] = "mana", ["Rage"] = "rage", ["Energy"] = "energy"}
function ExtractResourceCost(costText)
-- First extract resource type
local resource
for _, r in ipairs(tooltipResources) do
if string.find(costText, r) then
resource = string.lower(r)
for tooltipName, lowerName in pairs(tooltipResources) do
if string.find(costText, tooltipName) then
resource = lowerName
break
end
end
@ -562,8 +562,23 @@ function GetSpellID(spellname)
return foundID
end
local costCache = {}
local costTypeCache = {}
local costCacheDirty = false
function MarkSpellCostCacheDirty()
costCacheDirty = true
end
-- Returns the numerical cost and the resource name; "unknown" if the spell is unknown; 0 if the spell is free
function GetResourceCost(spellName)
if costCacheDirty then
ClearTable(costCache)
ClearTable(costTypeCache)
costCacheDirty = false
end
if costCache[spellName] then
return costCache[spellName], costTypeCache[spellName]
end
ScanningTooltip:SetOwner(UIParent, "ANCHOR_NONE");
local spellID, bookType
@ -587,13 +602,15 @@ function GetResourceCost(spellName)
local leftText = _G["PTScanningTooltipTextLeft2"]
if leftText:GetText() then
return ExtractResourceCost(leftText:GetText())
costCache[spellName], costTypeCache[spellName] = ExtractResourceCost(leftText:GetText())
return costCache[spellName], costTypeCache[spellName]
end
costCache[spellName] = 0
return 0
end
-- Returns the aura's name and its school type
function GetAuraInfo(unit, type, index)
function ScanAuraInfo(unit, index, type)
-- Make these texts blank since they don't clear otherwise
local leftText = _G["PTScanningTooltipTextLeft1"]
leftText:SetText("")
@ -607,6 +624,36 @@ function GetAuraInfo(unit, type, index)
return leftText:GetText() or "", rightText:GetText() or ""
end
if SuperWoW or TurtleWow then
local auraNameCache = {}
local auraTypeCache = {}
function GetAuraInfo(unit, index, type, id)
if not id then
if type == "Buff" then
local _, _, i = UnitBuff(unit, index)
id = i
else
local _, _, _, i = UnitDebuff(unit, index)
id = i
end
if not id then -- Uh oh, Turtle lost the ID
return ScanAuraInfo(unit, index, type)
end
end
if not auraNameCache[id] then
auraNameCache[id], auraTypeCache[id] = ScanAuraInfo(unit, index, type)
end
return auraNameCache[id], auraTypeCache[id]
end
function GetCachedAuraInfo(id)
return auraNameCache[id], auraTypeCache[id]
end
else
GetAuraInfo = ScanAuraInfo
end
-- Returns an array of the units in the party number or the unit's raid group
function GetRaidPartyMembers(partyNumberOrUnit)
if not RAID_SUBGROUP_LISTS then
@ -1030,6 +1077,34 @@ function GetCenterScreenPoint(componentWidth, componentHeight)
return "TOPLEFT", (GetScreenWidth() / 2) - (componentWidth / 2), -((GetScreenHeight() / 2) - (componentHeight / 2))
end
-- Keeps the frame at the current position, while modifying the anchor point
function ConvertAnchor(frame, anchor)
local leftX, rightX, topY, bottomY = frame:GetLeft(), frame:GetRight(), frame:GetTop(), frame:GetBottom()
local centerX, centerY = frame:GetCenter()
local x, y
if anchor == "TOPLEFT" then
x, y = leftX, topY
elseif anchor == "TOPRIGHT" then
x, y = rightX, topY
elseif anchor == "BOTTOMLEFT" then
x, y = leftX, bottomY
elseif anchor == "BOTTOMRIGHT" then
x, y = rightX, bottomY
elseif anchor == "TOP" then
x, y = centerX, topY
elseif anchor == "BOTTOM" then
x, y = centerX, bottomY
elseif anchor == "LEFT" then
x, y = leftX, centerY
elseif anchor == "RIGHT" then
x, y = rightX, centerY
elseif anchor == "CENTER" then
x, y = centerX, centerY
end
frame:ClearAllPoints()
frame:SetPoint(anchor, UIParent, "TOPLEFT", x, y - GetScreenHeight())
end
function GetPowerType(unit)
return PowerTypeMap[UnitPowerType(unit)]
end
@ -1151,6 +1226,10 @@ function CanClientSightCheck()
return UnitXPSP3
end
function CanClientGetAuraIDs()
return SuperWoW or TurtleWow
end
function IsSuperWowPresent()
return SuperWoW
end

View File

@ -23,4 +23,21 @@ function PTGuiCheckbox:OnDispose()
self:SetChecked(false)
end
function PTGuiCheckbox:SetScript(scriptName, script, noSelf)
if scriptName ~= "OnClick" then
return self.super.SetScript(self, scriptName, script, noSelf)
end
self:GetHandle():SetScript(scriptName, function()
if self:GetChecked() then
PlaySound("igMainMenuOptionCheckBoxOn")
else
PlaySound("igMainMenuOptionCheckBoxOff")
end
if script then
script(self)
end
end)
return self
end
PTGuiLib.RegisterComponent(PTGuiCheckbox)