Weapons/PeachLauncher/SplatEffect.cs
/// <summary>
/// SplatEffect — a lightweight action-graph-first component for defining
/// custom visual/audio/gameplay reactions to being splatted.
///
/// USAGE
/// ──────
/// Drop on any GO alongside SplatReceiver. Wire the action graph slots
/// in the inspector — no code needed for common reactions.
///
/// For more complex reactions (material swap, animation trigger, component
/// toggle) use the OnSplatted / OnSplatCleared action graph slots directly.
///
/// This component also implements ISplatReactor so it receives events from
/// SplatReceiver automatically without any manual wiring.
///
/// EXAMPLES (all done in inspector via action graph, zero code):
///   Peach tree: spawn a "leaves falling" particle on splat, reset on clear
///   Crate:      tint the ModelRenderer orange on splat, revert on clear  
///   Turret:     disable the TurretComponent for SplatDuration, re-enable on clear
///   Wall tile:  play a wet slap sound + spawn a drip particle
/// </summary>
[Title( "Splat Effect" )]
[Category( "Game / Peach" )]
public sealed class SplatEffect : Component, ISplatReactor
{
	// ── Action graph slots ────────────────────────────────────────────────────

	/// <summary>Fired when this object receives a splat. SplatInfo available as "Splat".</summary>
	[Property] public Action<SplatInfo> OnSplatted  { get; set; }

	/// <summary>Fired when the splat state clears (duration expired or ClearSplat() called).</summary>
	[Property] public Action             OnCleared   { get; set; }

	// ── Optional built-in effects (no action graph needed for these) ──────────

	/// <summary>Particle prefab to clone at the splat position on impact.</summary>
	[Property] public GameObject ImpactParticlePrefab { get; set; }

	/// <summary>Sound to play on splat.</summary>
	[Property] public SoundEvent ImpactSound { get; set; }

	/// <summary>
	/// If set, tints this ModelRenderer to the splat colour on impact and
	/// reverts it when the splat clears.
	/// </summary>
	[Property] public ModelRenderer TintTarget { get; set; }

	[Property] public Color SplatTint   { get; set; } = new Color( 1.0f, 0.6f, 0.2f ); // peach orange
	[Property] public Color NormalTint  { get; set; } = Color.White;

	/// <summary>
	/// If set, this component will be disabled for the duration of the splat
	/// and re-enabled when it clears. Perfect for stunning turrets.
	/// </summary>
	[Property] public Component ComponentToDisable { get; set; }

	// ── ISplatReactor ─────────────────────────────────────────────────────────

	void ISplatReactor.OnSplatReceived( SplatInfo info )
	{
		// Built-in: impact particle
		if ( ImpactParticlePrefab.IsValid() )
			ImpactParticlePrefab.Clone( new Transform( info.Position, Rotation.LookAt( info.Normal ) ) );

		// Built-in: impact sound
		if ( ImpactSound.IsValid() )
			Sound.Play( ImpactSound, info.Position );

		// Built-in: tint
		if ( TintTarget.IsValid() )
			TintTarget.Tint = SplatTint;

		// Built-in: disable component
		if ( ComponentToDisable.IsValid() )
			ComponentToDisable.Enabled = false;

		// Action graph
		OnSplatted?.Invoke( info );
	}

	void ISplatReactor.OnSplatCleared()
	{
		// Built-in: revert tint
		if ( TintTarget.IsValid() )
			TintTarget.Tint = NormalTint;

		// Built-in: re-enable component
		if ( ComponentToDisable.IsValid() )
			ComponentToDisable.Enabled = true;

		// Action graph
		OnCleared?.Invoke();
	}
}