// Copyright Rane (elijahrane@gmail.com) 2025
// All rights reserved. Relicensed under AGPL with permission
using System.Numerics;
using Content.Shared._Mono.FireControl;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
namespace Content.Client._Mono.FireControl;
///
/// Client-side system that visualizes firing directions for debug purposes
///
public sealed class FireControlVisualizerSystem : EntitySystem
{
[Dependency] private readonly IOverlayManager _overlayManager = default!;
private readonly Dictionary _activeVisualizations = new();
public sealed class VisualizationData
{
public Dictionary Directions { get; }
public VisualizationData(Dictionary directions)
{
Directions = directions;
}
}
public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent(OnFireControlVisualization);
_overlayManager.AddOverlay(new FireControlOverlay(this, EntityManager));
}
public override void Shutdown()
{
base.Shutdown();
_overlayManager.RemoveOverlay();
}
private void OnFireControlVisualization(FireControlVisualizationEvent ev)
{
// Convert NetEntity back to EntityUid
var entityUid = GetEntity(ev.Entity);
if (ev.Enabled && ev.Directions != null)
{
// Enable or update visualization
_activeVisualizations[entityUid] = new VisualizationData(ev.Directions);
}
else
{
// Disable visualization
_activeVisualizations.Remove(entityUid);
}
}
///
/// Gets the current active visualizations for the overlay to render
///
public IReadOnlyDictionary GetVisualizations()
{
return _activeVisualizations;
}
///
/// Overlay that renders the fire direction visualization
///
private sealed class FireControlOverlay : Overlay
{
private readonly FireControlVisualizerSystem _system;
private readonly IEntityManager _entityManager;
private readonly SharedTransformSystem _transformSystem;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public FireControlOverlay(FireControlVisualizerSystem system, IEntityManager entityManager)
{
_system = system;
_entityManager = entityManager;
_transformSystem = entityManager.System();
}
protected override void Draw(in OverlayDrawArgs args)
{
var handle = args.WorldHandle;
var visualizations = _system.GetVisualizations();
foreach (var (uid, data) in visualizations)
{
if (!_entityManager.TryGetComponent(uid, out TransformComponent? transform))
continue;
var position = _transformSystem.GetWorldPosition(transform);
// Draw a small circle at the weapon position
handle.DrawCircle(position, 0.2f, Color.Yellow, true);
// Draw rays for each direction
foreach (var (angle, canFire) in data.Directions)
{
// Convert angle to radians
var radians = angle * Math.PI / 180;
// Calculate end point (25 units length)
var direction = new Vector2((float)Math.Cos(radians), (float)Math.Sin(radians));
var endPoint = position + direction * 50f;
// Choose color based on whether firing is possible
var color = canFire ? Color.Green : Color.Red;
// Draw the ray (remove the thickness parameter)
handle.DrawLine(position, endPoint, color);
}
}
}
}
}