Adding major combat changes and features

This commit is contained in:
Matt F 2026-05-12 21:31:10 -07:00
parent abcefcd7f1
commit 42ee0bf65d
28 changed files with 1653 additions and 46 deletions

View file

@ -0,0 +1,90 @@
// 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();
}
}
}