Executable Item

v1.0

Overview

ExecutableItem is the base class for all items that can be executed by an ActionExecutor. There are two concrete implementations:

  • ActionItem - Wraps a single Action asset and executes it

  • GroupItem - Contains multiple ExecutableItems that execute in parallel with configurable exit conditions

Both types support conditionals, chance-based execution, looping, and runtime lookup via unique identifiers (UIDs).


ActionItem vs GroupItem

An ActionItem represents a single action from your Action assets.

A GroupItem represents a collection of items. Groups can contain both ActionItems and nested GroupItems. Group Items have exit conditions, which determine when the Executor will start the next ExecutableItem.

Condition
Description
Use Case

Barrier

Wait for all items to complete

Synchronized animations, coordinated effects

Race

Complete when first item finishes

Competitive animations, interrupt-driven sequences

Timed

Complete after a fixed duration

Time-limited effects, fire-and-forget groups


Looking Up ExecutableItems by Unique ID (UID)

Every ExecutableItem has a unique identifier that's automatically generated and persisted. This can be modified to a custom string in the Inspector, when viewing any Action Executor.

You can look up items in any ActionExecutor programmatically using their UID.

using MagicPigGames.JuicyActions;

public class AbilityController : MonoBehaviour
{
    [SerializeField] private ActionExecutor executor;
    [SerializeField] private string criticalHitActionUID;
    
    public void TriggerCriticalHit()
    {
        // Look up the specific action by its UID
        var criticalHitItem = executor.GetExecutableItem(criticalHitActionUID);
        
        if (criticalHitItem != null && criticalHitItem is ActionItem actionItem)
        {
            // Modify the action before it executes
            actionItem.chances = 1f; // Guarantee execution
            actionItem.enabled = true;
        }
        
        executor.Execute(this);
    }
}

Working with ExecutableItems

Accessing Items from ActionExecutor

using MagicPigGames.JuicyActions;

public class DynamicActionController : MonoBehaviour
{
    [SerializeField] private ActionExecutor executor;
    
    void Start()
    {
        // Get all items
        var allItems = executor.Items;
        
        // Iterate through items
        foreach (var item in allItems)
        {
            Debug.Log($"Item UID: {item.UID}");
            Debug.Log($"Item Type: {(item.IsAction ? "Action" : "Group")}");
            Debug.Log($"Enabled: {item.enabled}");
            Debug.Log($"Chances: {item.chances}");
        }
    }
}

Modifying Items at Runtime

using MagicPigGames.JuicyActions;

public class DifficultyManager : MonoBehaviour
{
    [SerializeField] private ActionExecutor bossAbilities;
    
    public void SetDifficulty(float difficultyMultiplier)
    {
        foreach (var item in bossAbilities.Items)
        {
            if (item is ActionItem actionItem)
            {
                // Increase action frequency based on difficulty
                actionItem.chances = Mathf.Clamp01(0.5f * difficultyMultiplier);
                
                // Access the action's duration through overrides
                if (actionItem.overrides != null)
                {
                    // Make actions faster on higher difficulties
                    actionItem.overrides.timeBeforeNextAction /= difficultyMultiplier;
                }
            }
            else if (item is GroupItem groupItem)
            {
                // Make group-based attacks more frequent
                groupItem.chances = Mathf.Clamp01(0.3f * difficultyMultiplier);
            }
        }
    }
}

Filtering Items

using System.Linq;
using MagicPigGames.JuicyActions;

public class ActionFilter : MonoBehaviour
{
    [SerializeField] private ActionExecutor executor;
    
    // Get all ActionItems
    public List<ActionItem> GetAllActions()
    {
        return executor.Items
            .Where(item => item.IsAction)
            .Cast<ActionItem>()
            .ToList();
    }
    
    // Get all GroupItems
    public List<GroupItem> GetAllGroups()
    {
        return executor.Items
            .Where(item => item.IsGroup)
            .Cast<GroupItem>()
            .ToList();
    }
    
    // Get all enabled items
    public List<ExecutableItem> GetEnabledItems()
    {
        return executor.Items
            .Where(item => item.enabled)
            .ToList();
    }
    
    // Get items with chance > 0.5
    public List<ExecutableItem> GetHighProbabilityItems()
    {
        return executor.Items
            .Where(item => item.chances > 0.5f)
            .ToList();
    }
}

Common Properties

All ExecutableItems share these properties:

Basic Settings

// Enable/disable the item
item.enabled = true;

// Control execution probability (0.0 = never, 1.0 = always)
item.chances = 0.75f;

Conditional Execution

// Enable conditional checks
item.useConditional = true;

// Set how multiple conditions are combined
item.conditionalJoin = ConditionalJoin.And; // All must be true
// OR
item.conditionalJoin = ConditionalJoin.Or;  // At least one must be true

// Add conditions (see Conditional documentation)
item.conditionals.Add(new Conditional());

Looping

// Enable looping
item.enableLoop = true;

// Set to infinite loop
item.infiniteLoop = true;

// OR set a specific loop count
item.infiniteLoop = false;
item.LoopCount = 3; // Execute 3 times

Runtime Loop State

// Check current loop iteration
int currentIteration = item.CurrentLoopIteration;

// Check if should continue looping
bool shouldLoop = item.ShouldContinueLooping();

// Reset loop state (called automatically by executor)
item.ResetLoopState();

// Increment loop counter (called automatically by executor)
item.IncrementLoopIteration();

Working with ActionItem Overrides

ActionItems support per-instance overrides without modifying the original Action asset.

Duration Overrides

var actionItem = executor.Items[0] as ActionItem;

if (actionItem != null && actionItem.overrides != null)
{
    // Override the time before next action
    actionItem.overrides.timeBeforeNextAction = 2.5f;
}

Field Overrides

For fields marked with [CanOverride] or [MustOverride] in your Action:

// Using ActionExecutor helper methods
executor.SetFieldOverride<MyCustomAction>("damageAmount", 50, turnOverrideOn: true);

// Check if override is enabled
bool isEnabled = executor.IsFieldOverrideEnabled<MyCustomAction>("damageAmount");

// Get override value
if (executor.TryGetFieldOverride<MyCustomAction, int>("damageAmount", out int damage))
{
    Debug.Log($"Damage override: {damage}");
}

// Clear all overrides for an action type
executor.ClearFieldOverrides<MyCustomAction>();

Working with GroupItems

Accessing Group Contents

var groupItem = executor.Items[0] as GroupItem;

if (groupItem != null)
{
    Debug.Log($"Group: {groupItem.name}");
    Debug.Log($"Exit Condition: {groupItem.exitCondition}");
    Debug.Log($"Item Count: {groupItem.items.Count}");
    
    // Iterate through group items
    foreach (var item in groupItem.items)
    {
        if (item.IsAction)
        {
            var action = item as ActionItem;
            Debug.Log($"  - Action: {action.actionAsset.name}");
        }
        else if (item.IsGroup)
        {
            var nestedGroup = item as GroupItem;
            Debug.Log($"  - Group: {nestedGroup.name}");
        }
    }
}

Modifying Group Behavior

var groupItem = executor.Items[0] as GroupItem;

if (groupItem != null)
{
    // Change exit condition
    groupItem.exitCondition = GroupExitCondition.Race;
    
    // Set time limit for timed groups
    groupItem.timeLimit = 5f;
    
    // End actions when group exits
    groupItem.endActionsOnExit = true;
    
    // Add new items to the group at runtime
    var newAction = new ActionItem
    {
        enabled = true,
        actionAsset = myActionAsset,
        overrides = new ActionOverrides()
    };
    groupItem.items.Add(newAction);
}

Advanced: Dynamic Item Management

Adding Items at Runtime

using MagicPigGames.JuicyActions;

public class DynamicActionBuilder : MonoBehaviour
{
    [SerializeField] private ActionExecutor executor;
    [SerializeField] private Action explosionAction;
    [SerializeField] private Action soundAction;
    
    public void AddExplosionCombo()
    {
        // Create a new group
        var comboGroup = new GroupItem
        {
            enabled = true,
            name = "Explosion Combo",
            exitCondition = GroupExitCondition.Barrier,
            items = new List<ExecutableItem>()
        };
        
        // Add explosion action
        comboGroup.items.Add(new ActionItem
        {
            enabled = true,
            actionAsset = explosionAction,
            overrides = new ActionOverrides()
        });
        
        // Add sound action
        comboGroup.items.Add(new ActionItem
        {
            enabled = true,
            actionAsset = soundAction,
            overrides = new ActionOverrides()
        });
        
        // Add the group to executor
        executor.Items.Add(comboGroup);
    }
}

Removing Items

// Remove by reference
executor.RemoveItem(item);

// Remove by UID
var itemToRemove = executor.GetExecutableItem(uid);
if (itemToRemove != null)
{
    executor.RemoveItem(itemToRemove);
}

// Remove all items
executor.ClearActions();

Validation

ExecutableItems provide validation to help catch configuration errors:

var item = executor.Items[0];

// Validate the item
var issues = item.Validate();

if (issues.Count > 0)
{
    Debug.LogWarning("Item validation issues:");
    foreach (var issue in issues)
    {
        Debug.LogWarning($"  - {issue}");
    }
}

Common Validation Issues:

  • Missing action asset reference

  • Chances outside valid range (0-1)

  • Invalid loop count

  • Empty groups

  • Invalid conditional configuration


Best Practices

1

Use UIDs for Persistent References

// Store UIDs in serialized fields for persistent references
[SerializeField] private string ultimateAbilityUID;

void TriggerUltimate()
{
    var item = executor.GetExecutableItem(ultimateAbilityUID);
    if (item != null)
    {
        item.enabled = true;
        executor.Execute(this);
    }
}
2

Cache Item References

// Cache frequently accessed items
private ActionItem cachedPowerup;

void Start()
{
    var item = executor.GetExecutableItem(powerupUID);
    cachedPowerup = item as ActionItem;
}

void ActivatePowerup()
{
    if (cachedPowerup != null)
    {
        cachedPowerup.chances = 1f;
    }
}
3

Validate Before Execution

void ExecuteWithValidation()
{
    foreach (var item in executor.Items)
    {
        var issues = item.Validate();
        if (issues.Count > 0)
        {
            Debug.LogError($"Item {item.UID} has validation errors!");
            return;
        }
    }
    
    executor.Execute(this);
}
4

Use Groups for Coordinated Effects

// Instead of separate sequential actions
// Use a Barrier group to synchronize multiple effects
var effectGroup = new GroupItem
{
    name = "Impact Effects",
    exitCondition = GroupExitCondition.Barrier,
    items = new List<ExecutableItem>
    {
        new ActionItem { actionAsset = particleAction },
        new ActionItem { actionAsset = soundAction },
        new ActionItem { actionAsset = cameraShakeAction }
    }
};
5

Use Conditionals for State-Based Execution

// Configure items with conditionals rather than runtime enable/disable
var item = new ActionItem
{
    enabled = true,
    useConditional = true,
    conditionalJoin = ConditionalJoin.And,
    conditionals = new List<Conditional>
    {
        // Only execute when player health is low
        new Conditional { /* configure health check */ }
    }
};

See Also

Last updated

Was this helpful?