using System.Linq; using Content.Server._Mono.Projectiles.TargetGuided; using Content.Shared._Mono.FireControl; using Content.Shared.Projectiles; using Content.Shared.Weapons.Ranged.Components; using Content.Shared.Weapons.Ranged.Events; using Content.Shared.Shuttles.Components; using EntityCoordinates = Robust.Shared.Map.EntityCoordinates; namespace Content.Server._Mono.FireControl; public sealed partial class FireControlSystem { [Dependency] private readonly TargetGuidedSystem _targetGuided = null!; /// /// List of active guided missiles that need cursor position updates /// private readonly HashSet _activeMissiles = new(); /// /// Map of console entities to their current mouse positions /// private readonly Dictionary _consoleMousePositions = new(); /// /// Registers handlers for events related to target guided projectiles. /// private void InitializeTargetGuided() { SubscribeLocalEvent(OnTargetGuidedShot); SubscribeLocalEvent(OnGuidedMissileShutdown); // Track fire messages to update cursor positions SubscribeLocalEvent(OnConsoleFireEvent); } /// /// Track console fire events to update cursor positions /// private void OnConsoleFireEvent(EntityUid uid, FireControlConsoleComponent component, FireControlConsoleFireEvent args) { // Store the current mouse position for this console _consoleMousePositions[uid] = GetCoordinates(args.Coordinates); } /// /// Subscribed to AmmoShotEvent to check for and configure guided projectiles. /// private void OnTargetGuidedShot(EntityUid uid, GunComponent component, AmmoShotEvent args) { if (args.FiredProjectiles.Count == 0) return; // Get the shooter entity EntityUid? shooter = null; if (TryComp(args.FiredProjectiles[0], out var projectileComp)) { shooter = projectileComp.Shooter; } // We need to get the target coordinates from the gun component var targetCoords = component.ShootCoordinates; if (!targetCoords.HasValue || !targetCoords.Value.IsValid(EntityManager)) return; // Find the controlling console for position updates if this is a fire controllable EntityUid? controllingConsole = null; if (TryComp(uid, out var fireControllable) && fireControllable.ControllingServer != null) { // Find the active console that fired this var query = EntityQueryEnumerator(); while (query.MoveNext(out var consoleUid, out var console)) { if (console.ConnectedServer == fireControllable.ControllingServer) { controllingConsole = consoleUid; // Store initial cursor position if we're seeing it for the first time if (!_consoleMousePositions.ContainsKey(consoleUid)) { _consoleMousePositions[consoleUid] = targetCoords.Value; } break; } } } foreach (var projectileUid in args.FiredProjectiles) { if (!TryComp(projectileUid, out var guidedComp)) continue; // If firing ship is in FTL, missile won't have guidance if (shooter.HasValue && Transform(shooter.Value).GridUid is { } shipGrid) { if (TryComp(shipGrid, out _)) { // Skip guidance setup if ship is in FTL continue; } } // Set up initial target for guided missile guidedComp.TargetPosition = targetCoords.Value; // Add to our tracking list for cursor position updates _activeMissiles.Add(projectileUid); // Record the console this was fired from for position updates if (controllingConsole.HasValue) { guidedComp.ControllingConsole = controllingConsole; } } } /// /// Cleanup guided missiles when they're destroyed /// private void OnGuidedMissileShutdown(EntityUid uid, TargetGuidedComponent component, ComponentShutdown args) { _activeMissiles.Remove(uid); } /// /// Updates the cursor position for any tracking missiles from a given console /// public void OnGuidanceUpdate(EntityUid consoleUid, EntityCoordinates targetCoordinates) { // Store the updated position for this console _consoleMousePositions[consoleUid] = targetCoordinates; // Update any active missiles being controlled by this console foreach (var missile in _activeMissiles) { if (!TryComp(missile, out var guidedComp)) continue; if (guidedComp.ControllingConsole != consoleUid) continue; // Don't update position if the missile's ship is in FTL if (TryComp(missile, out var projectileComp) && projectileComp.Shooter.HasValue && Transform(projectileComp.Shooter.Value).GridUid is { } shipGrid && TryComp(shipGrid, out _)) { continue; } guidedComp.TargetPosition = targetCoordinates; } } /// /// Helper method to get the current position of a specific console /// public EntityCoordinates? GetConsolePosition(EntityUid consoleUid) { if (_consoleMousePositions.TryGetValue(consoleUid, out var coords)) return coords; return null; } public override void Update(float frameTime) { base.Update(frameTime); // Update target positions for active missiles based on the current cursor position foreach (var missileUid in _activeMissiles.ToArray()) { if (!TryComp(missileUid, out var guidedComp) || !guidedComp.ControllingConsole.HasValue) continue; // Get the controlling console var consoleUid = guidedComp.ControllingConsole.Value; if (!_consoleMousePositions.TryGetValue(consoleUid, out var mousePosition)) continue; // Don't update position if the missile's ship is in FTL if (TryComp(missileUid, out var projectileComp) && projectileComp.Shooter.HasValue && Transform(projectileComp.Shooter.Value).GridUid is { } shipGrid && TryComp(shipGrid, out _)) { continue; } // Update the missile's target to the console's current mouse position _targetGuided.SetTargetPosition(missileUid, mousePosition); } // Clean up any console positions for consoles that no longer exist or have no active missiles CleanupConsolePositions(); } /// /// Remove any console positions that no longer have active missiles /// private void CleanupConsolePositions() { // Get all consoles that are actually controlling missiles var activeConsoles = new HashSet(); foreach (var missileUid in _activeMissiles) { if (TryComp(missileUid, out var guidedComp) && guidedComp.ControllingConsole.HasValue) { activeConsoles.Add(guidedComp.ControllingConsole.Value); } } // Remove positions for consoles without any missiles foreach (var consoleUid in _consoleMousePositions.Keys.ToList()) { if (!activeConsoles.Contains(consoleUid) || !EntityManager.EntityExists(consoleUid)) { _consoleMousePositions.Remove(consoleUid); } } } }