Action Modules & Groups

Action Modules and Groups

ItemActions were built with modularity and flexibility in mind. Everything related to if, when and how an item is used can be defined as ActionModules.

The ItemActionModule class inherits the BoundStateObject class. The BoundStateObject class allows modules to be affected at runtime by both states and bindings.

States are used to override values with another constant value.
Bindings are used to override values with variable values.
The Ultimate Inventory System uses the Bindings to bind values on the module to Attribute values on the Inventory Item? This allows for great flexibility such as keeping the damage or ammo consistent between the Inventory Item and the Character Item

ActionModuleGroups are generic objects that only accepts a certain type of ActionModule. The common ActionModuleGroups for all UsableItemActions are the Trigger and Usable groups.

ActionModules usually work by inheriting interfaces such as IModuleCanStartUseItem or IModuleItemUseComplete
For UsableActions you may find the full list of used interfaces in the IUsableActionModuleInterfaces script.
All ActionModuleGroups inside a CharacterItemAction will be stored in a list. Allowing us to access all ActionModules of a CharacterItemAction easily and call functions on subsets. To call a function on all objects with a certain interface on a CharacterItemAction use the following function:
InvokeOnModulesWithType<IMyModuleInterface>(module => module.MyInterfacefunction());

There are overloads to pass in arguments without creating any allocations:

InvokeOnModulesWithType<IMyModuleInterface>((module, i1) => module.MyInterfacefunction(i1));
In addition ActionModules come with some functions that do not require interfaces:
/// <summary>
/// Updates the registered events when the item is equipped and the module is enabled.
/// </summary>
/// <param name="register">register or unregister?</param>
protected virtual void UpdateRegisteredEventsInternal(bool register) { }
/// <summary>
/// The item was picked up.
/// </summary>
public virtual void Pickup() { }

/// <summary>
/// The item will be equipped.
/// </summary>
public virtual void WillEquip() { }
/// <summary>
/// The item was equipped.
/// </summary>
public virtual void Equip(){ }

/// <summary>
/// The item will start unequipping.
/// </summary>
public virtual void StartUnequip() { }

/// <summary>
/// The Item was unequipped.
/// </summary>
public virtual void Unequip() { }

/// <summary>
/// The item was removed from the character.
/// </summary>
public virtual void RemoveItem() { }

/// <summary>
/// Clean up the module when it is destroyed.
/// </summary>
public virtual void OnDestroy() { }

The ‘UpdateRegisteredEventsInternal’ function is particularly important as it allows the ItemAction to listen to certain events only while the Item is equipped and the module is enabled. It is a good place to listen to AnimationEventTrigger events or ability events.

Custom Item Action Modules

When making a custom ItemActionModule the first thing to do is identify in what group you would like your custom action to be part of.

If you would like to use your action inside multiple groups of different types you may create a simple class and then make a custom ItemActionModule for each required type and have them call functions on that class. Have a look at the ImpactActions and ItemEffects for a good example.
For this example we’ll make a custom UsableActionModule that logs a message on Use. The relevant interface to inherit is IModuleUseItem found in the list of interfaces within the IUsableActionModuleInterfaces script
/// <summary>
/// A simple module which has a boolean to enable can start.
/// </summary>
[Serializable]
public class DebugUse : UsableActionModule, IModuleUseItem
{
    [Tooltip("The message to print on Use")]
    [SerializeField] private string m_Message = "Hello :)";

    /// <summary>
    /// Use the item.
    /// </summary>
    public void UseItem()
    {
        Debug.Log(m_Message);
    }
}