CoverSurface.cs
using Sandbox;
namespace PaintballBaddies;
/// <summary>Explicit opt-in for geometry usable by the cover controller.</summary>
public sealed class CoverSurface : Component
{
[Property] public float ModelHeight { get; set; }
[Property] public bool Curved { get; set; }
// For chest-high machinery where the marker cannot safely fire over the top.
[Property] public bool StandingOnly { get; set; }
// Optional semantic volume over solid workshop geometry. This identifies
// the original map's crates without duplicating their renderers/collision.
[Property] public Vector3 WorkshopSize {get;set;}
public bool Matches(SceneTraceResult hit)=>hit.Hit && (Find(hit.GameObject)==this ||
(WorkshopSize.x>0 && Contains(hit.HitPosition)));
private static CoverSurface Find(GameObject go)
{
while(go.IsValid()){var cover=go.Components.Get<CoverSurface>();if(cover.IsValid())return cover;go=go.Parent;}
return null;
}
private bool Contains(Vector3 position)
{
var p=WorldRotation.Inverse*(position-WorldPosition);
return System.MathF.Abs(p.x)<=WorkshopSize.x*.5f+3 && System.MathF.Abs(p.y)<=WorkshopSize.y*.5f+3
&& p.z>=-3 && p.z<=WorkshopSize.z+3;
}
public static CoverSurface Resolve(Scene scene,SceneTraceResult hit)
{
if(!hit.Hit)return null;
var direct=Find(hit.GameObject);
if(direct.IsValid() || (hit.GameObject?.Components.Get<MapCollider>() is null && hit.GameObject?.Tags.Has("workshop_geometry")!=true))return direct;
return direct ?? scene.GetAllComponents<CoverSurface>().FirstOrDefault(s=>s.WorkshopSize.x>0 && s.Enabled && s.GameObject.Active && s.Contains(hit.HitPosition));
}
public float Top
{
get
{
var box = Components.Get<BoxCollider>();
return WorldPosition.z + (box is not null ? box.Center.z + box.Scale.z * .5f : ModelHeight) * WorldScale.z;
}
}
}