diff --git a/Assets/Scripts/MatchManager.cs b/Assets/Scripts/MatchManager.cs
index 7bc4e83..09a5730 100644
--- a/Assets/Scripts/MatchManager.cs
+++ b/Assets/Scripts/MatchManager.cs
@@ -29,8 +29,9 @@ using UnityEngine.SceneManagement;
///
/// Flow: server start -> additive sync mode set -> Lobby loaded additively -> Player spawned for every
/// connected peer (full mode set, including First Person). Late joiners spawn on connect into the
-/// already-loaded scene. The host walks into the Start Match trigger -> : one
-/// random connected client is rolled Hunter, the rest Hider; the Lobby is unloaded and Test Map loaded
+/// already-loaded scene. The host walks into the Start Match trigger -> : a
+/// configurable number of random connected clients are rolled Hunter (,
+/// default 1), the rest Hider; the Lobby is unloaded and Test Map loaded
/// (strictly serialized - an NGO constraint, only one scene op in flight); then every player object is
/// despawned and respawned as its role's prefab variant.
///
@@ -81,6 +82,12 @@ public class MatchManager : MonoBehaviour
"free-cam rig is a separate scene object. Must be a registered network prefab.")]
[SerializeField] private GameObject spectatorPrefab;
+ [Header("Roles")]
+ [Tooltip("How many randomly chosen players become Hunters at match start; everyone else is a Hider. " +
+ "0 = all Hiders; a value at or above the player count makes everyone a Hunter. Default 1.")]
+ [Min(0)]
+ [SerializeField] private int hunterCount = 1;
+
[Header("Spawn")]
[Tooltip("World position lobby avatars are spawned at (raised a touch above the floor so gravity " +
"settles them). Per-client ring fan-out is applied on top (NetworkPlayerSetup.PlaceOnSpawnRing).")]
@@ -351,13 +358,26 @@ public class MatchManager : MonoBehaviour
if (ids.Count == 0) return;
Phase = MatchPhase.Transitioning;
- ulong hunterId = ids[_roleRng.Next(ids.Count)];
- Debug.Log($"[MatchManager] Starting match. Hunter = client {hunterId}, {ids.Count} player(s).", this);
- StartCoroutine(MatchSequence(hunterId));
+ // Roll N distinct random Hunters (clamped to the player count); everyone else is a Hider.
+ // hunterCount 0 -> all Hiders, >= player count -> all Hunters. Partial Fisher-Yates: swap a
+ // random not-yet-picked id into each leading slot, then take that slot as a Hunter.
+ int wanted = Mathf.Clamp(hunterCount, 0, ids.Count);
+ var hunterIds = new HashSet();
+ for (int i = 0; i < wanted; i++)
+ {
+ int j = i + _roleRng.Next(ids.Count - i);
+ (ids[i], ids[j]) = (ids[j], ids[i]);
+ hunterIds.Add(ids[i]);
+ }
+
+ Debug.Log($"[MatchManager] Starting match. {hunterIds.Count} hunter(s), " +
+ $"{ids.Count - hunterIds.Count} hider(s), {ids.Count} player(s).", this);
+
+ StartCoroutine(MatchSequence(hunterIds));
}
- private IEnumerator MatchSequence(ulong hunterId)
+ private IEnumerator MatchSequence(HashSet hunterIds)
{
var sm = _net.SceneManager;
@@ -371,7 +391,7 @@ public class MatchManager : MonoBehaviour
SceneEventType.LoadEventCompleted, matchSceneName);
// 3. Respawn every player as its rolled role's prefab variant.
- RespawnAll(id => (id == hunterId) ? hunterPrefab : hiderPrefab, matchSpawnCenter);
+ RespawnAll(id => hunterIds.Contains(id) ? hunterPrefab : hiderPrefab, matchSpawnCenter);
Phase = MatchPhase.InMatch;
Debug.Log("[MatchManager] Match started.", this);