Add conversion statues shown in resolution phase

In Conversion mode, capture a snapshot (pose index + paint) of each hider
at conversion time and materialize a frozen, painted statue at the found
spot for the resolution phase, independent of the now-hunter avatar.

- MatchStatue.cs (new): server-init pose/paint, network-driven pose then
  freeze, reveal-gate (hidden until paint applied), kills inherited sim.
- PlayerPaintSync: _isStatue (sibling auto-detect) suppresses the three
  auto-behaviours so MatchStatue drives the paint flow deterministically.
- MatchManager: statuePrefab; capture in ConvertToHunter; spawn on
  resolution enter; despawn + record clear on EndMatch/teardown/start.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 4.8 committed 2026-07-06 09:33:15 +02:00
1 parent 0a1e286a34
commit 0f108fce47
4 files changed
+232 -5

No files matched your search

+16 -5
View File
@@ -138,7 +138,18 @@ public class PlayerPaintSync : NetworkBehaviour
// spawn), applied in LateUpdate once this fresh painter is ready. Null = nothing to carry.
private byte[] _pendingCarry;
private void Awake() => _painter = GetComponentInChildren<MeshPainter>(true);
// True when this receiver sits on a MatchStatue: a statue drives its painting explicitly (the server
// loads the captured snapshot then broadcasts it), so the owner-carry take, the client auto snapshot-
// request and the no-carry blank publish are all suppressed to keep them from racing that push.
private bool _isStatue;
private void Awake()
{
_painter = GetComponentInChildren<MeshPainter>(true);
// Detected from the sibling component so no inspector flag can be forgotten on the prefab.
var statue = GetComponentInChildren<MatchStatue>(true);
_isStatue = statue;
}
public override void OnNetworkSpawn()
{
@@ -151,7 +162,7 @@ public class PlayerPaintSync : NetworkBehaviour
_painter.OnHistoryCheckpoint += OnLocalCheckpoint;
_painter.OnHistoryRestore += OnLocalRestore;
if (MatchManager.Instance) _pendingCarry = MatchManager.Instance.TakeCarriedPaint();
if (MatchManager.Instance && !_isStatue) _pendingCarry = MatchManager.Instance.TakeCarriedPaint();
}
else
{
@@ -178,8 +189,8 @@ public class PlayerPaintSync : NetworkBehaviour
_heightApplyPending = true;
// The host's replica already holds the full paint via the live stream; only a genuine
// late joiner needs the baseline pulled over.
if (!IsServer)
// late joiner needs the baseline pulled over. A statue never pulls - the server pushes it.
if (!IsServer && !_isStatue)
{
_awaitingSnapshot = true;
if (logSync) LogSync("late joiner: requesting baseline snapshot");
@@ -293,7 +304,7 @@ public class PlayerPaintSync : NetworkBehaviour
// avatar must still hand every peer an explicit (blank) snapshot, otherwise its body stays hidden
// forever behind the receiver-side reveal gate. Carry/manual-load already publish (and set the
// cutoff), so this only fires for the no-carry case.
if (_publishedCutoff.Value < 0 && _pendingCarry == null && _painter && _painter.IsPaintReady)
if (_publishedCutoff.Value < 0 && _pendingCarry == null && !_isStatue && _painter && _painter.IsPaintReady)
BroadcastLoadedPaint();
// Delta-only publish: the NetworkVariable dirties only on an actual change.