// Assets/_Project/Scripts/Gameplay/BuilderSpells/BuilderSpellPool.cs using System; using UnityEngine; using TD.Core; namespace TD.Gameplay.BuilderSpells { /// /// Scene singleton holding every available this match, /// authored as a flat array in the inspector for convenience. Internally it builds a /// fixed-size table indexed by so lookups are a single array /// access, not a scan. /// /// /// Plain MonoBehaviour: identical on every peer (same assets), so there is nothing to sync. /// The server reads it to resolve casts; clients read it to resolve and to render the cast hotbar. Mirrors /// exactly. /// public class BuilderSpellPool : MonoBehaviour { public static BuilderSpellPool Instance { get; private set; } [Tooltip("Every BuilderSpellDefinition asset available this match. One entry per " + "BuilderSpellKind — order doesn't matter, Kind on the asset itself decides " + "its slot.")] [SerializeField] private BuilderSpellDefinition[] spells; // Fixed-size, enum-indexed lookup built once in Awake. Sized to the enum's entry count, // not authored count, so an out-of-range Kind is a compile-time impossibility rather // than a bounds check we'd otherwise need on every Get(). private BuilderSpellDefinition[] byKind; private void Awake() { if (Instance != null && Instance != this) { Debug.LogError("[BuilderSpellPool] Multiple instances detected. Only one per scene."); return; } Instance = this; int kindCount = Enum.GetValues(typeof(BuilderSpellKind)).Length; byKind = new BuilderSpellDefinition[kindCount]; if (spells == null) return; for (int i = 0; i < spells.Length; i++) { var def = spells[i]; if (def == null) continue; byKind[(int)def.Kind] = def; } } private void OnDestroy() { if (Instance == this) Instance = null; } /// Returns the spell asset for , or null if none is /// authored in this pool. public BuilderSpellDefinition Get(BuilderSpellKind kind) { int i = (int)kind; return (byKind != null && i >= 0 && i < byKind.Length) ? byKind[i] : null; } } }