rabidus-test/Assets/SCSM/SciFiShipController/Demos/TechDemo/Scripts/TechDemo2.cs

2670 lines
122 KiB
C#

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
{
/// <summary>
/// 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
/// </summary>
[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;
/// <summary>
/// Friendly squadron A ID.
/// </summary>
public int squadronAId = 1;
[Header("Friendly Squadron B")]
public GameObject squadronBGameObject;
public ShipControlModule squadronBShipPrefab;
public int minDockingIndexB = 0;
public int maxDockingIndexB = 0;
/// <summary>
/// Friendly squadron B ID.
/// </summary>
public int squadronBId = 2;
[Header("Enemy Squadron C")]
public GameObject squadronCGameObject;
public ShipControlModule squadronCShipPrefab;
public int minDockingIndexC = 0;
public int maxDockingIndexC = 0;
/// <summary>
/// Enemy base squadron ID.
/// </summary>
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;
/// <summary>
/// Enemy air squadron ID.
/// </summary>
public int squadronDId = 4;
[Header("Enemy Capital Ship (E)")]
public ShipControlModule enemyCapitalShip;
/// <summary>
/// Enemy capital ship squadron ID.
/// </summary>
public int squadronEId = 5;
/// <summary>
/// The delay time between initial undockings of the ships in the capital ship hangar
/// </summary>
public float capitalUndockingDelay = 1f;
[Header("Enemy Base (F)")]
public GameObject enemyTurretsGameObject;
/// <summary>
/// Enemy base turrets squadron ID.
/// </summary>
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")]
/// <summary>
/// Docking Station for friendly fighters at the friendly base
/// </summary>
public ShipDockingStation friendlyBaseDockingStation = null;
/// <summary>
/// The delay time between initial undockings of the friendly ships
/// </summary>
public float friendlyUndockingDelay = 1f;
/// <summary>
/// Docking Station for enemy fighters at the enemy base
/// </summary>
public ShipDockingStation enemyBaseDockingStation1 = null;
/// <summary>
/// Docking Station where the capital ship is docked
/// </summary>
public ShipDockingStation enemyBaseDockingStation2 = null;
[Header("Demo Sections")]
/// <summary>
/// When the player or any friendly ship gets within this distance of the enemy base, section 2
/// of the demo will be triggered.
/// </summary>
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<ShipAIInputModule> squadronAShips;
private int squadronAShipsCount = 0;
private List<ShipAIInputModule> squadronBShips;
private int squadronBShipsCount = 0;
private List<ShipAIInputModule> squadronCShips;
private int squadronCShipsCount = 0;
private List<ShipAIInputModule> squadronDShips;
private int squadronDShipsCount = 0;
// A list of ships that are currently paused
private List<ShipAIInputModule> pausedAIShips;
private List<SurfaceTurretModule> 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<SSCRadarBlip> 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;
/// <summary>
/// 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
/// </summary>
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<ShipAIInputModule>();
squadronBShips = new List<ShipAIInputModule>();
squadronCShips = new List<ShipAIInputModule>();
squadronDShips = new List<ShipAIInputModule>();
// 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<SurfaceTurretModule>();
if (enemyTurretsGameObject != null)
{
// Find all the enemy surface turrets
SurfaceTurretModule[] surfaceTurrets = enemyTurretsGameObject.GetComponentsInChildren<SurfaceTurretModule>();
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<ShipDockingStation>();
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<SSCRadarBlip>();
// 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", "<b>Objective</b>: 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 = "<b>Objective</b>: Destroy the enemy turrets (0/4).";
shipDisplayModule.AddMessage(section2Message);
section3Message = shipDisplayModule.CopyDisplayMessage(section1Message, "Section 3 Message");
section3Message.messageString = "<b>Objective</b>: 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<scsmmedia.MusicController>();
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("<b>Objective</b>: 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("<b>Objective</b>: 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
/// <summary>
/// Method to be invoked for undocking squadrons A and B.
/// </summary>
private void InitiallyUndockSquadronsAAndB ()
{
// Undock 1 ship from squadron A
UndockSquadronShips(squadronAShips, 1);
// Undock 1 ship from squadron B
UndockSquadronShips(squadronBShips, 1);
}
/// <summary>
/// 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.
/// </summary>
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);
}
}
}
}
/// <summary>
/// Undocks shipsToUndock ships from shipsList (of the ships that are currently docked).
/// Sets up a callback method for Squadron D from the capital ship.
/// </summary>
/// <param name="shipsList"></param>
/// <param name="shipsToUndock"></param>
private void UndockSquadronShips(List<ShipAIInputModule> 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<ShipDocking>();
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;
}
}
}
/// <summary>
/// Create and initialises a squadron of AI ships in the idle state.
/// </summary>
/// <param name="squadronParentGameObject"></param>
/// <param name="squadronShipPrefab"></param>
/// <param name="squadronSpawnPoints"></param>
private void InitialiseSquadron(GameObject squadronParentGameObject, ShipControlModule squadronShipPrefab,
ShipDockingStation squadronDockingStation, int minDockingIndex, int maxDockingIndex,
List<ShipAIInputModule> 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<ShipControlModule>();
// Get the ship AI input module instance
ShipAIInputModule shipAIInputModuleInstance = shipGameObjectInstance.GetComponent<ShipAIInputModule>();
// Get the ship docking instance
ShipDocking shipDockingInstance = shipGameObjectInstance.GetComponent<ShipDocking>();
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();
}
}
}
}
/// <summary>
/// Assigns targets for an entire squadron.
/// </summary>
/// <param name="squadronShipList"></param>
/// <param name="squadronShipsCount"></param>
/// <param name="radarQueryCentre"></param>
/// <param name="radarQueryRange"></param>
/// <param name="radarQueryFactionID"></param>
/// <param name="radarQueryFactionsToExclude"></param>
private void AssignSquadronTargets(SSCRadarQuery squadronRadarQuery, List<ShipAIInputModule> 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.");
}
}
}
/// <summary>
/// Sets up the radar query for a squadron, so that the correct targets can be found when we need them.
/// </summary>
/// <param name="squadronRadarQuery"></param>
/// <param name="radarQueryCentre"></param>
/// <param name="radarQueryRange"></param>
/// <param name="radarQueryFactionsToInclude"></param>
/// <param name="radarQueryFactionsToExclude"></param>
/// <param name="radarQuerySquadronsToInclude"></param>
/// <param name="radarQuerySquadronsToExclude"></param>
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;
}
}
/// <summary>
/// Assigns a new target to an AI ship.
/// </summary>
/// <param name="shipAIInputModuleInstance"></param>
/// <param name="shipControlModuleInstance"></param>
/// <param name="squadronRadarQuery"></param>
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<SurfaceTurretModule>();
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.");
}
}
/// <summary>
/// Assigns a new path to an AI ship.
/// </summary>
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);
}
}
/// <summary>
/// Gives an AI ship new instructions.
/// </summary>
/// <param name="shipControlModuleInstance"></param>
/// <param name="shipAIInputModuleInstance"></param>
/// <param name="completedStateAction"></param>
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
}
}
}
}
/// <summary>
/// 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.
/// </summary>
private void SetGameQuality()
{
bool isHigh = gameQuality == GameQuality.High;
bool isMedium = gameQuality == GameQuality.Medium;
#region Terrain Settings
if (landscapeParent != null)
{
Terrain[] terrains = landscapeParent.GetComponentsInChildren<Terrain>();
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<Light>();
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
}
/// <summary>
/// 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.
/// </summary>
/// <param name="squadronAIList"></param>
private void SetThrusterEffectQuality(List<ShipAIInputModule> 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();
}
}
}
}
/// <summary>
/// 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.
/// </summary>
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<PlayerInputModule>();
if (playerInputModule != null) { playerInputModule.SetTargetDisplay(2); }
}
if (menuPanel != null) { menuPanel.transform.parent.GetComponent<Canvas>().targetDisplay = 1; }
}
// Ensure the POI camera show stars
if (celestials != null && celestials.IsInitialised)
{
celestials.camera2 = pointOfInterestCamera.GetComponent<Camera>();
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(); }
}
/// <summary>
/// Stop the PoI camera from rendering
/// </summary>
private void DisablePointOfInterestCamera()
{
if (pointOfInterestCamera != null)
{
pointOfInterestCamera.DisableCamera();
if (pointOfInterestCamera.gameObject.activeSelf)
{
pointOfInterestCamera.gameObject.SetActive(false);
}
}
}
/// <summary>
/// Switch the Point of View camera to either the capital ship or one of the ships that were launched from the capital ship
/// </summary>
/// <param name="shipToWatch"></param>
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
/// <summary>
/// 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.
/// </summary>
/// <param name="isVisible"></param>
private void ShowMenu(bool isVisible)
{
if (menuPanel != null) { menuPanel.SetActive(isVisible); }
if (shipDisplayModule != null)
{
if (isVisible || !shipDisplayModule.autoHideCursor) { shipDisplayModule.ShowCursor(); } else { shipDisplayModule.HideCursor(); }
}
}
/// <summary>
/// Show or hide the Quality settings in the scene.
/// Note, this is a child of the Menu panel.
/// </summary>
/// <param name="isVisible"></param>
private void ShowQuality(bool isVisible)
{
if (isVisible) { HighlightQuality(); }
if (qualityPanel != null) { qualityPanel.SetActive(isVisible); }
}
/// <summary>
/// Highlight the button border of the current game quality
/// </summary>
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;
}
}
}
/// <summary>
/// Show or hide the Game Over panel and text in the scene
/// </summary>
/// <param name="isVisible"></param>
/// <param name="isMissionSuccessful"></param>
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);
}
}
/// <summary>
/// Show or hide a button
/// </summary>
/// <param name="button"></param>
/// <param name="isVisible"></param>
private void ShowButton(Button button, bool isVisible)
{
if (button != null) { button.gameObject.SetActive(isVisible); }
}
/// <summary>
/// Set the focus to a button in the UI
/// </summary>
/// <param name="button"></param>
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);
}
}
/// <summary>
/// The button has been selected or deselected.
/// UI.Text is stored as a reference in the scene to avoid having to do
/// a GetComponentsInChildren.
/// </summary>
/// <param name="button"></param>
/// <param name="text"></param>
/// <param name="isSelected"></param>
private void SelectButton(Button button, Text text, bool isSelected)
{
if (button != null && text != null)
{
text.color = isSelected ? colourSelectedBtnText : colourNonSelectedBtnText;
}
}
/// <summary>
/// Wait until the player camera is in the correct position,
/// pause updates, then show the Start options.
/// </summary>
/// <returns></returns>
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();
}
/// <summary>
/// The mission failed so pause game and tell the user
/// </summary>
private void MissionFailed()
{
PauseGame();
ShowGameOver(true, false);
ShowButton(resumeButton, false);
ShowButton(quitButton, true);
SetButtonFocus(restartButton);
}
/// <summary>
/// The mission was successful, so pause game and tell the user
/// </summary>
private void MissionCompleted()
{
PauseGame();
ShowGameOver(true, true);
ShowButton(resumeButton, false);
ShowButton(quitButton, true);
SetButtonFocus(restartButton);
}
#endregion
#region Pause Game Methods
/// <summary>
/// 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
/// </summary>
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<ShipAIInputModule>(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;
}
/// <summary>
/// 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.
/// </summary>
private IEnumerator UnPauseGameNextFrame()
{
Time.timeScale = 1f;
yield return new WaitForEndOfFrame();
ShowMenu(false);
UnPauseGame();
}
/// <summary>
/// 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.
/// </summary>
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;
}
/// <summary>
/// Add all the AI Ships in a squadron to the list of ships to pause and unpause
/// </summary>
/// <param name="numSquadronAIShips"></param>
/// <param name="squadronAIList"></param>
private void AddSquadronToPausedList(int numSquadronAIShips, List<ShipAIInputModule> 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
/// <summary>
/// Callback for when an AI ship completes a state action.
/// </summary>
/// <param name="shipAIInputModuleInstance"></param>
public void CompletedStateActionCallback(ShipAIInputModule shipAIInputModuleInstance)
{
if (shipAIInputModuleInstance != null)
{
ShipControlModule shipControlModuleInstance = shipAIInputModuleInstance.GetShipControlModule;
GetNewInstructions(shipControlModuleInstance, shipAIInputModuleInstance, shipControlModuleInstance == null ? null : shipControlModuleInstance.GetShipDocking(false), true, false);
}
}
/// <summary>
/// Callback for when an AI ship respawns.
/// </summary>
/// <param name="shipControlModuleInstance"></param>
/// <param name="shipAIInputModuleInstance"></param>
public void OnRespawnCallback(ShipControlModule shipControlModuleInstance, ShipAIInputModule shipAIInputModuleInstance)
{
if (shipControlModuleInstance != null)
{
GetNewInstructions(shipControlModuleInstance, shipAIInputModuleInstance, shipControlModuleInstance.GetShipDocking(false), false, true);
}
}
/// <summary>
/// Callback for when the Player ship respawns
/// </summary>
/// <param name="shipControlModuleInstance"></param>
/// <param name="shipAIInputModuleInstance"></param>
public void OnPlayerRespawnCallback(ShipControlModule shipControlModuleInstance, ShipAIInputModule shipAIInputModuleInstance)
{
// Is the player ship currently using the HUD?
if (shipDisplayModule.IsSourceShip(shipControlModuleInstance))
{
shipDisplayModule.ShowHUD();
}
}
/// <summary>
/// Callback for when the player ship is destroyed.
/// Turn off player ship HUD
/// </summary>
/// <param name="shipInstance"></param>
public void OnPlayerDestroyed(Ship shipInstance)
{
// Is the player ship currently using the HUD?
if (shipDisplayModule.IsSourceShip(shipInstance))
{
shipDisplayModule.HideHUD();
}
}
/// <summary>
/// Callback for when a surface turret is destroyed in the enemy base
/// </summary>
/// <param name="surfaceTurretModuleInstance"></param>
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("<b>Objective</b>: Destroy the enemy turrets (");
tempDisplayMessageString.Add(4 - enemySurfaceTurretsCount);
tempDisplayMessageString.Add("/4).");
shipDisplayModule.SetDisplayMessageText(section2Message, tempDisplayMessageString.ToString());
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="shipDocking"></param>
/// <param name="shipControlModule"></param>
/// <param name="shipAIInputModule"></param>
/// <param name="prevState"></param>
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;
}
}
/// <summary>
/// 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.
/// </summary>
/// <param name="shipDocking"></param>
/// <param name="shipControlModule"></param>
/// <param name="shipAIInputModule"></param>
/// <param name="prevState"></param>
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();
}
}
/// <summary>
/// When the Capital Ship is destroyed, the mission has been successful.
/// </summary>
/// <param name="ship"></param>
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
/// <summary>
/// Initiates section 1 of tech demo 2. Starts the friendly ships flying towards the enemy base from Hanger2.
/// </summary>
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);
}
/// <summary>
/// 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.
/// </summary>
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<ShipDocking>();
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);
}
/// <summary>
/// Initiates section 3 of tech demo 2.
/// Capital ship gets ready for departure
/// </summary>
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<SSCDoorAnimator>();
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);
}
/// <summary>
/// Squadrons A, B and Player to engage capital Ship.
/// </summary>
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); }
}
}
/// <summary>
/// Launch Squadron D from Capital Ship
/// Have squadron D engage (friendly) squadrons A and B
/// </summary>
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;
}
/// <summary>
/// Enable Capital Ship and undock from Hanger1
/// </summary>
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
/// <summary>
/// Cycles the camera view for the tech demo 2 player camera.
/// </summary>
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
/// <summary>
/// Toggle Pause on/off. When using (new) Unity Input, like say on Xbox,
/// this can be called as a CustomInput from PlayerInputModule.
/// </summary>
public void TogglePause()
{
if (!isGamePaused) { PauseGame(); }
else { StartCoroutine(UnPauseGameNextFrame()); }
}
/// <summary>
/// This is hooked up to the Resume Button in the UI
/// </summary>
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());
}
/// <summary>
/// This is hooked up to the Start Button in the UI
/// </summary>
public void StartGame()
{
SetGameQuality();
ShowMenu(false);
if (!isPOICameraDisplayed) { CheckPointOfInterestCamera(); }
StartCoroutine(UnPauseGameNextFrame());
}
/// <summary>
/// This is hooked up to the Restart Button in the UI
/// </summary>
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);
}
/// <summary>
/// Called when the Quit button is clicked on screen.
/// </summary>
public void QuitGame()
{
#if UNITY_EDITOR
UnityEditor.EditorApplication.isPlaying = false;
#else
Application.Quit();
#endif
}
/// <summary>
/// This is hooked up to Resume button EventTrigger in the UI
/// </summary>
/// <param name="isSelected"></param>
public void ResumeSelected(bool isSelected)
{
SelectButton(resumeButton, resumeButtonText, isSelected);
}
/// <summary>
/// This is hooked up to Start button EventTrigger in the UI
/// </summary>
/// <param name="isSelected"></param>
public void StartSelected(bool isSelected)
{
SelectButton(startButton, startButtonText, isSelected);
}
/// <summary>
/// This is hooked up to Restart button EventTrigger in the UI
/// </summary>
/// <param name="isSelected"></param>
public void RestartSelected(bool isSelected)
{
SelectButton(restartButton, restartButtonText, isSelected);
}
/// <summary>
/// This is hooked up to Quit button EventTrigger in the UI
/// </summary>
/// <param name="isSelected"></param>
public void QuitSelected(bool isSelected)
{
SelectButton(quitButton, quitButtonText, isSelected);
}
/// <summary>
/// This is hooked up to Quality Low button EventTrigger in the UI
/// </summary>
/// <param name="isSelected"></param>
public void QualityLowSelected(bool isSelected)
{
SelectButton(qualityLowButton, qualityLowButtonText, isSelected);
}
/// <summary>
/// This is hooked up to Quality Medium button EventTrigger in the UI
/// </summary>
/// <param name="isSelected"></param>
public void QualityMedSelected(bool isSelected)
{
SelectButton(qualityMedButton, qualityMedButtonText, isSelected);
}
/// <summary>
/// This is hooked up to Quality High button EventTrigger in the UI
/// </summary>
/// <param name="isSelected"></param>
public void QualityHighSelected(bool isSelected)
{
SelectButton(qualityHighButton, qualityHighButtonText, isSelected);
}
/// <summary>
/// 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.
/// </summary>
/// <param name="quality"></param>
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
/// <summary>
/// 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.
/// </summary>
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
}
}