From: Hannah Giovanna Dawson Date: Tue, 4 Jun 2024 13:23:09 +0000 (+0100) Subject: SS14-26964 Clown Waddling Replicates, etc (#26983) X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=d4fe7eda515a1374ec753e740f025aefcae18e5a;p=space-station-14.git SS14-26964 Clown Waddling Replicates, etc (#26983) --- diff --git a/Content.Client/Movement/Systems/WaddleAnimationSystem.cs b/Content.Client/Movement/Systems/WaddleAnimationSystem.cs index 9555c1f6b9..0ed2d04f69 100644 --- a/Content.Client/Movement/Systems/WaddleAnimationSystem.cs +++ b/Content.Client/Movement/Systems/WaddleAnimationSystem.cs @@ -2,94 +2,115 @@ using Content.Client.Buckle; using Content.Client.Gravity; using Content.Shared.ActionBlocker; -using Content.Shared.Buckle.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Movement.Components; -using Content.Shared.Movement.Events; -using Content.Shared.StatusEffect; -using Content.Shared.Stunnable; +using Content.Shared.Movement.Systems; using Robust.Client.Animations; using Robust.Client.GameObjects; using Robust.Shared.Animations; -using Robust.Shared.Timing; namespace Content.Client.Movement.Systems; -public sealed class WaddleAnimationSystem : EntitySystem +public sealed class WaddleAnimationSystem : SharedWaddleAnimationSystem { [Dependency] private readonly AnimationPlayerSystem _animation = default!; [Dependency] private readonly GravitySystem _gravity = default!; - [Dependency] private readonly IGameTiming _timing = default!; [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!; [Dependency] private readonly BuckleSystem _buckle = default!; [Dependency] private readonly MobStateSystem _mobState = default!; public override void Initialize() { - SubscribeLocalEvent(OnMovementInput); - SubscribeLocalEvent(OnStartedWalking); - SubscribeLocalEvent(OnStoppedWalking); + base.Initialize(); + + SubscribeAllEvent(OnStartWaddling); SubscribeLocalEvent(OnAnimationCompleted); - SubscribeLocalEvent(OnStunned); - SubscribeLocalEvent(OnKnockedDown); - SubscribeLocalEvent(OnBuckleChange); + SubscribeAllEvent(OnStopWaddling); } - private void OnMovementInput(EntityUid entity, WaddleAnimationComponent component, MoveInputEvent args) + private void OnStartWaddling(StartedWaddlingEvent msg, EntitySessionEventArgs args) { - // Prediction mitigation. Prediction means that MoveInputEvents are spammed repeatedly, even though you'd assume - // they're once-only for the user actually doing something. As such do nothing if we're just repeating this FoR. - if (!_timing.IsFirstTimePredicted) - { + if (TryComp(GetEntity(msg.Entity), out var comp)) + StartWaddling((GetEntity(msg.Entity), comp)); + } + + private void OnStopWaddling(StoppedWaddlingEvent msg, EntitySessionEventArgs args) + { + if (TryComp(GetEntity(msg.Entity), out var comp)) + StopWaddling((GetEntity(msg.Entity), comp)); + } + + private void StartWaddling(Entity entity) + { + if (_animation.HasRunningAnimation(entity.Owner, entity.Comp.KeyName)) return; - } - if (!args.HasDirectionalMovement && component.IsCurrentlyWaddling) - { - var stopped = new StoppedWaddlingEvent(entity); + if (!TryComp(entity.Owner, out var mover)) + return; - RaiseLocalEvent(entity, ref stopped); + if (_gravity.IsWeightless(entity.Owner)) + return; + if (!_actionBlocker.CanMove(entity.Owner, mover)) return; - } - // Only start waddling if we're not currently AND we're actually moving. - if (component.IsCurrentlyWaddling || !args.HasDirectionalMovement) + // Do nothing if buckled in + if (_buckle.IsBuckled(entity.Owner)) return; - var started = new StartedWaddlingEvent(entity); + // Do nothing if crit or dead (for obvious reasons) + if (_mobState.IsIncapacitated(entity.Owner)) + return; - RaiseLocalEvent(entity, ref started); + PlayWaddleAnimationUsing( + (entity.Owner, entity.Comp), + CalculateAnimationLength(entity.Comp, mover), + CalculateTumbleIntensity(entity.Comp) + ); } - private void OnStartedWalking(EntityUid uid, WaddleAnimationComponent component, StartedWaddlingEvent args) + private static float CalculateTumbleIntensity(WaddleAnimationComponent component) { - if (_animation.HasRunningAnimation(uid, component.KeyName)) - return; + return component.LastStep ? 360 - component.TumbleIntensity : component.TumbleIntensity; + } + + private static float CalculateAnimationLength(WaddleAnimationComponent component, InputMoverComponent mover) + { + return mover.Sprinting ? component.AnimationLength * component.RunAnimationLengthMultiplier : component.AnimationLength; + } - if (!TryComp(uid, out var mover)) + private void OnAnimationCompleted(Entity entity, ref AnimationCompletedEvent args) + { + if (args.Key != entity.Comp.KeyName) return; - if (_gravity.IsWeightless(uid)) + if (!TryComp(entity.Owner, out var mover)) return; + PlayWaddleAnimationUsing( + (entity.Owner, entity.Comp), + CalculateAnimationLength(entity.Comp, mover), + CalculateTumbleIntensity(entity.Comp) + ); + } - if (!_actionBlocker.CanMove(uid, mover)) + private void StopWaddling(Entity entity) + { + if (!_animation.HasRunningAnimation(entity.Owner, entity.Comp.KeyName)) return; - // Do nothing if buckled in - if (_buckle.IsBuckled(uid)) - return; + _animation.Stop(entity.Owner, entity.Comp.KeyName); - // Do nothing if crit or dead (for obvious reasons) - if (_mobState.IsIncapacitated(uid)) + if (!TryComp(entity.Owner, out var sprite)) return; - var tumbleIntensity = component.LastStep ? 360 - component.TumbleIntensity : component.TumbleIntensity; - var len = mover.Sprinting ? component.AnimationLength * component.RunAnimationLengthMultiplier : component.AnimationLength; + sprite.Offset = new Vector2(); + sprite.Rotation = Angle.FromDegrees(0); + } - component.LastStep = !component.LastStep; - component.IsCurrentlyWaddling = true; + private void PlayWaddleAnimationUsing(Entity entity, float len, float tumbleIntensity) + { + entity.Comp.LastStep = !entity.Comp.LastStep; var anim = new Animation() { @@ -116,58 +137,13 @@ public sealed class WaddleAnimationSystem : EntitySystem KeyFrames = { new AnimationTrackProperty.KeyFrame(new Vector2(), 0), - new AnimationTrackProperty.KeyFrame(component.HopIntensity, len/2), + new AnimationTrackProperty.KeyFrame(entity.Comp.HopIntensity, len/2), new AnimationTrackProperty.KeyFrame(new Vector2(), len/2), } } } }; - _animation.Play(uid, anim, component.KeyName); - } - - private void OnStoppedWalking(EntityUid uid, WaddleAnimationComponent component, StoppedWaddlingEvent args) - { - StopWaddling(uid, component); - } - - private void OnAnimationCompleted(EntityUid uid, WaddleAnimationComponent component, AnimationCompletedEvent args) - { - var started = new StartedWaddlingEvent(uid); - - RaiseLocalEvent(uid, ref started); - } - - private void OnStunned(EntityUid uid, WaddleAnimationComponent component, StunnedEvent args) - { - StopWaddling(uid, component); - } - - private void OnKnockedDown(EntityUid uid, WaddleAnimationComponent component, KnockedDownEvent args) - { - StopWaddling(uid, component); - } - - private void OnBuckleChange(EntityUid uid, WaddleAnimationComponent component, BuckleChangeEvent args) - { - StopWaddling(uid, component); - } - - private void StopWaddling(EntityUid uid, WaddleAnimationComponent component) - { - if (!component.IsCurrentlyWaddling) - return; - - _animation.Stop(uid, component.KeyName); - - if (!TryComp(uid, out var sprite)) - { - return; - } - - sprite.Offset = new Vector2(); - sprite.Rotation = Angle.FromDegrees(0); - - component.IsCurrentlyWaddling = false; + _animation.Play(entity.Owner, anim, entity.Comp.KeyName); } } diff --git a/Content.Server/Movement/Systems/WaddleAnimationSystem.cs b/Content.Server/Movement/Systems/WaddleAnimationSystem.cs new file mode 100644 index 0000000000..e6083210e1 --- /dev/null +++ b/Content.Server/Movement/Systems/WaddleAnimationSystem.cs @@ -0,0 +1,5 @@ +using Content.Shared.Movement.Systems; + +namespace Content.Server.Movement.Systems; + +public sealed class WaddleAnimationSystem : SharedWaddleAnimationSystem; diff --git a/Content.Shared/Clothing/Components/WaddleWhenWornComponent.cs b/Content.Shared/Clothing/Components/WaddleWhenWornComponent.cs index 5cd7a72457..fb7490ef4f 100644 --- a/Content.Shared/Clothing/Components/WaddleWhenWornComponent.cs +++ b/Content.Shared/Clothing/Components/WaddleWhenWornComponent.cs @@ -1,35 +1,36 @@ using System.Numerics; +using Robust.Shared.GameStates; namespace Content.Shared.Clothing.Components; /// /// Defines something as causing waddling when worn. /// -[RegisterComponent] +[RegisterComponent, NetworkedComponent, AutoGenerateComponentState] public sealed partial class WaddleWhenWornComponent : Component { /// /// How high should they hop during the waddle? Higher hop = more energy. /// - [DataField] + [DataField, AutoNetworkedField] public Vector2 HopIntensity = new(0, 0.25f); /// /// How far should they rock backward and forward during the waddle? /// Each step will alternate between this being a positive and negative rotation. More rock = more scary. /// - [DataField] + [DataField, AutoNetworkedField] public float TumbleIntensity = 20.0f; /// /// How long should a complete step take? Less time = more chaos. /// - [DataField] + [DataField, AutoNetworkedField] public float AnimationLength = 0.66f; /// /// How much shorter should the animation be when running? /// - [DataField] + [DataField, AutoNetworkedField] public float RunAnimationLengthMultiplier = 0.568f; } diff --git a/Content.Client/Clothing/Systems/WaddleClothingSystem.cs b/Content.Shared/Clothing/EntitySystems/WaddleClothingSystem.cs similarity index 59% rename from Content.Client/Clothing/Systems/WaddleClothingSystem.cs rename to Content.Shared/Clothing/EntitySystems/WaddleClothingSystem.cs index b8ac3c207b..b445eb258e 100644 --- a/Content.Client/Clothing/Systems/WaddleClothingSystem.cs +++ b/Content.Shared/Clothing/EntitySystems/WaddleClothingSystem.cs @@ -1,8 +1,9 @@ -using Content.Shared.Clothing.Components; +using Content.Shared.Clothing; +using Content.Shared.Clothing.Components; using Content.Shared.Movement.Components; using Content.Shared.Inventory.Events; -namespace Content.Client.Clothing.Systems; +namespace Content.Shared.Clothing.EntitySystems; public sealed class WaddleClothingSystem : EntitySystem { @@ -10,13 +11,13 @@ public sealed class WaddleClothingSystem : EntitySystem { base.Initialize(); - SubscribeLocalEvent(OnGotEquipped); - SubscribeLocalEvent(OnGotUnequipped); + SubscribeLocalEvent(OnGotEquipped); + SubscribeLocalEvent(OnGotUnequipped); } - private void OnGotEquipped(EntityUid entity, WaddleWhenWornComponent comp, GotEquippedEvent args) + private void OnGotEquipped(EntityUid entity, WaddleWhenWornComponent comp, ClothingGotEquippedEvent args) { - var waddleAnimComp = EnsureComp(args.Equipee); + var waddleAnimComp = EnsureComp(args.Wearer); waddleAnimComp.AnimationLength = comp.AnimationLength; waddleAnimComp.HopIntensity = comp.HopIntensity; @@ -24,8 +25,8 @@ public sealed class WaddleClothingSystem : EntitySystem waddleAnimComp.TumbleIntensity = comp.TumbleIntensity; } - private void OnGotUnequipped(EntityUid entity, WaddleWhenWornComponent comp, GotUnequippedEvent args) + private void OnGotUnequipped(EntityUid entity, WaddleWhenWornComponent comp, ClothingGotUnequippedEvent args) { - RemComp(args.Equipee); + RemComp(args.Wearer); } } diff --git a/Content.Shared/Movement/Components/WaddleAnimationComponent.cs b/Content.Shared/Movement/Components/WaddleAnimationComponent.cs index c43ef3042e..3cd9a3749e 100644 --- a/Content.Shared/Movement/Components/WaddleAnimationComponent.cs +++ b/Content.Shared/Movement/Components/WaddleAnimationComponent.cs @@ -1,31 +1,32 @@ using System.Numerics; +using Robust.Shared.Serialization; namespace Content.Shared.Movement.Components; /// /// Declares that an entity has started to waddle like a duck/clown. /// -/// The newly be-waddled. -[ByRefEvent] -public record struct StartedWaddlingEvent(EntityUid Entity) +/// The newly be-waddled. +[Serializable, NetSerializable] +public sealed class StartedWaddlingEvent(NetEntity entity) : EntityEventArgs { - public EntityUid Entity = Entity; + public NetEntity Entity = entity; } /// /// Declares that an entity has stopped waddling like a duck/clown. /// -/// The former waddle-er. -[ByRefEvent] -public record struct StoppedWaddlingEvent(EntityUid Entity) +/// The former waddle-er. +[Serializable, NetSerializable] +public sealed class StoppedWaddlingEvent(NetEntity entity) : EntityEventArgs { - public EntityUid Entity = Entity; + public NetEntity Entity = entity; } /// /// Defines something as having a waddle animation when it moves. /// -[RegisterComponent] +[RegisterComponent, AutoGenerateComponentState] public sealed partial class WaddleAnimationComponent : Component { /// @@ -38,26 +39,26 @@ public sealed partial class WaddleAnimationComponent : Component /// /// How high should they hop during the waddle? Higher hop = more energy. /// - [DataField] + [DataField, AutoNetworkedField] public Vector2 HopIntensity = new(0, 0.25f); /// /// How far should they rock backward and forward during the waddle? /// Each step will alternate between this being a positive and negative rotation. More rock = more scary. /// - [DataField] + [DataField, AutoNetworkedField] public float TumbleIntensity = 20.0f; /// /// How long should a complete step take? Less time = more chaos. /// - [DataField] + [DataField, AutoNetworkedField] public float AnimationLength = 0.66f; /// /// How much shorter should the animation be when running? /// - [DataField] + [DataField, AutoNetworkedField] public float RunAnimationLengthMultiplier = 0.568f; /// @@ -68,5 +69,6 @@ public sealed partial class WaddleAnimationComponent : Component /// /// Stores if we're currently waddling so we can start/stop as appropriate and can tell other systems our state. /// + [AutoNetworkedField] public bool IsCurrentlyWaddling; } diff --git a/Content.Shared/Movement/Systems/SharedWaddleAnimationSystem.cs b/Content.Shared/Movement/Systems/SharedWaddleAnimationSystem.cs new file mode 100644 index 0000000000..2fcb4fc60b --- /dev/null +++ b/Content.Shared/Movement/Systems/SharedWaddleAnimationSystem.cs @@ -0,0 +1,106 @@ +using Content.Shared.Buckle.Components; +using Content.Shared.Gravity; +using Content.Shared.Movement.Components; +using Content.Shared.Movement.Events; +using Content.Shared.Movement.Systems; +using Content.Shared.Standing; +using Content.Shared.Stunnable; +using Robust.Shared.Timing; + +namespace Content.Shared.Movement.Systems; + +public abstract class SharedWaddleAnimationSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _timing = default!; + + public override void Initialize() + { + // Startup + SubscribeLocalEvent(OnComponentStartup); + + // Start moving possibilities + SubscribeLocalEvent(OnMovementInput); + SubscribeLocalEvent(OnStood); + + // Stop moving possibilities + SubscribeLocalEvent((Entity ent, ref StunnedEvent _) => StopWaddling(ent)); + SubscribeLocalEvent((Entity ent, ref DownedEvent _) => StopWaddling(ent)); + SubscribeLocalEvent((Entity ent, ref BuckleChangeEvent _) => StopWaddling(ent)); + SubscribeLocalEvent(OnGravityChanged); + } + + private void OnGravityChanged(Entity ent, ref GravityChangedEvent args) + { + if (!args.HasGravity && ent.Comp.IsCurrentlyWaddling) + StopWaddling(ent); + } + + private void OnComponentStartup(Entity entity, ref ComponentStartup args) + { + if (!TryComp(entity.Owner, out var moverComponent)) + return; + + // If the waddler is currently moving, make them start waddling + if ((moverComponent.HeldMoveButtons & MoveButtons.AnyDirection) == MoveButtons.AnyDirection) + { + RaiseNetworkEvent(new StartedWaddlingEvent(GetNetEntity(entity.Owner))); + } + } + + private void OnMovementInput(Entity entity, ref MoveInputEvent args) + { + // Prediction mitigation. Prediction means that MoveInputEvents are spammed repeatedly, even though you'd assume + // they're once-only for the user actually doing something. As such do nothing if we're just repeating this FoR. + if (!_timing.IsFirstTimePredicted) + { + return; + } + + if (!args.HasDirectionalMovement && entity.Comp.IsCurrentlyWaddling) + { + StopWaddling(entity); + + return; + } + + // Only start waddling if we're not currently AND we're actually moving. + if (entity.Comp.IsCurrentlyWaddling || !args.HasDirectionalMovement) + return; + + entity.Comp.IsCurrentlyWaddling = true; + + RaiseNetworkEvent(new StartedWaddlingEvent(GetNetEntity(entity.Owner))); + } + + private void OnStood(Entity entity, ref StoodEvent args) + { + // Prediction mitigation. Prediction means that MoveInputEvents are spammed repeatedly, even though you'd assume + // they're once-only for the user actually doing something. As such do nothing if we're just repeating this FoR. + if (!_timing.IsFirstTimePredicted) + { + return; + } + + if (!TryComp(entity.Owner, out var mover)) + { + return; + } + + if ((mover.HeldMoveButtons & MoveButtons.AnyDirection) == MoveButtons.None) + return; + + if (entity.Comp.IsCurrentlyWaddling) + return; + + entity.Comp.IsCurrentlyWaddling = true; + + RaiseNetworkEvent(new StartedWaddlingEvent(GetNetEntity(entity.Owner))); + } + + private void StopWaddling(Entity entity) + { + entity.Comp.IsCurrentlyWaddling = false; + + RaiseNetworkEvent(new StoppedWaddlingEvent(GetNetEntity(entity.Owner))); + } +}