Files
Project-M/Assets/_Project/Scripts/Simulation/Combat/AbilityDatabaseBlob.cs
T
kronic 36950ca21d LANTERN P1 Group B: 5-Spark systems — Aoe/Hitscan dispatch + reel/zone/decoy
Aoe/Hitscan archetype dispatch in AbilityFireSystem (both-worlds cooldown;
server-only ecb.Instantiate — the client-never-instantiates invariant; ZoneEffect
vs DecoyTag chosen by prefab membership). Harpooner REEL as a homing state
(ReelState + ReelSystem re-aim the hit Husk's KnockbackState toward the caster's
live position each tick; EnemyAISystem stays sole Position writer), stamped by
ProjectileDamageSystem on a Reel-flag hit. ZonePulseSystem (enemy-only periodic
AoE, caster attribution, no friendly fire, Vortex pull, born-0 lazy-stamp).
Decoy aggro in EnemyAISystem + DecoySystem despawn. AbilityDefBlob gains
EffectFlags (Reel) + DurationTicks. +3 ZonePulseSystem EditMode tests (497 green).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 00:00:41 -07:00

89 lines
3.7 KiB
C#

using Unity.Collections;
using Unity.Entities;
namespace ProjectM.Simulation
{
/// <summary>Ability KIND for the AbilityFireSystem dispatch (MC-4 spike for MC-6). Backed by <c>byte</c> so the
/// baked blob stores a byte (the Burst cross-assembly enum rule); the runtime switch compares the byte value.</summary>
public enum AbilityArchetype : byte
{
Projectile = 0, // today's path: spawn a projectile ghost
Hitscan = 1, // MC-6
Cone = 2, // MC-6 (melee-style cone via the ability system)
Aoe = 3, // MC-6
Movement = 4, // LANTERN: NOT dispatched by AbilityFireSystem (falls through); a sibling predicted system (BlinkSystem) drives it
}
/// <summary>One authored ability definition, baked immutable into the AbilityDatabase blob.</summary>
public struct AbilityDefBlob
{
public byte Id; // AbilityId
public byte Archetype; // AbilityArchetype (0 = Projectile); read at dispatch in AbilityFireSystem (MC-6 spike)
public float Damage;
public float ProjectileSpeed;
public float Range;
public float AutoTargetRange;
public float AutoTargetConeRadians;
public int CooldownTicks;
public byte EffectFlags; // ProjectileEffectFlag bits stamped at spawn (the Reel bit ⇒ HookPull homes the hit target to the caster). Config, not replicated.
public int DurationTicks; // Aoe/zone + decoy lifetime, ticks (0 = system default). Reel leash is a system const.
public FixedString64Bytes Name;
}
/// <summary>One authored character-stats definition, baked immutable into the AbilityDatabase blob.</summary>
public struct CharacterStatsBlob
{
public byte Id; // CharacterId
public float MoveSpeed;
public float TurnRateRadiansPerSec;
public float MaxHealth;
public FixedString64Bytes Name;
}
/// <summary>
/// Immutable designer-authored definition database, baked from ScriptableObjects to a blob asset and
/// shared by every entity (Burst-fast, zero per-instance cost). Looked up by stable id. Entity/prefab
/// references are NOT stored here (blobs don't remap entity refs) - see AbilityPrefabElement.
///
/// NOTE: the lookups are intentionally NOT 'readonly' methods. A readonly struct method forces a
/// defensive copy of a field when calling a non-readonly member on it; copying a BlobArray breaks its
/// relative-offset pointer, so the array would read as empty. Plain (non-readonly) methods access the
/// BlobArray in place. Always reach these through 'ref blob.Value'.
/// </summary>
public struct AbilityDatabaseBlob
{
public BlobArray<AbilityDefBlob> Abilities;
public BlobArray<CharacterStatsBlob> Characters;
/// <summary>Linear lookup by ability id (the array is tiny). Returns false if not present.</summary>
public bool TryGetAbility(byte id, out AbilityDefBlob def)
{
for (int i = 0; i < Abilities.Length; i++)
{
if (Abilities[i].Id == id)
{
def = Abilities[i];
return true;
}
}
def = default;
return false;
}
/// <summary>Linear lookup by character id (the array is tiny). Returns false if not present.</summary>
public bool TryGetCharacter(byte id, out CharacterStatsBlob def)
{
for (int i = 0; i < Characters.Length; i++)
{
if (Characters[i].Id == id)
{
def = Characters[i];
return true;
}
}
def = default;
return false;
}
}
}