Files
beyond/Assets/AI/Demon/SA_CallMeteor.cs
Szymon Miś 7cccafbb2b Demon fixes
2025-08-29 11:48:10 +02:00

144 lines
4.3 KiB
C#

using Invector.vCharacterController.AI.FSMBehaviour;
using Lean.Pool;
using System.Collections;
using UnityEngine;
namespace DemonBoss.Magic
{
/// <summary>
/// FSM Action: Boss calls down a meteor.
/// Shows a decal at the player's position, locks an impact point on ground,
/// then spawns the MeteorProjectile prefab above that point after a delay.
/// Cancels cleanly on state exit.
/// </summary>
[CreateAssetMenu(menuName = "Invector/FSM/Actions/DemonBoss/Call Meteor")]
public class SA_CallMeteor : vStateAction
{
public override string categoryName => "DemonBoss/Magic";
public override string defaultName => "Call Meteor";
[Header("Meteor Setup")]
[Tooltip("Prefab with MeteorProjectile component")]
public GameObject meteorPrefab;
[Tooltip("Visual decal prefab marking impact point")]
public GameObject decalPrefab;
[Tooltip("Height above ground at which meteor spawns")]
public float spawnHeight = 40f;
[Tooltip("Delay before meteor spawns after decal")]
public float castDelay = 1.5f;
[Header("Ground")]
[Tooltip("Layer mask for ground raycast")]
public LayerMask groundMask = -1;
[Header("Debug")]
public bool enableDebug = false;
private Transform player;
private GameObject spawnedDecal;
private Vector3 impactPoint;
private Coroutine _spawnRoutine;
private CoroutineRunner _runner;
/// <summary>
/// Entry point for the FSM action, delegates to OnEnter/OnExit depending on execution type.
/// </summary>
public override void DoAction(vIFSMBehaviourController fsm, vFSMComponentExecutionType executionType = vFSMComponentExecutionType.OnStateUpdate)
{
if (executionType == vFSMComponentExecutionType.OnStateEnter)
OnEnter(fsm);
if (executionType == vFSMComponentExecutionType.OnStateExit)
OnExit();
}
/// <summary>
/// Acquires the player, locks the impact point on the ground, shows the decal,
/// and starts the delayed meteor spawn coroutine.
/// </summary>
private void OnEnter(vIFSMBehaviourController fsm)
{
player = GameObject.FindGameObjectWithTag("Player")?.transform;
if (player == null)
{
if (enableDebug) Debug.LogWarning("[SA_CallMeteor] No player found!");
return;
}
// Raycast down from the player to lock the impact point
Vector3 rayStart = player.position + Vector3.up * 5f;
if (Physics.Raycast(rayStart, Vector3.down, out RaycastHit hit, 100f, groundMask, QueryTriggerInteraction.Ignore))
impactPoint = hit.point;
else
impactPoint = player.position;
// Spawn decal
if (decalPrefab != null)
spawnedDecal = LeanPool.Spawn(decalPrefab, impactPoint, Quaternion.identity);
// Get or add a dedicated runner for coroutines
var hostGO = fsm.transform.gameObject;
_runner = hostGO.GetComponent<CoroutineRunner>();
if (_runner == null) _runner = hostGO.AddComponent<CoroutineRunner>();
// Start delayed spawn
_spawnRoutine = _runner.StartCoroutine(SpawnMeteorAfterDelay());
}
/// <summary>
/// Cancels the pending spawn and cleans up the decal when exiting the state.
/// </summary>
private void OnExit()
{
if (_runner != null && _spawnRoutine != null)
{
_runner.StopCoroutine(_spawnRoutine);
_spawnRoutine = null;
}
if (spawnedDecal != null)
{
LeanPool.Despawn(spawnedDecal);
spawnedDecal = null;
}
}
/// <summary>
/// Waits for the configured cast delay and then spawns the meteor.
/// </summary>
private IEnumerator SpawnMeteorAfterDelay()
{
yield return new WaitForSeconds(castDelay);
SpawnMeteor();
}
/// <summary>
/// Spawns the meteor prefab above the locked impact point and cleans up the decal.
/// </summary>
private void SpawnMeteor()
{
if (meteorPrefab == null) return;
Vector3 spawnPos = impactPoint + Vector3.up * spawnHeight;
LeanPool.Spawn(meteorPrefab, spawnPos, Quaternion.identity);
if (enableDebug) Debug.Log($"[SA_CallMeteor] Meteor spawned at {spawnPos}, impact={impactPoint}");
if (spawnedDecal != null)
{
LeanPool.Despawn(spawnedDecal);
spawnedDecal = null;
}
}
}
/// <summary>
/// Lightweight helper component dedicated to running coroutines for ScriptableObject actions.
/// </summary>
public sealed class CoroutineRunner : MonoBehaviour
{ }
}