90 lines
3.3 KiB
C#
90 lines
3.3 KiB
C#
// Assets/_Project/Scripts/Gameplay/EnemyHealth.cs
|
|
using Unity.Netcode;
|
|
using UnityEngine;
|
|
using TD.Core;
|
|
|
|
namespace TD.Gameplay
|
|
{
|
|
/// <summary>
|
|
/// Per-enemy HP component. Holds replicated HP and is the single point
|
|
/// through which all damage flows, so resistance lookups (Phase 1.5+) can
|
|
/// be added in one place without touching every damage source.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Lives on the enemy prefab root alongside <see cref="EnemyStatus"/> and the
|
|
/// future <c>EnemyMovement</c> component (Phase 1.5/1.6). HP is server-written
|
|
/// and replicated to all clients so health bars can render on any peer.
|
|
///
|
|
/// <b>Death flow (server-only):</b>
|
|
/// <c>TakeDamage</c> clamps HP to 0, fires <see cref="OnDied"/>, then calls
|
|
/// <c>NetworkObject.Despawn</c>. Subscribers must not touch the NetworkObject
|
|
/// after <c>OnDied</c> returns.
|
|
/// </remarks>
|
|
[RequireComponent(typeof(NetworkObject))]
|
|
public class EnemyHealth : NetworkBehaviour
|
|
{
|
|
[SerializeField] private float maxHp = 100f;
|
|
|
|
private readonly NetworkVariable<float> hp = new NetworkVariable<float>(
|
|
0f,
|
|
NetworkVariableReadPermission.Everyone,
|
|
NetworkVariableWritePermission.Server);
|
|
|
|
// ----- Public state -----------------------------------------------
|
|
|
|
public float CurrentHp => hp.Value;
|
|
public float MaxHp => maxHp;
|
|
public bool IsDead => hp.Value <= 0f;
|
|
|
|
// Stub: set by EnemyMovement or spawner in Phase 1.5/1.6.
|
|
// TowerCombat reads this to honour the GroundedOnly tower flag.
|
|
public bool IsFlying => false;
|
|
|
|
// ----- Events -----------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Fired on the server immediately before the enemy NetworkObject is despawned.
|
|
/// <see cref="TD.Combat.TowerCombat"/> subscribes to clear its target reference.
|
|
/// Do not access the NetworkObject after this event returns.
|
|
/// </summary>
|
|
public event System.Action<EnemyHealth> OnDied;
|
|
|
|
// ----- NGO lifecycle ----------------------------------------------
|
|
|
|
public override void OnNetworkSpawn()
|
|
{
|
|
if (IsServer)
|
|
hp.Value = maxHp;
|
|
}
|
|
|
|
// ----- Server API -------------------------------------------------
|
|
|
|
/// <summary>
|
|
/// Applies <paramref name="damage"/> to this enemy. Server-only; no-op on clients.
|
|
/// <paramref name="type"/> is recorded for future resistance/weakness lookups —
|
|
/// all damage is full-value until the resistance table is implemented (Phase 1.5+).
|
|
/// </summary>
|
|
public void TakeDamage(float damage, DamageType type)
|
|
{
|
|
if (!IsServer) return;
|
|
if (IsDead) return;
|
|
|
|
// STUB — resistance table slot:
|
|
// float modified = ResistanceTable.Apply(damage, type, this);
|
|
float modified = damage;
|
|
|
|
hp.Value = Mathf.Max(0f, hp.Value - modified);
|
|
|
|
if (hp.Value <= 0f)
|
|
HandleDeath();
|
|
}
|
|
|
|
// ----- Private ----------------------------------------------------
|
|
|
|
private void HandleDeath()
|
|
{
|
|
OnDied?.Invoke(this);
|
|
NetworkObject.Despawn();
|
|
}
|
|
}
|
|
}
|