using System; using System.Collections.Generic; using UnityEngine; namespace Game.Scripts.Runtime.Data { [CreateAssetMenu(menuName = "Game/Spawning/Spawn Table", fileName = "SpawnTable")] public class SpawnTable : ScriptableObject { [Serializable] public class Entry { public EnemyDefinition Def; [Min(0)] public int Weight = 1; public int MinWave = 0; public int MaxWave = int.MaxValue; [Tooltip("Optional cap per wave. Values <= 0 mean unlimited.")] public int Cap = -1; public bool IsWaveValid(int wave) => wave >= MinWave && wave <= MaxWave; public bool IsUnderCap(int currentCount) { if (Cap <= 0) { return true; } return currentCount < Cap; } } [SerializeField] private List entries = new(); private readonly List _eligibleBuffer = new(); public IReadOnlyList Entries => entries; public EnemyDefinition Pick(int wave, System.Random rng) { if (entries == null || entries.Count == 0) { return null; } _eligibleBuffer.Clear(); foreach (var entry in entries) { if (entry?.Def == null || entry.Weight <= 0) { continue; } if (entry.IsWaveValid(wave)) { _eligibleBuffer.Add(entry); } } if (_eligibleBuffer.Count == 0) { return null; } var totalWeight = 0; foreach (var entry in _eligibleBuffer) { totalWeight += Mathf.Max(1, entry.Weight); } var roll = rng.Next(totalWeight); foreach (var entry in _eligibleBuffer) { var weight = Mathf.Max(1, entry.Weight); if (roll < weight) { return entry.Def; } roll -= weight; } return _eligibleBuffer[^1].Def; } public IEnumerable EnumerateEligibleEntries(int wave) { if (entries == null) { yield break; } foreach (var entry in entries) { if (entry?.Def == null || entry.Weight <= 0) { continue; } if (entry.IsWaveValid(wave)) { yield return entry; } } } } }