Add scroll zooming for admins and ghosts (#14061)
* 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>
This commit is contained in:
@@ -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);
|
||||
|
||||
26
Content.Client/Movement/Systems/ContentEyeSystem.cs
Normal file
26
Content.Client/Movement/Systems/ContentEyeSystem.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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);
|
||||
|
||||
29
Content.Server/Movement/Systems/ContentEyeSystem.cs
Normal file
29
Content.Server/Movement/Systems/ContentEyeSystem.cs
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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";
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace Content.Shared.Movement.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class ActiveContentEyeComponent : Component
|
||||
{
|
||||
|
||||
}
|
||||
22
Content.Shared/Movement/Components/ContentEyeComponent.cs
Normal file
22
Content.Shared/Movement/Components/ContentEyeComponent.cs
Normal file
@@ -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;
|
||||
}
|
||||
195
Content.Shared/Movement/Systems/SharedContentEyeSystem.cs
Normal file
195
Content.Shared/Movement/Systems/SharedContentEyeSystem.cs
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
name: admin observer
|
||||
noSpawn: true
|
||||
components:
|
||||
- type: ContentEye
|
||||
maxZoom: 8.916104, 8.916104
|
||||
- type: Tag
|
||||
tags:
|
||||
- CanPilot
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
save: false
|
||||
description: Boo!
|
||||
components:
|
||||
- type: ContentEye
|
||||
maxZoom: 1.44,1.44
|
||||
- type: Mind
|
||||
- type: Clickable
|
||||
- type: InteractionOutline
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user