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 greetings; [SerializeField] List 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 heldTickets = new List(); [HideInInspector] List heldFood = new List(); [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 todo = new Queue(); [SerializeField] Employee employee = Employee.Elaine; // Start is called before the first frame update void Start() { audioSource = GetComponent(); destination = GetComponent(); animator = GetComponent(); aiLerp = GetComponent(); gameStateController = GameObject.FindGameObjectWithTag("GameStateController").GetComponent(); // 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 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(); 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(); 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); } } }