using UdonSharp; using UnityEngine; using VRC.SDK3.Data; using VRC.SDK3.UdonNetworkCalling; using VRC.Udon.Common.Interfaces; using VRC.SDKBase; using VRC.SDK3.StringLoading; public enum PresentationMedium { VideoClue, VideoMusicClue, ACMECrimenetComputer } [UdonBehaviourSyncMode(BehaviourSyncMode.Manual)] public class GameManagerRound1 : GameManagerBase { [Space] [SerializeField] private VRCUrl _TiebreakerData; [UdonSynced] protected bool _GameHasBegun = false; [UdonSynced] private int _QuestionIndex = 0; [UdonSynced] private int _QuestionStage = 0; [UdonSynced] private int _QuestionSubstage = 0; [UdonSynced] private int _QuestionCorrectResponse = 0; [UdonSynced] private RoundSegmentType _CurrentQuestionType = RoundSegmentType.BetweenSegments; private DataList _QuestionsList = new DataList(); private DataDictionary _CurrentQuestion; [UdonSynced] private bool _BuzzInAllowed = false; [UdonSynced] private bool[] _PlayerBuzzInAllowed; [UdonSynced] private int _BuzzedInPlayer = 0; [UdonSynced] private int[] _FinalRoundPlayersSortedByScore; [UdonSynced] private int[] _TiebreakerPlayerNumbers; [Space] [SerializeField] private PlayerPodium[] _PlayerPodiums; [Space, Header("Props")] [SerializeField] private CaseVideoSyncPlayer _VideoPlayer; [SerializeField] private ACMECrimenetComputer _ACMECrimenetComputer; [SerializeField] private VideoMusicClueSkateboard _VideoMusicClueSkateboard; [SerializeField] private Modem _Modem; [SerializeField] private ArrivalDisplay _ArrivalDisplay; [Space, Header("Effects")] [SerializeField] private Animator _LightningRoundAnimator; public override void InitialiseGameMode() { base.InitialiseGameMode(); _GameHasBegun = false; _QuestionIndex = 0; _QuestionStage = 0; _PlayerBuzzInAllowed = new bool[_PlayerPodiums.Length]; _Modem.Activate(false); _ArrivalDisplay.Activate(false); EnableAudienceSilencer(true); _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlayMusic", MusicEventType.WhereInTheWorld); SendCustomEventDelayedSeconds(nameof(PlaySecondPartOfThemeMusic), 3.6666666666f); DisableInteraction(); RequestSerialization(); } public override void DeinitialiseGameMode() { for (int i = 0; i < _PlayerPodiums.Length; i++) { _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.All, "ResetPodium"); } base.DeinitialiseGameMode(); } public override void SetOwnershipOfObjects(VRCPlayerApi NewOwner) { Networking.SetOwner(NewOwner, _VideoPlayer.gameObject); Networking.SetOwner(NewOwner, _ACMECrimenetComputer.gameObject); Networking.SetOwner(NewOwner, _VideoMusicClueSkateboard.gameObject); Networking.SetOwner(NewOwner, _Modem.gameObject); Networking.SetOwner(NewOwner, _ArrivalDisplay.gameObject); base.SetOwnershipOfObjects(NewOwner); } public void PlaySecondPartOfThemeMusic() { EnableInteraction("Start Game"); _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlayMusicLoop", MusicEventType.OpeningLoop); } public override void LoadQuestionData(DataToken Data) { _QuestionsList.Clear(); HostCardBetweenRoundsInterface Interface = (HostCardBetweenRoundsInterface)GetHostCardInterface(RoundSegmentType.BetweenSegments); DataList DataDict = Data.DataList; for (int i = 0; i < DataDict.Count; i++) { if (DataDict[i].TokenType == TokenType.DataDictionary) { _QuestionsList.Add(DataDict[i]); } } if (_QuestionsList.Count == 0) { Interface.HeaderUI.text = "Error loading case file"; Interface.CommentUI.text = "Unable to find any questions. Ensure the root array elements are all objects."; return; } Interface.HeaderUI.text = _CaseManager.GetCaseTitle() + " has been loaded."; Interface.CommentUI.text = _CaseManager.GetCaseDescription(); TryIntroVideoLoad(); _GameHasBegun = false; } public void TryIntroVideoLoad() { int IntroVideoIndex = _CaseManager.GetIntroVideo(); if (IntroVideoIndex >= 0) { _VideoPlayer.VideoIndex = IntroVideoIndex; } else { Debug.LogWarning("[GameManagerRound1] Intro video index is currently invalid. Retrying..."); SendCustomEventDelayedSeconds(nameof(TryIntroVideoLoad), 1.0f); } } private void PlayIntroVideo() { _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "FadeOutMusic"); HostCardBetweenRoundsInterface Interface = (HostCardBetweenRoundsInterface)GetHostCardInterface(RoundSegmentType.BetweenSegments); Interface.HeaderUI.text = "Playing intro video..."; Interface.CommentUI.text = _CaseManager.GetIntroVideoTranscript(); for (int i = 0; i < _PlayerPodiums.Length; i++) { _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.Owner, "ShowAuxiliaryVideoScreen", true); } _VideoPlayer.PlayVideo = true; EnableInteraction("Assign Points"); } private void InitialiseQuestion() { _CurrentQuestion = _QuestionsList[_QuestionIndex].DataDictionary; // Again, why does this work, but not just casting to an enum? _CurrentQuestionType = (RoundSegmentType)(int)_CurrentQuestion["Type"].Number; _QuestionStage = 0; LoadMapsIntoQueue(); } private void LoadMapsIntoQueue() { if (_CurrentQuestion.ContainsKey("Maps") && _CurrentQuestion["Maps"].TokenType == TokenType.DataList) { DataList Maps = _CurrentQuestion["Maps"].DataList; int[] MapIndices = new int[Maps.Count]; for (int i = 0; i < MapIndices.Length; i++) { MapIndices[i] = (int)Maps[i].Number; } _VideoPlayer.SendCustomNetworkEvent(NetworkEventTarget.All, "QueueMapDownloads", MapIndices); } } private void AssignStarterPoints() { InitialiseQuestion(); for (int i = 0; i < _PlayerPodiums.Length; i++) { _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.All, "DisplayScore"); } _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlaySFXAtPitch", SFXEventType.Ding, AudioManager.D6); DisableChoiceCards(); DisableRiskCards(); DisableBuzzers(); ShowBetweenQuestionsInterface(); _VideoPlayer.ShowScreen = ClueScreenType.Blank; _GameHasBegun = true; } private void InitialiseCluePresentation() { for (int i = 0; i < _PlayerPodiums.Length; i++) { _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.Owner, "ShowAuxiliaryVideoScreen", true); } HostCardBetweenRoundsInterface Interface; if (!_CurrentQuestion.ContainsKey("Presentation") || _CurrentQuestion["Presentation"].TokenType != TokenType.DataDictionary || !_CurrentQuestion["Presentation"].DataDictionary.ContainsKey("Medium")) { Interface = (HostCardBetweenRoundsInterface)GetHostCardInterface(RoundSegmentType.BetweenSegments); Interface.HeaderUI.text = "No special presentation available. Continuing on..."; Interface.CommentUI.text = ""; EnableInteraction("Reveal Question"); return; } DataDictionary Presentation = _CurrentQuestion["Presentation"].DataDictionary; switch ((PresentationMedium)(int)Presentation["Medium"].Number) { case PresentationMedium.VideoClue: InitialiseVideoClue(Presentation); break; case PresentationMedium.VideoMusicClue: InitialiseVideoMusicClue(Presentation); break; case PresentationMedium.ACMECrimenetComputer: InitialiseACMECrimenetComputer(Presentation); break; } } private void InitialiseVideoClue(DataDictionary Presentation) { DataList Videos = Presentation["Videos"].DataList; if (_QuestionSubstage < Videos.Count) { HostCardBetweenRoundsInterface Interface = (HostCardBetweenRoundsInterface)GetHostCardInterface(RoundSegmentType.BetweenSegments); Interface.HeaderUI.text = "Playing video..."; if (Presentation.ContainsKey("Video Transcripts")) { Interface.CommentUI.text = Presentation["Video Transcripts"].DataList[_QuestionSubstage].String; } else { Interface.CommentUI.text = ""; } _VideoPlayer.PlayVideo = true; _QuestionSubstage++; if (_QuestionSubstage < Videos.Count) { EnableInteraction("Play Video"); } else { EnableInteraction("Reveal Question"); } } else { MultipleChoiceRevealQuestion(); } RequestSerialization(); } private void InitialiseVideoMusicClue(DataDictionary Presentation) { switch (_QuestionSubstage) { case 0: { _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlaySFX", SFXEventType.VideoMusicClue); _VideoMusicClueSkateboard.SendCustomNetworkEvent(NetworkEventTarget.All, "SwooceRightIn"); string Header = "Error Reading Data"; string Clue = "just wing it i guess idk"; if (Presentation.ContainsKey("Video Artist") && Presentation.ContainsKey("Note")) { Header = Presentation["Video Artist"].String; Clue = Presentation["Note"].String; } _VideoMusicClueSkateboard.SendCustomNetworkEvent(NetworkEventTarget.All, "SetCardTexts", Header, Clue); _QuestionSubstage++; EnableInteraction("Play Video"); } break; case 1: { if (Presentation.ContainsKey("Videos")) { HostCardBetweenRoundsInterface Interface = (HostCardBetweenRoundsInterface)GetHostCardInterface(RoundSegmentType.BetweenSegments); Interface.HeaderUI.text = "Playing video..."; if (Presentation.ContainsKey("Video Artist")) { Interface.CommentUI.text = "Artist: " + Presentation["Video Artist"].String; } else { Interface.CommentUI.text = ""; } DataList Videos = Presentation["Videos"].DataList; _VideoPlayer.PlayVideo = true; } _QuestionSubstage++; EnableInteraction("Reveal Question"); } break; default: { _VideoMusicClueSkateboard.SendCustomNetworkEvent(NetworkEventTarget.All, "ResetSkateboard"); MultipleChoiceRevealQuestion(); } break; } RequestSerialization(); } private void InitialiseACMECrimenetComputer(DataDictionary Presentation) { if (_QuestionSubstage < 1) { HostCardBetweenRoundsInterface Interface = (HostCardBetweenRoundsInterface)GetHostCardInterface(RoundSegmentType.BetweenSegments); Interface.HeaderUI.text = "Check the computer for clues that need translating."; Interface.CommentUI.text = ""; string UntranslatedText = ""; string TranslatedText = ""; if (Presentation.ContainsKey("Untranslated Text Placeholder") && Presentation.ContainsKey("Translated Text")) { UntranslatedText = Presentation["Untranslated Text Placeholder"].String; TranslatedText = Presentation["Translated Text"].String; } _ACMECrimenetComputer.SendCustomNetworkEvent(NetworkEventTarget.All, "SetCardTexts", UntranslatedText, TranslatedText); _ACMECrimenetComputer.SendCustomNetworkEvent(NetworkEventTarget.All, "SwooceRightIn"); _QuestionSubstage++; EnableInteraction("Reveal Question"); } else { _ACMECrimenetComputer.SendCustomNetworkEvent(NetworkEventTarget.All, "ResetComputer"); MultipleChoiceRevealQuestion(); } RequestSerialization(); } private void MultipleChoiceRevealQuestion() { _QuestionSubstage = 0; _VideoPlayer.SubMapIndex = 0; for (int i = 0; i < _PlayerPodiums.Length; i++) { _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.All, "ShowAuxiliaryVideoScreen", false); _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.Owner, "ShowAuxiliaryMapScreen", true); } HostCardMultipleChoiceInterface Interface = (HostCardMultipleChoiceInterface)GetHostCardInterface(RoundSegmentType.MultipleChoice); ResetMultipleChoiceInterface(Interface); Interface.HeaderUI.text = RoundSegmentTypeToString(_CurrentQuestionType); DataList ClueStrings = _CurrentQuestion["Clues"].DataList; for (int i = 0; i < Interface.CluesUI.Length && i < ClueStrings.Count; i++) { Interface.CluesUI[i].text = ClueStrings[i].ToString(); } DataList Choices = _CurrentQuestion["Choices"].DataList; for (int i = 0; i < Choices.Count && i < Interface.ChoiceUI.Length; i++) { Interface.ChoiceUI[i].text = Choices[i].ToString(); } _QuestionCorrectResponse = (int)_CurrentQuestion["Correct Response"].Number; EnableInteraction("Reveal Choice 1"); } private void MultipleChoiceRevealChoices() { SendCustomEvent(nameof(MultipleChoiceRevealChoice1)); SendCustomEventDelayedSeconds(nameof(MultipleChoiceRevealChoice2), 1.25f); SendCustomEventDelayedSeconds(nameof(MultipleChoiceRevealChoice3), 2.5f); } private void MultipleChoiceRevealChoice1() { _VideoPlayer.SubMapIndex = 1; HostCardMultipleChoiceInterface Interface = (HostCardMultipleChoiceInterface)GetHostCardInterface(RoundSegmentType.MultipleChoice); Interface.ChoiceButtonImages[0].color = (_QuestionCorrectResponse == 1) ? Color.green : Color.red; _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlaySFXAtPitch", SFXEventType.Ding, AudioManager.As5); EnableInteraction("Reveal Choice 2"); } private void MultipleChoiceRevealChoice2() { _VideoPlayer.SubMapIndex = 2; HostCardMultipleChoiceInterface Interface = (HostCardMultipleChoiceInterface)GetHostCardInterface(RoundSegmentType.MultipleChoice); Interface.ChoiceButtonImages[1].color = (_QuestionCorrectResponse == 2) ? Color.green : Color.red; _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlaySFXAtPitch", SFXEventType.Ding, AudioManager.C6); EnableInteraction("Reveal Choice 3"); } private void MultipleChoiceRevealChoice3() { _VideoPlayer.SubMapIndex = 3; HostCardMultipleChoiceInterface Interface = (HostCardMultipleChoiceInterface)GetHostCardInterface(RoundSegmentType.MultipleChoice); Interface.ChoiceButtonImages[2].color = (_QuestionCorrectResponse == 3) ? Color.green : Color.red; DataList Choices = _CurrentQuestion["Choices"].DataList; EnableChoiceCards(); // Complex per-podium randomiser, to prevent peeking Random.InitState(Networking.GetServerTimeInMilliseconds()); for (int i = 0; i < _PlayerPodiums.Length; i++) { int[] Indices = { 0, 1, 2 }; int[] ChoiceOrder = { -1, -1, -1 }; int Choice1Index = Indices[Random.Range(0, 3)]; ChoiceOrder[0] = Choice1Index; Indices[Choice1Index] = -1; int Choice2Index = -1; while (Choice2Index == -1) { Choice2Index = Indices[Random.Range(0, 3)]; } ChoiceOrder[1] = Choice2Index; Indices[Choice2Index] = -1; int Choice3Index = -1; while (Choice3Index == -1) { Choice3Index = Indices[Random.Range(0, 3)]; } ChoiceOrder[2] = Choice3Index; Indices[Choice3Index] = -1; string[] ChoiceStrings = { Choices[0].ToString(), Choices[1].ToString(), Choices[2].ToString() }; _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.All, "SetCardChoices", ChoiceStrings, ChoiceOrder); } _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlaySFXAtPitch", SFXEventType.Ding, AudioManager.D6); EnableInteraction("Lock Answers"); } private void MultipleChoiceLockAnswers() { for (int i = 0; i < _PlayerPodiums.Length; i++) { _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.All, "LockInChoice"); _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.Owner, "ShowAuxiliaryMapScreen", false); } HostCardMultipleChoiceInterface Interface = (HostCardMultipleChoiceInterface)GetHostCardInterface(RoundSegmentType.MultipleChoice); Interface.HeaderUI.text = "LOCKED IN"; for (int i = 0; i < Interface.CluesUI.Length; i++) { Interface.CluesUI[i].text = ""; } for (int i = 0; i < Interface.ChoiceUI.Length; i++) { if (i != (_QuestionCorrectResponse - 1)) { Interface.ChoiceUI[i].text = ""; } } EnableInteraction("Reveal Answers And Assign Points"); } private void MultipleChoiceRevealAnswersAndAssignPoints() { for (int i = 0; i < _PlayerPodiums.Length; i++) { _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.Owner, "ShowAuxiliaryMapScreen", true); } HostCardMultipleChoiceInterface Interface = (HostCardMultipleChoiceInterface)GetHostCardInterface(RoundSegmentType.MultipleChoice); Interface.HeaderUI.text = "ANSWER REVEALED"; bool IsSomeoneCorrect = false; for (int i = 0; i < _PlayerPodiums.Length; i++) { bool Correct = _PlayerPodiums[i].VerifyMultipleChoiceResponse(_QuestionCorrectResponse); IsSomeoneCorrect = IsSomeoneCorrect ? true : Correct; } if (IsSomeoneCorrect) { _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlaySFX", SFXEventType.Round1Correct); } else { _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlaySFX", SFXEventType.Round1Incorrect); } _VideoPlayer.FlashCorrectAnswer = true; EnableInteraction("Next Question"); } private void BeginLightningRound() { SendCustomNetworkEvent(NetworkEventTarget.All, nameof(PlayLightningRoundAnimation)); HostCardLightningRoundInterface Interface = (HostCardLightningRoundInterface)GetHostCardInterface(RoundSegmentType.LightningRound); Interface.HeaderUI.text = RoundSegmentTypeToString(_CurrentQuestionType) + " | " + _CurrentQuestion["Location"].ToString(); for (int i = 0; i < Interface.ChoiceUI.Length && i < Interface.ChoiceButtons.Length; i++) { Interface.ChoiceUI[i].text = ""; } Interface.QuestionUI.text = "All of these questions are worth 5 Acme Crimebucks. These questions are all about " + _CaseManager.GetCrookName() + "'s most recent whereabouts, which is " + _CurrentQuestion["Location"].ToString() + ". Hands on your buzzers. Here's the first question..."; EnableBuzzers(); EnableInteraction("First Question"); } [NetworkCallable] public void PlayLightningRoundAnimation() { _LightningRoundAnimator.SetBool("Lightning", true); _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlaySFX", SFXEventType.Thunder); SendCustomEventDelayedSeconds(nameof(ResetLightningRoundAnimation), 5.0f); } public void ResetLightningRoundAnimation() { _LightningRoundAnimator.SetBool("Lightning", false); } private void LightningRoundCluesLoop() { DataList QuestionsList = _CurrentQuestion["Questions"].DataList; if (_QuestionSubstage >= QuestionsList.Count) { _QuestionSubstage = 0; EnableInteraction("End Lightning Round"); return; } DataDictionary CurrentQuestion = _CurrentQuestion["Questions"].DataList[_QuestionSubstage].DataDictionary; HostCardLightningRoundInterface Interface = (HostCardLightningRoundInterface)GetHostCardInterface(RoundSegmentType.LightningRound); Interface.QuestionUI.text = CurrentQuestion["Question"].ToString(); DataList Choices = CurrentQuestion["Choices"].DataList; for (int i = 0; i < Choices.Count && i < Interface.ChoiceUI.Length; i++) { Interface.ChoiceUI[i].text = Choices[i].ToString(); } _QuestionCorrectResponse = (int)CurrentQuestion["Correct Response"].Number; for (int i = 0; i < Interface.ChoiceButtons.Length && i < Interface.ChoiceButtonImages.Length; i++) { Interface.ChoiceButtonImages[i].color = (_QuestionCorrectResponse == (i + 1)) ? Color.green : Color.red; Interface.ChoiceButtons[i].interactable = true; } Interface.OtherButton.interactable = true; EnableBuzzInPeriodForAllPlayers(); } private void LightningRoundCheckAnswer(int Answer) { if (_QuestionCorrectResponse == Answer) { HostCardLightningRoundInterface Interface = (HostCardLightningRoundInterface)GetHostCardInterface(RoundSegmentType.LightningRound); for (int i = 0; i < Interface.ChoiceButtons.Length; i++) { Interface.ChoiceButtons[i].interactable = false; } Interface.OtherButton.interactable = false; int PodiumIndex = _BuzzedInPlayer - 1; if (PodiumIndex >= 0 && PodiumIndex < _PlayerPodiums.Length) { _PlayerPodiums[PodiumIndex].SendCustomNetworkEvent(NetworkEventTarget.Owner, "AdjustScore", 5); } EndBuzzInPeriod(); _QuestionSubstage++; AdvanceQuestion(); } else { WaitForBuzzInsWithoutLastPlayer(); } } public void LightningRoundCheckAnswer1() { LightningRoundCheckAnswer(1); } public void LightningRoundCheckAnswer2() { LightningRoundCheckAnswer(2); } public void LightningRoundCheckAnswer3() { LightningRoundCheckAnswer(3); } public void LightningRoundOtherOptionChosen() { if (_BuzzedInPlayer <= 0) { EndBuzzInPeriod(); _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlaySFX", SFXEventType.Round1Incorrect); } else { WaitForBuzzInsWithoutLastPlayer(); } } private void BeginTheChase() { HostCardTheChaseInterface Interface = (HostCardTheChaseInterface)GetHostCardInterface(RoundSegmentType.TheChase); Interface.HeaderUI.text = RoundSegmentTypeToString(_CurrentQuestionType); Interface.ClueUI.text = ""; for (int i = 0; i < Interface.ChoiceUI.Length && i < Interface.ChoiceButtons.Length; i++) { Interface.ChoiceUI[i].text = ""; } EnableBuzzers(); EnableInteraction("Play The Music"); } private void PlayTheChaseMusic() { HostCardBetweenRoundsInterface Interface = (HostCardBetweenRoundsInterface)GetHostCardInterface(RoundSegmentType.BetweenSegments); _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlayMusic", MusicEventType.TheChase); Interface.HeaderUI.text = RoundSegmentTypeToString(RoundSegmentType.TheChase); Interface.CommentUI.text = "All of these questions are worth 5 Acme Crimebucks. Hands on your buzzers. Listen carefully. Here we go."; EnableBuzzers(); EnableInteraction("Here We Go"); } private void TheChaseCluesLoop() { for (int i = 0; i < _PlayerPodiums.Length; i++) { _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.Owner, "ShowAuxiliaryMapScreen", true); } DataList CluesList = _CurrentQuestion["Clues"].DataList; if (_QuestionSubstage >= CluesList.Count) { _QuestionSubstage = 0; EnableInteraction("End The Chase"); return; } DataDictionary CurrentClue = _CurrentQuestion["Clues"].DataList[_QuestionSubstage].DataDictionary; HostCardTheChaseInterface Interface = (HostCardTheChaseInterface)GetHostCardInterface(RoundSegmentType.TheChase); Interface.HeaderUI.text = "The Chase" + (_CurrentQuestion.ContainsKey("Location") ? (" | " + _CurrentQuestion["Location"].String) : ""); Interface.ClueUI.text = CurrentClue["Clue"].ToString(); DataList Choices = CurrentClue["Choices"].DataList; for (int i = 0; i < Choices.Count && i < Interface.ChoiceUI.Length; i++) { Interface.ChoiceUI[i].text = Choices[i].ToString(); } Interface.OtherButton.interactable = true; _QuestionCorrectResponse = (int)CurrentClue["Correct Response"].Number; for (int i = 0; i < Interface.ChoiceButtons.Length && i < Interface.ChoiceButtonImages.Length; i++) { Interface.ChoiceButtonImages[i].color = (_QuestionCorrectResponse == (i + 1)) ? Color.green : Color.red; Interface.ChoiceButtons[i].interactable = true; } SendCustomEventDelayedSeconds(nameof(DisplayNextChaseMap), 1.25f); EnableBuzzInPeriodForAllPlayers(); } public void DisplayNextChaseMap() { _VideoPlayer.SubMapIndex = _QuestionSubstage * 2; } private void TheChaseCheckAnswer(int Answer) { if (_QuestionCorrectResponse == Answer) { int PodiumIndex = _BuzzedInPlayer - 1; if (PodiumIndex >= 0 && PodiumIndex < _PlayerPodiums.Length) { _PlayerPodiums[PodiumIndex].SendCustomNetworkEvent(NetworkEventTarget.Owner, "AdjustScore", 5); } } TheChaseEndClue(); } public void TheChaseCheckAnswer1() { TheChaseCheckAnswer(1); } public void TheChaseCheckAnswer2() { TheChaseCheckAnswer(2); } public void TheChaseCheckAnswer3() { TheChaseCheckAnswer(3); } public void TheChaseEndClue() { _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlaySFXAtPitch", SFXEventType.Ding, AudioManager.D6); HostCardTheChaseInterface Interface = (HostCardTheChaseInterface)GetHostCardInterface(RoundSegmentType.TheChase); for (int i = 0; i < Interface.ChoiceButtons.Length; i++) { Interface.ChoiceButtons[i].interactable = false; } Interface.OtherButton.interactable = false; EndBuzzInPeriod(); _VideoPlayer.SubMapIndex++; _QuestionSubstage++; AdvanceQuestion(); } private void IntroduceFinalRound() { HostCardBetweenRoundsInterface Interface = (HostCardBetweenRoundsInterface)GetHostCardInterface(RoundSegmentType.BetweenSegments); Interface.HeaderUI.text = "The Final Round"; Interface.CommentUI.text = "- Now it's time for the final clue of this round, which means you have to decide how many of your ACME Crimebucks you want to risk.\n" + "- If you're right, we'll add that amount to your score.\n" + "- If you're not right, we'll subtract that amount from your score.\n" + "- You can risk 0, 10, 20, 30, 40, or 50 ACME Crimebucks.\n\n" + "- Choose how much to risk by placing your wager to the other side of your podium."; LoadMapsIntoQueue(); EnableInteraction("Show Map Preview"); } private void FinalRoundShowMapPreview() { _VideoPlayer.SubMapIndex = 3; EnableRiskCards(); HostCardBetweenRoundsInterface Interface = (HostCardBetweenRoundsInterface)GetHostCardInterface(RoundSegmentType.BetweenSegments); Interface.HeaderUI.text = "Show The Map Preview"; Interface.CommentUI.text = "- Here's a portion of the world where we think " + _CaseManager.GetCrookName() + " is headed with " + _CaseManager.GetLoot() + ".\n" + "- If you think you know a lot about this part of the world, you may want to risk a lot.\n" + "- If you don't know very much about it, though, you may not want to risk quite so much.\n\n" + "So, take a look at the map, and think about it."; EnableInteraction("Think About It"); } private void FinalRoundPlayThinkingMusic() { HostCardBetweenRoundsInterface Interface = (HostCardBetweenRoundsInterface)GetHostCardInterface(RoundSegmentType.BetweenSegments); Interface.HeaderUI.text = "Think about it..."; Interface.CommentUI.text = ""; _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlayMusic", MusicEventType.ThinkAboutIt); SendCustomEventDelayedSeconds(nameof(ThinkAboutItCountdownFinished), 15.0f); } public void ThinkAboutItCountdownFinished() { for (int i = 0; i < _PlayerPodiums.Length; i++) { _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.Owner, "ShowAuxiliaryMapScreen", false); } HostCardMultipleChoiceInterface Interface = (HostCardMultipleChoiceInterface)GetHostCardInterface(RoundSegmentType.FinalRound); Interface.HeaderUI.text = RoundSegmentTypeToString(_CurrentQuestionType); DataList ClueStrings = _CurrentQuestion["Clues"].DataList; for (int i = 0; i < Interface.CluesUI.Length && i < ClueStrings.Count; i++) { Interface.CluesUI[i].text = ClueStrings[i].ToString(); } DataList Choices = _CurrentQuestion["Choices"].DataList; for (int i = 0; i < Choices.Count && i < Interface.ChoiceUI.Length; i++) { Interface.ChoiceUI[i].text = Choices[i].ToString(); } _QuestionCorrectResponse = (int)_CurrentQuestion["Correct Response"].Number; for (int i = 0; i < _PlayerPodiums.Length; i++) { _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.All, "LockInRisk"); } ShowBetweenQuestionsInterface(); CreateClueSpecificHostInteractionText(_CurrentQuestion["Presentation"].DataDictionary); } private void FinalRoundRevealChoices() { SendCustomEvent(nameof(FinalRoundRevealChoice1)); SendCustomEventDelayedSeconds(nameof(FinalRoundRevealChoice2), 1.25f); SendCustomEventDelayedSeconds(nameof(FinalRoundRevealChoice3), 2.5f); } private void FinalRoundRevealChoice1() { _VideoPlayer.SubMapIndex = 1; HostCardMultipleChoiceInterface Interface = (HostCardMultipleChoiceInterface)GetHostCardInterface(RoundSegmentType.FinalRound); Interface.ChoiceButtonImages[0].color = (_QuestionCorrectResponse == 1) ? Color.green : Color.red; _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlaySFXAtPitch", SFXEventType.Ding, AudioManager.As5); EnableInteraction("Reveal Choice 2"); } private void FinalRoundRevealChoice2() { _VideoPlayer.SubMapIndex = 2; HostCardMultipleChoiceInterface Interface = (HostCardMultipleChoiceInterface)GetHostCardInterface(RoundSegmentType.FinalRound); Interface.ChoiceButtonImages[1].color = (_QuestionCorrectResponse == 2) ? Color.green : Color.red; _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlaySFXAtPitch", SFXEventType.Ding, AudioManager.C6); EnableInteraction("Reveal Choice 3"); } private void FinalRoundRevealChoice3() { _VideoPlayer.SubMapIndex = 3; HostCardMultipleChoiceInterface Interface = (HostCardMultipleChoiceInterface)GetHostCardInterface(RoundSegmentType.FinalRound); Interface.ChoiceButtonImages[2].color = (_QuestionCorrectResponse == 3) ? Color.green : Color.red; DataList Choices = _CurrentQuestion["Choices"].DataList; EnableChoiceCards(); // Complex per-podium randomiser, to prevent peeking Random.InitState(Networking.GetServerTimeInMilliseconds()); for (int i = 0; i < _PlayerPodiums.Length; i++) { int[] Indices = { 0, 1, 2 }; int[] ChoiceOrder = { -1, -1, -1 }; int Choice1Index = Indices[Random.Range(0, 3)]; ChoiceOrder[0] = Choice1Index; Indices[Choice1Index] = -1; int Choice2Index = -1; while (Choice2Index == -1) { Choice2Index = Indices[Random.Range(0, 3)]; } ChoiceOrder[1] = Choice2Index; Indices[Choice2Index] = -1; int Choice3Index = -1; while (Choice3Index == -1) { Choice3Index = Indices[Random.Range(0, 3)]; } ChoiceOrder[2] = Choice3Index; Indices[Choice3Index] = -1; string[] ChoiceStrings = { Choices[0].ToString(), Choices[1].ToString(), Choices[2].ToString() }; _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.All, "SetCardChoices", ChoiceStrings, ChoiceOrder); } _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlaySFXAtPitch", SFXEventType.Ding, AudioManager.D6); EnableInteraction("Lock Answers"); } private void FinalRoundLockAnswers() { for (int i = 0; i < _PlayerPodiums.Length; i++) { _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.All, "LockInChoice"); _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.Owner, "ShowAuxiliaryMapScreen", false); } HostCardMultipleChoiceInterface Interface = (HostCardMultipleChoiceInterface)GetHostCardInterface(RoundSegmentType.FinalRound); Interface.HeaderUI.text = "LOCKED IN"; for (int i = 0; i < Interface.CluesUI.Length; i++) { Interface.CluesUI[i].text = ""; } for (int i = 0; i < Interface.ChoiceUI.Length; i++) { if (i != (_QuestionCorrectResponse - 1)) { Interface.ChoiceUI[i].text = ""; } } SortPlayersHighToLowScore(); EnableInteraction("Reveal Third Place"); } private void SortPlayersHighToLowScore() { int PlayerPodiumsCount = _PlayerPodiums.Length; // We're gonna pull a trick here that lets us sort the scores and // players properly. By multiplying the score by 1000 and then adding // the player number to it, we can easily sort by score, followed by // player number, and then convert that back to the needed information // later. We also store the inverse player number (1000 - player no.) // so that they are sorted in the opposite order they normally would // when scores are equal, ensuring that if there is a 3-way tie, the // order will still be Player 1 -> Player 2 -> Player 3. DataList ScoresList = new DataList(); for (int i = 0; i < PlayerPodiumsCount; i++) { ScoresList.Add(((_PlayerPodiums[i].PlayerScore) * 1000) + (1000 - _PlayerPodiums[i].PlayerNumber)); } ScoresList.Sort(); _FinalRoundPlayersSortedByScore = new int[ScoresList.Count]; for (int i = 0; i < _FinalRoundPlayersSortedByScore.Length; i++) { int DecodedPlayerNumber = 1000 - (ScoresList[i].Int % 1000); _FinalRoundPlayersSortedByScore[_FinalRoundPlayersSortedByScore.Length - i - 1] = DecodedPlayerNumber; } } private void FinalRoundRevealPlayerPlace(int PlayerPlace) { int PlayerNumber = _FinalRoundPlayersSortedByScore[PlayerPlace - 1]; HostCardBetweenRoundsInterface Interface = (HostCardBetweenRoundsInterface)GetHostCardInterface(RoundSegmentType.BetweenSegments); Interface.HeaderUI.text = "Player: " + _PlayerPodiums[PlayerNumber - 1].PlayerName; Interface.CommentUI.text = "- Before you show us your answer, how much did you risk?\n" + "- What is your answer?"; _PlayerPodiums[PlayerNumber - 1].HighlightPodium(true); EnableInteraction("Assign Points"); } private void FinalRoundAssignPointsToPlayerPlace(int PlayerPlace) { int PlayerNumber = _FinalRoundPlayersSortedByScore[PlayerPlace - 1]; _PlayerPodiums[PlayerNumber - 1].HighlightPodium(false); _PlayerPodiums[PlayerNumber - 1].VerifyFinalRoundResponse(_QuestionCorrectResponse); // If we're assigning points to the first-place player, then we're // about to finish up, so just move on to determining winners. if (PlayerPlace == 1) { EnableInteraction("End Of Round"); } else { EnableInteraction("Reveal Next Player Answer"); } } private bool FinalRoundCheckNeedForTiebreaker() { DisableChoiceCards(); DisableRiskCards(); DisableBuzzers(); SortPlayersHighToLowScore(); int[] SortedPlayerScores = new int[_PlayerPodiums.Length]; for (int i = 0; i < _PlayerPodiums.Length; i++) { SortedPlayerScores[i] = _PlayerPodiums[_FinalRoundPlayersSortedByScore[i] - 1].PlayerScore; } HostCardBetweenRoundsInterface Interface = (HostCardBetweenRoundsInterface)GetHostCardInterface(RoundSegmentType.BetweenSegments); PlayerPodium Number1Podium = _PlayerPodiums[_FinalRoundPlayersSortedByScore[0] - 1]; PlayerPodium Number2Podium = _PlayerPodiums[_FinalRoundPlayersSortedByScore[1] - 1]; PlayerPodium Number3Podium = _PlayerPodiums[_FinalRoundPlayersSortedByScore[2] - 1]; VRCPlayerApi Number1 = Networking.GetOwner(Number1Podium.gameObject); VRCPlayerApi Number2 = Networking.GetOwner(Number2Podium.gameObject); VRCPlayerApi Number3 = Networking.GetOwner(Number3Podium.gameObject); bool TiebreakerNeeded = false; if (SortedPlayerScores[1] == SortedPlayerScores[2]) { if (SortedPlayerScores[0] == SortedPlayerScores[1]) { Interface.HeaderUI.text = "Three-Way Tie."; Interface.CommentUI.text = "We'll start with a tiebreaker between " + Number2.displayName + " and " + Number3.displayName + "."; } else { Interface.HeaderUI.text = Number1.displayName + " Wins"; Interface.CommentUI.text = "- " + Number1.displayName + " will be moving on to the next round.\n" + "- There is a tie for second place between " + Number2.displayName + " and " + Number3.displayName + ", so we will move on to a tiebreaker."; } TiebreakerNeeded = true; PrepareTiebreakerRound(Number2Podium.PlayerNumber, Number3Podium.PlayerNumber); } else { string[] WinningPlayers = new string[2]; if (SortedPlayerScores[0] == SortedPlayerScores[1]) { Random.InitState(Networking.GetServerTimeInMilliseconds()); VRCPlayerApi[] Randomiser = new VRCPlayerApi[2]; Randomiser[0] = Number1; Randomiser[1] = Number2; int RandomIndex = Random.Range(0, 2); WinningPlayers[0] = Randomiser[RandomIndex].displayName; WinningPlayers[1] = Randomiser[(RandomIndex + 1) % 2].displayName; Interface.HeaderUI.text = "Tie For First"; Interface.CommentUI.text = "- " + Number1.displayName + " and " + Number2.displayName + " are tied for first place.\n" + "- Both will move on to the next round."; } else { WinningPlayers[0] = Number1.displayName; WinningPlayers[1] = Number2.displayName; Interface.HeaderUI.text = "Winners"; Interface.CommentUI.text = Number1.displayName + " and " + Number2.displayName + " will move on to the next round."; } _CaseManager.SetCurrentWinningPlayers(WinningPlayers); } return TiebreakerNeeded; } private void PrepareTiebreakerRound(int FirstPlayer, int SecondPlayer) { _QuestionStage = 0; _TiebreakerPlayerNumbers = new int[2]; _TiebreakerPlayerNumbers[0] = FirstPlayer; _TiebreakerPlayerNumbers[1] = SecondPlayer; } private void AdvanceToNextQuestion() { DisableChoiceCards(); DisableRiskCards(); DisableBuzzers(); _VideoPlayer.SendCustomNetworkEvent(NetworkEventTarget.All, "ClearScreen"); _QuestionIndex++; if (_QuestionIndex >= _QuestionsList.Count) { PrepareEndGame(); } else { PrepareNextQuestion(); } } private void PrepareNextQuestion() { InitialiseQuestion(); if (_CurrentQuestionType == RoundSegmentType.FinalRound) { HostCardBetweenRoundsInterface Interface = (HostCardBetweenRoundsInterface)GetHostCardInterface(RoundSegmentType.BetweenSegments); Interface.HeaderUI.text = "The Final Round"; Interface.CommentUI.text = ""; AdvanceQuestion(); } else { ShowBetweenQuestionsInterface(); } } private void PrepareEndGame() { bool TiebreakerNeeded = FinalRoundCheckNeedForTiebreaker(); if (TiebreakerNeeded) { _CurrentQuestionType = RoundSegmentType.Tiebreaker; EnableInteraction("Advance To Tiebreaker"); } else { _QuestionStage = 0; _CurrentQuestionType = RoundSegmentType.EndGame; EnableInteraction("Play Round End Music"); } } private void PrepareModem() { HostCardBetweenRoundsInterface Interface = (HostCardBetweenRoundsInterface)GetHostCardInterface(RoundSegmentType.BetweenSegments); Interface.HeaderUI.text = "Round is over."; Interface.CommentUI.text = "Everybody enter the Modem"; _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlayMusicLoop", MusicEventType.CapitalLoop); _Modem.Activate(true); _ArrivalDisplay.Activate(true); EnableInteraction("Activate Modem"); } private void ActivateModem() { _Modem.Teleport(); SendCustomEventDelayedSeconds(nameof(_ContinueToRound2_Private), 2.5f); } public void _ContinueToRound2_Private() { _CaseManager.ContinueToRound2(); EnableInteraction("Continue To Round 2"); } private void BeginTiebreakerRound() { HostCardBetweenRoundsInterface Interface = (HostCardBetweenRoundsInterface)GetHostCardInterface(RoundSegmentType.BetweenSegments); Interface.HeaderUI.text = "Preparing tiebreaker..."; Interface.CommentUI.text = ""; VRCStringDownloader.LoadUrl(_TiebreakerData, (IUdonEventReceiver)this); } public override void OnStringLoadSuccess(IVRCStringDownload TiebreakerString) { HostCardTiebreakerInterface Interface = (HostCardTiebreakerInterface)GetHostCardInterface(RoundSegmentType.Tiebreaker); string ErrorString = ""; string JSONString = TiebreakerString.Result; if (VRCJson.TryDeserializeFromJson(JSONString, out DataToken JSONResult)) { if (JSONResult.TokenType == TokenType.DataDictionary) { DataDictionary TiebreakerDictionary = JSONResult.DataDictionary; DataList TiebreakerRegions = TiebreakerDictionary.GetKeys(); Random.InitState(Networking.GetServerTimeInMilliseconds()); int TiebreakerIndex = Random.Range(0, TiebreakerRegions.Count); string TiebreakerRegion = TiebreakerRegions[TiebreakerIndex].String; Interface.ChoiceUI[1].text = TiebreakerRegion; if (TiebreakerDictionary[TiebreakerRegion].TokenType == TokenType.DataDictionary) { DataDictionary RegionDictionary = TiebreakerDictionary[TiebreakerRegion].DataDictionary; if (RegionDictionary.ContainsKey("Type") && RegionDictionary.ContainsKey("Key Locations")) { Interface.HeaderUI.text = "Tiebreaker | " + RegionDictionary["Type"].String; if (RegionDictionary["Key Locations"].TokenType == TokenType.DataList) { for (int i = 0; i < Interface.CluesUI.Length; i++) { Interface.CluesUI[i].text = ""; } DataList KeyLocations = RegionDictionary["Key Locations"].DataList; for (int i = 0; i < KeyLocations.Count && i < Interface.CluesUI.Length; i++) { Interface.CluesUI[i].text = KeyLocations[i].String; } EnableBuzzInPeriodForPlayer(_TiebreakerPlayerNumbers[0]); EnableBuzzInPeriodForPlayer(_TiebreakerPlayerNumbers[1]); } else { ErrorString = "The Key Locations key should be a list of locations."; } } else { ErrorString = "Each region entry should have a region type and a list of key locations."; } } else { ErrorString = "Ensure the elements of the root dictionary are all dictionaries."; } } else { ErrorString = "Ensure the first element is a dictionary."; } } else { ErrorString = "Could not parse JSON document."; } if (ErrorString != "") { Debug.LogError("Malformed tiebreaker data file. " + ErrorString); } } public void TiebreakerIncorrectResponse() { _BuzzedInPlayer = -1; _BuzzInAllowed = true; RequestSerialization(); } public void TiebreakerCorrectResponse() { int PodiumIndex = _BuzzedInPlayer - 1; if (PodiumIndex >= 0 && PodiumIndex < _PlayerPodiums.Length) { _PlayerPodiums[PodiumIndex].SendCustomNetworkEvent(NetworkEventTarget.Owner, "AdjustScore", 5); } _BuzzedInPlayer = -1; DisableBuzzers(); EndBuzzInPeriod(); EnableInteraction("End Tiebreaker"); } private void ResetMultipleChoiceInterface(HostCardMultipleChoiceInterface Interface) { Interface.HeaderUI.text = ""; for (int i = 0; i < Interface.CluesUI.Length; i++) { Interface.CluesUI[i].text = ""; } for (int i = 0; i < Interface.ChoiceButtonImages.Length; i++) { Interface.ChoiceButtonImages[i].color = Color.white; } for (int i = 0; i < Interface.ChoiceUI.Length; i++) { Interface.ChoiceUI[i].text = ""; } } private void EnableChoiceCards() { for (int i = 0; i < _PlayerPodiums.Length; i++) { _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.All, "EnableChoiceCards", true, _QuestionIndex % _PlayerPodiums[i].GetColourOptionsCount()); } } private void DisableChoiceCards() { for (int i = 0; i < _PlayerPodiums.Length; i++) { _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.All, "EnableChoiceCards", false, 0); } } private void EnableRiskCards() { for (int i = 0; i < _PlayerPodiums.Length; i++) { _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.All, "EnableRiskCards", true); } } private void DisableRiskCards() { for (int i = 0; i < _PlayerPodiums.Length; i++) { _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.All, "EnableRiskCards", false); } } private void EnableBuzzers() { for (int i = 0; i < _PlayerPodiums.Length; i++) { _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.All, "EnableBuzzer", true); } } private void DisableBuzzers() { for (int i = 0; i < _PlayerPodiums.Length; i++) { _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.All, "EnableBuzzer", false); } } private void EnableBuzzInPeriodForAllPlayers() { _BuzzInAllowed = true; ResetBuzzers(); RequestSerialization(); } private void EnableBuzzInPeriodForPlayer(int PlayerNumber) { for (int i = 0; i < _PlayerPodiums.Length; i++) { if (_PlayerPodiums[i].PlayerNumber == PlayerNumber) { _PlayerBuzzInAllowed[i] = true; _PlayerPodiums[i].SendCustomNetworkEvent(NetworkEventTarget.All, "EnableBuzzer", true); _BuzzedInPlayer = -1; break; } } _BuzzInAllowed = true; RequestSerialization(); } private void WaitForBuzzInsWithoutLastPlayer() { _BuzzInAllowed = true; _BuzzedInPlayer = -1; RequestSerialization(); } [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(); _PlayerPodiums[PlayerIndex].SendCustomNetworkEvent(NetworkEventTarget.Owner, "StartBuzzedInEffect"); // Play the buzzer sound globally. _AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlaySFX", SFXEventType.Buzzer); } private void EndBuzzInPeriod() { _BuzzInAllowed = false; ResetBuzzers(); RequestSerialization(); } private void ResetBuzzers() { for (int i = 0; i < _PlayerPodiums.Length; i++) { _PlayerBuzzInAllowed[i] = true; } _BuzzedInPlayer = -1; } private void AdvanceQuestion() { // Disable interaction until we unlock it again later. DisableInteraction(); // Only advance to the next stage if we aren't in the middle of a substage if (_QuestionSubstage <= 0) { _QuestionStage++; } // Handle the intro until the game properly begins. if (!_GameHasBegun) { AdvanceIntro(); return; } // TO-DO: Ask someone at either Microsoft or VRChat why the VM crashes if you // cast an int to an enum in a switch parameter, but not if you cast an enum // to an int in a case statement. I'm starting to wonder if either C# or U# // are just fucking terrible languages. C++ figured this problem out in at // least 1985, and it turns out the proper solution was "it's not a problem, // it's two numbers, they're the same fucking thing". switch (_CurrentQuestionType) { case RoundSegmentType.MultipleChoice: AdvanceMultipleChoiceStage(); break; case RoundSegmentType.LightningRound: AdvanceLightningRoundQuestion(); break; case RoundSegmentType.TheChase: AdvanceTheChase(); break; case RoundSegmentType.FinalRound: AdvanceFinalRound(); break; case RoundSegmentType.Tiebreaker: AdvanceTiebreaker(); break; case RoundSegmentType.EndGame: AdvanceEndGame(); break; } RequestSerialization(); } private void AdvanceIntro() { switch (_QuestionStage) { case 1: PlayIntroVideo(); break; case 2: AssignStarterPoints(); break; default: break; } } private void AdvanceMultipleChoiceStage() { switch (_QuestionStage) { case 1: InitialiseCluePresentation(); break; case 2: MultipleChoiceRevealChoice1(); break; case 3: MultipleChoiceRevealChoice2(); break; case 4: MultipleChoiceRevealChoice3(); break; case 5: MultipleChoiceLockAnswers(); break; case 6: MultipleChoiceRevealAnswersAndAssignPoints(); break; case 7: AdvanceToNextQuestion(); break; default: break; } } private void AdvanceLightningRoundQuestion() { switch (_QuestionStage) { case 1: BeginLightningRound(); break; case 2: LightningRoundCluesLoop(); break; case 3: AdvanceToNextQuestion(); break; default: break; } } private void AdvanceTheChase() { switch (_QuestionStage) { case 1: PlayTheChaseMusic(); break; case 2: TheChaseCluesLoop(); break; case 3: AdvanceToNextQuestion(); break; default: break; } } private void AdvanceFinalRound() { switch (_QuestionStage) { case 1: IntroduceFinalRound(); break; case 2: FinalRoundShowMapPreview(); break; case 3: FinalRoundPlayThinkingMusic(); break; case 4: InitialiseCluePresentation(); break; case 5: FinalRoundRevealChoice1(); break; case 6: FinalRoundRevealChoice2(); break; case 7: FinalRoundRevealChoice3(); break; case 8: FinalRoundLockAnswers(); break; case 9: FinalRoundRevealPlayerPlace(3); break; case 10: FinalRoundAssignPointsToPlayerPlace(3); break; case 11: FinalRoundRevealPlayerPlace(2); break; case 12: FinalRoundAssignPointsToPlayerPlace(2); break; case 13: FinalRoundRevealPlayerPlace(1); break; case 14: FinalRoundAssignPointsToPlayerPlace(1); break; case 15: AdvanceToNextQuestion(); break; default: break; } } private void AdvanceTiebreaker() { switch(_QuestionStage) { case 1: BeginTiebreakerRound(); break; case 2: AdvanceToNextQuestion(); break; default: break; } } private void AdvanceEndGame() { switch (_QuestionStage) { case 1: PrepareModem(); break; case 2: ActivateModem(); break; default: break; } } public VRCUrl GetMapURL(int MapIndex) { return _CaseManager.GetMap(MapIndex); } public int GetMapCount() { return _CaseManager.GetMapCount(); } public VRCUrl GetClueImageURL(int ImageIndex) { return _CaseManager.GetClueImage(ImageIndex); } protected override void _HostCardUseButtonDown_Internal() { AdvanceQuestion(); } protected override HostCardInterfaceBase GetHostCardInterface(RoundSegmentType Question) { return _HostCard.EnableHostCardDisplay(RoundType.LocateTheCrook, Question); } private void CreateClueSpecificHostInteractionText(DataDictionary Presentation) { switch ((PresentationMedium)(int)Presentation["Medium"].Number) { case PresentationMedium.VideoClue: case PresentationMedium.VideoMusicClue: EnableInteraction("Play Video"); break; case PresentationMedium.ACMECrimenetComputer: EnableInteraction("ACME Crimenet Computer"); break; default: EnableInteraction("Present Next Clue"); break; } } private HostCardBetweenRoundsInterface ShowBetweenQuestionsInterface() { HostCardBetweenRoundsInterface Interface = (HostCardBetweenRoundsInterface)GetHostCardInterface(RoundSegmentType.BetweenSegments); Interface.CommentUI.text = ""; bool VideosAvailable = false; string PresentationMediumString = ""; if (_CurrentQuestion.ContainsKey("Presentation")) { DataDictionary Presentation = _CurrentQuestion["Presentation"].DataDictionary; if (_CurrentQuestion["Presentation"].DataDictionary.ContainsKey("Clue Title")) { PresentationMediumString = Presentation["Clue Title"].String; } else if (_CurrentQuestion["Presentation"].DataDictionary.ContainsKey("Medium")) { PresentationMediumString = PresentationMediumToString((PresentationMedium)(int)Presentation["Medium"].Number); } if (_CurrentQuestionType == RoundSegmentType.FinalRound) { EnableInteraction("Introduce Final Round"); } else { CreateClueSpecificHostInteractionText(Presentation); } if (Presentation.ContainsKey("Clue Description")) { Interface.CommentUI.text = Presentation["Clue Description"].String; } if (Presentation.ContainsKey("Videos")) { DataList Videos = Presentation["Videos"].DataList; _VideoPlayer.VideoIndex = (int)Videos[_QuestionSubstage].Number; VideosAvailable = true; } } else { PresentationMediumString = RoundSegmentTypeToString(_CurrentQuestionType); if (_CurrentQuestion.ContainsKey("Clue Description")) { Interface.CommentUI.text = _CurrentQuestion["Clue Description"].String; } if (VideosAvailable) { EnableInteraction("Play Videos"); } else { EnableInteraction("Begin " + PresentationMediumString); } } Interface.HeaderUI.text = "Upcoming Question: " + PresentationMediumString; return Interface; } protected string PresentationMediumToString(PresentationMedium Type) { switch (Type) { case PresentationMedium.VideoClue: return "Standard Video Clue"; case PresentationMedium.VideoMusicClue: return "Video Music Clue"; case PresentationMedium.ACMECrimenetComputer: return "ACME Crimenet Computer"; default: return "[[ERROR]]"; } } }