using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Object = UnityEngine.Object;
// Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved.
namespace SciFiShipController
{
///
/// We "could" make the ShipSpawner behave much like the SSCManager or even
/// integrate it into the SSCManager. It could be converted over pretty easily...
///
public class ShipSpawner
{
#region Enumerations
#endregion
#region Public variables
#endregion
#region Private variables
#endregion
#region Public Methods
///
/// Create a squadon of ships.
/// If notification when a ship is about to be destroyed is required, pass in the name
/// of the custom method to call, else set callbackOnDestroy to null.
/// Optionally add an AI script component to each ship
///
///
///
///
///
///
public bool CreateSquadron(Squadron squadron, Transform parentTrfm, ShipControlModule.CallbackOnDestroy callbackOnDestroy, System.Type aiComponentType = null)
{
bool isSuccesful = false;
// Use the rules within the squadron to instantiate the ships in the scene.
if (squadron != null)
{
// Verify the ship prefab
if (squadron.shipPrefab != null && squadron.shipPrefab.GetComponent() != null)
{
Vector3 upDirection = Vector3.up;
List shipPositions = new List(50);
#region Build list of ship positions
Vector3 shipPosition = Vector3.zero;
// The default location for the player in the squadron.
int playerIdx = 0;
if (squadron.tacticalFormation == Squadron.TacticalFormation.Vic)
{
// The V-formation is at 45 degrees if x and z offsets are the same.
// Only the number of rows on z and y axis are considered
// The bottom "layer" of ships starts at the y position of the squadron
float currentY = squadron.anchorPosition.y;
float currentX = squadron.anchorPosition.x;
// Loop through all the (stacked) x,z lines on the y-axis
for (int y = 0; y < squadron.rowsY; y++)
{
// The first row of ships starts at the anchor position
float currentZ = squadron.anchorPosition.z;
// Loop through all the lines of ships on the z-axis
for (int z = 0; z < squadron.rowsZ; z++)
{
shipPosition.y = currentY;
shipPosition.z = currentZ;
// The head of the v is always the anchor position of the formation + the y-offset
if (z == 0)
{
shipPosition.x = currentX;
shipPositions.Add(shipPosition);
}
else
{
// Each ship is 45 degrees to the left and right behind the previous ship(s)
// Right ship
shipPosition.x = currentX + (z * squadron.offsetX);
shipPositions.Add(shipPosition);
// Left ship
shipPosition.x = currentX + (z * -squadron.offsetX);
shipPositions.Add(shipPosition);
}
currentZ -= squadron.offsetZ;
}
currentY += squadron.offsetY;
}
}
else if (squadron.tacticalFormation == Squadron.TacticalFormation.Wedge)
{
// Wedge is like a filled in Vic (vee) formation
// squadron.rowsX is not used for Wedge
// The bottom "layer" of ships starts at the y position of the squadron
float currentY = squadron.anchorPosition.y;
float currentX, currentZ;
// Loop through all the (stacked) x,z lines on the y-axis
for (int y = 0; y < squadron.rowsY; y++)
{
// The first row of ships starts at the anchor position
currentZ = squadron.anchorPosition.z;
// Loop through all the lines of ships on the z-axis
for (int z = 0; z < squadron.rowsZ; z++)
{
shipPosition.y = currentY;
shipPosition.z = currentZ;
// Each row along x-axis will have z ships
// If the zero-based row number is even it will have an odd number of ships
if (((z >> 1) << 1) == z)
{
// Odd rows have a ship in the centre of the row.
// Ships start on the left and are populated along the tow to the right
currentX = squadron.anchorPosition.x - (z + 1) / 2 * squadron.offsetX;
for (int x = 0; x <= z; x++)
{
shipPosition.x = currentX;
shipPositions.Add(shipPosition);
currentX += squadron.offsetX;
}
}
else
{
// Odd rows are offset half the offset x distance to the right and left of the centre
currentX = squadron.anchorPosition.x - ((float)z / 2f * squadron.offsetX);
for (int x = 0; x <= z; x++)
{
shipPosition.x = currentX;
shipPositions.Add(shipPosition);
currentX += squadron.offsetX;
}
}
currentZ -= squadron.offsetZ;
}
currentY += squadron.offsetY;
}
}
else if (squadron.tacticalFormation == Squadron.TacticalFormation.LeftEchelon || squadron.tacticalFormation == Squadron.TacticalFormation.RightEchelon)
{
// Left or right offset
float offsetX = squadron.tacticalFormation == Squadron.TacticalFormation.LeftEchelon ? -squadron.offsetX : squadron.offsetX;
// The bottom "layer" of ships starts at the y position of the squadron
float currentY = squadron.anchorPosition.y;
// Loop through all the (stacked) x,z lines on the y-axis
for (int y = 0; y < squadron.rowsY; y++)
{
// The first row of ships starts at the anchor position
float currentZ = squadron.anchorPosition.z;
shipPosition.y = currentY;
// Loop through all the lines of ships on the z-axis
for (int z = 0; z < squadron.rowsZ; z++)
{
float currentX = squadron.anchorPosition.x;
// Lines are populated from left to right
for (int x = 0; x < squadron.rowsX; x++)
{
// Each ship is angled to the left/right behind the previous ship.
// If x and z offsets are the same the angle is 45 degrees
shipPosition.x = currentX;
//shipPosition.y = currentY;
shipPosition.z = currentZ - (squadron.offsetX * x);
shipPositions.Add(shipPosition);
currentX += offsetX;
}
currentZ -= squadron.offsetZ;
}
currentY += squadron.offsetY;
}
}
else if (squadron.tacticalFormation == Squadron.TacticalFormation.Line)
{
playerIdx = Mathf.FloorToInt(squadron.rowsX / 2);
float extentX = (squadron.rowsX - 1) * squadron.offsetX;
//float extentY = (squadron.rowsY - 1) * squadron.offsetY;
//float extentZ = (squadron.rowsZ - 1) * squadron.offsetZ;
// The bottom "layer" of ships starts at the y position of the squadron
float currentY = squadron.anchorPosition.y;
// Loop through all the (stacked) x,z lines on the y-axis
for (int y = 0; y < squadron.rowsY; y++)
{
// The first row of ships starts at the anchor position
float currentZ = squadron.anchorPosition.z;
// Loop through all the lines of ships on the z-axis
for (int z = 0; z < squadron.rowsZ; z++)
{
float currentX = squadron.anchorPosition.x - (extentX/2f);
// Lines are populated from left to right
for (int x = 0; x < squadron.rowsX; x++)
{
shipPosition.x = currentX;
shipPosition.y = currentY;
shipPosition.z = currentZ;
shipPositions.Add(shipPosition);
currentX += squadron.offsetX;
}
currentZ -= squadron.offsetZ;
}
currentY += squadron.offsetY;
}
}
else if (squadron.tacticalFormation == Squadron.TacticalFormation.Column || squadron.tacticalFormation == Squadron.TacticalFormation.StaggeredColumn)
{
playerIdx = Mathf.FloorToInt(squadron.rowsX / 2);
// The anchor point for a column or staggered column is at the bottom left (a Line's anchor point is bottom centre)
bool isStaggered = squadron.tacticalFormation == Squadron.TacticalFormation.StaggeredColumn;
// The bottom "layer" of ships starts at the y position of the squadron
float currentY = squadron.anchorPosition.y;
// Loop through all the (stacked) x,z lines on the y-axis
for (int y = 0; y < squadron.rowsY; y++)
{
// The first row of ships starts at the anchor position
float currentZ = squadron.anchorPosition.z;
// Loop through all the lines of ships on the z-axis
for (int z = 0; z < squadron.rowsZ; z++)
{
// Unlike Line formations, columns start at the anchor point
float currentX = squadron.anchorPosition.x;
// For staggered formation, check if this row on z-axis is odd?
if (isStaggered && ((z >> 1) << 1) == z) { currentX += (squadron.offsetX * squadron.rowsX); }
// Lines are populated from left to right
for (int x = 0; x < squadron.rowsX; x++)
{
shipPosition.x = currentX;
shipPosition.y = currentY;
shipPosition.z = currentZ;
shipPositions.Add(shipPosition);
currentX += squadron.offsetX;
}
currentZ -= squadron.offsetZ;
}
currentY += squadron.offsetY;
}
}
#endregion
#region Instantiate ships
int numShips = shipPositions == null ? 0 : shipPositions.Count;
// If a player ship is not being placed at the head of the squadron, reset its postion
// so a regular shipPrefab will be instantiated for all squadron members.
if (squadron.playerShip == null) { playerIdx = -1; }
// Calculate the rotation once outside the loop
Quaternion rotation = Quaternion.LookRotation(squadron.fwdDirection, upDirection);
for (int spIdx = 0; spIdx < numShips; spIdx++)
{
Vector3 rotatedPosition = (squadron.anchorPosition + (rotation * (shipPositions[spIdx] - squadron.anchorPosition)));
// Are we moving the player ship?
if (spIdx == playerIdx)
{
squadron.playerShip.transform.SetPositionAndRotation(rotatedPosition, rotation);
}
else
{
GameObject shipGameObjectInstance = Object.Instantiate(squadron.shipPrefab, rotatedPosition, rotation);
if (shipGameObjectInstance != null)
{
// Set the object's parent if one is supplied
if (parentTrfm != null) { shipGameObjectInstance.transform.SetParent(parentTrfm); }
// uncomment for testing in editor
//shipGameObjectInstance.name += spIdx;
// Add the ship to the squadron
squadron.shipList.Add(shipGameObjectInstance.transform.GetInstanceID());
// Initialise the ship
ShipControlModule shipControlModule = shipGameObjectInstance.GetComponent();
if (shipControlModule != null)
{
// Set the name of the custom callback method.
shipControlModule.callbackOnDestroy = callbackOnDestroy;
if (!shipControlModule.IsInitialised) { shipControlModule.InitialiseShip(); }
// Update squadron-related fields in the ship
if (shipControlModule.shipInstance != null)
{
shipControlModule.shipInstance.factionId = squadron.factionId;
shipControlModule.shipInstance.squadronId = squadron.squadronId;
}
}
// Optionally add an AI Script with default values
if (aiComponentType != null)
{
if (shipGameObjectInstance.GetComponent(aiComponentType) == null) { shipGameObjectInstance.AddComponent(aiComponentType); }
}
}
}
}
isSuccesful = true;
#endregion
}
#if UNITY_EDITOR
else { Debug.LogWarning("ERROR: " + squadron.squadronName + " squadron does not have a valid ship prefab. The prefab needs to have a ShipControlModule attached to the parent gameobject."); }
#endif
}
// FUTURE - Optionally create ships using DOTS
return isSuccesful;
}
///
/// Create a ship from a prefab. Optionally supply a parent transform, a
/// callback method for when the ship is destroyed, and an AI script to attach.
///
///
///
///
///
///
///
///
public ShipControlModule CreateShip(GameObject shipPrefab, Vector3 shipPosition, Quaternion shipRotation, Transform parentTrfm = null, ShipControlModule.CallbackOnDestroy callbackOnDestroy = null, System.Type aiComponentType = null)
{
ShipControlModule shipControlModule = null;
GameObject shipGameObjectInstance = Object.Instantiate(shipPrefab, shipPosition, shipRotation);
if (shipGameObjectInstance != null)
{
// Set the object's parent if one is supplied
if (parentTrfm != null) { shipGameObjectInstance.transform.SetParent(parentTrfm); }
shipControlModule = shipGameObjectInstance.GetComponent();
if (shipControlModule != null)
{
// Set the name of the custom callback method.
shipControlModule.callbackOnDestroy = callbackOnDestroy;
if (!shipControlModule.IsInitialised) { shipControlModule.InitialiseShip(); }
}
// Optionally add an AI Script with default values
if (aiComponentType != null)
{
if (shipGameObjectInstance.GetComponent(aiComponentType) == null) { shipGameObjectInstance.AddComponent(aiComponentType); }
}
}
return shipControlModule;
}
#endregion
#region Private Methods
#endregion
}
}