updated demon

This commit is contained in:
2025-08-20 14:28:49 +02:00
parent 3b07a6f937
commit e79d12ebec
13 changed files with 26080 additions and 17039 deletions

View File

@@ -1,352 +1,352 @@
using Invector.vCharacterController.AI.FSMBehaviour;
using Lean.Pool;
using System.Collections;
using UnityEngine;
namespace DemonBoss.Magic
{
/// <summary>
/// StateAction for Meteor Strike spell - boss summons meteor falling on player
/// First places decal at player position, after 1.5s checks if path is clear and drops rock
/// </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 Configuration")]
[Tooltip("Meteor rock prefab")]
public GameObject rockPrefab;
[Tooltip("Decal prefab showing impact location")]
public GameObject decalPrefab;
[Tooltip("Height from which meteor falls")]
public float meteorHeight = 30f;
[Tooltip("Delay between placing decal and spawning meteor")]
public float castDelay = 1.5f;
[Tooltip("Obstacle check radius above target")]
public float obstacleCheckRadius = 2f;
[Tooltip("Layer mask for obstacles blocking meteor")]
public LayerMask obstacleLayerMask = -1;
[Tooltip("Layer mask for ground")]
public LayerMask groundLayerMask = -1;
[Tooltip("Animator trigger name for meteor summoning animation")]
public string animatorTrigger = "CastMeteor";
[Header("Targeting")]
[Tooltip("Maximum raycast distance to find ground")]
public float maxGroundDistance = 100f;
[Tooltip("Height above ground for obstacle checking")]
public float airCheckHeight = 5f;
[Header("Debug")]
[Tooltip("Enable debug logging")]
public bool enableDebug = false;
[Tooltip("Show gizmos in Scene View")]
public bool showGizmos = true;
private GameObject spawnedDecal;
private Transform playerTransform;
private Vector3 targetPosition;
private bool meteorCasting = false;
private MonoBehaviour coroutineRunner;
/// <summary>
/// Main action execution method called by FSM
/// </summary>
public override void DoAction(vIFSMBehaviourController fsmBehaviour, vFSMComponentExecutionType executionType = vFSMComponentExecutionType.OnStateUpdate)
{
if (executionType == vFSMComponentExecutionType.OnStateEnter)
{
OnStateEnter(fsmBehaviour);
}
else if (executionType == vFSMComponentExecutionType.OnStateExit)
{
OnStateExit(fsmBehaviour);
}
}
/// <summary>
/// Called when entering state - starts meteor casting
/// </summary>
private void OnStateEnter(vIFSMBehaviourController fsmBehaviour)
{
if (enableDebug) Debug.Log("[SA_CallMeteor] Starting meteor casting");
FindPlayer(fsmBehaviour);
var animator = fsmBehaviour.transform.GetComponent<Animator>();
if (animator != null && !string.IsNullOrEmpty(animatorTrigger))
{
animator.SetTrigger(animatorTrigger);
if (enableDebug) Debug.Log($"[SA_CallMeteor] Set trigger: {animatorTrigger}");
}
StartMeteorCast(fsmBehaviour);
}
/// <summary>
/// Called when exiting state - cleanup
/// </summary>
private void OnStateExit(vIFSMBehaviourController fsmBehaviour)
{
if (enableDebug) Debug.Log("[SA_CallMeteor] Exiting meteor state");
meteorCasting = false;
}
/// <summary>
/// Finds player transform
/// </summary>
private void FindPlayer(vIFSMBehaviourController fsmBehaviour)
{
GameObject player = GameObject.FindGameObjectWithTag("Player");
if (player != null)
{
playerTransform = player.transform;
if (enableDebug) Debug.Log("[SA_CallMeteor] Player found by tag");
return;
}
var aiController = fsmBehaviour as Invector.vCharacterController.AI.vIControlAI;
if (aiController != null && aiController.currentTarget != null)
{
playerTransform = aiController.currentTarget.transform;
if (enableDebug) Debug.Log("[SA_CallMeteor] Player found through AI target");
return;
}
if (enableDebug) Debug.LogWarning("[SA_CallMeteor] Player not found!");
}
/// <summary>
/// Starts meteor casting process
/// </summary>
private void StartMeteorCast(vIFSMBehaviourController fsmBehaviour)
{
if (playerTransform == null)
{
if (enableDebug) Debug.LogWarning("[SA_CallMeteor] No target - finishing state");
return;
}
if (FindGroundUnderPlayer(out Vector3 groundPos))
{
targetPosition = groundPos;
SpawnDecal(targetPosition);
coroutineRunner = fsmBehaviour.transform.GetComponent<MonoBehaviour>();
if (coroutineRunner != null)
{
coroutineRunner.StartCoroutine(MeteorCastCoroutine(fsmBehaviour));
}
else
{
CastMeteorImmediate();
}
meteorCasting = true;
if (enableDebug) Debug.Log($"[SA_CallMeteor] Meteor target: {targetPosition}");
}
else
{
if (enableDebug) Debug.LogWarning("[SA_CallMeteor] Cannot find ground under player");
}
}
/// <summary>
/// Finds ground under player using raycast
/// </summary>
private bool FindGroundUnderPlayer(out Vector3 groundPosition)
{
groundPosition = Vector3.zero;
Vector3 playerPos = playerTransform.position;
Vector3 rayStart = playerPos + Vector3.up * 5f;
Ray groundRay = new Ray(rayStart, Vector3.down);
if (Physics.Raycast(groundRay, out RaycastHit hit, maxGroundDistance, groundLayerMask))
{
groundPosition = hit.point;
return true;
}
groundPosition = playerPos;
return true;
}
/// <summary>
/// Spawns decal showing meteor impact location
/// </summary>
private void SpawnDecal(Vector3 position)
{
if (decalPrefab == null)
{
if (enableDebug) Debug.LogWarning("[SA_CallMeteor] Missing decal prefab");
return;
}
Quaternion decalRotation = Quaternion.identity;
Ray surfaceRay = new Ray(position + Vector3.up * 2f, Vector3.down);
if (Physics.Raycast(surfaceRay, out RaycastHit hit, 5f, groundLayerMask))
{
decalRotation = Quaternion.FromToRotation(Vector3.up, hit.normal);
}
spawnedDecal = LeanPool.Spawn(decalPrefab, position, decalRotation);
if (enableDebug) Debug.Log($"[SA_CallMeteor] Decal spawned at: {position}");
}
/// <summary>
/// Coroutine handling meteor casting process with delay
/// </summary>
private IEnumerator MeteorCastCoroutine(vIFSMBehaviourController fsmBehaviour)
{
yield return new WaitForSeconds(castDelay);
if (enableDebug) Debug.Log("[SA_CallMeteor] Checking if path is clear for meteor");
if (IsPathClearForMeteor(targetPosition))
{
SpawnMeteor(targetPosition);
if (enableDebug) Debug.Log("[SA_CallMeteor] Meteor spawned");
}
else
{
if (enableDebug) Debug.Log("[SA_CallMeteor] Path blocked - meteor not spawned");
}
CleanupDecal();
DEC_CheckCooldown.SetCooldownStatic(fsmBehaviour, "Meteor", 20f);
meteorCasting = false;
}
/// <summary>
/// Immediate meteor cast without coroutine (fallback)
/// </summary>
private void CastMeteorImmediate()
{
if (IsPathClearForMeteor(targetPosition))
{
SpawnMeteor(targetPosition);
}
CleanupDecal();
}
/// <summary>
/// Checks if path above target is clear for meteor
/// </summary>
private bool IsPathClearForMeteor(Vector3 targetPos)
{
Vector3 checkStart = targetPos + Vector3.up * airCheckHeight;
Vector3 checkEnd = targetPos + Vector3.up * meteorHeight;
if (Physics.CheckSphere(checkStart, obstacleCheckRadius, obstacleLayerMask))
{
return false;
}
Ray pathRay = new Ray(checkEnd, Vector3.down);
if (Physics.SphereCast(pathRay, obstacleCheckRadius, meteorHeight - airCheckHeight, obstacleLayerMask))
{
return false;
}
return true;
}
/// <summary>
/// Spawns meteor in air above target
/// </summary>
private void SpawnMeteor(Vector3 targetPos)
{
if (rockPrefab == null)
{
Debug.LogError("[SA_CallMeteor] Missing meteor prefab!");
return;
}
Vector3 meteorSpawnPos = targetPos + Vector3.up * meteorHeight;
GameObject meteor = LeanPool.Spawn(rockPrefab, meteorSpawnPos, Quaternion.identity);
Rigidbody meteorRb = meteor.GetComponent<Rigidbody>();
if (meteorRb != null)
{
meteorRb.linearVelocity = Vector3.down * 5f;
meteorRb.angularVelocity = Random.insideUnitSphere * 2f;
}
if (enableDebug) Debug.Log($"[SA_CallMeteor] Meteor spawned at height: {meteorHeight}m");
}
/// <summary>
/// Removes decal from map
/// </summary>
private void CleanupDecal()
{
if (spawnedDecal != null)
{
LeanPool.Despawn(spawnedDecal);
spawnedDecal = null;
if (enableDebug) Debug.Log("[SA_CallMeteor] Decal removed");
}
}
/// <summary>
/// Checks if meteor is currently being cast
/// </summary>
public bool IsCasting()
{
return meteorCasting;
}
/// <summary>
/// Returns meteor target position
/// </summary>
public Vector3 GetTargetPosition()
{
return targetPosition;
}
/// <summary>
/// Draws gizmos in Scene View for debugging
/// </summary>
private void OnDrawGizmosSelected()
{
if (!showGizmos) return;
if (meteorCasting && targetPosition != Vector3.zero)
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(targetPosition, 1f);
Vector3 spawnPos = targetPosition + Vector3.up * meteorHeight;
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(spawnPos, 0.5f);
Gizmos.color = Color.yellow;
Gizmos.DrawLine(spawnPos, targetPosition);
Vector3 checkPos = targetPosition + Vector3.up * airCheckHeight;
Gizmos.color = Color.blue;
Gizmos.DrawWireSphere(checkPos, obstacleCheckRadius);
}
}
}
using Invector.vCharacterController.AI.FSMBehaviour;
using Lean.Pool;
using System.Collections;
using UnityEngine;
namespace DemonBoss.Magic
{
/// <summary>
/// StateAction for Meteor Strike spell - boss summons meteor falling on player
/// First places decal at player position, after 1.5s checks if path is clear and drops rock
/// </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 Configuration")]
[Tooltip("Meteor rock prefab")]
public GameObject rockPrefab;
[Tooltip("Decal prefab showing impact location")]
public GameObject decalPrefab;
[Tooltip("Height from which meteor falls")]
public float meteorHeight = 30f;
[Tooltip("Delay between placing decal and spawning meteor")]
public float castDelay = 1.5f;
[Tooltip("Obstacle check radius above target")]
public float obstacleCheckRadius = 2f;
[Tooltip("Layer mask for obstacles blocking meteor")]
public LayerMask obstacleLayerMask = -1;
[Tooltip("Layer mask for ground")]
public LayerMask groundLayerMask = -1;
[Tooltip("Animator trigger name for meteor summoning animation")]
public string animatorTrigger = "CastMeteor";
[Header("Targeting")]
[Tooltip("Maximum raycast distance to find ground")]
public float maxGroundDistance = 100f;
[Tooltip("Height above ground for obstacle checking")]
public float airCheckHeight = 5f;
[Header("Debug")]
[Tooltip("Enable debug logging")]
public bool enableDebug = false;
[Tooltip("Show gizmos in Scene View")]
public bool showGizmos = true;
private GameObject spawnedDecal;
private Transform playerTransform;
private Vector3 targetPosition;
private bool meteorCasting = false;
private MonoBehaviour coroutineRunner;
/// <summary>
/// Main action execution method called by FSM
/// </summary>
public override void DoAction(vIFSMBehaviourController fsmBehaviour, vFSMComponentExecutionType executionType = vFSMComponentExecutionType.OnStateUpdate)
{
if (executionType == vFSMComponentExecutionType.OnStateEnter)
{
OnStateEnter(fsmBehaviour);
}
else if (executionType == vFSMComponentExecutionType.OnStateExit)
{
OnStateExit(fsmBehaviour);
}
}
/// <summary>
/// Called when entering state - starts meteor casting
/// </summary>
private void OnStateEnter(vIFSMBehaviourController fsmBehaviour)
{
if (enableDebug) Debug.Log("[SA_CallMeteor] Starting meteor casting");
FindPlayer(fsmBehaviour);
var animator = fsmBehaviour.transform.GetComponent<Animator>();
if (animator != null && !string.IsNullOrEmpty(animatorTrigger))
{
animator.SetTrigger(animatorTrigger);
if (enableDebug) Debug.Log($"[SA_CallMeteor] Set trigger: {animatorTrigger}");
}
StartMeteorCast(fsmBehaviour);
}
/// <summary>
/// Called when exiting state - cleanup
/// </summary>
private void OnStateExit(vIFSMBehaviourController fsmBehaviour)
{
if (enableDebug) Debug.Log("[SA_CallMeteor] Exiting meteor state");
meteorCasting = false;
}
/// <summary>
/// Finds player transform
/// </summary>
private void FindPlayer(vIFSMBehaviourController fsmBehaviour)
{
GameObject player = GameObject.FindGameObjectWithTag("Player");
if (player != null)
{
playerTransform = player.transform;
if (enableDebug) Debug.Log("[SA_CallMeteor] Player found by tag");
return;
}
var aiController = fsmBehaviour as Invector.vCharacterController.AI.vIControlAI;
if (aiController != null && aiController.currentTarget != null)
{
playerTransform = aiController.currentTarget.transform;
if (enableDebug) Debug.Log("[SA_CallMeteor] Player found through AI target");
return;
}
if (enableDebug) Debug.LogWarning("[SA_CallMeteor] Player not found!");
}
/// <summary>
/// Starts meteor casting process
/// </summary>
private void StartMeteorCast(vIFSMBehaviourController fsmBehaviour)
{
if (playerTransform == null)
{
if (enableDebug) Debug.LogWarning("[SA_CallMeteor] No target - finishing state");
return;
}
if (FindGroundUnderPlayer(out Vector3 groundPos))
{
targetPosition = groundPos;
SpawnDecal(targetPosition);
coroutineRunner = fsmBehaviour.transform.GetComponent<MonoBehaviour>();
if (coroutineRunner != null)
{
coroutineRunner.StartCoroutine(MeteorCastCoroutine(fsmBehaviour));
}
else
{
CastMeteorImmediate();
}
meteorCasting = true;
if (enableDebug) Debug.Log($"[SA_CallMeteor] Meteor target: {targetPosition}");
}
else
{
if (enableDebug) Debug.LogWarning("[SA_CallMeteor] Cannot find ground under player");
}
}
/// <summary>
/// Finds ground under player using raycast
/// </summary>
private bool FindGroundUnderPlayer(out Vector3 groundPosition)
{
groundPosition = Vector3.zero;
Vector3 playerPos = playerTransform.position;
Vector3 rayStart = playerPos + Vector3.up * 5f;
Ray groundRay = new Ray(rayStart, Vector3.down);
if (Physics.Raycast(groundRay, out RaycastHit hit, maxGroundDistance, groundLayerMask))
{
groundPosition = hit.point;
return true;
}
groundPosition = playerPos;
return true;
}
/// <summary>
/// Spawns decal showing meteor impact location
/// </summary>
private void SpawnDecal(Vector3 position)
{
if (decalPrefab == null)
{
if (enableDebug) Debug.LogWarning("[SA_CallMeteor] Missing decal prefab");
return;
}
Quaternion decalRotation = Quaternion.identity;
Ray surfaceRay = new Ray(position + Vector3.up * 2f, Vector3.down);
if (Physics.Raycast(surfaceRay, out RaycastHit hit, 5f, groundLayerMask))
{
decalRotation = Quaternion.FromToRotation(Vector3.up, hit.normal);
}
spawnedDecal = LeanPool.Spawn(decalPrefab, position, decalRotation);
if (enableDebug) Debug.Log($"[SA_CallMeteor] Decal spawned at: {position}");
}
/// <summary>
/// Coroutine handling meteor casting process with delay
/// </summary>
private IEnumerator MeteorCastCoroutine(vIFSMBehaviourController fsmBehaviour)
{
yield return new WaitForSeconds(castDelay);
if (enableDebug) Debug.Log("[SA_CallMeteor] Checking if path is clear for meteor");
if (IsPathClearForMeteor(targetPosition))
{
SpawnMeteor(targetPosition);
if (enableDebug) Debug.Log("[SA_CallMeteor] Meteor spawned");
}
else
{
if (enableDebug) Debug.Log("[SA_CallMeteor] Path blocked - meteor not spawned");
}
CleanupDecal();
DEC_CheckCooldown.SetCooldownStatic(fsmBehaviour, "Meteor", 20f);
meteorCasting = false;
}
/// <summary>
/// Immediate meteor cast without coroutine (fallback)
/// </summary>
private void CastMeteorImmediate()
{
if (IsPathClearForMeteor(targetPosition))
{
SpawnMeteor(targetPosition);
}
CleanupDecal();
}
/// <summary>
/// Checks if path above target is clear for meteor
/// </summary>
private bool IsPathClearForMeteor(Vector3 targetPos)
{
Vector3 checkStart = targetPos + Vector3.up * airCheckHeight;
Vector3 checkEnd = targetPos + Vector3.up * meteorHeight;
if (Physics.CheckSphere(checkStart, obstacleCheckRadius, obstacleLayerMask))
{
return false;
}
Ray pathRay = new Ray(checkEnd, Vector3.down);
if (Physics.SphereCast(pathRay, obstacleCheckRadius, meteorHeight - airCheckHeight, obstacleLayerMask))
{
return false;
}
return true;
}
/// <summary>
/// Spawns meteor in air above target
/// </summary>
private void SpawnMeteor(Vector3 targetPos)
{
if (rockPrefab == null)
{
Debug.LogError("[SA_CallMeteor] Missing meteor prefab!");
return;
}
Vector3 meteorSpawnPos = targetPos + Vector3.up * meteorHeight;
GameObject meteor = LeanPool.Spawn(rockPrefab, meteorSpawnPos, Quaternion.identity);
Rigidbody meteorRb = meteor.GetComponent<Rigidbody>();
if (meteorRb != null)
{
meteorRb.linearVelocity = Vector3.down * 5f;
meteorRb.angularVelocity = Random.insideUnitSphere * 2f;
}
if (enableDebug) Debug.Log($"[SA_CallMeteor] Meteor spawned at height: {meteorHeight}m");
}
/// <summary>
/// Removes decal from map
/// </summary>
private void CleanupDecal()
{
if (spawnedDecal != null)
{
LeanPool.Despawn(spawnedDecal);
spawnedDecal = null;
if (enableDebug) Debug.Log("[SA_CallMeteor] Decal removed");
}
}
/// <summary>
/// Checks if meteor is currently being cast
/// </summary>
public bool IsCasting()
{
return meteorCasting;
}
/// <summary>
/// Returns meteor target position
/// </summary>
public Vector3 GetTargetPosition()
{
return targetPosition;
}
/// <summary>
/// Draws gizmos in Scene View for debugging
/// </summary>
private void OnDrawGizmosSelected()
{
if (!showGizmos) return;
if (meteorCasting && targetPosition != Vector3.zero)
{
Gizmos.color = Color.red;
Gizmos.DrawWireSphere(targetPosition, 1f);
Vector3 spawnPos = targetPosition + Vector3.up * meteorHeight;
Gizmos.color = Color.yellow;
Gizmos.DrawWireSphere(spawnPos, 0.5f);
Gizmos.color = Color.yellow;
Gizmos.DrawLine(spawnPos, targetPosition);
Vector3 checkPos = targetPosition + Vector3.up * airCheckHeight;
Gizmos.color = Color.blue;
Gizmos.DrawWireSphere(checkPos, obstacleCheckRadius);
}
}
}
}