// MIT License - Copyright (c) 2025 wallstop
// Full license text: https://github.com/wallstop/unity-helpers/blob/main/LICENSE
namespace WallstopStudios.UnityHelpers.Tags
{
using System.Collections.Generic;
using Core.Extension;
using UnityEngine;
///
/// Abstract base class for cosmetic effect behaviors that provide visual or audio feedback for effects.
/// CosmeticEffectComponents are attached to CosmeticEffectData and are invoked when effects are applied or removed.
///
///
///
/// Cosmetic effects can be:
/// - Shared across all effect applications (RequiresInstance = false)
/// - Instanced per application (RequiresInstance = true) for independent control
///
///
/// Example implementations:
/// - Particle effects that play when an effect is active
/// - Audio clips that trigger on effect application
/// - Visual indicators like status icons or color tints
/// - Animation triggers
///
///
/// Example usage:
///
/// public class PoisonVisuals : CosmeticEffectComponent
/// {
/// public override bool RequiresInstance => true;
/// public ParticleSystem poisonParticles;
///
/// public override void OnApplyEffect(GameObject target)
/// {
/// base.OnApplyEffect(target);
/// poisonParticles.Play();
/// }
///
/// public override void OnRemoveEffect(GameObject target)
/// {
/// base.OnRemoveEffect(target);
/// poisonParticles.Stop();
/// }
/// }
///
///
///
[RequireComponent(typeof(CosmeticEffectData))]
public abstract class CosmeticEffectComponent : MonoBehaviour
{
///
/// If true, this cosmetic effect requires a new instance to be created for each effect application.
/// If false, the same instance is shared across all applications.
///
public virtual bool RequiresInstance => false;
///
/// If true, this component handles its own cleanup (e.g., via delayed destruction or animations).
/// If false, the GameObject will be destroyed immediately when the effect is removed.
///
public virtual bool CleansUpSelf => false;
///
/// Tracks all GameObjects this cosmetic effect has been applied to.
///
protected readonly List _appliedTargets = new();
///
/// Cleanup method that removes the effect from all targets when this component is destroyed.
///
protected virtual void OnDestroy()
{
if (_appliedTargets.Count <= 0)
{
return;
}
foreach (GameObject appliedTarget in _appliedTargets.ToArray())
{
if (appliedTarget == null)
{
continue;
}
OnRemoveEffect(appliedTarget);
}
}
///
/// Called when the associated effect is applied to a target GameObject.
/// Override this to implement custom behavior (e.g., play particles, show UI).
///
/// The GameObject the effect was applied to.
public virtual void OnApplyEffect(GameObject target)
{
_appliedTargets.Add(target);
}
///
/// Called when the associated effect is removed from a target GameObject.
/// Only invoked for non-instant effects. Override this to implement cleanup behavior.
///
/// The GameObject the effect was removed from.
public virtual void OnRemoveEffect(GameObject target)
{
int appliedIndex = _appliedTargets.IndexOf(target);
if (0 <= appliedIndex)
{
_appliedTargets.RemoveAtSwapBack(appliedIndex);
}
}
}
}