Files
beyond/Assets/AI/_Demon/DestroyableTurret.cs
2025-11-20 14:34:59 +01:00

365 lines
9.2 KiB
C#

using Invector;
using Lean.Pool;
using UnityEngine;
namespace DemonBoss.Magic
{
/// <summary>
/// Turret component that allows player to destroy crystal turrets
/// Implements Invector interfaces for damage system integration
/// Attach to turret prefab along with appropriate collider (non-trigger)
/// </summary>
public class DestroyableTurret : MonoBehaviour, vIDamageReceiver, vIHealthController
{
[Header("Health Configuration")]
[Tooltip("Maximum health points of the turret")]
public int maxHealth = 50;
[Tooltip("Current health points (read-only)")]
[SerializeField] private int currentHealth;
[Header("Destruction Effects")]
[Tooltip("Visual effect played when turret is destroyed")]
public GameObject destructionEffect;
[Tooltip("Sound played when turret is destroyed")]
public AudioClip destructionSound;
[Tooltip("Sound played when turret receives damage")]
public AudioClip hitSound;
[Header("Visual Feedback")]
[Tooltip("Material applied when turret is damaged")]
public Material damagedMaterial;
[Tooltip("HP threshold below which damaged material is applied (percentage)")]
[Range(0f, 1f)]
public float damagedThreshold = 0.5f;
[Header("Debug")]
[Tooltip("Enable debug logging")]
public bool enableDebug = false;
// Private fields
private AudioSource audioSource;
private CrystalShooterAI shooterAI;
private Renderer turretRenderer;
private Material originalMaterial;
private bool isDestroyed = false;
// Invector system events
public UnityEngine.Events.UnityEvent OnReceiveDamage { get; set; }
public UnityEngine.Events.UnityEvent OnDead { get; set; }
// vIHealthController implementation
public int currentHealthRecovery { get; set; }
public int maxHealthRecovery { get; set; }
public bool isDead => currentHealth <= 0;
public OnReceiveDamage onStartReceiveDamage => throw new System.NotImplementedException();
public OnReceiveDamage onReceiveDamage => throw new System.NotImplementedException();
public OnDead onDead => throw new System.NotImplementedException();
float vIHealthController.currentHealth => throw new System.NotImplementedException();
public int MaxHealth => throw new System.NotImplementedException();
bool vIHealthController.isDead { get => isDead; set => throw new System.NotImplementedException(); }
/// <summary>
/// Initialize components and validate setup
/// </summary>
private void Awake()
{
InitializeComponents();
InitializeHealth();
}
/// <summary>
/// Find and configure required components
/// </summary>
private void InitializeComponents()
{
// Find components
shooterAI = GetComponent<CrystalShooterAI>();
turretRenderer = GetComponent<Renderer>();
// Store original material
if (turretRenderer != null)
{
originalMaterial = turretRenderer.material;
}
// Setup AudioSource
audioSource = GetComponent<AudioSource>();
if (audioSource == null && (destructionSound != null || hitSound != null))
{
audioSource = gameObject.AddComponent<AudioSource>();
audioSource.playOnAwake = false;
audioSource.spatialBlend = 1f; // 3D sound
}
// Validate collider setup
Collider col = GetComponent<Collider>();
if (col != null && col.isTrigger && enableDebug)
{
Debug.LogWarning("[DestroyableTurret] Collider should not be trigger for damage system!");
}
}
/// <summary>
/// Setup initial health values
/// </summary>
private void InitializeHealth()
{
currentHealth = maxHealth;
maxHealthRecovery = maxHealth;
currentHealthRecovery = 0;
if (enableDebug) Debug.Log($"[DestroyableTurret] Initialized with {currentHealth}/{maxHealth} HP");
}
#region vIDamageReceiver Implementation
/// <summary>
/// Handle incoming damage from Invector damage system
/// </summary>
public void TakeDamage(vDamage damage)
{
if (isDestroyed) return;
int damageValue = Mathf.RoundToInt(damage.damageValue);
TakeDamage(damageValue);
if (enableDebug)
Debug.Log($"[DestroyableTurret] Received {damageValue} damage from {damage.sender?.name}");
}
/// <summary>
/// Handle incoming damage with hit reaction parameter
/// </summary>
public void TakeDamage(vDamage damage, bool hitReaction)
{
TakeDamage(damage);
}
#endregion vIDamageReceiver Implementation
#region vIHealthController Implementation
/// <summary>
/// Apply damage to the turret and handle destruction
/// </summary>
public void TakeDamage(int damage)
{
if (isDestroyed || currentHealth <= 0) return;
currentHealth = Mathf.Max(0, currentHealth - damage);
if (enableDebug)
Debug.Log($"[DestroyableTurret] HP: {currentHealth}/{maxHealth} (-{damage})");
// Play hit effects
PlayHitEffects();
UpdateVisualState();
// Trigger damage event
OnReceiveDamage?.Invoke();
// Check if destroyed
if (currentHealth <= 0)
{
DestroyTurret();
}
}
/// <summary>
/// Change health by specified amount (positive for healing, negative for damage)
/// </summary>
public void ChangeHealth(int value)
{
if (isDestroyed) return;
if (value < 0)
{
TakeDamage(-value);
}
else
{
currentHealth = Mathf.Min(maxHealth, currentHealth + value);
if (enableDebug) Debug.Log($"[DestroyableTurret] Healed by {value}, HP: {currentHealth}/{maxHealth}");
}
}
/// <summary>
/// Modify maximum health value
/// </summary>
public void ChangeMaxHealth(int value)
{
maxHealth = Mathf.Max(1, maxHealth + value);
currentHealth = Mathf.Min(currentHealth, maxHealth);
}
#endregion vIHealthController Implementation
/// <summary>
/// Play sound and visual effects when receiving damage
/// </summary>
private void PlayHitEffects()
{
// Play hit sound
if (audioSource != null && hitSound != null)
{
audioSource.PlayOneShot(hitSound);
}
}
/// <summary>
/// Update visual appearance based on current health
/// </summary>
private void UpdateVisualState()
{
// Change material if turret is heavily damaged
if (turretRenderer != null && damagedMaterial != null)
{
float healthPercent = (float)currentHealth / maxHealth;
if (healthPercent <= damagedThreshold && turretRenderer.material != damagedMaterial)
{
turretRenderer.material = damagedMaterial;
if (enableDebug) Debug.Log("[DestroyableTurret] Changed to damaged material");
}
}
}
/// <summary>
/// Handle turret destruction sequence
/// </summary>
private void DestroyTurret()
{
if (isDestroyed) return;
isDestroyed = true;
if (enableDebug) Debug.Log("[DestroyableTurret] Turret has been destroyed!");
// Stop shooting
if (shooterAI != null)
{
shooterAI.StopShooting();
}
// Trigger death event
OnDead?.Invoke();
// Play destruction effects
PlayDestructionEffects();
// Destroy after brief delay (let effects play)
StartCoroutine(DestroyAfterDelay(0.1f));
}
/// <summary>
/// Play visual and audio effects for turret destruction
/// </summary>
private void PlayDestructionEffects()
{
// Spawn visual effect
if (destructionEffect != null)
{
GameObject effect = Instantiate(destructionEffect, transform.position, transform.rotation);
Destroy(effect, 5f);
}
// Play destruction sound
if (audioSource != null && destructionSound != null)
{
audioSource.PlayOneShot(destructionSound);
// Keep AudioSource alive to finish playing
audioSource.transform.parent = null;
Destroy(audioSource.gameObject, destructionSound.length + 1f);
}
}
/// <summary>
/// Coroutine to destroy turret after specified delay
/// </summary>
private System.Collections.IEnumerator DestroyAfterDelay(float delay)
{
yield return new UnityEngine.WaitForSeconds(delay);
// Remove through Lean Pool (if used) or regular Destroy
if (gameObject.scene.isLoaded) // Check if object still exists
{
LeanPool.Despawn(gameObject);
}
}
/// <summary>
/// Force immediate turret destruction (e.g., when boss dies)
/// </summary>
public void ForceDestroy()
{
if (enableDebug) Debug.Log("[DestroyableTurret] Forced destruction");
currentHealth = 0;
//isDead = true;
DestroyTurret();
}
/// <summary>
/// Returns current health as percentage of maximum health
/// </summary>
public float GetHealthPercentage()
{
return maxHealth > 0 ? (float)currentHealth / maxHealth : 0f;
}
#if UNITY_EDITOR
/// <summary>
/// Draw turret status visualization in Scene View
/// </summary>
private void OnDrawGizmosSelected()
{
// Show turret status
if (Application.isPlaying)
{
Gizmos.color = currentHealth > maxHealth * 0.5f ? Color.green :
currentHealth > maxHealth * 0.25f ? Color.yellow : Color.red;
}
else
{
Gizmos.color = Color.green;
}
Gizmos.DrawWireCube(transform.position + Vector3.up * 2f, Vector3.one * 0.5f);
// HP text in Scene View
if (Application.isPlaying)
{
UnityEditor.Handles.Label(transform.position + Vector3.up * 2.5f, $"HP: {currentHealth}/{maxHealth}");
}
}
#endif
public void AddHealth(int value)
{
throw new System.NotImplementedException();
}
public void ResetHealth(float health)
{
throw new System.NotImplementedException();
}
public void ResetHealth()
{
throw new System.NotImplementedException();
}
}
}