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

431 lines
16 KiB
C#

using UnityEngine;
// Sci-Fi Ship Controller. Copyright (c) 2018-2023 SCSM Pty Ltd. All rights reserved.
namespace SciFiShipController
{
[AddComponentMenu("Sci-Fi Ship Controller/Weapon Components/Beam Module")]
[HelpURL("http://scsmmedia.com/ssc-documentation")]
public class BeamModule : MonoBehaviour
{
#region Public Enumerations
#endregion
#region Public Variables
/// <summary>
/// The speed the beam travels in metres per second
/// </summary>
public float speed = 200f;
/// <summary>
/// The type of damage the beam 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 beam 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 ProjectileModule.DamageType damageType = ProjectileModule.DamageType.Default;
/// <summary>
/// The amount of damage this beam does, per second, to the ship or object it hits. NOTE: Non-ship objects need a DamageReceiver component.
/// </summary>
public float damageRate = 10f;
/// <summary>
/// Whether pooling is used when spawning beams 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 = 10;
/// <summary>
/// The maximum allowed size of the pool. Only relevant when usePooling is enabled.
/// </summary>
public int maxPoolSize = 100;
/// <summary>
/// The start width (in metres) of the beam on the local x-axis
/// In this version the width will be the same for the entire length of the beam.
/// </summary>
[Range(0.001f, 5f)] public float beamStartWidth = 0.1f;
/// <summary>
/// The minimum amount of time, in seconds, the beam must be active
/// </summary>
[Range(0.1f, 5f)] public float minBurstDuration = 0.5f;
/// <summary>
/// The maximum amount of time, in seconds, the beam can be active in a single burst
/// </summary>
[Range(0.1f, 30f)] public float maxBurstDuration = 5f;
/// <summary>
/// The time (in seconds) it takes a single beam to discharge the beam weapon from full charge
/// </summary>
[Range(0.1f, 60f)] public float dischargeDuration = 10f;
/// <summary>
/// The ID number for this beam prefab (as assigned by the Ship Controller Manager in the scene).
/// This is the index in the SSCManager beamTemplatesList.
/// [INTERNAL USE ONLY]
/// </summary>
public int beamPrefabID;
/// <summary>
/// The sound or particle FX used when a collision occurs.
/// If you modify this, call shipControlModule.ReinitialiseShipBeams() for each ship
/// this beam is used on.
/// </summary>
public EffectsModule effectsObject = null;
/// <summary>
/// The ID number for this beam's 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 and/or particle FX used when a beam is fired from a weapon.
/// If you modify this, call shipControlModule.ReinitialiseShipBeams() for each ship
/// this beam 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 beam
/// is instantiated in front or forwards from the actual weapon.
/// </summary>
public Vector3 muzzleEffectsOffset = Vector3.zero;
/// <summary>
/// The ID number for this beam'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>
[System.NonSerialized] public int muzzleEffectsObjectPrefabID = -1;
/// <summary>
/// The Id of the ship that fired the beam
/// </summary>
[System.NonSerialized] public int sourceShipId = -1;
/// <summary>
/// The Squadron which the ship belonged to when it fired the beam
/// </summary>
[System.NonSerialized] public int sourceSquadronId = -1;
#endregion
#region Private and Internal Variables
[System.NonSerialized] internal bool isInitialised = false;
[System.NonSerialized] internal bool isBeamEnabled = false;
[System.NonSerialized] internal float burstDuration = 0f;
/// <summary>
/// [INTERNAL ONLY]
/// Reference to the LineRenderer component which should be on a child
/// gameobject of this module.
/// </summary>
[System.NonSerialized] internal LineRenderer lineRenderer = null;
/// <summary>
/// [INTERNAL ONLY]
/// Used to determine uniqueness
/// </summary>
[System.NonSerialized] internal uint itemSequenceNumber;
/// <summary>
/// The zero-based index of the weapon on the ship that fired the beam
/// </summary>
[System.NonSerialized] internal int weaponIndex;
/// <summary>
/// The zero-based index of the fire position on the weapon that fired the beam
/// </summary>
[System.NonSerialized] internal int firePositionIndex;
/// <summary>
/// The item key of the muzzle effects object (if any) firing from this beam
/// </summary>
[System.NonSerialized] internal SSCEffectItemKey muzzleEffectsItemKey;
/// <summary>
/// The item key of the effects object that is spawned when the beam hits something
/// </summary>
[System.NonSerialized] internal SSCEffectItemKey effectsItemKey;
#endregion
#region Internal Delegates
internal delegate void CallbackOnMove(BeamModule beamModule);
[System.NonSerialized] internal CallbackOnMove callbackOnMove = null;
#endregion
#region Internal Static Variables
internal static uint nextSequenceNumber = 1;
#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 && isBeamEnabled)
{
burstDuration += Time.deltaTime;
if (callbackOnMove != null) { callbackOnMove(this); }
}
}
#endregion
#region Private and Internal Methods
/// <summary>
/// Intialise the beam and return it's unique sequence number
/// </summary>
/// <param name="ibParms"></param>
internal uint InitialiseBeam(InstantiateBeamParameters ibParms)
{
// Store the index to the BeamTemplate in the SSCManager beamTemplatesList
// This is used with Beam FX when we know the BeamModule but not the parent BeamTemplate.
this.beamPrefabID = ibParms.beamPrefabID;
// Store the index to the EffectsObjectTemplate in sscManager.effectsObjectTemplatesList
this.effectsObjectPrefabID = ibParms.effectsObjectPrefabID;
// Store the details on what fired the beam
this.sourceShipId = ibParms.shipId;
this.sourceSquadronId = ibParms.squadronId;
this.weaponIndex = ibParms.weaponIndex;
this.firePositionIndex = ibParms.firePositionIndex;
IncrementSequenceNumber();
muzzleEffectsItemKey = new SSCEffectItemKey(-1, -1, 0);
effectsItemKey = new SSCEffectItemKey(-1, -1, 0);
if (lineRenderer == null) { lineRenderer = GetComponentInChildren<LineRenderer>(); }
if (lineRenderer != null)
{
lineRenderer.startWidth = beamStartWidth;
lineRenderer.endWidth = beamStartWidth;
lineRenderer.alignment = LineAlignment.View;
lineRenderer.startColor = Color.white;
lineRenderer.endColor = Color.white;
// Our calculations in ship.MoveBeam(..) assume world-space positions
lineRenderer.useWorldSpace = true;
// After a given amount of time, automatically destroy this beam
burstDuration = 0f;
isBeamEnabled = gameObject.activeSelf;
isInitialised = true;
}
return itemSequenceNumber;
}
/// <summary>
/// Makes this BeamModule unique from all others that have gone before them.
/// This is called every time beam is Initialised.
/// </summary>
internal void IncrementSequenceNumber()
{
itemSequenceNumber = nextSequenceNumber++;
// if sequence number needs to be wrapped, do so to a high-ish number that is unlikely to be in use
if (nextSequenceNumber > uint.MaxValue - 100) { nextSequenceNumber = 100000; }
}
/// <summary>
/// [INTERNAL ONLY]
/// Removes the beam. How this is done depends on what system is being used (i.e. pooling etc.).
/// </summary>
internal void DestroyBeam()
{
// Reset
weaponIndex = -1;
firePositionIndex = -1;
isBeamEnabled = false;
// Remove the reference
callbackOnMove = null;
SSCManager sscManager = null;
// Stop muzzle fx
if (muzzleEffectsItemKey.effectsObjectSequenceNumber > 0)
{
sscManager = SSCManager.GetOrCreateManager();
if (sscManager != null)
{
sscManager.DestroyEffectsObject(muzzleEffectsItemKey);
}
}
// Stop hit fx
if (effectsItemKey.effectsObjectSequenceNumber > 0)
{
if (sscManager == null) { sscManager = SSCManager.GetOrCreateManager(); }
if (sscManager != null)
{
sscManager.DestroyEffectsObject(effectsItemKey);
}
}
if (usePooling)
{
// Deactivate the beam
gameObject.SetActive(false);
}
else
{
// Destroy the beam
Destroy(gameObject);
}
}
#endregion
#region Public API Static Methods
/// <summary>
/// Determine if a ship has been hit. Apply damage as required based on the damage rate, damage type
/// and hit duration.
/// Hit duration is the time in seconds that the beam has been in contact with the target object since
/// it was last checked (typically the duration of the last frame or time step).
/// </summary>
/// <param name="raycastHitInfo"></param>
/// <param name="beamModule">Must not be null</param>
/// <param name="hitDuration"></param>
/// <returns></returns>
public static bool CheckShipHit (RaycastHit raycastHitInfo, BeamModule beamModule, float hitDuration)
{
bool isHit = false;
Rigidbody hitRigidbody = raycastHitInfo.rigidbody;
if (hitRigidbody != null && beamModule != null)
{
// Check if there is a ship control module attached to the hit object
ShipControlModule shipControlModule = hitRigidbody.GetComponent<ShipControlModule>();
if (shipControlModule != null)
{
float damageAmount = beamModule.damageRate * hitDuration;
// Apply damage to the ship
shipControlModule.shipInstance.ApplyNormalDamage(damageAmount, beamModule.damageType, 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 = null,
beamPrefab = sscManager.GetBeamPrefab(beamModule.beamPrefabID),
damageAmount = damageAmount,
sourceSquadronId = beamModule.sourceSquadronId,
sourceShipId = beamModule.sourceShipId
};
// Call the custom callback
shipControlModule.callbackOnHit(callbackOnShipHitParameters);
}
isHit = true;
}
}
return isHit;
}
/// <summary>
/// Determine if an object with a DamageReceiver component attached has been hit by a Beam.
/// </summary>
/// <param name="raycastHitInfo"></param>
/// <param name="beamModule">Must not be null</param>
/// <returns></returns>
public static bool CheckObjectHit (RaycastHit raycastHitInfo, BeamModule beamModule, float hitDuration)
{
bool isHit = false;
Rigidbody hitRigidbody = raycastHitInfo.rigidbody;
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 = null,
beamPrefab = sscManager.GetBeamPrefab(beamModule.beamPrefabID),
damageAmount = beamModule.damageRate * hitDuration,
sourceSquadronId = beamModule.sourceSquadronId
};
// Call the custom callback
damageReceiver.callbackOnHit(callbackOnObjectHitParameters);
isHit = true;
}
}
return isHit;
}
#endregion
#region Public API Methods
/// <summary>
/// Re-enable a beam after it has been disabled with
/// DisableBeam(). See also SSCManager.ResumeBeams()
/// </summary>
public void EnableBeam()
{
isBeamEnabled = 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.PauseBeams().
/// </summary>
public void DisableBeam()
{
isBeamEnabled = false;
}
#endregion
}
}