// 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;
}
}
}