using UdonSharp; using UnityEngine; using UnityEngine.UI; using VRC.SDK3.Data; using VRC.SDK3.UdonNetworkCalling; using VRC.Udon.Common.Interfaces; using VRC.SDKBase; using System.Linq; using VRC.SDK3.StringLoading; using System.Runtime.CompilerServices; using TMPro; public enum QuestionType { None, MultipleChoice, LightningRound, TheChase, FinalRound, Tiebreaker } public enum MusicEventType { None, WhereInTheWorld, RockapellaIdent } public enum SFXEventType { None, Buzzer } [UdonBehaviourSyncMode(BehaviourSyncMode.Manual)] public class GameManager : UdonSharpBehaviour { [SerializeField] private GameObject _AdminPanel; [UdonSynced] private bool BuzzInAllowed = false; [UdonSynced] private bool[] PlayerBuzzInAllowed; [UdonSynced] private int _BuzzedInPlayer = -1; private DataDictionary _CurrentQuestion; [UdonSynced] private QuestionType _CurrentQuestionType = QuestionType.MultipleChoice; [UdonSynced] private int _CurrentQuestionStage = 0; [UdonSynced] private int _CurrentQuestionCorrectResponse = -1; public PlayerPodium[] PlayerPodiums; [UdonSynced] public VRCUrl QuestionURL; [Header("Multiple Choice Card UI")] [SerializeField] private TextMeshProUGUI _InfoHeader; [SerializeField] private TextMeshProUGUI[] _InfoClues; [SerializeField] private TextMeshProUGUI[] _InfoChoices; [SerializeField] private Image[] _InfoChoiceButtons; [SerializeField] private TextMeshProUGUI _Answer; [Header("Audio")] [SerializeField] private AudioClip BuzzerSound = null; [SerializeField] private AudioClip WhereInTheWorld = null; [SerializeField] private AudioClip RockapellaIdent = null; [SerializeField] private AudioSource MusicPlayer = null; [SerializeField] private AudioSource SFXPlayer = null; void Start() { PlayerBuzzInAllowed = new bool[PlayerPodiums.Length]; ResetBuzzers(); // Download our test question. VRCStringDownloader.LoadUrl(QuestionURL, (IUdonEventReceiver)this); } public override void OnStringLoadSuccess(IVRCStringDownload DownloadedString) { string JSONString = DownloadedString.Result; if (VRCJson.TryDeserializeFromJson(JSONString, out DataToken JSONResult)) { if (JSONResult.TokenType == TokenType.DataDictionary) { NetworkCalling.SendCustomNetworkEvent((IUdonEventReceiver)this, NetworkEventTarget.All, nameof(UpdateInfoCard), JSONString); } } else { Debug.LogError("The question should be a Dictionary type."); } } [NetworkCallable] public void UpdateInfoCard(string Data) { DisableChoiceCards(); if (VRCJson.TryDeserializeFromJson(Data, out DataToken ResultToken)) { _CurrentQuestion = ResultToken.DataDictionary; string Type = _CurrentQuestion["Type"].ToString(); if (Type == "Multiple Choice") { NewMultipleChoiceQuestion(); } } } private void NewMultipleChoiceQuestion() { _CurrentQuestionType = QuestionType.MultipleChoice; _CurrentQuestionStage = 0; _InfoHeader.text = _CurrentQuestion["Type"].ToString(); _InfoClues[0].text = _CurrentQuestion["Clues"].DataList[0].ToString(); _InfoClues[1].text = _CurrentQuestion["Clues"].DataList[1].ToString(); _InfoClues[2].text = _CurrentQuestion["Clues"].DataList[2].ToString(); DataList Choices = _CurrentQuestion["Choices"].DataList; _InfoChoices[0].text = Choices[0].ToString(); _InfoChoices[1].text = Choices[1].ToString(); _InfoChoices[2].text = Choices[2].ToString(); _CurrentQuestionCorrectResponse = (int)_CurrentQuestion["Correct Response"].Double; _Answer.text = Choices[_CurrentQuestionCorrectResponse].ToString(); } private void MultipleChoiceRevealChoices() { Debug.LogError("Advancing the current question stage now."); SendCustomEvent(nameof(MultipleChoiceRevealChoice1)); SendCustomEventDelayedSeconds(nameof(MultipleChoiceRevealChoice2), 1.25f); SendCustomEventDelayedSeconds(nameof(MultipleChoiceRevealChoice3), 2.5f); } public void MultipleChoiceRevealChoice1() { _InfoChoiceButtons[0].color = (_CurrentQuestionCorrectResponse == 0) ? Color.green : Color.red; } public void MultipleChoiceRevealChoice2() { _InfoChoiceButtons[1].color = (_CurrentQuestionCorrectResponse == 1) ? Color.green : Color.red; } public void MultipleChoiceRevealChoice3() { _InfoChoiceButtons[2].color = (_CurrentQuestionCorrectResponse == 2) ? Color.green : Color.red; DataList Choices = _CurrentQuestion["Choices"].DataList; EnableChoiceCards(); for (int i = 0; i < PlayerPodiums.Length; i++) { NetworkCalling.SendCustomNetworkEvent((IUdonEventReceiver)PlayerPodiums[i], NetworkEventTarget.Owner, "SetCardChoices", Choices[0].ToString(), Choices[1].ToString(), Choices[2].ToString()); } } public void EnableChoiceCards() { for (int i = 0; i < PlayerPodiums.Length; i++) { NetworkCalling.SendCustomNetworkEvent((IUdonEventReceiver)PlayerPodiums[i], NetworkEventTarget.All, "EnableChoiceCards", true); } } public void DisableChoiceCards() { for (int i = 0; i < PlayerPodiums.Length; i++) { NetworkCalling.SendCustomNetworkEvent((IUdonEventReceiver)PlayerPodiums[i], NetworkEventTarget.All, "EnableChoiceCards", false); } } public void EnableBuzzInPeriodForAllPlayers() { BuzzInAllowed = true; ResetBuzzers(); } [NetworkCallable] public void PlayerBuzzedIn(int PlayerNumber) { int PlayerIndex = PlayerNumber - 1; if (!BuzzInAllowed || !PlayerBuzzInAllowed[PlayerIndex]) { return; } // Prevent new buzz-ins and store which player is currently buzzed in. BuzzInAllowed = false; PlayerBuzzInAllowed[PlayerIndex] = false; _BuzzedInPlayer = PlayerNumber; RequestSerialization(); NetworkCalling.SendCustomNetworkEvent( (IUdonEventReceiver)PlayerPodiums[PlayerIndex], NetworkEventTarget.All, "EnableBuzzInEffect", true); // Play the buzzer sound globally. SendCustomNetworkEvent(NetworkEventTarget.All, nameof(PlaySFX), SFXEventType.Buzzer); } public void WaitForBuzzInsWithoutLastPlayer() { BuzzInAllowed = true; NetworkCalling.SendCustomNetworkEvent( (IUdonEventReceiver)PlayerPodiums[_BuzzedInPlayer - 1], NetworkEventTarget.All, "EnableBuzzInEffect", false); _BuzzedInPlayer = -1; RequestSerialization(); } public void EndBuzzInPeriod() { BuzzInAllowed = false; ResetBuzzers(); } public void ResetBuzzers() { for (int i = 0; i < PlayerPodiums.Length; i++) { PlayerBuzzInAllowed[i] = true; } _BuzzedInPlayer = -1; } private void AdvanceQuestionStage() { _CurrentQuestionStage++; switch(_CurrentQuestionType) { case QuestionType.MultipleChoice: AdvanceMultipleChoiceStage(); break; } RequestSerialization(); } private void AdvanceMultipleChoiceStage() { switch(_CurrentQuestionStage) { case 1: MultipleChoiceRevealChoices(); break; default: return; } } [NetworkCallable] public void PlayMusic(MusicEventType MusicEvent) { MusicPlayer.Stop(); switch (MusicEvent) { case MusicEventType.WhereInTheWorld: MusicPlayer.clip = WhereInTheWorld; break; case MusicEventType.RockapellaIdent: MusicPlayer.clip = RockapellaIdent; break; default: MusicPlayer.clip = null; break; } if (MusicPlayer.clip != null) MusicPlayer.Play(); } [NetworkCallable] public void PlaySFX(SFXEventType SFXEvent) { SFXPlayer.Stop(); switch (SFXEvent) { case SFXEventType.Buzzer: SFXPlayer.clip = BuzzerSound; break; default: SFXPlayer.clip = null; break; } if (SFXPlayer.clip != null) SFXPlayer.Play(); } public override void OnPickupUseDown() { AdvanceQuestionStage(); base.OnPickupUseDown(); } }