From d4697c000cd0bf20c63b575202c920134afbb94b Mon Sep 17 00:00:00 2001 From: Flipp Syder <76629141+vulppine@users.noreply.github.com> Date: Tue, 31 May 2022 01:44:57 -0700 Subject: [PATCH] Surveillance cameras (#8246) * cameras but i didn't feel like git stashing them * Adds more functionality server-side for surveillance cameras * rider moment * Adds skeleton for SurveillanceCameraMonitorBoundUi on client * whoops * makes surveillance camera monitor UI more defined * removes tree from SurveillanceCameraMonitorWindow * surveillance camera active flag, other routing things * actually sets how SurveillanceCameraMonitorSystem sends camera info to clients * adds entity yaml, changes field * adds the camera/monitor entities, makes the UI open * SurveillanceCameraRouters (not implemented fully) * subnets for cameras, server-side * it works! * fixes rotation in cameras * whoops restores surveillance cameras to ignored components makes it so that switching cameras now lerps the other camera * makes the UI work * makes it so that cameras actually refresh now * cleanup * adds camera.rsi * cleans up prototypes a bit * adds camera subnet frequencies, cameras in subnets * adds surveillance camera router subnets * might fix testing errors * adds the circuit board to the surveillance camera monitor * fixes up the camera monitor (the detective will get his tv soon) * adds heartbeat, ensures subnet data is passed into cameras to send * fixes up a few things * whoops * changes to UI internals * fixes subnet selection issue * localized strings for UI * changes 'test' id to 'camera' for cameras * whoops * missing s * camera static! * adds a delay to camera switching * adjusts a few things in camera timing * adds setup for cameras/routers, localization for frequency names * adds setup ui for routers, makes subnet names in monitor window follow frequency name in prototype * localization, some cleanup * ui adjustments * adds surveillance camera visuals * fixes a bug when closing the UI for monitors * adds disconnect message to UI * adds construction graph to cameras * adds the camera to the construction menu * fixes network selection for setup, tweak to assembly * adds surveillance camera router construction, fixes up surveillance camera wire cutting * adds disconnect button to monitor UI * switches around the status text * tweaks monitor UI * makes the address actually show * might make tests pass * UI adjustments, hard name limit * ok, that didn't work * adds wireless cameras * makes the television work/look nicer * adds tripod cameras in addition to mobile cameras * makes wireless cameras constructable * fixes up those prototypes * reorganization in C#, small cleanup * ensures that power changes deactivate some devices * adds a component to the television, comments out a function * actually, never mind, i forgot that wireless cameras existed/are creatable for a second * tweaks to router construction, removes SubnetTest from prototypes * removes it from frequencies too * Apply suggestions from code review Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * type serializers into components * setup window opens centered, enum is now byte * replaces active monitor list with ActiveSurveillanceCameraMonitorComponent * adds paused/deleted entity checks, changes how verbs are given * removes EntitySystem.Get() references * fixes bug related to selecting network from setup, alphabet-orders network listing in setup * rider moment * adds minwidth to surveillance camera setup window * addresses reviews * should fix the issue with camera visuals not updating properly * addresses some reviews * addresses further review * addresses reviews related to RSIs * never needed a key there anyways * changes a few things with routers to ensure that they're active * whoops * ensurecomp over addcomp * whoops Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- .../ActiveSurveillanceCameraMonitor.cs | 9 + .../SurveillanceCameraMonitorSystem.cs | 49 ++ .../SurveillanceCameraVisualsComponent.cs | 12 + .../SurveillanceCameraVisualsSystem.cs | 29 + .../UI/SurveillanceCameraMonitorBoundUi.cs | 123 +++++ .../UI/SurveillanceCameraMonitorWindow.xaml | 25 + .../SurveillanceCameraMonitorWindow.xaml.cs | 188 +++++++ .../UI/SurveillanceCameraSetupBoundUi.cs | 68 +++ .../UI/SurveillanceCameraSetupWindow.xaml | 14 + .../UI/SurveillanceCameraSetupWindow.xaml.cs | 78 +++ Content.Server/Entry/IgnoredComponents.cs | 3 +- ...ctiveSurveillanceCameraMonitorComponent.cs | 7 + .../Components/SurveillanceCameraComponent.cs | 46 ++ .../SurveillanceCameraMonitorComponent.cs | 35 ++ .../SurveillanceCameraRouterComponent.cs | 31 ++ .../SurveillanceCameraMonitorSystem.cs | 497 ++++++++++++++++++ .../Systems/SurveillanceCameraRouterSystem.cs | 268 ++++++++++ .../Systems/SurveillanceCameraSystem.cs | 417 +++++++++++++++ .../SharedSurveillanceCameraMonitorSystem.cs | 128 +++++ .../SharedSurveillanceCameraSystem.cs | 22 + .../Locale/en-US/devices/device-network.ftl | 14 +- .../surveillance-camera-ui.ftl | 12 + .../Catalog/Research/technologies.yml | 4 + .../Device/devicenet_frequencies.yml | 50 ++ .../Circuitboards/Machine/production.yml | 53 ++ .../Devices/Circuitboards/computer.yml | 18 + .../Machines/Computers/computers.yml | 117 ++++- .../Entities/Structures/Machines/lathe.yml | 1 + .../Machines/surveillance_camera_routers.yml | 148 ++++++ .../Machines/wireless_surveillance_camera.yml | 117 +++++ .../Wallmounts/surveillance_camera.yml | 212 ++++++++ .../Construction/Graphs/utilities/cameras.yml | 57 ++ .../Recipes/Construction/utilities.yml | 16 +- .../Prototypes/Recipes/Lathes/electronics.yml | 36 ++ Resources/Prototypes/Shaders/shaders.yml | 5 + Resources/Prototypes/Wires/layouts.yml | 6 + .../Textures/Interface/Nano/square_black.png | Bin 0 -> 4688 bytes Resources/Textures/Shaders/camera_static.swsl | 40 ++ .../Wallmounts/camera.rsi/camera.png | Bin 0 -> 2918 bytes .../Wallmounts/camera.rsi/camera_assembly.png | Bin 0 -> 894 bytes .../Wallmounts/camera.rsi/camera_emp.png | Bin 0 -> 2864 bytes .../Wallmounts/camera.rsi/camera_in_use.png | Bin 0 -> 3019 bytes .../Wallmounts/camera.rsi/camera_off.png | Bin 0 -> 873 bytes .../Wallmounts/camera.rsi/cameracase.png | Bin 0 -> 283 bytes .../Wallmounts/camera.rsi/meta.json | 1 + .../Wallmounts/camera.rsi/xraycamera.png | Bin 0 -> 3973 bytes .../camera.rsi/xraycamera_assembly.png | Bin 0 -> 1023 bytes .../Wallmounts/camera.rsi/xraycamera_emp.png | Bin 0 -> 3236 bytes .../camera.rsi/xraycamera_in_use.png | Bin 0 -> 4014 bytes .../Wallmounts/camera.rsi/xraycamera_off.png | Bin 0 -> 1064 bytes .../Structures/monitors.rsi/meta.json | 16 + .../Structures/monitors.rsi/mobilevision.png | Bin 0 -> 1941 bytes .../Structures/monitors.rsi/party.png | Bin 0 -> 315 bytes .../Textures/Structures/monitors.rsi/rad0.png | Bin 0 -> 283 bytes .../Textures/Structures/monitors.rsi/rad1.png | Bin 0 -> 378 bytes .../Structures/monitors.rsi/shipalert0.png | Bin 0 -> 434 bytes .../Structures/monitors.rsi/shipalert1.png | Bin 0 -> 408 bytes .../Structures/monitors.rsi/shipalert2.png | Bin 0 -> 475 bytes .../Structures/monitors.rsi/television.png | Bin 0 -> 1398 bytes 59 files changed, 2945 insertions(+), 27 deletions(-) create mode 100644 Content.Client/SurveillanceCamera/ActiveSurveillanceCameraMonitor.cs create mode 100644 Content.Client/SurveillanceCamera/SurveillanceCameraMonitorSystem.cs create mode 100644 Content.Client/SurveillanceCamera/SurveillanceCameraVisualsComponent.cs create mode 100644 Content.Client/SurveillanceCamera/SurveillanceCameraVisualsSystem.cs create mode 100644 Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorBoundUi.cs create mode 100644 Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorWindow.xaml create mode 100644 Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorWindow.xaml.cs create mode 100644 Content.Client/SurveillanceCamera/UI/SurveillanceCameraSetupBoundUi.cs create mode 100644 Content.Client/SurveillanceCamera/UI/SurveillanceCameraSetupWindow.xaml create mode 100644 Content.Client/SurveillanceCamera/UI/SurveillanceCameraSetupWindow.xaml.cs create mode 100644 Content.Server/SurveillanceCamera/Components/ActiveSurveillanceCameraMonitorComponent.cs create mode 100644 Content.Server/SurveillanceCamera/Components/SurveillanceCameraComponent.cs create mode 100644 Content.Server/SurveillanceCamera/Components/SurveillanceCameraMonitorComponent.cs create mode 100644 Content.Server/SurveillanceCamera/Components/SurveillanceCameraRouterComponent.cs create mode 100644 Content.Server/SurveillanceCamera/Systems/SurveillanceCameraMonitorSystem.cs create mode 100644 Content.Server/SurveillanceCamera/Systems/SurveillanceCameraRouterSystem.cs create mode 100644 Content.Server/SurveillanceCamera/Systems/SurveillanceCameraSystem.cs create mode 100644 Content.Shared/SurveillanceCamera/SharedSurveillanceCameraMonitorSystem.cs create mode 100644 Content.Shared/SurveillanceCamera/SharedSurveillanceCameraSystem.cs create mode 100644 Resources/Locale/en-US/surveillance-camera/surveillance-camera-ui.ftl create mode 100644 Resources/Prototypes/Entities/Structures/Machines/surveillance_camera_routers.yml create mode 100644 Resources/Prototypes/Entities/Structures/Machines/wireless_surveillance_camera.yml create mode 100644 Resources/Prototypes/Entities/Structures/Wallmounts/surveillance_camera.yml create mode 100644 Resources/Prototypes/Recipes/Construction/Graphs/utilities/cameras.yml create mode 100644 Resources/Textures/Interface/Nano/square_black.png create mode 100644 Resources/Textures/Shaders/camera_static.swsl create mode 100644 Resources/Textures/Structures/Wallmounts/camera.rsi/camera.png create mode 100644 Resources/Textures/Structures/Wallmounts/camera.rsi/camera_assembly.png create mode 100644 Resources/Textures/Structures/Wallmounts/camera.rsi/camera_emp.png create mode 100644 Resources/Textures/Structures/Wallmounts/camera.rsi/camera_in_use.png create mode 100644 Resources/Textures/Structures/Wallmounts/camera.rsi/camera_off.png create mode 100644 Resources/Textures/Structures/Wallmounts/camera.rsi/cameracase.png create mode 100644 Resources/Textures/Structures/Wallmounts/camera.rsi/meta.json create mode 100644 Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera.png create mode 100644 Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_assembly.png create mode 100644 Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_emp.png create mode 100644 Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_in_use.png create mode 100644 Resources/Textures/Structures/Wallmounts/camera.rsi/xraycamera_off.png create mode 100644 Resources/Textures/Structures/monitors.rsi/meta.json create mode 100644 Resources/Textures/Structures/monitors.rsi/mobilevision.png create mode 100644 Resources/Textures/Structures/monitors.rsi/party.png create mode 100644 Resources/Textures/Structures/monitors.rsi/rad0.png create mode 100644 Resources/Textures/Structures/monitors.rsi/rad1.png create mode 100644 Resources/Textures/Structures/monitors.rsi/shipalert0.png create mode 100644 Resources/Textures/Structures/monitors.rsi/shipalert1.png create mode 100644 Resources/Textures/Structures/monitors.rsi/shipalert2.png create mode 100644 Resources/Textures/Structures/monitors.rsi/television.png diff --git a/Content.Client/SurveillanceCamera/ActiveSurveillanceCameraMonitor.cs b/Content.Client/SurveillanceCamera/ActiveSurveillanceCameraMonitor.cs new file mode 100644 index 0000000000..e2c30db904 --- /dev/null +++ b/Content.Client/SurveillanceCamera/ActiveSurveillanceCameraMonitor.cs @@ -0,0 +1,9 @@ +namespace Content.Client.SurveillanceCamera; + +[RegisterComponent] +public sealed class ActiveSurveillanceCameraMonitorVisualsComponent : Component +{ + public float TimeLeft = 30f; + + public Action? OnFinish; +} diff --git a/Content.Client/SurveillanceCamera/SurveillanceCameraMonitorSystem.cs b/Content.Client/SurveillanceCamera/SurveillanceCameraMonitorSystem.cs new file mode 100644 index 0000000000..31fe357427 --- /dev/null +++ b/Content.Client/SurveillanceCamera/SurveillanceCameraMonitorSystem.cs @@ -0,0 +1,49 @@ +using Robust.Shared.Utility; + +namespace Content.Client.SurveillanceCamera; + +public sealed class SurveillanceCameraMonitorSystem : EntitySystem +{ + private readonly RemQueue _toRemove = new(); + + public override void Update(float frameTime) + { + foreach (var comp in EntityQuery()) + { + if (Paused(comp.Owner)) + { + continue; + } + + comp.TimeLeft -= frameTime; + + if (comp.TimeLeft <= 0 || Deleted(comp.Owner)) + { + if (comp.OnFinish != null) + { + comp.OnFinish(); + } + + _toRemove.Add(comp.Owner); + } + } + + foreach (var uid in _toRemove) + { + EntityManager.RemoveComponent(uid); + } + + _toRemove.List?.Clear(); + } + + public void AddTimer(EntityUid uid, Action onFinish) + { + var comp = EnsureComp(uid); + comp.OnFinish = onFinish; + } + + public void RemoveTimer(EntityUid uid) + { + EntityManager.RemoveComponent(uid); + } +} diff --git a/Content.Client/SurveillanceCamera/SurveillanceCameraVisualsComponent.cs b/Content.Client/SurveillanceCamera/SurveillanceCameraVisualsComponent.cs new file mode 100644 index 0000000000..3883b2e48f --- /dev/null +++ b/Content.Client/SurveillanceCamera/SurveillanceCameraVisualsComponent.cs @@ -0,0 +1,12 @@ +using Content.Shared.SurveillanceCamera; + +namespace Content.Client.SurveillanceCamera; + +// Dummy component so that targetted events work on client for +// appearance events. +[RegisterComponent] +public sealed class SurveillanceCameraVisualsComponent : Component +{ + [DataField("sprites")] + public readonly Dictionary CameraSprites = new(); +} diff --git a/Content.Client/SurveillanceCamera/SurveillanceCameraVisualsSystem.cs b/Content.Client/SurveillanceCamera/SurveillanceCameraVisualsSystem.cs new file mode 100644 index 0000000000..18e05f3223 --- /dev/null +++ b/Content.Client/SurveillanceCamera/SurveillanceCameraVisualsSystem.cs @@ -0,0 +1,29 @@ +using Content.Shared.SurveillanceCamera; +using Robust.Client.GameObjects; + +namespace Content.Client.SurveillanceCamera; + +public sealed class SurveillanceCameraVisualsSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnAppearanceChange); + } + + private void OnAppearanceChange(EntityUid uid, SurveillanceCameraVisualsComponent component, + ref AppearanceChangeEvent args) + { + if (!args.AppearanceData.TryGetValue(SurveillanceCameraVisualsKey.Key, out var data) + || data is not SurveillanceCameraVisuals key + || args.Sprite == null + || !args.Sprite.LayerMapTryGet(SurveillanceCameraVisualsKey.Layer, out int layer) + || !component.CameraSprites.TryGetValue(key, out var state)) + { + return; + } + + args.Sprite.LayerSetState(layer, state); + } +} diff --git a/Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorBoundUi.cs b/Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorBoundUi.cs new file mode 100644 index 0000000000..0a33cbd0d5 --- /dev/null +++ b/Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorBoundUi.cs @@ -0,0 +1,123 @@ +using Content.Client.Eye; +using Content.Shared.SurveillanceCamera; +using Robust.Client.GameObjects; + +namespace Content.Client.SurveillanceCamera.UI; + +public sealed class SurveillanceCameraMonitorBoundUserInterface : BoundUserInterface +{ + [Dependency] private readonly IEntityManager _entityManager = default!; + private readonly EyeLerpingSystem _eyeLerpingSystem; + private readonly SurveillanceCameraMonitorSystem _surveillanceCameraMonitorSystem; + + private SurveillanceCameraMonitorWindow? _window; + private EntityUid? _currentCamera; + + public SurveillanceCameraMonitorBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) + { + IoCManager.InjectDependencies(this); + _eyeLerpingSystem = _entityManager.EntitySysManager.GetEntitySystem(); + _surveillanceCameraMonitorSystem = _entityManager.EntitySysManager.GetEntitySystem(); + } + + protected override void Open() + { + base.Open(); + + _window = new SurveillanceCameraMonitorWindow(); + + if (State != null) + { + UpdateState(State); + } + + _window.OpenCentered(); + + _window.CameraSelected += OnCameraSelected; + _window.SubnetOpened += OnSubnetRequest; + _window.CameraRefresh += OnCameraRefresh; + _window.SubnetRefresh += OnSubnetRefresh; + _window.OnClose += Close; + _window.CameraSwitchTimer += OnCameraSwitchTimer; + _window.CameraDisconnect += OnCameraDisconnect; + } + + private void OnCameraSelected(string address) + { + SendMessage(new SurveillanceCameraMonitorSwitchMessage(address)); + } + + private void OnSubnetRequest(string subnet) + { + SendMessage(new SurveillanceCameraMonitorSubnetRequestMessage(subnet)); + } + + private void OnCameraSwitchTimer() + { + _surveillanceCameraMonitorSystem.AddTimer(Owner.Owner, _window!.OnSwitchTimerComplete); + } + + private void OnCameraRefresh() + { + SendMessage(new SurveillanceCameraRefreshCamerasMessage()); + } + + private void OnSubnetRefresh() + { + SendMessage(new SurveillanceCameraRefreshSubnetsMessage()); + } + + private void OnCameraDisconnect() + { + SendMessage(new SurveillanceCameraDisconnectMessage()); + } + + protected override void UpdateState(BoundUserInterfaceState state) + { + if (_window == null || state is not SurveillanceCameraMonitorUiState cast) + { + return; + } + + if (cast.ActiveCamera == null) + { + _window.UpdateState(null, cast.Subnets, cast.ActiveAddress, cast.ActiveSubnet, cast.Cameras); + + if (_currentCamera != null) + { + _surveillanceCameraMonitorSystem.RemoveTimer(Owner.Owner); + _eyeLerpingSystem.RemoveEye(_currentCamera.Value); + _currentCamera = null; + } + } + else + { + if (_currentCamera == null) + { + _eyeLerpingSystem.AddEye(cast.ActiveCamera.Value); + _currentCamera = cast.ActiveCamera; + } + else if (_currentCamera != cast.ActiveCamera) + { + _eyeLerpingSystem.RemoveEye(_currentCamera.Value); + _eyeLerpingSystem.AddEye(cast.ActiveCamera.Value); + _currentCamera = cast.ActiveCamera; + } + + if (_entityManager.TryGetComponent(cast.ActiveCamera, out EyeComponent eye)) + { + _window.UpdateState(eye.Eye, cast.Subnets, cast.ActiveAddress, cast.ActiveSubnet, cast.Cameras); + } + } + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + + if (disposing) + { + _window?.Dispose(); + } + } +} diff --git a/Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorWindow.xaml b/Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorWindow.xaml new file mode 100644 index 0000000000..8f996b8171 --- /dev/null +++ b/Content.Client/SurveillanceCamera/UI/SurveillanceCameraMonitorWindow.xaml @@ -0,0 +1,25 @@ + + + + + +