rabidus-test/Assets/SCSM/SciFiShipController/Scripts/Physics/Behaviours/ProjectileModule.cs

858 lines
36 KiB
C#

using UnityEngine;
using System.Collections.Generic;
// Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved.
namespace SciFiShipController
{
[HelpURL("http://scsmmedia.com/ssc-documentation")]
[AddComponentMenu("Sci-Fi Ship Controller/Weapon Components/Projectile Module")]
public class ProjectileModule : MonoBehaviour
{
#region Public Enumerations
public enum DamageType
{
/// <summary>
/// Damage from projectiles of this type will be unaffected by ship damage multipliers
/// i.e. the amount of damage done to the ship will be identical to damageAmount.
/// </summary>
Default = 0,
TypeA = 100,
TypeB = 105,
TypeC = 110,
TypeD = 115,
TypeE = 120,
TypeF = 125,
}
#endregion
#region Public Variables
/// <summary>
/// The starting speed of the projectile in metres per second.
/// </summary>
public float startSpeed = 100f;
/// <summary>
/// Whether the projectile is affected by gravity.
/// </summary>
public bool useGravity = false;
/// <summary>
/// The type of damage the projectile does. The amount of damage dealt to a ship upon collision is dependent
/// on the ship's multiplier for this damage type (i.e. if a Type A projectile with a damage amount of 10 hits a ship
/// with a Type A damage multiplier of 2, a total damage of 20 will be done to the ship). If the damage type is set to Default,
/// the damage multipliers are ignored i.e. the damage amount is unchanged.
/// </summary>
public DamageType damageType = DamageType.Default;
/// <summary>
/// The amount of damage the projectile does on collision with a ship or object. NOTE: Non-ship objects need
/// a DamageReceiver component.
/// </summary>
public float damageAmount = 10f;
/// <summary>
/// Whether Entity Component System and Job System is used when spawning projectiles of this type
/// </summary>
public bool useECS = false;
/// <summary>
/// Whether pooling is used when spawning projectiles of this type.
/// Currently we don't support changing this at runtime.
/// </summary>
public bool usePooling = true;
/// <summary>
/// The starting size of the pool. Only relevant when usePooling is enabled.
/// </summary>
public int minPoolSize = 100;
/// <summary>
/// The maximum allowed size of the pool. Only relevant when usePooling is enabled.
/// </summary>
public int maxPoolSize = 1000;
/// <summary>
/// The projectile will be automatically despawned after this amount of time (in seconds) has elapsed.
/// </summary>
public float despawnTime = 1f;
/// <summary>
/// The ID number for this projectile prefab (as assigned by the Ship Controller Manager in the scene).
/// This is the index in the SSCManager projectileTemplatesList.
/// [INTERNAL USE ONLY]
/// </summary>
public int projectilePrefabID;
/// <summary>
/// The sound or particle FX used when a collision occurs.
/// If you modify this, call shipControlModule.ReinitialiseShipProjectilesAndEffects() and/or
/// surfaceTurretModule.ReinitialiseTurretProjectilesAndEffects() for each ship/surface turret
/// this projectile is used on.
/// </summary>
public EffectsModule effectsObject = null;
/// <summary>
/// The ID number for this projectile's destruction effects object prefab (as assigned by the Ship Controller Manager in the scene).
/// This is the index in the SSCManager effectsObjectTemplatesList. Not defined = -1.
/// [INTERNAL USE ONLY]
/// </summary>
public int effectsObjectPrefabID = -1;
/// <summary>
/// The sound or particle FX used when a collision occurs with a shield (instead of the effectsObject).
/// If you modify this, call shipControlModule.ReinitialiseShipProjectilesAndEffects() and/or
/// surfaceTurretModule.ReinitialiseTurretProjectilesAndEffects() for each ship/surface turret
/// this projectile is used on.
/// </summary>
public EffectsModule shieldEffectsObject = null;
/// <summary>
/// The ID number for this projectile's effects object prefab (as assigned by the Ship Controller Manager in the scene)
/// for when a shielded ship is hit.
/// This is the index in the SSCManager effectsObjectTemplatesList. Not defined = -1.
/// [INTERNAL USE ONLY]
/// </summary>
public int shieldEffectsObjectPrefabID = -1;
/// <summary>
/// The sound and/or particle FX used when a projectile is fired from a weapon.
/// If you modify this, call shipControlModule.ReinitialiseShipProjectilesAndEffects() and/or
/// surfaceTurretModule.ReinitialiseTurretProjectilesAndEffects() for each ship/surface turret
/// this projectile is used on.
/// </summary>
public EffectsModule muzzleEffectsObject = null;
/// <summary>
/// The distance in local space that the muzzle Effects Object should be instantiated
/// from the weapon firing point. Typically only the z-axis would be used when the projectile
/// is instantiated in front or forwards from the actual weapon.
/// </summary>
public Vector3 muzzleEffectsOffset = Vector3.zero;
/// <summary>
/// The ID number for this projectile's muzzle object prefab (as assigned by the Ship Controller Manager in the scene).
/// This is the index in the SSCManager effectsObjectTemplatesList. Not defined = -1.
/// [INTERNAL USE ONLY]
/// </summary>
public int muzzleEffectsObjectPrefabID = -1;
/// <summary>
/// The Id of the ship that fired the projectile
/// </summary>
public int sourceShipId = -1;
/// <summary>
/// The Squadron which the ship belonged to when it fired the projectile
/// </summary>
public int sourceSquadronId = -1;
/// <summary>
/// Is this projectile guided to a target with kinematics? Position and velocity will be determined by aiming at a target. It will
/// not consider the forces required to move the projectile.
/// NOTE: All projectiles in SSC use kinematics but by default they are fire and forget rather than guided towards a target.
/// If you modify this, call shipControlModule.ReinitialiseShipProjectilesAndEffects() and/or
/// surfaceTurretModule.ReinitialiseTurretProjectilesAndEffects() for each ship/surface turret
/// this projectile is used on.
/// Currently does not support DOTS/ECS.
/// </summary>
public bool isKinematicGuideToTarget = false;
/// <summary>
/// The max turning speed in degrees per second for a guided projectile.
/// Only relevant when isKinematicGuideToTarget is enabled.
/// </summary>
[Range(10f, 360f)] public float guidedMaxTurnSpeed = 90f;
/// <summary>
/// The layer mask used for collision testing for this projectile.
/// Default is everything.
/// </summary>
public LayerMask collisionLayerMask = ~0;
/// <summary>
/// The estimated range (in metres) of this projectile assuming it travels at a constant velocity.
/// </summary>
public float estimatedRange { get { return startSpeed * despawnTime; } }
/// <summary>
/// Current velocity of the projectile.
/// Should only be updated when using the sscManager.callbackProjectileMoveTo delegate.
/// </summary>
public Vector3 Velocity { get { return velocity; } set { if (isKinematicGuideToTarget) { velocity = value; } } }
/// <summary>
/// The world space position of the projectile in the current frame
/// </summary>
public Vector3 ThisFramePosition { get { return thisFramePosition; } internal set { thisFramePosition = value; } }
/// <summary>
/// The world space position of the projectile in the last frame
/// </summary>
public Vector3 LastFramePosition { get { return lastFramePosition; } internal set { lastFramePosition = value; } }
/// <summary>
/// If a ship is being targeted, will return its name. If it is being targeted by is NULL, will assume destroyed.
/// </summary>
public string TargetShipName { get { return isTargetShip ? (targetShip == null ? "Destroyed" : targetShip.name) : "-"; } }
/// <summary>
/// If a ship damage region is being targeted, will return its name. If it is being targeted but is NULL, will assume destroyed.
/// </summary>
public string TargetShipDamageRegionName { get { return isTargetShipDamageRegion ? (targetShip == null ? "Destroyed" : targetShip.IsInitialised && targetguidHash != 0 ? targetShip.shipInstance.GetDamageRegion(targetguidHash).name : "-") : "-"; } }
/// <summary>
/// If a gameobject is being targeted, will return its name
/// </summary>
public string TargetGameObjectName { get { return targetGameObject != null ? (string.IsNullOrEmpty(targetGameObject.name) ? "no name" : targetGameObject.name) : "-"; } }
#endregion
#region Protected variables
// These variables can be modified by classes that inherit from ProjectileModule
protected Vector3 velocity = Vector3.zero;
protected float speed = 0f;
protected Vector3 lastFramePosition = Vector3.zero;
protected Vector3 thisFramePosition = Vector3.zero;
protected RaycastHit hitInfo;
protected bool isInitialised = false;
protected bool isProjectileEnabled = true;
protected float despawnTimer = 0f;
#endregion
#region Private variables
/// <summary>
/// From Ship, the magnitude of the acceleration (in metres per second squared) due to gravity.
/// </summary>
private float gravitationalAcceleration;
/// <summary>
/// From Ship, the direction in world space in which gravity acts upon the ship.
/// </summary>
private Vector3 gravityDirection;
/// <summary>
/// The current ship (if any) being targeted when isKinematicGuideToTarget is true.
/// </summary>
private ShipControlModule targetShip = null;
/// <summary>
/// The guidHash of the target. Currently only set for ship damage regions.
/// </summary>
private int targetguidHash = 0;
/// <summary>
/// The current ship damage region (if any) being targeted when isKinematicGuideToTarget is true.
/// </summary>
private DamageRegion targetShipDamageRegion = null;
/// <summary>
/// The current GameObject (if any) being targeted when isKinematicGuideToTarget is true.
/// </summary>
private GameObject targetGameObject = null;
// Is a ship being targeted?
private bool isTargetShip = false;
/// <summary>
/// Is a ship's damage region being targeted?
/// </summary>
private bool isTargetShipDamageRegion = false;
/// <summary>
/// Private reference to the SSCManager in the scene. Currently only populated
/// when isKinematicGuideToTarget is true. Used to get the CallbackProjectileMoveTo
/// method from SSCManager.
/// </summary>
private SSCManager sscManager = null;
// Is there a user-defined CallbackProjectileMoveTo configured for this projectile?
private bool isCallbackOnMoveTo = false;
// Augmented Proportional Navigation (APN) variables
// current and previous frame's Line of Sight (normalised)
private Vector3 lastFrameLOSN = Vector3.zero;
private Vector3 thisFrameLOSN = Vector3.zero;
private static readonly float NavConst = 3f;
#endregion
#region Enable/Disable Event Methods
void OnDisable()
{
isInitialised = false;
}
#endregion
#region Update Methods
// FixedUpdate is called once per physics update (typically about 50 times per second)
private void FixedUpdate ()
{
if (isInitialised && isProjectileEnabled)
{
// Remember last frame position as last frame position - we are about to update this frame position
lastFramePosition = thisFramePosition;
CalcPositionAndVelocity();
// Check to see if the projectile has collided with anything during this frame
// If nothing hit but using pooling, decrement the timer.
if (!CheckCollision() && usePooling)
{
// To avoid a pooled projectile being despawned at the incorrect time,
// do it here rather than calling Invoke("DestroyProjectile", despawnTime)
// when it is initialised.
despawnTimer += Time.deltaTime;
if (despawnTimer >= despawnTime)
{
DestroyProjectile();
}
}
// Update the position of the object
if (!isCallbackOnMoveTo) { transform.position = thisFramePosition; }
}
}
#endregion
#region Public Obsolete Methods
[System.Obsolete("This method will be removed in a future version. Please use InitialiseProjectile (InstantiateProjectileParameters ipParms).")]
public void InitialiseProjectile(Vector3 weaponVelocity, int projectilePrefabID, float gravity, Vector3 gravityDirection, int shipId, int squadronId)
{
InitialiseProjectile(new InstantiateProjectileParameters
{
weaponVelocity = weaponVelocity,
projectilePrefabID = projectilePrefabID,
gravity = gravity,
gravityDirection = gravityDirection,
shipId = shipId,
squadronId = squadronId,
// targets are not supported with this older method
targetShip = null,
targetGameObject = null,
targetguidHash = 0
}
);
}
#endregion
#region Virtual and Protected Methods
/// <summary>
/// Initialises the projectile. If you wish to override this in a child (inherited) class you
/// almost always will want to call the base method first.
/// public override void InitialiseProjectile(InstantiateProjectileParameters ipParms)
/// {
/// base.InitialiseProjectile(ipParms);
/// // Do stuff here
/// }
/// </summary>
public virtual void InitialiseProjectile(InstantiateProjectileParameters ipParms)
{
// Initialise the velocity based on the forwards direction
// The forwards direction should have been set correctly prior to enabling the object
speed = startSpeed;
velocity = transform.forward * speed;
// Initialise last/this frame positions
// Shift the position forward by the weapon velocity, so that projectiles don't ever end up behind the ship
lastFramePosition = transform.position + (velocity * Time.fixedDeltaTime);
thisFramePosition = lastFramePosition;
transform.position = thisFramePosition;
// Add the weapon velocity to the projectile velocity
// This needs to be done after the above so that the projectiles aren't spawned to the sides of the ship
velocity += ipParms.weaponVelocity;
// Store the index to the ProjectileTemplate in the SSCManager projectileTemplatesList
// This is used with Projectile FX when we know the ProjectileModule but not the parent ProjectileTemplate.
this.projectilePrefabID = ipParms.projectilePrefabID;
// Store the index to the EffectsObjectTemplate in sscManager.effectsObjectTemplatesList
this.effectsObjectPrefabID = ipParms.effectsObjectPrefabID;
this.shieldEffectsObjectPrefabID = ipParms.shieldEffectsObjectPrefabID;
this.gravitationalAcceleration = ipParms.gravity;
this.gravityDirection = ipParms.gravityDirection;
this.sourceShipId = ipParms.shipId;
this.sourceSquadronId = ipParms.squadronId;
if (!useECS && isKinematicGuideToTarget)
{
targetShip = ipParms.targetShip;
targetGameObject = ipParms.targetGameObject;
targetguidHash = ipParms.targetguidHash;
if (ipParms.targetShip != null)
{
targetShipDamageRegion = null;
if (targetguidHash != 0 && targetShip.IsInitialised && targetShip.shipInstance != null)
{
targetShipDamageRegion = targetShip.shipInstance.GetDamageRegion(targetguidHash);
}
isTargetShipDamageRegion = targetShipDamageRegion != null;
isTargetShip = !isTargetShipDamageRegion;
// If not a damage region or one was not found, reset the targetguidHash.
if (isTargetShip) { targetguidHash = 0; }
}
else
{
isTargetShip = false;
isTargetShipDamageRegion = false;
targetShipDamageRegion = null;
}
// reset this frame Line of Sight (normalised)
thisFrameLOSN.x = 0f;
thisFrameLOSN.y = 0f;
thisFrameLOSN.z = 0f;
// If in a pool, this may have already been called
if (!usePooling || sscManager == null) { sscManager = SSCManager.GetOrCreateManager(); }
isCallbackOnMoveTo = sscManager != null && sscManager.callbackProjectileMoveTo != null;
}
else
{
ClearTarget();
}
// After a given amount of time, automatically destroy this projectile
if (usePooling) { despawnTimer = 0f; }
else { Invoke("DestroyProjectile", despawnTime); }
isInitialised = true;
}
/// <summary>
/// Calculate new velocity and thisFramePosition
/// </summary>
protected virtual void CalcPositionAndVelocity()
{
if (useGravity)
{
// F = ma
// a = dv/dt
// F = mdv/dt
// dv = Fdt/m
velocity += (gravitationalAcceleration * Time.deltaTime * gravityDirection);
}
if (isKinematicGuideToTarget)
{
if (isCallbackOnMoveTo) { sscManager.callbackProjectileMoveTo(this); }
else { GuideToTarget(); }
}
// Move the projectile according to its current velocity
thisFramePosition = lastFramePosition + (velocity * Time.deltaTime);
}
/// <summary>
/// Check to see if the projectile has collided with anything during this frame
/// </summary>
/// <returns></returns>
protected virtual bool CheckCollision()
{
// TODO: Look into whether this can be done using RaycastCommand with JobSystem
// LayerMask defaults to everything (inverse of nothing ~0). Don't detect trigger colliders
if (Physics.Linecast(lastFramePosition, thisFramePosition, out hitInfo, collisionLayerMask, QueryTriggerInteraction.Ignore))
{
bool isShieldHit = false;
ShipControlModule shipControlModule;
// Do we need to check for ship shield hits?
if (shieldEffectsObjectPrefabID >= 0 && CheckShipHit(hitInfo, damageAmount, damageType, sourceShipId, sourceSquadronId, projectilePrefabID, out shipControlModule))
{
isShieldHit = shipControlModule.shipInstance.HasActiveShield(hitInfo.point);
}
// No shield effects so perform a regular CheckShipHit
else if (shieldEffectsObjectPrefabID < 0 && CheckShipHit(hitInfo, damageAmount, damageType, sourceShipId, sourceSquadronId, projectilePrefabID))
{
// No need to do anything else here
}
else
{
// If it hit an object with a DamageReceiver script attached, take appropriate action like call a custom method
CheckObjectHit(hitInfo, damageAmount, damageType, sourceShipId, sourceSquadronId, projectilePrefabID);
}
if (isShieldHit && shieldEffectsObjectPrefabID >= 0)
{
if (sscManager == null) { sscManager = SSCManager.GetOrCreateManager(); }
if (sscManager != null)
{
InstantiateEffectsObjectParameters ieParms = new InstantiateEffectsObjectParameters
{
effectsObjectPrefabID = shieldEffectsObjectPrefabID,
position = hitInfo.point + (hitInfo.normal * 0.0005f),
rotation = Quaternion.LookRotation(-hitInfo.normal),
};
// For projectiles we don't need to get the effectsObject key from ieParms.
sscManager.InstantiateEffectsObject(ref ieParms);
}
}
else if (!isShieldHit && effectsObjectPrefabID >= 0 && effectsObject != null)
{
if (sscManager == null) { sscManager = SSCManager.GetOrCreateManager(); }
//SSCManager sscManager = SSCManager.GetOrCreateManager();
if (sscManager != null)
{
InstantiateEffectsObjectParameters ieParms = new InstantiateEffectsObjectParameters
{
effectsObjectPrefabID = effectsObjectPrefabID,
position = hitInfo.point,
rotation = transform.rotation
};
// For projectiles we don't need to get the effectsObject key from ieParms.
sscManager.InstantiateEffectsObject(ref ieParms);
}
}
DestroyProjectile();
return true;
}
else { return false; }
}
/// <summary>
/// Removes the projectile. How this is done depends on what system is being used (i.e. pooling etc.).
/// When overriding, write your own logic then call base.DestroyProjectile().
/// </summary>
protected virtual void DestroyProjectile()
{
if (usePooling)
{
if (isKinematicGuideToTarget)
{
ClearTarget();
}
// Deactivate the projectile
gameObject.SetActive(false);
}
else
{
// Destroy the projectile
Destroy(gameObject);
}
}
/// <summary>
/// It MUST update the velocity and tranform.forward. Does not update the transform.position.
/// FUTURE - be able to track other rigid bodies, not just Ships.
/// FUTURE - be able to be guided towards a Location (which may not have a gameObject)
/// To write your own version set sscManager.callbackProjectileMoveTo(..).
/// </summary>
protected void GuideToTarget()
{
if (isKinematicGuideToTarget && (isTargetShip || isTargetShipDamageRegion || targetGameObject != null))
{
// Did the target become null without us knowing?
if ((isTargetShip || isTargetShipDamageRegion) && targetShip == null) { ClearTarget(); }
// If the target ship has been destroyed, stop targetting this ship
else if ((isTargetShip || isTargetShipDamageRegion) && targetShip.shipInstance.Destroyed()) { ClearTarget(); }
else
{
// Simple (rubbish) chase-style guidance
//transform.LookAt(targetShip.transform);
//velocity = transform.forward * velocity.magnitude;
// Augmented Proportional Navigation
Vector3 targetPosition = isTargetShip ? targetShip.shipInstance.TransformPosition : (isTargetShipDamageRegion ? targetShip.shipInstance.GetDamageRegionWSPosition(targetShipDamageRegion) : targetGameObject.transform.position);
lastFrameLOSN = thisFrameLOSN;
thisFrameLOSN = (targetPosition - transform.position).normalized;
if (lastFrameLOSN.x != 0f || lastFrameLOSN.y != 0f || lastFrameLOSN.z != 0f)
{
Vector3 deltaLOS = thisFrameLOSN - lastFrameLOSN;
// The rate at which the LOS angle is changing
// When angleRateLOS is zero the missile is on a collision course with the target.
float angleRateLOS = deltaLOS.magnitude;
// Closing velocity is -deltaLOS.
// Proportional Navigation v = NavConst * closing_velocity * angleRateLOS
float apnBias = gravitationalAcceleration * Time.deltaTime * (NavConst * 0.5f);
// NOTE: Final velocity should have the same magnitude (length) of the original velocity.
Vector3 apn_acceleration = (thisFrameLOSN * (angleRateLOS * NavConst) + (deltaLOS * apnBias)).normalized;
// Avoid forward = zero vector.
if (apn_acceleration.x == 0f && apn_acceleration.y == 0f && apn_acceleration.z == 0f) { }
else
{
// The initial rotation is defined by the transform.forwards direction
// The target rotation is defined by the apn_acceleration direction
// In this frame we are only allowed to turn by (guidedMaxTurnSpeed * Time.deltaTime) degrees
transform.forward = Quaternion.RotateTowards(Quaternion.LookRotation(transform.forward),
Quaternion.LookRotation(apn_acceleration), guidedMaxTurnSpeed * Time.deltaTime) * Vector3.forward;
}
velocity = transform.forward * velocity.magnitude;
// Commanded Acceleration = NavConst * Vc * angleRateLOS + ( NavConst * Nt ) / 2
}
}
}
}
#endregion
#region Private Methods
/// <summary>
/// [INTERNAL ONLY]
/// Stop targeting anything.
/// </summary>
internal void ClearTarget()
{
targetShip = null;
targetGameObject = null;
targetShipDamageRegion = null;
targetguidHash = 0;
isTargetShip = false;
isTargetShipDamageRegion = false;
}
/// <summary>
/// [INTERNAL ONLY] - subject to change without notice.
/// Currently does not support DOTS/ECS
/// </summary>
/// <param name="shipControlModule"></param>
internal void SetTargetShip(ShipControlModule shipControlModule)
{
if (!useECS && isKinematicGuideToTarget)
{
targetShip = shipControlModule;
isTargetShip = targetShip != null;
if (isTargetShip) { targetGameObject = shipControlModule.gameObject; }
else { targetGameObject = null; }
}
}
/// <summary>
/// [INTERNAL ONLY] - subject to change without notice.
/// Currently does not support DOTS/ECS.
/// If a ship is being targeted, call SetTargetShip(..) instead.
/// </summary>
/// <param name="targetGameObj"></param>
internal void SetTarget(GameObject targetGameObj)
{
if (!useECS && isKinematicGuideToTarget)
{
targetGameObject = targetGameObj;
// clear target ship
targetShip = null;
isTargetShip = false;
}
}
#endregion
#region Public Static Methods
/// <summary>
/// Determine if a ship has been hit. Apply damage as required.
/// Can be called by both an instance of ProjectileModule and ProjectileSystem.
/// </summary>
/// <param name="raycastHitInfo"></param>
/// <param name="projectileDamageAmount"></param>
/// <param name="projectileDamageType"></param>
/// <param name="sourceShipId"></param>
/// <param name="sourceShipSquadronId"></param>
/// <param name="projectilePrefabID"></param>
/// <returns></returns>
public static bool CheckShipHit (RaycastHit raycastHitInfo, float projectileDamageAmount, DamageType projectileDamageType,
int sourceShipId, int sourceShipSquadronId, int projectilePrefabID)
{
bool isHit = false;
Rigidbody hitRigidbody = raycastHitInfo.rigidbody;
if (hitRigidbody != null)
{
// Check if there is a ship control module attached to the hit object
ShipControlModule shipControlModule;
//if (shipControlModule != null)
if (hitRigidbody.TryGetComponent(out shipControlModule))
{
// Apply damage to the ship
shipControlModule.shipInstance.ApplyNormalDamage(projectileDamageAmount, projectileDamageType, raycastHitInfo.point);
// If required, call the custom method
if (shipControlModule.callbackOnHit != null)
{
// Get a reference to the SSCManager
SSCManager sscManager = SSCManager.GetOrCreateManager();
// Create a struct with the necessary parameters
CallbackOnShipHitParameters callbackOnShipHitParameters = new CallbackOnShipHitParameters
{
hitInfo = raycastHitInfo,
projectilePrefab = sscManager.GetProjectilePrefab(projectilePrefabID),
beamPrefab = null,
damageAmount = projectileDamageAmount,
sourceSquadronId = sourceShipSquadronId,
sourceShipId = sourceShipId
};
// Call the custom callback
shipControlModule.callbackOnHit(callbackOnShipHitParameters);
}
isHit = true;
}
}
return isHit;
}
/// <summary>
/// Determine if a ship has been hit. Apply damage as required.
/// </summary>
/// <param name="raycastHitInfo"></param>
/// <param name="projectileDamageAmount"></param>
/// <param name="sourceShipId"></param>
/// <param name="sourceShipSquadronId"></param>
public static bool CheckShipHit (RaycastHit raycastHitInfo, float projectileDamageAmount, DamageType projectileDamageType,
int sourceShipId, int sourceShipSquadronId, int projectilePrefabID, out ShipControlModule shipControlModule)
{
bool isHit = false;
Rigidbody hitRigidbody = raycastHitInfo.rigidbody;
if (hitRigidbody != null)
{
// Check if there is a ship control module attached to the hit object
if (hitRigidbody.TryGetComponent(out shipControlModule))
{
// Apply damage to the ship
shipControlModule.shipInstance.ApplyNormalDamage(projectileDamageAmount, projectileDamageType, raycastHitInfo.point);
// If required, call the custom method
if (shipControlModule.callbackOnHit != null)
{
// Get a reference to the SSCManager
SSCManager sscManager = SSCManager.GetOrCreateManager();
// Create a struct with the necessary parameters
CallbackOnShipHitParameters callbackOnShipHitParameters = new CallbackOnShipHitParameters
{
hitInfo = raycastHitInfo,
projectilePrefab = sscManager.GetProjectilePrefab(projectilePrefabID),
beamPrefab = null,
damageAmount = projectileDamageAmount,
sourceSquadronId = sourceShipSquadronId,
sourceShipId = sourceShipId
};
// Call the custom callback
shipControlModule.callbackOnHit(callbackOnShipHitParameters);
}
isHit = true;
}
}
else { shipControlModule = null; }
return isHit;
}
/// <summary>
/// Determine if an object with a DamageReceiver component attached has been hit by a Projectile.
/// TODO - check performance
/// </summary>
/// <param name="raycastHitInfo"></param>
/// <param name="projectileDamageAmount"></param>
/// <param name="sourceShipId"></param>
/// <param name="sourceShipSquadronId"></param>
/// <param name="projectilePrefabID"></param>
/// <returns></returns>
public static bool CheckObjectHit (RaycastHit raycastHitInfo, float projectileDamageAmount, DamageType projectileDamageType,
int sourceShipId, int sourceShipSquadronId, int projectilePrefabID)
{
bool isHit = false;
if (raycastHitInfo.collider != null)
{
DamageReceiver damageReceiver = raycastHitInfo.collider.GetComponent<DamageReceiver>();
if (damageReceiver != null && damageReceiver.callbackOnHit != null)
{
// Get a reference to the SSCManager
SSCManager sscManager = SSCManager.GetOrCreateManager();
// Create a struct with the necessary parameters
CallbackOnObjectHitParameters callbackOnObjectHitParameters = new CallbackOnObjectHitParameters
{
hitInfo = raycastHitInfo,
projectilePrefab = sscManager.GetProjectilePrefab(projectilePrefabID),
beamPrefab = null,
damageAmount = projectileDamageAmount,
sourceSquadronId = sourceShipSquadronId
};
// Call the custom callback
damageReceiver.callbackOnHit(callbackOnObjectHitParameters);
isHit = true;
}
}
return isHit;
}
#endregion
#region Public API Methods
/// <summary>
/// If
/// </summary>
/// <returns></returns>
public ShipControlModule GetTargetShip()
{
if (isKinematicGuideToTarget) { return targetShip; }
else { return null; }
}
/// <summary>
/// Re-enable a projectile after it has been disabled with
/// DisableProjectile(). See also SSCManager.ResumeProjectiles()
/// </summary>
public void EnableProjectile()
{
isProjectileEnabled = true;
}
/// <summary>
/// Useful when you want to pause the action in a game.
/// Should always be called BEFORE setting Time.timeScale to 0.
/// See also SSCManager.PauseProjectiles().
/// </summary>
public void DisableProjectile()
{
isProjectileEnabled = false;
}
#endregion
}
}