// Copyright (c) 2015 - 2023 Doozy Entertainment. All Rights Reserved. // This code can only be used under the standard Unity Asset Store End User License Agreement // A Copy of the EULA APPENDIX 1 is available at http://unity3d.com/company/legal/as_terms using System; using System.Collections; using Doozy.Runtime.Common; using Doozy.Runtime.Mody; using UnityEngine; using UnityEngine.Events; namespace Doozy.Runtime.Signals { public abstract partial class SignalProvider : MonoBehaviour, ISignalProvider { public ProviderAttributes attributes { get; } public SignalStream stream { get; private set; } public bool isConnected { get; private set; } #region Provider State /// Provider current state (disabled, idle, running or cooldown) [SerializeField] private ProviderState ProviderCurrentState; /// Provider current state (disabled, idle, running or cooldown) public ProviderState currentState { get => ProviderCurrentState; private set { ProviderCurrentState = value; onStateChanged?.Invoke(value); } } public UnityAction onStateChanged { get; set; } /// Provider is ready and can be triggered public bool isIdle => currentState == ProviderState.Idle; /// Provider has started and is running public bool isRunning => currentState == ProviderState.IsRunning; /// Provider is in the 'InCooldown' state and cannot be triggered again during this time public bool inCooldown => currentState == ProviderState.InCooldown; /// Provider has started running and is either in 'IsRunning' or 'InCooldown' state public bool isActive => isRunning || inCooldown; #endregion #region Signal Cooldown /// Cooldown time after a signal was sent. During this time, no Signal will be sent [SerializeField] private float SignalCooldown; /// Cooldown time after a signal was sent. During this time, no Signal will be sent public float cooldown { get => SignalCooldown > 0 ? SignalCooldown : 0; set => SignalCooldown = value > 0 ? value : 0; } #endregion #region Signal Timescale Independent /// /// Determine if the Signal's timers will be affected by the application's timescale /// Timescale is the scale at which time passes /// Timescale.Independent - (Realtime) Not affected by the application's timescale value /// Timescale.Dependent - (Application Time) Affected by the application's timescale value /// [SerializeField] private Timescale SignalTimescale; /// /// Determine if the Signal's timers will be affected by the application's timescale /// Timescale is the scale at which time passes /// TRUE - Timescale.Independent - (Realtime) Not affected by the application's timescale value /// FALSE - Timescale.Dependent - (Application Time) Affected by the application's timescale value /// public bool isTimescaleIndependent { get => SignalTimescale == Timescale.Independent; internal set => SignalTimescale = value ? Timescale.Independent : Timescale.Dependent; } #endregion #region Coroutines private Coroutine m_CooldownCoroutine; #endregion protected SignalProvider(ProviderType providerType, string providerCategory, string providerName, Type typeOfProvider) { attributes = new ProviderAttributes(providerType, providerCategory, providerName, typeOfProvider); stream = null; ProviderCurrentState = ProviderState.Disabled; SignalCooldown = 0; SignalTimescale = Timescale.Independent; } public void OpenStream() { if (isConnected) return; stream = SignalsService.GetStream().SetSignalProvider(this); SignalsService.AddProvider(this); isConnected = true; currentState = ProviderState.Idle; } public void CloseStream() { if (!isConnected) return; SignalsService.CloseStream(stream); stream = null; isConnected = false; currentState = ProviderState.Disabled; } protected virtual void Awake() { stream = null; isConnected = false; OpenStream(); } protected virtual void OnEnable() { currentState = isConnected ? ProviderState.Idle : ProviderState.Disabled; } protected virtual void OnDisable() { StopCooldown(); currentState = ProviderState.Disabled; } protected virtual void OnDestroy() => CloseStream(); /// Send a Signal on this provider's stream public bool SendSignal() => SendSignal(string.Empty); /// Send a Signal on this provider's stream /// Text message used to pass info about this Signal public bool SendSignal(string message) { bool result; switch (currentState) { case ProviderState.Disabled: case ProviderState.InCooldown: return false; case ProviderState.Idle: case ProviderState.IsRunning: currentState = ProviderState.IsRunning; result = isConnected && stream.SendSignal(this, message); break; default: throw new ArgumentOutOfRangeException(); } StartCooldown(); return result; } /// Send a MetaSignal with a T signal value on this provider's stream /// Signal value /// Signal value type public bool SendSignal(T signalValue) => SendSignal(signalValue, string.Empty); /// Send a MetaSignal with a T signal value on this provider's stream /// Signal value /// Text message used to pass info about this Signal /// Signal value type public bool SendSignal(T signalValue, string message) { bool result; switch (currentState) { case ProviderState.Disabled: case ProviderState.InCooldown: return false; case ProviderState.Idle: case ProviderState.IsRunning: currentState = ProviderState.IsRunning; result = isConnected && stream.SendSignal(signalValue, this, message); break; default: throw new ArgumentOutOfRangeException(); } StartCooldown(); return result; } /// /// Start the Provider's cooldown timer, that makes it unable to start running again until the timer finishes. /// If the Provider is in the 'Disabled' state, this method will NOT do anything. /// If the Provider is in the 'InCooldown' state, this method will restart the cooldown timer. /// public void StartCooldown() { if (currentState == ProviderState.Disabled) { return; } if (cooldown == 0) { currentState = ProviderState.Idle; return; } if (currentState == ProviderState.InCooldown) { StopCooldown(); } m_CooldownCoroutine = StartCoroutine(ExecuteCooldown()); } /// /// Executes the cooldown cycle as follows: /// >> (time interval) Cooldown /// >> (method) StopCooldown /// protected IEnumerator ExecuteCooldown() { currentState = ProviderState.InCooldown; if (isTimescaleIndependent) { yield return new WaitForSecondsRealtime(cooldown); } else { yield return new WaitForSeconds(cooldown); } StopCooldown(); } /// /// Stop the Provider's cooldown timer and set it ready to start running again. /// public void StopCooldown() { if (m_CooldownCoroutine != null) { StopCoroutine(m_CooldownCoroutine); m_CooldownCoroutine = null; } currentState = isActiveAndEnabled && isConnected ? ProviderState.Idle : ProviderState.Disabled; } } }