Add files via upload
This commit is contained in:
@@ -44,5 +44,6 @@ modules\castbar.lua
|
||||
modules\guiddebug.lua
|
||||
modules\tankmode.lua
|
||||
modules\healthpercent.lua
|
||||
modules\personalnameplate.lua
|
||||
modules\combopoints.lua
|
||||
modules\commands.lua
|
||||
|
||||
@@ -79,6 +79,14 @@ BNP.defaults = BNP.defaults or {
|
||||
questPlateIconSize = 16,
|
||||
questPlateXOffset = 10,
|
||||
questPlateYOffset = -7,
|
||||
personalNameplate = false,
|
||||
personalNameplateCombatOnly = true,
|
||||
personalNameplateClassColor = false,
|
||||
personalNameplateHealthText = "both",
|
||||
personalNameplateDebuffs = false,
|
||||
personalNameplateDebuffYOffset = 0,
|
||||
personalNameplateScale = 1.0,
|
||||
personalNameplateYOffset = -90,
|
||||
}
|
||||
|
||||
function BNP:InitConfig()
|
||||
@@ -236,6 +244,14 @@ function BNP:InitConfig()
|
||||
if BNP_DB.questPlateIconSize == nil then BNP_DB.questPlateIconSize = self.defaults.questPlateIconSize end
|
||||
if BNP_DB.questPlateXOffset == nil then BNP_DB.questPlateXOffset = self.defaults.questPlateXOffset end
|
||||
if BNP_DB.questPlateYOffset == nil then BNP_DB.questPlateYOffset = self.defaults.questPlateYOffset end
|
||||
if BNP_DB.personalNameplate == nil then BNP_DB.personalNameplate = self.defaults.personalNameplate end
|
||||
if BNP_DB.personalNameplateCombatOnly == nil then BNP_DB.personalNameplateCombatOnly = self.defaults.personalNameplateCombatOnly end
|
||||
if BNP_DB.personalNameplateClassColor == nil then BNP_DB.personalNameplateClassColor = self.defaults.personalNameplateClassColor end
|
||||
if BNP_DB.personalNameplateHealthText == nil then BNP_DB.personalNameplateHealthText = self.defaults.personalNameplateHealthText end
|
||||
if BNP_DB.personalNameplateDebuffs == nil then BNP_DB.personalNameplateDebuffs = self.defaults.personalNameplateDebuffs end
|
||||
if BNP_DB.personalNameplateDebuffYOffset == nil then BNP_DB.personalNameplateDebuffYOffset = self.defaults.personalNameplateDebuffYOffset end
|
||||
if BNP_DB.personalNameplateScale == nil then BNP_DB.personalNameplateScale = self.defaults.personalNameplateScale end
|
||||
if BNP_DB.personalNameplateYOffset == nil then BNP_DB.personalNameplateYOffset = self.defaults.personalNameplateYOffset end
|
||||
end
|
||||
|
||||
function BNP:GetNameplateScale()
|
||||
@@ -699,6 +715,54 @@ end
|
||||
|
||||
|
||||
|
||||
function BNP:IsPersonalNameplateEnabled()
|
||||
return BNP_DB and BNP_DB.personalNameplate == true
|
||||
end
|
||||
|
||||
function BNP:IsPersonalNameplateCombatOnly()
|
||||
return not BNP_DB or BNP_DB.personalNameplateCombatOnly ~= false
|
||||
end
|
||||
|
||||
|
||||
function BNP:IsPersonalNameplateClassColorEnabled()
|
||||
return BNP_DB and BNP_DB.personalNameplateClassColor == true
|
||||
end
|
||||
|
||||
function BNP:GetPersonalNameplateHealthTextMode()
|
||||
local mode = (BNP_DB and BNP_DB.personalNameplateHealthText) or self.defaults.personalNameplateHealthText or "both"
|
||||
if mode ~= "off" and mode ~= "percent" and mode ~= "hp" and mode ~= "both" then
|
||||
mode = "both"
|
||||
end
|
||||
return mode
|
||||
end
|
||||
|
||||
function BNP:IsPersonalNameplateDebuffsEnabled()
|
||||
return BNP_DB and BNP_DB.personalNameplateDebuffs == true
|
||||
end
|
||||
|
||||
function BNP:GetPersonalNameplateDebuffYOffset()
|
||||
local value = (BNP_DB and tonumber(BNP_DB.personalNameplateDebuffYOffset)) or self.defaults.personalNameplateDebuffYOffset or 0
|
||||
if value < -50 then value = -50 end
|
||||
if value > 50 then value = 50 end
|
||||
if value >= 0 then return math.floor(value + 0.5) end
|
||||
return math.ceil(value - 0.5)
|
||||
end
|
||||
|
||||
function BNP:GetPersonalNameplateScale()
|
||||
local value = (BNP_DB and tonumber(BNP_DB.personalNameplateScale)) or self.defaults.personalNameplateScale or 1.0
|
||||
if value < 0.70 then value = 0.70 end
|
||||
if value > 1.50 then value = 1.50 end
|
||||
return value
|
||||
end
|
||||
|
||||
function BNP:GetPersonalNameplateYOffset()
|
||||
local value = (BNP_DB and tonumber(BNP_DB.personalNameplateYOffset)) or self.defaults.personalNameplateYOffset or -90
|
||||
if value < -250 then value = -250 end
|
||||
if value > 150 then value = 150 end
|
||||
if value >= 0 then return math.floor(value + 0.5) end
|
||||
return math.ceil(value - 0.5)
|
||||
end
|
||||
|
||||
function BNP:AreQuestPlateIndicatorsEnabled()
|
||||
return not BNP_DB or BNP_DB.questPlateIndicators ~= false
|
||||
end
|
||||
|
||||
+229
-1
@@ -825,7 +825,7 @@ function BNP:CreateOptions()
|
||||
|
||||
local frame = CreateFrame("Frame", "BNPOptionsFrame", UIParent)
|
||||
frame:SetWidth(470)
|
||||
frame:SetHeight(710)
|
||||
frame:SetHeight(comboOptionsClass and 735 or 710)
|
||||
-- Keep the top edge in roughly the same place and add the extra room
|
||||
-- downward. The Icons page uses the height for Totems, Raid Marks and
|
||||
-- Quest Indicators without crowding or clipping.
|
||||
@@ -842,6 +842,12 @@ function BNP:CreateOptions()
|
||||
frame:RegisterForDrag("LeftButton")
|
||||
frame:SetScript("OnDragStart", function() this:StartMoving() end)
|
||||
frame:SetScript("OnDragStop", function() this:StopMovingOrSizing() end)
|
||||
frame:SetScript("OnShow", function()
|
||||
if BNP.RefreshPersonalNameplate then BNP:RefreshPersonalNameplate() end
|
||||
end)
|
||||
frame:SetScript("OnHide", function()
|
||||
if BNP.RefreshPersonalNameplate then BNP:RefreshPersonalNameplate() end
|
||||
end)
|
||||
frame:Hide()
|
||||
|
||||
local title = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
|
||||
@@ -1295,6 +1301,168 @@ function BNP:CreateOptions()
|
||||
frame.comboPointsYOffsetSlider = comboYOffset
|
||||
end
|
||||
|
||||
-- PERSONAL NAMEPLATE -----------------------------------------------------
|
||||
-- Clean two-column grid: visibility, appearance, text/debuff positioning,
|
||||
-- then plate scale/position. Rogue/Druid get a little extra window height
|
||||
-- so the same layout stays intact below the Combo Points section.
|
||||
local personalSectionY = comboOptionsClass and -494 or -442
|
||||
CreateSection(nameplatesPage, "Personal Nameplate", personalSectionY)
|
||||
|
||||
-- Row 1: visibility.
|
||||
local personalNameplate = CreateCheck(nameplatesPage, "Enable Personal Nameplate", personalSectionY - 22, function()
|
||||
BNP_DB.personalNameplate = this:GetChecked() and true or false
|
||||
if BNP.RefreshPersonalNameplate then BNP:RefreshPersonalNameplate() end
|
||||
if frame.UpdateDependentControls then frame:UpdateDependentControls() end
|
||||
end, 22)
|
||||
personalNameplate:SetScript("OnEnter", function()
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetText("Personal Nameplate", 1, 0.82, 0)
|
||||
GameTooltip:AddLine("Shows your own Health and Mana/Rage/Energy below the character using BNP's classic nameplate style.", 1, 1, 1, true)
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
personalNameplate:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
||||
frame.personalNameplateCheck = personalNameplate
|
||||
|
||||
local personalCombatOnly = CreateCheck(nameplatesPage, "Combat Only", personalSectionY - 22, function()
|
||||
BNP_DB.personalNameplateCombatOnly = this:GetChecked() and true or false
|
||||
if BNP.RefreshPersonalNameplate then BNP:RefreshPersonalNameplate() end
|
||||
end, 246)
|
||||
personalCombatOnly:SetScript("OnEnter", function()
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetText("Combat Only", 1, 0.82, 0)
|
||||
GameTooltip:AddLine("Only shows the Personal Nameplate while you are in combat. The options window keeps a preview visible for positioning.", 1, 1, 1, true)
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
personalCombatOnly:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
||||
frame.personalNameplateCombatOnlyCheck = personalCombatOnly
|
||||
|
||||
-- Row 2: appearance / aura toggles.
|
||||
local personalClassColor = CreateCheck(nameplatesPage, "Class Color", personalSectionY - 48, function()
|
||||
BNP_DB.personalNameplateClassColor = this:GetChecked() and true or false
|
||||
if BNP.RefreshPersonalNameplate then BNP:RefreshPersonalNameplate() end
|
||||
end, 22)
|
||||
personalClassColor:SetScript("OnEnter", function()
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetText("Class Color", 1, 0.82, 0)
|
||||
GameTooltip:AddLine("Colors your Personal Nameplate health bar by class. Off uses the normal green health bar.", 1, 1, 1, true)
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
personalClassColor:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
||||
frame.personalNameplateClassColorCheck = personalClassColor
|
||||
|
||||
local personalDebuffs = CreateCheck(nameplatesPage, "Show Debuffs", personalSectionY - 48, function()
|
||||
BNP_DB.personalNameplateDebuffs = this:GetChecked() and true or false
|
||||
if BNP.RefreshPersonalNameplate then BNP:RefreshPersonalNameplate() end
|
||||
end, 246)
|
||||
personalDebuffs:SetScript("OnEnter", function()
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetText("Show Debuffs", 1, 0.82, 0)
|
||||
GameTooltip:AddLine("Shows short combat debuffs on you above the Personal Nameplate. Long debuffs over 60 seconds are ignored.", 1, 1, 1, true)
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
personalDebuffs:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
||||
frame.personalNameplateDebuffsCheck = personalDebuffs
|
||||
|
||||
-- Row 3: text mode on the left, debuff offset on the right.
|
||||
local personalHealthTextLabel = nameplatesPage:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
||||
personalHealthTextLabel:SetPoint("TOPLEFT", nameplatesPage, "TOPLEFT", 28, personalSectionY - 72)
|
||||
personalHealthTextLabel:SetText("Health Text")
|
||||
frame.personalNameplateHealthTextLabel = personalHealthTextLabel
|
||||
|
||||
local personalHealthTextDropdown = CreateFrame("Frame", "BNPPersonalHealthTextDropdown", nameplatesPage, "UIDropDownMenuTemplate")
|
||||
personalHealthTextDropdown:SetPoint("TOPLEFT", nameplatesPage, "TOPLEFT", 10, personalSectionY - 82)
|
||||
UIDropDownMenu_SetWidth(142, personalHealthTextDropdown)
|
||||
|
||||
local personalDebuffYOffset = CreateSlider(nameplatesPage, "Debuff Y Offset", -50, 50, 1, personalSectionY - 82, 246, 150)
|
||||
personalDebuffYOffset:SetScript("OnValueChanged", function()
|
||||
if not BNP_DB then return end
|
||||
local value = RoundSignedInteger(this:GetValue())
|
||||
BNP_DB.personalNameplateDebuffYOffset = value
|
||||
getglobal(this:GetName() .. "Text"):SetText("Debuff Y Offset: " .. (value > 0 and "+" or "") .. value)
|
||||
if BNP.RefreshPersonalNameplate then BNP:RefreshPersonalNameplate() end
|
||||
end)
|
||||
personalDebuffYOffset:SetScript("OnEnter", function()
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetText("Personal Debuff Y Offset", 1, 0.82, 0)
|
||||
GameTooltip:AddLine("Moves the Personal Nameplate debuff row up or down without moving the plate itself.", 1, 1, 1, true)
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
personalDebuffYOffset:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
||||
frame.personalNameplateDebuffYOffsetSlider = personalDebuffYOffset
|
||||
|
||||
local personalHealthModeLabels = {
|
||||
off = "Off",
|
||||
percent = "Percent",
|
||||
hp = "HP",
|
||||
both = "HP + Percent",
|
||||
}
|
||||
|
||||
local function SetPersonalHealthTextMode(mode)
|
||||
if not personalHealthModeLabels[mode] then mode = "both" end
|
||||
BNP_DB.personalNameplateHealthText = mode
|
||||
UIDropDownMenu_SetSelectedValue(personalHealthTextDropdown, mode)
|
||||
UIDropDownMenu_SetText(personalHealthModeLabels[mode], personalHealthTextDropdown)
|
||||
if BNP.RefreshPersonalNameplate then BNP:RefreshPersonalNameplate() end
|
||||
end
|
||||
|
||||
UIDropDownMenu_Initialize(personalHealthTextDropdown, function()
|
||||
local modes = { "off", "percent", "hp", "both" }
|
||||
local n
|
||||
for n = 1, table.getn(modes) do
|
||||
local mode = modes[n]
|
||||
local info = {}
|
||||
info.text = personalHealthModeLabels[mode]
|
||||
info.value = mode
|
||||
info.func = function() SetPersonalHealthTextMode(this.value) end
|
||||
info.checked = (BNP:GetPersonalNameplateHealthTextMode() == mode)
|
||||
UIDropDownMenu_AddButton(info)
|
||||
end
|
||||
end)
|
||||
personalHealthTextDropdown:SetScript("OnEnter", function()
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetText("Personal Health Text", 1, 0.82, 0)
|
||||
GameTooltip:AddLine("Choose Off, Percent, HP, or HP + Percent for the text centered on your Personal Nameplate.", 1, 1, 1, true)
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
personalHealthTextDropdown:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
||||
frame.personalNameplateHealthTextDropdown = personalHealthTextDropdown
|
||||
frame.SetPersonalHealthTextMode = SetPersonalHealthTextMode
|
||||
|
||||
-- Row 4: plate size and position, aligned to the same two columns.
|
||||
local personalScale = CreateSlider(nameplatesPage, "Scale", 0.70, 1.50, 0.05, personalSectionY - 116, 28, 150)
|
||||
personalScale:SetScript("OnValueChanged", function()
|
||||
if not BNP_DB then return end
|
||||
local value = Round(this:GetValue(), 0.05)
|
||||
BNP_DB.personalNameplateScale = value
|
||||
getglobal(this:GetName() .. "Text"):SetText("Scale: " .. string.format("%.2f", value))
|
||||
if BNP.RefreshPersonalNameplate then BNP:RefreshPersonalNameplate() end
|
||||
end)
|
||||
personalScale:SetScript("OnEnter", function()
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetText("Personal Scale", 1, 0.82, 0)
|
||||
GameTooltip:AddLine("Changes the size of the Personal Nameplate only.", 1, 1, 1, true)
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
personalScale:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
||||
frame.personalNameplateScaleSlider = personalScale
|
||||
|
||||
local personalYOffset = CreateSlider(nameplatesPage, "Y Offset", -250, 150, 5, personalSectionY - 116, 246, 150)
|
||||
personalYOffset:SetScript("OnValueChanged", function()
|
||||
if not BNP_DB then return end
|
||||
local value = RoundSignedInteger(this:GetValue() / 5) * 5
|
||||
BNP_DB.personalNameplateYOffset = value
|
||||
getglobal(this:GetName() .. "Text"):SetText("Y Offset: " .. (value > 0 and "+" or "") .. value)
|
||||
if BNP.RefreshPersonalNameplate then BNP:RefreshPersonalNameplate() end
|
||||
end)
|
||||
personalYOffset:SetScript("OnEnter", function()
|
||||
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
||||
GameTooltip:SetText("Personal Y Offset", 1, 0.82, 0)
|
||||
GameTooltip:AddLine("Moves the Personal Nameplate up or down below your character.", 1, 1, 1, true)
|
||||
GameTooltip:Show()
|
||||
end)
|
||||
personalYOffset:SetScript("OnLeave", function() GameTooltip:Hide() end)
|
||||
frame.personalNameplateYOffsetSlider = personalYOffset
|
||||
|
||||
-- AURAS TAB --------------------------------------------------------------
|
||||
local aurasPage = frame.pages.auras
|
||||
CreateSection(aurasPage, "Debuffs", -4)
|
||||
@@ -2103,6 +2271,31 @@ function BNP:CreateOptions()
|
||||
SetCheckEnabled(self.darkComboPointBorderCheck, comboEnabled)
|
||||
SetCheckEnabled(self.darkNameplateBorderCheck, not (BNP.IsNameplateBorderHidden and BNP:IsNameplateBorderHidden()))
|
||||
|
||||
local personalEnabled = BNP.IsPersonalNameplateEnabled and BNP:IsPersonalNameplateEnabled() or false
|
||||
SetCheckEnabled(self.personalNameplateCombatOnlyCheck, personalEnabled)
|
||||
SetCheckEnabled(self.personalNameplateClassColorCheck, personalEnabled)
|
||||
SetCheckEnabled(self.personalNameplateDebuffsCheck, personalEnabled)
|
||||
if self.personalNameplateHealthTextDropdown then
|
||||
self.personalNameplateHealthTextDropdown:SetAlpha(personalEnabled and 1.0 or 0.45)
|
||||
if personalEnabled then
|
||||
if UIDropDownMenu_EnableDropDown then UIDropDownMenu_EnableDropDown(self.personalNameplateHealthTextDropdown) end
|
||||
else
|
||||
if UIDropDownMenu_DisableDropDown then UIDropDownMenu_DisableDropDown(self.personalNameplateHealthTextDropdown) end
|
||||
end
|
||||
end
|
||||
if self.personalNameplateHealthTextLabel then
|
||||
self.personalNameplateHealthTextLabel:SetTextColor(personalEnabled and 1 or 0.5, personalEnabled and 0.82 or 0.5, personalEnabled and 0 or 0.5)
|
||||
end
|
||||
local personalSliders = { self.personalNameplateScaleSlider, self.personalNameplateYOffsetSlider, self.personalNameplateDebuffYOffsetSlider }
|
||||
local personalIndex
|
||||
for personalIndex = 1, table.getn(personalSliders) do
|
||||
local personalSlider = personalSliders[personalIndex]
|
||||
if personalSlider then
|
||||
personalSlider:SetAlpha(personalEnabled and 1.0 or 0.45)
|
||||
if personalSlider.EnableMouse then personalSlider:EnableMouse(personalEnabled) end
|
||||
end
|
||||
end
|
||||
|
||||
local invertTankEnabled = BNP:IsTankModeEnabled() and BNP:AreTankModeColorsInverted()
|
||||
local function SetTankSwatchEnabled(swatch, enabled)
|
||||
if not swatch then return end
|
||||
@@ -2258,6 +2451,41 @@ function BNP:SyncOptions()
|
||||
frame.comboPointsYOffsetSlider:SetValue(value)
|
||||
getglobal(frame.comboPointsYOffsetSlider:GetName() .. "Text"):SetText("Combo Point Y Offset: " .. (value > 0 and "+" or "") .. value)
|
||||
end
|
||||
if frame.personalNameplateCheck then
|
||||
frame.personalNameplateCheck:SetChecked(self.IsPersonalNameplateEnabled and self:IsPersonalNameplateEnabled() or false)
|
||||
end
|
||||
if frame.personalNameplateCombatOnlyCheck then
|
||||
local combatOnly = true
|
||||
if self.IsPersonalNameplateCombatOnly then combatOnly = self:IsPersonalNameplateCombatOnly() end
|
||||
frame.personalNameplateCombatOnlyCheck:SetChecked(combatOnly)
|
||||
end
|
||||
if frame.personalNameplateClassColorCheck then
|
||||
frame.personalNameplateClassColorCheck:SetChecked(self.IsPersonalNameplateClassColorEnabled and self:IsPersonalNameplateClassColorEnabled() or false)
|
||||
end
|
||||
if frame.personalNameplateHealthTextDropdown then
|
||||
local mode = self.GetPersonalNameplateHealthTextMode and self:GetPersonalNameplateHealthTextMode() or "both"
|
||||
local labels = { off = "Off", percent = "Percent", hp = "HP", both = "HP + Percent" }
|
||||
UIDropDownMenu_SetSelectedValue(frame.personalNameplateHealthTextDropdown, mode)
|
||||
UIDropDownMenu_SetText(labels[mode] or "HP + Percent", frame.personalNameplateHealthTextDropdown)
|
||||
end
|
||||
if frame.personalNameplateDebuffsCheck then
|
||||
frame.personalNameplateDebuffsCheck:SetChecked(self.IsPersonalNameplateDebuffsEnabled and self:IsPersonalNameplateDebuffsEnabled() or false)
|
||||
end
|
||||
if frame.personalNameplateScaleSlider then
|
||||
local value = self.GetPersonalNameplateScale and self:GetPersonalNameplateScale() or 1.0
|
||||
frame.personalNameplateScaleSlider:SetValue(value)
|
||||
getglobal(frame.personalNameplateScaleSlider:GetName() .. "Text"):SetText("Scale: " .. string.format("%.2f", value))
|
||||
end
|
||||
if frame.personalNameplateYOffsetSlider then
|
||||
local value = self.GetPersonalNameplateYOffset and self:GetPersonalNameplateYOffset() or -90
|
||||
frame.personalNameplateYOffsetSlider:SetValue(value)
|
||||
getglobal(frame.personalNameplateYOffsetSlider:GetName() .. "Text"):SetText("Y Offset: " .. (value > 0 and "+" or "") .. value)
|
||||
end
|
||||
if frame.personalNameplateDebuffYOffsetSlider then
|
||||
local value = self.GetPersonalNameplateDebuffYOffset and self:GetPersonalNameplateDebuffYOffset() or 0
|
||||
frame.personalNameplateDebuffYOffsetSlider:SetValue(value)
|
||||
getglobal(frame.personalNameplateDebuffYOffsetSlider:GetName() .. "Text"):SetText("Debuff Y Offset: " .. (value > 0 and "+" or "") .. value)
|
||||
end
|
||||
if frame.debuffYOffsetSlider then
|
||||
local value = self:GetDebuffYOffset()
|
||||
frame.debuffYOffsetSlider:SetValue(value)
|
||||
|
||||
@@ -168,6 +168,7 @@ function BNP:RefreshClassColors()
|
||||
end
|
||||
end
|
||||
end
|
||||
if self.RefreshPersonalNameplateStyle then self:RefreshPersonalNameplateStyle() end
|
||||
end
|
||||
|
||||
local function EnforceClassColor(plate)
|
||||
|
||||
@@ -0,0 +1,613 @@
|
||||
BNP = BNP or {}
|
||||
|
||||
-- Personal Nameplate -------------------------------------------------------
|
||||
-- A lightweight player resource display that visually follows BNP's classic
|
||||
-- nameplate styling. It is screen-space UI (not a projected world nameplate),
|
||||
-- so it stays stable beneath the player and does not interfere with Blizzard's
|
||||
-- nameplate discovery/click handling.
|
||||
|
||||
local FRAME_WIDTH = 128
|
||||
local FRAME_HEIGHT = 45
|
||||
local HEALTH_WIDTH = 104
|
||||
local HEALTH_HEIGHT = 9
|
||||
local POWER_WIDTH = 104
|
||||
local POWER_HEIGHT = 5
|
||||
local PERSONAL_DEBUFF_MAX_DURATION = 60
|
||||
local PERSONAL_DEBUFF_MAX_ICONS = 6
|
||||
local PERSONAL_DEBUFF_SPACING = 1
|
||||
-- Match BNP health text anchoring: healthbar CENTER +3px. The personal frame
|
||||
-- is wider than the healthbar because of the level medallion, so centering
|
||||
-- debuffs on the whole frame makes the row look shifted to the right.
|
||||
local PERSONAL_HEALTH_TEXT_X = 3
|
||||
local PERSONAL_HEALTH_TEXT_Y = 1
|
||||
local PERSONAL_POWER_TEXT_Y = 1
|
||||
local PERSONAL_CONTENT_CENTER_X = (5 + HEALTH_WIDTH / 2 + PERSONAL_HEALTH_TEXT_X) - (FRAME_WIDTH / 2)
|
||||
local STATUS_TEXTURE = "Interface\\TargetingFrame\\UI-StatusBar"
|
||||
local BORDER_TEXTURE = "Interface\\AddOns\\BlizzNameplatesPlus\\media\\classic_nameplate_border"
|
||||
local FONT = "Fonts\\FRIZQT__.TTF"
|
||||
|
||||
local classFallback = {
|
||||
WARRIOR = { 0.78, 0.61, 0.43 },
|
||||
MAGE = { 0.41, 0.80, 0.94 },
|
||||
ROGUE = { 1.00, 0.96, 0.41 },
|
||||
DRUID = { 1.00, 0.49, 0.04 },
|
||||
HUNTER = { 0.67, 0.83, 0.45 },
|
||||
SHAMAN = { 0.00, 0.44, 0.87 },
|
||||
PRIEST = { 1.00, 1.00, 1.00 },
|
||||
WARLOCK = { 0.58, 0.51, 0.79 },
|
||||
PALADIN = { 0.96, 0.55, 0.73 },
|
||||
}
|
||||
|
||||
local powerColors = {
|
||||
[0] = { 0.00, 0.45, 1.00 }, -- Mana
|
||||
[1] = { 1.00, 0.12, 0.12 }, -- Rage
|
||||
[2] = { 1.00, 0.50, 0.25 }, -- Focus (custom clients/classes)
|
||||
[3] = { 1.00, 0.85, 0.10 }, -- Energy
|
||||
[4] = { 0.00, 0.70, 1.00 }, -- Happiness / fallback legacy resource
|
||||
}
|
||||
|
||||
local personalFrame
|
||||
local inCombat = false
|
||||
local updateElapsed = 0
|
||||
local debuffScanElapsed = 0
|
||||
|
||||
local function FormatValue(value)
|
||||
value = tonumber(value) or 0
|
||||
if value < 0 then value = 0 end
|
||||
if value >= 1000000 then
|
||||
local shown = math.floor((value / 1000000) * 10 + 0.5) / 10
|
||||
return tostring(shown) .. "m"
|
||||
elseif value >= 10000 then
|
||||
return tostring(math.floor(value / 1000 + 0.5)) .. "k"
|
||||
end
|
||||
return tostring(math.floor(value + 0.5))
|
||||
end
|
||||
|
||||
local function CreateSolidTexture(parent, layer)
|
||||
local texture = parent:CreateTexture(nil, layer or "BACKGROUND")
|
||||
texture:SetTexture("Interface\\ChatFrame\\ChatFrameBackground")
|
||||
return texture
|
||||
end
|
||||
|
||||
local function SetThinBorderColor(frame, r, g, b, a)
|
||||
if not frame then return end
|
||||
local edges = frame.BNPEdges
|
||||
if not edges then return end
|
||||
local i
|
||||
for i = 1, table.getn(edges) do
|
||||
edges[i]:SetVertexColor(r, g, b, a or 1)
|
||||
end
|
||||
end
|
||||
|
||||
local function CreateThinBorder(parent)
|
||||
local edges = {}
|
||||
local top = CreateSolidTexture(parent, "OVERLAY")
|
||||
top:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, 0)
|
||||
top:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, 0)
|
||||
top:SetHeight(1)
|
||||
table.insert(edges, top)
|
||||
|
||||
local bottom = CreateSolidTexture(parent, "OVERLAY")
|
||||
bottom:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT", 0, 0)
|
||||
bottom:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", 0, 0)
|
||||
bottom:SetHeight(1)
|
||||
table.insert(edges, bottom)
|
||||
|
||||
local left = CreateSolidTexture(parent, "OVERLAY")
|
||||
left:SetPoint("TOPLEFT", parent, "TOPLEFT", 0, 0)
|
||||
left:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT", 0, 0)
|
||||
left:SetWidth(1)
|
||||
table.insert(edges, left)
|
||||
|
||||
local right = CreateSolidTexture(parent, "OVERLAY")
|
||||
right:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, 0)
|
||||
right:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", 0, 0)
|
||||
right:SetWidth(1)
|
||||
table.insert(edges, right)
|
||||
|
||||
parent.BNPEdges = edges
|
||||
end
|
||||
|
||||
local function CreatePersonalFrame()
|
||||
if personalFrame then return personalFrame end
|
||||
|
||||
local frame = CreateFrame("Frame", "BNPPersonalNameplate", UIParent)
|
||||
frame:SetWidth(FRAME_WIDTH)
|
||||
frame:SetHeight(FRAME_HEIGHT)
|
||||
frame:SetFrameStrata("MEDIUM")
|
||||
frame:SetFrameLevel(8)
|
||||
frame:EnableMouse(false)
|
||||
frame:Hide()
|
||||
|
||||
-- Main health bar, sized to sit inside BNP's classic nameplate border art.
|
||||
local health = CreateFrame("StatusBar", nil, frame)
|
||||
health:SetWidth(HEALTH_WIDTH)
|
||||
health:SetHeight(HEALTH_HEIGHT)
|
||||
health:SetPoint("TOPLEFT", frame, "TOPLEFT", 5, -19)
|
||||
health:SetStatusBarTexture(STATUS_TEXTURE)
|
||||
health:SetMinMaxValues(0, 1)
|
||||
health:SetValue(1)
|
||||
frame.healthbar = health
|
||||
|
||||
local healthBG = CreateSolidTexture(health, "BACKGROUND")
|
||||
healthBG:SetAllPoints(health)
|
||||
healthBG:SetVertexColor(0, 0, 0, 1)
|
||||
frame.healthBG = healthBG
|
||||
|
||||
-- Put border art on a tiny higher-level overlay frame. Child StatusBars can
|
||||
-- otherwise render above parent textures on old clients and eat the rim.
|
||||
local overlay = CreateFrame("Frame", nil, frame)
|
||||
overlay:SetAllPoints(frame)
|
||||
overlay:SetFrameLevel(frame:GetFrameLevel() + 4)
|
||||
overlay:EnableMouse(false)
|
||||
frame.overlay = overlay
|
||||
|
||||
local border = overlay:CreateTexture(nil, "ARTWORK")
|
||||
border:SetTexture(BORDER_TEXTURE)
|
||||
border:SetWidth(128)
|
||||
border:SetHeight(32)
|
||||
border:SetPoint("TOPLEFT", frame, "TOPLEFT", 0, 0)
|
||||
frame.border = border
|
||||
|
||||
local healthText = health:CreateFontString(nil, "OVERLAY")
|
||||
healthText:SetFont(FONT, 8, "OUTLINE")
|
||||
healthText:SetPoint("CENTER", health, "CENTER", PERSONAL_HEALTH_TEXT_X, PERSONAL_HEALTH_TEXT_Y)
|
||||
healthText:SetTextColor(1, 1, 1)
|
||||
frame.healthText = healthText
|
||||
|
||||
-- BNP's classic border contains the small round level medallion at the right.
|
||||
local levelText = overlay:CreateFontString(nil, "OVERLAY")
|
||||
levelText:SetFont(FONT, 8, "OUTLINE")
|
||||
levelText:SetPoint("CENTER", frame, "TOPLEFT", 115, -24)
|
||||
levelText:SetTextColor(1, 0.82, 0)
|
||||
frame.levelText = levelText
|
||||
|
||||
-- Slim power bar below the health nameplate. The 1px rim uses the same
|
||||
-- gold/dark language as BNP's border while keeping the resource bar compact.
|
||||
local powerHolder = CreateFrame("Frame", nil, frame)
|
||||
powerHolder:SetWidth(108)
|
||||
powerHolder:SetHeight(9)
|
||||
powerHolder:SetPoint("TOPLEFT", frame, "TOPLEFT", 3, -34)
|
||||
CreateThinBorder(powerHolder)
|
||||
frame.powerHolder = powerHolder
|
||||
|
||||
local powerBG = CreateSolidTexture(powerHolder, "BACKGROUND")
|
||||
powerBG:SetPoint("TOPLEFT", powerHolder, "TOPLEFT", 1, -1)
|
||||
powerBG:SetPoint("BOTTOMRIGHT", powerHolder, "BOTTOMRIGHT", -1, 1)
|
||||
powerBG:SetVertexColor(0, 0, 0, 0.78)
|
||||
frame.powerBG = powerBG
|
||||
|
||||
local power = CreateFrame("StatusBar", nil, powerHolder)
|
||||
power:SetWidth(POWER_WIDTH)
|
||||
power:SetHeight(POWER_HEIGHT)
|
||||
power:SetPoint("CENTER", powerHolder, "CENTER", 0, 0)
|
||||
power:SetStatusBarTexture(STATUS_TEXTURE)
|
||||
power:SetMinMaxValues(0, 1)
|
||||
power:SetValue(1)
|
||||
frame.powerbar = power
|
||||
|
||||
local powerText = power:CreateFontString(nil, "OVERLAY")
|
||||
powerText:SetFont(FONT, 7, "OUTLINE")
|
||||
powerText:SetPoint("CENTER", power, "CENTER", 0, PERSONAL_POWER_TEXT_Y)
|
||||
powerText:SetTextColor(1, 1, 1)
|
||||
frame.powerText = powerText
|
||||
|
||||
-- Short player debuffs sit centered above the personal plate. They use the
|
||||
-- same compact icon + countdown language as BNP's normal aura row.
|
||||
local debuffHolder = CreateFrame("Frame", nil, frame)
|
||||
debuffHolder:SetWidth(FRAME_WIDTH)
|
||||
debuffHolder:SetHeight(22)
|
||||
debuffHolder:SetPoint("BOTTOM", frame, "TOP", PERSONAL_CONTENT_CENTER_X, 1)
|
||||
debuffHolder:SetFrameLevel(frame:GetFrameLevel() + 6)
|
||||
debuffHolder:EnableMouse(false)
|
||||
debuffHolder.icons = {}
|
||||
frame.debuffHolder = debuffHolder
|
||||
|
||||
local i
|
||||
for i = 1, PERSONAL_DEBUFF_MAX_ICONS do
|
||||
local icon = CreateFrame("Frame", nil, debuffHolder)
|
||||
icon:SetWidth(18)
|
||||
icon:SetHeight(18)
|
||||
icon:SetFrameLevel(debuffHolder:GetFrameLevel())
|
||||
icon:EnableMouse(false)
|
||||
|
||||
local iconBG = CreateSolidTexture(icon, "BACKGROUND")
|
||||
iconBG:SetPoint("TOPLEFT", icon, "TOPLEFT", -1, 1)
|
||||
iconBG:SetPoint("BOTTOMRIGHT", icon, "BOTTOMRIGHT", 1, -1)
|
||||
iconBG:SetVertexColor(0, 0, 0, 0.95)
|
||||
|
||||
local texture = icon:CreateTexture(nil, "ARTWORK")
|
||||
texture:SetAllPoints(icon)
|
||||
icon.texture = texture
|
||||
|
||||
local timer = icon:CreateFontString(nil, "OVERLAY")
|
||||
timer:SetFont(FONT, 8, "OUTLINE")
|
||||
timer:SetPoint("CENTER", icon, "CENTER", 0, 0)
|
||||
timer:SetTextColor(1, 1, 1)
|
||||
icon.timer = timer
|
||||
|
||||
local stack = icon:CreateFontString(nil, "OVERLAY")
|
||||
stack:SetFont(FONT, 7, "OUTLINE")
|
||||
stack:SetPoint("BOTTOMRIGHT", icon, "BOTTOMRIGHT", -1, 1)
|
||||
stack:SetTextColor(1, 1, 1)
|
||||
stack:SetText("")
|
||||
icon.stack = stack
|
||||
|
||||
icon:Hide()
|
||||
debuffHolder.icons[i] = icon
|
||||
end
|
||||
debuffHolder:Hide()
|
||||
|
||||
personalFrame = frame
|
||||
BNP.personalNameplateFrame = frame
|
||||
return frame
|
||||
end
|
||||
|
||||
local function GetPlayerHealthColor()
|
||||
if BNP.IsPersonalNameplateClassColorEnabled and BNP:IsPersonalNameplateClassColorEnabled() then
|
||||
local _, class = UnitClass("player")
|
||||
if RAID_CLASS_COLORS and class and RAID_CLASS_COLORS[class] then
|
||||
local c = RAID_CLASS_COLORS[class]
|
||||
return c.r, c.g, c.b
|
||||
end
|
||||
local c = class and classFallback[class]
|
||||
if c then return c[1], c[2], c[3] end
|
||||
end
|
||||
return 0.10, 0.95, 0.10
|
||||
end
|
||||
|
||||
local function UpdateStyle(frame)
|
||||
if not frame then return end
|
||||
|
||||
local dark = BNP.IsDarkNameplateBorderEnabled and BNP:IsDarkNameplateBorderEnabled()
|
||||
local hidden = BNP.IsNameplateBorderHidden and BNP:IsNameplateBorderHidden()
|
||||
local blackBG = BNP.IsBlackHealthbarBackgroundEnabled and BNP:IsBlackHealthbarBackgroundEnabled()
|
||||
|
||||
if hidden then
|
||||
frame.border:Hide()
|
||||
else
|
||||
frame.border:Show()
|
||||
if dark then
|
||||
frame.border:SetVertexColor(0.38, 0.38, 0.38, 1)
|
||||
else
|
||||
frame.border:SetVertexColor(1, 1, 1, 1)
|
||||
end
|
||||
end
|
||||
|
||||
local edge = dark and 0.32 or 0.78
|
||||
local edgeG = dark and 0.32 or 0.60
|
||||
local edgeB = dark and 0.32 or 0.08
|
||||
SetThinBorderColor(frame.powerHolder, edge, edgeG, edgeB, hidden and 0 or 1)
|
||||
|
||||
if blackBG then
|
||||
frame.healthBG:SetVertexColor(0, 0, 0, 1)
|
||||
else
|
||||
frame.healthBG:SetVertexColor(0, 0, 0, 1)
|
||||
end
|
||||
|
||||
if hidden then frame.levelText:Hide() else frame.levelText:Show() end
|
||||
|
||||
local hr, hg, hb = GetPlayerHealthColor()
|
||||
frame.healthbar:SetStatusBarColor(hr, hg, hb)
|
||||
end
|
||||
|
||||
local function UpdatePosition(frame)
|
||||
if not frame then return end
|
||||
local scale = BNP.GetPersonalNameplateScale and BNP:GetPersonalNameplateScale() or 1.0
|
||||
local y = BNP.GetPersonalNameplateYOffset and BNP:GetPersonalNameplateYOffset() or -90
|
||||
|
||||
frame:SetScale(scale)
|
||||
frame:ClearAllPoints()
|
||||
frame:SetPoint("CENTER", UIParent, "CENTER", 0, y)
|
||||
|
||||
if frame.debuffHolder then
|
||||
local debuffY = BNP.GetPersonalNameplateDebuffYOffset and BNP:GetPersonalNameplateDebuffYOffset() or 0
|
||||
frame.debuffHolder:ClearAllPoints()
|
||||
frame.debuffHolder:SetPoint("BOTTOM", frame, "TOP", PERSONAL_CONTENT_CENTER_X, 1 + debuffY)
|
||||
end
|
||||
end
|
||||
|
||||
local function GetPersonalDebuffIconSize()
|
||||
local size = BNP.GetIconSize and tonumber(BNP:GetIconSize()) or 18
|
||||
if not size then size = 18 end
|
||||
if size < 12 then size = 12 end
|
||||
if size > 20 then size = 20 end
|
||||
return size
|
||||
end
|
||||
|
||||
local function FormatDebuffTimer(remaining)
|
||||
remaining = tonumber(remaining)
|
||||
if not remaining or remaining <= 0 then return "" end
|
||||
if remaining >= 10 then return tostring(math.ceil(remaining)) end
|
||||
return string.format("%.1f", remaining)
|
||||
end
|
||||
|
||||
local function ReadPlayerDebuff(index)
|
||||
if type(C_UnitAuras) == "table" and type(C_UnitAuras.GetDebuffDataByIndex) == "function" then
|
||||
local ok, aura = pcall(C_UnitAuras.GetDebuffDataByIndex, "player", index)
|
||||
if not ok or not aura then return nil end
|
||||
return aura.icon, aura.applications or aura.count or 0, aura.duration, aura.expirationTime, aura.name, aura.spellId or aura.spellID
|
||||
end
|
||||
|
||||
if type(C_UnitAuras) == "table" and type(C_UnitAuras.UnitDebuff) == "function" then
|
||||
local name, icon, count, dispelType, duration, expirationTime, source, isStealable, nameplateShowPersonal, spellID = C_UnitAuras.UnitDebuff("player", index)
|
||||
if not name then return nil end
|
||||
return icon, count or 0, duration, expirationTime, name, spellID
|
||||
end
|
||||
|
||||
-- Legacy UnitDebuff on pure 1.12 usually has no reliable duration. Read it
|
||||
-- only when duration/expiration are supplied by the custom client; otherwise
|
||||
-- skip it rather than accidentally showing multi-minute effects.
|
||||
if UnitDebuff then
|
||||
local texture, count, dispelType, spellID, duration, expirationTime = UnitDebuff("player", index)
|
||||
if not texture then return nil end
|
||||
return texture, count or 0, duration, expirationTime, nil, spellID
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
local function HidePersonalDebuffs(frame)
|
||||
if not frame or not frame.debuffHolder then return end
|
||||
local i
|
||||
for i = 1, table.getn(frame.debuffHolder.icons or {}) do
|
||||
local icon = frame.debuffHolder.icons[i]
|
||||
icon.timer:SetText("")
|
||||
icon.stack:SetText("")
|
||||
icon:Hide()
|
||||
end
|
||||
frame.debuffHolder:Hide()
|
||||
end
|
||||
|
||||
local function UpdatePersonalDebuffs(frame, forceScan)
|
||||
if not frame or not frame.debuffHolder then return end
|
||||
if not (BNP.IsPersonalNameplateDebuffsEnabled and BNP:IsPersonalNameplateDebuffsEnabled()) then
|
||||
frame.BNPPersonalDebuffEntries = nil
|
||||
HidePersonalDebuffs(frame)
|
||||
return
|
||||
end
|
||||
|
||||
local now = GetTime()
|
||||
local entries = frame.BNPPersonalDebuffEntries
|
||||
if forceScan or not entries then
|
||||
entries = {}
|
||||
local index
|
||||
for index = 1, 64 do
|
||||
local texture, count, duration, expirationTime, name, spellID = ReadPlayerDebuff(index)
|
||||
if not texture then break end
|
||||
|
||||
duration = tonumber(duration) or 0
|
||||
expirationTime = tonumber(expirationTime) or 0
|
||||
if duration > 0 and duration <= PERSONAL_DEBUFF_MAX_DURATION and expirationTime > now then
|
||||
table.insert(entries, {
|
||||
texture = texture,
|
||||
count = tonumber(count) or 0,
|
||||
duration = duration,
|
||||
expirationTime = expirationTime,
|
||||
name = name,
|
||||
spellID = spellID,
|
||||
})
|
||||
if table.getn(entries) >= PERSONAL_DEBUFF_MAX_ICONS then break end
|
||||
end
|
||||
end
|
||||
frame.BNPPersonalDebuffEntries = entries
|
||||
end
|
||||
|
||||
local visible = 0
|
||||
local i
|
||||
for i = 1, table.getn(entries) do
|
||||
if entries[i].expirationTime and entries[i].expirationTime > now then
|
||||
visible = visible + 1
|
||||
end
|
||||
end
|
||||
|
||||
if visible < 1 then
|
||||
HidePersonalDebuffs(frame)
|
||||
return
|
||||
end
|
||||
|
||||
local size = GetPersonalDebuffIconSize()
|
||||
local totalWidth = visible * size + (visible - 1) * PERSONAL_DEBUFF_SPACING
|
||||
local startX = -totalWidth / 2
|
||||
local displayIndex = 0
|
||||
for i = 1, table.getn(entries) do
|
||||
local entry = entries[i]
|
||||
if entry.expirationTime and entry.expirationTime > now then
|
||||
displayIndex = displayIndex + 1
|
||||
local icon = frame.debuffHolder.icons[displayIndex]
|
||||
if icon then
|
||||
if icon.BNPLastSize ~= size then
|
||||
icon:SetWidth(size)
|
||||
icon:SetHeight(size)
|
||||
icon.timer:SetFont(FONT, math.max(7, math.floor(size * 0.44 + 0.5)), "OUTLINE")
|
||||
icon.stack:SetFont(FONT, math.max(6, math.floor(size * 0.38 + 0.5)), "OUTLINE")
|
||||
icon.BNPLastSize = size
|
||||
end
|
||||
icon:ClearAllPoints()
|
||||
icon:SetPoint("LEFT", frame.debuffHolder, "CENTER", startX + (displayIndex - 1) * (size + PERSONAL_DEBUFF_SPACING), 0)
|
||||
if icon.BNPLastTexture ~= entry.texture then
|
||||
icon.texture:SetTexture(entry.texture)
|
||||
icon.BNPLastTexture = entry.texture
|
||||
end
|
||||
icon.timer:SetText(FormatDebuffTimer(entry.expirationTime - now))
|
||||
icon.stack:SetText(entry.count > 1 and tostring(entry.count) or "")
|
||||
icon:Show()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
for i = displayIndex + 1, table.getn(frame.debuffHolder.icons) do
|
||||
local icon = frame.debuffHolder.icons[i]
|
||||
icon.timer:SetText("")
|
||||
icon.stack:SetText("")
|
||||
icon:Hide()
|
||||
end
|
||||
frame.debuffHolder:Show()
|
||||
end
|
||||
|
||||
local function UpdateValues(frame)
|
||||
if not frame then return end
|
||||
|
||||
local health = tonumber(UnitHealth("player")) or 0
|
||||
local maxHealth = tonumber(UnitHealthMax("player")) or 0
|
||||
if maxHealth < 1 then maxHealth = 1 end
|
||||
if health < 0 then health = 0 end
|
||||
if health > maxHealth then health = maxHealth end
|
||||
frame.healthbar:SetMinMaxValues(0, maxHealth)
|
||||
frame.healthbar:SetValue(health)
|
||||
|
||||
-- Personal Health Text mirrors BNP's regular nameplate display modes, but
|
||||
-- remains independently configurable so the player HUD does not have to
|
||||
-- match enemy nameplates.
|
||||
local healthMode = BNP.GetPersonalNameplateHealthTextMode and BNP:GetPersonalNameplateHealthTextMode() or "both"
|
||||
local healthDisplay = nil
|
||||
if healthMode == "percent" then
|
||||
local pct = math.floor((health / maxHealth) * 100 + 0.5)
|
||||
if pct < 0 then pct = 0 end
|
||||
if pct > 100 then pct = 100 end
|
||||
healthDisplay = tostring(pct) .. "%"
|
||||
elseif healthMode == "hp" then
|
||||
healthDisplay = FormatValue(health)
|
||||
elseif healthMode == "both" then
|
||||
local pct = math.floor((health / maxHealth) * 100 + 0.5)
|
||||
if pct < 0 then pct = 0 end
|
||||
if pct > 100 then pct = 100 end
|
||||
healthDisplay = FormatValue(health) .. " | " .. tostring(pct) .. "%"
|
||||
end
|
||||
|
||||
if healthDisplay then
|
||||
frame.healthText:SetText(healthDisplay)
|
||||
frame.healthText:Show()
|
||||
else
|
||||
frame.healthText:SetText("")
|
||||
frame.healthText:Hide()
|
||||
end
|
||||
|
||||
local power = tonumber(UnitMana("player")) or 0
|
||||
local maxPower = tonumber(UnitManaMax("player")) or 0
|
||||
if maxPower < 1 then maxPower = 1 end
|
||||
if power < 0 then power = 0 end
|
||||
if power > maxPower then power = maxPower end
|
||||
frame.powerbar:SetMinMaxValues(0, maxPower)
|
||||
frame.powerbar:SetValue(power)
|
||||
frame.powerText:SetText(FormatValue(power) .. " / " .. FormatValue(maxPower))
|
||||
|
||||
local powerType = 0
|
||||
if UnitManaType then powerType = tonumber(UnitManaType("player")) or 0 end
|
||||
local c = powerColors[powerType] or powerColors[0]
|
||||
frame.powerbar:SetStatusBarColor(c[1], c[2], c[3])
|
||||
|
||||
local level = UnitLevel and UnitLevel("player") or nil
|
||||
if level and level > 0 then frame.levelText:SetText(tostring(level)) else frame.levelText:SetText("") end
|
||||
end
|
||||
|
||||
local function OptionsPreviewActive()
|
||||
return BNP.optionsFrame and BNP.optionsFrame.IsShown and BNP.optionsFrame:IsShown()
|
||||
end
|
||||
|
||||
local function ShouldShow()
|
||||
if not (BNP.IsPersonalNameplateEnabled and BNP:IsPersonalNameplateEnabled()) then
|
||||
return false
|
||||
end
|
||||
|
||||
if not (BNP.IsPersonalNameplateCombatOnly and BNP:IsPersonalNameplateCombatOnly()) then
|
||||
return true
|
||||
end
|
||||
|
||||
-- Keep it visible while the options window is open so Scale/Y Offset can be
|
||||
-- adjusted outside combat without needing a training dummy.
|
||||
return inCombat or OptionsPreviewActive()
|
||||
end
|
||||
|
||||
local function Refresh()
|
||||
local frame = CreatePersonalFrame()
|
||||
UpdatePosition(frame)
|
||||
UpdateStyle(frame)
|
||||
UpdateValues(frame)
|
||||
|
||||
if ShouldShow() then
|
||||
frame:Show()
|
||||
UpdatePersonalDebuffs(frame, true)
|
||||
else
|
||||
frame:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
function BNP:RefreshPersonalNameplate()
|
||||
Refresh()
|
||||
end
|
||||
|
||||
function BNP:RefreshPersonalNameplateStyle()
|
||||
if not personalFrame then return end
|
||||
UpdateStyle(personalFrame)
|
||||
UpdateValues(personalFrame)
|
||||
if personalFrame:IsShown() then UpdatePersonalDebuffs(personalFrame, true) end
|
||||
end
|
||||
|
||||
local events = CreateFrame("Frame")
|
||||
events:RegisterEvent("PLAYER_ENTERING_WORLD")
|
||||
events:RegisterEvent("PLAYER_REGEN_DISABLED")
|
||||
events:RegisterEvent("PLAYER_REGEN_ENABLED")
|
||||
events:RegisterEvent("UNIT_HEALTH")
|
||||
events:RegisterEvent("UNIT_MANA")
|
||||
events:RegisterEvent("UNIT_AURA")
|
||||
events:RegisterEvent("PLAYER_LEVEL_UP")
|
||||
|
||||
events:SetScript("OnEvent", function()
|
||||
if event == "PLAYER_REGEN_DISABLED" then
|
||||
inCombat = true
|
||||
Refresh()
|
||||
return
|
||||
elseif event == "PLAYER_REGEN_ENABLED" then
|
||||
inCombat = false
|
||||
Refresh()
|
||||
return
|
||||
elseif event == "PLAYER_ENTERING_WORLD" then
|
||||
if UnitAffectingCombat then
|
||||
inCombat = UnitAffectingCombat("player") and true or false
|
||||
else
|
||||
inCombat = false
|
||||
end
|
||||
Refresh()
|
||||
return
|
||||
end
|
||||
|
||||
if (event == "UNIT_HEALTH" or event == "UNIT_MANA" or event == "UNIT_AURA") and arg1 and arg1 ~= "player" then
|
||||
return
|
||||
end
|
||||
|
||||
if personalFrame then
|
||||
UpdateValues(personalFrame)
|
||||
UpdateStyle(personalFrame)
|
||||
if event == "UNIT_AURA" and personalFrame:IsShown() then UpdatePersonalDebuffs(personalFrame, true) end
|
||||
end
|
||||
end)
|
||||
|
||||
local updater = CreateFrame("Frame")
|
||||
updater:SetScript("OnUpdate", function()
|
||||
updateElapsed = updateElapsed + arg1
|
||||
if updateElapsed < 0.10 then return end
|
||||
updateElapsed = 0
|
||||
|
||||
if not personalFrame then return end
|
||||
|
||||
-- Cheap 10 Hz fallback for custom 1.12 clients that do not emit every modern
|
||||
-- player-resource event consistently. It only runs value work while visible.
|
||||
local shouldShow = ShouldShow()
|
||||
if shouldShow then
|
||||
if not personalFrame:IsShown() then
|
||||
personalFrame:Show()
|
||||
debuffScanElapsed = 0.50
|
||||
end
|
||||
UpdateValues(personalFrame)
|
||||
|
||||
debuffScanElapsed = debuffScanElapsed + 0.10
|
||||
local forceScan = false
|
||||
if debuffScanElapsed >= 0.50 then
|
||||
debuffScanElapsed = 0
|
||||
forceScan = true
|
||||
end
|
||||
UpdatePersonalDebuffs(personalFrame, forceScan)
|
||||
elseif personalFrame:IsShown() then
|
||||
personalFrame:Hide()
|
||||
end
|
||||
end)
|
||||
+25
-6
@@ -179,10 +179,23 @@ end
|
||||
-- receives a real "mouseover" unit even though clicks are forwarded to the
|
||||
-- original Blizzard nameplate.
|
||||
local function GetPlateMouseoverUnit(plate)
|
||||
if not plate or not plate.GetName then return nil end
|
||||
if not plate then return nil end
|
||||
|
||||
-- Prefer ClassicAPI's exact nameplateN token when it is available. Besides
|
||||
-- being stable for the lifetime of the projected plate, this is a normal
|
||||
-- unit token and gives GameTooltip the same reaction/hostility information
|
||||
-- it receives from Blizzard unit frames.
|
||||
local classicToken = plate.BNPClassicUnitToken
|
||||
if classicToken and UnitExists then
|
||||
local existsOK, exists = pcall(UnitExists, classicToken)
|
||||
if existsOK and exists then return classicToken end
|
||||
end
|
||||
|
||||
-- SuperWoW fallback: frame:GetName(1) returns the GUID attached to the
|
||||
-- nameplate. SuperWoW extends unit-taking functions to accept that GUID.
|
||||
if not plate.GetName then return nil end
|
||||
local ok, unit = pcall(function() return plate:GetName(1) end)
|
||||
if not ok or not unit then return nil end
|
||||
if not ok or not unit or unit == "" then return nil end
|
||||
|
||||
if UnitExists then
|
||||
local existsOK, exists = pcall(UnitExists, unit)
|
||||
@@ -195,17 +208,23 @@ end
|
||||
local function ShowPlateTooltip(wrapper, unit)
|
||||
if not wrapper or not unit or not GameTooltip or not GameTooltip.SetUnit then return end
|
||||
|
||||
-- SetMouseoverUnit() has already made "mouseover" a valid unit. Prefer
|
||||
-- that token so tooltip addons see the same unit surface as mouseover macros.
|
||||
if GameTooltip_SetDefaultAnchor then
|
||||
GameTooltip_SetDefaultAnchor(GameTooltip, wrapper)
|
||||
elseif GameTooltip.SetOwner then
|
||||
GameTooltip:SetOwner(wrapper, "ANCHOR_CURSOR")
|
||||
end
|
||||
|
||||
local ok = pcall(function() GameTooltip:SetUnit("mouseover") end)
|
||||
-- Do NOT build the tooltip from the synthetic "mouseover" token.
|
||||
-- SetMouseoverUnit() is still required for mouseover macros/addons, but on
|
||||
-- some SuperWoW/ClassicAPI combinations the synthetic token can briefly
|
||||
-- expose stale reaction data. That is visible as a hostile NPC name
|
||||
-- flickering between yellow and red in GameTooltip. Feed GameTooltip the
|
||||
-- exact plate unit instead (nameplateN when possible, GUID otherwise), just
|
||||
-- like established custom unit-frame implementations do.
|
||||
local ok = pcall(function() GameTooltip:SetUnit(unit) end)
|
||||
if not ok then
|
||||
pcall(function() GameTooltip:SetUnit(unit) end)
|
||||
-- Compatibility fallback for older clients that cannot tooltip a GUID.
|
||||
pcall(function() GameTooltip:SetUnit("mouseover") end)
|
||||
end
|
||||
|
||||
if GameTooltip.Show then GameTooltip:Show() end
|
||||
|
||||
@@ -174,6 +174,7 @@ function BNP:RefreshNameplateBorderStyle()
|
||||
if self.RefreshEliteDragonVisibility then
|
||||
self:RefreshEliteDragonVisibility()
|
||||
end
|
||||
if self.RefreshPersonalNameplateStyle then self:RefreshPersonalNameplateStyle() end
|
||||
end
|
||||
|
||||
function BNP:RefreshNameplateLevelVisibility()
|
||||
@@ -185,6 +186,7 @@ function BNP:RefreshNameplateLevelVisibility()
|
||||
if BNP.RefreshImmunityLayoutForPlate then BNP:RefreshImmunityLayoutForPlate(plate) end
|
||||
end
|
||||
end
|
||||
if self.RefreshPersonalNameplateStyle then self:RefreshPersonalNameplateStyle() end
|
||||
end
|
||||
|
||||
function BNP:RefreshHealthbarBackground()
|
||||
@@ -192,6 +194,7 @@ function BNP:RefreshHealthbarBackground()
|
||||
for plate in pairs(BNP.plates or {}) do
|
||||
ApplyHealthbarBackground(plate)
|
||||
end
|
||||
if self.RefreshPersonalNameplateStyle then self:RefreshPersonalNameplateStyle() end
|
||||
end
|
||||
|
||||
if BNP.libnameplate then
|
||||
|
||||
Reference in New Issue
Block a user