diff --git a/Assets/Scripts/Network/MatchManager.cs b/Assets/Scripts/Network/MatchManager.cs index ca93d89..8c00189 100644 --- a/Assets/Scripts/Network/MatchManager.cs +++ b/Assets/Scripts/Network/MatchManager.cs @@ -321,11 +321,11 @@ public class MatchManager : MonoBehaviour private int _loadGenerationClient; private Coroutine _clientLoadPoll; - // Server-only. Conversion statues: a snapshot of each converted hider (spot / stance / painting) captured - // on the conversion, materialized as frozen statue objects only at the start of Resolution and despawned - // when the match ends. Only populated in Conversion mode (ConvertToHunter is the sole capture site). + // Server-only. Statues: a snapshot of each hit hider (spot / stance / painting) captured on the hit + // (Conversion convert / Classic found), materialized as frozen statue objects only at the start of + // Resolution and despawned when the match ends. private class StatueRecord { public Vector3 pos; public Quaternion rot; public int poseIndex; public byte[] paint; } - private readonly List _conversionRecords = new List(); + private readonly List _statueRecords = new List(); private readonly List _statues = new List(); // Server-only. Per-participant outcome records for the running match, keyed by clientId. Created on match @@ -496,7 +496,7 @@ public class MatchManager : MonoBehaviour _clientLoadPoll = null; // Teardown unloads the gameplay scenes (destroys the statue objects); just drop our references. _statues.Clear(); - _conversionRecords.Clear(); + _statueRecords.Clear(); _matchRecords.Clear(); if (_net) UnhookSessionCallbacks(); @@ -717,7 +717,7 @@ public class MatchManager : MonoBehaviour private IEnumerator MatchSequence(HashSet hunterIds) { - _conversionRecords.Clear(); // fresh match inherits no stale statue captures (EndMatch also clears) + _statueRecords.Clear(); // fresh match inherits no stale statue captures (EndMatch also clears) // One outcome record per participant. Survived is the pending-hider default (flips on a hit / // disconnect); names are resolved later - the freshly respawned avatars' name NetworkVariables @@ -774,14 +774,14 @@ public class MatchManager : MonoBehaviour // (which was already live during the match). FinalizeMatchResults(); - // Conversion mode: materialize a frozen statue of every converted hider at its capture spot. The last - // hider is often still converting (its statue's painting hasn't replicated to every client yet), so + // Materialize a frozen statue of every hit hider (converted / found) at its capture spot. The last + // hider is often still resolving (its statue's painting hasn't replicated to every client yet), so // hold the Resolution countdown - frozen at 0:00, exactly like the pre-match loading barrier - until // every client has each statue spawned & painted. Then release the timer. No statue prefab / no - // conversions / Classic -> no hold, the countdown starts immediately (unchanged behaviour). - bool holdForStatues = statuePrefab && _conversionRecords.Count > 0; + // hits -> no hold, the countdown starts immediately (unchanged behaviour). + bool holdForStatues = statuePrefab && _statueRecords.Count > 0; SetPhaseServer(MatchSubPhase.Resolution, holdForStatues ? 0f : resolveDuration); - SpawnConversionStatues(); + SpawnStatues(); if (holdForStatues) { yield return RunStatueBarrier(); @@ -1308,6 +1308,10 @@ public class MatchManager : MonoBehaviour ConvertToHunter(hiderClientId, c.PlayerObject); else { + // Classic: snapshot the spot/stance/painting too, so Resolution shows a statue of every + // found hider (the found avatar itself stays hidden - its owner is free-cam spectating). + CaptureStatue(c.PlayerObject.transform.position, c.PlayerObject.transform.rotation, + c.PlayerObject); var setup = c.PlayerObject.GetComponent(); if (setup) setup.ServerMarkFound(); } @@ -1331,8 +1335,8 @@ public class MatchManager : MonoBehaviour Vector3 pos = current.transform.position; Quaternion rot = current.transform.rotation; // Snapshot the hider's spot / stance / painting NOW, before it despawns - a frozen statue is - // materialized from this at the start of Resolution (see SpawnConversionStatues). - CaptureConversionStatue(pos, rot, current); + // materialized from this at the start of Resolution (see SpawnStatues). + CaptureStatue(pos, rot, current); current.Despawn(true); var go = Instantiate(hunterPrefab, pos, rot); @@ -1343,11 +1347,11 @@ public class MatchManager : MonoBehaviour go.GetComponent().SpawnAsPlayerObject(clientId); } - // Server-only. Captures a converting hider's spot / stance / painting so a frozen statue can be - // materialized from it at Resolution. Skipped entirely when no statue prefab is assigned. The painting - // is captured from the SERVER's own replica (the host holds the full paint via the live stream); a - // capture miss stores a null snapshot -> the statue simply stays on the blank seed look. - private void CaptureConversionStatue(Vector3 pos, Quaternion rot, NetworkObject hider) + // Server-only. Captures a hit hider's spot / stance / painting (Conversion convert AND Classic found) so + // a frozen statue can be materialized from it at Resolution. Skipped entirely when no statue prefab is + // assigned. The painting is captured from the SERVER's own replica (the host holds the full paint via the + // live stream); a capture miss stores a null snapshot -> the statue simply stays on the blank seed look. + private void CaptureStatue(Vector3 pos, Quaternion rot, NetworkObject hider) { if (!statuePrefab) return; @@ -1359,17 +1363,17 @@ public class MatchManager : MonoBehaviour var painter = hider.GetComponentInChildren(true); if (painter) painter.TryCaptureSnapshot(out paint); - _conversionRecords.Add(new StatueRecord { pos = pos, rot = rot, poseIndex = poseIndex, paint = paint }); + _statueRecords.Add(new StatueRecord { pos = pos, rot = rot, poseIndex = poseIndex, paint = paint }); } // Server-only. Spawns one frozen statue per captured conversion at its recorded pos/rot, handing each the // stance + painting snapshot before it spawns (server-owned, non-player). Called once at Resolution start; - // the records are consumed here. No-op with no prefab / no records (Classic never records any). - private void SpawnConversionStatues() + // the records are consumed here. No-op with no prefab / no records (no hider was hit). + private void SpawnStatues() { - if (!statuePrefab || _conversionRecords.Count == 0) return; + if (!statuePrefab || _statueRecords.Count == 0) return; - foreach (var rec in _conversionRecords) + foreach (var rec in _statueRecords) { var go = Instantiate(statuePrefab, rec.pos, rec.rot); var statue = go.GetComponent(); @@ -1386,10 +1390,10 @@ public class MatchManager : MonoBehaviour Destroy(go); } } - _conversionRecords.Clear(); + _statueRecords.Clear(); } - private void DespawnConversionStatues() + private void DespawnStatues() { for (int i = 0; i < _statues.Count; i++) { @@ -1575,8 +1579,8 @@ public class MatchManager : MonoBehaviour // the replicated sub-phase so every client drops its match HUD/visibility before the scene swaps. if (_phaseRoutine != null) { StopCoroutine(_phaseRoutine); _phaseRoutine = null; } _pendingHiders.Clear(); - DespawnConversionStatues(); // drop any Resolution statues before the map unloads - _conversionRecords.Clear(); // and any captures from a match ended before Resolution + DespawnStatues(); // drop any Resolution statues before the map unloads + _statueRecords.Clear(); // and any captures from a match ended before Resolution _matchRecords.Clear(); // and the outcome records (the None broadcast clears every client's mirror) SetPhaseServer(MatchSubPhase.None, 0f); diff --git a/Assets/Scripts/Network/MatchStatue.cs b/Assets/Scripts/Network/MatchStatue.cs index a32b5c9..c21b842 100644 --- a/Assets/Scripts/Network/MatchStatue.cs +++ b/Assets/Scripts/Network/MatchStatue.cs @@ -5,8 +5,8 @@ using Chameleon.Components; /// /// A frozen, painted "statue" of a hider at the exact spot / stance / painting it held the moment it was -/// CONVERTED into a hunter (Conversion mode only); captures those on the -/// conversion and spawns one statue per converted hider at Resolution start. Purely presentational (no +/// hit (Conversion: converted into a hunter; Classic: marked found); captures +/// those on the hit and spawns one statue per hit hider at Resolution start. Purely presentational (no /// controller / collision / role) and a dedicated prefab, NOT a avatar - /// so it never pollutes the scoreboard, per-client visibility matrix or resolution X-ray reveal. /// @@ -24,7 +24,7 @@ public class MatchStatue : NetworkBehaviour [Min(0f)] [SerializeField] private float poseSettle = 0.35f; - // Server-only handoff from MatchManager.SpawnConversionStatues, consumed in OnNetworkSpawn / the first + // Server-only handoff from MatchManager.SpawnStatues, consumed in OnNetworkSpawn / the first // ready frame. The paint may be null (capture failed) -> the statue stays on the blank seed look. private byte[] _serverPaint; private int _serverPoseIndex;