Files
moon-well-ui/data/addon/ForeverUI/PetFrame.lua
T
Adrestie c994e4c16d ForeverUI : barres des cadres d'unité au-dessus de l'art
Les barres de vie et de ressource du joueur, de la cible, de la cible de
la cible et du familier étaient dans le calque BORDER, sous l'art du
cadre (ARTWORK). Les emplacements des barres de cet art sont un voile
sombre : noir à 40 % en 69893, brun à 74 % en 70450, qui recouvrait donc
les barres et les rendait très sombres. Camelot dessine ses barres
(StatusBar, cadres enfants) au-dessus de l'art : elles passent en
OVERLAY. Les cadres de groupe les avaient déjà dans un cadre enfant.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-17 23:06:49 +02:00

365 lines
12 KiB
Lua

-- ForeverUI: the pet frame, from Camelot's mainline PetFrame template as camelot/PetFrame.lua
-- (1.60.1.70450, PetFrameMixin:OnLoad) re-anchors it. The 3.3.5 PetFrame is a
-- child of PlayerFrame, so it goes away with the client's player frame (see PlayerFrame.lua).
-- It uses the target-of-target art on purpose. 3.3.5 has no MaskTexture, so the bars are not
-- clipped to the frame shape. The debuff icons under the frame are left out; buffs stay in the
-- tooltip.
local player = ForeverUI.PlayerFrame
local FRAME_ART = "ui-hud-unitframe-targetoftarget-portraiton"
local THREAT_ART = "ui-hud-unitframe-targetoftarget-portraiton-incombat"
local ATTACK_ART = "ui-hud-unitframe-targetoftarget-portraiton-status"
local HEALTH_FILL = "ui-hud-unitframe-targetoftarget-portraiton-bar-health"
local POWER_FILL = {
MANA = "ui-hud-unitframe-targetoftarget-portraiton-bar-mana",
RAGE = "ui-hud-unitframe-targetoftarget-portraiton-bar-rage",
FOCUS = "ui-hud-unitframe-targetoftarget-portraiton-bar-focus",
ENERGY = "ui-hud-unitframe-targetoftarget-portraiton-bar-energy",
RUNIC_POWER = "ui-hud-unitframe-targetoftarget-portraiton-bar-runicpower",
}
local HAPPINESS_FILE = "Interface\\PetPaperDollFrame\\UI-PetHappiness"
local HAPPINESS_COORDS = {
[1] = { 0.375, 0.5625, 0, 0.359375 },
[2] = { 0.1875, 0.375, 0, 0.359375 },
[3] = { 0, 0.1875, 0, 0.359375 },
}
local frame = CreateFrame("Button", "ForeverUIPetFrame", player, "SecureUnitButtonTemplate")
frame:SetWidth(120)
frame:SetHeight(49)
frame:SetFrameStrata("LOW")
frame:SetHitRectInsets(7, 66, 6, 7)
-- Below the runes if any (their container already sits where Camelot's does), 2 apart;
-- otherwise where Camelot's PlayerBottomManagedFrameContainer is, (30, 25), plus 7.5 for the
-- pet's 15 left margin
local runes = _G.ForeverUIClassResourceContainer
if runes then
frame:SetPoint("TOP", runes, "BOTTOM", 7.5, -2)
else
frame:SetPoint("TOP", player, "BOTTOM", 30 + 7.5, 25)
end
frame.unit = "pet"
frame:SetAttribute("unit", "pet")
frame:SetAttribute("toggleForVehicle", true)
frame:SetAttribute("*type1", "target")
frame:SetAttribute("*type2", "menu")
frame:RegisterForClicks("AnyUp")
frame.menu = function(self)
ForeverUI.UnitMenu.open(ToggleDropDownMenu, 1, nil, PetFrameDropDown, self, 44, 8)
end
local portrait = frame:CreateTexture(nil, "BACKGROUND")
portrait:SetWidth(37)
portrait:SetHeight(37)
portrait:SetPoint("TOPLEFT", 5, -5)
-- Bars above the art (OVERLAY), as camelot's StatusBars over the frame texture
local healthFill = frame:CreateTexture(nil, "OVERLAY")
healthFill:SetPoint("TOPLEFT", 45, -17.5)
local powerFill = frame:CreateTexture(nil, "OVERLAY")
powerFill:SetPoint("TOPLEFT", 43, -29.5)
local art = frame:CreateTexture(nil, "ARTWORK")
art:SetPoint("CENTER")
ForeverUI.SetAtlas(art, FRAME_ART)
local threat = frame:CreateTexture(nil, "OVERLAY")
threat:SetPoint("TOPLEFT", 0, 0)
ForeverUI.SetAtlas(threat, THREAT_ART)
threat:Hide()
local attack = frame:CreateTexture(nil, "OVERLAY")
attack:SetPoint("TOPLEFT", 0, 0)
ForeverUI.SetAtlas(attack, ATTACK_ART)
attack:SetBlendMode("ADD")
attack:Hide()
local nameText = frame:CreateFontString(nil, "OVERLAY", "GameFontNormalSmall")
nameText:SetWidth(68)
nameText:SetHeight(10)
nameText:SetJustifyH("CENTER")
nameText:SetJustifyV("BOTTOM")
nameText:SetPoint("TOPLEFT", portrait, "TOPRIGHT", 2, -1)
local healthText = frame:CreateFontString(nil, "OVERLAY", "TextStatusBarText")
healthText:SetPoint("CENTER", frame, "TOPLEFT", 45 + 35, -17.5 - 5)
healthText:Hide()
local powerText = frame:CreateFontString(nil, "OVERLAY", "TextStatusBarText")
powerText:SetPoint("CENTER", frame, "TOPLEFT", 43 + 37, -29.5 - 3.5)
powerText:Hide()
local hitText = frame:CreateFontString(nil, "OVERLAY", "NumberFontNormalHuge")
hitText:SetPoint("TOPLEFT", 5, -5)
hitText:Hide()
if CombatFeedback_Initialize then
CombatFeedback_Initialize(frame, hitText, 30)
end
-- Hunter pet happiness (PetFrameHappinessTemplate)
local happiness = CreateFrame("Frame", "ForeverUIPetFrameHappiness", frame)
happiness:SetWidth(24)
happiness:SetHeight(23)
happiness:SetPoint("LEFT", frame, "RIGHT", 0, -4)
happiness:EnableMouse(true)
local happinessTexture = happiness:CreateTexture(nil, "BACKGROUND")
happinessTexture:SetTexture(HAPPINESS_FILE)
happinessTexture:SetAllPoints(happiness)
happiness:Hide()
-- ------------------------------------------------------------ updates
-- The unit shown: the player when a vehicle takes over the player frame
-- (toggleForVehicle, as in SecureButton_GetModifiedUnit)
local function displayedUnit()
if UnitHasVehicleUI and UnitHasVehicleUI("player") then
return "player"
end
return "pet"
end
local function updateHealth()
local unit = displayedUnit()
local maximum = UnitHealthMax(unit)
local fraction = 0
if maximum and maximum > 0 then
fraction = UnitHealth(unit) / maximum
end
ForeverUI.SetAtlasFill(healthFill, HEALTH_FILL, fraction)
end
local function updatePower()
local unit = displayedUnit()
local powerType, powerToken = UnitPowerType(unit)
local maximum = UnitPowerMax(unit, powerType)
local fraction = 0
if maximum and maximum > 0 then
fraction = UnitPower(unit, powerType) / maximum
end
ForeverUI.SetAtlasFill(powerFill, POWER_FILL[powerToken or ""] or POWER_FILL.MANA, fraction)
end
-- Bar text follows petStatusText, and shows on hover
local function formatValue(value, maximum)
if not maximum or maximum <= 0 then
return ""
end
if GetCVarBool and GetCVarBool("statusTextPercentage") then
return tostring(math.ceil(value / maximum * 100)) .. "%"
end
return value .. " / " .. maximum
end
local function updateTexts()
local always = GetCVar and GetCVar("petStatusText") == "1"
if not (always or frame.hovered) then
healthText:Hide()
powerText:Hide()
return
end
local unit = displayedUnit()
local powerType = UnitPowerType(unit)
healthText:SetText(formatValue(UnitHealth(unit), UnitHealthMax(unit)))
healthText:Show()
-- PetFrameMixin:Update: no power text without power
local powerMax = UnitPowerMax(unit, powerType)
if powerMax and powerMax > 0 then
powerText:SetText(formatValue(UnitPower(unit, powerType), powerMax))
powerText:Show()
else
powerText:Hide()
end
end
-- UnitFrame_UpdateThreatIndicator: shown above zero, tinted by threat level
local function updateThreat()
local status = UnitThreatSituation and UnitThreatSituation(displayedUnit())
if status and status > 0 and GetThreatStatusColor then
threat:SetVertexColor(GetThreatStatusColor(status))
threat:Show()
else
threat:Hide()
end
end
local function updateHappiness()
local mood, damage, loyalty = nil, 0, 0
if GetPetHappiness then
mood, damage, loyalty = GetPetHappiness()
end
local _, isHunterPet = HasPetUI()
if not mood or not isHunterPet then
happiness:Hide()
return
end
local c = HAPPINESS_COORDS[mood]
if c then
happinessTexture:SetTexCoord(c[1], c[2], c[3], c[4])
end
happiness.tooltip = _G["PET_HAPPINESS" .. mood] or NONE
happiness.tooltipDamage = PET_DAMAGE_PERCENTAGE and format(PET_DAMAGE_PERCENTAGE, damage or 0)
if (loyalty or 0) < 0 then
happiness.tooltipLoyalty = LOSING_LOYALTY
elseif (loyalty or 0) > 0 then
happiness.tooltipLoyalty = GAINING_LOYALTY
else
happiness.tooltipLoyalty = nil
end
happiness:Show()
end
local function updateAll()
local unit = displayedUnit()
-- The unit read by the tooltip and PartyMemberBuffTooltip_Update
frame.unit = unit
updateHealth()
updatePower()
nameText:SetText(UnitName(unit))
SetPortraitTexture(portrait, unit)
updateTexts()
updateThreat()
updateHappiness()
end
happiness:SetScript("OnEnter", function(self)
if not self.tooltip then return end
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
GameTooltip:SetText(self.tooltip)
if self.tooltipDamage then GameTooltip:AddLine(self.tooltipDamage, "", 1, 1, 1) end
if self.tooltipLoyalty then GameTooltip:AddLine(self.tooltipLoyalty, "", 1, 1, 1) end
if GetPetFoodTypes and PET_DIET_TEMPLATE then
local diet = { GetPetFoodTypes() }
local text = #diet > 0 and table.concat(diet, PET_FOOD_DELIMIT or ", ") or NONE
GameTooltip:AddLine(format(PET_DIET_TEMPLATE, text), "", 1, 1, 1)
end
GameTooltip:Show()
end)
happiness:SetScript("OnLeave", function() GameTooltip:Hide() end)
-- In 3.3.5, PartyMemberBuffTooltip_Update takes the frame (it reads self.unit)
-- and shows or hides the tooltip itself
frame:SetScript("OnEnter", function(self)
self.hovered = true
updateTexts()
if UnitFrame_OnEnter then UnitFrame_OnEnter(self) end
if PartyMemberBuffTooltip and PartyMemberBuffTooltip_Update then
PartyMemberBuffTooltip:SetPoint("TOPLEFT", self, "TOPLEFT", 60, -35)
PartyMemberBuffTooltip_Update(self)
end
end)
frame:SetScript("OnLeave", function(self)
self.hovered = false
updateTexts()
if UnitFrame_OnLeave then UnitFrame_OnLeave(self) end
if PartyMemberBuffTooltip then PartyMemberBuffTooltip:Hide() end
end)
frame:SetScript("OnShow", updateAll)
-- Attack pulse (PetFrameMixin:OnUpdate), red between PET_ATTACK_START and PET_ATTACK_STOP,
-- and damage taken
frame.attackCounter, frame.attackSign = 0, -1
frame:SetScript("OnUpdate", function(self, elapsed)
if attack:IsShown() then
local counter = self.attackCounter + elapsed
if counter > 0.5 then
self.attackSign = -self.attackSign
end
counter = math.fmod(counter, 0.5)
self.attackCounter = counter
local alpha
if self.attackSign == 1 then
alpha = (55 + counter * 400) / 255
else
alpha = (255 - counter * 400) / 255
end
attack:SetVertexColor(1, 0, 0, alpha)
end
if CombatFeedback_OnUpdate then
CombatFeedback_OnUpdate(self, elapsed)
end
end)
frame:SetScript("OnEvent", function(self, event, unit, ...)
if event == "PET_ATTACK_START" then
attack:SetVertexColor(1, 0, 0, 1)
attack:Show()
return
end
if event == "PET_ATTACK_STOP" then
attack:Hide()
return
end
if event == "CVAR_UPDATE" then
updateTexts()
return
end
if event == "PLAYER_ENTERING_WORLD" or event == "PET_UI_UPDATE" then
updateAll()
return
end
if event == "UNIT_PET" or event == "UNIT_ENTERED_VEHICLE" or event == "UNIT_EXITED_VEHICLE" then
if unit == "player" then
attack:Hide()
updateAll()
end
return
end
if unit ~= displayedUnit() then
return
end
if event == "UNIT_COMBAT" then
if CombatFeedback_OnCombatEvent then
CombatFeedback_OnCombatEvent(self, ...)
end
elseif event == "UNIT_HEALTH" or event == "UNIT_MAXHEALTH" then
updateHealth()
updateTexts()
elseif event == "UNIT_NAME_UPDATE" then
nameText:SetText(UnitName(unit))
elseif event == "UNIT_PORTRAIT_UPDATE" or event == "UNIT_MODEL_CHANGED" then
SetPortraitTexture(portrait, unit)
elseif event == "UNIT_THREAT_SITUATION_UPDATE" then
updateThreat()
elseif event == "UNIT_HAPPINESS" then
updateHappiness()
else
updatePower()
updateTexts()
end
end)
for _, event in ipairs({
"PLAYER_ENTERING_WORLD", "UNIT_PET", "PET_UI_UPDATE", "UNIT_ENTERED_VEHICLE", "UNIT_EXITED_VEHICLE",
"PET_ATTACK_START", "PET_ATTACK_STOP", "UNIT_COMBAT", "UNIT_HEALTH", "UNIT_MAXHEALTH",
"UNIT_NAME_UPDATE", "UNIT_PORTRAIT_UPDATE", "UNIT_MODEL_CHANGED", "UNIT_THREAT_SITUATION_UPDATE",
"UNIT_HAPPINESS", "UNIT_DISPLAYPOWER", "UNIT_MANA", "UNIT_RAGE", "UNIT_FOCUS", "UNIT_ENERGY",
"UNIT_RUNIC_POWER", "UNIT_MAXMANA", "UNIT_MAXRAGE", "UNIT_MAXFOCUS", "UNIT_MAXENERGY",
"UNIT_MAXRUNIC_POWER", "CVAR_UPDATE",
}) do
frame:RegisterEvent(event)
end
-- The client shows and hides the frame depending on whether the pet exists
if RegisterUnitWatch then
RegisterUnitWatch(frame)
end
frame.portrait = portrait
frame.healthFill = healthFill
frame.powerFill = powerFill
frame.art = art
frame.threat = threat
frame.attack = attack
frame.nameText = nameText
frame.healthText = healthText
frame.powerText = powerText
frame.happiness = happiness
frame.happinessTexture = happinessTexture
ForeverUI.PetFrame = frame
ForeverUI.PetFrameUpdate = updateAll