979 lines
43 KiB
C#
979 lines
43 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using UnityEngine;
|
|
|
|
// Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved.
|
|
namespace SciFiShipController
|
|
{
|
|
/// <summary>
|
|
/// This component can be attached to a ShipControlModule or a GameObject.
|
|
/// When attached to a (mother)ship, it would typically be a slower moving capital ship
|
|
/// or large platform. If attached to GameObject without a shipControlModule,
|
|
/// it should be a stationary object in the scene.
|
|
/// </summary>
|
|
[AddComponentMenu("Sci-Fi Ship Controller/Docking Components/Ship Docking Station")]
|
|
[HelpURL("http://scsmmedia.com/ssc-documentation")]
|
|
public class ShipDockingStation : MonoBehaviour
|
|
{
|
|
#region Enumerations
|
|
|
|
#endregion
|
|
|
|
#region Public Variables
|
|
|
|
/// <summary>
|
|
/// If enabled, the Initialise() will be called as soon as Awake() runs. This should be disabled if you are
|
|
/// instantiating the ShipDockingStation through code.
|
|
/// </summary>
|
|
public bool initialiseOnAwake = false;
|
|
|
|
public List<ShipDockingPoint> shipDockingPointList;
|
|
|
|
/// <summary>
|
|
/// The colour of the selected Docking Points in the scene view
|
|
/// Non-selected docking points are slightly transparent
|
|
/// </summary>
|
|
public Color dockingPointGizmoColour = new Color(1f, 0.92f, 0.016f, 1.0f);
|
|
|
|
/// <summary>
|
|
/// Methods that get called immediately before Undock or UndockDelayed are executed.
|
|
/// WARNING: Do NOT call UndockShip from this event.
|
|
/// </summary>
|
|
public SSCDockingEvt1 onPreUndock = null;
|
|
|
|
/// <summary>
|
|
/// Methods that get called immediately after docking is complete.
|
|
/// WARNING: Do NOT call DockShip from this event.
|
|
/// </summary>
|
|
public SSCDockingEvt1 onPostDocked = null;
|
|
|
|
/// <summary>
|
|
/// [INTERNAL ONLY]
|
|
/// </summary>
|
|
public bool isDockingPointListExpanded = true;
|
|
|
|
/// <summary>
|
|
/// [INTERNAL ONLY]
|
|
/// </summary>
|
|
[HideInInspector] public bool allowRepaint = false;
|
|
|
|
#endregion
|
|
|
|
#region Public Properties
|
|
|
|
public bool IsInitialised { get; private set; }
|
|
|
|
public bool IsDockingPointPathsInitialised { get; private set; }
|
|
|
|
public int NumberOfDockingPoints { get { return shipDockingPointList == null ? 0 : shipDockingPointList.Count; } }
|
|
|
|
/// <summary>
|
|
/// [READONLY] Does this ShipDockingStation have a (mother)ship attached to it
|
|
/// and has it been initialised?
|
|
/// </summary>
|
|
public bool IsMotherShip { get { return isStationShipInitialised; } }
|
|
|
|
/// <summary>
|
|
/// [READONLY] The runtime unique identifier for this Ship Docking Station
|
|
/// </summary>
|
|
public int ShipDockingStationId { get { return shipDockingStationId; } }
|
|
|
|
#endregion
|
|
|
|
#region Public Static Properties
|
|
|
|
public static readonly int NoDockingPoint = -1;
|
|
|
|
#endregion
|
|
|
|
#region Private variables
|
|
|
|
/// <summary>
|
|
/// If the docking station is attached to a ship, cache the
|
|
/// shipControlModule.
|
|
/// </summary>
|
|
private ShipControlModule shipControlModuleStation;
|
|
private bool isStationShipInitialised = false;
|
|
private RigidbodyInterpolation originalRBInterpolation = RigidbodyInterpolation.None;
|
|
private bool isOriginalRBInterpolationSet = false;
|
|
private Vector3 initialStationPosition;
|
|
private Quaternion initialStationRotation = Quaternion.identity;
|
|
private Vector3 currentStationPosition = Vector3.zero;
|
|
private Quaternion currentStationRotation = Quaternion.identity;
|
|
private Vector3 deltaPosition;
|
|
private Quaternion deltaRotation = Quaternion.identity;
|
|
private Vector3 pathVelocity, pathAngularVelocity;
|
|
private SSCManager sscManager;
|
|
private int numDockingPoints = 0;
|
|
private int shipDockingStationId = -1;
|
|
private Vector3 tfrmScale = Vector3.one;
|
|
|
|
/// <summary>
|
|
/// [INTERNAL ONLY]
|
|
/// Remember which tabs etc were shown in the editor
|
|
/// </summary>
|
|
[SerializeField] private int selectedTabInt = 0;
|
|
|
|
#endregion
|
|
|
|
#region Initialisation Methods
|
|
|
|
void Awake()
|
|
{
|
|
if (initialiseOnAwake) { Initialise(true); }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Initialise the docking station. This will have no effect if IsInitialised is true.
|
|
/// If the station is attached to a mothership (rather than a stationary gameobject), the ship
|
|
/// will be initialised automatically if it is not already initialised.
|
|
/// Ships that are assigned with to a docking point, will also be automatically initialised.
|
|
/// </summary>
|
|
/// <param name="initDockingPointPaths">Assumes ShipDockingStation position and rotation is same as when Entry and Exit Paths where created.
|
|
/// If not, set to false and afterwards call InitialiseDockingPointsPaths(..)</param>
|
|
public void Initialise (bool initDockingPointPaths = true)
|
|
{
|
|
if (!IsInitialised)
|
|
{
|
|
if (shipDockingPointList == null) { shipDockingPointList = new List<ShipDockingPoint>(10); }
|
|
|
|
tfrmScale = transform.localScale;
|
|
shipDockingStationId = GetInstanceID();
|
|
|
|
// Check to see if this Docking Station is attached to a large ship
|
|
if (TryGetComponent(out shipControlModuleStation))
|
|
{
|
|
// Auto-initialise mothership.
|
|
if (!shipControlModuleStation.IsInitialised) { shipControlModuleStation.InitialiseShip(); }
|
|
|
|
// cache to avoid having to check for null etc in FixedUpdate
|
|
isStationShipInitialised = shipControlModuleStation.IsInitialised;
|
|
|
|
if (isStationShipInitialised)
|
|
{
|
|
initialStationPosition = shipControlModuleStation.shipInstance.TransformPosition;
|
|
initialStationRotation = shipControlModuleStation.shipInstance.TransformRotation;
|
|
}
|
|
}
|
|
|
|
sscManager = SSCManager.GetOrCreateManager();
|
|
|
|
// cache the number of docking points
|
|
numDockingPoints = shipDockingPointList == null ? 0 : shipDockingPointList.Count;
|
|
|
|
// Keep compiler happy
|
|
if (selectedTabInt < 0) { }
|
|
|
|
IsInitialised = true;
|
|
|
|
// Check to see if any ships are assigned the docking points
|
|
// This need to be AFTER IsInitialised = true.
|
|
InitialiseDockingPointShips();
|
|
|
|
if (initDockingPointPaths) { InitialiseDockingPointsPaths(Vector3.zero, Quaternion.identity); }
|
|
|
|
SetInterpolate(true);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Check to see if any ships are assigned the docking points.
|
|
/// Set the DockingState of the ships that are assigned a docking point.
|
|
/// Initialise the ships (if required).
|
|
/// </summary>
|
|
private void InitialiseDockingPointShips()
|
|
{
|
|
for (int dpIdx = 0; dpIdx < numDockingPoints; dpIdx++)
|
|
{
|
|
ShipDockingPoint shipDockingPoint = shipDockingPointList[dpIdx];
|
|
if (shipDockingPoint != null)
|
|
{
|
|
if (shipDockingPoint.dockedShip != null)
|
|
{
|
|
// Check for a ShipDocking component on the ship that is assigned to the docking point
|
|
ShipDocking shipDocking = shipDockingPoint.dockedShip.GetShipDocking(true);
|
|
|
|
if (shipDocking != null)
|
|
{
|
|
if (!shipDockingPoint.dockedShip.IsInitialised) { shipDockingPoint.dockedShip.InitialiseShip(); }
|
|
if (!shipDocking.IsInitialised) { shipDocking.Initialise(); }
|
|
|
|
shipDocking.shipDockingStation = this;
|
|
shipDocking.DockingPointId = dpIdx;
|
|
shipDocking.dockWithShip = shipControlModuleStation;
|
|
shipDocking.SetState((ShipDocking.DockingState)shipDocking.initialDockingState);
|
|
}
|
|
else
|
|
{
|
|
#if UNITY_EDITOR
|
|
Debug.LogWarning("ShipDockingStation.Initialise - " + shipDockingPoint.dockedShip.name + " is missing a ShipDocking component, but is assigned to Docking Point " + (dpIdx + 1) + " on Docking Station " + gameObject.name);
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Update Methods
|
|
|
|
private void Update()
|
|
{
|
|
// Is this docking station attached to a mothership?
|
|
if (isStationShipInitialised)
|
|
{
|
|
currentStationPosition = shipControlModuleStation.shipInstance.TransformPosition;
|
|
currentStationRotation = shipControlModuleStation.shipInstance.TransformRotation;
|
|
|
|
deltaPosition = currentStationPosition - initialStationPosition;
|
|
deltaRotation = currentStationRotation * Quaternion.Inverse(initialStationRotation);
|
|
|
|
// Has the docking station moved during this last frame?
|
|
if (deltaPosition.x != 0f || deltaPosition.y != 0f || deltaPosition.z != 0f)
|
|
{
|
|
// The sequence number if used to ensure PathData is not updated multiple times per frame
|
|
float updateSeqNumber = Time.time;
|
|
|
|
// AI path following requires the velocity, angular velocity, and current position of the docking station
|
|
pathVelocity = shipControlModuleStation.shipInstance.WorldVelocity;
|
|
pathAngularVelocity = shipControlModuleStation.shipInstance.WorldAngularVelocity;
|
|
|
|
// If configured, move Entry and Exit path points in the scene to match the movement of the docking station
|
|
for (int dpIdx = 0; dpIdx < numDockingPoints; dpIdx++)
|
|
{
|
|
ShipDockingPoint shipDockingPoint = shipDockingPointList[dpIdx];
|
|
|
|
if (shipDockingPoint != null)
|
|
{
|
|
if (shipDockingPoint.guidHashEntryPath != 0)
|
|
{
|
|
PathData pathData = sscManager.GetPath(shipDockingPoint.guidHashEntryPath);
|
|
sscManager.MoveLocations(pathData, updateSeqNumber, currentStationPosition, deltaPosition, deltaRotation, pathVelocity, pathAngularVelocity);
|
|
}
|
|
|
|
if (shipDockingPoint.guidHashExitPath != 0 && shipDockingPoint.guidHashExitPath != shipDockingPoint.guidHashEntryPath)
|
|
{
|
|
PathData pathData = sscManager.GetPath(shipDockingPoint.guidHashExitPath);
|
|
sscManager.MoveLocations(pathData, updateSeqNumber, currentStationPosition, deltaPosition, deltaRotation, pathVelocity, pathAngularVelocity);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Private and Internal Methods
|
|
|
|
/// <summary>
|
|
/// Take into consideration the local scale (set at initialisation) and return the relative position
|
|
/// of the Ship Docking Point on the ShipDockingStation.
|
|
/// </summary>
|
|
/// <param name="shipDockingPoint"></param>
|
|
/// <returns></returns>
|
|
internal Vector3 GetScaledRelativePosition(ShipDockingPoint shipDockingPoint)
|
|
{
|
|
if (shipDockingPoint != null)
|
|
{
|
|
return new Vector3(shipDockingPoint.relativePosition.x / tfrmScale.x, shipDockingPoint.relativePosition.y / tfrmScale.y, shipDockingPoint.relativePosition.z / tfrmScale.z);
|
|
}
|
|
else { return Vector3.zero; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Will set RigidBody Interpolate if isOn is true,
|
|
/// else will set it to None. It applies to all ships
|
|
/// assigned to docking station points (except those in
|
|
/// and undocked state, AND to the mothership of the
|
|
/// Docking Station, if it has one.
|
|
/// NOTE: Only applies to initialised ships.
|
|
/// </summary>
|
|
/// <param name="isOn"></param>
|
|
internal void SetInterpolate(bool isOn)
|
|
{
|
|
// Does this Docking Station have a mothership?
|
|
if (isStationShipInitialised)
|
|
{
|
|
// If we haven't recorded the original interpolation mode,
|
|
// record it now and remember that it has been recorded.
|
|
if (!isOriginalRBInterpolationSet)
|
|
{
|
|
isOriginalRBInterpolationSet = true;
|
|
originalRBInterpolation = shipControlModuleStation.ShipRigidbody.interpolation;
|
|
}
|
|
|
|
shipControlModuleStation.ShipRigidbody.interpolation = isOn ? RigidbodyInterpolation.Interpolate : RigidbodyInterpolation.None;
|
|
}
|
|
|
|
if (IsInitialised)
|
|
{
|
|
// For ships that aren't Undocked but assigned to a docking point,
|
|
// remmeber their original interpolation setting, then set according
|
|
// to the paramter isOn.
|
|
for (int dpIdx = 0; dpIdx < numDockingPoints; dpIdx++)
|
|
{
|
|
ShipDockingPoint shipDockingPoint = shipDockingPointList[dpIdx];
|
|
// Is there an initialised ship assigned to this docking point?
|
|
if (shipDockingPoint != null && shipDockingPoint.dockedShip != null && shipDockingPoint.dockedShip.IsInitialised)
|
|
{
|
|
// Is the ship docked, undocking or docking?
|
|
ShipDocking shipDocking = shipDockingPoint.dockedShip.GetShipDocking(false);
|
|
if (shipDocking != null && shipDocking.GetStateInt() != ShipDocking.notDockedInt)
|
|
{
|
|
// If it hasn't already been recorded,
|
|
// save the original interpolation setting
|
|
shipDocking.SaveInterpolation();
|
|
|
|
shipDockingPoint.dockedShip.ShipRigidbody.interpolation = isOn ? RigidbodyInterpolation.Interpolate : RigidbodyInterpolation.None;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Reset the interpolation of the Station to the original setting.
|
|
/// This does not update the docked ships.
|
|
/// NOTE: NOT IN USE - can be changed as required.
|
|
/// </summary>
|
|
internal void ResetInterpolation()
|
|
{
|
|
if (isOriginalRBInterpolationSet && isStationShipInitialised)
|
|
{
|
|
shipControlModuleStation.ShipRigidbody.interpolation = originalRBInterpolation;
|
|
isOriginalRBInterpolationSet = false;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Delay the undocking manoeuvre.
|
|
/// </summary>
|
|
/// <param name="dockedShip"></param>
|
|
/// <param name="shipDocking"></param>
|
|
/// <param name="delayTime"></param>
|
|
/// <returns></returns>
|
|
private IEnumerator UndockShipDelayed(ShipControlModule dockedShip, ShipDocking shipDocking, float delayTime)
|
|
{
|
|
yield return new WaitForSeconds(delayTime);
|
|
|
|
// Check that the docked ship hasn't been destroyed during the delay
|
|
if (dockedShip != null && !dockedShip.shipInstance.Destroyed())
|
|
{
|
|
UnDockShipInternal(dockedShip, shipDocking);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Undock a ship from the docking point.
|
|
/// </summary>
|
|
/// <param name="dockedShip"></param>
|
|
private void UnDockShipInternal (ShipControlModule dockedShip, ShipDocking shipDocking)
|
|
{
|
|
ShipAIInputModule aiShip = dockedShip.GetShipAIInputModule(false);
|
|
|
|
// If it is an initialised AI ship then attempt to go into the Undocking state (and fly along the exit path - assuming there is one...)
|
|
// NOTE: Behaviour change in 1.3.3 (will attempt to hover in Undocking state even if no exit path)
|
|
if (aiShip != null && aiShip.IsInitialised) { shipDocking.SetState(ShipDocking.DockingState.Undocking); }
|
|
// This is either NOT an AI nor an AI-assisted ship, so go straight to not docked state
|
|
else { shipDocking.SetState(ShipDocking.DockingState.NotDocked); }
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Public API Methods
|
|
|
|
/// <summary>
|
|
/// Remember initial Entry/Exit docking path location positions for all ShipDockingPoints.
|
|
/// Offset and rotate them as indicated. Initial positions may need to be modified if the ShipDockingStation
|
|
/// has moved in the scene from where the Entry/Exit Paths were first setup.
|
|
/// Also sets the initial velocity, angular velocity, and anchorPoint.
|
|
/// </summary>
|
|
/// <param name="positionOffset">The difference between where docking station was setup with the Paths in the scene and now.</param>
|
|
/// <param name="rotationDelta">The amount the docking station has been rotated since the Paths were setup in the scene.</param>
|
|
public void InitialiseDockingPointsPaths(Vector3 positionOffset, Quaternion rotationDelta)
|
|
{
|
|
if (!IsDockingPointPathsInitialised && IsInitialised)
|
|
{
|
|
// update cached number of docking points
|
|
numDockingPoints = shipDockingPointList == null ? 0 : shipDockingPointList.Count;
|
|
|
|
if (isStationShipInitialised)
|
|
{
|
|
currentStationPosition = shipControlModuleStation.shipInstance.TransformPosition;
|
|
pathVelocity = shipControlModuleStation.shipInstance.WorldVelocity;
|
|
pathAngularVelocity = shipControlModuleStation.shipInstance.WorldAngularVelocity;
|
|
}
|
|
else
|
|
{
|
|
currentStationPosition = transform.position;
|
|
pathVelocity = Vector3.zero;
|
|
pathAngularVelocity = Vector3.zero;
|
|
}
|
|
|
|
for (int dpIdx = 0; dpIdx < numDockingPoints; dpIdx++)
|
|
{
|
|
ShipDockingPoint shipDockingPoint = shipDockingPointList[dpIdx];
|
|
if (shipDockingPoint != null)
|
|
{
|
|
if (shipDockingPoint.guidHashEntryPath != 0)
|
|
{
|
|
PathData pathData = sscManager.GetPath(shipDockingPoint.guidHashEntryPath);
|
|
sscManager.InitialiseLocations(pathData, currentStationPosition, positionOffset, rotationDelta, pathVelocity, pathAngularVelocity);
|
|
}
|
|
|
|
if (shipDockingPoint.guidHashExitPath != 0 && shipDockingPoint.guidHashExitPath != shipDockingPoint.guidHashEntryPath)
|
|
{
|
|
PathData pathData = sscManager.GetPath(shipDockingPoint.guidHashExitPath);
|
|
sscManager.InitialiseLocations(pathData, currentStationPosition, positionOffset, rotationDelta, pathVelocity, pathAngularVelocity);
|
|
}
|
|
}
|
|
}
|
|
|
|
IsDockingPointPathsInitialised = true;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Assigns a ship to the docking point with index dockingPointIndex, if that docking point is currently available.
|
|
/// You can check whether the docking point is available by calling IsDockingPointAvailable().
|
|
/// If the ship and/or ShipDocking components are not initialised, they will be automatically initialised if the docking point is available.
|
|
/// See also UnassignDockingPoint(dockingPointIndex).
|
|
/// </summary>
|
|
/// <param name="shipControlModule"></param>
|
|
/// <param name="shipDocking"></param>
|
|
/// <param name="dockingPointIndex"></param>
|
|
/// <returns></returns>
|
|
public bool AssignShipToDockingPoint (ShipControlModule shipControlModule, ShipDocking shipDocking, int dockingPointIndex)
|
|
{
|
|
if (!IsInitialised)
|
|
{
|
|
#if UNITY_EDITOR
|
|
Debug.LogWarning("ERROR: ShipDockingStation.AssignShipToDockingPoint was called before it was initialised on " + gameObject.name + ". Either set initialiseOnAwake in the Editor or call Initialise() at runtime.");
|
|
#endif
|
|
return false;
|
|
}
|
|
else if (shipControlModule != null && shipDocking != null && shipDockingPointList.Count > dockingPointIndex)
|
|
{
|
|
shipDockingPointList[dockingPointIndex].dockedShip = shipControlModule;
|
|
|
|
if (!shipControlModule.IsInitialised) { shipControlModule.InitialiseShip(); }
|
|
if (!shipDocking.IsInitialised) { shipDocking.Initialise(); }
|
|
|
|
// Cache the station, docking point and station ship (if there is one).
|
|
shipDocking.shipDockingStation = this;
|
|
shipDocking.DockingPointId = dockingPointIndex;
|
|
shipDocking.dockWithShip = shipControlModuleStation;
|
|
return true;
|
|
}
|
|
else { return false; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Assigns a ship to the first available docking point (if any are available).
|
|
/// If the ship and/or ShipDocking components are not initialised, they will be automatically initialised if a docking point is available.
|
|
/// See also UnassignDockingPoint(dockingPointIndex).
|
|
/// </summary>
|
|
/// <param name="shipControlModule"></param>
|
|
/// <param name="shipDocking"></param>
|
|
/// <returns></returns>
|
|
public bool AssignShipToDockingPoint(ShipControlModule shipControlModule, ShipDocking shipDocking)
|
|
{
|
|
bool isAssignSuccessful = false;
|
|
|
|
if (!IsInitialised)
|
|
{
|
|
#if UNITY_EDITOR
|
|
Debug.LogWarning("ERROR: ShipDockingStation.AssignShipToDockingPoint was called before it was initialised on " + gameObject.name + ". Either set initialiseOnAwake in the Editor or call Initialise() at runtime.");
|
|
#endif
|
|
return false;
|
|
}
|
|
else if (shipControlModule != null && shipDocking != null)
|
|
{
|
|
for (int dpIdx = 0; dpIdx < numDockingPoints; dpIdx++)
|
|
{
|
|
// If this proves to be to costly, we could use a BitArray for occupied docking points
|
|
// like we use in SSCRadar.
|
|
if (shipDockingPointList[dpIdx].dockedShip == null)
|
|
{
|
|
shipDockingPointList[dpIdx].dockedShip = shipControlModule;
|
|
|
|
if (!shipControlModule.IsInitialised) { shipControlModule.InitialiseShip(); }
|
|
if (!shipDocking.IsInitialised) { shipDocking.Initialise(); }
|
|
|
|
// Cache the station, docking point and station ship (if there is one).
|
|
shipDocking.shipDockingStation = this;
|
|
shipDocking.DockingPointId = dpIdx;
|
|
shipDocking.dockWithShip = shipControlModuleStation;
|
|
isAssignSuccessful = true;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return isAssignSuccessful;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start the docking process for an initialised ship at a valid (zero-based) docking
|
|
/// point index. The ship must be Undocked. If this is an AI ship and there is an
|
|
/// entry path, it will go into the docking state. Otherwise it will become Docked.
|
|
/// The ship must already be assigned to the Docking Point.
|
|
/// See also AssignShipToDockingPoint(..)
|
|
/// </summary>
|
|
/// <param name="dockingPointIndex"></param>
|
|
public void DockShip (int dockingPointIndex)
|
|
{
|
|
ShipControlModule shipControlModule = GetAssignedShip(dockingPointIndex);
|
|
|
|
if (shipControlModule != null)
|
|
{
|
|
ShipAIInputModule aiShip = shipControlModule.GetShipAIInputModule(false);
|
|
|
|
// Initially don't force a GetComponent.
|
|
ShipDocking shipDocking = shipControlModule.GetShipDocking(false);
|
|
|
|
// If the initial reference check failed, test again with a forced GetComponent
|
|
if (shipDocking == null) { shipDocking = shipControlModule.GetShipDocking(true); }
|
|
|
|
if (shipDocking != null)
|
|
{
|
|
// If it is an initialised AI ship then attempt to go into the docking state (and fly along the entry path - assuming there is one...)
|
|
//if (aiShip != null && aiShip.IsInitialised && GetDockingPointEntryPathguidHash(dockingPointIndex) != 0) { shipDocking.SetState(ShipDocking.DockingState.Docking); }
|
|
if (aiShip != null && aiShip.IsInitialised) { shipDocking.SetState(ShipDocking.DockingState.Docking); }
|
|
// This is either NOT and AI ship OR there is no entry path
|
|
else { shipDocking.SetState(ShipDocking.DockingState.Docked); }
|
|
}
|
|
#if UNITY_EDITOR
|
|
else
|
|
{
|
|
Debug.LogWarning("ShipDockingStation.DockShip could not find ShipDocking component on " + shipControlModule.name + " at Docking Point " + (dockingPointIndex+1).ToString());
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Start the docking process for an initialised ship at a valid (zero-based) docking
|
|
/// point index. The ship must be Undocked. If this is an AI ship and there is an
|
|
/// entry path, it will go into the docking state. Otherwise it will become Docked.
|
|
/// The ship must already be assigned to the Docking Point.
|
|
/// See also AssignShipToDockingPoint(..)
|
|
/// </summary>
|
|
/// <param name="shipControlModule"></param>
|
|
public void DockShip (ShipControlModule shipControlModule)
|
|
{
|
|
if (shipControlModule != null && shipControlModule.IsInitialised)
|
|
{
|
|
DockShip(GetDockingPointIndex(shipControlModule.shipInstance.shipId));
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Safely retrieve a ShipDockingPoint from the ShipDockingStation.
|
|
/// Example: ShipDockingPoint shipDockingPoint = shipDockingStation.GetDockingPoint(shipDocking.DockingPointId);
|
|
/// </summary>
|
|
/// <param name="dockingPointIndex"></param>
|
|
/// <returns></returns>
|
|
public ShipDockingPoint GetDockingPoint (int dockingPointIndex)
|
|
{
|
|
if (IsInitialised && dockingPointIndex >= 0 && numDockingPoints > dockingPointIndex)
|
|
{
|
|
return shipDockingPointList[dockingPointIndex];
|
|
}
|
|
else { return null; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the zero-based docking point index which a ship is assigned to.
|
|
/// If the ship is not initialised or the ship is not assigned to a docking
|
|
/// point on this docking station, it will return ShipDockingStation.NoDockingPoint (-1).
|
|
/// USAGE: int dpIdx = GetDockingPointIndex(shipControlModule.shipInstance.shipId);
|
|
/// </summary>
|
|
/// <param name="shipId"></param>
|
|
/// <returns></returns>
|
|
public int GetDockingPointIndex (int shipId)
|
|
{
|
|
int dockingPointIndex = NoDockingPoint;
|
|
|
|
if (IsInitialised)
|
|
{
|
|
ShipControlModule shipControlModule;
|
|
|
|
for (int dpIdx = 0; dpIdx < numDockingPoints; dpIdx++)
|
|
{
|
|
shipControlModule = shipDockingPointList[dpIdx].dockedShip;
|
|
|
|
if (shipControlModule != null && shipControlModule.IsInitialised && shipControlModule.shipInstance.shipId == shipId)
|
|
{
|
|
dockingPointIndex = dpIdx;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return dockingPointIndex;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return the world-space up direction of the docking point based on the relative rotation.
|
|
/// THIS NEEDS TO BE DOUBLY CHECKED!!!
|
|
/// </summary>
|
|
/// <param name="shipDockingPoint"></param>
|
|
/// <returns></returns>
|
|
public Vector3 GetDockingPointUp (ShipDockingPoint shipDockingPoint)
|
|
{
|
|
if (shipDockingPoint != null)
|
|
{
|
|
return (isStationShipInitialised ? shipControlModuleStation.shipInstance.TransformRotation : transform.rotation)
|
|
* Quaternion.Euler(shipDockingPoint.relativeRotation)
|
|
* Quaternion.Euler(270f, 0f, 0f) * Vector3.forward;
|
|
}
|
|
|
|
else { return Vector3.up; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the world space docking point rotation
|
|
/// </summary>
|
|
/// <param name="shipDockingPoint"></param>
|
|
/// <returns></returns>
|
|
public Quaternion GetDockingPointRotation (ShipDockingPoint shipDockingPoint)
|
|
{
|
|
if (shipDockingPoint != null)
|
|
{
|
|
return (isStationShipInitialised ? shipControlModuleStation.shipInstance.TransformRotation : transform.rotation) * Quaternion.Euler(shipDockingPoint.relativeRotation);
|
|
}
|
|
else { return Quaternion.identity; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the world space position of the docking point.
|
|
/// </summary>
|
|
/// <param name="shipDockingPoint"></param>
|
|
/// <returns></returns>
|
|
public Vector3 GetDockingPointPositionWS (ShipDockingPoint shipDockingPoint)
|
|
{
|
|
if (shipDockingPoint != null)
|
|
{
|
|
return transform.position + (transform.rotation * shipDockingPoint.relativePosition);
|
|
|
|
// I don't think we need to use scaled relative position - but not sure
|
|
//return transform.position + (transform.rotation * GetScaledRelativePosition(shipDockingPoint));
|
|
}
|
|
else { return transform.position; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// If a ship is assigned to this docking point, the ShipControlModule for that ship is returned.
|
|
/// If the ShipDockingStation is not initialised, or the docking point index is invalid, or there is
|
|
/// no assigned ship, this method will return null.
|
|
/// NOTE An assigned ship can have a DockingState of Docked, Undocking, or Docking.
|
|
/// See also GetDockedShip(..).
|
|
/// </summary>
|
|
/// <param name="dockingPointIndex"></param>
|
|
/// <returns></returns>
|
|
public ShipControlModule GetAssignedShip (int dockingPointIndex)
|
|
{
|
|
if (IsInitialised && dockingPointIndex >= 0 && numDockingPoints > dockingPointIndex)
|
|
{
|
|
return shipDockingPointList[dockingPointIndex].dockedShip;
|
|
}
|
|
else { return null; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// If the ShipDockingStation is initialised, and the docking point is valid, and a ship is assigned
|
|
/// to the docking point, and the ship's ShipDocking.DockingState is Docked, then return the ShipControlModule.
|
|
/// For all other scenarios, return null.
|
|
/// </summary>
|
|
/// <param name="dockingPointIndex"></param>
|
|
/// <returns></returns>
|
|
public ShipControlModule GetDockedShip (int dockingPointIndex)
|
|
{
|
|
if (IsInitialised && dockingPointIndex >= 0 && numDockingPoints > dockingPointIndex)
|
|
{
|
|
ShipControlModule dockedShip = shipDockingPointList[dockingPointIndex].dockedShip;
|
|
|
|
if (dockedShip != null && dockedShip.ShipIsDocked()) { return dockedShip; }
|
|
else { return null; }
|
|
}
|
|
else { return null; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns the velocity of the station.
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
public Vector3 GetStationVelocity ()
|
|
{
|
|
// Check if there is a ship attached to this station
|
|
if (isStationShipInitialised)
|
|
{
|
|
return shipControlModuleStation.shipInstance.WorldVelocity;
|
|
}
|
|
else
|
|
{
|
|
return Vector3.zero;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the guidHash of the Entry Path for a docking point using the zero-based index.
|
|
/// If the guidHash == 0 there is no path assigned.
|
|
/// </summary>
|
|
/// <param name="dockingPointIndex"></param>
|
|
/// <returns></returns>
|
|
public int GetDockingPointEntryPathguidHash (int dockingPointIndex)
|
|
{
|
|
if (IsInitialised && dockingPointIndex >= 0 && numDockingPoints > dockingPointIndex)
|
|
{
|
|
return shipDockingPointList[dockingPointIndex].guidHashEntryPath;
|
|
}
|
|
else { return 0; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Get the guidHash of the Exit Path for a docking point using the zero-based index.
|
|
/// If the guidHash == 0 there is no path assigned, the station is not initialised, or
|
|
/// the docking point index is invalid.
|
|
/// </summary>
|
|
/// <param name="dockingPointIndex"></param>
|
|
/// <returns></returns>
|
|
public int GetDockingPointExitPathguidHash(int dockingPointIndex)
|
|
{
|
|
if (IsInitialised && dockingPointIndex >= 0 && numDockingPoints > dockingPointIndex)
|
|
{
|
|
return shipDockingPointList[dockingPointIndex].guidHashExitPath;
|
|
}
|
|
else { return 0; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Is the docking point with index dockingPointIndex currently available?
|
|
/// </summary>
|
|
/// <param name="dockingPointIndex"></param>
|
|
/// <returns></returns>
|
|
public bool IsDockingPointAvailable(int dockingPointIndex)
|
|
{
|
|
if (IsInitialised && dockingPointIndex >= 0 && numDockingPoints > dockingPointIndex)
|
|
{
|
|
// Availability is determined by whether there is currently a ship docked,
|
|
// or in the process of docking or undocking
|
|
return shipDockingPointList[dockingPointIndex].dockedShip == null;
|
|
}
|
|
else { return false; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Is the ship assigned to any docking points of the Ship Docking Station?
|
|
/// The ship does not have to be docked, to be assigned.
|
|
/// </summary>
|
|
/// <param name="shipId"></param>
|
|
/// <returns></returns>
|
|
public bool IsShipAssigned (int shipId)
|
|
{
|
|
return GetDockingPointIndex(shipId) >= 0;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Is the ship docked at any docking point of this Ship Docking Station?
|
|
/// </summary>
|
|
/// <param name="shipId"></param>
|
|
/// <returns></returns>
|
|
public bool IsShipDocked (int shipId)
|
|
{
|
|
return GetDockedShip(GetDockingPointIndex(shipId)) != null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Call this when you wish to remove any custom event listeners, like
|
|
/// after creating them in code and then destroying the object.
|
|
/// You could add this to your game play OnDestroy code.
|
|
/// </summary>
|
|
public void RemoveListeners()
|
|
{
|
|
if (IsInitialised)
|
|
{
|
|
if (onPreUndock != null) { onPreUndock.RemoveAllListeners(); }
|
|
if (onPostDocked != null) { onPostDocked.RemoveAllListeners(); }
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempt to unassign any ship allocated to a docking point within the ShipDockingStation.
|
|
/// If the ship is currently docked, this method will fail.
|
|
/// </summary>
|
|
/// <param name="dockingPointIndex"></param>
|
|
public bool UnassignDockingPoint (int dockingPointIndex)
|
|
{
|
|
bool isSuccessful = false;
|
|
|
|
if (IsInitialised && dockingPointIndex >= 0 && numDockingPoints > dockingPointIndex)
|
|
{
|
|
// TODO - check if there is a docked ship here...
|
|
|
|
ShipControlModule assignedShip = shipDockingPointList[dockingPointIndex].dockedShip;
|
|
|
|
if (assignedShip == null || !assignedShip.ShipIsDocked())
|
|
{
|
|
shipDockingPointList[dockingPointIndex].dockedShip = null;
|
|
isSuccessful = true;
|
|
}
|
|
}
|
|
|
|
return isSuccessful;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Attempt to unassign a ship that may be assigned to a Docking Point.
|
|
/// It will success if the ship is NOT assigned or NOT Docked with a
|
|
/// Docking Point.
|
|
/// </summary>
|
|
/// <param name="shipId"></param>
|
|
public bool UnassignShip (int shipId)
|
|
{
|
|
bool isSuccessful = true;
|
|
|
|
int dockingPointIndex = GetDockingPointIndex(shipId);
|
|
|
|
if (dockingPointIndex >= 0)
|
|
{
|
|
isSuccessful = UnassignDockingPoint(dockingPointIndex);
|
|
}
|
|
|
|
return isSuccessful;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Undock an initialised ship that currently has ShipDocking.DockingState of Docked
|
|
/// at a valid (zero-based) docking point index. If the docked ship is an AI ship it
|
|
/// will go into the Undocking state (from SSC v1.3.3+ it no longer requires an Exit Path).
|
|
/// The undocking ship will be delayed by ShipDocking.undockingDelay.
|
|
/// WARNING: Do NOT call this from the onPreUndock event.
|
|
/// </summary>
|
|
/// <param name="dockingPointIndex"></param>
|
|
public void UnDockShip (int dockingPointIndex)
|
|
{
|
|
ShipControlModule dockedShip = GetDockedShip(dockingPointIndex);
|
|
if (dockedShip != null )
|
|
{
|
|
// Call any methods defined configured for this event
|
|
if (onPreUndock != null) { onPreUndock.Invoke(shipDockingStationId, dockedShip.GetShipId, dockingPointIndex + 1, Vector3.zero); }
|
|
|
|
// This must return not null or else GetDockedShip above would have failed
|
|
ShipDocking shipDocking = dockedShip.GetShipDocking(false);
|
|
|
|
shipDocking.ResetAutoUndock();
|
|
|
|
if (shipDocking.undockingDelay > 0f)
|
|
{
|
|
StartCoroutine(UndockShipDelayed(dockedShip, shipDocking, shipDocking.undockingDelay));
|
|
}
|
|
else
|
|
{
|
|
UnDockShipInternal(dockedShip, shipDocking);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Undock an initialised ship that currently has ShipDocking.DockingState of Docked
|
|
/// at a valid (zero-based) docking point index. If the docked ship is an AI ship it
|
|
/// will go into the Undocking state (from SSC v1.3.3+ it no longer requires an Exit Path).
|
|
/// The undocking ship will be delayed by ShipDocking.undockingDelay.
|
|
/// WARNING: Do NOT call this from the onPreUndock event.
|
|
/// </summary>
|
|
/// <param name="shipControlModule"></param>
|
|
public void UnDockShip (ShipControlModule shipControlModule)
|
|
{
|
|
if (shipControlModule != null && shipControlModule.IsInitialised)
|
|
{
|
|
UnDockShip(GetDockingPointIndex(shipControlModule.shipInstance.shipId));
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Import a json file from disk and return as list of ShipDockingPoints
|
|
/// </summary>
|
|
/// <param name="folderPath"></param>
|
|
/// <param name="fileName"></param>
|
|
/// <returns></returns>
|
|
public List<ShipDockingPoint> ImportDockingPointDataFromJson (string folderPath, string fileName)
|
|
{
|
|
List<ShipDockingPoint> importedDockingPointList = null;
|
|
|
|
if (!string.IsNullOrEmpty(folderPath) && !string.IsNullOrEmpty(fileName))
|
|
{
|
|
try
|
|
{
|
|
string filePath = System.IO.Path.Combine(folderPath, fileName);
|
|
if (System.IO.File.Exists(filePath))
|
|
{
|
|
string jsonText = System.IO.File.ReadAllText(filePath);
|
|
|
|
importedDockingPointList = SSCUtils.FromJson<ShipDockingPoint>(jsonText);
|
|
|
|
// Currently cannot do this because we need a wrapper class for the list
|
|
//JsonUtility.FromJsonOverwrite(jsonText, importedDockingPointList);
|
|
}
|
|
#if UNITY_EDITOR
|
|
else
|
|
{
|
|
Debug.LogWarning("ERROR: ShipDockingStation Import Docking Points. Could not find file at " + filePath);
|
|
}
|
|
#endif
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
#if UNITY_EDITOR
|
|
Debug.LogWarning("ERROR: ShipDockingStation - could not import docking point from: " + folderPath + " PLEASE REPORT - " + ex.Message);
|
|
#else
|
|
// Keep compiler happy
|
|
if (ex != null) { }
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return importedDockingPointList;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Save the list of docking points for this ShipDockingStation to a json file on disk.
|
|
/// </summary>
|
|
/// <param name="filePath"></param>
|
|
public bool SaveDockingPointDataAsJson (string filePath)
|
|
{
|
|
bool isSuccessful = false;
|
|
|
|
if (shipDockingPointList != null && !string.IsNullOrEmpty(filePath))
|
|
{
|
|
try
|
|
{
|
|
string jsonData = SSCUtils.ToJson(shipDockingPointList);
|
|
|
|
if (!string.IsNullOrEmpty(jsonData) && !string.IsNullOrEmpty(filePath))
|
|
{
|
|
System.IO.File.WriteAllText(filePath, jsonData);
|
|
isSuccessful = true;
|
|
}
|
|
}
|
|
catch (System.Exception ex)
|
|
{
|
|
#if UNITY_EDITOR
|
|
Debug.LogWarning("ERROR: ShipDockingStation - could not export: " + transform.name + " PLEASE REPORT - " + ex.Message);
|
|
#else
|
|
// Keep compiler happy
|
|
if (ex != null) { }
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return isSuccessful;
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
} |