From da4a488197e6f5d79a495e7f7a0cf599a38bbe18 Mon Sep 17 00:00:00 2001 From: Red <96445749+TheShuEd@users.noreply.github.com> Date: Sun, 4 Jan 2026 00:29:48 +0300 Subject: [PATCH] Melee weapons animations upgrade (#41425) * upgrading * Update MeleeWeaponSystem.Effects.cs * Easing --- .../Melee/MeleeWeaponSystem.Effects.cs | 79 +++++++++++-------- .../Weapons/Melee/MeleeWeaponComponent.cs | 19 ++++- .../Weapons/Melee/SharedMeleeWeaponSystem.cs | 6 ++ 3 files changed, 69 insertions(+), 35 deletions(-) diff --git a/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs b/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs index 3fbd4dce69..0bccd5b1b3 100644 --- a/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs +++ b/Content.Client/Weapons/Melee/MeleeWeaponSystem.Effects.cs @@ -43,6 +43,9 @@ public sealed partial class MeleeWeaponSystem return; } + var length = 1f; + var offset = 1f; + var spriteRotation = Angle.Zero; if (arcComponent.Animation != WeaponArcAnimation.None && TryComp(weapon, out MeleeWeaponComponent? meleeWeaponComponent)) @@ -55,9 +58,11 @@ public sealed partial class MeleeWeaponSystem if (meleeWeaponComponent.SwingLeft) angle *= -1; + + length = (1 / meleeWeaponComponent.AttackRate) * 0.6f; + offset = meleeWeaponComponent.AnimationOffset; } _sprite.SetRotation((animationUid, sprite), localPos.ToWorldAngle()); - var distance = Math.Clamp(localPos.Length() / 2f, 0.2f, 1f); var xform = _xformQuery.GetComponent(animationUid); TrackUserComponent track; @@ -67,16 +72,16 @@ public sealed partial class MeleeWeaponSystem case WeaponArcAnimation.Slash: track = EnsureComp(animationUid); track.User = user; - _animation.Play(animationUid, GetSlashAnimation(sprite, angle, spriteRotation), SlashAnimationKey); + _animation.Play(animationUid, GetSlashAnimation((animationUid, sprite), angle, spriteRotation, length, offset), SlashAnimationKey); if (arcComponent.Fadeout) - _animation.Play(animationUid, GetFadeAnimation(sprite, 0.065f, 0.065f + 0.05f), FadeAnimationKey); + _animation.Play(animationUid, GetFadeAnimation(sprite, length * 0.5f, length + 0.15f), FadeAnimationKey); break; case WeaponArcAnimation.Thrust: track = EnsureComp(animationUid); track.User = user; - _animation.Play(animationUid, GetThrustAnimation((animationUid, sprite), distance, spriteRotation), ThrustAnimationKey); + _animation.Play(animationUid, GetThrustAnimation((animationUid, sprite), offset, spriteRotation, length), ThrustAnimationKey); if (arcComponent.Fadeout) - _animation.Play(animationUid, GetFadeAnimation(sprite, 0.05f, 0.15f), FadeAnimationKey); + _animation.Play(animationUid, GetFadeAnimation(sprite, length * 0.5f, length + 0.15f), FadeAnimationKey); break; case WeaponArcAnimation.None: var (mapPos, mapRot) = TransformSystem.GetWorldPositionRotation(userXform); @@ -89,21 +94,22 @@ public sealed partial class MeleeWeaponSystem } } - private Animation GetSlashAnimation(SpriteComponent sprite, Angle arc, Angle spriteRotation) + private Animation GetSlashAnimation(Entity sprite, Angle arc, Angle spriteRotation, float length, float offset) { - const float slashStart = 0.03f; - const float slashEnd = 0.065f; - const float length = slashEnd + 0.05f; - var startRotation = sprite.Rotation + arc / 2; - var endRotation = sprite.Rotation - arc / 2; - var startRotationOffset = startRotation.RotateVec(new Vector2(0f, -1f)); - var endRotationOffset = endRotation.RotateVec(new Vector2(0f, -1f)); + var startRotation = sprite.Comp.Rotation + (arc * 0.5f); + var endRotation = sprite.Comp.Rotation - (arc * 0.5f); + + var startRotationOffset = startRotation.RotateVec(new Vector2(0f, -offset * 0.9f)); + var minRotationOffset = sprite.Comp.Rotation.RotateVec(new Vector2(0f, -offset * 1.1f)); + var endRotationOffset = endRotation.RotateVec(new Vector2(0f, -offset * 0.9f)); + startRotation += spriteRotation; endRotation += spriteRotation; + sprite.Comp.NoRotation = true; return new Animation() { - Length = TimeSpan.FromSeconds(length), + Length = TimeSpan.FromSeconds(length + 0.05f), AnimationTracks = { new AnimationTrackComponentProperty() @@ -112,10 +118,12 @@ public sealed partial class MeleeWeaponSystem Property = nameof(SpriteComponent.Rotation), KeyFrames = { - new AnimationTrackProperty.KeyFrame(startRotation, 0f), - new AnimationTrackProperty.KeyFrame(startRotation, slashStart), - new AnimationTrackProperty.KeyFrame(endRotation, slashEnd) - } + new AnimationTrackProperty.KeyFrame(Angle.Lerp(startRotation,endRotation,0.0f), length * 0.0f), + new AnimationTrackProperty.KeyFrame(Angle.Lerp(startRotation,endRotation,0.5f), length * 0.10f), + new AnimationTrackProperty.KeyFrame(Angle.Lerp(startRotation,endRotation,1.0f), length * 0.15f), + new AnimationTrackProperty.KeyFrame(Angle.Lerp(startRotation,endRotation,0.9f), length * 0.20f), + new AnimationTrackProperty.KeyFrame(Angle.Lerp(startRotation,endRotation,0.80f), length * 0.6f, Easings.OutQuart) + }, }, new AnimationTrackComponentProperty() { @@ -123,21 +131,21 @@ public sealed partial class MeleeWeaponSystem Property = nameof(SpriteComponent.Offset), KeyFrames = { - new AnimationTrackProperty.KeyFrame(startRotationOffset, 0f), - new AnimationTrackProperty.KeyFrame(startRotationOffset, slashStart), - new AnimationTrackProperty.KeyFrame(endRotationOffset, slashEnd) + new AnimationTrackProperty.KeyFrame(Vector2.Lerp(startRotationOffset,endRotationOffset,0.0f), length * 0.0f), + new AnimationTrackProperty.KeyFrame(minRotationOffset, length * 0.10f), + new AnimationTrackProperty.KeyFrame(Vector2.Lerp(startRotationOffset,endRotationOffset,1.0f), length * 0.15f), + new AnimationTrackProperty.KeyFrame(Vector2.Lerp(startRotationOffset,endRotationOffset,0.80f), length * 0.6f, Easings.OutQuart) } }, } }; } - private Animation GetThrustAnimation(Entity sprite, float distance, Angle spriteRotation) + private Animation GetThrustAnimation(Entity sprite, float offset, Angle spriteRotation, float length) { - const float thrustEnd = 0.05f; - const float length = 0.15f; - var startOffset = sprite.Comp.Rotation.RotateVec(new Vector2(0f, -distance / 5f)); - var endOffset = sprite.Comp.Rotation.RotateVec(new Vector2(0f, -distance)); + var startOffset = sprite.Comp.Rotation.RotateVec(new Vector2(0f, 0f)); + var endOffset = sprite.Comp.Rotation.RotateVec(new Vector2(0f, -offset * 1.2f)); + _sprite.SetRotation(sprite.AsNullable(), sprite.Comp.Rotation + spriteRotation); return new Animation() @@ -151,9 +159,11 @@ public sealed partial class MeleeWeaponSystem Property = nameof(SpriteComponent.Offset), KeyFrames = { - new AnimationTrackProperty.KeyFrame(startOffset, 0f), - new AnimationTrackProperty.KeyFrame(endOffset, thrustEnd), - new AnimationTrackProperty.KeyFrame(endOffset, length), + new AnimationTrackProperty.KeyFrame(Vector2.Lerp(startOffset, endOffset, 0f), length * 0f), + new AnimationTrackProperty.KeyFrame(Vector2.Lerp(startOffset, endOffset, 0.65f), length * 0.10f), + new AnimationTrackProperty.KeyFrame(Vector2.Lerp(startOffset, endOffset, 1f), length * 0.20f), + new AnimationTrackProperty.KeyFrame(Vector2.Lerp(startOffset, endOffset, 0.9f), length * 0.30f), + new AnimationTrackProperty.KeyFrame(Vector2.Lerp(startOffset, endOffset, 0.7f), length * 0.60f, Easings.OutQuart) } }, } @@ -200,11 +210,12 @@ public sealed partial class MeleeWeaponSystem InterpolationMode = AnimationInterpolationMode.Linear, KeyFrames = { - new AnimationTrackProperty.KeyFrame(direction.Normalized() * 0.15f, 0f), - new AnimationTrackProperty.KeyFrame(Vector2.Zero, length) - } - } - } + new AnimationTrackProperty.KeyFrame(Vector2.Zero, 0f), + new AnimationTrackProperty.KeyFrame(direction.Normalized() * 0.15f, length*0.4f), + new AnimationTrackProperty.KeyFrame(Vector2.Zero, length*0.6f), + }, + }, + }, }; } diff --git a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs index 84e88156ad..54e6ddbc1c 100644 --- a/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs +++ b/Content.Shared/Weapons/Melee/MeleeWeaponComponent.cs @@ -100,7 +100,7 @@ public sealed partial class MeleeWeaponComponent : Component public Angle Angle = Angle.FromDegrees(60); [DataField, AutoNetworkedField] - public EntProtoId Animation = "WeaponArcPunch"; + public EntProtoId Animation = "WeaponArcThrust"; [DataField, AutoNetworkedField] public EntProtoId WideAnimation = "WeaponArcSlash"; @@ -112,9 +112,26 @@ public sealed partial class MeleeWeaponComponent : Component [DataField, AutoNetworkedField] public Angle WideAnimationRotation = Angle.Zero; + /// + /// Attack animation direction. + /// [DataField, AutoNetworkedField] public bool SwingLeft; + /// + /// Change after every attack. Allows each attack to take turns being either left or right. + /// Thats looks cool visually + /// + [DataField, AutoNetworkedField] + public bool SwingBeverage = true; + + /// + /// How far away from the player the animation should be played. + /// We don't connect it with attack range, because different weapons have different sprites, + /// and this value should be adjusted manually for every weapon ideally + /// + [DataField] + public float AnimationOffset = 1f; // Sounds diff --git a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs index e00a634c21..6f4b078fe2 100644 --- a/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs +++ b/Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs @@ -428,6 +428,12 @@ public abstract class SharedMeleeWeaponSystem : EntitySystem var ev = new AttemptMeleeEvent(); RaiseLocalEvent(weaponUid, ref ev); + if (weapon.SwingBeverage) + { + weapon.SwingLeft = !weapon.SwingLeft; + DirtyField(weaponUid, weapon, nameof(MeleeWeaponComponent.SwingLeft)); + } + if (ev.Cancelled) { if (ev.Message != null) -- 2.52.0