using Content.Server.Popups; using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Server.Shuttles.Components; using Content.Shared.ActionBlocker; using Content.Shared.Alert; using Content.Shared.Interaction; using Content.Shared.Popups; using Content.Shared.Shuttles; using Content.Shared.Shuttles.Components; using Content.Shared.Tag; using Content.Shared.Verbs; using Robust.Shared.Map; using Robust.Shared.Player; using Robust.Shared.Utility; namespace Content.Server.Shuttles.EntitySystems { internal sealed class ShuttleConsoleSystem : SharedShuttleConsoleSystem { [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly ActionBlockerSystem _blocker = default!; [Dependency] private readonly AlertsSystem _alertsSystem = default!; [Dependency] private readonly PopupSystem _popup = default!; [Dependency] private readonly TagSystem _tags = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(HandleConsoleShutdown); SubscribeLocalEvent(HandleConsoleInteract); SubscribeLocalEvent(HandlePowerChange); SubscribeLocalEvent>(OnConsoleInteract); SubscribeLocalEvent(HandlePilotMove); } private void OnConsoleInteract(EntityUid uid, ShuttleConsoleComponent component, GetVerbsEvent args) { if (!args.CanAccess || !args.CanInteract) return; var xform = EntityManager.GetComponent(uid); // Maybe move mode onto the console instead? if (!_mapManager.TryGetGrid(xform.GridEntityId, out var grid) || !EntityManager.TryGetComponent(grid.GridEntityId, out ShuttleComponent? shuttle)) return; InteractionVerb verb = new() { Text = Loc.GetString("shuttle-mode-toggle"), Act = () => ToggleShuttleMode(args.User, component, shuttle), Disabled = !xform.Anchored || !this.IsPowered(uid, EntityManager), }; args.Verbs.Add(verb); } private void ToggleShuttleMode(EntityUid user, ShuttleConsoleComponent consoleComponent, ShuttleComponent shuttleComponent, TransformComponent? consoleXform = null) { // Re-validate if (!this.IsPowered(consoleComponent.Owner, EntityManager)) return; if (!Resolve(consoleComponent.Owner, ref consoleXform)) return; if (!consoleXform.Anchored || consoleXform.GridEntityId != EntityManager.GetComponent(shuttleComponent.Owner).GridEntityId) return; switch (shuttleComponent.Mode) { case ShuttleMode.Cruise: shuttleComponent.Mode = ShuttleMode.Docking; _popup.PopupEntity(Loc.GetString("shuttle-mode-docking"), consoleComponent.Owner, Filter.Entities(user)); break; case ShuttleMode.Docking: shuttleComponent.Mode = ShuttleMode.Cruise; _popup.PopupEntity(Loc.GetString("shuttle-mode-cruise"), consoleComponent.Owner, Filter.Entities(user)); break; default: throw new ArgumentOutOfRangeException(); } } public override void Update(float frameTime) { base.Update(frameTime); var toRemove = new RemQueue(); foreach (var comp in EntityManager.EntityQuery()) { if (comp.Console == null) continue; if (!_blocker.CanInteract(comp.Owner, comp.Console.Owner)) { toRemove.Add(comp); } } foreach (var comp in toRemove) { RemovePilot(comp); } } /// /// Console requires power to operate. /// private void HandlePowerChange(EntityUid uid, ShuttleConsoleComponent component, PowerChangedEvent args) { if (!args.Powered) { component.Enabled = false; ClearPilots(component); } else { component.Enabled = true; } } /// /// If pilot is moved then we'll stop them from piloting. /// private void HandlePilotMove(EntityUid uid, PilotComponent component, ref MoveEvent args) { if (component.Console == null || component.Position == null) { DebugTools.Assert(component.Position == null && component.Console == null); EntityManager.RemoveComponent(uid); return; } if (args.NewPosition.TryDistance(EntityManager, component.Position.Value, out var distance) && distance < PilotComponent.BreakDistance) return; RemovePilot(component); } /// /// For now pilots just interact with the console and can start piloting with wasd. /// private void HandleConsoleInteract(EntityUid uid, ShuttleConsoleComponent component, ActivateInWorldEvent args) { if (!_tags.HasTag(args.User, "CanPilot")) { return; } var pilotComponent = EntityManager.EnsureComponent(args.User); if (!component.Enabled) { args.User.PopupMessage($"Console is not powered."); return; } args.Handled = true; var console = pilotComponent.Console; if (console != null) { RemovePilot(pilotComponent); if (console == component) { return; } } AddPilot(args.User, component); } protected override void HandlePilotShutdown(EntityUid uid, PilotComponent component, ComponentShutdown args) { base.HandlePilotShutdown(uid, component, args); RemovePilot(component); } private void HandleConsoleShutdown(EntityUid uid, ShuttleConsoleComponent component, ComponentShutdown args) { ClearPilots(component); } public void AddPilot(EntityUid entity, ShuttleConsoleComponent component) { if (!EntityManager.TryGetComponent(entity, out PilotComponent? pilotComponent) || component.SubscribedPilots.Contains(pilotComponent)) { return; } if (TryComp(entity, out var eye)) { eye.Zoom = component.Zoom; } component.SubscribedPilots.Add(pilotComponent); _alertsSystem.ShowAlert(entity, AlertType.PilotingShuttle); entity.PopupMessage(Loc.GetString("shuttle-pilot-start")); pilotComponent.Console = component; ActionBlockerSystem.UpdateCanMove(entity); pilotComponent.Position = EntityManager.GetComponent(entity).Coordinates; pilotComponent.Dirty(); } public void RemovePilot(PilotComponent pilotComponent) { var console = pilotComponent.Console; if (console is not ShuttleConsoleComponent helmsman) return; pilotComponent.Console = null; pilotComponent.Position = null; if (TryComp(pilotComponent.Owner, out var eye)) { eye.Zoom = new(1.0f, 1.0f); } if (!helmsman.SubscribedPilots.Remove(pilotComponent)) return; _alertsSystem.ClearAlert(pilotComponent.Owner, AlertType.PilotingShuttle); pilotComponent.Owner.PopupMessage(Loc.GetString("shuttle-pilot-end")); if (pilotComponent.LifeStage < ComponentLifeStage.Stopping) EntityManager.RemoveComponent(pilotComponent.Owner); } public void RemovePilot(EntityUid entity) { if (!EntityManager.TryGetComponent(entity, out PilotComponent? pilotComponent)) return; RemovePilot(pilotComponent); } public void ClearPilots(ShuttleConsoleComponent component) { while (component.SubscribedPilots.TryGetValue(0, out var pilot)) { RemovePilot(pilot); } } } }