diff --git a/Content.Client/MouseRotator/MouseRotatorSystem.cs b/Content.Client/MouseRotator/MouseRotatorSystem.cs
index 4b7f937347..9615862dc5 100644
--- a/Content.Client/MouseRotator/MouseRotatorSystem.cs
+++ b/Content.Client/MouseRotator/MouseRotatorSystem.cs
@@ -41,6 +41,22 @@ public sealed class MouseRotatorSystem : SharedMouseRotatorSystem
var curRot = _transform.GetWorldRotation(xform);
+ // 4-dir handling is separate --
+ // only raise event if the cardinal direction has changed
+ if (rotator.Simple4DirMode)
+ {
+ var angleDir = angle.GetCardinalDir();
+ if (angleDir == curRot.GetCardinalDir())
+ return;
+
+ RaisePredictiveEvent(new RequestMouseRotatorRotationSimpleEvent()
+ {
+ Direction = angleDir,
+ });
+
+ return;
+ }
+
// Don't raise event if mouse ~hasn't moved (or if too close to goal rotation already)
var diff = Angle.ShortestDistance(angle, curRot);
if (Math.Abs(diff.Theta) < rotator.AngleTolerance.Theta)
diff --git a/Content.Shared/CombatMode/CombatModeComponent.cs b/Content.Shared/CombatMode/CombatModeComponent.cs
index ace8105b99..6696f8af80 100644
--- a/Content.Shared/CombatMode/CombatModeComponent.cs
+++ b/Content.Shared/CombatMode/CombatModeComponent.cs
@@ -1,3 +1,5 @@
+using Content.Shared.MouseRotator;
+using Content.Shared.Movement.Components;
using Content.Shared.Targeting;
using Robust.Shared.Audio;
using Robust.Shared.GameStates;
@@ -41,6 +43,13 @@ namespace Content.Shared.CombatMode
[ViewVariables(VVAccess.ReadWrite), DataField("isInCombatMode"), AutoNetworkedField]
public bool IsInCombatMode;
+ ///
+ /// Will add and
+ /// to entities with this flag enabled that enter combat mode, and vice versa for removal.
+ ///
+ [DataField, AutoNetworkedField]
+ public bool ToggleMouseRotator = true;
+
[ViewVariables(VVAccess.ReadWrite), DataField("activeZone"), AutoNetworkedField]
public TargetingZone ActiveZone;
}
diff --git a/Content.Shared/CombatMode/SharedCombatModeSystem.cs b/Content.Shared/CombatMode/SharedCombatModeSystem.cs
index 263f3e8311..66b31d01ff 100644
--- a/Content.Shared/CombatMode/SharedCombatModeSystem.cs
+++ b/Content.Shared/CombatMode/SharedCombatModeSystem.cs
@@ -1,4 +1,6 @@
using Content.Shared.Actions;
+using Content.Shared.MouseRotator;
+using Content.Shared.Movement.Components;
using Content.Shared.Popups;
using Content.Shared.Targeting;
using Robust.Shared.Network;
@@ -30,6 +32,8 @@ public abstract class SharedCombatModeSystem : EntitySystem
private void OnShutdown(EntityUid uid, CombatModeComponent component, ComponentShutdown args)
{
_actionsSystem.RemoveAction(uid, component.CombatToggleActionEntity);
+
+ SetMouseRotatorComponents(uid, false);
}
private void OnActionPerform(EntityUid uid, CombatModeComponent component, ToggleCombatActionEvent args)
@@ -76,6 +80,12 @@ public abstract class SharedCombatModeSystem : EntitySystem
if (component.CombatToggleActionEntity != null)
_actionsSystem.SetToggled(component.CombatToggleActionEntity, component.IsInCombatMode);
+
+ // Change mouse rotator comps if flag is set
+ if (!component.ToggleMouseRotator)
+ return;
+
+ SetMouseRotatorComponents(entity, value);
}
public virtual void SetActiveZone(EntityUid entity, TargetingZone zone,
@@ -86,6 +96,20 @@ public abstract class SharedCombatModeSystem : EntitySystem
component.ActiveZone = zone;
}
+
+ private void SetMouseRotatorComponents(EntityUid uid, bool value)
+ {
+ if (value)
+ {
+ EnsureComp(uid);
+ EnsureComp(uid);
+ }
+ else
+ {
+ RemComp(uid);
+ RemComp(uid);
+ }
+ }
}
public sealed partial class ToggleCombatActionEvent : InstantActionEvent { }
diff --git a/Content.Shared/MouseRotator/MouseRotatorComponent.cs b/Content.Shared/MouseRotator/MouseRotatorComponent.cs
index 9b4dac54ba..a35dfe0a28 100644
--- a/Content.Shared/MouseRotator/MouseRotatorComponent.cs
+++ b/Content.Shared/MouseRotator/MouseRotatorComponent.cs
@@ -14,22 +14,31 @@ public sealed partial class MouseRotatorComponent : Component
///
/// How much the desired angle needs to change before a predictive event is sent
///
- [DataField]
- [ViewVariables(VVAccess.ReadWrite)]
- public Angle AngleTolerance = Angle.FromDegrees(5.0);
+ [DataField, AutoNetworkedField]
+ public Angle AngleTolerance = Angle.FromDegrees(20.0);
///
/// The angle that will be lerped to
///
- [AutoNetworkedField, DataField]
+ [DataField, AutoNetworkedField]
public Angle? GoalRotation;
///
/// Max degrees the entity can rotate per second
///
- [DataField]
- [ViewVariables(VVAccess.ReadWrite)]
+ [DataField, AutoNetworkedField]
public double RotationSpeed = float.MaxValue;
+
+ ///
+ /// This one is important. If this is true, does not apply, and the system will
+ /// use instead. In this mode, the client will only send
+ /// events when an entity should snap to a different cardinal direction, rather than for every angle change.
+ ///
+ /// This is useful for cases like humans, where what really matters is the visual sprite direction, as opposed to something
+ /// like turrets or ship guns, which have finer range of movement.
+ ///
+ [DataField, AutoNetworkedField]
+ public bool Simple4DirMode = true;
}
///
@@ -41,3 +50,13 @@ public sealed class RequestMouseRotatorRotationEvent : EntityEventArgs
{
public Angle Rotation;
}
+
+///
+/// Simpler version of for implementations
+/// that only require snapping to 4-dir and not full angle rotation.
+///
+[Serializable, NetSerializable]
+public sealed class RequestMouseRotatorRotationSimpleEvent : EntityEventArgs
+{
+ public Direction Direction;
+}
diff --git a/Content.Shared/MouseRotator/SharedMouseRotatorSystem.cs b/Content.Shared/MouseRotator/SharedMouseRotatorSystem.cs
index 4ff309682a..c57d477bd2 100644
--- a/Content.Shared/MouseRotator/SharedMouseRotatorSystem.cs
+++ b/Content.Shared/MouseRotator/SharedMouseRotatorSystem.cs
@@ -16,6 +16,7 @@ public abstract class SharedMouseRotatorSystem : EntitySystem
base.Initialize();
SubscribeAllEvent(OnRequestRotation);
+ SubscribeAllEvent(OnRequestSimpleRotation);
}
public override void Update(float frameTime)
@@ -48,13 +49,27 @@ public abstract class SharedMouseRotatorSystem : EntitySystem
private void OnRequestRotation(RequestMouseRotatorRotationEvent msg, EntitySessionEventArgs args)
{
- if (args.SenderSession.AttachedEntity is not { } ent || !TryComp(ent, out var rotator))
+ if (args.SenderSession.AttachedEntity is not { } ent
+ || !TryComp(ent, out var rotator) || rotator.Simple4DirMode)
{
- Log.Error($"User {args.SenderSession.Name} ({args.SenderSession.UserId}) tried setting local rotation without a mouse rotator component attached!");
+ Log.Error($"User {args.SenderSession.Name} ({args.SenderSession.UserId}) tried setting local rotation directly without a valid mouse rotator component attached!");
return;
}
rotator.GoalRotation = msg.Rotation;
Dirty(ent, rotator);
}
+
+ private void OnRequestSimpleRotation(RequestMouseRotatorRotationSimpleEvent ev, EntitySessionEventArgs args)
+ {
+ if (args.SenderSession.AttachedEntity is not { } ent
+ || !TryComp(ent, out var rotator) || !rotator.Simple4DirMode)
+ {
+ Log.Error($"User {args.SenderSession.Name} ({args.SenderSession.UserId}) tried setting 4-dir rotation directly without a valid mouse rotator component attached!");
+ return;
+ }
+
+ rotator.GoalRotation = ev.Direction.ToAngle();
+ Dirty(ent, rotator);
+ }
}
diff --git a/Resources/Prototypes/Entities/Objects/Weapons/Guns/turrets.yml b/Resources/Prototypes/Entities/Objects/Weapons/Guns/turrets.yml
index 3c7d0dd5d0..632e86e4fc 100644
--- a/Resources/Prototypes/Entities/Objects/Weapons/Guns/turrets.yml
+++ b/Resources/Prototypes/Entities/Objects/Weapons/Guns/turrets.yml
@@ -66,6 +66,7 @@
interactSuccessSound:
path: /Audio/Effects/double_beep.ogg
- type: CombatMode
+ toggleMouseRotator: false
- type: Damageable
damageContainer: Inorganic
- type: Destructible
@@ -110,7 +111,9 @@
SoundTargetInLOS: !type:SoundPathSpecifier
path: /Audio/Effects/double_beep.ogg
- type: MouseRotator
+ angleTolerance: 5
rotationSpeed: 180
+ simple4DirMode: false
- type: NoRotateOnInteract
- type: NoRotateOnMove
- type: Input