diff --git a/Content.Client/_Goobstation/Emoting/AnimatedEmotesSystem.cs b/Content.Client/_Goobstation/Emoting/AnimatedEmotesSystem.cs new file mode 100644 index 00000000000..33b89fbed10 --- /dev/null +++ b/Content.Client/_Goobstation/Emoting/AnimatedEmotesSystem.cs @@ -0,0 +1,137 @@ +using Robust.Client.Animations; +using Robust.Shared.Animations; +using Robust.Shared.GameStates; +using Robust.Client.GameObjects; +using Content.Shared.Emoting; +using System.Numerics; +using Robust.Shared.Prototypes; +using Content.Shared.Chat.Prototypes; +using Content.Shared.Rotation; + +namespace Content.Client.Emoting; + +public sealed partial class AnimatedEmotesSystem : SharedAnimatedEmotesSystem +{ + [Dependency] private readonly AnimationPlayerSystem _anim = default!; + [Dependency] private readonly AppearanceSystem _appearance = default!; + [Dependency] private readonly IPrototypeManager _prot = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnHandleState); + + SubscribeLocalEvent(OnFlip); + SubscribeLocalEvent(OnSpin); + SubscribeLocalEvent(OnJump); + } + + public void PlayEmote(EntityUid uid, Animation anim, string animationKey = "emoteAnimKeyId") + { + if (_anim.HasRunningAnimation(uid, animationKey)) + return; + + _anim.Play(uid, anim, animationKey); + } + + private void OnHandleState(EntityUid uid, AnimatedEmotesComponent component, ref ComponentHandleState args) + { + if (args.Current is not AnimatedEmotesComponentState state + || !_prot.TryIndex(state.Emote, out var emote)) + return; + + if (emote.Event != null) + RaiseLocalEvent(uid, emote.Event); + } + + private void OnFlip(Entity ent, ref AnimationFlipEmoteEvent args) + { + var angle = Angle.Zero; + + if (TryComp(ent, out var rotation)) + { + _appearance.TryGetData(ent, RotationVisuals.RotationState, out var state); + + angle = state switch + { + RotationState.Vertical => rotation.VerticalRotation, + RotationState.Horizontal => rotation.HorizontalRotation, + _ => Angle.Zero + }; + } + + var a = new Animation + { + Length = TimeSpan.FromMilliseconds(500), + AnimationTracks = + { + new AnimationTrackComponentProperty + { + ComponentType = typeof(SpriteComponent), + Property = nameof(SpriteComponent.Rotation), + InterpolationMode = AnimationInterpolationMode.Linear, + KeyFrames = + { + new AnimationTrackProperty.KeyFrame(angle, 0f), + new AnimationTrackProperty.KeyFrame(angle + Angle.FromDegrees(180), 0.25f), + new AnimationTrackProperty.KeyFrame(angle + Angle.FromDegrees(360), 0.25f), + } + } + } + }; + PlayEmote(ent, a); + } + private void OnSpin(Entity ent, ref AnimationSpinEmoteEvent args) + { + var a = new Animation + { + Length = TimeSpan.FromMilliseconds(600), + AnimationTracks = + { + new AnimationTrackComponentProperty + { + ComponentType = typeof(TransformComponent), + Property = nameof(TransformComponent.LocalRotation), + InterpolationMode = AnimationInterpolationMode.Linear, + KeyFrames = + { + new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(0), 0f), + new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(90), 0.075f), + new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(180), 0.075f), + new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(270), 0.075f), + new AnimationTrackProperty.KeyFrame(Angle.Zero, 0.075f), + new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(90), 0.075f), + new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(180), 0.075f), + new AnimationTrackProperty.KeyFrame(Angle.FromDegrees(270), 0.075f), + new AnimationTrackProperty.KeyFrame(Angle.Zero, 0.075f), + } + } + } + }; + PlayEmote(ent, a, "emoteAnimSpin"); + } + private void OnJump(Entity ent, ref AnimationJumpEmoteEvent args) + { + var a = new Animation + { + Length = TimeSpan.FromMilliseconds(250), + AnimationTracks = + { + new AnimationTrackComponentProperty + { + ComponentType = typeof(SpriteComponent), + Property = nameof(SpriteComponent.Offset), + InterpolationMode = AnimationInterpolationMode.Cubic, + KeyFrames = + { + new AnimationTrackProperty.KeyFrame(Vector2.Zero, 0f), + new AnimationTrackProperty.KeyFrame(new Vector2(0, .35f), 0.125f), + new AnimationTrackProperty.KeyFrame(Vector2.Zero, 0.125f), + } + } + } + }; + PlayEmote(ent, a); + } +} diff --git a/Content.Server/_Goobstation/Emoting/AnimatedEmotesSystem.cs b/Content.Server/_Goobstation/Emoting/AnimatedEmotesSystem.cs new file mode 100644 index 00000000000..cc4863d31f7 --- /dev/null +++ b/Content.Server/_Goobstation/Emoting/AnimatedEmotesSystem.cs @@ -0,0 +1,28 @@ +using Robust.Shared.GameStates; +using Content.Server.Chat.Systems; +using Content.Shared.Chat.Prototypes; +using Content.Shared.Emoting; +using Robust.Shared.Prototypes; + +namespace Content.Server.Emoting; + +public sealed partial class AnimatedEmotesSystem : SharedAnimatedEmotesSystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnEmote); + } + + private void OnEmote(EntityUid uid, AnimatedEmotesComponent component, ref EmoteEvent args) + { + PlayEmoteAnimation(uid, component, args.Emote.ID); + } + + public void PlayEmoteAnimation(EntityUid uid, AnimatedEmotesComponent component, ProtoId prot) + { + component.Emote = prot; + Dirty(uid, component); + } +} diff --git a/Content.Shared/Chat/Prototypes/EmotePrototype.cs b/Content.Shared/Chat/Prototypes/EmotePrototype.cs index 7ee958ee6a7..34d54bc3600 100644 --- a/Content.Shared/Chat/Prototypes/EmotePrototype.cs +++ b/Content.Shared/Chat/Prototypes/EmotePrototype.cs @@ -68,6 +68,10 @@ public sealed partial class EmotePrototype : IPrototype /// [DataField] public HashSet ChatTriggers = new(); + + // goob edit - animations + [DataField] + public object? Event = null; } /// diff --git a/Content.Shared/_Goobstation/Emoting/AnimatedEmotesComponent.cs b/Content.Shared/_Goobstation/Emoting/AnimatedEmotesComponent.cs new file mode 100644 index 00000000000..fc8121bbe5a --- /dev/null +++ b/Content.Shared/_Goobstation/Emoting/AnimatedEmotesComponent.cs @@ -0,0 +1,28 @@ +using Content.Shared.Chat.Prototypes; +using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization; + +namespace Content.Shared.Emoting; + +// use as a template +//[Serializable, NetSerializable, DataDefinition] public sealed partial class AnimationNameEmoteEvent : EntityEventArgs { } + +[Serializable, NetSerializable, DataDefinition] public sealed partial class AnimationFlipEmoteEvent : EntityEventArgs { } +[Serializable, NetSerializable, DataDefinition] public sealed partial class AnimationSpinEmoteEvent : EntityEventArgs { } +[Serializable, NetSerializable, DataDefinition] public sealed partial class AnimationJumpEmoteEvent : EntityEventArgs { } + +[RegisterComponent, NetworkedComponent] public sealed partial class AnimatedEmotesComponent : Component +{ + [DataField] public ProtoId? Emote; +} + +[Serializable, NetSerializable] public sealed partial class AnimatedEmotesComponentState : ComponentState +{ + public ProtoId? Emote; + + public AnimatedEmotesComponentState(ProtoId? emote) + { + Emote = emote; + } +} diff --git a/Content.Shared/_Goobstation/Emoting/SharedAnimatedEmotesSystem.cs b/Content.Shared/_Goobstation/Emoting/SharedAnimatedEmotesSystem.cs new file mode 100644 index 00000000000..b19e8c26a1e --- /dev/null +++ b/Content.Shared/_Goobstation/Emoting/SharedAnimatedEmotesSystem.cs @@ -0,0 +1,18 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Emoting; + +public abstract class SharedAnimatedEmotesSystem : EntitySystem +{ + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnGetState); + } + + private void OnGetState(Entity ent, ref ComponentGetState args) + { + args.State = new AnimatedEmotesComponentState(ent.Comp.Emote); + } +} diff --git a/Resources/Locale/en-US/_Goobstation/emotes.ftl b/Resources/Locale/en-US/_Goobstation/emotes.ftl new file mode 100644 index 00000000000..f212a413abf --- /dev/null +++ b/Resources/Locale/en-US/_Goobstation/emotes.ftl @@ -0,0 +1,5 @@ + +chat-emote-name-spin = Spin +chat-emote-name-jump = Jump +chat-emote-msg-spin = spins! +chat-emote-msg-jump = jumps! diff --git a/Resources/Prototypes/Entities/Mobs/Player/guardian.yml b/Resources/Prototypes/Entities/Mobs/Player/guardian.yml index dca47e1c837..e81859421d9 100644 --- a/Resources/Prototypes/Entities/Mobs/Player/guardian.yml +++ b/Resources/Prototypes/Entities/Mobs/Player/guardian.yml @@ -109,6 +109,7 @@ - type: Tag tags: - CannotSuicide + - type: AnimatedEmotes # goob edit - animated emotes # From the uplink injector - type: entity diff --git a/Resources/Prototypes/Entities/Mobs/base.yml b/Resources/Prototypes/Entities/Mobs/base.yml index 90f3e8380f3..84428577471 100644 --- a/Resources/Prototypes/Entities/Mobs/base.yml +++ b/Resources/Prototypes/Entities/Mobs/base.yml @@ -43,6 +43,7 @@ - type: MovementSpeedModifier - type: RequireProjectileTarget active: False + - type: AnimatedEmotes # goob edit - animated emotes - type: entity save: false diff --git a/Resources/Prototypes/_Goobstation/Actions/emotes.yml b/Resources/Prototypes/_Goobstation/Actions/emotes.yml new file mode 100644 index 00000000000..a7485248db2 --- /dev/null +++ b/Resources/Prototypes/_Goobstation/Actions/emotes.yml @@ -0,0 +1,27 @@ +- type: emote + id: Spin + name: chat-emote-name-spin + chatMessages: ["chat-emote-msg-spin"] + icon: _Impstation/Interface/Emotes/spin.png #imp + chatTriggers: + - spin + - spins + - spins. + - spins! + event: !type:AnimationSpinEmoteEvent + +- type: emote + id: Jump + name: chat-emote-name-jump + chatMessages: ["chat-emote-msg-jump"] + icon: _Impstation/Interface/Emotes/jump.png #imp + chatTriggers: + - jump + - jumps + - jumps. + - jumps! + - bounce + - bounces + - bounces. + - bounces! + event: !type:AnimationJumpEmoteEvent diff --git a/Resources/Textures/_Impstation/Interface/Emotes/attributions.yml b/Resources/Textures/_Impstation/Interface/Emotes/attributions.yml new file mode 100644 index 00000000000..dc86ad5ba96 --- /dev/null +++ b/Resources/Textures/_Impstation/Interface/Emotes/attributions.yml @@ -0,0 +1,6 @@ +- files: + - jump.png + - spin.png + license: "CC-BY-SA-4.0" + copyright: "Created by Carousel for Impstation" + source: "https://github.com/impstation/imp-station-14" diff --git a/Resources/Textures/_Impstation/Interface/Emotes/jump.png b/Resources/Textures/_Impstation/Interface/Emotes/jump.png new file mode 100644 index 00000000000..d0de87ef617 Binary files /dev/null and b/Resources/Textures/_Impstation/Interface/Emotes/jump.png differ diff --git a/Resources/Textures/_Impstation/Interface/Emotes/spin.png b/Resources/Textures/_Impstation/Interface/Emotes/spin.png new file mode 100644 index 00000000000..c5de318e63b Binary files /dev/null and b/Resources/Textures/_Impstation/Interface/Emotes/spin.png differ