Jamie Greunbaum c739ceda2a - All data files moved to the BunnyCDN servers.
- Added all the rest of the In Jail sounds for all crooks.
- Adjusted format of the ACME Crimenet Computer presentation JSON.
- Cameras are now properly deactivated when cameras are disabled.
2025-11-27 03:59:09 -05:00

1751 lines
51 KiB
C#

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,
DetectiveNoirEffect
}
[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 DetectiveNoirEffect _DetectiveNoirEffect;
[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();
_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;
case PresentationMedium.DetectiveNoirEffect: InitialiseDetectiveNoirEffect(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("Clue Text"))
{
UntranslatedText = Presentation["Clue Text"].String;
}
if (Presentation.ContainsKey("Translated Text"))
{
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 InitialiseDetectiveNoirEffect(DataDictionary Presentation)
{
_QuestionSubstage++;
switch (_QuestionSubstage)
{
case 1:
_AudioManager.SendCustomNetworkEvent(NetworkEventTarget.All, "PlaySFX", SFXEventType.DistantFoghorn);
EnableInteraction("Black And White");
break;
case 2:
_DetectiveNoirEffect.Activate = true;
EnableInteraction("Play Video");
break;
case 3:
_VideoPlayer.PlayVideo = true;
EnableInteraction("Back To Colour");
break;
default:
_DetectiveNoirEffect.Activate = false;
MultipleChoiceRevealQuestion();
break;
}
}
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()
{
_VideoPlayer.ShowScreen = ClueScreenType.Blank;
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]]";
}
}
}