// MIT License - Copyright (c) 2025 wallstop
// Full license text: https://github.com/wallstop/unity-helpers/blob/main/LICENSE
namespace WallstopStudios.UnityHelpers.Tags
{
using UnityEngine;
///
/// Base class for authoring custom effect behaviours that respond to the lifecycle of an active .
///
///
///
/// Each behaviour asset is cloned per , which means derived classes can safely store mutable state
/// between calls to , ,
/// , and .
///
///
/// Attach behaviour assets to to augment the data-driven attribute pipeline with bespoke
/// gameplay logic, visual or audio feedback, or integration hooks into other game systems.
///
///
/// All callbacks are synchronously invoked by on the main thread, ensuring safe interaction with Unity APIs.
///
///
///
///
/// using UnityEngine;
/// using WallstopStudios.UnityHelpers.Tags;
///
/// [CreateAssetMenu(menuName = "Game/Effects/Burning Behaviour")]
/// public sealed class BurningBehavior : EffectBehavior
/// {
/// [SerializeField]
/// private GameObject flamePrefab;
///
/// private GameObject spawnedInstance;
///
/// public override void OnApply(EffectBehaviorContext context)
/// {
/// if (flamePrefab == null)
/// {
/// return;
/// }
///
/// Transform parent = context.Target.transform;
/// spawnedInstance = Object.Instantiate(flamePrefab, parent.position, parent.rotation, parent);
/// }
///
/// public override void OnPeriodicTick(EffectBehaviorContext context, PeriodicEffectTickContext tickContext)
/// {
/// // Cancel the effect early once the periodic bundle has executed three times.
/// if (tickContext.executedTicks >= 3)
/// {
/// context.handler.RemoveEffect(context.handle);
/// }
/// }
///
/// public override void OnRemove(EffectBehaviorContext context)
/// {
/// if (spawnedInstance != null)
/// {
/// Object.Destroy(spawnedInstance);
/// spawnedInstance = null;
/// }
/// }
/// }
///
/// // Assign the behaviour asset to an AttributeEffect so it is cloned per application.
/// AttributeEffect burnEffect = ScriptableObject.CreateInstance<AttributeEffect>();
/// burnEffect.behaviors.Add(burningBehaviorAsset);
///
///
public abstract class EffectBehavior : ScriptableObject
{
///
/// Invoked once when the effect handle becomes active.
///
/// Runtime context for the effect instance.
public virtual void OnApply(EffectBehaviorContext context) { }
///
/// Invoked every frame while the effect remains active.
///
/// Runtime context for the effect instance.
public virtual void OnTick(EffectBehaviorContext context) { }
///
/// Invoked after a periodic tick has been processed for the owning effect.
///
/// Runtime context for the effect instance.
/// Information about the specific periodic tick.
public virtual void OnPeriodicTick(
EffectBehaviorContext context,
PeriodicEffectTickContext tickContext
) { }
///
/// Invoked when the effect handle is removed or expires.
///
/// Runtime context for the effect instance.
public virtual void OnRemove(EffectBehaviorContext context) { }
}
///
/// Immutable runtime data passed to behaviour callbacks.
///
public readonly struct EffectBehaviorContext
{
///
/// Gets the effect asset backing the handle.
///
public AttributeEffect Effect => handle.effect;
///
/// Gets the GameObject targeted by the effect handler.
///
public GameObject Target => handler.gameObject;
///
/// Gets the deltaTime used for the current invocation. For ,
/// this value is zero.
///
public readonly float deltaTime;
///
/// Gets the handler managing the effect.
///
public readonly EffectHandler handler;
///
/// Gets the handle associated with this behaviour invocation.
///
public readonly EffectHandle handle;
public EffectBehaviorContext(EffectHandler handler, EffectHandle handle, float deltaTime)
{
this.handler = handler;
this.handle = handle;
this.deltaTime = deltaTime;
}
}
///
/// Details about a specific periodic tick that just executed.
///
public readonly struct PeriodicEffectTickContext
{
///
/// Gets the periodic definition that produced this tick.
///
public readonly PeriodicEffectDefinition definition;
///
/// Gets the number of ticks executed so far, including this one.
///
public readonly int executedTicks;
///
/// Gets the timestamp when the tick occurred.
///
public readonly float currentTime;
public PeriodicEffectTickContext(
PeriodicEffectDefinition definition,
int executedTicks,
float currentTime
)
{
this.definition = definition;
this.executedTicks = executedTicks;
this.currentTime = currentTime;
}
}
}