Compare commits

...

18 Commits
v1.0.4 ... 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
19 changed files with 488 additions and 110 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)
@ -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)

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
@ -366,7 +384,7 @@ function OnAddonLoaded()
end
initUnitFrames()
StartDistanceScanner()
StartUnitTracker()
PuppeteerLib:RegisterEvent("Banzai_UnitGainedAggro", function(unit)
if PTGuidRoster then
@ -738,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()
@ -815,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.4
## 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,13 +133,19 @@ function SetDefaults()
["Medium"] = 10, -- <=2 min
["Long"] = 60 * 2 -- >2 min
},
["Experiments"] = {
["AutoRole"] = false
["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,
@ -287,6 +293,9 @@ function SetDefaults()
do
local defaults = {
["ShowLoadMessage"] = true,
["Experiments"] = {
["AutoRole"] = false
},
["OptionsVersion"] = OPTIONS_VERSION
}
ApplyDefaults(PTGlobalOptions, defaults)
@ -307,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
@ -459,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
@ -476,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

@ -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)
@ -568,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

@ -120,8 +120,17 @@ RegisterEventHandler("RAID_TARGET_UPDATE", function()
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

@ -308,7 +308,7 @@ function InitRoleDropdown()
func = massRoleFunc
}
}
if PTOptions.Experiments.AutoRole then
if PTGlobalOptions.Experiments.AutoRole then
table.insert(options, 6, {
text = colorize("Auto Detect", 1, 0.6, 0),
func = function()

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,34 +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 AllUnits = util.AllUnits
local distanceTrackedUnits = util.CloneTable(AllUnits) -- Initially scan all units
local sightTrackedUnits = util.CloneTable(AllUnits)
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)
@ -53,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
@ -64,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)
@ -93,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
@ -109,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

@ -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",
@ -489,8 +492,19 @@ function CreateTab_Options_Advanced(panel)
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, -10)
: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, "..
@ -541,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)
@ -551,14 +565,6 @@ function CreateTab_Options_Advanced(panel)
:OnClick(function()
ReloadUI()
end)
local experimentsLabel = CreateLabel(container, "Experiments")
:SetPoint("TOP", reloadButton, "BOTTOM", 0, -20)
:SetFontSize(14)
layout:offset(0, -260)
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)}, "Experiments.AutoRole",
Puppeteer.InitRoleDropdown)
end
function CreateTab_Options_Mods(panel)
@ -593,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)
@ -627,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:")
@ -665,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)
@ -674,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
@ -699,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)
@ -758,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"})
@ -766,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()
@ -840,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 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

@ -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

@ -8,6 +8,7 @@ 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()
@ -36,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
@ -54,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
@ -130,6 +133,7 @@ end
function PTUnit:UpdateAll()
self:UpdateAuras()
self:UpdatePVP()
self:UpdateDistance()
self:UpdateSight()
end
@ -142,6 +146,22 @@ 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

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

@ -1077,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