HeavenStudio/Assets/Scripts/Games/DoubleDate/DoubleDate.cs
minenice55 bb2ae74339
Integration of Jukebox Library (#451)
* add Jukebox library

todo:
- saving / loading of new format
- inferrence of unknown data like past versions
- move the temporary float casts to proper use of double
- make sound related functions take double for timing
- inform people that the Jukebox sound player was renamed to SoundByte lol

* make sound, input scheduling, and super curve use double precision

* successfully load charts

* editor works again

v1 riqs can be saved and loaded

* first tempo and volume markers are unmovable

fix loading of charts' easing values

* use gsync / freesync

* update Jukebox refs to SoundByte

* game events use double part 1

Air Rally - Glee Club converted

* don't load song if chart load fails

* finish conversion of all minigames

* remove editor waveform toggle

* timeline now respects added song offset length

clear cache files on app close
prepped notes for dsp sync

* update timeline length when offset changed

* update to latest Jukebox

* make error panel object in global game manager

* improve conductor music scheduling

* added error message box

fix first game events sometimes not playing
2023-06-10 15:13:29 -04:00

357 lines
12 KiB
C#

using HeavenStudio.Util;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace HeavenStudio.Games.Loaders
{
using static Minigames;
public static class RvlDoubleDateLoader
{
public static Minigame AddGame(EventCaller eventCaller) {
return new Minigame("doubleDate", "Double Date", "ef854a", false, false, new List<GameAction>()
{
new GameAction("bop", "Bop")
{
function = delegate { var e = eventCaller.currentEntity; DoubleDate.instance.Bop(e.beat, e.length, e["bop"], e["autoBop"]); },
resizable = true,
parameters = new List<Param>()
{
new Param("bop", true, "Bop", "Should the two couples bop?"),
new Param("autoBop", false, "Bop (Auto)", "Should the two couples auto bop?")
}
},
new GameAction("soccer", "Soccer Ball")
{
preFunction = delegate { var e = eventCaller.currentEntity; DoubleDate.QueueSoccerBall(e.beat); },
preFunctionLength = 1f,
defaultLength = 2f,
},
new GameAction("basket", "Basket Ball")
{
preFunction = delegate { var e = eventCaller.currentEntity; DoubleDate.QueueBasketBall(e.beat); },
preFunctionLength = 1f,
defaultLength = 2f,
},
new GameAction("football", "Football")
{
preFunction = delegate { var e = eventCaller.currentEntity; DoubleDate.QueueFootBall(e.beat); },
preFunctionLength = 1f,
defaultLength = 2.5f,
},
},
new List<string>() {"rvl", "normal"},
"rvldate", "en",
new List<string>() {}
);
}
}
}
namespace HeavenStudio.Games
{
using Scripts_DoubleDate;
public class DoubleDate : Minigame
{
[Header("Prefabs")]
[SerializeField] GameObject soccer;
[SerializeField] GameObject basket;
[SerializeField] GameObject football;
[SerializeField] GameObject dropShadow;
[SerializeField] ParticleSystem leaves;
[Header("Components")]
[SerializeField] Animator boyAnim;
[SerializeField] Animator girlAnim;
[SerializeField] DoubleDateWeasels weasels;
[SerializeField] Animator treeAnim;
[SerializeField] GameObject clouds;
[Header("Variables")]
[SerializeField] public float cloudSpeed;
[SerializeField] public float cloudDistance;
[SerializeField] public float floorHeight;
[SerializeField] public float shadowDepthScaleMin;
[SerializeField] public float shadowDepthScaleMax;
[SerializeField] SuperCurveObject.Path[] ballBouncePaths;
double lastGirlGacha = double.MinValue;
bool shouldBop = true;
bool canBop = true;
GameEvent bop = new GameEvent();
public static DoubleDate instance;
public static List<QueuedBall> queuedBalls = new List<QueuedBall>();
[NonSerialized] public double lastHitWeasel = double.MinValue;
public enum BallType
{
Soccer,
Basket,
Football
}
public struct QueuedBall
{
public double beat;
public BallType type;
}
// Editor gizmo to draw trajectories
new void OnDrawGizmos()
{
base.OnDrawGizmos();
foreach (SuperCurveObject.Path path in ballBouncePaths)
{
if (path.preview)
{
soccer.GetComponent<SoccerBall>().DrawEditorGizmo(path);
}
}
}
public override void OnPlay(double beat)
{
queuedBalls.Clear();
}
private void OnDestroy() {
queuedBalls.Clear();
foreach (var evt in scheduledInputs)
{
evt.Disable();
}
}
private void Awake()
{
instance = this;
}
private void Start() {
clouds.transform.position = Vector3.left * ((Time.realtimeSinceStartup * cloudSpeed) % cloudDistance);
}
void Update()
{
var cond = Conductor.instance;
if (cond.isPlaying && !cond.isPaused)
{
if (queuedBalls.Count != 0)
{
foreach (QueuedBall ball in queuedBalls)
{
switch (ball.type)
{
case BallType.Soccer:
SpawnSoccerBall(ball.beat);
break;
case BallType.Basket:
SpawnBasketBall(ball.beat);
break;
case BallType.Football:
SpawnFootBall(ball.beat);
break;
}
}
queuedBalls.Clear();
}
if (cond.ReportBeat(ref bop.lastReportedBeat, bop.startBeat % 1) && shouldBop)
{
SingleBop();
}
}
else
{
if ((!cond.isPaused) && queuedBalls.Count != 0)
{
queuedBalls.Clear();
}
}
if (PlayerInput.Pressed() && !IsExpectingInputNow(InputType.STANDARD_DOWN))
{
SoundByte.PlayOneShotGame("doubleDate/kick_whiff");
Kick(true, true, false);
}
clouds.transform.position = Vector3.left * ((Time.realtimeSinceStartup * cloudSpeed) % cloudDistance);
}
public void ToggleBop(bool go)
{
canBop = go;
}
public void Bop(double beat, float length, bool goBop, bool autoBop)
{
shouldBop = autoBop;
if (goBop)
{
for (int i = 0; i < length; i++)
{
BeatAction.New(instance.gameObject, new List<BeatAction.Action>()
{
new BeatAction.Action(beat + i, delegate { SingleBop(); })
});
}
}
}
void SingleBop()
{
if (canBop)
{
boyAnim.DoScaledAnimationAsync("IdleBop", 1f);
}
if (Conductor.instance.songPositionInBeatsAsDouble > lastGirlGacha)
girlAnim.DoScaledAnimationAsync("GirlBop", 1f);
weasels.Bop();
}
public void Kick(bool hit = true, bool forceNoLeaves = false, bool weaselsHappy = true, bool jump = false)
{
if (hit)
{
boyAnim.DoScaledAnimationAsync("Kick", 0.5f);
if (jump)
{
weasels.Jump();
lastGirlGacha = Conductor.instance.songPositionInBeatsAsDouble + 0.5f;
girlAnim.DoScaledAnimationAsync("GirlLookUp", 0.5f);
}
else if (weaselsHappy) weasels.Happy();
if (!forceNoLeaves)
{
BeatAction.New(instance.gameObject, new List<BeatAction.Action>()
{
new BeatAction.Action(Conductor.instance.songPositionInBeatsAsDouble + 1f, delegate
{
leaves.Play();
treeAnim.DoScaledAnimationAsync("TreeRustle", 1f);
})
});
}
}
else
{
boyAnim.DoScaledAnimationAsync("Barely", 0.5f);
weasels.Surprise();
}
}
public static void QueueSoccerBall(double beat)
{
if (GameManager.instance.currentGame != "doubleDate")
{
queuedBalls.Add(new QueuedBall()
{
beat = beat,
type = BallType.Soccer
});
}
else
{
instance.SpawnSoccerBall(beat);
}
SoundByte.PlayOneShotGame("doubleDate/soccerBounce", beat, forcePlay: true);
}
public static void QueueBasketBall(double beat)
{
if (GameManager.instance.currentGame != "doubleDate")
{
queuedBalls.Add(new QueuedBall()
{
beat = beat,
type = BallType.Basket
});
}
else
{
instance.SpawnBasketBall(beat);
}
MultiSound.Play(new MultiSound.Sound[]
{
new MultiSound.Sound("doubleDate/basketballBounce", beat),
new MultiSound.Sound("doubleDate/basketballBounce", beat + 0.75f),
}, forcePlay: true);
}
public static void QueueFootBall(double beat)
{
if (GameManager.instance.currentGame != "doubleDate")
{
queuedBalls.Add(new QueuedBall()
{
beat = beat,
type = BallType.Football
});
}
else
{
instance.SpawnFootBall(beat);
}
MultiSound.Play(new MultiSound.Sound[]
{
new MultiSound.Sound("doubleDate/footballBounce", beat),
new MultiSound.Sound("doubleDate/footballBounce", beat + 0.75f),
}, forcePlay: true);
}
public void SpawnSoccerBall(double beat)
{
SoccerBall spawnedBall = Instantiate(soccer, instance.transform).GetComponent<SoccerBall>();
spawnedBall.Init(beat);
}
public void SpawnBasketBall(double beat)
{
Basketball spawnedBall = Instantiate(basket, instance.transform).GetComponent<Basketball>();
spawnedBall.Init(beat);
}
public void SpawnFootBall(double beat)
{
Football spawnedBall = Instantiate(football, instance.transform).GetComponent<Football>();
spawnedBall.Init(beat);
}
public void MissKick(double beat, bool hit = false)
{
lastGirlGacha = Conductor.instance.songPositionInBeatsAsDouble + 1.5f;
girlAnim.DoScaledAnimationAsync("GirlSad", 0.5f);
if (hit)
{
lastHitWeasel = Conductor.instance.songPositionInBeatsAsDouble;
BeatAction.New(gameObject, new List<BeatAction.Action>()
{
new BeatAction.Action(beat - (0.25f/3f), delegate { weasels.Hit(beat); }),
});
}
else
{
lastHitWeasel = Conductor.instance.songPositionInBeatsAsDouble;
BeatAction.New(gameObject, new List<BeatAction.Action>()
{
new BeatAction.Action(beat + 0.25, delegate { weasels.Hide(beat + 0.25f); }),
});
}
}
public SuperCurveObject.Path GetPath(string name)
{
foreach (SuperCurveObject.Path path in ballBouncePaths)
{
if (path.name == name)
{
return path;
}
}
return default(SuperCurveObject.Path);
}
public GameObject MakeDropShadow()
{
GameObject shadow = Instantiate(dropShadow, transform);
return shadow;
}
}
}