]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Add scroll zooming for admins and ghosts (#14061)
authormetalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Sat, 22 Apr 2023 10:26:41 +0000 (20:26 +1000)
committerGitHub <noreply@github.com>
Sat, 22 Apr 2023 10:26:41 +0000 (04:26 -0600)
* Add scroll zooming for admins

* Bunch of work

* Kinda better kinda worse scrolling

* shared code

* nyoom

* house md

* Fixes

* Log spam

* Fixes

---------

Co-authored-by: metalgearsloth <metalgearsloth@gmail.com>
13 files changed:
Content.Client/Input/ContentContexts.cs
Content.Client/Movement/Systems/ContentEyeSystem.cs [new file with mode: 0644]
Content.Client/Options/UI/Tabs/KeyRebindTab.xaml
Content.Client/Options/UI/Tabs/KeyRebindTab.xaml.cs
Content.Server/Movement/Systems/ContentEyeSystem.cs [new file with mode: 0644]
Content.Shared/Input/ContentKeyFunctions.cs
Content.Shared/Movement/Components/ActiveContentEyeComponent.cs [new file with mode: 0644]
Content.Shared/Movement/Components/ContentEyeComponent.cs [new file with mode: 0644]
Content.Shared/Movement/Systems/SharedContentEyeSystem.cs [new file with mode: 0644]
Resources/Locale/en-US/escape-menu/ui/options-menu.ftl
Resources/Prototypes/Entities/Mobs/Player/admin_ghost.yml
Resources/Prototypes/Entities/Mobs/Player/observer.yml
Resources/keybinds.yml

index d9c103b7ebe095f5259fc9e0952e384d41d805aa..9b45d63b605a1c4d2c2f356dbc77e05a717b1628 100644 (file)
@@ -28,6 +28,9 @@ namespace Content.Client.Input
             common.AddFunction(ContentKeyFunctions.TakeScreenshot);
             common.AddFunction(ContentKeyFunctions.TakeScreenshotNoUI);
             common.AddFunction(ContentKeyFunctions.Point);
+            common.AddFunction(ContentKeyFunctions.ZoomOut);
+            common.AddFunction(ContentKeyFunctions.ZoomIn);
+            common.AddFunction(ContentKeyFunctions.ResetZoom);
 
             // Not in engine, because engine cannot check for sanbox/admin status before starting placement.
             common.AddFunction(ContentKeyFunctions.EditorCopyObject);
diff --git a/Content.Client/Movement/Systems/ContentEyeSystem.cs b/Content.Client/Movement/Systems/ContentEyeSystem.cs
new file mode 100644 (file)
index 0000000..7ed0c77
--- /dev/null
@@ -0,0 +1,26 @@
+using Content.Shared.Movement.Components;
+using Content.Shared.Movement.Systems;
+using Robust.Client.GameObjects;
+using Robust.Client.Player;
+
+namespace Content.Client.Movement.Systems;
+
+public sealed class ContentEyeSystem : SharedContentEyeSystem
+{
+    [Dependency] private readonly IPlayerManager _player = default!;
+
+    public override void Update(float frameTime)
+    {
+        base.Update(frameTime);
+
+        var localPlayer = _player.LocalPlayer?.ControlledEntity;
+
+        if (!TryComp<ContentEyeComponent>(localPlayer, out var content) ||
+            !TryComp<EyeComponent>(localPlayer, out var eye))
+        {
+            return;
+        }
+
+        UpdateEye(localPlayer.Value, content, eye, frameTime);
+    }
+}
index 08dafbbbab4670535796b25460837760bdb14b23..93859099eeb3b565d7b132d5030ae89987491f4a 100644 (file)
@@ -9,7 +9,7 @@
             </BoxContainer>
         </ScrollContainer>
         <controls:StripeBack HasBottomEdge="False" HasMargins="False">
-            <BoxContainer Orientation="Horizontal">
+            <BoxContainer Orientation="Horizontal" Margin="8 8">
                 <Control MinSize="8 0" />
                 <Label Text="{Loc 'ui-options-binds-explanation'}" StyleClasses="LabelSubText" />
                 <Button Name="ResetAllButton"
index 9d2614c096b5bfb803ad0cd9656dab0190c6615e..c693f6f1ab3b22a46ea8b3857c09221b896d6ce4 100644 (file)
@@ -102,6 +102,9 @@ namespace Content.Client.Options.UI.Tabs
             AddButton(EngineKeyFunctions.CameraRotateLeft);
             AddButton(EngineKeyFunctions.CameraRotateRight);
             AddButton(EngineKeyFunctions.CameraReset);
+            AddButton(ContentKeyFunctions.ZoomIn);
+            AddButton(ContentKeyFunctions.ZoomOut);
+            AddButton(ContentKeyFunctions.ResetZoom);
 
             AddHeader("ui-options-header-interaction-basic");
             AddButton(EngineKeyFunctions.Use);
diff --git a/Content.Server/Movement/Systems/ContentEyeSystem.cs b/Content.Server/Movement/Systems/ContentEyeSystem.cs
new file mode 100644 (file)
index 0000000..9076028
--- /dev/null
@@ -0,0 +1,29 @@
+using Content.Shared.Movement.Components;
+using Content.Shared.Movement.Systems;
+
+namespace Content.Server.Movement.Systems;
+
+public sealed class ContentEyeSystem : SharedContentEyeSystem
+{
+    public override void Update(float frameTime)
+    {
+        base.Update(frameTime);
+
+        var eyeQuery = GetEntityQuery<SharedEyeComponent>();
+
+        foreach (var (_, comp) in EntityQuery<ActiveContentEyeComponent, ContentEyeComponent>(true))
+        {
+            var uid = comp.Owner;
+
+            // Use a separate query jjuussstt in case any actives mistakenly hang around.
+            if (!eyeQuery.TryGetComponent(comp.Owner, out var eyeComp) ||
+                eyeComp.Zoom.Equals(comp.TargetZoom))
+            {
+                RemComp<ActiveContentEyeComponent>(comp.Owner);
+                continue;
+            }
+
+            UpdateEye(uid, comp, eyeComp, frameTime);
+        }
+    }
+}
index f9742eb6d50c9f364df30efadc3e80091c37dda7..2492aba34703206301aba092f811da18c8f63b99 100644 (file)
@@ -43,6 +43,10 @@ namespace Content.Shared.Input
         public static readonly BoundKeyFunction TakeScreenshot = "TakeScreenshot";
         public static readonly BoundKeyFunction TakeScreenshotNoUI = "TakeScreenshotNoUI";
         public static readonly BoundKeyFunction Point = "Point";
+        public static readonly BoundKeyFunction ZoomOut = "ZoomOut";
+        public static readonly BoundKeyFunction ZoomIn = "ZoomIn";
+        public static readonly BoundKeyFunction ResetZoom = "ResetZoom";
+
         public static readonly BoundKeyFunction ArcadeUp = "ArcadeUp";
         public static readonly BoundKeyFunction ArcadeDown = "ArcadeDown";
         public static readonly BoundKeyFunction ArcadeLeft = "ArcadeLeft";
@@ -50,6 +54,7 @@ namespace Content.Shared.Input
         public static readonly BoundKeyFunction Arcade1 = "Arcade1";
         public static readonly BoundKeyFunction Arcade2 = "Arcade2";
         public static readonly BoundKeyFunction Arcade3 = "Arcade3";
+
         public static readonly BoundKeyFunction OpenActionsMenu = "OpenAbilitiesMenu";
         public static readonly BoundKeyFunction ShuttleStrafeLeft = "ShuttleStrafeLeft";
         public static readonly BoundKeyFunction ShuttleStrafeUp = "ShuttleStrafeUp";
diff --git a/Content.Shared/Movement/Components/ActiveContentEyeComponent.cs b/Content.Shared/Movement/Components/ActiveContentEyeComponent.cs
new file mode 100644 (file)
index 0000000..d81af98
--- /dev/null
@@ -0,0 +1,7 @@
+namespace Content.Shared.Movement.Components;
+
+[RegisterComponent]
+public sealed class ActiveContentEyeComponent : Component
+{
+
+}
diff --git a/Content.Shared/Movement/Components/ContentEyeComponent.cs b/Content.Shared/Movement/Components/ContentEyeComponent.cs
new file mode 100644 (file)
index 0000000..4af15ac
--- /dev/null
@@ -0,0 +1,22 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Movement.Components;
+
+/// <summary>
+/// Holds SS14 eye data not relevant for engine, e.g. lerp targets.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed class ContentEyeComponent : Component
+{
+    /// <summary>
+    /// Zoom we're lerping to.
+    /// </summary>
+    [DataField("targetZoom")]
+    public Vector2 TargetZoom = Vector2.One;
+
+    /// <summary>
+    /// How far we're allowed to zoom out.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField("maxZoom")]
+    public Vector2 MaxZoom = Vector2.One;
+}
diff --git a/Content.Shared/Movement/Systems/SharedContentEyeSystem.cs b/Content.Shared/Movement/Systems/SharedContentEyeSystem.cs
new file mode 100644 (file)
index 0000000..1131bea
--- /dev/null
@@ -0,0 +1,195 @@
+using Content.Shared.Input;
+using Content.Shared.Movement.Components;
+using Robust.Shared.GameStates;
+using Robust.Shared.Input;
+using Robust.Shared.Input.Binding;
+using Robust.Shared.Players;
+using Robust.Shared.Serialization;
+using Robust.Shared.Timing;
+
+namespace Content.Shared.Movement.Systems;
+
+/// <summary>
+/// Lets specific sessions scroll and set their zoom directly.
+/// </summary>
+public abstract class SharedContentEyeSystem : EntitySystem
+{
+    private const float ZoomMod = 1.2f;
+    private const byte ZoomMultiple = 10;
+
+    protected static readonly Vector2 MinZoom = new(MathF.Pow(ZoomMod, -ZoomMultiple), MathF.Pow(ZoomMod, -ZoomMultiple));
+
+    protected ISawmill Sawmill = Logger.GetSawmill("ceye");
+
+    public override void Initialize()
+    {
+        base.Initialize();
+        SubscribeLocalEvent<ContentEyeComponent, ComponentGetState>(OnGetState);
+        SubscribeLocalEvent<ContentEyeComponent, ComponentHandleState>(OnHandleState);
+        SubscribeLocalEvent<ContentEyeComponent, ComponentStartup>(OnContentEyeStartup);
+
+        CommandBinds.Builder
+            .Bind(ContentKeyFunctions.ZoomIn,  new ScrollInputCmdHandler(true, this))
+            .Bind(ContentKeyFunctions.ZoomOut, new ScrollInputCmdHandler(false, this))
+            .Bind(ContentKeyFunctions.ResetZoom, new ResetZoomInputCmdHandler(this))
+            .Register<SharedContentEyeSystem>();
+
+        Sawmill.Level = LogLevel.Info;
+    }
+
+    public override void Shutdown()
+    {
+        base.Shutdown();
+        CommandBinds.Unregister<SharedContentEyeSystem>();
+    }
+
+    private void OnContentEyeStartup(EntityUid uid, ContentEyeComponent component, ComponentStartup args)
+    {
+        if (!TryComp<SharedEyeComponent>(uid, out var eyeComp))
+            return;
+
+        component.TargetZoom = eyeComp.Zoom;
+        Dirty(component);
+    }
+
+    private void OnGetState(EntityUid uid, ContentEyeComponent component, ref ComponentGetState args)
+    {
+        args.State = new ContentEyeComponentState()
+        {
+            TargetZoom = component.TargetZoom,
+            MaxZoom = component.MaxZoom,
+        };
+    }
+
+    private void OnHandleState(EntityUid uid, ContentEyeComponent component, ref ComponentHandleState args)
+    {
+        if (args.Current is not ContentEyeComponentState state)
+            return;
+
+        component.TargetZoom = state.TargetZoom;
+        component.MaxZoom = state.MaxZoom;
+    }
+
+    protected void UpdateEye(EntityUid uid, ContentEyeComponent content, SharedEyeComponent eye, float frameTime)
+    {
+        var diff = content.TargetZoom - eye.Zoom;
+
+        if (diff.LengthSquared < 0.0000001f)
+        {
+            eye.Zoom = content.TargetZoom;
+            Dirty(eye);
+            RemComp<ActiveContentEyeComponent>(uid);
+            return;
+        }
+
+        var change = diff * 8f * frameTime;
+
+        eye.Zoom += change;
+        Dirty(eye);
+    }
+
+    private bool CanZoom(EntityUid uid, ContentEyeComponent? component = null)
+    {
+        return Resolve(uid, ref component, false);
+    }
+
+    private void ResetZoom(EntityUid uid, ContentEyeComponent? component = null)
+    {
+        if (!Resolve(uid, ref component))
+            return;
+
+        if (component.TargetZoom.Equals(Vector2.One))
+            return;
+
+        component.TargetZoom = Vector2.One;
+        EnsureComp<ActiveContentEyeComponent>(uid);
+        Dirty(component);
+    }
+
+    private void Zoom(EntityUid uid, bool zoomIn, ContentEyeComponent? component = null)
+    {
+        if (!Resolve(uid, ref component))
+            return;
+
+        var actual = component.TargetZoom;
+
+        if (zoomIn)
+        {
+            actual /= ZoomMod;
+        }
+        else
+        {
+            actual *= ZoomMod;
+        }
+
+        actual = Vector2.ComponentMax(MinZoom, actual);
+        actual = Vector2.ComponentMin(component.MaxZoom, actual);
+
+        if (actual.Equals(component.TargetZoom))
+            return;
+
+        component.TargetZoom = actual;
+        EnsureComp<ActiveContentEyeComponent>(uid);
+        Dirty(component);
+        Sawmill.Debug($"Set target zoom to {actual}");
+    }
+
+    [Serializable, NetSerializable]
+    private sealed class ContentEyeComponentState : ComponentState
+    {
+        public Vector2 TargetZoom;
+        public Vector2 MaxZoom;
+    }
+
+    private sealed class ResetZoomInputCmdHandler : InputCmdHandler
+    {
+        private readonly SharedContentEyeSystem _system;
+
+        public ResetZoomInputCmdHandler(SharedContentEyeSystem system)
+        {
+            _system = system;
+        }
+
+        public override bool HandleCmdMessage(ICommonSession? session, InputCmdMessage message)
+        {
+            ContentEyeComponent? component = null;
+
+            if (message is not FullInputCmdMessage full || session?.AttachedEntity == null ||
+                full.State != BoundKeyState.Down ||
+                !_system.CanZoom(session.AttachedEntity.Value, component))
+            {
+                return false;
+            }
+
+            _system.ResetZoom(session.AttachedEntity.Value, component);
+            return false;
+        }
+    }
+
+    private sealed class ScrollInputCmdHandler : InputCmdHandler
+    {
+        private readonly bool _zoomIn;
+        private readonly SharedContentEyeSystem _system;
+
+        public ScrollInputCmdHandler(bool zoomIn, SharedContentEyeSystem system)
+        {
+            _zoomIn = zoomIn;
+            _system = system;
+        }
+
+        public override bool HandleCmdMessage(ICommonSession? session, InputCmdMessage message)
+        {
+            ContentEyeComponent? component = null;
+
+            if (message is not FullInputCmdMessage full || session?.AttachedEntity == null ||
+                full.State != BoundKeyState.Down ||
+                !_system.CanZoom(session.AttachedEntity.Value, component))
+            {
+                return false;
+            }
+
+            _system.Zoom(session.AttachedEntity.Value, _zoomIn, component);
+            return false;
+        }
+    }
+}
index af825fff461f921e14996d03d68a0672c63ff692..27fd788b7c6b1e8807a78cdd2c75dac753b73d29 100644 (file)
@@ -92,6 +92,9 @@ ui-options-function-walk = Walk
 ui-options-function-camera-rotate-left = Rotate left
 ui-options-function-camera-rotate-right = Rotate right
 ui-options-function-camera-reset = Reset
+ui-options-function-zoom-in = Zoom in
+ui-options-function-zoom-out = Zoom out
+ui-options-function-reset-zoom = Reset zoom
 
 ui-options-function-use = Use
 ui-options-function-use-secondary = Use secondary
index fc0418c4e0fe287515afe6bfc9efa741c765935e..59381a5e5f70cd089db540f70acbbd5e68f00b32 100644 (file)
@@ -5,6 +5,8 @@
   name: admin observer
   noSpawn: true
   components:
+  - type: ContentEye
+    maxZoom: 8.916104, 8.916104
   - type: Tag
     tags:
     - CanPilot
index 1d4063743189f62d2c103f8b8a1b52dec82daada..85da0737570acd7d97fb2e5455b77c2a08f97578 100644 (file)
@@ -5,6 +5,8 @@
   save: false
   description: Boo!
   components:
+  - type: ContentEye
+    maxZoom: 1.44,1.44
   - type: Mind
   - type: Clickable
   - type: InteractionOutline
index 7a03fdb88d8beba9ba06407f3f12ad1e25d0c794..f30e9dea37bc8a3a8d6e4f9bbd76a91a41895ab9 100644 (file)
@@ -76,6 +76,15 @@ binds:
 - function: CameraReset
   type: State
   key: NumpadNum8
+- function: ZoomOut
+  type: State
+  key: NumpadNum4
+- function: ZoomIn
+  type: State
+  key: NumpadNum6
+- function: ResetZoom
+  type: State
+  key: NumpadNum5
 # Misc
 - function: ShowEscapeMenu
   type: State