// 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); } } } } }