Player/Components/WallHexProjector.cs
using KOTH;
using Sandbox;
using System;
using System.Linq;
public sealed class WallHexProjector : Component
{
[Property, Group("Detection")]
[Range(8f, 512f)]
public float Range { get; set; } = 80f;
[Property, Group("Detection")]
[Range(3, 24)]
public int RayCount { get; set; } = 10;
[Property, Group("Detection")]
public bool IncludeVertical { get; set; } = true;
[Property, Group("Detection")]
public TagSet IgnoreTags { get; set; }
[Property, Group("Detection")]
[Range(0.05f, 4f)]
public float SurfaceOffset { get; set; } = 0.5f;
[Property, Group("Hex")]
[Range(0.5f, 128f)]
public float HexSize { get; set; } = 8f;
[Property, Group("Hex")]
[Range(0f, 0.45f)]
public float LineThickness { get; set; } = 0.07f;
[Property, Group("Hex")]
[Range(0f, 1f)]
public float CellFill { get; set; } = 0.06f;
[Property, Group("Hex")]
[Range(0.25f, 8f)]
public float Falloff { get; set; } = 2f;
[Property, Group("Hex")]
[Range(0f, 1f)]
public float Pulse { get; set; } = 0.35f;
[Property, Group("Hex")]
public Color Tint { get; set; } = new Color(0.2f, 0.9f, 1f);
[Property, Group("Hex")]
[Range(0f, 10f)]
public float Brightness { get; set; } = 2f;
[Property, Group("Setup")]
public Material HexMaterial { get; set; }
[Property, Group("Setup")]
public Model PanelModel { get; set; }
[Property, Group("Setup")]
public float PanelModelSize { get; set; } = 100f;
[Property, Group("Setup")]
public Angles PanelAlignment { get; set; } = new Angles(90f, 0f, 0f);
private ModelRenderer[] Panels;
public PlayerPawn LocalPawn = null;
protected override void OnStart()
{
RebuildPanels();
}
protected override void OnDisabled()
{
DestroyPanels();
}
private int TotalPanels => RayCount + (IncludeVertical ? 2 : 0);
private void DestroyPanels()
{
if (Panels is null) return;
foreach (var Panel in Panels)
{
Panel?.GameObject?.Destroy();
}
Panels = null;
}
private void RebuildPanels()
{
DestroyPanels();
var Model = PanelModel ?? Sandbox.Model.Load("models/dev/plane.vmdl");
Panels = new ModelRenderer[TotalPanels];
for (int PanelIndex = 0; PanelIndex < Panels.Length; PanelIndex++)
{
var Object = Scene.CreateObject();
Object.Name = $"HexPanel_{PanelIndex}";
Object.Flags |= GameObjectFlags.NotSaved | GameObjectFlags.Hidden;
var Renderer = Object.Components.Create<ModelRenderer>();
Renderer.Model = Model;
Renderer.MaterialOverride = HexMaterial;
Renderer.RenderType = ModelRenderer.ShadowRenderType.Off;
Panels[PanelIndex] = Renderer;
}
}
protected override void OnUpdate()
{
if (LocalPawn != null)
{
if (LocalPawn.IsDummy)
{
return;
}
}
if (Panels is null || Panels.Length != TotalPanels)
{
RebuildPanels();
}
int Index = 0;
for (int RayIndex = 0; RayIndex < RayCount; RayIndex++)
{
float Yaw = 360f / RayCount * RayIndex;
PlacePanel(Panels[Index++], WorldPosition, Rotation.FromYaw(Yaw).Forward);
}
if (IncludeVertical)
{
PlacePanel(Panels[Index++], WorldPosition, Vector3.Up);
PlacePanel(Panels[Index++], WorldPosition, Vector3.Down);
}
}
private void PlacePanel(ModelRenderer Panel, Vector3 origin, Vector3 dir)
{
if (!Panel.IsValid()) return;
var WorldTrace = Scene.Trace
.Ray(origin, origin + dir * Range)
.IgnoreGameObjectHierarchy(GameObject)
.WithoutTags(IgnoreTags ?? new TagSet())
.Run();
Panel.GameObject.Enabled = WorldTrace.Hit;
if (!WorldTrace.Hit) return;
Panel.WorldPosition = WorldTrace.HitPosition + WorldTrace.Normal * SurfaceOffset;
Panel.WorldRotation = Rotation.LookAt(WorldTrace.Normal) * Rotation.From(PanelAlignment);
float size = MathF.Max(PanelModelSize, 1f);
float scale = Range * 2.5f / size;
Panel.WorldScale = new Vector3(scale, scale, 1f);
var Attribute = Panel.SceneObject?.Attributes;
if (Attribute is null) return;
Attribute.Set("FocusPos", origin);
Attribute.Set("HexSize", HexSize);
Attribute.Set("Thickness", LineThickness);
Attribute.Set("Fill", CellFill);
Attribute.Set("Range", Range);
Attribute.Set("Falloff", Falloff);
Attribute.Set("Pulse", Pulse);
Attribute.Set("Tint", Tint);
Attribute.Set("Brightness", Brightness);
}
}