]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Fix rotate verbs not being predicted (#38165)
authorDrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com>
Sun, 3 Aug 2025 00:34:36 +0000 (17:34 -0700)
committerGitHub <noreply@github.com>
Sun, 3 Aug 2025 00:34:36 +0000 (17:34 -0700)
* Fix rotate verbs not being predicted

* fixes

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
Content.Server/Rotatable/FlippableComponent.cs [deleted file]
Content.Server/Rotatable/RotatableSystem.cs [deleted file]
Content.Shared/Rotatable/FlippableComponent.cs [new file with mode: 0644]
Content.Shared/Rotatable/RotatableComponent.cs
Content.Shared/Rotatable/RotatableSystem.cs [new file with mode: 0644]
Resources/Locale/en-US/rotation/components/rotatable-component.ftl

diff --git a/Content.Server/Rotatable/FlippableComponent.cs b/Content.Server/Rotatable/FlippableComponent.cs
deleted file mode 100644 (file)
index 98eb0f6..0000000
+++ /dev/null
@@ -1,15 +0,0 @@
-using Robust.Shared.Prototypes;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
-
-namespace Content.Server.Rotatable
-{
-    [RegisterComponent]
-    public sealed partial class FlippableComponent : Component
-    {
-        /// <summary>
-        ///     Entity to replace this entity with when the current one is 'flipped'.
-        /// </summary>
-        [DataField("mirrorEntity", required: true, customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
-        public string MirrorEntity = default!;
-    }
-}
diff --git a/Content.Server/Rotatable/RotatableSystem.cs b/Content.Server/Rotatable/RotatableSystem.cs
deleted file mode 100644 (file)
index 5190b0e..0000000
+++ /dev/null
@@ -1,197 +0,0 @@
-using Content.Server.Popups;
-using Content.Shared.ActionBlocker;
-using Content.Shared.Input;
-using Content.Shared.Interaction;
-using Content.Shared.Rotatable;
-using Content.Shared.Verbs;
-using Robust.Shared.Input.Binding;
-using Robust.Shared.Map;
-using Robust.Shared.Player;
-using Robust.Shared.Physics;
-using Robust.Shared.Physics.Components;
-using Robust.Shared.Utility;
-
-namespace Content.Server.Rotatable
-{
-    /// <summary>
-    ///     Handles verbs for the <see cref="RotatableComponent"/> and <see cref="FlippableComponent"/> components.
-    /// </summary>
-    public sealed class RotatableSystem : EntitySystem
-    {
-        [Dependency] private readonly PopupSystem _popup = default!;
-        [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
-        [Dependency] private readonly SharedInteractionSystem _interaction = default!;
-        [Dependency] private readonly SharedTransformSystem _transform = default!;
-
-        public override void Initialize()
-        {
-            SubscribeLocalEvent<FlippableComponent, GetVerbsEvent<Verb>>(AddFlipVerb);
-            SubscribeLocalEvent<RotatableComponent, GetVerbsEvent<Verb>>(AddRotateVerbs);
-
-            CommandBinds.Builder
-                .Bind(ContentKeyFunctions.RotateObjectClockwise, new PointerInputCmdHandler(HandleRotateObjectClockwise))
-                .Bind(ContentKeyFunctions.RotateObjectCounterclockwise, new PointerInputCmdHandler(HandleRotateObjectCounterclockwise))
-                .Bind(ContentKeyFunctions.FlipObject, new PointerInputCmdHandler(HandleFlipObject))
-                .Register<RotatableSystem>();
-        }
-
-        private void AddFlipVerb(EntityUid uid, FlippableComponent component, GetVerbsEvent<Verb> args)
-        {
-            if (!args.CanAccess
-                || !args.CanInteract
-                || !args.CanComplexInteract)
-                return;
-
-            // Check if the object is anchored.
-            if (TryComp(uid, out PhysicsComponent? physics) && physics.BodyType == BodyType.Static)
-                return;
-
-            Verb verb = new()
-            {
-                Act = () => Flip(uid, component),
-                Text = Loc.GetString("flippable-verb-get-data-text"),
-                Category = VerbCategory.Rotate,
-                Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/flip.svg.192dpi.png")),
-                Priority = -3, // show flip last
-                DoContactInteraction = true
-            };
-            args.Verbs.Add(verb);
-        }
-
-        private void AddRotateVerbs(EntityUid uid, RotatableComponent component, GetVerbsEvent<Verb> args)
-        {
-            if (!args.CanAccess
-                || !args.CanInteract
-                || !args.CanComplexInteract
-                || Transform(uid).NoLocalRotation) // Good ol prototype inheritance, eh?
-                return;
-
-            // Check if the object is anchored, and whether we are still allowed to rotate it.
-            if (!component.RotateWhileAnchored &&
-                TryComp(uid, out PhysicsComponent? physics) &&
-                physics.BodyType == BodyType.Static)
-                return;
-
-            Verb resetRotation = new()
-            {
-                DoContactInteraction = true,
-                Act = () => Comp<TransformComponent>(uid).LocalRotation = Angle.Zero,
-                Category = VerbCategory.Rotate,
-                Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/refresh.svg.192dpi.png")),
-                Text = "Reset",
-                Priority = -2, // show CCW, then CW, then reset
-                CloseMenu = false,
-            };
-            args.Verbs.Add(resetRotation);
-
-            // rotate clockwise
-            Verb rotateCW = new()
-            {
-                Act = () => Comp<TransformComponent>(uid).LocalRotation -= component.Increment,
-                Category = VerbCategory.Rotate,
-                Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/rotate_cw.svg.192dpi.png")),
-                Priority = -1,
-                CloseMenu = false, // allow for easy double rotations.
-            };
-            args.Verbs.Add(rotateCW);
-
-            // rotate counter-clockwise
-            Verb rotateCCW = new()
-            {
-                Act = () => Comp<TransformComponent>(uid).LocalRotation += component.Increment,
-                Category = VerbCategory.Rotate,
-                Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/rotate_ccw.svg.192dpi.png")),
-                Priority = 0,
-                CloseMenu = false, // allow for easy double rotations.
-            };
-            args.Verbs.Add(rotateCCW);
-        }
-
-        /// <summary>
-        ///     Replace a flippable entity with it's flipped / mirror-symmetric entity.
-        /// </summary>
-        public void Flip(EntityUid uid, FlippableComponent component)
-        {
-            var oldTransform = Comp<TransformComponent>(uid);
-            var entity = Spawn(component.MirrorEntity, oldTransform.Coordinates);
-            var newTransform = Comp<TransformComponent>(entity);
-            newTransform.LocalRotation = oldTransform.LocalRotation;
-            _transform.Unanchor(entity, newTransform);
-            Del(uid);
-        }
-
-        public bool HandleRotateObjectClockwise(ICommonSession? playerSession, EntityCoordinates coordinates, EntityUid entity)
-        {
-            if (playerSession?.AttachedEntity is not { Valid: true } player || !Exists(player))
-                return false;
-
-            if (!TryComp<RotatableComponent>(entity, out var rotatableComp))
-                return false;
-
-            if (!_actionBlocker.CanInteract(player, entity)
-                || !_actionBlocker.CanComplexInteract(player)
-                || !_interaction.InRangeAndAccessible(player, entity))
-                return false;
-
-            // Check if the object is anchored, and whether we are still allowed to rotate it.
-            if (!rotatableComp.RotateWhileAnchored && TryComp(entity, out PhysicsComponent? physics) &&
-                physics.BodyType == BodyType.Static)
-            {
-                _popup.PopupEntity(Loc.GetString("rotatable-component-try-rotate-stuck"), entity, player);
-                return false;
-            }
-
-            Transform(entity).LocalRotation -= rotatableComp.Increment;
-            return true;
-        }
-
-        public bool HandleRotateObjectCounterclockwise(ICommonSession? playerSession, EntityCoordinates coordinates, EntityUid entity)
-        {
-            if (playerSession?.AttachedEntity is not { Valid: true } player || !Exists(player))
-                return false;
-
-            if (!TryComp<RotatableComponent>(entity, out var rotatableComp))
-                return false;
-
-            if (!_actionBlocker.CanInteract(player, entity)
-                || !_actionBlocker.CanComplexInteract(player)
-                || !_interaction.InRangeAndAccessible(player, entity))
-                return false;
-
-            // Check if the object is anchored, and whether we are still allowed to rotate it.
-            if (!rotatableComp.RotateWhileAnchored && TryComp(entity, out PhysicsComponent? physics) &&
-                physics.BodyType == BodyType.Static)
-            {
-                _popup.PopupEntity(Loc.GetString("rotatable-component-try-rotate-stuck"), entity, player);
-                return false;
-            }
-
-            Transform(entity).LocalRotation += rotatableComp.Increment;
-            return true;
-        }
-
-        public bool HandleFlipObject(ICommonSession? playerSession, EntityCoordinates coordinates, EntityUid entity)
-        {
-            if (playerSession?.AttachedEntity is not { Valid: true } player || !Exists(player))
-                return false;
-
-            if (!TryComp<FlippableComponent>(entity, out var flippableComp))
-                return false;
-
-            if (!_actionBlocker.CanInteract(player, entity)
-                || !_actionBlocker.CanComplexInteract(player)
-                || !_interaction.InRangeAndAccessible(player, entity))
-                return false;
-
-            // Check if the object is anchored.
-            if (TryComp(entity, out PhysicsComponent? physics) && physics.BodyType == BodyType.Static)
-            {
-                _popup.PopupEntity(Loc.GetString("flippable-component-try-flip-is-stuck"), entity, player);
-                return false;
-            }
-
-            Flip(entity, flippableComp);
-            return true;
-        }
-    }
-}
diff --git a/Content.Shared/Rotatable/FlippableComponent.cs b/Content.Shared/Rotatable/FlippableComponent.cs
new file mode 100644 (file)
index 0000000..fdc8c15
--- /dev/null
@@ -0,0 +1,17 @@
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Rotatable;
+
+/// <summary>
+/// Allows an entity to be flipped (mirrored) by using a verb.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class FlippableComponent : Component
+{
+    /// <summary>
+    /// Entity to replace this entity with when the current one is 'flipped'.
+    /// </summary>
+    [DataField(required: true), AutoNetworkedField]
+    public EntProtoId MirrorEntity = default!;
+}
index b008c281602240ed58a625a9c63a0eb78409013b..336c484d8c0f72331ac286880dbc83a86e70b51e 100644 (file)
@@ -1,27 +1,28 @@
-namespace Content.Shared.Rotatable
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Rotatable;
+
+/// <summary>
+/// Allows an entity to be rotated by using a verb.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class RotatableComponent : Component
 {
-    [RegisterComponent]
-    public sealed partial class RotatableComponent : Component
-    {
-        /// <summary>
-        ///     If true, this entity can be rotated even while anchored.
-        /// </summary>
-        [ViewVariables(VVAccess.ReadWrite)]
-        [DataField("rotateWhileAnchored")]
-        public bool RotateWhileAnchored { get; private set; }
+    /// <summary>
+    /// If true, this entity can be rotated even while anchored.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public bool RotateWhileAnchored;
 
-        /// <summary>
-        ///     If true, will rotate entity in players direction when pulled
-        /// </summary>
-        [ViewVariables(VVAccess.ReadWrite)]
-        [DataField("rotateWhilePulling")]
-        public bool RotateWhilePulling { get; private set; } = true;
+    /// <summary>
+    /// If true, will rotate entity in players direction when pulled
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public bool RotateWhilePulling = true;
 
-        /// <summary>
-        ///     The angular value to change when using the rotate verbs.
-        /// </summary>
-        [ViewVariables(VVAccess.ReadWrite)]
-        [DataField("increment")]
-        public Angle Increment { get; private set; } = Angle.FromDegrees(90);
-    }
+    /// <summary>
+    /// The angular value to change when using the rotate verbs.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public Angle Increment = Angle.FromDegrees(90);
 }
diff --git a/Content.Shared/Rotatable/RotatableSystem.cs b/Content.Shared/Rotatable/RotatableSystem.cs
new file mode 100644 (file)
index 0000000..94d9206
--- /dev/null
@@ -0,0 +1,213 @@
+using Content.Shared.ActionBlocker;
+using Content.Shared.Input;
+using Content.Shared.Interaction;
+using Content.Shared.Popups;
+using Content.Shared.Verbs;
+using Robust.Shared.Input.Binding;
+using Robust.Shared.Map;
+using Robust.Shared.Physics;
+using Robust.Shared.Physics.Components;
+using Robust.Shared.Player;
+using Robust.Shared.Utility;
+
+namespace Content.Shared.Rotatable;
+
+/// <summary>
+/// Handles verbs for the <see cref="RotatableComponent"/> and <see cref="FlippableComponent"/> components.
+/// </summary>
+public sealed class RotatableSystem : EntitySystem
+{
+    [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
+    [Dependency] private readonly SharedInteractionSystem _interaction = default!;
+    [Dependency] private readonly SharedPopupSystem _popup = default!;
+    [Dependency] private readonly SharedTransformSystem _transform = default!;
+
+    public override void Initialize()
+    {
+        SubscribeLocalEvent<FlippableComponent, GetVerbsEvent<Verb>>(AddFlipVerb);
+        SubscribeLocalEvent<RotatableComponent, GetVerbsEvent<Verb>>(AddRotateVerbs);
+
+        CommandBinds.Builder
+            .Bind(ContentKeyFunctions.RotateObjectClockwise, new PointerInputCmdHandler(HandleRotateObjectClockwise))
+            .Bind(ContentKeyFunctions.RotateObjectCounterclockwise, new PointerInputCmdHandler(HandleRotateObjectCounterclockwise))
+            .Bind(ContentKeyFunctions.FlipObject, new PointerInputCmdHandler(HandleFlipObject))
+            .Register<RotatableSystem>();
+    }
+
+    private void AddFlipVerb(EntityUid uid, FlippableComponent component, GetVerbsEvent<Verb> args)
+    {
+        if (!args.CanAccess
+            || !args.CanInteract
+            || !args.CanComplexInteract)
+            return;
+
+        // Check if the object is anchored.
+        if (TryComp<PhysicsComponent>(uid, out var physics) && physics.BodyType == BodyType.Static)
+            return;
+
+        Verb verb = new()
+        {
+            Act = () => Flip(uid, component),
+            Text = Loc.GetString("flippable-verb-get-data-text"),
+            Category = VerbCategory.Rotate,
+            Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/flip.svg.192dpi.png")),
+            Priority = -3, // show flip last
+            DoContactInteraction = true
+        };
+        args.Verbs.Add(verb);
+    }
+
+    private void AddRotateVerbs(EntityUid uid, RotatableComponent component, GetVerbsEvent<Verb> args)
+    {
+        if (!args.CanAccess
+            || !args.CanInteract
+            || !args.CanComplexInteract
+            || Transform(uid).NoLocalRotation) // Good ol prototype inheritance, eh?
+            return;
+
+        // Check if the object is anchored, and whether we are still allowed to rotate it.
+        if (!component.RotateWhileAnchored &&
+            TryComp<PhysicsComponent>(uid, out var physics) &&
+            physics.BodyType == BodyType.Static)
+            return;
+
+        Verb resetRotation = new()
+        {
+            DoContactInteraction = true,
+            Act = () => ResetRotation(uid),
+            Category = VerbCategory.Rotate,
+            Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/refresh.svg.192dpi.png")),
+            Text = Loc.GetString("rotate-reset-verb-get-data-text"),
+            Priority = -2, // show CCW, then CW, then reset
+            CloseMenu = false,
+        };
+        args.Verbs.Add(resetRotation);
+
+        // rotate clockwise
+        Verb rotateCW = new()
+        {
+            Act = () => Rotate(uid, -component.Increment),
+            Category = VerbCategory.Rotate,
+            Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/rotate_cw.svg.192dpi.png")),
+            Text = Loc.GetString("rotate-verb-get-data-text"),
+            Priority = -1,
+            CloseMenu = false, // allow for easy double rotations.
+        };
+        args.Verbs.Add(rotateCW);
+
+        // rotate counter-clockwise
+        Verb rotateCCW = new()
+        {
+            Act = () => Rotate(uid, component.Increment),
+            Category = VerbCategory.Rotate,
+            Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/VerbIcons/rotate_ccw.svg.192dpi.png")),
+            Text = Loc.GetString("rotate-counter-verb-get-data-text"),
+            Priority = 0,
+            CloseMenu = false, // allow for easy double rotations.
+        };
+        args.Verbs.Add(rotateCCW);
+    }
+
+    /// <summary>
+    /// Replace a flippable entity with it's flipped / mirror-symmetric entity.
+    /// </summary>
+    public void Flip(EntityUid uid, FlippableComponent component)
+    {
+        var oldTransform = Comp<TransformComponent>(uid);
+        var entity = PredictedSpawnAtPosition(component.MirrorEntity, oldTransform.Coordinates);
+        var newTransform = Comp<TransformComponent>(entity);
+        _transform.SetLocalRotation(entity, oldTransform.LocalRotation);
+        _transform.Unanchor(entity, newTransform);
+        PredictedDel(uid);
+    }
+
+    private bool HandleRotateObjectClockwise(ICommonSession? playerSession, EntityCoordinates coordinates, EntityUid entity)
+    {
+        if (playerSession?.AttachedEntity is not { Valid: true } player || !Exists(player))
+            return false;
+
+        if (!TryComp<RotatableComponent>(entity, out var rotatableComp))
+            return false;
+
+        if (!_actionBlocker.CanInteract(player, entity)
+            || !_actionBlocker.CanComplexInteract(player)
+            || !_interaction.InRangeAndAccessible(player, entity))
+            return false;
+
+        // Check if the object is anchored, and whether we are still allowed to rotate it.
+        if (!rotatableComp.RotateWhileAnchored && TryComp<PhysicsComponent>(entity, out var physics) &&
+            physics.BodyType == BodyType.Static)
+        {
+            _popup.PopupClient(Loc.GetString("rotatable-component-try-rotate-stuck"), entity, player);
+            return false;
+        }
+
+        Rotate(entity, -rotatableComp.Increment);
+        return false;
+    }
+
+    private bool HandleRotateObjectCounterclockwise(ICommonSession? playerSession, EntityCoordinates coordinates, EntityUid entity)
+    {
+        if (playerSession?.AttachedEntity is not { Valid: true } player || !Exists(player))
+            return false;
+
+        if (!TryComp<RotatableComponent>(entity, out var rotatableComp))
+            return false;
+
+        if (!_actionBlocker.CanInteract(player, entity)
+            || !_actionBlocker.CanComplexInteract(player)
+            || !_interaction.InRangeAndAccessible(player, entity))
+            return false;
+
+        // Check if the object is anchored, and whether we are still allowed to rotate it.
+        if (!rotatableComp.RotateWhileAnchored && TryComp<PhysicsComponent>(entity, out var physics) &&
+            physics.BodyType == BodyType.Static)
+        {
+            _popup.PopupClient(Loc.GetString("rotatable-component-try-rotate-stuck"), entity, player);
+            return false;
+        }
+
+        Rotate(entity, rotatableComp.Increment);
+        return false;
+    }
+
+    private bool HandleFlipObject(ICommonSession? playerSession, EntityCoordinates coordinates, EntityUid entity)
+    {
+        if (playerSession?.AttachedEntity is not { Valid: true } player || !Exists(player))
+            return false;
+
+        if (!TryComp<FlippableComponent>(entity, out var flippableComp))
+            return false;
+
+        if (!_actionBlocker.CanInteract(player, entity)
+            || !_actionBlocker.CanComplexInteract(player)
+            || !_interaction.InRangeAndAccessible(player, entity))
+            return false;
+
+        // Check if the object is anchored.
+        if (TryComp<PhysicsComponent>(entity, out var physics) && physics.BodyType == BodyType.Static)
+        {
+            _popup.PopupClient(Loc.GetString("flippable-component-try-flip-is-stuck"), entity, player);
+            return false;
+        }
+
+        Flip(entity, flippableComp);
+        return false;
+    }
+
+    private void Rotate(Entity<TransformComponent?> ent, Angle angle)
+    {
+        if (!Resolve(ent, ref ent.Comp, false))
+            return;
+
+        _transform.SetLocalRotation(ent.Owner, ent.Comp.LocalRotation + angle);
+    }
+
+    private void ResetRotation(Entity<TransformComponent?> ent)
+    {
+        if (!Resolve(ent, ref ent.Comp, false))
+            return;
+
+        _transform.SetLocalRotation(ent.Owner, Angle.Zero);
+    }
+}
index bc23e488fb79c7d739b4329cc56550c1e46e1007..18d8d55bdd15db454aa061be489f8164d05aa9a8 100644 (file)
@@ -6,3 +6,6 @@ rotate-verb-get-data-text = Rotate clockwise
 
 # RotateCounterVerb
 rotate-counter-verb-get-data-text = Rotate counter-clockwise
+
+# ResetVerb
+rotate-reset-verb-get-data-text = Reset