66ccb1ba1f
Ensembles d'équipement : le crochet qui abandonne une modification sur Annuler ou Échap visait la globale GearManagerDialogPopup_OnHide, que le XML lie directement (function=) : il ne se déclenchait jamais. La session restée ouverte faisait de la sauvegarde suivante une modification : l'ancien ensemble était rééquipé, enregistré sous le nom tapé, puis supprimé. Le script du cadre est désormais greffé (comme Taxi.lua). Réputation : la description et les trois cases venaient de la liste du client, qui ne les remplit que pour ses 15 lignes visibles ; cachée et jamais défilée, elle laissait à partir de la 16e faction celles de la précédente, et un clic agissait sur la nouvelle à contresens (en guerre, barre suivie). Elles sont lues sur la faction elle-même. Combat : PetPaperDollFrame, et donc CharacterFrame, sont protégés par les boutons de compagnon sécurisés. Les masquer, les déplacer ou retailler la feuille depuis l'addon en combat était bloqué : à chaque rafraîchissement du familier (PET_BAR_UPDATE...), à chaque ouverture de la feuille, à chaque onglet. Ces appels attendent la fin du combat ; Panes n'appelle plus Show ou Hide sans changement, que le client refuse aussi. Performance : les événements d'unité de la feuille (inventaire, portrait, modèle) ne relancent plus toute la mise en page pour les autres unités ni feuille fermée ; l'onglet du familier ne réagit plus qu'à son familier ; l'onglet des statistiques relit l'arbre au plus deux fois par seconde. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KxRZNHkq453dyUMcWsyUf1
547 lines
18 KiB
Lua
547 lines
18 KiB
Lua
-- ForeverUI: the pet tab, built on 3.3.5's PetPaperDollFrame (camelot shows the pet as a
|
|
-- right-pane sidebar instead). Left pane: the client's pet model. Right pane: the level, then
|
|
-- General (health, armor, damage, attack power, crit) and Resistances, laid out like the
|
|
-- character stats. Values are read from the client's own frames, which keep their formatting.
|
|
-- 3.3.5 has no pet crit API: the crit shown is only GetCritChanceFromAgility("pet").
|
|
|
|
local ForeverUI = ForeverUI or {}
|
|
_G.ForeverUI = ForeverUI
|
|
local L = ForeverUI.L
|
|
|
|
-- Same measures as the character stats (CharacterFrame.lua), so both pages share one edge.
|
|
local MARGIN = 20 -- STAT_MARGIN
|
|
local STEP = 13 -- STAT_STEP, one row's height
|
|
local HEADER_H = 34 -- STAT_HEADER
|
|
local HEADER_OVERHANG = 5 -- STAT_HEADER_OVERHANG
|
|
local GROUP_GAP = 16 -- STAT_GROUP_GAP
|
|
local TOP = 14 -- below the pane's top
|
|
local LEVEL_H = 20
|
|
local LEVEL_GAP = -8
|
|
|
|
-- Rotation arrows, placed like the character's (placeModel in CharacterFrame.lua): centered
|
|
-- on the pane's top, side by side, above the model so they get the click. Both screens use
|
|
-- 35 x 35 buttons (client XML), so only the position changes.
|
|
local ROTATION_Y = -12
|
|
local ROTATION_GAP = 4
|
|
|
|
local ATLAS_HEADER = "ui-character-info-title"
|
|
local ATLAS_LEVEL = "ui-character-info-itemlevel-bounce"
|
|
local ATLAS_DARK_BACKGROUND = "ui-character-info-itemlevel-bounce"
|
|
local ATLAS_LIGHT_BACKGROUND = "ui-character-info-line-bounce"
|
|
|
|
-- Default pane sizes: a content may be built on first open, before the window is laid out.
|
|
local PANE_W = 233
|
|
local LEFT_PANE_W, LEFT_PANE_H = 398, 464
|
|
|
|
local panel, detail
|
|
|
|
-- ---------------------------------------------------------------- Data
|
|
|
|
local function hasPet()
|
|
return (HasPetUI and HasPetUI()) and UnitExists and UnitExists("pet")
|
|
end
|
|
|
|
-- Reads a stat's text from the client's frame after letting the client compute it,
|
|
-- so the client keeps its formatting. place: the client's PaperDollFrame_Set* function.
|
|
local function readFromClient(frameName, place)
|
|
local frame = _G[frameName]
|
|
if not frame then
|
|
return nil
|
|
end
|
|
if place then
|
|
place(frame, "Pet")
|
|
end
|
|
local text = _G[frameName .. "StatText"]
|
|
return text and text:GetText()
|
|
end
|
|
|
|
-- Rows of the General category: { name, value }.
|
|
local function readGeneral()
|
|
local rows = {}
|
|
|
|
rows[#rows + 1] = {
|
|
name = HEALTH,
|
|
value = tostring((UnitHealthMax and UnitHealthMax("pet")) or 0),
|
|
}
|
|
rows[#rows + 1] = {
|
|
name = ARMOR,
|
|
value = readFromClient("PetArmorFrame", PaperDollFrame_SetArmor) or "",
|
|
}
|
|
rows[#rows + 1] = {
|
|
name = DAMAGE,
|
|
value = readFromClient("PetDamageFrame", PaperDollFrame_SetDamage) or "",
|
|
}
|
|
-- ATTACK_POWER is "Power" in this client; ATTACK_POWER_TOOLTIP is "Attack Power".
|
|
rows[#rows + 1] = {
|
|
name = ATTACK_POWER_TOOLTIP or ATTACK_POWER,
|
|
value = readFromClient("PetAttackPowerFrame",
|
|
PaperDollFrame_SetAttackPower) or "",
|
|
}
|
|
|
|
-- Only the agility part of crit: 3.3.5 has no pet crit API.
|
|
local critChance = (GetCritChanceFromAgility and GetCritChanceFromAgility("pet")) or 0
|
|
rows[#rows + 1] = {
|
|
name = MELEE_CRIT_CHANCE,
|
|
value = string.format("%.2f%%", critChance),
|
|
}
|
|
|
|
return rows
|
|
end
|
|
|
|
-- Resistance rows { name, value }, for the debug report.
|
|
local function readResistances()
|
|
local rows = {}
|
|
local numResistances = NUM_PET_RESISTANCE_TYPES or 5
|
|
for rank = 1, numResistances do
|
|
local frame = _G["PetMagicResFrame" .. rank]
|
|
local school = frame and frame.GetID and frame:GetID()
|
|
if school then
|
|
local _, value = UnitResistance("pet", school)
|
|
rows[#rows + 1] = {
|
|
name = _G["RESISTANCE" .. school .. "_NAME"] or tostring(school),
|
|
value = tostring(value or 0),
|
|
}
|
|
end
|
|
end
|
|
return rows
|
|
end
|
|
|
|
-- --------------------------------------------------------------- Right pane
|
|
|
|
local groups = {}
|
|
local rowLines = {}
|
|
|
|
-- One stat row like StatFrameTemplate: label left, value right, alternating background.
|
|
local function createRow(rank, width)
|
|
local row = CreateFrame("Frame", "ForeverUIPetStat" .. rank, detail)
|
|
row:SetWidth(width)
|
|
row:SetHeight(STEP)
|
|
|
|
local background = row:CreateTexture(nil, "BACKGROUND")
|
|
background:SetPoint("TOPLEFT", row, "TOPLEFT", 0, 0)
|
|
background:SetPoint("BOTTOMRIGHT", row, "BOTTOMRIGHT", 0, 0)
|
|
row.background = background
|
|
|
|
local label = row:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
|
label:SetJustifyH("LEFT")
|
|
label:SetPoint("LEFT", row, "LEFT", 0, 0)
|
|
row.label = label
|
|
|
|
local value = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
|
value:SetJustifyH("RIGHT")
|
|
value:SetPoint("RIGHT", row, "RIGHT", 0, 0)
|
|
value:SetPoint("LEFT", label, "RIGHT", 4, 0)
|
|
row.value = value
|
|
|
|
rowLines[rank] = row
|
|
return row
|
|
end
|
|
|
|
local function createHeader(rank, width)
|
|
local header = CreateFrame("Frame", "ForeverUIPetCategory" .. rank, detail)
|
|
header:SetWidth(width + 2 * HEADER_OVERHANG)
|
|
header:SetHeight(HEADER_H)
|
|
|
|
-- camelot's header frame, stretched, as on the character stat category selectors.
|
|
local background = header:CreateTexture(nil, "BACKGROUND")
|
|
ForeverUI.SetAtlas(background, ATLAS_HEADER, true)
|
|
background:SetAllPoints(header)
|
|
|
|
local label = header:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
|
|
label:SetPoint("CENTER", header, "CENTER", 0, 1)
|
|
header.label = label
|
|
|
|
groups[rank] = header
|
|
return header
|
|
end
|
|
|
|
-- Resistances are the client's own icons (PetMagicResFrame1-5), placed in a row, so the
|
|
-- client keeps their value, color and tooltip. They are reparented to the right pane
|
|
-- because the sweep hides their parent screen. placeResistances returns the height used.
|
|
local RES_W, RES_H = 32, 29 -- MagicResistanceFrameTemplate
|
|
|
|
local function placeResistances(width, y)
|
|
if PetPaperDollFrame_SetResistances then
|
|
PetPaperDollFrame_SetResistances()
|
|
end
|
|
|
|
local numResistances = NUM_PET_RESISTANCE_TYPES or 5
|
|
-- Spread evenly over the row width.
|
|
local gap = 0
|
|
if numResistances > 1 then
|
|
gap = (width - numResistances * RES_W) / (numResistances - 1)
|
|
if gap < 0 then
|
|
gap = 0
|
|
end
|
|
end
|
|
|
|
for rank = 1, numResistances do
|
|
local frame = _G["PetMagicResFrame" .. rank]
|
|
if frame then
|
|
frame:SetParent(detail)
|
|
frame:SetWidth(RES_W)
|
|
frame:SetHeight(RES_H)
|
|
frame:ClearAllPoints()
|
|
frame:SetPoint("TOPLEFT", detail, "TOPLEFT",
|
|
MARGIN + (rank - 1) * (RES_W + gap), -y)
|
|
frame:Show()
|
|
for _, region in ipairs({ frame:GetRegions() }) do
|
|
if region.Show then
|
|
region:Show()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
return RES_H
|
|
end
|
|
|
|
-- Stacks each category header and its rows.
|
|
local function layout()
|
|
if not detail then
|
|
return
|
|
end
|
|
|
|
local width = detail:GetWidth() or 0
|
|
if width < 50 then
|
|
width = PANE_W
|
|
end
|
|
width = width - 2 * MARGIN
|
|
|
|
local blocks = {
|
|
{ name = GENERAL, rows = readGeneral() },
|
|
{ name = L.PETTAB_RESISTANCES, icons = true },
|
|
}
|
|
|
|
local y = TOP
|
|
|
|
-- "Level X <name>" at the top of the pane.
|
|
local level = (UnitLevel and UnitLevel("pet")) or 0
|
|
local name = (UnitName and UnitName("pet")) or ""
|
|
detail.level:SetWidth(width)
|
|
detail.level:ClearAllPoints()
|
|
detail.level:SetPoint("TOP", detail, "TOP", 0, -y)
|
|
detail.level:SetText(string.format("%s %s",
|
|
string.format(UNIT_LEVEL_TEMPLATE, level), name))
|
|
y = y + LEVEL_H - LEVEL_GAP
|
|
|
|
local headerIndex, rowIndex = 0, 0
|
|
for _, block in ipairs(blocks) do
|
|
headerIndex = headerIndex + 1
|
|
local header = groups[headerIndex] or createHeader(headerIndex, width)
|
|
header:SetWidth(width + 2 * HEADER_OVERHANG)
|
|
header:ClearAllPoints()
|
|
header:SetPoint("TOPLEFT", detail, "TOPLEFT", MARGIN - HEADER_OVERHANG, -y)
|
|
header.label:SetText(block.name)
|
|
header:Show()
|
|
y = y + HEADER_H
|
|
|
|
if block.icons then
|
|
y = y + placeResistances(width, y)
|
|
else
|
|
for number, data in ipairs(block.rows) do
|
|
rowIndex = rowIndex + 1
|
|
local row = rowLines[rowIndex] or createRow(rowIndex, width)
|
|
row:SetWidth(width)
|
|
row:ClearAllPoints()
|
|
row:SetPoint("TOPLEFT", detail, "TOPLEFT", MARGIN, -y)
|
|
row.label:SetText(string.format(STAT_FORMAT, data.name))
|
|
row.value:SetText(data.value)
|
|
-- Dark first, restarting at each category, as in the character stats.
|
|
ForeverUI.SetAtlas(row.background,
|
|
(number % 2 == 1) and ATLAS_DARK_BACKGROUND or ATLAS_LIGHT_BACKGROUND,
|
|
true)
|
|
row:Show()
|
|
y = y + STEP
|
|
end
|
|
end
|
|
|
|
y = y + GROUP_GAP
|
|
end
|
|
|
|
for rank = headerIndex + 1, #groups do
|
|
groups[rank]:Hide()
|
|
end
|
|
for rank = rowIndex + 1, #rowLines do
|
|
rowLines[rank]:Hide()
|
|
end
|
|
end
|
|
|
|
-- Fills the right pane, or empties it when there is no pet.
|
|
local function updateDetail()
|
|
if not detail then
|
|
return
|
|
end
|
|
if not hasPet() then
|
|
detail.level:SetText("")
|
|
for _, header in ipairs(groups) do
|
|
header:Hide()
|
|
end
|
|
for _, row in ipairs(rowLines) do
|
|
row:Hide()
|
|
end
|
|
for rank = 1, (NUM_PET_RESISTANCE_TYPES or 5) do
|
|
local frame = _G["PetMagicResFrame" .. rank]
|
|
if frame then
|
|
frame:Hide()
|
|
end
|
|
end
|
|
return
|
|
end
|
|
layout()
|
|
end
|
|
ForeverUI.PetDetail = updateDetail
|
|
|
|
-- ------------------------------------------------------------- Left pane
|
|
|
|
-- Hides a client frame's art, text and children, except the spared ones.
|
|
-- PetModelFrame is a grandchild of PetPaperDollFrame (inside PetPaperDollFramePetFrame),
|
|
-- so both levels are swept, each sparing what holds the model.
|
|
-- spared: set of child frames to leave shown.
|
|
local function sweep(frame, spared)
|
|
if not frame then
|
|
return
|
|
end
|
|
|
|
if frame.SetBackdrop then
|
|
frame:SetBackdrop(nil)
|
|
end
|
|
for _, region in ipairs({ frame:GetRegions() }) do
|
|
local objectType = region.GetObjectType and region:GetObjectType()
|
|
if objectType == "Texture" then
|
|
region:SetAlpha(0)
|
|
elseif objectType == "FontString" and region.Hide then
|
|
region:Hide()
|
|
end
|
|
end
|
|
if frame.GetChildren then
|
|
-- PetPaperDollFrameCompanionFrame holds the secure CompanionButtons, so the client
|
|
-- protects it: in combat an insecure Hide on it is blocked (ADDON_ACTION_BLOCKED)
|
|
local locked = InCombatLockdown()
|
|
for _, childFrame in ipairs({ frame:GetChildren() }) do
|
|
if not spared[childFrame] and childFrame.Hide
|
|
and not (locked and childFrame:IsProtected()) then
|
|
childFrame:Hide()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Hides the client pet screen, except the model and its carrier.
|
|
local function suppressClientScreen()
|
|
local screen = _G["PetPaperDollFrame"]
|
|
local carrier = _G["PetPaperDollFramePetFrame"]
|
|
local model = _G["PetModelFrame"]
|
|
if not screen then
|
|
return
|
|
end
|
|
|
|
sweep(screen, { [panel or false] = true, [carrier or false] = true,
|
|
[model or false] = true })
|
|
sweep(carrier, { [model or false] = true })
|
|
|
|
-- PetPaperDollFrame_SetTab hides it when another client tab is chosen; it holds the model.
|
|
if carrier then
|
|
carrier:Show()
|
|
end
|
|
end
|
|
|
|
-- Puts the pet model and its rotation arrows in the left pane.
|
|
local function updatePreview()
|
|
local model = _G["PetModelFrame"]
|
|
if not model or not panel then
|
|
return
|
|
end
|
|
|
|
suppressClientScreen()
|
|
|
|
model:ClearAllPoints()
|
|
model:SetPoint("TOPLEFT", panel, "TOPLEFT", 0, 0)
|
|
model:SetPoint("BOTTOMRIGHT", panel, "BOTTOMRIGHT", 0, 0)
|
|
|
|
-- The arrows are children of the model (PetPaperDollFrame.xml), so the sweep misses them;
|
|
-- they only need to move where the character's are.
|
|
local left = _G["PetModelFrameRotateLeftButton"]
|
|
local right = _G["PetModelFrameRotateRightButton"]
|
|
if left and right then
|
|
local half = (left:GetWidth() or 0) / 2 + ROTATION_GAP / 2
|
|
local level = (model:GetFrameLevel() or 0) + 2
|
|
|
|
left:ClearAllPoints()
|
|
left:SetPoint("TOP", panel, "TOP", -half, ROTATION_Y)
|
|
left:SetFrameLevel(level)
|
|
|
|
right:ClearAllPoints()
|
|
right:SetPoint("TOP", panel, "TOP", half, ROTATION_Y)
|
|
right:SetFrameLevel(level)
|
|
end
|
|
|
|
if hasPet() then
|
|
if model.SetUnit then
|
|
model:SetUnit("pet")
|
|
end
|
|
model:Show()
|
|
if left then left:Show() end
|
|
if right then right:Show() end
|
|
else
|
|
model:Hide()
|
|
if left then left:Hide() end
|
|
if right then right:Hide() end
|
|
end
|
|
end
|
|
ForeverUI.PetPreview = updatePreview
|
|
|
|
-- ---------------------------------------------------------- Build
|
|
|
|
-- Left pane content; host: the left pane frame.
|
|
-- Returns no root and the client frames it owns (see Panes.Register).
|
|
local function build(host)
|
|
local frame = _G["PetPaperDollFrame"]
|
|
if not frame or not host then
|
|
return nil, {}
|
|
end
|
|
|
|
-- Protected through its secure CompanionButtons: moved out of combat only (its XML
|
|
-- setAllPoints on CharacterFrame stays meanwhile; the model hangs on our panel)
|
|
if not InCombatLockdown() then
|
|
frame:ClearAllPoints()
|
|
frame:SetPoint("TOPLEFT", host, "TOPLEFT", 0, 0)
|
|
frame:SetPoint("BOTTOMRIGHT", host, "BOTTOMRIGHT", 0, 0)
|
|
end
|
|
|
|
if panel then
|
|
updatePreview()
|
|
updateDetail()
|
|
return nil, { frame }
|
|
end
|
|
|
|
panel = CreateFrame("Frame", "ForeverUIPetPane", frame)
|
|
panel:SetPoint("TOPLEFT", host, "TOPLEFT", 0, 0)
|
|
panel:SetPoint("BOTTOMRIGHT", host, "BOTTOMRIGHT", 0, 0)
|
|
local width = host:GetWidth() or 0
|
|
if width < 100 then
|
|
width = LEFT_PANE_W
|
|
end
|
|
panel:SetWidth(width)
|
|
local height = host:GetHeight() or 0
|
|
if height < 100 then
|
|
height = LEFT_PANE_H
|
|
end
|
|
panel:SetHeight(height)
|
|
|
|
updatePreview()
|
|
updateDetail()
|
|
return nil, { frame }
|
|
end
|
|
|
|
-- Right pane content; host: the right pane frame. Returns its root (see Panes.Register).
|
|
local function buildDetail(host)
|
|
if detail then
|
|
updateDetail()
|
|
return detail, {}
|
|
end
|
|
if not host then
|
|
return nil, {}
|
|
end
|
|
|
|
detail = CreateFrame("Frame", "ForeverUIPetStats", host)
|
|
detail:SetPoint("TOPLEFT", host, "TOPLEFT", 0, 0)
|
|
detail:SetPoint("BOTTOMRIGHT", host, "BOTTOMRIGHT", 0, 0)
|
|
local width = host:GetWidth() or 0
|
|
if width < 50 then
|
|
width = PANE_W
|
|
end
|
|
detail:SetWidth(width)
|
|
|
|
-- "Level X <name>", on the same background as the character's level line.
|
|
local background = detail:CreateTexture(nil, "BACKGROUND")
|
|
ForeverUI.SetAtlas(background, ATLAS_LEVEL, true)
|
|
|
|
local level = detail:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
|
|
level:SetHeight(LEVEL_H)
|
|
level:SetJustifyH("CENTER")
|
|
level:SetJustifyV("MIDDLE")
|
|
detail.level = level
|
|
|
|
background:SetPoint("TOP", level, "TOP", 0, 0)
|
|
background:SetPoint("BOTTOM", level, "BOTTOM", 0, 0)
|
|
background:SetPoint("LEFT", level, "LEFT", 0, 0)
|
|
background:SetPoint("RIGHT", level, "RIGHT", 0, 0)
|
|
|
|
updateDetail()
|
|
return detail, {}
|
|
end
|
|
|
|
ForeverUI.PetTab = { Build = build, BuildRight = buildDetail }
|
|
|
|
-- The client redraws its screen in PetPaperDollFrame_Update: re-apply both panes after it.
|
|
if hooksecurefunc and type(_G["PetPaperDollFrame_Update"]) == "function" then
|
|
hooksecurefunc("PetPaperDollFrame_Update", function()
|
|
updatePreview()
|
|
updateDetail()
|
|
end)
|
|
end
|
|
|
|
-- Mounts have no place on this tab: their sub-tab goes, and the pet's with it when no other is
|
|
-- left (the client hides a lone sub-tab). A mount page left selected goes back to the pet.
|
|
if hooksecurefunc and type(_G["PetPaperDollFrame_UpdateTabs"]) == "function" then
|
|
hooksecurefunc("PetPaperDollFrame_UpdateTabs", function()
|
|
local pet, critters, mounts = PetPaperDollFrameTab1, PetPaperDollFrameTab2, PetPaperDollFrameTab3
|
|
if not (pet and critters and mounts) then
|
|
return
|
|
end
|
|
mounts:Hide()
|
|
if not critters:IsShown() then
|
|
pet:Hide()
|
|
end
|
|
-- PetPaperDollFrame_SetTab shows and hides the protected companion frame
|
|
if PetPaperDollFrame.selectedTab == 3 and HasPetUI() and not InCombatLockdown() then
|
|
PetPaperDollFrame_SetTab(1)
|
|
end
|
|
end)
|
|
end
|
|
|
|
-- UNIT_PET: the pet changes; the other three: its numbers change.
|
|
local listener = CreateFrame("Frame")
|
|
listener:RegisterEvent("UNIT_PET")
|
|
listener:RegisterEvent("UNIT_STATS")
|
|
listener:RegisterEvent("UNIT_ATTACK_POWER")
|
|
listener:RegisterEvent("UNIT_RESISTANCES")
|
|
listener:SetScript("OnEvent", function(_, event, unit)
|
|
-- Only the player's pet: UNIT_PET names the owner, the other three the unit itself (they
|
|
-- fire for every group member, and each refresh reloads the model)
|
|
if unit ~= ((event == "UNIT_PET") and "player" or "pet") then
|
|
return
|
|
end
|
|
updatePreview()
|
|
updateDetail()
|
|
end)
|
|
|
|
-- Debug report: /fui pet.
|
|
function ForeverUI.PetDebug()
|
|
local say = function(text)
|
|
DEFAULT_CHAT_FRAME:AddMessage("|cff66ccffForeverUI|r " .. text)
|
|
end
|
|
|
|
say(string.format(L.PETTAB_DEBUG_PET,
|
|
tostring(HasPetUI and HasPetUI()),
|
|
tostring(UnitExists and UnitExists("pet")),
|
|
tostring(UnitLevel and UnitLevel("pet")),
|
|
tostring(UnitName and UnitName("pet")),
|
|
tostring(UnitCreatureFamily and UnitCreatureFamily("pet"))))
|
|
|
|
for _, data in ipairs(readGeneral()) do
|
|
DEFAULT_CHAT_FRAME:AddMessage(string.format(" %-18s %s",
|
|
data.name, tostring(data.value)))
|
|
end
|
|
for _, data in ipairs(readResistances()) do
|
|
DEFAULT_CHAT_FRAME:AddMessage(string.format(" %-18s %s",
|
|
data.name, tostring(data.value)))
|
|
end
|
|
|
|
local model = _G["PetModelFrame"]
|
|
say(string.format(L.PETTAB_DEBUG_PREVIEW,
|
|
tostring(model and model:IsShown()),
|
|
tostring(detail and detail:IsShown())))
|
|
end
|