using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEngine.EventSystems; using UnityEngine.SceneManagement; using UnityEngine.UI; // Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved. namespace SciFiShipController { /// /// Demo gameplay controller for Sci-Fi Ship Controller Tech Demo #2. /// This script contains a number of techniques that could be used in developing /// a game with Sci-Fi Ship Controller. Its purpose is to help you write /// your own game with our API. For example it shows: /// 1. How AI ships can be spawned /// 2. How AI ships can be docked and undocked from a ShipDockingStation /// 3. How Radar can be used to find and allocate targets to AI ships and weapons /// 4. How gameplay can be paused and resumed /// 5. How a mission can be broken up in several phases or stages /// 6. How AI ships can follow a path to a destination then take another action /// 7. How a second Point of Interest camera could be used on a second monitor /// 8. How to add a Heads-up Display /// 9. How AI ships can be undocked from a capital ship /// 10. How to use Damage Regions for health and weapon targeting /// [HelpURL("http://scsmmedia.com/ssc-documentation")] public class TechDemo2 : MonoBehaviour { #region Enumerations public enum GameQuality { Low = 0, Medium = 1, High = 2 } #endregion #region Public Variables [Header("General Settings")] public GameQuality gameQuality = GameQuality.High; public bool limitScreenToHD = true; public bool limitFrameRate = true; [Header("Unity Layers")] public int smallShipsUnityLayer = 27; [Header("Factions")] public int friendlyFactionID = 1; public int enemyFactionID = 2; [Header("Cameras")] public ShipCameraModule shipCameraModule1; public SampleChangeCameraView changeCameraView; public ShipCameraModule pointOfInterestCamera; public bool usePOICamera = true; public bool isSwitchDisplays = false; [Header("Player 1 Ship")] public ShipControlModule player1Ship; public int player1SquadronId = 10; [Header("Friendly Squadron A")] public GameObject squadronAGameObject; public ShipControlModule squadronAShipPrefab; public int minDockingIndexA = 0; public int maxDockingIndexA = 0; /// /// Friendly squadron A ID. /// public int squadronAId = 1; [Header("Friendly Squadron B")] public GameObject squadronBGameObject; public ShipControlModule squadronBShipPrefab; public int minDockingIndexB = 0; public int maxDockingIndexB = 0; /// /// Friendly squadron B ID. /// public int squadronBId = 2; [Header("Enemy Squadron C")] public GameObject squadronCGameObject; public ShipControlModule squadronCShipPrefab; public int minDockingIndexC = 0; public int maxDockingIndexC = 0; /// /// Enemy base squadron ID. /// public int squadronCId = 3; [Header("Enemy Squadron D")] // These ships are in the capital ship hangar public GameObject squadronDGameObject; public ShipControlModule squadronDShipPrefab; public int minDockingIndexD = 0; public int maxDockingIndexD = 0; /// /// Enemy air squadron ID. /// public int squadronDId = 4; [Header("Enemy Capital Ship (E)")] public ShipControlModule enemyCapitalShip; /// /// Enemy capital ship squadron ID. /// public int squadronEId = 5; /// /// The delay time between initial undockings of the ships in the capital ship hangar /// public float capitalUndockingDelay = 1f; [Header("Enemy Base (F)")] public GameObject enemyTurretsGameObject; /// /// Enemy base turrets squadron ID. /// public int squadronFId = 6; public float enemyTurretTargetRadius = 25f; public float enemyBaseRadius = 1000f; [Header("Other")] public int otherFactionID = 100; [Header("Location Names")] public string friendlyBaseLocationName = "Location Name Here"; public string enemyBaseLocationName = "Location Name Here"; public string S2RespawnLocationName = "Location Name Here"; [Header("Path Names")] public string enemyBaseExitPathName = "Path Name Here"; public string friendlyApproachPathName = "Path Name Here"; public string diversionPath1Name = "Path Name Here"; [Header("Docking Stations")] /// /// Docking Station for friendly fighters at the friendly base /// public ShipDockingStation friendlyBaseDockingStation = null; /// /// The delay time between initial undockings of the friendly ships /// public float friendlyUndockingDelay = 1f; /// /// Docking Station for enemy fighters at the enemy base /// public ShipDockingStation enemyBaseDockingStation1 = null; /// /// Docking Station where the capital ship is docked /// public ShipDockingStation enemyBaseDockingStation2 = null; [Header("Demo Sections")] /// /// When the player or any friendly ship gets within this distance of the enemy base, section 2 /// of the demo will be triggered. /// public float section2Dist = 8000f; [Header("Misc")] public MeshFilter gameplayArea = null; public GameObject landscapeParent = null; public Celestials celestials = null; public ShipDisplayModule shipDisplayModule = null; [Range(0f, 1f)] public float cockpitViewHeight = 0.75f; public bool isGamePaused { get; private set; } [Header("Menu")] public GameObject menuPanel = null; public GameObject qualityPanel = null; public Button resumeButton = null; public Button startButton = null; public Button restartButton = null; public Button quitButton = null; public Button qualityLowButton = null; public Button qualityMedButton = null; public Button qualityHighButton = null; public Text resumeButtonText = null; public Text startButtonText = null; public Text restartButtonText = null; public Text quitButtonText = null; public Text qualityLowButtonText = null; public Text qualityMedButtonText = null; public Text qualityHighButtonText = null; public Image qualityLowBorderImg = null; public Image qualityMedBorderImg = null; public Image qualityHighBorderImg = null; public GameObject missionOverPanel = null; public GameObject missionSuccessTitle = null; public GameObject missionSuccessSubTitle = null; public GameObject missionFailedTitle = null; public GameObject missionFailedSubTitle = null; #endregion #region Private variables private SSCManager sscManager = null; private SSCRadar sscRadar = null; private List squadronAShips; private int squadronAShipsCount = 0; private List squadronBShips; private int squadronBShipsCount = 0; private List squadronCShips; private int squadronCShipsCount = 0; private List squadronDShips; private int squadronDShipsCount = 0; // A list of ships that are currently paused private List pausedAIShips; private List enemySurfaceTurrets; private int enemySurfaceTurretsCount = 0; private LocationData friendlyBaseLocation; private LocationData enemyBaseLocation; private LocationData section2PlayerRespawnLocation; private PathData friendlyApproachPath; private PathData enemyBaseExitPath; private PathData diversionPath1; private List sscRadarBlipsList = null; private int sscRadarBlipsListCount = 0; private SSCRadarQuery squadronARadarQuery = null; private SSCRadarQuery squadronBRadarQuery = null; private SSCRadarQuery squadronCRadarQuery = null; private SSCRadarQuery squadronDRadarQuery = null; private int[] friendlyFactions = new int[0]; private int[] enemyFactions = new int[0]; private ShipDockingStation capitalshipDockingStation = null; /// /// 0 - Start of demo. /// 1 - Player and friendly AI ships flying to enemy base. /// 2 - Attack on enemy base. /// 3 - Capital ship gets ready for departure /// 4 - Attack on enemy capital ship. /// 5 - Launch Squadron D from Capital Ship /// private int demoSectionIndex = 0; private bool isSmallShipsLayerDefined = false; private Bounds gameplayAreaBounds; private bool isPOICameraDisplayed = false; private bool isPOIwithSquadronD = false; private bool isBridgeAttackEnabled = false; private scsmmedia.MusicController musicController = null; // UI and HUD variables private EventSystem eventSystem; private Color colourNonSelectedBtnText; private Color colourSelectedBtnText; private Color colourDefaultBorder; private Color colourSelectedBorder; private bool isHUDFirstTimeShown = true; private DisplayMessage section1Message = null; private DisplayMessage section2Message = null; private DisplayMessage section3Message = null; private int prevNumShieldGeneratorsDestroyed = 0; private scsmmedia.SCSMString tempDisplayMessageString; #endregion #region Initialisation Methods // Use this for initialization void Start() { isGamePaused = false; if (limitScreenToHD) { SSCUtils.MaxScreenHD(); } // Get or create the SSC manager and radar components sscManager = SSCManager.GetOrCreateManager(); sscRadar = SSCRadar.GetOrCreateRadar(); #region Unity Layers // Small ships are added to a Unity Layer // The Capital Ship's AI will ignore small ships with obstacle avoidance // We check it in the demo because we don't have control over who has and has not added // the Unity Layer in the editor. string smallShipsLayerName = LayerMask.LayerToName(smallShipsUnityLayer); isSmallShipsLayerDefined = !string.IsNullOrEmpty(smallShipsLayerName) && smallShipsLayerName.ToLower() == "small ships"; #if UNITY_EDITOR if (!isSmallShipsLayerDefined) { string msg = "Please configure a Unity Layer called 'Small Ships'. By default the TechDemo2 script uses Layer 27"; Debug.LogWarning("[ERROR] TechDemo2 - " + msg); UnityEditor.EditorUtility.DisplayDialog("TechDemo Config", msg, "Got it!"); } #endif #endregion #region Player Initialisation // Set the player's faction and squadron ID player1Ship.shipInstance.factionId = friendlyFactionID; player1Ship.shipInstance.squadronId = player1SquadronId; // Add the player's ship to the Small Ships Unity Layer so that the Capital ship // ignore it for obstacle avoidance if (isSmallShipsLayerDefined) { player1Ship.gameObject.layer = smallShipsUnityLayer; } // Set up the game play area based on the area defined by a simple cube in the scene // The cube should have the renderer and collider disabled. The collider remains so that // it can be visualised in the editor for design purposes. if (gameplayArea != null && gameplayArea.mesh != null) { // Typically we'd use the MeshRenderer bounds but that is not available // so will need to use the mesh and the scaled value gameplayAreaBounds = gameplayArea.mesh.bounds; // This works because we have a 1x1x1 cube gameplayAreaBounds.size = gameplayArea.transform.localScale; } else { gameplayAreaBounds = new Bounds(); } #if UNITY_EDITOR if (player1Ship.shipInstance.respawningMode == Ship.RespawningMode.DontRespawn) { Debug.LogWarning("Tech Demo - The Player ship for this demo needs to be automatically respawned. See the Combat tab."); } #endif // This enables us to turn off the Heads up display when the ship is destroyed // We could also do things like update scores or end the mission etc. player1Ship.callbackOnDestroy = OnPlayerDestroyed; // This enables us to turn the HUD on again after the ship has been respawned player1Ship.callbackOnRespawn = OnPlayerRespawnCallback; #endregion #region Docking Station Initialisation // Initialise Docking Stations if (friendlyBaseDockingStation != null && !friendlyBaseDockingStation.IsInitialised) { friendlyBaseDockingStation.Initialise(true); } if (enemyBaseDockingStation1 != null && !enemyBaseDockingStation1.IsInitialised) { enemyBaseDockingStation1.Initialise(true); } if (enemyBaseDockingStation2 != null && !enemyBaseDockingStation2.IsInitialised) { enemyBaseDockingStation2.Initialise(true); } // The Capital Ship is also a DockingStation but is setup with the ship below #endregion #region Squadrons Initialisation // Initialise the lists of ships squadronAShips = new List(); squadronBShips = new List(); squadronCShips = new List(); squadronDShips = new List(); // Initialise the squadrons InitialiseSquadron(squadronAGameObject, squadronAShipPrefab, friendlyBaseDockingStation, minDockingIndexA, maxDockingIndexA, squadronAShips, friendlyFactionID, squadronAId); InitialiseSquadron(squadronBGameObject, squadronBShipPrefab, friendlyBaseDockingStation, minDockingIndexB, maxDockingIndexB, squadronBShips, friendlyFactionID, squadronBId); InitialiseSquadron(squadronCGameObject, squadronCShipPrefab, enemyBaseDockingStation1, minDockingIndexC, maxDockingIndexC, squadronCShips, enemyFactionID, squadronCId); // Store how many ships are in each squadron squadronAShipsCount = squadronAShips.Count; squadronBShipsCount = squadronBShips.Count; squadronCShipsCount = squadronCShips.Count; #endregion #region Enemy Base Initialisation // Initialise the list of enemy turrets enemySurfaceTurrets = new List(); if (enemyTurretsGameObject != null) { // Find all the enemy surface turrets SurfaceTurretModule[] surfaceTurrets = enemyTurretsGameObject.GetComponentsInChildren(); int surfaceTurretsLength = surfaceTurrets.Length; // Loop through all of the turrets SurfaceTurretModule surfaceTurretModuleInstance; for (int i = 0; i < surfaceTurretsLength; i++) { surfaceTurretModuleInstance = surfaceTurrets[i]; surfaceTurretModuleInstance.isVisibleToRadar = true; surfaceTurretModuleInstance.autoCreateLocation = false; // Set the faction and squadron IDs for the surface turret surfaceTurretModuleInstance.factionId = enemyFactionID; surfaceTurretModuleInstance.squadronId = squadronFId; // Set up notification when the turret is destroyed surfaceTurretModuleInstance.callbackOnDestroy = OnSurfaceTurretDestroyed; // Initialise the turret in manual fire mode (so that it initially will do nothing) // Will also add to radar as RadarItemType.GameObject surfaceTurretModuleInstance.Initialise(); surfaceTurretModuleInstance.SetManualFire(); // Squadron Id is not usually used with a surface turret. So set it now. //sscRadar.SetSquardronId(surfaceTurretModuleInstance.RadarId, squadronFId); // Add it to the list enemySurfaceTurrets.Add(surfaceTurretModuleInstance); } } // Store how many enemy surface turrets there are enemySurfaceTurretsCount = enemySurfaceTurrets.Count; #endregion #region Enemy Capital Ship Initialisation if (enemyCapitalShip != null) { // Set the faction and squadron ID of the enemy capital ship enemyCapitalShip.shipInstance.factionId = enemyFactionID; enemyCapitalShip.shipInstance.squadronId = squadronEId; enemyCapitalShip.callbackOnDestroy = OnMissionSuccess; // The capital ship is initialised above when the ship docking station (SSCHanger1) it is docked // with, is initialised. The docking component is also initialised by the docking station // as it was assiged to the docking point in the editor. // This will cache the AI component on the ShipControlModule and return it here ShipAIInputModule enemyCapitalShipAI = enemyCapitalShip.GetShipAIInputModule(); if (enemyCapitalShipAI != null) { // If the Unity Layer for small ships is setup correctly, exclude these // from Capital Ship obstacle avoidance. if (isSmallShipsLayerDefined && SSCUtils.IsInLayerMask(smallShipsUnityLayer, enemyCapitalShipAI.obstacleLayerMask)) { // Exclude Small Ships if not already excluded enemyCapitalShipAI.obstacleLayerMask -= 1 << smallShipsUnityLayer; } enemyCapitalShipAI.Initialise(); } // Initialise the docking station which is part of the capital ship // This is where the enemy SquadronD fighters are launched from after the capital ship takes off. capitalshipDockingStation = enemyCapitalShip.GetComponent(); if (capitalshipDockingStation != null && !capitalshipDockingStation.IsInitialised) { capitalshipDockingStation.Initialise(true); InitialiseSquadron(squadronDGameObject, squadronDShipPrefab, capitalshipDockingStation, minDockingIndexD, maxDockingIndexD, squadronDShips, enemyFactionID, squadronDId); squadronDShipsCount = squadronDShips.Count; } // Disabling the ship will also make it invisible to radar enemyCapitalShip.DisableShip(false); } #endregion #region Locations Initialisation // Get the locations using their names from the SSC Manager friendlyBaseLocation = sscManager.GetLocation(friendlyBaseLocationName); if (friendlyBaseLocation != null) { // Set the friendly base location to an unused faction ID friendlyBaseLocation.factionId = otherFactionID; } enemyBaseLocation = sscManager.GetLocation(enemyBaseLocationName); if (enemyBaseLocation != null) { // Set the enemy base location to an unused faction ID enemyBaseLocation.factionId = otherFactionID; } section2PlayerRespawnLocation = sscManager.GetLocation(S2RespawnLocationName); #endregion #region Paths Initialisation // Get the paths using their names from the SSC Manager friendlyApproachPath = sscManager.GetPath(friendlyApproachPathName); enemyBaseExitPath = sscManager.GetPath(enemyBaseExitPathName); diversionPath1 = sscManager.GetPath(diversionPath1Name); // We might not be using this any more if (enemyBaseExitPath != null) { } #endregion #region Initialise Radar Query Data // Initialise squadron radar queries squadronARadarQuery = new SSCRadarQuery(); squadronBRadarQuery = new SSCRadarQuery(); squadronCRadarQuery = new SSCRadarQuery(); squadronDRadarQuery = new SSCRadarQuery(); // Initialise a list of radar results sscRadarBlipsList = new List(); // Create int lists for groups of factions/squadrons that we will use repeatedly friendlyFactions = new int[] { friendlyFactionID }; enemyFactions = new int[] { enemyFactionID }; #endregion #region Point Of Interest Camera if (pointOfInterestCamera != null) { DisablePointOfInterestCamera(); } #endregion #region Change Camera View #if UNITY_EDITOR if (changeCameraView == null) { Debug.LogWarning("ERROR: TechDemo - ChangeCameraView is not defined in the Cameras section in the inspector"); } #endif #endregion #region Quality Settings // Start up validation #if UNITY_EDITOR if (landscapeParent == null) { Debug.LogWarning("Tech Demo - cannot find landscape parent gameobject."); } #endif SetGameQuality(); #endregion #region Heads-Up Display (HUD) if (shipDisplayModule != null) { // We would typically call shipDisplayModule.HideHUD() after configuring it here, // but due to how we play the scene for 0.5 of a second to allow the camera to reposition // that will not work. So make sure things all are turned off in the HUD when the scene // first starts. We use a bool isHUDFirstTimeShown to reanable things in UnpauseGame(). // Before shipDisplayModule is initialised, we can simply update the variables directly shipDisplayModule.showActiveDisplayReticle = false; shipDisplayModule.showAirspeed = false; shipDisplayModule.showAltitude = false; shipDisplayModule.Initialise(); int guidHashReticle = shipDisplayModule.GetDisplayReticleGuidHash(2); shipDisplayModule.ChangeDisplayReticle(guidHashReticle); // Pre-create display messages (by design they are not shown when first added) // We could have also set these up in the editor. // Add a message telling the player to fly to the enemy base section1Message = shipDisplayModule.AddMessage("Section 1 Message", "Objective: Fly to the enemy base."); section1Message.scrollSpeed = 0.75f; section1Message.isScrollFullscreen = true; shipDisplayModule.SetDisplayMessageOffset(section1Message, 0f, 0.53f); shipDisplayModule.SetDisplayMessageSize(section1Message, 0.5f, 0.1f); shipDisplayModule.SetDisplayMessageScrollDirection(section1Message, DisplayMessage.ScrollDirectionLR); shipDisplayModule.SetDisplayMessageTextFontSize(section1Message, false, 1, 20); // Create new message with the attributes of section1Message section2Message = shipDisplayModule.CopyDisplayMessage(section1Message, "Section 2 Message"); // We can modify attributes directly before calling AddMessage or we can call SetDisplayMessage[...]() methods // after adding it - like was done with section1Message above. section2Message.messageString = "Objective: Destroy the enemy turrets (0/4)."; shipDisplayModule.AddMessage(section2Message); section3Message = shipDisplayModule.CopyDisplayMessage(section1Message, "Section 3 Message"); section3Message.messageString = "Objective: Destroy the shield generators of the enemy capital ship (0/4)."; shipDisplayModule.AddMessage(section3Message); // Create Display Targets at runtime (by design they are not shown when first added) // Display Targets should not have overlaying factions or squadrons. That is, each DisplayTarget // should be limited to a unique group or category of radar items. The same faction or squadron // should not appear in multiple DisplayTargets. // Create an enemy "target" guidHashReticle = shipDisplayModule.GetDisplayReticleGuidHash("SSCUITarget1"); DisplayTarget displayTarget = shipDisplayModule.AddTarget(guidHashReticle); displayTarget.factionsToInclude = enemyFactions; // Enemy ship squadrons displayTarget.squadronsToInclude = new int[] { squadronCId, squadronDId, squadronEId }; shipDisplayModule.AddTargetSlots(displayTarget, 2); shipDisplayModule.SetDisplayTargetReticleColour(displayTarget, Color.red); // Create an enemy turret target guidHashReticle = shipDisplayModule.GetDisplayReticleGuidHash("SSCUITarget2"); displayTarget = shipDisplayModule.AddTarget(guidHashReticle); displayTarget.factionsToInclude = enemyFactions; // Enemy turret squadron displayTarget.squadronsToInclude = new int[] { squadronFId }; shipDisplayModule.AddTargetSlots(displayTarget, 2); shipDisplayModule.SetDisplayTargetReticleColour(displayTarget, Color.red); // Create a friendly "target" guidHashReticle = shipDisplayModule.GetDisplayReticleGuidHash("SSCUITarget4"); displayTarget = shipDisplayModule.AddTarget(guidHashReticle); displayTarget.isTargetable = false; displayTarget.factionsToInclude = friendlyFactions; displayTarget.squadronsToInclude = new int[] { squadronAId, squadronBId }; shipDisplayModule.SetDisplayTargetReticleColour(displayTarget, Color.blue); shipDisplayModule.HideDisplayMessages(); shipDisplayModule.HideDisplayGauges(); shipDisplayModule.HideDisplayTargets(); } #if UNITY_EDITOR else { Debug.LogWarning("ERROR: TechDemo - ShipDisplayModule is not defined in the Misc section in the inspector"); } #endif #endregion #region Initialise Menu #if UNITY_EDITOR if (menuPanel == null) { Debug.LogWarning("ERROR: TechDemo UI menuPanel is not configured"); } if (qualityPanel == null) { Debug.LogWarning("ERROR: TechDemo UI qualityPanel is not configured"); } if (resumeButton == null) { Debug.LogWarning("ERROR: TechDemo resumeButton is not configured"); } if (startButton == null) { Debug.LogWarning("ERROR: TechDemo startButton is not configured"); } if (restartButton == null) { Debug.LogWarning("ERROR: TechDemo restartButton is not configured"); } if (quitButton == null) { Debug.LogWarning("ERROR: TechDemo quitButton is not configured"); } if (qualityLowButton == null) { Debug.LogWarning("ERROR: TechDemo qualityLowButton is not configured"); } if (qualityMedButton == null) { Debug.LogWarning("ERROR: TechDemo qualityMedButton is not configured"); } if (qualityHighButton == null) { Debug.LogWarning("ERROR: TechDemo qualityHighButton is not configured"); } if (resumeButtonText == null) { Debug.LogWarning("ERROR: TechDemo resumeButtonText is not configured"); } if (startButtonText == null) { Debug.LogWarning("ERROR: TechDemo startButtonText is not configured"); } if (restartButtonText == null) { Debug.LogWarning("ERROR: TechDemo restartButtonText is not configured"); } if (quitButtonText == null) { Debug.LogWarning("ERROR: TechDemo quitButtonText is not configured"); } if (qualityLowButtonText == null) { Debug.LogWarning("ERROR: TechDemo qualityLowButtonText is not configured"); } if (qualityMedButtonText == null) { Debug.LogWarning("ERROR: TechDemo qualityMedButtonText is not configured"); } if (qualityHighButtonText == null) { Debug.LogWarning("ERROR: TechDemo qualityHighButtonText is not configured"); } if (qualityLowBorderImg == null) { Debug.LogWarning("ERROR: TechDemo qualityLowBorderImg is not configured"); } if (qualityMedBorderImg == null) { Debug.LogWarning("ERROR: TechDemo qualityMedBorderImg is not configured"); } if (qualityHighBorderImg == null) { Debug.LogWarning("ERROR: TechDemo qualityHighBorderImg is not configured"); } if (missionOverPanel == null) { Debug.LogWarning("ERROR: TechDemo missionOverPanel is not configured"); } if (missionSuccessTitle == null) { Debug.LogWarning("ERROR: TechDemo missionSuccessTitle is not configured"); } if (missionSuccessSubTitle == null) { Debug.LogWarning("ERROR: TechDemo missionSuccessSubTitle is not configured"); } if (missionFailedTitle == null) { Debug.LogWarning("ERROR: TechDemo missionFailedTitle is not configured"); } if (missionFailedSubTitle == null) { Debug.LogWarning("ERROR: TechDemo missionFailedSubTitle is not configured"); } #endif eventSystem = EventSystem.current; colourNonSelectedBtnText = new Color(245f / 255f, 245f / 255f, 245f / 255f, 1f); colourSelectedBtnText = new Color(168f / 255f, 168f / 255f, 227f / 255f, 1f); colourDefaultBorder = new Color(72f / 255f, 72f / 255f, 188f / 255f, 40f/255f); // White with the same alpha a original border colour colourSelectedBorder = new Color(1f, 1f, 1f, 40f / 255f); StartCoroutine(ShowStart()); #endregion #region Music Controller Initialisation musicController = GetComponent(); if (musicController != null) { musicController.Initialise(); } #endregion } #endregion #region Update Methods // Update is called once per frame void Update() { // Game is paused/unpaused from a Custom Player Input on the Player Input Module // which calls TogglePause(). if (!isGamePaused) { // Check if player is outside gameplay area if (!gameplayAreaBounds.Contains(player1Ship.shipInstance.TransformPosition)) { MissionFailed(); } if (demoSectionIndex == 0) { #region Demo Section 0 if (!isHUDFirstTimeShown) { InitiateDemoSection1(); demoSectionIndex = 1; } #endregion } else if (demoSectionIndex == 1) { #region Demo Section 1 // As soon as the player or a friendly AI ship gets close enough to the enemy base, initiate demo section 2 if (enemyBaseLocation != null) { bool initiateDemoSection2 = false; // Check the player ship if (player1Ship != null) { if (Vector3.SqrMagnitude(player1Ship.transform.position - enemyBaseLocation.position) < section2Dist * section2Dist) { initiateDemoSection2 = true; } } // Check the squadron A ships if (!initiateDemoSection2 && squadronAShips != null && squadronAShipsCount > 0) { for (int i = 0; i < squadronAShipsCount; i++) { if (Vector3.SqrMagnitude(squadronAShips[i].transform.position - enemyBaseLocation.position) < section2Dist * section2Dist) { initiateDemoSection2 = true; i = squadronAShipsCount; } } } // Check the squadron B ships if (!initiateDemoSection2 && squadronBShips != null && squadronAShipsCount > 0) { for (int i = 0; i < squadronAShipsCount; i++) { if (Vector3.SqrMagnitude(squadronAShips[i].transform.position - enemyBaseLocation.position) < section2Dist * section2Dist) { initiateDemoSection2 = true; i = squadronBShipsCount; } } } if (initiateDemoSection2) { InitiateDemoSection2(); demoSectionIndex = 2; } } #endregion } else if (demoSectionIndex == 2) { #region Demo Section 2 // Have all the enemy surface turrets been destroyed? if (enemySurfaceTurretsCount == 0) { InitiateDemoSection3(); demoSectionIndex = 3; } #endregion } else if (demoSectionIndex == 4) { #region Demo Section 4 if (enemyCapitalShip != null && enemyCapitalShip.shipInstance.LocalVelocity.z > 25f) { InitiateDemoSection5(); } #endregion } else if (demoSectionIndex == 5) { #region Demo Section 5 #endregion } if (demoSectionIndex >= 3) { #region Demo Sections 3-5 if (enemyCapitalShip != null) { // If we have not yet brought down the shields for the bridge, check if we should if (enemyCapitalShip.shipInstance.localisedDamageRegionList[7].useShielding) { // Count the number of shield generators (their damage regions) with zero health int currentNumShieldGeneratorsDestroyed = 0; if (enemyCapitalShip.shipInstance.localisedDamageRegionList[3].Health <= 0f) { currentNumShieldGeneratorsDestroyed++; } if (enemyCapitalShip.shipInstance.localisedDamageRegionList[4].Health <= 0f) { currentNumShieldGeneratorsDestroyed++; } if (enemyCapitalShip.shipInstance.localisedDamageRegionList[5].Health <= 0f) { currentNumShieldGeneratorsDestroyed++; } if (enemyCapitalShip.shipInstance.localisedDamageRegionList[6].Health <= 0f) { currentNumShieldGeneratorsDestroyed++; } // Bring down the shield if the health of all the shield generator regions is zero if (currentNumShieldGeneratorsDestroyed == 4) { enemyCapitalShip.shipInstance.localisedDamageRegionList[7].useShielding = false; isBridgeAttackEnabled = true; // We want to see the player attacking the capital ship bridge for the end-game scenario if (isPOICameraDisplayed) { SwitchPointOfInterestCamera(enemyCapitalShip); } // Update the objective message if (tempDisplayMessageString == null) { tempDisplayMessageString = new scsmmedia.SCSMString(100); } tempDisplayMessageString.Set("Objective: Destroy the bridge of the enemy capital ship."); shipDisplayModule.SetDisplayMessageText(section3Message, tempDisplayMessageString.ToString()); } else if (currentNumShieldGeneratorsDestroyed > prevNumShieldGeneratorsDestroyed) { // If we have destroyed one or more shield generators in the last frame, update the objective message if (tempDisplayMessageString == null) { tempDisplayMessageString = new scsmmedia.SCSMString(100); } tempDisplayMessageString.Set("Objective: Destroy the shield generators of the enemy capital ship ("); tempDisplayMessageString.Add(currentNumShieldGeneratorsDestroyed); tempDisplayMessageString.Add("/4)."); shipDisplayModule.SetDisplayMessageText(section3Message, tempDisplayMessageString.ToString()); // Remember the number of shield generators we have destroyed prevNumShieldGeneratorsDestroyed = currentNumShieldGeneratorsDestroyed; } } // Else if the shields are already down, check if the bridge has been destroyed else if (enemyCapitalShip.shipInstance.localisedDamageRegionList[7].Health <= 0f) { // If the bridge has been destroyed, destroy the capital ship enemyCapitalShip.shipInstance.mainDamageRegion.useShielding = false; enemyCapitalShip.shipInstance.ApplyNormalDamage(1000f, ProjectileModule.DamageType.Default, Vector3.zero); } } #endregion } } } #endregion #region Private Member Methods /// /// Method to be invoked for undocking squadrons A and B. /// private void InitiallyUndockSquadronsAAndB () { // Undock 1 ship from squadron A UndockSquadronShips(squadronAShips, 1); // Undock 1 ship from squadron B UndockSquadronShips(squadronBShips, 1); } /// /// Method to be invoked for undocking squadron D ships /// from the capital ship hangar bay. /// Switch the Point of Interest camera on second monitor (if available) /// to one of the enemy ships in Capital Ship Docking bay. /// private void InitiallyUndockSquadronD() { // Undock 1 ship from squadron D UndockSquadronShips(squadronDShips, 1); // This method is called multiple times, so only switch PoI camera once // This only works in a standalone build as second monitor support is simulated // in the editor game window. // Don't switch the PoI camera if the player is already attacking the capital ship bridge - we want to see that... if (isPOICameraDisplayed && !isBridgeAttackEnabled && !isPOIwithSquadronD && squadronDShipsCount > 1) { isPOIwithSquadronD = true; // Get the second squadron D ship ShipAIInputModule shipAIInputModuleInstance = squadronDShips[1]; if (shipAIInputModuleInstance != null && shipAIInputModuleInstance.IsInitialised) { ShipControlModule shipControlModuleInstance = shipAIInputModuleInstance.GetShipControlModule; if (shipControlModuleInstance != null) { SwitchPointOfInterestCamera(shipControlModuleInstance); } } } } /// /// Undocks shipsToUndock ships from shipsList (of the ships that are currently docked). /// Sets up a callback method for Squadron D from the capital ship. /// /// /// private void UndockSquadronShips(List shipsList, int shipsToUndock) { // Keep track of how many ships we have undocked in this function int shipsUndocked = 0; // Loop through all of the ships in this squadron ShipDocking shipDockingInstance; int shipsListCount = shipsList.Count; for (int i = 0; i < shipsListCount; i++) { // Check the docking state of this ship shipDockingInstance = shipsList[i].GetComponent(); if (shipDockingInstance != null) { if (shipDockingInstance.GetState() == ShipDocking.DockingState.Docked) { // Get notified when the docking state changes so we can adjust flight behaviour if (shipsList[i].GetShip.squadronId == squadronDId) { shipDockingInstance.callbackOnStateChange = OnChangeDockingStateSquadronD; } // If this ship is docked, set the ship to undocking shipDockingInstance.SetState(ShipDocking.DockingState.Undocking); // Record that we have undocked this ship shipsUndocked++; } } // Once we have undocked enough ships, exit the loop if (shipsUndocked >= shipsToUndock) { i = shipsListCount; } } } /// /// Create and initialises a squadron of AI ships in the idle state. /// /// /// /// private void InitialiseSquadron(GameObject squadronParentGameObject, ShipControlModule squadronShipPrefab, ShipDockingStation squadronDockingStation, int minDockingIndex, int maxDockingIndex, List squadronShipList, int factionID, int squadronID) { if (squadronParentGameObject != null && squadronShipPrefab != null && squadronAShips != null && squadronDockingStation != null && squadronDockingStation.shipDockingPointList != null && squadronDockingStation.shipDockingPointList.Count > minDockingIndex && squadronDockingStation.shipDockingPointList.Count > maxDockingIndex) { // Loop through the docking points for (int i = minDockingIndex; i <= maxDockingIndex; i++) { // Instantiate the ship at the origin GameObject shipGameObjectInstance = Object.Instantiate(squadronShipPrefab.gameObject, Vector3.zero, Quaternion.identity); // Append an index to the name shipGameObjectInstance.name += " " + (i - minDockingIndex + 1).ToString(); // Parent the ship to the squadron transform shipGameObjectInstance.transform.parent = squadronParentGameObject.transform; // Add the ship to the Small Ships Unity Layer so that the Capital ship // ignore it for obstacle avoidance if (isSmallShipsLayerDefined) { shipGameObjectInstance.layer = smallShipsUnityLayer; } // Get the ship control module instance ShipControlModule shipControlModuleInstance = shipGameObjectInstance.GetComponent(); // Get the ship AI input module instance ShipAIInputModule shipAIInputModuleInstance = shipGameObjectInstance.GetComponent(); // Get the ship docking instance ShipDocking shipDockingInstance = shipGameObjectInstance.GetComponent(); if (shipControlModuleInstance != null && shipAIInputModuleInstance != null) { // Add the ship to the list squadronShipList.Add(shipAIInputModuleInstance); // Initialise the ship shipControlModuleInstance.InitialiseShip(); shipAIInputModuleInstance.Initialise(); // Start with the ships in the idle state if (shipAIInputModuleInstance.IsInitialised) { shipAIInputModuleInstance.SetState(AIState.idleStateID); } // Set the faction ID of the ship shipControlModuleInstance.shipInstance.factionId = factionID; // Set the squadron ID of the ship shipControlModuleInstance.shipInstance.squadronId = squadronID; // Set up callbacks shipControlModuleInstance.callbackOnRespawn = OnRespawnCallback; shipAIInputModuleInstance.callbackCompletedStateAction = CompletedStateActionCallback; // In this game, ships start docked if (shipDockingInstance != null) { if (!shipDockingInstance.IsInitialised) { shipDockingInstance.Initialise(); } // Dock the ship with the correct docking point - this also sets its position squadronDockingStation.AssignShipToDockingPoint(shipControlModuleInstance, shipDockingInstance, i); shipDockingInstance.SetState(ShipDocking.DockingState.Docked); } // Set the spawn point of the ship if (shipControlModuleInstance.shipInstance.squadronId != squadronID) { shipControlModuleInstance.shipInstance.customRespawnPosition = shipGameObjectInstance.transform.position; shipControlModuleInstance.shipInstance.respawningMode = Ship.RespawningMode.RespawnAtSpecifiedPosition; } // Make the ship visible to radar shipControlModuleInstance.EnableRadar(); } } } } /// /// Assigns targets for an entire squadron. /// /// /// /// /// /// /// private void AssignSquadronTargets(SSCRadarQuery squadronRadarQuery, List squadronShipList, int squadronShipsCount, Vector3 radarQueryCentre, float radarQueryRange, int[] radarQueryFactionsToInclude, int[] radarQueryFactionsToExclude, int[] radarQuerySquadronsToInclude, int[] radarQuerySquadronsToExclude, SSCRadarQuery.QuerySortOrder querySortOrder) { if (squadronShipList != null && squadronShipsCount > 0 && squadronRadarQuery != null) { // Set up radar query SetSquadronTargets(squadronRadarQuery, radarQueryCentre, radarQueryRange, radarQueryFactionsToInclude, radarQueryFactionsToExclude, radarQuerySquadronsToInclude, radarQuerySquadronsToExclude, querySortOrder); // Execute the query sscRadar.GetRadarResults(squadronRadarQuery, sscRadarBlipsList); sscRadarBlipsListCount = sscRadarBlipsList.Count; if (sscRadarBlipsListCount > 0) { // Loop through the ships of the specified squadron int radarResultIndex = 0; for (int shipIndex = 0; shipIndex < squadronShipsCount; shipIndex++) { // Get the radar result and assign it as this ship's target squadronShipList[shipIndex].SetState(AIState.dogfightStateID); // TODO: This won't work if the target is not a ship... squadronShipList[shipIndex].AssignTargetShip(sscRadarBlipsList[radarResultIndex].shipControlModule); // Increment radar result index radarResultIndex++; if (radarResultIndex >= sscRadarBlipsListCount) { radarResultIndex = 0; } } } else { Debug.Log("TechDemo2.cs AssignSquadronTargets: could not assign targets as none match the provided criteria."); } } } /// /// Sets up the radar query for a squadron, so that the correct targets can be found when we need them. /// /// /// /// /// /// /// /// private void SetSquadronTargets(SSCRadarQuery squadronRadarQuery, Vector3 radarQueryCentre, float radarQueryRange, int[] radarQueryFactionsToInclude, int[] radarQueryFactionsToExclude, int[] radarQuerySquadronsToInclude, int[] radarQuerySquadronsToExclude, SSCRadarQuery.QuerySortOrder querySortOrder) { if (squadronRadarQuery != null) { // Set up radar query - this will be used for assigning individual targets when we need them squadronRadarQuery.centrePosition = radarQueryCentre; squadronRadarQuery.range = radarQueryRange; squadronRadarQuery.factionId = -1; squadronRadarQuery.factionsToInclude = radarQueryFactionsToInclude; squadronRadarQuery.factionsToExclude = radarQueryFactionsToExclude; squadronRadarQuery.squadronsToInclude = radarQuerySquadronsToInclude; squadronRadarQuery.squadronsToExclude = radarQuerySquadronsToExclude; squadronRadarQuery.is3DQueryEnabled = false; squadronRadarQuery.querySortOrder = querySortOrder; } } /// /// Assigns a new target to an AI ship. /// /// /// /// private void AssignNewTarget (ShipAIInputModule shipAIInputModuleInstance, ShipControlModule shipControlModuleInstance, SSCRadarQuery squadronRadarQuery) { if (shipAIInputModuleInstance != null && shipControlModuleInstance != null && squadronRadarQuery != null) { // Execute the query sscRadar.GetRadarResults(squadronRadarQuery, sscRadarBlipsList); sscRadarBlipsListCount = sscRadarBlipsList.Count; if (sscRadarBlipsListCount > 0) { // Choose a random radar blip int chosenRadarBlipIndex = 0; if ((int)squadronRadarQuery.querySortOrder == SSCRadarQuery.querySortOrderNoneInt) { // If there was no sort order for the query, choose completely at random chosenRadarBlipIndex = UnityEngine.Random.Range(0, sscRadarBlipsListCount); } else { // If there was a specified sort order for the query, make sure we pick one of the top three results UnityEngine.Random.Range(0, sscRadarBlipsListCount < 3 ? sscRadarBlipsListCount : 3); } SSCRadarBlip chosenRadarBlip = sscRadarBlipsList[chosenRadarBlipIndex]; if (chosenRadarBlip.radarItemType == SSCRadarItem.RadarItemType.AIShip || chosenRadarBlip.radarItemType == SSCRadarItem.RadarItemType.PlayerShip) { // Chosen target is a ship, so attack it with the dogfight state shipAIInputModuleInstance.SetState(AIState.dogfightStateID); shipAIInputModuleInstance.AssignTargetShip(chosenRadarBlip.shipControlModule); } else if (chosenRadarBlip.radarItemType == SSCRadarItem.RadarItemType.GameObject) { GameObject chosenRadarBlipGameObject = chosenRadarBlip.itemGameObject; if (chosenRadarBlipGameObject != null) { SurfaceTurretModule surfaceTurretModuleInstance = chosenRadarBlipGameObject.GetComponent(); if (surfaceTurretModuleInstance != null) { // Chosen target is a turret, so attack it with the strafing run state shipAIInputModuleInstance.SetState(AIState.strafingRunStateID); shipAIInputModuleInstance.AssignTargetPosition(surfaceTurretModuleInstance.TransformPosition); shipAIInputModuleInstance.AssignSurfaceTurretsToEvade(enemySurfaceTurrets); shipAIInputModuleInstance.AssignTargetRadius(enemyTurretTargetRadius); } else { Debug.LogWarning("TechDemo2.cs AssignNewTarget: surface turret module instance is null."); } } else { Debug.LogWarning("TechDemo2.cs AssignNewTarget: chosen radar blip gameobject is null."); } } else { Debug.LogWarning("TechDemo2.cs AssignNewTarget: Radar blip has wrong type."); } } else { Debug.LogWarning("TechDemo2.cs AssignNewTarget: could not assign target as none match the provided criteria."); } } else { Debug.LogWarning("TechDemo2.cs AssignNewTarget: squadron radar query is null."); } } /// /// Assigns a new path to an AI ship. /// private void AssignNewPath (ShipAIInputModule shipAIInputModuleInstance, PathData newPathToFollow) { if (shipAIInputModuleInstance != null && newPathToFollow != null) { // Set the ship's state to the "move to" state shipAIInputModuleInstance.SetState(AIState.moveToStateID); // Set the target path to the new path shipAIInputModuleInstance.AssignTargetPath(newPathToFollow); } } /// /// Gives an AI ship new instructions. /// /// /// /// private void GetNewInstructions (ShipControlModule shipControlModuleInstance, ShipAIInputModule shipAIInputModuleInstance, ShipDocking shipDockingInstance, bool completedStateAction, bool respawned) { if (shipControlModuleInstance != null && shipAIInputModuleInstance != null && shipDockingInstance != null) { int squadronId = shipControlModuleInstance.shipInstance.squadronId; if (respawned && !(demoSectionIndex >= 2 && (squadronId == squadronAId || squadronId == squadronBId))) { #region Respawn Instructions if (squadronId == squadronAId) { // Recently respawned. Needs to reset to docked position, then initiate undocking process. friendlyBaseDockingStation.AssignShipToDockingPoint(shipControlModuleInstance, shipDockingInstance); shipDockingInstance.SetState(ShipDocking.DockingState.Docked); shipDockingInstance.SetState(ShipDocking.DockingState.Undocking); } else if (squadronId == squadronBId) { // Recently respawned. Needs to reset to docked position, then initiate undocking process. friendlyBaseDockingStation.AssignShipToDockingPoint(shipControlModuleInstance, shipDockingInstance); shipDockingInstance.SetState(ShipDocking.DockingState.Docked); shipDockingInstance.SetState(ShipDocking.DockingState.Undocking); } else if (squadronId == squadronCId) { // Recently respawned. Needs to reset to docked position, then initiate undocking process. enemyBaseDockingStation1.AssignShipToDockingPoint(shipControlModuleInstance, shipDockingInstance); shipDockingInstance.SetState(ShipDocking.DockingState.Docked); shipDockingInstance.SetState(ShipDocking.DockingState.Undocking); } else if (squadronId == squadronDId) { // If we have configured the capital ship hangar ship's to respawn at original location, don't attempt to launch them from the hangar dock again. // At the moment, the initial setting for AI ships is overridden in InitialiseSquadron(..). if (shipControlModuleInstance.shipInstance.respawningMode == Ship.RespawningMode.RespawnAtOriginalPosition) { // Recently respawned. Needs to reset to docked position, then initiate undocking process. capitalshipDockingStation.AssignShipToDockingPoint(shipControlModuleInstance, shipDockingInstance); shipDockingInstance.SetState(ShipDocking.DockingState.Docked); shipDockingInstance.SetState(ShipDocking.DockingState.Undocking); } else { shipDockingInstance.SetState(ShipDocking.DockingState.NotDocked); } } #endregion } else { if (demoSectionIndex == 1) { #region Demo Section 1 if (squadronId == squadronAId) { #region Squadron A // Finished undocking. Needs to follow the approach path. AssignNewPath(shipAIInputModuleInstance, friendlyApproachPath); #endregion } else if (squadronId == squadronBId) { #region Squadron B // Finished undocking. Needs to follow the approach path. AssignNewPath(shipAIInputModuleInstance, friendlyApproachPath); #endregion } #endregion } else { #region Demo Sections 2-5 // For demo section 2, targets should be prioritised by distance to the enemy base // For demo sections 3-5, targets should be prioritised by distance to the enemy capital ship bool centreQueryAroundCapitalShip = demoSectionIndex > 2 && enemyCapitalShip != null; if (squadronId == squadronAId) { #region Squadron A // Get the current AI state int currentStateID = shipAIInputModuleInstance.GetState(); if (currentStateID == AIState.moveToStateID) { PathData lastTargetPath = shipAIInputModuleInstance.GetTargetPath(); if (lastTargetPath == friendlyApproachPath || lastTargetPath == diversionPath1) { // Finished following path. Need to get a target. AssignNewTarget(shipAIInputModuleInstance, shipControlModuleInstance, squadronARadarQuery); } else { // Finished undocking. Need to follow approach path. AssignNewPath(shipAIInputModuleInstance, friendlyApproachPath); } } else { // Destroyed the target. Need to get a new target. if (centreQueryAroundCapitalShip) { squadronARadarQuery.centrePosition = enemyCapitalShip.transform.position; } AssignNewTarget(shipAIInputModuleInstance, shipControlModuleInstance, squadronARadarQuery); } #endregion } else if (squadronId == squadronBId) { #region Squadron B // Get the current AI state int currentStateID = shipAIInputModuleInstance.GetState(); if (currentStateID == AIState.moveToStateID) { PathData lastTargetPath = shipAIInputModuleInstance.GetTargetPath(); if (lastTargetPath == friendlyApproachPath || lastTargetPath == diversionPath1) { // Finished following path. Need to get a target. AssignNewTarget(shipAIInputModuleInstance, shipControlModuleInstance, squadronBRadarQuery); } else { // Finished undocking. Need to follow approach path. AssignNewPath(shipAIInputModuleInstance, friendlyApproachPath); } } else { // Destroyed the target, or else finished the strafing run. Need to get a new target. if (centreQueryAroundCapitalShip) { squadronBRadarQuery.centrePosition = enemyCapitalShip.transform.position; } AssignNewTarget(shipAIInputModuleInstance, shipControlModuleInstance, squadronBRadarQuery); } #endregion } else if (squadronId == squadronCId) { #region Squadron C // Finished undocking, or else destroyed the target. Need to get a new target. if (centreQueryAroundCapitalShip) { squadronCRadarQuery.centrePosition = enemyCapitalShip.transform.position; } AssignNewTarget(shipAIInputModuleInstance, shipControlModuleInstance, squadronCRadarQuery); #endregion } else if (squadronId == squadronDId) { #region Squadron D // Finished undocking, or else destroyed the target. Need to get a new target. if (centreQueryAroundCapitalShip) { squadronDRadarQuery.centrePosition = enemyCapitalShip.transform.position; } AssignNewTarget(shipAIInputModuleInstance, shipControlModuleInstance, squadronDRadarQuery); #endregion } #endregion } } } } /// /// Will set the quality of the game based on the current /// gameQuality setting. /// This assumes the Ship AI Input Module is already initialised on /// all AI ships. /// private void SetGameQuality() { bool isHigh = gameQuality == GameQuality.High; bool isMedium = gameQuality == GameQuality.Medium; #region Terrain Settings if (landscapeParent != null) { Terrain[] terrains = landscapeParent.GetComponentsInChildren(); int numTerrains = terrains == null ? 0 : terrains.Length; for (int tIdx = 0; tIdx < numTerrains; tIdx++) { Terrain terrain = terrains[tIdx]; if (terrain != null && terrain.terrainData != null) { terrain.heightmapPixelError = isHigh ? 1f : isMedium ? 5f : 10f; terrain.basemapDistance = isHigh ? 5000f : isMedium ? 3000f : 1000f; #if UNITY_2019_1_OR_NEWER terrain.shadowCastingMode = isHigh ? UnityEngine.Rendering.ShadowCastingMode.On : UnityEngine.Rendering.ShadowCastingMode.Off; #else terrain.castShadows = isHigh ? true : false; #endif } } } #endregion #region Lights if (friendlyBaseDockingStation != null) { Light[] friendlyBaseLights = friendlyBaseDockingStation.GetComponentsInChildren(); int numLights = friendlyBaseLights == null ? 0 : friendlyBaseLights.Length; for (int lgtIdx = 0; lgtIdx < numLights; lgtIdx++) { Light light = friendlyBaseLights[lgtIdx]; if (light != null) { if (light.type == LightType.Spot) { light.enabled = isHigh ? true : false; } if (light.type == LightType.Point) { light.enabled = isHigh || isMedium ? true : false; } } } } #endregion #region Particle Effects SetThrusterEffectQuality(squadronAShips); SetThrusterEffectQuality(squadronBShips); SetThrusterEffectQuality(squadronCShips); SetThrusterEffectQuality(squadronDShips); if (player1Ship != null) { if (isHigh || isMedium) { player1Ship.EnableThrusterEffects("Glow"); } else { player1Ship.DisableThrusterEffects("Glow"); } player1Ship.ReinitialiseThrusterEffects(); } #endregion #region General // Override the target frame rate by not using the monitor refresh rate (vSync = 1) // Some devices can have a high monitor refresh rate like 120 or 144. QualitySettings.vSyncCount = 0; if (limitFrameRate) { Application.targetFrameRate = isHigh ? 60 : 30; } #endregion } /// /// Turn on or off some thruster particle effects based on the gameQuality level. /// This assumes the Ship AI Input Module is already initialised on all AI ships. /// For this demo we want to not use particle systems that produce a Glow Effect /// unless the gameQuality is High. /// /// private void SetThrusterEffectQuality(List squadronAIList) { bool isHigh = gameQuality == GameQuality.High; int numSquadronAIShips = squadronAIList == null ? 0 : squadronAIList.Count; for (int i = 0; i < numSquadronAIShips; i++) { ShipAIInputModule aiShip = squadronAIList[i]; // If the AI ship module has been initialised, we can get the ship control module // without performing another GetComponent(). if (aiShip != null && aiShip.IsInitialised) { ShipControlModule ship = aiShip.GetShipControlModule; if (ship != null && ship.IsInitialised) { // Look for, and enable/disable particle systems on a gameobject // with "Glow" in their name. if (isHigh) { ship.EnableThrusterEffects("Glow"); } else { ship.DisableThrusterEffects("Glow"); } ship.ReinitialiseThrusterEffects(); } } } } /// /// Check to see if the Point of Interest camera is used on the second monitor (if there is one). /// Currently turning it off doesn't work due to a Unity limitation with additional displays.... /// Only enable if Game Quality is High. /// NOTE: This only works in a standalone build as second monitor support is simulated in the editor Game view. /// In the editor Display.displays will always return 1. So as a workaround, go to Game view, click /// Add Tab->Game view and move it to the second monitor. Then, on second Game view, set Display to Display 2. /// In the editor, with 2+ Game views, Maximise On Play will have no effect. /// private void CheckPointOfInterestCamera() { // Point of interest camera is only available on standalone and in the editor. // It currently does not work in a UWP Win10 build. It is not supported on Xbox. // It makes no sense on android. #if (!UNITY_STANDALONE_OSX && !UNITY_STANDALONE_WIN && !UNITY_EDITOR) usePOICamera = false; #endif if (usePOICamera && !isPOICameraDisplayed && pointOfInterestCamera != null && gameQuality == GameQuality.High) { // Multiple displays can be activated (once) in a build and then cannot be de-activated for the current session if (SSCUtils.VerifyTargetDisplay(2, true)) { if (!pointOfInterestCamera.gameObject.activeSelf) { pointOfInterestCamera.gameObject.SetActive(true); } // Switch the displays (monitors) around. if (isSwitchDisplays) { pointOfInterestCamera.SetCameraTargetDisplay(1); if (shipCameraModule1 != null) { shipCameraModule1.SetCameraTargetDisplay(2); } if (shipDisplayModule != null) { shipDisplayModule.SetCanvasTargetDisplay(2); } if (sscRadar != null) { sscRadar.SetCanvasTargetDisplay(2); } if (player1Ship != null) { PlayerInputModule playerInputModule = player1Ship.GetComponent(); if (playerInputModule != null) { playerInputModule.SetTargetDisplay(2); } } if (menuPanel != null) { menuPanel.transform.parent.GetComponent().targetDisplay = 1; } } // Ensure the POI camera show stars if (celestials != null && celestials.IsInitialised) { celestials.camera2 = pointOfInterestCamera.GetComponent(); celestials.InitialiseCamera2(); if (isSwitchDisplays) { // We've updated the target displays for the two cameras, so need // to refresh celestials. celestials.RefreshCameras(); } } isPOICameraDisplayed = true; } else { DisablePointOfInterestCamera(); } } else { DisablePointOfInterestCamera(); } } /// /// Stop the PoI camera from rendering /// private void DisablePointOfInterestCamera() { if (pointOfInterestCamera != null) { pointOfInterestCamera.DisableCamera(); if (pointOfInterestCamera.gameObject.activeSelf) { pointOfInterestCamera.gameObject.SetActive(false); } } } /// /// Switch the Point of View camera to either the capital ship or one of the ships that were launched from the capital ship /// /// private void SwitchPointOfInterestCamera(ShipControlModule shipToWatch) { if (isPOICameraDisplayed && shipToWatch != null) { int _shipId = shipToWatch.shipInstance.shipId; if (_shipId == enemyCapitalShip.shipInstance.shipId) { // The PoI camera will track along beside the capital ship and watch the action pointOfInterestCamera.targetOffset = new Vector3(-600f, 400f, 0f); pointOfInterestCamera.cameraRotationMode = ShipCameraModule.CameraRotationMode.AimAtTarget; pointOfInterestCamera.lockToTargetPosition = false; pointOfInterestCamera.lockToTargetRotation = false; } else { // A ship from the capital ship hangar. pointOfInterestCamera.targetOffset = new Vector3(0f, 2.5f, -9f); pointOfInterestCamera.cameraRotationMode = ShipCameraModule.CameraRotationMode.FollowTargetRotation; pointOfInterestCamera.lockToTargetPosition = true; pointOfInterestCamera.lockToTargetRotation = true; } pointOfInterestCamera.SetTarget(shipToWatch); } } #endregion #region UI Methods /// /// Show or hide the Menu in the scene. /// Enable the cursor when the menu is shown /// Disable the cursor immediately when the menu is disabled. /// /// private void ShowMenu(bool isVisible) { if (menuPanel != null) { menuPanel.SetActive(isVisible); } if (shipDisplayModule != null) { if (isVisible || !shipDisplayModule.autoHideCursor) { shipDisplayModule.ShowCursor(); } else { shipDisplayModule.HideCursor(); } } } /// /// Show or hide the Quality settings in the scene. /// Note, this is a child of the Menu panel. /// /// private void ShowQuality(bool isVisible) { if (isVisible) { HighlightQuality(); } if (qualityPanel != null) { qualityPanel.SetActive(isVisible); } } /// /// Highlight the button border of the current game quality /// private void HighlightQuality() { if (qualityLowButton != null && qualityMedButton != null && qualityHighButton != null) { if (gameQuality == GameQuality.Low) { qualityLowBorderImg.color = colourSelectedBorder; qualityMedBorderImg.color = colourDefaultBorder; qualityHighBorderImg.color = colourDefaultBorder; } else if (gameQuality == GameQuality.Medium) { qualityLowBorderImg.color = colourDefaultBorder; qualityMedBorderImg.color = colourSelectedBorder; qualityHighBorderImg.color = colourDefaultBorder; } else { qualityLowBorderImg.color = colourDefaultBorder; qualityMedBorderImg.color = colourDefaultBorder; qualityHighBorderImg.color = colourSelectedBorder; } } } /// /// Show or hide the Game Over panel and text in the scene /// /// /// private void ShowGameOver(bool isVisible, bool isMissionSuccessful) { if (missionOverPanel != null) { if (isVisible) { if (isMissionSuccessful) { if (missionSuccessTitle != null) { missionSuccessTitle.SetActive(true); } if (missionSuccessSubTitle != null) { missionSuccessSubTitle.SetActive(true); } if (missionFailedTitle != null) { missionFailedTitle.SetActive(false); } if (missionFailedSubTitle != null) { missionFailedSubTitle.SetActive(false); } } else { if (missionSuccessTitle != null) { missionSuccessTitle.SetActive(false); } if (missionSuccessSubTitle != null) { missionSuccessSubTitle.SetActive(false); } if (missionFailedTitle != null) { missionFailedTitle.SetActive(true); } if (missionFailedSubTitle != null) { missionFailedSubTitle.SetActive(true); } } } missionOverPanel.SetActive(isVisible); } } /// /// Show or hide a button /// /// /// private void ShowButton(Button button, bool isVisible) { if (button != null) { button.gameObject.SetActive(isVisible); } } /// /// Set the focus to a button in the UI /// /// private void SetButtonFocus(Button button) { if (button != null && eventSystem != null) { // If the button was already selected when button was last enabled, // when it is made active again it doesn't appear in it's selected state (grey) // However, the object is actually selected. So unselect it, then reselect it. eventSystem.SetSelectedGameObject(null); eventSystem.SetSelectedGameObject(button.gameObject); } } /// /// The button has been selected or deselected. /// UI.Text is stored as a reference in the scene to avoid having to do /// a GetComponentsInChildren. /// /// /// /// private void SelectButton(Button button, Text text, bool isSelected) { if (button != null && text != null) { text.color = isSelected ? colourSelectedBtnText : colourNonSelectedBtnText; } } /// /// Wait until the player camera is in the correct position, /// pause updates, then show the Start options. /// /// private IEnumerator ShowStart() { ShowMenu(false); // While the PlayerCamera is repositioning we need to prevent player ship moving if (player1Ship != null) { player1Ship.DisableShipMovement(); } yield return new WaitForSeconds(0.5f); PauseGame(); ShowButton(resumeButton, false); ShowButton(startButton, true); ShowButton(restartButton, false); ShowButton(quitButton, true); ShowQuality(true); ShowMenu(true); SetButtonFocus(startButton); System.GC.Collect(); } /// /// The mission failed so pause game and tell the user /// private void MissionFailed() { PauseGame(); ShowGameOver(true, false); ShowButton(resumeButton, false); ShowButton(quitButton, true); SetButtonFocus(restartButton); } /// /// The mission was successful, so pause game and tell the user /// private void MissionCompleted() { PauseGame(); ShowGameOver(true, true); ShowButton(resumeButton, false); ShowButton(quitButton, true); SetButtonFocus(restartButton); } #endregion #region Pause Game Methods /// /// Pause the game by: /// 1. Pausing the camera /// 2. Pausing the player ship /// 3. Pausing all AI squadrons /// 4. Pausing the Capital Ship if it is escaping /// private void PauseGame() { // Pause Cameras (assumes the camera is enabled) - this may not always be true... // Disable camera movement, making sure to perform camera movement for this frame if (shipCameraModule1 != null && shipCameraModule1.target != null) { shipCameraModule1.MoveCamera(); shipCameraModule1.DisableCamera(); } if (isPOICameraDisplayed) { if (pointOfInterestCamera.target != null) { pointOfInterestCamera.MoveCamera(); } pointOfInterestCamera.DisableCamera(); } // Pause ships // Assume the player ship is always enabled. This could be a problem // if the ship is respawning... if (player1Ship != null && player1Ship.IsInitialised) { player1Ship.DisableShip(false); } // If this is the first time paused, reserve enough capacity for all the AI Ships int numSquadronAShips = squadronAShips == null ? 0 : squadronAShips.Count; int numSquadronBShips = squadronBShips == null ? 0 : squadronBShips.Count; int numSquadronCShips = squadronCShips == null ? 0 : squadronCShips.Count; int numSquadronDShips = squadronDShips == null ? 0 : squadronDShips.Count; if (pausedAIShips == null) { pausedAIShips = new List(numSquadronAShips + numSquadronBShips + numSquadronCShips + numSquadronDShips); } AddSquadronToPausedList(numSquadronAShips, squadronAShips); AddSquadronToPausedList(numSquadronBShips, squadronBShips); AddSquadronToPausedList(numSquadronCShips, squadronCShips); AddSquadronToPausedList(numSquadronDShips, squadronDShips); // If the Capital Ship is moving, need to also disable it if (demoSectionIndex >= 4 && enemyCapitalShip != null && enemyCapitalShip.IsInitialised && enemyCapitalShip.ShipIsEnabled()) { enemyCapitalShip.DisableShip(false); } // Pause game music (if any) if (musicController != null) { musicController.PauseMusic(); } AudioListener.pause = true; // Pause Beams, Destructs, Projectiles and effects sscManager.PauseBeams(); sscManager.PauseDestructs(); sscManager.PauseProjectiles(); sscManager.PauseEffectsObjects(); // turn off the heads-up display if (shipDisplayModule != null) { shipDisplayModule.HideHUD(); } // turn off the radar mini-map if (sscRadar != null) { sscRadar.HideUI(); } // Change the UI ShowButton(resumeButton, true); ShowButton(startButton, false); ShowButton(restartButton, true); ShowButton(quitButton, true); ShowQuality(false); ShowMenu(true); SetButtonFocus(resumeButton); Time.timeScale = 0f; isGamePaused = true; } /// /// Set the game to be unpaused during the next frame. /// When timeScale has been set to 0, ships and camera modules /// will error with NaN if we attempt to set the timeScale to /// non-zero and then immediately try to calculate moment and /// force. /// private IEnumerator UnPauseGameNextFrame() { Time.timeScale = 1f; yield return new WaitForEndOfFrame(); ShowMenu(false); UnPauseGame(); } /// /// Unpause the game by: /// 1. Unpausing beams, destructs, projectiles and effects objects /// 2. Unpausing the player ship /// 3. Unpausing the Capital Ship if it is escaping /// 4. Unpausing all AI squadrons and check if any are still docked in a hanger /// 5. Unpausing the camera /// NOTE: We don't want to do this in the same frame /// that timeScale was changed from 0 to say 1.0. /// private void UnPauseGame() { // Unpause beams, destructs, projectiles and effects sscManager.ResumeBeams(); sscManager.ResumeDestructs(); sscManager.ResumeProjectiles(); sscManager.ResumeEffectsObjects(); // Unpause ships if (player1Ship != null && player1Ship.IsInitialised) { player1Ship.EnableShip(false, false); } // If the capital ship was taking off or escaping, re-enable it if (demoSectionIndex == 4 && enemyCapitalShip != null && enemyCapitalShip.IsInitialised) { enemyCapitalShip.EnableShip(false, false); } int numPauseAIShips = pausedAIShips == null ? 0 : pausedAIShips.Count; for (int i = 0; i < numPauseAIShips; i++) { ShipAIInputModule aiShip = pausedAIShips[i]; if (aiShip != null && aiShip.IsInitialised) { ShipControlModule shipControlModule = aiShip.GetShipControlModule; if (shipControlModule != null && shipControlModule.IsInitialised) { if (shipControlModule.IsRespawning) { shipControlModule.ResumeRespawning(); } else { shipControlModule.EnableShip(false, false); // Check if any AI ships are still docked in one of the hangers. if (shipControlModule.GetShipDocking(false).GetStateInt() == ShipDocking.dockedInt) { // Re-dock the ship after EnableShip(..) was called. shipControlModule.GetShipDocking(false).SetState(ShipDocking.DockingState.Docked); } } } } } // Enable the cameras if (shipCameraModule1 != null) { shipCameraModule1.EnableCamera(); } if (isPOICameraDisplayed) { pointOfInterestCamera.EnableCamera(); } // Unpause game music (if any) if (musicController != null) { musicController.ResumeMusic(); } AudioListener.pause = false; if (shipDisplayModule != null) { // Due to a delayed startup procedure in the TechDemo, we need to // re-enable certain items on the HUD here. if (isHUDFirstTimeShown) { isHUDFirstTimeShown = false; // Show the active reticle. This will also turn on the heads-up display. shipDisplayModule.ShowDisplayReticle(); shipDisplayModule.ShowAltitude(); shipDisplayModule.ShowAirSpeed(); shipDisplayModule.ShowDisplayGauges(); } else { shipDisplayModule.ShowHUD(); } // If we have the menu and HUD on the second monitor, we don't want to centre the cursor // on the first monitor. if (!(isPOICameraDisplayed && isSwitchDisplays)) { shipDisplayModule.CentreCursor(); } } // turn on the radar mini-map if (sscRadar != null) { sscRadar.ShowUI(); } if (numPauseAIShips > 0) { pausedAIShips.Clear(); } isGamePaused = false; } /// /// Add all the AI Ships in a squadron to the list of ships to pause and unpause /// /// /// private void AddSquadronToPausedList(int numSquadronAIShips, List squadronAIList) { for (int i = 0; i < numSquadronAIShips; i++) { ShipAIInputModule aiShip = squadronAIList[i]; if (aiShip != null && aiShip.IsInitialised) { ShipControlModule ship = aiShip.GetShipControlModule; if (ship != null && ship.IsInitialised) { // Only record ships that are either respawning or are currently enabled. if (ship.IsRespawning) { pausedAIShips.Add(aiShip); ship.PauseRespawning(); } else if (ship.ShipIsEnabled()) { pausedAIShips.Add(aiShip); ship.DisableShip(false); } } } } } #endregion #region Callback Methods /// /// Callback for when an AI ship completes a state action. /// /// public void CompletedStateActionCallback(ShipAIInputModule shipAIInputModuleInstance) { if (shipAIInputModuleInstance != null) { ShipControlModule shipControlModuleInstance = shipAIInputModuleInstance.GetShipControlModule; GetNewInstructions(shipControlModuleInstance, shipAIInputModuleInstance, shipControlModuleInstance == null ? null : shipControlModuleInstance.GetShipDocking(false), true, false); } } /// /// Callback for when an AI ship respawns. /// /// /// public void OnRespawnCallback(ShipControlModule shipControlModuleInstance, ShipAIInputModule shipAIInputModuleInstance) { if (shipControlModuleInstance != null) { GetNewInstructions(shipControlModuleInstance, shipAIInputModuleInstance, shipControlModuleInstance.GetShipDocking(false), false, true); } } /// /// Callback for when the Player ship respawns /// /// /// public void OnPlayerRespawnCallback(ShipControlModule shipControlModuleInstance, ShipAIInputModule shipAIInputModuleInstance) { // Is the player ship currently using the HUD? if (shipDisplayModule.IsSourceShip(shipControlModuleInstance)) { shipDisplayModule.ShowHUD(); } } /// /// Callback for when the player ship is destroyed. /// Turn off player ship HUD /// /// public void OnPlayerDestroyed(Ship shipInstance) { // Is the player ship currently using the HUD? if (shipDisplayModule.IsSourceShip(shipInstance)) { shipDisplayModule.HideHUD(); } } /// /// Callback for when a surface turret is destroyed in the enemy base /// /// public void OnSurfaceTurretDestroyed(SurfaceTurretModule surfaceTurretModuleInstance) { if (surfaceTurretModuleInstance != null) { for (int i = 0; i < enemySurfaceTurretsCount; i++) { if (enemySurfaceTurrets[i] != null && enemySurfaceTurrets[i].GetInstanceID() == surfaceTurretModuleInstance.GetInstanceID()) { // Remove the turret from the list enemySurfaceTurrets.RemoveAt(i); enemySurfaceTurretsCount--; break; } } // Update the objective message if (tempDisplayMessageString == null) { tempDisplayMessageString = new scsmmedia.SCSMString(100); } tempDisplayMessageString.Set("Objective: Destroy the enemy turrets ("); tempDisplayMessageString.Add(4 - enemySurfaceTurretsCount); tempDisplayMessageString.Add("/4)."); shipDisplayModule.SetDisplayMessageText(section2Message, tempDisplayMessageString.ToString()); } } /// /// Called when a Squadron D ship (from the capital ship) changes it's docking state. /// We are using this to change the ship behaviour while undocking or in normal flight. /// /// /// /// /// public void OnChangeDockingStateSquadronD(ShipDocking shipDocking, ShipControlModule shipControlModule, ShipAIInputModule shipAIInputModule, ShipDocking.DockingState prevState) { int currentState = shipDocking.GetStateInt(); if (currentState == ShipDocking.notDockedInt) { // Configure ship for normal flight shipAIInputModule.movementAlgorithm = ShipAIInputModule.AIMovementAlgorithm.PlanarFlightBanking; // Lift off thruster shipControlModule.shipInstance.thrusterList[1].maxThrust = 10000f; // Left/Right thrusters shipControlModule.shipInstance.thrusterList[3].maxThrust = 0f; shipControlModule.shipInstance.thrusterList[4].maxThrust = 0f; // Reverse thruster shipControlModule.shipInstance.thrusterList[5].maxThrust = 10000f; // In normal flight, help the ship face the direction it is flying shipControlModule.shipInstance.arcadeMaxFlightTurningAcceleration = 300f; } else { // Configure ship for a docking procedure with the capital ship shipAIInputModule.movementAlgorithm = ShipAIInputModule.AIMovementAlgorithm.Full3DFlight; // Lift off thruster shipControlModule.shipInstance.thrusterList[1].maxThrust = 600000f; // Left/Right thrusters shipControlModule.shipInstance.thrusterList[3].maxThrust = 2000000f; shipControlModule.shipInstance.thrusterList[4].maxThrust = 2000000f; // Reverse thruster shipControlModule.shipInstance.thrusterList[5].maxThrust = 200000f; // When ship starts undocking the Flight Turn Acceleration should be 0 shipControlModule.shipInstance.arcadeMaxFlightTurningAcceleration = 0f; } } /// /// Check to see if the Capital Ship has reached the end of the Hanger 1 exit path. /// If it has, the Player has failed to destroy it. /// /// /// /// /// public void OnMissionFailure(ShipDocking shipDocking, ShipControlModule shipControlModule, ShipAIInputModule shipAIInputModule, ShipDocking.DockingState prevState) { // Has the Capital ship reached the end of the hanger 1 exit path? if (shipDocking.GetStateInt() == ShipDocking.notDockedInt) { shipAIInputModule.SetState(AIState.idleStateID); MissionFailed(); } } /// /// When the Capital Ship is destroyed, the mission has been successful. /// /// public void OnMissionSuccess(Ship ship) { // Destroy any ships are still docked in the capital ship for (int i = 0; i < squadronDShipsCount; i++) { ShipAIInputModule shipAIInputModuleInstance = squadronDShips[i]; if (shipAIInputModuleInstance != null) { ShipControlModule aiShipControlModuleInstance = shipAIInputModuleInstance.GetShipControlModule; // Prevent squadron D (which launch from the capital ship) from respawning aiShipControlModuleInstance.shipInstance.respawningMode = Ship.RespawningMode.DontRespawn; ShipDocking shipDockingInstance = aiShipControlModuleInstance.GetShipDocking(); if (shipDockingInstance.IsInitialised && shipDockingInstance.GetStateInt() != ShipDocking.notDockedInt) { // Undock the AI ship from the now destroyed capital ship shipDockingInstance.SetState(ShipDocking.DockingState.NotDocked); // Destroy the squadron D ships which are probably docked or undocking from the capital ship when it explodes. // Immediately apply an excessive amount of damage aiShipControlModuleInstance.shipInstance.ApplyNormalDamage(1000f, ProjectileModule.DamageType.Default, Vector3.zero); } } } // Give user a chance to see or hear any relevant explosions and the destruction of the capital ship. Invoke("MissionCompleted", 8f); } #endregion #region Public Member Methods #region Initiate Demo Section Methods /// /// Initiates section 1 of tech demo 2. Starts the friendly ships flying towards the enemy base from Hanger2. /// public void InitiateDemoSection1 () { // Set squadron targets for squadrons A, B and C if (enemyBaseLocation != null) { // Set friendly squadron A to attack the enemy turrets SetSquadronTargets(squadronARadarQuery, enemyBaseLocation.position, enemyBaseRadius, null, null, new int[] { squadronFId }, null, SSCRadarQuery.QuerySortOrder.None); // Set friendly squadron B to attack squadron C SetSquadronTargets(squadronBRadarQuery, enemyBaseLocation.position, enemyBaseRadius, null, null, new int[] { squadronCId }, null, SSCRadarQuery.QuerySortOrder.None); // Set enemy squadron C to attack squadrons A and B and the player SetSquadronTargets(squadronCRadarQuery, enemyBaseLocation.position, 10000f, null, null, new int[] { squadronAId, squadronBId, player1SquadronId }, null, SSCRadarQuery.QuerySortOrder.DistanceAsc3D); } // Invoke undocking methods to undock ships at different times for (int i = 0; i < Mathf.Max(squadronAShipsCount, squadronBShipsCount); i++) { Invoke("InitiallyUndockSquadronsAAndB", i*friendlyUndockingDelay + 1f); } // Tell the player to fly to the enemy base // NOTE: This currently makes msg appear just before HUD is disabled (not ideal...) shipDisplayModule.ShowDisplayMessage(section1Message); } /// /// Initiates section 2 of tech demo 2. /// Sets all of the enemy surface turrets to auto fire mode, /// undocks squadron C and sets the player/friendly respawn point. /// public void InitiateDemoSection2 () { if (enemySurfaceTurrets != null) { // Loop through all of the enemy surface turrets for (int i = 0; i < enemySurfaceTurretsCount; i++) { // Set each turret to auto fire mode enemySurfaceTurrets[i].SetAutoFire(); } } // Undock all of the ships in squadron C if (enemyBaseDockingStation1 != null) { if (squadronCShips != null && squadronCShipsCount > 0) { // Loop through all of the ships in squadron C ShipDocking shipDockingInstance; for (int i = 0; i < squadronCShipsCount; i++) { // Set the ship to undocking shipDockingInstance = squadronCShips[i].GetComponent(); if (shipDockingInstance != null) { shipDockingInstance.SetState(ShipDocking.DockingState.Undocking); } } } } // Set the player respawn point player1Ship.shipInstance.respawningMode = Ship.RespawningMode.RespawnAtSpecifiedPosition; player1Ship.shipInstance.customRespawnPosition = section2PlayerRespawnLocation.position; player1Ship.shipInstance.customRespawnRotation = Vector3.up * -90f; // Set the respawn point for squadrons A and B if (squadronAShips != null && squadronAShipsCount > 0) { // Loop through all of the ships in squadron A ShipAIInputModule shipAIInputModuleInstance; for (int i = 0; i < squadronAShipsCount; i++) { shipAIInputModuleInstance = squadronAShips[i]; if (shipAIInputModuleInstance != null) { // Set the spawn point of the ship shipAIInputModuleInstance.GetShipControlModule.shipInstance.customRespawnPosition = section2PlayerRespawnLocation.position - (Vector3.forward * i * 20f); shipAIInputModuleInstance.GetShipControlModule.shipInstance.customRespawnRotation = Vector3.up * -90f; shipAIInputModuleInstance.GetShipControlModule.shipInstance.respawningMode = Ship.RespawningMode.RespawnAtSpecifiedPosition; } } } if (squadronBShips != null && squadronBShipsCount > 0) { // Loop through all of the ships in squadron B ShipAIInputModule shipAIInputModuleInstance; for (int i = 0; i < squadronBShipsCount; i++) { shipAIInputModuleInstance = squadronBShips[i]; if (shipAIInputModuleInstance != null) { // Set the spawn point of the ship shipAIInputModuleInstance.GetShipControlModule.shipInstance.customRespawnPosition = section2PlayerRespawnLocation.position + (Vector3.forward * i * 20f); shipAIInputModuleInstance.GetShipControlModule.shipInstance.customRespawnRotation = Vector3.up * -90f; shipAIInputModuleInstance.GetShipControlModule.shipInstance.respawningMode = Ship.RespawningMode.RespawnAtSpecifiedPosition; } } } // Tell the player to attack the enemy turrets shipDisplayModule.HideDisplayMessage(section1Message); shipDisplayModule.ShowDisplayMessage(section2Message); } /// /// Initiates section 3 of tech demo 2. /// Capital ship gets ready for departure /// public void InitiateDemoSection3 () { if (enemyCapitalShip != null) { ShipDocking enemyCapitalShipDocking = enemyCapitalShip.GetShipDocking(); ShipAIInputModule enemyCapitalShipAI = enemyCapitalShip.GetShipAIInputModule(); if (enemyCapitalShipAI != null && enemyCapitalShipDocking != null) { enemyCapitalShip.EnableShip(false, true); enemyCapitalShipDocking.callbackOnStateChange = OnMissionFailure; // Open the docking station bay doors if (enemyBaseDockingStation2 != null) { SSCDoorAnimator doorAnim = enemyBaseDockingStation2.GetComponent(); if (doorAnim != null) { doorAnim.OpenSpeed = 0.2f; doorAnim.OpenDoors(); Invoke("CapitalShipTakeOff", 6f); } } } } if (squadronAShips != null && squadronAShipsCount > 0) { // Loop through all of the ships in squadron A ShipAIInputModule shipAIInputModuleInstance; for (int i = 0; i < squadronAShipsCount; i++) { shipAIInputModuleInstance = squadronAShips[i]; if (shipAIInputModuleInstance != null) { // Assign them to follow the first diversion path AssignNewPath(shipAIInputModuleInstance, diversionPath1); // Set them to respawn at their last position shipAIInputModuleInstance.GetShipControlModule.shipInstance.respawningMode = Ship.RespawningMode.RespawnAtLastPosition; } } } if (squadronBShips != null && squadronBShipsCount > 0) { // Loop through all of the ships in squadron B ShipAIInputModule shipAIInputModuleInstance; for (int i = 0; i < squadronBShipsCount; i++) { shipAIInputModuleInstance = squadronBShips[i]; if (shipAIInputModuleInstance != null) { // Assign them to follow the first diversion path AssignNewPath(shipAIInputModuleInstance, diversionPath1); // Set them to respawn at their last position shipAIInputModuleInstance.GetShipControlModule.shipInstance.respawningMode = Ship.RespawningMode.RespawnAtLastPosition; } } } if (squadronCShips != null && squadronCShipsCount > 0) { // Loop through all of the ships in squadron C ShipAIInputModule shipAIInputModuleInstance; for (int i = 0; i < squadronCShipsCount; i++) { shipAIInputModuleInstance = squadronCShips[i]; if (shipAIInputModuleInstance != null) { // Assign them to follow the first diversion path AssignNewPath(shipAIInputModuleInstance, diversionPath1); // Set them to respawn at their last position shipAIInputModuleInstance.GetShipControlModule.shipInstance.respawningMode = Ship.RespawningMode.RespawnAtLastPosition; } } } // Set the player to respawn at their last position player1Ship.shipInstance.respawningMode = Ship.RespawningMode.RespawnAtLastPosition; // Tell the player to attack the enemy capital ship shipDisplayModule.HideDisplayMessage(section2Message); shipDisplayModule.ShowDisplayMessage(section3Message); } /// /// Squadrons A, B and Player to engage capital Ship. /// public void InitiateDemoSection4() { // Capital Ship uses on-board weapons and radar to defend itself // This is controlled by Auto-Targeting component attached to Capital Ship. // Capital Ship weapons are set to Auto-Targeting // Squadrons A and B engage Capital Ship // Set friendly squadron A to attack squadron D (the enemy capital ship squadron) SetSquadronTargets(squadronARadarQuery, enemyBaseLocation.position, 10000f, null, null, new int[] { squadronCId, squadronDId }, null, SSCRadarQuery.QuerySortOrder.None); // Set friendly squadron B to attack squadron D (the enemy capital ship squadron) SetSquadronTargets(squadronBRadarQuery, enemyBaseLocation.position, 10000f, null, null, new int[] { squadronDId }, null, SSCRadarQuery.QuerySortOrder.None); // if enabled on the second display, switch the Point of Interest camera to the capital ship if (isPOICameraDisplayed) { if (pointOfInterestCamera.target == null && enemyCapitalShip != null) { pointOfInterestCamera.SetTarget(enemyCapitalShip); } } } /// /// Launch Squadron D from Capital Ship /// Have squadron D engage (friendly) squadrons A and B /// public void InitiateDemoSection5() { // Launch Squadron D from Capital Ship if (enemyCapitalShip != null) { // Invoke undocking methods to undock ships at different times from the capital ship hangar bay. for (int i = 0; i < squadronDShipsCount; i++) { Invoke("InitiallyUndockSquadronD", i * capitalUndockingDelay + 1f); } // Set enemy squadron D to attack squadrons A and B and the player // TODO this should do some sort of priority using distance to the capital ship SetSquadronTargets(squadronDRadarQuery, enemyBaseLocation.position, 10000f, null, null, new int[] { squadronAId, squadronBId, player1SquadronId }, null, SSCRadarQuery.QuerySortOrder.DistanceAsc3D); } demoSectionIndex = 5; } /// /// Enable Capital Ship and undock from Hanger1 /// public void CapitalShipTakeOff() { if (enemyCapitalShip != null) { ShipDocking enemyCapitalShipDocking = enemyCapitalShip.GetShipDocking(); ShipAIInputModule enemyCapitalShipAI = enemyCapitalShip.GetShipAIInputModule(); if (enemyCapitalShipAI != null && enemyCapitalShipDocking != null) { enemyCapitalShip.EnableShip(false, true); enemyCapitalShipDocking.SetState(ShipDocking.DockingState.Undocking); InitiateDemoSection4(); demoSectionIndex = 4; } } } #endregion #region Camera Methods /// /// Cycles the camera view for the tech demo 2 player camera. /// public void TechDemoCycleCameraView (Vector3 inputValue, int customPlayerInputEventType) { if (changeCameraView != null) { // Cycle the camera view changeCameraView.CycleCameraView(inputValue, customPlayerInputEventType); // Update the HUD depending on whether the new view is inside or outside the cockpit int newCameraViewIndex = changeCameraView.GetCurrentCameraViewIndex(); if (newCameraViewIndex == 0) { // Inside cockpit shipDisplayModule.SetTargetsViewportSize(1f, cockpitViewHeight); shipDisplayModule.SetTargetsViewportOffset(0f, (1f - cockpitViewHeight) / 2f); } else { // Outside cockpit shipDisplayModule.SetTargetsViewportSize(1f, 1f); shipDisplayModule.SetTargetsViewportOffset(0f, 0f); } } } #endregion #endregion #region Public Menu Member Methods /// /// Toggle Pause on/off. When using (new) Unity Input, like say on Xbox, /// this can be called as a CustomInput from PlayerInputModule. /// public void TogglePause() { if (!isGamePaused) { PauseGame(); } else { StartCoroutine(UnPauseGameNextFrame()); } } /// /// This is hooked up to the Resume Button in the UI /// public void ResumeGame() { // When this is called from clicking on the UI, // we need to wait until the next frame before processing // the unpause. StartCoroutine(UnPauseGameNextFrame()); } /// /// This is hooked up to the Start Button in the UI /// public void StartGame() { SetGameQuality(); ShowMenu(false); if (!isPOICameraDisplayed) { CheckPointOfInterestCamera(); } StartCoroutine(UnPauseGameNextFrame()); } /// /// This is hooked up to the Restart Button in the UI /// public void RestartGame() { // Reset the timescale. When the scene loads the PlayerCamera will Awake before // Start in the script runs. If timeScale is still 0, we'll get errors with the PlayerCamera script, // because DisableCamera hasn't been called. Time.timeScale = 1f; SceneManager.LoadScene(SceneManager.GetActiveScene().buildIndex); } /// /// Called when the Quit button is clicked on screen. /// public void QuitGame() { #if UNITY_EDITOR UnityEditor.EditorApplication.isPlaying = false; #else Application.Quit(); #endif } /// /// This is hooked up to Resume button EventTrigger in the UI /// /// public void ResumeSelected(bool isSelected) { SelectButton(resumeButton, resumeButtonText, isSelected); } /// /// This is hooked up to Start button EventTrigger in the UI /// /// public void StartSelected(bool isSelected) { SelectButton(startButton, startButtonText, isSelected); } /// /// This is hooked up to Restart button EventTrigger in the UI /// /// public void RestartSelected(bool isSelected) { SelectButton(restartButton, restartButtonText, isSelected); } /// /// This is hooked up to Quit button EventTrigger in the UI /// /// public void QuitSelected(bool isSelected) { SelectButton(quitButton, quitButtonText, isSelected); } /// /// This is hooked up to Quality Low button EventTrigger in the UI /// /// public void QualityLowSelected(bool isSelected) { SelectButton(qualityLowButton, qualityLowButtonText, isSelected); } /// /// This is hooked up to Quality Medium button EventTrigger in the UI /// /// public void QualityMedSelected(bool isSelected) { SelectButton(qualityMedButton, qualityMedButtonText, isSelected); } /// /// This is hooked up to Quality High button EventTrigger in the UI /// /// public void QualityHighSelected(bool isSelected) { SelectButton(qualityHighButton, qualityHighButtonText, isSelected); } /// /// This is hooked up to the Quality Low, Med, High buttons in the UI /// This method doesn't change the game quality (that happens only when /// the game is started or restarted). The menu changes what the value /// should be when the game starts. /// /// public void UpdateGameQualitySetting(int quality) { // UI events cannot pass enumerations so we use a matching integer. gameQuality = (GameQuality)quality; HighlightQuality(); } #endregion #region Public Teleport Test Methods /// /// Can be called by setting up a Custom Player Input on the Player Input Module. /// This is some test code which basically gets the friendly ships to the citidel /// instantly, and surprises the enemy. Made really to help test the capital ship /// attack. We left it here just to demonstrate how you can teleport ships. /// public void TeleportCheat() { if (demoSectionIndex == 1) { Vector3 deltaTP = enemyBaseLocation.position + (Vector3.up * 450f) + (Vector3.right * 750f) + (Vector3.forward) - player1Ship.shipInstance.TransformPosition; player1Ship.TelePort(deltaTP, false); InitiateDemoSection2(); demoSectionIndex = 2; for (int i = 0; i < squadronAShipsCount; i++) { squadronAShips[i].TelePort(deltaTP, false); CompletedStateActionCallback(squadronAShips[i]); } for (int i = 0; i < squadronBShipsCount; i++) { squadronBShips[i].TelePort(deltaTP, false); CompletedStateActionCallback(squadronBShips[i]); } sscManager.TelePortProjectiles(deltaTP); } } #endregion } }