projectEli/Assets/NeoFPS/Samples/SinglePlayer/Scenes/FeatureDemos/FiringRange/FiringRangeTarget.cs
2022-11-06 20:28:33 -05:00

257 lines
7.5 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using NeoSaveGames.Serialization;
using NeoSaveGames;
namespace NeoFPS.Samples.SinglePlayer
{
[HelpURL("https://docs.neofps.com/manual/samplesref-mb-firingrangetarget.html")]
public class FiringRangeTarget : MonoBehaviour, IDamageHandler, INeoSerializableComponent
{
[SerializeField, Tooltip("The damage threshold for the target to drop and register as a hit.")]
private float m_DamageThreshold = 1f;
[SerializeField, Tooltip("The duration the target will be visible. If the target is not hit in this time it registers as a miss.")]
private float m_PopupDuration = 0.5f;
[SerializeField, Tooltip("The axis to rotate the target around when it pops up.")]
private Vector3 m_RotationAxis = new Vector3(1f, 0f, 0f);
[SerializeField, Tooltip("The rotation of the target around the specified axis when it is fully hidden.")]
private float m_HiddenRotation = 180f;
private Transform m_RotationTransform = null;
private FiringRangeSequencer m_Sequencer = null;
private Coroutine m_SequenceCoroutine = null;
private float m_Lerp = 0f;
private float m_Timer = 0f;
private bool m_Hit = false;
private TargetState m_State = TargetState.Idle;
enum TargetState
{
Idle,
Raising,
Raised,
Lowering
}
protected bool initialised
{
get;
private set;
}
public bool hit
{
get { return m_Hit; }
}
public virtual bool hidden
{
get { return m_SequenceCoroutine == null; }
}
#if UNITY_EDITOR
void OnValidate()
{
if (m_DamageThreshold < 1f)
m_DamageThreshold = 1f;
if (m_PopupDuration < 0.1f)
m_PopupDuration = 0.1f;
m_HiddenRotation = Mathf.Clamp(m_HiddenRotation, -180f, 180f);
}
#endif
public virtual void Initialise (FiringRangeSequencer sequencer)
{
m_Sequencer = sequencer;
m_RotationTransform = transform;
if (!initialised)
{
m_RotationTransform.localRotation = Quaternion.AngleAxis(m_HiddenRotation, m_RotationAxis);
m_Hit = false;
initialised = true;
}
}
public virtual void Popup (float duration)
{
if (m_SequenceCoroutine != null)
StopCoroutine (m_SequenceCoroutine);
m_Timer = duration;
m_SequenceCoroutine = StartCoroutine (SequencePopup (0f));
}
public virtual void ResetTarget ()
{
if (!hidden && gameObject.activeInHierarchy)
{
if (m_SequenceCoroutine != null)
StopCoroutine(m_SequenceCoroutine);
m_SequenceCoroutine = StartCoroutine (SequenceReset(0f));
}
}
private IEnumerator SequencePopup (float lerp)
{
m_State = TargetState.Raising;
float inversePopup = 1f / m_PopupDuration;
// Pop up
m_Lerp = lerp;
while (m_Lerp < 1f)
{
yield return null;
m_Lerp += Time.deltaTime * inversePopup;
// Flip up
m_RotationTransform.localRotation = Quaternion.AngleAxis (Mathf.Lerp (m_HiddenRotation, 0f, m_Lerp), m_RotationAxis);
}
StartRaisedSequence();
}
private IEnumerator SequenceRaised()
{
m_State = TargetState.Raised;
while (!hit && m_Timer > 0f)
{
yield return null;
m_Timer -= Time.deltaTime;
}
yield return null;
StartResetSequence();
}
private IEnumerator SequenceReset (float lerp)
{
m_State = TargetState.Lowering;
float inversePopup = 1f / m_PopupDuration;
// Pop down
m_Lerp = lerp;
while (m_Lerp < 1f)
{
yield return null;
m_Lerp += Time.deltaTime * inversePopup;
// Flip down
m_RotationTransform.localRotation = Quaternion.AngleAxis (Mathf.Lerp (0f, m_HiddenRotation, m_Lerp), m_RotationAxis);
}
if (m_Hit == true)
m_Hit = false;
else
m_Sequencer.AddMiss();
yield return null;
m_State = TargetState.Idle;
m_SequenceCoroutine = null;
}
protected virtual void StartRaisedSequence()
{
m_SequenceCoroutine = StartCoroutine(SequenceRaised());
}
protected virtual void StartResetSequence()
{
m_SequenceCoroutine = StartCoroutine(SequenceReset(0f));
}
protected virtual void OnSequenceCompleted()
{
}
#region IDamageHandler Implementation
private DamageFilter m_InDamageFilter = DamageFilter.AllDamageAllTeams;
public DamageFilter inDamageFilter
{
get { return m_InDamageFilter; }
set { m_InDamageFilter = value; }
}
public DamageResult AddDamage (float damage)
{
return AddDamage (damage, null);
}
public DamageResult AddDamage (float damage, IDamageSource source)
{
if (damage >= m_DamageThreshold && !hidden && !hit)
{
m_Hit = true;
m_Sequencer.AddHit();
ResetTarget ();
// Report damage dealt
if (damage > 0f && source != null && source.controller != null)
source.controller.currentCharacter.ReportTargetHit(false);
}
return DamageResult.Standard;
}
public DamageResult AddDamage(float damage, RaycastHit hit)
{
return AddDamage(damage);
}
public DamageResult AddDamage(float damage, RaycastHit hit, IDamageSource source)
{
return AddDamage(damage, source);
}
#endregion
private static readonly NeoSerializationKey k_StateKey = new NeoSerializationKey("state");
private static readonly NeoSerializationKey k_LerpKey = new NeoSerializationKey("lerp");
private static readonly NeoSerializationKey k_TimerKey = new NeoSerializationKey("timer");
private static readonly NeoSerializationKey k_HitKey = new NeoSerializationKey("hit");
public virtual void WriteProperties(INeoSerializer writer, NeoSerializedGameObject nsgo, SaveMode saveMode)
{
if (m_SequenceCoroutine != null)
{
writer.WriteValue(k_StateKey, (int)m_State);
writer.WriteValue(k_LerpKey, m_Lerp);
writer.WriteValue(k_TimerKey, m_Timer);
writer.WriteValue(k_HitKey, m_Hit);
}
}
public virtual void ReadProperties(INeoDeserializer reader, NeoSerializedGameObject nsgo)
{
int state = 0;
if (reader.TryReadValue(k_StateKey, out state, 0))
{
m_State = (TargetState)state;
reader.TryReadValue(k_LerpKey, out m_Lerp, m_Lerp);
reader.TryReadValue(k_TimerKey, out m_Timer, m_Timer);
reader.TryReadValue(k_HitKey, out m_Hit, m_Hit);
switch(m_State)
{
case TargetState.Raising:
m_SequenceCoroutine = StartCoroutine(SequencePopup(m_Lerp));
break;
case TargetState.Raised:
m_SequenceCoroutine = StartCoroutine(SequenceRaised());
break;
case TargetState.Lowering:
m_SequenceCoroutine = StartCoroutine(SequenceReset(m_Lerp));
break;
}
}
initialised = true;
}
}
}