using Godot; using System.Collections.Generic; namespace SupaLidlGame.Utils; public partial class AnimationManager : Node { public List Channels { get; set; } private List _holds = new List(); private struct AnimationHold { public Animation PriorityAnim; // channel to be prioritized public Animation HoldAnim; // channel with disabled track public int TrackIndex; // track idx on HoldAnim } public override void _Ready() { Channels = new List(); foreach (var node in GetChildren()) { if (node is AnimationPlayer player) { Channels.Add(player); } } // lower position = lower priority for (int _p1Index = 0; _p1Index < Channels.Count; _p1Index++) { // apparently _p1Index is "static" when referencing it inside the // anonymous functions, so we declare a temp variable so it stays // the same value var p1Index = _p1Index; var p1 = Channels[p1Index]; p1.AnimationStarted += (StringName name) => { var anim = p1.GetAnimation(name); RemoveAllHolds(anim); for (int p2Index = 0; p2Index < Channels.Count; p2Index++) { var p2 = Channels[p2Index]; if (p1 != p2) { CheckConflicts(p1, p1Index, p2, p2Index); } } }; p1.AnimationChanged += (StringName oldName, StringName newName) => { var anim = p1.GetAnimation(oldName); //GD.Print(oldName + "resolve"); ResolveConflicts(anim); RemoveAllHolds(anim); }; p1.AnimationFinished += (StringName name) => { var anim = p1.GetAnimation(name); ResolveConflicts(anim); RemoveAllHolds(anim); }; } } private string NormPath(string path) { return path.Replace("../", "") .Replace("./", "") .Replace("%", ""); } private void CheckConflicts(AnimationPlayer p1, int p1Priority, AnimationPlayer p2, int p2Priority) { if (p1Priority == p2Priority) { throw new System.InvalidOperationException(); } if (p1.IsPlaying() && p2.IsPlaying()) { var anim1 = p1.GetAnimation(p1.CurrentAnimation); var anim2 = p2.GetAnimation(p2.CurrentAnimation); // compare each track to see if they conflict nodepaths for (int p1Track = 0; p1Track < anim1.GetTrackCount(); p1Track++) { NodePath anim1Path = anim1.TrackGetPath(p1Track); NodePath anim1Prop = anim1Path.GetConcatenatedSubNames(); NodePath anim1Name = anim1Path.GetConcatenatedNames(); Node anim1Node = p1.GetParent().GetNode(anim1Name); for (int p2Track = 0; p2Track < anim2.GetTrackCount(); p2Track++) { NodePath anim2Path = anim2.TrackGetPath(p2Track); NodePath anim2Prop = anim2Path.GetConcatenatedSubNames(); NodePath anim2Name = anim2Path.GetConcatenatedNames(); Node anim2Node = p2.GetParent().GetNode(anim2Name); // check if they point to the same node and property if (anim1Node == anim2Node && anim1Prop == anim2Prop) { var hold = new AnimationHold(); if (p1Priority > p2Priority) { hold.PriorityAnim = anim1; hold.HoldAnim = anim2; hold.TrackIndex = p2Track; anim2.TrackSetEnabled(p2Track, false); } else { hold.PriorityAnim = anim2; hold.HoldAnim = anim1; hold.TrackIndex = p1Track; anim1.TrackSetEnabled(p1Track, false); } //GD.Print($"{hold.PriorityAnim.ResourceName} > {hold.HoldAnim.ResourceName}"); _holds.Add(hold); break; } } } } } private void ResolveConflicts(Animation anim) { // loop through all holds for (int i = 0; i < _holds.Count; i++) { var hold = _holds[i]; // if the animation is a priority animation, remove it // if there are no other holds on the track, then set the track to // be enabled if (hold.PriorityAnim == anim) { //GD.Print($"{anim.ResourceName} was holding ${hold.HoldAnim.ResourceName}"); _holds.RemoveAt(i); i--; // if can't find hold with same track index, then it is safe to // enable the track if (_holds.FindIndex(h => h.HoldAnim == hold.HoldAnim && h.TrackIndex == hold.TrackIndex) < 0) { //GD.Print($"{hold.HoldAnim.ResourceName} is now free"); hold.HoldAnim.TrackSetEnabled(hold.TrackIndex, true); } } } //GD.Print("There are currently " + _holds.Count + " holds"); } private void RemoveAllHolds(Animation anim) { // remove all holds where the Animation is being held for (int i = 0; i < _holds.Count; i++) { var hold = _holds[i]; // if the animation is a priority animation, remove it // if there are no other holds on the track, then set the track to // be enabled if (hold.HoldAnim == anim) { _holds.RemoveAt(i); i--; hold.HoldAnim.TrackSetEnabled(hold.TrackIndex, true); } } } }