HeavenStudioPlus/Assets/Scripts/GameManager.cs
Braedon 8760412bb2 Dynamic editor theme (WIP), read desc for more info
Spaceball has been improved, you can now hit multiple balls at a time. Fork Lifter and Karate Man need this update soon as well. No idea why I did it the way I did.

Time jumping also has been improved by pausing.

Dynamic editor themes for custom theme support, however that won't be implemented until later.
2022-01-13 21:33:51 -05:00

298 lines
No EOL
10 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using Starpelly;
using Newtonsoft.Json;
using RhythmHeavenMania.Games;
namespace RhythmHeavenMania
{
public class GameManager : MonoBehaviour
{
public static GameManager instance;
private EventCaller eventCaller;
public Beatmap Beatmap = new Beatmap();
[HideInInspector] public List<Beatmap.Entity> playerEntities = new List<Beatmap.Entity>();
public int currentEvent, currentPlayerEvent;
public TextAsset txt;
public float startOffset;
public Camera GameCamera, CursorCam;
public CircleCursor CircleCursor;
[Header("Games")]
Coroutine currentGameSwitchIE;
public string currentGame;
public float startBeat;
private GameObject currentGameO;
private List<GameObject> preloadedGames = new List<GameObject>();
[HideInInspector] public GameObject GamesHolder;
public bool playOnStart;
private void Awake()
{
instance = this;
}
public void Init()
{
this.transform.localScale = new Vector3(3000, 3000);
SpriteRenderer sp = this.gameObject.AddComponent<SpriteRenderer>();
sp.enabled = false;
sp.color = Color.black;
sp.sprite = Resources.Load<Sprite>("Sprites/GeneralPurpose/Square");
sp.sortingOrder = 30000;
this.gameObject.layer = 3;
string json = txt.text;
Beatmap = JsonConvert.DeserializeObject<Beatmap>(json);
SortEventsList();
GlobalGameManager.Init();
eventCaller = this.gameObject.AddComponent<EventCaller>();
eventCaller.GamesHolder = GamesHolder.transform;
eventCaller.Init();
Conductor.instance.SetBpm(Beatmap.bpm);
if (playOnStart) StartCoroutine(Begin());
// SetCurrentGame(eventCaller.GamesHolder.transform.GetComponentsInChildren<Transform>()[1].name);
if (Beatmap.entities.Count >= 1)
{
SetCurrentGame(Beatmap.entities[0].datamodel.Split('/')[0]);
SetGame(Beatmap.entities[0].datamodel.Split('/')[0]);
}
}
private IEnumerator Begin()
{
yield return new WaitForSeconds(startOffset);
// Conductor.instance.Play(startBeat);
}
private void Update()
{
if (Beatmap.entities.Count < 1)
return;
if (!Conductor.instance.isPlaying)
return;
List<float> entities = Beatmap.entities.Select(c => c.beat).ToList();
if (currentEvent < Beatmap.entities.Count && currentEvent >= 0)
{
if (Conductor.instance.songPositionInBeats >= entities[currentEvent] && SongPosLessThanClipLength(Conductor.instance.songPositionInBeats))
{
// allows for multiple events on the same beat to be executed on the same frame, so no more 1-frame delay
var entitesAtSameBeat = Beatmap.entities.FindAll(c => c.beat == Beatmap.entities[currentEvent].beat && c.datamodel.Split('/')[0] != "gameManager");
var gameManagerEntities = Beatmap.entities.FindAll(c => c.beat == Beatmap.entities[currentEvent].beat && c.datamodel.Split('/')[0] == "gameManager");
// GameManager entities should ALWAYS execute before gameplay entities
for (int i = 0; i < gameManagerEntities.Count; i++)
{
var gameManagerEntity = gameManagerEntities[i];
if ((gameManagerEntity.beat + eventCaller.GetGameAction(eventCaller.GetMinigame(gameManagerEntity.datamodel.Split(0)), gameManagerEntity.datamodel.Split(1)).defaultLength) > Conductor.instance.songPositionInBeats)
{
eventCaller.CallEvent(gameManagerEntities[i].datamodel);
}
}
for (int i = 0; i < entitesAtSameBeat.Count; i++)
{
var entity = entitesAtSameBeat[i];
if ((entity.beat + eventCaller.GetGameAction(eventCaller.GetMinigame(entity.datamodel.Split(0)), entity.datamodel.Split(1)).defaultLength) > Conductor.instance.songPositionInBeats)
{
// if game isn't loaded, preload game so whatever event that would be called will still run outside if needed
if (entitesAtSameBeat[i].datamodel.Split('/')[0] != currentGame && !preloadedGames.Contains(preloadedGames.Find(c => c.name == entitesAtSameBeat[i].datamodel.Split('/')[0])))
{
PreloadGame(entitesAtSameBeat[i].datamodel.Split('/')[0]);
}
eventCaller.CallEvent(entitesAtSameBeat[i].datamodel);
}
}
currentEvent += entitesAtSameBeat.Count + gameManagerEntities.Count;
}
}
}
#region Play Events
public void Play(float beat)
{
StartCoroutine(PlayCo(beat));
}
private IEnumerator PlayCo(float beat)
{
yield return null;
bool paused = Conductor.instance.isPaused;
Conductor.instance.Play(beat);
if (!paused)
{
SetCurrentEventToClosest(beat);
}
}
public void Pause()
{
Conductor.instance.Pause();
}
public void Stop(float beat)
{
Conductor.instance.Stop(beat);
SetCurrentEventToClosest(beat);
}
#endregion
#region List Functions
public void SortEventsList()
{
Beatmap.entities.Sort((x, y) => x.beat.CompareTo(y.beat));
}
public void SetCurrentEventToClosest(float beat)
{
SortEventsList();
if (Beatmap.entities.Count > 0)
{
List<float> entities = Beatmap.entities.Select(c => c.beat).ToList();
if (playerEntities != null)
{
List<float> entities_p = playerEntities.Select(c => c.beat).ToList();
currentPlayerEvent = entities_p.IndexOf(Mathp.GetClosestInList(entities_p, beat));
}
currentEvent = entities.IndexOf(Mathp.GetClosestInList(entities, beat));
string newGame = Beatmap.entities[currentEvent].datamodel.Split('/')[0];
if (Beatmap.entities[currentEvent].datamodel.Split('/')[1] != "switchGame")
{
if (newGame == "gameManager")
{
// holy shit
newGame = Beatmap.entities[entities.IndexOf(Mathp.GetClosestInList(Beatmap.entities.FindAll(c => c.datamodel != "gameManager" && c.beat < Conductor.instance.songPositionInBeats).ToList().Select(c => c.beat).ToList(), beat))].datamodel.Split('/')[0];
}
SetGame(newGame);
}
}
}
#endregion
public void SwitchGame(string game)
{
if (currentGameSwitchIE != null) StopCoroutine(currentGameSwitchIE);
currentGameSwitchIE = StartCoroutine(SwitchGameIE(game));
}
IEnumerator SwitchGameIE(string game)
{
this.GetComponent<SpriteRenderer>().enabled = true;
SetGame(game);
yield return new WaitForSeconds(0.1666f);
this.GetComponent<SpriteRenderer>().enabled = false;
}
private void SetGame(string game, bool onGameSwitch = true)
{
Destroy(currentGameO);
var instantiate = true;
if (preloadedGames.Count > 0)
{
for (int i = 0; i < preloadedGames.Count; i++)
{
if (preloadedGames[i].gameObject != null)
{
if (preloadedGames[i].gameObject.name == game)
{
preloadedGames[i].SetActive(true);
currentGameO = preloadedGames[i];
preloadedGames.Remove(preloadedGames[i]);
instantiate = false;
}
}
}
}
if (instantiate)
{
currentGameO = Instantiate(GetGame(game).holder);
currentGameO.transform.parent = eventCaller.GamesHolder.transform;
currentGameO.name = game;
}
GameCamera.orthographic = true;
if (onGameSwitch)
{
if (GetGame(currentGame).holder.GetComponent<Minigame>() != null)
GetGame(game).holder.GetComponent<Minigame>().OnGameSwitch();
}
SetCurrentGame(game);
}
private void PreloadGame(string game)
{
if (preloadedGames.Contains(preloadedGames.Find(c => c.name == game)))
return;
var g = Instantiate(GetGame(game).holder);
g.transform.parent = eventCaller.GamesHolder.transform;
g.SetActive(false);
g.name = game;
preloadedGames.Add(g);
}
public EventCaller.MiniGame GetGame(string name)
{
return eventCaller.minigames.Find(c => c.name == name);
}
// never gonna use this
public EventCaller.MiniGame GetCurrentGame()
{
return eventCaller.minigames.Find(c => c.name == transform.GetComponentsInChildren<Transform>()[1].name);
}
public void SetCurrentGame(string game)
{
currentGame = game;
CircleCursor.InnerCircle.GetComponent<SpriteRenderer>().color = Colors.Hex2RGB(GetGame(currentGame).color);
}
private bool SongPosLessThanClipLength(float t)
{
if (Conductor.instance.musicSource.clip != null)
return Conductor.instance.GetSongPosFromBeat(t) < Conductor.instance.musicSource.clip.length;
else
return true;
}
}
}