Files
beyond/Assets/AI/_Summoner/SA_CastFireball.cs
SzymonMis 06f9c7349d Summoner
2026-02-19 21:34:07 +01:00

146 lines
3.8 KiB
C#

using DemonBoss.AI;
using Invector.vCharacterController.AI.FSMBehaviour;
using UnityEngine;
using UnityEngine.Animations;
using UnityEngine.Playables;
namespace DemonBoss.Summoner
{
/// <summary>
/// FSM Action: cast a single fireball with optional overlay clip
/// </summary>
[CreateAssetMenu(menuName = "Invector/FSM/Actions/Summoner/Cast Fireball")]
public class SA_CastFireball : vStateAction
{
public override string categoryName => "Summoner";
public override string defaultName => "Cast Fireball";
[Header("Timing")]
[Tooltip("Optional delay before the fireball is spawned")]
public float castDelay = 0f;
[Header("One-off Overlay Clip (No Animator Params)")]
public AnimationClip overlayClip;
[Tooltip("Playback speed (1 = normal)")]
public float overlaySpeed = 1f;
[Tooltip("Blend-in seconds (instant in this minimal impl)")]
public float overlayFadeIn = 0.10f;
[Tooltip("Blend-out seconds (instant in this minimal impl)")]
public float overlayFadeOut = 0.10f;
[Header("Debug")]
public bool enableDebug = false;
private SummonerAI _summoner;
private bool _castScheduled;
// --- Playables runtime ---
private PlayableGraph _overlayGraph;
private AnimationPlayableOutput _overlayOutput;
private AnimationClipPlayable _overlayPlayable;
private bool _overlayPlaying;
private float _overlayStopAtTime;
public override void DoAction(vIFSMBehaviourController fsm, vFSMComponentExecutionType execType = vFSMComponentExecutionType.OnStateUpdate)
{
if (execType == vFSMComponentExecutionType.OnStateEnter)
{
OnEnter(fsm);
}
else if (execType == vFSMComponentExecutionType.OnStateUpdate)
{
if (_overlayPlaying && Time.time >= _overlayStopAtTime)
{
StopOverlayWithFade();
}
}
else if (execType == vFSMComponentExecutionType.OnStateExit)
{
OnExit();
}
}
private void OnEnter(vIFSMBehaviourController fsm)
{
_summoner = fsm.gameObject.GetComponent<SummonerAI>();
if (_summoner == null)
{
if (enableDebug) Debug.LogWarning("[SA_CastFireball] No SummonerAI component found!");
return;
}
_castScheduled = false;
// Play overlay clip (no Animator params)
PlayOverlayOnce(fsm.transform);
if (castDelay > 0f)
{
fsm.gameObject.AddComponent<DelayedInvoker>().Init(castDelay, SpawnFireballNow);
_castScheduled = true;
}
else
{
SpawnFireballNow();
}
}
private void OnExit()
{
StopOverlayImmediate();
_castScheduled = false;
}
private void SpawnFireballNow()
{
if (_summoner == null) return;
_summoner.CastFireball();
if (enableDebug) Debug.Log("[SA_CastFireball] Fireball cast");
}
// ------------------ Overlay helpers ------------------
private void PlayOverlayOnce(Transform owner)
{
if (overlayClip == null || owner == null) return;
Animator anim = owner.GetComponent<Animator>();
if (anim == null) return;
_overlayGraph = PlayableGraph.Create("ActionOverlay(CastFireball)");
_overlayGraph.SetTimeUpdateMode(DirectorUpdateMode.GameTime);
_overlayPlayable = AnimationClipPlayable.Create(_overlayGraph, overlayClip);
_overlayPlayable.SetSpeed(Mathf.Max(0.0001f, overlaySpeed));
_overlayPlayable.SetApplyFootIK(false);
_overlayPlayable.SetApplyPlayableIK(false);
_overlayOutput = AnimationPlayableOutput.Create(_overlayGraph, "AnimOut", anim);
_overlayOutput.SetSourcePlayable(_overlayPlayable);
_overlayOutput.SetWeight(1f);
_overlayGraph.Play();
float duration = (float)overlayClip.length / Mathf.Max(0.0001f, overlaySpeed);
_overlayStopAtTime = Time.time + duration;
_overlayPlaying = true;
}
private void StopOverlayWithFade()
{
StopOverlayImmediate();
}
private void StopOverlayImmediate()
{
if (!_overlayGraph.IsValid()) return;
_overlayGraph.Stop();
_overlayGraph.Destroy();
_overlayPlaying = false;
}
}
}