player/PlayerCameraAim.cs

Component attached to a player that, on start, orients the local owning player's camera to point at a global CountdownDrone so players see the pre-game countdown. It waits up to MaxWaitSeconds for the drone to exist, then sets the PlayerController's EyeAngles to look at the drone.

using System.Threading.Tasks;
using Sandbox;

/// <summary>
/// On spawn, points the local player's camera at the countdown drone so players
/// can see the pre-game countdown immediately. Runs only on the owning client —
/// PlayerController.EyeAngles is owner-authoritative.
/// </summary>
public sealed class PlayerCameraAim : Component
{
    [Property] public PlayerController PlayerController { get; set; }

    // Drone spawn message can race the player spawn on remote clients; poll briefly.
    [Property] public float MaxWaitSeconds { get; set; } = 1f;

    protected override void OnStart()
    {
        _ = AimAtDroneAsync();
    }

    private async Task AimAtDroneAsync()
    {
        if ( GameObject.Network.Owner != Connection.Local ) return;

        PlayerController pc = PlayerController ?? GetComponent<PlayerController>();
        if ( pc == null ) return;

        TimeUntil giveUp = MaxWaitSeconds;
        while ( CountdownDrone.Current == null && (float)giveUp > 0f )
        {
            await Task.DelayRealtimeSeconds( 0.05f );
            if ( !this.IsValid() ) return;
        }

        CountdownDrone drone = CountdownDrone.Current;
        if ( drone == null ) return;

        Vector3 targetPosition = drone.WorldPosition + Vector3.Down * 150f;
        Vector3 dir = (targetPosition - pc.WorldPosition).Normal;
        if ( dir.LengthSquared < 0.0001f ) return;

        pc.EyeAngles = Rotation.LookAt( dir, Vector3.Up ).Angles();
    }
}