// Assets/_Project/Scripts/Gameplay/BuilderEffects/BuilderEffectPool.cs using System; using UnityEngine; using TD.Core; namespace TD.Gameplay.BuilderEffects { /// /// 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 grants; clients read it to render an "active effects" list. /// public class BuilderEffectPool : MonoBehaviour { public static BuilderEffectPool Instance { get; private set; } [Tooltip("Every BuilderEffectDefinition asset available this match. One entry per " + "BuilderEffectKind — order doesn't matter, Kind on the asset itself decides " + "its slot.")] [SerializeField] private BuilderEffectDefinition[] effects; // 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 BuilderEffectDefinition[] byKind; private void Awake() { if (Instance != null && Instance != this) { Debug.LogError("[BuilderEffectPool] Multiple instances detected. Only one per scene."); return; } Instance = this; int kindCount = Enum.GetValues(typeof(BuilderEffectKind)).Length; byKind = new BuilderEffectDefinition[kindCount]; if (effects == null) return; for (int i = 0; i < effects.Length; i++) { var def = effects[i]; if (def == null) continue; byKind[(int)def.Kind] = def; } } private void OnDestroy() { if (Instance == this) Instance = null; } /// Returns the effect asset for , or null if none is /// authored in this pool. public BuilderEffectDefinition Get(BuilderEffectKind kind) { int i = (int)kind; return (byKind != null && i >= 0 && i < byKind.Length) ? byKind[i] : null; } } }