2078 lines
91 KiB
C#
2078 lines
91 KiB
C#
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
using System.Collections;
|
|
#if UNITY_EDITOR
|
|
using UnityEditor;
|
|
#endif
|
|
|
|
// Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved.
|
|
namespace SciFiShipController
|
|
{
|
|
[AddComponentMenu("Sci-Fi Ship Controller/Ship Components/Ship Control Module")]
|
|
[HelpURL("https://scsmmedia.com/ssc-documentation")]
|
|
[RequireComponent(typeof(Rigidbody))]
|
|
[DisallowMultipleComponent]
|
|
public class ShipControlModule : MonoBehaviour
|
|
{
|
|
#region Public Variables and Properties
|
|
|
|
[HideInInspector] public bool allowRepaint = false;
|
|
|
|
/// <summary>
|
|
/// The class in which the majority of ship data is stored in. Typically use this to access/modify data relating to a ship.
|
|
/// You should check IsInitialised before referencing the shipInstance.
|
|
/// </summary>
|
|
public Ship shipInstance;
|
|
|
|
/// <summary>
|
|
/// [INTERNAL USE ONLY]
|
|
/// </summary>
|
|
public GameObject[] thrusterEffectObjects = new GameObject[1];
|
|
|
|
// Remember which tabs etc were shown
|
|
[HideInInspector] public int selectedTabInt = 0;
|
|
|
|
/// <summary>
|
|
/// [READONLY] Has the ship been initialised?
|
|
/// </summary>
|
|
public bool IsInitialised { get { return isInitialised; } }
|
|
|
|
/// <summary>
|
|
/// [READONLY] Session-only transform InstanceID.
|
|
/// </summary>
|
|
public int GetShipId { get { return isInitialised ? shipInstance.shipId : transform.GetInstanceID(); } }
|
|
|
|
/// <summary>
|
|
/// Attempt to get or set gravitational acceleration (must be initialised)
|
|
/// </summary>
|
|
public float GravitationalAcceleration { get { return isInitialised ? shipInstance.gravitationalAcceleration : 0f; } set { if (isInitialised) { shipInstance.gravitationalAcceleration = value; } } }
|
|
|
|
/// <summary>
|
|
/// Attempt to get or set the direction gravity is acting the ship (must be initialised)
|
|
/// </summary>
|
|
public Vector3 GravityDirection { get { return isInitialised ? shipInstance.gravityDirection : Vector3.zero; } set { if (isInitialised) { shipInstance.gravityDirection = value; } } }
|
|
|
|
/// <summary>
|
|
/// [READONLY] The rigidbody of the ship.
|
|
/// </summary>
|
|
public Rigidbody ShipRigidbody { get { return rBody; } }
|
|
|
|
/// <summary>
|
|
/// Are the thruster systems online or in the process of coming online?
|
|
/// At runtime call StartupThrusterSystems() or ShutdownThrusterSystems().
|
|
/// </summary>
|
|
public bool IsThrusterSystemsStarted { get { return isInitialised ? shipInstance.isThrusterSystemsStarted : false; } }
|
|
|
|
/// <summary>
|
|
/// [READONLY] Is the ship currently being respawned?
|
|
/// The ship will be disabled during the respawn time.
|
|
/// </summary>
|
|
public bool IsRespawning { get { return isRespawning; } }
|
|
|
|
/// <summary>
|
|
/// [READONLY] Has respawning been paused? If so, this ship cannot respawn
|
|
/// until ResumeRespawning() has been called. See also PauseRespawning().
|
|
/// </summary>
|
|
public bool IsRespawningPaused { get { return isRespawingPaused; } }
|
|
|
|
/// <summary>
|
|
/// [READONLY] Is this ship visible to the radar?
|
|
/// </summary>
|
|
public bool IsVisbleToRadar { get { return isInitialised && shipInstance != null && shipInstance.isRadarEnabled && sscRadar != null && sscRadar.GetVisibility(shipInstance.radarItemIndex); } }
|
|
|
|
/// <summary>
|
|
/// [READONLY] The number of weapons on this ship. Will always return 0 if ship has not been initialised.
|
|
/// See also ship.NumberOfWeapons.
|
|
/// </summary>
|
|
public int NumberOfWeapons { get { return isInitialised ? (shipInstance.weaponList == null ? 0 : shipInstance.weaponList.Count) : 0; } }
|
|
|
|
/// <summary>
|
|
/// [READONLY] The number of times the ship has been respawned. This is incremented
|
|
/// when the ship is respawned - not when it is destroyed.
|
|
/// </summary>
|
|
public int NumberOfRespawns { get { return respawnCount; } }
|
|
|
|
/// <summary>
|
|
/// [INTERNAL USE ONLY]
|
|
/// </summary>
|
|
public string editorSearchThrusterFilter;
|
|
|
|
#if UNITY_EDITOR
|
|
|
|
public string ThrusterSystemsStatus { get { return isInitialised ? (shipInstance.isThrusterSystemsStarted ? (shipInstance.thrusterSystemShutdownTimer > 0f ? "shutting down" : (shipInstance.thrusterSystemStartupTimer > 0f ? "starting up" : "online") ) : "offline") : "--"; } }
|
|
|
|
#endif
|
|
|
|
#endregion
|
|
|
|
#region Private Variables
|
|
|
|
private bool isInitialised = false;
|
|
private SSCManager sscManager;
|
|
private SSCRadar sscRadar;
|
|
|
|
private ThrusterEffects[] thrusterEffects;
|
|
|
|
/// <summary>
|
|
/// Used internally to reset PID Controllers on AI ships
|
|
/// when ship velocity is set to 0.
|
|
/// </summary>
|
|
private ShipAIInputModule shipAIInputModule;
|
|
private bool isShipAIInputModuleAttached = false;
|
|
|
|
private ShipDocking shipDocking;
|
|
private bool isShipDockingAttached = false;
|
|
|
|
private Rigidbody rBody;
|
|
private List<Renderer> activeRenderersList;
|
|
private List<AudioSource> audioSourcesList;
|
|
/// <summary>
|
|
/// This prevents the activeRenderersList being updated while the ship
|
|
/// is disabled
|
|
/// </summary>
|
|
private bool isShipVisibilityDisabled = false;
|
|
|
|
private int componentIndex;
|
|
private int arrayLength;
|
|
private Thruster thruster;
|
|
|
|
private bool shipIsEnabled = true;
|
|
|
|
/// <summary>
|
|
/// This is a subset of shipIsEnabled. Current applies to:
|
|
/// 1) Physics
|
|
/// 2) Thruster Effects
|
|
/// 3) User or AI Input
|
|
/// 4) Sound FX (audio)
|
|
/// </summary>
|
|
[SerializeField]
|
|
private bool isMovementEnabled = true;
|
|
|
|
private int lastDamageEventIndex = 0;
|
|
private float damageRumbleTimer = -1f;
|
|
|
|
#region Input Variables
|
|
|
|
private Vector3 pilotForceInput = Vector3.zero;
|
|
// Make non-serialized internal so is visible to ShipCameraModule
|
|
[System.NonSerialized] internal Vector3 pilotMomentInput = Vector3.zero;
|
|
// TODO: There could be a problem with ship fire input
|
|
// If framerate drops below 50 FPS, fixedupdate will be called more often than update
|
|
// Therefore it would be possible for fixedupate to be called twice or more between update calls
|
|
// So if fire can be held is false, fire could still be true for multiple frames for a single fire call
|
|
// However, maybe this won't be a problem if reload time is kept high enough?
|
|
private bool pilotPrimaryFireInput = false;
|
|
private bool pilotSecondaryFireInput = false;
|
|
private bool pilotDockInput = false;
|
|
|
|
#endregion
|
|
|
|
#region FixedUpdate Variables
|
|
|
|
private Vector3 localResultantForce;
|
|
private Vector3 localResultantMoment;
|
|
|
|
private bool isRespawning = false;
|
|
private bool isRespawingPaused = false;
|
|
private int respawnCount = 0;
|
|
private float respawnTimer = 0f;
|
|
private float stuckTimer = 0f;
|
|
|
|
#endregion
|
|
|
|
#endregion
|
|
|
|
#region Public Static Version Properties
|
|
|
|
public static string SSCVersion { get { return "1.3.7"; } }
|
|
public static string SSCBetaVersion { get { return ""; } }
|
|
|
|
#endregion
|
|
|
|
#region Public Delegates
|
|
|
|
public delegate void CallbackOnCollision(ShipControlModule shipControlModule, Collision collision);
|
|
public delegate void CallbackOnDestroy(Ship ship);
|
|
public delegate void CallbackOnRespawn(ShipControlModule shipControlModule, ShipAIInputModule shipAIInputModule);
|
|
public delegate void CallbackOnHit(CallbackOnShipHitParameters callbackOnShipHitParameters);
|
|
public delegate void CallbackOnStuck(ShipControlModule shipControlModule);
|
|
public delegate void CallbackOnRumble(float rumbleAmount);
|
|
|
|
/// <summary>
|
|
/// The name of the custom method that is called immediately after a collision.
|
|
/// Your method must take 2 parameters (ShipControlModule and Collision).
|
|
/// This should be a lightweight method to avoid performance issues.
|
|
/// </summary>
|
|
[System.NonSerialized] public CallbackOnCollision callbackOnCollision = null;
|
|
|
|
/// <summary>
|
|
/// The name of the custom method that is called immediately
|
|
/// before the ship is destroyed. Your method must take 1
|
|
/// parameter of class Ship. This should be a lightweight
|
|
/// method to avoid performance issues. It could be used to update
|
|
/// a score or remove a ship from a squadron.
|
|
/// </summary>
|
|
[System.NonSerialized] public CallbackOnDestroy callbackOnDestroy = null;
|
|
|
|
/// <summary>
|
|
/// The name of the custom method that is called immediately
|
|
/// after the ship is respawned. Your method must take 2 parameters: ShipControlModule
|
|
/// and ShipAIInputModule. The first is never null but there may be no AI module
|
|
/// attached to this ship. This should be a lightweight method to avoid
|
|
/// performance issues.
|
|
/// </summary>
|
|
[System.NonSerialized] public CallbackOnRespawn callbackOnRespawn = null;
|
|
|
|
/// <summary>
|
|
/// The name of the custom method that is called immediately
|
|
/// after the ship is hit by a projectile or beam. Your method must take 1
|
|
/// parameter of type CallbackOnShipHitParameters. This should be
|
|
/// a lightweight method to avoid performance issues. It could be used to
|
|
/// take evasive action while being pursued by an enemy ship. It could
|
|
/// also be used to detect friendly fire.
|
|
/// </summary>
|
|
[System.NonSerialized] public CallbackOnHit callbackOnHit = null;
|
|
|
|
/// <summary>
|
|
/// The name of the custom method that is called immediately after
|
|
/// a ship is detected as stuck. To avoid performance issues, action
|
|
/// should be taken otherwise your method may be called each subsequent
|
|
/// frame. See also ship.stuckTime and ship.stuckSpeedThreshold.
|
|
/// </summary>
|
|
[System.NonSerialized] public CallbackOnStuck callbackOnStuck = null;
|
|
|
|
/// <summary>
|
|
/// Generally reserved for internal use by the PlayerInputModule. If the human
|
|
/// player ship does not have the PlayerInputModule component attached, you can
|
|
/// create a lightweight custom method and assign it at runtime so that it is
|
|
/// called whenever device rumble or force feedback is required.
|
|
/// </summary>
|
|
[System.NonSerialized] public CallbackOnRumble callbackOnRumble = null;
|
|
|
|
#endregion
|
|
|
|
#region Private Initialise Methods
|
|
|
|
// Use this for initialization
|
|
void Awake()
|
|
{
|
|
if (shipInstance.initialiseOnAwake)
|
|
{
|
|
InitialiseShip();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Update Methods
|
|
|
|
void Update()
|
|
{
|
|
if (isInitialised)
|
|
{
|
|
if (isRespawning)
|
|
{
|
|
#region Respawning
|
|
|
|
// If we are currently respawning, count down respawn timer
|
|
respawnTimer -= isRespawingPaused ? 0f : Time.deltaTime;
|
|
|
|
// Once respawn time reaches zero, respawn the ship
|
|
if (respawnTimer <= 0f)
|
|
{
|
|
// We are now not in the process of respawning
|
|
isRespawning = false;
|
|
respawnCount++;
|
|
// Reset the health of the ship
|
|
shipInstance.ResetHealth();
|
|
shipInstance.ResetThrusterSystems();
|
|
|
|
if (shipInstance.respawningMode == Ship.RespawningMode.RespawnOnPath)
|
|
{
|
|
RespawnOnPath(shipInstance.respawningPathGUIDHash, true, false);
|
|
}
|
|
else
|
|
{
|
|
// Set ship position and rotation
|
|
transform.SetPositionAndRotation(shipInstance.GetRespawnPosition(), shipInstance.GetRespawnRotation());
|
|
|
|
// Re-enable the ship and make it visible again
|
|
EnableOrDisableShip(true, true, false);
|
|
}
|
|
|
|
// Re-initialise ground match variables since this will also reset PID controller
|
|
shipInstance.ReinitialiseGroundMatchVariables();
|
|
|
|
// Set ship velocity
|
|
rBody.velocity = transform.TransformDirection(shipInstance.respawnVelocity);
|
|
rBody.angularVelocity = Vector3.zero;
|
|
|
|
// If there is an AI component attached, reset the PID controllers
|
|
// TODO: Test whether this is effective for when an AI respawns with a given velocity
|
|
if (isShipAIInputModuleAttached && shipAIInputModule != null)
|
|
{
|
|
if (shipAIInputModule.IsInitialised) { shipAIInputModule.ResetPIDControllers(); }
|
|
else { shipAIInputModule.Initialise(); }
|
|
}
|
|
|
|
if (callbackOnRespawn != null) { callbackOnRespawn(this, shipAIInputModule); }
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
else
|
|
{
|
|
// Only apply input, calculate movement, update effects or process damage if the ship is enabled
|
|
if (shipIsEnabled)
|
|
{
|
|
// Weapons can only fire, respawning after a collision, when movement is enabled.
|
|
#region Pass input / apply combat output
|
|
if (isMovementEnabled || shipInstance.useWeaponsWhenMovementDisabled)
|
|
{
|
|
// Pass position and movement data to the ship
|
|
shipInstance.UpdatePositionAndMovementData(transform, rBody);
|
|
// Pass pilot weapons input to the ship
|
|
shipInstance.pilotPrimaryFireInput = pilotPrimaryFireInput;
|
|
shipInstance.pilotSecondaryFireInput = pilotSecondaryFireInput;
|
|
// Update weapons and damage data
|
|
shipInstance.UpdateWeaponsAndDamage(sscManager);
|
|
}
|
|
#endregion
|
|
|
|
// Thrusters only work when movement is enabled
|
|
// OR when isThrusterFXStationary is true.
|
|
#region Visual/Audial Effects
|
|
|
|
// Behaviour change for 1.3.7 Beta 2a
|
|
if (shipInstance.isThrusterSystemsStarted && (isMovementEnabled || shipInstance.isThrusterFXStationary))
|
|
{
|
|
UpdateThrusterFX();
|
|
}
|
|
#endregion
|
|
|
|
#region Localised Damage Region Destruction, shield recharge, or Radar Update
|
|
|
|
if (shipInstance.shipDamageModel == Ship.ShipDamageModel.Localised &&
|
|
shipInstance.numLocalisedDamageRegions > 0 && sscManager != null)
|
|
{
|
|
// Loop through localised damage regions
|
|
DamageRegion damageRegion;
|
|
for (int d = 0; d < shipInstance.numLocalisedDamageRegions; d++)
|
|
{
|
|
damageRegion = shipInstance.localisedDamageRegionList[d];
|
|
|
|
#region Effects Object for damage region
|
|
// If a damage region has been "destroyed" but no effects object has been instantiated...
|
|
if (damageRegion.Health <= 0f && !damageRegion.isDestructionEffectsObjectInstantiated)
|
|
{
|
|
// If there is an effects object for this damage region, instantiate it.
|
|
if (damageRegion.effectsObjectPrefabID > 0)
|
|
{
|
|
InstantiateEffectsObjectParameters ieParms = new InstantiateEffectsObjectParameters
|
|
{
|
|
effectsObjectPrefabID = damageRegion.effectsObjectPrefabID,
|
|
position = transform.TransformPoint(damageRegion.relativePosition),
|
|
rotation = shipInstance.TransformRotation
|
|
};
|
|
|
|
// Keep track of the EffectsModule instance that was instantiated for this region
|
|
if (sscManager.InstantiateEffectsObject(ref ieParms) != null)
|
|
{
|
|
damageRegion.destructionEffectItemKey = new SSCEffectItemKey(damageRegion.effectsObjectPrefabID, ieParms.effectsObjectPoolListIndex, ieParms.effectsObjectSequenceNumber);
|
|
}
|
|
}
|
|
// Then remember that we have instantiated an effects object, so we don't do it again
|
|
damageRegion.isDestructionEffectsObjectInstantiated = true;
|
|
}
|
|
// Check if the effects object should move with the ship
|
|
else if (damageRegion.isMoveDestructionEffectsObject && damageRegion.destructionEffectItemKey.effectsObjectSequenceNumber > 0)
|
|
{
|
|
// Find the effects object, validate it is the correct one (it hasn't been despawned) and update position/rotation
|
|
if (!sscManager.MoveEffectsObject(damageRegion.destructionEffectItemKey, transform.TransformPoint(damageRegion.relativePosition), shipInstance.TransformRotation, true))
|
|
{
|
|
// The effect may have been despawned
|
|
// Once the effect has finished, it will not be respawned for this damage region
|
|
damageRegion.destructionEffectItemKey = new SSCEffectItemKey(-1, -1, 0);
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
if (damageRegion.Health > 0f)
|
|
{
|
|
#region Damage Region Radar Update
|
|
if (shipInstance.isRadarEnabled && damageRegion.isRadarEnabled && damageRegion.radarItemIndex >= 0)
|
|
{
|
|
// Update fields that may change at any time
|
|
damageRegion.sscRadarPacket.isVisibleToRadar = true;
|
|
damageRegion.sscRadarPacket.position = shipInstance.GetDamageRegionWSPosition(damageRegion);
|
|
damageRegion.sscRadarPacket.velocity = shipInstance.WorldVelocity;
|
|
damageRegion.sscRadarPacket.factionId = shipInstance.factionId;
|
|
damageRegion.sscRadarPacket.squadronId = shipInstance.squadronId;
|
|
|
|
// Use the internal radarItemIndex rather than RadarId to avoid the property lookup
|
|
sscRadar.UpdateItem(damageRegion.radarItemIndex, damageRegion.sscRadarPacket);
|
|
}
|
|
#endregion
|
|
|
|
#region Local Damage Region Shield Recharge
|
|
damageRegion.CheckShieldRecharge();
|
|
#endregion
|
|
}
|
|
// Only destroy the damage region once
|
|
else if (!damageRegion.isDestroyed)
|
|
{
|
|
#region Damage Region Destruct
|
|
DestructDamageRegion(damageRegion, false);
|
|
|
|
if (damageRegion.regionChildTransform != null)
|
|
{
|
|
damageRegion.regionChildTransform.gameObject.SetActive(false);
|
|
|
|
// Return any weapon muzzle FX to the pool
|
|
DestroyFX(damageRegion.regionChildTransform);
|
|
}
|
|
|
|
// The damage region destroy action has occurred
|
|
damageRegion.isDestroyed = true;
|
|
|
|
if (damageRegion.isRadarEnabled) { DisableRadar(damageRegion); }
|
|
|
|
#endregion
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Check if ship is stuck
|
|
if (isMovementEnabled && IsShipStuck())
|
|
{
|
|
if (shipInstance.stuckAction == Ship.StuckAction.InvokeCallback)
|
|
{
|
|
if (callbackOnStuck != null) { callbackOnStuck(this); }
|
|
}
|
|
else if (shipInstance.stuckAction == Ship.StuckAction.RespawnOnPath)
|
|
{
|
|
// Stop camera shake if it is currently happening
|
|
if (shipInstance.callbackOnCameraShake != null) { shipInstance.callbackOnCameraShake(0f); }
|
|
|
|
RespawnOnPath(shipInstance.stuckActionPathGUIDHash, false, true);
|
|
}
|
|
else if (shipInstance.stuckAction == Ship.StuckAction.SameAsRespawningMode)
|
|
{
|
|
if (shipInstance.respawningMode != Ship.RespawningMode.DontRespawn)
|
|
{
|
|
// We are now in the process of respawning
|
|
isRespawning = true;
|
|
// Set the respawn timer
|
|
respawnTimer = shipInstance.respawnTime;
|
|
|
|
// Stop camera shake if it is currently happening
|
|
if (shipInstance.callbackOnCameraShake != null) { shipInstance.callbackOnCameraShake(0f); }
|
|
|
|
// Now we want to "despawn" this ship
|
|
// So we simply disable all visual and physics components
|
|
|
|
// Disable the ship and make it invisible
|
|
EnableOrDisableShip(false, true, false);
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Damage - Rumble and Camera Shake
|
|
|
|
if (isMovementEnabled && !shipInstance.Destroyed())
|
|
{
|
|
// Check if the last damage event index has changed
|
|
int thisDamageEventIndex = shipInstance.LastDamageEventIndex();
|
|
if (lastDamageEventIndex != thisDamageEventIndex)
|
|
{
|
|
// If the last damage event index has changed, there has been a damage event
|
|
lastDamageEventIndex = thisDamageEventIndex;
|
|
|
|
if (shipInstance.applyControllerRumble)
|
|
{
|
|
// Apply rumble
|
|
if (callbackOnRumble != null) { callbackOnRumble(shipInstance.RequiredDamageRumbleAmount()); }
|
|
// Set the rumble timer
|
|
damageRumbleTimer = 0.5f;
|
|
}
|
|
|
|
if (shipInstance.callbackOnCameraShake != null) { shipInstance.callbackOnCameraShake(shipInstance.RequiredCameraShakeAmount()); }
|
|
}
|
|
// NOTE: CameraShake is turned off by a timer in ShipCameraModule or by calling StopCameraShake(..)
|
|
else if (shipInstance.applyControllerRumble)
|
|
{
|
|
// Count down the rumble timer
|
|
if (damageRumbleTimer > 0f)
|
|
{
|
|
damageRumbleTimer -= Time.deltaTime;
|
|
}
|
|
// If the timer reaches zero, disable rumble
|
|
if (damageRumbleTimer < 0f)
|
|
{
|
|
if (callbackOnRumble != null) { callbackOnRumble(0f); }
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Docking
|
|
// If ship is still enabled, perform dock action
|
|
if (pilotDockInput && shipIsEnabled && isShipDockingAttached && shipDocking.IsInitialised)
|
|
{
|
|
// Currently only supports Docked and Undocked for player ships
|
|
if (shipDocking.GetStateInt() == ShipDocking.dockedInt)
|
|
{
|
|
shipDocking.SetState(ShipDocking.DockingState.NotDocked);
|
|
}
|
|
else
|
|
{
|
|
shipDocking.SetState(ShipDocking.DockingState.Docked);
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
|
|
#region Ship Destruction OR Ship radar update and Thruster Systems startup/shutdown
|
|
|
|
// Check if the ship has been destroyed
|
|
if (shipInstance.Destroyed())
|
|
{
|
|
// Stop camera shake if it is currently happening
|
|
if (shipInstance.callbackOnCameraShake != null) { shipInstance.callbackOnCameraShake(0f); }
|
|
|
|
DestroyFX(transform);
|
|
|
|
if (callbackOnDestroy != null) { callbackOnDestroy(this.shipInstance); }
|
|
|
|
// Check if the ship should be respawned
|
|
if (shipInstance.respawningMode != Ship.RespawningMode.DontRespawn)
|
|
{
|
|
// We are now in the process of respawning
|
|
isRespawning = true;
|
|
// Set the respawn timer
|
|
respawnTimer = shipInstance.respawnTime;
|
|
|
|
// Now we want to "despawn" this ship
|
|
// So we simply disable all visual and physics components
|
|
|
|
// Disable the ship and make it invisible
|
|
EnableOrDisableShip(false, true, false);
|
|
|
|
if (sscManager != null)
|
|
{
|
|
#region Instantiate the ship destruction effects prefab
|
|
if (shipInstance.mainDamageRegion.destructionEffectsObject != null)
|
|
{
|
|
InstantiateEffectsObjectParameters ieParms = new InstantiateEffectsObjectParameters
|
|
{
|
|
effectsObjectPrefabID = shipInstance.mainDamageRegion.effectsObjectPrefabID,
|
|
position = transform.position,
|
|
rotation = transform.rotation
|
|
};
|
|
|
|
sscManager.InstantiateEffectsObject(ref ieParms);
|
|
}
|
|
#endregion
|
|
|
|
DestructDamageRegion(shipInstance.mainDamageRegion, true);
|
|
}
|
|
|
|
// End the rumble timer
|
|
damageRumbleTimer = -1f;
|
|
}
|
|
else
|
|
{
|
|
shipInstance.DeactivateBeams(sscManager);
|
|
|
|
// If the ship is using radar, remove it from radar before the ship gameobject
|
|
// is destroyed. Also remove any Localised Damage Regions from radar.
|
|
if (shipInstance.isRadarEnabled && shipInstance.radarItemIndex >= 0)
|
|
{
|
|
DisableRadar();
|
|
//sscRadar.DisableRadar(shipInstance.radarItemIndex);
|
|
}
|
|
|
|
if (sscManager != null)
|
|
{
|
|
#region Instantiate the ship destruction effects prefab
|
|
if (shipInstance.mainDamageRegion.destructionEffectsObject != null)
|
|
{
|
|
InstantiateEffectsObjectParameters ieParms = new InstantiateEffectsObjectParameters
|
|
{
|
|
effectsObjectPrefabID = shipInstance.mainDamageRegion.effectsObjectPrefabID,
|
|
position = transform.position,
|
|
rotation = transform.rotation
|
|
};
|
|
|
|
sscManager.InstantiateEffectsObject(ref ieParms);
|
|
}
|
|
#endregion
|
|
|
|
DestructDamageRegion(shipInstance.mainDamageRegion, true);
|
|
}
|
|
|
|
// Destroy this ship
|
|
// Prevent the shipInstance referencing the ShipControlModule transform.
|
|
if (isInitialised) { shipInstance.shipTransform = null; }
|
|
Destroy(gameObject);
|
|
// Need to also destroy the ship class instance
|
|
if (shipInstance != null) { isInitialised = false; shipInstance = null; }
|
|
}
|
|
}
|
|
else if (shipIsEnabled)
|
|
{
|
|
if (shipInstance.isRadarEnabled)
|
|
{
|
|
// Update fields that may change at any time
|
|
shipInstance.sscRadarPacket.isVisibleToRadar = true;
|
|
shipInstance.sscRadarPacket.position = shipInstance.TransformPosition;
|
|
shipInstance.sscRadarPacket.velocity = shipInstance.WorldVelocity;
|
|
shipInstance.sscRadarPacket.factionId = shipInstance.factionId;
|
|
shipInstance.sscRadarPacket.squadronId = shipInstance.squadronId;
|
|
|
|
// Use the internal radarItemIndex rather than RadarId to avoid the property lookup
|
|
sscRadar.UpdateItem(shipInstance.radarItemIndex, shipInstance.sscRadarPacket);
|
|
}
|
|
|
|
bool hasStarted, hasShutdown;
|
|
if (shipInstance.CheckThrusterSystems(Time.deltaTime, out hasStarted, out hasShutdown))
|
|
{
|
|
if (hasShutdown)
|
|
{
|
|
StopThrusterEffects();
|
|
}
|
|
}
|
|
|
|
shipInstance.mainDamageRegion.CheckShieldRecharge();
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|
|
}
|
|
|
|
// FixedUpdate is called once per physics update (typically about 50 times per second)
|
|
void FixedUpdate()
|
|
{
|
|
if (isInitialised)
|
|
{
|
|
if (!isRespawning)
|
|
{
|
|
//float calcStartTime = Time.realtimeSinceStartup;
|
|
|
|
// When shipIsEnabled is false, isMovementEnabled is also false,
|
|
// though the opposite may not be true.
|
|
if (shipIsEnabled && isMovementEnabled)
|
|
{
|
|
#region Pass input / apply movement output
|
|
|
|
// Pass position and movement data to the ship
|
|
shipInstance.UpdatePositionAndMovementData(transform, rBody);
|
|
// Pass pilot movement input to the ship
|
|
shipInstance.pilotForceInput = pilotForceInput;
|
|
shipInstance.pilotMomentInput = pilotMomentInput;
|
|
// Calculate the local resultant force and moment
|
|
shipInstance.CalculateForceAndMoment(ref localResultantForce, ref localResultantMoment);
|
|
|
|
// Apply the local resultant force and moment to the rigidbody
|
|
rBody.AddRelativeForce(localResultantForce);
|
|
rBody.AddRelativeTorque(localResultantMoment);
|
|
|
|
#endregion
|
|
}
|
|
|
|
//float calcEndTime = Time.realtimeSinceStartup;
|
|
//Debug.Log("| Total: " + ((calcEndTime - calcStartTime) * 1000f).ToString("0.0000") + " ms |");
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
#region Private Methods
|
|
|
|
/// <summary>
|
|
/// Enables or disables the ship (depending on the value of isEnabled). If updateVisibility is set to true,
|
|
/// changes whether the ship is visible. If resetVelocity is set to true, resets the velocity of the ship to zero.
|
|
/// Movement is not enabled if the ship is docked, although collision detection is updated.
|
|
/// </summary>
|
|
/// <param name="isEnabled"></param>
|
|
/// <param name="updateVisibility"></param>
|
|
/// <param name="resetVelocity"></param>
|
|
private void EnableOrDisableShip(bool isEnabled, bool updateVisibility, bool resetVelocity)
|
|
{
|
|
// This does not prove the ship is docked, but the ship thinks it is docked. For absolute proof, we would
|
|
// need to check the ShipDockingStation's docking points (which is prob too expensive here).
|
|
bool isDocked = isShipDockingAttached && shipDocking.IsInitialised && shipDocking.GetStateInt() == ShipDocking.dockedInt;
|
|
|
|
if (isDocked && isEnabled)
|
|
{
|
|
ShipRigidbody.detectCollisions = shipDocking.detectCollisionsWhenDocked;
|
|
}
|
|
else
|
|
{
|
|
EnableOrDisableShipPhysics(isEnabled, resetVelocity);
|
|
// Pause/resume thruster effects
|
|
PauseOrResumeThrusters(isEnabled);
|
|
}
|
|
|
|
#region Update Visuals
|
|
|
|
if (updateVisibility)
|
|
{
|
|
if (isEnabled)
|
|
{
|
|
// Re-enable previously active renderers
|
|
arrayLength = activeRenderersList.Count;
|
|
for (componentIndex = 0; componentIndex < arrayLength; componentIndex++)
|
|
{
|
|
activeRenderersList[componentIndex].enabled = true;
|
|
}
|
|
isShipVisibilityDisabled = false;
|
|
}
|
|
else
|
|
{
|
|
shipInstance.DeactivateBeams(sscManager);
|
|
|
|
// We are disabling the ship - so we disable all visual and audial components
|
|
|
|
#region Find and Disable Renderers
|
|
|
|
if (!isShipVisibilityDisabled)
|
|
{
|
|
// Prevent the list being cleared when the ship is already disabled
|
|
isShipVisibilityDisabled = true;
|
|
|
|
// Get all the renderers in the ship and store them in a list
|
|
// Use a pre-defined reusable list. If respawning happens multiple
|
|
// times there will be a lot of GC which will hurt performance.
|
|
GetComponentsInChildren(activeRenderersList);
|
|
|
|
// Loop through the renderers
|
|
arrayLength = activeRenderersList.Count;
|
|
componentIndex = 0;
|
|
while (componentIndex < arrayLength)
|
|
{
|
|
if (!activeRenderersList[componentIndex].enabled)
|
|
{
|
|
// If the renderer is currently disabled, remove it from the list
|
|
// (We don't want to enable it when we respawn)
|
|
activeRenderersList.RemoveAt(componentIndex);
|
|
arrayLength--;
|
|
}
|
|
else
|
|
{
|
|
// If the renderer is currently enabled, disable it (and leave it in the list)
|
|
activeRenderersList[componentIndex].enabled = false;
|
|
componentIndex++;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
|
|
StopAudioSources();
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Update Rumble
|
|
|
|
if (!isEnabled && callbackOnRumble != null) { callbackOnRumble(0f); }
|
|
|
|
#endregion
|
|
|
|
#region Update Radar
|
|
if (shipInstance.isRadarEnabled)
|
|
{
|
|
sscRadar.SetVisibility(shipInstance.RadarId, isEnabled);
|
|
|
|
// If any localised damage regions have radar enabled, set the visibility.
|
|
if (shipInstance.shipDamageModel == Ship.ShipDamageModel.Localised)
|
|
{
|
|
for (int dmIdx = 0; dmIdx < shipInstance.numLocalisedDamageRegions; dmIdx++)
|
|
{
|
|
DamageRegion damageRegion = shipInstance.localisedDamageRegionList[dmIdx];
|
|
if (damageRegion != null && damageRegion.isRadarEnabled)
|
|
{
|
|
sscRadar.SetVisibility(damageRegion.radarItemIndex, isEnabled);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Added for TechDemo3 (SSC v1.2.6 Beta 1b)
|
|
if (isEnabled && isInitialised)
|
|
{
|
|
shipInstance.UpdatePositionAndMovementData(transform, rBody);
|
|
|
|
// Update fields that may change at any time
|
|
shipInstance.sscRadarPacket.isVisibleToRadar = true;
|
|
shipInstance.sscRadarPacket.position = shipInstance.TransformPosition;
|
|
shipInstance.sscRadarPacket.velocity = shipInstance.WorldVelocity;
|
|
shipInstance.sscRadarPacket.factionId = shipInstance.factionId;
|
|
shipInstance.sscRadarPacket.squadronId = shipInstance.squadronId;
|
|
|
|
// Use the internal radarItemIndex rather than RadarId to avoid the property lookup
|
|
sscRadar.UpdateItem(shipInstance.radarItemIndex, shipInstance.sscRadarPacket);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
// Update the shipIsEnabled variable
|
|
shipIsEnabled = isEnabled;
|
|
|
|
// Movement is a subset of the ship being enabled/disabled.
|
|
isMovementEnabled = isEnabled && !isDocked;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This is a subset of EnableOrDisableShip(...). The ship can still incur damage,
|
|
/// be destroyed and respawn (and become Enabled), and appear on Radar.
|
|
/// It applies to:
|
|
/// 1) Physics
|
|
/// 2) Thruster Effects
|
|
/// 3) User or AI Input to the Ship
|
|
/// 4) Sound FX (audio)
|
|
/// Movement is not enabled if the ship is docked, although collision detection is updated.
|
|
/// </summary>
|
|
/// <param name="isEnabled"></param>
|
|
/// <param name="resetVelocity"></param>
|
|
private void EnableOrDisableShipMovement(bool isEnabled, bool resetVelocity)
|
|
{
|
|
// This does not prove the ship is docked, but the ship thinks it is docked. For absolute proof, we would
|
|
// need to check the ShipDockingStation's docking points (which is prob too expensive here).
|
|
bool isDocked = isShipDockingAttached && shipDocking.IsInitialised && shipDocking.GetStateInt() == ShipDocking.dockedInt;
|
|
|
|
if (isDocked && isEnabled)
|
|
{
|
|
ShipRigidbody.detectCollisions = shipDocking.detectCollisionsWhenDocked;
|
|
}
|
|
else
|
|
{
|
|
EnableOrDisableShipPhysics(isEnabled, resetVelocity);
|
|
PauseOrResumeThrusters(isEnabled);
|
|
if (!isEnabled && callbackOnRumble != null) { callbackOnRumble(0f); }
|
|
if (!isEnabled) { StopAudioSources(); }
|
|
|
|
isMovementEnabled = isEnabled;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enable or disable the Physics on a ship. If this ia an AI ship, resets the PID controllers
|
|
/// when isEnabled is true.
|
|
/// If resetVelocity and isEnabled are true, resets the velocity of the ship to zero.
|
|
/// NOTE: This should NOT be called when the ship is docked.
|
|
/// </summary>
|
|
/// <param name="isEnabled"></param>
|
|
/// <param name="resetVelocity"></param>
|
|
private void EnableOrDisableShipPhysics(bool isEnabled, bool resetVelocity)
|
|
{
|
|
// Ensure the rigidbody's collision detection mode is configured to permit isKinematic.
|
|
if (!isEnabled) { ShipRigidbody.collisionDetectionMode = CollisionDetectionMode.Discrete; }
|
|
|
|
if (isEnabled)
|
|
{
|
|
// When enabling, turn on collisions and set the ship rigidbody to not kinematic
|
|
ShipRigidbody.detectCollisions = true;
|
|
ShipRigidbody.isKinematic = false;
|
|
|
|
// Re-configure the rigidbody's collision detection mode
|
|
ShipRigidbody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
|
|
}
|
|
else
|
|
{
|
|
// When disabling, turn off collisions and set the ship rigidbody to kinematic
|
|
ShipRigidbody.detectCollisions = false;
|
|
ShipRigidbody.isKinematic = true;
|
|
|
|
// Added 1.3.7 Beta 5c so that cached velo data correctly returns Vector.Zero
|
|
// NOTE: This hasn't been fully regression tested.
|
|
if (resetVelocity) { shipInstance.ResetVelocityData(); }
|
|
}
|
|
|
|
// Set our velocity when we enable the ship
|
|
if (isEnabled)
|
|
{
|
|
if (resetVelocity)
|
|
{
|
|
// Reset velocity to zero
|
|
ShipRigidbody.velocity = Vector3.zero;
|
|
ShipRigidbody.angularVelocity = Vector3.zero;
|
|
|
|
// If there is an AI component attached, reset the PID controllers
|
|
if (isShipAIInputModuleAttached && shipAIInputModule != null)
|
|
{
|
|
if (shipAIInputModule.IsInitialised) { shipAIInputModule.ResetPIDControllers(); }
|
|
else { shipAIInputModule.Initialise(); }
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Set velocity to the velocity we had before the ship was disabled
|
|
// NOTE: This assumes shipInstance.ResetVelocityData() isn't called when this method
|
|
// is called with isEnabled = true.
|
|
ShipRigidbody.velocity = shipInstance.WorldVelocity;
|
|
ShipRigidbody.angularVelocity = shipInstance.WorldAngularVelocity;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pause or Resume (play) all Thruster Effects on the ship
|
|
/// </summary>
|
|
/// <param name="isEnabled"></param>
|
|
private void PauseOrResumeThrusters(bool isEnabled)
|
|
{
|
|
arrayLength = thrusterEffects == null ? 0 : thrusterEffects.Length;
|
|
for (componentIndex = 0; componentIndex < arrayLength; componentIndex++)
|
|
{
|
|
if (thrusterEffects[componentIndex] != null)
|
|
{
|
|
if (isEnabled) { thrusterEffects[componentIndex].Play(); }
|
|
else { thrusterEffects[componentIndex].Pause(); }
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Find and stop all audio sources
|
|
/// </summary>
|
|
private void StopAudioSources()
|
|
{
|
|
// Use the pre-defined reuseable list to minmize GC
|
|
GetComponentsInChildren(audioSourcesList);
|
|
arrayLength = audioSourcesList.Count;
|
|
for (componentIndex = 0; componentIndex < arrayLength; componentIndex++)
|
|
{
|
|
if (audioSourcesList[componentIndex].enabled && audioSourcesList[componentIndex].isPlaying)
|
|
{
|
|
audioSourcesList[componentIndex].Stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// CURRENTLY NOT USED - FOR TESTING ONLY
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private IEnumerator RBodyReset()
|
|
{
|
|
//rBody.velocity = transform.TransformDirection(shipInstance.respawnVelocity);
|
|
rBody.velocity = Vector3.zero;
|
|
rBody.angularVelocity = Vector3.zero;
|
|
yield return new WaitForFixedUpdate();
|
|
}
|
|
|
|
/// <summary>
|
|
/// A ship can be considered stuck if it is enabled, initialised, respawnStuckTime > 0
|
|
/// and it is trying to move for respawnStuckTime but cannot.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private bool IsShipStuck()
|
|
{
|
|
if (!shipIsEnabled || !isInitialised || shipInstance.stuckTime <= 0f) { return false; }
|
|
else
|
|
{
|
|
// velo in m/s
|
|
if (Mathf.Abs((shipInstance.TransformInverseRotation * shipInstance.WorldVelocity).z) > shipInstance.stuckSpeedThreshold)
|
|
{
|
|
stuckTimer = 0f;
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
stuckTimer += Time.deltaTime;
|
|
return stuckTimer >= shipInstance.stuckTime;
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Respawn (move) the ship to the closest point on the given Path.
|
|
/// The ship must be initialised and the Path must be valid.
|
|
/// </summary>
|
|
/// <param name="pathguidHash"></param>
|
|
/// <param name="updateVisibility"></param>
|
|
/// <param name="resetVelocity"></param>
|
|
private void RespawnOnPath(int pathguidHash, bool updateVisibility, bool resetVelocity)
|
|
{
|
|
if (isInitialised && pathguidHash != 0)
|
|
{
|
|
PathData pathData = sscManager.GetPath(pathguidHash);
|
|
if (pathData != null)
|
|
{
|
|
Vector3 closestPointOnPath = Vector3.zero;
|
|
float closestPointOnPathTValue = 0f;
|
|
int prevPathLocationIdx = 0;
|
|
|
|
if (SSCMath.FindClosestPointOnPath(pathData, shipInstance.TransformPosition, ref closestPointOnPath, ref closestPointOnPathTValue, ref prevPathLocationIdx))
|
|
{
|
|
DisableShip(updateVisibility);
|
|
|
|
// Check for closest object
|
|
float lookDistance = 10f, minDistance = 10000f;
|
|
RaycastHit raycastHit;
|
|
Vector3 objectNormal = Vector3.zero;
|
|
|
|
Ray ray = new Ray(closestPointOnPath, Vector3.up);
|
|
|
|
SSCUtils.GetClosestCollider(ray, Vector3.up, lookDistance, ref minDistance, ref objectNormal, out raycastHit);
|
|
SSCUtils.GetClosestCollider(ray, Vector3.down, lookDistance, ref minDistance, ref objectNormal, out raycastHit);
|
|
SSCUtils.GetClosestCollider(ray, Vector3.left, lookDistance, ref minDistance, ref objectNormal, out raycastHit);
|
|
SSCUtils.GetClosestCollider(ray, Vector3.right, lookDistance, ref minDistance, ref objectNormal, out raycastHit);
|
|
SSCUtils.GetClosestCollider(ray, Vector3.forward, lookDistance, ref minDistance, ref objectNormal, out raycastHit);
|
|
SSCUtils.GetClosestCollider(ray, Vector3.back, lookDistance, ref minDistance, ref objectNormal, out raycastHit);
|
|
|
|
if (objectNormal.sqrMagnitude > 0.01f)
|
|
{
|
|
Vector3 pathTangent = Vector3.zero;
|
|
// Get the direction of the path so ship faces in the correct direction AND is orientated upwards similar to closest object.
|
|
if (SSCMath.GetPathTangent(pathData, prevPathLocationIdx, closestPointOnPathTValue, ref pathTangent))
|
|
{
|
|
transform.SetPositionAndRotation(closestPointOnPath, Quaternion.LookRotation(pathTangent, objectNormal));
|
|
}
|
|
else
|
|
{
|
|
transform.SetPositionAndRotation(closestPointOnPath, Quaternion.Euler(objectNormal));
|
|
}
|
|
}
|
|
else { transform.position = closestPointOnPath; }
|
|
|
|
EnableShip(updateVisibility, resetVelocity);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check to see if there is a ShipAIInputModule attached to this ship.
|
|
/// </summary>
|
|
private void FetchShipAIInputModule()
|
|
{
|
|
isShipAIInputModuleAttached = TryGetComponent(out shipAIInputModule);
|
|
|
|
//shipAIInputModule = GetComponent<ShipAIInputModule>();
|
|
//isShipAIInputModuleAttached = shipAIInputModule != null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check to see if there is a ShipDocking component attached to this ship
|
|
/// </summary>
|
|
private void FetchShipDocking()
|
|
{
|
|
isShipDockingAttached = TryGetComponent(out shipDocking);
|
|
|
|
//shipDocking = GetComponent<ShipDocking>();
|
|
//isShipDockingAttached = shipDocking != null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// [INTERNAL ONLY] Initialise radar for this ship
|
|
/// Assumes shipInstance is not null and that
|
|
/// sscRadar = SSCRadar.GetOrCreateRadar() has already
|
|
/// been called.
|
|
/// </summary>
|
|
private void InitialiseRadar()
|
|
{
|
|
// Not assigned in the radar system
|
|
shipInstance.radarItemIndex = -1;
|
|
|
|
if (shipInstance.isRadarEnabled && sscRadar != null)
|
|
{
|
|
SSCRadarItem sscRadarItem = new SSCRadarItem();
|
|
// No data from the ship yet, so hide it from radar
|
|
sscRadarItem.isVisibleToRadar = false;
|
|
sscRadarItem.blipSize = shipInstance.radarBlipSize;
|
|
sscRadarItem.shipControlModule = this;
|
|
|
|
// Create a packet to be used to send data to the radar system
|
|
shipInstance.sscRadarPacket = new SSCRadarPacket();
|
|
|
|
shipInstance.radarItemIndex = sscRadar.AddItem(sscRadarItem);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// [INTERNAL ONLY] Initialise radar for a localised damage region.
|
|
/// Assumes shipInstance is not null, and that
|
|
/// sscRadar = SSCRadar.GetOrCreateRadar() has already been called.
|
|
/// </summary>
|
|
private void InitialiseLocalDamageRegionRadar(DamageRegion damageRegion)
|
|
{
|
|
// Not assigned in the radar system
|
|
damageRegion.radarItemIndex = -1;
|
|
|
|
if (shipInstance.isRadarEnabled && damageRegion.isRadarEnabled && sscRadar != null)
|
|
{
|
|
SSCRadarItem sscRadarItem = new SSCRadarItem();
|
|
// No data from the ship damage region yet, so hide it from radar
|
|
sscRadarItem.isVisibleToRadar = false;
|
|
sscRadarItem.blipSize = 1;
|
|
sscRadarItem.shipControlModule = this;
|
|
// The damage region child transform can be used to help determine LoS. It may not be set.
|
|
sscRadarItem.itemGameObject = damageRegion.regionChildTransform == null ? null : damageRegion.regionChildTransform.gameObject;
|
|
sscRadarItem.radarItemType = SSCRadarItem.RadarItemType.ShipDamageRegion;
|
|
sscRadarItem.guidHash = damageRegion.guidHash;
|
|
|
|
// Create a packet to be used to send data to the radar system
|
|
damageRegion.sscRadarPacket = new SSCRadarPacket();
|
|
|
|
damageRegion.radarItemIndex = sscRadar.AddItem(sscRadarItem);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// If muzzle FX on weapons are pooled and have been parented to the weapons, when a
|
|
/// ship or damage region is destroyed (o rmade inactive), they need to be reparented to the pool.
|
|
/// NOTE: This may impact GC
|
|
/// </summary>
|
|
private void DestroyFX (Transform tfm)
|
|
{
|
|
EffectsModule[] effectsModules = tfm.GetComponentsInChildren<EffectsModule>(true);
|
|
|
|
int numEffectsModules = effectsModules == null ? 0 : effectsModules.Length;
|
|
|
|
for (int emIdx = 0; emIdx < numEffectsModules; emIdx++)
|
|
{
|
|
EffectsModule effectsModule = effectsModules[emIdx];
|
|
|
|
if (effectsModule.usePooling && effectsModule.isReparented)
|
|
{
|
|
effectsModule.DestroyEffectsObject();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Automatically attempt to determine what kind of ship this is for use in the radar system.
|
|
/// Typically called after InitialiseRadar().
|
|
/// It defaults ot PlayerShip.
|
|
/// </summary>
|
|
private void RadarAutoSetType()
|
|
{
|
|
if (sscRadar != null)
|
|
{
|
|
if (isShipAIInputModuleAttached) { sscRadar.SetItemType(shipInstance.RadarId, SSCRadarItem.RadarItemType.AIShip); }
|
|
else { sscRadar.SetItemType(shipInstance.RadarId, SSCRadarItem.RadarItemType.PlayerShip); }
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initiate a destruct prefab on a damage region of the ship
|
|
/// NOTE: sscManager must not be null when calling this method.
|
|
/// </summary>
|
|
/// <param name="damageRegion"></param>
|
|
/// <param name="isMainRegion"></param>
|
|
private void DestructDamageRegion(DamageRegion damageRegion, bool isMainRegion)
|
|
{
|
|
if (!damageRegion.isDestructObjectActivated && damageRegion.destructObjectPrefabID >= 0 && damageRegion.destructObject != null)
|
|
{
|
|
Vector3 destructPosition = shipInstance.TransformPosition;
|
|
Quaternion destructRotation = shipInstance.TransformRotation;
|
|
|
|
if (isMainRegion)
|
|
{
|
|
if (shipInstance.respawningMode == Ship.RespawningMode.DontRespawn)
|
|
{
|
|
// Turn off all colliders. As we are going to destroy the gameobject,
|
|
// we can simply deactivate it.
|
|
gameObject.SetActive(false);
|
|
}
|
|
else
|
|
{
|
|
// Cater for respawning - before DestructDamageRegion is called the ship is disabled
|
|
// with EnableOrDisableShip(false, true, false). Here we should not turn off the gameObject.
|
|
// This "seems" to work without having to disable the colliders but might need more testing.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Get the worldspace position of the region
|
|
destructPosition = shipInstance.GetDamageRegionWSPosition(damageRegion);
|
|
|
|
// Damage region needs to be repairable
|
|
|
|
}
|
|
|
|
// Instantiate the region destruct prefab
|
|
InstantiateDestructParameters dstParms = new InstantiateDestructParameters
|
|
{
|
|
destructPrefabID = damageRegion.destructObjectPrefabID,
|
|
position = destructPosition,
|
|
rotation = destructRotation,
|
|
explosionPowerFactor = 1f,
|
|
explosionRadiusFactor = 1f
|
|
};
|
|
|
|
// Keep track of the DestructModule instance that was instantiated for this region
|
|
if (sscManager.InstantiateDestruct(ref dstParms) != null)
|
|
{
|
|
damageRegion.destructItemKey = new SSCDestructItemKey(damageRegion.destructObjectPrefabID, dstParms.destructPoolListIndex, dstParms.destructSequenceNumber);
|
|
}
|
|
damageRegion.isDestructObjectActivated = true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Update the Thruster Effects
|
|
/// </summary>
|
|
private void UpdateThrusterFX()
|
|
{
|
|
arrayLength = thrusterEffects == null ? 0 : thrusterEffects.Length;
|
|
Vector3 localVelo = shipInstance.LocalVelocity;
|
|
for (componentIndex = 0; componentIndex < arrayLength; componentIndex++)
|
|
{
|
|
if (thrusterEffects[componentIndex] != null)
|
|
{
|
|
thrusterEffects[componentIndex].UpdateThrusterInput(shipInstance.thrusterList[componentIndex], localVelo);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Events
|
|
|
|
// Called when the ship rigidbody collides with another collider
|
|
private void OnCollisionEnter(Collision collision)
|
|
{
|
|
if (callbackOnCollision != null) { callbackOnCollision.Invoke(this, collision); }
|
|
else { shipInstance.ApplyCollisionDamage(collision); }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public API Methods - Initialisation
|
|
|
|
/// <summary>
|
|
/// Runs all necessary initialisation processes for the ship.
|
|
/// </summary>
|
|
public void InitialiseShip()
|
|
{
|
|
if (!isInitialised)
|
|
{
|
|
// Find the rigidbody
|
|
rBody = GetComponent<Rigidbody>();
|
|
|
|
// Cater for scenario where [RequireComponent(typeof(Rigidbody))] doesn't work
|
|
// RequireComponent only seems to work when first adding the script to a gameobject.
|
|
if (rBody == null) { isInitialised = false; return; }
|
|
// Configure the rigidbody
|
|
rBody.drag = 0f;
|
|
rBody.angularDrag = 0f;
|
|
rBody.useGravity = false;
|
|
rBody.isKinematic = false;
|
|
rBody.collisionDetectionMode = CollisionDetectionMode.ContinuousDynamic;
|
|
// Set the mass and centre of mass of the ship
|
|
ReinitialiseMass();
|
|
|
|
// Initialise all necessary ship data
|
|
shipInstance.UpdatePositionAndMovementData(transform, rBody);
|
|
shipInstance.Initialise(transform);
|
|
|
|
// Initialise ship health
|
|
shipInstance.ResetHealth();
|
|
|
|
ReinitialiseThrusterEffects();
|
|
|
|
if (!shipInstance.isThrusterSystemsStarted)
|
|
{
|
|
StopThrusterEffects();
|
|
}
|
|
|
|
// Get a reference to the Ship Controller Manager instance
|
|
sscManager = SSCManager.GetOrCreateManager();
|
|
|
|
// Initialise projectiles and effects objects
|
|
ReinitialiseShipProjectilesAndEffects();
|
|
|
|
// Initialise beam weapons and the effects objects used by those weapons.
|
|
ReinitialiseShipBeams();
|
|
|
|
// Initialise destruct objects used by damage regions
|
|
ReinitialiseShipDestructObjects();
|
|
|
|
// Initialise required lists
|
|
// Pre-size the list of renderers that is used when
|
|
// ships are disable/enabled during respawing. If the list
|
|
// is too small, it be expanded the first time it is used (and affect GC).
|
|
activeRenderersList = new List<Renderer>(5);
|
|
|
|
// Assume only 1 audio source per ship. Will auto expand if required.
|
|
// Increase the capacity of the list if ships tend to have more audio sources.
|
|
audioSourcesList = new List<AudioSource>(1);
|
|
|
|
FetchShipAIInputModule();
|
|
FetchShipDocking();
|
|
|
|
if (shipInstance.isRadarEnabled)
|
|
{
|
|
sscRadar = SSCRadar.GetOrCreateRadar();
|
|
InitialiseRadar();
|
|
RadarAutoSetType();
|
|
|
|
if (shipInstance.shipDamageModel == Ship.ShipDamageModel.Localised)
|
|
{
|
|
for (int dmIdx = 0; dmIdx < shipInstance.numLocalisedDamageRegions; dmIdx++)
|
|
{
|
|
InitialiseLocalDamageRegionRadar(shipInstance.localisedDamageRegionList[dmIdx]);
|
|
}
|
|
}
|
|
}
|
|
|
|
isInitialised = true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reinitialises variables related to the mass of the ship.
|
|
/// Call after modifying shipInstance.mass or shipInstance.centreOfMass.
|
|
/// </summary>
|
|
public void ReinitialiseMass()
|
|
{
|
|
// Set the mass and centre of mass of the ship
|
|
rBody.mass = shipInstance.mass;
|
|
rBody.centerOfMass = shipInstance.centreOfMass;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reinitialises variables required for projectiles and effects of the ship.
|
|
/// Call this after modifying any projectile or effect data for this ship.
|
|
/// </summary>
|
|
public void ReinitialiseShipProjectilesAndEffects()
|
|
{
|
|
if (sscManager != null)
|
|
{
|
|
// Initialise projectiles and effects objects
|
|
sscManager.UpdateProjectilesAndEffects(shipInstance);
|
|
}
|
|
#if UNITY_EDITOR
|
|
else
|
|
{
|
|
Debug.LogWarning("ShipControlModule.ReinitialiseShipProjectilesAndEffects Warning: could not find SSCManager to update projectiles and effects.");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reinitialises variables required for destruct objects of the ship.
|
|
/// Call after modifying any destruct data for this ship.
|
|
/// </summary>
|
|
public void ReinitialiseShipDestructObjects ()
|
|
{
|
|
if (sscManager != null)
|
|
{
|
|
// Initialise destruct objects
|
|
sscManager.UpdateDestructObjects(shipInstance);
|
|
}
|
|
#if UNITY_EDITOR
|
|
else
|
|
{
|
|
Debug.LogWarning("ShipControlModule.ReinitialiseShipDestructObjects Warning: could not find SSCManager to update destruct objects.");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reinitialises variables required for ship beam weapons and effects used by those beams.
|
|
/// Call this after modifying any beams or beam effect data for this ship.
|
|
/// </summary>
|
|
public void ReinitialiseShipBeams()
|
|
{
|
|
if (sscManager != null)
|
|
{
|
|
// Initialise beams, and effects objects used by those beams
|
|
sscManager.UpdateBeamsAndEffects(shipInstance);
|
|
}
|
|
#if UNITY_EDITOR
|
|
else
|
|
{
|
|
Debug.LogWarning("ShipControlModule.ReinitialiseShipBeams Warning: could not find SSCManager to update (weapon) beams");
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reinitialises the thruster effects for a ship.
|
|
/// Call this after modifying thruster effects objects for this ship.
|
|
/// WARNING: This will generate Garbage so use sparingly.
|
|
/// </summary>
|
|
public void ReinitialiseThrusterEffects()
|
|
{
|
|
// Initialise all thruster effects
|
|
arrayLength = shipInstance.thrusterList == null ? 0 : shipInstance.thrusterList.Count;
|
|
int numThrusterEffects = thrusterEffects == null ? 0 : thrusterEffects.Length;
|
|
if (arrayLength > 0)
|
|
{
|
|
// There is a list of thrusterEffectObjects. Each thruster has a slot however it also may be null if the thruster has no effects parent gameobject.
|
|
// Each thruster should have a single ThrusterEffects slot (which could be null if thruster has no effects parent gameobject in the scene)
|
|
// One ThrusterEffects component is added the to thruster effects parent gameobject (if there is one) for each Thruster in the scene.
|
|
// Each ThrusterEffects component is then initialised.
|
|
|
|
if (numThrusterEffects == 0) { thrusterEffects = new ThrusterEffects[arrayLength]; }
|
|
else if (arrayLength != numThrusterEffects) { System.Array.Resize(ref thrusterEffects, arrayLength); }
|
|
|
|
numThrusterEffects = thrusterEffects == null ? 0 : thrusterEffects.Length;
|
|
|
|
for (componentIndex = 0; componentIndex < arrayLength; componentIndex++)
|
|
{
|
|
if (thrusterEffectObjects[componentIndex] != null)
|
|
{
|
|
// Check if the ThrusterEffects component has previously been added. If not, add it now.
|
|
ThrusterEffects thrusterEffect = thrusterEffectObjects[componentIndex].GetComponent<ThrusterEffects>();
|
|
if (thrusterEffect == null) { thrusterEffect = thrusterEffectObjects[componentIndex].AddComponent<ThrusterEffects>(); }
|
|
|
|
thrusterEffects[componentIndex] = thrusterEffect;
|
|
thrusterEffects[componentIndex].Initialise();
|
|
}
|
|
else
|
|
{
|
|
|
|
//thrusterEffects[componentIndex].Clear();
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Clean up old thruster effect class instances
|
|
if (numThrusterEffects > 0)
|
|
{
|
|
for (componentIndex = 0; componentIndex < numThrusterEffects; componentIndex++)
|
|
{
|
|
thrusterEffects[componentIndex].Clear();
|
|
}
|
|
System.Array.Clear(thrusterEffects, 0, numThrusterEffects);
|
|
}
|
|
thrusterEffects = null;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public API Methods - Reset, Enable, Disable Ship
|
|
/// <summary>
|
|
/// A fast way of re-initialising a ship without changing its position or core
|
|
/// settings. Typically called when re-initialising a scene or bringing a ship
|
|
/// out of hibernation. If you want to temporarily stop a ship from moving, like
|
|
/// when a user brings up a menu, call DisableShip() and EnableShip() instead.
|
|
/// </summary>
|
|
public void ResetShip()
|
|
{
|
|
// Temporily prevent other things from occuring
|
|
isInitialised = false;
|
|
|
|
// Disable ship but don't make it invisible. Reset velocity
|
|
EnableOrDisableShip(false, false, true);
|
|
|
|
// Stop all Thrusters on the ship
|
|
arrayLength = thrusterEffects == null ? 0 : thrusterEffects.Length;
|
|
for (componentIndex = 0; componentIndex < arrayLength; componentIndex++)
|
|
{
|
|
if (thrusterEffects[componentIndex] != null)
|
|
{
|
|
thruster = shipInstance.thrusterList[componentIndex];
|
|
if (thruster != null) { thrusterEffects[componentIndex].Stop(); }
|
|
}
|
|
}
|
|
|
|
isRespawning = false;
|
|
|
|
// (Re)initialise all necessary ship data
|
|
shipInstance.UpdatePositionAndMovementData(transform, rBody);
|
|
|
|
// NOTE: This may cause issues with Radar as it sets radarItemIndex = -1
|
|
// If we see a problem, maybe we need to remove from radar first.
|
|
shipInstance.Initialise(transform);
|
|
|
|
// Reset the health of the ship
|
|
shipInstance.ResetHealth();
|
|
|
|
// Get the last damage event index of the ship
|
|
lastDamageEventIndex = shipInstance.LastDamageEventIndex();
|
|
|
|
// Reset rumble
|
|
damageRumbleTimer = -1f;
|
|
|
|
isInitialised = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enables the ship and make visible.
|
|
/// If resetVelocity is set to true, resets the velocity of the ship to zero.
|
|
/// </summary>
|
|
/// <param name="resetVelocity"></param>
|
|
public void EnableShip (bool resetVelocity)
|
|
{
|
|
EnableOrDisableShip(true, true, resetVelocity);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enables the ship. If updateVisibility is set to true, makes the ship visible.
|
|
/// If resetVelocity is set to true, resets the velocity of the ship to zero.
|
|
/// </summary>
|
|
/// <param name="updateVisibility"></param>
|
|
/// <param name="resetVelocity"></param>
|
|
public void EnableShip(bool updateVisibility, bool resetVelocity)
|
|
{
|
|
EnableOrDisableShip(true, updateVisibility, resetVelocity);
|
|
}
|
|
|
|
|
|
public bool IsActive => isActive;
|
|
private bool isActive = false;
|
|
|
|
public void LittleEnableShip()
|
|
{
|
|
isActive = true;
|
|
}
|
|
|
|
public void LittleDisableShip()
|
|
{
|
|
isActive = false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Disables the ship. If updateVisibility is set to true, makes the ship invisible.
|
|
/// </summary>
|
|
/// <param name="updateVisibility"></param>
|
|
public void DisableShip(bool updateVisibility)
|
|
{
|
|
EnableOrDisableShip(false, updateVisibility, false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns whether the ship is currently enabled. When disabled, it
|
|
/// cannot move, be visible or radar, nor received damage and be destroyed.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public bool ShipIsEnabled()
|
|
{
|
|
return shipIsEnabled;
|
|
}
|
|
#endregion
|
|
|
|
#region Public API Methods - Enable, Disable Ship Movement
|
|
|
|
/// <summary>
|
|
/// Returns whether the ship movement is curently enabled.
|
|
/// See also ShipIsEnabled(). A ship's movement may be disabled
|
|
/// while it can still receive damaage, be destroyed or be visible
|
|
/// to radar.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public bool ShipMovementIsEnabled()
|
|
{
|
|
return isMovementEnabled;
|
|
}
|
|
|
|
/// <summary>
|
|
/// This is a subset of EnableShip(...).
|
|
/// It only applies to:
|
|
/// 1) Physics
|
|
/// 2) Thruster Effects
|
|
/// 3) User or AI Input to the Ship
|
|
/// 4) Sound FX (audio)
|
|
/// If a ship is also disabled, this will re-enable the ship.
|
|
/// </summary>
|
|
/// <param name="resetVelocity"></param>
|
|
public void EnableShipMovement(bool resetVelocity)
|
|
{
|
|
if (isInitialised)
|
|
{
|
|
if (!shipIsEnabled) { EnableOrDisableShip(true, true, resetVelocity); }
|
|
else { EnableOrDisableShipMovement(true, resetVelocity); }
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// This is a subset of DisableShip(...).
|
|
/// It only applies to:
|
|
/// 1) Physics
|
|
/// 2) Thruster Effects
|
|
/// 3) User or AI Input to the Ship
|
|
/// 4) Sound FX (audio)
|
|
/// </summary>
|
|
public void DisableShipMovement()
|
|
{
|
|
if (isInitialised)
|
|
{
|
|
EnableOrDisableShipMovement(false, false);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Teleport the ship to a new location by moving by an amount
|
|
/// in the x, y and z directions. This could be useful if changing
|
|
/// the origin or centre of your world to compensate for float-point
|
|
/// error.
|
|
/// NOTE: This does not alter the current Respawn position.
|
|
/// </summary>
|
|
/// <param name="delta"></param>
|
|
/// <param name="resetVelocity"></param>
|
|
public void TelePort (Vector3 delta, bool resetVelocity)
|
|
{
|
|
// Remember current situation of the ship
|
|
bool isMovementEnabled = false;
|
|
|
|
if (isInitialised) { DisableShipMovement(); isMovementEnabled = true; }
|
|
|
|
transform.position += delta;
|
|
|
|
// If movement was enabled, re-enable it
|
|
if (isMovementEnabled) { EnableShipMovement(resetVelocity); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Teleport the ship to a new location with a new rotation.
|
|
/// NOTE: This does not alter the current Respawn position.
|
|
/// </summary>
|
|
/// <param name="newPosition"></param>
|
|
/// <param name="newRotation"></param>
|
|
/// <param name="resetVelocity"></param>
|
|
public void TelePort (Vector3 newPosition, Quaternion newRotation, bool resetVelocity)
|
|
{
|
|
// Remember current situation of the ship
|
|
bool isMovementEnabled = false;
|
|
|
|
if (isInitialised) { DisableShipMovement(); isMovementEnabled = true; }
|
|
|
|
transform.SetPositionAndRotation(newPosition, newRotation);
|
|
|
|
// If movement was enabled, re-enable it
|
|
if (isMovementEnabled) { EnableShipMovement(resetVelocity); }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public API Methods - Respawning
|
|
|
|
/// <summary>
|
|
/// If the ship is currently respawning, this will stop
|
|
/// the countdown timer, preventing the ship from respawning
|
|
/// until ResumeRespawning() is called.
|
|
/// NOTE: Has no effect if not already respawning.
|
|
/// </summary>
|
|
public void PauseRespawning()
|
|
{
|
|
if (isRespawning) { isRespawingPaused = true; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// If respawning is currently paused, the respawning
|
|
/// timer will now continue until the ship is respawned.
|
|
/// NOTE: Has no effect if respawningMode is DontRespawn
|
|
/// </summary>
|
|
public void ResumeRespawning()
|
|
{
|
|
isRespawingPaused = false;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public API Methods - Radar
|
|
|
|
/// <summary>
|
|
/// The ship will no longer send tracking information to the radar system.
|
|
/// If you want to change the visibility to other radar consumers, consider
|
|
/// changing the radar item data rather than disabling the radar and (later)
|
|
/// calling EnableRadar again. When using a Localised ShipDamageModel, it
|
|
/// will also disable radar on all localised Damage Regions.
|
|
/// </summary>
|
|
public void DisableRadar()
|
|
{
|
|
if (isInitialised)
|
|
{
|
|
if (shipInstance.isRadarEnabled && sscRadar != null && sscRadar.IsInitialised)
|
|
{
|
|
// If enabled, remove the localised damage regions from radar
|
|
if (shipInstance.shipDamageModel == Ship.ShipDamageModel.Localised)
|
|
{
|
|
for (int dmIdx = 0; dmIdx < shipInstance.numLocalisedDamageRegions; dmIdx++)
|
|
{
|
|
DisableRadar(shipInstance.localisedDamageRegionList[dmIdx]);
|
|
}
|
|
}
|
|
|
|
// Remove this ship from radar
|
|
sscRadar.RemoveItem(shipInstance.radarItemIndex);
|
|
}
|
|
shipInstance.sscRadarPacket = null;
|
|
shipInstance.isRadarEnabled = false;
|
|
shipInstance.radarItemIndex = -1;
|
|
}
|
|
sscRadar = null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// The ship will no longer send tracking information to the radar system for this
|
|
/// damage region. If you want to change the visibility to other radar consumers, consider
|
|
/// changing the radar item data rather than disabling the radar and (later)
|
|
/// calling EnableRadar(damageRegion) again.
|
|
/// NOTE: You do not need to call this if calling DisableRadar() for the ship.
|
|
/// </summary>
|
|
/// <param name="damageRegion"></param>
|
|
public void DisableRadar(DamageRegion damageRegion)
|
|
{
|
|
if (damageRegion != null)
|
|
{
|
|
if (damageRegion.isRadarEnabled && damageRegion.radarItemIndex >= 0)
|
|
{
|
|
sscRadar.RemoveItem(damageRegion.radarItemIndex);
|
|
}
|
|
damageRegion.sscRadarPacket = null;
|
|
damageRegion.isRadarEnabled = false;
|
|
damageRegion.radarItemIndex = -1;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enable the ship to send tracking information to the
|
|
/// radar system. The ship must first be initialised.
|
|
/// </summary>
|
|
public void EnableRadar()
|
|
{
|
|
if (isInitialised)
|
|
{
|
|
sscRadar = SSCRadar.GetOrCreateRadar();
|
|
|
|
if (sscRadar != null)
|
|
{
|
|
shipInstance.isRadarEnabled = true;
|
|
InitialiseRadar();
|
|
RadarAutoSetType();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enable the ship to send tracking information to the radar system
|
|
/// for this damage region. The ship must be initialised and radar must
|
|
/// already be enabled for the ship. See also EnableRadar().
|
|
/// </summary>
|
|
/// <param name="damageRegion"></param>
|
|
public void EnableRadar(DamageRegion damageRegion)
|
|
{
|
|
if (isInitialised && damageRegion.radarItemIndex == -1)
|
|
{
|
|
InitialiseLocalDamageRegionRadar(damageRegion);
|
|
damageRegion.isRadarEnabled = damageRegion.radarItemIndex >= 0;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public API Methods - Ship Input
|
|
|
|
/// <summary>
|
|
/// Provide an instance of shipInput, and have it populated with the current values from the ship.
|
|
/// </summary>
|
|
/// <param name="shipInput"></param>
|
|
public void GetShipInput (ShipInput shipInput)
|
|
{
|
|
if (shipInput != null)
|
|
{
|
|
shipInput.horizontal = pilotForceInput.x;
|
|
shipInput.vertical = pilotForceInput.y;
|
|
shipInput.longitudinal = pilotForceInput.z;
|
|
shipInput.pitch = pilotMomentInput.x;
|
|
shipInput.yaw = pilotMomentInput.y;
|
|
shipInput.roll = pilotMomentInput.z;
|
|
shipInput.primaryFire = pilotPrimaryFireInput;
|
|
shipInput.secondaryFire = pilotSecondaryFireInput;
|
|
shipInput.dock = pilotDockInput;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sends the specified input to the ship control module in order to control the ship.
|
|
/// By default all data inputs should be enabled even when sending 0 values.
|
|
/// </summary>
|
|
/// <param name="shipInput"></param>
|
|
public void SendInput(ShipInput shipInput)
|
|
{
|
|
if (shipInput.isHorizontalDataEnabled) { pilotForceInput.x = shipInput.horizontal; }
|
|
if (shipInput.isVerticalDataEnabled) { pilotForceInput.y = shipInput.vertical; }
|
|
if (shipInput.isLongitudinalDataEnabled) { pilotForceInput.z = shipInput.longitudinal; }
|
|
|
|
if (shipInput.isPitchDataEnabled) { pilotMomentInput.x = shipInput.pitch; }
|
|
if (shipInput.isYawDataEnabled) { pilotMomentInput.y = shipInput.yaw; }
|
|
if (shipInput.isRollDataEnabled) { pilotMomentInput.z = shipInput.roll; }
|
|
|
|
if (shipInput.isPrimaryFireDataEnabled) { pilotPrimaryFireInput = shipInput.primaryFire; }
|
|
if (shipInput.isSecondaryFireDataEnabled) { pilotSecondaryFireInput = shipInput.secondaryFire; }
|
|
if (shipInput.isDockDataEnabled) { pilotDockInput = shipInput.dock; }
|
|
}
|
|
#endregion
|
|
|
|
#region Public API Methods - Docking
|
|
|
|
/// <summary>
|
|
/// Is the ShipDocking component attached to this ship, and if so, is the ship's state 'Docked'?
|
|
/// NOTE: This does not mean it must be docked with a ShipDockingStation. For that, you would
|
|
/// need to check the ShipDockingStation's docking points.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public bool ShipIsDocked()
|
|
{
|
|
return isShipDockingAttached && shipDocking.GetState() == ShipDocking.DockingState.Docked;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Is the ShipDocking component attached to this ship, and if so, is the ship's state 'Not Docked'?
|
|
/// NOTE: This does not mean it must be not docked with a ShipDockingStation. For that, you would
|
|
/// need to check the ShipDockingStation's docking points.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public bool ShipIsNotDocked()
|
|
{
|
|
return isShipDockingAttached && shipDocking.GetStateInt() == ShipDocking.notDockedInt;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Retrieves a reference to the ShipDocking script if one was attached at the time this
|
|
/// module was initiated.
|
|
/// </summary>
|
|
/// <param name="forceCheck">Ignore cached value and call GetComponent when true</param>
|
|
/// <returns></returns>
|
|
public ShipDocking GetShipDocking(bool forceCheck = false)
|
|
{
|
|
if (forceCheck) { FetchShipDocking(); }
|
|
return isShipDockingAttached ? shipDocking : null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public API Methods - Ship AI
|
|
|
|
/// <summary>
|
|
/// Retrieves a reference to the ShipAIInputModule script if one was attached at the time
|
|
/// this module was initialised.
|
|
/// </summary>
|
|
/// <param name="forceCheck">Ignore cached value and call GetComponent when true</param>
|
|
/// <returns></returns>
|
|
public ShipAIInputModule GetShipAIInputModule(bool forceCheck = false)
|
|
{
|
|
if (forceCheck) { FetchShipAIInputModule(); }
|
|
return isShipAIInputModuleAttached ? shipAIInputModule : null;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public API Methods - Thrusters
|
|
|
|
/// <summary>
|
|
/// Add 1 second of forward boost.
|
|
/// See also StopBoost().
|
|
/// For more control, see shipInstance.AddBoost(..).
|
|
/// </summary>
|
|
/// <param name="forceAmountKNeutons"></param>
|
|
public void AddBoost (float forceAmountKNeutons, float time = 1)
|
|
{
|
|
if (isInitialised)
|
|
{
|
|
shipInstance.AddBoost(Vector3.forward, forceAmountKNeutons * 1000f, time);
|
|
}
|
|
}
|
|
|
|
public void MultiplyVelocity(float percent)
|
|
{
|
|
if (isInitialised)
|
|
{
|
|
rBody.velocity *= percent;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enable all the thruster effects where the gameobject contains the specified string.
|
|
/// Typically used to turn on an effect to improve the quality or look of a game.
|
|
/// Call ReinitialiseThrusterEffects() after calling 1 or more of these methods.
|
|
/// If you wish to enable or start a thruster, it is more likely you want to use EnableShip()
|
|
/// or EnableShipMovement().
|
|
/// WARNING: This will generate Garbage so use sparingly.
|
|
/// </summary>
|
|
/// <param name="effectNameContains"></param>
|
|
public void EnableThrusterEffects(string effectNameContains)
|
|
{
|
|
arrayLength = thrusterEffectObjects == null ? 0 : thrusterEffectObjects.Length;
|
|
|
|
// Look through all the thrusters for effects parent gameobjects
|
|
for (componentIndex = 0; componentIndex < arrayLength; componentIndex++)
|
|
{
|
|
GameObject thrusterEffectsGO = thrusterEffectObjects[componentIndex];
|
|
if (thrusterEffectsGO != null)
|
|
{
|
|
// If the top level effects gameobject contains the string, then disable it.
|
|
if (thrusterEffectsGO.name.Contains(effectNameContains)) { thrusterEffectsGO.SetActive(true); }
|
|
else
|
|
{
|
|
// Search through the child objects including inactive transforms
|
|
Transform[] childTrfms = thrusterEffectsGO.GetComponentsInChildren<Transform>(true);
|
|
int numChildren = childTrfms == null ? 0 : childTrfms.Length;
|
|
for (int t = 0; t < numChildren; t++)
|
|
{
|
|
if (childTrfms[t] != null && childTrfms[t].name.Contains(effectNameContains))
|
|
{
|
|
childTrfms[t].gameObject.SetActive(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Disable all the thruster effects where the gameobject contains the specified string.
|
|
/// Typically used to turn off an effect to reduce the performance overhead of running it.
|
|
/// Call ReinitialiseThrusterEffects() after calling 1 or more of these methods.
|
|
/// If you wish to pause or stop a thruster, it is more likely you want to use DisableShip()
|
|
/// or DisableShipMovement().
|
|
/// WARNING: This will generate Garbage so use sparingly.
|
|
/// </summary>
|
|
/// <param name="effectNameContains"></param>
|
|
public void DisableThrusterEffects (string effectNameContains)
|
|
{
|
|
arrayLength = thrusterEffectObjects == null ? 0 : thrusterEffectObjects.Length;
|
|
|
|
// Look through all the thrusters for effects parent gameobjects
|
|
for (componentIndex = 0; componentIndex < arrayLength; componentIndex++)
|
|
{
|
|
GameObject thrusterEffectsGO = thrusterEffectObjects[componentIndex];
|
|
if (thrusterEffectsGO != null)
|
|
{
|
|
// If the top level effects gameobject contains the string, then disable it.
|
|
if (thrusterEffectsGO.name.Contains(effectNameContains)) { thrusterEffectsGO.SetActive(false); }
|
|
else
|
|
{
|
|
// Search through the child objects
|
|
Transform[] childTrfms = thrusterEffectsGO.GetComponentsInChildren<Transform>();
|
|
int numChildren = childTrfms == null ? 0 : childTrfms.Length;
|
|
for (int t = 0; t < numChildren; t++)
|
|
{
|
|
if (childTrfms[t] != null && childTrfms[t].name.Contains(effectNameContains))
|
|
{
|
|
childTrfms[t].gameObject.SetActive(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Set the maximum volume for a given thruster. Numbers begin at 1. Values should be between 0.0 and 1.0.
|
|
/// </summary>
|
|
/// <param name="thrusterNumber"></param>
|
|
/// <param name="newMaxVolume"></param>
|
|
public void SetThrusterMaxVolume (int thrusterNumber, float newMaxVolume)
|
|
{
|
|
if (isInitialised && thrusterNumber > 0 && thrusterNumber <= (thrusterEffects == null ? 0 : thrusterEffects.Length))
|
|
{
|
|
if (thrusterEffects[thrusterNumber - 1] != null)
|
|
{
|
|
thrusterEffects[thrusterNumber - 1].SetMaxVolume(newMaxVolume);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Begin to shut down the thrusters. Optionally override the shutdown duration.
|
|
/// </summary>
|
|
public void ShutdownThrusterSystems (bool isInstantShutdown = false)
|
|
{
|
|
if (isInitialised)
|
|
{
|
|
shipInstance.ShutdownThrusterSystems(isInstantShutdown);
|
|
|
|
//if (!shipInstance.isThrusterSystemsStarted)
|
|
{
|
|
UpdateThrusterFX();
|
|
//StopThrusterEffects();
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Begin to bring the thrusters online. Optionally, override the startup duration.
|
|
/// As soon as the systems begin to start up, shipInstance.isThrusterSystemsStarted will be true.
|
|
/// </summary>
|
|
/// <param name="isInstantStartup"></param>
|
|
public void StartupThrusterSystems (bool isInstantStartup = false)
|
|
{
|
|
if (isInitialised)
|
|
{
|
|
shipInstance.StartupThrusterSystems(isInstantStartup);
|
|
PauseOrResumeThrusters(true);
|
|
UpdateThrusterFX();
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Immediately stop any boost that has been applied with AddBoost(..).
|
|
/// </summary>
|
|
public void StopBoost()
|
|
{
|
|
if (isInitialised) { shipInstance.StopBoost(); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Stop all Thruster Effects on the ship
|
|
/// </summary>
|
|
public void StopThrusterEffects()
|
|
{
|
|
arrayLength = thrusterEffects == null ? 0 : thrusterEffects.Length;
|
|
for (componentIndex = 0; componentIndex < arrayLength; componentIndex++)
|
|
{
|
|
if (thrusterEffects[componentIndex] != null)
|
|
{
|
|
thrusterEffects[componentIndex].Stop();
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
}
|
|
|
|
#region Public Structures
|
|
|
|
/// <summary>
|
|
/// Paramaters structure for CallbackOnHit (callback for Ship Control Module).
|
|
/// We do not recommend keeping references to any fields within this structure.
|
|
/// Use them in one frame, then discard them.
|
|
/// </summary>
|
|
public struct CallbackOnShipHitParameters
|
|
{
|
|
/// <summary>
|
|
/// Hit information for the raycast hit against the ship.
|
|
/// </summary>
|
|
public RaycastHit hitInfo;
|
|
/// <summary>
|
|
/// Prefab for the projectile that hit the ship.
|
|
/// </summary>
|
|
public ProjectileModule projectilePrefab;
|
|
/// <summary>
|
|
/// Prefab for the beam that hit the ship
|
|
/// </summary>
|
|
public BeamModule beamPrefab;
|
|
/// <summary>
|
|
/// Amount of damage done by the projectile or beam.
|
|
/// </summary>
|
|
public float damageAmount;
|
|
/// <summary>
|
|
/// The squadron ID of the ship that fired the projectile or beam.
|
|
/// </summary>
|
|
public int sourceSquadronId;
|
|
/// <summary>
|
|
/// Ship that fired the projectile or beam, else 0
|
|
/// </summary>
|
|
public int sourceShipId;
|
|
};
|
|
|
|
#endregion
|
|
}
|