using System.Collections; using System.Collections.Generic; using UnityEngine; using Pathfinding; using Com.LuisPedroFonseca.ProCamera2D; using TMPro; using UnityEngine.UI; public enum PatronType { Default } public class Party { // Gameplay stats. // Set of patrons to be seated. The size determines eligible tables. // The type determines what sprites to use. public List patrons; // Default amount of time before the party walks out when waiting on something. public int patience = 5; // Chance for a party to give a task instead of an order. public int baseTaskRate = 50; // Default speed at which a party decides on an order and eats. public int baseSpeed = 10; public Party(List patrons, int patience, int baseTaskRate, int baseSpeed) { this.patrons = patrons; this.patience = patience; this.baseTaskRate = baseTaskRate; this.baseSpeed = baseSpeed; } } public class TalentState { public int energy { get; set; } = 100; public int tips { get; set; } = 0; public int exp { get; set; } = 0; // Not owned. public TableController assignedTable { get; set; } = null; // public StageSlotController assignedStageSlot = null; // public int assignedBackroom = -1; // public bool atFrontDesk = false; } public class GameStateController : MonoBehaviour { [SerializeField] TalentListController talentList; [HideInInspector] GameAudioController audioController; [HideInInspector] int selectedTab = -1; [HideInInspector] ProCamera2D proCamera2D; [HideInInspector] ProCamera2DPanAndZoom proCamera2DPan; [HideInInspector] ProCamera2DTransitionsFX proCamera2DTransition; [HideInInspector] Animator expandedCG; [HideInInspector] SummaryController summaryController; // List of all tables in the map. // Used to validate party seating eligibility. [HideInInspector] private List tables = new List(); [SerializeField] public List orderPlacements; // Game loop stats. [HideInInspector] public int moneyEarned = 0; [HideInInspector] public int currentTime = 0; [HideInInspector] public int patrons = 0; [HideInInspector] public Queue frontDeskQueue = new Queue(); [HideInInspector] public Queue orderBacklog = new Queue(); [HideInInspector] public bool gameRunning = false; // Game init values. [SerializeField] int secondsPerTick = 10; [SerializeField] int maxTime = 30; [SerializeField] int openingHour = 6; // Game init values (configurable). // Varies based on progress in the game. Will be loaded dynamically. // Max number of parties waiting to be seated. [SerializeField] int queueCapacity = 3; // Spawn speed of new parties per minute. [SerializeField] int baseSpawnRate = 1; // Seconds to wait before spawning the first party. [SerializeField] int firstSpawnTime = 3; // EXP earned for completing actions. // Subject to bonuses based on what the talent does. [SerializeField] public int baseExp = 5; // Probability distribution of party size, starting at 1. // Works the same as the Grand Star item lottery. // Must add to 100. [SerializeField] List partySizeDistribution = new List() { 25, 50, 20, 5 }; // Multiplier distribution to receive different letter grades. // C, B, A, S, SS. You get an F if you're below the goal. [SerializeField] public List gradeDistribution = new List() { 1.0f, 1.5f, 2.0f, 2.5f, 3.0f }; // Goal for the day. You fail and the day resets if you don't hit this goal. [SerializeField] public int moneyGoal = 100; // Base multiplier for earned money per patron. [SerializeField] public float baseMoneyMultiplier = 100.0f; // Base difficulty for patron patience and tipping. [SerializeField] public float difficulty = 1.0f; // Status indicators. // Not owned. [SerializeField] public TextMeshProUGUI text_CurrentTime; [SerializeField] public TextMeshProUGUI text_CurrentMoney; [SerializeField] public TextMeshProUGUI text_CurrentPatrons; // Guaranteed waypoints. // Not owned. // Backroom is targeted automatically when a talent receives a task. [HideInInspector] public Transform backroom; // Bar is targeted with a hotkey (B). [HideInInspector] public Transform bar; // Front desk is targeted with a hotkey (F). [HideInInspector] public Transform frontDesk; // Dishes can be targeted with a hotkey (D). [HideInInspector] public Transform dishes; // Should load from GameData later. public TalentState[] talentState = { // Elaine new TalentState() { energy = GameData.GLOBAL .employeeStats[(int)Employee.Elaine].maxEnergy }, new TalentState() { energy = GameData.GLOBAL .employeeStats[(int)Employee.Irida].maxEnergy }, new TalentState() { energy = GameData.GLOBAL .employeeStats[(int)Employee.Clem].maxEnergy }, new TalentState() { energy = GameData.GLOBAL .employeeStats[(int)Employee.Schroder].maxEnergy }, }; private void EndTimer() { Debug.Log("TODO: Make all employees stop what they're doing"); gameRunning = false; summaryController.gameObject.SetActive(true); summaryController.SetText(); } IEnumerator GameTimer() { while (currentTime < maxTime) { yield return new WaitForSeconds(secondsPerTick); currentTime += 1; } EndTimer(); } IEnumerator SpawnTimer() { yield return new WaitForSeconds(firstSpawnTime); if (frontDeskQueue.Count < queueCapacity) { NewParty(); } while (gameRunning) { yield return new WaitForSeconds(60 / baseSpawnRate); if (frontDeskQueue.Count < queueCapacity) { NewParty(); } } } private void StartTimer() { if (currentTime < maxTime) { gameRunning = true; StartCoroutine("GameTimer"); StartCoroutine("SpawnTimer"); } } private PatronType GeneratePatron() { Debug.Log("TODO: Patron type variance"); return PatronType.Default; } private void NewParty() { int lotto = Random.Range(0, 100); int lottoSum = 0; int i = 0; List patrons = new List() { GeneratePatron() }; while (i < partySizeDistribution.Count && partySizeDistribution[i] + lottoSum < lotto) { lottoSum += partySizeDistribution[i]; i += 1; patrons.Add(GeneratePatron()); } Debug.Log("TODO: Party stat variance"); frontDeskQueue.Enqueue(new Party( patrons, /*patience =*/ 5, /*baseTaskRate =*/ 10, /*baseSpeed =*/ 15)); Debug.Log("New Party (" + frontDeskQueue.Count.ToString() + ")"); } // Start is called before the first frame update void Start() { audioController = GetComponent(); backroom = GameObject.FindGameObjectWithTag("BackroomWaypoint").transform; bar = GameObject.FindGameObjectWithTag("BarWaypoint").transform; frontDesk = GameObject.FindGameObjectWithTag("FrontDeskWaypoint").transform; dishes = GameObject.FindGameObjectWithTag("DishesWaypoint").transform; proCamera2D = GameObject.FindGameObjectWithTag("MainCamera").GetComponent(); proCamera2DPan = proCamera2D.GetComponent(); proCamera2DTransition = proCamera2D.GetComponent(); expandedCG = GameObject.FindGameObjectWithTag("ExpandedCG").GetComponent(); expandedCG.gameObject.SetActive(false); summaryController = GameObject.FindGameObjectWithTag("Summary").GetComponent(); summaryController.gameObject.SetActive(false); proCamera2DTransition.TransitionEnter(); int i = 1; foreach (GameObject g in GameObject.FindGameObjectsWithTag("Table")) { TableController table = g.GetComponent(); table.tableId = i; i++; tables.Add(table); } Debug.Log("TODO: Move this to a ready-set-go"); StartTimer(); } TalentController GetSelectedEmployee() { GameObject employee = GameObject.FindGameObjectWithTag(((Employee)selectedTab).ToString()); if (employee != null) { return employee.GetComponent(); } return null; } public void GainExp(Employee e, int amount) { talentState[(int)e].exp += amount; } public void GainTips(Employee e, int amount) { moneyEarned += amount; talentState[(int)e].tips += amount; } public void GainMoney(int amount) { moneyEarned += amount; } // Attempt to select the [tabIndex]'th Talent. public void HandleSelect(int tabIndex) { if (selectedTab == tabIndex) { // Deselect tab Debug.Log("Attempting to de-select tab " + tabIndex.ToString()); talentList.setActiveTab(-1); selectedTab = -1; audioController.sound.PlayOneShot( audioController.talentTabClose); // Return pan to the player. proCamera2D.RemoveAllCameraTargets(); proCamera2DPan.enabled = true; } else { // Select tab Debug.Log("Attempting to select tab " + tabIndex.ToString()); talentList.setActiveTab(tabIndex); selectedTab = tabIndex; audioController.sound.PlayOneShot( audioController.talentTabOpen); // Switch target to the newly selected employee if in target mode. if (proCamera2DPan.enabled == false) { HandleTargetSelection(); } } } void HandleShowCG() { if (expandedCG.isActiveAndEnabled) { talentList.gameObject.SetActive(true); expandedCG.gameObject.SetActive(false); Debug.Log("TODO: Stop loop sound for animation"); } else if (selectedTab != -1) { talentList.gameObject.SetActive(false); expandedCG.gameObject.SetActive(true); Debug.Log("TODO: Set animator float for CG of " + ((Employee)selectedTab).ToString()); Debug.Log("TODO: Play loop sound for animation"); } } void HandleTargetSelection() { if (!proCamera2DPan.enabled) { // Turn off targeting. proCamera2D.RemoveAllCameraTargets(); proCamera2DPan.enabled = true; } else if (selectedTab != -1) { TalentController talent = GetSelectedEmployee(); if (talent != null) { talent.OnTarget(); // Targeting takes pan away from the player. proCamera2DPan.enabled = false; proCamera2D.RemoveAllCameraTargets(); proCamera2D.AddCameraTarget(talent.transform, 1, 1, 1); } } } // If an employee is selected, have them tend to the table. public void HandleTableClick(TableController table) { if (selectedTab != -1 && table.waypoint != null) { // The target employee's name should be the object tag associated with their selection index. // Elaine = 0, Irida = 1, Clem = 2, Schroder = 3. TalentController talent = GetSelectedEmployee(); if (talent != null) { Debug.Log("Queueing " + ((Employee)selectedTab).ToString() + " to a table."); talent.QueueDestination(table.waypoint, AssignmentType.Table, table); } } } // If an employee is selected, have them visit this order waypoint. public void HandleOrderClick(OrderPlacementController order) { if (selectedTab != -1 && order.waypoint != null) { // The target employee's name should be the object tag associated with their selection index. // Elaine = 0, Irida = 1, Clem = 2, Schroder = 3. TalentController talent = GetSelectedEmployee(); if (talent != null) { Debug.Log("Queueing " + ((Employee)selectedTab).ToString() + " to pick up an order."); talent.QueueDestination(order.waypoint, AssignmentType.Order, null, order); } } } private IEnumerator FinishCooking(Order o, float time) { yield return new WaitForSeconds(time); bool orderAccepted = false; foreach (OrderPlacementController placement in orderPlacements) { if (placement.order == null) { placement.TakeOrder(o); orderAccepted = true; break; } } if (!orderAccepted) { orderBacklog.Enqueue(o); } } // Starts coroutines to cook a list of orders. public void Cook(List orders) { foreach (Order o in orders) { Debug.Log("TODO: Cook time based on difficulty"); StartCoroutine(FinishCooking(o, Random.Range(1.0f, 5.0f))); } } // Returns true iff an open table with sufficient capacity is available for seating. public bool ValidateParty(Party p) { foreach (TableController t in tables) { if (t.chairs.Count >= p.patrons.Count && t.party == null) { return true; } } return false; } // Check if the front desk is already occupied. // If not, attempt to assign the selected talent to the front desk. void HandleFrontDeskClick() { if (selectedTab != -1) { TalentController talent = GetSelectedEmployee(); if (talent != null) { Debug.Log("Queueing " + ((Employee)selectedTab).ToString() + " to the front desk."); talent.QueueDestination(frontDesk, AssignmentType.FrontDesk); } } } // Attempt to assign the selected talent to the bar. void HandleBarClick() { if (selectedTab != -1) { TalentController talent = GetSelectedEmployee(); if (talent != null) { Debug.Log("Queueing " + ((Employee)selectedTab).ToString() + " to the bar."); talent.QueueDestination(bar, AssignmentType.Bar); } } } // Attempt to assign the selected talent to the bar. void HandleDishesClick() { if (selectedTab != -1) { TalentController talent = GetSelectedEmployee(); if (talent != null) { Debug.Log("Queueing " + ((Employee)selectedTab).ToString() + " to the dishes."); talent.QueueDestination(dishes, AssignmentType.Dishes); } } } // Turn a time tick into XX:YY PM/AM. // Starting time is set by openingHour and each tick is 15 minutes. // So tick 5 with openingHour 6 is 7:15 PM. private string BuildTimeString(int time) { int minute = (time % 4) * 15; int hour = (time / 4 + openingHour) % 12; if (hour == 0) hour = 12; return hour.ToString() + ":" + minute.ToString().PadLeft(2, '0') + ((hour < openingHour || hour == 12) ? " AM" : " PM"); } private void LateUpdate() { text_CurrentTime.SetText(BuildTimeString(currentTime)); text_CurrentMoney.SetText("$" + moneyEarned.ToString()); text_CurrentPatrons.SetText(patrons.ToString()); } // Update is called once per frame void Update() { if (Input.GetKeyDown(KeyCode.Alpha1)) { HandleSelect(0); } else if (Input.GetKeyDown(KeyCode.Alpha2)) { HandleSelect(1); } else if (Input.GetKeyDown(KeyCode.Alpha3)) { HandleSelect(2); } else if (Input.GetKeyDown(KeyCode.Alpha4)) { HandleSelect(3); } else if (Input.GetKeyDown(KeyCode.Alpha5)) { HandleSelect(4); } else if (Input.GetKeyDown(KeyCode.Alpha6)) { HandleSelect(5); } else if (Input.GetKeyDown(KeyCode.E)) { HandleShowCG(); } else if (Input.GetKeyDown(KeyCode.T)) { HandleTargetSelection(); } else if (Input.GetKeyDown(KeyCode.F)) { HandleFrontDeskClick(); } else if (Input.GetKeyDown(KeyCode.B)) { HandleBarClick(); } else if (Input.GetKeyDown(KeyCode.D)) { HandleDishesClick(); } } }