1
0

ShuttleConsoleSystem.cs 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444
  1. using Content.Server.Power.Components;
  2. using Content.Server.Power.EntitySystems;
  3. using Content.Server.Shuttles.Components;
  4. using Content.Server.Shuttles.Events;
  5. using Content.Server.Station.Systems;
  6. using Content.Shared.ActionBlocker;
  7. using Content.Shared.Alert;
  8. using Content.Shared.Popups;
  9. using Content.Shared.Shuttles.BUIStates;
  10. using Content.Shared.Shuttles.Components;
  11. using Content.Shared.Shuttles.Events;
  12. using Content.Shared.Shuttles.Systems;
  13. using Content.Shared.Tag;
  14. using Content.Shared.Movement.Systems;
  15. using Content.Shared.Power;
  16. using Content.Shared.Shuttles.UI.MapObjects;
  17. using Content.Shared.Timing;
  18. using Robust.Server.GameObjects;
  19. using Robust.Shared.Collections;
  20. using Robust.Shared.GameStates;
  21. using Robust.Shared.Map;
  22. using Robust.Shared.Utility;
  23. using Content.Shared.UserInterface;
  24. namespace Content.Server.Shuttles.Systems;
  25. public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
  26. {
  27. [Dependency] private readonly SharedMapSystem _mapSystem = default!;
  28. [Dependency] private readonly ActionBlockerSystem _blocker = default!;
  29. [Dependency] private readonly AlertsSystem _alertsSystem = default!;
  30. [Dependency] private readonly EntityLookupSystem _lookup = default!;
  31. [Dependency] private readonly SharedPopupSystem _popup = default!;
  32. [Dependency] private readonly SharedTransformSystem _transform = default!;
  33. [Dependency] private readonly ShuttleSystem _shuttle = default!;
  34. [Dependency] private readonly StationSystem _station = default!;
  35. [Dependency] private readonly TagSystem _tags = default!;
  36. [Dependency] private readonly UserInterfaceSystem _ui = default!;
  37. [Dependency] private readonly SharedContentEyeSystem _eyeSystem = default!;
  38. private EntityQuery<MetaDataComponent> _metaQuery;
  39. private EntityQuery<TransformComponent> _xformQuery;
  40. private readonly HashSet<Entity<ShuttleConsoleComponent>> _consoles = new();
  41. public override void Initialize()
  42. {
  43. base.Initialize();
  44. _metaQuery = GetEntityQuery<MetaDataComponent>();
  45. _xformQuery = GetEntityQuery<TransformComponent>();
  46. SubscribeLocalEvent<ShuttleConsoleComponent, ComponentShutdown>(OnConsoleShutdown);
  47. SubscribeLocalEvent<ShuttleConsoleComponent, PowerChangedEvent>(OnConsolePowerChange);
  48. SubscribeLocalEvent<ShuttleConsoleComponent, AnchorStateChangedEvent>(OnConsoleAnchorChange);
  49. SubscribeLocalEvent<ShuttleConsoleComponent, ActivatableUIOpenAttemptEvent>(OnConsoleUIOpenAttempt);
  50. Subs.BuiEvents<ShuttleConsoleComponent>(ShuttleConsoleUiKey.Key, subs =>
  51. {
  52. subs.Event<ShuttleConsoleFTLBeaconMessage>(OnBeaconFTLMessage);
  53. subs.Event<ShuttleConsoleFTLPositionMessage>(OnPositionFTLMessage);
  54. subs.Event<BoundUIClosedEvent>(OnConsoleUIClose);
  55. });
  56. SubscribeLocalEvent<DroneConsoleComponent, ConsoleShuttleEvent>(OnCargoGetConsole);
  57. SubscribeLocalEvent<DroneConsoleComponent, AfterActivatableUIOpenEvent>(OnDronePilotConsoleOpen);
  58. Subs.BuiEvents<DroneConsoleComponent>(ShuttleConsoleUiKey.Key, subs =>
  59. {
  60. subs.Event<BoundUIClosedEvent>(OnDronePilotConsoleClose);
  61. });
  62. SubscribeLocalEvent<DockEvent>(OnDock);
  63. SubscribeLocalEvent<UndockEvent>(OnUndock);
  64. SubscribeLocalEvent<PilotComponent, ComponentGetState>(OnGetState);
  65. SubscribeLocalEvent<PilotComponent, StopPilotingAlertEvent>(OnStopPilotingAlert);
  66. SubscribeLocalEvent<FTLDestinationComponent, ComponentStartup>(OnFtlDestStartup);
  67. SubscribeLocalEvent<FTLDestinationComponent, ComponentShutdown>(OnFtlDestShutdown);
  68. InitializeFTL();
  69. }
  70. private void OnFtlDestStartup(EntityUid uid, FTLDestinationComponent component, ComponentStartup args)
  71. {
  72. RefreshShuttleConsoles();
  73. }
  74. private void OnFtlDestShutdown(EntityUid uid, FTLDestinationComponent component, ComponentShutdown args)
  75. {
  76. RefreshShuttleConsoles();
  77. }
  78. private void OnDock(DockEvent ev)
  79. {
  80. RefreshShuttleConsoles();
  81. }
  82. private void OnUndock(UndockEvent ev)
  83. {
  84. RefreshShuttleConsoles();
  85. }
  86. /// <summary>
  87. /// Refreshes all the shuttle console data for a particular grid.
  88. /// </summary>
  89. public void RefreshShuttleConsoles(EntityUid gridUid)
  90. {
  91. var exclusions = new List<ShuttleExclusionObject>();
  92. GetExclusions(ref exclusions);
  93. _consoles.Clear();
  94. _lookup.GetChildEntities(gridUid, _consoles);
  95. DockingInterfaceState? dockState = null;
  96. foreach (var entity in _consoles)
  97. {
  98. UpdateState(entity, ref dockState);
  99. }
  100. }
  101. /// <summary>
  102. /// Refreshes all of the data for shuttle consoles.
  103. /// </summary>
  104. public void RefreshShuttleConsoles()
  105. {
  106. var exclusions = new List<ShuttleExclusionObject>();
  107. GetExclusions(ref exclusions);
  108. var query = AllEntityQuery<ShuttleConsoleComponent>();
  109. DockingInterfaceState? dockState = null;
  110. while (query.MoveNext(out var uid, out _))
  111. {
  112. UpdateState(uid, ref dockState);
  113. }
  114. }
  115. /// <summary>
  116. /// Stop piloting if the window is closed.
  117. /// </summary>
  118. private void OnConsoleUIClose(EntityUid uid, ShuttleConsoleComponent component, BoundUIClosedEvent args)
  119. {
  120. if ((ShuttleConsoleUiKey)args.UiKey != ShuttleConsoleUiKey.Key)
  121. {
  122. return;
  123. }
  124. RemovePilot(args.Actor);
  125. }
  126. private void OnConsoleUIOpenAttempt(EntityUid uid, ShuttleConsoleComponent component,
  127. ActivatableUIOpenAttemptEvent args)
  128. {
  129. if (!TryPilot(args.User, uid))
  130. args.Cancel();
  131. }
  132. private void OnConsoleAnchorChange(EntityUid uid, ShuttleConsoleComponent component,
  133. ref AnchorStateChangedEvent args)
  134. {
  135. DockingInterfaceState? dockState = null;
  136. UpdateState(uid, ref dockState);
  137. }
  138. private void OnConsolePowerChange(EntityUid uid, ShuttleConsoleComponent component, ref PowerChangedEvent args)
  139. {
  140. DockingInterfaceState? dockState = null;
  141. UpdateState(uid, ref dockState);
  142. }
  143. private bool TryPilot(EntityUid user, EntityUid uid)
  144. {
  145. if (!_tags.HasTag(user, "CanPilot") ||
  146. !TryComp<ShuttleConsoleComponent>(uid, out var component) ||
  147. !this.IsPowered(uid, EntityManager) ||
  148. !Transform(uid).Anchored ||
  149. !_blocker.CanInteract(user, uid))
  150. {
  151. return false;
  152. }
  153. var pilotComponent = EnsureComp<PilotComponent>(user);
  154. var console = pilotComponent.Console;
  155. if (console != null)
  156. {
  157. RemovePilot(user, pilotComponent);
  158. // This feels backwards; is this intended to be a toggle?
  159. if (console == uid)
  160. return false;
  161. }
  162. AddPilot(uid, user, component);
  163. return true;
  164. }
  165. private void OnGetState(EntityUid uid, PilotComponent component, ref ComponentGetState args)
  166. {
  167. args.State = new PilotComponentState(GetNetEntity(component.Console));
  168. }
  169. private void OnStopPilotingAlert(Entity<PilotComponent> ent, ref StopPilotingAlertEvent args)
  170. {
  171. if (ent.Comp.Console != null)
  172. {
  173. RemovePilot(ent, ent);
  174. }
  175. }
  176. /// <summary>
  177. /// Returns the position and angle of all dockingcomponents.
  178. /// </summary>
  179. public Dictionary<NetEntity, List<DockingPortState>> GetAllDocks()
  180. {
  181. // TODO: NEED TO MAKE SURE THIS UPDATES ON ANCHORING CHANGES!
  182. var result = new Dictionary<NetEntity, List<DockingPortState>>();
  183. var query = AllEntityQuery<DockingComponent, TransformComponent, MetaDataComponent>();
  184. while (query.MoveNext(out var uid, out var comp, out var xform, out var metadata))
  185. {
  186. if (xform.ParentUid != xform.GridUid)
  187. continue;
  188. var gridDocks = result.GetOrNew(GetNetEntity(xform.GridUid.Value));
  189. var state = new DockingPortState()
  190. {
  191. Name = metadata.EntityName,
  192. Coordinates = GetNetCoordinates(xform.Coordinates),
  193. Angle = xform.LocalRotation,
  194. Entity = GetNetEntity(uid),
  195. GridDockedWith =
  196. _xformQuery.TryGetComponent(comp.DockedWith, out var otherDockXform) ?
  197. GetNetEntity(otherDockXform.GridUid) :
  198. null,
  199. };
  200. gridDocks.Add(state);
  201. }
  202. return result;
  203. }
  204. private void UpdateState(EntityUid consoleUid, ref DockingInterfaceState? dockState)
  205. {
  206. EntityUid? entity = consoleUid;
  207. var getShuttleEv = new ConsoleShuttleEvent
  208. {
  209. Console = entity,
  210. };
  211. RaiseLocalEvent(entity.Value, ref getShuttleEv);
  212. entity = getShuttleEv.Console;
  213. TryComp(entity, out TransformComponent? consoleXform);
  214. var shuttleGridUid = consoleXform?.GridUid;
  215. NavInterfaceState navState;
  216. ShuttleMapInterfaceState mapState;
  217. dockState ??= GetDockState();
  218. if (shuttleGridUid != null && entity != null)
  219. {
  220. navState = GetNavState(entity.Value, dockState.Docks);
  221. mapState = GetMapState(shuttleGridUid.Value);
  222. }
  223. else
  224. {
  225. navState = new NavInterfaceState(0f, null, null, new Dictionary<NetEntity, List<DockingPortState>>());
  226. mapState = new ShuttleMapInterfaceState(
  227. FTLState.Invalid,
  228. default,
  229. new List<ShuttleBeaconObject>(),
  230. new List<ShuttleExclusionObject>());
  231. }
  232. if (_ui.HasUi(consoleUid, ShuttleConsoleUiKey.Key))
  233. {
  234. _ui.SetUiState(consoleUid, ShuttleConsoleUiKey.Key, new ShuttleBoundUserInterfaceState(navState, mapState, dockState));
  235. }
  236. }
  237. public override void Update(float frameTime)
  238. {
  239. base.Update(frameTime);
  240. var toRemove = new ValueList<(EntityUid, PilotComponent)>();
  241. var query = EntityQueryEnumerator<PilotComponent>();
  242. while (query.MoveNext(out var uid, out var comp))
  243. {
  244. if (comp.Console == null)
  245. continue;
  246. if (!_blocker.CanInteract(uid, comp.Console))
  247. {
  248. toRemove.Add((uid, comp));
  249. }
  250. }
  251. foreach (var (uid, comp) in toRemove)
  252. {
  253. RemovePilot(uid, comp);
  254. }
  255. }
  256. protected override void HandlePilotShutdown(EntityUid uid, PilotComponent component, ComponentShutdown args)
  257. {
  258. base.HandlePilotShutdown(uid, component, args);
  259. RemovePilot(uid, component);
  260. }
  261. private void OnConsoleShutdown(EntityUid uid, ShuttleConsoleComponent component, ComponentShutdown args)
  262. {
  263. ClearPilots(component);
  264. }
  265. public void AddPilot(EntityUid uid, EntityUid entity, ShuttleConsoleComponent component)
  266. {
  267. if (!EntityManager.TryGetComponent(entity, out PilotComponent? pilotComponent)
  268. || component.SubscribedPilots.Contains(entity))
  269. {
  270. return;
  271. }
  272. _eyeSystem.SetZoom(entity, component.Zoom, ignoreLimits: true);
  273. component.SubscribedPilots.Add(entity);
  274. _alertsSystem.ShowAlert(entity, pilotComponent.PilotingAlert);
  275. pilotComponent.Console = uid;
  276. ActionBlockerSystem.UpdateCanMove(entity);
  277. pilotComponent.Position = EntityManager.GetComponent<TransformComponent>(entity).Coordinates;
  278. Dirty(entity, pilotComponent);
  279. }
  280. public void RemovePilot(EntityUid pilotUid, PilotComponent pilotComponent)
  281. {
  282. var console = pilotComponent.Console;
  283. if (!TryComp<ShuttleConsoleComponent>(console, out var helm))
  284. return;
  285. pilotComponent.Console = null;
  286. pilotComponent.Position = null;
  287. _eyeSystem.ResetZoom(pilotUid);
  288. if (!helm.SubscribedPilots.Remove(pilotUid))
  289. return;
  290. _alertsSystem.ClearAlert(pilotUid, pilotComponent.PilotingAlert);
  291. _popup.PopupEntity(Loc.GetString("shuttle-pilot-end"), pilotUid, pilotUid);
  292. if (pilotComponent.LifeStage < ComponentLifeStage.Stopping)
  293. EntityManager.RemoveComponent<PilotComponent>(pilotUid);
  294. }
  295. public void RemovePilot(EntityUid entity)
  296. {
  297. if (!EntityManager.TryGetComponent(entity, out PilotComponent? pilotComponent))
  298. return;
  299. RemovePilot(entity, pilotComponent);
  300. }
  301. public void ClearPilots(ShuttleConsoleComponent component)
  302. {
  303. var query = GetEntityQuery<PilotComponent>();
  304. while (component.SubscribedPilots.TryGetValue(0, out var pilot))
  305. {
  306. if (query.TryGetComponent(pilot, out var pilotComponent))
  307. RemovePilot(pilot, pilotComponent);
  308. }
  309. }
  310. /// <summary>
  311. /// Specific for a particular shuttle.
  312. /// </summary>
  313. public NavInterfaceState GetNavState(Entity<RadarConsoleComponent?, TransformComponent?> entity, Dictionary<NetEntity, List<DockingPortState>> docks)
  314. {
  315. if (!Resolve(entity, ref entity.Comp1, ref entity.Comp2))
  316. return new NavInterfaceState(SharedRadarConsoleSystem.DefaultMaxRange, null, null, docks);
  317. return GetNavState(
  318. entity,
  319. docks,
  320. entity.Comp2.Coordinates,
  321. entity.Comp2.LocalRotation);
  322. }
  323. public NavInterfaceState GetNavState(
  324. Entity<RadarConsoleComponent?, TransformComponent?> entity,
  325. Dictionary<NetEntity, List<DockingPortState>> docks,
  326. EntityCoordinates coordinates,
  327. Angle angle)
  328. {
  329. if (!Resolve(entity, ref entity.Comp1, ref entity.Comp2))
  330. return new NavInterfaceState(SharedRadarConsoleSystem.DefaultMaxRange, GetNetCoordinates(coordinates), angle, docks);
  331. return new NavInterfaceState(
  332. entity.Comp1.MaxRange,
  333. GetNetCoordinates(coordinates),
  334. angle,
  335. docks);
  336. }
  337. /// <summary>
  338. /// Global for all shuttles.
  339. /// </summary>
  340. /// <returns></returns>
  341. public DockingInterfaceState GetDockState()
  342. {
  343. var docks = GetAllDocks();
  344. return new DockingInterfaceState(docks);
  345. }
  346. /// <summary>
  347. /// Specific to a particular shuttle.
  348. /// </summary>
  349. public ShuttleMapInterfaceState GetMapState(Entity<FTLComponent?> shuttle)
  350. {
  351. FTLState ftlState = FTLState.Available;
  352. StartEndTime stateDuration = default;
  353. if (Resolve(shuttle, ref shuttle.Comp, false) && shuttle.Comp.LifeStage < ComponentLifeStage.Stopped)
  354. {
  355. ftlState = shuttle.Comp.State;
  356. stateDuration = _shuttle.GetStateTime(shuttle.Comp);
  357. }
  358. List<ShuttleBeaconObject>? beacons = null;
  359. List<ShuttleExclusionObject>? exclusions = null;
  360. GetBeacons(ref beacons);
  361. GetExclusions(ref exclusions);
  362. return new ShuttleMapInterfaceState(
  363. ftlState,
  364. stateDuration,
  365. beacons ?? new List<ShuttleBeaconObject>(),
  366. exclusions ?? new List<ShuttleExclusionObject>());
  367. }
  368. }