projectEli/Assets/NeoFPS/Core/Weapons/WieldableTools/WieldableTool.cs
2022-11-06 20:28:33 -05:00

451 lines
15 KiB
C#

using NeoFPS.Constants;
using NeoSaveGames;
using NeoSaveGames.Serialization;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
namespace NeoFPS.WieldableTools
{
[ExecuteAlways]
public class WieldableTool : BaseWieldableItem, IWieldableTool
{
[SerializeField, Tooltip("Actions to fire immediately when the tool primary is used (in sequence).")]
private List<BaseWieldableToolModule> m_PrimaryModules = new List<BaseWieldableToolModule>();
[SerializeField, Tooltip("Actions to fire immediately when the tool primary is used (in sequence).")]
private List<BaseWieldableToolModule> m_SecondaryModules = new List<BaseWieldableToolModule>();
[Header("Misc")]
[SerializeField, Tooltip("The crosshair to show when the weapon is drawn.")]
private FpsCrosshair m_Crosshair = FpsCrosshair.Default;
#if UNITY_EDITOR
[SerializeField, HideInInspector]
private List<BaseWieldableToolModule> m_UnmappedModules = new List<BaseWieldableToolModule>();
#endif
public event UnityAction onPrimaryActionStart;
public event UnityAction onPrimaryActionEnd;
public event UnityAction onSecondaryActionStart;
public event UnityAction onSecondaryActionEnd;
private InputState m_PrimaryState = InputState.None;
private InputState m_SecondaryState = InputState.None;
private bool m_PrimaryPressed = false;
private bool m_SecondaryPressed = false;
private bool m_ContinuousSuccess = true;
private bool m_Interrupted = false;
enum InputState
{
None,
Down,
Held,
Released
}
private InputState primaryState
{
get { return m_PrimaryState; }
set
{
if (m_PrimaryState != value)
{
m_PrimaryState = value;
switch (m_PrimaryState)
{
case InputState.None:
if (onPrimaryActionEnd != null)
onPrimaryActionEnd();
break;
case InputState.Down:
if (onPrimaryActionStart != null)
onPrimaryActionStart();
break;
}
}
}
}
private InputState secondaryState
{
get { return m_SecondaryState; }
set
{
if (m_SecondaryState != value)
{
m_SecondaryState = value;
switch (m_SecondaryState)
{
case InputState.None:
if (onSecondaryActionEnd != null)
onSecondaryActionEnd();
break;
case InputState.Down:
if (onSecondaryActionStart != null)
onSecondaryActionStart();
break;
}
}
}
}
#if UNITY_EDITOR
private static List<BaseWieldableToolModule> s_AvailableActions = new List<BaseWieldableToolModule>();
public void CheckAvailableActions()
{
// Check unmapped actions
for (int i = m_UnmappedModules.Count - 1; i >= 0; --i)
{
if (m_UnmappedModules[i] == null || CheckActionIsReferenced(m_UnmappedModules[i]))
m_UnmappedModules.RemoveAt(i);
}
// Skim through available actions
GetComponents(s_AvailableActions);
foreach (var available in s_AvailableActions)
{
if (!CheckActionIsReferenced(available) && !m_UnmappedModules.Contains(available))
m_UnmappedModules.Add(available);
}
}
protected override void OnValidate()
{
base.OnValidate();
// Check mapped actions are valid
for (int i = m_PrimaryModules.Count - 1; i >= 0; --i)
{
if (m_PrimaryModules[i] == null)
m_PrimaryModules.RemoveAt(i);
}
for (int i = m_SecondaryModules.Count - 1; i >= 0; --i)
{
if (m_SecondaryModules[i] == null)
m_SecondaryModules.RemoveAt(i);
}
CheckAvailableActions();
}
bool CheckActionIsReferenced(BaseWieldableToolModule action)
{
foreach (var a in m_PrimaryModules)
if (a == action)
return true;
foreach (var a in m_SecondaryModules)
if (a == action)
return true;
return false;
}
protected override void Awake()
{
if (Application.isPlaying)
base.Awake();
}
protected override void OnEnable()
{
if (Application.isPlaying)
base.OnEnable();
}
protected override void OnDisable()
{
Interrupt();
if (Application.isPlaying)
base.OnDisable();
}
void Update()
{
if (!Application.isPlaying)
OnValidate();
}
#endif
public bool CheckCanUse()
{
return !(CheckModulesBlocked(m_PrimaryModules) || CheckModulesBlocked(m_SecondaryModules));
}
void Start()
{
if (Application.isPlaying)
{
// Initialise actions
for (int i = 0; i < m_PrimaryModules.Count; ++i)
{
if (m_PrimaryModules[i] != null)
m_PrimaryModules[i].Initialise(this);
}
for (int i = 0; i < m_SecondaryModules.Count; ++i)
{
if (m_SecondaryModules[i] != null)
m_SecondaryModules[i].Initialise(this);
}
}
}
bool CheckModulesBlocked(List<BaseWieldableToolModule> modules)
{
for (int i = 0; i < m_PrimaryModules.Count; ++i)
{
if (m_PrimaryModules[i].blocking)
return true;
}
return false;
}
public void PrimaryPress()
{
// Track trigger pressed
m_PrimaryPressed = true;
// Set new state if currently valid
if (primaryState == InputState.None && secondaryState == InputState.None)
{
if (!CheckModulesBlocked(m_PrimaryModules))
primaryState = InputState.Down;
}
}
public void PrimaryRelease()
{
// Reset state if currently valid
if (primaryState == InputState.Held)
primaryState = InputState.Released;
// Track trigger released
m_PrimaryPressed = false;
// Set alternative state if waiting
if (m_SecondaryPressed && secondaryState == InputState.None)
secondaryState = InputState.Down;
}
public void SecondaryPress()
{
// Track trigger pressed
m_SecondaryPressed = true;
// Set new state if currently valid
if (primaryState == InputState.None && secondaryState == InputState.None)
{
if (!CheckModulesBlocked(m_SecondaryModules))
secondaryState = InputState.Down;
}
}
public void SecondaryRelease()
{
// Reset state if currently valid
if (secondaryState == InputState.Held)
secondaryState = InputState.Released;
// Track trigger released
m_SecondaryPressed = false;
// Set alternative state if waiting
if (m_PrimaryPressed && primaryState == InputState.None)
primaryState = InputState.Down;
}
public void Interrupt()
{
// Track triggers released
m_PrimaryPressed = false;
m_SecondaryPressed = false;
// Reset states if required
if (primaryState == InputState.Held)
{
// Interrupt modules
for (int i = 0; i < m_PrimaryModules.Count; ++i)
m_PrimaryModules[i].Interrupt();
// Reset state
primaryState = InputState.Released;
}
if (secondaryState == InputState.Held)
{
// Interrupt modules
for (int i = 0; i < m_SecondaryModules.Count; ++i)
m_SecondaryModules[i].Interrupt();
// Reset state
secondaryState = InputState.Released;
}
m_Interrupted = true;
}
void FixedUpdate()
{
if (Application.isPlaying)
{
if (primaryState != InputState.None)
primaryState = TickActions(m_PrimaryModules, primaryState, m_PrimaryPressed, secondaryState);
if (secondaryState != InputState.None)
secondaryState = TickActions(m_SecondaryModules, secondaryState, m_SecondaryPressed, primaryState);
}
}
InputState TickActions(List<BaseWieldableToolModule> actions, InputState state, bool pressed, InputState otherState)
{
bool blocked = CheckModulesBlocked(actions);
// Check if just unblocked
if (!blocked && pressed && state == InputState.None && otherState == InputState.None)
state = InputState.Down;
// Initial press
if (state == InputState.Down)
{
if (blocked)
state = InputState.None;
else
{
// Fire actions
for (int i = 0; i < actions.Count; ++i)
{
if (actions[i] != null && actions[i].enabled && actions[i].isValid && (actions[i].timing & WieldableToolActionTiming.Start) == WieldableToolActionTiming.Start)
actions[i].FireStart();
}
state = InputState.Held;
}
}
// Held
if (state == InputState.Held)
{
if (blocked)
state = InputState.Released;
else
{
// Reset success check
m_ContinuousSuccess = true;
// Reset interrupted
m_Interrupted = false;
// Tick continuous actions
for (int i = 0; i < actions.Count; ++i)
{
if (actions[i] != null && actions[i].enabled && actions[i].isValid && (actions[i].timing & WieldableToolActionTiming.Continuous) == WieldableToolActionTiming.Continuous)
m_ContinuousSuccess &= actions[i].TickContinuous();
}
// Check if interrupted during actions
if (m_Interrupted)
state = InputState.Released;
}
}
// Released
if (state == InputState.Released)
{
// Fire end actions (if can)
for (int i = 0; i < actions.Count; ++i)
{
if (actions[i] != null && actions[i].enabled && actions[i].isValid && (actions[i].timing & WieldableToolActionTiming.End) == WieldableToolActionTiming.End)
actions[i].FireEnd(m_ContinuousSuccess);
}
// Set input state
state = InputState.None;
}
return state;
}
#region ICrosshairDriver IMPLEMENTATION
private bool m_HideCrosshair = false;
public FpsCrosshair crosshair
{
get { return m_Crosshair; }
}
private float m_Accuracy = 1f;
public float accuracy
{
get { return m_Accuracy; }
private set
{
m_Accuracy = value;
if (onAccuracyChanged != null)
onAccuracyChanged(m_Accuracy);
}
}
public event UnityAction<FpsCrosshair> onCrosshairChanged;
public event UnityAction<float> onAccuracyChanged;
public void HideCrosshair()
{
if (!m_HideCrosshair)
{
bool triggerEvent = (onCrosshairChanged != null && crosshair == FpsCrosshair.None);
m_HideCrosshair = true;
if (triggerEvent)
onCrosshairChanged(FpsCrosshair.None);
}
}
public void ShowCrosshair()
{
if (m_HideCrosshair)
{
// Reset
m_HideCrosshair = false;
// Fire event
if (onCrosshairChanged != null && crosshair != FpsCrosshair.None)
onCrosshairChanged(crosshair);
}
}
#endregion
#region INeoSerializableComponent IMPLEMENTATION
private static readonly NeoSerializationKey k_PrimaryStateKey = new NeoSerializationKey("pState");
private static readonly NeoSerializationKey k_SecondaryStateKey = new NeoSerializationKey("sState");
private static readonly NeoSerializationKey k_SuccessKey = new NeoSerializationKey("success");
public override void WriteProperties(INeoSerializer writer, NeoSerializedGameObject nsgo, SaveMode saveMode)
{
base.WriteProperties(writer, nsgo, saveMode);
writer.WriteValue(k_PrimaryStateKey, (int)m_PrimaryState);
writer.WriteValue(k_SecondaryStateKey, (int)m_SecondaryState);
writer.WriteValue(k_SuccessKey, m_ContinuousSuccess);
}
public override void ReadProperties(INeoDeserializer reader, NeoSerializedGameObject nsgo)
{
base.ReadProperties(reader, nsgo);
int intValue;
reader.TryReadValue(k_SuccessKey, out intValue, (int)m_PrimaryState);
m_PrimaryState = (InputState)intValue;
reader.TryReadValue(k_SecondaryStateKey, out intValue, (int)m_SecondaryState);
m_SecondaryState = (InputState)intValue;
reader.TryReadValue(k_SuccessKey, out m_ContinuousSuccess, m_ContinuousSuccess);
}
#endregion
}
}