diff --git a/src/Alex.ResourcePackLib/Json/Bedrock/Entity/Animation.cs b/src/Alex.ResourcePackLib/Json/Bedrock/Entity/Animation.cs index 164c348aa..98680da32 100644 --- a/src/Alex.ResourcePackLib/Json/Bedrock/Entity/Animation.cs +++ b/src/Alex.ResourcePackLib/Json/Bedrock/Entity/Animation.cs @@ -6,6 +6,11 @@ namespace Alex.ResourcePackLib.Json.Bedrock.Entity { + public class LoopElement + { + public bool IsString { get; set; } + public bool IsExpression { get; set; } + } /// /// Defines a model's animations. /// @@ -22,8 +27,23 @@ static Animation() /// Determines whether the animation should go back to T0 when finished. /// [JsonProperty("loop")] - public IExpression Loop { get; set; } = new BooleanExpression(false); + public string Loop { get; set; } + /// + /// How long to wait in seconds before playing this animation. + /// Note that this expression is evaluated once before playing, and only re-evaluated if asked to play from the beginning again. + /// A looping animation should use 'loop_delay' if it wants a delay between loops. + /// + [JsonProperty("start_delay")] + public IExpression StartDelay { get; set; } + + /// + /// How long to wait in seconds before looping this animation. + /// Note that this expression is evaluated after each loop and on looping animation only. + /// + [JsonProperty("loop_delay")] + public IExpression LoopDelay { get; set; } + /// /// How does time pass when playing the animation. /// Defaults to "query.anim_time + query.delta_time" which means advance in seconds. @@ -45,8 +65,7 @@ static Animation() public float AnimationLength { get; set; } = 0f; /// - /// Should this animation override the rotation etc? - /// If set to true, all animations applied before this one don't have any effect. + /// reset bones in this animation to the default pose before applying this animation /// [JsonProperty("override_previous_animation")] public bool OverridePreviousAnimation { get; set; } = false; diff --git a/src/Alex/Graphics/Models/Entity/Animations/EntityAnimation.cs b/src/Alex/Graphics/Models/Entity/Animations/EntityAnimation.cs index 7e7c1689c..6f824f59d 100644 --- a/src/Alex/Graphics/Models/Entity/Animations/EntityAnimation.cs +++ b/src/Alex/Graphics/Models/Entity/Animations/EntityAnimation.cs @@ -6,6 +6,7 @@ using Alex.ResourcePackLib.Json.Bedrock.MoLang; using ConcreteMC.MolangSharp.Runtime; using ConcreteMC.MolangSharp.Runtime.Exceptions; +using ConcreteMC.MolangSharp.Runtime.Value; using Microsoft.Xna.Framework; using NLog; @@ -25,6 +26,10 @@ public class EntityAnimation : IAnimation private readonly Animation _definition; private bool _loop; + public bool HoldOnLastFrame { get; set; } = false; + private double _startDelay = -1; + private double _loopDelay = -1; + private bool _isFirstLoop = true; public EntityAnimation(AnimationComponent parent, Animation definition, string name) { _parent = parent; @@ -38,7 +43,20 @@ public EntityAnimation(AnimationComponent parent, Animation definition, string n { if (definition.Loop != null) { - _loop = parent.Execute(definition.Loop).AsBool(); + var loop = definition.Loop; + + if (loop == "hold_on_last_frame") + { + HoldOnLastFrame = true; + _loop = false; + } + else + { + if (bool.TryParse(loop, out _loop)) + { + + } + } } else { @@ -59,6 +77,9 @@ public EntityAnimation(AnimationComponent parent, Animation definition, string n public bool CanPlay() { + if (HoldOnLastFrame && !_isFirstLoop) + return false; + return true; } @@ -71,6 +92,18 @@ public void Tick() if (!Playing) return; + if (_startDelay > 0d) + { + if (_timeSinceStart.Elapsed.TotalSeconds < _startDelay) + return; + } + + if (_loopDelay > 0d && !_isFirstLoop) + { + if (_timeSinceLastLoop.Elapsed.TotalSeconds < _loopDelay) + return; + } + try { var entity = _parent.Entity; @@ -92,7 +125,7 @@ public void Tick() bone.Tick( _parent.Runtime, _elapsedTimer.Elapsed.TotalSeconds, _animationTime, - anim.OverridePreviousAnimation); + false); } } finally @@ -102,9 +135,17 @@ public void Tick() if (_animationLength > 0 && _animationTime >= _animationLength) { + _isFirstLoop = false; if (_loop) { _animationTime = 0; + + if (_definition.LoopDelay != null) + { + var loopDelay = _parent.Execute(_definition.LoopDelay); + _loopDelay = loopDelay.AsDouble(); + } + _timeSinceLastLoop.Restart(); } else { @@ -124,15 +165,22 @@ public void UpdateBindings(ModelRenderer renderer) } } } - + public void Play() { if (Playing) return; - + + if (_definition.StartDelay != null) + { + var startDelay = _parent.Execute(_definition.StartDelay); + _startDelay = startDelay.AsDouble(); + } + + _timeSinceStart.Restart(); foreach (var bone in _components) { - bone.Start(); + bone.Start(_definition.OverridePreviousAnimation); } Playing = true; @@ -140,6 +188,8 @@ public void Play() _elapsedTimer.Restart(); } + private Stopwatch _timeSinceLastLoop = new Stopwatch(); + private Stopwatch _timeSinceStart = new Stopwatch(); public void Stop() { if (Playing) @@ -176,7 +226,7 @@ public BoneComp(string name, EntityAnimation animation, AnimationBoneElement ele private Vector3 _startPosition = Vector3.Zero; private Vector3 _startScale = Vector3.Zero; - public void Start() + public void Start(bool reset = false) { if (_started) return; diff --git a/src/Alex/Utils/ItemExtensions.cs b/src/Alex/Utils/ItemExtensions.cs index d2a18cc79..616dbac8d 100644 --- a/src/Alex/Utils/ItemExtensions.cs +++ b/src/Alex/Utils/ItemExtensions.cs @@ -67,7 +67,7 @@ public static Item ToAlexItem(this MiNET.Items.Item item, [CallerMemberName] str { if (!ItemFactory.TryGetBedrockItem(item.Name, item.Metadata, out result)) Log.Warn( - $"Failed to convert MiNET item to Alex item. (MiNET={item}, CallingMethod={source}{(item == null ? "" : $", Name={item.Name}")})"); + $"Failed to convert MiNET item to Alex item. ({(item == null ? "" : $"Name={item.Name}, ")} CallingMethod={source}, MiNET={item})"); } if (result == null) diff --git a/submodules/RocketUI b/submodules/RocketUI index f0d25c7d1..a7fe76335 160000 --- a/submodules/RocketUI +++ b/submodules/RocketUI @@ -1 +1 @@ -Subproject commit f0d25c7d12f990da82026ec1ca6948c6237105cb +Subproject commit a7fe7633564b5427851a546ce47730d8c550783a