CapersProject/Assets/02.Scripts/Character/Npc/Customer/Customer.cs
2024-11-07 18:13:54 +09:00

521 lines
16 KiB
C#

using System;
using BehaviorDesigner.Runtime;
using BlueWater.Enemies;
using BlueWater.Interfaces;
using BlueWater.Items;
using BlueWater.Npcs.Crews;
using BlueWater.Npcs.Crews.Server;
using BlueWater.Players;
using BlueWater.Tycoons;
using BlueWater.Uis;
using Pathfinding;
using PixelCrushers.DialogueSystem;
using Sirenix.OdinInspector;
using UnityEngine;
using Random = UnityEngine.Random;
namespace BlueWater.Npcs.Customers
{
public static class CustomerSpineAnimation
{
public const string Idle = "Idle";
public const string Walk = "Run";
public const string Happy = "Happy";
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 enum CustomerInteractionType
{
None = 0,
ServedCocktail
}
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; private set; }
[SerializeField]
private Vomiting _vomiting;
[field: Title("실시간 데이터")]
[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 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 IsOrderedSucceed { get; private set; }
[field: SerializeField]
public bool IsServedPlayer { get; private set; }
[SerializeField]
private CustomerInteractionType _customerInteractionType;
public bool IsMoving { get; private set; }
public bool IsVomited { get; private set; }
private Vector3 _currentDirection = Vector3.right;
public Vector3 CurrentDirection
{
get => _currentDirection;
set
{
if (value == Vector3.zero) return;
_currentDirection = value;
}
}
private IAstarAI _astarAi;
private Transform _spawnTransform;
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 Awake()
{
InitializeComponents();
}
private void Start()
{
EventManager.OnGaugeResetCustomers += ResetGauge;
EventManager.OnPurifiedCustomerAll += Purify;
}
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()
{
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>();
BalloonUi = InteractionCanvas.transform.GetComponentInChildren<BalloonUi>();
SpineController = GetComponent<SpineController>();
AIMovement = GetComponent<AiMovement>();
_astarAi = GetComponent<IAstarAI>();
}
public void Initialize(LevelData levelData, Transform spawnTransform)
{
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 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(CocktailData cocktailData)
{
BalloonUi.ReceiveItem(cocktailData);
if (IsOrderedSucceed)
{
CurrentTableSeat.SetFood();
StateMachineController.TransitionToState(HappyState, this);
var tip = 0;
if (IsServedPlayer)
{
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);
}
}
else
{
StateMachineController.TransitionToState(UpsetState, this);
}
EventManager.InvokeCocktailServedToCustomer(cocktailData, IsServedPlayer);
EventManager.InvokeOrderResult(this, IsOrderedSucceed);
}
public void Interaction()
{
switch (_customerInteractionType)
{
case CustomerInteractionType.None:
break;
case CustomerInteractionType.ServedCocktail:
var currentPickupItem = GameManager.Instance.CurrentTycoonPlayer.TycoonPickupHandler.GetCurrentPickupItem();
var servedCocktailData = ItemManager.Instance.CocktailDataSo.GetDataByIdx(currentPickupItem.Idx);
IsOrderedSucceed = currentPickupItem.Idx == OrderedCocktailData.Idx;
IsReceivedItem = true;
IsServedPlayer = true;
ServedItem(servedCocktailData);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
public virtual void CancelInteraction() { }
public bool CanInteraction()
{
switch (_customerInteractionType)
{
case CustomerInteractionType.None:
return false;
case CustomerInteractionType.ServedCocktail:
var currentPickupItem = GameManager.Instance.CurrentTycoonPlayer.TycoonPickupHandler.GetCurrentPickupItem();
return currentPickupItem != null;
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);
IsOrderedSucceed = 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);
}
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 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 = BalloonUi.IsWaitTimeOver();
if (isWaitTimeOver)
{
EventManager.InvokeOrderResult(this, false);
}
return isWaitTimeOver;
}
public void PayMoney()
{
var random = Random.Range(0f, 100f);
if (random <= TycoonManager.Instance.TycoonStageController.StageDataSo.DirtyTablePercent)
{
CurrentTableSeat.DirtyTable();
}
else
{
CurrentTableSeat.CleanTable();
}
var exp = (int)(CurrentLevelData.Exp * TycoonManager.Instance.TycoonStatus.ExpMultiplier);
EventManager.InvokeChangeExp(exp);
}
public void Vomit()
{
AIMovement.StopMove();
StateMachineController.TransitionToState(VomitState, this);
}
public void InstanceVomit()
{
var spawnPosition = transform.position + new Vector3(0f, 0f, 0.1f);
Instantiate(_vomiting, spawnPosition, _vomiting.transform.rotation);
IsVomited = true;
StateMachineController.TransitionToState(IdleState, this);
}
public void CheckOut()
{
AIMovement.StopMove();
BehaviorTree.DisableBehavior();
Destroy(gameObject);
}
// 상호작용 메서드
public void OrderCocktail()
{
IsReceivedItem = false;
IsOrderedSucceed = false;
InteractionMessage = "칵테일 전달";
OrderedCocktailData = TycoonManager.Instance.TycoonIngredientController.GetRandomCocktailData();
var hurryTime = CurrentLevelData.HurryTime + TycoonManager.Instance.TycoonStatus.CustomerHurryTimeIncrease;
BalloonUi.OrderItem(OrderedCocktailData.Idx, CurrentLevelData.WaitTime, hurryTime);
_customerInteractionType = CustomerInteractionType.ServedCocktail;
RegisterPlayerInteraction();
EventManager.InvokeOrderedCocktail(this);
}
public void MoveSpawnPosition()
{
if (CurrentTableSeat)
{
CurrentTableSeat.VacateSeat();
CurrentTableSeat = null;
}
AIMovement.Move(_spawnTransform.position);
StateMachineController.TransitionToState(WalkingState, this);
}
public void ResetGauge()
{
if (!IsOrderedCocktail()) return;
BalloonUi.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
}
}