Follow mouse rotation in combat mode (#20433)
This commit is contained in:
@@ -41,6 +41,22 @@ public sealed class MouseRotatorSystem : SharedMouseRotatorSystem
|
|||||||
|
|
||||||
var curRot = _transform.GetWorldRotation(xform);
|
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)
|
// Don't raise event if mouse ~hasn't moved (or if too close to goal rotation already)
|
||||||
var diff = Angle.ShortestDistance(angle, curRot);
|
var diff = Angle.ShortestDistance(angle, curRot);
|
||||||
if (Math.Abs(diff.Theta) < rotator.AngleTolerance.Theta)
|
if (Math.Abs(diff.Theta) < rotator.AngleTolerance.Theta)
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
using Content.Shared.MouseRotator;
|
||||||
|
using Content.Shared.Movement.Components;
|
||||||
using Content.Shared.Targeting;
|
using Content.Shared.Targeting;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
@@ -41,6 +43,13 @@ namespace Content.Shared.CombatMode
|
|||||||
[ViewVariables(VVAccess.ReadWrite), DataField("isInCombatMode"), AutoNetworkedField]
|
[ViewVariables(VVAccess.ReadWrite), DataField("isInCombatMode"), AutoNetworkedField]
|
||||||
public bool IsInCombatMode;
|
public bool IsInCombatMode;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Will add <see cref="MouseRotatorComponent"/> and <see cref="NoRotateOnMoveComponent"/>
|
||||||
|
/// to entities with this flag enabled that enter combat mode, and vice versa for removal.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool ToggleMouseRotator = true;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("activeZone"), AutoNetworkedField]
|
[ViewVariables(VVAccess.ReadWrite), DataField("activeZone"), AutoNetworkedField]
|
||||||
public TargetingZone ActiveZone;
|
public TargetingZone ActiveZone;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
using Content.Shared.Actions;
|
using Content.Shared.Actions;
|
||||||
|
using Content.Shared.MouseRotator;
|
||||||
|
using Content.Shared.Movement.Components;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Shared.Targeting;
|
using Content.Shared.Targeting;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
@@ -30,6 +32,8 @@ public abstract class SharedCombatModeSystem : EntitySystem
|
|||||||
private void OnShutdown(EntityUid uid, CombatModeComponent component, ComponentShutdown args)
|
private void OnShutdown(EntityUid uid, CombatModeComponent component, ComponentShutdown args)
|
||||||
{
|
{
|
||||||
_actionsSystem.RemoveAction(uid, component.CombatToggleActionEntity);
|
_actionsSystem.RemoveAction(uid, component.CombatToggleActionEntity);
|
||||||
|
|
||||||
|
SetMouseRotatorComponents(uid, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnActionPerform(EntityUid uid, CombatModeComponent component, ToggleCombatActionEvent args)
|
private void OnActionPerform(EntityUid uid, CombatModeComponent component, ToggleCombatActionEvent args)
|
||||||
@@ -76,6 +80,12 @@ public abstract class SharedCombatModeSystem : EntitySystem
|
|||||||
|
|
||||||
if (component.CombatToggleActionEntity != null)
|
if (component.CombatToggleActionEntity != null)
|
||||||
_actionsSystem.SetToggled(component.CombatToggleActionEntity, component.IsInCombatMode);
|
_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,
|
public virtual void SetActiveZone(EntityUid entity, TargetingZone zone,
|
||||||
@@ -86,6 +96,20 @@ public abstract class SharedCombatModeSystem : EntitySystem
|
|||||||
|
|
||||||
component.ActiveZone = zone;
|
component.ActiveZone = zone;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetMouseRotatorComponents(EntityUid uid, bool value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
EnsureComp<MouseRotatorComponent>(uid);
|
||||||
|
EnsureComp<NoRotateOnMoveComponent>(uid);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
RemComp<MouseRotatorComponent>(uid);
|
||||||
|
RemComp<NoRotateOnMoveComponent>(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public sealed partial class ToggleCombatActionEvent : InstantActionEvent { }
|
public sealed partial class ToggleCombatActionEvent : InstantActionEvent { }
|
||||||
|
|||||||
@@ -14,22 +14,31 @@ public sealed partial class MouseRotatorComponent : Component
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// How much the desired angle needs to change before a predictive event is sent
|
/// How much the desired angle needs to change before a predictive event is sent
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField, AutoNetworkedField]
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
public Angle AngleTolerance = Angle.FromDegrees(20.0);
|
||||||
public Angle AngleTolerance = Angle.FromDegrees(5.0);
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The angle that will be lerped to
|
/// The angle that will be lerped to
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AutoNetworkedField, DataField]
|
[DataField, AutoNetworkedField]
|
||||||
public Angle? GoalRotation;
|
public Angle? GoalRotation;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Max degrees the entity can rotate per second
|
/// Max degrees the entity can rotate per second
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField]
|
[DataField, AutoNetworkedField]
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public double RotationSpeed = float.MaxValue;
|
public double RotationSpeed = float.MaxValue;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This one is important. If this is true, <see cref="AngleTolerance"/> does not apply, and the system will
|
||||||
|
/// use <see cref="RequestMouseRotatorRotationSimpleEvent"/> 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.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool Simple4DirMode = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -41,3 +50,13 @@ public sealed class RequestMouseRotatorRotationEvent : EntityEventArgs
|
|||||||
{
|
{
|
||||||
public Angle Rotation;
|
public Angle Rotation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Simpler version of <see cref="RequestMouseRotatorRotationEvent"/> for implementations
|
||||||
|
/// that only require snapping to 4-dir and not full angle rotation.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class RequestMouseRotatorRotationSimpleEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public Direction Direction;
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ public abstract class SharedMouseRotatorSystem : EntitySystem
|
|||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeAllEvent<RequestMouseRotatorRotationEvent>(OnRequestRotation);
|
SubscribeAllEvent<RequestMouseRotatorRotationEvent>(OnRequestRotation);
|
||||||
|
SubscribeAllEvent<RequestMouseRotatorRotationSimpleEvent>(OnRequestSimpleRotation);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
@@ -48,13 +49,27 @@ public abstract class SharedMouseRotatorSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnRequestRotation(RequestMouseRotatorRotationEvent msg, EntitySessionEventArgs args)
|
private void OnRequestRotation(RequestMouseRotatorRotationEvent msg, EntitySessionEventArgs args)
|
||||||
{
|
{
|
||||||
if (args.SenderSession.AttachedEntity is not { } ent || !TryComp<MouseRotatorComponent>(ent, out var rotator))
|
if (args.SenderSession.AttachedEntity is not { } ent
|
||||||
|
|| !TryComp<MouseRotatorComponent>(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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
rotator.GoalRotation = msg.Rotation;
|
rotator.GoalRotation = msg.Rotation;
|
||||||
Dirty(ent, rotator);
|
Dirty(ent, rotator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void OnRequestSimpleRotation(RequestMouseRotatorRotationSimpleEvent ev, EntitySessionEventArgs args)
|
||||||
|
{
|
||||||
|
if (args.SenderSession.AttachedEntity is not { } ent
|
||||||
|
|| !TryComp<MouseRotatorComponent>(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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,6 +66,7 @@
|
|||||||
interactSuccessSound:
|
interactSuccessSound:
|
||||||
path: /Audio/Effects/double_beep.ogg
|
path: /Audio/Effects/double_beep.ogg
|
||||||
- type: CombatMode
|
- type: CombatMode
|
||||||
|
toggleMouseRotator: false
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
damageContainer: Inorganic
|
damageContainer: Inorganic
|
||||||
- type: Destructible
|
- type: Destructible
|
||||||
@@ -110,7 +111,9 @@
|
|||||||
SoundTargetInLOS: !type:SoundPathSpecifier
|
SoundTargetInLOS: !type:SoundPathSpecifier
|
||||||
path: /Audio/Effects/double_beep.ogg
|
path: /Audio/Effects/double_beep.ogg
|
||||||
- type: MouseRotator
|
- type: MouseRotator
|
||||||
|
angleTolerance: 5
|
||||||
rotationSpeed: 180
|
rotationSpeed: 180
|
||||||
|
simple4DirMode: false
|
||||||
- type: NoRotateOnInteract
|
- type: NoRotateOnInteract
|
||||||
- type: NoRotateOnMove
|
- type: NoRotateOnMove
|
||||||
- type: Input
|
- type: Input
|
||||||
|
|||||||
Reference in New Issue
Block a user