Round manager component for an arena game. It tracks round state (waiting, countdown, in-game, round end), transitions based on timers and player count, logs state changes, respawns players at a spawn point, and disables eliminated players.
using Sandbox;
using System.Linq;
public enum ArenaRoundState
{
WaitingForPlayers,
Countdown,
InGame,
RoundEnd
}
public sealed class ArenaRoundManager : Component
{
[Property] public float CountdownDuration { get; set; } = 5.0f;
[Property] public float RoundDuration { get; set; } = 60.0f;
[Property] public float EndPauseDuration { get; set; } = 4.0f;
public ArenaRoundState CurrentState { get; private set; } = ArenaRoundState.WaitingForPlayers;
public TimeUntil StateTimer { get; private set; }
private SpawnPoint _spawnPoint;
protected override void OnStart()
{
_spawnPoint = Scene.GetAllComponents<SpawnPoint>().FirstOrDefault();
SetState( ArenaRoundState.WaitingForPlayers );
}
protected override void OnUpdate()
{
switch ( CurrentState )
{
case ArenaRoundState.WaitingForPlayers:
if ( Connection.All.Count >= 1 )
{
SetState( ArenaRoundState.Countdown );
}
break;
case ArenaRoundState.Countdown:
if ( StateTimer )
{
SetState( ArenaRoundState.InGame );
}
break;
case ArenaRoundState.InGame:
if ( StateTimer )
{
SetState( ArenaRoundState.RoundEnd );
}
break;
case ArenaRoundState.RoundEnd:
if ( StateTimer )
{
SetState( ArenaRoundState.WaitingForPlayers );
}
break;
}
}
private void SetState( ArenaRoundState newState )
{
CurrentState = newState;
switch ( newState )
{
case ArenaRoundState.WaitingForPlayers:
Log.Info( "[ArenaRoundManager] Ожидание игроков..." );
break;
case ArenaRoundState.Countdown:
StateTimer = CountdownDuration;
Log.Info( $"[ArenaRoundManager] Отсчёт! До старта: {CountdownDuration} сек." );
break;
case ArenaRoundState.InGame:
RespawnPlayers();
StateTimer = RoundDuration;
Log.Info( "[ArenaRoundManager] РАУНД НАЧАЛСЯ!" );
break;
case ArenaRoundState.RoundEnd:
StateTimer = EndPauseDuration;
Log.Info( "[ArenaRoundManager] Раунд окончен!" );
break;
}
}
public void OnPlayerEliminated( GameObject player )
{
if ( CurrentState != ArenaRoundState.InGame || !player.IsValid() ) return;
player.Enabled = false;
int remainingPlayers = Scene.GetAllComponents<PlayerHealth>()
.Count( health => health.IsValid() && health.GameObject.IsValid() &&
health.GameObject.Enabled && !health.IsDead && !health.IsDowned );
if ( remainingPlayers <= 1 )
{
SetState( ArenaRoundState.RoundEnd );
}
}
private void RespawnPlayers()
{
Vector3 spawnPosition = _spawnPoint.IsValid() ? _spawnPoint.WorldPosition : new Vector3( 0, 0, 250 );
foreach ( var health in Scene.GetAllComponents<PlayerHealth>() )
{
if ( !health.IsValid() || !health.GameObject.IsValid() ) continue;
health.GameObject.Enabled = true;
health.GameObject.WorldPosition = spawnPosition;
health.Respawn();
}
}
}