@@ -9,6 +9,11 @@ local addonName, DF = ...
99DF .petFrames = DF .petFrames or {}
1010DF .partyPetFrames = DF .partyPetFrames or {}
1111DF .raidPetFrames = DF .raidPetFrames or {}
12+ -- Arena pets are their own set, mirroring the separate arenaHeader. They CANNOT
13+ -- share DF.raidPetFrames: those are anchored to the raid header's children, which
14+ -- is a different (hidden) frame set in arena, and they carry isRaidFrame = true,
15+ -- which would resolve their settings to the RAID config. See UpdateAllRaidPetFrames.
16+ DF .arenaPetFrames = DF .arenaPetFrames or {}
1217
1318-- Storage for test mode pet frames (non-secure, independent of live frames)
1419DF .testPetFrames = DF .testPetFrames or {} -- [0]=player pet, [1-4]=party pets
@@ -817,6 +822,14 @@ function DF:LightweightUpdatePetFrames()
817822 DF :PositionPetFrame (DF .raidPetFrames [i ])
818823 end
819824 end
825+ -- Arena pets are a separate set (see UpdateAllRaidPetFrames); without this
826+ -- they would keep their creation-time look and ignore every settings change.
827+ for i = 1 , 5 do
828+ if DF .arenaPetFrames [i ] then
829+ DF :ApplyPetFrameStyle (DF .arenaPetFrames [i ])
830+ DF :PositionPetFrame (DF .arenaPetFrames [i ])
831+ end
832+ end
820833 end
821834end
822835
@@ -914,6 +927,18 @@ function DF:UpdatePetGroupLayout()
914927 table.insert (petFrames , DF .testPetFrames [i ])
915928 end
916929 end
930+ elseif DF .IsInArena and DF :IsInArena () then
931+ -- Arena: the team is raid-kind, so the pets are raidpet* on the arena
932+ -- header's frames — partypet* does not exist here and collecting it would
933+ -- leave the group empty. The container itself is fine: the arena header is
934+ -- parented to DF.partyContainer, which this layout already anchors against.
935+ -- (Grouped arena pets read the party config for the same reason the
936+ -- attached ones do — see UpdateArenaPetFrames.)
937+ for i = 1 , 5 do
938+ if DF .arenaPetFrames [i ] and UnitExists (" raidpet" .. i ) then
939+ table.insert (petFrames , DF .arenaPetFrames [i ])
940+ end
941+ end
917942 else
918943 -- Normal mode - check which pets actually exist
919944 if DF .petFrames .player and UnitExists (" pet" ) then
@@ -1403,6 +1428,10 @@ function DF:UpdateAllPetFrames(force)
14031428 -- Mirrors the raid-test-mode guard above, for real raids. Without this the party pet frames can
14041429 -- linger as an orphaned overlay over the raid frames, since the party frames they anchor to are
14051430 -- hidden once raid frames take over.
1431+ -- ⚠ ARENA lands here too — IsInRaid() is true and the units are raid-kind. Handing over is
1432+ -- still right (partypet* does not exist in arena), but the receiver is the ARENA track in
1433+ -- UpdateAllRaidPetFrames, not the raid one; the raid header is hidden in arena. Before that
1434+ -- existed, this early return was half of why pets never appeared in 2v2 / 3v3 / shuffle.
14061435 if not DF .testMode and IsInRaid () then
14071436 DF :Debug (" PET" , " UpdateAllPetFrames: hiding party pets (live raid active)" )
14081437 if DF .petFrames .player then DF :SetPetFrameVisible (DF .petFrames .player , false ) end
@@ -1494,12 +1523,99 @@ end
14941523
14951524local lastRaidPetUpdateTime = 0
14961525
1526+ -- Hide every arena pet (leaving arena, or pets switched off).
1527+ local function HideArenaPetFrames ()
1528+ for i = 1 , 5 do
1529+ if DF .arenaPetFrames [i ] then DF :SetPetFrameVisible (DF .arenaPetFrames [i ], false ) end
1530+ end
1531+ end
1532+
1533+ -- ARENA PETS. Arena is the odd one out: the group is RAID-kind (IsInRaid() is true,
1534+ -- units are raid1-5), so the party pet track hides itself and hands over to the raid
1535+ -- track — but the raid track walks the raid header, which is hidden in arena, while
1536+ -- DF shows its own arenaHeader. Nothing walked the arena header, so pets simply never
1537+ -- appeared in 2v2 / 3v3 / shuffle, with no error to show for it.
1538+ --
1539+ -- Settings come from the PARTY config even though the units are raidpet*: an arena
1540+ -- team reads as a party to a player, and that is where they will have configured it.
1541+ -- That is why the frames are created with isRaid = false — every downstream path
1542+ -- resolves through DF:GetFrameDB(frame), which keys off frame.isRaidFrame, so this
1543+ -- one flag routes size, layout and petEnabled to the party settings for free.
1544+ local function UpdateArenaPetFrames ()
1545+ local db = DF :GetDB () -- party config, deliberately (see above)
1546+
1547+ if not db .petEnabled then
1548+ HideArenaPetFrames ()
1549+ -- The PARTY container, not the raid one: grouped arena pets are laid out by
1550+ -- UpdatePetGroupLayout, which owns DF.petGroupContainer and re-parents the
1551+ -- pet buttons onto it. Hiding raidPetGroupContainer left the real container
1552+ -- shown.
1553+ DF :HidePetGroupContainer (DF .petGroupContainer )
1554+ return
1555+ end
1556+
1557+ -- Everything below this point touches protected state: CreatePetFrame builds a
1558+ -- SecureUnitButtonTemplate and calls SetSize/SetAttribute, and PositionPetFrame
1559+ -- does ClearAllPoints/SetParent/SetPoint. The raid track gets away without a
1560+ -- guard because you only reach it forming a group, out of combat. Arena does
1561+ -- not: a pet summoned or resurrected mid-match, a Solo Shuffle round
1562+ -- transition, or Core's PLAYER_REGEN_DISABLED handler ending test mode all
1563+ -- land here with the lockdown up.
1564+ --
1565+ -- Defer rather than drop, so the pets appear the moment combat ends instead of
1566+ -- waiting for whatever roster event happens to come next.
1567+ if InCombatLockdown () then
1568+ DF .pendingArenaPetUpdate = true
1569+ return
1570+ end
1571+ DF .pendingArenaPetUpdate = nil
1572+
1573+ -- Deferred creation, same shape as the raid track. The arena header's children
1574+ -- are the owners; raidpet<N> matches its raid<N> unit tokens.
1575+ local idx = 0
1576+ if DF .IterateArenaFrames then
1577+ DF :IterateArenaFrames (function (frame )
1578+ idx = idx + 1
1579+ if frame and not DF .arenaPetFrames [idx ] then
1580+ DF .arenaPetFrames [idx ] = DF :CreatePetFrame (" raidpet" .. idx , frame , false )
1581+ end
1582+ end )
1583+ end
1584+
1585+ for i = 1 , 5 do
1586+ if DF .arenaPetFrames [i ] then
1587+ DF :UpdatePetFrame (DF .arenaPetFrames [i ])
1588+ DF :PositionPetFrame (DF .arenaPetFrames [i ])
1589+ end
1590+ end
1591+
1592+ -- GROUPED mode positions from the layout pass, not per-frame (PositionPetFrame
1593+ -- returns early for it). The party layout is the right one — same config — and
1594+ -- it collects arenaPetFrames when in arena.
1595+ if db .petGroupMode == " GROUPED" then
1596+ DF :UpdatePetGroupLayout ()
1597+ end
1598+ end
1599+
14971600function DF :UpdateAllRaidPetFrames (force )
14981601 -- Throttle: skip if already ran this frame (multiple callers during startup/updates)
14991602 local now = GetTime ()
15001603 if not force and now == lastRaidPetUpdateTime then return end
15011604 lastRaidPetUpdateTime = now
15021605
1606+ -- Arena runs its own track (party settings, arena header) and never the raid one:
1607+ -- the raid header is hidden here, so its pets would anchor to invisible frames.
1608+ if not (DF .testMode or DF .raidTestMode ) and DF .IsInArena and DF :IsInArena () then
1609+ for i = 1 , 40 do
1610+ if DF .raidPetFrames [i ] then DF :SetPetFrameVisible (DF .raidPetFrames [i ], false ) end
1611+ end
1612+ UpdateArenaPetFrames ()
1613+ return
1614+ end
1615+ -- Left arena (or never in one): make sure no arena pet is left hanging over the
1616+ -- party/raid frames. Cheap — the table is empty until arena has been entered once.
1617+ HideArenaPetFrames ()
1618+
15031619 local db = DF :GetRaidDB ()
15041620
15051621 -- Hide raid pet frames if in party test mode (not raid test mode)
@@ -1604,6 +1720,15 @@ end
16041720-- PET EVENT HANDLING
16051721-- ============================================================
16061722
1723+ -- raid<N> / raidpet<N> tokens are used by BOTH the raid frames and the arena team,
1724+ -- so the token alone does not say which pet set owns the index. A pet summoned or
1725+ -- resurrected mid-match fires these; without the arena case the event would look up
1726+ -- the raid set, find nothing, and the frame would sit stale until the next full pass.
1727+ local function PetSetForRaidIndex ()
1728+ if DF .IsInArena and DF :IsInArena () then return DF .arenaPetFrames end
1729+ return DF .raidPetFrames
1730+ end
1731+
16071732function DF :OnPetChanged (unit )
16081733 -- Determine which pet frame to update based on unit
16091734 if unit == " player" or unit == " pet" then
@@ -1622,13 +1747,15 @@ function DF:OnPetChanged(unit)
16221747 end
16231748 elseif unit :match (" ^raid%d+$" ) then
16241749 local index = tonumber (unit :match (" raid(%d+)" ))
1625- if index and DF .raidPetFrames [index ] then
1626- DF :UpdatePetFrame (DF .raidPetFrames [index ])
1750+ local set = PetSetForRaidIndex ()
1751+ if index and set [index ] then
1752+ DF :UpdatePetFrame (set [index ])
16271753 end
16281754 elseif unit :match (" ^raidpet%d+$" ) then
16291755 local index = tonumber (unit :match (" raidpet(%d+)" ))
1630- if index and DF .raidPetFrames [index ] then
1631- DF :UpdatePetFrame (DF .raidPetFrames [index ])
1756+ local set = PetSetForRaidIndex ()
1757+ if index and set [index ] then
1758+ DF :UpdatePetFrame (set [index ])
16321759 end
16331760 end
16341761end
0 commit comments