460 lines
17 KiB
C#
460 lines
17 KiB
C#
|
|
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<PatronType> 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<PatronType> 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<TableController> tables = new List<TableController>();
|
||
|
|
|
||
|
|
[SerializeField] public List<OrderPlacementController> orderPlacements;
|
||
|
|
|
||
|
|
// Game loop stats.
|
||
|
|
[HideInInspector] public int moneyEarned = 0;
|
||
|
|
[HideInInspector] public int currentTime = 0;
|
||
|
|
[HideInInspector] public int patrons = 0;
|
||
|
|
[HideInInspector] public Queue<Party> frontDeskQueue = new Queue<Party>();
|
||
|
|
[HideInInspector] public Queue<Order> orderBacklog = new Queue<Order>();
|
||
|
|
[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<int> partySizeDistribution =
|
||
|
|
new List<int>() { 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<float> gradeDistribution =
|
||
|
|
new List<float>() { 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<PatronType> patrons = new List<PatronType>() {
|
||
|
|
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<GameAudioController>();
|
||
|
|
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<ProCamera2D>();
|
||
|
|
proCamera2DPan = proCamera2D.GetComponent<ProCamera2DPanAndZoom>();
|
||
|
|
proCamera2DTransition = proCamera2D.GetComponent<ProCamera2DTransitionsFX>();
|
||
|
|
|
||
|
|
expandedCG = GameObject.FindGameObjectWithTag("ExpandedCG").GetComponent<Animator>();
|
||
|
|
expandedCG.gameObject.SetActive(false);
|
||
|
|
|
||
|
|
summaryController = GameObject.FindGameObjectWithTag("Summary").GetComponent<SummaryController>();
|
||
|
|
summaryController.gameObject.SetActive(false);
|
||
|
|
|
||
|
|
proCamera2DTransition.TransitionEnter();
|
||
|
|
|
||
|
|
int i = 1;
|
||
|
|
foreach (GameObject g in GameObject.FindGameObjectsWithTag("Table")) {
|
||
|
|
TableController table = g.GetComponent<TableController>();
|
||
|
|
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<TalentController>();
|
||
|
|
}
|
||
|
|
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<Order> 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();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|