using System; using System.Collections; using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.UI; using Random = UnityEngine.Random; public class WaveManager : MonoBehaviour { #region Statication public static WaveManager instance; private void Awake() { if (instance != null && instance != this) { Destroy(gameObject); return; } instance = this; } #endregion [System.Serializable] public class Wave { public Enemy[] enemies; public bool bossWave; public Enemy bossEnemy; //optional public Texture bossName; //public DialogueScript bossDialogue; } public GameObject pauseMenu; [Header("Player Point")] public Player player; //only for continues lol [SerializeField] private Transform tlPoint; [SerializeField] private Transform brPoint; [Header("Enemy Point")] [SerializeField] private Transform enemyTLPoint; [SerializeField] private Transform enemyBRPoint; public Transform enemySpawnPoint; [Header("Waves")] public int wave = 1; public Wave[] waveList; [Header("Boss")] public Enemy bossInstance; public GameObject bossUI; [SerializeField] private RawImage bossNameUI; [SerializeField] private Image bossHPBar; [Header("Final Boss")] [SerializeField] private Prismriver lunasa; [SerializeField] private Prismriver merlin; [SerializeField] private Prismriver lyrica; public Prismriver lunasaInstance; public Prismriver merlinInstance; public Prismriver lyricaInstance; public int prismMaxHP; public int prismHP; public DialogueScript prismriverDialogue; [Header("Enemies")] public Transform enemyFolder; public List enemiesInPlay = new(); [SerializeField] private float enemyCheckRadius; [SerializeField] private LayerMask enemyLayer; public int healInterval; [Header("Audio")] public AudioClip bossDialogueLoop; public AudioClip bossMusic; public float bossVolume; public AudioClip finalDialogueLoop; public AudioClip finalMusic; public AudioSource audioSource; public AudioSource musicSource; [SerializeField] private AudioClip musicIntro; public AudioClip musicLoop; [Header("UI")] public GameObject loseUI; public StatsUI statsUI; [Header("Endless")] [SerializeField] private Enemy[] enemyList; [SerializeField] private Enemy[] bossList; [SerializeField] private int bossWaveInterval; [Header("Enemy Count Calculations")] [SerializeField] private float baseSpawnAmount; [SerializeField] private float exponentIncrease; public float exponentHealthIncrease; private void Start() { if (GameManager.instance.isEndless) { SpawnEndlessWave(); } else { SpawnWave(); } //SpawnFinalBoss(); musicSource.PlayOneShot(musicIntro); StartCoroutine(WaitForLoop()); } public Vector3 GetRandomPlayerPoint() { return new Vector3(Random.Range(tlPoint.position.x, brPoint.position.x), Random.Range(brPoint.position.y, tlPoint.position.y), 0); } public Vector3 GetRandomEnemyPoint() { Vector3 randomPoint; int currentAttempts = 0; do { randomPoint = new Vector3(Random.Range(enemyTLPoint.position.x, enemyBRPoint.position.x), Random.Range(enemyBRPoint.position.y, enemyTLPoint.position.y), 0); currentAttempts++; if (currentAttempts > 50) { return new Vector3(Random.Range(enemyTLPoint.position.x, enemyBRPoint.position.x), Random.Range(enemyBRPoint.position.y, enemyTLPoint.position.y), 0); } } while (CheckEnemyOverlap(randomPoint)); return randomPoint; } public void CallPauseMenu() { GameManager.instance.SetPauseMenu(); } public void CallExitGame() { GameManager.instance.ExitGame(); } public void CallRestartGame() { GameManager.instance.RestartGame(); } public void CallContinue() { GameManager.instance.ContinueGame(); } private bool CheckEnemyOverlap(Vector3 areaToCheck) { if (Physics2D.OverlapCircle(areaToCheck, enemyCheckRadius, enemyLayer)) { return true; } return false; } protected virtual void SpawnWave() { foreach (Enemy enemy in waveList[wave-1].enemies) { SpawnEnemy(enemy); } if (wave == waveList.Length) { SpawnFinalBoss(); Debug.Log("here"); } else if (waveList[wave-1].bossWave) { SpawnBoss(waveList[wave-1].bossEnemy); } wave++; } private void SpawnEndlessWave() { //waveUI.text = $"Wave: {wave}"; int amountToSpawn = Mathf.FloorToInt(baseSpawnAmount * Mathf.Pow(wave, exponentIncrease)); for (int i = 0; i < amountToSpawn; i++) { SpawnEnemy(enemyList[Random.Range(0, enemyList.Length)]); } if (wave % bossWaveInterval == 0) { SpawnBoss(bossList[Random.Range(0, bossList.Length)]); } wave++; } protected void SpawnBoss(Enemy boss) { Enemy newBoss = SpawnEnemy(boss); bossInstance = newBoss; bossNameUI.texture = waveList[wave - 1].bossName; bossNameUI.SetNativeSize(); UpdateBossUI(); if (!GameManager.instance.isEndless) { player.isStalled = true; //wait for boss to enter then start dialogue player.invulnerable = true; } else { newBoss.maxHealth = Mathf.FloorToInt(newBoss.maxHealth * Mathf.Pow(wave, exponentHealthIncrease)); newBoss.health = newBoss.maxHealth; } } private void SpawnFinalBoss() { lunasaInstance = SpawnPrism(lunasa); merlinInstance = SpawnPrism(merlin); lyricaInstance = SpawnPrism(lyrica); //DialogueManager.instance.StartDialogue(prismriverDialogue); lunasaInstance.isStalled = true; merlinInstance.isStalled = true; lyricaInstance.isStalled = true; player.isStalled = true; //wait for boss to enter then start dialogue player.invulnerable = true; bossNameUI.texture = waveList[wave - 1].bossName; bossNameUI.SetNativeSize(); } public Enemy SpawnEnemy(Enemy enemy) { Enemy newEnemy = Instantiate(enemy, enemyFolder); Vector3 movepos = GetRandomEnemyPoint(); if (GameManager.instance.isEndless) { newEnemy.maxHealth = Mathf.FloorToInt(newEnemy.maxHealth * Mathf.Pow(wave, exponentHealthIncrease)); newEnemy.health = newEnemy.maxHealth; } newEnemy.currentMovementRoutine = newEnemy.StartCoroutine(newEnemy.MoveToPosition(enemySpawnPoint.position,movepos, newEnemy.moveSpeed)); newEnemy.originalPosition = movepos; enemiesInPlay.Add(newEnemy); newEnemy.isStalled = true; newEnemy.invulnerable = true; return newEnemy; } public Prismriver SpawnPrism(Prismriver prism) { Prismriver newEnemy = Instantiate(prism, enemyFolder); Vector3 movepos = GetRandomEnemyPoint(); newEnemy.currentMovementRoutine = newEnemy.StartCoroutine(newEnemy.MoveToPosition(enemySpawnPoint.position,movepos, newEnemy.moveSpeed)); newEnemy.originalPosition = movepos; enemiesInPlay.Add(newEnemy); newEnemy.isStalled = true; newEnemy.invulnerable = true; return newEnemy; } public void UpdateBossUI() { if (lyricaInstance) { bossHPBar.fillAmount = 1 - (float)prismHP / prismMaxHP; return; } bossHPBar.fillAmount = 1 - (float)bossInstance.health / bossInstance.maxHealth; } public virtual void UpdateKills() { if (enemiesInPlay.Count == 0) { Debug.Log("interval: " + ((wave+1) % healInterval)); if ((wave + 1) % healInterval == 0) { Debug.Log("here"); player.Heal(1); } if (GameManager.instance.isEndless) { SpawnEndlessWave(); return; } SpawnWave(); } if (lunasaInstance && prismHP <= 0) { GameManager.instance.WinGame(); } } private IEnumerator WaitForLoop() { bool stopped = false; while (!stopped) { if (musicSource.isPlaying) { yield return null; } else { stopped = true; musicSource.Play(); } } } }