diff --git a/Assets/Scripts/Games/KarateMan/KarateManJoe.cs b/Assets/Scripts/Games/KarateMan/KarateManJoe.cs
index dda18bacb..b7b333537 100644
--- a/Assets/Scripts/Games/KarateMan/KarateManJoe.cs
+++ b/Assets/Scripts/Games/KarateMan/KarateManJoe.cs
@@ -219,7 +219,7 @@ namespace HeavenStudio.Games.Scripts_KarateMan
private bool _lastPunchedHeavy = false;
- public bool Punch(int forceHand = 0, bool touchCharge = false, bool punchedHeavy = false)
+ public bool Punch(int forceHand = 0, bool touchCharge = false, bool punchedHeavy = false, double wantBeat = double.MaxValue)
{
if (GameManager.instance.currentGame != "karateman") return false;
var cond = Conductor.instance;
@@ -246,7 +246,8 @@ namespace HeavenStudio.Games.Scripts_KarateMan
noNuriJabTime = cond.songPositionInBeatsAsDouble;
break;
default:
- if (cond.songPositionInBeatsAsDouble <= cond.GetBeatFromSongPos(lastPunchTime + Minigame.NgLateTime() - 1) + 0.25)
+ Debug.Log($"Punching with beat {wantBeat} and lastPunchTime {lastPunchTime}");
+ if (wantBeat <= cond.GetBeatFromSongPos(lastPunchTime + Minigame.NgLateTime() - 1) + 0.25)
{
lastPunchTime = double.MinValue;
anim.DoScaledAnimationAsync("Straight", 0.5f);
@@ -254,7 +255,7 @@ namespace HeavenStudio.Games.Scripts_KarateMan
}
else
{
- lastPunchTime = cond.songPositionAsDouble;
+ lastPunchTime = cond.GetSongPosFromBeat(wantBeat, true);
anim.DoScaledAnimationAsync("Jab", 0.5f);
}
break;
diff --git a/Assets/Scripts/Games/KarateMan/KarateManPot.cs b/Assets/Scripts/Games/KarateMan/KarateManPot.cs
index 934ae397a..179527b11 100644
--- a/Assets/Scripts/Games/KarateMan/KarateManPot.cs
+++ b/Assets/Scripts/Games/KarateMan/KarateManPot.cs
@@ -765,7 +765,7 @@ namespace HeavenStudio.Games.Scripts_KarateMan
var joe = KarateMan.instance.Joe;
if (state <= -1f || state >= 1f)
{
- bool straight = joe.Punch(ItemPunchHand(), false, ItemPunchHand() == 2);
+ bool straight = joe.Punch(ItemPunchHand(), false, ItemPunchHand() == 2, caller.startBeat + caller.timer);
startBeat = Conductor.instance.unswungSongPositionInBeatsAsDouble;
CurrentCurve = ItemCurves[6];
curveTargetBeat = 1f;
@@ -798,7 +798,7 @@ namespace HeavenStudio.Games.Scripts_KarateMan
return;
}
}
- bool straight = joe.Punch(ItemPunchHand(), false, ItemPunchHand() == 2);
+ bool straight = joe.Punch(ItemPunchHand(), false, ItemPunchHand() == 2, caller.startBeat + caller.timer);
DoHitExpression(startBeat + 1f);
ItemHitEffect(straight);
status = FlyStatus.Hit;
diff --git a/Assets/Scripts/Util/AnimationHelpers.cs b/Assets/Scripts/Util/AnimationHelpers.cs
index 03565169b..743a30931 100644
--- a/Assets/Scripts/Util/AnimationHelpers.cs
+++ b/Assets/Scripts/Util/AnimationHelpers.cs
@@ -11,6 +11,14 @@ namespace HeavenStudio.Util
var stateInfo = anim.GetCurrentAnimatorStateInfo(0);
return (stateInfo.normalizedTime >= stateInfo.speed || stateInfo.loop) && !anim.IsInTransition(0);
}
+
+ public static bool IsAnimationNotPlaying(this Animator anim, int layer)
+ {
+ if (anim == null) return true;
+ var stateInfo = anim.GetCurrentAnimatorStateInfo(layer);
+ return (stateInfo.normalizedTime >= stateInfo.speed || stateInfo.loop) && !anim.IsInTransition(layer);
+ }
+
///
/// Returns true if animName is currently playing on animator
///
@@ -24,6 +32,19 @@ namespace HeavenStudio.Util
return (stateInfo.normalizedTime < stateInfo.speed || stateInfo.loop) && isPlaying;
}
+ ///
+ /// Returns true if animName is currently playing on animator on a specific layer
+ ///
+ /// Animator to check
+ /// name(s) of animation to look out for
+ public static bool IsPlayingAnimationNames(this Animator anim, int layer, params string[] animNames)
+ {
+ if (anim == null) return false;
+ var stateInfo = anim.GetCurrentAnimatorStateInfo(layer);
+ var isPlaying = Array.Exists(animNames, animName => stateInfo.IsName(animName));
+ return (stateInfo.normalizedTime < stateInfo.speed || stateInfo.loop) && isPlaying;
+ }
+
///
/// Sets animator's progress on an animation based on current song beat between startTime and length
/// function must be called in actor's Update loop to update properly
@@ -43,6 +64,27 @@ namespace HeavenStudio.Util
anim.speed = 1f; //not 0 so these can still play their script events
}
+ ///
+ /// Sets animator's progress on a state based on current song beat between startTime and length
+ /// function must be called in actor's Update loop to update properly
+ /// also sets the speed of the state 1x speed
+ /// Animator must have a float parameter named "{state}_Speed" for this to work, and have the state's speed multiplier mapped to that parameter in the state tree
+ ///
+ /// Animator to update
+ /// name of animation to play
+ /// reference start time of animation (progress 0.0)
+ /// duration of animation (progress 1.0)
+ /// multiplier for animation progress (smaller values make animation slower)
+ /// animator layer to play animation on
+ public static void DoScaledState(this Animator anim, string state, double startTime, double length = 1, float timeScale = 1f, int animLayer = -1, bool clamp = false, bool ignoreSwing = true)
+ {
+ if (anim == null) return;
+ float pos = Conductor.instance.GetPositionFromBeat(startTime, length, ignoreSwing: ignoreSwing) * timeScale;
+ if (clamp) pos = Mathf.Clamp01(pos);
+ anim.Play(state, animLayer, pos);
+ anim.SetFloat($"{state}_Speed", 1);
+ }
+
///
/// Sets animator progress on an animation according to pos
///
@@ -85,6 +127,36 @@ namespace HeavenStudio.Util
anim.DoScaledAnimationAsync(animName, timeScale, pos, animLayer);
}
+ ///
+ /// Sets the speed of an animation state on an animator, scaled to the BPM, then plays it
+ /// Animator must have a float parameter named "{state}Speed" for this to work, and have the state's speed multiplier mapped to that parameter in the state tree
+ ///
+ /// Animator to play animation on
+ /// name of animation to play
+ /// multiplier for animation speed
+ /// beat that this animation would start on
+ /// animator layer to play animation on
+ public static void DoScaledStateFromBeatAsync(this Animator anim, string animName, float timeScale = 1f, double startBeat = 0, int animLayer = -1)
+ {
+ if (anim == null) return;
+ float pos = 0;
+ if (!double.IsNaN(startBeat)) {
+ var cond = Conductor.instance;
+ var animClip = Array.Find(anim.runtimeAnimatorController.animationClips, x => x.name == animName);
+ if (animClip == null) {
+ Debug.LogError("Animation clip " + animName + " not found!");
+ return;
+ }
+ double animLength = cond.SecsToBeats(animClip.length, cond.GetBpmAtBeat(startBeat));
+ pos = cond.GetPositionFromBeat(startBeat, animLength) * timeScale;
+ } else {
+ Debug.LogWarning("DoScaledAnimationFromBeatAsync()'s startBeat was NaN; using DoScaledAnimationAsync() instead.");
+ }
+ anim.DoScaledStateAsync(animName, timeScale, pos, animLayer);
+ }
+
+
+
///
/// Plays animation on animator, scaling speed to song BPM
/// call this function once, when playing an animation
@@ -101,6 +173,21 @@ namespace HeavenStudio.Util
anim.speed = (1f / Conductor.instance.pitchedSecPerBeat) * timeScale;
}
+ ///
+ /// Sets the speed of an animation state on an animator, scaled to the BPM, then plays it
+ /// Animator must have a float parameter named "{state}Speed" for this to work, and have the state's speed multiplier mapped to that parameter in the state tree
+ ///
+ /// Animator to play animation on
+ /// name of animation to play
+ /// multiplier for animation speed
+ /// starting progress of animation
+ /// animator layer to play animation on
+ public static void DoScaledStateAsync(this Animator anim, string state, float timeScale = 1f, float startPos = 0f, int animLayer = -1)
+ {
+ if (anim == null) return;
+ anim.PlayStateAtSpeed(state, (1f / Conductor.instance.pitchedSecPerBeat) * timeScale, startPos, animLayer);
+ }
+
public static void SetScaledAnimationSpeed(this Animator anim, float timeScale = 0.5f)
{
if (anim == null) return;
@@ -121,5 +208,21 @@ namespace HeavenStudio.Util
anim.Play(animName, animLayer, startPos);
anim.speed = 1f;
}
+
+ ///
+ /// Sets the speed of an animation state on an animator, then plays it
+ /// Animator must have a float parameter named "{state}_Speed" for this to work, and have the state's speed multiplier mapped to that parameter in the state tree
+ ///
+ /// Animator to play animation on
+ /// name of animation to play
+ /// multiplier for animation speed
+ /// starting progress of animation
+ /// animator layer to play animation on
+ public static void PlayStateAtSpeed(this Animator anim, string state, float timeScale = 1f, float startPos = 0f, int animLayer = -1)
+ {
+ if (anim == null) return;
+ anim.SetFloat($"{state}_Speed", timeScale);
+ anim.Play(state, animLayer, startPos);
+ }
}
}
\ No newline at end of file