Monkey watch (#555)

* basic setup+inputs

this is gonna be so annoying to optimize
i am SO overwhelmed with the options that i have
object pooling, disabling the monkeys, literally just placing them manually... idk.

* custom monkeys + more setup stuff

it's all coming together .

* a few tweaks

committing to update the spritesheet

* hi ev

* player stuff

* player anims

* particles

* more of prefab

* upscale for this sheet

* prefab more

* oops

* anims n stuff

* assign the stuff, a little bit of rotation

* better sheet

* balloon sheet

* particles

* Revert "particles"

This reverts commit fe4d589731.

* Revert "Revert "particles""

This reverts commit ce117b280d.

* fixed the watch outline + pink monkey prefab variant

the bccad really is a life saver

* anims for monkeys

* convert to dictionary

yippee for dictionaries
* better camera
* click animations are all done
* custom monkeys are actually good now :)
* prefab adjustments

* oopps2

* tweaked particles

* inactive monkeys/custom monkeys + prefab fixes

they should work perfectly now, and the prefab should also need no further adjustment
hopefully...

* many animations

* click fixes

* the prefab needed One more adjustment

* oops again

hopefully the shadow is good now

* sheet adjustment

* all yellow anims done

* progress.

starting on the monkeys appear block rn, and then i'll work on the monkey spawning logic. honestly should be pretty easy (and i DON'T think this will be something i look back on as if i was crazy 😄 )

* open / close hole

* monkey spawning working better

* pink monkey anims

* clean slate

* gonna test something

* proper camera

* Camera movements done

* pink monkey sounds and a small bug fix

* clock arrow now moves

* gettin ready to spawn el monkeys

* monkeys should spawn now ig

* bug fixes

* oops

* monkeys appear added

* more fixes yahoo

* shadow tweak

* bug fixes

* zoom out beginnings

* hour more smooth

* smooth minute too

* it now zooms out lol

* middle monkey

* oopsie doopsie

* hot air balloon

* oops

* anim

* disappear fix

* ticks on input now

* prepare earlier

* tiny tweak

* oops again

* fixed the input bug

* holes

* middle monkey anims

* fixed layering

* zoom out tweaks and shadow movement

* camera tweak

* tweaks

* quad

* camera tweak

* quart

* inspcetor

* shadow correct

* Okay

* zoom out seperation

* instant

* balloon movement

* balloon fixed

* fixed particle

* icon

* fixed beataction parameters

* monkey watch camera refactor

run sourcegen

---------

Co-authored-by: AstrlJelly <bdlawson115@gmail.com>
Co-authored-by: ev <85412919+evdial@users.noreply.github.com>
Co-authored-by: minenice55 <star.elementa@gmail.com>
This commit is contained in:
Rapandrasmus
2024-01-29 04:04:19 +01:00
committed by GitHub
parent fc55712779
commit 1f03646118
212 changed files with 152193 additions and 0 deletions

View File

@ -0,0 +1,8 @@
fileFormatVersion: 2
guid: 9498cb5d0e7602445ae5927c270f232e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,133 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
namespace HeavenStudio.Games.Scripts_MonkeyWatch
{
public class BalloonHandler : MonoBehaviour
{
[Header("Components")]
[SerializeField] private Transform anchor;
[SerializeField] private Transform target;
[SerializeField] private Transform balloonTrans;
[SerializeField] private SpriteRenderer[] srs;
[SerializeField] private SpriteRenderer shadow;
[Header("Properties")]
[SerializeField] private float xOffset;
[SerializeField] private float yOffset;
private float shadowOpacity;
private float additionalXOffset;
private float additionalYOffset;
private double movementFirstBeat = -2;
private double movementLastBeat = -1;
private List<Movement> movements = new();
private int movementIndex = 0;
private struct Movement
{
public double beat;
public float length;
public float xStart;
public float xEnd;
public float yStart;
public float yEnd;
public float angleStart;
public float angleEnd;
public Util.EasingFunction.Ease ease;
}
private void Awake()
{
shadowOpacity = shadow.color.a;
}
public void Init(double beat)
{
var allEvents = EventCaller.GetAllInGameManagerList("monkeyWatch", new string[] { "balloon" });
allEvents.Sort((x, y) => x.beat.CompareTo(y.beat));
if (allEvents.Count > 0)
{
movementFirstBeat = allEvents[0].beat;
movementLastBeat = allEvents[^1].beat + allEvents[^1].length - 0.25;
}
foreach (var e in allEvents)
{
movements.Add(new Movement
{
beat = e.beat,
length = e.length,
xStart = e["xStart"],
xEnd = e["xEnd"],
yStart = e["yStart"],
yEnd = e["yEnd"],
angleStart = e["angleStart"],
angleEnd = e["angleEnd"],
ease = (Util.EasingFunction.Ease)e["ease"]
});
}
Update();
}
private void UpdateIndex()
{
movementIndex++;
if (movementIndex >= movements.Count) return;
if (Conductor.instance.songPositionInBeatsAsDouble > movements[movementIndex].beat + movements[movementIndex].length)
{
UpdateIndex();
}
}
private void Update()
{
var cond = Conductor.instance;
float normalizedFirst = Mathf.Clamp01(cond.GetPositionFromBeat(movementFirstBeat, 0.25f));
float normalizedLast = Mathf.Clamp01(cond.GetPositionFromBeat(movementLastBeat, 0.25f));
if (normalizedFirst >= 1)
{
foreach (var sr in srs)
{
sr.color = new Color(sr.color.r, sr.color.g, sr.color.b, 1 - normalizedLast);
}
shadow.color = new Color(shadow.color.r, shadow.color.g, shadow.color.b, Mathf.Lerp(shadowOpacity, 0, normalizedLast));
}
else
{
foreach (var sr in srs)
{
sr.color = new Color(sr.color.r, sr.color.g, sr.color.b, normalizedFirst);
}
shadow.color = new Color(shadow.color.r, shadow.color.g, shadow.color.b, Mathf.Lerp(0, shadowOpacity, normalizedFirst));
}
if (movements.Count > 0 && movementIndex < movements.Count)
{
if (cond.songPositionInBeatsAsDouble > movements[movementIndex].beat + movements[movementIndex].length)
{
UpdateIndex();
if (movementIndex >= movements.Count) return;
}
var e = movements[movementIndex];
float normalizedBeat = Mathf.Clamp01(cond.GetPositionFromBeat(e.beat, e.length));
var func = Util.EasingFunction.GetEasingFunction(e.ease);
float newX = func(e.xStart, e.xEnd, normalizedBeat);
float newY = func(e.yStart, e.yEnd, normalizedBeat);
float newAngle = func(e.angleStart, e.angleEnd, normalizedBeat);
additionalXOffset = newX;
additionalYOffset = newY;
anchor.localEulerAngles = new Vector3(0, 0, newAngle);
}
balloonTrans.position = new Vector3(target.position.x + xOffset + additionalXOffset, target.position.y + yOffset + additionalYOffset, target.position.z);
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: d4201255ef0e619469e1ef4af5aebcb8
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,115 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HeavenStudio.Util;
using Starpelly;
namespace HeavenStudio.Games.Scripts_MonkeyWatch
{
public class MonkeyClockArrow : MonoBehaviour
{
[Header("Components")]
[SerializeField] private Animator anim;
[SerializeField] private Transform anchorRotateTransform;
[SerializeField] private Animator playerMonkeyAnim;
[SerializeField] private ParticleSystem yellowClap;
[SerializeField] private ParticleSystem pinkClap;
[SerializeField] private Transform shadowTrans;
[SerializeField] private Transform camMoveTrans;
[Header("Properties")]
[SerializeField] private float shadowXRange = 2f;
[SerializeField] private float shadowYRange = 1f;
private MonkeyWatch game;
private void Awake()
{
game = MonkeyWatch.instance;
}
private void Update()
{
if (PlayerInput.GetIsAction(MonkeyWatch.InputAction_BasicPress) && !game.IsExpectingInputNow(MonkeyWatch.InputAction_BasicPress))
{
PlayerClap(false, false, true);
}
}
public void Move()
{
anchorRotateTransform.localEulerAngles = new Vector3(0, 0, anchorRotateTransform.localEulerAngles.z - 6);
anim.DoScaledAnimationAsync("Click", 0.4f);
MoveShadow();
}
private void MoveShadow()
{
float realAngle = anchorRotateTransform.localEulerAngles.z % 360 - 360;
realAngle *= -1;
float x;
float y;
if (realAngle <= 180)
{
float normalizedAngle = Mathp.Normalize(realAngle, 0, 180);
x = Mathf.Lerp(0, shadowXRange, normalizedAngle);
}
else
{
float normalizedAngle = Mathp.Normalize(realAngle, 180, 360);
x = Mathf.Lerp(shadowXRange, 0, normalizedAngle);
}
float realAngleY = anchorRotateTransform.localEulerAngles.z % 180 - 180;
realAngleY *= -1;
if (realAngleY <= 90)
{
float normalizedAngle = Mathp.Normalize(realAngleY, 0, 90);
y = Mathf.Lerp(0, shadowYRange, normalizedAngle);
}
else
{
float normalizedAngle = Mathp.Normalize(realAngleY, 90, 180);
y = Mathf.Lerp(shadowYRange, 0, normalizedAngle);
}
shadowTrans.localPosition = new Vector3(x, y);
}
public void MoveToAngle(float angle)
{
anchorRotateTransform.localEulerAngles = new Vector3(0, 0, -angle);
}
public bool PlayerIsClapAnim()
{
return !playerMonkeyAnim.IsAnimationNotPlaying();
}
public void PlayerClap(bool big, bool barely, bool whiff)
{
if (playerMonkeyAnim.IsPlayingAnimationNames("PlayerClapBarely") && whiff) return;
if (whiff)
{
game.middleMonkey.DoScaledAnimationAsync("MiddleMonkeyMiss", 0.4f);
SoundByte.PlayOneShot("miss");
}
if (barely || whiff)
{
playerMonkeyAnim.DoScaledAnimationAsync("PlayerClapBarely", 0.4f);
}
else
{
playerMonkeyAnim.DoScaledAnimationAsync(big ? "PlayerClapBig" : "PlayerClap", 0.4f);
ParticleSystem clapToSpawn = big ? pinkClap : yellowClap;
ParticleSystem spawnedClap = Instantiate(clapToSpawn, camMoveTrans, true);
spawnedClap.transform.eulerAngles = Vector3.zero;
spawnedClap.transform.GetChild(0).GetComponent<ParticleSystem>().SetAsyncScaling(0.4f);
spawnedClap.PlayScaledAsync(0.4f);
}
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: bb4fe7e944029da4b950e4ed4957b63b
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,712 @@
using HeavenStudio.Util;
using System;
using System.Collections.Generic;
using UnityEngine;
using Jukebox;
namespace HeavenStudio.Games.Loaders
{
using static Minigames;
public static class RvlMonkeyWatchLoader
{
public static Minigame AddGame(EventCaller eventCaller)
{
return new Minigame("monkeyWatch", "Monkey Watch", "f0338d", false, false, new List<GameAction>()
{
new GameAction("appear", "Monkeys Appear")
{
function = delegate
{
var e = eventCaller.currentEntity;
MonkeyWatch.instance.MonkeysAppear(e.beat, e.length, e["value"], e.beat);
},
defaultLength = 2f,
resizable = true,
parameters = new List<Param>()
{
new Param("value", new EntityTypes.Integer(1, 30, 4), "Repeat Amount")
}
},
new GameAction("clap", "Clapping")
{
preFunction = delegate
{
MonkeyWatch.PreStartClapping(eventCaller.currentEntity.beat);
},
preFunctionLength = 4f,
defaultLength = 2f,
parameters = new List<Param>()
{
new Param("min", new EntityTypes.Integer(0, 59, 0), "Set Starting Second", "A second is equivalent to one monkey.")
}
},
new GameAction("off", "Pink Monkeys")
{
preFunction = delegate
{
var e = eventCaller.currentEntity;
MonkeyWatch.PinkMonkeySound(e.beat, e.length, e["muteC"], e["muteE"]);
},
defaultLength = 2f,
parameters = new List<Param>()
{
new Param("muteC", false, "Mute Ooki"),
new Param("muteE", false, "Mute Eeks")
}
},
new GameAction("offStretch", "Pink Monkeys (Stretchable)")
{
preFunction = delegate
{
var e = eventCaller.currentEntity;
MonkeyWatch.PinkMonkeySound(e.beat, e.length, e["muteC"], e["muteE"]);
},
defaultLength = 2f,
resizable = true,
parameters = new List<Param>()
{
new Param("muteC", false, "Mute Ooki"),
new Param("muteE", false, "Mute Eeks")
}
},
new GameAction("offInterval", "Custom Pink Monkey Interval")
{
preFunction = delegate
{
var e = eventCaller.currentEntity;
MonkeyWatch.PinkMonkeySoundCustom(e.beat, e.length, e["muteC"], e["muteE"]);
},
defaultLength = 2f,
resizable = true,
parameters = new List<Param>()
{
new Param("muteC", false, "Mute Ooki"),
new Param("muteE", false, "Mute Eeks")
}
},
new GameAction("offCustom", "Custom Pink Monkey")
{
defaultLength = 0.5f
},
new GameAction("zoomOut", "Zoom Out")
{
function = delegate
{
var e = eventCaller.currentEntity;
MonkeyWatch.instance.ZoomOut(e.beat, e["timeMode"], e["hour"], e["minute"], e["instant"]);
},
defaultLength = 2f,
parameters = new List<Param>()
{
new Param("instant", false, "Instant"),
new Param("timeMode", MonkeyWatch.TimeMode.RealTime, "Time Mode", "Set the clock to system time or a certain time"),
new Param("hour", new EntityTypes.Integer(0, 12, 3), "Hour"),
new Param("minute", new EntityTypes.Integer(0, 59, 0), "Minute")
}
},
new GameAction("zoomIn", "Zoom In")
{
function = delegate
{
var e = eventCaller.currentEntity;
MonkeyWatch.instance.ZoomIn(e.beat, e["timeMode"], e["hour"], e["minute"], e["instant"]);
},
defaultLength = 2f,
parameters = new List<Param>()
{
new Param("instant", false, "Instant"),
new Param("timeMode", MonkeyWatch.TimeMode.RealTime, "Time Mode", "Set the clock to system time or a certain time"),
new Param("hour", new EntityTypes.Integer(0, 12, 3), "Hour"),
new Param("minute", new EntityTypes.Integer(0, 59, 0), "Minute")
}
},
new GameAction("balloon", "Balloon Movement")
{
resizable = true,
defaultLength = 4f,
parameters = new List<Param>()
{
new Param("angleStart", new EntityTypes.Float(-360, 360, 0), "Start Angle"),
new Param("angleEnd", new EntityTypes.Float(-360, 360, 0), "End Angle"),
new Param("yStart", new EntityTypes.Float(-200, 200, 0), "Y Start"),
new Param("yEnd", new EntityTypes.Float(-200, 200, 0), "Y End"),
new Param("xStart", new EntityTypes.Float(-200, 200, 0), "X Start"),
new Param("xEnd", new EntityTypes.Float(-200, 200, 0), "X End"),
new Param("ease", Util.EasingFunction.Ease.Linear, "Ease")
}
}
});
}
}
}
namespace HeavenStudio.Games
{
using Scripts_MonkeyWatch;
public class MonkeyWatch : Minigame
{
public enum TimeMode
{
RealTime,
SetTime
}
private const float degreePerMonkey = 6f;
public static MonkeyWatch instance;
[Header("Components")]
[SerializeField] private Transform cameraAnchor;
[SerializeField] private Transform cameraTransform;
[SerializeField] private Transform cameraMoveable;
public MonkeyClockArrow monkeyClockArrow;
[SerializeField] private WatchMonkeyHandler monkeyHandler;
[SerializeField] private WatchBackgroundHandler backgroundHandler;
[SerializeField] private BalloonHandler balloonHandler;
public Animator middleMonkey;
[Header("Properties")]
[SerializeField] private float fullZoomOut = 40f;
[SerializeField] private Util.EasingFunction.Ease zoomOutEase;
[SerializeField] private float zoomOutBeatLength = 2f;
[SerializeField] private float zoomInBeatLength = 2f;
[SerializeField] private Util.EasingFunction.Ease zoomInEase;
private float lastAngle = 0f;
private int cameraIndex = 0;
private float cameraWantAngle, cameraAngleDelay;
private float delayRate = 0.5f, targetDelayRate;
private void Awake()
{
instance = this;
funcOut = Util.EasingFunction.GetEasingFunction(zoomOutEase);
funcIn = Util.EasingFunction.GetEasingFunction(zoomInEase);
pinkMonkeys = EventCaller.GetAllInGameManagerList("monkeyWatch", new string[] { "off", "offStretch" });
pinkMonkeysCustom = EventCaller.GetAllInGameManagerList("monkeyWatch", new string[] { "offInterval" });
}
private void Start()
{
CameraUpdate();
}
private double lastReportedBeat = 0;
private void Update()
{
CameraUpdate();
}
public override void OnBeatPulse(double beat)
{
middleMonkey.DoScaledAnimationAsync("MiddleMonkeyBop", 0.4f);
}
public void PlayerMonkeyClap(bool big, bool barely)
{
monkeyClockArrow.PlayerClap(big, barely, false);
}
public void ZoomOut(double beat, int timeMode, int hours, int minutes, bool instant)
{
zoomOutStartBeat = beat - (instant ? zoomOutBeatLength : 0);
zoomIn = false;
backgroundHandler.SetFade(beat, instant ? 0 : 0.25f, true, (TimeMode)timeMode, hours, minutes);
CameraUpdate();
}
public void ZoomIn(double beat, int timeMode, int hours, int minutes, bool instant)
{
zoomOutStartBeat = beat - (instant ? zoomInBeatLength : 0);
zoomIn = true;
backgroundHandler.SetFade(beat, instant ? 0 : 0.25f, false, (TimeMode)timeMode, hours, minutes);
CameraUpdate();
}
public void PersistZoomOut(double beat)
{
var allZooms = EventCaller.GetAllInGameManagerList("monkeyWatch", new string[] { "zoomOut" }).FindAll(x => x.beat < beat && x.beat + x.length > beat);
foreach (var zoom in allZooms)
{
ZoomOut(zoom.beat, zoom["timeMode"], zoom["hour"], zoom["minute"], zoom["instant"]);
}
var allZoomsIn = EventCaller.GetAllInGameManagerList("monkeyWatch", new string[] { "zoomIn" }).FindAll(x => x.beat < beat && x.beat + x.length > beat);
foreach (var zoom in allZoomsIn)
{
ZoomIn(zoom.beat, zoom["timeMode"], zoom["hour"], zoom["minute"], zoom["instant"]);
}
}
private double persistBeat = 0;
private double theNextGameSwitchBeat = double.MaxValue;
private double clappingBeat = 0;
public override void OnGameSwitch(double beat)
{
balloonHandler.Init(beat);
persistBeat = beat;
GetCameraMovements(beat, false);
monkeyClockArrow.MoveToAngle(lastAngle);
monkeyHandler.Init((int)(lastAngle / degreePerMonkey));
if (wantClap >= beat && IsClapBeat(wantClap))
{
StartClapping(wantClap);
}
PersistAppear(beat);
PersistZoomOut(beat);
bool IsClapBeat(double clapBeat)
{
return EventCaller.GetAllInGameManagerList("monkeyWatch", new string[] { "clap" }).Find(x => x.beat == clapBeat) != null;
}
}
public override void OnPlay(double beat)
{
balloonHandler.Init(beat);
persistBeat = beat;
GetCameraMovements(beat, true);
monkeyClockArrow.MoveToAngle(lastAngle);
monkeyHandler.Init((int)(lastAngle / degreePerMonkey));
PersistAppear(beat);
PersistZoomOut(beat);
}
private void PersistAppear(double beat)
{
var allEvents = EventCaller.GetAllInGameManagerList("monkeyWatch", new string[] { "appear" }).FindAll(x => x.beat + x["value"] > beat && x.beat < beat);
foreach (var e in allEvents)
{
MonkeysAppear(e.beat, e.length, e["value"], beat);
}
}
public void MonkeysAppear(double beat, float length, int repeatAmount, double gameSwitchBeat)
{
List<BeatAction.Action> actions = new();
double lastBeat = clappingBeat;
int index = 0;
while (index < repeatAmount)
{
if (IsPinkMonkeyAtBeat(lastBeat, out float pinkLength))
{
for (int i = 0; i < pinkLength; i++)
{
if (index >= repeatAmount) break;
bool beforeGameSwitch = beat + (index * length) < gameSwitchBeat;
double realLastBeat = lastBeat;
actions.Add(new BeatAction.Action(beat + (index * length), delegate
{
monkeyHandler.SpawnMonkey(realLastBeat, true, beforeGameSwitch);
}));
index++;
lastBeat += 1;
}
}
else if (IsCustomPinkMonkeyAtBeat(lastBeat, out float pinkCustomLength))
{
var relevantPinks = FindCustomOffbeatMonkeysBetweenBeat(lastBeat, lastBeat + pinkCustomLength);
relevantPinks.Sort((x, y) => x.beat.CompareTo(y.beat));
for (int i = 0; i < relevantPinks.Count; i++)
{
if (index >= repeatAmount) break;
int jindex = i;
bool beforeGameSwitch = beat + (index * length) < gameSwitchBeat;
actions.Add(new BeatAction.Action(beat + (index * length), delegate
{
monkeyHandler.SpawnMonkey(relevantPinks[jindex].beat, true, beforeGameSwitch);
}));
index++;
}
lastBeat += pinkCustomLength;
}
else
{
double realLastBeat = lastBeat;
bool beforeGameSwitch = beat + (index * length) < gameSwitchBeat;
actions.Add(new BeatAction.Action(beat + (index * length), delegate
{
monkeyHandler.SpawnMonkey(realLastBeat, false, beforeGameSwitch);
}));
index++;
lastBeat += 2;
}
}
actions.Sort((x, y) => x.beat.CompareTo(y.beat));
BeatAction.New(instance, actions);
}
#region clapping
private bool clapRecursing = false;
private List<RiqEntity> pinkMonkeys = new();
private List<RiqEntity> pinkMonkeysCustom = new();
private bool IsPinkMonkeyAtBeat(double beat, out float length)
{
length = 2;
var e = pinkMonkeys.Find(x => x.beat == beat);
bool isNotNull = e != null;
if (isNotNull) length = e.length;
return isNotNull;
}
private bool IsCustomPinkMonkeyAtBeat(double beat, out float length)
{
length = 2;
var e = pinkMonkeysCustom.Find(x => x.beat == beat);
bool isNotNull = e != null;
if (isNotNull) length = e.length;
return isNotNull;
}
private static double wantClap = double.MinValue;
public static void PreStartClapping(double beat)
{
if (GameManager.instance.currentGame == "monkeyWatch")
{
instance.StartClapping(beat);
}
wantClap = beat;
}
private void StartClapping(double beat)
{
if (clapRecursing) return;
clapRecursing = true;
ClapRecursing(beat);
}
private void ClapRecursing(double beat)
{
if (beat >= theNextGameSwitchBeat) return;
if (IsPinkMonkeyAtBeat(beat, out float length1))
{
PinkClap(length1);
}
else if (IsCustomPinkMonkeyAtBeat(beat, out float length2))
{
PinkClapCustom(length2);
}
else
{
NormalClap();
}
void NormalClap()
{
BeatAction.New(instance, new List<BeatAction.Action>()
{
new BeatAction.Action(beat - 4, delegate
{
monkeyHandler.SpawnMonkey(beat, false, beat - 4 < persistBeat);
ClapRecursing(beat + 2);
cameraWantAngle += degreePerMonkey;
}),
new BeatAction.Action(beat - 1, delegate
{
monkeyHandler.GetMonkeyAtBeat(beat).Prepare(beat, beat + 1);
}),
});
}
void PinkClap(float length)
{
List<BeatAction.Action> actions = new List<BeatAction.Action>()
{
new BeatAction.Action(beat - 4, delegate
{
ClapRecursing(beat + length);
})
};
for (int i = 0; i < length; i++)
{
int index = i;
actions.AddRange(new List<BeatAction.Action>()
{
new BeatAction.Action(beat + i - 4, delegate
{
monkeyHandler.SpawnMonkey(beat + index, true, beat + index - 4 < persistBeat);
cameraWantAngle += degreePerMonkey;
}),
new BeatAction.Action(beat + i - 1, delegate
{
monkeyHandler.GetMonkeyAtBeat(beat + index).Prepare(beat + index, beat + index + 0.5);
}),
});
}
actions.Sort((x, y) => x.beat.CompareTo(y.beat));
BeatAction.New(instance, actions);
}
void PinkClapCustom(float length)
{
List<BeatAction.Action> actions = new List<BeatAction.Action>()
{
new BeatAction.Action(beat - 4, delegate
{
ClapRecursing(beat + length);
})
};
var relevantEvents = FindCustomOffbeatMonkeysBetweenBeat(beat, beat + length);
relevantEvents.Sort((x, y) => x.beat.CompareTo(y.beat));
for (int i = 0; i < relevantEvents.Count; i++)
{
var e = relevantEvents[i];
actions.AddRange(new List<BeatAction.Action>()
{
new BeatAction.Action(e.beat - 4, delegate
{
monkeyHandler.SpawnMonkey(e.beat, true, e.beat - 4 < persistBeat);
cameraWantAngle += degreePerMonkey;
}),
new BeatAction.Action(e.beat - 1.5, delegate
{
monkeyHandler.GetMonkeyAtBeat(e.beat).Prepare(e.beat - 0.5, e.beat);
}),
});
}
actions.Sort((x, y) => x.beat.CompareTo(y.beat));
BeatAction.New(instance, actions);
}
}
#endregion
#region Camera
private void GetCameraMovements(double beat, bool onPlay)
{
double lastGameSwitchBeat = beat;
if (onPlay)
{
var allEndsBeforeBeat = EventCaller.GetAllInGameManagerList("gameManager", new string[] { "switchGame" }).FindAll(x => x.beat <= beat);
if (allEndsBeforeBeat.Count > 0)
{
allEndsBeforeBeat.Sort((x, y) => x.beat.CompareTo(y.beat));
lastGameSwitchBeat = allEndsBeforeBeat[^1].beat;
}
else
{
lastGameSwitchBeat = 0f;
}
}
double nextGameSwitchBeat = double.MaxValue;
var allEnds = EventCaller.GetAllInGameManagerList("gameManager", new string[] { "switchGame", "end" }).FindAll(x => x.beat > lastGameSwitchBeat);
if (allEnds.Count > 0)
{
allEnds.Sort((x, y) => x.beat.CompareTo(y.beat));
nextGameSwitchBeat = allEnds[0].beat;
}
theNextGameSwitchBeat = nextGameSwitchBeat;
double startClappingBeat = 0;
float startAngle = 0;
bool overrideStartBeat = true;
var clappingEvents = EventCaller.GetAllInGameManagerList("monkeyWatch", new string[] { "clap" }).FindAll(x => x.beat >= lastGameSwitchBeat && x.beat < nextGameSwitchBeat);
if (clappingEvents.Count > 0)
{
clappingEvents.Sort((x, y) => x.beat.CompareTo(y.beat));
startClappingBeat = clappingEvents[0].beat;
startAngle = clappingEvents[0]["min"] * degreePerMonkey;
cameraWantAngle = startAngle;
cameraAngleDelay = startAngle;
overrideStartBeat = false;
}
lastAngle = startAngle;
clappingBeat = startClappingBeat;
var pinkClappingEvents = EventCaller.GetAllInGameManagerList("monkeyWatch", new string[] { "off", "offStretch", "offInterval" }).FindAll(x => x.beat >= lastGameSwitchBeat && x.beat < nextGameSwitchBeat);
if (pinkClappingEvents.Count > 0)
{
pinkClappingEvents.Sort((x, y) => x.beat.CompareTo(y.beat));
if (overrideStartBeat) startClappingBeat = pinkClappingEvents[0].beat;
clappingBeat = startClappingBeat;
var relevantPinkClappingEvents = pinkClappingEvents.FindAll(x => (x.beat - startClappingBeat) % 2 == 0);
relevantPinkClappingEvents.Sort((x, y) => x.beat.CompareTo(y.beat));
double lastClappingBeat = startClappingBeat;
float lastAngleToCheck = startAngle;
for (int i = 0; i < relevantPinkClappingEvents.Count; i++)
{
var e = relevantPinkClappingEvents[i];
if (e.beat < lastClappingBeat) continue;
float angleToAdd;
if (e.datamodel == "monkeyWatch/offInterval")
{
angleToAdd = FindCustomOffbeatMonkeysBetweenBeat(e.beat, e.beat + e.length).Count * degreePerMonkey;
}
else
{
angleToAdd = Mathf.Ceil(e.length) * degreePerMonkey;
}
if (e.beat - lastClappingBeat > 0)
{
lastAngleToCheck += (float)((e.beat - lastClappingBeat) / 2) * degreePerMonkey;
}
lastAngleToCheck += angleToAdd;
lastClappingBeat = e.beat + e.length;
}
startClappingBeat = lastClappingBeat;
startAngle = lastAngleToCheck;
}
}
private void UpdateCamera()
{
// lastAngle = cameraMovements[cameraIndex].degreeTo;
// cameraIndex++;
// if (cameraIndex + 1 < cameraMovements.Count && conductor.songPositionInBeats >= cameraMovements[cameraIndex].beat + cameraMovements[cameraIndex].length)
// {
// UpdateCamera();
// }
}
private double zoomOutStartBeat = -2;
private bool zoomIn = true;
private Util.EasingFunction.Function funcOut;
private Util.EasingFunction.Function funcIn;
private void CameraUpdate()
{
if (conductor.isPlaying && !conductor.isPaused)
{
float degreesBehind = cameraWantAngle - 2 * degreePerMonkey;
float degreesBehindFast = cameraWantAngle - 3f * degreePerMonkey;
targetDelayRate = Mathf.Max((cameraAngleDelay - degreesBehindFast) / (degreesBehindFast - cameraWantAngle), 0) + 0.5f;
delayRate = Mathf.Lerp(delayRate, targetDelayRate, Time.deltaTime * (1f / conductor.pitchedSecPerBeat) * 0.5f);
if (cameraAngleDelay < degreesBehind)
{
cameraAngleDelay += degreePerMonkey * Time.deltaTime * (1f / conductor.pitchedSecPerBeat) * delayRate;
cameraAngleDelay = Mathf.Min(cameraAngleDelay, cameraWantAngle);
}
cameraAnchor.localEulerAngles = new Vector3(0, 0, cameraAngleDelay);
}
float normalizedZoomBeat = conductor.GetPositionFromBeat(zoomOutStartBeat, zoomIn ? zoomInBeatLength : zoomOutBeatLength);
float newX = 0f;
float newY = 0f;
float newZ = 0f;
if (zoomIn)
{
newX = funcIn(0, cameraTransform.position.x, Mathf.Clamp01(normalizedZoomBeat));
newY = funcIn(0, cameraTransform.position.y, Mathf.Clamp01(normalizedZoomBeat));
newZ = funcIn(fullZoomOut, 0, Mathf.Clamp01(normalizedZoomBeat));
}
else
{
newX = funcOut(cameraTransform.position.x, 0, Mathf.Clamp01(normalizedZoomBeat));
newY = funcOut(cameraTransform.position.y, 0, Mathf.Clamp01(normalizedZoomBeat));
newZ = funcOut(0, fullZoomOut, Mathf.Clamp01(normalizedZoomBeat));
}
if (zoomIn && normalizedZoomBeat > 1f)
{
cameraMoveable.position = new Vector3(cameraTransform.position.x, cameraTransform.position.y * -1);
}
else
{
cameraMoveable.position = new Vector3(newX, -newY, newZ);
}
}
#endregion
public static List<RiqEntity> FindCustomOffbeatMonkeysBetweenBeat(double beat, double endBeat)
{
return EventCaller.GetAllInGameManagerList("monkeyWatch", new string[] { "offCustom" }).FindAll(x => x.beat >= beat && x.beat < endBeat);
}
#region pink monkey sounds
public static void PinkMonkeySound(double beat, float length, bool muteOoki, bool muteEek)
{
List<MultiSound.Sound> soundsToPlay = new();
if (!muteOoki)
{
soundsToPlay.AddRange(new List<MultiSound.Sound>()
{
new MultiSound.Sound("monkeyWatch/voiceUki1", beat - 2),
new MultiSound.Sound("monkeyWatch/voiceUki1Echo1", beat - 1.75),
new MultiSound.Sound("monkeyWatch/voiceUki2", beat - 1),
new MultiSound.Sound("monkeyWatch/voiceUki2Echo1", beat - 0.75),
new MultiSound.Sound("monkeyWatch/voiceUki3", beat),
new MultiSound.Sound("monkeyWatch/voiceUki3Echo1", beat + 0.25),
});
}
if (!muteEek)
{
for (int i = 0; i < length; i++)
{
int randomKi = UnityEngine.Random.Range(1, 3);
soundsToPlay.AddRange(new List<MultiSound.Sound>()
{
new MultiSound.Sound($"monkeyWatch/voiceKi{randomKi}", beat + i + 0.5),
new MultiSound.Sound($"monkeyWatch/voiceKi{randomKi}Echo{UnityEngine.Random.Range(1, 3)}", beat + i + 0.75),
});
}
}
if (soundsToPlay.Count > 0) MultiSound.Play(soundsToPlay.ToArray(), forcePlay: true);
}
public static void PinkMonkeySoundCustom(double beat, float length, bool muteOoki, bool muteEek)
{
List<MultiSound.Sound> soundsToPlay = new();
var allCustoms = FindCustomOffbeatMonkeysBetweenBeat(beat, beat + length);
if (!muteOoki)
{
soundsToPlay.AddRange(new List<MultiSound.Sound>()
{
new MultiSound.Sound("monkeyWatch/voiceUki1", beat - 2),
new MultiSound.Sound("monkeyWatch/voiceUki1Echo1", beat - 1.75),
new MultiSound.Sound("monkeyWatch/voiceUki2", beat - 1),
new MultiSound.Sound("monkeyWatch/voiceUki2Echo1", beat - 0.75),
});
if (allCustoms.Find(x => x.beat == beat) == null)
{
soundsToPlay.AddRange(new List<MultiSound.Sound>()
{
new MultiSound.Sound("monkeyWatch/voiceUki3", beat),
new MultiSound.Sound("monkeyWatch/voiceUki3Echo1", beat + 0.25),
});
}
}
if (!muteEek)
{
foreach (var custom in allCustoms)
{
int randomKi = UnityEngine.Random.Range(1, 3);
soundsToPlay.AddRange(new List<MultiSound.Sound>()
{
new MultiSound.Sound($"monkeyWatch/voiceKi{randomKi}", custom.beat),
new MultiSound.Sound($"monkeyWatch/voiceKi{randomKi}Echo{UnityEngine.Random.Range(1, 3)}", custom.beat + 0.25),
});
}
}
if (soundsToPlay.Count > 0) MultiSound.Play(soundsToPlay.ToArray(), forcePlay: true);
}
#endregion
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 99a29cf6197a42148a8863b67958be70
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,89 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HeavenStudio.Util;
using Starpelly;
namespace HeavenStudio.Games.Scripts_MonkeyWatch
{
public class WatchBackgroundHandler : MonoBehaviour
{
[Header("Components")]
[SerializeField] private SpriteRenderer[] srsIn;
[SerializeField] private SpriteRenderer[] srsOut;
[SerializeField] private Transform anchorHour;
[SerializeField] private Transform anchorMinute;
private double fadeBeat = -1;
private float fadeLength = 0f;
private bool fadeOut = false;
private bool realTime = true;
private void Awake()
{
Update();
}
private void Update()
{
var cond = Conductor.instance;
float normalizedBeat = Mathf.Clamp01(cond.GetPositionFromBeat(fadeBeat, fadeLength));
if (fadeOut)
{
foreach (var s in srsIn)
{
s.color = new Color(s.color.r, s.color.g, s.color.b, 1 - normalizedBeat);
}
foreach (var s in srsOut)
{
s.color = new Color(s.color.r, s.color.g, s.color.b, normalizedBeat);
}
}
else
{
foreach (var s in srsIn)
{
s.color = new Color(s.color.r, s.color.g, s.color.b, normalizedBeat);
}
foreach (var s in srsOut)
{
s.color = new Color(s.color.r, s.color.g, s.color.b, 1 - normalizedBeat);
}
}
if (realTime)
{
var nowTime = System.DateTime.Now;
SetArrowsToTime(nowTime.Hour, nowTime.Minute, nowTime.Second);
}
}
public void SetFade(double beat, float length, bool outFade, MonkeyWatch.TimeMode timeMode, int hours, int minutes)
{
fadeBeat = beat;
fadeLength = length;
fadeOut = outFade;
realTime = timeMode == MonkeyWatch.TimeMode.RealTime;
if (!realTime)
{
SetArrowsToTime(hours, minutes, 0);
}
Update();
}
private void SetArrowsToTime(int hours, int minutes, int seconds)
{
float normalizedHour = Mathp.Normalize(hours + (minutes / 60f) + (seconds / 3600f), 0f, 12f);
anchorHour.localEulerAngles = new Vector3(0, 0, -Mathf.LerpUnclamped(0, 360, normalizedHour));
float normalizedMinute = Mathp.Normalize(minutes + (seconds / 60f), 0, 60);
anchorMinute.localEulerAngles = new Vector3(0, 0, -Mathf.LerpUnclamped(0, 360, normalizedMinute));
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: c89276e2caf959949a2a775c9f228443
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,113 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HeavenStudio.Util;
namespace HeavenStudio.Games.Scripts_MonkeyWatch
{
public class WatchMonkey : MonoBehaviour
{
private Animator anim;
private Animator holeAnim;
[Header("Properties")]
[SerializeField] private bool isPink;
private MonkeyWatch game;
private int direction = 0;
public double monkeyBeat;
private double disappearBeat = 0;
private bool disappear = false;
private PlayerActionEvent inputEvent;
private void Awake()
{
game = MonkeyWatch.instance;
anim = GetComponent<Animator>();
}
private void Update()
{
if (disappear)
{
float normalizedBeat = Conductor.instance.GetPositionFromBeat(disappearBeat, 0.5f);
anim.DoNormalizedAnimation(isPink ? "PinkAppear" : "Appear", 1 - normalizedBeat);
float normalizedBeatClose = Conductor.instance.GetPositionFromBeat(disappearBeat + 0.25f, 0.25f);
holeAnim.DoNormalizedAnimation("HoleClose", Mathf.Clamp01(normalizedBeatClose));
if (normalizedBeat > 1f)
{
Destroy(gameObject);
}
}
}
public void Appear(double beat, bool instant, Animator hole, int dir)
{
monkeyBeat = beat;
direction = dir;
holeAnim = hole;
holeAnim.DoScaledAnimationAsync("HoleOpen", 0.4f, instant ? 1 : 0);
anim.DoScaledAnimationAsync(isPink ? "PinkAppear" : "Appear", 0.4f, instant ? 1 : 0);
}
public void Disappear(double beat)
{
disappear = true;
disappearBeat = beat;
Update();
}
public void Prepare(double prepareBeat, double inputBeat)
{
anim.DoScaledAnimationAsync(isPink ? "PinkPrepare" + direction : "Prepare" + direction, 0);
inputEvent = game.ScheduleInput(prepareBeat, inputBeat - prepareBeat, MonkeyWatch.InputAction_BasicPress, Just, Miss, Empty);
BeatAction.New(this, new List<BeatAction.Action>()
{
new BeatAction.Action(prepareBeat - 0.25, delegate
{
if (inputEvent != null && inputEvent.enabled) anim.DoScaledAnimationAsync(isPink ? "PinkPrepare" + direction : "Prepare" + direction, 0.4f);
})
});
}
private void Just(PlayerActionEvent caller, float state)
{
bool barely = state >= 1f || state <= -1f;
if (barely)
{
SoundByte.PlayOneShot("nearMiss");
}
else
{
SoundByte.PlayOneShotGame(isPink ? "monkeyWatch/clapOffbeat" : $"monkeyWatch/clapOnbeat{UnityEngine.Random.Range(1, 6)}");
}
game.monkeyClockArrow.Move();
game.PlayerMonkeyClap(isPink, barely);
anim.DoScaledAnimationAsync(isPink ? "PinkClap" + direction : "Clap" + direction, 0.4f);
BeatAction.New(this, new List<BeatAction.Action>()
{
new BeatAction.Action(caller.timer + caller.startBeat + 1, delegate
{
string whichAnim = barely ? "Barely" : "Just";
anim.DoScaledAnimationAsync(isPink ? "Pink" + whichAnim : whichAnim, 0.4f);
})
});
}
private void Miss(PlayerActionEvent caller)
{
anim.DoScaledAnimationAsync(isPink ? "PinkMiss" : "Miss", 0.4f);
game.monkeyClockArrow.Move();
}
private void Empty(PlayerActionEvent caller)
{
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: 23217e6c428ce954e8fd08940b856233
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -0,0 +1,90 @@
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using HeavenStudio.Util;
using UnityEngine.Rendering;
namespace HeavenStudio.Games.Scripts_MonkeyWatch
{
public class WatchMonkeyHandler : MonoBehaviour
{
[Header("Components")]
[SerializeField] private WatchMonkey yellowMonkeyRef;
[SerializeField] private WatchMonkey pinkMonkeyRef;
[Header("Properties")]
[SerializeField] private int maxMonkeys = 30;
private MonkeyWatch game;
private int startMinute = 0;
private int watchHoleIndex = 0;
private List<Transform> watchHoles = new();
private List<WatchMonkey> currentMonkeys = new();
private void Awake()
{
game = MonkeyWatch.instance;
for (int i = 0; i < 60; i++)
{
watchHoles.Add(transform.GetChild(i));
}
}
public void Init(int minute)
{
startMinute = minute;
watchHoleIndex = startMinute;
}
private Transform GetNextAvailableWatchHole()
{
int currentIndex = watchHoleIndex;
watchHoleIndex++;
if (watchHoleIndex >= watchHoles.Count)
{
watchHoleIndex = 0;
}
return watchHoles[currentIndex];
}
public void SpawnMonkey(double beat, bool isPink, bool instant)
{
if (GetMonkeyAtBeat(beat) != null) return;
int index = watchHoleIndex;
var hole = GetNextAvailableWatchHole();
WatchMonkey spawnedMonkey = Instantiate(isPink ? pinkMonkeyRef : yellowMonkeyRef, hole);
spawnedMonkey.Appear(beat, instant, hole.GetComponent<Animator>(), GetMonkeyAngle(index));
spawnedMonkey.transform.eulerAngles = Vector3.zero;
var sortingGroup = spawnedMonkey.GetComponent<SortingGroup>();
if (index <= 30) sortingGroup.sortingOrder = 50 + watchHoleIndex;
else sortingGroup.sortingOrder = 50 + watchHoleIndex - (watchHoleIndex - 29);
currentMonkeys.Add(spawnedMonkey);
if (currentMonkeys.Count > maxMonkeys)
{
currentMonkeys[0].Disappear(Conductor.instance.songPositionInBeats);
currentMonkeys.Remove(currentMonkeys[0]);
}
}
public WatchMonkey GetMonkeyAtBeat(double beat)
{
return currentMonkeys.Find(x => x.monkeyBeat == beat);
}
private int GetMonkeyAngle(int monkey)
{
return monkey switch
{
>= 4 and <= 12 => 2,
>= 12 and <= 19 => 3,
>= 19 and <= 27 => 4,
>= 27 and <= 34 => 5,
>= 34 and <= 42 => 6,
>= 42 and <= 49 => 7,
>= 49 and <= 56 => 8,
_ => 1,
};
}
}
}

View File

@ -0,0 +1,11 @@
fileFormatVersion: 2
guid: e0769f835f6039b41b98b9487eb743d9
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -169,6 +169,7 @@ namespace HeavenStudio.Games.Scripts_SeeSaw
break;
}
float newCamY = 0f;
if (!see && game.cameraMove)
{
switch (currentState)