CapersProject/Assets/02.Scripts/BlueWater/Character/Npc/Customer/Customer.cs
2025-02-18 06:47:56 +09:00

706 lines
23 KiB
C#

using System;
using System.Collections.Generic;
using BehaviorDesigner.Runtime;
using DDD.Audios;
using DDD.Enemies;
using DDD.Interfaces;
using DDD.Items;
using DDD.Npcs.Crews;
using DDD.Npcs.Crews.Server;
using DDD.Players;
using DDD.ScriptableObjects;
using DDD.Tycoons;
using DDD.Uis;
using DDD.Uis.Tycoon;
using Pathfinding;
using PixelCrushers.DialogueSystem;
using Sirenix.OdinInspector;
using UnityEngine;
using Random = UnityEngine.Random;
namespace DDD.Npcs.Customers
{
public static class CustomerSpineAnimation
{
public const string Idle = "Idle";
public const string Walk = "Run";
public const string Happy = "HappyIdle";
public const string HappyRun = "HappyRun";
public const string Upset = "Upset";
public const string UpsetRun = "UpsetRun";
public const string Vomiting = "Vomiting";
public const string VomitingForm = "VomitingForm";
public const string VomitingIdle = "VomitingIdle";
public const string VomitingRun = "VomitingRun";
}
public static class CatSpineAnimation
{
public const string Idle = "Cat/Idle";
public const string Walk = "Cat/Run";
public const string Happy = "Cat/HappyIdle";
public const string HappyRun = "Cat/HappyRun";
public const string Upset = "Cat/Upset";
public const string UpsetRun = "Cat/UpsetRun";
public const string Vomiting = "Cat/Vomiting";
public const string VomitingForm = "Cat/VomitingForm";
public const string VomitingIdle = "Cat/VomitingIdle";
public const string VomitingRun = "Cat/VomitingRun";
}
public static class WitchSpineAnimation
{
public const string Idle = "Witch/Idle";
public const string Walk = "Witch/Run";
public const string Happy = "Witch/HappyIdle";
public const string HappyRun = "Witch/HappyRun";
public const string Upset = "Witch/Upset";
public const string UpsetRun = "Witch/UpsetRun";
}
public enum CustomerInteractionType
{
None = 0,
OrderedCook
}
public enum CustomerSkin
{
BigCat = 0,
Casper = 1,
CasperBlack = 2,
Cat = 3,
PumkinHead = 4,
Reaper = 5,
Witch = 6
}
public class Customer : MonoBehaviour, IPlayerInteraction, ICrewInteraction
{
// Variables
#region Variables
// Components
[field: SerializeField]
public Transform CenterTransform { get; private set; }
[field: SerializeField]
public Rigidbody Rigidbody { get; private set; }
[field: SerializeField]
public CapsuleCollider CharacterCollider { get; private set; }
[field: SerializeField]
public BehaviorTree BehaviorTree { get; private set; }
[field: SerializeField]
public Transform VisualLook { get; private set; }
[field: SerializeField]
public MeshRenderer MeshRenderer { get; private set; }
[field: SerializeField]
public BarkTrigger BarkTrigger { get; private set; }
[field: SerializeField]
public InteractionCanvas InteractionCanvas { get; private set; }
// [field: SerializeField]
// public BalloonUi BalloonUi { get; private set; }
[SerializeField]
private PayMoneyUi _payMoneyUiObject;
[SerializeField]
private Vector3 _offset = new(0f, 1.5f, 0f);
// Classes
[field: SerializeField, Required]
public SpineController SpineController { get; private set; }
[field: SerializeField, Required]
public AiMovement AIMovement { get; private set; }
[field: SerializeField]
public bool EnableInteraction { get; private set; } = true;
[field: SerializeField]
public float InteractionRadius { get; private set; } = 2f;
[field: SerializeField]
public string InteractionMessage { get; set; }
[field: SerializeField]
public LevelData CurrentLevelData { get; private set; }
[field: SerializeField]
public TableSeat CurrentTableSeat { get; private set; }
[field: SerializeField]
public CocktailData OrderedCocktailData { get; private set; }
[field: SerializeField]
public CraftRecipeData OrderedCraftRecipeData { get; private set; }
[field: SerializeField]
public Bill CurrentBill { get; private set; }
[field: SerializeField]
public bool IsMatchedServer { get; private set; }
[field: SerializeField]
public bool IsReceivedItem { get; private set; }
[field: SerializeField]
public bool IsOrderedCorrected { get; private set; }
[field: SerializeField]
public bool IsServedPlayer { get; private set; }
[SerializeField]
private CustomerInteractionType _customerInteractionType;
public bool IsMoving { get; private set; }
public bool CanVomit { get; private set; }
public bool IsVomited { get; private set; }
[SerializeField]
private string _succeedServingSfxName = "SucceedServing";
[SerializeField]
private string _failedServingSfxName = "FailedServing";
private Vector3 _currentDirection = Vector3.right;
public Vector3 CurrentDirection
{
get => _currentDirection;
set
{
if (value == Vector3.zero) return;
_currentDirection = value;
}
}
[Title("스킨 연출")]
[field: SerializeField]
public CustomerSkin CustomerSkin { get; private set; }
[SerializeField]
private Vector3 _bigSize = new(1.5f, 1.5f, 1.5f);
[SerializeField]
private Vector3 _smallSize = new(0.5f, 0.5f, 0.5f);
public int HurryTime { get; private set; }
private IAstarAI _astarAi;
private Transform _spawnTransform;
private MoneyCounter _moneyCounter;
private int _paidAmount;
private int _foodPrice;
private int _tipAmount;
// State
public StateMachineController<Customer> StateMachineController { get; private set; }
public IStateMachine<Customer> IdleState { get; private set; }
public IStateMachine<Customer> WalkingState { get; private set; }
public IStateMachine<Customer> HappyState { get; private set; }
public IStateMachine<Customer> UpsetState { get; private set; }
public IStateMachine<Customer> VomitState { get; private set; }
public event Action OnInteractionCompleted;
#endregion
// Unity events
#region Unity events
private void OnDrawGizmosSelected()
{
if (!CenterTransform) return;
Gizmos.color = Color.blue;
Gizmos.DrawWireSphere(CenterTransform.position, InteractionRadius);
}
private void Awake()
{
InitializeComponents();
}
private void Start()
{
EventManager.OnGaugeResetCustomers += ResetGauge;
EventManager.OnPurifiedCustomerAll += Purify;
string currentSkinName = SpineController.GetCurrentSkin();
foreach (CustomerSkin element in Enum.GetValues(typeof(CustomerSkin)))
{
if (element.ToString().Equals(currentSkinName))
{
CustomerSkin = element;
break;
}
}
if (CustomerSkin is CustomerSkin.BigCat or CustomerSkin.Witch)
{
CanVomit = false;
transform.localScale = _bigSize;
}
else
{
CanVomit = true;
}
}
private void Update()
{
StateMachineController.UpdateState(this);
HandleMovement();
FlipVisualLook();
}
private void OnDestroy()
{
EventManager.OnGaugeResetCustomers -= ResetGauge;
EventManager.OnPurifiedCustomerAll -= Purify;
EventManager.InvokeDestroyCustomer(this);
}
#endregion
// Initialize methods
#region Initialize methods
[Button("컴포넌트 초기화")]
protected virtual void InitializeComponents()
{
if (!CenterTransform)
{
CenterTransform = transform;
}
Rigidbody = GetComponent<Rigidbody>();
CharacterCollider = GetComponent<CapsuleCollider>();
BehaviorTree = GetComponent<BehaviorTree>();
VisualLook = transform.Find("VisualLook");
MeshRenderer = VisualLook.GetComponent<MeshRenderer>();
BarkTrigger = transform.Find("DialogueSystem").GetComponent<BarkTrigger>();
InteractionCanvas = transform.GetComponentInChildren<InteractionCanvas>();
SpineController = GetComponent<SpineController>();
AIMovement = GetComponent<AiMovement>();
_astarAi = GetComponent<IAstarAI>();
}
public void Initialize(LevelData levelData, Transform spawnTransform, List<string> skins)
{
if (skins.Count > 0)
{
var randomSkin = skins[Random.Range(0, skins.Count)];
SpineController.SetSkin(randomSkin);
}
_moneyCounter = FindAnyObjectByType<MoneyCounter>();
CurrentLevelData = levelData;
_spawnTransform = spawnTransform;
IdleState = new IdleState();
WalkingState = new WalkingState();
HappyState = new HappyState();
UpsetState = new UpsetState();
VomitState = new VomitState();
StateMachineController = new StateMachineController<Customer>(this, IdleState);
BehaviorTree.EnableBehavior();
}
#endregion
// Methods
#region Methods
private void HandleMovement()
{
if (!_astarAi.canMove || _astarAi.isStopped)
{
IsMoving = false;
return;
}
CurrentDirection = _astarAi.velocity.normalized;
IsMoving = _astarAi.velocity != Vector3.zero || _astarAi.velocity != Vector3.positiveInfinity;
}
private void FlipVisualLook()
{
var localScale = VisualLook.localScale;
localScale.x = CurrentDirection.x switch
{
> 0.01f => -Mathf.Abs(localScale.x),
< -0.01f => Mathf.Abs(localScale.x),
_ => localScale.x
};
VisualLook.localScale = localScale;
}
public void Interaction()
{
switch (_customerInteractionType)
{
case CustomerInteractionType.None:
break;
case CustomerInteractionType.OrderedCook:
CraftRecipeData playerPickupCook = GameManager.Instance.CurrentTycoonPlayer.TycoonPickupHandler.CurrentCraftRecipeData;
//var servedCocktailData = ItemManager.Instance.CocktailDataSo.GetDataByIdx(currentPickupItem.Idx);
//IsOrderedCorrected = currentPickupItem.Idx == OrderedCocktailData.Idx;
IsReceivedItem = true;
IsServedPlayer = true;
IsOrderedCorrected = true;
AudioManager.Instance.PlaySfx(_succeedServingSfxName);
ServedItem(playerPickupCook);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
public virtual void CancelInteraction() { }
public bool CanInteraction()
{
switch (_customerInteractionType)
{
case CustomerInteractionType.None:
return false;
case CustomerInteractionType.OrderedCook:
CraftRecipeData playerPickupCook = GameManager.Instance.CurrentTycoonPlayer.TycoonPickupHandler.CurrentCraftRecipeData;
return playerPickupCook?.Idx == OrderedCraftRecipeData.Idx;
default:
throw new ArgumentOutOfRangeException();
}
}
public void InteractionCrew(Crew crew)
{
var serverCrew = (ServerCrew)crew;
var currentPickupItem = serverCrew.CurrentPickupItem;
//var servedCocktailData = ItemManager.Instance.CocktailDataSo.GetDataByIdx(currentPickupItem.Idx);
IsOrderedCorrected = currentPickupItem.Idx == OrderedCocktailData.Idx;
IsReceivedItem = true;
IsServedPlayer = false;
//ServedItem(servedCocktailData);
serverCrew.BalloonUi.DiscardItem();
serverCrew.ResetMission();
}
public void CancelInteractionCrew()
{
throw new NotImplementedException();
}
public bool CanInteractionCrew(Crew crew = null)
{
return IsOrderedCocktail();
}
public virtual void ShowInteractionUi()
{
//SpineController.EnableCustomMaterial();
EventManager.InvokeShowInteractionUi(InteractionMessage);
EventManager.InvokeHoldInteracting(0f);
}
public virtual void HideInteractionUi()
{
//SpineController.DisableCustomMaterial();
EventManager.InvokeHideInteractionUi();
}
public void RegisterPlayerInteraction()
{
if (EnableInteraction)
{
GameManager.Instance.CurrentTycoonPlayer.TycoonInput.RegisterPlayerInteraction(this);
}
}
public void UnregisterPlayerInteraction()
{
if (EnableInteraction)
{
GameManager.Instance.CurrentTycoonPlayer.TycoonInput.UnregisterPlayerInteraction(this);
}
_customerInteractionType = CustomerInteractionType.None;
}
public void OrderCook()
{
IsReceivedItem = false;
OrderedCraftRecipeData = FindAnyObjectByType<MenuBoardUi>().GetRandomTodayMenu();
HurryTime = CurrentLevelData.HurryTime + TycoonManager.Instance.TycoonStatus.CustomerHurryTimeIncrease;
CurrentTableSeat.OrderCook(OrderedCraftRecipeData, isReverse:true);
_customerInteractionType = CustomerInteractionType.OrderedCook;
RegisterPlayerInteraction();
EventManager.InvokeOrderedCocktail(this);
// if (!ES3.Load(SaveData.TutorialB, false))
// {
// EventManager.InvokeTutorial(TutorialName.TutorialB);
// ES3.Save(SaveData.TutorialB, true);
// }
//
// if (!ES3.Load(SaveData.TutorialJ, false) && OrderedCocktailData.Idx == "Cocktail006")
// {
// EventManager.InvokeTutorial(TutorialName.TutorialJ);
// ES3.Save(SaveData.TutorialJ, true);
// }
}
public void SetTableSeat(TableSeat tableSeat)
{
CurrentTableSeat = tableSeat;
}
public void SetCurrentDirection(Vector3 normalDirection) => CurrentDirection = normalDirection;
public void SetTableSeatPositionAndDirection()
{
transform.position = CurrentTableSeat.SeatTransform.position;
CurrentTableSeat.OccupySeat();
CurrentTableSeat.UnreserveSeat();
SetCurrentDirection(CurrentTableSeat.TableDirection);
}
public void ServedItem(CraftRecipeData craftRecipeData)
{
CurrentTableSeat.MenuBalloonUi.ReceiveItem();
if (IsOrderedCorrected)
{
CurrentTableSeat.SetFood(craftRecipeData.Sprite);
StateMachineController.TransitionToState(HappyState, this);
// int tip;
// if (IsServedPlayer)
// {
// if (TycoonManager.Instance.TycoonStatus.ContainsPassiveCard(PassiveCard.ServingBonus))
// {
// TycoonManager.Instance.TycoonStageController.ServingBonus();
// }
//
// tip = (int)(CurrentLevelData.Gold * TycoonManager.Instance.TycoonStatus.TipMultiplier);
// }
// else
// {
// tip = (int)(CurrentLevelData.Gold * TycoonManager.Instance.TycoonStatus.ServerTipMultiplier);
// }
//
// if (tip > 0)
// {
// var payMoneyUi = Instantiate(_payMoneyUiObject, transform.position + _offset,
// Quaternion.identity, TycoonUiManager.Instance.WorldCanvas.transform);
// payMoneyUi.Initialize(tip, true);
// }
}
else
{
StateMachineController.TransitionToState(UpsetState, this);
}
EventManager.InvokeServedCookToCustomer();
EventManager.InvokeServedResult(this, IsOrderedCorrected);
//EventManager.InvokeSucceedServing(IsOrderedCorrected);
//EventManager.InvokeCheckedSkin(CustomerSkin);
}
public void Bark(string conversation, BarkOrder barkOrder = BarkOrder.Random)
{
if (string.IsNullOrEmpty(conversation)) return;
BarkTrigger.barkOrder = barkOrder;
BarkTrigger.conversation = conversation;
BarkTrigger.OnUse();
}
public bool IsWaitTimeOver()
{
var isWaitTimeOver = CurrentTableSeat.MenuBalloonUi.IsWaitTimeOver();
if (isWaitTimeOver)
{
EventManager.InvokeOrderResult(this, false);
EventManager.InvokeMissedServing();
EventManager.InvokeCheckedSkin(CustomerSkin);
}
return isWaitTimeOver;
}
public void FinishFood()
{
var random = Random.Range(0f, 100f);
if (random <= TycoonManager.Instance.TycoonStageController.StageDataSo.DirtyTablePercent)
{
CurrentTableSeat.DirtyTable();
}
else
{
CurrentTableSeat.CleanTable();
}
//GainExp();
}
private void GainExp()
{
var exp = (int)(CurrentLevelData.Exp * TycoonManager.Instance.TycoonStatus.ExpMultiplier);
EventManager.InvokeChangeExp(exp);
}
public void PayMoney()
{
var gold = (int)(CurrentLevelData.Gold * TycoonManager.Instance.TycoonStatus.GoldMultiplier);
_moneyCounter.AddCurrentGold(gold);
// if (!ES3.Load(SaveData.TutorialC, false))
// {
// EventManager.InvokeTutorial(TutorialName.TutorialC);
// ES3.Save(SaveData.TutorialC, true);
// }
}
public void Vomit()
{
AIMovement.StopMove();
StateMachineController.TransitionToState(VomitState, this);
}
public void InstanceVomit()
{
var spawnPosition = transform.position + new Vector3(0f, 0f, -0.4f);
EventManager.InvokeCreateVomiting(spawnPosition);
IsVomited = true;
StateMachineController.TransitionToState(IdleState, this);
}
public void CheckOut()
{
AIMovement.StopMove();
BehaviorTree.DisableBehavior();
Destroy(gameObject);
}
// 상호작용 메서드
public void OrderCocktail()
{
IsReceivedItem = false;
IsOrderedCorrected = false;
OrderedCocktailData = TycoonManager.Instance.TycoonIngredientController.GetRandomCocktailData();
HurryTime = CurrentLevelData.HurryTime + TycoonManager.Instance.TycoonStatus.CustomerHurryTimeIncrease;
CurrentTableSeat.OrderCocktail(OrderedCocktailData.Idx, CurrentLevelData.WaitTime, HurryTime, true);
_customerInteractionType = CustomerInteractionType.OrderedCook;
RegisterPlayerInteraction();
EventManager.InvokeOrderedCocktail(this);
if (!ES3.Load(SaveData.TutorialB, false))
{
EventManager.InvokeTutorial(TutorialName.TutorialB);
ES3.Save(SaveData.TutorialB, true);
}
if (!ES3.Load(SaveData.TutorialJ, false) && OrderedCocktailData.Idx == "Cocktail006")
{
EventManager.InvokeTutorial(TutorialName.TutorialJ);
ES3.Save(SaveData.TutorialJ, true);
}
}
public void MovePosition(Vector3 targetPosition)
{
AIMovement.Move(targetPosition);
StateMachineController.TransitionToState(WalkingState, this);
}
public bool CanMoneyCounterInteractionPosition()
{
if (_moneyCounter.CenterTransform == null) return false;
return AIMovement.HasReachedDestination() ||
Vector3.Distance(_moneyCounter.CenterTransform.position, transform.position) <=
_moneyCounter.InteractionRadius;
}
public void MoveMoneyCounter()
{
VacateTable();
MovePosition(_moneyCounter.transform.position);
}
public void MoveSpawnPosition()
{
VacateTable();
MovePosition(_spawnTransform.position);
}
public void VacateTable()
{
if (CurrentTableSeat)
{
CurrentTableSeat.VacateSeat();
CurrentTableSeat = null;
}
}
public void ResetGauge()
{
if (!IsOrderedCocktail()) return;
CurrentTableSeat.MenuBalloonUi.ResetGauge();
CurrentBill.ResetGauge();
}
public bool IsOrderedCocktail()
{
return CurrentTableSeat && CurrentTableSeat.IsOccupied && !IsReceivedItem;
}
public void TryMatchedServer()
{
if (!CanInteractionCrew()) return;
IsMatchedServer = true;
}
public void SetCurrentBill(Bill bill) => CurrentBill = bill;
public void Purify()
{
if (CurrentTableSeat)
{
CurrentTableSeat.Purify();
CurrentTableSeat = null;
}
if (CurrentBill)
{
CurrentBill.Destroy();
CurrentBill = null;
}
UnregisterPlayerInteraction();
CheckOut();
}
#endregion
}
}