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 { }