c994e4c16d
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>
365 lines
12 KiB
Lua
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
|