using System; using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.AI; using UnityEngine.AddressableAssets; namespace UnityRoyale { public class GameManager : MonoBehaviour { [Header("Settings")] public bool autoStart = false; [Header("Public References")] public NavMeshSurface navMesh; public GameObject playersCastle, opponentCastle; public GameObject introTimeline; public PlaceableData castlePData; public ParticlePool appearEffectPool; private CardManager cardManager; private CPUOpponent CPUOpponent; private AudioManager audioManager; private UIManager UIManager; private CinematicsManager cinematicsManager; private List playerUnits, opponentUnits; private List playerBuildings, opponentBuildings; private List allPlayers, allOpponents; //contains both Buildings and Units private List allThinkingPlaceables; private List allProjectiles; private bool gameOver = false; private bool updateAllPlaceables = false; //used to force an update of all AIBrains in the Update loop private const float THINKING_DELAY = 2f; private Placeable.Faction loserFaction = Placeable.Faction.None; private void Awake() { cardManager = GetComponentInChildren(); CPUOpponent = GetComponent(); audioManager = GetComponentInChildren(); cinematicsManager = GetComponentInChildren(); UIManager = GetComponentInChildren(); if (autoStart) introTimeline.SetActive(false); //listeners on other managers cardManager.OnCardUsed += UseCard; CPUOpponent.OnCardUsed += UseCard; //initialise Placeable lists, for the AIs to pick up and find a target playerUnits = new List(); playerBuildings = new List(); opponentUnits = new List(); opponentBuildings = new List(); allPlayers = new List(); allOpponents = new List(); allThinkingPlaceables = new List(); allProjectiles = new List(); } private void Start() { //Insert castles into lists SetupPlaceable(playersCastle, castlePData, Placeable.Faction.Player); SetupPlaceable(opponentCastle, castlePData, Placeable.Faction.Opponent); if (autoStart) { DisplayCards(); StartMatch(); } } public void DisplayCards() { cardManager.LoadDeck(); CPUOpponent.LoadDeck(); } //called by the intro cutscene public void StartMatch() { CPUOpponent.StartActing(); } //the Update loop pings all the ThinkingPlaceables in the scene, and makes them act private void Update() { if (gameOver) return; ThinkingPlaceable targetToPass; //ref ThinkingPlaceable p; //ref for (int pN=0; pN= p.lastBlowTime + p.attackRatio) { p.DealBlow(); //Animation will produce the damage, calling animation events OnDealDamage and OnProjectileFired. See ThinkingPlaceable } } break; case ThinkingPlaceable.States.Dead: Debug.LogError("A dead ThinkingPlaceable shouldn't be in this loop"); break; } } Projectile currProjectile; float progressToTarget; for (int prjN=0; prjN= 1f) { if(currProjectile.target.state != ThinkingPlaceable.States.Dead) //target might be dead already as this projectile is flying { float newHP = currProjectile.target.SufferDamage(currProjectile.damage); currProjectile.target.healthBar.SetHealth(newHP); } Destroy(currProjectile.gameObject); allProjectiles.RemoveAt(prjN); } } updateAllPlaceables = false; //is set to true by UseCard() } private List GetAttackList(Placeable.Faction f, Placeable.PlaceableTarget t) { switch (t) { case Placeable.PlaceableTarget.Both: return (f == Placeable.Faction.Player) ? allOpponents : allPlayers; case Placeable.PlaceableTarget.OnlyBuildings: return (f == Placeable.Faction.Player) ? opponentBuildings : playerBuildings; default: Debug.LogError("What faction is this?? Not Player nor Opponent."); return null; } } private bool FindClosestInList(Vector3 p, List list, out ThinkingPlaceable t) { t = null; bool targetFound = false; float closestDistanceSqr = Mathf.Infinity; //anything closer than here becomes the new designated target for(int i=0; i(prefabToSpawn, position + cardData.relativeOffsets[pNum], rot); SetupPlaceable(newPlaceableGO, pDataRef, pFaction); appearEffectPool.UseParticles(position + cardData.relativeOffsets[pNum]); } updateAllPlaceables = true; //will force all AIBrains to update next time the Update loop is run } //setups all scripts and listeners on a Placeable GameObject private void SetupPlaceable(GameObject go, PlaceableData pDataRef, Placeable.Faction pFaction) { //Add the appropriate script switch(pDataRef.pType) { case Placeable.PlaceableType.Unit: Unit uScript = go.GetComponent(); uScript.Activate(pFaction, pDataRef); //enables NavMeshAgent uScript.OnDealDamage += OnPlaceableDealtDamage; uScript.OnProjectileFired += OnProjectileFired; AddPlaceableToList(uScript); //add the Unit to the appropriate list UIManager.AddHealthUI(uScript); break; case Placeable.PlaceableType.Building: case Placeable.PlaceableType.Castle: Building bScript = go.GetComponent(); bScript.Activate(pFaction, pDataRef); bScript.OnDealDamage += OnPlaceableDealtDamage; bScript.OnProjectileFired += OnProjectileFired; AddPlaceableToList(bScript); //add the Building to the appropriate list UIManager.AddHealthUI(bScript); //special case for castles if(pDataRef.pType == Placeable.PlaceableType.Castle) { bScript.OnDie += OnCastleDead; } navMesh.BuildNavMesh(); //rebake the Navmesh break; case Placeable.PlaceableType.Obstacle: Obstacle oScript = go.GetComponent(); oScript.Activate(pDataRef); navMesh.BuildNavMesh(); //rebake the Navmesh break; case Placeable.PlaceableType.Spell: //Spell sScript = newPlaceable.AddComponent(); //sScript.Activate(pFaction, cardData.hitPoints); //TODO: activate the spell and… ? break; } go.GetComponent().OnDie += OnPlaceableDead; } private void OnProjectileFired(ThinkingPlaceable p) { Vector3 adjTargetPos = p.target.transform.position; adjTargetPos.y = 1.5f; Quaternion rot = Quaternion.LookRotation(adjTargetPos-p.projectileSpawnPoint.position); Projectile prj = Instantiate(p.projectilePrefab, p.projectileSpawnPoint.position, rot).GetComponent(); prj.target = p.target; prj.damage = p.damage; allProjectiles.Add(prj); } private void OnPlaceableDealtDamage(ThinkingPlaceable p) { if(p.target.state != ThinkingPlaceable.States.Dead) { float newHealth = p.target.SufferDamage(p.damage); p.target.healthBar.SetHealth(newHealth); } } private void OnCastleDead(Placeable c) { loserFaction = c.faction; cinematicsManager.PlayCollapseCutscene(c.faction); c.OnDie -= OnCastleDead; gameOver = true; //stops the thinking loop //stop all the ThinkingPlaceables ThinkingPlaceable thkPl; for(int pN=0; pN