rabidus-test/Assets/SCSM/SciFiShipController/Demos/TechDemo3/Scripts/TechDemo3.cs

4822 lines
214 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
using UnityEngine.SceneManagement;
using UnityEngine.UI;
#if SCSM_S3D
using scsmmedia;
#endif
// Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved.
namespace SciFiShipController
{
/// <summary>
/// Setup for Sticky3D Controller integration:
/// 1. Add Sticky3DController\Demos\Prefabs\Characters\NPC_Bob into the scene under "Characters"
/// 2. Add Sticky3DController\Demos\Prefabs\Characters\PlayerJaneSuited into the scene under "Characters"
/// 3. Add Sticky3DController\Demos\Prefabs\ThirdPersonCamera into the scene under "Characters"
/// 4. Set the PlayerJaneSuited transform position to -66, 28, -218
/// 5. Set the NPC_Bob transform position to 10, 10, 10
/// 6. Add the NPC_Bob from the scene to Services Assistant slot
/// 7. Add the PlayerJaneSuited from the scene to Pilot Controller slot
/// 8. Add Sticky3DController\Demos\AnimClipSets\Sticky3D Demo Sit Set1 to the Player Shuttle Anim Clip Set slot
/// 9. Add Sticky3DController\Demos\AnimClipSets\Sticky3D Demo Sit Set5 to the Assistant Anim Clip Set slot
/// 10. Add Sticky3DController\Demos\Models\Characters\s3d_bob\s3d_bob_interact to Default Interact Animation
/// 11. Add Sticky3DController\Demos\Animations\s3d_jane_gestures\s3d_jane_wave5 to Services NPC Wave Animation
/// 12. On the PlayerJaneSuited in the scene, on the Look tab, add the ThirdPersonCamera from "Characters" to the "Look Camera"
/// 13. On the PlayerJaneSuited in the scene, on the StickyPartsModule, untick "Initialise On Start"
/// 14. On the NPC_Bob in the scene, on the Collide tab, set Reference Update Type to "Auto First"
/// 15. On this component, turn off "Use Walk Thru"
/// 16. Optionally Save Scene as "techdemo3scene2" to avoid it being overwritten on the next SSC update
/// WARNING: Currently uses Sticky3D when changing cameras for Celestials (stars)
/// </summary>
[HelpURL("http://scsmmedia.com/ssc-documentation")]
public class TechDemo3 : MonoBehaviour
{
#region Enumerations
public enum GameQuality
{
Low = 0,
Medium = 1,
High = 2
}
#endregion
#region Public Variables
[Header("General Settings")]
public Celestials celestials = null;
[Tooltip("The chair in the services room")]
public GameObject assistantChair = null;
[Tooltip("Ladder for getting into Hawk cockpit")]
public GameObject ladder1 = null;
[Tooltip("Enable the walk through camera. On by default when Sticky3D is not installed.")]
public bool useWalkThru = false;
[Tooltip("Main directional light")]
public Light mainLight = null;
public GameQuality gameQuality = GameQuality.High;
public bool limitScreenToHD = true;
public bool limitFrameRate = true;
[Header("Space Port Lighting")]
public Light entranceLight1 = null;
public Light entranceLight2 = null;
public Light servicesLight1 = null;
public Light servicesLight2 = null;
public Light bottomLiftLight1 = null;
public Light corridorLeftLight1 = null;
public Light corridorRightLight1 = null;
public Light topLift1Light1 = null;
public Light topLift1Light2 = null;
[Header("Player Shuttle Ship")]
public ShipControlModule playerShuttle = null;
public ShipCameraModule shipCamera = null;
public GameObject shuttleSideDoorControl = null;
[Tooltip("The chair in the shuttle where the character starts")]
public GameObject shuttlePilotChair = null;
[Tooltip("The flashing light on the console")]
public GameObject dockingButton = null;
public Light shuttleCockpitLight = null;
public Light shuttleCargoBayLight = null;
[Header("Player Hawk Ship")]
public ShipControlModule playerHawk = null;
public GameObject hawkStaticColliders = null;
public MeshCollider hawkMeshCollider = null;
public BoxCollider hawkCanopyCollider = null;
public SSCDoorAnimator hawkCanopyDoor = null;
public SSCDoorProximity hawkCanopyDoorProximity = null;
public Light hawkCockpitLight = null;
[Tooltip("Always show the full Hawk HUD, even on Low Quality")]
public bool hawkFullHUD = true;
[Header("Sound FX")]
public EffectsModule soundPoolPrefab = null;
public AudioClip dockingSoundClip = null;
public AudioClip attackExplosion = null;
public AudioClip shuttleAmbientClip = null;
public AudioClip spacePortAmbientClip = null;
[Header("Heads Up Display")]
public ShipDisplayModule shipDisplayModule = null;
[Range(0f, 1f)] public float cockpitViewHeight = 0.75f;
[Header("Attack Items - General")]
public BeaconLight beaconLight = null;
public SSCLightStrobe liftStrobeLight1 = null;
public SSCLightStrobe liftStrobeLight2 = null;
public Vector3 battleCentralPos = Vector3.up * 120f;
public GameObject shipsParentGameObject = null;
[Header("Attack Items - Friendly")]
public ShipControlModule friendlyShipPrefab = null;
public int numFriendlyShip = 5;
public float friendlyShipSpawnDist = 25f;
public int numFriendlyRespawns = 3;
[Range(0f, 1f)] public float friendlyAccuracy = 0.75f;
private int friendlyFactionID = 1;
private int playerSquadronID = 1;
private int friendlySquadronID = 2;
private int barrelsSquadronID = 3;
[Header("Attack Items - Enemy")]
public ShipControlModule enemyShip1Prefab = null;
public int numEnemyShip1 = 5;
public ShipControlModule enemyShip2Prefab = null;
public int numEnemyShip2 = 5;
public float enemyShipSpawnDist = 500f;
public ShipControlModule enemyCapitalShipPrefab = null;
public Vector3[] capitalShipSpawnPoints = null;
public Vector3[] capitalShipSpawnRots = null;
public EffectsModule sparksEffectsObjectPrefab = null;
public Vector3[] sparkFXPoints = null;
public int numEnemyRespawns = 3;
[Range(0f, 1f)] public float enemyAccuracy = 0.5f;
private int enemyFactionID = 2;
private int enemySquadron1ID = 4;
private int enemySquadron2ID = 5;
private int enemyCapitalSquadronID = 6;
[Header("Attack Items - AI")]
public string friendlyIdlePathName = "Path Name Here";
public float friendlyIdleSpeed = 50f;
public string enemyIdlePathName = "Path Name Here";
public float enemyIdleSpeed = 50f;
public List<string> friendlyEscapePathNames = new List<string>();
public List<string> enemyEscapePathNames = new List<string>();
public float pursuitSpeed = 100f;
public float strafingRunSpeed = 100f;
[Header("SpacePort Doors")]
public SSCDoorAnimator lift1BottomDoors = null;
public SSCDoorAnimator lift1OuterDoors = null;
public SSCDoorAnimator lift2BottomDoors = null;
public GameObject lift1DoorControl = null;
public SSCDoorAnimator outerDockingBayDoor1 = null;
public SSCDoorProximity outerDockingBayDoorProximity = null;
public SSCDoorAnimator[] delayedUnlockDoors = null;
[Header("SpacePort Lift")]
public GameObject lift1Control = null;
public SSCMovingPlatform lift1MovingPlatform = null;
public Light lift1CallLight = null;
public GameObject lift1SafetyCabinet = null;
public DemoSSCCycleMaterials lift1Barrier = null;
[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;
[Header("Walk Thru")]
[Tooltip("The camera used when Sticky3D Controller is not installed")]
public Camera walkThruCamera1 = null;
public ShipCameraModule walkThruCamera2 = null;
public ShipControlModule cameraShip = null;
public string walkThruPath1Name = "Shuttle Fly Thru Path";
public string walkThruPath2Name = "Space port Fly Thru Entrance";
public string walkThruPath3Name = "Space port Fly Thru Services";
public string walkThruPath4Name = "Space port Fly Thru Call Lift";
public string walkThruPath5Name = "Space port Fly Thru Enter Lift";
public string walkThruPath6Name = "Space port Fly Thru Top Lift";
public SSCProximity cockPitEntryProximity = null;
[Header("Sticky3D Controller Setup")]
[Tooltip("Used when Sticky3D Controller is in project to modify character behaviour")]
public GameObject[] stickyZones = null;
[Tooltip("Used when Sticky3D Controller is in project. To assign to stickyZones.")]
public Transform[] stickyZoneRefFrames = null;
[Tooltip("Used when Sticky3D Controller is in project. Assign the Demo Sit Set5 here")]
public ScriptableObject playerShuttleAnimClipSet = null;
[Tooltip("Used when Sticky3D Controller is in project. Assign the Demo Sit Set5 here")]
public ScriptableObject assistantAnimClipSet = null;
[Tooltip("Used when Sticky3D Controller is in project, this is the Bob Interact animation")]
public AnimationClip defaultInteractAnimation = null;
[Tooltip("Used when Sticky3D Controller is in project, this is the animation used to attract the player")]
public AnimationClip servicesNPCWaveAnimation = null;
#endregion
#region Public Properties
public bool isGamePaused { get; private set; }
#endregion
#region Character Controller variables
[Header("Sticky3D Characters")]
[Tooltip("Used when Sticky3D Controller is in project add PlayerJaneSuited character here")]
public GameObject pilotController = null;
[Tooltip("Used when Sticky3D Controller is in project add the NPC_Rod character here")]
public GameObject servicesAssistant = null;
#endregion
#region Private Variables - General
private bool isInitialised = false;
private bool attackStarted = false;
private bool bombingRunsStarted = false;
private bool isUpdateHawkHUDMetrics = false;
private bool isInHawkCockpit = false;
private SSCDoorAnimator sscShuttleDoorAnimator = null;
private int soundeffectsObjectPrefabID = -1;
private int sparksEffectsObjectPrefabID = -1;
private float sparksTimer = 0f;
private float sparksInterval = 5f;
private int numSparkFXPoints = 0;
private Weapon playerHawkLMissiles = null;
private Weapon playerHawkRMissiles = null;
private SSCRandom sparksRandom = null;
private SSCManager sscManager = null;
private SSCRadar sscRadar = null;
private DisplayGauge dockedIndicator = null;
private DisplayGauge fuelLevelGauge = null;
private DisplayGauge heatLevelGauge = null;
private DisplayGauge healthGauge = null;
private DisplayGauge missilesGauge = null;
private DisplayGauge shieldsGauge = null;
private DisplayGauge launchGauge = null;
private DisplayGauge gearGauge = null;
private DisplayGauge enemyGauge = null;
private DisplayMessage startDockingMessage = null;
private DisplayMessage dockingInProgressMessage = null;
private DisplayMessage exitShuttleMessage = null;
private DisplayMessage findServiceDeskMessage = null;
private DisplayMessage getToDeckMessage = null;
private DisplayMessage getToHawkMessage = null;
private DisplayMessage getToHawkMessageWithHelmet = null;
private PlayerInputModule shuttlePlayerInputModule = null;
private PlayerInputModule hawkPlayerInputModule = null;
private AutoTargetingModule hawkPlayerAutoTargetingModule = null;
private ShipDocking shipDocking = null;
private SampleChangeCameraView changeShipCameraView = null;
private LocationData hawkLandingLocation = null;
private SSCDoorControl sscDoorControlShuttleSideDoors = null;
private SSCDoorControl sscDoorControlLift1Doors = null;
private DemoSSCChangeMaterial lift1ControlChangeMaterial = null;
private List<SSCRadarBlip> sscRadarBlipsList = null;
private int sscRadarBlipsListCount = 0;
private SSCRadarQuery friendlyRadarQuery = null;
private SSCRadarQuery enemyRadarQuery = null;
private List<ShipAIInputModule> friendlyShips = null;
private List<ShipAIInputModule> enemyType1Ships = null;
private List<ShipAIInputModule> enemyType2Ships = null;
private List<ShipAIInputModule> enemyCapitalShips = null;
/// <summary>
/// List of available friendly ShipIds
/// </summary>
private List<int> availableFriendlyShips = null;
/// <summary>
/// List of available enemy ShipIds
/// </summary>
private List<int> availableEnemyShips = null;
private int numEnemyRemaining = 0;
private int numEnemyCapitalShips = 0;
private SSCRandom aiRandom = null;
private PathData friendlyIdlePath;
private PathData enemyIdlePath;
private List<PathData> friendlyEscapePaths = null;
private List<PathData> enemyEscapePaths = null;
// A list of ships that are currently paused
private List<ShipAIInputModule> pausedAIShips;
// Use to calculate new position of walk thru path in shuttle
private Vector3 shuttleOriginalPos = Vector3.zero;
private Quaternion shuttleOriginalRot = Quaternion.identity;
// UI variables
private EventSystem eventSystem;
private Color colourNonSelectedBtnText;
private Color colourSelectedBtnText;
private Color colourDefaultBorder;
private Color colourSelectedBorder;
private AudioSource ambientAudioSource = null;
private scsmmedia.MusicController musicController = null;
private WaitForSeconds fadeUpLightWait = null;
private System.Type hdLightType = null;
private bool isURP = false;
private bool isHDRP = false;
#endregion
#region Private Variables - Walk Thru
private int walkThruSectionNumber = 0;
private ShipAIInputModule cameraShipAI = null;
private bool isWalkThruInitialised = false;
private PathData walkThruPath1 = null;
private PathData walkThruPath2 = null;
private PathData walkThruPath3 = null;
private PathData walkThruPath4 = null;
private PathData walkThruPath5 = null;
private PathData walkThruPath6 = null;
#endregion
#region Private Sticky3D Controller variables (if present)
#if SCSM_S3D
private StickyControlModule s3dPlayer = null;
private StickyInputModule s3dPlayerInput = null;
private int s3dPlayerInteractStateId = 0;
private SampleSitAction sampleSitActionPlayer = null;
private StickyControlModule s3dServiceAssistant = null;
private int servicesAssistantInteractStateId = 0;
private StickyInteractive shuttleSideDoorControlInteractive = null;
private StickyInteractive shuttleDockingButtonInteractive = null;
private StickyInteractive lift1DoorControlInteractive = null;
private StickyInteractive lift1ControlInteractive = null;
private StickyInteractive lift1SafetyCabinetInteractive = null;
private StickyPartsModule s3dPlayerParts = null;
private CustomInput s3dciMouseClick = null;
private CustomInput s3dciSitting = null;
private CustomInput s3dciPauseGame = null;
private Color32 defaultReticleColour;
private Color32 lookingAtReticleColour;
private bool isPlayerEnabledOnPause = false;
#endif
#endregion
#region Initialisation Methods
// Start is called before the first frame update
void Start()
{
#region Check for key components
ambientAudioSource = GetComponent<AudioSource>();
if (playerShuttle == null)
{
#if UNITY_EDITOR
Debug.LogWarning("ERROR: TechDemo3 could not find shuttle player ship");
#endif
}
else if (playerHawk == null)
{
#if UNITY_EDITOR
Debug.LogWarning("ERROR: TechDemo3 could not find hawk player ship");
#endif
}
else if (shipCamera == null)
{
#if UNITY_EDITOR
Debug.LogWarning("ERROR: TechDemo3 could not find ship player camera");
#endif
}
else if (ambientAudioSource == null)
{
#if UNITY_EDITOR
Debug.LogWarning("ERROR: TechDemo3 could not find AudioSource component on " + gameObject.name);
#endif
}
else
#endregion
{
isGamePaused = false;
if (limitScreenToHD) { SSCUtils.MaxScreenHD(); }
isURP = SSCUtils.IsURP(false);
if (!isURP) { isHDRP = SSCUtils.IsHDRP(false); }
ConfigurePlayerShips();
// Get or create the SSC manager component
sscManager = SSCManager.GetOrCreateManager();
#region Configure Radar
sscRadar = SSCRadar.GetOrCreateRadar();
// Initialise a list of radar results
sscRadarBlipsList = new List<SSCRadarBlip>();
// Create a landing location for the player ship, and make it appear
// on Hawk radar HUD to help orientate the player.
hawkLandingLocation = new LocationData()
{
name = "PlayerLandingLocation",
factionId = 0,
position = playerHawk != null ? playerHawk.transform.position : Vector3.zero,
isRadarEnabled = true,
radarBlipSize = 5,
showGizmosInSceneView = true
};
if (hawkLandingLocation != null)
{
sscManager.AddLocation(hawkLandingLocation);
}
// The Radar UI mini-map which appears when the pilot is in the Hawk,
// will query the area around the Hawk as it is flown. This will also
// determine who appears as friend, foe or neutral on the UI mini-map.
if (playerHawk != null)
{
sscRadar.FollowShip(playerHawk);
}
#endregion
#region Initialise Paths
friendlyIdlePath = sscManager.GetPath(friendlyIdlePathName);
enemyIdlePath = sscManager.GetPath(enemyIdlePathName);
if (friendlyEscapePathNames != null && friendlyEscapePathNames.Count > 0)
{
int numFriendlyEscapePaths = friendlyEscapePathNames.Count;
friendlyEscapePaths = new List<PathData>(numFriendlyEscapePaths);
for (int i = 0; i < numFriendlyEscapePaths; i++)
{
friendlyEscapePaths.Add(sscManager.GetPath(friendlyEscapePathNames[i]));
}
}
else
{
Debug.LogWarning("ERROR: TechDemo3 no friendly escape paths defined");
}
if (enemyEscapePathNames != null && enemyEscapePathNames.Count > 0)
{
int numEnemyEscapePaths = enemyEscapePathNames.Count;
enemyEscapePaths = new List<PathData>(numEnemyEscapePaths);
for (int i = 0; i < numEnemyEscapePaths; i++)
{
enemyEscapePaths.Add(sscManager.GetPath(enemyEscapePathNames[i]));
}
}
else
{
Debug.LogWarning("ERROR: TechDemo3 no enemy escape paths defined");
}
#endregion
// Initialise all ships
InitialiseAttackShips();
// Setup custom scene-specific FX.
ConfigureEffectsPools();
ConfigureHUD();
sscShuttleDoorAnimator = playerShuttle.GetComponentInChildren<SSCDoorAnimator>();
#if UNITY_EDITOR
if (sscShuttleDoorAnimator == null)
{
Debug.LogWarning("ERROR: TechDemo3 could not find SSCDoorAnimator component in Shuttle");
}
#endif
// Some doors may have a SSC Door Proximity component. If the player is inside the proximity when
// the game starts the trigger enter and exit events will fire. For these doors, the SSC Door Animator
// component should have the doors locked, but the Proximity component should not automatically unlock them.
// Instead, we'll unlock these doors after the scene starts.
if (delayedUnlockDoors != null)
{
// On slower computers we need to give enough time for all the Awake methods
// in the scene to be called.
Invoke("DelayedUnlockDoors", 1.0f);
}
#if SCSM_S3D
if (useWalkThru)
{
ConfigureNoStickyController();
ConfigureServicesAssistantNPC();
}
else
{
ConfigureStick3DCharacters();
ConfigureStickyInteractive();
// Give the characters a chance to initialise over a few frames and become grounded,
// set their reference frame etc.
Invoke("InitialiseStickyZones", 0.1f);
}
if (sscShuttleDoorAnimator != null) { sscShuttleDoorAnimator.LockDoors(); }
#else
useWalkThru = true;
ConfigureNoStickyController();
#endif
isInitialised = shuttlePlayerInputModule != null && shipDisplayModule != null && sscManager != null && shipDocking != null;
UpdateGauges();
SetGameQuality();
InitialiseMenu();
if (celestials != null)
{
// Refresh the celestials cameras after the scene first renders
Invoke("SetSceneCamera", 0.01f);
}
System.GC.Collect();
ShowMenu(false);
// When Sticky3D player is sitting in the shuttle,
// give it some time to sit down.
Invoke("ShowStart", useWalkThru ? 0.25f : 1.5f);
#region Music Controller Initialisation
musicController = GetComponent<scsmmedia.MusicController>();
if (musicController != null) { musicController.Initialise(); }
#endregion
}
}
/// <summary>
/// Check panels and buttons are configured in the menu
/// </summary>
private void InitialiseMenu()
{
#region Initialise Menu
#if UNITY_EDITOR
if (menuPanel == null) { Debug.LogWarning("ERROR: TechDemo3UI menuPanel is not configured"); }
if (qualityPanel == null) { Debug.LogWarning("ERROR: TechDemo3UI qualityPanel is not configured"); }
if (resumeButton == null) { Debug.LogWarning("ERROR: TechDemo3resumeButton is not configured"); }
if (startButton == null) { Debug.LogWarning("ERROR: TechDemo3startButton is not configured"); }
if (restartButton == null) { Debug.LogWarning("ERROR: TechDemo3restartButton is not configured"); }
if (quitButton == null) { Debug.LogWarning("ERROR: TechDemo3quitButton is not configured"); }
if (qualityLowButton == null) { Debug.LogWarning("ERROR: TechDemo3qualityLowButton is not configured"); }
if (qualityMedButton == null) { Debug.LogWarning("ERROR: TechDemo3qualityMedButton is not configured"); }
if (qualityHighButton == null) { Debug.LogWarning("ERROR: TechDemo3qualityHighButton is not configured"); }
if (resumeButtonText == null) { Debug.LogWarning("ERROR: TechDemo3resumeButtonText is not configured"); }
if (startButtonText == null) { Debug.LogWarning("ERROR: TechDemo3startButtonText is not configured"); }
if (restartButtonText == null) { Debug.LogWarning("ERROR: TechDemo3restartButtonText is not configured"); }
if (quitButtonText == null) { Debug.LogWarning("ERROR: TechDemo3quitButtonText is not configured"); }
if (qualityLowButtonText == null) { Debug.LogWarning("ERROR: TechDemo3qualityLowButtonText is not configured"); }
if (qualityMedButtonText == null) { Debug.LogWarning("ERROR: TechDemo3qualityMedButtonText is not configured"); }
if (qualityHighButtonText == null) { Debug.LogWarning("ERROR: TechDemo3qualityHighButtonText is not configured"); }
if (qualityLowBorderImg == null) { Debug.LogWarning("ERROR: TechDemo3qualityLowBorderImg is not configured"); }
if (qualityMedBorderImg == null) { Debug.LogWarning("ERROR: TechDemo3qualityMedBorderImg is not configured"); }
if (qualityHighBorderImg == null) { Debug.LogWarning("ERROR: TechDemo3qualityHighBorderImg is not configured"); }
if (missionOverPanel == null) { Debug.LogWarning("ERROR: TechDemo3missionOverPanel is not configured"); }
if (missionSuccessTitle == null) { Debug.LogWarning("ERROR: TechDemo3missionSuccessTitle is not configured"); }
if (missionSuccessSubTitle == null) { Debug.LogWarning("ERROR: TechDemo3missionSuccessSubTitle is not configured"); }
if (missionFailedTitle == null) { Debug.LogWarning("ERROR: TechDemo3 missionFailedTitle is not configured"); }
if (missionFailedSubTitle == null) { Debug.LogWarning("ERROR: TechDemo3 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);
#endregion
}
#endregion
#region Update Methods
// Update is called once per frame
void Update()
{
if (isInitialised && !isGamePaused)
{
#region Update Space Port FX
// Show sparks around the space port when the attack has started but not when the play is flying the Hawk.
if (attackStarted && !isInHawkCockpit && sparksEffectsObjectPrefabID >= 0 && numSparkFXPoints > 0)
{
sparksTimer += Time.deltaTime;
if (sparksTimer > sparksInterval)
{
InstantiateEffectsObjectParameters ieParms = new InstantiateEffectsObjectParameters
{
effectsObjectPrefabID = sparksEffectsObjectPrefabID,
position = sparkFXPoints[sparksRandom.Range(0, numSparkFXPoints-1)]
};
sscManager.InstantiateEffectsObject(ref ieParms);
sparksTimer = 0f;
}
}
#endregion
// This only occurs when game quality is medium or high and player is in the Hawk
if (isUpdateHawkHUDMetrics)
{
UpdateGauges();
}
#region Update Walk Thru
if (isWalkThruInitialised)
{
if (walkThruSectionNumber == 1)
{
WalkThruUpdateSection1();
}
else if (walkThruSectionNumber == 8)
{
WalkThruUpdateSection8();
}
else if (walkThruSectionNumber == 10)
{
WalkThruUpdateSection10();
}
}
#endregion
#region Check if we need to update Capital Ship shields
if (attackStarted && isInHawkCockpit)
{
CheckCapitalShipHealth();
}
#endregion
}
}
#endregion
#region Events
private void OnDestroy()
{
#if SCSM_S3D
// Remove listeners
if (lift1DoorControlInteractive != null)
{
lift1DoorControlInteractive.RemoveListeners();
}
if (lift1ControlInteractive != null)
{
lift1ControlInteractive.RemoveListeners();
}
if (shuttleSideDoorControlInteractive != null)
{
shuttleSideDoorControlInteractive.RemoveListeners();
}
if (lift1SafetyCabinetInteractive != null)
{
lift1SafetyCabinetInteractive.RemoveListeners();
}
#endif
}
#endregion
#region Private Methods - Sticky3D Controller
#if SCSM_S3D
private void CameraChanged(int stickyID, Camera oldCamera, Camera newCamera, bool isThirdPersonCamera)
{
//Debug.Log("[DEBUG] camera changed. From: " + oldCamera.name + " to " + newCamera.name + ". Is third person? " + isThirdPersonCamera);
if (celestials != null)
{
celestials.camera1 = newCamera;
celestials.RefreshCameras();
}
}
/// <summary>
/// If Sticky3D Controller is in the project, add the StickyZones at runtime to avoid having to include
/// S3D in the SSC TechDemo3.
/// WARNING: The string name compares probably incur GC.
/// </summary>
private void InitialiseStickyZones()
{
int numS3DZones = stickyZones == null ? 0 : stickyZones.Length;
int numS3DRefFrames = stickyZoneRefFrames == null ? 0 : stickyZoneRefFrames.Length;
if (numS3DZones != numS3DRefFrames)
{
#if UNITY_EDITOR
Debug.LogWarning("ERROR: TechDemo3 the number of sticky zone reference frames (" + numS3DRefFrames + ") must match the number of sticky zones (" + numS3DZones + ").");
#endif
}
else
{
for (int znIdx = 0; znIdx < numS3DZones; znIdx++)
{
GameObject go = stickyZones[znIdx];
if (go != null)
{
StickyZone stickyZone = go.GetComponent<StickyZone>();
if (stickyZone == null) { stickyZone = go.AddComponent<StickyZone>(); }
if (stickyZone != null)
{
// Default settings for zones in Tech Demo3
stickyZone.referenceTransform = stickyZoneRefFrames[znIdx];
stickyZone.overrideReferenceFrame = true;
stickyZone.isRestoreDefaultRefTransformOnExit = false;
stickyZone.isRestorePreviousRefTransformOnExit = false;
stickyZone.overrideLookFirstPerson = false;
stickyZone.overrideGravity = false;
stickyZone.overrideAnimClips = false;
stickyZone.initialiseOnStart = true;
if (go.name.Equals("StickyZonePortA") || go.name.Equals("ShuttleDoorStickyZone"))
{
stickyZone.overrideLookThirdPerson = true;
}
else if (go.name.Contains("Lift"))
{
// When the S3D character steps off the lift, restore the previous reference transform
stickyZone.isRestorePreviousRefTransformOnExit = true;
}
else if (go.name.Equals("StickyZoneServicesChairBob"))
{
// Configure the zone to replace the default sit animations for Bob.
stickyZone.overrideAnimClips = true;
stickyZone.isRestorePreviousAnimClipsOnExit = true;
stickyZone.overrideReferenceFrame = false;
if (servicesAssistant != null)
{
s3dServiceAssistant = servicesAssistant.GetComponent<StickyControlModule>();
if (s3dServiceAssistant != null && assistantAnimClipSet != null)
{
// This only applies to assistant Bob
stickyZone.SetModelsFilter(new int[] { s3dServiceAssistant.modelId });
stickyZone.AddAnimClipSet((S3DAnimClipSet)assistantAnimClipSet);
}
#if UNITY_EDITOR
else
{
Debug.LogWarning("[ERROR] TechDemo3 - could not configure or find Assistant Anim Clip Set");
}
#endif
}
}
else if (go.name.Equals("StickyZoneShuttlePilotChair"))
{
// Configure the zone to replace the default sit animations for Player Jane Suited.
stickyZone.overrideAnimClips = true;
stickyZone.isRestorePreviousAnimClipsOnExit = true;
stickyZone.overrideReferenceFrame = false;
if (s3dPlayer != null && playerShuttleAnimClipSet != null)
{
// This should only apply to the player character
stickyZone.SetModelsFilter(new int[] { s3dPlayer.modelId });
stickyZone.AddAnimClipSet((S3DAnimClipSet)playerShuttleAnimClipSet);
}
#if UNITY_EDITOR
else
{
Debug.LogWarning("[ERROR] TechDemo3 - could not configure or find Player Shuttle Anim Clip Set");
}
#endif
}
// We start the game with some of the zone gameobjects disabled so that characters that
// are inside the trigger collider zones don't pre-maturely trigger a OnTriggerEnter
// event before the zones are initialised.
if (!stickyZone.gameObject.activeSelf) { stickyZone.gameObject.SetActive(true); }
if (!stickyZone.IsInitialised) { stickyZone.Initialise(); }
}
}
}
// Force garbage collection so that we don't get an unexpect GC event
// while the game is running.
System.GC.Collect();
}
}
/// <summary>
/// If Sticky3D Controller is in the project, configure the characters.
/// The majority of these things could be manually configured in the Unity editor,
/// however, as we're using default prefabs that come with Sticky3D Controller, we
/// need to ensure things will work correctly with Tech Demo 3 in Sci-Fi Ship Controller.
/// It also demonstrates how to do things in code.
/// </summary>
private void ConfigureStick3DCharacters()
{
#region Main Player
if (pilotController != null)
{
// Ensure the walk through camera is disabled
ConfigureWalkThrough(false);
// If Sticky3D Controller asset is installed, check if the pilot is a S3D character
s3dPlayer = pilotController.GetComponent<StickyControlModule>();
if (s3dPlayer != null)
{
// The player was not placed correctly in the shuttle and didn't get a reference frame
if (!s3dPlayer.IsInitialised)
{
pilotController.transform.position = playerShuttle.transform.position + (playerShuttle.transform.up * 0.5f);
s3dPlayer.Initialise();
}
#region Parts Setup
// At start, turn off helmet, visor, and jetpack
s3dPlayerParts = pilotController.GetComponent<StickyPartsModule>();
if (s3dPlayerParts != null)
{
s3dPlayerParts.Initialise();
s3dPlayerParts.DisableAllParts();
s3dPlayer.DisableJetPackAvailability();
}
#endregion
#region Camera and Look Setup
// Help avoid the camera clipping through walls etc.
s3dPlayer.clipObjects = true;
s3dPlayer.clipMinDistance = 0.7f;
s3dPlayer.clipResponsiveness = 0.95f;
s3dPlayer.callbackOnCameraChange = CameraChanged;
if (celestials) { celestials.camera1 = s3dPlayer.lookCamera1; }
// Restrict camera vertical movement when in shuttle pilot seat
s3dPlayer.lookPitchUpLimit = 25f;
s3dPlayer.lookPitchDownLimit = 35f;
// turn off auto hide on S3D as we'll control this with the SSC HUD.
s3dPlayer.lookAutoHideCursor = false;
#endregion
#region Climbing Setup
// Set the climb speed to match the s3d_jane_suited_climbtop3 animation
// which is designed to work with the SSCLadder1.
s3dPlayer.climbSpeed = 0.75f;
s3dPlayer.climbTopDetection = true;
#endregion
#region Head and Foot IK Setup
s3dPlayer.headIKMoveDamping = 0.1f;
s3dPlayer.EnableHeadIK(true);
// turn off foot ik for now
s3dPlayer.DisableFootIK();
#endregion
#region Setup the SSC HUD for use with the Sticky character.
// We could use the StickyDisplayModule instead. However, we'll use the
// SSC HUD as we want to use it later in the Hawk ship for targeting.
if (shipDisplayModule != null)
{
shipDisplayModule.lockDisplayReticleToCursor = true;
int guidHashReticle = shipDisplayModule.GetDisplayReticleGuidHash(1);
shipDisplayModule.ChangeDisplayReticle(guidHashReticle);
// Remmeber the colour that was set in the editor
defaultReticleColour = shipDisplayModule.activeDisplayReticleColour;
// Set the look at colour to green
lookingAtReticleColour = new Color32(0, 255, 0, defaultReticleColour.a);
shipDisplayModule.ShowDisplayReticle();
shipDisplayModule.HideCursor();
}
#endregion
#region Interactive and Hand IK Setup
// Get notified when the character changes what they are looking at
s3dPlayer.callbackOnChangeLookAtInteractive = OnInteractiveLookAtChanged;
// Set how close the character camera must be to see an interactive-enabled object
s3dPlayer.lookMaxInteractiveDistance = 3f;
// Let the character select a interactive-enabled object in the scene
// e.g. the docking button in the shuttle.
// Allow for the Call Elevator button to remain selected while the player
// is wanting to say put on their helment (which requires 2 selectables at the same time).
s3dPlayer.SetStoreMaxSelectableInScene(2, true);
// Hand IK setup (we could just set these things in the prefab...)
// Currently both hands use the left hand radius.
s3dPlayer.leftHandRadius = 0.08f;
s3dPlayer.SetLeftHandPalmOffset(new Vector3(-0.012f, 0.07f, 0.01f));
//s3dPlayer.SetLeftHandPalmRotation(new Vector3(0f, 315f, 270f));
s3dPlayer.SetRightHandPalmOffset(new Vector3(0.012f, 0.07f, 0.01f));
//s3dPlayer.SetRightHandPalmRotation(new Vector3(0f, 45f, 90f));
s3dPlayer.EnableHandIK(true);
s3dPlayer.EnableLookInteractive();
#endregion
#region Configure Sitting setup
if (s3dPlayer.IsAnimateEnabled)
{
s3dPlayerInteractStateId = s3dPlayer.GetAnimationStateId("Interact");
}
// Add a SampleSitAction component if one doesn't already exist on the player.
sampleSitActionPlayer = pilotController.GetComponent<SampleSitAction>();
if (sampleSitActionPlayer == null) { sampleSitActionPlayer = pilotController.AddComponent<SampleSitAction>(); }
if (sampleSitActionPlayer != null)
{
sampleSitActionPlayer.disableLook = false;
}
if (shuttlePilotChair != null && sampleSitActionPlayer != null)
{
// Move (TelePort) the player to be infront of the pilot chair.
Vector3 newPosition = shuttlePilotChair.transform.position + (shuttlePilotChair.transform.forward * 0.55f);
s3dPlayer.TelePort(newPosition, shuttlePilotChair.transform.rotation, true);
// Attempt to have the Player sit in the shuttle pilot's chair
Invoke("SitShuttlePilot", 1f);
}
#endregion
#region Configure the S3D input
// This could all be setup in the editor but this demonstrates how to do it at runtime.
// And by default, S3D is not included with SSC.
s3dPlayerInput = s3dPlayer.GetComponent<StickyInputModule>();
if (s3dPlayerInput != null)
{
s3dciMouseClick = new CustomInput();
s3dciSitting = new CustomInput();
s3dciPauseGame = new CustomInput();
// We want to disable user input at the start to avoid character rotating before they sit down.
s3dPlayerInput.DisableInput(true);
if (s3dciMouseClick != null && s3dciSitting != null)
{
s3dciMouseClick.customInputEvt = new CustomInputEvt();
s3dciSitting.customInputEvt = new CustomInputEvt();
s3dciPauseGame.customInputEvt = new CustomInputEvt();
// Add the listeners
if (s3dciMouseClick.customInputEvt != null && s3dciSitting.customInputEvt != null &&
s3dciPauseGame.customInputEvt != null &&
shuttlePlayerInputModule != null)
{
// One or more listeners can be added to the same custom input event.
// They will be executed in the order they are added when the user presses
// the appropriate button on the keyboard or gamepad
s3dciMouseClick.customInputEvt.AddListener(delegate { s3dPlayer.EngageLookingAtInteractive(false); });
// Allow the player to sit on the bench and stand up while in the services room
s3dciSitting.customInputEvt.AddListener(delegate { sampleSitActionPlayer.ToggleSit(); });
// Menu inputs for the character
s3dciPauseGame.customInputEvt.AddListener(delegate { TogglePause(); });
}
// Configure the buttons
s3dciMouseClick.canBeHeldDown = false;
s3dciMouseClick.isButton = true;
s3dciMouseClick.isButtonEnabled = true;
s3dciSitting.canBeHeldDown = false;
s3dciSitting.isButton = true;
s3dciSitting.isButtonEnabled = true;
s3dciPauseGame.canBeHeldDown = false;
s3dciPauseGame.isButton = true;
s3dciPauseGame.isButtonEnabled = true;
if (s3dPlayerInput.inputMode == StickyInputModule.InputMode.DirectKeyboard)
{
// Assume we're using the legacy input system for keyboard and mouse input
#if ENABLE_LEGACY_INPUT_MANAGER || !UNITY_2019_2_OR_NEWER
// Configure left mouse click for interactive-enabled object targeting (door control panels)
s3dciMouseClick.dkmPositiveKeycode = KeyCode.Mouse0;
// I key for docking shuttle
//s3dciDocking.dkmPositiveKeycode = KeyCode.I;
//// U key for undocking shuttle
//s3dciUndocking.dkmPositiveKeycode = KeyCode.U;
// T key to toggle sitting and standing
s3dciSitting.dkmPositiveKeycode = KeyCode.T;
// Menu ESC toggle game pause
s3dciPauseGame.dkmPositiveKeycode = KeyCode.Escape;
// If using UIS in future we could configure them here
#elif SSC_UIS
#endif
}
// Add the custom inputs to the list
s3dPlayerInput.customInputList.Add(s3dciMouseClick);
//s3dPlayerInput.customInputList.Add(s3dciDocking);
//s3dPlayerInput.customInputList.Add(s3dciUndocking);
s3dPlayerInput.customInputList.Add(s3dciSitting);
s3dPlayerInput.customInputList.Add(s3dciPauseGame);
// Reinitialise custom input so the custom input(s) we have added take effect
s3dPlayerInput.ReinitialiseCustomInput();
}
}
#endregion
}
}
else
{
ConfigureWalkThrough(true);
}
#endregion
ConfigureServicesAssistantNPC();
}
/// <summary>
/// This is the NPC behind the Services desk in the spaceport
/// </summary>
private void ConfigureServicesAssistantNPC()
{
if (servicesAssistant != null)
{
// If Sticky3D Controller asset is installed, check if the services assistant is a S3D character
s3dServiceAssistant = servicesAssistant.GetComponent<StickyControlModule>();
if (s3dServiceAssistant != null)
{
if (!s3dServiceAssistant.IsInitialised) { s3dServiceAssistant.Initialise(); }
if (s3dServiceAssistant.IsAnimateEnabled)
{
servicesAssistantInteractStateId = s3dServiceAssistant.GetAnimationStateId("Interact");
}
// Sit the NPC at the services desk
if (assistantChair != null)
{
// Move (TelePort) the NPC to be infront of the chair.
Vector3 newPosition = assistantChair.transform.position + (assistantChair.transform.forward * 0.6f);
s3dServiceAssistant.TelePort(newPosition, assistantChair.transform.rotation, true);
// Add a SampleSitAction component if one doesn't already exist on the NPC.
SampleSitAction sampleSitAction = servicesAssistant.GetComponent<SampleSitAction>();
if (sampleSitAction == null) { sampleSitAction = servicesAssistant.AddComponent<SampleSitAction>(); }
// Attempt to have the NPC sit on the chair
if (sampleSitAction != null)
{
// The space port is not moving, so we can just disable movement
sampleSitAction.lockPositonToRefFrame = false;
sampleSitAction.Invoke("SitDown", 1.2f);
}
}
}
}
}
/// <summary>
/// Add and configure any StickyInteractive components required in the scene.
/// Typically this would all be setup in the editor, but this shows how to do it
/// at runtime in code.
/// </summary>
private void ConfigureStickyInteractive()
{
#region Lift Door Control
// Configure the control panel that allows the player to open the doors to the upper deck lift.
if (lift1DoorControl != null)
{
// When using the door control panel, stop the doors opening automatically when they are unlocked
SSCDoorProximity sscDoorProximity = lift1BottomDoors.gameObject.GetComponentInChildren<SSCDoorProximity>();
if (sscDoorProximity != null)
{
sscDoorProximity.isOpenDoorsOnEntry = false;
sscDoorProximity.isCloseDoorsOnExit = true;
}
// If the component hasn't been added, do it now
if (lift1DoorControlInteractive == null)
{
lift1DoorControlInteractive = lift1DoorControl.GetComponent<StickyInteractive>();
if (lift1DoorControlInteractive == null) { lift1DoorControlInteractive = lift1DoorControl.AddComponent<StickyInteractive>(); }
}
if (lift1DoorControlInteractive != null)
{
lift1DoorControlInteractive.Initialise();
lift1DoorControlInteractive.SetIsTouchable(true);
lift1DoorControlInteractive.handHold1Offset = new Vector3(0f, 0.1f, 0.05f);
lift1DoorControlInteractive.handHold1Rotation = new Vector3(10f, 180f, 90f);
// The door control has a 3-button panel to give the player some visual feedback
sscDoorControlLift1Doors = lift1DoorControl.GetComponent<SSCDoorControl>();
if (sscDoorControlLift1Doors != null)
{
sscDoorControlLift1Doors.Initialise();
// Hook up a listener to open the doors
// See also OnDestroy() which removes the listeners
lift1DoorControlInteractive.onTouched = new S3DInteractiveEvt1();
lift1DoorControlInteractive.onTouched.AddListener(delegate { SSCDoorControl.SelectOpen(sscDoorControlLift1Doors); });
// Get notified when the player stops touching the door control buttons
// This will stop the character reaching for the button
lift1DoorControlInteractive.onStoppedTouching = new S3DInteractiveEvt2();
lift1DoorControlInteractive.onStoppedTouching.AddListener(DoorControlStopTouching);
}
}
}
#endregion
#region Lift Control
// Configure the control panel that allows the player call the lift to the upper deck.
if (lift1Control != null)
{
// If the component hasn't been added, do it now
if (lift1ControlInteractive == null)
{
lift1ControlInteractive = lift1Control.GetComponent<StickyInteractive>();
if (lift1ControlInteractive == null) { lift1ControlInteractive = lift1Control.AddComponent<StickyInteractive>(); }
}
if (lift1ControlInteractive != null && lift1MovingPlatform != null)
{
// Get the component that will replace the control panel material
lift1ControlChangeMaterial = lift1Control.GetComponent<DemoSSCChangeMaterial>();
lift1ControlInteractive.Initialise();
lift1ControlInteractive.SetIsSelectable(true);
// See also OnDestroy() which removes the listeners
lift1ControlInteractive.onSelected = new S3DInteractiveEvt3();
lift1ControlInteractive.onUnselected = new S3DInteractiveEvt2();
// Switch the material on the call lift control so user can see that they have selected it
if (lift1ControlChangeMaterial != null)
{
lift1ControlInteractive.onSelected.AddListener(delegate { lift1ControlChangeMaterial.GetGroup1Material(1); });
lift1ControlInteractive.onUnselected.AddListener(delegate { lift1ControlChangeMaterial.GetGroup1Material(0); });
}
// Call the lift
lift1ControlInteractive.onSelected.AddListener(delegate { lift1MovingPlatform.CallToStartPosition(true); });
}
}
#endregion
#region Lift1 Safety Cabinet
// Configure the cabinet handles to put on the player helmet at the top of Lift1
if (lift1SafetyCabinet != null && s3dPlayerParts != null)
{
// If the component hasn't been added, do it now
if (lift1SafetyCabinetInteractive == null)
{
lift1SafetyCabinetInteractive = lift1SafetyCabinet.GetComponent<StickyInteractive>();
if (lift1SafetyCabinetInteractive == null) { lift1SafetyCabinetInteractive = lift1SafetyCabinet.AddComponent<StickyInteractive>(); }
}
if (lift1SafetyCabinetInteractive != null)
{
lift1SafetyCabinetInteractive.Initialise();
lift1SafetyCabinetInteractive.SetIsSelectable(true);
// Make this object clickable rather than select and unselectable.
lift1SafetyCabinetInteractive.SetIsAutoUnselect(true);
// See also OnDestroy() which removes the listeners
lift1SafetyCabinetInteractive.onSelected = new S3DInteractiveEvt3();
// Toggle putting on and taking off the helmet.
// Head IK is enabled when the helmet is off.
// Unlock (or lock) the outer doors on the top deck of the space port.
lift1SafetyCabinetInteractive.onSelected.AddListener(delegate
{
s3dPlayerParts.ToggleEnablePartByIndex(2);
if (s3dPlayerParts.IsPartEnabledByIndex(2))
{
s3dPlayer.DisableHeadIK(false);
if (lift1OuterDoors != null) { lift1OuterDoors.UnlockDoors(); }
}
else
{
s3dPlayer.EnableHeadIK(true);
if (lift1OuterDoors != null) { lift1OuterDoors.LockDoors(); }
}
}
);
}
}
#endregion
#region Shuttle Side Door Control
// Configure the control panel that allows the player to exit the shuttle into the space port
if (shuttleSideDoorControl != null)
{
// If the component hasn't been added, do it now
if (shuttleSideDoorControlInteractive == null)
{
shuttleSideDoorControlInteractive = shuttleSideDoorControl.GetComponent<StickyInteractive>();
if (shuttleSideDoorControlInteractive == null) { shuttleSideDoorControlInteractive = shuttleSideDoorControl.AddComponent<StickyInteractive>(); }
}
if (shuttleSideDoorControlInteractive != null)
{
shuttleSideDoorControlInteractive.Initialise();
shuttleSideDoorControlInteractive.SetIsTouchable(true);
shuttleSideDoorControlInteractive.handHold1Offset = new Vector3(0f, 0.1f, 0.05f);
shuttleSideDoorControlInteractive.handHold1Rotation = new Vector3(10f, 180f, 90f);
// The door control has a 3-button panel to give the player some visual feedback
sscDoorControlShuttleSideDoors = shuttleSideDoorControl.GetComponent<SSCDoorControl>();
if (sscDoorControlShuttleSideDoors != null)
{
sscDoorControlShuttleSideDoors.Initialise();
// Hook up a listener to open the doors
// See also OnDestroy() which removes the listeners
shuttleSideDoorControlInteractive.onTouched = new S3DInteractiveEvt1();
// We use a delegate as we are calling a method that doesn't use the event parameters.
shuttleSideDoorControlInteractive.onTouched.AddListener(delegate { SSCDoorControl.SelectOpen(sscDoorControlShuttleSideDoors); });
// These should really test if the door is open first but we're a little lazy...
shuttleSideDoorControlInteractive.onTouched.AddListener(delegate { StartSpacePortAmbientFX(); });
shuttleSideDoorControlInteractive.onTouched.AddListener(delegate { LightingEnterSpaceport(); });
// Get notified when the player stops touching the door control buttons
// This will stop the character reaching for the button
shuttleSideDoorControlInteractive.onStoppedTouching = new S3DInteractiveEvt2();
shuttleSideDoorControlInteractive.onStoppedTouching.AddListener(DoorControlStopTouching);
}
}
}
#endregion
#region Shuttle Console Dock Button
if (dockingButton != null)
{
// If the component hasn't been added, do it now
if (shuttleDockingButtonInteractive == null)
{
shuttleDockingButtonInteractive = dockingButton.GetComponent<StickyInteractive>();
if (shuttleDockingButtonInteractive == null) { shuttleDockingButtonInteractive = dockingButton.AddComponent<StickyInteractive>(); }
}
if (shuttleDockingButtonInteractive != null)
{
shuttleDockingButtonInteractive.Initialise();
shuttleDockingButtonInteractive.SetIsSelectable(true);
shuttleDockingButtonInteractive.onSelected = new S3DInteractiveEvt3();
// We don't need a delegate method here as we have a method with parameters that
// matches the event parameters.
shuttleDockingButtonInteractive.onSelected.AddListener(OnInterativeButtonSelected);
}
}
#endregion
}
/// <summary>
/// Attempt to have the Player sit in the shuttle pilot's chair
/// </summary>
private void SitShuttlePilot()
{
if (sampleSitActionPlayer != null)
{
sampleSitActionPlayer.SitDown();
// Re-enable input so player can look around while seated
s3dPlayerInput.EnableInput();
// Start in 3rd person, switch to first person to sit down
if (s3dPlayer.IsLookThirdPersonEnabled) { s3dPlayer.ToggleFirstThirdPerson(); }
s3dPlayer.SetFreeLook(true);
// We are using the look at point in update to set
// the head ik target, so make sure we get the data.
s3dPlayer.SetUpdateLookingAtPoint(true);
s3dPlayer.SetHeadIKLookAtInteractive(true);
s3dPlayer.EnableHeadIK(true);
}
}
/// <summary>
/// Attempt to have the Player stand up from the shuttle pilot's chair
/// </summary>
private void StandupShuttlePilot()
{
if (sampleSitActionPlayer != null)
{
sampleSitActionPlayer.StandUp();
// Make sure we're in third person mode
if (!s3dPlayer.IsLookThirdPersonEnabled) { s3dPlayer.ToggleFirstThirdPerson(); }
// Make sure character can reach the shuttle door control
s3dPlayer.SetRightHandMaxReachDistance(0.5f);
}
}
#endif
#endregion
#region Private Methods - General
/// <summary>
/// Sometimes the enemy will attempt to attack the space port.
/// </summary>
/// <param name="shipAIInputModuleInstance"></param>
/// <param name="shipControlModuleInstance"></param>
/// <param name="squadronRadarQuery"></param>
private bool AttemptAttackSpaceport (ShipAIInputModule shipAIInputModuleInstance, ShipControlModule shipControlModuleInstance)
{
bool isTargetAssigned = false;
// Only attack the space port sometimes
if (aiRandom != null && aiRandom.Range(0f, 1f) > 0.7f)
{
//int squadronId = shipControlModuleInstance.shipInstance.squadronId;
int shipId = shipControlModuleInstance.shipInstance.shipId;
// Execute the query
sscRadar.GetRadarResults(enemyRadarQuery, sscRadarBlipsList);
sscRadarBlipsListCount = sscRadarBlipsList.Count;
if (sscRadarBlipsListCount > 0)
{
// Choose a random radar blip
int chosenRadarBlipIndex = 0;
if ((int)enemyRadarQuery.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.GameObject)
{
GameObject chosenRadarBlipGameObject = chosenRadarBlip.itemGameObject;
if (chosenRadarBlipGameObject != null)
{
// Chosen target is a gameobject, so attack it with the strafing run state
shipAIInputModuleInstance.SetState(AIState.strafingRunStateID);
shipAIInputModuleInstance.AssignTargetPosition(chosenRadarBlip.wsPosition);
shipAIInputModuleInstance.AssignTargetRadius(150f);
shipAIInputModuleInstance.maxSpeed = strafingRunSpeed;
// Ensure this ship is not in the available list
availableEnemyShips.Remove(shipId);
//Debug.Log("[DEBUG] " + shipControlModuleInstance.name + " is attacking " + chosenRadarBlipGameObject + " at T:" + Time.time);
isTargetAssigned = true;
}
#if UNITY_EDITOR
else
{
Debug.LogWarning("TechDemo3.cs AttemptAttackSpaceport: chosen radar blip gameobject is null.");
}
#endif
}
#if UNITY_EDITOR
else
{
Debug.LogWarning("TechDemo3.cs AttemptAttackSpaceport: Radar blip has wrong type (" + chosenRadarBlip.radarItemType + ")");
}
#endif
}
}
return isTargetAssigned;
}
/// <summary>
/// Attempts to pair a ship with another ship. If successful, one ship will then pursue the other ship.
/// </summary>
/// <param name="shipAIInputModuleInstance"></param>
/// <param name="shipControlModuleInstance"></param>
/// <returns></returns>
private void AttemptPairing (ShipAIInputModule shipAIInputModuleInstance, ShipControlModule shipControlModuleInstance)
{
// TODO need to make sure that when ships are destroyed they are removed from the relevant lists
if (shipAIInputModuleInstance != null && shipControlModuleInstance != null)
{
bool pairingSuccessful = false;
ShipAIInputModule friendlyShipAIInputModule = null;
ShipAIInputModule enemyShipAIInputModule = null;
// Check whether this ship is a friendly ship or an enemy ship
bool isFriendlyShip = shipControlModuleInstance.shipInstance.factionId == friendlyFactionID;
if (isFriendlyShip)
{
friendlyShipAIInputModule = shipAIInputModuleInstance;
int thisShipListIndex = availableFriendlyShips.FindIndex(a => a == shipControlModuleInstance.GetShipId);
// If this is a friendly ship, look for an enemy ship to pair it with
if (availableEnemyShips != null && availableEnemyShips.Count > 0)
{
// Pick the first enemy ship from the list (as this has theoretically been in the queue for the longest)
int pairedShipId = availableEnemyShips[0];
// Find the paired ship in the lists of enemy ships
for (int i = 0; i < numEnemyShip1; i++)
{
if (enemyType1Ships[i].GetShipControlModule.GetShipId == pairedShipId)
{
enemyShipAIInputModule = enemyType1Ships[i];
}
}
if (enemyShipAIInputModule == null)
{
for (int i = 0; i < numEnemyShip2; i++)
{
if (enemyType2Ships[i].GetShipControlModule.GetShipId == pairedShipId)
{
enemyShipAIInputModule = enemyType2Ships[i];
}
}
}
// Only continue if we managed to find the paired ship
if (enemyShipAIInputModule != null)
{
// Remove this ship from the list of available friendly ships (if it has been added)
if (thisShipListIndex != -1) { availableFriendlyShips.RemoveAt(thisShipListIndex); }
// Remove the enemy ship we found from the list of available enemy ships
availableEnemyShips.RemoveAt(0);
// Record that pairing was successful
pairingSuccessful = true;
}
}
else
{
// If no enemy ship was found to pair it with, follow the friendly idle path
// Essentially, we want to wait for an enemy ship to be available to pair with us
shipAIInputModuleInstance.SetState(AIState.moveToStateID);
shipAIInputModuleInstance.AssignTargetPath(friendlyIdlePath);
shipAIInputModuleInstance.maxSpeed = friendlyIdleSpeed;
// Add this ship to the list of available friendly ships (if it hasn't already been added)
if (thisShipListIndex == -1) { availableFriendlyShips.Add(shipControlModuleInstance.GetShipId); }
}
}
else
{
// If this is an enemy ship, look for a friendly ship to pair it with
enemyShipAIInputModule = shipAIInputModuleInstance;
int thisShipListIndex = availableEnemyShips.FindIndex(a => a == shipControlModuleInstance.GetShipId);
if (availableFriendlyShips != null && availableFriendlyShips.Count > 0)
{
// Pick the first friendly ship from the list (as this has theoretically been in the queue for the longest)
int pairedShipId = availableFriendlyShips[0];
// Find the paired ship in the list of friendly ships
for (int i = 0; i < numFriendlyShip; i++)
{
if (friendlyShips[i].GetShipControlModule.GetShipId == pairedShipId)
{
friendlyShipAIInputModule = friendlyShips[i];
}
}
// Only continue if we managed to find the paired ship
if (friendlyShipAIInputModule != null)
{
// Remove this ship from the list of available enemy ships (if it has been added)
if (thisShipListIndex != -1) { availableEnemyShips.RemoveAt(thisShipListIndex); }
// Remove the friendly ship we found from the list of available friendly ships
availableFriendlyShips.RemoveAt(0);
// Record that pairing was successful
pairingSuccessful = true;
}
}
else
{
// If no friendly ship was found to pair it with, attempt a strafing run (if bombing has started)
// Essentially, we want to wait for an friendly ship to be availabe to pair with us
if (!bombingRunsStarted || !AttemptAttackSpaceport(shipAIInputModuleInstance, shipControlModuleInstance))
{
// Otherwise follow the enemy idle path
shipAIInputModuleInstance.SetState(AIState.moveToStateID);
shipAIInputModuleInstance.AssignTargetPath(enemyIdlePath);
shipAIInputModuleInstance.maxSpeed = enemyIdleSpeed;
// Add this ship to the list of available enemy ships (if it hasn't already been added)
if (thisShipListIndex == -1) { availableEnemyShips.Add(shipControlModuleInstance.GetShipId); }
}
}
}
// If we found two ships to pair together...
if (pairingSuccessful && friendlyShipAIInputModule != null && enemyShipAIInputModule != null)
{
// Randomly choose whether the friendly ship will be pursuing the enemy ship (or the other way around)
bool friendlyShipPursuing = aiRandom.Normalised() > 0.5f;
// One ship is pursuing, one ship is escaping
if (friendlyShipPursuing)
{
friendlyShipAIInputModule.SetState(AIState.dogfightStateID);
friendlyShipAIInputModule.AssignTargetShip(enemyShipAIInputModule.GetShipControlModule);
friendlyShipAIInputModule.maxSpeed = pursuitSpeed;
enemyShipAIInputModule.SetState(AIState.moveToStateID);
int enemyEscapePathsCount = enemyEscapePaths.Count;
int enemyEscapePathIndex = aiRandom.Range(0, enemyEscapePathsCount-1);
enemyShipAIInputModule.AssignTargetPath(enemyEscapePaths[enemyEscapePathIndex]);
enemyShipAIInputModule.AssignShipsToEvade(new List<Ship>(new Ship[]{ friendlyShipAIInputModule.GetShipControlModule.shipInstance }));
enemyShipAIInputModule.maxSpeed = pursuitSpeed;
}
else
{
enemyShipAIInputModule.SetState(AIState.dogfightStateID);
enemyShipAIInputModule.AssignTargetShip(friendlyShipAIInputModule.GetShipControlModule);
enemyShipAIInputModule.maxSpeed = pursuitSpeed;
friendlyShipAIInputModule.SetState(AIState.moveToStateID);
int friendlyEscapePathsCount = friendlyEscapePaths.Count;
int friendlyEscapePathIndex = aiRandom.Range(0, friendlyEscapePathsCount);
friendlyShipAIInputModule.AssignTargetPath(friendlyEscapePaths[0]);
friendlyShipAIInputModule.AssignShipsToEvade(new List<Ship>(new Ship[] { enemyShipAIInputModule.GetShipControlModule.shipInstance }));
friendlyShipAIInputModule.maxSpeed = pursuitSpeed;
}
}
}
}
/// <summary>
/// Check if each of the capital ships is destroyed and the state of the shield generators. If all
/// shield generators on a capital ship have been destroyed, lower the shield on the bridge.
/// </summary>
private void CheckCapitalShipHealth()
{
// Loop through all the capital ships
for (int i = 0; i < numEnemyCapitalShips; i++)
{
ShipAIInputModule enemyShip = enemyCapitalShips[i];
if (enemyShip != null)
{
ShipControlModule enemyShipCM = enemyShip.GetShipControlModule;
if (enemyShipCM != null && enemyShipCM.ShipIsEnabled() && !enemyShipCM.shipInstance.Destroyed())
{
// If the bridge is still shielded, it means the shield generators haven't been destroyed
if (enemyShipCM.shipInstance.localisedDamageRegionList[7].useShielding)
{
// Count the number of shield generators (their damage regions) with zero health
int currentNumShieldGeneratorsDestroyed = 0;
if (enemyShipCM.shipInstance.localisedDamageRegionList[3].Health <= 0f) { currentNumShieldGeneratorsDestroyed++; }
if (enemyShipCM.shipInstance.localisedDamageRegionList[4].Health <= 0f) { currentNumShieldGeneratorsDestroyed++; }
if (enemyShipCM.shipInstance.localisedDamageRegionList[5].Health <= 0f) { currentNumShieldGeneratorsDestroyed++; }
if (enemyShipCM.shipInstance.localisedDamageRegionList[6].Health <= 0f) { currentNumShieldGeneratorsDestroyed++; }
// Bring down the shield if the health of all the shield generator regions is zero
if (currentNumShieldGeneratorsDestroyed == 4)
{
enemyShipCM.shipInstance.localisedDamageRegionList[7].useShielding = false;
}
}
// Else if the shields are already down, check if the bridge has been destroyed
else if (enemyShipCM.shipInstance.localisedDamageRegionList[7].Health <= 0f)
{
// If the bridge has been destroyed, destroy the capital ship
enemyShipCM.shipInstance.mainDamageRegion.useShielding = false;
enemyShipCM.shipInstance.ApplyNormalDamage(1000f, ProjectileModule.DamageType.Default, Vector3.zero);
}
}
}
}
}
/// <summary>
/// Configure custom EffectsModules we use for scene-specific FX
/// </summary>
private void ConfigureEffectsPools()
{
// Used for playing a sound FX
if (soundPoolPrefab != null)
{
if (sscManager != null)
{
soundeffectsObjectPrefabID = sscManager.GetorCreateEffectsPool(soundPoolPrefab);
}
}
#if UNITY_EDITOR
else
{
Debug.LogWarning("ERROR: TechDemo3 - the soundPoolPrefab has not been added in the inspector.");
}
#endif
if (sparksEffectsObjectPrefab != null)
{
sparksEffectsObjectPrefabID = sscManager.GetorCreateEffectsPool(sparksEffectsObjectPrefab);
// Cache the number of places the sparks can be randomly instantiated
numSparkFXPoints = sparkFXPoints == null ? 0 : sparkFXPoints.Length;
sparksRandom = new SSCRandom();
sparksRandom.SetSeed(7252221);
}
#if UNITY_EDITOR
else
{
Debug.LogWarning("ERROR: TechDemo3 - the sparksEffectsObjectPrefab has not been added in the inspector.");
}
#endif
}
/// <summary>
/// Configure the heads-up display
/// </summary>
private void ConfigureHUD()
{
if (shipDisplayModule != null)
{
// Use the HUD to show indicators when the shuttle is docked
dockedIndicator = shipDisplayModule.GetDisplayGauge("Docked Indicator");
// Gauges
fuelLevelGauge = shipDisplayModule.GetDisplayGauge("Fuel Level");
heatLevelGauge = shipDisplayModule.GetDisplayGauge("Heat Level");
healthGauge = shipDisplayModule.GetDisplayGauge("Health");
missilesGauge = shipDisplayModule.GetDisplayGauge("Missiles");
shieldsGauge = shipDisplayModule.GetDisplayGauge("Shields");
launchGauge = shipDisplayModule.GetDisplayGauge("Launch");
gearGauge = shipDisplayModule.GetDisplayGauge("Gear");
enemyGauge = shipDisplayModule.GetDisplayGauge("Enemy");
if (enemyGauge != null) { enemyGauge.gaugeMaxValue = numEnemyShip1 + numEnemyShip2; }
// Find the message to tell player to dock the shuttle with the space port
startDockingMessage = shipDisplayModule.GetDisplayMessage("Start docking");
shipDisplayModule.ShowDisplayMessage(startDockingMessage);
// This message is displayed when useFlyThru is enabled and the shuttle is docking
dockingInProgressMessage = shipDisplayModule.GetDisplayMessage("Docking in Progress");
// Find the message to tell player to leave the shuttle via the side door
exitShuttleMessage = shipDisplayModule.GetDisplayMessage("Exit Shuttle");
// Find the message to tell player to find the services room
findServiceDeskMessage = shipDisplayModule.GetDisplayMessage("Get to services");
// Find the message we can use in the services room to tell the player to take the lift to the top deck
getToDeckMessage = shipDisplayModule.GetDisplayMessage("Get to deck");
// Find the messages used at the top of lift1. With Helmet is used with Stick3D when user hasn't put on helmet
// my selecting the handles of the safety cabinet.
getToHawkMessage = shipDisplayModule.GetDisplayMessage("Get to Hawk");
getToHawkMessageWithHelmet = shipDisplayModule.GetDisplayMessage("Get to Hawk with Helmet");
// 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"
int guidHashReticle = shipDisplayModule.GetDisplayReticleGuidHash("SSCUIAim3");
DisplayTarget displayTarget = shipDisplayModule.AddTarget(guidHashReticle);
displayTarget.factionsToInclude = new int[] { enemyFactionID };
shipDisplayModule.AddTargetSlots(displayTarget, 2);
shipDisplayModule.SetDisplayTargetReticleColour(displayTarget, Color.red);
// Create a friendly "target"
guidHashReticle = shipDisplayModule.GetDisplayReticleGuidHash("SSCUIAim3");
displayTarget = shipDisplayModule.AddTarget(guidHashReticle);
displayTarget.isTargetable = false;
displayTarget.factionsToInclude = new int[] { friendlyFactionID };
shipDisplayModule.SetDisplayTargetReticleColour(displayTarget, Color.blue);
shipDisplayModule.HideHeading();
#if SCSM_S3D
shipDisplayModule.Initialise();
#endif
//shipDisplayModule.HideCursor();
}
#if UNITY_EDITOR
else
{
Debug.LogWarning("ERROR: TechDemo3- the shipDisplayModule (HUD) has not been added in the inspector.");
}
#endif
}
/// <summary>
/// Setup the scene for when there is no integration with Sticky Control Module
/// </summary>
private void ConfigureNoStickyController()
{
#if SCSM_S3D
if (pilotController != null)
{
s3dPlayer = pilotController.GetComponent<StickyControlModule>();
if (s3dPlayer != null)
{
s3dPlayer.DisableCharacter(false);
}
pilotController.gameObject.SetActive(false);
}
#endif
// Make sure we can get out the shuttle side doors
if (shuttleSideDoorControl != null && sscShuttleDoorAnimator != null)
{
// The door control has a 3-button panel to give the player some visual feedback
sscDoorControlShuttleSideDoors = shuttleSideDoorControl.GetComponent<SSCDoorControl>();
if (sscDoorControlShuttleSideDoors != null)
{
sscDoorControlShuttleSideDoors.Initialise();
}
sscShuttleDoorAnimator.UnlockDoor(0);
}
// Sticky3D characters can activate the doors using the SSCDoorControl panel. But without this, we should
// just open the doors, once they are unlocked, using the Door Proximity component with the trigger collider.
if (lift1BottomDoors != null)
{
SSCDoorProximity sscDoorProximity = lift1BottomDoors.gameObject.GetComponentInChildren<SSCDoorProximity>();
if (sscDoorProximity != null)
{
sscDoorProximity.isOpenDoorsOnEntry = true;
sscDoorProximity.isCloseDoorsOnExit = true;
}
}
// When Sticky3D is not being used, we still might want to simulate clicking the
// call lift button during the walk through.
if (lift1Control != null && lift1ControlChangeMaterial == null)
{
// Get the component that will replace the control panel material
lift1ControlChangeMaterial = lift1Control.GetComponent<DemoSSCChangeMaterial>();
TurnOffLiftCallButton();
}
// There is no S3D character to put on a helmet, so just unlock the outer doors at top of lift1
if (lift1OuterDoors != null) { lift1OuterDoors.UnlockDoors(); }
if (startDockingMessage != null && shipDisplayModule != null)
{
shipDisplayModule.SetDisplayMessageText(startDockingMessage, "(I)nitiate docking procedure");
}
if (playerShuttle != null && playerShuttle.IsInitialised)
{
shuttleOriginalPos = playerShuttle.shipInstance.TransformPosition;
shuttleOriginalRot = playerShuttle.shipInstance.TransformRotation;
}
ConfigureWalkThrough(true);
}
/// <summary>
/// Destroy the shuttle the player arrived in
/// </summary>
private void DestroyShuttle()
{
if (playerShuttle != null && playerShuttle.IsInitialised)
{
playerShuttle.shipInstance.mainDamageRegion.Health = 0;
}
}
/// <summary>
/// Get the AIShipInputModule using only the Ship script reference
/// </summary>
/// <param name="shipInstance"></param>
/// <returns></returns>
private ShipAIInputModule FindAIShip(Ship shipInstance)
{
ShipAIInputModule shipAIInputModule = null;
int factionId = shipInstance.factionId;
int squadronId = shipInstance.squadronId;
int shipId = shipInstance.shipId;
// Look in the appropriate list
if (factionId == friendlyFactionID)
{
for (int i = 0; i < numFriendlyShip; i++)
{
ShipAIInputModule friendlyShip = friendlyShips[i];
if (friendlyShip != null)
{
// Is this the ship we're looking for?
if (friendlyShip.GetShipId == shipId)
{
shipAIInputModule = friendlyShip;
break;
}
}
}
}
else if (factionId == enemyFactionID)
{
if (squadronId == enemySquadron1ID)
{
for (int i = 0; i < numEnemyShip1; i++)
{
ShipAIInputModule enemyShip = enemyType1Ships[i];
if (enemyShip != null)
{
if (enemyShip.GetShipId == shipId)
{
shipAIInputModule = enemyShip;
break;
}
}
}
}
else if (squadronId == enemySquadron2ID)
{
for (int i = 0; i < numEnemyShip2; i++)
{
ShipAIInputModule enemyShip = enemyType2Ships[i];
if (enemyShip != null)
{
if (enemyShip.GetShipId == shipId)
{
shipAIInputModule = enemyShip;
break;
}
}
}
}
}
return shipAIInputModule;
}
/// <summary>
/// Spawns in and initialises the ships for the attack.
/// </summary>
private void InitialiseAttackShips ()
{
#region Set Up Radar Queries
// Set up friendly radar query
friendlyRadarQuery = new SSCRadarQuery();
friendlyRadarQuery.centrePosition = battleCentralPos;
friendlyRadarQuery.range = 10000f;
friendlyRadarQuery.factionId = enemyFactionID;
friendlyRadarQuery.factionsToInclude = null;
friendlyRadarQuery.factionsToExclude = null;
friendlyRadarQuery.squadronsToInclude = null;
friendlyRadarQuery.squadronsToExclude = null;
friendlyRadarQuery.is3DQueryEnabled = true;
friendlyRadarQuery.querySortOrder = SSCRadarQuery.QuerySortOrder.DistanceAsc3D;
// Set up enemy radar query
enemyRadarQuery = new SSCRadarQuery();
enemyRadarQuery.centrePosition = battleCentralPos;
enemyRadarQuery.range = 10000f;
enemyRadarQuery.factionId = friendlyFactionID;
enemyRadarQuery.factionsToInclude = null;
enemyRadarQuery.factionsToExclude = null;
enemyRadarQuery.squadronsToInclude = new int[] { barrelsSquadronID };
enemyRadarQuery.squadronsToExclude = null;
enemyRadarQuery.is3DQueryEnabled = true;
enemyRadarQuery.querySortOrder = SSCRadarQuery.QuerySortOrder.None;
#endregion
#region Spawn in and Initialise Ships
// Initialise random generator
aiRandom = new SSCRandom();
aiRandom.SetSeed(0);
// Initialise lists of all the ships
friendlyShips = new List<ShipAIInputModule>(numFriendlyShip);
enemyType1Ships = new List<ShipAIInputModule>(numEnemyShip1);
enemyType2Ships = new List<ShipAIInputModule>(numEnemyShip2);
enemyCapitalShips = new List<ShipAIInputModule>(capitalShipSpawnPoints != null ? capitalShipSpawnPoints.Length : 0);
// Initialise lists of available ships
availableFriendlyShips = new List<int>(numFriendlyShip);
availableEnemyShips = new List<int>(numEnemyShip1 + numEnemyShip2);
ShipDocking shipDocking;
// Spawn in friendly ships
if (friendlyShipPrefab != null && numFriendlyShip > 0)
{
for (int i = 0; i < numFriendlyShip; i++)
{
// Instantiate the ship at the origin
GameObject shipGameObjectInstance = Object.Instantiate(friendlyShipPrefab.gameObject,
Vector3.zero, Quaternion.identity);
// Append an index to the name
shipGameObjectInstance.name += " " + (i + 1).ToString();
// Parent the ship to the ships gameobject
if (shipsParentGameObject != null)
{
shipGameObjectInstance.transform.SetParent(shipsParentGameObject.transform);
}
// If there is a docking component on the prefab, remove it as we don't need it in this demo
if (shipGameObjectInstance.TryGetComponent(out shipDocking))
{
Destroy(shipDocking);
}
// Position the ship correctly
float rotY = 360f * ((float)i / numFriendlyShip);
shipGameObjectInstance.transform.position = battleCentralPos +
(Quaternion.Euler(0f, rotY, 0f) * Vector3.forward * friendlyShipSpawnDist);
// Rotate the ship correctly
shipGameObjectInstance.transform.rotation = Quaternion.Euler(0f, rotY, 0f);
// Get the ship control module instance
ShipControlModule shipControlModuleInstance = shipGameObjectInstance.GetComponent<ShipControlModule>();
// Get the ship AI input module instance
ShipAIInputModule shipAIInputModuleInstance = shipGameObjectInstance.GetComponent<ShipAIInputModule>();
// Initialise the ship
shipControlModuleInstance.InitialiseShip();
shipAIInputModuleInstance.Initialise();
// Set the ship to the idle state
if (shipAIInputModuleInstance.IsInitialised)
{
shipAIInputModuleInstance.SetState(AIState.idleStateID);
}
// Set the faction/squadron ID of the ship
shipControlModuleInstance.shipInstance.factionId = friendlyFactionID;
shipControlModuleInstance.shipInstance.squadronId = friendlySquadronID;
// Set the spawn point of the ship
shipControlModuleInstance.shipInstance.customRespawnPosition = shipGameObjectInstance.transform.position;
shipControlModuleInstance.shipInstance.respawningMode = Ship.RespawningMode.RespawnAtSpecifiedPosition;
// Stuck action
shipControlModuleInstance.shipInstance.stuckTime = 2f;
shipControlModuleInstance.shipInstance.stuckSpeedThreshold = 0.1f;
shipControlModuleInstance.shipInstance.stuckAction = Ship.StuckAction.InvokeCallback;
// Make the ship visible to radar
shipControlModuleInstance.EnableRadar();
// Set the accuracy of the ship
shipAIInputModuleInstance.targetingAccuracy = friendlyAccuracy;
// Set up callbacks
shipControlModuleInstance.callbackOnRespawn = OnRespawnCallback;
shipControlModuleInstance.callbackOnStuck = OnShipStuck;
shipControlModuleInstance.callbackOnDestroy = OnAIShipDestroyed;
shipAIInputModuleInstance.callbackCompletedStateAction = CompletedStateActionCallback;
// Disable the ship until the attack starts
shipControlModuleInstance.DisableShip(true);
// To improve obstacle avoidance increase the ship's radius
// NOTE: This also affects path following so don't make it too large
if (shipAIInputModuleInstance.shipRadius < 10f)
{
shipAIInputModuleInstance.shipRadius = 10f;
}
// Store this ship in a list for future reference
friendlyShips.Add(shipAIInputModuleInstance);
availableFriendlyShips.Add(shipControlModuleInstance.GetShipId);
}
}
// Spawn in enemy attack ships
if (enemyShip1Prefab != null && numEnemyShip1 > 0)
{
for (int i = 0; i < numEnemyShip1; i++)
{
// Instantiate the ship at the origin
GameObject shipGameObjectInstance = Object.Instantiate(enemyShip1Prefab.gameObject,
Vector3.zero, Quaternion.identity);
// Append an index to the name
shipGameObjectInstance.name += " " + (i + 1).ToString();
// Parent the ship to the ships gameobject
if (shipsParentGameObject != null)
{
shipGameObjectInstance.transform.SetParent(shipsParentGameObject.transform);
}
// If there is a docking component on the prefab, remove it as we don't need it in this demo
if (shipGameObjectInstance.TryGetComponent(out shipDocking))
{
Destroy(shipDocking);
}
// Position the ship correctly
float rotY = 360f * ((float)(i) / (numEnemyShip1 + numEnemyShip2));
shipGameObjectInstance.transform.position = battleCentralPos +
(Quaternion.Euler(0f, rotY, 0f) * Vector3.forward * enemyShipSpawnDist) + (Vector3.up * 200f);
// Rotate the ship correctly
shipGameObjectInstance.transform.rotation = Quaternion.Euler(0f, rotY + 180f, 0f);
// Get the ship control module instance
ShipControlModule shipControlModuleInstance = shipGameObjectInstance.GetComponent<ShipControlModule>();
// Get the ship AI input module instance
ShipAIInputModule shipAIInputModuleInstance = shipGameObjectInstance.GetComponent<ShipAIInputModule>();
// Initialise the ship
shipControlModuleInstance.InitialiseShip();
shipAIInputModuleInstance.Initialise();
// Set the ship to the idle state
if (shipAIInputModuleInstance.IsInitialised)
{
shipAIInputModuleInstance.SetState(AIState.idleStateID);
}
// Set the faction/squadron ID of the ship
shipControlModuleInstance.shipInstance.factionId = enemyFactionID;
shipControlModuleInstance.shipInstance.squadronId = enemySquadron1ID;
// Set the spawn point of the ship
shipControlModuleInstance.shipInstance.customRespawnPosition = shipGameObjectInstance.transform.position;
shipControlModuleInstance.shipInstance.respawningMode = Ship.RespawningMode.RespawnAtSpecifiedPosition;
// Stuck action
shipControlModuleInstance.shipInstance.stuckTime = 2f;
shipControlModuleInstance.shipInstance.stuckSpeedThreshold = 0.1f;
shipControlModuleInstance.shipInstance.stuckAction = Ship.StuckAction.InvokeCallback;
// Make the ship visible to radar
shipControlModuleInstance.EnableRadar();
// Set the accuracy of the ship
shipAIInputModuleInstance.targetingAccuracy = enemyAccuracy;
// Set up callbacks
shipControlModuleInstance.callbackOnRespawn = OnRespawnCallback;
shipControlModuleInstance.callbackOnStuck = OnShipStuck;
shipControlModuleInstance.callbackOnDestroy = OnAIShipDestroyed;
shipAIInputModuleInstance.callbackCompletedStateAction = CompletedStateActionCallback;
// Disable the ship until the attack starts
shipControlModuleInstance.DisableShip(true);
// To improve obstacle avoidance increase the ship's radius
// NOTE: This also affects path following so don't make it too large
if (shipAIInputModuleInstance.shipRadius < 7f)
{
shipAIInputModuleInstance.shipRadius = 7f;
}
// Store this ship in a list for future reference
enemyType1Ships.Add(shipAIInputModuleInstance);
availableEnemyShips.Add(shipControlModuleInstance.GetShipId);
}
}
if (enemyShip2Prefab != null && numEnemyShip2 > 0)
{
for (int i = 0; i < numEnemyShip2; i++)
{
// Instantiate the ship at the origin
GameObject shipGameObjectInstance = Object.Instantiate(enemyShip2Prefab.gameObject,
Vector3.zero, Quaternion.identity);
// Append an index to the name
shipGameObjectInstance.name += " " + (i + 1).ToString();
// Parent the ship to the ships gameobject
if (shipsParentGameObject != null)
{
shipGameObjectInstance.transform.SetParent(shipsParentGameObject.transform);
}
// If there is a docking component on the prefab, remove it as we don't need it in this demo
if (shipGameObjectInstance.TryGetComponent(out shipDocking))
{
Destroy(shipDocking);
}
// Position the ship correctly
float rotY = 360f * ((float)(i + numEnemyShip1) / (numEnemyShip1 + numEnemyShip2));
shipGameObjectInstance.transform.position = battleCentralPos +
(Quaternion.Euler(0f, rotY, 0f) * Vector3.forward * enemyShipSpawnDist) + (Vector3.up * 200f);
// Rotate the ship correctly
shipGameObjectInstance.transform.rotation = Quaternion.Euler(0f, rotY + 180f, 0f);
// Get the ship control module instance
ShipControlModule shipControlModuleInstance = shipGameObjectInstance.GetComponent<ShipControlModule>();
// Get the ship AI input module instance
ShipAIInputModule shipAIInputModuleInstance = shipGameObjectInstance.GetComponent<ShipAIInputModule>();
// Initialise the ship
shipControlModuleInstance.InitialiseShip();
shipAIInputModuleInstance.Initialise();
// Set the ship to the idle state
if (shipAIInputModuleInstance.IsInitialised)
{
shipAIInputModuleInstance.SetState(AIState.idleStateID);
}
// Set the faction/squadron ID of the ship
shipControlModuleInstance.shipInstance.factionId = enemyFactionID;
shipControlModuleInstance.shipInstance.squadronId = enemySquadron2ID;
// Set the spawn point of the ship
shipControlModuleInstance.shipInstance.customRespawnPosition = shipGameObjectInstance.transform.position;
shipControlModuleInstance.shipInstance.respawningMode = Ship.RespawningMode.RespawnAtSpecifiedPosition;
// Stuck action
shipControlModuleInstance.shipInstance.stuckTime = 2f;
shipControlModuleInstance.shipInstance.stuckSpeedThreshold = 0.1f;
shipControlModuleInstance.shipInstance.stuckAction = Ship.StuckAction.InvokeCallback;
// Make the ship visible to radar
shipControlModuleInstance.EnableRadar();
// Set the accuracy of the ship
shipAIInputModuleInstance.targetingAccuracy = enemyAccuracy;
// Set up callbacks
shipControlModuleInstance.callbackOnRespawn = OnRespawnCallback;
shipControlModuleInstance.callbackOnStuck = OnShipStuck;
shipControlModuleInstance.callbackOnDestroy = OnAIShipDestroyed;
shipAIInputModuleInstance.callbackCompletedStateAction = CompletedStateActionCallback;
// Disable the ship until the attack starts
shipControlModuleInstance.DisableShip(true);
// Store this ship in a list for future reference
enemyType2Ships.Add(shipAIInputModuleInstance);
availableEnemyShips.Add(shipControlModuleInstance.GetShipId);
}
}
// Spawn in enemy capital ships
if (enemyCapitalShipPrefab != null && capitalShipSpawnPoints != null && capitalShipSpawnPoints.Length > 0)
{
for (int i = 0; i < capitalShipSpawnPoints.Length; i++)
{
// Instantiate the ship at the origin
GameObject shipGameObjectInstance = Object.Instantiate(enemyCapitalShipPrefab.gameObject,
Vector3.zero, Quaternion.identity);
// Append an index to the name
shipGameObjectInstance.name += " " + (i + 1).ToString();
// Parent the ship to the ships gameobject
if (shipsParentGameObject != null)
{
shipGameObjectInstance.transform.parent = shipsParentGameObject.transform;
}
// Position the ship correctly
shipGameObjectInstance.transform.position = capitalShipSpawnPoints[i];
// Rotate the ship correctly
shipGameObjectInstance.transform.rotation = Quaternion.Euler(capitalShipSpawnRots[i]);
// Get the ship control module instance
ShipControlModule shipControlModuleInstance = shipGameObjectInstance.GetComponent<ShipControlModule>();
// Get the ship AI input module instance
ShipAIInputModule shipAIInputModuleInstance = shipGameObjectInstance.GetComponent<ShipAIInputModule>();
// Initialise the ship
shipControlModuleInstance.InitialiseShip();
shipAIInputModuleInstance.Initialise();
// Set the ship to the idle state
if (shipAIInputModuleInstance.IsInitialised)
{
shipAIInputModuleInstance.SetState(AIState.idleStateID);
}
// Set the faction ID and squadron ID of the ship
shipControlModuleInstance.shipInstance.factionId = enemyFactionID;
shipControlModuleInstance.shipInstance.squadronId = enemyCapitalSquadronID;
// In the battle, movement will be disabled on the capital ships, but
// we still want the turrets to fire at the friendly ships (including the player)
shipControlModuleInstance.shipInstance.useWeaponsWhenMovementDisabled = true;
// Adjust the turret weapons
int numWeapons = shipControlModuleInstance.NumberOfWeapons;
for (int wpIdx = 0; wpIdx < numWeapons; wpIdx++)
{
Weapon weapon = shipControlModuleInstance.shipInstance.GetWeaponByIndex(wpIdx);
if (weapon != null && weapon.IsTurretWeapon && weapon.isAutoTargetingEnabled)
{
// Give the friendly ships a better chance of not getting hit by the turrets
weapon.turretReturnToParkInterval = 3f;
weapon.reloadTime = 0.7f;
weapon.inaccuracy = 0.5f;
}
}
// Disable respawning
shipControlModuleInstance.shipInstance.respawningMode = Ship.RespawningMode.DontRespawn;
// Disable the ship until the attack starts
shipControlModuleInstance.DisableShip(true);
// Store this ship in a list for future reference
enemyCapitalShips.Add(shipAIInputModuleInstance);
}
}
#endregion
}
/// <summary>
/// Given a ShipId, check if the ship is a capital ship
/// </summary>
/// <param name="shipID"></param>
/// <returns></returns>
private bool IsCapitalShip(int shipId)
{
bool isCapitalShip = false;
int numEnemyCapitalShips = enemyCapitalShips == null ? 0 : enemyCapitalShips.Count;
for (int i = 0; i < numEnemyCapitalShips; i++)
{
if (enemyCapitalShips[i] != null && enemyCapitalShips[i].GetShipId == shipId)
{
isCapitalShip = true;
break;
}
}
return isCapitalShip;
}
/// <summary>
/// Play an audioclip at the specified world-space position at a volume.
/// This uses the pooled EffectsModules created during initialisation.
/// </summary>
/// <param name="audioClip"></param>
/// <param name="audioPosition"></param>
/// <param name="clipVolume"></param>
private void PlaySoundFX(AudioClip audioClip, Vector3 audioPosition, float clipVolume)
{
// Check to see if the sound fx pool was created ok and the clip is not null.
if (soundeffectsObjectPrefabID >= 0 && audioClip != null)
{
// Play the sound clip using a pool EffectsModule
InstantiateSoundFXParameters sfxParms = new InstantiateSoundFXParameters()
{
effectsObjectPrefabID = soundeffectsObjectPrefabID,
position = audioPosition,
volume = clipVolume
};
sscManager.InstantiateSoundFX(sfxParms, audioClip);
}
}
/// <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.
/// NOTE: In a real game we'd probably use baked lighting where possible
/// to improve overall performance.
/// </summary>
private void SetGameQuality()
{
bool isHigh = gameQuality == GameQuality.High;
bool isMedium = gameQuality == GameQuality.Medium;
bool isLow = !isHigh && !isMedium;
ReinitialiseLighting(isLow, isMedium, isHigh);
#region Particle Effects
// Sparks - How often are they instantiated
sparksInterval = isHigh ? 5f : isMedium ? 8f : 30f;
// Enemy 1 ships (Vectras) have Glow and Emission FX that may be too
// heavy for some hardware.
SetThrusterEffectQuality(enemyType1Ships);
// Captial ship has Liftoff thruster FX.
SetThrusterEffectQuality(enemyCapitalShips);
#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 || isMedium ? 60 : 30; }
// TESTING ONLY
//if (limitFrameRate) { Application.targetFrameRate = 60; }
#endregion
#region Ship Setup
if (playerHawk != null && playerHawk.IsInitialised)
{
// NOTE: HUD gauges get turned on/off in HawkReadyForTakeOff()
playerHawk.shipInstance.useCentralFuel = true;
Thruster firstThruster = playerHawk.shipInstance.thrusterList[0];
// Fuel consumption only happens on Medium and High
firstThruster.fuelBurnRate = isLow ? 0f : 0.1f;
// Engine heating only happens on Medium and High
firstThruster.heatUpRate = isLow ? 0f : 0.6f;
firstThruster.heatDownRate = 2f;
// Look up the weapons. Use the zero-based index to avoid GC with the weapon name
if (playerHawkLMissiles == null) { playerHawkLMissiles = playerHawk.shipInstance.GetWeaponByIndex(2); }
if (playerHawkRMissiles == null) { playerHawkRMissiles = playerHawk.shipInstance.GetWeaponByIndex(3); }
if (playerHawkLMissiles != null && playerHawkRMissiles != null)
{
// Low Quality has unlimited ammo and doesn't display the gauges in the HUD
playerHawkLMissiles.ammunition = isLow ? -1 : 40;
playerHawkRMissiles.ammunition = isLow ? -1 : 40;
if (missilesGauge != null)
{
// When the in Medium or High Quality, left + right missile ammo is 80
missilesGauge.gaugeMaxValue = 80;
}
}
}
#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.
/// The Vectra ships have Glow child thruster effects.
/// </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" or "Emission" in their name.
if (isHigh)
{
ship.EnableThrusterEffects("Glow");
ship.EnableThrusterEffects("Emission");
ship.EnableThrusterEffects("Liftoff");
}
else
{
ship.DisableThrusterEffects("Glow");
ship.DisableThrusterEffects("Emission");
ship.DisableThrusterEffects("Liftoff");
}
ship.ReinitialiseThrusterEffects();
}
}
}
}
/// <summary>
/// Invoked when the shuttle has docked.
/// Also called when the attack begins to improve performance.
/// </summary>
private void StopAmbientAudio()
{
if (ambientAudioSource.clip != null && ambientAudioSource.isPlaying)
{
ambientAudioSource.Stop();
}
}
#endregion
#region Private and internal Methods - Lighting
/// <summary>
/// Fade up the main direct light as the player crosses the top deck
/// of the space port. This helps to illuminate the other ships.
/// The current downside is that the planet in the celestials become
/// shiny.
/// </summary>
/// <returns></returns>
private IEnumerator FadeUpDirectLight()
{
yield return null;
if (mainLight != null)
{
// Only create once to avoid GC
if (fadeUpLightWait == null) { fadeUpLightWait = new WaitForSeconds(0.02f); }
float targetIntensity = isHDRP ? 0.05f : 0.8f;
while (mainLight.intensity < targetIntensity)
{
yield return fadeUpLightWait;
if (isHDRP)
{
SSCUtils.HDLightIntensityMultiply(hdLightType, mainLight, 1.02f);
}
else
{
mainLight.intensity += 0.001f;
}
}
}
}
/// <summary>
/// Reinitialise all the point lights
/// </summary>
private void ReinitialiseLighting(bool isLowQuality, bool isMediumQuality, bool isHighQuality)
{
#if UNITY_EDITOR
if (QualitySettings.pixelLightCount < 2)
{
Debug.LogWarning("TechDemo3 requires at least 2 pixel lights to provide the minimum lighting required. Check your Quality Settings.");
}
#endif
if (servicesLight2 != null) { servicesLight2.enabled = isHighQuality; }
if (isHDRP)
{
hdLightType = SSCUtils.GetHDLightDataType(true);
if (hdLightType != null)
{
// When a built-in scene is converted to HDRP, the lights get a default 600 Lumen value.. which seems much too bright for our setup
if (bottomLiftLight1 != null) { SSCUtils.SetHDLightIntensity(hdLightType, bottomLiftLight1, 0.3f); }
if (lift1CallLight != null)
{
// move the light a bit further away from the control panel to avoid glare in HDRP
lift1CallLight.transform.position += Vector3.up * 0.01f;
SSCUtils.SetHDLightIntensity(hdLightType, lift1CallLight, 0.001f);
}
if (entranceLight1 != null) { SSCUtils.SetHDLightIntensity(hdLightType, entranceLight1, 0.3f); }
if (entranceLight2 != null) { SSCUtils.SetHDLightIntensity(hdLightType, entranceLight2, 0.3f); }
if (servicesLight1 != null) { SSCUtils.SetHDLightIntensity(hdLightType, servicesLight1, 0.4f); }
if (servicesLight2 != null) { SSCUtils.SetHDLightIntensity(hdLightType, servicesLight2, 0.4f); }
if (corridorLeftLight1 != null) { SSCUtils.SetHDLightIntensity(hdLightType, corridorLeftLight1, 0.2f); }
if (corridorRightLight1 != null) { SSCUtils.SetHDLightIntensity(hdLightType, corridorRightLight1, 0.2f); }
if (topLift1Light1 != null) { SSCUtils.SetHDLightIntensity(hdLightType, topLift1Light1, 0.1f); }
if (topLift1Light2 != null) { SSCUtils.SetHDLightIntensity(hdLightType, topLift1Light2, 0.1f); }
}
#if UNITY_EDITOR
else { Debug.LogWarning("ERROR: TechDemo3 could not configure lights for High Definition Render Pipeline"); }
#endif
}
else
{
// URP and built-in lighting
if (bottomLiftLight1 != null) { bottomLiftLight1.intensity = 0.7f; }
if (lift1CallLight != null)
{
// When the point light is very close to the lift call control, the colour
// is washed out on URP and HDRP. Making the intensity of the light very low, fixes this.
lift1CallLight.intensity = isURP ? 0.01f : 0.7f;
}
}
SetLightShadows(bottomLiftLight1, isLowQuality, isMediumQuality, isHighQuality);
SetLightShadows(entranceLight1, isLowQuality, isMediumQuality, isHighQuality);
SetLightShadows(entranceLight2, isLowQuality, isMediumQuality, isHighQuality);
SetLightShadows(servicesLight1, isLowQuality, isMediumQuality, isHighQuality);
SetLightShadows(servicesLight2, isLowQuality, isMediumQuality, isHighQuality);
SetLightShadows(corridorLeftLight1, isLowQuality, isMediumQuality, isHighQuality);
SetLightShadows(corridorRightLight1, isLowQuality, isMediumQuality, isHighQuality);
SetLightShadows(topLift1Light1, isLowQuality, isMediumQuality, isHighQuality);
SetLightShadows(topLift1Light2, isLowQuality, isMediumQuality, isHighQuality);
SetLightImportance(shuttleCockpitLight, true);
SetLightImportance(shuttleCargoBayLight, true);
SetLightImportance(entranceLight1, false);
SetLightImportance(entranceLight2, false);
SetLightImportance(servicesLight1, false);
SetLightImportance(servicesLight2, false);
SetLightImportance(bottomLiftLight1, false);
SetLightImportance(corridorLeftLight1, false);
SetLightImportance(corridorRightLight1, false);
SetLightImportance(topLift1Light1, false);
SetLightImportance(topLift1Light2, false);
SetLightImportance(hawkCockpitLight, false);
// Turn hawk cockpit light off at the start of the game
LightingHawkCockpit(false);
}
// When entering the space port, adjust the lighting
private void LightingEnterSpaceport()
{
SetLightImportance(shuttleCargoBayLight, false);
SetLightImportance(entranceLight2, true);
if (gameQuality == GameQuality.Low)
{
// We really need the second point light, even on Low.
SetLightImportance(servicesLight1, true);
}
else
{
// Make sure the wall poster behind the assistance is well lit.
SetLightImportance(servicesLight1, true);
}
}
/// <summary>
/// Called when the player exits the outer doors at the top of lift1
/// and begins walking across the top deck toward the Hawk.
/// </summary>
private void LightingExitOuterDoors()
{
// Completely turn off unneeded lighting
if (lift1CallLight != null) { lift1CallLight.enabled = false; }
if (entranceLight1 != null) { entranceLight1.enabled = false; }
if (entranceLight2 != null) { entranceLight2.enabled = false; }
if (servicesLight1 != null) { servicesLight1.enabled = false; }
if (bottomLiftLight1 != null) { bottomLiftLight1.enabled = false; }
SetLightImportance(servicesLight2, false);
SetLightImportance(corridorLeftLight1, false);
SetLightImportance(corridorRightLight1, false);
SetLightImportance(topLift1Light2, false);
LightingHawkCockpit(true);
}
/// <summary>
/// Called from SSCDoor2_Lift1_Access SSCDoorAnimator onOpening event
/// </summary>
public void LightingExitServices()
{
SetLightImportance(servicesLight1, false);
SetLightImportance(entranceLight2, false);
SetLightImportance(bottomLiftLight1, true);
// Highlight the lift1 control panel
if (lift1CallLight != null) { lift1CallLight.enabled = true; }
}
/// <summary>
/// Enable or disable the cockpit light in the player Hawk ship on the top deck
/// </summary>
/// <param name="isEnabled"></param>
private void LightingHawkCockpit(bool isEnabled)
{
if (hawkCockpitLight != null)
{
hawkCockpitLight.enabled = isEnabled;
if (isEnabled)
{
SetLightImportance(hawkCockpitLight, gameQuality != GameQuality.Low);
}
}
}
/// <summary>
/// Set the importance of a light. In Unity 2019.4 with realtime point lights, Unity often
/// seems to pick the wrong "important" light, and our signage is often not illuminated.
/// </summary>
/// <param name="light"></param>
/// <param name="isImportant"></param>
private void SetLightImportance(Light light, bool isImportant, bool isAuto = false)
{
if (light != null)
{
light.renderMode = isAuto ? LightRenderMode.Auto : isImportant ? LightRenderMode.ForcePixel : LightRenderMode.ForceVertex;
}
}
/// <summary>
/// Configure the light based on the game quality level
/// </summary>
/// <param name="light"></param>
/// <param name="isLow"></param>
/// <param name="isMedium"></param>
/// <param name="isHigh"></param>
private void SetLightShadows(Light light, bool isLow, bool isMedium, bool isHigh)
{
if (light != null)
{
light.shadows = isLow ? LightShadows.None : isMedium ? LightShadows.Hard : LightShadows.Soft;
light.shadowStrength = 0.15f;
}
}
#endregion
#region Private Methods - Player ships
/// <summary>
/// Configure the player ships (Shuttle and the Hawk) for the mini-game.
/// Assume player Hawk and shuttle have InitialiseOnAwake already set in the editor.
/// </summary>
private void ConfigurePlayerShips()
{
// Get some references
shuttlePlayerInputModule = playerShuttle.GetComponent<PlayerInputModule>();
hawkPlayerInputModule = playerHawk.GetComponent<PlayerInputModule>();
hawkPlayerAutoTargetingModule = playerHawk.GetComponent<AutoTargetingModule>();
changeShipCameraView = shipCamera.GetComponent<SampleChangeCameraView>();
#if UNITY_EDITOR
if (shuttlePlayerInputModule == null)
{
Debug.LogWarning("ERROR: TechDemo3 could not find PlayerInputModule component on player ship: " + playerShuttle.name);
}
if (hawkPlayerAutoTargetingModule == null)
{
Debug.LogWarning("ERROR: TechDemo3 could not find AutoTargetingModule component on player ship: " + playerHawk.name);
}
if (hawkPlayerInputModule == null)
{
Debug.LogWarning("ERROR: TechDemo3 could not find PlayerInputModule component on player ship: " + playerHawk.name);
}
if (changeShipCameraView == null)
{
Debug.LogWarning("ERROR: TechDemo3 could not find SampleChangeCameraView component on " + shipCamera.name);
}
#endif
shipDocking = playerShuttle.GetShipDocking(true);
if (shipDocking != null)
{
shipDocking.callbackOnStateChange = OnDockingStateChanged;
}
#if UNITY_EDITOR
else
{
Debug.LogWarning("ERROR: TechDemo3 could not find ShipDocking component on shuttle ship");
}
#endif
// Identify the player ships so we don't get attacked by friendly ships
playerHawk.shipInstance.factionId = friendlyFactionID;
playerShuttle.shipInstance.factionId = friendlyFactionID;
playerHawk.shipInstance.squadronId = playerSquadronID;
// To begin with, both ships shouldn't be visible to (enemy) radar.
playerHawk.DisableRadar();
playerShuttle.DisableRadar();
// This enables us to turn off the Heads up display when the Hawk is destroyed
// We could also do things like update scores or end the mission etc.
playerHawk.callbackOnDestroy = OnPlayerDestroyed;
// This enables us to turn the HUD on again after the player Hawk ship has been respawned
playerHawk.callbackOnRespawn = OnPlayerRespawnCallback;
// Prevent the ship being moved by flying debris
playerHawk.ShipRigidbody.constraints = RigidbodyConstraints.FreezeAll;
}
/// <summary>
/// Called from HawkCockpitEntry()
/// </summary>
private void HawkPrepareForTakeOff()
{
if (playerHawk != null)
{
playerHawk.ShipRigidbody.constraints = RigidbodyConstraints.None;
playerHawk.EnableShip(true, true);
float delayTakeoff = 1f;
// Flicker and turn on the SSC HUD
if (shipDisplayModule != null)
{
shipDisplayModule.lockDisplayReticleToCursor = false;
// Switch to the cross-hairs reticle
int guidHashReticle = shipDisplayModule.GetDisplayReticleGuidHash(0);
shipDisplayModule.ChangeDisplayReticle(guidHashReticle);
// Reset the reticle back to the centre of the screen
shipDisplayModule.SetDisplayReticleOffset(Vector2.zero);
shipDisplayModule.HideDisplayMessages();
shipDisplayModule.ShowOverlay();
shipDisplayModule.ShowHeading();
shipDisplayModule.ShowAttitude();
shipDisplayModule.FlickerOn(shipDisplayModule.flickerDefaultDuration);
// Delay takeoff for the duration that the HUD is flickering
delayTakeoff = shipDisplayModule.flickerDefaultDuration;
// TODO I've modified this
//shipDisplayModule.HideCursor();
shipDisplayModule.ShowCursor();
}
Invoke("HawkReadyForTakeOff", delayTakeoff);
}
}
/// <summary>
/// Permit the player to control the Hawk and engage the enemy ships
/// </summary>
private void HawkReadyForTakeOff()
{
if (playerHawk != null && hawkPlayerInputModule != null)
{
// Enable Hawk Input and permit to take off
hawkPlayerInputModule.EnableInput();
if (shipDisplayModule != null)
{
shipDisplayModule.sourceShip = playerHawk;
shipDisplayModule.ShowDisplayReticle();
shipDisplayModule.ShowAirSpeed();
shipDisplayModule.ShowAltitude();
// Only show Hawk metric on HUD if Medium or High quality UNLESS overridden in inspector
// See also SetGameQuality()
isUpdateHawkHUDMetrics = hawkFullHUD || gameQuality != GameQuality.Low;
if (isUpdateHawkHUDMetrics)
{
shipDisplayModule.ShowDisplayGauge(fuelLevelGauge);
shipDisplayModule.ShowDisplayGauge(heatLevelGauge);
shipDisplayModule.ShowDisplayGauge(healthGauge);
if (gameQuality != GameQuality.Low)
{
shipDisplayModule.ShowDisplayGauge(missilesGauge);
shipDisplayModule.ShowDisplayGauge(shieldsGauge);
shipDisplayModule.ShowDisplayGauge(launchGauge);
shipDisplayModule.ShowDisplayGauge(gearGauge);
shipDisplayModule.ShowDisplayGauge(enemyGauge);
}
}
}
// Apply some up and forward thrust to help the player take off
playerHawk.shipInstance.AddBoost(new Vector3(0f,1f,0.1f), 300000f, 5f);
// Raise landing gear
if (hawkPlayerAutoTargetingModule != null) { hawkPlayerAutoTargetingModule.Initialise(); }
// Give the player a chance to take off, then make them visible to enemy radar
Invoke("HawkDelayedRadar", 5f);
}
}
/// <summary>
/// Make the player ship visible to radar and vulnerable to enemy attack.
/// Make the player ship vincible to damage.
/// </summary>
private void HawkDelayedRadar()
{
playerHawk.shipInstance.MakeShipVincible();
//playerHawk.shipInstance.mainDamageRegion.shieldingDamageThreshold = 10f;
// Make the player visible to radar and vulnerable to enemy attack
playerHawk.EnableRadar();
if (sscRadar != null) { sscRadar.ShowUI(); }
}
#endregion
#region Private 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.
/// Invoked from Start() when scene loads.
/// </summary>
/// <returns></returns>
//private System.Collections.IEnumerator ShowStart()
private void ShowStart()
{
ShowMenu(false);
PauseGame();
// Configure ambient sound while in shuttle
ambientAudioSource.clip = shuttleAmbientClip;
ambientAudioSource.volume = 0.1f;
// Lower priority
ambientAudioSource.priority = 64;
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 Private Game Pause Methods
/// <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);
}
}
}
}
}
/// <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 in the scene
/// 5. Pause the Sticky3D player
/// 6. Pause the Sticky3D assistant
/// </summary>
private void PauseGame()
{
#if SCSM_S3D
if (s3dPlayer != null && !useWalkThru)
{
// If look is enabled, so must be the player character
isPlayerEnabledOnPause = s3dPlayer.IsLookEnabled;
if (isPlayerEnabledOnPause)
{
s3dPlayer.PauseCharacter();
}
}
if (s3dServiceAssistant != null)
{
s3dServiceAssistant.DisableCharacter();
}
#endif
// Assume the player ship is always enabled.
if (!attackStarted && playerShuttle != null && playerShuttle.IsInitialised)
{
playerShuttle.DisableShip(false);
if (useWalkThru) { shuttlePlayerInputModule.DisableInput(false); }
}
// If this is the first time paused, reserve enough capacity for all the AI Ships
int numFriendlyShips = friendlyShips == null ? 0 : friendlyShips.Count;
int numEnemyType1Ships = enemyType1Ships == null ? 0 : enemyType1Ships.Count;
int numEnemyType2Ships = enemyType2Ships == null ? 0 : enemyType2Ships.Count;
int numEnemyCapitalShips = enemyCapitalShips == null ? 0 : enemyCapitalShips.Count;
if (pausedAIShips == null) { pausedAIShips = new List<ShipAIInputModule>(numFriendlyShips + numEnemyType1Ships + numEnemyType2Ships + numEnemyCapitalShips); }
AddSquadronToPausedList(numFriendlyShips, friendlyShips);
AddSquadronToPausedList(numEnemyType1Ships, enemyType1Ships);
AddSquadronToPausedList(numEnemyType2Ships, enemyType2Ships);
AddSquadronToPausedList(numEnemyCapitalShips, enemyCapitalShips);
// Pause ambient audio
if (ambientAudioSource.clip != null && ambientAudioSource.isPlaying)
{
ambientAudioSource.Pause();
}
// 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(); }
// 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 in the scene
/// 4. Unpausing all AI squadrons
/// 5. Unpausing the camera
/// 6. Unpause the Sticky3D assistant
/// 7. Unpause the Sticky3D player
/// 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();
if (!attackStarted && playerShuttle != null && playerShuttle.IsInitialised)
{
playerShuttle.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
{
if (attackStarted)
{
shipControlModule.EnableShip(false, false);
// Is this a capital ship?
if (IsCapitalShip(shipControlModule.GetShipId))
{
shipControlModule.DisableShipMovement();
shipControlModule.ShipRigidbody.detectCollisions = true;
}
// Check if any AI ships are still docked in one of the hangers.
// Currently we don't really need this for TechDemo3.
if (shipControlModule.GetShipDocking(false).GetStateInt() == ShipDocking.dockedInt)
{
// Re-dock the ship after EnableShip(..) was called.
shipControlModule.GetShipDocking(false).SetState(ShipDocking.DockingState.Docked);
}
}
}
}
}
}
// Unpause game music (if any)
if (musicController != null) { musicController.ResumeMusic(); }
// Unpause ambient audio if required
// Probably should also check if still in shuttle...
if (ambientAudioSource.clip != null && !attackStarted)
{
ambientAudioSource.Play();
}
AudioListener.pause = false;
#if SCSM_S3D
if (s3dServiceAssistant != null) { s3dServiceAssistant.EnableCharacter(false); }
if (s3dPlayer != null && isPlayerEnabledOnPause) { s3dPlayer.EnableCharacter(false); }
#endif
if (useWalkThru && playerShuttle != null && playerShuttle.IsInitialised && !playerShuttle.shipInstance.Destroyed() && shuttlePlayerInputModule != null)
{
// Allow custom inputs only from the shuttle while in walk thru mode.
shuttlePlayerInputModule.EnableInput();
shuttlePlayerInputModule.DisableInput(true);
}
if (shipDisplayModule != null)
{
shipDisplayModule.ShowHUD();
shipDisplayModule.HideCursor();
}
if (numPauseAIShips > 0) { pausedAIShips.Clear(); }
isGamePaused = false;
}
#endregion
#region Private Walk Thru Member Methods
/// <summary>
/// Configure the walk through camera and turn it on or off.
/// There are 2 walk thru cameras. A "normal" Unity camera and
/// a SSC camera. The SSC camera is configured to follow a camera "ship"
/// which can follow a path etc.
/// </summary>
/// <param name="isEnabled"></param>
private void ConfigureWalkThrough(bool isEnabled)
{
/// Set up a camera walk through for the scene
if (walkThruCamera1 != null)
{
if (isEnabled)
{
if (cameraShip != null && walkThruCamera2 != null && sscManager != null)
{
walkThruCamera2.SetTarget(cameraShip);
cameraShipAI = cameraShip.GetShipAIInputModule(true);
walkThruPath1 = sscManager.GetPath(walkThruPath1Name);
walkThruPath2 = sscManager.GetPath(walkThruPath2Name);
walkThruPath3 = sscManager.GetPath(walkThruPath3Name);
walkThruPath4 = sscManager.GetPath(walkThruPath4Name);
walkThruPath5 = sscManager.GetPath(walkThruPath5Name);
walkThruPath6 = sscManager.GetPath(walkThruPath6Name);
#if UNITY_EDITOR
if (cameraShipAI == null) { Debug.LogWarning("TechDemo3 - " + cameraShip.name + " needs a Ship AI Input Module"); }
if (walkThruPath1 == null) { Debug.LogWarning("TechDemo3 - could not find a path in SSCManager called " + walkThruPath1Name); }
if (walkThruPath2 == null) { Debug.LogWarning("TechDemo3 - could not find a path in SSCManager called " + walkThruPath2Name); }
if (walkThruPath3 == null) { Debug.LogWarning("TechDemo3 - could not find a path in SSCManager called " + walkThruPath3Name); }
if (walkThruPath4 == null) { Debug.LogWarning("TechDemo3 - could not find a path in SSCManager called " + walkThruPath4Name); }
if (walkThruPath5 == null) { Debug.LogWarning("TechDemo3 - could not find a path in SSCManager called " + walkThruPath5Name); }
if (walkThruPath6 == null) { Debug.LogWarning("TechDemo3 - could not find a path in SSCManager called " + walkThruPath6Name); }
#endif
if (cameraShipAI != null) { cameraShipAI.Initialise(); }
// We want to move the path inside the shuttle during gameplay (ie make it dynamic), so we need to initialise it (for movement).
sscManager.InitialiseLocations(walkThruPath1, shuttleOriginalPos, Vector3.zero, Quaternion.identity, Vector3.zero, Vector3.zero);
cameraShip.DisableShip(true);
isWalkThruInitialised = cameraShipAI != null && walkThruPath1 != null && walkThruPath2 != null && walkThruPath3 != null &&
walkThruPath4 != null && walkThruPath5 != null && walkThruPath6 != null && cockPitEntryProximity != null;
}
// For walk thru, when the cameraShip is disabled inside the canopy door proximity sphere collider,
// it will close the Hawk canopy, which is not what we want.
if (hawkCanopyDoorProximity != null) { hawkCanopyDoorProximity.isCloseDoorsOnExit = false; }
// Set the start position of the camera
if (shuttlePilotChair != null)
{
// Move the camera to the pilot chair.
Vector3 newPosition = shuttlePilotChair.transform.position + (shuttlePilotChair.transform.up * 1.2f);
walkThruCamera1.transform.SetPositionAndRotation(newPosition, shuttlePilotChair.transform.rotation);
// In the shuttle while docking, we want the camera to be locked to the pilot chair.
walkThruCamera1.transform.SetParent(shuttlePilotChair.transform);
}
if (celestials != null) { celestials.camera1 = walkThruCamera1; celestials.RefreshCameras(); }
if (shipDisplayModule != null)
{
shipDisplayModule.SetCamera(walkThruCamera1);
shipDisplayModule.Initialise();
shipDisplayModule.HideCursor();
}
}
// Turn camera on/off
walkThruCamera1.enabled = isEnabled;
walkThruCamera1.gameObject.SetActive(isEnabled);
}
#if UNITY_EDITOR
else
{
Debug.LogWarning("ERROR TechDemo3 - could not find WalkThru Camera, check this component in the scene");
}
#endif
}
/// <summary>
/// Walk (fly) from the pilot seat to the shuttle side door
/// </summary>
private void WalkThruStartSection2()
{
if (isWalkThruInitialised)
{
Vector3 deltaPathPos = playerShuttle.transform.position - shuttleOriginalPos;
Quaternion deltaPathRot = playerShuttle.transform.rotation * Quaternion.Inverse(shuttleOriginalRot);
// Move the path locations to match the new position of the shuttle. Although the shuttle is not
// a mothership (with a docking station module etc), we can use the same API to move the locations.
sscManager.MoveLocations(walkThruPath1, Time.time, playerShuttle.transform.position, deltaPathPos, deltaPathRot, Vector3.zero, Vector3.zero);
cameraShip.EnableShip(false, true);
LocationData locationData = sscManager.GetFirstLocation(walkThruPath1);
if (locationData != null)
{
// Move our AI-driven "ship" to the start of the path inside the shuttle
cameraShip.TelePort(locationData.position, walkThruCamera1.transform.rotation, true);
// Move the camera that will be following our camera "ship".
walkThruCamera2.MoveTo(walkThruCamera1.transform.position, walkThruCamera1.transform.rotation.eulerAngles);
}
cameraShipAI.SetState(AIState.moveToStateID);
cameraShipAI.AssignTargetPath(walkThruPath1);
cameraShipAI.callbackCompletedStateAction = WalkThruFinishedPath1;
walkThruSectionNumber = 2;
walkThruCamera1.enabled = false;
walkThruCamera1.gameObject.SetActive(false);
// Move the non-ship camera back under the original gameobject so it isn't attached to the shuttle
walkThruCamera1.transform.SetParent(walkThruCamera2.transform.parent);
walkThruCamera2.StartCamera();
walkThruCamera2.EnableCamera();
// Tell the stars background we've changed cameras
if (celestials != null) { celestials.camera1 = walkThruCamera2.GetCamera1; }
// Tell the HUD we've changed cameras
if (shipDisplayModule != null)
{
shipDisplayModule.SetCamera(walkThruCamera2.GetCamera1);
}
}
}
/// <summary>
/// Open the shuttle side door
/// Start "walking" towards the services room
/// </summary>
private void WalkThruStartSection3()
{
SSCDoorControl.SelectOpen(sscDoorControlShuttleSideDoors);
StartSpacePortAmbientFX();
LightingEnterSpaceport();
if (shipDisplayModule != null)
{
shipDisplayModule.HideDisplayGauge(dockedIndicator);
shipDisplayModule.HideDisplayMessage(exitShuttleMessage);
shipDisplayModule.ShowDisplayMessage(findServiceDeskMessage);
}
cameraShipAI.maxSpeed = 1.3f;
cameraShipAI.SetState(AIState.moveToStateID);
cameraShipAI.AssignTargetPath(walkThruPath2);
cameraShipAI.callbackCompletedStateAction = WalkThruFinishedPath2;
walkThruSectionNumber = 3;
}
/// <summary>
/// We have reached the services room
/// </summary>
private void WalkThruStartSection4()
{
cameraShipAI.SetState(AIState.moveToStateID);
cameraShipAI.AssignTargetPath(walkThruPath3);
cameraShipAI.callbackCompletedStateAction = WalkThruFinishedPath3;
walkThruSectionNumber = 4;
}
/// <summary>
/// We have reached the services assistant area
/// </summary>
private void WalkThruStartSection5()
{
cameraShipAI.maxSpeed = 1.3f;
cameraShipAI.SetState(AIState.moveToStateID);
cameraShipAI.AssignTargetPath(walkThruPath4);
cameraShipAI.callbackCompletedStateAction = WalkThruFinishedPath4;
walkThruSectionNumber = 5;
}
/// <summary>
/// We have reached the call lift area
/// </summary>
private void WalkThruStartSection6()
{
// Call the lift
if (lift1ControlChangeMaterial != null) { lift1ControlChangeMaterial.GetGroup1Material(1); }
if (lift1MovingPlatform != null) { lift1MovingPlatform.CallToStartPosition(true); }
walkThruSectionNumber = 6;
}
/// <summary>
/// The lift has arrived and now the "player" needs to take the lift
/// to the upper deck.
/// </summary>
private void WalkThruStartSection7()
{
cameraShipAI.maxSpeed = 1.3f;
cameraShipAI.SetState(AIState.moveToStateID);
cameraShipAI.AssignTargetPath(walkThruPath5);
cameraShipAI.callbackCompletedStateAction = WalkThruFinishedPath5;
walkThruSectionNumber = 7;
}
/// <summary>
/// Ride the lift to the upper deck.
/// </summary>
private void WalkThruStartSection8()
{
// Switch cameras
walkThruCamera1.transform.SetPositionAndRotation(walkThruCamera2.transform.position, walkThruCamera2.transform.rotation);
cameraShip.DisableShip(true);
walkThruCamera2.StopCamera();
// parent camera to lift
walkThruCamera1.transform.SetParent(lift1MovingPlatform.transform);
// Start the camera rendering
walkThruCamera1.enabled = true;
walkThruCamera1.gameObject.SetActive(true);
if (shipDisplayModule != null) { shipDisplayModule.SetCamera(walkThruCamera1); }
if (celestials != null) { celestials.camera1 = walkThruCamera1; }
walkThruSectionNumber = 8;
}
/// <summary>
/// Arrived at the top of the lift. This is called each time lift1 gets
/// to the top. It is configured in the editor for the lift moving platform (SSCLiftPlate1).
/// It needs to be public so it can be configured in the editor.
/// Head towards the Hawk ship.
/// See also StartBombingRuns() which makes the camera ship move more rapidly.
/// </summary>
public void WalkThruStartSection9()
{
if (useWalkThru && walkThruSectionNumber == 8)
{
LocationData locationData = sscManager.GetFirstLocation(walkThruPath6);
if (locationData != null)
{
cameraShip.EnableShip(false, true);
// Move our AI-driven "ship" to the start of the path inside the shuttle
cameraShip.TelePort(locationData.position, walkThruCamera1.transform.rotation, true);
// Move the camera that will be following our camera "ship".
walkThruCamera2.MoveTo(walkThruCamera1.transform.position, walkThruCamera1.transform.rotation.eulerAngles);
// Move the camera slowly between the top of lift and the outer doors.
// Give the outer doors enough time to open, especially on slower hardware - which can present path following issues
cameraShipAI.maxSpeed = 0.5f;
cameraShipAI.SetState(AIState.moveToStateID);
cameraShipAI.AssignTargetPath(walkThruPath6);
cameraShipAI.callbackCompletedStateAction = WalkThruFinishedPath6;
walkThruSectionNumber = 9;
walkThruCamera1.enabled = false;
walkThruCamera1.gameObject.SetActive(false);
// Move the non-ship camera back under the original gameobject so it isn't attached to the shuttle
walkThruCamera1.transform.SetParent(walkThruCamera2.transform.parent);
walkThruCamera2.StartCamera();
walkThruCamera2.EnableCamera();
// Tell the stars background we've changed cameras
if (celestials != null) { celestials.camera1 = walkThruCamera2.GetCamera1; }
// Tell the HUD we've changed cameras
if (shipDisplayModule != null) { shipDisplayModule.SetCamera(walkThruCamera2.GetCamera1); }
}
}
}
/// <summary>
/// At the ladder of the Hawk
/// </summary>
private void WalkThruStartSection10()
{
// Switch cameras
walkThruCamera1.transform.SetPositionAndRotation(walkThruCamera2.transform.position, walkThruCamera2.transform.rotation);
cameraShip.DisableShip(true);
walkThruCamera2.StopCamera();
// Start the camera rendering
walkThruCamera1.enabled = true;
walkThruCamera1.gameObject.SetActive(true);
if (shipDisplayModule != null)
{
shipDisplayModule.SetCamera(walkThruCamera1);
shipDisplayModule.HideCursor();
}
if (celestials != null) { celestials.camera1 = walkThruCamera1; }
walkThruSectionNumber = 10;
}
/// <summary>
/// "Player" is seated in shuttle pilot seat, waiting for docking
/// to be completed.
/// </summary>
private void WalkThruUpdateSection1()
{
Vector3 lookDirection = (assistantChair.transform.position - walkThruCamera1.transform.position).normalized;
// Keep the camera relatively flat or aligned with the shuttle
lookDirection = Vector3.ProjectOnPlane(lookDirection, playerShuttle.shipInstance.TransformUp);
Quaternion lookRot = Quaternion.LookRotation(lookDirection, playerShuttle.shipInstance.TransformUp);
walkThruCamera1.transform.rotation = Quaternion.RotateTowards(walkThruCamera1.transform.rotation, lookRot, 0.2f);
}
/// <summary>
/// Rotate the "player" as they travel up the lift
/// </summary>
private void WalkThruUpdateSection8()
{
Quaternion lookRot = Quaternion.LookRotation(Vector3.back, Vector3.up);
walkThruCamera1.transform.rotation = Quaternion.RotateTowards(walkThruCamera1.transform.rotation, lookRot, 0.5f);
}
/// <summary>
/// Climb the ladder into the Hawk.
/// Check if the camera is near the Hawk cockpit and ready for entry.
/// </summary>
private void WalkThruUpdateSection10()
{
Vector3 _camPosition = walkThruCamera1.transform.position;
// Have we reached the top of the ladder?
if (_camPosition.y > 35.2f)
{
// Move toward the cockpit at the top of the ladder.
_camPosition += walkThruCamera1.transform.forward * Time.deltaTime * 1.5f;
}
else { _camPosition.y += Time.deltaTime * 0.8f; }
walkThruCamera1.transform.position = _camPosition;
// Face towards the cockpit entry point
Quaternion lookRot = Quaternion.LookRotation(ladder1.transform.forward * -1f, Vector3.up);
walkThruCamera1.transform.rotation = Quaternion.RotateTowards(walkThruCamera1.transform.rotation, lookRot, 0.3f);
// Check if the camera is near the Hawk cockpit and ready for entry.
// At this point we're not using a moving rigidbody so check if the camera is within the trigger area.
if (cockPitEntryProximity.ProximityRegion.Contains(_camPosition))
{
HawkCockpitEntry();
cockPitEntryProximity.gameObject.SetActive(false);
}
}
#endregion
#region Public Walk Thru Member Methods
public void WalkThruFinishedPath1(ShipAIInputModule shipAIInputModule)
{
cameraShipAI.SetState(AIState.idleStateID);
cameraShipAI.callbackCompletedStateAction = null;
WalkThruStartSection3();
}
public void WalkThruFinishedPath2(ShipAIInputModule shipAIInputModule)
{
cameraShipAI.SetState(AIState.idleStateID);
cameraShipAI.callbackCompletedStateAction = null;
WalkThruStartSection4();
}
public void WalkThruFinishedPath3(ShipAIInputModule shipAIInputModule)
{
cameraShipAI.SetState(AIState.idleStateID);
cameraShipAI.callbackCompletedStateAction = null;
WalkThruStartSection5();
}
public void WalkThruFinishedPath4(ShipAIInputModule shipAIInputModule)
{
cameraShipAI.SetState(AIState.idleStateID);
cameraShipAI.callbackCompletedStateAction = null;
WalkThruStartSection6();
}
public void WalkThruFinishedPath5(ShipAIInputModule shipAIInputModule)
{
cameraShipAI.SetState(AIState.idleStateID);
cameraShipAI.callbackCompletedStateAction = null;
WalkThruStartSection8();
}
public void WalkThruFinishedPath6(ShipAIInputModule shipAIInputModule)
{
cameraShipAI.SetState(AIState.idleStateID);
cameraShipAI.callbackCompletedStateAction = null;
WalkThruStartSection10();
}
#endregion
#region Public General Member Methods
/// <summary>
/// When Sticky3D is in the project, the NPC at the Services desk,
/// signals that the player should approach the desk.
/// See also StopLookingAtServiceAssistant().
/// </summary>
public void CallPlayerToServicesDesk()
{
#if SCSM_S3D
if (isInitialised && s3dServiceAssistant != null && s3dServiceAssistant.IsInitialised)
{
// Configure the Waving animation for the NPC behind the services desk
if (defaultInteractAnimation != null && servicesNPCWaveAnimation != null)
{
if (!s3dServiceAssistant.IsHeadIKEnabled) { s3dServiceAssistant.EnableHeadIK(true); }
// Let the NPC look at the player when the NPC is seated and movement is disabled.
s3dServiceAssistant.headIKWhenMovementDisabled = true;
s3dServiceAssistant.headIKMoveMaxSpeed = 0.7f;
// Tell the NPC to look at the player
if (useWalkThru && isWalkThruInitialised)
{
s3dServiceAssistant.SetHeadIKTarget(walkThruCamera2.transform, Vector3.zero, true, false);
}
else
{
s3dServiceAssistant.SetHeadIKTarget(s3dPlayer.transform, Vector3.zero, true, true);
}
s3dServiceAssistant.ReplaceAnimationClipNoRef(defaultInteractAnimation, servicesNPCWaveAnimation);
s3dServiceAssistant.PlayAnimationState(servicesAssistantInteractStateId, -1);
// Get the player to look towards the services assistant with a slight delay
//Invoke("PlayerLookAtServicesAssistant", 1f);
}
}
#endif
}
/// <summary>
/// This is called automatically just after the scene starts.
/// </summary>
public void DelayedUnlockDoors()
{
int numDoorsToUnlock = delayedUnlockDoors == null ? 0 : delayedUnlockDoors.Length;
for (int drIdx = 0; drIdx < numDoorsToUnlock; drIdx++)
{
SSCDoorAnimator doorAnimator = delayedUnlockDoors[drIdx];
if (doorAnimator != null) { doorAnimator.UnlockDoors(); }
}
}
/// <summary>
/// Commence the docking procedure.
/// You could call this code or added these individual items from this method
/// to events on a custom player input in the editor.
/// See also UndockShuttle().
/// </summary>
public void DockShuttle()
{
shuttlePlayerInputModule.EnableAIDocking();
shuttlePlayerInputModule.DisableInput(false);
shipDisplayModule.HideDisplayMessage(startDockingMessage);
if (useWalkThru)
{
shipDisplayModule.ShowDisplayMessage(dockingInProgressMessage);
if (dockingButton != null) { Destroy(dockingButton); }
walkThruSectionNumber = isWalkThruInitialised ? 1 : 0;
}
else
{
shipDisplayModule.ShowDisplayMessage(exitShuttleMessage);
}
UpdateGauges();
}
/// <summary>
/// Commence the undocking procedure.
/// You could call this code or added these individual items from this method
/// to events on a custom player input in the editor.
/// See also DockShuttle().
/// </summary>
public void UndockShuttle()
{
shuttlePlayerInputModule.EnableAIUndocking();
shuttlePlayerInputModule.DisableInput(false);
UpdateGauges();
}
/// <summary>
/// Display a message to the user to get to their ship on the upper deck.
/// Remind them if they have forgotten to put on their helmet (Stick3D only).
/// This is triggered by attempting to go through the outer doors at the top
/// of lift1.
/// </summary>
public void GetToShipMessage()
{
shipDisplayModule.HideDisplayMessage(getToDeckMessage);
#if SCSM_S3D
if (useWalkThru || s3dPlayerParts.IsPartEnabledByIndex(2))
{
shipDisplayModule.HideDisplayMessage(getToHawkMessageWithHelmet);
shipDisplayModule.ShowDisplayMessage(getToHawkMessage);
}
else
{
shipDisplayModule.HideDisplayMessage(getToHawkMessage);
shipDisplayModule.ShowDisplayMessage(getToHawkMessageWithHelmet);
}
#else
shipDisplayModule.ShowDisplayMessage(getToHawkMessage);
#endif
}
/// <summary>
/// Switch the character to the Hawk ship
/// </summary>
public void HawkCockpitEntry()
{
if (isInitialised && playerHawk != null && shipCamera != null)
{
// If on, turn off the HUD
if (shipDisplayModule != null)
{
shipDisplayModule.HideDisplayGauges();
// Ensure the HUD turns off the instance cockpit entry begins
shipDisplayModule.isHideHUDWithFlicker = false;
shipDisplayModule.HideCursor();
shipDisplayModule.HideHUD();
}
// Set ship camera to Hawk
shipCamera.SetTarget(playerHawk);
// Set ship camera to cockpit view
changeShipCameraView.SetCurrentView(0);
// Move the ladder away from the hawk to avoid issues with the convex ship collider
if (ladder1 != null) { ladder1.transform.position += ladder1.transform.rotation * (Vector3.forward * 3f); }
// Switch Hawk from static to mesh convex colliders for flight
if (hawkStaticColliders != null && hawkMeshCollider != null)
{
// Turn off the static primitive colliders which are used when stationary on the deck of the space port
if (hawkStaticColliders.activeSelf) { hawkStaticColliders.SetActive(false); }
// Turn off the collider on the Hawk canopy and adjust material transparency
if (hawkCanopyCollider != null)
{
MeshRenderer canopyRM = hawkCanopyCollider.GetComponent<MeshRenderer>();
if (canopyRM != null)
{
// set the non-share material instance on the canopy
// NOTE: We may need to switch it back when user selects one of the
// 2 outside camera views while in the Hawk otherwise it will look
// like there is no canopy.
canopyRM.material.color = Color.clear;
}
hawkCanopyCollider.enabled = false;
}
// Turn on the convex mesh collider for the hawk
// This assume there is no active character in the cockpit seat.
if (!hawkMeshCollider.enabled)
{
hawkMeshCollider.convex = true;
hawkMeshCollider.enabled = true;
playerHawk.ShipRigidbody.ResetCenterOfMass();
playerHawk.ReinitialiseMass();
}
}
if (useWalkThru)
{
walkThruSectionNumber = 11;
// Turn off the walk thru camera currently in use
walkThruCamera1.enabled = false;
walkThruCamera1.gameObject.SetActive(false);
}
else
{
// Disable Sticky3D character
#if SCSM_S3D
if (s3dPlayer != null )
{
// Gracefully disable the character
s3dPlayer.DisableCharacter(true);
// Turn off the character
s3dPlayer.gameObject.SetActive(false);
}
#endif
}
// Enable Hawk Camera to move
shipCamera.EnableCamera();
// Get the stars to rotate with the ship camera rather than the Sticky3D character
if (celestials != null && celestials.IsInitialised)
{
celestials.camera1 = shipCamera.GetCamera1;
celestials.RefreshCameras();
}
// Assign the Hawk camera to the HUD so that Targets get shown
// in the correct position on the screen.
if (shipDisplayModule != null)
{
shipDisplayModule.SetCamera(shipCamera.GetCamera1);
}
// Render the Hawk Camera
shipCamera.StartCamera();
// Lower the Hawk canopy
if (hawkCanopyDoor != null) { hawkCanopyDoor.CloseDoors(); }
isInHawkCockpit = true;
if (beaconLight != null) { beaconLight.FadeOutAudio(2f); }
// Wait a few seconds while the canopy closes
Invoke("HawkPrepareForTakeOff", 3f);
}
}
/// <summary>
/// The player has passed through the outer doors on the top deck.
/// Called from SSC Proximity on the TriggerBombingRuns gameobject.
/// </summary>
public void PastOuterDoors()
{
// Once we're through the outer doors, "run" across the top deck toward the Hawk
if (!bombingRunsStarted)
{
if (useWalkThru && isWalkThruInitialised)
{
cameraShipAI.maxSpeed = 3f;
walkThruCamera2.moveSpeed = 6f;
}
// Disable Sticky3D assistant
// No need to have them animating etc when out of view
#if SCSM_S3D
if (s3dServiceAssistant != null )
{
// Gracefully disable the character
s3dServiceAssistant.DisableCharacter(true);
// Turn off the character
s3dServiceAssistant.gameObject.SetActive(false);
}
#endif
LightingExitOuterDoors();
StartCoroutine(FadeUpDirectLight());
}
}
/// <summary>
/// Shortly after the scene first renders we want to display the correct camera.
/// This is called automatically.
/// </summary>
public void SetSceneCamera()
{
if (celestials.camera1 == null) { celestials.camera1 = Camera.main; }
celestials.RefreshCameras();
}
/// <summary>
/// Start the attack on the Spaceport.
/// See the SSCProximity component on TriggerAttack gameobject in the scene.
/// </summary>
public void StartAttack ()
{
if (!attackStarted)
{
// Stop current ambient audio to improve performance
StopAmbientAudio();
// Turn off lights in the shuttle
if (shuttleCockpitLight != null) { shuttleCockpitLight.enabled = false; }
if (shuttleCargoBayLight != null) { shuttleCargoBayLight.enabled = false; }
// Rotating light on top deck
if (beaconLight != null) { beaconLight.TurnOn(true); }
// Strobe lights at top of each lift
if (liftStrobeLight1 != null) { liftStrobeLight1.TurnOn(); }
if (liftStrobeLight2 != null) { liftStrobeLight2.TurnOn(); }
// Unlock lift doors
if (lift1BottomDoors != null) { lift1BottomDoors.UnlockDoors(); }
if (lift2BottomDoors != null) { lift2BottomDoors.UnlockDoors(); }
// Lock outer bay door where shuttle docked to prevent re-entry to shuttle when attack starts
if (outerDockingBayDoor1 != null) { outerDockingBayDoor1.LockDoors(); }
if (outerDockingBayDoorProximity != null) { outerDockingBayDoorProximity.isUnlockDoorsOnEntry = false; }
// Update lift door control panels after the doors have been unlocked
if (sscDoorControlLift1Doors != null) { sscDoorControlLift1Doors.UpdateLockStatus(); }
// The lift barrier emissive material flashing can bleed light through the services
// room doors. It also doesn't need to be on all the time. Start On is not enabled in the editor.
if (lift1Barrier != null) { lift1Barrier.TurnGroupOn(1); }
#region Explosion and camera shake on Sticky3D
Vector3 explosionPosition = Vector3.zero;
if (useWalkThru && isWalkThruInitialised)
{
walkThruCamera2.ShakeCamera(0.5f, 0.2f);
// Set the explosion sound 1m infront of the camera "ship" for maximum effect
explosionPosition = cameraShip.shipInstance.TransformPosition + cameraShip.shipInstance.TransformForward;
}
else
{
#if SCSM_S3D
if (s3dPlayer != null)
{
s3dPlayer.ShakeCamera(0.5f, 0.2f);
// Set the explosion sound 1m infront of the character for maximum effect
explosionPosition = s3dPlayer.GetCurrentTop() + s3dPlayer.GetCurrentForward;
//s3dPlayer.headIKAdjustForVelocity = true;
}
#else
{
// Fallback position of explosion
if (lift1BottomDoors != null)
{
explosionPosition = lift1BottomDoors.transform.position + lift1BottomDoors.transform.forward;
}
}
#endif
}
// Ensure the guages are turned off
if (shipDisplayModule != null)
{
shipDisplayModule.HideDisplayGauges();
shipDisplayModule.HideDisplayMessages();
// Guide the player toward the lift so that they get to the upper deck of the space port
// COMMENTED OUT TO TEST COCKPIT ENTRY
if (getToDeckMessage != null) { shipDisplayModule.ShowDisplayMessage(getToDeckMessage); }
}
PlaySoundFX(attackExplosion, explosionPosition, 1f);
#endregion
#region Enable Ships
// Enable and set initial targets for the friendly ships
if (friendlyShipPrefab != null && numFriendlyShip > 0)
{
for (int i = 0; i < numFriendlyShip; i++)
{
ShipAIInputModule friendlyShip = friendlyShips[i];
if (friendlyShip != null)
{
ShipControlModule friendlyShipCM = friendlyShip.GetShipControlModule;
if (friendlyShipCM != null)
{
friendlyShipCM.EnableShip(true, true);
}
}
}
}
// Enable and set initial targets for the type 1 enemy ships
if (enemyShip1Prefab != null && numEnemyShip1 > 0)
{
for (int i = 0; i < numEnemyShip1; i++)
{
ShipAIInputModule enemyShip = enemyType1Ships[i];
if (enemyShip != null)
{
ShipControlModule enemyShipCM = enemyShip.GetShipControlModule;
if (enemyShipCM != null)
{
enemyShipCM.EnableShip(true, true);
}
}
}
}
// Enable and set initial targets for the type 2 enemy ships
if (enemyShip2Prefab != null && numEnemyShip2 > 0)
{
for (int i = 0; i < numEnemyShip2; i++)
{
ShipAIInputModule enemyShip = enemyType2Ships[i];
if (enemyShip != null)
{
ShipControlModule enemyShipCM = enemyShip.GetShipControlModule;
if (enemyShipCM != null)
{
enemyShipCM.EnableShip(true, true);
}
}
}
}
// Enable the enemy capital ships
numEnemyCapitalShips = enemyCapitalShips != null ? enemyCapitalShips.Count : 0;
if (enemyCapitalShipPrefab != null && numEnemyCapitalShips > 0)
{
for (int i = 0; i < numEnemyCapitalShips; i++)
{
ShipAIInputModule enemyShip = enemyCapitalShips[i];
if (enemyShip != null)
{
ShipControlModule enemyShipCM = enemyShip.GetShipControlModule;
if (enemyShipCM != null)
{
enemyShipCM.EnableShip(true, true);
enemyShipCM.DisableShipMovement();
enemyShipCM.ShipRigidbody.detectCollisions = true;
}
}
}
}
numEnemyRemaining = numEnemyShip1 + numEnemyShip2;
#endregion
#region Pair Ships
// Pair all friendly ships with an enemy ship
if (friendlyShipPrefab != null && numFriendlyShip > 0)
{
for (int i = 0; i < numFriendlyShip; i++)
{
ShipAIInputModule friendlyShip = friendlyShips[i];
if (friendlyShip != null)
{
ShipControlModule friendlyShipCM = friendlyShip.GetShipControlModule;
if (friendlyShipCM != null)
{
AttemptPairing(friendlyShip, friendlyShipCM);
}
}
}
}
// Set remaining enemy ships to follow waiting routines
if (enemyShip1Prefab != null && numEnemyShip1 > 0)
{
for (int i = 0; i < numEnemyShip1; i++)
{
ShipAIInputModule enemyShip = enemyType1Ships[i];
if (enemyShip != null)
{
ShipControlModule enemyShipCM = enemyShip.GetShipControlModule;
if (enemyShipCM != null)
{
int thisShipListIndex = availableEnemyShips.FindIndex(a => a == enemyShipCM.GetShipId);
if (thisShipListIndex != -1) { AttemptPairing(enemyShip, enemyShipCM); }
}
}
}
}
if (enemyShip2Prefab != null && numEnemyShip2 > 0)
{
for (int i = 0; i < numEnemyShip2; i++)
{
ShipAIInputModule enemyShip = enemyType2Ships[i];
if (enemyShip != null)
{
ShipControlModule enemyShipCM = enemyShip.GetShipControlModule;
if (enemyShipCM != null)
{
int thisShipListIndex = availableEnemyShips.FindIndex(a => a == enemyShipCM.GetShipId);
if (thisShipListIndex != -1) { AttemptPairing(enemyShip, enemyShipCM); }
}
}
}
}
#endregion
// Perform a delayed destruction of the shuttle,
// removing any chance of the player escaping
Invoke("DestroyShuttle", 2f);
attackStarted = true;
// Debug.Log("Pause editor...");
//UnityEditor.EditorApplication.isPaused = true;
}
}
/// <summary>
/// Start the bombing runs on the fuel drums.
/// This gets called from the TriggerBombingRuns SSCProximity component
/// at the top of the Lift1 outer doors.
/// </summary>
public void StartBombingRuns ()
{
if (!bombingRunsStarted)
{
bombingRunsStarted = true;
// We no longer need the lift barrier flashing on/off
if (lift1Barrier != null) { lift1Barrier.TurnGroupOff(1); }
}
}
/// <summary>
/// When the player opens the shuttle doors start playing the ambient background sounds.
/// See also StopAmbientAudio()
/// </summary>
public void StartSpacePortAmbientFX()
{
if (spacePortAmbientClip != null && !ambientAudioSource.isPlaying)
{
ambientAudioSource.clip = spacePortAmbientClip;
ambientAudioSource.volume = 0.3f;
ambientAudioSource.Play();
}
}
/// <summary>
/// Stop the assistant looking at the player. This is called from the Lift1 access doors when
/// the player opens the doors that lead from the services area to the bottom of Lift1.
/// </summary>
public void StopAssistantLookingAtPlayer()
{
#if SCSM_S3D
if (isInitialised && s3dServiceAssistant != null && s3dServiceAssistant.IsInitialised)
{
// Stop assistant looking at Player
s3dServiceAssistant.DisableHeadIK(true);
}
#endif
}
/// <summary>
/// This gets triggered when the player has chatted with the service
/// assistant and is now walking away (hopefully toward to the Lift Deck door control panel).
/// See the SSCProximity component on TriggerAttack gameobject in the scene.
/// </summary>
public void StopLookingAtServiceAssistant()
{
#if SCSM_S3D
if (isInitialised && s3dServiceAssistant != null && s3dServiceAssistant.IsInitialised)
{
// Stop assistant looking at Player
s3dServiceAssistant.DisableHeadIK(true);
// Leave Head IK enabled, just stop player looking at anything right now
if (!useWalkThru && s3dPlayer != null) { s3dPlayer.SetHeadIKTarget(null); }
}
#endif
}
/// <summary>
/// Turn off the Lift Call button in the scene.
/// For walk thru, also checks if the "player" is waiting for the lift.
/// </summary>
public void TurnOffLiftCallButton()
{
if (lift1ControlChangeMaterial != null)
{
#if SCSM_S3D
if (!useWalkThru && lift1ControlInteractive != null && s3dPlayer != null)
{
// The lift control is a Selectable S3D Interactive-enabled object. The selected
// state is tracked by individual S3D characters rather than the object itself.
// Therefore we need to tell the S3D character than it has been unselected.
// If the button was not selected, this will have no effect.
s3dPlayer.UnselectInteractive(lift1ControlInteractive);
}
else
{
// Turn off the control display. i.e. change the material to the off material
lift1ControlChangeMaterial.GetGroup1Material(0);
}
#else
// Turn off the control display. i.e. change the material to the off material
lift1ControlChangeMaterial.GetGroup1Material(0);
#endif
if (useWalkThru && isWalkThruInitialised && walkThruSectionNumber == 6)
{
WalkThruStartSection7();
}
}
}
/// <summary>
/// Update the status of HUD gauges when on the Shuttle or in the Hawk
/// </summary>
public void UpdateGauges()
{
if (isInHawkCockpit && isUpdateHawkHUDMetrics)
{
// Assume using the Central fuel level for the whole ship
shipDisplayModule.SetDisplayGaugeValue(fuelLevelGauge, playerHawk.shipInstance.GetFuelLevel() / 100f);
// Update Thruster temperature on the HUD
shipDisplayModule.SetDisplayGaugeValue(heatLevelGauge, playerHawk.shipInstance.GetHeatLevel(1) / 100f);
// Update ship overall health level on the HUD
shipDisplayModule.SetDisplayGaugeValue(healthGauge, playerHawk.shipInstance.HealthNormalised);
// Missiles, Shield, Launch and Gear gauges only are displayed when game quality is medium or high
shipDisplayModule.SetDisplayGaugeValue(missilesGauge, (playerHawkLMissiles.ammunition + playerHawkRMissiles.ammunition) / missilesGauge.gaugeMaxValue);
shipDisplayModule.SetDisplayGaugeValue(shieldsGauge, playerHawk.shipInstance.mainDamageRegion.ShieldNormalised);
// Update how may enemy fighters remain
shipDisplayModule.SetDisplayGaugeValue(enemyGauge, numEnemyRemaining / enemyGauge.gaugeMaxValue);
}
else if (isInitialised && dockedIndicator != null)
{
// Is the ship docked with the space port?
shipDisplayModule.SetDisplayGaugeValue(dockedIndicator, shipDocking.GetStateInt() == ShipDocking.dockedInt ? 1f : 0f);
}
}
#endregion
#region Public Camera Member Methods
/// <summary>
/// Cycles the camera view for the tech demo 3 Hawk player camera.
/// </summary>
public void TechDemoCycleCameraView(Vector3 inputValue, int customPlayerInputEventType)
{
if (changeShipCameraView != null)
{
// Cycle the camera view
changeShipCameraView.CycleCameraView(inputValue, customPlayerInputEventType);
// Update the HUD depending on whether the new view is inside or outside the cockpit
int newCameraViewIndex = changeShipCameraView.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
#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);
// Make the cargo bay light "important"
SetLightImportance(shuttleCargoBayLight, true);
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 Callback Member 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;
if (shipControlModuleInstance != null)
{
// Attempt new pairing
AttemptPairing(shipAIInputModuleInstance, shipControlModuleInstance);
}
}
}
/// <summary>
/// Callback for when an AI ship is destroyed
/// </summary>
/// <param name="shipInstance"></param>
public void OnAIShipDestroyed(Ship shipInstance)
{
// Do nothing if the ship is going to be respawned
if (shipInstance.respawningMode != Ship.RespawningMode.DontRespawn) { return; }
int factionId = shipInstance.factionId;
int squadronId = shipInstance.squadronId;
int shipId = shipInstance.shipId;
ShipAIInputModule shipAIInputModuleInstance = FindAIShip(shipInstance);
if (factionId == friendlyFactionID)
{
// update relevant lists of friendly ships when ships are destroyed
if (squadronId == friendlySquadronID)
{
if (shipAIInputModuleInstance != null)
{
friendlyShips.Remove(shipAIInputModuleInstance);
numFriendlyShip--;
}
availableFriendlyShips.Remove(shipId);
}
}
else if (factionId == enemyFactionID)
{
// update relevant lists of enemy ships when ships are destroyed
if (squadronId == enemySquadron1ID)
{
if (shipAIInputModuleInstance != null)
{
enemyType1Ships.Remove(shipAIInputModuleInstance);
numEnemyShip1--;
}
}
else if (squadronId == enemySquadron2ID)
{
if (shipAIInputModuleInstance != null)
{
enemyType2Ships.Remove(shipAIInputModuleInstance);
numEnemyShip2--;
}
}
availableEnemyShips.Remove(shipId);
numEnemyRemaining--;
// Currently don't seem to be able to find/destroy last 2 ships
if (numEnemyRemaining < 1)
{
MissionCompleted();
}
}
}
/// <summary>
/// This method is automatically called by SSC when the docking state has changed. We asked SSC to notify us in ConfigurePlayerShips().
/// </summary>
/// <param name="shipDocking"></param>
/// <param name="shipControlModule"></param>
/// <param name="shipAIInputModule"></param>
/// <param name="previousDockingState"></param>
public void OnDockingStateChanged(ShipDocking shipDocking, ShipControlModule shipControlModule, ShipAIInputModule shipAIInputModule, ShipDocking.DockingState previousDockingState)
{
int dockingStateInt = shipDocking.GetStateInt();
if (dockingStateInt == ShipDocking.dockedInt && previousDockingState == ShipDocking.DockingState.Docking)
{
// The shuttle has just docked with the space station
if (soundeffectsObjectPrefabID >= 0 && shipDocking.shipDockingStation != null)
{
// Get the docking point
ShipDockingPoint shipDockingPoint = shipDocking.shipDockingStation.GetDockingPoint(shipDocking.DockingPointId);
if (shipDockingPoint != null)
{
// Get the docking point in world space
Vector3 dockingPointWS = shipDocking.shipDockingStation.GetDockingPointPositionWS(shipDockingPoint);
// Play the docking sound using a pool EffectsModule
PlaySoundFX(dockingSoundClip, dockingPointWS, 0.5f);
}
// Unlock the shuttle side doors
if (sscShuttleDoorAnimator != null) { sscShuttleDoorAnimator.UnlockDoor(0); }
// Update the lock status on the on the door control panel
if (sscDoorControlShuttleSideDoors != null) { sscDoorControlShuttleSideDoors.UpdateLockStatus(); }
// Tell player to get to the services desk
if (shipDisplayModule != null)
{
if (useWalkThru)
{
shipDisplayModule.HideDisplayMessage(dockingInProgressMessage);
shipDisplayModule.ShowDisplayMessage(exitShuttleMessage);
}
else
{
shipDisplayModule.HideDisplayMessage(exitShuttleMessage);
shipDisplayModule.ShowDisplayMessage(findServiceDeskMessage);
}
}
#if SCSM_S3D
if (s3dPlayer != null)
{
// Once the shuttle has docked we no longer need this option,
// so turn it off to improve performance.
//s3dPlayer.headIKAdjustForVelocity = false;
}
#endif
if (useWalkThru)
{
if (isWalkThruInitialised)
{
WalkThruStartSection2();
}
#if UNITY_EDITOR
else { Debug.LogWarning("TechDemo 3 - Walk Thru has not been intialised."); }
#endif
}
// Stop the shuttle "engines" after a short delay. Must be careful not to start
// another ambient clip before this is called otherwise it will stop
// the "new" ambient clip.
Invoke("StopAmbientAudio", 2f);
}
UpdateGauges();
}
}
/// <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();
if (sscRadar != null) { sscRadar.ShowUI(); }
}
}
/// <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();
if (sscRadar != null) { sscRadar.HideUI(); }
}
}
/// <summary>
/// Callback for when ship gets stuck
/// </summary>
/// <param name="shipControlModule"></param>
public void OnShipStuck (ShipControlModule shipControlModule)
{
// Destroy the ship next frame
shipControlModule.shipInstance.mainDamageRegion.Health = 0;
}
/// <summary>
/// Callback for when an AI ship respawns.
/// Once the player is in the Hawk cockpit, the number of respawns for AI ships are limited.
/// After a ship cannot be respawned, the next time it losses all health, only OnAIShipDestroyed(..) will be called.
/// </summary>
/// <param name="shipAIInputModuleInstance"></param>
public void OnRespawnCallback(ShipControlModule shipControlModuleInstance, ShipAIInputModule shipAIInputModuleInstance)
{
if (shipControlModuleInstance != null && shipAIInputModuleInstance != null)
{
int factionId = shipControlModuleInstance.shipInstance.factionId;
int squadronId = shipControlModuleInstance.shipInstance.squadronId;
// Call get new target function with correct parameters
if (factionId == friendlyFactionID)
{
// If this is the nth time the friendly ship is respawned, don't allow it to be respawned again.
if (isInHawkCockpit && shipControlModuleInstance.NumberOfRespawns >= numFriendlyRespawns)
{
shipControlModuleInstance.shipInstance.respawningMode = Ship.RespawningMode.DontRespawn;
}
// Attempt new pairing
AttemptPairing(shipAIInputModuleInstance, shipControlModuleInstance);
}
else if (factionId == enemyFactionID)
{
// If this is the nth time the enemy ship is respawned, don't allow it to be respawned again
if (isInHawkCockpit && shipControlModuleInstance.NumberOfRespawns >= numEnemyRespawns)
{
shipControlModuleInstance.shipInstance.respawningMode = Ship.RespawningMode.DontRespawn;
}
// Attempt new pairing
AttemptPairing(shipAIInputModuleInstance, shipControlModuleInstance);
}
}
}
#endregion
#region Public Sticky3D Member Methods
#if SCSM_S3D
/// <summary>
/// This is called when the player stops touching a door control panel.
/// It will stop the character reaching for the control panel.
/// </summary>
/// <param name="stickyInteractiveID"></param>
/// <param name="stickyID"></param>
public void DoorControlStopTouching(int stickyInteractiveID, int stickyID)
{
// The player has stopped touching the interactive door control panel, so stop trying to reach for it.
if (stickyID == s3dPlayer.StickyID && stickyInteractiveID == s3dPlayer.GetRightHandIKTargetInteractiveID())
{
s3dPlayer.SetRightHandIKTargetInteractive(null, false, false);
}
}
/// <summary>
/// This gets automatically called by the S3D character when it changes what objects it is looking at in the scene.
/// We added this via a Listener in ConfigureStick3DCharacters().
/// </summary>
/// <param name="stickyControlModule"></param>
/// <param name="oldLookAtObject"></param>
/// <param name="newLookAtObject"></param>
public void OnInteractiveLookAtChanged(StickyControlModule stickyControlModule, StickyInteractive oldLookAtObject, StickyInteractive newLookAtObject)
{
if (shipDisplayModule != null)
{
// Change the aiming reticle colour to indicate when the character is looking at a Sticky Interactive object
shipDisplayModule.SetDisplayReticleColour(newLookAtObject == null ? defaultReticleColour : lookingAtReticleColour);
}
}
/// <summary>
/// This is called automatically by Sticky3D when the user selects the "button" in the scene.
/// We added this via a Listener in ConfigureStickyInteractive().
/// </summary>
/// <param name="stickyInteractiveID"></param>
/// <param name="stickyID"></param>
/// <param name="selectedStoreItemID"></param>
public void OnInterativeButtonSelected(int stickyInteractiveID, int stickyID, int selectedStoreItemID)
{
// Make sure this was the player who clicked the button (and not some random NPC)
if (stickyID == s3dPlayer.StickyID)
{
// Check which interactive "button" was selected.
if (dockingButton != null && shuttleDockingButtonInteractive != null && stickyInteractiveID == shuttleDockingButtonInteractive.StickyInteractiveID)
{
// Remove the listeners before destroying the component
shuttleDockingButtonInteractive.RemoveListeners();
// Tell the character to unselect the button that was just selected by the player.
// We don't want the character to be retaining this after we destroy it below.
s3dPlayer.UnselectInteractiveByStoreItemID(selectedStoreItemID);
// Remove the light, interactive-enabled object and collider from the scene
// as we won't need it again in this demo
Destroy(dockingButton);
DockShuttle();
StandupShuttlePilot();
// When the character is moving rapidly with the shuttle, the head
// may not follow where the user is pointing. This option attempts
// to overcome this.
//s3dPlayer.headIKAdjustForVelocity = true;
}
}
}
public void PlayerLookAtServicesAssistant()
{
if (s3dPlayer != null && s3dServiceAssistant != null)
{
s3dPlayer.headIKMoveMaxSpeed = 0.9f;
// If the player is sitting, make sure Head IK still works.
s3dPlayer.headIKWhenMovementDisabled = true;
s3dPlayer.SetHeadIKTarget(s3dServiceAssistant.transform, Vector3.zero, false, true);
}
}
#endif
#endregion
}
}