using System.Collections; using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.UI; using UnityEngine.SceneManagement; public class StreamController : MonoBehaviour { // May be a flatUI arrow or pixel art arrow. // Pixel art is used in gameplay streams. // FlatUI arrows are used in cutaway scene streams. [SerializeField] public GameObject arrowPrefab; [SerializeField] public AudioClip hitClip; [SerializeField] public AudioClip missClip; [SerializeField] public AudioClip winClip; [SerializeField] public AudioClip loseClip; [SerializeField] public GameObject arrowGroup; [SerializeField] public AudioSource audioSource; [SerializeField] public List arrows; [SerializeField] public float resetDelay = 0.5f; [SerializeField] public bool debug = false; [SerializeField] public int debugLength = 5; [SerializeField] public float debugTimeLimit = 30f; [SerializeField] Button continueButton; StreamInfo info; bool isTicking = false; bool isOver = false; int passed = 0; [SerializeField] TextMeshProUGUI clearCountTMPro; [SerializeField] TextMeshProUGUI timeTMPro; [SerializeField] TextMeshProUGUI summaryTMPro; [SerializeField] public int numWon = 0; float startTimestamp = 0.0f; float freezeTimestamp = 0.0f; public class StreamInfo { public int numArrows { get; set; } public float timeLimit { get; set; } public float startDelay { get; set; } public float endDelay { get; set; } // Whether or not to auto-close the Stream after winning or losing. // Infinite scenes are for special enjoyment events. public bool infinite { get; set; } // In sudden death, missing one arrow fails the event even if // the timer is still running. public bool suddenDeath { get; set; } public Sprite beforeBG { get; set; } = null; public Sprite winBG { get; set; } = null; public Sprite loseBG { get; set; } = null; public AudioClip beforeClip { get; set; } = null; public AudioClip winClip { get; set; } = null; public AudioClip loseClip { get; set; } = null; } public void Init(StreamInfo info, bool wait = true) { this.info = info; passed = 0; // Calculate int[] keys for (int i = 0; i < info.numArrows; i++) { ArrowController arrow = Instantiate(arrowPrefab).GetComponent(); arrow.key = Random.Range(0, 4); arrow.Init(); arrow.transform.position = arrowGroup.transform.position; arrow.transform.SetParent(arrowGroup.transform); arrow.transform.localScale = new Vector3(1, 1, 1); arrows.Add(arrow); } if (wait) { Invoke("StartTimer", info.startDelay); } } public void Start() { if (debug) { Init(new StreamInfo { numArrows = debugLength, timeLimit = debugTimeLimit, startDelay = 1.0f, endDelay = 1.0f, infinite = true, suddenDeath = false }); } audioSource = GameObject.FindWithTag("SFX").GetComponent(); continueButton.gameObject.SetActive(false); } public void MakeGlow(int arrowIndex) { // TODO: implement (make the arrow at ArrowIndex glow) // use the shader thing } void StartTimer() { startTimestamp = Time.time; isTicking = true; MakeGlow(0); } void Reset() { Debug.Log("Reset"); if (isOver) return; foreach (ArrowController arrow in arrows) { Destroy(arrow.gameObject); } arrows.Clear(); Init(this.info, false); isTicking = true; MakeGlow(0); } char GetRank() { float winRate = numWon * info.numArrows * (1 + resetDelay); if (winRate > info.timeLimit * 3) return 'S'; if (winRate > info.timeLimit * 2.5) return 'A'; if (winRate > info.timeLimit * 2) return 'B'; if (winRate > info.timeLimit * 1.5) return 'C'; if (winRate > info.timeLimit * 1) return 'D'; return 'F'; } int GetScore(char rank) { return numWon * 30; /* switch (rank) { case 'S': return 300; case 'A': return 250; case 'B': return 200; case 'C': return 150; case 'D': return 100; default: return 0; } */ } public void GoBack() { GameData.GLOBAL.IncrementTime(); SceneManager.LoadScene("Manager"); } void AllowContinue() { continueButton.gameObject.SetActive(true); } void TimesUp() { Debug.Log("Time's Up!"); isOver = true; char rank = GetRank(); int score = GetScore(rank); summaryTMPro.SetText("Time's Up!\nRank: " + rank.ToString() + "\nPay: $" + score.ToString()); GameData.GLOBAL.money += score; GameData.GLOBAL.workedToday = true; Invoke("AllowContinue", 1.0f); } void TryHit(int key) { if (isOver) return; if (passed < info.numArrows) { if (arrows[passed].key == key) { // You hit the right arrow, move to the next one audioSource.PlayOneShot(hitClip); arrows[passed].Succeed(); passed += 1; } else { arrows[passed].Fail(); if (!info.suddenDeath) { audioSource.PlayOneShot(missClip); isTicking = false; Invoke("Reset", resetDelay); } else { // TODO: do a lose thing audioSource.PlayOneShot(loseClip); } } if (passed == info.numArrows) { // TODO: do a win thing audioSource.PlayOneShot(winClip); numWon += 1; isTicking = false; Invoke("Reset", resetDelay); } else { MakeGlow(passed); } } } // Update is called once per frame void Update() { if (isOver) return; // Process key presses if the delay is over if (isTicking) { if (Input.GetKeyDown(KeyCode.LeftArrow)) { Debug.Log("Left"); TryHit(0); } if (Input.GetKeyDown(KeyCode.DownArrow)) { Debug.Log("Down"); TryHit(1); } if (Input.GetKeyDown(KeyCode.UpArrow)) { Debug.Log("Up"); TryHit(2); } if (Input.GetKeyDown(KeyCode.RightArrow)) { Debug.Log("Right"); TryHit(3); } } clearCountTMPro.SetText(numWon.ToString()); } void FixedUpdate() { float time; if (isTicking) { time = info.timeLimit - (Time.time - startTimestamp); if (time <= 0f) { isTicking = false; time = 0f; TimesUp(); } freezeTimestamp = time; } else { time = freezeTimestamp; } timeTMPro.SetText(time.ToString("#0.00")); } }