Files
ihob/Assets/Scripts/MainGame/TalentController.cs
2026-02-21 17:04:05 -08:00

373 lines
14 KiB
C#

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Pathfinding;
public enum AssignmentType {
None,
Table,
Backroom,
FrontDesk,
Bar,
Order,
Dishes
}
// To-do item for an employee. The player queues these up,
// and the employee performs each one best-effort as quickly as they can.
public class Destination {
public Transform waypoint;
public AssignmentType assignmentType;
public TableController assignedTable;
public OrderPlacementController order;
public Destination(Transform waypoint, AssignmentType assignmentType, TableController assignedTable, OrderPlacementController order) {
this.waypoint = waypoint;
this.assignmentType = assignmentType;
this.assignedTable = assignedTable;
this.order = order;
}
}
public class TalentController : MonoBehaviour {
[SerializeField] List<AudioClip> greetings;
[SerializeField] List<AudioClip> comply;
// Prefab to spawn XP/Money text.
[SerializeField] GameObject fadingTextProto;
[HideInInspector] AudioSource audioSource;
[HideInInspector] Animator animator;
// Pathfinding related objects.
[HideInInspector] AIDestinationSetter destination;
[HideInInspector] AILerp aiLerp;
// Not owned.
[HideInInspector] GameStateController gameStateController;
[HideInInspector] bool busy = false;
// The distance at which an employee will stop away from a table, if they're
// walking towards it while it's being serviced by another employee.
[SerializeField] float maxServiceWaitDistance = 4.0f;
// Table service variables.
[SerializeField] TableController assignedTable = null;
[HideInInspector] List<Order> heldTickets = new List<Order>();
[HideInInspector] List<Order> heldFood = new List<Order>();
[HideInInspector] OrderPlacementController assignedOrder = null;
[HideInInspector] bool isAtWaypoint = true;
[HideInInspector] Party seatingParty = null;
[HideInInspector] bool isHoldingPlates = false;
[HideInInspector] AssignmentType assignmentType = AssignmentType.None;
// TOOD: The list of destinations is overrideable by a visit to the Backroom.
[SerializeField] Queue<Destination> todo = new Queue<Destination>();
[SerializeField] Employee employee = Employee.Elaine;
// Start is called before the first frame update
void Start() {
audioSource = GetComponent<AudioSource>();
destination = GetComponent<AIDestinationSetter>();
animator = GetComponent<Animator>();
aiLerp = GetComponent<AILerp>();
gameStateController = GameObject.FindGameObjectWithTag("GameStateController").GetComponent<GameStateController>();
// TODO: Maybe something safer on GetComponent.
if (!GameData.IsPlayerUnlocked(employee)) {
Destroy(gameObject);
}
}
void NewDestination(Transform waypoint,
AssignmentType destinationType = AssignmentType.None,
TableController table = null,
OrderPlacementController order = null) {
destination.target = waypoint;
assignmentType = destinationType;
assignedTable = table;
assignedOrder = order;
isAtWaypoint = false;
busy = true;
}
// Vector push-back a new Destination onto the employee's to-do list.
// They will act upon all destinations, in the order they were enqueued by the player.
public void QueueDestination(Transform waypoint,
AssignmentType destinationType = AssignmentType.None,
TableController table = null,
OrderPlacementController order = null) {
if (todo.Count == 0 && !busy) {
PlayRandomSound(comply);
}
todo.Enqueue(new Destination(waypoint, destinationType, table, order));
}
public void PlayRandomSound(List<AudioClip> sounds) {
if (sounds.Count > 0) {
audioSource.PlayOneShot(sounds[Random.Range(0, sounds.Count)]);
}
}
public void OnTarget() {
PlayRandomSound(greetings);
}
private void GainPay(int amount, int tips) {
Debug.Log("Spawn text with amount + tip amount");
gameStateController.GainMoney(amount);
gameStateController.GainTips(employee, tips);
TextFadeController tfc = Instantiate(fadingTextProto).GetComponent<TextFadeController>();
tfc.transform.position = transform.position;
tfc.baseColor = Color.green;
tfc.text.SetText("+$" + amount.ToString() + " +$" + tips.ToString());
}
private void GainExp(int amount) {
Debug.Log("Spawn text with exp amount");
gameStateController.GainExp(employee, amount);
TextFadeController tfc = Instantiate(fadingTextProto).GetComponent<TextFadeController>();
tfc.transform.position = transform.position;
tfc.baseColor = Color.yellow;
tfc.text.SetText("+" + amount.ToString() + " XP");
}
private IEnumerator PickUpTicket() {
Debug.Log("TODO: Make dynamic based on employee speed");
Debug.Log("TODO: Update animation to having conversation");
Debug.Log("TODO: Implement chance of generating a task instead");
yield return new WaitForSeconds(2);
heldTickets.Add(assignedTable.GenerateOrder());
assignedTable.servicing = false;
assignedTable = null;
GainExp(gameStateController.baseExp);
assignmentType = AssignmentType.None;
busy = false;
}
private IEnumerator DropOffTickets() {
Debug.Log("TODO: Make dynamic based on employee speed");
Debug.Log("TODO: Update animation to dropping off tickets");
yield return new WaitForSeconds(1);
gameStateController.Cook(heldTickets);
heldTickets.Clear();
GainExp(gameStateController.baseExp);
assignmentType = AssignmentType.None;
busy = false;
}
private IEnumerator PickUpFood() {
Debug.Log("TODO: Make dynamic based on employee speed");
Debug.Log("TODO: Update animation to grabbing the order");
yield return new WaitForSeconds(1);
heldFood.Add(assignedOrder.PickUp());
assignedOrder = null;
GainExp(gameStateController.baseExp);
assignmentType = AssignmentType.None;
busy = false;
}
private IEnumerator DropOffFood() {
int i = 0;
bool droppedOff = false;
for (; i < heldFood.Count; i++) {
if (heldFood[i].destination.tableId == assignedTable.tableId) {
Debug.Log("TODO: Make dynamic based on employee speed");
Debug.Log("TODO: Update animation to dropping off food");
yield return new WaitForSeconds(2);
assignedTable.StartEating();
GainExp(gameStateController.baseExp);
droppedOff = true;
break;
}
}
if (droppedOff) {
heldFood.RemoveAt(i);
}
assignedTable.servicing = false;
assignedTable = null;
assignmentType = AssignmentType.None;
busy = false;
}
private IEnumerator PickUpDishes() {
Debug.Log("TODO: Make dynamic based on employee speed");
Debug.Log("TODO: Update animation to picking up dishes");
yield return new WaitForSeconds(2);
isHoldingPlates = true;
assignedTable.CleanUpDishes();
assignedTable.servicing = false;
assignedTable = null;
GainExp(gameStateController.baseExp);
assignmentType = AssignmentType.None;
busy = false;
}
private IEnumerator PickUpCheck() {
Debug.Log("TODO: Yay animation");
yield return new WaitForSeconds(1);
assignedTable.PickUpCheck();
GainExp(gameStateController.baseExp);
GainPay(assignedTable.GetPay(), assignedTable.GetTip());
assignedTable.servicing = false;
assignedTable = null;
assignmentType = AssignmentType.None;
busy = false;
}
void InteractWithTable() {
// Set animation and timeout for corresponding action from possibilities:
// If talent is leading employees, dump them at the table.
// Otherwise:
// TableState.Ready -> Table chooses between a food order and task
// TableState.WaitingOnOrder -> If the employee is holding the table's food, fulfills the order
// TableState.WaitingOnCheck -> Gather the check and empty the table of plates and guests.
assignedTable.servicing = true;
if (seatingParty != null) {
if (assignedTable.party == null && assignedTable.state == TableState.Empty) {
Debug.Log("TODO: Convert to Coroutine to take time seating a party");
assignedTable.AssignParty(seatingParty);
GainExp(gameStateController.baseExp);
seatingParty = null;
}
} else if (assignedTable.state != TableState.Empty) {
switch(assignedTable.state) {
case TableState.WaitingOnOrder:
// Coroutine should set assignedTable.servicing=false, assignedTable=null, assignmentType=None.
if (!isHoldingPlates && seatingParty == null && heldTickets.Count == 0) {
StartCoroutine("DropOffFood");
return;
}
break;
case TableState.WaitingOnCheck:
// Coroutine should set assignedTable.servicing=false, assignedTable=null, assignmentType=None.
StartCoroutine("PickUpCheck");
return;
case TableState.ReadyToOrder:
// Coroutine should set assignedTable.servicing=false, assignedTable=null, assignmentType=None.
if (!isHoldingPlates && seatingParty == null && heldFood.Count == 0) {
StartCoroutine("PickUpTicket");
return;
}
break;
case TableState.Plates:
// Coroutine should set assignedTable.servicing=false, assignedTable=null, assignmentType=None.
if (seatingParty == null && heldFood.Count == 0 && heldTickets.Count == 0) {
StartCoroutine("PickUpDishes");
return;
}
break;
default:
Debug.Log("TODO: Sound for un-interactable table, maybe");
break;
}
}
assignedTable.servicing = false;
assignmentType = AssignmentType.None;
assignedTable = null;
busy = false;
}
void InteractWithBar() {
// Drop off any held tickets from customers.
if (heldTickets.Count > 0) {
StartCoroutine("DropOffTickets");
return;
}
assignmentType = AssignmentType.None;
busy = false;
}
void InteractWithOrder() {
// Pick up an order at the waypoint.
// Should fail if the employee is holding anything other an another order.
if (assignedOrder.order != null && !isHoldingPlates && heldTickets.Count == 0 && seatingParty == null) {
StartCoroutine("PickUpFood");
return;
}
assignmentType = AssignmentType.None;
busy = false;
}
void InteractWithDishes() {
// Drop off any held dishes at the waypoint.
// Does nothing else.
if (isHoldingPlates) {
Debug.Log("TODO: Make a dish noise?");
GainExp(gameStateController.baseExp);
}
isHoldingPlates = false;
busy = false;
}
// If the front desk has a party queued, grab the party.
// Fails if busy or ineligible.
void InteractWithFrontDesk() {
if (isHoldingPlates || heldFood.Count > 0 || heldTickets.Count > 0 || seatingParty != null) {
Debug.Log("TODO: Play sound for failed queue interaction");
} else if (gameStateController.frontDeskQueue.Count == 0) {
Debug.Log("TODO: Play sound for empty queue");
} else {
Debug.Log("TODO: Parameterize front desk interact for particular party");
Party candidateParty = gameStateController.frontDeskQueue.Peek();
if (gameStateController.ValidateParty(candidateParty)) {
seatingParty = gameStateController.frontDeskQueue.Dequeue();
} else {
Debug.Log("TODO: Party grab failure sound");
}
}
busy = false;
}
// Update is called once per frame
void Update()
{
// This is true on the frame at which the employee reaches the waypoint.
if (!isAtWaypoint && aiLerp.reachedDestination) {
isAtWaypoint = true;
if (assignmentType == AssignmentType.Table && assignedTable != null) {
Debug.Log("I reached the table!");
InteractWithTable();
} else if (assignmentType == AssignmentType.Bar) {
Debug.Log("I reached the bar to drop off a ticket!");
InteractWithBar();
} else if (assignmentType == AssignmentType.Order) {
Debug.Log("I reached the bar to pick up an order!");
InteractWithOrder();
} else if (assignmentType == AssignmentType.Dishes) {
Debug.Log("I reached the dishes!");
InteractWithDishes();
} else if (assignmentType == AssignmentType.FrontDesk) {
Debug.Log("I reached the front desk!");
InteractWithFrontDesk();
}
}
// If the employee is available, pop the top of the queue and set it as a destination.
if (!busy && todo.Count > 0) {
Destination dest = todo.Dequeue();
NewDestination(dest.waypoint, dest.assignmentType, dest.assignedTable, dest.order);
}
// If another employee is servicing the assigned table, wait for them to finish.
if (assignedTable != null &&
assignedTable.servicing &&
Vector2.Distance(transform.position, assignedTable.waypoint.transform.position) <= maxServiceWaitDistance) {
aiLerp.canMove = false;
} else {
aiLerp.canMove = true;
}
}
private void LateUpdate() {
if (aiLerp.canMove) {
animator.SetInteger("hSpeed", (int)aiLerp.velocity.x);
animator.SetInteger("vSpeed", (int)aiLerp.velocity.y);
} else {
animator.SetInteger("hSpeed", 0);
animator.SetInteger("vSpeed", 0);
}
}
}