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