2021-12-19 04:10:43 +00:00
using System ;
using System.Collections ;
using System.Collections.Generic ;
using System.Linq ;
using UnityEngine ;
2023-09-11 22:28:04 +00:00
using UnityEngine.Pool ;
2021-12-19 04:10:43 +00:00
2023-06-10 19:13:29 +00:00
using Jukebox ;
2023-06-12 21:18:37 +00:00
using HeavenStudio.Util ;
2022-03-14 14:21:05 +00:00
using HeavenStudio.Games ;
2023-03-11 04:51:22 +00:00
using HeavenStudio.Common ;
2023-10-29 19:44:47 +00:00
using Cysharp.Threading.Tasks ;
2021-12-19 04:10:43 +00:00
2022-03-14 14:21:05 +00:00
namespace HeavenStudio
2021-12-21 01:10:49 +00:00
{
public class GameManager : MonoBehaviour
{
2023-09-11 22:28:04 +00:00
const int SoundPoolSizeMin = 32 ;
const int SoundPoolSizeMax = 32 ;
2022-01-21 01:24:30 +00:00
[Header("Lists")]
2023-06-10 19:13:29 +00:00
[NonSerialized] public RiqBeatmap Beatmap = new ( ) ;
2024-01-24 05:17:35 +00:00
private Dictionary < string , GameObject > cachedGamePrefabs = new ( ) ;
2023-09-11 22:28:04 +00:00
[NonSerialized] public ObjectPool < Sound > SoundObjects ;
2021-12-19 04:10:43 +00:00
2022-01-21 01:24:30 +00:00
[Header("Components")]
2023-05-07 20:33:15 +00:00
[NonSerialized] public Camera GameCamera , CursorCam , OverlayCamera , StaticCamera ;
[NonSerialized] public CircleCursor CircleCursor ;
[NonSerialized] public GameObject GamesHolder ;
[NonSerialized] public Games . Global . Flash fade ;
[NonSerialized] public Games . Global . Filter filter ;
2022-01-21 01:24:30 +00:00
2021-12-24 03:36:16 +00:00
[Header("Games")]
2022-01-21 01:24:30 +00:00
Coroutine currentGameSwitchIE ;
2021-12-19 04:10:43 +00:00
2023-09-11 22:28:04 +00:00
[NonSerialized]
public int currentEvent , currentTempoEvent , currentVolumeEvent , currentSectionEvent ,
2023-01-05 04:04:31 +00:00
currentPreEvent , currentPreSwitch , currentPreSequence ;
2024-01-25 17:29:05 +00:00
public string currentGame { get ; private set ; }
public GameObject minigameObj { get ; private set ; }
public Minigame minigame { get ; private set ; }
public RiqEntity lastSection { get ; private set ; }
public RiqEntity currentSection { get ; private set ; }
public double endBeat { get ; private set ; }
public double startBeat { get ; private set ; }
public double nextSectionBeat { get ; private set ; }
2023-12-26 05:22:51 +00:00
public double SectionProgress { get ; private set ; }
public float MarkerWeight { get ; private set ; }
2024-01-25 17:29:05 +00:00
2023-12-26 05:22:51 +00:00
public int MarkerCategory { get ; private set ; }
2022-02-08 01:07:03 +00:00
2024-01-25 17:29:05 +00:00
public bool playMode { get ; private set ; }
public bool autoplay { get ; private set ; }
public bool canInput { get ; private set ; }
2023-10-29 19:44:47 +00:00
public bool GameHasSplitColours
{
get
{
var inf = GetGameInfo ( currentGame ) ;
if ( inf = = null ) return false ;
return inf . splitColorL ! = null & & inf . splitColorR ! = null ;
}
}
2023-06-13 20:55:02 +00:00
bool AudioLoadDone ;
bool ChartLoadError ;
2024-01-21 21:50:05 +00:00
bool exiting ;
2023-06-13 20:55:02 +00:00
2024-01-15 02:04:10 +00:00
List < double > eventBeats , preSequenceBeats , tempoBeats , volumeBeats , sectionBeats ;
2023-09-12 20:38:28 +00:00
List < RiqEntity > allGameSwitches ;
2023-09-11 22:28:04 +00:00
2023-06-10 19:13:29 +00:00
public event Action < double > onBeatChanged ;
2023-12-26 05:22:51 +00:00
public event Action < RiqEntity , RiqEntity > onSectionChange ;
2023-11-23 16:19:39 +00:00
public event Action < double > onBeatPulse ;
2023-12-26 05:22:51 +00:00
public event Action < double > onPlay ;
public event Action < double > onPause ;
public event Action < double > onUnPause ;
2022-02-08 01:07:03 +00:00
2022-01-30 23:40:12 +00:00
public int BeatmapEntities ( )
{
2023-06-10 19:13:29 +00:00
return Beatmap . Entities . Count + Beatmap . TempoChanges . Count + Beatmap . VolumeChanges . Count + Beatmap . SectionMarkers . Count ;
2022-01-30 23:40:12 +00:00
}
2021-12-31 14:46:11 +00:00
2022-01-21 01:24:30 +00:00
public static GameManager instance { get ; private set ; }
private EventCaller eventCaller ;
2022-01-06 00:11:33 +00:00
2023-01-25 03:54:19 +00:00
// average input accuracy (msec)
2023-01-14 04:53:25 +00:00
List < int > inputOffsetSamples = new List < int > ( ) ;
float averageInputOffset = 0 ;
public float AvgInputOffset
{
get
{
return averageInputOffset ;
}
set
{
inputOffsetSamples . Add ( ( int ) value ) ;
averageInputOffset = ( float ) inputOffsetSamples . Average ( ) ;
}
}
2023-01-25 03:54:19 +00:00
// input accuracy (%)
2024-01-21 21:50:05 +00:00
bool skillStarCollected = false , noMiss = true ;
2023-01-25 03:54:19 +00:00
2023-05-07 20:33:15 +00:00
// cleared sections
List < bool > clearedSections = new List < bool > ( ) ;
public bool ClearedSection
{
set
{
clearedSections . Add ( value ) ;
}
}
2023-12-26 05:22:51 +00:00
JudgementManager . JudgementInfo judgementInfo ;
2021-12-21 01:10:49 +00:00
private void Awake ( )
{
instance = this ;
2024-01-21 21:50:05 +00:00
exiting = false ;
2021-12-21 01:10:49 +00:00
}
2021-12-19 04:10:43 +00:00
2023-06-10 19:13:29 +00:00
public void Init ( bool preLoaded = false )
2021-12-21 01:10:49 +00:00
{
2023-06-13 20:55:02 +00:00
AudioLoadDone = false ;
ChartLoadError = false ;
2023-09-11 22:28:04 +00:00
currentPreEvent = 0 ;
2022-06-12 19:32:00 +00:00
currentPreSwitch = 0 ;
2023-01-05 04:04:31 +00:00
currentPreSequence = 0 ;
2023-05-07 20:33:15 +00:00
2023-01-27 03:42:16 +00:00
GameObject filter = new GameObject ( "filter" ) ;
this . filter = filter . AddComponent < Games . Global . Filter > ( ) ;
2021-12-19 04:10:43 +00:00
2022-01-03 22:42:43 +00:00
eventCaller = this . gameObject . AddComponent < EventCaller > ( ) ;
eventCaller . GamesHolder = GamesHolder . transform ;
2024-01-25 17:29:05 +00:00
eventCaller . Init ( this ) ;
canInput = true ;
2021-12-23 02:28:05 +00:00
2023-03-11 04:51:22 +00:00
// note: serialize this shit in the inspector //
2023-05-07 20:33:15 +00:00
GameObject textbox = Instantiate ( Resources . Load < GameObject > ( "Prefabs/Common/Textbox" ) ) ;
textbox . name = "Textbox" ;
2023-03-11 04:51:22 +00:00
2023-05-07 20:33:15 +00:00
GameObject timingDisp = Instantiate ( Resources . Load < GameObject > ( "Prefabs/Common/Overlays/TimingAccuracy" ) ) ;
timingDisp . name = "TimingDisplay" ;
2023-03-11 04:51:22 +00:00
2023-05-07 20:33:15 +00:00
GameObject skillStarDisp = Instantiate ( Resources . Load < GameObject > ( "Prefabs/Common/Overlays/SkillStar" ) ) ;
skillStarDisp . name = "SkillStar" ;
2023-03-11 04:51:22 +00:00
2023-05-07 20:33:15 +00:00
GameObject overlays = Instantiate ( Resources . Load < GameObject > ( "Prefabs/Common/Overlays" ) ) ;
overlays . name = "Overlays" ;
2023-03-11 04:51:22 +00:00
2023-05-07 20:33:15 +00:00
GoForAPerfect . instance . Disable ( ) ;
2023-03-11 04:51:22 +00:00
/////
2023-09-11 22:28:04 +00:00
2024-02-11 05:24:52 +00:00
SoundByte . BasicCheck ( ) ;
2023-09-11 22:28:04 +00:00
SoundObjects = new ObjectPool < Sound > ( CreatePooledSound , OnTakePooledSound , OnReturnPooledSound , OnDestroyPooledSound , true , SoundPoolSizeMin , SoundPoolSizeMax ) ;
2023-03-11 04:51:22 +00:00
2023-06-10 19:13:29 +00:00
if ( preLoaded )
2022-01-15 22:52:53 +00:00
{
2023-06-10 19:13:29 +00:00
LoadRemix ( false ) ;
2023-01-12 01:42:12 +00:00
}
else
{
2023-06-13 20:55:02 +00:00
RiqFileHandler . ClearCache ( ) ;
2023-01-12 01:42:12 +00:00
NewRemix ( ) ;
2022-01-15 22:52:53 +00:00
}
2021-12-24 03:36:16 +00:00
2023-01-12 01:42:12 +00:00
SortEventsList ( ) ;
2023-12-26 05:22:51 +00:00
Conductor . instance . SetBpm ( Beatmap . TempoChanges [ 0 ] [ "tempo" ] ) ;
Conductor . instance . SetVolume ( Beatmap . VolumeChanges [ 0 ] [ "volume" ] ) ;
Conductor . instance . firstBeatOffset = Beatmap . data . offset ;
2021-12-28 02:36:27 +00:00
2024-02-11 05:24:52 +00:00
if ( ! preLoaded )
2021-12-28 02:36:27 +00:00
{
2024-02-11 05:24:52 +00:00
if ( Beatmap . Entities . Count > = 1 )
{
string game = Beatmap . Entities [ 0 ] . datamodel . Split ( 0 ) ;
SetCurrentGame ( game ) ;
StartCoroutine ( WaitAndSetGame ( game ) ) ;
}
else
{
SetGame ( "noGame" ) ;
}
2022-01-30 09:09:26 +00:00
}
2023-01-12 01:42:12 +00:00
2023-12-26 05:22:51 +00:00
if ( playMode )
2023-01-12 01:42:12 +00:00
{
2023-12-26 05:22:51 +00:00
StartCoroutine ( WaitReadyAndPlayCo ( startBeat , 1f ) ) ;
2023-01-12 01:42:12 +00:00
}
2021-12-21 01:10:49 +00:00
}
2021-12-19 04:10:43 +00:00
2023-09-11 22:28:04 +00:00
Sound CreatePooledSound ( )
{
GameObject oneShot = new GameObject ( $"Pooled Scheduled Sound" ) ;
oneShot . transform . SetParent ( transform ) ;
AudioSource audioSource = oneShot . AddComponent < AudioSource > ( ) ;
audioSource . playOnAwake = false ;
Sound snd = oneShot . AddComponent < Sound > ( ) ;
oneShot . SetActive ( false ) ;
return snd ;
}
// Called when an item is returned to the pool using Release
void OnReturnPooledSound ( Sound snd )
{
snd . Stop ( ) ;
}
// Called when an item is taken from the pool using Get
void OnTakePooledSound ( Sound snd )
{
snd . gameObject . SetActive ( true ) ;
}
// If the pool capacity is reached then any items returned will be destroyed.
void OnDestroyPooledSound ( Sound snd )
{
snd . Stop ( ) ;
Destroy ( snd . gameObject ) ;
}
2022-02-26 03:41:32 +00:00
public void NewRemix ( )
2023-09-11 22:28:04 +00:00
{
2023-12-26 05:22:51 +00:00
Debug . Log ( "Creating new remix" ) ;
2023-06-13 20:55:02 +00:00
AudioLoadDone = false ;
2023-06-10 19:13:29 +00:00
Beatmap = new ( "1" , "HeavenStudio" ) ;
2023-12-26 05:22:51 +00:00
Beatmap . data . properties = new ( Minigames . propertiesModel ) ;
2023-06-10 19:13:29 +00:00
Beatmap . AddNewTempoChange ( 0 , 120f ) ;
Beatmap . AddNewVolumeChange ( 0 , 100f ) ;
Beatmap . data . offset = 0f ;
2022-02-26 19:06:52 +00:00
Conductor . instance . musicSource . clip = null ;
2023-06-10 19:13:29 +00:00
RiqFileHandler . WriteRiq ( Beatmap ) ;
2023-06-13 20:55:02 +00:00
AudioLoadDone = true ;
2022-02-26 03:41:32 +00:00
}
2023-06-10 19:13:29 +00:00
public IEnumerator LoadMusic ( )
2022-01-30 12:03:37 +00:00
{
2023-06-13 20:55:02 +00:00
ChartLoadError = false ;
2023-06-10 19:13:29 +00:00
IEnumerator load = RiqFileHandler . LoadSong ( ) ;
while ( true )
2022-02-26 03:41:32 +00:00
{
2023-06-10 19:13:29 +00:00
object current = load . Current ;
try
2022-08-21 23:46:45 +00:00
{
2023-06-10 19:13:29 +00:00
if ( load . MoveNext ( ) = = false )
{
2022-08-21 23:46:45 +00:00
break ;
2023-06-10 19:13:29 +00:00
}
current = load . Current ;
2022-08-21 23:46:45 +00:00
}
2023-06-11 16:12:25 +00:00
catch ( System . IO . FileNotFoundException f )
{
Debug . LogWarning ( "chart has no music: " + f . Message ) ;
Conductor . instance . musicSource . clip = null ;
2023-06-13 20:55:02 +00:00
AudioLoadDone = true ;
yield break ;
2023-06-11 16:12:25 +00:00
}
2023-06-10 19:13:29 +00:00
catch ( Exception e )
{
Debug . LogError ( $"Failed to load music: {e.Message}" ) ;
GlobalGameManager . ShowErrorMessage ( "Error Loading Music" , e . Message + "\n\n" + e . StackTrace ) ;
2023-06-13 20:55:02 +00:00
AudioLoadDone = true ;
ChartLoadError = true ;
2023-06-10 19:13:29 +00:00
yield break ;
}
yield return current ;
2022-02-26 03:41:32 +00:00
}
2023-06-10 19:13:29 +00:00
Conductor . instance . musicSource . clip = RiqFileHandler . StreamedAudioClip ;
2023-06-13 20:55:02 +00:00
AudioLoadDone = true ;
2023-06-10 19:13:29 +00:00
}
public void LoadRemix ( bool editor = false )
{
2023-06-13 20:55:02 +00:00
AudioLoadDone = false ;
ChartLoadError = false ;
2023-06-10 19:13:29 +00:00
try
2022-02-26 03:41:32 +00:00
{
2023-06-10 19:13:29 +00:00
Beatmap = RiqFileHandler . ReadRiq ( ) ;
}
catch ( Exception e )
{
Debug . LogError ( $"Failed to load remix: {e.Message}" ) ;
GlobalGameManager . ShowErrorMessage ( "Error Loading RIQ" , e . Message + "\n\n" + e . StackTrace ) ;
2023-06-13 20:55:02 +00:00
ChartLoadError = true ;
2023-06-10 19:13:29 +00:00
return ;
2022-02-26 03:41:32 +00:00
}
2023-06-10 19:13:29 +00:00
if ( ! editor )
StartCoroutine ( LoadMusic ( ) ) ;
2022-08-22 23:14:38 +00:00
SortEventsList ( ) ;
2023-06-10 19:13:29 +00:00
Conductor . instance . SetBpm ( Beatmap . TempoChanges [ 0 ] [ "tempo" ] ) ;
Conductor . instance . SetVolume ( Beatmap . VolumeChanges [ 0 ] [ "volume" ] ) ;
Conductor . instance . firstBeatOffset = Beatmap . data . offset ;
2023-12-26 05:22:51 +00:00
if ( ! playMode )
2023-05-07 20:33:15 +00:00
{
Stop ( 0 ) ;
}
2024-02-11 05:24:52 +00:00
SetCurrentEventToClosest ( 0 , true ) ;
2023-06-12 21:18:37 +00:00
if ( editor )
{
if ( Beatmap . data . riqOrigin ! = "HeavenStudio" )
{
2023-07-29 02:23:07 +00:00
string origin = Beatmap . data . riqOrigin ? . DisplayName ( ) ? ? "Unknown Origin" ;
2023-09-11 22:28:04 +00:00
GlobalGameManager . ShowErrorMessage ( "Warning" ,
2023-07-29 02:23:07 +00:00
$"This chart came from\n<alpha=#AA>{origin}</color>\nand uses content not included in Heaven Studio.\n\n<color=\" yellow \ ">You may be able to edit this chart in Heaven Studio to be used in its original program.</color>" ) ;
2023-06-12 21:18:37 +00:00
}
}
2022-01-30 12:03:37 +00:00
}
2023-12-26 05:22:51 +00:00
public void ScoreInputAccuracy ( double beat , double accuracy , bool late , double time , float weight = 1 , bool doDisplay = true )
2023-01-25 03:54:19 +00:00
{
2024-01-14 07:18:46 +00:00
// push the hit event to the timing display
if ( doDisplay )
TimingAccuracyDisplay . instance . MakeAccuracyVfx ( time , late ) ;
2023-12-26 05:22:51 +00:00
if ( weight > 0 & & MarkerWeight > 0 )
{
judgementInfo . inputs . Add ( new JudgementManager . InputInfo
{
beat = beat ,
accuracyState = accuracy ,
timeOffset = time ,
weight = weight * MarkerWeight ,
category = MarkerCategory
} ) ;
2024-01-21 21:50:05 +00:00
if ( accuracy < Minigame . rankOkThreshold )
{
SkillStarManager . instance . KillStar ( ) ;
GoForAPerfect . instance . Miss ( ) ;
SectionMedalsManager . instance . MakeIneligible ( ) ;
noMiss = false ;
}
2023-03-11 04:51:22 +00:00
}
2023-09-11 22:28:04 +00:00
2023-03-11 04:51:22 +00:00
if ( SkillStarManager . instance . IsEligible & & ! skillStarCollected & & accuracy > = 1f )
{
if ( SkillStarManager . instance . DoStarJust ( ) )
skillStarCollected = true ;
}
2023-01-25 03:54:19 +00:00
}
2023-12-26 05:22:51 +00:00
public void DoSectionCompletion ( double beat , bool clear , string name , double score )
{
judgementInfo . medals . Add ( new JudgementManager . MedalInfo
{
beat = beat ,
cleared = clear
} ) ;
}
public List < Minigames . Minigame > SeekAheadAndPreload ( double start , float seekTime = 8f )
2022-06-12 19:32:00 +00:00
{
2023-12-26 05:22:51 +00:00
List < Minigames . Minigame > gamesToPreload = new ( ) ;
2023-09-11 22:28:04 +00:00
List < RiqEntity > entitiesAtSameBeat = ListPool < RiqEntity > . Get ( ) ;
Minigames . Minigame inf ;
2022-06-12 19:32:00 +00:00
//seek ahead to preload games that have assetbundles
2023-09-12 20:38:28 +00:00
if ( currentPreSwitch < allGameSwitches . Count & & currentPreSwitch > = 0 )
2023-09-11 22:28:04 +00:00
{
2023-09-12 20:38:28 +00:00
if ( start + seekTime > = allGameSwitches [ currentPreSwitch ] . beat )
2023-09-11 22:28:04 +00:00
{
2023-09-12 20:38:28 +00:00
string gameName = allGameSwitches [ currentPreSwitch ] . datamodel . Split ( '/' ) [ 2 ] ;
2023-09-11 22:28:04 +00:00
inf = GetGameInfo ( gameName ) ;
2023-12-26 05:22:51 +00:00
if ( inf ! = null & & ! ( inf . inferred | | inf . fxOnly ) )
2022-06-12 19:32:00 +00:00
{
2024-02-11 05:24:52 +00:00
if ( inf . usesAssetBundle & & ! ( inf . AssetsLoaded | | inf . AlreadyLoading ) )
2023-12-26 05:22:51 +00:00
{
gamesToPreload . Add ( inf ) ;
Debug . Log ( $"ASYNC loading assetbundles for game {gameName}" ) ;
inf . LoadAssetsAsync ( ) . Forget ( ) ;
}
2022-06-12 19:32:00 +00:00
}
currentPreSwitch + + ;
}
}
//then check game entities
2023-06-10 19:13:29 +00:00
if ( currentPreEvent < Beatmap . Entities . Count & & currentPreEvent > = 0 )
2022-06-12 19:32:00 +00:00
{
2023-09-11 22:28:04 +00:00
if ( start + seekTime > = eventBeats [ currentPreEvent ] )
2022-06-12 19:32:00 +00:00
{
2023-09-11 22:28:04 +00:00
foreach ( RiqEntity entity in Beatmap . Entities )
{
if ( entity . beat = = Beatmap . Entities [ currentPreEvent ] . beat & & ! EventCaller . FXOnlyGames ( ) . Contains ( eventCaller . GetMinigame ( entity . datamodel . Split ( '/' ) [ 0 ] ) ) )
{
entitiesAtSameBeat . Add ( entity ) ;
}
}
2023-01-19 02:31:08 +00:00
SortEventsByPriority ( entitiesAtSameBeat ) ;
2023-06-10 19:13:29 +00:00
foreach ( RiqEntity entity in entitiesAtSameBeat )
2022-06-12 19:32:00 +00:00
{
2023-01-12 01:42:12 +00:00
string gameName = entity . datamodel . Split ( '/' ) [ 0 ] ;
2023-09-11 22:28:04 +00:00
inf = GetGameInfo ( gameName ) ;
2023-12-26 05:22:51 +00:00
if ( inf ! = null & & ! ( inf . inferred | | inf . fxOnly ) )
2022-06-12 19:32:00 +00:00
{
2023-12-26 05:22:51 +00:00
if ( inf . usesAssetBundle & & ! inf . AssetsLoaded )
{
gamesToPreload . Add ( inf ) ;
Debug . Log ( $"ASYNC loading assetbundles for game {gameName}" ) ;
inf . LoadAssetsAsync ( ) . Forget ( ) ;
}
2022-06-12 19:32:00 +00:00
}
2023-01-12 01:42:12 +00:00
currentPreEvent + + ;
2022-06-12 19:32:00 +00:00
}
}
}
2023-09-11 22:28:04 +00:00
ListPool < RiqEntity > . Release ( entitiesAtSameBeat ) ;
2023-12-26 05:22:51 +00:00
return gamesToPreload ;
2022-06-12 19:32:00 +00:00
}
2023-02-18 22:12:49 +00:00
public void SeekAheadAndDoPreEvent ( double start )
2023-01-05 04:04:31 +00:00
{
2023-06-10 19:13:29 +00:00
if ( currentPreSequence < Beatmap . Entities . Count & & currentPreSequence > = 0 )
2023-01-05 04:04:31 +00:00
{
2024-01-25 17:29:05 +00:00
List < RiqEntity > entitiesInRange = ListPool < RiqEntity > . Get ( ) ;
while ( currentPreSequence < preSequenceBeats . Count & & start > = preSequenceBeats [ currentPreSequence ] )
2023-09-11 22:28:04 +00:00
{
2024-01-25 17:29:05 +00:00
entitiesInRange . Clear ( ) ;
2024-01-15 02:04:10 +00:00
foreach ( RiqEntity entity in Beatmap . Entities )
2023-09-11 22:28:04 +00:00
{
2024-01-15 02:04:10 +00:00
string [ ] entityDatamodel = entity . datamodel . Split ( '/' ) ;
double seekTime = eventCaller . GetGameAction ( entityDatamodel [ 0 ] , entityDatamodel [ 1 ] ) . preFunctionLength ;
if ( entity . beat - seekTime = = preSequenceBeats [ currentPreSequence ] )
{
entitiesInRange . Add ( entity ) ;
}
2023-09-11 22:28:04 +00:00
}
2024-01-15 02:04:10 +00:00
SortEventsByPriority ( entitiesInRange ) ;
2023-02-18 22:12:49 +00:00
2024-01-15 02:04:10 +00:00
foreach ( RiqEntity entity in entitiesInRange )
2023-01-05 04:04:31 +00:00
{
2023-01-12 01:42:12 +00:00
string gameName = entity . datamodel . Split ( '/' ) [ 0 ] ;
var inf = GetGameInfo ( gameName ) ;
2023-09-11 22:28:04 +00:00
if ( inf ! = null & & inf . usesAssetBundle & & inf . AssetsLoaded & & ! inf . SequencesPreloaded )
2023-01-12 01:42:12 +00:00
{
2024-01-25 17:29:05 +00:00
Debug . Log ( $"Preparing game {gameName}" ) ;
2023-01-12 01:42:12 +00:00
PreloadGameSequences ( gameName ) ;
}
2023-06-10 19:13:29 +00:00
eventCaller . CallPreEvent ( entity ) ;
2024-01-15 02:04:10 +00:00
currentPreSequence + + ;
2023-01-05 04:04:31 +00:00
}
}
2024-01-25 17:29:05 +00:00
ListPool < RiqEntity > . Release ( entitiesInRange ) ;
2023-01-05 04:04:31 +00:00
}
}
2022-02-20 17:28:56 +00:00
private void Update ( )
2021-12-21 01:10:49 +00:00
{
2023-12-26 05:22:51 +00:00
if ( BeatmapEntities ( ) < 1 )
2021-12-21 01:10:49 +00:00
return ;
2022-01-07 11:36:23 +00:00
if ( ! Conductor . instance . isPlaying )
2021-12-30 12:17:22 +00:00
return ;
2023-03-11 04:51:22 +00:00
Conductor cond = Conductor . instance ;
2023-12-26 05:22:51 +00:00
double clampedBeat = Math . Max ( cond . songPositionInBeatsAsDouble , 0 ) ;
2021-12-19 04:10:43 +00:00
2023-06-10 19:13:29 +00:00
if ( currentTempoEvent < Beatmap . TempoChanges . Count & & currentTempoEvent > = 0 )
2022-06-09 03:35:15 +00:00
{
2023-09-11 22:28:04 +00:00
if ( cond . songPositionInBeatsAsDouble > = tempoBeats [ currentTempoEvent ] )
2022-06-09 03:35:15 +00:00
{
2023-06-10 19:13:29 +00:00
cond . SetBpm ( Beatmap . TempoChanges [ currentTempoEvent ] [ "tempo" ] ) ;
2022-06-09 03:35:15 +00:00
currentTempoEvent + + ;
}
}
2023-06-10 19:13:29 +00:00
if ( currentVolumeEvent < Beatmap . VolumeChanges . Count & & currentVolumeEvent > = 0 )
2022-09-18 20:48:14 +00:00
{
2023-09-11 22:28:04 +00:00
if ( cond . songPositionInBeatsAsDouble > = volumeBeats [ currentVolumeEvent ] )
2022-09-18 20:48:14 +00:00
{
2023-06-10 19:13:29 +00:00
cond . SetVolume ( Beatmap . VolumeChanges [ currentVolumeEvent ] [ "volume" ] ) ;
2022-09-18 20:48:14 +00:00
currentVolumeEvent + + ;
}
}
2023-06-10 19:13:29 +00:00
if ( currentSectionEvent < Beatmap . SectionMarkers . Count & & currentSectionEvent > = 0 )
2022-09-18 20:48:14 +00:00
{
2023-09-11 22:28:04 +00:00
if ( cond . songPositionInBeatsAsDouble > = sectionBeats [ currentSectionEvent ] )
2022-09-18 20:48:14 +00:00
{
2023-12-26 05:22:51 +00:00
RiqEntity marker = Beatmap . SectionMarkers [ currentSectionEvent ] ;
if ( ! string . IsNullOrEmpty ( marker [ "sectionName" ] ) )
{
Debug . Log ( "Section " + marker [ "sectionName" ] + " started" ) ;
lastSection = currentSection ;
if ( currentSectionEvent < Beatmap . SectionMarkers . Count )
currentSection = marker ;
else
currentSection = null ;
nextSectionBeat = endBeat ;
foreach ( RiqEntity futureSection in Beatmap . SectionMarkers )
{
if ( futureSection . beat < marker . beat ) continue ;
if ( futureSection = = marker ) continue ;
if ( ! string . IsNullOrEmpty ( futureSection [ "sectionName" ] ) )
{
nextSectionBeat = futureSection . beat ;
break ;
}
}
onSectionChange ? . Invoke ( currentSection , lastSection ) ;
}
if ( OverlaysManager . OverlaysEnabled )
{
if ( PersistentDataManager . gameSettings . perfectChallengeType ! = PersistentDataManager . PerfectChallengeType . Off )
{
if ( marker [ "startPerfect" ] & & GoForAPerfect . instance ! = null & & GoForAPerfect . instance . perfect & & ! GoForAPerfect . instance . gameObject . activeSelf )
{
GoForAPerfect . instance . Enable ( marker . beat ) ;
}
}
}
MarkerWeight = marker [ "weight" ] ;
MarkerCategory = marker [ "category" ] ;
2022-09-18 20:48:14 +00:00
currentSectionEvent + + ;
}
}
2023-11-23 16:19:39 +00:00
if ( cond . songPositionInBeatsAsDouble > = Math . Ceiling ( _playStartBeat ) + _pulseTally )
{
2024-01-25 17:29:05 +00:00
if ( minigame ! = null ) minigame . OnBeatPulse ( Math . Ceiling ( _playStartBeat ) + _pulseTally ) ;
2023-11-23 16:19:39 +00:00
onBeatPulse ? . Invoke ( Math . Ceiling ( _playStartBeat ) + _pulseTally ) ;
_pulseTally + + ;
}
2022-06-12 19:32:00 +00:00
float seekTime = 8f ;
//seek ahead to preload games that have assetbundles
2023-12-26 05:22:51 +00:00
SeekAheadAndPreload ( clampedBeat , seekTime ) ;
SeekAheadAndDoPreEvent ( clampedBeat ) ;
2023-01-05 04:04:31 +00:00
2023-06-10 19:13:29 +00:00
if ( currentEvent < Beatmap . Entities . Count & & currentEvent > = 0 )
2021-12-19 04:10:43 +00:00
{
2024-01-25 17:29:05 +00:00
List < RiqEntity > entitiesInRange = 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
while ( currentEvent < eventBeats . Count & & clampedBeat > = eventBeats [ currentEvent ] & & Conductor . instance . isPlaying )
2021-12-19 04:10:43 +00:00
{
2024-01-25 17:29:05 +00:00
fxEntities . Clear ( ) ;
entitiesInRange . Clear ( ) ;
2023-09-11 22:28:04 +00:00
using ( PooledObject < List < RiqEntity > > pool = ListPool < RiqEntity > . Get ( out List < RiqEntity > currentBeatEntities ) )
{
2024-01-15 02:04:10 +00:00
currentBeatEntities = Beatmap . Entities . FindAll ( c = > c . beat = = eventBeats [ currentEvent ] ) ;
2023-09-11 22:28:04 +00:00
foreach ( RiqEntity entity in currentBeatEntities )
{
if ( EventCaller . FXOnlyGames ( ) . Contains ( eventCaller . GetMinigame ( entity . datamodel . Split ( '/' ) [ 0 ] ) ) )
{
fxEntities . Add ( entity ) ;
}
else
{
2024-01-15 02:04:10 +00:00
entitiesInRange . Add ( entity ) ;
2023-09-11 22:28:04 +00:00
}
}
}
2021-12-31 14:46:11 +00:00
2023-01-19 02:31:08 +00:00
SortEventsByPriority ( fxEntities ) ;
2024-01-15 02:04:10 +00:00
SortEventsByPriority ( entitiesInRange ) ;
2023-01-19 02:31:08 +00:00
2022-02-03 03:58:08 +00:00
// FX entities should ALWAYS execute before gameplay entities
2023-09-11 22:28:04 +00:00
foreach ( RiqEntity entity in fxEntities )
2021-12-31 14:46:11 +00:00
{
2023-09-11 22:28:04 +00:00
eventCaller . CallEvent ( entity , true ) ;
2022-01-31 05:02:36 +00:00
currentEvent + + ;
2021-12-31 14:46:11 +00:00
}
2021-12-23 00:08:35 +00:00
2024-01-15 02:04:10 +00:00
foreach ( RiqEntity entity in entitiesInRange )
2021-12-26 05:11:54 +00:00
{
2022-01-15 17:45:08 +00:00
// if game isn't loaded, preload game so whatever event that would be called will still run outside if needed
2023-01-12 01:42:12 +00:00
if ( entity . datamodel . Split ( '/' ) [ 0 ] ! = currentGame )
2021-12-31 14:46:11 +00:00
{
2023-01-12 01:42:12 +00:00
eventCaller . CallEvent ( entity , false ) ;
2022-03-02 21:59:35 +00:00
}
else
{
2023-01-12 01:42:12 +00:00
eventCaller . CallEvent ( entity , true ) ;
2021-12-31 14:46:11 +00:00
}
2022-01-31 04:59:15 +00:00
// Thank you to @shshwdr for bring this to my attention
currentEvent + + ;
2021-12-26 05:11:54 +00:00
}
2021-12-19 04:10:43 +00:00
}
2024-01-25 17:29:05 +00:00
ListPool < RiqEntity > . Release ( entitiesInRange ) ;
ListPool < RiqEntity > . Release ( fxEntities ) ;
2021-12-19 04:10:43 +00:00
}
2023-03-11 04:51:22 +00:00
if ( currentSection = = null )
{
2023-12-26 05:22:51 +00:00
SectionProgress = 0 ;
2023-03-11 04:51:22 +00:00
}
else
{
2023-06-10 19:13:29 +00:00
double currectSectionStart = cond . GetSongPosFromBeat ( currentSection . beat ) ;
2023-03-11 04:51:22 +00:00
2023-12-26 05:22:51 +00:00
SectionProgress = ( cond . songPosition - currectSectionStart ) / ( cond . GetSongPosFromBeat ( nextSectionBeat ) - currectSectionStart ) ;
2023-03-11 04:51:22 +00:00
}
}
2023-09-11 22:28:04 +00:00
private void LateUpdate ( )
{
2023-03-11 04:51:22 +00:00
OverlaysManager . instance . TogleOverlaysVisibility ( Editor . Editor . instance = = null | | Editor . Editor . instance . fullscreen | | ( ( PersistentDataManager . gameSettings . overlaysInEditor ) & & ( ! Editor . Editor . instance . fullscreen ) ) | | HeavenStudio . Editor . GameSettings . InPreview ) ;
2024-01-15 02:04:10 +00:00
2023-12-31 04:06:31 +00:00
if ( ! Conductor . instance . isPlaying )
return ;
2024-01-15 02:04:10 +00:00
2023-12-31 04:06:31 +00:00
if ( Conductor . instance . songPositionInBeatsAsDouble > = Math . Ceiling ( _playStartBeat ) + _latePulseTally )
{
2024-01-25 17:29:05 +00:00
if ( minigame ! = null ) minigame . OnLateBeatPulse ( Math . Ceiling ( _playStartBeat ) + _latePulseTally ) ;
2023-12-31 04:06:31 +00:00
onBeatPulse ? . Invoke ( Math . Ceiling ( _playStartBeat ) + _latePulseTally ) ;
_latePulseTally + + ;
}
2021-12-19 04:10:43 +00:00
}
Advanced Blocks (#720)
* play sfx and play animation blocks
i also changed prescheduleFunction to preFunction, and removed the unused preFunction argument in GameAction
i can revert this if need be but it just seemed vestigial
* count in rework + preloading, multisound addition
multisound was using an array that was converted to a list..?
very silly when you consider it's a list first so sometimes it's list -> array -> list lol
new Count-In and Play SFX block preloads sfx now!! epic.
* prefab-ify event properties, Button EntityType
* things are very nearly working!
however i just hit an insane hurdle. how do i modify a dropdown while still being able to access the index/int value of that param directly. UGHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
* okay it's WORKING now
i just need to do some better dropdown stuff
* ITS WORKING ITS WORKING ITS WORKING
arbitrary animations, now accessible to those without prefab knowledge! and it's piss easy to use!!
* about to make a struct + class, tooltip improvements
gonna make the struct define it, then the class will actually be the dropdown
this is gonna make things so so so so much easier to comprehend
* finishing up, probably one more commit after this
* split up Dropdown into Dropdown and DropdownObj, which basically fixed all of my problems lol
* fixed a count bug
* added param tooltip toggle
* grah it's ALMOST DONE
* it's 99.9% finished.
just some touch ups, i don't think i even know of any bugs
* alright, looks like that's all the bugs gone
* EVERYTHING IS FINISHED!!
2024-02-26 01:46:23 +00:00
public static void PlaySFXArbitrary ( double beat , float length , string game , string name , float pitch , float volume , bool looping , int offset )
{
if ( string . IsNullOrEmpty ( name ) ) return ;
Sound sound ;
if ( game = = "common" ) {
sound = SoundByte . PlayOneShot ( name , beat , pitch , volume , looping , null , ( offset / 1000f ) ) ;
} else {
SoundByte . PreloadGameAudioClips ( game ) ;
sound = SoundByte . PlayOneShotGame ( game + "/" + name , beat , pitch , volume , looping , true , ( offset / 1000f ) ) ;
}
if ( looping ) {
BeatAction . New ( null , new ( ) {
new ( beat + length , ( ) = > sound . KillLoop ( 0 ) ) ,
} ) ;
}
}
public void PlayAnimationArbitrary ( string animator , string animation , float scale )
{
Transform animTrans = minigameObj . transform . Find ( animator ) ;
if ( animTrans ! = null & & animTrans . TryGetComponent ( out Animator anim ) ) {
anim . DoScaledAnimationAsync ( animation , scale ) ;
}
}
2022-03-01 07:27:49 +00:00
public void ToggleInputs ( bool inputs )
{
canInput = inputs ;
}
2024-01-25 17:29:05 +00:00
public void ToggleAutoplay ( bool auto )
{
autoplay = auto ;
}
public void TogglePlayMode ( bool mode )
{
playMode = mode ;
}
2022-01-14 00:35:41 +00:00
#region Play Events
2023-11-23 16:19:39 +00:00
private double _playStartBeat = 0 ;
private int _pulseTally = 0 ;
2023-12-31 04:06:31 +00:00
private int _latePulseTally = 0 ;
2023-11-23 16:19:39 +00:00
2023-06-10 19:13:29 +00:00
public void Play ( double beat , float delay = 0f )
2022-01-14 00:35:41 +00:00
{
2023-05-07 20:33:15 +00:00
bool paused = Conductor . instance . isPaused ;
Debug . Log ( "Playing at " + beat ) ;
2023-11-23 16:19:39 +00:00
_playStartBeat = beat ;
_pulseTally = 0 ;
2023-12-31 04:06:31 +00:00
_latePulseTally = 0 ;
2022-03-01 07:27:49 +00:00
canInput = true ;
2023-05-07 20:33:15 +00:00
if ( ! paused )
{
inputOffsetSamples . Clear ( ) ;
averageInputOffset = 0 ;
2023-01-25 03:54:19 +00:00
2023-05-07 20:33:15 +00:00
TimingAccuracyDisplay . instance . ResetArrow ( ) ;
SkillStarManager . instance . Reset ( ) ;
skillStarCollected = false ;
2024-01-21 21:50:05 +00:00
noMiss = true ;
2023-03-11 04:51:22 +00:00
2023-05-07 20:33:15 +00:00
GoForAPerfect . instance . perfect = true ;
GoForAPerfect . instance . Disable ( ) ;
2023-03-11 04:51:22 +00:00
2023-05-07 20:33:15 +00:00
SectionMedalsManager . instance . Reset ( ) ;
clearedSections . Clear ( ) ;
2023-12-26 05:22:51 +00:00
judgementInfo = new JudgementManager . JudgementInfo
{
inputs = new List < JudgementManager . InputInfo > ( ) ,
medals = new List < JudgementManager . MedalInfo > ( )
} ;
MarkerWeight = 1 ;
MarkerCategory = 0 ;
if ( playMode & & delay > 0 )
{
GlobalGameManager . ForceFade ( 0 , delay * 0.5f , delay * 0.5f ) ;
}
2023-05-07 20:33:15 +00:00
}
2023-03-11 04:51:22 +00:00
2023-05-07 20:33:15 +00:00
StartCoroutine ( PlayCo ( beat , delay ) ) ;
2023-11-21 16:57:03 +00:00
//onBeatChanged?.Invoke(beat);
2022-01-14 00:35:41 +00:00
}
2023-06-10 19:13:29 +00:00
private IEnumerator PlayCo ( double beat , float delay = 0f )
2022-01-14 00:35:41 +00:00
{
2022-01-14 02:33:51 +00:00
bool paused = Conductor . instance . isPaused ;
2022-01-28 02:50:57 +00:00
2023-12-26 05:22:51 +00:00
if ( ! paused )
2023-05-07 20:33:15 +00:00
{
2023-06-10 19:13:29 +00:00
Conductor . instance . SetBpm ( Beatmap . TempoChanges [ 0 ] [ "tempo" ] ) ;
Conductor . instance . SetVolume ( Beatmap . VolumeChanges [ 0 ] [ "volume" ] ) ;
Conductor . instance . firstBeatOffset = Beatmap . data . offset ;
2022-01-14 02:33:51 +00:00
SetCurrentEventToClosest ( beat ) ;
2023-05-07 20:33:15 +00:00
KillAllSounds ( ) ;
2022-01-21 01:24:30 +00:00
2023-12-26 05:22:51 +00:00
if ( delay > 0 )
{
yield return new WaitForSeconds ( delay ) ;
}
2024-01-13 18:31:05 +00:00
Conductor . instance . PlaySetup ( beat ) ;
2023-12-26 05:22:51 +00:00
Minigame miniGame = null ;
2024-01-25 17:29:05 +00:00
if ( minigameObj ! = null & & minigameObj . TryGetComponent < Minigame > ( out miniGame ) )
2023-12-26 05:22:51 +00:00
{
if ( miniGame ! = null )
{
miniGame . OnPlay ( beat ) ;
}
}
onPlay ? . Invoke ( beat ) ;
bool hasStartPerfect = false ;
foreach ( RiqEntity marker in Beatmap . SectionMarkers )
{
if ( marker [ "startPerfect" ] )
{
hasStartPerfect = true ;
break ;
}
}
if ( OverlaysManager . OverlaysEnabled & & ! hasStartPerfect )
{
if ( PersistentDataManager . gameSettings . perfectChallengeType ! = PersistentDataManager . PerfectChallengeType . Off )
{
GoForAPerfect . instance . Enable ( 0 ) ;
}
}
}
else
{
onUnPause ? . Invoke ( beat ) ;
2023-09-12 20:38:28 +00:00
}
2023-10-29 19:44:47 +00:00
2023-12-26 05:22:51 +00:00
if ( playMode )
{
CircleCursor . LockCursor ( true ) ;
}
2023-10-29 19:44:47 +00:00
Application . backgroundLoadingPriority = ThreadPriority . Low ;
2023-09-12 20:38:28 +00:00
Conductor . instance . Play ( beat ) ;
2022-01-14 00:35:41 +00:00
}
public void Pause ( )
{
Conductor . instance . Pause ( ) ;
2023-06-10 19:13:29 +00:00
Util . SoundByte . PauseOneShots ( ) ;
2023-12-26 05:22:51 +00:00
onPause ? . Invoke ( Conductor . instance . songPositionInBeatsAsDouble ) ;
2023-05-07 20:33:15 +00:00
canInput = false ;
2022-01-14 00:35:41 +00:00
}
2023-06-10 19:13:29 +00:00
public void Stop ( double beat , bool restart = false , float restartDelay = 0f )
2022-01-14 00:35:41 +00:00
{
2023-12-26 05:22:51 +00:00
// I feel like I should standardize the names
if ( Conductor . instance . isPlaying )
{
SkillStarManager . instance . KillStar ( ) ;
TimingAccuracyDisplay . instance . StopStarFlash ( ) ;
GoForAPerfect . instance . Disable ( ) ;
SectionMedalsManager . instance . OnRemixEnd ( endBeat , currentSection ) ;
}
Minigame miniGame ;
2024-01-25 17:29:05 +00:00
if ( minigameObj ! = null & & minigameObj . TryGetComponent < Minigame > ( out miniGame ) )
2023-12-26 05:22:51 +00:00
{
if ( miniGame ! = null )
{
miniGame . OnStop ( beat ) ;
}
}
2023-05-07 20:33:15 +00:00
2022-01-14 00:35:41 +00:00
Conductor . instance . Stop ( beat ) ;
SetCurrentEventToClosest ( beat ) ;
2023-09-11 22:28:04 +00:00
KillAllSounds ( ) ;
2023-12-26 05:22:51 +00:00
if ( restart )
2023-01-12 01:42:12 +00:00
{
2023-05-07 20:33:15 +00:00
Play ( 0 , restartDelay ) ;
2023-01-12 01:42:12 +00:00
}
2023-12-26 05:22:51 +00:00
else if ( playMode )
2023-10-29 19:44:47 +00:00
{
2024-01-21 21:50:05 +00:00
exiting = true ;
2023-12-26 05:22:51 +00:00
judgementInfo . star = skillStarCollected ;
judgementInfo . perfect = GoForAPerfect . instance . perfect ;
2024-01-21 21:50:05 +00:00
judgementInfo . noMiss = noMiss ;
2023-12-26 05:22:51 +00:00
judgementInfo . time = DateTime . Now ;
JudgementManager . SetPlayInfo ( judgementInfo , Beatmap ) ;
2024-01-21 21:50:05 +00:00
GlobalGameManager . LoadScene ( "Judgement" , 0.35f , 0f , DestroyGame ) ;
2023-12-26 05:22:51 +00:00
CircleCursor . LockCursor ( false ) ;
2023-10-29 19:44:47 +00:00
}
2023-12-26 05:22:51 +00:00
Application . backgroundLoadingPriority = ThreadPriority . Normal ;
2023-05-07 20:33:15 +00:00
}
2023-12-26 05:22:51 +00:00
public void SafePlay ( double beat , float delay , bool discord )
{
StartCoroutine ( WaitReadyAndPlayCo ( beat , delay , discord ) ) ;
}
private IEnumerator WaitReadyAndPlayCo ( double beat , float delay = 1f , bool discord = true )
2023-05-07 20:33:15 +00:00
{
2024-02-11 05:24:52 +00:00
SoundByte . UnloadAudioClips ( ) ;
SoundByte . PreloadAudioClipAsync ( "skillStar" ) ;
SoundByte . PreloadAudioClipAsync ( "perfectMiss" ) ;
2023-09-11 22:28:04 +00:00
WaitUntil yieldOverlays = new WaitUntil ( ( ) = > OverlaysManager . OverlaysReady ) ;
2023-12-26 05:22:51 +00:00
WaitUntil yieldBeatmap = new WaitUntil ( ( ) = > Beatmap ! = null & & BeatmapEntities ( ) > 0 ) ;
2023-09-11 22:28:04 +00:00
WaitUntil yieldAudio = new WaitUntil ( ( ) = > AudioLoadDone | | ( ChartLoadError & & ! GlobalGameManager . IsShowingDialog ) ) ;
2023-12-26 05:22:51 +00:00
WaitUntil yieldGame = null ;
List < Minigames . Minigame > gamesToPreload = SeekAheadAndPreload ( beat , 4f ) ;
Debug . Log ( $"Preloading {gamesToPreload.Count} games" ) ;
if ( gamesToPreload . Count > 0 )
{
yieldGame = new WaitUntil ( ( ) = > gamesToPreload . All ( x = > x . AssetsLoaded ) ) ;
}
2023-09-11 22:28:04 +00:00
2023-05-07 20:33:15 +00:00
// wait for overlays to be ready
2023-12-26 05:22:51 +00:00
Debug . Log ( "waiting for overlays" ) ;
2023-09-11 22:28:04 +00:00
yield return yieldOverlays ;
2023-12-26 05:22:51 +00:00
// wait for beatmap to be loaded
Debug . Log ( "waiting for beatmap" ) ;
2023-09-11 22:28:04 +00:00
yield return yieldBeatmap ;
2023-06-10 19:13:29 +00:00
//wait for audio clip to be loaded
2023-12-26 05:22:51 +00:00
Debug . Log ( "waiting for audio" ) ;
2023-09-11 22:28:04 +00:00
yield return yieldAudio ;
2023-12-26 05:22:51 +00:00
//wait for games to be loaded
Debug . Log ( "waiting for minigames" ) ;
if ( yieldGame ! = null )
yield return yieldGame ;
2023-05-07 20:33:15 +00:00
SkillStarManager . instance . KillStar ( ) ;
TimingAccuracyDisplay . instance . StopStarFlash ( ) ;
GoForAPerfect . instance . Disable ( ) ;
2023-12-26 05:22:51 +00:00
SectionMedalsManager . instance ? . Reset ( ) ;
2023-05-07 20:33:15 +00:00
2023-12-26 05:22:51 +00:00
if ( discord )
{
GlobalGameManager . UpdateDiscordStatus ( Beatmap [ "remixtitle" ] . ToString ( ) , false , true ) ;
}
2023-05-07 20:33:15 +00:00
2023-12-26 05:22:51 +00:00
Play ( beat , delay ) ;
2023-09-11 22:28:04 +00:00
yield break ;
2022-03-07 09:16:31 +00:00
}
public void KillAllSounds ( )
{
2023-09-11 22:28:04 +00:00
Debug . Log ( "Killing all sounds" ) ;
2022-03-07 09:41:07 +00:00
SoundObjects . Clear ( ) ;
2024-02-11 05:24:52 +00:00
SoundByte . KillOneShots ( ) ;
2022-01-14 00:35:41 +00:00
}
#endregion
#region List Functions
2021-12-21 01:10:49 +00:00
public void SortEventsList ( )
2021-12-19 04:10:43 +00:00
{
2023-06-10 19:13:29 +00:00
Beatmap . Entities . Sort ( ( x , y ) = > x . beat . CompareTo ( y . beat ) ) ;
Beatmap . TempoChanges . Sort ( ( x , y ) = > x . beat . CompareTo ( y . beat ) ) ;
Beatmap . VolumeChanges . Sort ( ( x , y ) = > x . beat . CompareTo ( y . beat ) ) ;
Beatmap . SectionMarkers . Sort ( ( x , y ) = > x . beat . CompareTo ( y . beat ) ) ;
2023-09-11 22:28:04 +00:00
eventBeats = Beatmap . Entities . Select ( c = > c . beat ) . ToList ( ) ;
tempoBeats = Beatmap . TempoChanges . Select ( c = > c . beat ) . ToList ( ) ;
volumeBeats = Beatmap . VolumeChanges . Select ( c = > c . beat ) . ToList ( ) ;
sectionBeats = Beatmap . SectionMarkers . Select ( c = > c . beat ) . ToList ( ) ;
2023-09-12 20:38:28 +00:00
allGameSwitches = EventCaller . GetAllInGameManagerList ( "gameManager" , new string [ ] { "switchGame" } ) ;
2024-01-15 02:04:10 +00:00
preSequenceBeats = new List < double > ( ) ;
foreach ( RiqEntity entity in Beatmap . Entities )
{
string [ ] seekEntityDatamodel = entity . datamodel . Split ( '/' ) ;
double seekTime = eventCaller . GetGameAction ( seekEntityDatamodel [ 0 ] , seekEntityDatamodel [ 1 ] ) . preFunctionLength ;
preSequenceBeats . Add ( entity . beat - seekTime ) ;
}
preSequenceBeats . Sort ( ) ;
2021-12-19 04:10:43 +00:00
}
2021-12-21 01:10:49 +00:00
2023-06-10 19:13:29 +00:00
void SortEventsByPriority ( List < RiqEntity > entities )
2023-01-19 02:31:08 +00:00
{
2023-09-12 20:38:28 +00:00
string [ ] xDatamodel ;
string [ ] yDatamodel ;
2023-09-11 22:28:04 +00:00
entities . Sort ( ( x , y ) = >
{
2023-09-12 20:38:28 +00:00
xDatamodel = x . datamodel . Split ( '/' ) ;
yDatamodel = y . datamodel . Split ( '/' ) ;
2024-01-08 06:38:05 +00:00
Minigames . GameAction xAction = eventCaller . GetGameAction ( xDatamodel [ 0 ] , xDatamodel [ 1 ] ) ;
Minigames . GameAction yAction = eventCaller . GetGameAction ( yDatamodel [ 0 ] , yDatamodel [ 1 ] ) ;
2023-01-19 02:31:08 +00:00
return yAction . priority . CompareTo ( xAction . priority ) ;
} ) ;
}
2023-06-10 19:13:29 +00:00
public static double GetClosestInList ( List < double > list , double compareTo )
{
if ( list . Count > 0 )
return list . Aggregate ( ( x , y ) = > Math . Abs ( x - compareTo ) < Math . Abs ( y - compareTo ) ? x : y ) ;
else
return double . MinValue ;
}
2023-09-11 22:28:04 +00:00
public static int GetIndexAfter ( List < double > list , double compareTo )
{
list . Sort ( ) ;
if ( list . Count > 0 )
{
foreach ( double item in list )
{
if ( item > = compareTo )
{
return Math . Max ( list . IndexOf ( item ) , 0 ) ;
}
}
return list . Count ;
}
return 0 ;
}
public static int GetIndexBefore ( List < double > list , double compareTo )
{
list . Sort ( ) ;
if ( list . Count > 0 )
{
foreach ( double item in list )
{
if ( item > = compareTo )
{
return Math . Max ( list . IndexOf ( item ) - 1 , 0 ) ;
}
}
return list . Count - 1 ;
}
return 0 ;
}
2024-02-11 05:24:52 +00:00
public void SetCurrentEventToClosest ( double beat , bool canPreload = false )
2021-12-19 04:10:43 +00:00
{
2022-01-09 23:35:55 +00:00
SortEventsList ( ) ;
2022-02-08 01:07:03 +00:00
onBeatChanged ? . Invoke ( beat ) ;
2023-06-10 19:13:29 +00:00
if ( Beatmap . Entities . Count > 0 )
2021-12-21 01:10:49 +00:00
{
2023-09-11 22:28:04 +00:00
currentEvent = GetIndexAfter ( eventBeats , beat ) ;
currentPreEvent = GetIndexAfter ( eventBeats , beat ) ;
currentPreSequence = GetIndexAfter ( eventBeats , beat ) ;
2021-12-31 14:46:11 +00:00
2023-09-12 20:38:28 +00:00
var gameSwitchs = Beatmap . Entities . FindAll ( c = > c . datamodel . Split ( "/" ) [ 1 ] = = "switchGame" ) ;
2022-01-15 17:45:08 +00:00
2023-09-11 22:28:04 +00:00
string newGame = Beatmap . Entities [ Math . Min ( currentEvent , eventBeats . Count - 1 ) ] . datamodel . Split ( 0 ) ;
2022-01-07 23:51:08 +00:00
2022-01-15 17:45:08 +00:00
if ( gameSwitchs . Count > 0 )
2021-12-31 14:46:11 +00:00
{
2023-09-11 22:28:04 +00:00
int index = GetIndexBefore ( gameSwitchs . Select ( c = > c . beat ) . ToList ( ) , beat ) ;
2022-06-12 19:32:00 +00:00
currentPreSwitch = index ;
2022-01-15 18:46:50 +00:00
var closestGameSwitch = gameSwitchs [ index ] ;
if ( closestGameSwitch . beat < = beat )
{
newGame = closestGameSwitch . datamodel . Split ( 2 ) ;
}
else if ( closestGameSwitch . beat > beat )
{
2022-01-30 01:26:53 +00:00
if ( index = = 0 )
2022-01-15 18:46:50 +00:00
{
2023-06-10 19:13:29 +00:00
newGame = Beatmap . Entities [ 0 ] . datamodel . Split ( 0 ) ;
2022-01-15 18:46:50 +00:00
}
else
{
2022-01-30 01:26:53 +00:00
if ( index - 1 > = 0 )
{
newGame = gameSwitchs [ index - 1 ] . datamodel . Split ( 2 ) ;
}
else
{
2023-06-10 19:13:29 +00:00
newGame = Beatmap . Entities [ Beatmap . Entities . IndexOf ( closestGameSwitch ) - 1 ] . datamodel . Split ( 0 ) ;
2022-01-30 01:26:53 +00:00
}
2022-01-15 18:46:50 +00:00
}
}
2024-02-16 06:17:16 +00:00
// newGame = gameSwitchs[gameSwitchs.IndexOf(gameSwitchs.Find(c => c.beat == MathUtils.GetClosestInList(gameSwitchs.Select(c => c.beat).ToList(), beat)))].datamodel.Split(2);
2022-01-15 17:45:08 +00:00
}
2022-01-07 23:51:08 +00:00
2022-02-03 03:58:08 +00:00
if ( ! GetGameInfo ( newGame ) . fxOnly )
{
2024-02-11 05:24:52 +00:00
if ( canPreload )
{
StartCoroutine ( WaitAndSetGame ( newGame ) ) ;
}
else
{
SetGame ( newGame ) ;
}
SetCurrentGame ( newGame ) ;
2022-02-03 03:58:08 +00:00
}
2022-09-18 20:48:14 +00:00
2023-06-10 19:13:29 +00:00
List < RiqEntity > allEnds = EventCaller . GetAllInGameManagerList ( "gameManager" , new string [ ] { "end" } ) ;
2022-09-18 20:48:14 +00:00
if ( allEnds . Count > 0 )
endBeat = allEnds . Select ( c = > c . beat ) . Min ( ) ;
else
2023-06-10 19:13:29 +00:00
endBeat = Conductor . instance . SongLengthInBeatsAsDouble ( ) ;
2021-12-21 01:10:49 +00:00
}
2022-01-30 09:09:26 +00:00
else
{
SetGame ( "noGame" ) ;
2023-06-10 19:13:29 +00:00
endBeat = Conductor . instance . SongLengthInBeatsAsDouble ( ) ;
2022-01-30 09:09:26 +00:00
}
2022-01-28 02:50:57 +00:00
2023-06-10 19:13:29 +00:00
if ( Beatmap . TempoChanges . Count > 0 )
2022-01-28 02:50:57 +00:00
{
2022-06-06 16:54:57 +00:00
currentTempoEvent = 0 ;
2022-01-28 02:50:57 +00:00
2022-06-06 16:54:57 +00:00
//for tempo changes, just go over all of em until the last one we pass
2023-09-11 22:28:04 +00:00
for ( int t = 0 ; t < tempoBeats . Count ; t + + )
2022-06-06 16:54:57 +00:00
{
// Debug.Log("checking tempo event " + t + " against beat " + beat + "( tc beat " + tempoChanges[t] + ")");
2023-09-11 22:28:04 +00:00
if ( tempoBeats [ t ] > beat )
2022-06-06 16:54:57 +00:00
{
break ;
}
currentTempoEvent = t ;
}
// Debug.Log("currentTempoEvent is now " + currentTempoEvent);
2022-01-28 02:50:57 +00:00
}
2022-06-12 19:32:00 +00:00
2023-06-10 19:13:29 +00:00
if ( Beatmap . VolumeChanges . Count > 0 )
2022-09-18 20:48:14 +00:00
{
currentVolumeEvent = 0 ;
2023-09-11 22:28:04 +00:00
for ( int t = 0 ; t < volumeBeats . Count ; t + + )
2022-09-18 20:48:14 +00:00
{
2023-09-11 22:28:04 +00:00
if ( volumeBeats [ t ] > beat )
2022-09-18 20:48:14 +00:00
{
break ;
}
currentVolumeEvent = t ;
}
}
2023-12-26 05:22:51 +00:00
lastSection = null ;
currentSection = null ;
2023-06-10 19:13:29 +00:00
if ( Beatmap . SectionMarkers . Count > 0 )
2022-09-18 20:48:14 +00:00
{
currentSectionEvent = 0 ;
2023-09-11 22:28:04 +00:00
for ( int t = 0 ; t < sectionBeats . Count ; t + + )
2022-09-18 20:48:14 +00:00
{
2023-09-11 22:28:04 +00:00
if ( sectionBeats [ t ] > beat )
2022-09-18 20:48:14 +00:00
{
break ;
}
currentSectionEvent = t ;
}
}
2023-12-26 05:22:51 +00:00
onSectionChange ? . Invoke ( currentSection , lastSection ) ;
2022-09-18 20:48:14 +00:00
2022-06-12 19:32:00 +00:00
SeekAheadAndPreload ( beat ) ;
2021-12-19 04:10:43 +00:00
}
2022-01-14 00:35:41 +00:00
#endregion
2023-06-10 19:13:29 +00:00
public void SwitchGame ( string game , double beat , bool flash )
2021-12-24 03:36:16 +00:00
{
2022-01-15 17:45:08 +00:00
if ( game ! = currentGame )
{
if ( currentGameSwitchIE ! = null )
StopCoroutine ( currentGameSwitchIE ) ;
2023-04-07 15:15:19 +00:00
currentGameSwitchIE = StartCoroutine ( SwitchGameIE ( game , beat , flash ) ) ;
2022-01-15 17:45:08 +00:00
}
2021-12-24 03:36:16 +00:00
}
2023-06-10 19:13:29 +00:00
IEnumerator SwitchGameIE ( string game , double beat , bool flash )
2021-12-24 03:36:16 +00:00
{
2023-09-08 14:27:15 +00:00
if ( flash )
2023-04-07 15:15:19 +00:00
{
2023-05-07 20:33:15 +00:00
HeavenStudio . StaticCamera . instance . ToggleCanvasVisibility ( false ) ;
2023-04-07 15:15:19 +00:00
}
2021-12-24 03:36:16 +00:00
2023-07-17 15:56:57 +00:00
SetGame ( game , false ) ;
2021-12-29 06:52:48 +00:00
2023-12-26 05:22:51 +00:00
Minigame miniGame ;
2024-01-25 17:29:05 +00:00
if ( minigameObj ! = null & & minigameObj . TryGetComponent < Minigame > ( out miniGame ) )
2023-12-26 05:22:51 +00:00
{
if ( miniGame ! = null )
{
miniGame . OnGameSwitch ( beat ) ;
}
}
2022-03-08 04:46:49 +00:00
2023-12-26 05:22:51 +00:00
while ( beat + 0.25 > Math . Max ( Conductor . instance . songPositionInBeatsAsDouble , 0 ) )
2023-09-08 14:27:15 +00:00
{
if ( ! Conductor . instance . isPlaying )
{
HeavenStudio . StaticCamera . instance . ToggleCanvasVisibility ( true ) ;
SetAmbientGlowToCurrentMinigameColor ( ) ;
StopCoroutine ( currentGameSwitchIE ) ;
}
yield return null ;
}
2021-12-29 06:52:48 +00:00
2023-05-07 20:33:15 +00:00
HeavenStudio . StaticCamera . instance . ToggleCanvasVisibility ( true ) ;
2023-07-17 15:56:57 +00:00
SetAmbientGlowToCurrentMinigameColor ( ) ;
2021-12-29 06:52:48 +00:00
}
2023-07-17 15:56:57 +00:00
private void SetGame ( string game , bool useMinigameColor = true )
2021-12-29 06:52:48 +00:00
{
2023-11-21 16:57:03 +00:00
ResetCamera ( ) ; // resetting camera before setting new minigame so minigames can set camera values in their awake call - Rasmus
2024-01-24 05:17:35 +00:00
GameObject prefab = GetGame ( game ) ;
if ( prefab = = null ) return ;
2021-12-31 14:46:11 +00:00
2024-01-25 17:29:05 +00:00
Destroy ( minigameObj ) ;
minigameObj = Instantiate ( prefab ) ;
if ( minigameObj . TryGetComponent < Minigame > ( out var minigame ) )
2023-11-23 16:19:39 +00:00
{
2024-01-25 17:29:05 +00:00
this . minigame = minigame ;
2024-01-14 07:18:46 +00:00
minigame . minigameName = game ;
2024-01-19 04:52:38 +00:00
minigame . gameManager = this ;
minigame . conductor = Conductor . instance ;
2023-11-23 16:19:39 +00:00
}
2024-01-25 17:29:05 +00:00
Vector3 originalScale = minigameObj . transform . localScale ;
minigameObj . transform . parent = eventCaller . GamesHolder . transform ;
minigameObj . transform . localScale = originalScale ;
minigameObj . name = game ;
2021-12-24 03:36:16 +00:00
2023-07-17 15:56:57 +00:00
SetCurrentGame ( game , useMinigameColor ) ;
2021-12-24 03:36:16 +00:00
}
2021-12-19 04:10:43 +00:00
2024-01-21 21:54:10 +00:00
public void DestroyGame ( )
2024-01-21 21:50:05 +00:00
{
2024-01-24 05:17:35 +00:00
cachedGamePrefabs . Clear ( ) ;
2024-02-11 05:24:52 +00:00
SoundByte . UnloadAudioClips ( ) ;
2024-01-21 21:50:05 +00:00
SetGame ( "noGame" ) ;
}
2023-12-26 05:22:51 +00:00
private IEnumerator WaitAndSetGame ( string game , bool useMinigameColor = true )
{
var inf = GetGameInfo ( game ) ;
2024-02-11 05:24:52 +00:00
if ( inf ! = null & & inf . usesAssetBundle )
2023-12-26 05:22:51 +00:00
{
2024-02-11 05:24:52 +00:00
if ( ! ( inf . AssetsLoaded | | inf . AlreadyLoading ) )
{
Debug . Log ( $"ASYNC loading assetbundles for game {game}" ) ;
inf . LoadAssetsAsync ( ) . Forget ( ) ;
}
2023-12-26 05:22:51 +00:00
yield return new WaitUntil ( ( ) = > inf . AssetsLoaded ) ;
2024-02-11 05:24:52 +00:00
SetGame ( game , useMinigameColor ) ;
}
else
{
SetGame ( game , useMinigameColor ) ;
2023-12-26 05:22:51 +00:00
}
}
2023-01-12 01:42:12 +00:00
public void PreloadGameSequences ( string game )
2021-12-31 14:46:11 +00:00
{
2023-01-12 01:42:12 +00:00
var gameInfo = GetGameInfo ( game ) ;
//load the games' sound sequences
2023-10-29 19:44:47 +00:00
// TODO: sound sequences sould be stored in a ScriptableObject
2023-01-12 01:42:12 +00:00
if ( gameInfo ! = null & & gameInfo . LoadedSoundSequences = = null )
gameInfo . LoadedSoundSequences = GetGame ( game ) . GetComponent < Minigame > ( ) . SoundSequences ;
2021-12-31 14:46:11 +00:00
}
2022-01-23 07:01:59 +00:00
public GameObject GetGame ( string name )
2021-12-21 01:10:49 +00:00
{
2022-02-03 07:47:38 +00:00
var gameInfo = GetGameInfo ( name ) ;
if ( gameInfo ! = null )
2022-01-29 23:30:29 +00:00
{
2024-03-30 02:52:14 +00:00
GameObject prefab ;
2023-12-26 05:22:51 +00:00
if ( gameInfo . inferred )
{
return Resources . Load < GameObject > ( $"Games/noGame" ) ;
}
2022-02-03 07:47:38 +00:00
if ( gameInfo . fxOnly )
{
2023-06-10 19:13:29 +00:00
var gameInfos = Beatmap . Entities
2023-04-02 17:15:19 +00:00
. Select ( x = > x . datamodel . Split ( 0 ) )
. Select ( x = > GetGameInfo ( x ) )
. Where ( x = > x ! = null )
. Where ( x = > ! x . fxOnly )
2024-01-24 05:17:35 +00:00
. Where ( x = > x . LoadableName is not "noGame" or "" or null ) ;
if ( gameInfos . Count ( ) > 0 )
{
gameInfo = gameInfos . FirstOrDefault ( ) ;
if ( gameInfo = = null ) return Resources . Load < GameObject > ( $"Games/noGame" ) ;
}
else
{
return Resources . Load < GameObject > ( $"Games/noGame" ) ;
}
2022-02-03 07:47:38 +00:00
}
2024-01-24 05:17:35 +00:00
if ( gameInfo . usesAssetBundle )
2022-06-12 19:32:00 +00:00
{
2024-01-24 05:17:35 +00:00
//game is packed in an assetbundle, load from that instead
if ( gameInfo . AssetsLoaded & & gameInfo . LoadedPrefab ! = null ) return gameInfo . LoadedPrefab ;
// couldn't load cached prefab, try loading from assetbundle
try
2022-06-12 19:32:00 +00:00
{
2024-01-24 05:17:35 +00:00
Debug . LogWarning ( $"Game prefab wasn't cached, loading from assetbundle for game {name}" ) ;
return gameInfo . LoadGamePrefab ( ) ;
}
catch ( Exception e )
{
Debug . LogWarning ( $"Failed to load assetbundle for game {name}, using sync loading: {e.Message}" ) ;
2024-03-30 02:52:14 +00:00
prefab = Resources . Load < GameObject > ( $"Games/{name}" ) ;
if ( prefab ! = null )
{
return prefab ;
}
else
{
Debug . LogWarning ( $"Game {name} not found, using noGame" ) ;
return Resources . Load < GameObject > ( $"Games/noGame" ) ;
}
2022-06-12 19:32:00 +00:00
}
}
2024-03-30 02:52:14 +00:00
prefab = Resources . Load < GameObject > ( $"Games/{name}" ) ;
if ( prefab ! = null )
{
return prefab ;
}
else
{
Debug . LogWarning ( $"Game {name} not found, using noGame" ) ;
return Resources . Load < GameObject > ( $"Games/noGame" ) ;
}
2024-01-24 05:17:35 +00:00
}
else
{
Debug . LogWarning ( $"Game {name} not found, using noGame" ) ;
return Resources . Load < GameObject > ( $"Games/noGame" ) ;
2022-01-29 23:30:29 +00:00
}
2022-01-23 07:01:59 +00:00
}
public Minigames . Minigame GetGameInfo ( string name )
{
2024-01-08 06:38:05 +00:00
return eventCaller . GetMinigame ( name ) ;
2021-12-26 01:04:23 +00:00
}
2024-03-11 20:18:40 +00:00
public bool TryGetMinigame < T > ( out T mg ) where T : Minigame
{
if ( minigame is T tempMinigame ) {
mg = tempMinigame ;
return true ;
} else {
mg = null ;
return false ;
}
}
2023-10-29 19:44:47 +00:00
Color colMain ;
2023-07-17 15:56:57 +00:00
public void SetCurrentGame ( string game , bool useMinigameColor = true )
2021-12-26 01:04:23 +00:00
{
currentGame = game ;
2023-03-12 02:56:26 +00:00
if ( GetGameInfo ( currentGame ) ! = null )
{
2024-02-16 06:17:16 +00:00
colMain = StringUtils . Hex2RGB ( GetGameInfo ( currentGame ) . color ) ;
CircleCursor . SetCursorColors ( colMain , StringUtils . Hex2RGB ( GetGameInfo ( currentGame ) . splitColorL ) , StringUtils . Hex2RGB ( GetGameInfo ( currentGame ) . splitColorR ) ) ;
2023-10-29 19:44:47 +00:00
if ( useMinigameColor ) HeavenStudio . StaticCamera . instance . SetAmbientGlowColour ( colMain , true ) ;
2023-07-17 15:56:57 +00:00
else HeavenStudio . StaticCamera . instance . SetAmbientGlowColour ( Color . black , false ) ;
2023-03-12 02:56:26 +00:00
}
2022-01-30 09:09:26 +00:00
else
2023-03-12 02:56:26 +00:00
{
2023-10-29 19:44:47 +00:00
CircleCursor . SetCursorColors ( Color . white , Color . white , Color . white ) ;
2023-07-17 15:56:57 +00:00
HeavenStudio . StaticCamera . instance . SetAmbientGlowColour ( Color . black , false ) ;
2023-03-12 02:56:26 +00:00
}
2023-12-26 05:22:51 +00:00
CircleCursor . ClearTrail ( false ) ;
2021-12-21 01:10:49 +00:00
}
2022-01-09 23:35:55 +00:00
2023-07-17 15:56:57 +00:00
private void SetAmbientGlowToCurrentMinigameColor ( )
{
2023-09-11 22:28:04 +00:00
if ( GetGameInfo ( currentGame ) ! = null )
2024-02-16 06:17:16 +00:00
HeavenStudio . StaticCamera . instance . SetAmbientGlowColour ( StringUtils . Hex2RGB ( GetGameInfo ( currentGame ) . color ) , true ) ;
2023-07-17 15:56:57 +00:00
}
2022-01-09 23:35:55 +00:00
private bool SongPosLessThanClipLength ( float t )
{
if ( Conductor . instance . musicSource . clip ! = null )
return Conductor . instance . GetSongPosFromBeat ( t ) < Conductor . instance . musicSource . clip . length ;
else
return true ;
}
2022-02-24 03:17:53 +00:00
public void ResetCamera ( )
{
2022-05-16 05:29:39 +00:00
HeavenStudio . GameCamera . ResetAdditionalTransforms ( ) ;
2022-02-24 03:17:53 +00:00
}
2021-12-19 04:10:43 +00:00
}
2021-12-21 01:10:49 +00:00
}