From c29cd172bfbe6fd953fb82640380b7e1ed75503e Mon Sep 17 00:00:00 2001 From: MelekTaus Moribundus Date: Tue, 30 Jun 2026 20:05:09 +0200 Subject: [PATCH] Destroy pre-match objects on seek and block hunter wall-climb while hiding Destroy every GameObject tagged "PreMatch" locally when the match enters the Seek sub-phase (hooked in ApplyPhaseLocal, runs on every machine; the objects are plain scene geometry with no NetworkObject, so each client cleans up its own copies). Block hunters from starting a wall- or ceiling-climb during the Hide sub-phase via a WallClimbBlockedNow() gate in UpdateWallClimbState; an in-progress climb is exited cleanly. Co-Authored-By: Claude Opus 4.8 --- Assets/Scripts/MatchManager.cs | 20 +++++++++++++++++++- Assets/Scripts/PlayerCharacterController.cs | 13 ++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/Assets/Scripts/MatchManager.cs b/Assets/Scripts/MatchManager.cs index 2d64cb2..15ab957 100644 --- a/Assets/Scripts/MatchManager.cs +++ b/Assets/Scripts/MatchManager.cs @@ -162,6 +162,8 @@ public class MatchManager : MonoBehaviour private float _phaseDuration; private MatchMode _localMode = MatchMode.Classic; // mirror of the server's matchMode, carried in the phase message + private const string PreMatchTag = "PreMatch"; // scene props destroyed when seek starts + /// True while a match sub-phase (Hide/Seek/Resolution) is running on this machine. public bool MatchActive => _subPhase != MatchSubPhase.None; /// This machine's current sub-phase (mirror of the server's broadcast). @@ -566,7 +568,23 @@ public class MatchManager : MonoBehaviour _phaseDuration = duration; _phaseEndTime = Time.time + duration; _localMode = mode; - if (changed) SubPhaseChanged?.Invoke(phase); + if (changed) + { + SubPhaseChanged?.Invoke(phase); + // Props staged only for the hide phase are gone the instant seek begins. + if (phase == MatchSubPhase.Seek) DestroyPreMatchObjects(); + } + } + + // Scene props tagged "PreMatch" exist only while players hide; the moment seek starts they must be gone + // on every machine. ApplyPhaseLocal runs on all of them (server directly, clients via the phase message), + // so each destroys its own local copies -- these are plain scene geometry, not NetworkObjects, so there + // is nothing to replicate. + private static void DestroyPreMatchObjects() + { + var props = GameObject.FindGameObjectsWithTag(PreMatchTag); + for (int i = 0; i < props.Length; i++) + Destroy(props[i]); } // Sends a sub-phase (byte) + its remaining duration (float) + the match mode (byte) to one client, or to diff --git a/Assets/Scripts/PlayerCharacterController.cs b/Assets/Scripts/PlayerCharacterController.cs index e846861..5b2da43 100644 --- a/Assets/Scripts/PlayerCharacterController.cs +++ b/Assets/Scripts/PlayerCharacterController.cs @@ -226,6 +226,7 @@ public class PlayerCharacterController : MonoBehaviour private const float RunningSpeedThresholdSqr = 0.04f; // (0.2 m/s)^2 private CharacterController _controller; + private NetworkPlayerSetup _setup; // sibling role source; null on non-networked test instances private float _verticalVelocity; private Vector3 _horizontalVelocity; // smoothed horizontal move velocity (accel/decel ramp) private bool _sprinting; // sprint key state this frame (exposed via IsSprinting) @@ -282,6 +283,7 @@ public class PlayerCharacterController : MonoBehaviour void Awake() { _controller = GetComponent(); + _setup = GetComponent(); BaseCapsuleCenterY = _controller.center.y; BaseCapsuleRadius = _controller.radius; BaseCapsuleHeight = _controller.height; @@ -520,12 +522,21 @@ public class PlayerCharacterController : MonoBehaviour /// (not on mere contact). Leaving while climbing is handled in /// . /// + // Hunters may not grab a wall during the hide phase (they wait it out); every other role/phase climbs + // normally. The controller only runs for the local owner, so _setup is this avatar's own role. + private bool WallClimbBlockedNow() + { + var match = MatchManager.Instance; + return match && match.LocalSubPhase == MatchManager.MatchSubPhase.Hide + && _setup && _setup.Role == PlayerRole.Hunter; + } + private void UpdateWallClimbState(Vector3 moveDir) { if (_reentryCooldown > 0f) _reentryCooldown -= Time.deltaTime; - if (!enableWallClimb) + if (!enableWallClimb || WallClimbBlockedNow()) { if (_isWallClimbing) ExitWallClimb();