using System; using System.Collections.Generic; using System.Linq; using System.Threading; using System.Threading.Tasks; using UnityEngine; namespace DDD { public class RestaurantRunController : FlowController { private RestaurantCustomerState _restaurantCustomerStateSo; private RestaurantRunState _restaurantRunStateSo; private CancellationTokenSource _cts; // Runtime dependencies used by the execution logic private LevelDataSo _levelDataSo; private CustomerDataSo _customerDataSo; private CustomerPoolDataSo _customerPoolDataSo; private ICustomerFactory _iCustomerFactory; private ISpawnPointProvider _spawnPointProvider; // Debug-only: last built schedule (kept in controller to keep State pure) private SpawnSchedule _spawnSchedule; public override Task InitializeController() { _restaurantCustomerStateSo = RestaurantState.Instance.CustomerState; _restaurantRunStateSo = RestaurantState.Instance.RunState; return Task.CompletedTask; } public override Task InitializeState() { _spawnPointProvider ??= new SpawnPointProvider(); _restaurantRunStateSo.InitializeSpawnPoint(_spawnPointProvider.GetSpawnPoint()); return Task.CompletedTask; } public override async Task OnReadyNewFlow(GameFlowState newFlowState) { if (newFlowState == GameFlowState.RunRestaurant) { _cts?.Cancel(); _cts?.Dispose(); _cts = new CancellationTokenSource(); await StartSpawnLoopAsync(_cts.Token); } } public override Task OnExitCurrentFlow(GameFlowState exitingFlowState) { if (exitingFlowState == GameFlowState.RunRestaurant) { _cts?.Cancel(); _cts?.Dispose(); _cts = null; } return Task.CompletedTask; } private async Task StartSpawnLoopAsync(CancellationToken token) { _iCustomerFactory ??= new CustomerFactory(); var currentGameLevel = GameState.Instance.LevelState.Level; _levelDataSo ??= DataManager.Instance.GetDataSo(); _customerDataSo ??= DataManager.Instance.GetDataSo(); _customerPoolDataSo ??= DataManager.Instance.GetDataSo(); var currentLevelData = _levelDataSo.GetDataList().FirstOrDefault(data => data.Level == currentGameLevel); Debug.Assert(currentLevelData != null, "currentLevelData is null"); if (currentLevelData == null) return; // 안전 가드 var normalPool = _customerPoolDataSo.GetDataById(currentLevelData.CustomerPool); var specialPool = _customerPoolDataSo.GetDataById(currentLevelData.SpecialCustomerPool); await RunSpawnLoopAsync(currentLevelData, normalPool, specialPool, token); } private async Task RunSpawnLoopAsync(LevelData levelData, CustomerPoolData normalPool, CustomerPoolData specialPool, CancellationToken token) { var runData = RestaurantData.Instance? RestaurantData.Instance.RunData : null; float firstDelay = Mathf.Max(0f, runData? runData.FirstSpawnDelaySeconds : 5f); if (firstDelay > 0) { await Awaitable.WaitForSecondsAsync(firstDelay, token); } var scheduleBuilder = CreateBuilder(levelData.SpawnType); int randomSeed = Environment.TickCount; SpawnSchedule MakeSchedule() => scheduleBuilder.Build(new SpawnScheduleBuildArgs { NormalIds = (IReadOnlyList) (normalPool?.ValidCustomers) ?? Array.Empty(), SpecialIds = (IReadOnlyList) (specialPool?.ValidCustomers) ?? Array.Empty(), NormalQuota = Math.Max(0, normalPool?.CustomerLimitCount ?? 0), SpecialQuota = Math.Max(0, specialPool?.CustomerLimitCount ?? 0), Seed = ++randomSeed }); _spawnSchedule = MakeSchedule(); float wait = Mathf.Max(0.1f, levelData.CustomerRespawnTime); while (token.IsCancellationRequested == false) { if (Application.isPlaying == false) { break; } if (_spawnSchedule.TryDequeue(out var customerId) == false) break; if (_customerDataSo.TryGetDataById(customerId, out var customerData)) { var rotation = Quaternion.identity; _ = _iCustomerFactory.CreateAsync(new CustomerSpawnArgs { CustomerData = customerData, Position = _restaurantRunStateSo.SpawnPoint, Rotation = rotation, Parent = null }); } await Task.Delay(TimeSpan.FromSeconds(wait), token); } } private static ISpawnScheduleBuilder CreateBuilder(SpawnType type) { return type switch { SpawnType.Random => new RandomSpawnScheduleBuilder(), SpawnType.Regular => new RegularSpawnScheduleBuilder(), _ => new RandomSpawnScheduleBuilder() }; } } }