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();