using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using HeavenStudio.Util; namespace HeavenStudio.Games.Loaders { using static Minigames; public static class AgbTapLoader { public static Minigame AddGame(EventCaller eventCaller) { return new Minigame("tapTrial", "Tap Trial", "94ffb5", false, false, new List() { new GameAction("bop", "Bop") { function = delegate { var e = eventCaller.currentEntity; TapTrial.instance.Bop(e.beat, e.length, e["toggle"], e["toggle2"]); }, resizable = true, parameters = new List() { new Param("toggle", true, "Bop", "Whether both will bop to the beat or not"), new Param("toggle2", false, "Bop (Auto)", "Whether both will bop automatically to the beat or not") } }, new GameAction("tap", "Tap") { function = delegate { TapTrial.instance.Tap(eventCaller.currentEntity.beat); }, defaultLength = 2.0f }, new GameAction("double tap", "Double Tap") { function = delegate { TapTrial.instance.DoubleTap(eventCaller.currentEntity.beat); }, defaultLength = 2.0f }, new GameAction("triple tap", "Triple Tap") { function = delegate { TapTrial.instance.TripleTap(eventCaller.currentEntity.beat); }, defaultLength = 4.0f }, new GameAction("jump tap prep", "Prepare Stance") { function = delegate { TapTrial.instance.JumpPrepare(); }, }, new GameAction("jump tap", "Jump Tap") { function = delegate { var e = eventCaller.currentEntity; TapTrial.instance.JumpTap(e.beat, e["final"]); }, defaultLength = 2.0f, parameters = new List() { new Param("final", false, "Final") } }, new GameAction("scroll event", "Scroll Background") { function = delegate { var e = eventCaller.currentEntity; TapTrial.instance.Scroll(e["toggle"], e["flash"], e["m"]); }, defaultLength = 1f, parameters = new List() { new Param("toggle", true, "Scroll FX", "Will scroll", new List() { new Param.CollapseParam(x => (bool)x, new string[] { "flash", "m"}) }), new Param("flash", true, "Flash FX", "Will flash to white"), new Param("m", new EntityTypes.Float(0, 10, 1), "Speed Multiplier") } }, new GameAction("giraffe events", "Giraffe Animations") { function = delegate { var e = eventCaller.currentEntity; TapTrial.instance.GiraffeAnims(e.beat, e.length, e["toggle"], e["instant"]); }, resizable = true, parameters = new List() { new Param("toggle", TapTrial.GiraffeAnimation.Enter, "Animation", "Which animation?"), new Param("instant", EasingFunction.Ease.Linear, "Ease", "Which ease will be used?") } }, // backwards-compatibility new GameAction("final jump tap", "Final Jump Tap") { function = delegate { var e = eventCaller.currentEntity; TapTrial.instance.JumpTap(e.beat, true); }, defaultLength = 2.0f, hidden = true }, }, new List() {"agb", "normal"}, "agbtap", "en", new List() {} ); } } } namespace HeavenStudio.Games { using HeavenStudio.Common; using Scripts_TapTrial; public class TapTrial : Minigame { [Header("Components")] [SerializeField] private TapTrialPlayer player; [SerializeField] private Animator monkeyL, monkeyR, giraffe; [SerializeField] private ParticleSystem monkeyTapLL, monkeyTapLR, monkeyTapRL, monkeyTapRR; [SerializeField] private Transform rootPlayer, rootMonkeyL, rootMonkeyR; [SerializeField] private CanvasScroll bgScroll; [SerializeField] private SpriteRenderer flash; [Header("Values")] [SerializeField] private float jumpHeight = 4f; [SerializeField] private float monkeyJumpHeight = 3f; [SerializeField] private float maxFlashOpacity = 0.8f; private GameEvent bop = new(); private bool canBop = true; private bool shouldBop = true; private double jumpStartBeat = double.MinValue; public static TapTrial instance; private void Awake() { instance = this; } private void Update() { var cond = Conductor.instance; if (cond.isPlaying && !cond.isPaused) { if (shouldBop && cond.ReportBeat(ref bop.lastReportedBeat, bop.startBeat % 1)) { SingleBop(); } GiraffeUpdate(cond); JumpUpdate(cond); ScrollUpdate(cond); } } public void Scroll(bool scroll, bool flash, float multiplier) { scrolling = scroll; flashing = flash; scrollMultiplier = multiplier; ResetScroll(); } public void ResetScroll() { currentScrollSpeed = 0; currentNormalizedY = 0; flash.color = new Color(1, 1, 1, 0); } private bool scrolling; private bool flashing; [SerializeField] private float maxScrollSpeed = 0.25f; [SerializeField] private float accelerationSpeed = 0.01f; private float currentScrollSpeed = 0; private float currentNormalizedY = 0; private float scrollMultiplier = 1; private void ScrollUpdate(Conductor cond) { if (!scrolling) { bgScroll.Normalized = Vector2.zero; ResetScroll(); return; } currentNormalizedY += currentScrollSpeed * Time.deltaTime; bgScroll.NormalizedY = currentNormalizedY * scrollMultiplier; if (flashing) flash.color = new Color(1, 1, 1, Mathf.Lerp(0, maxFlashOpacity, currentNormalizedY)); currentScrollSpeed += accelerationSpeed * Time.deltaTime; currentScrollSpeed = Mathf.Min(maxScrollSpeed, currentScrollSpeed); } private void GiraffeUpdate(Conductor cond) { float normalizedGiraffeBeat = cond.GetPositionFromBeat(animStartBeat, animLength); EasingFunction.Function func = EasingFunction.GetEasingFunction(currentEase); if (normalizedGiraffeBeat <= 1f && normalizedGiraffeBeat >= 0f) { switch (currentAnim) { case GiraffeAnimation.Enter: giraffe.DoNormalizedAnimation("Enter", func(0, 1, normalizedGiraffeBeat)); break; case GiraffeAnimation.Exit: giraffe.DoNormalizedAnimation("Exit", func(0, 1, normalizedGiraffeBeat)); break; default: break; } } } private void JumpUpdate(Conductor cond) { float normalizedJumpBeat = cond.GetPositionFromBeat(jumpStartBeat, 1); if (normalizedJumpBeat >= 0 && normalizedJumpBeat <= 1) { if (normalizedJumpBeat >= 0.5f) { float normalizedUp = cond.GetPositionFromBeat(jumpStartBeat, 0.5); EasingFunction.Function func = EasingFunction.GetEasingFunction(EasingFunction.Ease.EaseOutQuad); float newPlayerY = func(0, jumpHeight, normalizedUp); float newMonkeyY = func(0, monkeyJumpHeight, normalizedUp); rootPlayer.localPosition = new Vector3(0, newPlayerY); rootMonkeyL.localPosition = new Vector3(0, newMonkeyY); rootMonkeyR.localPosition = new Vector3(0, newMonkeyY); } else { float normalizedDown = cond.GetPositionFromBeat(jumpStartBeat + 0.5, 0.5); EasingFunction.Function func = EasingFunction.GetEasingFunction(EasingFunction.Ease.EaseInQuad); float newPlayerY = func(jumpHeight, 0, normalizedDown); float newMonkeyY = func(monkeyJumpHeight, 0, normalizedDown); rootPlayer.localPosition = new Vector3(0, newPlayerY); rootMonkeyL.localPosition = new Vector3(0, newMonkeyY); rootMonkeyR.localPosition = new Vector3(0, newMonkeyY); } } else { rootPlayer.localPosition = Vector3.zero; rootMonkeyL.localPosition = Vector3.zero; rootMonkeyR.localPosition = Vector3.zero; } } public enum GiraffeAnimation { Enter, Exit, Blink } private GiraffeAnimation currentAnim = GiraffeAnimation.Enter; private double animStartBeat = -1; private float animLength = 0; private EasingFunction.Ease currentEase = EasingFunction.Ease.Linear; public void GiraffeAnims(double beat, float length, int type, int ease) { animStartBeat = beat; animLength = length; currentAnim = (GiraffeAnimation)type; currentEase = (EasingFunction.Ease)ease; if (currentAnim == GiraffeAnimation.Blink) giraffe.DoScaledAnimationAsync("Blink", 0.5f); } public void Bop(double beat, float length, bool bop, bool autoBop) { shouldBop = autoBop; if (bop) { List actions = new(); for (int i = 0; i < length; i++) { actions.Add(new BeatAction.Action(beat + i, delegate { SingleBop(); })); } BeatAction.New(gameObject, actions); } } private void SingleBop() { if (!canBop) return; PlayMonkeyAnimationScaledAsync("Bop", 0.5f); player.Bop(); } private bool IsEventAtBeat(double beat, double endBeat) { return EventCaller.GetAllInGameManagerList("tapTrial", new string[] { "tap", "double tap", "triple tap", "jump tap" }).Find(x => x.beat >= beat && x.beat < endBeat) != null; } public void Tap(double beat) { canBop = false; BeatAction.New(instance.gameObject, new List() { new BeatAction.Action(beat, delegate { PlayMonkeyAnimationScaledAsync("TapPrepare", 0.5f); player.PrepareTap(); }), new BeatAction.Action(beat + 1, delegate { PlayMonkeyAnimationScaledAsync("Tap", 0.5f); MonkeyParticles(true); }), new BeatAction.Action(beat + 1.5, delegate { if (!IsEventAtBeat(beat + 1, beat + 2)) canBop = true; }) }); MultiSound.Play(new MultiSound.Sound[] { new MultiSound.Sound("tapTrial/ook", beat), new MultiSound.Sound("tapTrial/tapMonkey", beat + 1, 1.4f, 0.5f), }); ScheduleInput(beat, 1, InputType.STANDARD_DOWN, JustTap, Miss, Empty); } public void DoubleTap(double beat) { canBop = false; BeatAction.New(instance.gameObject, new List() { new BeatAction.Action(beat, delegate { PlayMonkeyAnimationScaledAsync("DoubleTapPrepare", 0.5f); player.PrepareTap(true); }), new BeatAction.Action(beat + 0.5, delegate { PlayMonkeyAnimationScaledAsync("DoubleTapPrepare_2", 0.5f); }), new BeatAction.Action(beat + 1, delegate { PlayMonkeyAnimationScaledAsync("DoubleTap", 0.5f); MonkeyParticles(false); }), new BeatAction.Action(beat + 1.5, delegate { PlayMonkeyAnimationScaledAsync("DoubleTap", 0.5f); MonkeyParticles(false); if (!IsEventAtBeat(beat + 1, beat + 2)) canBop = true; }), }); MultiSound.Play(new MultiSound.Sound[] { new MultiSound.Sound("tapTrial/ookook", beat), new MultiSound.Sound("tapTrial/ookook", beat + 0.5), new MultiSound.Sound("tapTrial/tapMonkey", beat + 1, 1.4f, 0.5f), new MultiSound.Sound("tapTrial/tapMonkey", beat + 1.5, 1.4f, 0.5f), }); ScheduleInput(beat, 1, InputType.STANDARD_DOWN, JustDoubleTap, Miss, Empty); ScheduleInput(beat, 1.5, InputType.STANDARD_DOWN, JustDoubleTap, Miss, Empty); } public void TripleTap(double beat) { canBop = false; BeatAction.New(instance.gameObject, new List() { new BeatAction.Action(beat, delegate { player.PrepareTripleTap(beat); PlayMonkeyAnimationScaledAsync("PostPrepare_1", 0.5f); }), new BeatAction.Action(beat + 0.5, delegate { PlayMonkeyAnimationScaledAsync("PostPrepare_2", 0.5f); }), new BeatAction.Action(beat + 2, delegate { PlayMonkeyAnimationScaledAsync("PostTap", 0.5f); MonkeyParticles(true); }), new BeatAction.Action(beat + 2.5, delegate { PlayMonkeyAnimationScaledAsync("PostTap_2", 0.5f); MonkeyParticles(false); }), new BeatAction.Action(beat + 3, delegate { PlayMonkeyAnimationScaledAsync("PostTap", 0.5f); MonkeyParticles(true); }), }); MultiSound.Play(new MultiSound.Sound[] { new MultiSound.Sound("tapTrial/ooki1", beat), new MultiSound.Sound("tapTrial/ooki2", beat + 0.5), new MultiSound.Sound("tapTrial/tapMonkey", beat + 2, 1.4f, 0.5f), new MultiSound.Sound("tapTrial/tapMonkey", beat + 2.5, 1.4f, 0.5f), new MultiSound.Sound("tapTrial/tapMonkey", beat + 3, 1.4f, 0.5f), }); ScheduleInput(beat, 2, InputType.STANDARD_DOWN, JustTripleTap, Miss, Empty); ScheduleInput(beat, 2.5, InputType.STANDARD_DOWN, JustTripleTap, Miss, Empty); ScheduleInput(beat, 3, InputType.STANDARD_DOWN, JustTripleTap, Miss, Empty); } public void JumpPrepare() { canBop = false; player.PrepareJump(); PlayMonkeyAnimationScaledAsync("JumpPrepare", 0.5f); } public void JumpTap(double beat, bool final) { canBop = false; jumpStartBeat = beat; BeatAction.New(gameObject, new List() { new BeatAction.Action(beat, delegate { player.Jump(final); PlayMonkeyAnimationScaledAsync(final ? "Jump" : "JumpTap", 0.5f); }), new BeatAction.Action(beat + 1, delegate { PlayMonkeyAnimationScaledAsync(final ? "FinalJumpTap" : "Jumpactualtap", 0.5f); MonkeyParticles(true); MonkeyParticles(false); }), new BeatAction.Action(beat + 1.5, delegate { if (!IsEventAtBeat(beat + 1, beat + 2)) canBop = final; }) }); MultiSound.Play(new MultiSound.Sound[] { new MultiSound.Sound(final ? "tapTrial/jumptap2" : "tapTrial/jumptap1", beat), new MultiSound.Sound("tapTrial/tapMonkey", beat + 1, 1.4f, 0.5f), }); ScheduleInput(beat, 1, InputType.STANDARD_DOWN, final ? JustJumpTapFinal : JustJumpTap, final ? MissJumpFinal : MissJump, Empty); } private void JustJumpTap(PlayerActionEvent caller, float state) { player.JumpTap(state < 1f && state > -1f, false); } private void JustJumpTapFinal(PlayerActionEvent caller, float state) { player.JumpTap(state < 1f && state > -1f, true); } private void MissJump(PlayerActionEvent caller) { player.JumpTapMiss(false); if (giraffe.IsAnimationNotPlaying()) giraffe.DoScaledAnimationAsync("Miss", 0.5f); ResetScroll(); } private void MissJumpFinal(PlayerActionEvent caller) { player.JumpTapMiss(true); if (giraffe.IsAnimationNotPlaying()) giraffe.DoScaledAnimationAsync("Miss", 0.5f); ResetScroll(); } private void JustTap(PlayerActionEvent caller, float state) { player.Tap(state < 1f && state > -1f); } private void JustDoubleTap(PlayerActionEvent caller, float state) { player.Tap(state < 1f && state > -1f, true); } private void JustTripleTap(PlayerActionEvent caller, float state) { player.TripleTap(state < 1f && state > -1f); } private void Miss(PlayerActionEvent caller) { if (giraffe.IsAnimationNotPlaying() && currentAnim != GiraffeAnimation.Exit) giraffe.DoScaledAnimationAsync("Miss", 0.5f); ResetScroll(); } private void Empty(PlayerActionEvent caller) { } private void PlayMonkeyAnimationScaledAsync(string name, float timeScale) { monkeyL.DoScaledAnimationAsync(name, timeScale); monkeyR.DoScaledAnimationAsync(name, timeScale); } private void MonkeyParticles(bool left) { ParticleSystem spawnedEffectL = Instantiate(left ? monkeyTapLL : monkeyTapLR, transform); spawnedEffectL.Play(); ParticleSystem spawnedEffectR = Instantiate(left ? monkeyTapRL : monkeyTapRR, transform); spawnedEffectR.Play(); } } }