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
{
///
/// Implement a particle and/or sound effect when something is hit, damaged or destroyed.
/// This can include multiple child particle systems and/or an audio source attached to the parent gameobject.
///
[AddComponentMenu("Sci-Fi Ship Controller/Object Components/Effects Module")]
[HelpURL("http://scsmmedia.com/ssc-documentation")]
public class EffectsModule : MonoBehaviour
{
#region Public Variables and Properties
///
/// Whether pooling is used when spawning effects objects of this type.
/// Currently we don't support changing this at runtime.
///
public bool usePooling = false;
///
/// The starting size of the pool.
///
public int minPoolSize = 5;
///
/// The maximum allowed size of the pool.
///
public int maxPoolSize = 100;
///
/// The effects object will be automatically despawned after this amount of time (in seconds) has elapsed.
///
public float despawnTime = 3f;
///
/// If an audio source is included, the volume can be optionally set at runtime to the default volume.
///
[Range(0f, 1f)] public float defaultVolume = 1f;
///
/// Does this object get parented to another object when activated? If so,
/// it will be reparented to the pool transform after use.
///
public bool isReparented = false;
///
/// Is the effects module enabled to emit particles and play an audio clip?
/// See also EnableEffects() and DisableEffects().
///
public bool IsEffectsModuleEnabled { get { return isEffectsModuleEnabled; } }
/////
///// The ID number for this effects object prefab (as assigned by the Ship Controller Manager in the scene).
///// This is the index in the SSCManager effectsObjectTemplatesList
///// [INTERNAL USE ONLY]
/////
//public int effectsObjectPrefabID;
#endregion
#region Private Variables
private AudioSource audioSource = null;
private AudioClip audioClip = null;
private List shurikenParticlesList = null;
private int numShurikenParticles = 0;
private bool isEffectsModuleEnabled = true;
private bool isAudioSourcePaused = false;
///
/// [INTERNAL ONLY]
/// Used to determine uniqueness
///
[System.NonSerialized] internal uint itemSequenceNumber;
///
/// [INTERNAL ONLY]
/// If isReparented is true, this is the original parent
/// transform used in the pooling system.
///
[System.NonSerialized] internal Transform poolParentTrfm;
#endregion
#region Internal Static Variables
internal static uint nextSequenceNumber = 1;
internal readonly static string destroyMethodName = "DestroyEffectsObject";
#endregion
#region Internal Methods
///
/// [INTERNAL USE ONLY]
/// Get a reference to the audio source and cache it, if it hasn't already been done.
/// NOTE: For performance reasons, the audio source must be on the parent gameobject.
///
///
internal AudioSource GetAudioSource()
{
if (audioSource == null) { audioSource = GetComponent(); }
return audioSource;
}
///
/// [INTERNAL USE ONLY]
/// Get a reference to the audio clip. Cache the audio source and audio clip if they
/// are not already cached.
///
///
internal AudioClip GetAudioClip()
{
if (audioSource == null) { audioSource = GetComponent(); }
// If the audioClip is not defined, attempt to cache it (if it exists)
if (audioClip == null && audioSource != null) { audioClip = audioSource.clip; }
return audioClip;
}
///
/// [INTERNAL USE ONLY]
/// Replace the existing clip with a new one
///
///
internal void SetAudioClip(AudioClip newAudioClip)
{
if (audioSource != null)
{
audioSource.clip = newAudioClip;
audioClip = audioSource.clip;
}
}
#endregion
#region Public Methods
///
/// Check to see if the EffectsModule has an AudioSource attached.
///
///
public bool HasAudioSource()
{
if (audioSource != null) { return true; }
else if (itemSequenceNumber != 0u) { return false; }
else
{
return TryGetComponent(out audioSource);
}
}
///
/// Check to see if the EffectsModule has one or more ParticleSystems
///
///
public bool HasParticleSystems()
{
if (numShurikenParticles > 0) { return true; }
else if (itemSequenceNumber != 0u) { return false; }
else
{
ParticleSystem[] particleSystems = GetComponentsInChildren();
return (particleSystems == null ? 0 : particleSystems.Length) > 0f;
}
}
///
/// Initialises the effects object. Play the optional audio clip if there is one.
/// Returns the unique identifier for this EffectsObject instance.
///
public uint InitialiseEffectsObject ()
{
// Check for an audiosource attached to the same gameobject in the prefab.
// For performance reasons, don't search child objects.
if (audioSource == null) { audioSource = GetComponent(); }
// If the audioClip is not defined, attempt to cache it (if it exists)
if (audioClip == null && audioSource != null) { audioClip = audioSource.clip; }
// If there is a valid clip, play it.
if (audioClip != null && audioSource.isActiveAndEnabled && !audioSource.isPlaying) { audioSource.Play(); }
isAudioSourcePaused = false;
IncrementSequenceNumber();
if (usePooling)
{
if (isReparented) { poolParentTrfm = transform.parent; }
// If pooling, cache the list of particle systems for this effect
if (shurikenParticlesList == null)
{
shurikenParticlesList = new List();
shurikenParticlesList.AddRange(GetComponentsInChildren());
numShurikenParticles = shurikenParticlesList == null ? 0 : shurikenParticlesList.Count;
}
ParticleSystem particleSystem = null;
// Start all particle systems that haven't started automatically on awake
for (int spIdx = 0; spIdx < numShurikenParticles; spIdx++)
{
particleSystem = shurikenParticlesList[spIdx];
if (particleSystem != null && !particleSystem.isPlaying) { particleSystem.Play(true); }
}
}
// After a given amount of time, automatically destroy this effects object
Invoke(destroyMethodName, despawnTime);
return itemSequenceNumber;
}
#endregion
#region Private Methods
///
/// Makes this EffectsModule unique from all others that have gone before them.
/// This is called every time the effect is Initialised.
///
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; }
}
///
/// Removes the effects object. How this is done depends on what system is being used (i.e. pooling etc.).
/// If pooling is enabled, and there is an audio clip still playing, stop it.
///
internal void DestroyEffectsObject()
{
//Debug.Log("[DEBUG] EffectsModule.DestroyEffectsObject T: " + Time.time);
if (usePooling)
{
// If there is a clip and it is still playing, stop it
if (audioClip != null && audioSource.isActiveAndEnabled && audioSource.isPlaying)
{
audioSource.Stop();
isAudioSourcePaused = false;
}
ParticleSystem particleSystem = null;
// Stop all particle systems
for (int spIdx = 0; spIdx < numShurikenParticles; spIdx++)
{
particleSystem = shurikenParticlesList[spIdx];
if (particleSystem != null && particleSystem.isPlaying) { shurikenParticlesList[spIdx].Stop(true); }
}
// Deactivate the effects object
gameObject.SetActive(false);
if (isReparented && poolParentTrfm != null)
{
//Debug.Log("[DEBUG] EffectsModule.DestroyEffectsObject and reparent " + name + " T: " + Time.time);
transform.SetParent(poolParentTrfm);
}
}
else
{
// Destroy the effects object
Destroy(gameObject);
}
}
#endregion
#region Public API Methods
///
/// Re-enable an Effect after it has been disabled with
/// DisableEffects(). See also SSCManager.ResumeEffectsObjects().
/// NOTE: Only takes action if IsEffectsModuleEnabled is false.
///
public void EnableEffects()
{
if (!isEffectsModuleEnabled)
{
// If there is a clip and it is still playing, unpause it
if (audioClip != null && audioSource.isActiveAndEnabled && isAudioSourcePaused)
{
audioSource.UnPause();
isAudioSourcePaused = false;
}
ParticleSystem particleSystem = null;
// Play (resume) all paused particle systems
for (int spIdx = 0; spIdx < numShurikenParticles; spIdx++)
{
particleSystem = shurikenParticlesList[spIdx];
if (particleSystem != null && particleSystem.isPaused) { shurikenParticlesList[spIdx].Play(true); }
}
isEffectsModuleEnabled = true;
}
}
///
/// Useful when you want to pause the action in a game.
/// Should always be called BEFORE setting Time.timeScale to 0.
/// See also SSCManager.PauseEffectsObjects().
/// NOTE: Only takes action if IsEffectsModuleEnabled is true.
///
public void DisableEffects()
{
if (isEffectsModuleEnabled)
{
// If there is a clip and it is still playing, pause it
if (audioClip != null && audioSource.isActiveAndEnabled && audioSource.isPlaying)
{
audioSource.Pause();
isAudioSourcePaused = true;
}
ParticleSystem particleSystem = null;
// Pause all particle systems
for (int spIdx = 0; spIdx < numShurikenParticles; spIdx++)
{
particleSystem = shurikenParticlesList[spIdx];
if (particleSystem != null && particleSystem.isPlaying) { shurikenParticlesList[spIdx].Pause(true); }
}
isEffectsModuleEnabled = false;
}
}
#endregion
}
}