ArenaMap.cs
using Sandbox;
using System;

namespace PaintballBaddies;

/// <summary>Map author settings. Reuses native SpawnPoint components for all six seats.</summary>
public sealed class ArenaMap : Component
{
    [Property] public string Title { get; set; } = "Community Arena";
    [Property] public bool ShowPracticeTargets { get; set; }

    public static ArenaMap Find(Scene scene) => scene.GetAllComponents<ArenaMap>().FirstOrDefault();
    public static string DisplayTitle(Scene scene) => Find(scene)?.Title ?? "Neon Foundry";

    // Use the same ordered six native markers for solo opponents and network seats.
    // Partial sets are rejected as a whole so a map never mixes two coordinate systems.
    public static SpawnPoint[] Spawns(Scene scene)
    {
        var all=scene.GetAllComponents<SpawnPoint>().Where(x=>x.GameObject.Active)
            .OrderBy(x=>x.GameObject.Name,StringComparer.Ordinal).ToArray();
        // Workshop maps can contain their own deathmatch spawns. Our six
        // authored markers form a complete overlay without changing the map.
        var authored=all.Where(x=>x.GameObject.Name.StartsWith("PB Spawn ")).ToArray();
        return authored.Length==6 ? authored : all;
    }
    public static Vector3[] PatrolPoints(Scene scene)=>scene.GetAllComponents<PopRiotSpawnPoint>()
        .Where(x=>x.GameObject.Active).Select(x=>x.WorldPosition).ToArray();

    public static Vector3 PlayerSpawn(Scene scene, int seat, Vector3 legacyPosition)
    {
        if(MultiplayerSession.Find(scene)?.StartFor(seat) is {} start)return start.Position;
        var points = Spawns(scene);
        return points.Length == 6 && seat >= 0 && seat < 6 ? points[seat].WorldPosition : legacyPosition;
    }
    public static Rotation SpawnRotation(Scene scene,int seat,Rotation fallback)
    {
        if(MultiplayerSession.Find(scene)?.StartFor(seat) is {} start)return start.Rotation;
        var points=Spawns(scene);
        return points.Length==6 && seat>=0 && seat<6 ? points[seat].WorldRotation : fallback;
    }

    public static ShieldSpawnPoint[] ShieldSpawns(Scene scene) => scene.GetAllComponents<ShieldSpawnPoint>()
        .Where(x => x.GameObject.Active).ToArray();

    protected override void OnStart()
    {
        if(!Scene.GetAllComponents<RestaurantLayoutProbe>().Any())Components.Create<RestaurantLayoutProbe>();
        if(Spawns(Scene).Length != 6)
            Log.Warning("Paintball map requires six native SpawnPoint objects named 01 through 06. Using legacy arena positions until corrected.");
    }
}

/// <summary>Place on walkable ground to choose a possible random shield pickup location.</summary>
public sealed class ShieldSpawnPoint : Component { }