mirror of
https://github.com/WWhiteDreamProject/wwdpublic.git
synced 2026-04-17 05:27:38 +03:00
* generic animation * AnimateOnHit * Dynamic Value * AnimateOnStartup * Animated Emotes * Play Animation Command * RedFox review * Rabbit review * RedFox review * PlayClient * Rabbit review * RedFox review
285 lines
9.3 KiB
C#
285 lines
9.3 KiB
C#
using System.Collections.Frozen;
|
|
using System.Diagnostics.CodeAnalysis;
|
|
using System.Linq;
|
|
using Content.Shared._White.Animations;
|
|
using Content.Shared._White.Animations.Prototypes;
|
|
using Content.Shared._White.Animations.Systems;
|
|
using Robust.Client.Animations;
|
|
using Robust.Client.Audio;
|
|
using Robust.Client.GameObjects;
|
|
using Robust.Client.Player;
|
|
using Robust.Shared.Animations;
|
|
using Robust.Shared.Player;
|
|
using Robust.Shared.Prototypes;
|
|
using Robust.Shared.Serialization.Manager;
|
|
using Robust.Shared.Serialization.Markdown.Value;
|
|
using Robust.Shared.Timing;
|
|
|
|
namespace Content.Client._White.Animations.Systems;
|
|
|
|
public sealed class WhiteAnimationPlayerSystem : SharedWhiteAnimationPlayerSystem
|
|
{
|
|
[Dependency] private readonly IComponentFactory _component = default!;
|
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
|
[Dependency] private readonly IPlayerManager _player = default!;
|
|
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
|
[Dependency] private readonly ISerializationManager _serialization = default!;
|
|
|
|
[Dependency] private readonly AnimationPlayerSystem _animationPlayer = default!;
|
|
[Dependency] private readonly AudioSystem _audio = default!;
|
|
|
|
private FrozenDictionary<string, AnimationData> _animations = default!;
|
|
|
|
public override void Initialize()
|
|
{
|
|
base.Initialize();
|
|
|
|
SubscribeAllEvent<PlayAnimationMessage>(OnPlayAnimationMessage);
|
|
SubscribeAllEvent<StopAnimationMessage>(OnStopAnimationMessage);
|
|
|
|
_prototype.PrototypesReloaded += OnPrototypeReload;
|
|
|
|
CachePrototypes();
|
|
}
|
|
|
|
public override void Shutdown()
|
|
{
|
|
base.Shutdown();
|
|
_prototype.PrototypesReloaded -= OnPrototypeReload;
|
|
}
|
|
|
|
#region Event Handling
|
|
|
|
private void OnPlayAnimationMessage(PlayAnimationMessage message) =>
|
|
Play(GetEntity(message.AnimatedEntity), message.AnimationId, message.Force);
|
|
|
|
private void OnStopAnimationMessage(StopAnimationMessage message) =>
|
|
Stop(GetEntity(message.AnimatedEntity), message.AnimationKey);
|
|
|
|
private void OnPrototypeReload(PrototypesReloadedEventArgs args)
|
|
{
|
|
if (args.WasModified<AnimationPrototype>())
|
|
CachePrototypes();
|
|
}
|
|
|
|
private void CachePrototypes()
|
|
{
|
|
var animations = new Dictionary<string, AnimationData>();
|
|
|
|
foreach (var animationPrototype in _prototype.EnumeratePrototypes<AnimationPrototype>())
|
|
{
|
|
var animation = new Animation();
|
|
var realLength = 0f;
|
|
|
|
foreach (var animationTrackData in animationPrototype.AnimationTracksData)
|
|
{
|
|
AnimationTrack? animationTrack = animationTrackData switch
|
|
{
|
|
AnimationTrackComponentPropertyData componentPropertyData => GetComponentProperty(componentPropertyData),
|
|
AnimationTrackPlaySoundData playSoundData => GetPlaySound(playSoundData),
|
|
AnimationTrackSpriteFlickData spriteFlickData => GetSpriteFlick(spriteFlickData),
|
|
_ => null
|
|
};
|
|
|
|
if (animationTrack == null)
|
|
continue;
|
|
|
|
var animationTrackLength = 0f;
|
|
foreach (var keyFrame in animationTrackData.KeyFrames)
|
|
animationTrackLength += keyFrame.Keyframe;
|
|
|
|
realLength = MathF.Max(realLength, animationTrackLength);
|
|
animation.AnimationTracks.Add(animationTrack);
|
|
}
|
|
|
|
animation.Length = animationPrototype.Length ?? TimeSpan.FromSeconds(realLength);
|
|
|
|
animations.Add(animationPrototype.ID, new (animation, animationPrototype.Key));
|
|
}
|
|
|
|
_animations = animations.ToFrozenDictionary();
|
|
}
|
|
#endregion
|
|
|
|
#region Private API
|
|
|
|
private AnimationTrackComponentProperty? GetComponentProperty(AnimationTrackComponentPropertyData animationTrackData)
|
|
{
|
|
var animationTrack = new AnimationTrackComponentProperty();
|
|
|
|
if (!_component.TryGetRegistration(animationTrackData.ComponentType, out var registration, true))
|
|
return null;
|
|
|
|
animationTrack.ComponentType = registration.Type;
|
|
animationTrack.Property = animationTrackData.Property;
|
|
animationTrack.InterpolationMode = animationTrackData.InterpolationMode;
|
|
|
|
var propertyType = AnimationHelper.GetAnimatableProperty(_component.GetComponent(registration), animationTrack.Property)?.GetType();
|
|
if (propertyType == null)
|
|
return null;
|
|
|
|
foreach (var keyFrame in animationTrackData.KeyFrames)
|
|
{
|
|
if (keyFrame is not KeyFramePropertyData keyFrameProperty)
|
|
continue;
|
|
|
|
var value = _serialization.Read(propertyType, new ValueDataNode(keyFrameProperty.Value));
|
|
if (value == null)
|
|
continue;
|
|
|
|
animationTrack.KeyFrames.Add(new (value, keyFrame.Keyframe));
|
|
}
|
|
|
|
return animationTrack;
|
|
}
|
|
|
|
private AnimationTrackPlaySound GetPlaySound(AnimationTrackPlaySoundData animationTrackData)
|
|
{
|
|
var animationTrack = new AnimationTrackPlaySound();
|
|
foreach (var keyFrame in animationTrackData.KeyFrames)
|
|
{
|
|
if (keyFrame is not KeyFrameSoundData keyFrameSound)
|
|
continue;
|
|
|
|
animationTrack.KeyFrames.Add(new(_audio.ResolveSound(keyFrameSound.Sound), keyFrame.Keyframe));
|
|
}
|
|
|
|
return animationTrack;
|
|
}
|
|
|
|
private AnimationTrackSpriteFlick GetSpriteFlick(AnimationTrackSpriteFlickData animationTrackData)
|
|
{
|
|
var animationTrack = new AnimationTrackSpriteFlick {LayerKey = animationTrackData.LayerKey, };
|
|
foreach (var keyFrame in animationTrackData.KeyFrames)
|
|
{
|
|
if (keyFrame is not KeyFrameSpriteFlickData keyFrameSpriteFlick)
|
|
continue;
|
|
|
|
animationTrack.KeyFrames.Add(new(keyFrameSpriteFlick.State, keyFrame.Keyframe));
|
|
}
|
|
|
|
return animationTrack;
|
|
}
|
|
#endregion
|
|
|
|
#region Public API
|
|
|
|
public bool TryGetAnimation(string animationId, [NotNullWhen(true)] out AnimationData? animation) =>
|
|
_animations.TryGetValue(animationId, out animation);
|
|
|
|
public override void Play(EntityUid uid, ProtoId<AnimationPrototype> animationId, bool force = false)
|
|
{
|
|
if (!TryGetAnimation(animationId, out var animation) || !uid.Valid)
|
|
return;
|
|
|
|
if (_animationPlayer.HasRunningAnimation(uid, animation.Key))
|
|
{
|
|
if (!force)
|
|
return;
|
|
|
|
_animationPlayer.Stop(uid, animation.Key);
|
|
}
|
|
|
|
_animationPlayer.Play(uid, animation.Animation, animation.Key);
|
|
}
|
|
|
|
public override void Play(EntityUid uid, ProtoId<AnimationPrototype> animationId, EntityUid recipient, bool force = false)
|
|
{
|
|
if (_player.LocalEntity != recipient)
|
|
return;
|
|
|
|
Play(uid, animationId, force);
|
|
}
|
|
|
|
public override void Play(EntityUid uid, ProtoId<AnimationPrototype> animationId, ICommonSession recipient, bool force = false)
|
|
{
|
|
if (_player.LocalSession != recipient)
|
|
return;
|
|
|
|
Play(uid, animationId, force);
|
|
}
|
|
|
|
public override void Play(EntityUid uid, ProtoId<AnimationPrototype> animationId, Filter filter, bool force = false)
|
|
{
|
|
if (!filter.Recipients.Contains(_player.LocalSession))
|
|
return;
|
|
|
|
Play(uid, animationId, force);
|
|
}
|
|
|
|
public override void PlayClient(EntityUid uid, ProtoId<AnimationPrototype> animationId, bool force = false)
|
|
{
|
|
if (!_gameTiming.IsFirstTimePredicted)
|
|
return;
|
|
|
|
Play(uid, animationId, force);
|
|
}
|
|
|
|
public override void PlayClient(EntityUid uid, ProtoId<AnimationPrototype> animationId, EntityUid recipient, bool force = false)
|
|
{
|
|
if (!_gameTiming.IsFirstTimePredicted)
|
|
return;
|
|
|
|
Play(uid, animationId, recipient, force);
|
|
}
|
|
|
|
public override void PlayPredicted(EntityUid uid, ProtoId<AnimationPrototype> animationId, EntityUid recipient, bool force = false)
|
|
{
|
|
if (!_gameTiming.IsFirstTimePredicted)
|
|
return;
|
|
|
|
Play(uid, animationId, recipient, force);
|
|
}
|
|
|
|
public override void Stop(EntityUid uid, string animationkey) =>
|
|
_animationPlayer.Stop(uid, animationkey);
|
|
|
|
public override void Stop(EntityUid uid, string animationkey, EntityUid recipient)
|
|
{
|
|
if (_player.LocalEntity != recipient)
|
|
return;
|
|
|
|
Stop(uid, animationkey);
|
|
}
|
|
|
|
public override void Stop(EntityUid uid, string animationkey, ICommonSession recipient)
|
|
{
|
|
if (_player.LocalSession != recipient)
|
|
return;
|
|
|
|
Stop(uid, animationkey);
|
|
}
|
|
|
|
public override void Stop(EntityUid uid, string animationkey, Filter filter)
|
|
{
|
|
if (!filter.Recipients.Contains(_player.LocalSession))
|
|
return;
|
|
|
|
Stop(uid, animationkey);
|
|
}
|
|
|
|
public override void StopClient(EntityUid uid, string animationkey, EntityUid recipient)
|
|
{
|
|
if (!_gameTiming.IsFirstTimePredicted)
|
|
return;
|
|
|
|
Stop(uid, animationkey, recipient);
|
|
}
|
|
|
|
public override void StopPredicted(EntityUid uid, string animationkey, EntityUid recipient)
|
|
{
|
|
if (!_gameTiming.IsFirstTimePredicted)
|
|
return;
|
|
|
|
Stop(uid, animationkey, recipient);
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
public sealed class AnimationData(Animation animation, string key)
|
|
{
|
|
public Animation Animation = animation;
|
|
public string Key = key;
|
|
}
|