From 5c782d30283d5e3661e1ce6ebc6d972f12671c41 Mon Sep 17 00:00:00 2001 From: SlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com> Date: Tue, 28 Jan 2025 00:20:45 +0100 Subject: [PATCH] Add conditional camera offset based on cursor - Hristov Rework, Part 1 (#31626) --- .../Components/EyeCursorOffsetComponent.cs | 19 ++++ .../Movement/Systems/ContentEyeSystem.cs | 11 +++ .../Movement/Systems/EyeCursorOffsetSystem.cs | 91 +++++++++++++++++++ Content.Client/Wieldable/WieldableSystem.cs | 49 ++++++++++ .../Components/EyeCursorOffsetComponent.cs | 12 +++ Content.Server/Wieldable/WieldableSystem.cs | 45 +++++++++ Content.Shared/Camera/GetEyeOffsetEvent.cs | 13 +++ Content.Shared/Camera/GetEyePvsScaleEvent.cs | 33 +++++++ .../Camera/SharedCameraRecoilSystem.cs | 8 +- .../EntitySystems/SharedHandsSystem.Relay.cs | 3 + .../CursorOffsetRequiresWieldComponent.cs | 13 +++ .../Components/EyeCursorOffsetComponent.cs | 32 +++++++ .../Systems/SharedContentEyeSystem.cs | 22 ++++- .../Components/MeleeRequiresWieldComponent.cs | 2 +- .../Components/GunRequiresWieldComponent.cs | 2 +- .../Components/GunWieldBonusComponent.cs | 2 +- .../IncreaseDamageOnWieldComponent.cs | 2 +- .../Components/WieldableComponent.cs | 2 +- ...ableSystem.cs => SharedWieldableSystem.cs} | 4 +- .../Objects/Weapons/Guns/Snipers/snipers.yml | 4 + 20 files changed, 357 insertions(+), 12 deletions(-) create mode 100644 Content.Client/Movement/Components/EyeCursorOffsetComponent.cs create mode 100644 Content.Client/Movement/Systems/EyeCursorOffsetSystem.cs create mode 100644 Content.Client/Wieldable/WieldableSystem.cs create mode 100644 Content.Server/Movement/Components/EyeCursorOffsetComponent.cs create mode 100644 Content.Server/Wieldable/WieldableSystem.cs create mode 100644 Content.Shared/Camera/GetEyePvsScaleEvent.cs create mode 100644 Content.Shared/Movement/Components/CursorOffsetRequiresWieldComponent.cs create mode 100644 Content.Shared/Movement/Components/EyeCursorOffsetComponent.cs rename Content.Shared/Wieldable/{WieldableSystem.cs => SharedWieldableSystem.cs} (99%) diff --git a/Content.Client/Movement/Components/EyeCursorOffsetComponent.cs b/Content.Client/Movement/Components/EyeCursorOffsetComponent.cs new file mode 100644 index 0000000000..5ed1bf4a6c --- /dev/null +++ b/Content.Client/Movement/Components/EyeCursorOffsetComponent.cs @@ -0,0 +1,19 @@ +using System.Numerics; +using Content.Client.Movement.Systems; +using Content.Shared.Movement.Components; + +namespace Content.Client.Movement.Components; + +[RegisterComponent] +public sealed partial class EyeCursorOffsetComponent : SharedEyeCursorOffsetComponent +{ + /// + /// The location the offset will attempt to pan towards; based on the cursor's position in the game window. + /// + public Vector2 TargetPosition = Vector2.Zero; + + /// + /// The current positional offset being applied. Used to enable gradual panning. + /// + public Vector2 CurrentPosition = Vector2.Zero; +} diff --git a/Content.Client/Movement/Systems/ContentEyeSystem.cs b/Content.Client/Movement/Systems/ContentEyeSystem.cs index 9fbd4b5c37..518a4a1bd4 100644 --- a/Content.Client/Movement/Systems/ContentEyeSystem.cs +++ b/Content.Client/Movement/Systems/ContentEyeSystem.cs @@ -1,6 +1,7 @@ using System.Numerics; using Content.Shared.Movement.Components; using Content.Shared.Movement.Systems; +using Robust.Client.GameObjects; using Robust.Client.Player; namespace Content.Client.Movement.Systems; @@ -52,4 +53,14 @@ public sealed class ContentEyeSystem : SharedContentEyeSystem { RaisePredictiveEvent(new RequestEyeEvent(drawFov, drawLight)); } + + public override void FrameUpdate(float frameTime) + { + base.FrameUpdate(frameTime); + var eyeEntities = AllEntityQuery(); + while (eyeEntities.MoveNext(out var entity, out ContentEyeComponent? contentComponent, out EyeComponent? eyeComponent)) + { + UpdateEyeOffset((entity, eyeComponent)); + } + } } diff --git a/Content.Client/Movement/Systems/EyeCursorOffsetSystem.cs b/Content.Client/Movement/Systems/EyeCursorOffsetSystem.cs new file mode 100644 index 0000000000..f3bff9ef0c --- /dev/null +++ b/Content.Client/Movement/Systems/EyeCursorOffsetSystem.cs @@ -0,0 +1,91 @@ +using System.Numerics; +using Content.Client.Movement.Components; +using Content.Shared.Camera; +using Content.Shared.Inventory; +using Content.Shared.Movement.Systems; +using Robust.Client.Graphics; +using Robust.Client.Input; +using Robust.Shared.Map; +using Robust.Client.Player; + +namespace Content.Client.Movement.Systems; + +public partial class EyeCursorOffsetSystem : EntitySystem +{ + [Dependency] private readonly IEyeManager _eyeManager = default!; + [Dependency] private readonly IInputManager _inputManager = default!; + [Dependency] private readonly IPlayerManager _player = default!; + [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly SharedContentEyeSystem _contentEye = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly IClyde _clyde = default!; + + // This value is here to make sure the user doesn't have to move their mouse + // all the way out to the edge of the screen to get the full offset. + static private float _edgeOffset = 0.9f; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGetEyeOffsetEvent); + } + + private void OnGetEyeOffsetEvent(EntityUid uid, EyeCursorOffsetComponent component, ref GetEyeOffsetEvent args) + { + var offset = OffsetAfterMouse(uid, component); + if (offset == null) + return; + + args.Offset += offset.Value; + } + + public Vector2? OffsetAfterMouse(EntityUid uid, EyeCursorOffsetComponent? component) + { + var localPlayer = _player.LocalPlayer?.ControlledEntity; + var mousePos = _inputManager.MouseScreenPosition; + var screenSize = _clyde.MainWindow.Size; + var minValue = MathF.Min(screenSize.X / 2, screenSize.Y / 2) * _edgeOffset; + + var mouseNormalizedPos = new Vector2(-(mousePos.X - screenSize.X / 2) / minValue, (mousePos.Y - screenSize.Y / 2) / minValue); // X needs to be inverted here for some reason, otherwise it ends up flipped. + + if (localPlayer == null) + return null; + + var playerPos = _transform.GetWorldPosition(localPlayer.Value); + + if (component == null) + { + component = EnsureComp(uid); + } + + // Doesn't move the offset if the mouse has left the game window! + if (mousePos.Window != WindowId.Invalid) + { + // The offset must account for the in-world rotation. + var eyeRotation = _eyeManager.CurrentEye.Rotation; + var mouseActualRelativePos = Vector2.Transform(mouseNormalizedPos, System.Numerics.Quaternion.CreateFromAxisAngle(-System.Numerics.Vector3.UnitZ, (float)(eyeRotation.Opposite().Theta))); // I don't know, it just works. + + // Caps the offset into a circle around the player. + mouseActualRelativePos *= component.MaxOffset; + if (mouseActualRelativePos.Length() > component.MaxOffset) + { + mouseActualRelativePos = mouseActualRelativePos.Normalized() * component.MaxOffset; + } + + component.TargetPosition = mouseActualRelativePos; + + //Makes the view not jump immediately when moving the cursor fast. + if (component.CurrentPosition != component.TargetPosition) + { + Vector2 vectorOffset = component.TargetPosition - component.CurrentPosition; + if (vectorOffset.Length() > component.OffsetSpeed) + { + vectorOffset = vectorOffset.Normalized() * component.OffsetSpeed; + } + component.CurrentPosition += vectorOffset; + } + } + return component.CurrentPosition; + } +} diff --git a/Content.Client/Wieldable/WieldableSystem.cs b/Content.Client/Wieldable/WieldableSystem.cs new file mode 100644 index 0000000000..2de837923c --- /dev/null +++ b/Content.Client/Wieldable/WieldableSystem.cs @@ -0,0 +1,49 @@ +using System.Numerics; +using Content.Client.Movement.Components; +using Content.Client.Movement.Systems; +using Content.Shared.Camera; +using Content.Shared.Hands; +using Content.Shared.Movement.Components; +using Content.Shared.Wieldable; +using Content.Shared.Wieldable.Components; +using Robust.Client.Timing; + +namespace Content.Client.Wieldable; + +public sealed class WieldableSystem : SharedWieldableSystem +{ + [Dependency] private readonly EyeCursorOffsetSystem _eyeOffset = default!; + [Dependency] private readonly IClientGameTiming _gameTiming = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnEyeOffsetUnwielded); + SubscribeLocalEvent>(OnGetEyeOffset); + } + + public void OnEyeOffsetUnwielded(Entity entity, ref ItemUnwieldedEvent args) + { + if (!TryComp(entity.Owner, out EyeCursorOffsetComponent? cursorOffsetComp)) + return; + + if (_gameTiming.IsFirstTimePredicted) + cursorOffsetComp.CurrentPosition = Vector2.Zero; + } + + public void OnGetEyeOffset(Entity entity, ref HeldRelayedEvent args) + { + if (!TryComp(entity.Owner, out WieldableComponent? wieldableComp)) + return; + + if (!wieldableComp.Wielded) + return; + + var offset = _eyeOffset.OffsetAfterMouse(entity.Owner, null); + if (offset == null) + return; + + args.Args.Offset += offset.Value; + } +} diff --git a/Content.Server/Movement/Components/EyeCursorOffsetComponent.cs b/Content.Server/Movement/Components/EyeCursorOffsetComponent.cs new file mode 100644 index 0000000000..b3809fe794 --- /dev/null +++ b/Content.Server/Movement/Components/EyeCursorOffsetComponent.cs @@ -0,0 +1,12 @@ +using System.Numerics; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; +using Robust.Shared.GameStates; + +namespace Content.Server.Movement.Components; + +[RegisterComponent] +public sealed partial class EyeCursorOffsetComponent : SharedEyeCursorOffsetComponent +{ + +} diff --git a/Content.Server/Wieldable/WieldableSystem.cs b/Content.Server/Wieldable/WieldableSystem.cs new file mode 100644 index 0000000000..35f75276a8 --- /dev/null +++ b/Content.Server/Wieldable/WieldableSystem.cs @@ -0,0 +1,45 @@ +using Content.Server.Movement.Components; +using Content.Server.Movement.Systems; +using Content.Shared.Camera; +using Content.Shared.Hands; +using Content.Shared.Movement.Components; +using Content.Shared.Wieldable; +using Content.Shared.Wieldable.Components; + +namespace Content.Server.Wieldable; + +public sealed class WieldableSystem : SharedWieldableSystem +{ + [Dependency] private readonly ContentEyeSystem _eye = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnEyeOffsetUnwielded); + SubscribeLocalEvent(OnEyeOffsetWielded); + SubscribeLocalEvent>(OnGetEyePvsScale); + } + + private void OnEyeOffsetUnwielded(Entity entity, ref ItemUnwieldedEvent args) + { + _eye.UpdatePvsScale(args.User); + } + + private void OnEyeOffsetWielded(Entity entity, ref ItemWieldedEvent args) + { + _eye.UpdatePvsScale(args.User); + } + + private void OnGetEyePvsScale(Entity entity, + ref HeldRelayedEvent args) + { + if (!TryComp(entity, out EyeCursorOffsetComponent? eyeCursorOffset) || !TryComp(entity.Owner, out WieldableComponent? wieldableComp)) + return; + + if (!wieldableComp.Wielded) + return; + + args.Args.Scale += eyeCursorOffset.PvsIncrease; + } +} diff --git a/Content.Shared/Camera/GetEyeOffsetEvent.cs b/Content.Shared/Camera/GetEyeOffsetEvent.cs index de9c7c9e17..0e3c00110a 100644 --- a/Content.Shared/Camera/GetEyeOffsetEvent.cs +++ b/Content.Shared/Camera/GetEyeOffsetEvent.cs @@ -1,4 +1,5 @@ using System.Numerics; +using Content.Shared.Inventory; using Content.Shared.Movement.Systems; namespace Content.Shared.Camera; @@ -17,3 +18,15 @@ namespace Content.Shared.Camera; /// [ByRefEvent] public record struct GetEyeOffsetEvent(Vector2 Offset); + +/// +/// Raised on any equipped and in-hand items that may modify the eye offset. +/// Pockets and suitstorage are excluded. +/// +[ByRefEvent] +public sealed class GetEyeOffsetRelayedEvent : EntityEventArgs, IInventoryRelayEvent +{ + public SlotFlags TargetSlots { get; } = ~(SlotFlags.POCKET & SlotFlags.SUITSTORAGE); + + public Vector2 Offset; +} diff --git a/Content.Shared/Camera/GetEyePvsScaleEvent.cs b/Content.Shared/Camera/GetEyePvsScaleEvent.cs new file mode 100644 index 0000000000..482b755db8 --- /dev/null +++ b/Content.Shared/Camera/GetEyePvsScaleEvent.cs @@ -0,0 +1,33 @@ +using System.Numerics; +using Content.Shared.Inventory; +using Content.Shared.Movement.Systems; + +namespace Content.Shared.Camera; + +/// +/// Raised directed by-ref when is called. +/// Should be subscribed to by any systems that want to modify an entity's eye PVS scale, +/// so that they do not override each other. Keep in mind that this should be done serverside; +/// the client may set a new PVS scale, but the server won't provide the data if it isn't done on the server. +/// +/// +/// The total scale to apply. +/// +/// +/// Note that in most cases should be incremented or decremented by subscribers, not set. +/// Otherwise, any offsets applied by previous subscribing systems will be overridden. +/// +[ByRefEvent] +public record struct GetEyePvsScaleEvent(float Scale); + +/// +/// Raised on any equipped and in-hand items that may modify the eye offset. +/// Pockets and suitstorage are excluded. +/// +[ByRefEvent] +public sealed class GetEyePvsScaleRelayedEvent : EntityEventArgs, IInventoryRelayEvent +{ + public SlotFlags TargetSlots { get; } = ~(SlotFlags.POCKET & SlotFlags.SUITSTORAGE); + + public float Scale; +} diff --git a/Content.Shared/Camera/SharedCameraRecoilSystem.cs b/Content.Shared/Camera/SharedCameraRecoilSystem.cs index a2ce0e77e2..a497016c46 100644 --- a/Content.Shared/Camera/SharedCameraRecoilSystem.cs +++ b/Content.Shared/Camera/SharedCameraRecoilSystem.cs @@ -1,4 +1,6 @@ using System.Numerics; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Systems; using JetBrains.Annotations; using Robust.Shared.Network; using Robust.Shared.Serialization; @@ -28,7 +30,7 @@ public abstract class SharedCameraRecoilSystem : EntitySystem /// protected const float KickMagnitudeMax = 1f; - [Dependency] private readonly SharedEyeSystem _eye = default!; + [Dependency] private readonly SharedContentEyeSystem _eye = default!; [Dependency] private readonly INetManager _net = default!; public override void Initialize() @@ -81,9 +83,7 @@ public abstract class SharedCameraRecoilSystem : EntitySystem continue; recoil.LastKick = recoil.CurrentKick; - var ev = new GetEyeOffsetEvent(); - RaiseLocalEvent(uid, ref ev); - _eye.SetOffset(uid, ev.Offset, eye); + _eye.UpdateEyeOffset((uid, eye)); } } diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Relay.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Relay.cs index 9e8e0fa744..a0e02cf2e1 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Relay.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Relay.cs @@ -1,3 +1,4 @@ +using Content.Shared.Camera; using Content.Shared.Hands.Components; using Content.Shared.Movement.Systems; @@ -7,6 +8,8 @@ public abstract partial class SharedHandsSystem { private void InitializeRelay() { + SubscribeLocalEvent(RelayEvent); + SubscribeLocalEvent(RelayEvent); SubscribeLocalEvent(RelayEvent); } diff --git a/Content.Shared/Movement/Components/CursorOffsetRequiresWieldComponent.cs b/Content.Shared/Movement/Components/CursorOffsetRequiresWieldComponent.cs new file mode 100644 index 0000000000..3f5a12874b --- /dev/null +++ b/Content.Shared/Movement/Components/CursorOffsetRequiresWieldComponent.cs @@ -0,0 +1,13 @@ +using Content.Shared.Wieldable; +using Robust.Shared.GameStates; + +namespace Content.Shared.Movement.Components; + +/// +/// Indicates that this item requires wielding for the cursor offset effect to be active. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedWieldableSystem))] +public sealed partial class CursorOffsetRequiresWieldComponent : Component +{ + +} diff --git a/Content.Shared/Movement/Components/EyeCursorOffsetComponent.cs b/Content.Shared/Movement/Components/EyeCursorOffsetComponent.cs new file mode 100644 index 0000000000..e0884222f2 --- /dev/null +++ b/Content.Shared/Movement/Components/EyeCursorOffsetComponent.cs @@ -0,0 +1,32 @@ +using System.Numerics; +using Content.Shared.Movement.Systems; +using Robust.Shared.GameStates; + +namespace Content.Shared.Movement.Components; + +/// +/// Displaces SS14 eye data when given to an entity. +/// +[ComponentProtoName("EyeCursorOffset"), NetworkedComponent] +public abstract partial class SharedEyeCursorOffsetComponent : Component +{ + /// + /// The amount the view will be displaced when the cursor is positioned at/beyond the max offset distance. + /// Measured in tiles. + /// + [DataField] + public float MaxOffset = 3f; + + /// + /// The speed which the camera adjusts to new positions. 0.5f seems like a good value, but can be changed if you want very slow/instant adjustments. + /// + [DataField] + public float OffsetSpeed = 0.5f; + + /// + /// The amount the PVS should increase to account for the max offset. + /// Should be 1/10 of MaxOffset most of the time. + /// + [DataField] + public float PvsIncrease = 0.3f; +} diff --git a/Content.Shared/Movement/Systems/SharedContentEyeSystem.cs b/Content.Shared/Movement/Systems/SharedContentEyeSystem.cs index faade44c85..71bc65a79e 100644 --- a/Content.Shared/Movement/Systems/SharedContentEyeSystem.cs +++ b/Content.Shared/Movement/Systems/SharedContentEyeSystem.cs @@ -140,11 +140,29 @@ public abstract class SharedContentEyeSystem : EntitySystem Dirty(uid, component); } - public void UpdateEyeOffset(Entity eye) + public void UpdateEyeOffset(Entity eye) { var ev = new GetEyeOffsetEvent(); RaiseLocalEvent(eye, ref ev); - _eye.SetOffset(eye, ev.Offset, eye); + + var evRelayed = new GetEyeOffsetRelayedEvent(); + RaiseLocalEvent(eye, ref evRelayed); + + _eye.SetOffset(eye, ev.Offset + evRelayed.Offset, eye); + } + + public void UpdatePvsScale(EntityUid uid, ContentEyeComponent? contentEye = null, EyeComponent? eye = null) + { + if (!Resolve(uid, ref contentEye) || !Resolve(uid, ref eye)) + return; + + var ev = new GetEyePvsScaleEvent(); + RaiseLocalEvent(uid, ref ev); + + var evRelayed = new GetEyePvsScaleRelayedEvent(); + RaiseLocalEvent(uid, ref evRelayed); + + _eye.SetPvsScale((uid, eye), 1 + ev.Scale + evRelayed.Scale); } /// diff --git a/Content.Shared/Weapons/Melee/Components/MeleeRequiresWieldComponent.cs b/Content.Shared/Weapons/Melee/Components/MeleeRequiresWieldComponent.cs index b322bbe51b..ffa78ba42a 100644 --- a/Content.Shared/Weapons/Melee/Components/MeleeRequiresWieldComponent.cs +++ b/Content.Shared/Weapons/Melee/Components/MeleeRequiresWieldComponent.cs @@ -6,7 +6,7 @@ namespace Content.Shared.Weapons.Melee.Components; /// /// Indicates that this meleeweapon requires wielding to be useable. /// -[RegisterComponent, NetworkedComponent, Access(typeof(WieldableSystem))] +[RegisterComponent, NetworkedComponent, Access(typeof(SharedWieldableSystem))] public sealed partial class MeleeRequiresWieldComponent : Component { diff --git a/Content.Shared/Weapons/Ranged/Components/GunRequiresWieldComponent.cs b/Content.Shared/Weapons/Ranged/Components/GunRequiresWieldComponent.cs index 2016459b9d..d5016c90e7 100644 --- a/Content.Shared/Weapons/Ranged/Components/GunRequiresWieldComponent.cs +++ b/Content.Shared/Weapons/Ranged/Components/GunRequiresWieldComponent.cs @@ -7,7 +7,7 @@ namespace Content.Shared.Weapons.Ranged.Components; /// Indicates that this gun requires wielding to be useable. /// [RegisterComponent, NetworkedComponent, AutoGenerateComponentState] -[Access(typeof(WieldableSystem))] +[Access(typeof(SharedWieldableSystem))] public sealed partial class GunRequiresWieldComponent : Component { [DataField, AutoNetworkedField] diff --git a/Content.Shared/Weapons/Ranged/Components/GunWieldBonusComponent.cs b/Content.Shared/Weapons/Ranged/Components/GunWieldBonusComponent.cs index 522319ccbd..bd2e9d4dd1 100644 --- a/Content.Shared/Weapons/Ranged/Components/GunWieldBonusComponent.cs +++ b/Content.Shared/Weapons/Ranged/Components/GunWieldBonusComponent.cs @@ -6,7 +6,7 @@ namespace Content.Shared.Weapons.Ranged.Components; /// /// Applies an accuracy bonus upon wielding. /// -[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(WieldableSystem))] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, Access(typeof(SharedWieldableSystem))] public sealed partial class GunWieldBonusComponent : Component { [ViewVariables(VVAccess.ReadWrite), DataField("minAngle"), AutoNetworkedField] diff --git a/Content.Shared/Wieldable/Components/IncreaseDamageOnWieldComponent.cs b/Content.Shared/Wieldable/Components/IncreaseDamageOnWieldComponent.cs index 3336923b02..86afe1897b 100644 --- a/Content.Shared/Wieldable/Components/IncreaseDamageOnWieldComponent.cs +++ b/Content.Shared/Wieldable/Components/IncreaseDamageOnWieldComponent.cs @@ -2,7 +2,7 @@ using Content.Shared.Damage; namespace Content.Shared.Wieldable.Components; -[RegisterComponent, Access(typeof(WieldableSystem))] +[RegisterComponent, Access(typeof(SharedWieldableSystem))] public sealed partial class IncreaseDamageOnWieldComponent : Component { [DataField("damage", required: true)] diff --git a/Content.Shared/Wieldable/Components/WieldableComponent.cs b/Content.Shared/Wieldable/Components/WieldableComponent.cs index 5dc6abbbbe..c146940b7a 100644 --- a/Content.Shared/Wieldable/Components/WieldableComponent.cs +++ b/Content.Shared/Wieldable/Components/WieldableComponent.cs @@ -7,7 +7,7 @@ namespace Content.Shared.Wieldable.Components; /// /// Used for objects that can be wielded in two or more hands, /// -[RegisterComponent, NetworkedComponent, Access(typeof(WieldableSystem)), AutoGenerateComponentState] +[RegisterComponent, NetworkedComponent, Access(typeof(SharedWieldableSystem)), AutoGenerateComponentState] public sealed partial class WieldableComponent : Component { [DataField("wieldSound")] diff --git a/Content.Shared/Wieldable/WieldableSystem.cs b/Content.Shared/Wieldable/SharedWieldableSystem.cs similarity index 99% rename from Content.Shared/Wieldable/WieldableSystem.cs rename to Content.Shared/Wieldable/SharedWieldableSystem.cs index 200a50d28f..24c63c5f2f 100644 --- a/Content.Shared/Wieldable/WieldableSystem.cs +++ b/Content.Shared/Wieldable/SharedWieldableSystem.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Shared.Camera; using Content.Shared.Examine; using Content.Shared.Hands; using Content.Shared.Hands.Components; @@ -7,6 +8,7 @@ using Content.Shared.IdentityManagement; using Content.Shared.Interaction.Events; using Content.Shared.Inventory.VirtualItem; using Content.Shared.Item; +using Content.Shared.Movement.Components; using Content.Shared.Popups; using Content.Shared.Timing; using Content.Shared.Verbs; @@ -23,7 +25,7 @@ using Robust.Shared.Timing; namespace Content.Shared.Wieldable; -public sealed class WieldableSystem : EntitySystem +public abstract class SharedWieldableSystem : EntitySystem { [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly INetManager _netManager = default!; diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml index 4ea0061c96..cca3a6f302 100644 --- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml +++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/Snipers/snipers.yml @@ -63,6 +63,10 @@ - CartridgeAntiMateriel capacity: 5 proto: CartridgeAntiMateriel + - type: CursorOffsetRequiresWield + - type: EyeCursorOffset + maxOffset: 3 + pvsIncrease: 0.3 - type: entity name: musket