using System.Collections; using System.Collections.Generic; using System.Linq; using DDD.Npcs.Crews; using DDD.Npcs.Crews.Bartender; using DDD.Npcs.Crews.Cleaner; using DDD.Npcs.Crews.Server; using DDD.Npcs.Customers; using DDD.Utility; using Sirenix.OdinInspector; using UnityEngine; namespace DDD.Tycoons { public class CrewController : MonoBehaviour { [Title("Crew 프리팹")] [SerializeField] private CleanerCrew _cleanerCrewPrefab; [SerializeField] private ServerCrew _servingCrewPrefab; [SerializeField] private BartenderCrew _bartenderCrewPrefab; [Title("Crew 생성 변수")] [SerializeField] private Transform _cleanerCrewSpawnTransform; [SerializeField] private Transform _servingCrewSpawnTransform; [SerializeField] private List _bartenderCrewSpawnTransforms = new(5); [Title("벨 연출")] [SerializeField] private float _randomRadius = 5f; [SerializeField] private float _crewMinDistance = 0.5f; [field: Title("실시간 데이터")] [field: SerializeField] public List Crews { get; private set; } [field: SerializeField] public List CleanerCrews { get; private set; } [field: SerializeField] public List ServerCrews { get; private set; } [field: SerializeField] public List BartenderCrews { get; private set; } private Coroutine _ringedBellInstance; private void Start() { EventManager.OnCreateCleanerCrew += CreateCleanerCrew; EventManager.OnCreateServerCrew += CreateServerCrew; EventManager.OnCreateBartenderCrew += CreateBartenderCrew; EventManager.OnOrderedCocktail += FindBartenderCrew; EventManager.OnRingedBell += RingedBell; } private void OnDestroy() { EventManager.OnCreateCleanerCrew -= CreateCleanerCrew; EventManager.OnCreateServerCrew -= CreateServerCrew; EventManager.OnCreateBartenderCrew -= CreateBartenderCrew; EventManager.OnOrderedCocktail -= FindBartenderCrew; EventManager.OnRingedBell -= RingedBell; } public CleanerCrew CreateCleanerCrew() { var newCrew = Instantiate(_cleanerCrewPrefab, _cleanerCrewSpawnTransform.position, _cleanerCrewPrefab.transform.rotation); Utils.RegisterList(CleanerCrews, newCrew); Utils.RegisterList(Crews, newCrew); newCrew.Initialize(); EventManager.InvokeUpdateCrewUi(BartenderCrews.Count, ServerCrews.Count, CleanerCrews.Count); // if (!ES3.Load(SaveData.TutorialF, false)) // { // EventManager.InvokeTutorial(TutorialName.TutorialF); // ES3.Save(SaveData.TutorialF, true); // } return newCrew; } public ServerCrew CreateServerCrew() { var newCrew = Instantiate(_servingCrewPrefab, _servingCrewSpawnTransform.position, _servingCrewPrefab.transform.rotation); Utils.RegisterList(ServerCrews, newCrew); Utils.RegisterList(Crews, newCrew); newCrew.Initialize(); EventManager.InvokeUpdateCrewUi(BartenderCrews.Count, ServerCrews.Count, CleanerCrews.Count); // if (!ES3.Load(SaveData.TutorialD, false)) // { // EventManager.InvokeTutorial(TutorialName.TutorialD); // ES3.Save(SaveData.TutorialD, true); // } return newCrew; } public BartenderCrew CreateBartenderCrew() { int randomIndex = Random.Range(0, _bartenderCrewSpawnTransforms.Count); Transform spawnTransform = _bartenderCrewSpawnTransforms[randomIndex]; _bartenderCrewSpawnTransforms.Remove(spawnTransform); var newCrew = Instantiate(_bartenderCrewPrefab, spawnTransform.position, _bartenderCrewPrefab.transform.rotation); Utils.RegisterList(BartenderCrews, newCrew); Utils.RegisterList(Crews, newCrew); newCrew.SetBartenderTable(spawnTransform.GetComponentInParent()); newCrew.Initialize(); EventManager.InvokeUpdateCrewUi(BartenderCrews.Count, ServerCrews.Count, CleanerCrews.Count); // if (!ES3.Load(SaveData.TutorialE, false)) // { // EventManager.InvokeTutorial(TutorialName.TutorialE); // ES3.Save(SaveData.TutorialE, true); // } return newCrew; } public T FindClosestAvailableCrew(Vector3 position, List crews) where T : Crew { if (crews == null || crews.Count == 0) return null; var availableCrews = crews.Where(crew => !crew.IsOnMission).ToList(); if (availableCrews.Count == 0) return null; return availableCrews .OrderBy(crew => Vector3.Distance(crew.transform.position, position)) .FirstOrDefault(); } public IEnumerator FindClosestCrewCoroutine(Vector3 position, List crews, System.Action onCrewFoundEvent) where T : Crew { var waitTime = new WaitForSeconds(2f); while (true) { var closestCrew = FindClosestAvailableCrew(position, crews); if (closestCrew != null) { onCrewFoundEvent?.Invoke(closestCrew); yield break; } yield return waitTime; } } private void FindBartenderCrew(Customer orderedCustomer) { StartCoroutine(FindBartenderCrewCoroutine(orderedCustomer)); } private IEnumerator FindBartenderCrewCoroutine(Customer orderedCustomer) { var waitTime = new WaitForSeconds(2f); var tycoonManager = TycoonManager.Instance; var tycoonIngredientController = tycoonManager.TycoonIngredientController; var orderedCocktailData = orderedCustomer.OrderedCocktailData; yield return new WaitForSeconds(0.5f); while (true) { var orderedCount = tycoonManager.CustomerController.OrderedCocktailCount(orderedCocktailData.Idx); if (!tycoonManager.TycoonStageController.CanMakingCocktail(orderedCocktailData.Idx, orderedCount)) { yield return waitTime; continue; } foreach (var element in BartenderCrews) { // if (element.MyBartenderTable.CurrentPickupItem != null || element.IsOnMission) continue; var orderedCocktailIngredients = orderedCocktailData.ValidIngredients; if (!tycoonIngredientController.IsMakingCocktail(orderedCocktailIngredients)) break; if (orderedCustomer.IsReceivedItem || orderedCustomer.IsMatchedServer) yield break; element.SetOrderedCustomer(orderedCustomer); tycoonIngredientController.ConsumeMakingCocktailIngredient(orderedCocktailIngredients); yield break; } yield return waitTime; } } private void RingedBell(Vector3 bellPosition) { // bellPosition : 중심 위치 // randomRadius : 중심 위치로부터 최대 반지름 // crew 끼리 서로 겹치지 않는 거리를 나타내는 변수명 필요 List assignedPositions = new List(); foreach (Crew crew in Crews) { Vector3 movePosition = bellPosition; bool validPositionFound = false; int attempt = 0; while (!validPositionFound && attempt < 1000) { // XZ 평면에서 랜덤한 점 생성 (Y는 고정) Vector2 randomPoint = Random.insideUnitCircle * _randomRadius; movePosition = bellPosition + new Vector3(randomPoint.x, 0, randomPoint.y); if (!crew.AIMovement.IsPositionMovable(movePosition)) continue; // 이전에 할당된 모든 위치와의 거리를 검사 validPositionFound = true; foreach (Vector3 pos in assignedPositions) { if (Vector3.Distance(pos, movePosition) < _crewMinDistance) { validPositionFound = false; break; } } attempt++; } assignedPositions.Add(movePosition); crew.RingedBell(movePosition); } Utils.StartUniqueCoroutine(this, ref _ringedBellInstance, RingedBellCoroutine()); } private IEnumerator RingedBellCoroutine() { WaitForSeconds waitTime = new WaitForSeconds(1f); while (Crews.Any(crew => !crew.HasReachedBell)) { yield return waitTime; } yield return new WaitForSeconds(10f); foreach (Crew crew in Crews) { crew.EndBell(); } EventManager.InvokeEndedBell(); } } }