HeavenStudio/Assets/Scripts/Games/FreezeFrame/FreezeFrame.cs
playinful 80ae5eaf6b
Mostly Bugfixes (#823)
UI tweaks:
- Removed "Clear Photo Cache" GameAction, added "Clear Previous Photos" parameter to the Car cues
- Rolled Move T.J., Rotate T.J., and Scale T.J. GameActions into a single GameAction
- Legacy functions still exist for remixes created on previous nightly builds

Bugs fixed:
- Fixed a bug where the spacing of slow cars would be incorrect after a game switch
- Made particles scale with tempo
  - Added functionality for respecting Rate over Distance in ParticleSystemHelpers
- Fixed a bug where the photo cache was not cleared after stopping playback of a remix.
- Fixed a bug where, upon showing photos, if the remix playback was stopped before the photos were removed from the screen, they would remain on the screen until playback started over.
- Removed an unnecessary call to Debug.Log that had been left in by mistake.
- Fixed a bug where cars' idle animations would play too slowly and at inconsistent timings.

Other refactorings:
- Created a "Car" MonoBehavior attached to both the NearCar and FarCar prefabs that handles the movement of cars, removing the need for the "QueuedCars" property of FreezeFrame.
- Added a failsafe in the ShowCrowd function for if the user indicates a crowd index that does not currently exist.
- Converted one SoundByte.PlayOneShot call into a MultiSound.Sound object.
2024-04-05 01:27:59 +00:00

1495 lines
66 KiB
C#

using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HeavenStudio.Util;
using HeavenStudio.InputSystem;
using Jukebox;
namespace HeavenStudio.Games.Loaders
{
using static Minigames;
public static class NtrFreezeFrameLoader
{
public static Minigame AddGame(EventCaller eventCaller)
{
return new Minigame("freezeFrame", "Freeze Frame", "8b93b4", false, false, new List<GameAction>()
{
new GameAction("bop", "Bop")
{
function = delegate { var e = eventCaller.currentEntity; FreezeFrame.SetBopping(e.beat, e.length, e["bop"], e["autoBop"], e["blink"], e["autoBlink"]); },
defaultLength = 1f,
resizable = true,
parameters = new List<Param>()
{
new Param("bop", false, "Bop", "Set the type of photo to use."),
new Param("autoBop", true, "Bop (Auto)", "Set the type of photo to use."),
new Param("blink", false, "Crosshair Blink", "Set the type of photo to use."),
new Param("autoBlink", true, "Crosshair Blink (Auto)", "Set the type of photo to use."),
}
},
// cues
new GameAction("slowCar", "Slow Car")
{
function = delegate
{
var e = eventCaller.currentEntity;
FreezeFrame.SlowCarCue(e.beat, e["variant"], e["mute"], e["clear"]);
},
inactiveFunction = delegate
{
var e = eventCaller.currentEntity;
if (!(bool)e["mute"]) FreezeFrame.SlowCarSFX();
},
defaultLength = 3f,
parameters = new List<Param>()
{
new Param("variant", FreezeFrame.PhotoType.Random, "Photo Variant", "Set the type of photo to use."),
new Param("mute", false, "Mute", "Mute the sound of the cue."),
new Param("clear", false, "Clear Previous Photos", "Dispose of all the photos taken before this one."),
new Param("autoShowPhotos", true, "Auto Show Photos", "Automagically show the photos after they're taken.", new List<Param.CollapseParam>()
{
new Param.CollapseParam((x, _) => (bool)x, new string[] { "gradeType", "audience" })
}),
new Param("gradeType", FreezeFrame.GradeType.Symbols, "Rating Type", "Choose whether to use the English or Japanese variant of the grading screen."),
new Param("audience", true, "Crowd Cheer", "Set whether or not the audience should cheer when the photos are shown."),
}
},
new GameAction("fastCar", "Fast Car")
{
function = delegate
{
var e = eventCaller.currentEntity;
FreezeFrame.FastCarCue(e.beat, e["variant"], e["mute"], e["clear"]);
},
inactiveFunction = delegate
{
var e = eventCaller.currentEntity;
if (!(bool)e["mute"]) FreezeFrame.FastCarSFX();
},
defaultLength = 3f,
parameters = new List<Param>()
{
new Param("variant", FreezeFrame.PhotoType.Random, "Photo Variant", "Set the type of photo to use."),
new Param("mute", false, "Mute", "Mute the sound of the cue."),
new Param("clear", false, "Clear Previous Photos", "Dispose of all the photos taken before this one."),
new Param("autoShowPhotos", true, "Auto Show Photos", "Automagically show the photos after they're taken.", new List<Param.CollapseParam>()
{
new Param.CollapseParam((x, _) => (bool)x, new string[] { "gradeType", "audience" })
}),
new Param("gradeType", FreezeFrame.GradeType.Symbols, "Rating Type", "Choose whether to use the English or Japanese variant of the grading screen."),
new Param("audience", true, "Crowd Cheer", "Set whether or not the audience should cheer when the photos are shown."),
}
},
new GameAction("showPhotos", "Show Photos")
{
function = delegate { var e = eventCaller.currentEntity; FreezeFrame.ShowPhotos(e.beat, e.length, e["gradeType"], e["audience"], e["clearCache"]); },
defaultLength = 1,
resizable = true,
parameters = new List<Param>()
{
new Param("gradeType", FreezeFrame.GradeType.Symbols, "Rating Type", "Choose whether to use the English or Japanese variant of the grading screen."),
new Param("audience", true, "Crowd Cheer", "Set whether or not the audience should cheer when the photos are shown."),
new Param("clearCache", true, "Clear Photos", "Clears the photo cache after the photos are shown."),
}
},
// distractions
new GameAction("spawnPerson", "Spawn Walker")
{
function = delegate { var e = eventCaller.currentEntity; FreezeFrame.SummonWalker(e); },
defaultLength = 4f,
resizable = true,
parameters = new List<Param>()
{
new Param("personType", FreezeFrame.PersonType.Dude1, "Walker Type", "Choose the type of walker to spawn."),
new Param("direction", FreezeFrame.PersonDirection.Random, "Direction", "Choose the direction from which to spawn the walker."),
new Param("layer", new EntityTypes.Integer(-10, 10, 0), "Layer", "The layer on which this walker should spawn (higher numbers are shown in front)."),
}
},
new GameAction("spawnCrowd", "Show/Hide Crowd")
{
function = delegate { var e = eventCaller.currentEntity; FreezeFrame.ToggleCrowd(e.beat, e["crowd"], e["customCrowd"], e["crowdFarLeft"], e["crowdLeft"], e["crowdRight"], e["crowdFarRight"], e["billboard"]); },
defaultLength = 0.5f,
parameters = new List<Param>()
{
new Param("crowd", true, "Show Crowd", "Choose whether to spawn or despawn the crowd."),
new Param("customCrowd", false, "Custom Crowd", "Select to customize the crowd.", new List<Param.CollapseParam>()
{
new Param.CollapseParam((x, _) => (bool)x, new string[] { "crowdFarLeft", "crowdLeft", "crowdRight", "crowdFarRight" })
}),
new Param("crowdFarLeft" , FreezeFrame.CustomCrowdType.PinkDancers, "Far Left" , "Select the graphic to display on the far left."),
new Param("crowdLeft" , FreezeFrame.CustomCrowdType.YellowDancers, "Center Left" , "Select the graphic to display on the near left."),
new Param("crowdRight" , FreezeFrame.CustomCrowdType.TealDancers, "Center Right", "Select the graphic to display on the near right."),
new Param("crowdFarRight", FreezeFrame.CustomCrowdType.PinkDancers, "Far Right" , "Select the graphic to display on the far right."),
new Param("billboard", false, "Show Billboard", "Choose whether to show or hide the billboards."),
}
},
new GameAction("introSign", "Intro Sign")
{
function = delegate { var e = eventCaller.currentEntity; FreezeFrame.DoIntroSign(e.beat, e.length, e["enter"], e["ease"]); },
defaultLength = 4f,
resizable = true,
parameters = new List<Param>()
{
new Param("enter", true, "Enter", "Choose the sign should enter or exit."),
new Param("ease", Util.EasingFunction.Ease.Linear, "Ease", "Set the easing of the action."),
}
},
new GameAction("introLights", "Intro Lights")
{
function = delegate { var e = eventCaller.currentEntity; FreezeFrame.IntroLightsAnim(e.beat, e.length, e["lightsOn"]); FreezeFrame.IntroLightsSound(e.beat, e.length, e["lightsOn"]); },
inactiveFunction = delegate { var e = eventCaller.currentEntity; FreezeFrame.IntroLightsSound(e.beat, e.length, e["lightsOn"]); },
defaultLength = 1f,
resizable = true,
parameters = new List<Param>()
{
new Param("lightsOn", true, "Turn Lights On", "Choose whether to turn the lights on or off."),
}
},
// settings
new GameAction("toggleOverlay", "Toggle Overlay")
{
function = delegate { var e = eventCaller.currentEntity; FreezeFrame.ToggleOverlay(e.beat, e["showOverlay"], e["showCameraMan"], e["followCamera"]); },
defaultLength = 0.5f,
parameters = new List<Param>()
{
new Param("showOverlay", true, "Show Overlay", "Choose whether or not to show the camera overlay."),
new Param("showCameraMan", true, "Show T.J.", "Choose whether or not to show the box containing T.J. Snapper."),
new Param("followCamera", true, "Follow Camera", "Choose whether or not the overlay should follow the camera."),
}
},
new GameAction("neoMoveCameraMan", "Move T.J.")
{
function = delegate { var e = eventCaller.currentEntity; FreezeFrame.SetMoveCameraMan(e); },
defaultLength = 1f,
resizable = true,
parameters = new List<Param>()
{
new Param("doMove", false, "Move", "Select this option if you want to move T.J.", new List<Param.CollapseParam>()
{
new Param.CollapseParam((x, _) => (bool)x, new string[] { "endMoveX", "endMoveY" } ),
new Param.CollapseParam((x, e) => (bool)x && (Util.EasingFunction.Ease)e["ease"] != Util.EasingFunction.Ease.Instant, new string[] { "startMoveX", "startMoveY" }),
new Param.CollapseParam((_, e) => (bool)e["doMove"] || (bool)e["doRotate"] || (bool)e["doScale"], new string[] { "ease" })
}),
new Param("startMoveX", new EntityTypes.Float(-20f, 20f, 0f), "Start X", "Set the X position from which to move."),
new Param("startMoveY", new EntityTypes.Float(-20f, 20f, 0f), "Start Y", "Set the Y position from which to move."),
new Param("endMoveX", new EntityTypes.Float(-20f, 20f, 0f), "End X", "Set the X position to which to move."),
new Param("endMoveY", new EntityTypes.Float(-20f, 20f, 0f), "End Y", "Set the Y position to which to move."),
new Param("doRotate", false, "Rotate", "Select this option if you want to rotate T.J.", new List<Param.CollapseParam>()
{
new Param.CollapseParam((x, _) => (bool)x, new string[] { "endRotDegrees" } ),
new Param.CollapseParam((x, e) => (bool)x && (Util.EasingFunction.Ease)e["ease"] != Util.EasingFunction.Ease.Instant, new string[] { "startRotDegrees" }),
new Param.CollapseParam((_, e) => (bool)e["doMove"] || (bool)e["doRotate"] || (bool)e["doScale"], new string[] { "ease" })
}),
new Param("startRotDegrees", new EntityTypes.Float(-360f, 360f, 0f), "Start Rotation", "Set the amount of degrees at which to begin rotating."),
new Param("endRotDegrees", new EntityTypes.Float(-360f, 360f, 0f), "End Rotation", "Set the amount of degrees at which to finish rotating."),
new Param("doScale", false, "Scale", "Select this option if you want to change T.J.'s scale.", new List<Param.CollapseParam>()
{
new Param.CollapseParam((x, _) => (bool)x, new string[] { "endScaleX", "endScaleY" } ),
new Param.CollapseParam((x, e) => (bool)x && (Util.EasingFunction.Ease)e["ease"] != Util.EasingFunction.Ease.Instant, new string[] { "startScaleX", "startScaleY" }),
new Param.CollapseParam((_, e) => (bool)e["doMove"] || (bool)e["doRotate"] || (bool)e["doScale"], new string[] { "ease" })
}),
new Param("startScaleX", new EntityTypes.Float(-5f, 5f, 1f), "Start Scale X", "Set the desired scale on the X axis at which to start."),
new Param("startScaleY", new EntityTypes.Float(-5f, 5f, 1f), "Start Scale Y", "Set the desired scale on the Y axis at which to start."),
new Param("endScaleX", new EntityTypes.Float(-5f, 5f, 1f), "End Scale X", "Set the desired scale on the X axis at which to end."),
new Param("endScaleY", new EntityTypes.Float(-5f, 5f, 1f), "End Scale Y", "Set the desired scale on the Y axis at which to end."),
new Param("ease", Util.EasingFunction.Ease.Linear, "Ease", "Set the easing for the action.", new List<Param.CollapseParam>()
{
new Param.CollapseParam((x, e) => (Util.EasingFunction.Ease)x != Util.EasingFunction.Ease.Instant && (bool)e["doMove"], new string[] { "startMoveX", "startMoveY" }),
new Param.CollapseParam((x, e) => (Util.EasingFunction.Ease)x != Util.EasingFunction.Ease.Instant && (bool)e["doRotate"], new string[] { "startRotDegrees" }),
new Param.CollapseParam((x, e) => (Util.EasingFunction.Ease)x != Util.EasingFunction.Ease.Instant && (bool)e["doScale"], new string[] { "startScaleX", "startScaleY" }),
})
}
},
// DEPRECATED ACTIONS
// DEPRECATED ACTIONS
// DEPRECATED ACTIONS
new GameAction("clearPhotos", "Clear Photo Cache (DEPRECATED)")
{
function = delegate { var e = eventCaller.currentEntity; FreezeFrame.ClearPhotos(); },
inactiveFunction = delegate { var e = eventCaller.currentEntity; FreezeFrame.ClearPhotos(); },
defaultLength = 0.5f,
parameters = new List<Param>()
{
},
hidden = true
},
new GameAction("moveCameraMan", "Move T.J. (DEPRECATED)")
{
function = delegate { var e = eventCaller.currentEntity; FreezeFrame.SetMoveCameraMan_OLD(e.beat, e.length, e["startPosX"], e["startPosY"], e["endPosX"], e["endPosY"], e["ease"]); },
defaultLength = 1f,
resizable = true,
hidden = true,
parameters = new List<Param>()
{
new Param("startPosX", new EntityTypes.Float(-5.0f, 5.0f, 0.0f), "Start X Position", "X position at which to start."),
new Param("startPosY", new EntityTypes.Float(-5.0f, 5.0f, 0.0f), "Start Y Position", "Y position at which to start."),
new Param("endPosX" , new EntityTypes.Float(-5.0f, 5.0f, 0.0f), "End X Position" , "X position at which to end."),
new Param("endPosY" , new EntityTypes.Float(-5.0f, 5.0f, 0.0f), "End Y Position" , "Y position at which to end."),
new Param("ease", Util.EasingFunction.Ease.Linear, "Ease", "Set the easing of the action.", new List<Param.CollapseParam>()
{
new Param.CollapseParam((x, _) => (Util.EasingFunction.Ease)x != Util.EasingFunction.Ease.Instant, new string[] { "startPosX", "startPosY" })
}),
//new Param("flipX", false, "Flip", "Set whether or not to flip T.J. horizontally."),
}
},
new GameAction("rotateCameraMan", "Rotate T.J. (DEPRECATED)")
{
function = delegate { var e = eventCaller.currentEntity; FreezeFrame.SetRotateCameraMan_OLD(e.beat, e.length, e["startRot"], e["endRot"], e["ease"]); },
defaultLength = 1f,
resizable = true,
hidden = true,
parameters = new List<Param>()
{
new Param("startRot", new EntityTypes.Float(-360.0f, 360.0f, 0.0f), "Start Rotation", "Rotation degrees at which to start."),
new Param("endRot" , new EntityTypes.Float(-360.0f, 360.0f, 0.0f), "End Rotation" , "Rotation degrees position at which to end."),
new Param("ease", Util.EasingFunction.Ease.Linear, "Ease", "Set the easing of the action.", new List<Param.CollapseParam>()
{
new Param.CollapseParam((x, _) => (Util.EasingFunction.Ease)x != Util.EasingFunction.Ease.Instant, new string[] { "startRot" })
}),
}
},
new GameAction("scaleCameraMan", "Scale T.J. (DEPRECATED)")
{
function = delegate { var e = eventCaller.currentEntity; FreezeFrame.SetScaleCameraMan_OLD(e.beat, e.length, e["startSizeX"], e["startSizeY"], e["endSizeX"], e["endSizeY"], e["ease"]); },
defaultLength = 1f,
resizable = true,
hidden = true,
parameters = new List<Param>()
{
new Param("startSizeX", new EntityTypes.Float(-5.0f, 5.0f, 1.0f), "Start Scale X", "Horizontal scale at which to start."),
new Param("startSizeY", new EntityTypes.Float(-5.0f, 5.0f, 1.0f), "Start Scale Y", "Vertical scale at which to start."),
new Param("endSizeX", new EntityTypes.Float(-5.0f, 5.0f, 1.0f), "End Scale X" , "Horizontal scale at which to end."),
new Param("endSizeY", new EntityTypes.Float(-5.0f, 5.0f, 1.0f), "End Scale Y" , "Vertical scale at which to end."),
new Param("ease", Util.EasingFunction.Ease.Linear, "Ease", "Set the easing of the action.", new List<Param.CollapseParam>()
{
new Param.CollapseParam((x, _) => (Util.EasingFunction.Ease)x != Util.EasingFunction.Ease.Instant, new string[] { "startSizeX", "startSizeY" })
}),
}
},
},
new List<string>() { "ntr", "normal" },
"ntrcameraman", "en",
chronologicalSortKey: 13
);
}
}
}
namespace HeavenStudio.Games
{
using HeavenStudio.Common;
using Scripts_FreezeFrame;
using UnityEngine.Rendering;
public class FreezeFrame : Minigame
{
/*
BIG LIST OF TODOS
- finish sounds
- make pitching and panning work
- wait for upscale
- make particles random sprites
*/
public static FreezeFrame Instance
{
get
{
if (GameManager.instance.minigame is FreezeFrame instance)
return instance;
return null;
}
}
[SerializeField] Animator CameraMan;
[SerializeField] Photograph[] Photographs;
[SerializeField] Photograph Photograph1;
[SerializeField] Photograph Photograph2;
[SerializeField] Photograph Photograph3;
[SerializeField] Animator Results;
[SerializeField] Animator IntroSign;
[SerializeField] GameObject Overlay;
[SerializeField] GameObject Crosshair;
[SerializeField] Animator Shutter;
[SerializeField] GameObject DimRect;
[SerializeField] StickyCanvas StickyLayer;
[SerializeField] Transform FarCarSpawn;
[SerializeField] GameObject FarCarPrefab;
[SerializeField] Transform NearCarSpawn;
[SerializeField] GameObject NearCarPrefab;
[SerializeField] Transform WalkerSpawn;
[SerializeField] GameObject WalkerPrefab;
[SerializeField] Animator Crowd;
[SerializeField] SpriteRenderer CrowdFarLeft;
[SerializeField] SpriteRenderer CrowdLeft;
[SerializeField] SpriteRenderer CrowdRight;
[SerializeField] SpriteRenderer CrowdFarRight;
[SerializeField] Sprite[] CrowdSprites;
[SerializeField] GameObject Billboards;
public bool DoAutoBop { get; set; } = true;
public bool DoAutoCrosshairBlink { get; set; } = true;
public bool ShowOverlay { get; set; } = true;
public bool ShowCameraMan { get; set; } = true;
public bool OverlayFollowCamera { get; set; } = true;
public bool ShowCrowd { get; set; } = false;
public bool ShowBillboard { get; set; } = false;
public List<WalkerArgs> Walkers { get; set; } = new();
public bool SignIsMoving { get; set; } = false;
public SignMoveArgs CurrentSignArgs { get; set; }
public static Vector3 CameraManStartPos { get; private set; }
public bool CameraManMoving { get; set; } = false;
public CameraManMoveArgs CurrentCameraManMoveArgs;
public bool CameraManRotating { get; set; } = false;
public CameraManRotateArgs CurrentCameraManRotateArgs;
public bool CameraManScaling { get; set; } = false;
public CameraManScaleArgs CurrentCameraManScaleArgs;
public static List<PhotoArgs> PhotoList = new();
public Dictionary<PlayerActionEvent, PhotoArgs> EventArgs = new();
public bool IsShowingPhotos { get; set; } = false;
public static Dictionary<RiqEntity, PersonDirection> WalkerDirections = new();
// UNITY BUILTIN METHODS
void Start()
{
if (conductor != null && !conductor.isPlaying && !conductor.isPaused)
{
ClearPhotos();
}
CameraManStartPos = CameraMan.transform.localPosition;
}
void Update()
{
if (conductor != null && !conductor.isPlaying && !conductor.isPaused)
{
CameraMan.Play("Idle");
IsShowingPhotos = false;
}
if (PlayerInput.GetIsAction(InputAction_BasicPress) && !IsExpectingInputNow(InputAction_BasicPress) && !IsShowingPhotos)
{
CameraFlash();
//ScoreMiss();
}
// sign
if (SignIsMoving)
{
float normalizedBeat = conductor.GetPositionFromBeat(CurrentSignArgs.StartTime, CurrentSignArgs.Length);
Util.EasingFunction.Function func = Util.EasingFunction.GetEasingFunction(CurrentSignArgs.Ease);
float newPos = func(0f, 1f, normalizedBeat);
IntroSign.DoNormalizedAnimation(CurrentSignArgs.AnimName, newPos, animLayer: 0);
if (normalizedBeat >= 1f)
SignIsMoving = false;
}
// move TJ
if (CameraManMoving)
{
float normalizedBeat = conductor.GetPositionFromBeat(CurrentCameraManMoveArgs.StartBeat, CurrentCameraManMoveArgs.Length);
Util.EasingFunction.Function func = Util.EasingFunction.GetEasingFunction(CurrentCameraManMoveArgs.Ease);
float newPos = func(0f, 1f, normalizedBeat);
Vector3 diff = CurrentCameraManMoveArgs.EndPos - CurrentCameraManMoveArgs.StartPos;
Vector3 diffPos = newPos * diff;
CameraMan.transform.localPosition = CurrentCameraManMoveArgs.StartPos + diffPos;
if (normalizedBeat >= 1f)
{
CameraMan.transform.localPosition = CurrentCameraManMoveArgs.EndPos;
CameraManMoving = false;
}
}
if (CameraManRotating)
{
float normalizedBeat = conductor.GetPositionFromBeat(CurrentCameraManRotateArgs.StartBeat, CurrentCameraManRotateArgs.Length);
Util.EasingFunction.Function func = Util.EasingFunction.GetEasingFunction(CurrentCameraManRotateArgs.Ease);
float newPos = func(0f, 1f, normalizedBeat);
float diff = CurrentCameraManRotateArgs.EndRot - CurrentCameraManRotateArgs.StartRot;
float diffPos = newPos * diff;
CameraMan.transform.localEulerAngles = new Vector3(0, 0, CurrentCameraManRotateArgs.StartRot + diffPos);
if (normalizedBeat >= 1f)
{
CameraMan.transform.localEulerAngles = new Vector3(0, 0, CurrentCameraManRotateArgs.EndRot);
CameraManRotating = false;
}
}
if (CameraManScaling)
{
float normalizedBeat = conductor.GetPositionFromBeat(CurrentCameraManScaleArgs.StartBeat, CurrentCameraManScaleArgs.Length);
Util.EasingFunction.Function func = Util.EasingFunction.GetEasingFunction(CurrentCameraManScaleArgs.Ease);
float newPos = func(0f, 1f, normalizedBeat);
Vector3 diff = CurrentCameraManScaleArgs.EndScale - CurrentCameraManScaleArgs.StartScale;
Vector3 diffPos = newPos * diff;
CameraMan.transform.localScale = CurrentCameraManScaleArgs.StartScale + diffPos;
if (normalizedBeat >= 1f)
{
CameraMan.transform.localScale = CurrentCameraManScaleArgs.EndScale;
CameraManScaling = false;
}
}
// boppers
if (Walkers.Count > 0)
{
Walkers.RemoveAll(w => w.Walker == null || w.Walker.gameObject == null);
foreach (WalkerArgs args in Walkers)
{
float normalizedBeat = conductor.GetPositionFromBeat(args.StartTime, args.Length);
args.Walker.DoNormalizedAnimation(args.AnimName, normalizedBeat, animLayer: 1);
if (normalizedBeat >= 1f)
Destroy(args.Walker.gameObject);
}
}
// car animations (deprecated)
/*if (QueuedCars.Count > 0)
{
var beat = conductor.songPositionInBeats;
if (beat >= 0)
{
QueuedCars.RemoveAll(e => e.Beat < beat - 5); // could probably be smaller. 5 just to be safe
foreach (SpawnCarArgs args in QueuedCars.Where(e => e.Beat <= beat))
{
SpawnCar(args);
}
QueuedCars.RemoveAll(e => e.Beat <= beat);
}
}*/
if (!IsShowingPhotos)
Instance.Overlay.SetActive(ShowOverlay);
Billboards.SetActive(ShowBillboard);
Instance.CameraMan.gameObject.SetActive(ShowCameraMan);
StickyLayer.Sticky = OverlayFollowCamera;
}
private void OnDestroy()
{
foreach (var evt in scheduledInputs)
{
evt.Disable();
}
//if (PhotoList.Count > 0) PhotoList.Clear();
}
// MINIGAME METHODS
public override void OnBeatPulse(double beat)
{
if (BeatIsInBopRegion(beat))
{
if (DoAutoBop && !IsShowingPhotos)
Bop();
if (DoAutoCrosshairBlink)
CrosshairBlink();
}
}
public override void OnPlay(double beat)
{
if (PhotoList.Count > 0) PhotoList.Clear();
OnGameSwitch(beat);
}
public override void OnGameSwitch(double beat)
{
Instance.Overlay.SetActive(ShowOverlay);
Instance.CameraMan.gameObject.SetActive(ShowCameraMan);
Instance.StickyLayer.Sticky = OverlayFollowCamera;
// calculation
CarbageCollection();
CalculateAutoShowPhotos();
CalculateCarSpawns();
PreRandomizeWalkers();
// setting local variables
RiqEntity e = GetLastEntityOfType(beat, "bop");
if (e is not null)
{
DoAutoBop = e["autoBop"];
DoAutoCrosshairBlink = e["autoBlink"];
}
e = GetLastEntityOfType(beat, "toggleOverlay");
if (e is not null)
{
ShowOverlay = e["showOverlay"];
ShowCameraMan = e["showCameraMan"];
OverlayFollowCamera = e["followCamera"];
}
e = GetLastEntityOfType(beat, "spawnCrowd");
if (e is not null)
{
ToggleCrowd(e.beat, e["crowd"], e["customCrowd"], e["crowdFarLeft"], e["crowdLeft"], e["crowdRight"], e["crowdFarRight"], e["billboard"]);
}
// walkers
List<RiqEntity> eList = GetCurrentlyActiveEntities(beat, "spawnPerson");
if (eList.Count > 0)
{
foreach (RiqEntity entity in eList)
{
SummonWalker(entity);
}
}
// bop entities
eList = GetCurrentlyActiveEntities(beat, "bop");
if (eList.Count > 0)
{
foreach (RiqEntity entity in eList)
{
SetBopping(entity.beat, entity.length, entity["bop"], entity["autoBop"], entity["blink"], entity["autoBlink"], beat);
}
}
// Intro
e = GetLastEntityOfType(beat, "introSign");
if (e is not null)
{
DoIntroSign(e.beat, e.length, e["enter"], e["ease"]);
}
e = GetLastEntityOfType(beat, "introLights");
if (e is not null)
{
IntroLightsAnim(e.beat, e.length, e["lightsOn"]);
}
// Camera Man Movement
foreach (RiqEntity entity in EventCaller.GetAllInGameManagerList("freezeFrame", new string[] { "neoMoveCameraMan", "moveCameraMan", "rotateCameraMan", "scaleCameraMan" }).OrderBy(en => en.beat).Where(en => en.beat <= beat))
{
switch (entity.datamodel)
{
case "freezeFrame/moveCameraMan":
SetMoveCameraMan_OLD(entity.beat, entity.length, entity["startPosX"], entity["startPosY"], entity["endPosX"], entity["endPosY"], entity["ease"]);
break;
case "freezeFrame/rotateCameraMan":
FreezeFrame.SetRotateCameraMan_OLD(entity.beat, entity.length, entity["startRot"], entity["endRot"], entity["ease"]);
break;
case "freezeFrame/scaleCameraMan":
SetScaleCameraMan_OLD(entity.beat, entity.length, entity["startSizeX"], entity["startSizeY"], entity["endSizeX"], entity["endSizeY"], entity["ease"]);
break;
case "freezeFrame/neoMoveCameraMan":
default:
SetMoveCameraMan(entity);
break;
}
}
// Deprecated:
/*e = GetLastEntityOfType(beat, "moveCameraMan");
if (e is not null)
{
SetMoveCameraMan_OLD(e.beat, e.length, e["startPosX"], e["startPosY"], e["endPosX"], e["endPosY"], e["ease"]);
}
e = GetLastEntityOfType(beat, "rotateCameraMan");
if (e is not null)
{
SetRotateCameraMan_OLD(e.beat, e.length, e["startRot"], e["endRot"], e["ease"]);
}
e = GetLastEntityOfType(beat, "scaleCameraMan");
if (e is not null)
{
SetScaleCameraMan_OLD(e.beat, e.length, e["startSizeX"], e["startSizeY"], e["endSizeX"], e["endSizeY"], e["ease"]);
}*/
// cues
eList = GetCurrentlyActiveEntities(beat, new string[] { "slowCar", "fastCar" });
if (eList.Count > 0)
{
foreach (RiqEntity entity in eList)
{
if (beat > entity.beat + 2 || beat <= entity.beat)
continue;
if (entity.datamodel == "freezeFrame/slowCar")
{
SlowCarCue(entity.beat, entity["variant"], true, entity["clear"]);
}
if (entity.datamodel == "freezeFrame/fastCar")
{
FastCarCue(entity.beat, entity["variant"], true, entity["clear"]);
}
}
}
}
// CUE FUNCTIONS
public static void SetBopping(double beat, float length, bool bop, bool autoBop, bool blink, bool autoBlink, double currentBeat = -1)
{
if (Instance == null) return;
Instance.DoAutoBop = autoBop;
Instance.DoAutoCrosshairBlink = autoBlink;
if (bop || blink)
{
List<BeatAction.Action> actions = new List<BeatAction.Action>();
for (int i = 0; i < length; i++) {
if (beat + i < currentBeat)
continue;
if (bop)
actions.Add(new(beat + i, delegate { Instance.Bop(); }));
if (blink)
actions.Add(new(beat + i, delegate { Instance.CrosshairBlink(); }));
}
BeatAction.New(Instance, actions);
}
}
public static void SlowCarCue(double beat, int photoType, bool mute = false, bool clear = false)
{
if (!mute) SlowCarSFX();
if (Instance == null) return;
Instance.EventArgs.Add(
Instance.ScheduleInput(beat, 2f, InputAction_BasicPress, Instance.PhotoSuccess, Instance.PhotoMiss, Instance.PhotoEmpty),
new PhotoArgs(CarType.SlowCar, (PhotoType)photoType, 0f, clear)
);
}
public static void SlowCarSFX()
{
SoundByte.PlayOneShotGame("freezeFrame/slowCarFar", forcePlay: true);
}
public static void FastCarCue(double beat, int photoType, bool mute = false, bool clear = false)
{
if (!mute) FastCarSFX();
if (Instance == null) return;
Instance.EventArgs.Add(
Instance.ScheduleInput(beat, 2f, InputAction_BasicPress, Instance.PhotoSuccess, Instance.PhotoMiss, Instance.PhotoEmpty),
new PhotoArgs(CarType.FastCar, (PhotoType)photoType, 0f, clear)
);
SoundByte.PlayOneShotGame("freezeFrame/fastCarNear", beat + 2);
}
public static void FastCarSFX()
{
SoundByte.PlayOneShotGame("freezeFrame/fastCarFar", forcePlay: true);
}
public static void ShowPhotos(double beat, float length, int gradeTypeI, bool audience, bool clearCache)
{
if (Instance == null) return;
GradeType gradeType = (GradeType)gradeTypeI;
if (PhotoList.Count <= 0)
return;
SoundByte.PlayOneShotGame("freezeFrame/pictureShow");
// 2 = Hi
// 1 = OK
// 0 = Ng
int goodScore = 2;
foreach (PhotoArgs photo in PhotoList)
{
if (photo.State <= -2)
{
goodScore = 0;
break;
}
if (goodScore == 2 && photo.State != 0)
goodScore = 1;
}
if (gradeType == GradeType.Symbols)
{
switch (goodScore)
{
case 0:
Instance.Results.DoScaledAnimationAsync("Batsu", 0.5f);
break;
case 1:
Instance.Results.DoScaledAnimationAsync("Sankaku", 0.5f);
break;
case 2:
default:
Instance.Results.DoScaledAnimationAsync("Maru", 0.5f);
break;
}
}
if (gradeType == GradeType.Thumbs)
{
switch (goodScore)
{
case 0:
Instance.Results.DoScaledAnimationAsync("ThumbsDown", 0.5f);
break;
case 1:
Instance.Results.DoScaledAnimationAsync("ThumbsSide", 0.5f);
break;
case 2:
default:
Instance.Results.DoScaledAnimationAsync("ThumbsUp", 0.5f);
break;
}
}
for (int i = 0; i < PhotoList.Count && i < Instance.Photographs.Length; i++)
{
Instance.Photographs[i].ShowPhoto(PhotoList[i]);
}
if (clearCache)
PhotoList.Clear();
Instance.Overlay.SetActive(false);
Instance.DimRect.SetActive(true);
Instance.IsShowingPhotos = true;
// reactions sounds
switch (goodScore)
{
case 2:
Instance.CameraMan.DoScaledAnimationAsync("Happy", 0.5f);
SoundByte.PlayOneShotGame("freezeFrame/result_Hi");
if (audience)
break;
break;
case 1:
Instance.CameraMan.DoScaledAnimationAsync("Oops", 0.5f);
SoundByte.PlayOneShotGame("freezeFrame/result_Ok");
if (audience)
SoundByte.PlayOneShot("applause");
break;
case 0:
Instance.CameraMan.DoScaledAnimationAsync("Cry", 0.5f);
SoundByte.PlayOneShotGame("freezeFrame/result_Ng");
if (audience)
break;
break;
default:
break;
}
BeatAction.New(Instance, new List<BeatAction.Action>()
{
new BeatAction.Action(beat + length, delegate { Instance.HidePhotos(beat + length); })
});
}
public static void ClearPhotos()
{
PhotoList?.Clear();
}
public static void SummonWalker(RiqEntity e)
{
if (Instance == null) return;
double beat = e.beat;
double length = e.length;
PersonType walkerType = (PersonType)e["personType"];
PersonDirection direction = (PersonDirection)e["direction"];
int layer = e["layer"];
GameObject walker = Instantiate(Instance.WalkerPrefab, Instance.WalkerSpawn.transform);
Animator animator = walker.GetComponent<Animator>();
walker.GetComponent<SortingGroup>().sortingOrder = layer;
switch (walkerType)
{
case PersonType.Girlfriend:
animator.DoScaledAnimationAsync("Girlfriend", animLayer: 2);
break;
case PersonType.Dude2:
animator.DoScaledAnimationAsync("Dude2", animLayer: 2);
break;
case PersonType.Dude1:
default:
animator.DoScaledAnimationAsync("Dude1", animLayer: 2);
break;
}
if (direction == PersonDirection.Random)
{
if (WalkerDirections.ContainsKey(e))
direction = WalkerDirections[e];
else
direction = PersonDirection.Right;
}
if (direction == PersonDirection.Left)
Instance.Walkers.Add(new WalkerArgs(animator, beat, length, "EnterLeft"));
else
Instance.Walkers.Add(new WalkerArgs(animator, beat, length, "EnterRight"));
double nextBeat = Math.Ceiling(beat);
List<BeatAction.Action> actions = new();
for (double i = nextBeat; i < beat + length; i += 1)
{
actions.Add(new(i, delegate { animator.DoScaledAnimationAsync("Bop", timeScale: 0.5f, animLayer: 0); }));
}
BeatAction.New(Instance, actions);
}
public static void ToggleCrowd(double beat, bool showCrowd, bool customCrowd, int crowdFarLeft, int crowdLeft, int crowdRight, int crowdFarRight, bool billboard)
{
if (Instance == null) return;
Instance.ShowCrowd = showCrowd;
if (Instance.ShowCrowd)
Instance.Crowd.DoScaledAnimationAsync("Show", 0.5f);
else
Instance.Crowd.DoScaledAnimationAsync("Hide", 0.5f);
if (customCrowd)
{
Instance.CrowdFarLeft.sprite = Instance.GetCrowdSprite(crowdFarLeft);
Instance.CrowdLeft.sprite = Instance.GetCrowdSprite(crowdLeft);
Instance.CrowdRight.sprite = Instance.GetCrowdSprite(crowdRight);
Instance.CrowdFarRight.sprite = Instance.GetCrowdSprite(crowdFarRight);
}
else
{
Instance.CrowdFarLeft.sprite = Instance.GetCrowdSprite(2);
Instance.CrowdLeft.sprite = Instance.GetCrowdSprite(1);
Instance.CrowdRight.sprite = Instance.GetCrowdSprite(0);
Instance.CrowdFarRight.sprite = Instance.GetCrowdSprite(2);
}
Instance.ShowBillboard = billboard;
}
public static void DoIntroSign(double beat, double length, bool enter, int easeIndex)
{
Util.EasingFunction.Ease ease = (Util.EasingFunction.Ease)easeIndex;
Instance.SignIsMoving = true;
if (enter)
Instance.CurrentSignArgs = new("Enter", beat, length, ease);
else
Instance.CurrentSignArgs = new("Exit", beat, length, ease);
}
public static void IntroLightsAnim(double beat, double length, bool lightsOn)
{
if (!lightsOn)
{
Instance.IntroSign.DoScaledAnimationAsync("LightsOff", timeScale: 0.5f, animLayer: 1);
return;
}
Instance.IntroSign.DoScaledAnimationFromBeatAsync("Light01", startBeat: beat, timeScale: 0.5f, animLayer: 1);
BeatAction.New(Instance, new List<BeatAction.Action>
{
new(beat + length, delegate { Instance.IntroSign.DoScaledAnimationFromBeatAsync("Light02", startBeat: beat + length, timeScale: 0.5f, animLayer: 1); }),
new(beat + (length * 2), delegate { Instance.IntroSign.DoScaledAnimationFromBeatAsync("Light03", startBeat: beat + (length * 2), timeScale: 0.5f, animLayer: 1); }),
}
);
}
public static void IntroLightsSound(double beat, double length, bool lightsOn)
{
if (!lightsOn)
return;
MultiSound.Play(new MultiSound.Sound[] {
new MultiSound.Sound("freezeFrame/beginningSignal1", beat),
new MultiSound.Sound("freezeFrame/beginningSignal1", beat + length),
new MultiSound.Sound("freezeFrame/beginningSignal2", beat + (length * 2)),
}, forcePlay: true);
}
public static void ToggleOverlay(double beat, bool showOverlay, bool showCameraMan, bool followCamera)
{
Instance.ShowOverlay = showOverlay;
Instance.ShowCameraMan = showCameraMan;
Instance.OverlayFollowCamera = followCamera;
}
public static void SetMoveCameraMan(RiqEntity e)
{
if ((bool)e["doMove"])
{
SetMoveCameraMan_OLD(e.beat, e.length, e["startMoveX"], e["startMoveY"], e["endMoveX"], e["endMoveY"], e["ease"]);
}
if ((bool)e["doRotate"])
{
SetRotateCameraMan_OLD(e.beat, e.length, e["startRotDegrees"], e["endRotDegrees"], e["ease"]);
}
if ((bool)e["doScale"])
{
SetScaleCameraMan_OLD(e.beat, e.length, e["startScaleX"], e["startScaleY"], e["endScaleX"], e["endScaleY"], e["ease"]);
}
}
public static void SetMoveCameraMan_OLD(double beat, double length, float startX, float startY, float endX, float endY, int easeIndex)
{
Instance.CurrentCameraManMoveArgs = new(
beat,
length,
new Vector3(startX, startY, 0) + CameraManStartPos,
new Vector3(endX, endY, 0) + CameraManStartPos,
(Util.EasingFunction.Ease)easeIndex
);
Instance.CameraManMoving = true;
}
public static void SetRotateCameraMan_OLD(double beat, double length, float startRot, float endRot, int easeIndex)
{
Instance.CurrentCameraManRotateArgs = new(
beat,
length,
startRot,
endRot,
(Util.EasingFunction.Ease)easeIndex
);
Instance.CameraManRotating = true;
}
public static void SetScaleCameraMan_OLD(double beat, double length, float startX, float startY, float endX, float endY, int easeIndex)
{
Instance.CurrentCameraManScaleArgs = new(
beat,
length,
new Vector3(startX, startY, 1),
new Vector3(endX, endY, 1),
(Util.EasingFunction.Ease)easeIndex
);
Instance.CameraManScaling = true;
}
// PRE-FUNCTIONS
public void SpawnCar(double beat, bool near, string animName, float length)
{
if (near)
{
if (NearCarPrefab == null || NearCarSpawn == null)
{
Debug.LogError($"Failed to spawn car at beat {beat - 1}.");
return;
}
Instantiate(NearCarPrefab, NearCarSpawn).GetComponent<Car>().Setup(beat, animName, length);;
}
else
{
if (FarCarPrefab == null || FarCarSpawn == null)
{
Debug.LogError($"Failed to spawn car at beat {beat - 1}.");
return;
}
Instantiate(FarCarPrefab, FarCarSpawn).GetComponent<Car>().Setup(beat, animName, length);;
}
}
/*public void SpawnCar(SpawnCarArgs args)
{
if (args.Near)
{
if (NearCarSpawn == null)
{
UnityEngine.Debug.LogError($"Failed to spawn a car at beat {args.Beat}.");
return;
}
Animator car = Instantiate(NearCarPrefab, NearCarSpawn).GetComponent<Animator>();
car.DoScaledAnimationFromBeatAsync("Idle", startBeat: 0f, timeScale: 0.5f, animLayer: 0);
if (args.Fast)
car.DoScaledAnimationFromBeatAsync("FastCarGo", startBeat: args.Beat, timeScale: 2.666666666666667f, animLayer: 1);
else
car.DoScaledAnimationFromBeatAsync("SlowCarGo", startBeat: args.Beat, timeScale: 1.5f, animLayer: 1);
//BeatAction.New(Instance, new List<BeatAction.Action>() { new BeatAction.Action(args.Beat + 3, delegate { Destroy(car); }) });
}
else
{
if (FarCarSpawn == null)
{
UnityEngine.Debug.LogError($"Failed to spawn a car at beat {args.Beat}.");
return;
}
Animator car = Instantiate(FarCarPrefab, FarCarSpawn).GetComponent<Animator>();
car.DoScaledAnimationFromBeatAsync("Idle", startBeat: 0f, timeScale: 0.5f, animLayer: 0);
if (args.Fast)
car.DoScaledAnimationFromBeatAsync("FastCarGo", startBeat: args.Beat, timeScale: 0.5f, animLayer: 1);
else
car.DoScaledAnimationFromBeatAsync("SlowCarGo", startBeat: args.Beat, timeScale: 0.16666666666666666666666666666667f, animLayer: 1);
//BeatAction.New(Instance, new List<BeatAction.Action>() { new BeatAction.Action(args.Beat + 8, delegate { Destroy(car); }) });
}
}*/
/*public void SpawnSlowCarNear(double beat)
{
if (NearCarSpawn == null) return;
Animator nearCar = Instantiate(NearCarPrefab, NearCarSpawn).GetComponent<Animator>();
nearCar.DoScaledAnimationFromBeatAsync("Idle", startBeat: 0f, timeScale: 0.5f, animLayer: 0);
double startBeat = beat + 2 - 0.16666666666666666666666666666667;
BeatAction.New(Instance, new List<BeatAction.Action>()
{
new BeatAction.Action(startBeat, delegate { nearCar.DoScaledAnimationFromBeatAsync("SlowCarGo", startBeat: startBeat, timeScale: 1.5f, animLayer: 1); }),
new BeatAction.Action(beat + 3, delegate { Destroy(nearCar); } )
});
}
public void SpawnSlowCarFar(double beat)
{
//if (beat < 0) return;
if (FarCarSpawn == null) return;
Animator farCar = Instantiate(FarCarPrefab, FarCarSpawn).GetComponent<Animator>();
farCar.DoScaledAnimationFromBeatAsync("Idle", startBeat: 0f, timeScale: 0.5f, animLayer: 0);
farCar.DoScaledAnimationFromBeatAsync("SlowCarGo", startBeat: beat, timeScale: 0.16666666666666666666666666666667f, animLayer: 1);
BeatAction.New(Instance, new List<BeatAction.Action>()
{
new BeatAction.Action(beat + 7, delegate { Destroy(farCar); } )
});
}
public void SpawnFastCar(double beat)
{
if (FarCarSpawn != null)
{
UnityEngine.Debug.Log("farcar");
Animator farCar = Instantiate(FarCarPrefab, FarCarSpawn).GetComponent<Animator>();
farCar.DoScaledAnimationFromBeatAsync("Idle", startBeat: 0f, timeScale: 0.5f, animLayer: 0);
BeatAction.New(Instance, new List<BeatAction.Action>()
{
new BeatAction.Action(beat - 0.5, delegate { if (beat >= 0.5) farCar.DoScaledAnimationFromBeatAsync("FastCarGo", startBeat: beat - 0.5, timeScale: 0.5f, animLayer: 1); }),
new BeatAction.Action(beat + 3, delegate { Destroy(farCar); } )
});
}
if (NearCarSpawn != null)
{
UnityEngine.Debug.Log("fartcar");
Animator nearCar = Instantiate(NearCarPrefab, NearCarSpawn).GetComponent<Animator>();
nearCar.DoScaledAnimationFromBeatAsync("Idle", startBeat: 0f, timeScale: 0.5f, animLayer: 0);
BeatAction.New(Instance, new List<BeatAction.Action>()
{
new BeatAction.Action(beat + 2 - 0.09375, delegate { nearCar.DoScaledAnimationFromBeatAsync("FastCarGo", startBeat: beat - 0.09375, timeScale: 2.666666666666667f, animLayer: 1); }),
new BeatAction.Action(beat + 3, delegate { Destroy(nearCar); } )
});
}
}*/
// INPUT RESULTS
public void PhotoSuccess(PlayerActionEvent caller, float state)
{
PhotoArgs args;
bool hasArgs = EventArgs.TryGetValue(caller, out args);
EventArgs.Remove(caller);
// passing the args for the photos
if (hasArgs)
{
if (state >= 1f)
{
args.State = 1;
}
else if (state <= -1f)
{
args.State = -1;
}
else
{
args.State = 0;
}
PushPhoto(args);
}
CameraFlash();
}
public void PhotoMiss(PlayerActionEvent caller)
{
PhotoArgs args;
bool hasArgs = EventArgs.TryGetValue(caller, out args);
EventArgs.Remove(caller);
if (hasArgs)
{
args.State = -2;
PushPhoto(args);
}
}
public void PhotoEmpty(PlayerActionEvent caller)
{
}
// GENERAL METHODS
public void Bop()
{
CameraMan.DoScaledAnimationAsync("Bop", 0.5f);
}
public void CrosshairBlink()
{
Crosshair.SetActive(!Crosshair.activeSelf);
}
public void CameraFlash()
{
Shutter.DoScaledAnimationAsync("Shut", 0.5f);
CameraMan.DoScaledAnimationAsync("Flash", 0.5f);
SoundByte.PlayOneShotGame("freezeFrame/shutter"/*, pitch: (float)new System.Random().NextDouble() + 0.5f*/);
}
public void PushPhoto(PhotoArgs args)
{
if (args.ClearPhotos)
ClearPhotos();
if (args.PhotoType == PhotoType.Random)
{
if (UnityEngine.Random.Range(0, 8) >= 7)
{
switch (UnityEngine.Random.Range(0, 3))
{
case 0:
args.PhotoType = PhotoType.Ninja;
break;
case 1:
args.PhotoType = PhotoType.Ghost;
break;
case 2:
args.PhotoType = PhotoType.Rats;
break;
default:
args.PhotoType = PhotoType.Default;
break;
}
}
else
args.PhotoType = PhotoType.Default;
}
PhotoList.Add(args);
}
public void HidePhotos(double beat)
{
Overlay.SetActive(ShowOverlay);
DimRect.SetActive(false);
foreach (Photograph photo in Photographs)
photo.HideAll();
Results.DoScaledAnimationAsync("None", 0.5f);
IsShowingPhotos = false;
if (beat % 1 == 0 && DoAutoBop)
CameraMan.DoScaledAnimationAsync("Bop", 0.5f);
else
CameraMan.DoScaledAnimationAsync("Idle", 0.5f);
}
public Sprite GetCrowdSprite(int i)
{
if (CrowdSprites.Length <= 0)
{
Debug.LogError("Error from Freeze Frame: CrowdSprites was empty.");
return null;
}
else
{
// just looping back thru the array jic there's an invalid index
return CrowdSprites[i % CrowdSprites.Length];
}
}
public void CalculateAutoShowPhotos()
{
List<RiqEntity> allCars = EventCaller.GetAllInGameManagerList("freezeFrame", new string[] { "slowCar", "fastCar" });
List<BeatAction.Action> actions = new();
List<double> beats = new(); // so you don't double up
foreach (RiqEntity entity in allCars.Where(car => (bool)car["autoShowPhotos"]))
{
double showBeat = 3f;
if (allCars.Any(car => car.beat == entity.beat - 1f))
showBeat = 3.5f;
if (entity.datamodel == "freezeFrame/fastCar")
showBeat = 4f;
if (allCars.Any(car => car.beat == entity.beat - 2f))
showBeat = 4.5f;
showBeat = entity.beat + showBeat;
if (!allCars.Any(car => car.beat > entity.beat && car.beat <= showBeat))
{
if (!beats.Any(b => showBeat >= b && showBeat < b + 2))
{
beats.Add(showBeat);
actions.Add(new BeatAction.Action(showBeat, delegate { ShowPhotos(showBeat, 2, (int)entity["gradeType"], (bool)entity["audience"], false); }));
}
}
}
if (actions.Count > 0)
{
BeatAction.New(Instance, actions);
}
}
public void CalculateCarSpawns()
{
List<RiqEntity> fastCars = EventCaller.GetAllInGameManagerList("freezeFrame", new string[] { "fastCar" });
foreach (RiqEntity e in fastCars)
{
BeatAction.New(this, new List<BeatAction.Action>()
{
new BeatAction.Action(e.beat - 0.5, delegate { SpawnCar(e.beat - 0.5, false, "FastCarGo", 1); }),
new BeatAction.Action(e.beat + 2 - 0.09375, delegate { SpawnCar(e.beat + 2 - 0.09375, true, "FastCarGo", 3f / 16f); }),
});
}
List<RiqEntity> slowCars = EventCaller.GetAllInGameManagerList("freezeFrame", new string[] { "slowCar" });
foreach (RiqEntity e in slowCars)
{
BeatAction.New(this, new List<BeatAction.Action>()
{
new BeatAction.Action(e.beat + 2 - 0.16666666666666666666666666666667, delegate { SpawnCar(e.beat + 2 - 0.16666666666666666666666666666667, true, "SlowCarGo", 1f / 3f); })
});
}
List<List<double>> clusters = new();
while (slowCars.Count > 0)
{
double minBeat = slowCars[0].beat;
double maxBeat = minBeat + 2;
clusters.Add(slowCars.Where(car => car.beat >= minBeat && car.beat < maxBeat).Select(car => car.beat).ToList());
slowCars.RemoveAll(car => car.beat >= minBeat && car.beat < maxBeat);
}
foreach (List<double> cluster in clusters)
{
double midBeat = cluster.Min() + ((cluster.Max() - cluster.Min()) / 2);
foreach (double beat in cluster)
{
double diff = midBeat - beat;
double modifiedBeat = midBeat + (diff / 4);
BeatAction.New(this, new List<BeatAction.Action>()
{
new BeatAction.Action(modifiedBeat - 4, delegate { SpawnCar(modifiedBeat - 4, false, "SlowCarGo", 3f); })
});
}
}
}
public void PreRandomizeWalkers()
{
IEnumerable<RiqEntity> walkers = EventCaller.GetAllInGameManagerList("freezeFrame", new string[] { "spawnPerson" }).Where(e => (PersonDirection)e["direction"] == PersonDirection.Random);
foreach (RiqEntity e in walkers)
{
if (!WalkerDirections.ContainsKey(e))
{
float rand = UnityEngine.Random.Range(0.0f, 1.0f);
if (rand >= 0.5)
{
WalkerDirections.Add(e, PersonDirection.Left);
}
else
{
WalkerDirections.Add(e, PersonDirection.Right);
}
}
}
List<RiqEntity> keysToRemove = new();
foreach (RiqEntity key in WalkerDirections.Keys)
{
if (!walkers.Contains(key))
keysToRemove.Add(key);
}
foreach (RiqEntity key in keysToRemove)
{
WalkerDirections.Remove(key);
}
}
public void CarbageCollection()
{
foreach (Transform child in FarCarSpawn)
{
Destroy(child.gameObject);
}
foreach (Transform child in NearCarSpawn)
{
Destroy(child.gameObject);
}
foreach (Transform child in WalkerSpawn)
{
Destroy(child.gameObject);
}
}
public RiqEntity GetLastEntityOfType(double beat, string datamodel)
{
foreach (RiqEntity e in EventCaller.GetAllInGameManagerList("freezeFrame", new string[] { datamodel }).OrderBy(e => -e.beat))
{
if (e.beat < beat)
return e;
}
return null;
}
public List<RiqEntity> GetCurrentlyActiveEntities(double beat, string datamodel)
{
return GetCurrentlyActiveEntities(beat, new string[] { datamodel });
}
public List<RiqEntity> GetCurrentlyActiveEntities(double beat, string[] datamodel)
{
List<RiqEntity> result = new();
foreach (RiqEntity e in EventCaller.GetAllInGameManagerList("freezeFrame", datamodel))
{
if (beat >= e.beat && beat <= e.beat + e.length)
result.Add(e);
if (e.beat > beat)
break;
}
return result;
}
// ENUMS
public enum CarType : int
{
SlowCar = 0,
FastCar = 1
}
public enum PhotoType : int
{
Random = 0,
Default = 1,
Ninja = 2,
Ghost = 3,
Rats = 4,
PeaceSign = 5,
GirlfriendRight = 6,
GirlfriendLeft = 7,
Dude1Right = 8,
Dude1Left = 9,
Dude2Right = 10,
Dude2Left = 11,
}
public enum PersonType : int
{
Dude1 = 0,
Dude2 = 1,
Girlfriend = 2
}
public enum PersonDirection : int
{
Random = 0,
Right = 1,
Left = 2
}
public enum GradeType : int
{
Symbols = 0,
Thumbs = 1,
None = 2
}
public enum CustomCrowdType : int
{
TealDancers = 0,
YellowDancers = 1,
PinkDancers = 2,
CyanDancers = 3,
}
// STRUCTS
public struct PhotoArgs
{
public CarType Car;
public PhotoType PhotoType;
public float State;
public bool ClearPhotos;
public PhotoArgs(CarType car, PhotoType photoType, float state, bool clear)
{
Car = car;
PhotoType = photoType;
State = state;
ClearPhotos = clear;
}
}
public struct WalkerArgs
{
public Animator Walker;
public double StartTime;
public double Length;
public string AnimName;
public WalkerArgs(Animator walker, double startTime, double length, string animName)
{
Walker = walker;
StartTime = startTime;
Length = length;
AnimName = animName;
}
}
public struct SignMoveArgs
{
public string AnimName;
public double StartTime;
public double Length;
public Util.EasingFunction.Ease Ease;
public SignMoveArgs(string animName, double startTime, double length, Util.EasingFunction.Ease ease)
{
AnimName = animName;
StartTime = startTime;
Length = length;
Ease = ease;
}
}
public struct CameraManMoveArgs
{
public double StartBeat;
public double Length;
public Vector3 StartPos;
public Vector3 EndPos;
public Util.EasingFunction.Ease Ease;
public CameraManMoveArgs(double startBeat, double length, Vector3 startPos, Vector3 endPos, Util.EasingFunction.Ease ease)
{
StartBeat = startBeat;
Length = length;
StartPos = startPos;
EndPos = endPos;
Ease = ease;
}
}
public struct CameraManRotateArgs
{
public double StartBeat;
public double Length;
public float StartRot;
public float EndRot;
public Util.EasingFunction.Ease Ease;
public CameraManRotateArgs(double startBeat, double length, float startRot, float endRot, Util.EasingFunction.Ease ease)
{
StartBeat = startBeat;
Length = length;
StartRot = startRot;
EndRot = endRot;
Ease = ease;
}
}
public struct CameraManScaleArgs
{
public double StartBeat;
public double Length;
public Vector3 StartScale;
public Vector3 EndScale;
public Util.EasingFunction.Ease Ease;
public CameraManScaleArgs(double startBeat, double length, Vector3 startScale, Vector3 endScale, Util.EasingFunction.Ease ease)
{
StartBeat = startBeat;
Length = length;
StartScale = startScale;
EndScale = endScale;
Ease = ease;
}
}
}
}
// This minigame ported by Yin. ☆