Auto-Swing (#827)

* BurstLinq

make BGM resync when changing pitch (to test)

* autoswing

some game implementations, most games already work fine

* more game tweaks

* 16th note swing

more game fixes
make pitch change resync optional in the API

* suppress some common warnings

* Update Credits.txt
This commit is contained in:
minenice55 2024-04-07 00:54:06 -04:00 committed by GitHub
parent c22c23f974
commit fd1df6687e
49 changed files with 2363 additions and 245 deletions

View file

@ -61,6 +61,7 @@
-<indent=5%>vincells</indent> -<indent=5%>vincells</indent>
-<indent=5%>Yumiko90</indent> -<indent=5%>Yumiko90</indent>
-<indent=5%>fu_majime</indent> -<indent=5%>fu_majime</indent>
-<indent=5%>bluriskayo</indent>
<align="center"><b>Music</b></align> <align="center"><b>Music</b></align>
-<indent=5%>Jellirby <alpha=#88>(Opening)<alpha=#FF></indent> -<indent=5%>Jellirby <alpha=#88>(Opening)<alpha=#FF></indent>
@ -81,6 +82,7 @@
-<indent=5%>JoyShockLibrary <alpha=#88>Jibb Smart, fork by RHeavenStudio<alpha=#FF></indent> -<indent=5%>JoyShockLibrary <alpha=#88>Jibb Smart, fork by RHeavenStudio<alpha=#FF></indent>
-<indent=5%>Jukebox <alpha=#88>RHeavenStudio<alpha=#FF></indent> -<indent=5%>Jukebox <alpha=#88>RHeavenStudio<alpha=#FF></indent>
-<indent=5%>BurstLinq</indent>
-<indent=5%>Dependencies Hunter</indent> -<indent=5%>Dependencies Hunter</indent>
-<indent=5%>DOTween</indent> -<indent=5%>DOTween</indent>
-<indent=5%>Graphy</indent> -<indent=5%>Graphy</indent>

File diff suppressed because it is too large Load diff

View file

@ -5,7 +5,6 @@ using UnityEngine;
using Jukebox; using Jukebox;
using HeavenStudio.Util; using HeavenStudio.Util;
using System.Data.Common;
namespace HeavenStudio namespace HeavenStudio
{ {
@ -20,13 +19,15 @@ namespace HeavenStudio
public List<AddedPitchChange> addedPitchChanges = new List<AddedPitchChange>(); public List<AddedPitchChange> addedPitchChanges = new List<AddedPitchChange>();
public GameManager gameManager;
// Song beats per minute // Song beats per minute
// This is determined by the song you're trying to sync up to // This is determined by the song you're trying to sync up to
public float songBpm; public float songBpm { get; private set; }
// The number of seconds for each song beat // The number of seconds for each song beat
public float secPerBeat => (float)secPerBeatAsDouble; public float secPerBeat => (float)secPerBeatAsDouble;
public double secPerBeatAsDouble; public double secPerBeatAsDouble { get; private set; }
// The number of seconds for each song beat, inversely scaled to song pitch (higer pitch = shorter time) // The number of seconds for each song beat, inversely scaled to song pitch (higer pitch = shorter time)
public float pitchedSecPerBeat => (float)pitchedSecPerBeatAsDouble; public float pitchedSecPerBeat => (float)pitchedSecPerBeatAsDouble;
@ -38,9 +39,11 @@ namespace HeavenStudio
public double songPositionAsDouble => songPos; public double songPositionAsDouble => songPos;
// Current song position, in beats // Current song position, in beats
public double songPosBeat; // for Conductor use only public double songPosBeat { get; private set; }
public float songPositionInBeats => (float)songPosBeat; public float songPositionInBeats => (float)songPositionInBeatsAsDouble;
public double songPositionInBeatsAsDouble => songPosBeat; public double songPositionInBeatsAsDouble { get; private set; }
public float unswungSongPositionInBeats => (float)songPosBeat;
public double unswungSongPositionInBeatsAsDouble => songPosBeat;
// Current time of the song // Current time of the song
private double time; private double time;
@ -79,7 +82,7 @@ namespace HeavenStudio
// Metronome tick sound enabled // Metronome tick sound enabled
public bool metronome = false; public bool metronome = false;
Util.Sound metronomeSound; Util.MultiSound metronomeSound;
private int _metronomeTally = 0; private int _metronomeTally = 0;
// pitch values // pitch values
@ -93,12 +96,14 @@ namespace HeavenStudio
private float timelineVolume = 1f; private float timelineVolume = 1f;
private float minigameVolume = 1f; private float minigameVolume = 1f;
public void SetTimelinePitch(float pitch) const bool doPitchResync = true;
public void SetTimelinePitch(float pitch, bool resync = false)
{ {
if (isPaused) return; if (isPaused || deferTimeKeeping) return;
if (pitch != 0 && pitch != timelinePitch) if (pitch != 0 && pitch != timelinePitch)
{ {
Debug.Log("added pitch change " + pitch * minigamePitch + " at" + absTime); // Debug.Log("added pitch change " + pitch * minigamePitch + " at " + absTime);
addedPitchChanges.Add(new AddedPitchChange { time = absTime, pitch = pitch * minigamePitch }); addedPitchChanges.Add(new AddedPitchChange { time = absTime, pitch = pitch * minigamePitch });
} }
@ -106,15 +111,22 @@ namespace HeavenStudio
if (musicSource != null && musicSource.clip != null) if (musicSource != null && musicSource.clip != null)
{ {
musicSource.pitch = SongPitch; musicSource.pitch = SongPitch;
if (doPitchResync && isPlaying && resync && !deferTimeKeeping)
{
time = MapTimeToPitchChanges(absTime + absTimeAdjust);
songPos = startPos + time;
songPosBeat = GetBeatFromSongPos(songPos);
SeekMusicToTime(songPos, firstBeatOffset);
}
} }
} }
public void SetMinigamePitch(float pitch) public void SetMinigamePitch(float pitch, bool resync = false)
{ {
if (isPaused || !isPlaying) return; if (isPaused || deferTimeKeeping || !isPlaying) return;
if (pitch != 0 && pitch != minigamePitch) if (pitch != 0 && pitch != minigamePitch)
{ {
Debug.Log("added pitch change " + pitch * timelinePitch + " at" + absTime); // Debug.Log("added pitch change " + pitch * timelinePitch + " at " + absTime);
addedPitchChanges.Add(new AddedPitchChange { time = absTime, pitch = pitch * timelinePitch }); addedPitchChanges.Add(new AddedPitchChange { time = absTime, pitch = pitch * timelinePitch });
} }
@ -122,6 +134,13 @@ namespace HeavenStudio
if (musicSource != null && musicSource.clip != null) if (musicSource != null && musicSource.clip != null)
{ {
musicSource.pitch = SongPitch; musicSource.pitch = SongPitch;
if (doPitchResync && isPlaying && resync && !deferTimeKeeping)
{
time = MapTimeToPitchChanges(absTime + absTimeAdjust);
songPos = startPos + time;
songPosBeat = GetBeatFromSongPos(songPos);
SeekMusicToTime(songPos, firstBeatOffset);
}
} }
} }
@ -141,12 +160,10 @@ namespace HeavenStudio
public void SetBeat(double beat) public void SetBeat(double beat)
{ {
var chart = GameManager.instance.Beatmap; var chart = gameManager.Beatmap;
double offset = chart.data.offset; double offset = chart.data.offset;
startPos = GetSongPosFromBeat(beat); startPos = GetSongPosFromBeat(beat);
double dspTime = AudioSettings.dspTime;
time = startPos; time = startPos;
firstBeatOffset = offset; firstBeatOffset = offset;
@ -154,13 +171,15 @@ namespace HeavenStudio
songPosBeat = GetBeatFromSongPos(time); songPosBeat = GetBeatFromSongPos(time);
GameManager.instance.SetCurrentEventToClosest(beat); gameManager.SetCurrentEventToClosest(beat);
} }
public void PlaySetup(double beat) public void PlaySetup(double beat)
{ {
deferTimeKeeping = true; deferTimeKeeping = true;
songPosBeat = beat; songPosBeat = beat;
absTime = 0;
startTime = DateTime.Now;
} }
public void Play(double beat) public void Play(double beat)
@ -183,7 +202,7 @@ namespace HeavenStudio
SetMinigameVolume(1f); SetMinigameVolume(1f);
} }
RiqBeatmap chart = GameManager.instance.Beatmap; RiqBeatmap chart = gameManager.Beatmap;
double offset = chart.data.offset; double offset = chart.data.offset;
double dspTime = AudioSettings.dspTime; double dspTime = AudioSettings.dspTime;
dspStart = dspTime; dspStart = dspTime;
@ -215,6 +234,7 @@ namespace HeavenStudio
_metronomeTally = 0; _metronomeTally = 0;
startTime = DateTime.Now; startTime = DateTime.Now;
absTime = 0;
absTimeAdjust = 0; absTimeAdjust = 0;
deferTimeKeeping = musicSource.clip != null; deferTimeKeeping = musicSource.clip != null;
@ -232,7 +252,8 @@ namespace HeavenStudio
{ {
deferTimeKeeping = false; deferTimeKeeping = false;
// Debug.Log($"dsptime: {dsp}, deferred timekeeping for {DateTime.Now - startTime} seconds (delta dsp {dsp - dspStart})"); // Debug.Log($"dsptime: {dsp}, deferred timekeeping for {DateTime.Now - startTime} seconds (delta dsp {dsp - dspStart})");
startTime += TimeSpan.FromSeconds(dsp - dspStart); startTime = DateTime.Now;
absTime = 0;
absTimeAdjust = 0; absTimeAdjust = 0;
dspStart = dsp; dspStart = dsp;
} }
@ -243,8 +264,8 @@ namespace HeavenStudio
if (!isPlaying) return; if (!isPlaying) return;
isPlaying = false; isPlaying = false;
isPaused = true; isPaused = true;
deferTimeKeeping = false;
SetMinigamePitch(1f); SetMinigamePitch(1f);
deferTimeKeeping = false;
musicSource.Stop(); musicSource.Stop();
Util.SoundByte.PauseOneShots(); Util.SoundByte.PauseOneShots();
@ -265,8 +286,8 @@ namespace HeavenStudio
isPlaying = false; isPlaying = false;
isPaused = false; isPaused = false;
deferTimeKeeping = false;
SetMinigamePitch(1f); SetMinigamePitch(1f);
deferTimeKeeping = false;
musicSource.Stop(); musicSource.Stop();
} }
@ -391,6 +412,7 @@ namespace HeavenStudio
songPos = startPos + time; songPos = startPos + time;
songPosBeat = GetBeatFromSongPos(songPos); songPosBeat = GetBeatFromSongPos(songPos);
songPositionInBeatsAsDouble = GetSwungBeat(songPosBeat);
} }
} }
@ -422,7 +444,14 @@ namespace HeavenStudio
{ {
if (songPositionInBeatsAsDouble >= Math.Ceiling(startBeat) + _metronomeTally) if (songPositionInBeatsAsDouble >= Math.Ceiling(startBeat) + _metronomeTally)
{ {
if (metronome) metronomeSound = Util.SoundByte.PlayOneShot("metronome", Math.Ceiling(startBeat) + _metronomeTally); // if (metronome) metronomeSound = Util.SoundByte.PlayOneShot("metronome", Math.Ceiling(startBeat) + _metronomeTally);
if (metronome)
{
metronomeSound = Util.MultiSound.Play(new List<Util.MultiSound.Sound> {
new Util.MultiSound.Sound("metronome", Math.Ceiling(startBeat) + _metronomeTally),
new Util.MultiSound.Sound("metronome", Math.Ceiling(startBeat) + _metronomeTally + 0.5, 1.5f, 0.5f)
}, false, true);
}
_metronomeTally++; _metronomeTally++;
} }
} }
@ -430,7 +459,7 @@ namespace HeavenStudio
{ {
if (metronomeSound != null) if (metronomeSound != null)
{ {
metronomeSound.Stop(); // metronomeSound.StopAll();
metronomeSound = null; metronomeSound = null;
} }
} }
@ -451,9 +480,9 @@ namespace HeavenStudio
return result; return result;
} }
public float GetLoopPositionFromBeat(float beatOffset, float length, bool beatClamp = true) public float GetLoopPositionFromBeat(float beatOffset, float length, bool beatClamp = true, bool ignoreSwing = true)
{ {
float beat = songPositionInBeats; float beat = ignoreSwing ? unswungSongPositionInBeats : songPositionInBeats;
if (beatClamp) if (beatClamp)
{ {
beat = Mathf.Max(beat, 0); beat = Mathf.Max(beat, 0);
@ -461,9 +490,9 @@ namespace HeavenStudio
return Mathf.Repeat((beat / length) + beatOffset, 1); return Mathf.Repeat((beat / length) + beatOffset, 1);
} }
public float GetPositionFromBeat(double startBeat, double length, bool beatClamp = true) public float GetPositionFromBeat(double startBeat, double length, bool beatClamp = true, bool ignoreSwing = true)
{ {
float beat = songPositionInBeats; float beat = ignoreSwing ? unswungSongPositionInBeats : songPositionInBeats;
if (beatClamp) if (beatClamp)
{ {
beat = Mathf.Max(beat, 0); beat = Mathf.Max(beat, 0);
@ -474,71 +503,113 @@ namespace HeavenStudio
private List<RiqEntity> GetSortedTempoChanges() private List<RiqEntity> GetSortedTempoChanges()
{ {
GameManager.instance.SortEventsList(); gameManager.SortEventsList();
return GameManager.instance.Beatmap.TempoChanges; return gameManager.Beatmap.TempoChanges;
} }
public float GetBpmAtBeat(double beat, out float swingRatio) public float GetBpmAtBeat(double beat, out float swingRatio, out float swingDivision)
{ {
swingRatio = 0.5f; swingRatio = 0.5f;
var chart = GameManager.instance.Beatmap; swingDivision = 1f;
var chart = gameManager.Beatmap;
if (chart.TempoChanges.Count == 0) if (chart.TempoChanges.Count == 0)
return 120f; return 120f;
float bpm = chart.TempoChanges[0]["tempo"];
swingRatio = chart.TempoChanges[0]["swing"] + 0.5f;
RiqEntity tempo = null;
foreach (RiqEntity t in chart.TempoChanges) foreach (RiqEntity t in chart.TempoChanges)
{ {
if (t.beat > beat) if (t.beat > beat)
{ {
break; break;
} }
bpm = t["tempo"]; tempo = t;
swingRatio = t["swing"] + 0.5f;
} }
tempo ??= chart.TempoChanges[0];
return bpm; swingRatio = tempo["swing"] + 0.5f;
swingDivision = tempo["swingDivision"] ?? 1f;
return tempo["tempo"];
} }
public float GetBpmAtBeat(double beat) public float GetBpmAtBeat(double beat)
{ {
return GetBpmAtBeat(beat, out _); return GetBpmAtBeat(beat, out _, out _);
} }
public float GetSwingRatioAtBeat(double beat) public float GetSwingRatioAtBeat(double beat, out float swingDivision)
{ {
float swingRatio; float swingRatio;
GetBpmAtBeat(beat, out swingRatio); GetBpmAtBeat(beat, out swingRatio, out swingDivision);
return swingRatio; return swingRatio;
} }
public double GetSwungBeat(double beat, float ratio) public double GetSwungBeat(double beat)
{ {
return beat + GetSwingOffset(beat, ratio); float ratio = GetSwingRatioAtBeat(beat, out float division);
int floor = (int)Math.Floor(beat / division);
return (floor * division) + GetSwingOffset(beat, ratio, division);
} }
public double GetSwingOffset(double beatFrac, float ratio) public double GetSwungBeat(double beat, float ratio, float division)
{ {
beatFrac %= 1; int floor = (int)Math.Floor(beat / division);
if (beatFrac <= 0.5) return (floor * division) + GetSwingOffset(beat, ratio, division);
}
public double GetSwingOffset(double beatFrac, float ratio, float division)
{
beatFrac %= division;
if (beatFrac <= ratio)
{ {
return 0.5 / ratio * beatFrac; return 0.5 / ratio * beatFrac;
} }
else else
{ {
return 0.5 + (0.5 / (1f - ratio) * (beatFrac - ratio)); return 0.5 + 0.5 / (1 - ratio) * (beatFrac - ratio);
} }
} }
public double GetSongPosFromBeat(double beat) public double GetUnSwungBeat(double beat)
{ {
var chart = GameManager.instance.Beatmap; float ratio = GetSwingRatioAtBeat(beat, out float division);
int floor = (int)Math.Floor(beat / division);
return (floor * division) + GetUnSwingOffset(beat, ratio, division);
}
public double GetUnSwungBeat(double beat, float ratio, float division)
{
int floor = (int)Math.Floor(beat / division);
return (floor * division) + GetUnSwingOffset(beat, ratio, division);
}
public double GetUnSwingOffset(double beatFrac, float ratio, float division)
{
beatFrac %= division;
if (beatFrac <= ratio)
{
return 2 * ratio * beatFrac;
}
else
{
return 2 * (beatFrac - 0.5f) * (1f - ratio) + ratio;
}
}
public double GetSongPosFromBeat(double beat, bool ignoreSwing = false)
{
var chart = gameManager.Beatmap;
float bpm = 120f; float bpm = 120f;
double counter = 0f; double counter = 0f;
double lastTempoChangeBeat = 0f; double lastTempoChangeBeat = 0f;
if (!ignoreSwing)
{
beat = GetUnSwungBeat(beat);
}
foreach (RiqEntity t in chart.TempoChanges) foreach (RiqEntity t in chart.TempoChanges)
{ {
if (t.beat > beat) if (t.beat > beat)
@ -572,7 +643,7 @@ namespace HeavenStudio
double counterSeconds = 0; double counterSeconds = 0;
float lastBpm = 120f; float lastBpm = 120f;
foreach (RiqEntity t in GameManager.instance.Beatmap.TempoChanges) foreach (RiqEntity t in gameManager.Beatmap.TempoChanges)
{ {
double beatToNext = t.beat - lastTempoChangeBeat; double beatToNext = t.beat - lastTempoChangeBeat;
double secToNext = BeatsToSecs(beatToNext, lastBpm); double secToNext = BeatsToSecs(beatToNext, lastBpm);

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using BurstLinq;
using System.Collections.Generic; using System.Collections.Generic;
using UnityEngine; using UnityEngine;
using Jukebox; using Jukebox;

View file

@ -4,6 +4,7 @@ using UnityEngine;
using HeavenStudio.Util; using HeavenStudio.Util;
using System.Linq; using System.Linq;
using BurstLinq;
using Jukebox; using Jukebox;
using Jukebox.Legacy; using Jukebox.Legacy;

View file

@ -86,11 +86,13 @@ namespace HeavenStudio
GameObject Conductor = new GameObject(); GameObject Conductor = new GameObject();
Conductor.name = "Conductor"; Conductor.name = "Conductor";
AudioSource source = Conductor.AddComponent<AudioSource>(); AudioSource source = Conductor.AddComponent<AudioSource>();
Conductor.AddComponent<Conductor>(); Conductor cond = Conductor.AddComponent<Conductor>();
Conductor.GetComponent<Conductor>().musicSource = source; cond.musicSource = source;
source.priority = 255; source.priority = 255;
source.outputAudioMixerGroup = Settings.GetMusicMixer(); source.outputAudioMixerGroup = Settings.GetMusicMixer();
// Conductor.AddComponent<AudioDspTimeKeeper>(); cond.gameManager = gameManager;
gameManager.conductor = cond;
GlobalGameManager.GameRenderTexture = gameRenderTexture; GlobalGameManager.GameRenderTexture = gameRenderTexture;
GlobalGameManager.OverlayRenderTexture = overlayRenderTexture; GlobalGameManager.OverlayRenderTexture = overlayRenderTexture;

View file

@ -2,6 +2,8 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using BurstLinq;
using UnityEngine; using UnityEngine;
using UnityEngine.Pool; using UnityEngine.Pool;
@ -87,6 +89,7 @@ namespace HeavenStudio
public static GameManager instance { get; private set; } public static GameManager instance { get; private set; }
private EventCaller eventCaller; private EventCaller eventCaller;
public Conductor conductor;
// average input accuracy (msec) // average input accuracy (msec)
List<int> inputOffsetSamples = new List<int>(); List<int> inputOffsetSamples = new List<int>();
@ -173,9 +176,9 @@ namespace HeavenStudio
} }
SortEventsList(); SortEventsList();
Conductor.instance.SetBpm(Beatmap.TempoChanges[0]["tempo"]); conductor.SetBpm(Beatmap.TempoChanges[0]["tempo"]);
Conductor.instance.SetVolume(Beatmap.VolumeChanges[0]["volume"]); conductor.SetVolume(Beatmap.VolumeChanges[0]["volume"]);
Conductor.instance.firstBeatOffset = Beatmap.data.offset; conductor.firstBeatOffset = Beatmap.data.offset;
if (!preLoaded) if (!preLoaded)
{ {
@ -233,14 +236,17 @@ namespace HeavenStudio
public void NewRemix() public void NewRemix()
{ {
Debug.Log("Creating new remix");
AudioLoadDone = false; AudioLoadDone = false;
Beatmap = new("1", "HeavenStudio"); Beatmap = new("1", "HeavenStudio");
Beatmap.data.properties = new(Minigames.propertiesModel); Beatmap.data.properties = new(Minigames.propertiesModel);
Beatmap.AddNewTempoChange(0, 120f);
RiqEntity t = Beatmap.AddNewTempoChange(0, 120f);
t.CreateProperty("swingDivision", 1f);
Beatmap.AddNewVolumeChange(0, 100f); Beatmap.AddNewVolumeChange(0, 100f);
Beatmap.data.offset = 0f; Beatmap.data.offset = 0f;
Conductor.instance.musicSource.clip = null; conductor.musicSource.clip = null;
RiqFileHandler.WriteRiq(Beatmap); RiqFileHandler.WriteRiq(Beatmap);
AudioLoadDone = true; AudioLoadDone = true;
} }
@ -263,7 +269,7 @@ namespace HeavenStudio
catch (System.IO.FileNotFoundException f) catch (System.IO.FileNotFoundException f)
{ {
Debug.LogWarning("chart has no music: " + f.Message); Debug.LogWarning("chart has no music: " + f.Message);
Conductor.instance.musicSource.clip = null; conductor.musicSource.clip = null;
AudioLoadDone = true; AudioLoadDone = true;
yield break; yield break;
} }
@ -277,7 +283,7 @@ namespace HeavenStudio
} }
yield return current; yield return current;
} }
Conductor.instance.musicSource.clip = RiqFileHandler.StreamedAudioClip; conductor.musicSource.clip = RiqFileHandler.StreamedAudioClip;
AudioLoadDone = true; AudioLoadDone = true;
} }
@ -299,9 +305,9 @@ namespace HeavenStudio
if (!editor) if (!editor)
StartCoroutine(LoadMusic()); StartCoroutine(LoadMusic());
SortEventsList(); SortEventsList();
Conductor.instance.SetBpm(Beatmap.TempoChanges[0]["tempo"]); conductor.SetBpm(Beatmap.TempoChanges[0]["tempo"]);
Conductor.instance.SetVolume(Beatmap.VolumeChanges[0]["volume"]); conductor.SetVolume(Beatmap.VolumeChanges[0]["volume"]);
Conductor.instance.firstBeatOffset = Beatmap.data.offset; conductor.firstBeatOffset = Beatmap.data.offset;
if (!playMode) if (!playMode)
{ {
Stop(0); Stop(0);
@ -444,7 +450,7 @@ namespace HeavenStudio
var inf = GetGameInfo(gameName); var inf = GetGameInfo(gameName);
if (inf != null && inf.usesAssetBundle && inf.AssetsLoaded && !inf.SequencesPreloaded) if (inf != null && inf.usesAssetBundle && inf.AssetsLoaded && !inf.SequencesPreloaded)
{ {
Debug.Log($"Preparing game {gameName}"); // Debug.Log($"Preparing game {gameName}");
PreloadGameSequences(gameName); PreloadGameSequences(gameName);
} }
eventCaller.CallPreEvent(entity); eventCaller.CallPreEvent(entity);
@ -459,32 +465,31 @@ namespace HeavenStudio
{ {
if (BeatmapEntities() < 1) if (BeatmapEntities() < 1)
return; return;
if (!Conductor.instance.isPlaying) if (conductor.WaitingForDsp || !conductor.isPlaying)
return; return;
Conductor cond = Conductor.instance; double clampedBeat = Math.Max(conductor.songPositionInBeatsAsDouble, 0);
double clampedBeat = Math.Max(cond.songPositionInBeatsAsDouble, 0);
if (currentTempoEvent < Beatmap.TempoChanges.Count && currentTempoEvent >= 0) if (currentTempoEvent < Beatmap.TempoChanges.Count && currentTempoEvent >= 0)
{ {
if (cond.songPositionInBeatsAsDouble >= tempoBeats[currentTempoEvent]) if (conductor.songPositionInBeatsAsDouble >= tempoBeats[currentTempoEvent])
{ {
cond.SetBpm(Beatmap.TempoChanges[currentTempoEvent]["tempo"]); conductor.SetBpm(Beatmap.TempoChanges[currentTempoEvent]["tempo"]);
currentTempoEvent++; currentTempoEvent++;
} }
} }
if (currentVolumeEvent < Beatmap.VolumeChanges.Count && currentVolumeEvent >= 0) if (currentVolumeEvent < Beatmap.VolumeChanges.Count && currentVolumeEvent >= 0)
{ {
if (cond.songPositionInBeatsAsDouble >= volumeBeats[currentVolumeEvent]) if (conductor.songPositionInBeatsAsDouble >= volumeBeats[currentVolumeEvent])
{ {
cond.SetVolume(Beatmap.VolumeChanges[currentVolumeEvent]["volume"]); conductor.SetVolume(Beatmap.VolumeChanges[currentVolumeEvent]["volume"]);
currentVolumeEvent++; currentVolumeEvent++;
} }
} }
if (currentSectionEvent < Beatmap.SectionMarkers.Count && currentSectionEvent >= 0) if (currentSectionEvent < Beatmap.SectionMarkers.Count && currentSectionEvent >= 0)
{ {
if (cond.songPositionInBeatsAsDouble >= sectionBeats[currentSectionEvent]) if (conductor.songPositionInBeatsAsDouble >= sectionBeats[currentSectionEvent])
{ {
RiqEntity marker = Beatmap.SectionMarkers[currentSectionEvent]; RiqEntity marker = Beatmap.SectionMarkers[currentSectionEvent];
if (!string.IsNullOrEmpty(marker["sectionName"])) if (!string.IsNullOrEmpty(marker["sectionName"]))
@ -526,7 +531,7 @@ namespace HeavenStudio
} }
} }
if (cond.songPositionInBeatsAsDouble >= Math.Ceiling(_playStartBeat) + _pulseTally) if (conductor.songPositionInBeatsAsDouble >= Math.Ceiling(_playStartBeat) + _pulseTally)
{ {
if (minigame != null) minigame.OnBeatPulse(Math.Ceiling(_playStartBeat) + _pulseTally); if (minigame != null) minigame.OnBeatPulse(Math.Ceiling(_playStartBeat) + _pulseTally);
onBeatPulse?.Invoke(Math.Ceiling(_playStartBeat) + _pulseTally); onBeatPulse?.Invoke(Math.Ceiling(_playStartBeat) + _pulseTally);
@ -543,7 +548,7 @@ namespace HeavenStudio
List<RiqEntity> entitiesInRange = ListPool<RiqEntity>.Get(); List<RiqEntity> entitiesInRange = ListPool<RiqEntity>.Get();
List<RiqEntity> fxEntities = ListPool<RiqEntity>.Get(); List<RiqEntity> fxEntities = ListPool<RiqEntity>.Get();
// allows for multiple events on the same beat to be executed on the same frame, so no more 1-frame delay // allows for multiple events on the same beat to be executed on the same frame, so no more 1-frame delay
while (currentEvent < eventBeats.Count && clampedBeat >= eventBeats[currentEvent] && Conductor.instance.isPlaying) while (currentEvent < eventBeats.Count && clampedBeat >= eventBeats[currentEvent] && this.conductor.isPlaying)
{ {
fxEntities.Clear(); fxEntities.Clear();
entitiesInRange.Clear(); entitiesInRange.Clear();
@ -599,9 +604,9 @@ namespace HeavenStudio
} }
else else
{ {
double currectSectionStart = cond.GetSongPosFromBeat(currentSection.beat); double currectSectionStart = conductor.GetSongPosFromBeat(currentSection.beat);
SectionProgress = (cond.songPosition - currectSectionStart) / (cond.GetSongPosFromBeat(nextSectionBeat) - currectSectionStart); SectionProgress = (conductor.songPosition - currectSectionStart) / (conductor.GetSongPosFromBeat(nextSectionBeat) - currectSectionStart);
} }
} }
@ -609,10 +614,10 @@ namespace HeavenStudio
{ {
OverlaysManager.instance.TogleOverlaysVisibility(Editor.Editor.instance == null || Editor.Editor.instance.fullscreen || ((PersistentDataManager.gameSettings.overlaysInEditor) && (!Editor.Editor.instance.fullscreen)) || HeavenStudio.Editor.GameSettings.InPreview); OverlaysManager.instance.TogleOverlaysVisibility(Editor.Editor.instance == null || Editor.Editor.instance.fullscreen || ((PersistentDataManager.gameSettings.overlaysInEditor) && (!Editor.Editor.instance.fullscreen)) || HeavenStudio.Editor.GameSettings.InPreview);
if (!Conductor.instance.isPlaying) if (!conductor.isPlaying)
return; return;
if (Conductor.instance.songPositionInBeatsAsDouble >= Math.Ceiling(_playStartBeat) + _latePulseTally) if (conductor.songPositionInBeatsAsDouble >= Math.Ceiling(_playStartBeat) + _latePulseTally)
{ {
if (minigame != null) minigame.OnLateBeatPulse(Math.Ceiling(_playStartBeat) + _latePulseTally); if (minigame != null) minigame.OnLateBeatPulse(Math.Ceiling(_playStartBeat) + _latePulseTally);
onBeatPulse?.Invoke(Math.Ceiling(_playStartBeat) + _latePulseTally); onBeatPulse?.Invoke(Math.Ceiling(_playStartBeat) + _latePulseTally);
@ -668,8 +673,7 @@ namespace HeavenStudio
public void Play(double beat, float delay = 0f) public void Play(double beat, float delay = 0f)
{ {
bool paused = Conductor.instance.isPaused; bool paused = conductor.isPaused;
Debug.Log("Playing at " + beat);
_playStartBeat = beat; _playStartBeat = beat;
_pulseTally = 0; _pulseTally = 0;
_latePulseTally = 0; _latePulseTally = 0;
@ -711,14 +715,16 @@ namespace HeavenStudio
private IEnumerator PlayCo(double beat, float delay = 0f) private IEnumerator PlayCo(double beat, float delay = 0f)
{ {
bool paused = Conductor.instance.isPaused; bool paused = conductor.isPaused;
if (!paused) if (!paused)
{ {
Conductor.instance.SetBpm(Beatmap.TempoChanges[0]["tempo"]); conductor.SetBpm(Beatmap.TempoChanges[0]["tempo"]);
Conductor.instance.SetVolume(Beatmap.VolumeChanges[0]["volume"]); conductor.SetVolume(Beatmap.VolumeChanges[0]["volume"]);
Conductor.instance.firstBeatOffset = Beatmap.data.offset; conductor.firstBeatOffset = Beatmap.data.offset;
conductor.PlaySetup(beat);
SetCurrentEventToClosest(beat); SetCurrentEventToClosest(beat);
Debug.Log("Playing at " + beat);
KillAllSounds(); KillAllSounds();
if (delay > 0) if (delay > 0)
@ -726,7 +732,6 @@ namespace HeavenStudio
yield return new WaitForSeconds(delay); yield return new WaitForSeconds(delay);
} }
Conductor.instance.PlaySetup(beat);
Minigame miniGame = null; Minigame miniGame = null;
if (minigameObj != null && minigameObj.TryGetComponent<Minigame>(out miniGame)) if (minigameObj != null && minigameObj.TryGetComponent<Minigame>(out miniGame))
{ {
@ -765,21 +770,21 @@ namespace HeavenStudio
CircleCursor.LockCursor(true); CircleCursor.LockCursor(true);
} }
Application.backgroundLoadingPriority = ThreadPriority.Low; Application.backgroundLoadingPriority = ThreadPriority.Low;
Conductor.instance.Play(beat); conductor.Play(beat);
} }
public void Pause() public void Pause()
{ {
Conductor.instance.Pause(); conductor.Pause();
Util.SoundByte.PauseOneShots(); Util.SoundByte.PauseOneShots();
onPause?.Invoke(Conductor.instance.songPositionInBeatsAsDouble); onPause?.Invoke(conductor.songPositionInBeatsAsDouble);
canInput = false; canInput = false;
} }
public void Stop(double beat, bool restart = false, float restartDelay = 0f) public void Stop(double beat, bool restart = false, float restartDelay = 0f)
{ {
// I feel like I should standardize the names // I feel like I should standardize the names
if (Conductor.instance.isPlaying) if (conductor.isPlaying)
{ {
SkillStarManager.instance.KillStar(); SkillStarManager.instance.KillStar();
TimingAccuracyDisplay.instance.StopStarFlash(); TimingAccuracyDisplay.instance.StopStarFlash();
@ -796,8 +801,8 @@ namespace HeavenStudio
} }
} }
Conductor.instance.Stop(beat); conductor.Stop(beat);
SetCurrentEventToClosest(beat); conductor.SetBeat(beat);
KillAllSounds(); KillAllSounds();
if (restart) if (restart)
@ -816,6 +821,10 @@ namespace HeavenStudio
GlobalGameManager.LoadScene("Judgement", 0.35f, 0f, DestroyGame); GlobalGameManager.LoadScene("Judgement", 0.35f, 0f, DestroyGame);
CircleCursor.LockCursor(false); CircleCursor.LockCursor(false);
} }
else
{
conductor.SetBeat(beat);
}
Application.backgroundLoadingPriority = ThreadPriority.Normal; Application.backgroundLoadingPriority = ThreadPriority.Normal;
} }
@ -830,6 +839,8 @@ namespace HeavenStudio
SoundByte.PreloadAudioClipAsync("skillStar"); SoundByte.PreloadAudioClipAsync("skillStar");
SoundByte.PreloadAudioClipAsync("perfectMiss"); SoundByte.PreloadAudioClipAsync("perfectMiss");
conductor.SetBeat(beat);
WaitUntil yieldOverlays = new WaitUntil(() => OverlaysManager.OverlaysReady); WaitUntil yieldOverlays = new WaitUntil(() => OverlaysManager.OverlaysReady);
WaitUntil yieldBeatmap = new WaitUntil(() => Beatmap != null && BeatmapEntities() > 0); WaitUntil yieldBeatmap = new WaitUntil(() => Beatmap != null && BeatmapEntities() > 0);
WaitUntil yieldAudio = new WaitUntil(() => AudioLoadDone || (ChartLoadError && !GlobalGameManager.IsShowingDialog)); WaitUntil yieldAudio = new WaitUntil(() => AudioLoadDone || (ChartLoadError && !GlobalGameManager.IsShowingDialog));
@ -842,16 +853,16 @@ namespace HeavenStudio
} }
// wait for overlays to be ready // wait for overlays to be ready
Debug.Log("waiting for overlays"); // Debug.Log("waiting for overlays");
yield return yieldOverlays; yield return yieldOverlays;
// wait for beatmap to be loaded // wait for beatmap to be loaded
Debug.Log("waiting for beatmap"); // Debug.Log("waiting for beatmap");
yield return yieldBeatmap; yield return yieldBeatmap;
//wait for audio clip to be loaded //wait for audio clip to be loaded
Debug.Log("waiting for audio"); // Debug.Log("waiting for audio");
yield return yieldAudio; yield return yieldAudio;
//wait for games to be loaded //wait for games to be loaded
Debug.Log("waiting for minigames"); // Debug.Log("waiting for minigames");
if (yieldGame != null) if (yieldGame != null)
yield return yieldGame; yield return yieldGame;
@ -871,7 +882,7 @@ namespace HeavenStudio
public void KillAllSounds() public void KillAllSounds()
{ {
Debug.Log("Killing all sounds"); // Debug.Log("Killing all sounds");
SoundObjects.Clear(); SoundObjects.Clear();
SoundByte.KillOneShots(); SoundByte.KillOneShots();
} }
@ -895,9 +906,10 @@ namespace HeavenStudio
allGameSwitches = EventCaller.GetAllInGameManagerList("gameManager", new string[] { "switchGame" }); allGameSwitches = EventCaller.GetAllInGameManagerList("gameManager", new string[] { "switchGame" });
preSequenceBeats = new List<double>(); preSequenceBeats = new List<double>();
string[] seekEntityDatamodel;
foreach (RiqEntity entity in Beatmap.Entities) foreach (RiqEntity entity in Beatmap.Entities)
{ {
string[] seekEntityDatamodel = entity.datamodel.Split('/'); seekEntityDatamodel = entity.datamodel.Split('/');
double seekTime = eventCaller.GetGameAction(seekEntityDatamodel[0], seekEntityDatamodel[1]).preFunctionLength; double seekTime = eventCaller.GetGameAction(seekEntityDatamodel[0], seekEntityDatamodel[1]).preFunctionLength;
preSequenceBeats.Add(entity.beat - seekTime); preSequenceBeats.Add(entity.beat - seekTime);
} }
@ -1023,12 +1035,12 @@ namespace HeavenStudio
if (allEnds.Count > 0) if (allEnds.Count > 0)
endBeat = allEnds.Select(c => c.beat).Min(); endBeat = allEnds.Select(c => c.beat).Min();
else else
endBeat = Conductor.instance.SongLengthInBeatsAsDouble(); endBeat = conductor.SongLengthInBeatsAsDouble();
} }
else else
{ {
SetGame("noGame"); SetGame("noGame");
endBeat = Conductor.instance.SongLengthInBeatsAsDouble(); endBeat = conductor.SongLengthInBeatsAsDouble();
} }
if (Beatmap.TempoChanges.Count > 0) if (Beatmap.TempoChanges.Count > 0)
@ -1112,9 +1124,9 @@ namespace HeavenStudio
} }
} }
while (beat + 0.25 > Math.Max(Conductor.instance.songPositionInBeatsAsDouble, 0)) while (beat + 0.25 > Math.Max(conductor.songPositionInBeatsAsDouble, 0))
{ {
if (!Conductor.instance.isPlaying) if (!conductor.isPlaying)
{ {
HeavenStudio.StaticCamera.instance.ToggleCanvasVisibility(true); HeavenStudio.StaticCamera.instance.ToggleCanvasVisibility(true);
SetAmbientGlowToCurrentMinigameColor(); SetAmbientGlowToCurrentMinigameColor();
@ -1141,7 +1153,7 @@ namespace HeavenStudio
this.minigame = minigame; this.minigame = minigame;
minigame.minigameName = game; minigame.minigameName = game;
minigame.gameManager = this; minigame.gameManager = this;
minigame.conductor = Conductor.instance; minigame.conductor = conductor;
} }
Vector3 originalScale = minigameObj.transform.localScale; Vector3 originalScale = minigameObj.transform.localScale;
minigameObj.transform.parent = eventCaller.GamesHolder.transform; minigameObj.transform.parent = eventCaller.GamesHolder.transform;
@ -1165,7 +1177,7 @@ namespace HeavenStudio
{ {
if (!(inf.AssetsLoaded || inf.AlreadyLoading)) if (!(inf.AssetsLoaded || inf.AlreadyLoading))
{ {
Debug.Log($"ASYNC loading assetbundles for game {game}"); // Debug.Log($"ASYNC loading assetbundles for game {game}");
inf.LoadAssetsAsync().Forget(); inf.LoadAssetsAsync().Forget();
} }
yield return new WaitUntil(() => inf.AssetsLoaded); yield return new WaitUntil(() => inf.AssetsLoaded);
@ -1300,8 +1312,8 @@ namespace HeavenStudio
private bool SongPosLessThanClipLength(float t) private bool SongPosLessThanClipLength(float t)
{ {
if (Conductor.instance.musicSource.clip != null) if (conductor.musicSource.clip != null)
return Conductor.instance.GetSongPosFromBeat(t) < Conductor.instance.musicSource.clip.length; return conductor.GetSongPosFromBeat(t) < conductor.musicSource.clip.length;
else else
return true; return true;
} }

View file

@ -41,6 +41,7 @@ namespace HeavenStudio.Games.Scripts_BlueBear
path.positions[0].height = pathToCopy.positions[0].height; path.positions[0].height = pathToCopy.positions[0].height;
path.positions[1].pos = pathToCopy.positions[1].pos; path.positions[1].pos = pathToCopy.positions[1].pos;
game.ScheduleInput(startBeat, flyBeats, isCake ? BlueBear.InputAction_Left : BlueBear.InputAction_Right, Just, Out, Out); game.ScheduleInput(startBeat, flyBeats, isCake ? BlueBear.InputAction_Left : BlueBear.InputAction_Right, Just, Out, Out);
startBeat = Conductor.instance.GetUnSwungBeat(startBeat);
Update(); Update();
} }
@ -52,7 +53,7 @@ namespace HeavenStudio.Games.Scripts_BlueBear
private void Update() private void Update()
{ {
var cond = Conductor.instance; var cond = Conductor.instance;
transform.localPosition = GetPathPositionFromBeat(path, cond.songPositionInBeatsAsDouble, startBeat); transform.localPosition = GetPathPositionFromBeat(path, cond.unswungSongPositionInBeatsAsDouble, startBeat);
float flyPos = cond.GetPositionFromBeat(startBeat, flyBeats); float flyPos = cond.GetPositionFromBeat(startBeat, flyBeats);
if (flyPos > 2f) if (flyPos > 2f)
@ -75,7 +76,7 @@ namespace HeavenStudio.Games.Scripts_BlueBear
SoundByte.PlayOneShotGame("blueBear/chompDonut"); SoundByte.PlayOneShotGame("blueBear/chompDonut");
} }
game.Bite(Conductor.instance.songPositionInBeatsAsDouble, isCake, hold); game.Bite(Conductor.instance.unswungSongPositionInBeatsAsDouble, isCake, hold);
game.EatTreat(); game.EatTreat();
SpawnCrumbs(); SpawnCrumbs();
@ -100,7 +101,7 @@ namespace HeavenStudio.Games.Scripts_BlueBear
path.positions[0].height = barelyHeight; path.positions[0].height = barelyHeight;
path.positions[0].duration = 1; path.positions[0].duration = 1;
path.positions[1].pos = new Vector3(path.positions[0].pos.x + (isCake ? -barelyDistX : barelyDistX), path.positions[0].pos.y + barelyDistY); path.positions[1].pos = new Vector3(path.positions[0].pos.x + (isCake ? -barelyDistX : barelyDistX), path.positions[0].pos.y + barelyDistY);
startBeat = Conductor.instance.songPositionInBeatsAsDouble; startBeat = Conductor.instance.unswungSongPositionInBeatsAsDouble;
Update(); Update();
return; return;
} }

View file

@ -74,11 +74,11 @@ namespace HeavenStudio.Games.Scripts_CatchyTune
{ {
if (barelyStart > 0f) if (barelyStart > 0f)
{ {
anim.DoScaledAnimation("barely", barelyStart, isPineapple ? 2f : 1f, clamp: true); anim.DoScaledAnimation("barely", Conductor.instance.GetUnSwungBeat(barelyStart), isPineapple ? 2f : 1f, clamp: true);
} }
else else
{ {
anim.DoScaledAnimation("fruit bounce", startBeat, beatLength + (isPineapple ? 4f : 2f), clamp: true); anim.DoScaledAnimation("fruit bounce", Conductor.instance.GetUnSwungBeat(startBeat), beatLength + (isPineapple ? 4f : 2f), clamp: true);
} }
} }
@ -136,7 +136,7 @@ namespace HeavenStudio.Games.Scripts_CatchyTune
if (state <= -1f || state >= 1f) if (state <= -1f || state >= 1f)
{ {
//near miss (barely) //near miss (barely)
barelyStart = Conductor.instance.songPositionInBeatsAsDouble; barelyStart = Conductor.instance.unswungSongPositionInBeatsAsDouble;
game.catchBarely(side); game.catchBarely(side);

View file

@ -131,7 +131,7 @@ namespace HeavenStudio.Games.Scripts_CropStomp
// var key2Pos = key2.Position; // var key2Pos = key2.Position;
// key2.Position = new Vector3(key2Pos.x, veggieTrans.position.y + 2f, key2Pos.z); // key2.Position = new Vector3(key2Pos.x, veggieTrans.position.y + 2f, key2Pos.z);
pickedBeat = Conductor.instance.songPositionInBeatsAsDouble; pickedBeat = Conductor.instance.unswungSongPositionInBeatsAsDouble;
SoundByte.PlayOneShot("miss"); SoundByte.PlayOneShot("miss");
@ -222,11 +222,11 @@ namespace HeavenStudio.Games.Scripts_CropStomp
veggieState = 1; veggieState = 1;
game.ScheduleInput(targetBeat, isMole ? 0.5f : 1f, CropStomp.InputAction_FlickRelease, PickJust, PickMiss, Out); game.ScheduleInput(targetBeat, isMole ? 0.5f : 1f, CropStomp.InputAction_FlickRelease, PickJust, PickMiss, Out);
targetBeat = targetBeat + (isMole ? 0.5f : 1f); targetBeat = cond.GetUnSwungBeat(targetBeat + (isMole ? 0.5f : 1f));
stompedBeat = cond.songPositionInBeatsAsDouble; stompedBeat = cond.unswungSongPositionInBeatsAsDouble;
landBeat = targetBeat + (float)cond.SecsToBeats(Minigame.NgLateTime() - 1, cond.GetBpmAtBeat(targetBeat)); landBeat = targetBeat + cond.SecsToBeats(Minigame.NgLateTime() - 1, cond.GetBpmAtBeat(targetBeat));
if (autoTriggered) if (autoTriggered)
{ {
@ -266,7 +266,7 @@ namespace HeavenStudio.Games.Scripts_CropStomp
var keyPos = key1.Position; var keyPos = key1.Position;
key1.Position = new Vector3(keyPos.x, veggieTrans.position.y, keyPos.z); key1.Position = new Vector3(keyPos.x, veggieTrans.position.y, keyPos.z);
pickedBeat = Conductor.instance.songPositionInBeatsAsDouble; pickedBeat = Conductor.instance.unswungSongPositionInBeatsAsDouble;
if (!isMole) if (!isMole)
{ {

View file

@ -25,7 +25,7 @@ namespace HeavenStudio.Games.Scripts_DoubleDate
void Update() void Update()
{ {
double beat = conductor.songPositionInBeatsAsDouble; double beat = conductor.unswungSongPositionInBeatsAsDouble;
double height = 0f; double height = 0f;
if (pathStartBeat > double.MinValue) if (pathStartBeat > double.MinValue)
{ {
@ -44,7 +44,7 @@ namespace HeavenStudio.Games.Scripts_DoubleDate
game.ScheduleInput(beat, 1f, DoubleDate.InputAction_FlickPress, Just, Miss, Empty); game.ScheduleInput(beat, 1f, DoubleDate.InputAction_FlickPress, Just, Miss, Empty);
path = game.GetPath("BasketBallIn"); path = game.GetPath("BasketBallIn");
UpdateLastRealPos(); UpdateLastRealPos();
pathStartBeat = beat - 1f; pathStartBeat = Conductor.instance.GetUnSwungBeat(beat - 1f);
Vector3 pos = GetPathPositionFromBeat(path, pathStartBeat, pathStartBeat); Vector3 pos = GetPathPositionFromBeat(path, pathStartBeat, pathStartBeat);
transform.position = pos; transform.position = pos;
@ -65,7 +65,7 @@ namespace HeavenStudio.Games.Scripts_DoubleDate
}), }),
}); });
UpdateLastRealPos(); UpdateLastRealPos();
pathStartBeat = conductor.songPositionInBeatsAsDouble; pathStartBeat = conductor.unswungSongPositionInBeatsAsDouble;
if (state >= 1f || state <= -1f) if (state >= 1f || state <= -1f)
{ {
path = game.GetPath("BasketBallNg" + (state > 0 ? "Late" : "Early")); path = game.GetPath("BasketBallNg" + (state > 0 ? "Late" : "Early"));
@ -80,7 +80,7 @@ namespace HeavenStudio.Games.Scripts_DoubleDate
void Hit() void Hit()
{ {
UpdateLastRealPos(); UpdateLastRealPos();
pathStartBeat = conductor.songPositionInBeatsAsDouble; pathStartBeat = conductor.unswungSongPositionInBeatsAsDouble;
path = game.GetPath("BasketBallJust"); path = game.GetPath("BasketBallJust");
game.Kick(true, false, true, _jump); game.Kick(true, false, true, _jump);
SoundByte.PlayOneShotGame("doubleDate/kick"); SoundByte.PlayOneShotGame("doubleDate/kick");

View file

@ -262,8 +262,7 @@ namespace HeavenStudio.Games
void Update() void Update()
{ {
var cond = Conductor.instance; if (conductor.isPlaying && !conductor.isPaused)
if (cond.isPlaying && !cond.isPaused)
{ {
if (queuedBalls.Count != 0) if (queuedBalls.Count != 0)
{ {
@ -287,7 +286,7 @@ namespace HeavenStudio.Games
} }
else else
{ {
if ((!cond.isPaused) && queuedBalls.Count != 0) if ((!conductor.isPaused) && queuedBalls.Count != 0)
{ {
queuedBalls.Clear(); queuedBalls.Clear();
} }
@ -353,7 +352,7 @@ namespace HeavenStudio.Games
{ {
boyAnim.DoScaledAnimationAsync(_isStaring ? "IdleBop2" : "IdleBop", _animSpeed); boyAnim.DoScaledAnimationAsync(_isStaring ? "IdleBop2" : "IdleBop", _animSpeed);
} }
if (Conductor.instance.songPositionInBeatsAsDouble > lastGirlGacha) if (conductor.songPositionInBeatsAsDouble > lastGirlGacha)
girlAnim.DoScaledAnimationAsync("GirlBop", _animSpeed); girlAnim.DoScaledAnimationAsync("GirlBop", _animSpeed);
weasels.Bop(); weasels.Bop();
} }
@ -366,7 +365,7 @@ namespace HeavenStudio.Games
if (jump) if (jump)
{ {
weasels.Jump(); weasels.Jump();
lastGirlGacha = Conductor.instance.songPositionInBeatsAsDouble + 0.5f; lastGirlGacha = conductor.songPositionInBeatsAsDouble + 0.5f;
girlAnim.DoScaledAnimationAsync("GirlLookUp", _animSpeed); girlAnim.DoScaledAnimationAsync("GirlLookUp", _animSpeed);
} }
else if (weaselsHappy) weasels.Happy(); else if (weaselsHappy) weasels.Happy();
@ -374,7 +373,7 @@ namespace HeavenStudio.Games
{ {
BeatAction.New(instance, new List<BeatAction.Action>() BeatAction.New(instance, new List<BeatAction.Action>()
{ {
new BeatAction.Action(Conductor.instance.songPositionInBeatsAsDouble + 1f, delegate new BeatAction.Action(conductor.songPositionInBeatsAsDouble + 1f, delegate
{ {
leaves.Play(); leaves.Play();
treeAnim.DoScaledAnimationAsync("TreeRustle", _animSpeed); treeAnim.DoScaledAnimationAsync("TreeRustle", _animSpeed);
@ -471,11 +470,11 @@ namespace HeavenStudio.Games
public void MissKick(double beat, bool hit = false) public void MissKick(double beat, bool hit = false)
{ {
lastGirlGacha = Conductor.instance.songPositionInBeatsAsDouble + 1.5f; lastGirlGacha = conductor.songPositionInBeatsAsDouble + 1.5f;
girlAnim.DoScaledAnimationAsync("GirlSad", _animSpeed); girlAnim.DoScaledAnimationAsync("GirlSad", _animSpeed);
if (hit) if (hit)
{ {
lastHitWeasel = Conductor.instance.songPositionInBeatsAsDouble; lastHitWeasel = conductor.songPositionInBeatsAsDouble;
BeatAction.New(this, new List<BeatAction.Action>() BeatAction.New(this, new List<BeatAction.Action>()
{ {
new BeatAction.Action(beat - (0.25f/3f), delegate { weasels.Hit(beat); }), new BeatAction.Action(beat - (0.25f/3f), delegate { weasels.Hit(beat); }),
@ -483,7 +482,7 @@ namespace HeavenStudio.Games
} }
else else
{ {
lastHitWeasel = Conductor.instance.songPositionInBeatsAsDouble; lastHitWeasel = conductor.songPositionInBeatsAsDouble;
BeatAction.New(this, new List<BeatAction.Action>() BeatAction.New(this, new List<BeatAction.Action>()
{ {
new BeatAction.Action(beat + 0.25, delegate { weasels.Hide(beat + 0.25f); }), new BeatAction.Action(beat + 0.25, delegate { weasels.Hide(beat + 0.25f); }),

View file

@ -25,7 +25,7 @@ namespace HeavenStudio.Games.Scripts_DoubleDate
void Update() void Update()
{ {
double beat = conductor.songPositionInBeatsAsDouble; double beat = conductor.unswungSongPositionInBeatsAsDouble;
double height = 0f; double height = 0f;
if (pathStartBeat > double.MinValue) if (pathStartBeat > double.MinValue)
{ {
@ -44,7 +44,7 @@ namespace HeavenStudio.Games.Scripts_DoubleDate
game.ScheduleInput(beat, 1.5f, DoubleDate.InputAction_FlickPress, Just, Miss, Empty); game.ScheduleInput(beat, 1.5f, DoubleDate.InputAction_FlickPress, Just, Miss, Empty);
path = game.GetPath("FootBallInNoHit"); // there's a second path for footballs that hit the weasels, use that if the weasels haven't been hit recently path = game.GetPath("FootBallInNoHit"); // there's a second path for footballs that hit the weasels, use that if the weasels haven't been hit recently
UpdateLastRealPos(); UpdateLastRealPos();
pathStartBeat = beat - 1f; pathStartBeat = Conductor.instance.GetUnSwungBeat(beat - 1f);
Vector3 pos = GetPathPositionFromBeat(path, pathStartBeat, pathStartBeat); Vector3 pos = GetPathPositionFromBeat(path, pathStartBeat, pathStartBeat);
transform.position = pos; transform.position = pos;
@ -60,7 +60,7 @@ namespace HeavenStudio.Games.Scripts_DoubleDate
if (state >= 1f || state <= -1f) if (state >= 1f || state <= -1f)
{ {
UpdateLastRealPos(); UpdateLastRealPos();
pathStartBeat = conductor.songPositionInBeatsAsDouble; pathStartBeat = conductor.unswungSongPositionInBeatsAsDouble;
path = game.GetPath("FootBallNg" + (state > 0 ? "Late" : "Early")); path = game.GetPath("FootBallNg" + (state > 0 ? "Late" : "Early"));
SoundByte.PlayOneShot("miss"); SoundByte.PlayOneShot("miss");
game.Kick(false); game.Kick(false);
@ -84,7 +84,7 @@ namespace HeavenStudio.Games.Scripts_DoubleDate
transform.localScale *= 0.25f; transform.localScale *= 0.25f;
path = game.GetPath("FootBallFall"); path = game.GetPath("FootBallFall");
UpdateLastRealPos(); UpdateLastRealPos();
pathStartBeat = conductor.songPositionInBeatsAsDouble + 1f; pathStartBeat = conductor.unswungSongPositionInBeatsAsDouble + 1f;
}), }),
new BeatAction.Action(conductor.songPositionInBeatsAsDouble + 12f, delegate new BeatAction.Action(conductor.songPositionInBeatsAsDouble + 12f, delegate
{ {
@ -96,7 +96,7 @@ namespace HeavenStudio.Games.Scripts_DoubleDate
void Hit() void Hit()
{ {
UpdateLastRealPos(); UpdateLastRealPos();
pathStartBeat = conductor.songPositionInBeatsAsDouble; pathStartBeat = conductor.unswungSongPositionInBeatsAsDouble;
path = game.GetPath("FootBallJust"); path = game.GetPath("FootBallJust");
game.Kick(true, true, jump: _jump); game.Kick(true, true, jump: _jump);
SoundByte.PlayOneShotGame("doubleDate/footballKick"); SoundByte.PlayOneShotGame("doubleDate/footballKick");

View file

@ -25,7 +25,7 @@ namespace HeavenStudio.Games.Scripts_DoubleDate
void Update() void Update()
{ {
double beat = conductor.songPositionInBeatsAsDouble; double beat = conductor.unswungSongPositionInBeatsAsDouble;
double height = 0f; double height = 0f;
if (pathStartBeat > double.MinValue) if (pathStartBeat > double.MinValue)
{ {
@ -44,7 +44,7 @@ namespace HeavenStudio.Games.Scripts_DoubleDate
game.ScheduleInput(beat, 1f, DoubleDate.InputAction_FlickPress, Just, Miss, Empty); game.ScheduleInput(beat, 1f, DoubleDate.InputAction_FlickPress, Just, Miss, Empty);
path = game.GetPath("SoccerIn"); path = game.GetPath("SoccerIn");
UpdateLastRealPos(); UpdateLastRealPos();
pathStartBeat = beat - 1f; pathStartBeat = Conductor.instance.GetUnSwungBeat(beat - 1f);
Vector3 pos = GetPathPositionFromBeat(path, pathStartBeat, pathStartBeat); Vector3 pos = GetPathPositionFromBeat(path, pathStartBeat, pathStartBeat);
transform.position = pos; transform.position = pos;
@ -65,7 +65,7 @@ namespace HeavenStudio.Games.Scripts_DoubleDate
}), }),
}); });
UpdateLastRealPos(); UpdateLastRealPos();
pathStartBeat = conductor.songPositionInBeatsAsDouble; pathStartBeat = conductor.unswungSongPositionInBeatsAsDouble;
if (state >= 1f || state <= -1f) if (state >= 1f || state <= -1f)
{ {
path = game.GetPath("SoccerNg" + (state > 0 ? "Late" : "Early")); path = game.GetPath("SoccerNg" + (state > 0 ? "Late" : "Early"));
@ -80,7 +80,7 @@ namespace HeavenStudio.Games.Scripts_DoubleDate
void Hit() void Hit()
{ {
UpdateLastRealPos(); UpdateLastRealPos();
pathStartBeat = conductor.songPositionInBeatsAsDouble; pathStartBeat = conductor.unswungSongPositionInBeatsAsDouble;
path = game.GetPath("SoccerJust"); path = game.GetPath("SoccerJust");
game.Kick(true, false, true, _jump); game.Kick(true, false, true, _jump);
SoundByte.PlayOneShotGame("doubleDate/kick"); SoundByte.PlayOneShotGame("doubleDate/kick");

View file

@ -390,7 +390,7 @@ namespace HeavenStudio.Games
//idol jumping physics //idol jumping physics
float jumpPos = conductor.GetPositionFromBeat(idolJumpStartTime, 1f); float jumpPos = conductor.GetPositionFromBeat(idolJumpStartTime, 1f);
float IDOL_SHADOW_SCALE = 1.18f; float IDOL_SHADOW_SCALE = 1.18f;
if (conductor.songPositionInBeatsAsDouble >= idolJumpStartTime && conductor.songPositionInBeatsAsDouble < idolJumpStartTime + 1f) if (conductor.unswungSongPositionInBeatsAsDouble >= idolJumpStartTime && conductor.unswungSongPositionInBeatsAsDouble < idolJumpStartTime + 1f)
{ {
hasJumped = true; hasJumped = true;
float yMul = jumpPos * 2f - 1f; float yMul = jumpPos * 2f - 1f;
@ -580,7 +580,7 @@ namespace HeavenStudio.Games
{ {
DisableBop(beat, length); DisableBop(beat, length);
DisableResponse(beat, length); DisableResponse(beat, length);
idolJumpStartTime = beat; idolJumpStartTime = conductor.GetUnSwungBeat(beat);
//play anim //play anim
BeatAction.New(instance, new List<BeatAction.Action>() BeatAction.New(instance, new List<BeatAction.Action>()

View file

@ -69,7 +69,7 @@ namespace HeavenStudio.Games.Scripts_FanClub
//idol jumping physics //idol jumping physics
float jumpPos = cond.GetPositionFromBeat(startJumpTime, 1f); float jumpPos = cond.GetPositionFromBeat(startJumpTime, 1f);
float IDOL_SHADOW_SCALE = 1.18f; float IDOL_SHADOW_SCALE = 1.18f;
if (cond.songPositionInBeatsAsDouble >= startJumpTime && cond.songPositionInBeatsAsDouble < startJumpTime + 1f) if (cond.unswungSongPositionInBeatsAsDouble >= startJumpTime && cond.unswungSongPositionInBeatsAsDouble < startJumpTime + 1f)
{ {
hasJumped = true; hasJumped = true;
float yMul = jumpPos * 2f - 1f; float yMul = jumpPos * 2f - 1f;
@ -77,7 +77,7 @@ namespace HeavenStudio.Games.Scripts_FanClub
rootTransform.transform.localPosition = new Vector3(startPostion + stepDistance * AnimCount, rootYPos + (2f * yWeight + 0.25f)); rootTransform.transform.localPosition = new Vector3(startPostion + stepDistance * AnimCount, rootYPos + (2f * yWeight + 0.25f));
shadow.transform.localScale = new Vector3((1f-yWeight*0.8f) * IDOL_SHADOW_SCALE, (1f-yWeight*0.8f) * IDOL_SHADOW_SCALE, 1f); shadow.transform.localScale = new Vector3((1f-yWeight*0.8f) * IDOL_SHADOW_SCALE, (1f-yWeight*0.8f) * IDOL_SHADOW_SCALE, 1f);
anim.DoScaledAnimation("Jump", startJumpTime, 1f); anim.DoScaledAnimation("Jump", startJumpTime, 1f, ignoreSwing: true);
} }
else else
{ {
@ -160,12 +160,11 @@ namespace HeavenStudio.Games.Scripts_FanClub
{ {
if (startStepBeat != double.MaxValue) return; if (startStepBeat != double.MaxValue) return;
if (!gameObject.activeInHierarchy) return; if (!gameObject.activeInHierarchy) return;
startJumpTime = beat; startJumpTime = Conductor.instance.GetUnSwungBeat(beat);
//play anim //play anim
BeatAction.New(this, new List<BeatAction.Action>() BeatAction.New(this, new List<BeatAction.Action>()
{ {
// new BeatAction.Action(beat, delegate { anim.Play("Jump", -1, 0); }),
new BeatAction.Action(beat + 1f, delegate { anim.Play("Land", -1, 0); }), new BeatAction.Action(beat + 1f, delegate { anim.Play("Land", -1, 0); }),
}); });
} }

View file

@ -116,7 +116,7 @@ namespace HeavenStudio.Games.Scripts_FanClub
} }
if (PlayerInput.GetIsAction(FanClub.InputAction_BasicPressing)) if (PlayerInput.GetIsAction(FanClub.InputAction_BasicPressing))
{ {
if (clappingStartTime != double.MinValue && cond.songPositionInBeatsAsDouble > clappingStartTime + 2f && !stopCharge) if (clappingStartTime != double.MinValue && cond.unswungSongPositionInBeatsAsDouble > clappingStartTime + 2f && !stopCharge)
{ {
animator.speed = 1f; animator.speed = 1f;
animator.Play("FanClapCharge", -1, 0); animator.Play("FanClapCharge", -1, 0);
@ -131,7 +131,7 @@ namespace HeavenStudio.Games.Scripts_FanClub
nonTouchDelay = 0f; nonTouchDelay = 0f;
stopCharge = true; stopCharge = true;
} }
if (clappingStartTime != double.MinValue && cond.songPositionInBeatsAsDouble > clappingStartTime + nonTouchDelay && stopCharge if (clappingStartTime != double.MinValue && cond.unswungSongPositionInBeatsAsDouble > clappingStartTime + nonTouchDelay && stopCharge
&& !FanClub.instance.IsExpectingInputNow(FanClub.InputAction_FlickRelease.inputLockCategory)) && !FanClub.instance.IsExpectingInputNow(FanClub.InputAction_FlickRelease.inputLockCategory))
{ {
if (FanClub.instance.JudgementPaused) if (FanClub.instance.JudgementPaused)
@ -163,14 +163,14 @@ namespace HeavenStudio.Games.Scripts_FanClub
} }
float jumpPos = cond.GetPositionFromBeat(jumpStartTime, 1f); float jumpPos = cond.GetPositionFromBeat(jumpStartTime, 1f);
if (cond.songPositionInBeatsAsDouble >= jumpStartTime && cond.songPositionInBeatsAsDouble < jumpStartTime + 1f) if (cond.unswungSongPositionInBeatsAsDouble >= jumpStartTime && cond.unswungSongPositionInBeatsAsDouble < jumpStartTime + 1f)
{ {
hasJumped = true; hasJumped = true;
float yMul = jumpPos * 2f - 1f; float yMul = jumpPos * 2f - 1f;
float yWeight = -(yMul*yMul) + 1f; float yWeight = -(yMul*yMul) + 1f;
motionRoot.transform.localPosition = new Vector3(0, 3f * yWeight); motionRoot.transform.localPosition = new Vector3(0, 3f * yWeight);
shadow.transform.localScale = new Vector3((1f-yWeight*0.8f) * 1.4f, (1f-yWeight*0.8f) * 1.4f, 1f); shadow.transform.localScale = new Vector3((1f-yWeight*0.8f) * 1.4f, (1f-yWeight*0.8f) * 1.4f, 1f);
animator.DoScaledAnimation("FanJump", jumpStartTime); animator.DoScaledAnimation("FanJump", jumpStartTime, ignoreSwing: true);
} }
else else
{ {
@ -205,12 +205,12 @@ namespace HeavenStudio.Games.Scripts_FanClub
var cond = Conductor.instance; var cond = Conductor.instance;
hasJumped = false; hasJumped = false;
stopBeat = true; stopBeat = true;
jumpStartTime = -99f; jumpStartTime = double.MinValue;
animator.speed = 1f; animator.speed = 1f;
animator.Play("FanClap", -1, 0); animator.Play("FanClap", -1, 0);
SoundByte.PlayOneShotGame("fanClub/play_clap"); SoundByte.PlayOneShotGame("fanClub/play_clap");
SoundByte.PlayOneShotGame("fanClub/crap_impact"); SoundByte.PlayOneShotGame("fanClub/crap_impact");
clappingStartTime = cond.songPositionInBeatsAsDouble; clappingStartTime = cond.unswungSongPositionInBeatsAsDouble;
if (doCharge) if (doCharge)
BeatAction.New(this, new List<BeatAction.Action>() BeatAction.New(this, new List<BeatAction.Action>()
@ -247,7 +247,7 @@ namespace HeavenStudio.Games.Scripts_FanClub
var cond = Conductor.instance; var cond = Conductor.instance;
animator.Play("FanJump", -1, 0); animator.Play("FanJump", -1, 0);
SoundByte.PlayOneShotGame("fanClub/play_jump"); SoundByte.PlayOneShotGame("fanClub/play_jump");
jumpStartTime = cond.songPositionInBeatsAsDouble; jumpStartTime = cond.unswungSongPositionInBeatsAsDouble;
clappingStartTime = double.MinValue; clappingStartTime = double.MinValue;
stopCharge = false; stopCharge = false;
} }
@ -255,7 +255,7 @@ namespace HeavenStudio.Games.Scripts_FanClub
public bool IsJumping() public bool IsJumping()
{ {
var cond = Conductor.instance; var cond = Conductor.instance;
return (cond.songPositionInBeatsAsDouble >= jumpStartTime && cond.songPositionInBeatsAsDouble < jumpStartTime + 1f); return (cond.unswungSongPositionInBeatsAsDouble >= jumpStartTime && cond.unswungSongPositionInBeatsAsDouble < jumpStartTime + 1f);
} }
public void Bop() public void Bop()

View file

@ -7,6 +7,7 @@ using HeavenStudio.Util;
using HeavenStudio.InputSystem; using HeavenStudio.InputSystem;
using Jukebox; using Jukebox;
using System.Linq; using System.Linq;
using BurstLinq;
namespace HeavenStudio.Games.Loaders namespace HeavenStudio.Games.Loaders
{ {

View file

@ -38,7 +38,7 @@ namespace HeavenStudio.Games.Scripts_KarateMan
public bool KickBarrelContent = false; public bool KickBarrelContent = false;
public bool ShouldGlow = false; public bool ShouldGlow = false;
public int OnHitExpression = (int) KarateMan.KarateManFaces.Normal; public int OnHitExpression = (int)KarateMan.KarateManFaces.Normal;
public int comboId = -1; public int comboId = -1;
static int _lastCombo = -1; static int _lastCombo = -1;
@ -266,8 +266,6 @@ namespace HeavenStudio.Games.Scripts_KarateMan
transform.rotation = Quaternion.Euler(0, 0, transform.rotation.eulerAngles.z + (-360f * Time.deltaTime) + UnityEngine.Random.Range(0f, 360f)); transform.rotation = Quaternion.Euler(0, 0, transform.rotation.eulerAngles.z + (-360f * Time.deltaTime) + UnityEngine.Random.Range(0f, 360f));
ShadowInstance = Instantiate(Shadow, KarateMan.instance.ItemHolder); ShadowInstance = Instantiate(Shadow, KarateMan.instance.ItemHolder);
shadowRenderer = ShadowInstance.GetComponent<SpriteRenderer>(); shadowRenderer = ShadowInstance.GetComponent<SpriteRenderer>();
shadowRenderer.color = KarateMan.instance.Joe.Shadows[0].color; shadowRenderer.color = KarateMan.instance.Joe.Shadows[0].color;
@ -585,9 +583,12 @@ namespace HeavenStudio.Games.Scripts_KarateMan
p.Play(); p.Play();
break; break;
case ItemType.KickBarrel: case ItemType.KickBarrel:
if (KickBarrelContent) { if (KickBarrelContent)
{
game.CreateItemInstance(startBeat + 1f, "Item03", OnHitExpression, ItemType.KickBall); game.CreateItemInstance(startBeat + 1f, "Item03", OnHitExpression, ItemType.KickBall);
} else { }
else
{
if (ShouldGlow) game.Joe.ApplyBombGlow(); if (ShouldGlow) game.Joe.ApplyBombGlow();
game.CreateItemInstance(startBeat + 1f, "Item04", OnHitExpression, ItemType.KickBomb); game.CreateItemInstance(startBeat + 1f, "Item04", OnHitExpression, ItemType.KickBomb);
} }
@ -629,7 +630,7 @@ namespace HeavenStudio.Games.Scripts_KarateMan
break; break;
} }
startBeat = Conductor.instance.songPositionInBeatsAsDouble; startBeat = Conductor.instance.unswungSongPositionInBeatsAsDouble;
status = FlyStatus.Hit; status = FlyStatus.Hit;
} }
@ -765,7 +766,7 @@ namespace HeavenStudio.Games.Scripts_KarateMan
if (state <= -1f || state >= 1f) if (state <= -1f || state >= 1f)
{ {
bool straight = joe.Punch(ItemPunchHand(), false, ItemPunchHand() == 2); bool straight = joe.Punch(ItemPunchHand(), false, ItemPunchHand() == 2);
startBeat = Conductor.instance.songPositionInBeatsAsDouble; startBeat = Conductor.instance.unswungSongPositionInBeatsAsDouble;
CurrentCurve = ItemCurves[6]; CurrentCurve = ItemCurves[6];
curveTargetBeat = 1f; curveTargetBeat = 1f;
SoundByte.PlayOneShot("miss"); SoundByte.PlayOneShot("miss");
@ -788,7 +789,7 @@ namespace HeavenStudio.Games.Scripts_KarateMan
if (ItemNeedNori() && KarateMan.instance.NoriPerformance < 0.6f) if (ItemNeedNori() && KarateMan.instance.NoriPerformance < 0.6f)
{ {
CreateHitMark(false); CreateHitMark(false);
startBeat = Conductor.instance.songPositionInBeatsAsDouble; startBeat = Conductor.instance.unswungSongPositionInBeatsAsDouble;
status = FlyStatus.HitWeak; status = FlyStatus.HitWeak;
SoundByte.PlayOneShotGame("karateman/hitNoNori", forcePlay: true); SoundByte.PlayOneShotGame("karateman/hitNoNori", forcePlay: true);
joe.Punch(3); joe.Punch(3);
@ -815,7 +816,7 @@ namespace HeavenStudio.Games.Scripts_KarateMan
joe.ForceFailCombo(Conductor.instance.songPositionInBeatsAsDouble); joe.ForceFailCombo(Conductor.instance.songPositionInBeatsAsDouble);
if (state <= -1f || state >= 1f) if (state <= -1f || state >= 1f)
{ {
startBeat = Conductor.instance.songPositionInBeatsAsDouble; startBeat = Conductor.instance.unswungSongPositionInBeatsAsDouble;
CurrentCurve = ItemCurves[6]; CurrentCurve = ItemCurves[6];
curveTargetBeat = 1f; curveTargetBeat = 1f;
SoundByte.PlayOneShot("miss"); SoundByte.PlayOneShot("miss");
@ -885,7 +886,7 @@ namespace HeavenStudio.Games.Scripts_KarateMan
joe.SetShouldComboId(comboId); joe.SetShouldComboId(comboId);
if (state <= -1f || state >= 1f) if (state <= -1f || state >= 1f)
{ {
startBeat = Conductor.instance.songPositionInBeatsAsDouble; startBeat = Conductor.instance.unswungSongPositionInBeatsAsDouble;
CurrentCurve = ItemCurves[6]; CurrentCurve = ItemCurves[6];
curveTargetBeat = 1f; curveTargetBeat = 1f;
SoundByte.PlayOneShot("miss"); SoundByte.PlayOneShot("miss");
@ -932,7 +933,7 @@ namespace HeavenStudio.Games.Scripts_KarateMan
bool straight = joe.Punch(ItemPunchHand()); bool straight = joe.Punch(ItemPunchHand());
if (state <= -1f || state >= 1f) if (state <= -1f || state >= 1f)
{ {
startBeat = Conductor.instance.songPositionInBeatsAsDouble; startBeat = Conductor.instance.unswungSongPositionInBeatsAsDouble;
CurrentCurve = ItemCurves[6]; CurrentCurve = ItemCurves[6];
curveTargetBeat = 1f; curveTargetBeat = 1f;
SoundByte.PlayOneShot("miss"); SoundByte.PlayOneShot("miss");
@ -962,7 +963,7 @@ namespace HeavenStudio.Games.Scripts_KarateMan
joe.ComboSequence(3); joe.ComboSequence(3);
if (state <= -1f || state >= 1f) if (state <= -1f || state >= 1f)
{ {
startBeat = Conductor.instance.songPositionInBeatsAsDouble; startBeat = Conductor.instance.unswungSongPositionInBeatsAsDouble;
CurrentCurve = ItemCurves[5]; CurrentCurve = ItemCurves[5];
curveTargetBeat = 1f; curveTargetBeat = 1f;
SoundByte.PlayOneShot("miss"); SoundByte.PlayOneShot("miss");
@ -1052,7 +1053,7 @@ namespace HeavenStudio.Games.Scripts_KarateMan
joe.Punch(ItemPunchHand()); joe.Punch(ItemPunchHand());
if (state <= -1f || state >= 1f) if (state <= -1f || state >= 1f)
{ {
startBeat = Conductor.instance.songPositionInBeatsAsDouble; startBeat = Conductor.instance.unswungSongPositionInBeatsAsDouble;
CurrentCurve = ItemCurves[6]; CurrentCurve = ItemCurves[6];
curveTargetBeat = 1f; curveTargetBeat = 1f;
SoundByte.PlayOneShot("miss"); SoundByte.PlayOneShot("miss");
@ -1114,7 +1115,7 @@ namespace HeavenStudio.Games.Scripts_KarateMan
joe.Kick(Conductor.instance.songPositionInBeatsAsDouble); joe.Kick(Conductor.instance.songPositionInBeatsAsDouble);
if (state <= -1f || state >= 1f) if (state <= -1f || state >= 1f)
{ {
startBeat = Conductor.instance.songPositionInBeatsAsDouble; startBeat = Conductor.instance.unswungSongPositionInBeatsAsDouble;
CurrentCurve = ItemCurves[8]; CurrentCurve = ItemCurves[8];
curveTargetBeat = 1f; curveTargetBeat = 1f;
SoundByte.PlayOneShot("miss"); SoundByte.PlayOneShot("miss");
@ -1140,7 +1141,7 @@ namespace HeavenStudio.Games.Scripts_KarateMan
ItemHitEffect(); ItemHitEffect();
status = FlyStatus.Hit; status = FlyStatus.Hit;
CurrentCurve = ItemCurves[7]; CurrentCurve = ItemCurves[7];
startBeat = Conductor.instance.songPositionInBeatsAsDouble; startBeat = Conductor.instance.unswungSongPositionInBeatsAsDouble;
curveTargetBeat = 3f; curveTargetBeat = 3f;
KarateMan.instance.Nori.DoHit(); KarateMan.instance.Nori.DoHit();
} }

View file

@ -7,6 +7,7 @@ using HeavenStudio.Common;
using HeavenStudio.InputSystem; using HeavenStudio.InputSystem;
using System; using System;
using System.Linq; using System.Linq;
using BurstLinq;
using Jukebox; using Jukebox;
namespace HeavenStudio.Games namespace HeavenStudio.Games

View file

@ -168,6 +168,8 @@ namespace HeavenStudio.Games
public int stepIterate = 0; public int stepIterate = 0;
private static double startSteppingBeat = double.MaxValue; private static double startSteppingBeat = double.MaxValue;
private static double startBlippingBeat = double.MaxValue; private static double startBlippingBeat = double.MaxValue;
private string currentMetronomeDir = "Right";
private static double metronomeBeat = double.MaxValue;
private bool stopStepping; private bool stopStepping;
public bool stopBlipping; public bool stopBlipping;
@ -241,6 +243,16 @@ namespace HeavenStudio.Games
man.RecursiveBlipping(startBlippingBeat); man.RecursiveBlipping(startBlippingBeat);
startBlippingBeat = double.MaxValue; startBlippingBeat = double.MaxValue;
} }
if (songPos > metronomeBeat + 1)
{
metronomeAnim.Play("MetronomeGo" + currentMetronomeDir, -1, 1);
metronomeAnim.speed = 0;
}
else if (songPos >= metronomeBeat)
{
metronomeAnim.DoScaledAnimation("MetronomeGo" + currentMetronomeDir, metronomeBeat, 1, ignoreSwing: false);
}
} }
} }
@ -291,9 +303,9 @@ namespace HeavenStudio.Games
stopStepping = false; stopStepping = false;
return; return;
} }
string dir = (stepIterate % 2 == 1) ? "Right" : "Left"; currentMetronomeDir = (stepIterate % 2 == 1) ? "Right" : "Left";
metronomeAnim.DoScaledAnimationAsync("MetronomeGo" + dir, 0.5f); SoundByte.PlayOneShotGame($"mrUpbeat/metronome{currentMetronomeDir}");
SoundByte.PlayOneShotGame("mrUpbeat/metronome" + dir); metronomeBeat = beat;
ScheduleStep(beat); ScheduleStep(beat);
BeatAction.New(this, new List<BeatAction.Action>() { BeatAction.New(this, new List<BeatAction.Action>() {
new(beat + 1, delegate { RecursiveStepping(beat + 1); }) new(beat + 1, delegate { RecursiveStepping(beat + 1); })
@ -306,11 +318,11 @@ namespace HeavenStudio.Games
var actions = new List<BeatAction.Action>(); var actions = new List<BeatAction.Action>();
for (int i = 0; i < length; i++) for (int i = 0; i < length; i++)
{ {
ScheduleStep(beat); ScheduleStep(beat + i);
actions.Add(new BeatAction.Action(beat + i, delegate { actions.Add(new BeatAction.Action(beat + i, delegate {
string dir = (stepIterate % 2 == 1) ? "Right" : "Left"; currentMetronomeDir = (stepIterate % 2 == 1) ? "Right" : "Left";
metronomeAnim.DoScaledAnimationAsync("MetronomeGo" + dir, 0.5f); SoundByte.PlayOneShotGame($"mrUpbeat/metronome{currentMetronomeDir}");
SoundByte.PlayOneShotGame("mrUpbeat/metronome" + dir); metronomeBeat = beat + i;
stepIterate++; stepIterate++;
})); }));
} }

View file

@ -123,7 +123,7 @@ namespace HeavenStudio.Games.Scripts_PajamaParty
public void Jump(double beat, int alt = 1) public void Jump(double beat, int alt = 1)
{ {
startJumpTime = beat; startJumpTime = Conductor.instance.GetUnSwungBeat(beat);
jumpAlt = 0; jumpAlt = 0;
if (alt > 1) if (alt > 1)
{ {
@ -140,7 +140,7 @@ namespace HeavenStudio.Games.Scripts_PajamaParty
public void Throw(double beat, bool highCheck) public void Throw(double beat, bool highCheck)
{ {
anim.DoUnscaledAnimation("MonkeyThrow" + animSuffix); anim.DoUnscaledAnimation("MonkeyThrow" + animSuffix);
startThrowTime = beat; startThrowTime = Conductor.instance.GetUnSwungBeat(beat);
Projectile.SetActive(true); Projectile.SetActive(true);
if (highCheck) if (highCheck)

View file

@ -182,7 +182,7 @@ namespace HeavenStudio.Games.Scripts_PajamaParty
{ {
throwType = false; throwType = false;
throwLength = 0.5; throwLength = 0.5;
Projectile.GetComponent<Animator>().DoScaledAnimation("ThrowOut", startThrowTime, throwLength); Projectile.GetComponent<Animator>().DoScaledAnimation("ThrowOut", Conductor.instance.GetUnSwungBeat(startThrowTime), throwLength);
Projectile.transform.rotation = Quaternion.Euler(0, 0, 360f * UnityEngine.Random.Range(0f, 1f)); Projectile.transform.rotation = Quaternion.Euler(0, 0, 360f * UnityEngine.Random.Range(0f, 1f));
} }
else else
@ -310,12 +310,12 @@ namespace HeavenStudio.Games.Scripts_PajamaParty
if (state <= -1f || state >= 1f) if (state <= -1f || state >= 1f)
{ {
SoundByte.PlayOneShot("miss"); SoundByte.PlayOneShot("miss");
PlayerJump(cond.songPositionInBeatsAsDouble, false, true); PlayerJump(cond.unswungSongPositionInBeatsAsDouble, false, true);
} }
else else
{ {
SoundByte.PlayOneShotGame("pajamaParty/jumpJust"); SoundByte.PlayOneShotGame("pajamaParty/jumpJust");
PlayerJump(cond.songPositionInBeatsAsDouble, false, false); PlayerJump(cond.unswungSongPositionInBeatsAsDouble, false, false);
} }
caller.CanHit(false); caller.CanHit(false);
} }
@ -328,7 +328,7 @@ namespace HeavenStudio.Games.Scripts_PajamaParty
if (canJump) if (canJump)
{ {
var cond = Conductor.instance; var cond = Conductor.instance;
PlayerThrough(cond.songPositionInBeatsAsDouble); PlayerThrough(cond.unswungSongPositionInBeatsAsDouble);
} }
} }
////// //////
@ -359,12 +359,12 @@ namespace HeavenStudio.Games.Scripts_PajamaParty
{ {
SoundByte.PlayOneShot("miss"); SoundByte.PlayOneShot("miss");
throwNg = true; throwNg = true;
EndCharge(cond.songPositionInBeatsAsDouble, true, throwNg); EndCharge(cond.unswungSongPositionInBeatsAsDouble, true, throwNg);
} }
else else
{ {
SoundByte.PlayOneShotGame("pajamaParty/throw5"); SoundByte.PlayOneShotGame("pajamaParty/throw5");
EndCharge(cond.songPositionInBeatsAsDouble, true, throwNg); EndCharge(cond.unswungSongPositionInBeatsAsDouble, true, throwNg);
} }
caller.CanHit(false); caller.CanHit(false);
} }
@ -374,7 +374,7 @@ namespace HeavenStudio.Games.Scripts_PajamaParty
if (canCharge) if (canCharge)
{ {
var cond = Conductor.instance; var cond = Conductor.instance;
PlayerThrough(cond.songPositionInBeatsAsDouble); PlayerThrough(cond.unswungSongPositionInBeatsAsDouble);
} }
} }
// //

View file

@ -149,6 +149,7 @@ namespace HeavenStudio.Games
private bool CheckEventLock() private bool CheckEventLock()
{ {
Conductor cond = Conductor.instance;
foreach (PlayerActionEvent toCompare in allEvents) foreach (PlayerActionEvent toCompare in allEvents)
{ {
if (toCompare == this) continue; if (toCompare == this) continue;
@ -161,9 +162,9 @@ namespace HeavenStudio.Games
&& toCompare.InputAction.inputLockCategory[catIdx] != InputAction.inputLockCategory[catIdx]) continue; && toCompare.InputAction.inputLockCategory[catIdx] != InputAction.inputLockCategory[catIdx]) continue;
} }
double t1 = this.startBeat + this.timer; double t1 = cond.GetUnSwungBeat(this.startBeat + this.timer);
double t2 = toCompare.startBeat + toCompare.timer; double t2 = cond.GetUnSwungBeat(toCompare.startBeat + toCompare.timer);
double songPos = Conductor.instance.songPositionInBeatsAsDouble; double songPos = cond.unswungSongPositionInBeatsAsDouble;
// compare distance between current time and the events // compare distance between current time and the events
// events that happen at the exact same time with the exact same inputs will return true // events that happen at the exact same time with the exact same inputs will return true
@ -213,7 +214,7 @@ namespace HeavenStudio.Games
{ {
var cond = Conductor.instance; var cond = Conductor.instance;
double currTime = cond.songPositionAsDouble; double currTime = cond.songPositionAsDouble;
double targetTime = cond.GetSongPosFromBeat(startBeat + timer); double targetTime = cond.GetSongPosFromBeat(cond.GetUnSwungBeat(startBeat + timer), true);
// HS timing window uses 1 as the middle point instead of 0 // HS timing window uses 1 as the middle point instead of 0
return 1 + (currTime - targetTime); return 1 + (currTime - targetTime);

View file

@ -43,7 +43,7 @@ namespace HeavenStudio.Games.Scripts_RhythmTweezers
var hairDirection = new Vector3(tst.x + 0.173f, tst.y) - holder.transform.position; var hairDirection = new Vector3(tst.x + 0.173f, tst.y) - holder.transform.position;
holder.transform.rotation = Quaternion.FromToRotation(Vector3.down, hairDirection); holder.transform.rotation = Quaternion.FromToRotation(Vector3.down, hairDirection);
float normalizedBeat = Conductor.instance.GetPositionFromBeat(inputBeat, 0.5f); float normalizedBeat = Conductor.instance.GetPositionFromBeat(inputBeat, 0.5f, ignoreSwing: false);
anim.Play("LoopPull", 0, normalizedBeat); anim.Play("LoopPull", 0, normalizedBeat);
tweezers.anim.Play("Tweezers_LongPluck", 0, normalizedBeat); tweezers.anim.Play("Tweezers_LongPluck", 0, normalizedBeat);
if (!game.IsExpectingInputNow(RhythmTweezers.InputAction_Release) && PlayerInput.GetIsAction(RhythmTweezers.InputAction_Release) && normalizedBeat < 1f) if (!game.IsExpectingInputNow(RhythmTweezers.InputAction_Release) && PlayerInput.GetIsAction(RhythmTweezers.InputAction_Release) && normalizedBeat < 1f)
@ -73,7 +73,7 @@ namespace HeavenStudio.Games.Scripts_RhythmTweezers
public void EndEarly() public void EndEarly()
{ {
var normalized = Conductor.instance.GetPositionFromBeat(inputBeat, 0.5f); var normalized = Conductor.instance.GetPositionFromBeat(inputBeat, 0.5f, ignoreSwing: false);
anim.Play("LoopPullReverse", 0, normalized); anim.Play("LoopPullReverse", 0, normalized);
tweezers.anim.Play("Tweezers_Idle", 0, 0); tweezers.anim.Play("Tweezers_Idle", 0, 0);

View file

@ -293,7 +293,7 @@ namespace HeavenStudio.Games
spawnedHairs.Add(hair); spawnedHairs.Add(hair);
hair.gameObject.SetActive(true); hair.gameObject.SetActive(true);
hair.GetComponent<Animator>().Play("SmallAppear", 0, 1); hair.GetComponent<Animator>().Play("SmallAppear", 0, 1);
float rot = -58f + 116 * crHandlerInstance.GetIntervalProgressFromBeat(beat, 1); float rot = -58f + 116 * crHandlerInstance.GetIntervalProgressFromBeat(conductor.GetUnSwungBeat(beat), 1);
hair.transform.eulerAngles = new Vector3(0, 0, rot); hair.transform.eulerAngles = new Vector3(0, 0, rot);
hair.createBeat = beat; hair.createBeat = beat;
} }
@ -306,7 +306,7 @@ namespace HeavenStudio.Games
spawnedLongs.Add(hair); spawnedLongs.Add(hair);
hair.gameObject.SetActive(true); hair.gameObject.SetActive(true);
hair.GetComponent<Animator>().Play("LongAppear", 0, 1); hair.GetComponent<Animator>().Play("LongAppear", 0, 1);
float rot = -58f + 116 * crHandlerInstance.GetIntervalProgressFromBeat(beat, 1); float rot = -58f + 116 * crHandlerInstance.GetIntervalProgressFromBeat(conductor.GetUnSwungBeat(beat), 1);
hair.transform.eulerAngles = new Vector3(0, 0, rot); hair.transform.eulerAngles = new Vector3(0, 0, rot);
hair.createBeat = beat; hair.createBeat = beat;
} }
@ -333,7 +333,7 @@ namespace HeavenStudio.Games
}) })
}); });
float rot = -58f + 116 * crHandlerInstance.GetIntervalProgressFromBeat(beat, 1); float rot = -58f + 116 * crHandlerInstance.GetIntervalProgressFromBeat(conductor.GetUnSwungBeat(beat), 1);
hair.transform.eulerAngles = new Vector3(0, 0, rot); hair.transform.eulerAngles = new Vector3(0, 0, rot);
hair.createBeat = beat; hair.createBeat = beat;
} }
@ -358,7 +358,7 @@ namespace HeavenStudio.Games
}) })
}); });
float rot = -58f + 116 * crHandlerInstance.GetIntervalProgressFromBeat(beat, 1); float rot = -58f + 116 * crHandlerInstance.GetIntervalProgressFromBeat(conductor.GetUnSwungBeat(beat), 1);
hair.transform.eulerAngles = new Vector3(0, 0, rot); hair.transform.eulerAngles = new Vector3(0, 0, rot);
hair.createBeat = beat; hair.createBeat = beat;
} }

View file

@ -68,7 +68,7 @@ namespace HeavenStudio.Games.Scripts_SeeSaw
{ {
var cond = Conductor.instance; var cond = Conductor.instance;
double currentBeat = cond.songPositionInBeatsAsDouble; double currentBeat = cond.unswungSongPositionInBeatsAsDouble;
if (cond.isPlaying && !cond.isPaused) if (cond.isPlaying && !cond.isPaused)
{ {
@ -299,7 +299,7 @@ namespace HeavenStudio.Games.Scripts_SeeSaw
{ {
lastState = currentState; lastState = currentState;
currentState = state; currentState = state;
startBeat = beat; startBeat = Conductor.instance.GetUnSwungBeat(beat);
heightLastFrame = 0; heightLastFrame = 0;
hasChangedAnimMidAir = false; hasChangedAnimMidAir = false;
switch (currentState) switch (currentState)

View file

@ -102,7 +102,7 @@ namespace HeavenStudio.Games
} }
if (Conductor.instance.isPlaying) if (Conductor.instance.isPlaying)
{ {
Conductor.instance.SetMinigamePitch(1f); Conductor.instance.SetMinigamePitch(1f, true);
} }
} }

View file

@ -39,10 +39,11 @@ namespace HeavenStudio.Games.Scripts_SpaceSoccer
} }
public void Init(Kicker kicker, double dispensedBeat) public void Init(Kicker kicker, double dispensedBeat)
{ {
Conductor conductor = Conductor.instance;
this.kicker = kicker; this.kicker = kicker;
kicker.ball = this; kicker.ball = this;
kicker.dispenserBeat = dispensedBeat; kicker.dispenserBeat = dispensedBeat;
double currentBeat = Conductor.instance.songPositionInBeatsAsDouble; double currentBeat = conductor.unswungSongPositionInBeatsAsDouble;
kickPath = SpaceSoccer.instance.GetPath("Kick"); kickPath = SpaceSoccer.instance.GetPath("Kick");
dispensePath = SpaceSoccer.instance.GetPath("Dispense"); dispensePath = SpaceSoccer.instance.GetPath("Dispense");
highKickPath = SpaceSoccer.instance.GetPath("HighKick"); highKickPath = SpaceSoccer.instance.GetPath("HighKick");
@ -53,7 +54,7 @@ namespace HeavenStudio.Games.Scripts_SpaceSoccer
{ {
//Debug.Log("Dispensing"); //Debug.Log("Dispensing");
state = State.Dispensing; state = State.Dispensing;
startBeat = dispensedBeat; startBeat = conductor.GetUnSwungBeat(dispensedBeat);
nextAnimBeat = startBeat + GetAnimLength(State.Dispensing); nextAnimBeat = startBeat + GetAnimLength(State.Dispensing);
kicker.kickTimes = 0; kicker.kickTimes = 0;
return; return;
@ -74,31 +75,32 @@ namespace HeavenStudio.Games.Scripts_SpaceSoccer
//Debug.Log("Setting state to kicked"); //Debug.Log("Setting state to kicked");
state = State.Kicked; state = State.Kicked;
double relativeBeat = currentBeat - dispensedBeat; double relativeBeat = currentBeat - dispensedBeat;
startBeat = dispensedBeat + (int)(relativeBeat - 0.1); //this makes the startBeat be for the kick that is currently in progress, but it won't play the kicker's animation for that kick. the -0.1 makes it so that if playback is started right when the kicker kicks, it still plays the kicker's animation. startBeat = conductor.GetUnSwungBeat(dispensedBeat) + (int)(relativeBeat - 0.1); //this makes the startBeat be for the kick that is currently in progress, but it won't play the kicker's animation for that kick. the -0.1 makes it so that if playback is started right when the kicker kicks, it still plays the kicker's animation.
nextAnimBeat = startBeat + GetAnimLength(State.Kicked); nextAnimBeat = startBeat + GetAnimLength(State.Kicked);
kicker.kickTimes = (int)(relativeBeat - 0.1) - numHighKicks - 1; //every high kick has 2 kicks in the same time a regular keep-up does 3 kicks. kicker.kickTimes = (int)(relativeBeat - 0.1) - numHighKicks - 1; //every high kick has 2 kicks in the same time a regular keep-up does 3 kicks.
break; break;
} }
else else
{ {
highKickSwing = 0.5f;
if (highKicks[i].beat + GetAnimLength(State.HighKicked) > currentBeat) if (highKicks[i].beat + GetAnimLength(State.HighKicked) > currentBeat)
{ {
highKickSwing = conductor.GetSwingRatioAtBeat(highKicks[i].beat, out _);
//Debug.Log("Setting state to high kick"); //Debug.Log("Setting state to high kick");
state = State.HighKicked; state = State.HighKicked;
double relativeBeat = highKicks[i].beat - dispensedBeat; double relativeBeat = highKicks[i].beat - dispensedBeat;
startBeat = dispensedBeat + Math.Ceiling(relativeBeat); //there is a chance this makes startBeat later than the current beat, but it shouldn't matter too much. It would only happen if the user places the high kicks incorrectly. startBeat = conductor.GetUnSwungBeat(dispensedBeat) + Math.Ceiling(relativeBeat); //there is a chance this makes startBeat later than the current beat, but it shouldn't matter too much. It would only happen if the user places the high kicks incorrectly.
nextAnimBeat = startBeat + GetAnimLength(State.HighKicked); nextAnimBeat = startBeat + GetAnimLength(State.HighKicked);
kicker.kickTimes = (int)Math.Ceiling(relativeBeat) - numHighKicks - 1; kicker.kickTimes = (int)Math.Ceiling(relativeBeat) - numHighKicks - 1;
break; break;
} }
else else
{ {
highKickSwing = conductor.GetSwingRatioAtBeat(highKicks[i].beat + GetAnimLength(State.HighKicked), out _);
//Debug.Log("Setting state to toe"); //Debug.Log("Setting state to toe");
state = State.Toe; state = State.Toe;
double relativeBeat = Math.Ceiling(highKicks[i].beat - dispensedBeat) + GetAnimLength(State.HighKicked); //there is a chance this makes startBeat later than the current beat, but it shouldn't matter too much. It would only happen if the user places the high kicks incorrectly. double relativeBeat = Math.Ceiling(highKicks[i].beat - dispensedBeat) + GetAnimLength(State.HighKicked); //there is a chance this makes startBeat later than the current beat, but it shouldn't matter too much. It would only happen if the user places the high kicks incorrectly.
startBeat = dispensedBeat + relativeBeat; startBeat = conductor.GetUnSwungBeat(dispensedBeat) + relativeBeat;
nextAnimBeat = startBeat + GetAnimLength(State.Toe); nextAnimBeat = startBeat + GetAnimLength(State.Toe);
kicker.kickTimes = (int)(relativeBeat - GetAnimLength(State.HighKicked)) - numHighKicks; kicker.kickTimes = (int)(relativeBeat - GetAnimLength(State.HighKicked)) - numHighKicks;
break; break;
@ -110,7 +112,7 @@ namespace HeavenStudio.Games.Scripts_SpaceSoccer
//Debug.Log("Defaulting to kicked state"); //Debug.Log("Defaulting to kicked state");
state = State.Kicked; state = State.Kicked;
double relativeBeat = currentBeat - dispensedBeat; double relativeBeat = currentBeat - dispensedBeat;
startBeat = dispensedBeat + (int)(relativeBeat - 0.1); //this makes the startBeat be for the kick that is currently in progress, but it won't play the kicker's animation for that kick. the -0.1 makes it so that if playback is started right when the kicker kicks, it still plays the kicker's animation. startBeat = conductor.GetUnSwungBeat(dispensedBeat) + (int)(relativeBeat - 0.1); //this makes the startBeat be for the kick that is currently in progress, but it won't play the kicker's animation for that kick. the -0.1 makes it so that if playback is started right when the kicker kicks, it still plays the kicker's animation.
nextAnimBeat = startBeat + GetAnimLength(State.Kicked); nextAnimBeat = startBeat + GetAnimLength(State.Kicked);
kicker.kickTimes = (int)(relativeBeat - 0.1) - numHighKicks - 1; kicker.kickTimes = (int)(relativeBeat - 0.1) - numHighKicks - 1;
} }
@ -178,7 +180,7 @@ namespace HeavenStudio.Games.Scripts_SpaceSoccer
private void Update() private void Update()
{ {
double beat = Conductor.instance.songPositionInBeatsAsDouble; double beat = Conductor.instance.unswungSongPositionInBeatsAsDouble;
switch (state) //handle animations switch (state) //handle animations
{ {
case State.None: //the only time any ball should ever have this state is if it's the unused offscreen ball (which is the only reason this state exists) case State.None: //the only time any ball should ever have this state is if it's the unused offscreen ball (which is the only reason this state exists)
@ -270,7 +272,7 @@ namespace HeavenStudio.Games.Scripts_SpaceSoccer
case State.Kicked: case State.Kicked:
return 1f; return 1f;
case State.HighKicked: case State.HighKicked:
return 2f - highKickSwing; return 1f + highKickSwing;
case State.Toe: case State.Toe:
return 2f - (1f - highKickSwing); return 2f - (1f - highKickSwing);
default: default:

View file

@ -20,7 +20,7 @@ namespace HeavenStudio.Games.Scripts_WizardsWaltz
void Update() void Update()
{ {
songPos = (float)(Conductor.instance.songPositionInBeatsAsDouble - game.wizardBeatOffset); songPos = (float)(Conductor.instance.unswungSongPositionInBeatsAsDouble - game.wizardBeatOffset);
var am = game.beatInterval / 2f; var am = game.beatInterval / 2f;
var x = Mathf.Sin(Mathf.PI * songPos / am) * game.xRange; var x = Mathf.Sin(Mathf.PI * songPos / am) * game.xRange;
var y = Mathf.Cos(Mathf.PI * songPos / am) * game.yRange; var y = Mathf.Cos(Mathf.PI * songPos / am) * game.yRange;

View file

@ -224,7 +224,7 @@ namespace HeavenStudio.Games
intervalStartBeat = beat; intervalStartBeat = beat;
foreach (var plant in currentPlants) foreach (var plant in currentPlants)
{ {
var songPos = (float)(plant.createBeat - wizardBeatOffset); var songPos = (float)(conductor.GetUnSwungBeat(plant.createBeat) - wizardBeatOffset);
var am = (beatInterval / 2f); var am = (beatInterval / 2f);
var x = Mathf.Sin(Mathf.PI * songPos / am) * xRange; var x = Mathf.Sin(Mathf.PI * songPos / am) * xRange;
var y = plantYOffset + Mathf.Cos(Mathf.PI * songPos / am) * (yRange * 1.5f); var y = plantYOffset + Mathf.Cos(Mathf.PI * songPos / am) * (yRange * 1.5f);
@ -261,7 +261,7 @@ namespace HeavenStudio.Games
if (!spawnedInactive) SoundByte.PlayOneShotGame("wizardsWaltz/plant", beat); if (!spawnedInactive) SoundByte.PlayOneShotGame("wizardsWaltz/plant", beat);
Plant plant = Instantiate(plantBase, plantHolder.transform).GetComponent<Plant>(); Plant plant = Instantiate(plantBase, plantHolder.transform).GetComponent<Plant>();
currentPlants.Add(plant); currentPlants.Add(plant);
var songPos = (float)(beat - wizardBeatOffset); var songPos = (float)(conductor.GetUnSwungBeat(beat) - wizardBeatOffset);
var am = (beatInterval / 2f); var am = (beatInterval / 2f);
var x = Mathf.Sin(Mathf.PI * songPos / am) * xRange; var x = Mathf.Sin(Mathf.PI * songPos / am) * xRange;
var y = plantYOffset + Mathf.Cos(Mathf.PI * songPos / am) * (yRange * 1.5f); var y = plantYOffset + Mathf.Cos(Mathf.PI * songPos / am) * (yRange * 1.5f);

View file

@ -1,7 +1,4 @@
using System; using System;
using System.Linq;
using System.Reflection;
using System.Collections.Generic; using System.Collections.Generic;
using HeavenStudio.InputSystem; using HeavenStudio.InputSystem;

View file

@ -15,6 +15,7 @@ using HeavenStudio.StudioDance;
using Jukebox; using Jukebox;
using UnityEditor; using UnityEditor;
using System.Linq; using System.Linq;
using BurstLinq;
namespace HeavenStudio.Editor namespace HeavenStudio.Editor
{ {
@ -269,6 +270,11 @@ namespace HeavenStudio.Editor
public void SelectMusic() public void SelectMusic()
{ {
if (Timeline.instance != null)
Timeline.instance?.Stop(0);
else
GameManager.instance.Stop(0);
var extensions = new[] var extensions = new[]
{ {
new ExtensionFilter("Music Files", "mp3", "ogg", "wav", "aiff", "aif", "aifc") new ExtensionFilter("Music Files", "mp3", "ogg", "wav", "aiff", "aif", "aifc")

View file

@ -160,6 +160,7 @@ namespace HeavenStudio.Editor.Track
lastTempo /= 2f; lastTempo /= 2f;
} }
RiqEntity tempoC = GameManager.instance.Beatmap.AddNewTempoChange(Timeline.instance.MousePos2BeatSnap, lastTempo); RiqEntity tempoC = GameManager.instance.Beatmap.AddNewTempoChange(Timeline.instance.MousePos2BeatSnap, lastTempo);
tempoC.CreateProperty("swingDivision", 1f);
tempoTimelineObj.chartEntity = tempoC; tempoTimelineObj.chartEntity = tempoC;
CommandManager.Instance.AddCommand(new Commands.AddMarker(tempoC, tempoC.guid, HoveringTypes.TempoChange)); CommandManager.Instance.AddCommand(new Commands.AddMarker(tempoC, tempoC.guid, HoveringTypes.TempoChange));
} }

View file

@ -14,6 +14,9 @@ public class TempoDialog : Dialog
[SerializeField] Button deleteButton; [SerializeField] Button deleteButton;
[SerializeField] TMP_InputField tempoInput; [SerializeField] TMP_InputField tempoInput;
[SerializeField] TMP_InputField swingInput;
[SerializeField] Slider swingSlider;
[SerializeField] Toggle swingDivisionToggle;
public void SwitchTempoDialog() public void SwitchTempoDialog()
{ {
@ -28,6 +31,9 @@ public class TempoDialog : Dialog
Editor.instance.inAuthorativeMenu = true; Editor.instance.inAuthorativeMenu = true;
ResetAllDialogs(); ResetAllDialogs();
dialog.SetActive(true); dialog.SetActive(true);
swingSlider.maxValue = 0.25f;
swingSlider.minValue = 0;
} }
} }
@ -45,6 +51,10 @@ public class TempoDialog : Dialog
deleteButton.gameObject.SetActive(!tempoObj.first); deleteButton.gameObject.SetActive(!tempoObj.first);
tempoInput.text = tempoObj.chartEntity["tempo"].ToString("F"); tempoInput.text = tempoObj.chartEntity["tempo"].ToString("F");
swingInput.text = (tempoObj.chartEntity["swing"] * 400).ToString("F");
swingSlider.value = tempoObj.chartEntity["swing"];
swingDivisionToggle.isOn = tempoObj.chartEntity["swingDivision"] != 1f;
} }
public void DeleteTempo() public void DeleteTempo()
@ -86,4 +96,33 @@ public class TempoDialog : Dialog
tempoInput.text = tempoObj.chartEntity["tempo"].ToString("F"); tempoInput.text = tempoObj.chartEntity["tempo"].ToString("F");
} }
} }
public void SwingSliderUpdate()
{
if (tempoObj != null)
{
tempoObj.SetSwing(System.MathF.Round(swingSlider.value, 4));
swingInput.text = (tempoObj.chartEntity["swing"] * 400).ToString("F");
swingSlider.value = tempoObj.chartEntity["swing"];
}
}
public void SetSwing()
{
if (tempoObj != null)
{
float swing = float.Parse(swingInput.text);
tempoObj.SetSwing(swing * 0.25f / 100f);
swingInput.text = (tempoObj.chartEntity["swing"] * 400).ToString("F");
swingSlider.value = tempoObj.chartEntity["swing"];
}
}
public void SwingDivisionToggle()
{
if (tempoObj != null)
{
tempoObj.SetSwingDivision(swingDivisionToggle.isOn);
}
}
} }

View file

@ -62,6 +62,18 @@ namespace HeavenStudio.Editor.Track
SetX(chartEntity); SetX(chartEntity);
} }
public void SetSwing(float swing)
{
chartEntity["swing"] = Mathf.Clamp(swing, 0, 0.5f);
UpdateTempo();
}
public void SetSwingDivision(bool sixteenth)
{
chartEntity["swingDivision"] = sixteenth ? 0.5f : 1f;
UpdateTempo();
}
public override void Init() public override void Init()
{ {
UpdateTempo(); UpdateTempo();

View file

@ -9,6 +9,7 @@ using TMPro;
using Jukebox; using Jukebox;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Linq; using System.Linq;
using BurstLinq;
using HeavenStudio.Util; using HeavenStudio.Util;
@ -475,7 +476,7 @@ namespace HeavenStudio.Editor.Track
if (Conductor.instance.metronome) if (Conductor.instance.metronome)
{ {
var startBeat = Mathf.FloorToInt(Conductor.instance.songPositionInBeats - 0.5f); var startBeat = Mathf.FloorToInt(Conductor.instance.songPositionInBeats - 0.5f);
var nm = Conductor.instance.GetLoopPositionFromBeat(0.5f, 1f); var nm = Conductor.instance.GetLoopPositionFromBeat(0.5f, 1f, ignoreSwing: false);
var loop = (startBeat % 2 == 0) ? Mathf.SmoothStep(-1.1f, 1f, nm) : Mathf.SmoothStep(1f, -1f, nm); var loop = (startBeat % 2 == 0) ? Mathf.SmoothStep(-1.1f, 1f, nm) : Mathf.SmoothStep(1f, -1f, nm);
rot = loop * 45f; rot = loop * 45f;
@ -630,7 +631,7 @@ namespace HeavenStudio.Editor.Track
{ {
TimelinePlaybackBeat.text = $"Beat {string.Format("{0:0.000}", PlaybackBeat)}"; TimelinePlaybackBeat.text = $"Beat {string.Format("{0:0.000}", PlaybackBeat)}";
if (TimelineSongPosLine != null) if (TimelineSongPosLine != null && !Conductor.instance.WaitingForDsp)
{ {
TimelineSongPosLine.transform.localPosition = new Vector3(Conductor.instance.songPositionInBeats * PixelsPerBeat, TimelineSongPosLine.transform.localPosition.y); TimelineSongPosLine.transform.localPosition = new Vector3(Conductor.instance.songPositionInBeats * PixelsPerBeat, TimelineSongPosLine.transform.localPosition.y);
} }
@ -655,13 +656,13 @@ namespace HeavenStudio.Editor.Track
{ {
if (!Conductor.instance.isPlaying) if (!Conductor.instance.isPlaying)
{ {
if (TimelineSongPosLine == null) if (Conductor.instance.isPaused)
{ {
Play(false, PlaybackBeat); Play(false, Conductor.instance.songPositionInBeats);
} }
else else
{ {
Play(false, Conductor.instance.songPositionInBeats); Play(false, PlaybackBeat);
} }
} }
else if (!Conductor.instance.isPaused) else if (!Conductor.instance.isPaused)
@ -673,16 +674,14 @@ namespace HeavenStudio.Editor.Track
public void Play(bool fromStart, float time) public void Play(bool fromStart, float time)
{ {
// if (fromStart) Stop(); GameManager.instance.SafePlay(time, 0, false);
if (!Conductor.instance.isPaused) if (!Conductor.instance.isPaused)
{ {
TimelineSongPosLine = Instantiate(TimelineSongPosLineRef, TimelineSongPosLineRef.parent).GetComponent<RectTransform>(); TimelineSongPosLine = Instantiate(TimelineSongPosLineRef, TimelineSongPosLineRef.parent).GetComponent<RectTransform>();
TimelineSongPosLine.gameObject.SetActive(true); TimelineSongPosLine.gameObject.SetActive(true);
TimelineSongPosLine.transform.localPosition = new Vector3(time * PixelsPerBeat, TimelineSongPosLine.transform.localPosition.y);
} }
GameManager.instance.SafePlay(time, 0, false);
SetTimeButtonColors(false, true, true); SetTimeButtonColors(false, true, true);
} }

View file

@ -5,6 +5,7 @@ using UnityEngine.Pool;
using Jukebox; using Jukebox;
using System.Linq; using System.Linq;
using BurstLinq;
namespace HeavenStudio.Editor.Track namespace HeavenStudio.Editor.Track
{ {

View file

@ -16,6 +16,7 @@ using SatorImaging.UnitySourceGenerator;
using System; using System;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using BurstLinq;
using UnityEngine.Assertions.Must; using UnityEngine.Assertions.Must;
using Newtonsoft.Json.Linq; using Newtonsoft.Json.Linq;
@ -98,6 +99,7 @@ namespace HeavenStudio
{ {
{"tempo", 120f}, {"tempo", 120f},
{"swing", 0f}, {"swing", 0f},
{"swingDivision", 1f},
{"timeSignature", new Vector2(4, 4)}, {"timeSignature", new Vector2(4, 4)},
}; };

View file

@ -8,6 +8,7 @@ using HeavenStudio.Common;
using HeavenStudio.InputSystem; using HeavenStudio.InputSystem;
using System.Linq; using System.Linq;
using BurstLinq;
using SFB; using SFB;
using Jukebox; using Jukebox;

View file

@ -34,10 +34,10 @@ namespace HeavenStudio.Util
/// <param name="length">duration of animation (progress 1.0)</param> /// <param name="length">duration of animation (progress 1.0)</param>
/// <param name="timeScale">multiplier for animation progress (smaller values make animation slower)</param> /// <param name="timeScale">multiplier for animation progress (smaller values make animation slower)</param>
/// <param name="animLayer">animator layer to play animation on</param> /// <param name="animLayer">animator layer to play animation on</param>
public static void DoScaledAnimation(this Animator anim, string animName, double startTime, double length = 1, float timeScale = 1f, int animLayer = -1, bool clamp = false) public static void DoScaledAnimation(this Animator anim, string animName, double startTime, double length = 1, float timeScale = 1f, int animLayer = -1, bool clamp = false, bool ignoreSwing = true)
{ {
if (anim == null) return; if (anim == null) return;
float pos = Conductor.instance.GetPositionFromBeat(startTime, length) * timeScale; float pos = Conductor.instance.GetPositionFromBeat(startTime, length, ignoreSwing: ignoreSwing) * timeScale;
if (clamp) pos = Mathf.Clamp01(pos); if (clamp) pos = Mathf.Clamp01(pos);
anim.Play(animName, animLayer, pos); anim.Play(animName, animLayer, pos);
anim.speed = 1f; //not 0 so these can still play their script events anim.speed = 1f; //not 0 so these can still play their script events

View file

@ -107,12 +107,16 @@ namespace HeavenStudio.Util
Destroy(gameObject); Destroy(gameObject);
} }
public void StopAll() public void StopAll(bool destroy = false)
{ {
foreach (Util.Sound sound in playingSounds) foreach (Util.Sound sound in playingSounds)
{ {
sound.Stop(); sound.Stop();
} }
if (destroy)
{
Destroy(gameObject);
}
} }
} }
} }

View file

@ -2,6 +2,7 @@ using System;
using System.Collections; using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using BurstLinq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks;
using UnityEngine; using UnityEngine;

View file

@ -1,5 +1,6 @@
{ {
"dependencies": { "dependencies": {
"com.annulusgames.burst-linq": "https://github.com/AnnulusGames/BurstLinq.git?path=/Assets/BurstLinq",
"com.coffee.softmask-for-ugui": "https://github.com/mob-sakai/SoftMaskForUGUI.git", "com.coffee.softmask-for-ugui": "https://github.com/mob-sakai/SoftMaskForUGUI.git",
"com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask", "com.cysharp.unitask": "https://github.com/Cysharp/UniTask.git?path=src/UniTask/Assets/Plugins/UniTask",
"com.nobi.roundedcorners": "https://github.com/kirevdokimov/Unity-UI-Rounded-Corners.git", "com.nobi.roundedcorners": "https://github.com/kirevdokimov/Unity-UI-Rounded-Corners.git",
@ -10,7 +11,8 @@
"com.tayx.graphy": "https://github.com/Tayx94/graphy.git", "com.tayx.graphy": "https://github.com/Tayx94/graphy.git",
"com.unity.2d.sprite": "1.0.0", "com.unity.2d.sprite": "1.0.0",
"com.unity.assetbundlebrowser": "https://github.com/Unity-Technologies/AssetBundles-Browser.git", "com.unity.assetbundlebrowser": "https://github.com/Unity-Technologies/AssetBundles-Browser.git",
"com.unity.collab-proxy": "2.0.1", "com.unity.burst": "1.6.6",
"com.unity.collections": "1.2.4",
"com.unity.ide.rider": "3.0.18", "com.unity.ide.rider": "3.0.18",
"com.unity.ide.visualstudio": "2.0.22", "com.unity.ide.visualstudio": "2.0.22",
"com.unity.nuget.newtonsoft-json": "3.0.2", "com.unity.nuget.newtonsoft-json": "3.0.2",

View file

@ -1,5 +1,12 @@
{ {
"dependencies": { "dependencies": {
"com.annulusgames.burst-linq": {
"version": "https://github.com/AnnulusGames/BurstLinq.git?path=/Assets/BurstLinq",
"depth": 0,
"source": "git",
"dependencies": {},
"hash": "fc354905c8e40b1491ea8a750ee722ee07adf484"
},
"com.coffee.softmask-for-ugui": { "com.coffee.softmask-for-ugui": {
"version": "https://github.com/mob-sakai/SoftMaskForUGUI.git", "version": "https://github.com/mob-sakai/SoftMaskForUGUI.git",
"depth": 0, "depth": 0,
@ -74,11 +81,23 @@
"dependencies": {}, "dependencies": {},
"hash": "b7c279278d1a343c6957c9f15b45173d3211f01c" "hash": "b7c279278d1a343c6957c9f15b45173d3211f01c"
}, },
"com.unity.collab-proxy": { "com.unity.burst": {
"version": "2.0.1", "version": "1.6.6",
"depth": 0, "depth": 0,
"source": "registry", "source": "registry",
"dependencies": {}, "dependencies": {
"com.unity.mathematics": "1.2.1"
},
"url": "https://packages.unity.com"
},
"com.unity.collections": {
"version": "1.2.4",
"depth": 0,
"source": "registry",
"dependencies": {
"com.unity.burst": "1.6.6",
"com.unity.test-framework": "1.1.31"
},
"url": "https://packages.unity.com" "url": "https://packages.unity.com"
}, },
"com.unity.ext.nunit": { "com.unity.ext.nunit": {
@ -106,6 +125,13 @@
}, },
"url": "https://packages.unity.com" "url": "https://packages.unity.com"
}, },
"com.unity.mathematics": {
"version": "1.2.6",
"depth": 1,
"source": "registry",
"dependencies": {},
"url": "https://packages.unity.com"
},
"com.unity.nuget.newtonsoft-json": { "com.unity.nuget.newtonsoft-json": {
"version": "3.2.1", "version": "3.2.1",
"depth": 1, "depth": 1,

View file

@ -0,0 +1,17 @@
{
"MonoBehaviour": {
"Version": 4,
"EnableBurstCompilation": true,
"EnableOptimisations": true,
"EnableSafetyChecks": false,
"EnableDebugInAllBuilds": false,
"UsePlatformSDKLinker": false,
"CpuMinTargetX32": 0,
"CpuMaxTargetX32": 0,
"CpuMinTargetX64": 0,
"CpuMaxTargetX64": 0,
"CpuTargetsX32": 6,
"CpuTargetsX64": 72,
"OptimizeFor": 0
}
}

View file

@ -0,0 +1,6 @@
{
"MonoBehaviour": {
"Version": 4,
"DisabledWarnings": ""
}
}

View file

@ -134,7 +134,7 @@ PlayerSettings:
16:10: 1 16:10: 1
16:9: 1 16:9: 1
Others: 1 Others: 1
bundleVersion: 1.0.9 bundleVersion: 1.0.10
preloadedAssets: preloadedAssets:
- {fileID: 102900000, guid: 5348c08b82446e0478cee8bda6c02cfc, type: 3} - {fileID: 102900000, guid: 5348c08b82446e0478cee8bda6c02cfc, type: 3}
metroInputSource: 0 metroInputSource: 0
@ -158,11 +158,11 @@ PlayerSettings:
applicationIdentifier: applicationIdentifier:
Standalone: com.RHeavenStudio.Heaven-Studio Standalone: com.RHeavenStudio.Heaven-Studio
buildNumber: buildNumber:
Standalone: 100009 Standalone: 100010
iPhone: 0 iPhone: 0
tvOS: 0 tvOS: 0
overrideDefaultApplicationIdentifier: 0 overrideDefaultApplicationIdentifier: 0
AndroidBundleVersionCode: 100009 AndroidBundleVersionCode: 100010
AndroidMinSdkVersion: 22 AndroidMinSdkVersion: 22
AndroidTargetSdkVersion: 0 AndroidTargetSdkVersion: 0
AndroidPreferredInstallLocation: 1 AndroidPreferredInstallLocation: 1
@ -689,7 +689,11 @@ PlayerSettings:
Windows Store Apps: UNITY_POST_PROCESSING_STACK_V2 Windows Store Apps: UNITY_POST_PROCESSING_STACK_V2
XboxOne: UNITY_POST_PROCESSING_STACK_V2 XboxOne: UNITY_POST_PROCESSING_STACK_V2
tvOS: UNITY_POST_PROCESSING_STACK_V2 tvOS: UNITY_POST_PROCESSING_STACK_V2
additionalCompilerArguments: {} additionalCompilerArguments:
Standalone:
- /nowarn:0168
- /nowarn:0219
- /nowarn:0414
platformArchitecture: {} platformArchitecture: {}
scriptingBackend: {} scriptingBackend: {}
il2cppCompilerConfiguration: {} il2cppCompilerConfiguration: {}

View file

@ -16,6 +16,31 @@ MonoBehaviour:
AutoEmitDisabledPaths: [] AutoEmitDisabledPaths: []
DenseViewWidthThreshold: 512 DenseViewWidthThreshold: 512
_disableAutoReloadInBackground: 0 _disableAutoReloadInBackground: 0
ImportedScriptPaths: [] ImportedScriptPaths:
- Assets/Scripts/Games/GleeClub/GleeClub.cs
- Assets/Scripts/Minigames.cs
- Assets/Scripts/LevelEditor/Editor.cs
- Assets/Scripts/Games/FreezeFrame/Car.cs
- Assets/Scripts/Games/Fillbots/Fillbots.cs
- Assets/Scripts/Games/Fillbots/FullBody.cs
- Assets/Scripts/Games/CatchOfTheDay/CatchOfTheDay.cs
- Assets/Scripts/Util/ParticleSystemHelpers.cs
- Assets/Scripts/Games/Fillbots/NtrFillbot.cs
- Assets/Scripts/USG.g/LoadMinigames.Minigames.MinigameLoaderGenerator.g.cs
- Assets/Scripts/Games/FreezeFrame/FreezeFrame.cs
- Assets/Scripts/Games/CatchOfTheDay/LakeScene.cs
- Assets/Scripts/Conductor.cs
- Assets/Scripts/GameManager.cs
- Assets/Scripts/LevelEditor/Timeline/TimelineBlockManager.cs
- Assets/Scripts/TitleManager.cs
- Assets/Scripts/Util/MultiSound.cs
- Assets/Scripts/LevelEditor/Timeline/Timeline.cs
- Assets/Scripts/EventCaller.cs
- Assets/Scripts/Games/Minigame.cs
- Assets/Scripts/InputSystem/PlayerInput.cs
- Assets/Scripts/GameCamera.cs
- Assets/Scripts/Util/SoundByte.cs
- Assets/Scripts/GameInitializer.cs
- Assets/Scripts/AppInfo.cs
PathsToSkipImportEvent: [] PathsToSkipImportEvent: []
PathsToIgnoreOverwriteSettingOnAttribute: [] PathsToIgnoreOverwriteSettingOnAttribute: []