ProjectDDD/Assets/_DDD/_Scripts/RestaurantController/Conrtollers/RestaurantRunController.cs

141 lines
5.5 KiB
C#

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<LevelDataSo>();
_customerDataSo ??= DataManager.Instance.GetDataSo<CustomerDataSo>();
_customerPoolDataSo ??= DataManager.Instance.GetDataSo<CustomerPoolDataSo>();
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<string>) (normalPool?.ValidCustomers) ?? Array.Empty<string>(),
SpecialIds = (IReadOnlyList<string>) (specialPool?.ValidCustomers) ?? Array.Empty<string>(),
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()
};
}
}
}