// 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.Generic; using System.Linq; using Doozy.Runtime.Common; using Doozy.Runtime.Common.Attributes; using UnityEngine; namespace Doozy.Runtime.Mody { /// /// Base class for modules in the Mody System. /// Any module that interacts with the system needs to derive from this. /// It can perform various tasks and uses Actions to trigger them. /// [Serializable] public abstract class ModyModule : MonoBehaviour, IHaveActions { #region Module Name /// /// Name of the Module /// [SerializeField] private string ModuleName; /// /// Name of the Module /// public string moduleName { get => ModuleName; internal set => ModuleName = value; } #endregion #region Module Actions /// /// Collection of Actions available for this Module /// private HashSet m_ModuleActions; /// /// Collection of Actions available for this Module /// public HashSet actions { get => m_ModuleActions ?? (m_ModuleActions = new HashSet()); protected internal set => m_ModuleActions = value; } /// /// Names of all the Actions available for this Module /// public IEnumerable actionNames => actions.Select(a => a.actionName); #endregion #region Module States /// /// Module current state (disabled, idle or active) /// [SerializeField] private ModuleState ModuleCurrentState = ModuleState.Disabled; /// /// Module current state (disabled, idle or active) /// public ModuleState state { get => ModuleCurrentState; internal set => ModuleCurrentState = value; } #endregion /// Initialized flag public bool initialized { get; protected set; } protected ModyModule(string moduleName) { ModuleName = moduleName; // ReSharper disable once VirtualMemberCallInConstructor SetupActions(); } /// Initialize the Module public virtual void Initialize() { if (initialized) return; SetupActions(); initialized = true; } /// Update the current state of the Module public void UpdateState() { state = ModuleState.Disabled; if (actions.Any(action => action.isActive)) { state = ModuleState.Active; return; } if (actions.Any(action => action.isIdle)) { state = ModuleState.Idle; } } /// /// Activate all the Actions, this Module has, by calling OnActivate for each Action. /// This method is automatically called by the Module in its OnEnable method. /// public void ActivateActions() { foreach (ModyAction action in actions) action.OnActivate(); } /// /// Deactivate all the Actions, this Module has, by calling OnDeactivate for each Action. /// This method is automatically called by the Module in its OnDisable method. /// public void DeactivateActions() { foreach (ModyAction action in actions) action.OnDeactivate(); } /// /// Start running the Action with the given action name. /// /// Name of the Action /// Ignore cooldown if the Action is in the 'InCooldown' state /// Execute method even if the Action is not enabled public void StartAction(string actionName, bool ignoreCooldown, bool forced = false) { foreach (ModyAction action in actions.Where(action => action.actionName.Equals(actionName))) action.StartRunning(null, ignoreCooldown, forced); } /// /// Stop running the Action with the given action name. /// The Action needs to be in the 'IsRunning' state for it to stop. /// If the Action is in the 'InCooldown' state, this method does NOT reset or stop its cooldown timer. /// This method does NOT execute the Finisher. /// /// Name of the Action public void StopAction(string actionName) { foreach (ModyAction action in actions.Where(action => action.actionName.Equals(actionName))) action.StopRunning(); } /// /// Stop running all the Actions this Module has. /// The Action needs to be in the 'IsRunning' state for it to stop. /// If the Action is in the 'InCooldown' state, this method does NOT reset or stop its cooldown timer. /// This method does NOT execute the Finisher. /// public void StopAllActions() { foreach (ModyAction action in actions) action.StopRunning(); } /// /// Finish running the Action with the given name, by doing the following: /// >> (method) StartCooldown /// >> (method) Execute Finisher (if enabled) /// The Action needs to have Enabled set to TRUE for this method to work. /// /// Name of the Action public void FinishAction(string actionName) { foreach (ModyAction action in actions.Where(action => action.actionName.Equals(actionName))) action.FinishRunning(); } /// /// Finish running all the Actions this Module has, by doing the following for each Action: /// >> (method) StartCooldown /// >> (method) Execute Finisher (if enabled) /// The Action needs to have Enabled set to TRUE for this method to work. /// public void FinishAllActions() { foreach (ModyAction action in actions) action.FinishRunning(); } /// /// Execute the given method on the Action with the given name. The available options are as follows: /// 1. StartRunning /// 2. StopRunning /// 3. FinishRunning /// /// Name of the Action /// Method to call /// Ignore cooldown if the Action is in the 'InCooldown' state (only for the StartRunning option) /// Execute method even if the Action is not enabled public void Execute(string actionName, RunAction method, bool ignoreCooldown = false, bool forced = false) { ModyAction action = GetAction(actionName); action?.ExecuteMethod(method, ignoreCooldown, forced); } /// /// Get the Action with the given name from the Module (if it exists) /// /// Name of the Action public ModyAction GetAction(string actionName) => actions.FirstOrDefault(action => action.actionName.Equals(actionName)); /// /// Returns TRUE if this Module has an Action with the given action name. /// /// Name of the Action public bool ContainsAction(string actionName) => actions.Any(action => action.actionName.Equals(actionName)); /// /// Setup Actions for this Module. /// This method is called in the constructor, in Initialize and in OnEnable. /// This is where the code that 'initializes' and adds the Actions for this Module is added. /// protected abstract void SetupActions(); /// /// Validate the Module's settings /// public virtual void Validate() { foreach (ModyAction action in actions) { action.SetBehaviour(this); action.Validate(); } } #region Unity Methods protected virtual void Awake() { RegisterToDatabase(); } protected virtual void Start() { Initialize(); } protected virtual void OnEnable() { Validate(); SetupActions(); actions.Remove(null); ActivateActions(); } protected virtual void OnDisable() { actions.Remove(null); DeactivateActions(); } protected virtual void OnDestroy() { UnregisterFromDatabase(); } #endregion #region Database /// Database that contains all the Modules that are available at runtime at any given point in time. [ClearOnReload(newInstance: true)] private static readonly ListDatabase Database = new ListDatabase(); /// Returns a list of all the Modules available on a target GameObject /// Target GameObject public static List GetModules(GameObject target) => Database.GetValues(target); /// /// Register this Module to the Database. /// This method is automatically called in Awake. /// private void RegisterToDatabase() => Database.Add(gameObject, this); /// /// Unregister this Module from the Database. /// This method is automatically called OnDestroy. /// private void UnregisterFromDatabase() => Database.Remove(gameObject, this); #endregion } /// Extension methods for public static class ModyModuleExtensions { /// Set Module Name /// Target Module /// Name of the Module public static T SetName(this T target, string value) where T : ModyModule { target.moduleName = value; return target; } /// Add the given ModyAction to this Module /// Target Module /// Action public static T AddAction(this T target, ModyAction action) where T : ModyModule { target.actions.Add(action); return target; } /// Remove the given ModyAction from this Module /// Target Module /// Action public static T RemoveAction(this T target, ModyAction action) where T : ModyModule { target.actions.Remove(action); return target; } /// Remove all the ModyAction, with the given action name, from the Module /// Target Module /// Name of the Action public static T RemoveAction(this T target, string actionName) where T : ModyModule { foreach (ModyAction action in target.actions.ToList()) { if (!actionName.Equals(action.actionName)) continue; target.actions.Remove(action); } return target; } } }