ProjectDDD/Assets/_DDD/_Scripts/RestaurantState/Customer/RestaurantCustomerStateSo.cs
2025-08-12 20:46:30 +09:00

137 lines
5.4 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Sirenix.OdinInspector;
using UnityEngine;
namespace DDD
{
[CreateAssetMenu(fileName = "RestaurantCustomerStateSo", menuName = "RestaurantState/RestaurantCustomerStateSo")]
public class RestaurantCustomerStateSo : ScriptableObject, IGameFlowHandler
{
[Title("스폰 제어")]
[Tooltip("플로우 시작 후 첫 손님이 등장하기까지 대기 시간(초)")]
[SerializeField] private float _firstSpawnDelaySeconds = 5f;
[SerializeField] private Vector3 _spawnPoint = new(5f, 0f, 4f);
[Title("디버그")]
[SerializeField] private SpawnSchedule _spawnSchedule;
private GameStateSo _gameStateSo;
private LevelDataSo _levelDataSo;
private CustomerDataSo _customerDataSo;
private CustomerPoolDataSo _customerPoolDataSo;
private ICustomerFactory _iCustomerFactory;
private CancellationTokenSource _spawnLoopCancellationTokenSource;
public async Task OnReadyNewFlow(GameFlowState newFlowState)
{
if (newFlowState == GameFlowState.RunRestaurant)
{
await InitializeRunRestaurant();
}
}
public Task OnExitCurrentFlow(GameFlowState exitingFlowState)
{
if (exitingFlowState == GameFlowState.RunRestaurant)
{
_spawnLoopCancellationTokenSource?.Cancel();
_spawnLoopCancellationTokenSource?.Dispose();
_spawnLoopCancellationTokenSource = null;
}
return Task.CompletedTask;
}
private async Task InitializeRunRestaurant()
{
_gameStateSo = await AssetManager.LoadAsset<GameStateSo>(DataConstants.GameStateSo);
Debug.Assert(_gameStateSo != null, "_gameStateSo is null");
_iCustomerFactory = new CustomerFactory();
var currentGameLevel = _gameStateSo.GetCurrentLevel();
if (_levelDataSo == null)
{
_levelDataSo = DataManager.Instance.GetDataSo<LevelDataSo>();
}
if (_customerDataSo == null)
{
_customerDataSo = DataManager.Instance.GetDataSo<CustomerDataSo>();
}
if (_customerPoolDataSo == null)
{
_customerPoolDataSo = DataManager.Instance.GetDataSo<CustomerPoolDataSo>();
}
var currentLevelData = _levelDataSo.GetDataList().FirstOrDefault(data => data.Level == currentGameLevel);
Debug.Assert(currentLevelData != null, "currentLevelData is null");
var normalPool = _customerPoolDataSo.GetDataById(currentLevelData.CustomerPool);
var specialPool = _customerPoolDataSo.GetDataById(currentLevelData.SpecialCustomerPool);
_spawnLoopCancellationTokenSource?.Cancel();
_spawnLoopCancellationTokenSource = new CancellationTokenSource();
_ = RunSpawnLoopAsync(currentLevelData, normalPool, specialPool, _spawnLoopCancellationTokenSource.Token);
}
private async Task RunSpawnLoopAsync(LevelData levelData, CustomerPoolData normalPool, CustomerPoolData specialPool, CancellationToken token)
{
if (_firstSpawnDelaySeconds > 0)
await Task.Delay(TimeSpan.FromSeconds(_firstSpawnDelaySeconds), 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 (_spawnSchedule.TryDequeue(out var customerId) == false) break;
if (_customerDataSo.TryGetDataById(customerId, out var customerData))
{
var rotation = Quaternion.identity;
_ = _iCustomerFactory.CreateAsync(new CustomerSpawnArgs
{
CustomerDataId = customerId,
CustomerData = customerData,
Position = _spawnPoint,
Rotation = rotation,
Parent = null
});
}
await Task.Delay(TimeSpan.FromSeconds(wait), token);
}
}
private ISpawnScheduleBuilder CreateBuilder(SpawnType type)
{
return type switch
{
SpawnType.Random => new RandomSpawnScheduleBuilder(),
SpawnType.Regular => new RegularSpawnScheduleBuilder(),
_ => new RandomSpawnScheduleBuilder()
};
}
}
}