CapersProject/Assets/02.Scripts/Character/Npc/Customer/Customer.cs
2024-11-19 16:57:05 +09:00

593 lines
19 KiB
C#

using System;
using BehaviorDesigner.Runtime;
using BlueWater.Audios;
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; set; }
[SerializeField]
private Vomiting _vomiting;
[SerializeField]
private Color _startColor = Color.green;
[SerializeField]
private Color _endColor = Color.red;
[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 IsOrderedCorrected { 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; }
[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;
}
}
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 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)
{
_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 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)
{
CurrentTableSeat.MenuBalloonUi.ReceiveItem(cocktailData);
if (IsOrderedCorrected)
{
CurrentTableSeat.SetFood();
StateMachineController.TransitionToState(HappyState, this);
int tip;
if (IsServedPlayer)
{
if (TycoonManager.Instance.TycoonStatus.CurrentPassiveCard == 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.InvokeCocktailServedToCustomer(cocktailData, IsServedPlayer);
EventManager.InvokeOrderResult(this, IsOrderedCorrected);
}
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);
IsOrderedCorrected = currentPickupItem.Idx == OrderedCocktailData.Idx;
IsReceivedItem = true;
IsServedPlayer = true;
AudioManager.Instance.PlaySfx(IsOrderedCorrected ? _succeedServingSfxName : _failedServingSfxName);
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);
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 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);
}
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);
}
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);
TycoonStageController.VomitCount++;
if (TycoonStageController.VomitCount >= 3)
{
var random = Random.Range(0, 101);
if (random <= 20)
{
EventManager.InvokeCreateMushroom();
}
else
{
EventManager.InvokeCreateMold();
}
TycoonStageController.VomitCount = 0;
}
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();
var hurryTime = CurrentLevelData.HurryTime + TycoonManager.Instance.TycoonStatus.CustomerHurryTimeIncrease;
CurrentTableSeat.MenuBalloonUi.OrderItem(OrderedCocktailData.Idx, CurrentLevelData.WaitTime, hurryTime, startColor: _startColor, endColor: _endColor);
_customerInteractionType = CustomerInteractionType.ServedCocktail;
RegisterPlayerInteraction();
EventManager.InvokeOrderedCocktail(this);
}
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
}
}