Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix loop behaviour in animate() #1801

Merged
merged 14 commits into from
Sep 3, 2024
58 changes: 43 additions & 15 deletions OpenDreamClient/Rendering/DreamIcon.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ private set {
private int _animationFrame;
private TimeSpan _animationFrameTime = gameTiming.CurTime;
private List<AppearanceAnimation>? _appearanceAnimations;
private int _appearanceAnimationsLoops;
private Box2? _cachedAABB;
private bool _textureDirty = true;
private IRenderTexture? _cachedTexture;
Expand Down Expand Up @@ -142,7 +143,19 @@ public void StartAppearanceAnimation(IconAppearance endingAppearance, TimeSpan d
start = _appearanceAnimations[^1].Start + _appearanceAnimations[^1].Duration; //if it's not parallel, it's chained

_appearanceAnimations ??= new List<AppearanceAnimation>();
_appearanceAnimations.Add(new AppearanceAnimation(start, duration, endingAppearance, easing, loops, flags, delay));
if(_appearanceAnimations.Count == 0) {//only valid on the first animation
_appearanceAnimationsLoops = loops;
}

for(int i=_appearanceAnimations.Count-1; i>=0; i--) //there can be only one last-in-sequence, and it might not be the last element of the list because it could be added to mid-loop
if(_appearanceAnimations[i].LastInSequence) {
var lastAnim = _appearanceAnimations[i];
lastAnim.LastInSequence = false;
_appearanceAnimations[i] = lastAnim;
break;
}

_appearanceAnimations.Add(new AppearanceAnimation(start, duration, endingAppearance, easing, flags, delay, true));
}

/// <summary>
Expand Down Expand Up @@ -186,6 +199,7 @@ public void GetWorldAABB(Vector2 worldPos, ref Box2? aabb) {
private void UpdateAnimation() {
if(DMI == null || Appearance == null)
return;

DMIParser.ParsedDMIState? dmiState = DMI.Description.GetStateOrDefault(Appearance.IconState);
if(dmiState == null)
return;
Expand All @@ -212,10 +226,11 @@ private void UpdateAnimation() {
_textureDirty = true; //if we have animations, we need to recalculate the texture
IconAppearance appearance = new IconAppearance(_appearance);
List<AppearanceAnimation>? toRemove = null;
List<AppearanceAnimation>? toReAdd = null;
for(int i = 0; i < _appearanceAnimations.Count; i++) {
AppearanceAnimation animation = _appearanceAnimations[i];
//if it's not the first one, and it's not parallel, break
if((animation.flags & AnimationFlags.AnimationParallel) == 0 && i != 0)
if((animation.Flags & AnimationFlags.AnimationParallel) == 0 && i != 0)
break;

float timeFactor = Math.Clamp((float)(DateTime.Now - animation.Start).Ticks / animation.Duration.Ticks, 0.0f, 1.0f);
Expand Down Expand Up @@ -388,21 +403,34 @@ private void UpdateAnimation() {
}

if (timeFactor >= 1f) {
if (animation.loops > 0) {
var tempAnimation = _appearanceAnimations[i];
tempAnimation.loops--;
_appearanceAnimations[i] = tempAnimation;
}
if (animation.loops == 0) {
toRemove ??= new();
toRemove.Add(animation);
toRemove ??= new();
toRemove.Add(animation);
if (_appearanceAnimationsLoops != 0) { //add it back to the list with the times updated
if(_appearanceAnimationsLoops != -1 && animation.LastInSequence)
_appearanceAnimationsLoops -= 1;
toReAdd ??= new();
DateTime start;
if((animation.Flags & AnimationFlags.AnimationParallel) != 0)
start = _appearanceAnimations[^1].Start; //either that's also a parallel, or its one that this should be parallel with
else
start = _appearanceAnimations[^1].Start + _appearanceAnimations[^1].Duration; //if it's not parallel, it's chained
AppearanceAnimation repeatAnimation = new AppearanceAnimation(start, animation.Duration, animation.EndAppearance, animation.Easing, animation.Flags, animation.Delay, animation.LastInSequence);
toReAdd.Add(repeatAnimation);
}

}
}

if(toRemove != null)
foreach (AppearanceAnimation animation in toRemove!) {
foreach (AppearanceAnimation animation in toRemove) {
EndAppearanceAnimation(animation);
}

if(toReAdd != null)
foreach (AppearanceAnimation animation in toReAdd) {
_appearanceAnimations.Add(animation);
}

return appearance;
}

Expand Down Expand Up @@ -504,13 +532,13 @@ private void DirtyTexture() {
CachedTexture = null;
}

private struct AppearanceAnimation(DateTime start, TimeSpan duration, IconAppearance endAppearance, AnimationEasing easing, int loops, AnimationFlags flags, int delay) {
private struct AppearanceAnimation(DateTime start, TimeSpan duration, IconAppearance endAppearance, AnimationEasing easing, AnimationFlags flags, int delay, bool lastInSequence) {

Check warning

Code scanning / InspectCode

Struct with default equality members is used for comparison: Private accessibility Warning

Struct 'AppearanceAnimation' is checked for equality using the inefficient runtime-provided implementation
public readonly DateTime Start = start;
public readonly TimeSpan Duration = duration;
public readonly IconAppearance EndAppearance = endAppearance;
public readonly AnimationEasing Easing = easing;
public int loops = loops;
public readonly AnimationFlags flags = flags;
public int delay = delay;
public readonly AnimationFlags Flags = flags;
public readonly int Delay = delay;
public bool LastInSequence = lastInSequence;
}
}
Loading