// Assets/_Project/Scripts/Gameplay/EnemyUpgrades/EnemyUpgradePool.cs
using System.Collections.Generic;
using UnityEngine;
namespace TD.Gameplay.EnemyUpgrades
{
///
/// Scene singleton holding every that can appear anywhere in
/// this match. The array index is the option's EnemyUpgradeOptionId — the stable
/// identifier used to replicate vote offers, ballots, and the upgrades recorded on wave slots.
///
///
/// Why a flat pool and per-phase groups. The pool supplies network identity: one
/// array, one index per card, identical on every peer. PhaseDefinition.EnemyUpgradeGroups
/// supplies gating: which of those cards a given phase may offer. Folding gating into the pool
/// would make ids phase-relative and therefore unstable across a phase change, which is
/// exactly when replicated slot upgrades are being cleared and rewritten.
///
/// Plain MonoBehaviour — identical assets on every peer, so there is nothing to sync.
/// The server reads it to generate and resolve votes; clients read it to render vote cards
/// from replicated ids.
///
public class EnemyUpgradePool : MonoBehaviour
{
public static EnemyUpgradePool Instance { get; private set; }
[Tooltip("Every EnemyUpgradeOption asset that can appear this match. Array index is the " +
"id used over the network — reordering mid-development is fine, but not between " +
"a host and its clients.")]
[SerializeField] private EnemyUpgradeOption[] options;
private void Awake()
{
if (Instance != null && Instance != this)
{
Debug.LogError("[EnemyUpgradePool] Multiple instances detected. Only one per scene.");
return;
}
Instance = this;
}
private void OnDestroy()
{
if (Instance == this) Instance = null;
}
/// Number of options in the pool.
public int Count => options?.Length ?? 0;
/// Returns the option at , or null if out of range.
public EnemyUpgradeOption Get(int id)
=> (options != null && id >= 0 && id < options.Length) ? options[id] : null;
/// Resolves an option asset back to its pool id, or -1 if it isn't in the pool.
public int GetId(EnemyUpgradeOption option)
{
if (options == null || option == null) return -1;
for (int i = 0; i < options.Length; i++)
if (options[i] == option) return i;
return -1;
}
///
/// Collects the pool ids of every card the given phase groups allow. Cards missing from
/// the pool are reported once and skipped — an authored group referencing an unpooled
/// card is a wiring mistake that would otherwise silently shrink the vote.
///
public void CollectAllowedIds(EnemyUpgradeGroup[] phaseGroups,
List entryScratch,
List intoIds,
List intoWeights)
{
intoIds.Clear();
intoWeights.Clear();
EnemyUpgradeGroup.CollectCandidates(phaseGroups, entryScratch);
foreach (var entry in entryScratch)
{
int id = GetId(entry.Option);
if (id < 0)
{
Debug.LogWarning($"[EnemyUpgradePool] '{entry.Option.name}' is referenced by a " +
$"phase's upgrade groups but is not in the pool — it can " +
$"never be offered. Add it to the pool's options array.");
continue;
}
intoIds.Add(id);
intoWeights.Add(entry.Weight);
}
}
}
}