using System; using System.Collections.Generic; using SepCore.DataTable; using SepCore.Definition; using UnityEngine; namespace SepCore.EnemyManager { public class EnemySpawnScheduler { private const float MinSpawnRateScale = 0.1f; private float _elapsedTime; private float[] _baseIntervals; private float[] _currentIntervals; private EnemyType[] _enemyTypes; private int[] _spawnCounts; private float[] _nextSpawnTimes; private float _spawnRateScale = 1f; public float SpawnRateScale => _spawnRateScale; public float ElapsedTime => _elapsedTime; public int WaveCount => _enemyTypes?.Length ?? 0; public void Init(DRLevel level) { _baseIntervals = (float[])level.Intervals.Clone(); _currentIntervals = (float[])_baseIntervals.Clone(); _enemyTypes = level.EntityTypes; _spawnCounts = level.EntityCounts; SetSpawnRateScale(_spawnRateScale); } public List Tick(float deltaTime) { _elapsedTime += deltaTime; var requests = new List(); if (_nextSpawnTimes == null) return requests; for (int i = 0; i < _nextSpawnTimes.Length; i++) { if (_elapsedTime < _nextSpawnTimes[i]) continue; requests.Add(new SpawnRequest { EnemyType = _enemyTypes[i], Count = _spawnCounts[i], WaveIndex = i }); _nextSpawnTimes[i] += _currentIntervals[i]; } return requests; } public void SetSpawnRateScale(float scale) { float newScale = Mathf.Max(MinSpawnRateScale, scale); if (_baseIntervals == null || _baseIntervals.Length == 0) { _spawnRateScale = newScale; return; } bool hasRuntimeState = _nextSpawnTimes != null && _currentIntervals != null && _nextSpawnTimes.Length == _baseIntervals.Length && _currentIntervals.Length == _baseIntervals.Length; float oldScale = _spawnRateScale; _spawnRateScale = newScale; if (!hasRuntimeState) { for (int i = 0; i < _baseIntervals.Length; i++) { _currentIntervals[i] = GetScaledInterval(_baseIntervals[i], _spawnRateScale); } _nextSpawnTimes = (float[])_currentIntervals.Clone(); return; } for (int i = 0; i < _baseIntervals.Length; i++) { float oldInterval = GetScaledInterval(_baseIntervals[i], oldScale); float newInterval = GetScaledInterval(_baseIntervals[i], _spawnRateScale); float remainTime = Mathf.Max(0f, _nextSpawnTimes[i] - _elapsedTime); float remainRatio = oldInterval > Mathf.Epsilon ? Mathf.Clamp01(remainTime / oldInterval) : 0f; _currentIntervals[i] = newInterval; _nextSpawnTimes[i] = _elapsedTime + newInterval * remainRatio; } } public void Reset() { _elapsedTime = 0; _baseIntervals = null; _currentIntervals = null; _enemyTypes = null; _spawnCounts = null; _nextSpawnTimes = null; _spawnRateScale = 1f; } private static float GetScaledInterval(float baseInterval, float scale) { float safeScale = Mathf.Max(MinSpawnRateScale, scale); return baseInterval / safeScale; } } public struct SpawnRequest { public EnemyType EnemyType; public int Count; public int WaveIndex; } }