Files
moon-well-ui/data/addon/ForeverUI/PetFrame.lua
T
Adrestie 5842eb7a2b ForeverUI : code en anglais, commentaires resserres, code mort retire
Traduction
- Identifiants en anglais dans tout le module : addon, ecrans d'accueil,
  outils Python, banc d'essai et patcheur C#.
- Fichiers au nom francais renommes (Gabarits.lua -> Templates.lua,
  Textes_*.lua -> Texts_*.lua, deployer.py -> deploy.py,
  tools/patcheur -> tools/patcher, etc.).
- Commandes /fui et options de deploy.py en anglais (--check, --server,
  --remove-dbc).
- Cles des variables sauvegardees et de data/dbc/statistics.json
  traduites, sans reprise des anciennes cles.
- API de la micro-barre : ForeverUI.AddMicroButton(def) avec les champs
  name, atlasSet, after, tooltip, onClick, ready, et
  ForeverUI.UpdateMicro(name, isOpen).
- Commentaires reecrits en anglais, courts, sans dates ni historique ;
  les fonctions au nom peu parlant et leurs parametres sont expliques.

Code mort
- Exports, fonctions et variables jamais lus retires.
- Retires a la demande des modules : ForeverUI.PvPJauge, les exports
  FondEntete / TuilerEntete / Polices de TradeSkill, l'art legacy-up-c60,
  honorfill(half)blue et inv_misc_book_09, leurs entrees d'atlas et les
  parties d'outils correspondantes.

Verifications : liaisons des noms comparees avant et apres, banc vert,
chemins de textures inchanges, generateurs reproduisant les fichiers,
patcheur identique octet pour octet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-29 09:15:52 +02:00

363 lines
11 KiB
Lua

-- ForeverUI: the pet frame, from Camelot's mainline PetFrame template. 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)
local healthFill = frame:CreateTexture(nil, "BORDER")
healthFill:SetPoint("TOPLEFT", 44, -17)
local powerFill = frame:CreateTexture(nil, "BORDER")
powerFill:SetPoint("TOPLEFT", 40, -28)
local art = frame:CreateTexture(nil, "ARTWORK")
art:SetPoint("CENTER")
ForeverUI.SetAtlas(art, FRAME_ART)
local threat = frame:CreateTexture(nil, "OVERLAY")
threat:SetPoint("TOPLEFT", 1, 0)
ForeverUI.SetAtlas(threat, THREAT_ART)
threat:Hide()
local attack = frame:CreateTexture(nil, "OVERLAY")
attack:SetPoint("TOPLEFT", 0, 1)
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("LEFT")
nameText:SetJustifyV("BOTTOM")
nameText:SetPoint("TOPLEFT", portrait, "TOPRIGHT", 2, 0)
local healthText = frame:CreateFontString(nil, "OVERLAY", "TextStatusBarText")
healthText:SetPoint("CENTER", frame, "TOPLEFT", 44 + 35, -17 - 5)
healthText:Hide()
local powerText = frame:CreateFontString(nil, "OVERLAY", "TextStatusBarText")
powerText:SetPoint("CENTER", frame, "TOPLEFT", 40 + 37 + 2, -28 - 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