first pass at enemy upgrades
This commit is contained in:
parent
a26d0acbfb
commit
8714885efa
13 changed files with 351 additions and 0 deletions
84
Assets/_Project/Scripts/Gameplay/EnemyAbility.cs
Normal file
84
Assets/_Project/Scripts/Gameplay/EnemyAbility.cs
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
// Assets/_Project/Scripts/Gameplay/EnemyAbility.cs
|
||||
using Unity.Netcode;
|
||||
using UnityEngine;
|
||||
using TD.Core;
|
||||
using TD.Gameplay.EnemyAbilities;
|
||||
|
||||
namespace TD.Gameplay
|
||||
{
|
||||
/// <summary>
|
||||
/// Per-enemy ability slot. Holds the <see cref="EnemyAbilityDefinition"/> rolled for this
|
||||
/// instance (if any) and drives its server-only hooks.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <b>Initialization:</b> Call <see cref="InitializeServer"/> on the server immediately after
|
||||
/// <c>Instantiate</c> and before <c>NetworkObject.Spawn()</c>, following the same pattern as
|
||||
/// <c>EnemyHealth.InitializeServer</c> / <c>EnemyMovement.InitializeServer</c>.
|
||||
///
|
||||
/// <b>Optional component:</b> Not required by <c>EnemyHealth</c> or <c>EnemyMovement</c> —
|
||||
/// <c>WaveManager.SpawnEnemy</c> only rolls and assigns an ability when this component is
|
||||
/// present on the prefab, so existing enemy prefabs are unaffected until it's added to them.
|
||||
///
|
||||
/// <b>On-death hook is not event-driven:</b> <c>WaveManager.HandleEnemyKilled</c> calls
|
||||
/// <see cref="EnemyAbilityDefinition.ServerOnDeath"/> directly (via <see cref="Definition"/>)
|
||||
/// rather than this component subscribing to <c>EnemyHealth.OnDied</c> itself — that keeps a
|
||||
/// split-spawn's <c>activeEnemyCount</c> increment ordered before the triggering kill's
|
||||
/// decrement, regardless of NetworkBehaviour subscription order.
|
||||
/// </remarks>
|
||||
[RequireComponent(typeof(NetworkObject))]
|
||||
public class EnemyAbility : NetworkBehaviour
|
||||
{
|
||||
private readonly NetworkVariable<byte> kind = new NetworkVariable<byte>(
|
||||
(byte)EnemyAbilityKind.None,
|
||||
NetworkVariableReadPermission.Everyone,
|
||||
NetworkVariableWritePermission.Server);
|
||||
|
||||
// ----- Pre-spawn init (server-local) ----------------------------------
|
||||
|
||||
private EnemyAbilityDefinition pendingDefinition;
|
||||
private bool hasPendingInit;
|
||||
|
||||
// ----- Public state -----------------------------------------------------
|
||||
|
||||
/// <summary>The ability rolled for this enemy, or null if none was rolled.</summary>
|
||||
public EnemyAbilityDefinition Definition { get; private set; }
|
||||
|
||||
/// <summary>Replicated kind of the rolled ability. <see cref="EnemyAbilityKind.None"/> if
|
||||
/// none was rolled.</summary>
|
||||
public EnemyAbilityKind Kind => (EnemyAbilityKind)kind.Value;
|
||||
|
||||
// ----- Server-only pre-spawn init -------------------------------------
|
||||
|
||||
/// <summary>
|
||||
/// Called by <c>WaveManager</c> on the server after <c>Instantiate</c> and before
|
||||
/// <c>NetworkObject.Spawn()</c>. <paramref name="definition"/> may be null, meaning no
|
||||
/// ability was rolled for this enemy.
|
||||
/// </summary>
|
||||
public void InitializeServer(EnemyAbilityDefinition definition)
|
||||
{
|
||||
pendingDefinition = definition;
|
||||
hasPendingInit = true;
|
||||
}
|
||||
|
||||
// ----- NGO lifecycle --------------------------------------------------
|
||||
|
||||
public override void OnNetworkSpawn()
|
||||
{
|
||||
if (!IsServer || !hasPendingInit) return;
|
||||
|
||||
Definition = pendingDefinition;
|
||||
kind.Value = (byte)(Definition != null ? Definition.Kind : EnemyAbilityKind.None);
|
||||
hasPendingInit = false;
|
||||
|
||||
Definition?.ServerOnSpawn(this);
|
||||
}
|
||||
|
||||
// ----- Server tick ------------------------------------------------
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (!IsServer || Definition == null) return;
|
||||
Definition.ServerTick(this, Time.deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue