Files

625 lines
27 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using EHR.Modules;
using HarmonyLib;
using Hazel;
using UnityEngine;
using static EHR.Translator;
namespace EHR.Gamemodes;
public static class Quiz
{
public static readonly HashSet<string> HasPlayedFriendCodes = [];
private static readonly Dictionary<Difficulty, HashSet<int>> AskedQuestions = new()
{
[Difficulty.Easy] = [],
[Difficulty.Medium] = [],
[Difficulty.Hard] = []
};
private static readonly Dictionary<Difficulty, int> TotalQuestions = new()
{
[Difficulty.Easy] = 223,
[Difficulty.Medium] = 123,
[Difficulty.Hard] = 133
};
private static readonly Dictionary<MapNames, Dictionary<char, SystemTypes>> UsedRooms = new()
{
[MapNames.Skeld] = new()
{
['A'] = SystemTypes.Reactor,
['B'] = SystemTypes.UpperEngine,
['C'] = SystemTypes.Security,
['D'] = SystemTypes.LowerEngine,
['E'] = SystemTypes.Outside
},
[MapNames.MiraHQ] = new()
{
['A'] = SystemTypes.Office,
['B'] = SystemTypes.Greenhouse,
['C'] = SystemTypes.Admin,
['D'] = SystemTypes.Comms,
['E'] = SystemTypes.LockerRoom
},
[MapNames.Polus] = new()
{
['A'] = SystemTypes.Security,
['B'] = SystemTypes.Electrical,
['C'] = SystemTypes.Outside,
['D'] = SystemTypes.LifeSupp,
['E'] = SystemTypes.BoilerRoom
},
[MapNames.Airship] = new()
{
['A'] = SystemTypes.Cockpit,
['B'] = SystemTypes.Comms,
['C'] = SystemTypes.Engine,
['D'] = SystemTypes.Armory,
['E'] = SystemTypes.Kitchen
},
[MapNames.Fungle] = new()
{
['A'] = SystemTypes.Cafeteria,
['B'] = SystemTypes.Dropship,
['C'] = SystemTypes.Storage,
['D'] = SystemTypes.MeetingRoom,
['E'] = SystemTypes.Kitchen
},
[(MapNames)6] = new()
{
['A'] = (SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.Observatory,
['B'] = SystemTypes.Cafeteria,
['C'] = (SystemTypes)SubmergedCompatibility.SubmergedSystemTypes.UpperCentral,
['D'] = SystemTypes.Lounge,
['E'] = SystemTypes.Admin
}
};
private static Dictionary<byte, Dictionary<Difficulty, int[]>> NumCorrectAnswers;
private static List<byte> DyingPlayers;
private static long QuestionTimeLimitEndTS;
private static (string Question, List<string> Answers, int CorrectAnswerIndex) CurrentQuestion;
private static Difficulty CurrentDifficulty;
private static int Round;
private static int QuestionsAsked;
private static int NumAllCorrectAnswers;
private static long FFAEndTS;
private static bool NoSuffix;
public static bool AllowKills;
private static OptionItem FFAEventLength;
private static readonly Dictionary<Difficulty, (OptionItem Rounds, OptionItem QuestionsAsked, OptionItem CorrectRequirement, OptionItem TimeLimit)> Settings = [];
private static OptionItem ChatDuringGame;
static Quiz()
{
UsedRooms[MapNames.Dleks] = UsedRooms[MapNames.Skeld];
}
public static bool Chat => ChatDuringGame.GetBool();
public static void SetupCustomOption()
{
var id = 69_220_001;
var color = Utils.GetRoleColor(CustomRoles.QuizMaster);
const CustomGameMode gameMode = CustomGameMode.Quiz;
FFAEventLength = new IntegerOptionItem(id++, "Quiz.Settings.FFAEventLength", new(5, 120, 1), 30, TabGroup.GameSettings)
.SetValueFormat(OptionFormat.Seconds)
.SetHeader(true)
.SetColor(color)
.SetGameMode(gameMode);
foreach (Difficulty difficulty in EnumHelper.GetValues<Difficulty>()[1..])
{
var rounds = new IntegerOptionItem(id++, $"Quiz.Settings.Rounds.{difficulty}", new(1, 50, 1), 3, TabGroup.GameSettings)
.SetHidden(difficulty == Difficulty.Hard)
.SetHeader(true)
.SetColor(color)
.SetGameMode(gameMode);
var questionsAsked = new IntegerOptionItem(id++, $"Quiz.Settings.QuestionsAsked.{difficulty}", new(1, 50, 1), 3, TabGroup.GameSettings)
.SetHeader(difficulty == Difficulty.Hard)
.SetColor(color)
.SetGameMode(gameMode);
var correctRequirement = new IntegerOptionItem(id++, $"Quiz.Settings.CorrectRequirement.{difficulty}", new(1, 50, 1), 3, TabGroup.GameSettings)
.SetColor(color)
.SetGameMode(gameMode);
var timeLimit = new IntegerOptionItem(id++, $"Quiz.Settings.TimeLimit.{difficulty}", new(5, 120, 1), 15, TabGroup.GameSettings)
.SetValueFormat(OptionFormat.Seconds)
.SetColor(color)
.SetGameMode(gameMode);
Settings[difficulty] = (rounds, questionsAsked, correctRequirement, timeLimit);
}
ChatDuringGame = new BooleanOptionItem(id, "Quiz.Settings.ChatDuringGame", false, TabGroup.GameSettings)
.SetHeader(true)
.SetColor(color)
.SetGameMode(gameMode);
}
public static string GetStatistics(byte id)
{
if (!NumCorrectAnswers.TryGetValue(id, out var data)) return string.Empty;
string str = data.Join(" | ", x => $"{x.Key.ToString()[0]}-{x.Value.Sum()}");
return GetString("Quiz.EndResults.CorrectAnswerNum") + str;
}
public static bool KnowTargetRoleColor(PlayerControl target, ref string color)
{
if (NoSuffix || FFAEndTS != 0 || QuestionTimeLimitEndTS != 0) return false;
color = DyingPlayers.Contains(target.PlayerId) ? "#ff0000" : "#00ff00";
return true;
}
public static string GetSuffix(PlayerControl seer)
{
if (NoSuffix) return string.Empty;
int requiredCorrect;
int gotCorrect;
bool failed;
if (FFAEndTS == 0)
{
int numPlayers = Main.AllAlivePlayerControlsCount;
if (QuestionTimeLimitEndTS != 0)
{
string answers = string.Empty;
for (int i = 0; i < CurrentQuestion.Answers.Count; i++)
{
string answer = CurrentQuestion.Answers[i];
char letter = (char)('A' + i);
SystemTypes room = UsedRooms[Main.CurrentMap][letter];
bool isInThisRoom = room == SystemTypes.Outside ? !seer.GetPlainShipRoom() || seer.IsInRoom(SystemTypes.Hallway) : seer.IsInRoom(room);
string prefix = isInThisRoom ? "\u27a1 <u>" : string.Empty;
string suffix = isInThisRoom ? "</u>" : string.Empty;
string add = $"{prefix}{letter}: {answer} ({GetString(room)}){suffix}\n";
answers += add;
}
string question = CurrentQuestion.Question;
for (var i = 50; i < question.Length; i += 50)
{
int index = question.LastIndexOf(' ', i);
if (index != -1) question = question.Insert(index + 1, "\n");
}
string invis = numPlayers <= 3 && CurrentDifficulty > Difficulty.Test ? $"\n<#888888>{GetString("Quiz.Suffix.Invisible")}</color>" : string.Empty;
return $"<#ffff44>{question}</color>\n{answers}<#CF2472>{QuestionTimeLimitEndTS - Utils.TimeStamp}</color>{invis}";
}
string correctAnswer = CurrentQuestion.Answers[CurrentQuestion.CorrectAnswerIndex];
char correctAnswerLetter = (char)('A' + CurrentQuestion.CorrectAnswerIndex);
string correctRoom = GetString(UsedRooms[Main.CurrentMap][correctAnswerLetter].ToString());
string color = DyingPlayers.Contains(seer.PlayerId) ? "#FF0000" : "#00FF00";
string str = string.Format(GetString("Quiz.Notify.CorrectAnswer"), color, correctAnswerLetter, correctAnswer, correctRoom);
if (DyingPlayers.Count == numPlayers) str += "\n" + GetString(numPlayers <= 3 ? "Quiz.Notify.AllWrongShort" : "Quiz.Notify.AllWrong");
if (CurrentDifficulty > Difficulty.Test && Settings[CurrentDifficulty].QuestionsAsked.GetInt() <= QuestionsAsked)
{
requiredCorrect = Settings[CurrentDifficulty].CorrectRequirement.GetInt();
gotCorrect = NumCorrectAnswers[seer.PlayerId][CurrentDifficulty][Round];
failed = gotCorrect < requiredCorrect;
if (seer.IsAlive()) str += "\n\n" + string.Format(Utils.ColorString(failed ? Color.red : Color.green, GetString("Quiz.Notify.CorrectAnswerNum")), gotCorrect, QuestionsAsked, requiredCorrect);
str += "\n" + NumCorrectAnswers.Count(x => x.Value[CurrentDifficulty][Round] < requiredCorrect && Main.PlayerStates.TryGetValue(x.Key, out var s) && !s.IsDead) switch
{
0 => GetString("Quiz.Notify.AllCorrect"),
var x when x == numPlayers => GetString("Quiz.Notify.AllDie"),
1 => failed ? GetString("Quiz.Notify.OnlyYouWrong") : GetString("Quiz.Notify.OneWrong"),
_ => failed ? GetString("Quiz.Notify.IncorrectFFA") : GetString("Quiz.Notify.CorrectFFA")
};
}
return str;
}
if (!AllowKills) return string.Empty;
requiredCorrect = Settings[CurrentDifficulty].CorrectRequirement.GetInt();
gotCorrect = NumCorrectAnswers[seer.PlayerId][CurrentDifficulty][Round - 1];
failed = gotCorrect < requiredCorrect;
var ffaTimeLeft = FFAEndTS - Utils.TimeStamp;
var rooms = Main.EnumerateAlivePlayerControls().Without(seer).Select(x => x.GetPlainShipRoom()).Where(x => x).Select(x => x.RoomId).Distinct().Without(SystemTypes.Hallway).Select(x => GetString(x.ToString())).Join();
return string.Format(GetString(failed ? "Quiz.Notify.FFAOngoing" : "Quiz.Notify.FFASpectating"), rooms, ffaTimeLeft);
}
public static void Init()
{
NumCorrectAnswers = [];
DyingPlayers = [];
QuestionTimeLimitEndTS = 0;
CurrentQuestion = (string.Empty, [], 0);
CurrentDifficulty = Difficulty.Easy;
Round = 0;
QuestionsAsked = 0;
NumAllCorrectAnswers = 0;
FFAEndTS = 0;
AllowKills = false;
NoSuffix = true;
foreach (PlayerControl pc in Main.EnumeratePlayerControls())
{
NumCorrectAnswers[pc.PlayerId] = new Dictionary<Difficulty, int[]>
{
[Difficulty.Easy] = new int[Settings[Difficulty.Easy].Rounds.GetInt()],
[Difficulty.Medium] = new int[Settings[Difficulty.Medium].Rounds.GetInt()],
[Difficulty.Hard] = new int[100] // Surely there won't be more than 100 hard rounds, right?
};
}
}
public static IEnumerator OnGameStart()
{
if (Chat) yield return new WaitForSecondsRealtime(1f);
NoSuffix = true;
var aapc = Main.AllAlivePlayerControlsToList;
bool showTutorial = !SubmergedCompatibility.IsSubmerged() && !Main.LIMap && aapc.ExceptBy(HasPlayedFriendCodes, x => x.FriendCode).Count() > aapc.Count / 2;
if (Main.LIMap)
{
PlainShipRoom[] rooms = ShipStatus.Instance.AllRooms.Where(x => x).DistinctBy(x => x.RoomId).ToArray();
List<SystemTypes> chosen = rooms.Select(plainShipRoom =>
{
Dictionary<SystemTypes, float> closestRooms = rooms.Select(x => new KeyValuePair<SystemTypes, float>(x.RoomId, Vector2.Distance(plainShipRoom.transform.position, x.transform.position))).OrderBy(x => x.Value).Take(4).ToDictionary(x => x.Key, x => x.Value);
float sum = closestRooms.Values.Sum();
return new KeyValuePair<SystemTypes[], float>([.. closestRooms.Keys, plainShipRoom.RoomId], sum);
}).MinBy(x => x.Value).Key.ToList();
Dictionary<char, SystemTypes> dict = [];
for (char c = 'A'; c <= 'E'; c++)
dict[c] = chosen[c - 'A'];
UsedRooms[(MapNames)7] = dict;
}
var usedRooms = UsedRooms[Main.CurrentMap].Join('\n', x => $"{x.Key}: {GetString(x.Value.ToString())}");
aapc.NotifyPlayers(string.Format(GetString("Quiz.Tutorial.Basics"), usedRooms), 11f);
yield return new WaitForSecondsRealtime(showTutorial ? 9f : 5f);
if (GameStates.IsMeeting || ExileController.Instance || !GameStates.InGame || GameStates.IsLobby) yield break;
NameNotifyManager.Reset();
if (showTutorial)
{
aapc.NotifyPlayers(GetString("Quiz.Tutorial.TestRound"));
yield return new WaitForSecondsRealtime(3f);
if (GameStates.IsMeeting || ExileController.Instance || !GameStates.InGame || GameStates.IsLobby) yield break;
NameNotifyManager.Reset();
NoSuffix = false;
CurrentDifficulty = Difficulty.Test;
QuestionTimeLimitEndTS = Utils.TimeStamp + 30;
CurrentQuestion = GetQuestion(0);
while (QuestionTimeLimitEndTS != 0)
{
yield return new WaitForSecondsRealtime(1f);
if (GameStates.IsMeeting || ExileController.Instance || !GameStates.InGame || GameStates.IsLobby) yield break;
}
yield return new WaitForSecondsRealtime(3f);
NoSuffix = true;
aapc.NotifyPlayers(GetString("Quiz.Tutorial.OnWrongAnswer"), 10f);
yield return new WaitForSecondsRealtime(3f);
if (GameStates.IsMeeting || ExileController.Instance || !GameStates.InGame || GameStates.IsLobby) yield break;
NameNotifyManager.Reset();
aapc.NotifyPlayers(GetString("Quiz.Tutorial.OnCorrectAnswer"));
yield return new WaitForSecondsRealtime(2f);
}
else
{
NoSuffix = true;
var settings = Settings[CurrentDifficulty];
aapc.NotifyPlayers(string.Format(GetString("Quiz.Notify.NextStage"), (int)CurrentDifficulty, settings.Rounds.GetInt(), settings.QuestionsAsked.GetInt(), settings.CorrectRequirement.GetInt(), settings.QuestionsAsked.GetInt(), settings.TimeLimit.GetInt()), 8f);
yield return new WaitForSecondsRealtime(6f);
}
if (GameStates.IsMeeting || ExileController.Instance || !GameStates.InGame || GameStates.IsLobby) yield break;
NameNotifyManager.Reset();
yield return NewQuestion(increaseDifficulty: showTutorial, newRound: true);
}
private static IEnumerator NewQuestion(bool increaseDifficulty = true, bool newRound = false)
{
if (increaseDifficulty && CurrentDifficulty != Difficulty.Hard)
{
Round = 0;
CurrentDifficulty++;
NoSuffix = true;
var settings = Settings[CurrentDifficulty];
Main.EnumerateAlivePlayerControls().NotifyPlayers(string.Format(GetString("Quiz.Notify.NextStage"), (int)CurrentDifficulty, settings.Rounds.GetInt(), settings.QuestionsAsked.GetInt(), settings.CorrectRequirement.GetInt(), settings.QuestionsAsked.GetInt(), settings.TimeLimit.GetInt()), 8f);
yield return new WaitForSecondsRealtime(6f);
if (GameStates.IsMeeting || ExileController.Instance || !GameStates.InGame || GameStates.IsLobby) yield break;
NameNotifyManager.Reset();
}
if (newRound)
{
if (CurrentDifficulty == Difficulty.Hard && Round >= 100) NumCorrectAnswers.Values.Do(x => x[CurrentDifficulty][Round] = 0);
NoSuffix = true;
Main.EnumerateAlivePlayerControls().NotifyPlayers(string.Format(GetString("Quiz.Notify.NextRound"), Round + 1));
yield return new WaitForSecondsRealtime(3f);
if (GameStates.IsMeeting || ExileController.Instance || !GameStates.InGame || GameStates.IsLobby) yield break;
NameNotifyManager.Reset();
}
NoSuffix = false;
int time = Settings[CurrentDifficulty].TimeLimit.GetInt();
int questionIndex = GetRandomQuestionIndex();
CurrentQuestion = GetQuestion(questionIndex);
time += CurrentQuestion.Question.Length / 20;
time += CurrentQuestion.Answers.Sum(x => x.Length / 15);
if (Main.CurrentMap is MapNames.Skeld or MapNames.Dleks) time -= 3;
time -= NumAllCorrectAnswers;
QuestionTimeLimitEndTS = Utils.TimeStamp + time;
var aapc = Main.AllAlivePlayerControlsToList;
if (aapc.Count is 3 or 2 && CurrentDifficulty > Difficulty.Test)
aapc.Do(x => x.RpcMakeInvisible());
Logger.Info($"New question: {CurrentQuestion.Question} | {string.Join(", ", CurrentQuestion.Answers)} | {CurrentQuestion.Answers[CurrentQuestion.CorrectAnswerIndex]}", "Quiz");
Logger.Info($"Time limit: {time}", "Quiz");
}
private static int GetRandomQuestionIndex()
{
var tries = 0;
while (true)
{
if (tries++ > 10)
{
Utils.ThrowException(new("Could not find a random question index after 10 tries"));
Utils.ErrorEnd("Quiz.GetRandomQuestionIndex: Could not find a random question index after 10 tries");
return 1;
}
try
{
HashSet<int> asked = AskedQuestions[CurrentDifficulty];
int max = TotalQuestions[CurrentDifficulty];
int index = Enumerable.Range(1, max).Except(asked).RandomElement();
asked.Add(index);
if (asked.Count >= max) asked.Clear();
return index;
}
catch (Exception e)
{
Utils.ThrowException(e);
Logger.Error($"Could not get a random question index for difficulty {CurrentDifficulty}. Retrying...", "Quiz");
}
}
}
private static (string Question, List<string> Answers, int CorrectAnswerIndex) GetQuestion(int questionIndex)
{
var tries = 0;
while (true)
{
if (tries++ > 10)
{
Utils.ThrowException(new("Could not get a question after 10 tries"));
Utils.ErrorEnd("Quiz.GetQuestion: Could not get a question after 10 tries");
return ("Error: Could not get a question after 10 tries", [], 0);
}
try
{
var baseString = $"Quiz.Questions.{CurrentDifficulty}.{questionIndex}";
string question = GetString(baseString);
Dictionary<char, string> answers = new[] { 'A', 'B', 'C', 'D', 'E' }.ToDictionary(x => x, x => GetString($"{baseString}.Answer.{x}"));
string correctAnswer;
if (CurrentDifficulty == Difficulty.Test)
{
MapNames map = Main.CurrentMap;
if (map == MapNames.Dleks) map = MapNames.Skeld;
if (map > MapNames.Dleks) map--;
correctAnswer = answers[(char)((int)map + 65)];
}
else
correctAnswer = answers[GetString($"{baseString}.CorrectAnswer")[0]];
List<string> shuffledAnswers = answers.Values.Shuffle();
int correctAnswerIndex = shuffledAnswers.IndexOf(correctAnswer);
return (question, shuffledAnswers, correctAnswerIndex);
}
catch (Exception e)
{
Utils.ThrowException(e);
Logger.Error($"Question index {questionIndex} for difficulty {CurrentDifficulty} could not be found. Retrying...", "Quiz");
questionIndex = GetRandomQuestionIndex();
}
}
}
private static IEnumerator EndQuestion()
{
QuestionsAsked++;
QuestionTimeLimitEndTS = 0;
var aapc = Main.AllAlivePlayerControlsToList;
var aapcCount = aapc.Count;
SystemTypes correctRoom = UsedRooms[Main.CurrentMap][(char)('A' + CurrentQuestion.CorrectAnswerIndex)];
DyingPlayers = aapc.Select(x => (ID: x.PlayerId, Room: x.GetPlainShipRoom())).Where(x => correctRoom == SystemTypes.Outside ? x.Room && x.Room.RoomId != SystemTypes.Hallway : !x.Room || x.Room.RoomId != correctRoom).Select(x => x.ID).ToList();
if (DyingPlayers.Count == 0) NumAllCorrectAnswers++;
bool everyoneWasWrong = DyingPlayers.Count == aapcCount;
if (!everyoneWasWrong) NumCorrectAnswers.IntersectBy(aapc.Select(x => x.PlayerId), x => x.Key).DoIf(x => !DyingPlayers.Contains(x.Key), x => x.Value[CurrentDifficulty][Round]++);
Logger.Info($"{(everyoneWasWrong ? "Everyone" : "Players who")} got the question wrong: {DyingPlayers.Join(", ", x => Main.AllPlayerNames.GetValueOrDefault(x, $"Someone (ID {x})"))}", "Quiz");
Logger.Info($"Number of correct answers for everyone currently: {NumCorrectAnswers.Join(", ", x => $"{Main.AllPlayerNames.GetValueOrDefault(x.Key, string.Empty)}: {x.Value[CurrentDifficulty][Round]}")}", "Quiz");
if (everyoneWasWrong) QuestionsAsked--;
Utils.NotifyRoles();
yield return new WaitForSecondsRealtime(everyoneWasWrong ? aapcCount <= 3 ? 4f : 6f : 3f);
if (GameStates.IsMeeting || ExileController.Instance || !GameStates.InGame || GameStates.IsLobby) yield break;
var settings = Settings[CurrentDifficulty];
if (settings.QuestionsAsked.GetInt() > QuestionsAsked)
{
yield return NewQuestion(increaseDifficulty: false);
yield break;
}
yield return new WaitForSecondsRealtime(2f);
var correctRequirement = settings.CorrectRequirement.GetInt();
List<byte> dyingPlayers = NumCorrectAnswers.Where(x => x.Value[CurrentDifficulty][Round] < correctRequirement).Select(x => x.Key).Where(x => Main.PlayerStates.TryGetValue(x, out var s) && !s.IsDead).ToList();
Logger.Info($"Round {Round + 1} of {CurrentDifficulty} difficulty ended. Dying players: {dyingPlayers.Count} | {dyingPlayers.Join(", ", x => Main.AllPlayerNames[x])}", "Quiz");
Round++;
QuestionsAsked = 0;
switch (dyingPlayers.Count)
{
case 0:
yield return NewQuestion(increaseDifficulty: settings.Rounds.GetInt() <= Round, newRound: true);
yield break;
case 1:
var pc = dyingPlayers[0].GetPlayer();
if (!pc) goto case 0;
pc.RpcMakeVisible();
pc.Suicide();
goto case 0;
case var x when x == aapcCount:
Round--;
NumCorrectAnswers.Values.Do(d => d[CurrentDifficulty][Round] = 0);
goto case 0;
default:
if (aapcCount is 3 or 2) aapc.Do(x => x.RpcMakeVisible());
yield return new WaitForSecondsRealtime(2f);
AllowKills = true;
FFAEndTS = Utils.TimeStamp + FFAEventLength.GetInt();
var spectators = aapc.ExceptBy(dyingPlayers, x => x.PlayerId).ToArray();
spectators.Do(x => x.ExileTemporarily());
yield return new WaitForSecondsRealtime(0.2f);
var stillLiving = dyingPlayers.ToValidPlayers().Where(x => x.IsAlive()).ToList();
stillLiving.ForEach(x => x.RpcChangeRoleBasis(CustomRoles.SerialKiller));
Utils.SendRPC(CustomRPC.QuizSync, AllowKills);
while (Utils.TimeStamp < FFAEndTS)
{
yield return new WaitForSecondsRealtime(1f);
if (GameStates.IsMeeting || ExileController.Instance || !GameStates.InGame || GameStates.IsLobby) yield break;
Utils.NotifyRoles(SendOption: SendOption.None);
stillLiving.RemoveAll(x => !x || !x.IsAlive());
if (stillLiving.Count <= 1) break;
}
spectators.Do(x => x.ReviveFromTemporaryExile());
AllowKills = false;
Utils.SendRPC(CustomRPC.QuizSync, AllowKills);
var location = RandomSpawn.SpawnMap.GetSpawnMap().Positions.IntersectBy(UsedRooms[Main.CurrentMap].Values, x => x.Key).RandomElement().Value;
switch (stillLiving.Count)
{
case 0:
break;
case 1:
PlayerControl ffaSurvivor = stillLiving[0];
ffaSurvivor.RpcChangeRoleBasis(CustomRoles.QuizPlayer);
ffaSurvivor.TP(location);
break;
default:
stillLiving.ForEach(x => x.Suicide());
break;
}
yield return new WaitForSecondsRealtime(1f);
if (Chat)
{
Main.AllAlivePlayerControlsToList.SetChatVisible(true);
yield return new WaitForSecondsRealtime(1f);
}
spectators.Do(x => x.TP(location));
yield return new WaitForSecondsRealtime(2f);
if (GameStates.IsMeeting || ExileController.Instance || !GameStates.InGame || GameStates.IsLobby) yield break;
FFAEndTS = 0;
yield return NewQuestion(increaseDifficulty: settings.Rounds.GetInt() <= Round, newRound: true);
yield break;
}
}
public static string GetTaskBarText()
{
try
{
var correctAnswers = NumCorrectAnswers.Join('\n', x => $"{x.Key.ColoredPlayerName()}: {x.Value.Join(' ', k => $"{k.Key.ToString()[0]}-{k.Value.Sum()}")}");
return string.Format(GetString("Quiz.TaskBarText"), CurrentDifficulty, Round + 1, QuestionsAsked + 1, correctAnswers);
}
catch { return string.Empty; }
}
enum Difficulty
{
Test,
Easy,
Medium,
Hard
}
//[HarmonyPatch(typeof(PlayerControl), nameof(PlayerControl.FixedUpdate))]
public static class FixedUpdatePatch
{
private static long LastUpdate;
public static void Postfix()
{
if (!AmongUsClient.Instance.AmHost || !GameStates.IsInTask || ExileController.Instance || Options.CurrentGameMode != CustomGameMode.Quiz || !Main.IntroDestroyed) return;
long now = Utils.TimeStamp;
if (LastUpdate == now) return;
LastUpdate = now;
if (QuestionTimeLimitEndTS != 0)
{
if (QuestionTimeLimitEndTS <= now)
{
Logger.Info("Question timer ended", "Quiz");
if (CurrentDifficulty == Difficulty.Test)
{
SystemTypes correctRoom = UsedRooms[Main.CurrentMap][(char)('A' + CurrentQuestion.CorrectAnswerIndex)];
DyingPlayers = Main.EnumerateAlivePlayerControls().Select(x => (ID: x.PlayerId, Room: x.GetPlainShipRoom())).Where(x => correctRoom == SystemTypes.Outside ? x.Room && x.Room.RoomId != SystemTypes.Hallway : !x.Room || x.Room.RoomId != correctRoom).Select(x => x.ID).ToList();
QuestionTimeLimitEndTS = 0;
Utils.NotifyRoles();
}
else
Main.Instance.StartCoroutine(EndQuestion());
}
else
Utils.NotifyRoles();
}
}
}
}