using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Logging;
using GameNetcodeStuff;
using HarmonyLib;
using Hura.Patches;
using Hura.Project;
using Hura.Project.Quiz;
using Hura.Project.Utility;
using UnityEngine;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("Hura")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("HP")]
[assembly: AssemblyProduct("Hura")]
[assembly: AssemblyCopyright("Copyright © HP 2023")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("351ddba5-b276-4062-9ba5-e04fb454ffc9")]
[assembly: AssemblyFileVersion("1.0.2.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.2.0")]
namespace Hura
{
[BepInPlugin("Hura.LethalQuiz", "Lethal Quiz", "1.0.2")]
public class LethalQuiz : BaseUnityPlugin
{
private const string MOD_GUID = "Hura.LethalQuiz";
private const string MOD_NAME = "Lethal Quiz";
private const string MOD_VERSION = "1.0.2";
private readonly Harmony harmony = new Harmony("Hura.LethalQuiz");
public static ManualLogSource logger;
public static LethalQuiz Instance { get; private set; }
private void Awake()
{
if ((Object)(object)Instance == (Object)null)
{
Instance = this;
}
logger = Logger.CreateLogSource("Hura.LethalQuiz");
QuizManager.ImportQuiz();
harmony.PatchAll(typeof(LethalQuiz));
harmony.PatchAll(typeof(TerminalHelper));
harmony.PatchAll(typeof(HUDManagerPatch));
harmony.PatchAll(typeof(QuizManager));
}
}
}
namespace Hura.Project
{
[HarmonyPatch(typeof(HUDManager))]
internal class HUDHelper
{
public static HUDManager HUDManager { get; private set; }
[HarmonyPatch("Awake")]
[HarmonyPostfix]
public static void RegisterTerminalInstance(ref HUDManager __instance)
{
HUDManager = __instance;
}
}
internal static class HuraHelper
{
public static PlayerControllerB[] Players => StartOfRound.Instance.allPlayerScripts;
}
[HarmonyPatch(typeof(Terminal))]
public static class TerminalHelper
{
private static StringBuilder stringBuilder = new StringBuilder();
public static Terminal Terminal { get; private set; }
[HarmonyPatch("Awake")]
[HarmonyPostfix]
public static void RegisterTerminalInstance(ref Terminal __instance)
{
Terminal = __instance;
}
public static TerminalNode CreateNode(string text, bool clear = true)
{
TerminalNode obj = ScriptableObject.CreateInstance<TerminalNode>();
obj.displayText = text;
obj.clearPreviousText = clear;
return obj;
}
public static TerminalKeyword CreateKeyword(string word, bool isVerb, TerminalNode destinationNode)
{
TerminalKeyword obj = ScriptableObject.CreateInstance<TerminalKeyword>();
obj.word = word;
obj.isVerb = isVerb;
obj.specialKeywordResult = destinationNode;
return obj;
}
public static void AddKeyworkToTerminal(TerminalKeyword keyword)
{
ArrayUtility.Add(ref Terminal.terminalNodes.allKeywords, keyword);
}
public static string CreateNodeText(params string[] args)
{
stringBuilder.Clear();
for (int i = 0; i < args.Length; i++)
{
stringBuilder.Append(args[i]).Append("\n");
}
return stringBuilder.ToString();
}
public static CompatibleNoun CreateCompatibleNoun(TerminalKeyword noun, TerminalNode destinationNode)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0014: Expected O, but got Unknown
return new CompatibleNoun
{
noun = noun,
result = destinationNode
};
}
public static void AddCompatibleNounToVerb(TerminalKeyword verb, TerminalKeyword noun, TerminalNode destinationNode)
{
if (!verb.isVerb)
{
LethalQuiz.logger.LogError((object)("Error while adding compatible noun to verb. Given verb is not a verb: " + verb.word));
return;
}
if (noun.isVerb)
{
LethalQuiz.logger.LogError((object)("Error while adding compatible noun to verb. Given noun is a verb: " + noun.word));
return;
}
CompatibleNoun element = CreateCompatibleNoun(noun, destinationNode);
ArrayUtility.Add(ref verb.compatibleNouns, element);
}
}
}
namespace Hura.Project.Utility
{
internal static class ArrayUtility
{
public static void Add<T>(ref T[] array, T element)
{
if (array == null)
{
array = new T[0];
}
Array.Resize(ref array, array.Length + 1);
array[array.Length - 1] = element;
}
}
internal static class HuraLinq
{
private static void ShuffleSelf<T>(this IList<T> list, Func<int, int> randomIndexMethod)
{
for (int num = list.Count - 1; num > 0; num--)
{
int index = randomIndexMethod(num);
T value = list[num];
list[num] = list[index];
list[index] = value;
}
}
public static void ShuffleSelf<T>(this IList<T> list)
{
list.ShuffleSelf(RandomIndex);
static int RandomIndex(int iterationIndex)
{
return Random.Range(0, iterationIndex + 1);
}
}
public static void ShuffleSelf<T>(this IList<T> list, Random random)
{
list.ShuffleSelf(RandomIndex);
int RandomIndex(int iterationIndex)
{
return random.Next(iterationIndex + 1);
}
}
}
}
namespace Hura.Project.Quiz
{
internal class FoggyCurse : ICurse
{
private readonly HashSet<LevelWeatherType> allowedWeather = new HashSet<LevelWeatherType>
{
(LevelWeatherType)(-1),
(LevelWeatherType)0,
(LevelWeatherType)1
};
public float Weight => 1f;
public void ApplyCurse()
{
//IL_000b: Unknown result type (might be due to invalid IL or missing references)
RoundManager.Instance.currentLevel.currentWeather = (LevelWeatherType)3;
((object)RoundManager.Instance).GetType().GetMethod("SetToCurrentLevelWeather", BindingFlags.Instance | BindingFlags.NonPublic).Invoke(RoundManager.Instance, null);
}
public bool CanBeApplied()
{
//IL_001d: Unknown result type (might be due to invalid IL or missing references)
RoundManager instance = RoundManager.Instance;
if ((Object)(object)instance == (Object)null)
{
return false;
}
return allowedWeather.Contains(instance.currentLevel.currentWeather);
}
}
internal interface ICurse
{
float Weight { get; }
bool CanBeApplied();
void ApplyCurse();
}
internal class MoneyCurse : ICurse
{
public const int CREDITS_LOST = 40;
public float Weight => 1f;
public void ApplyCurse()
{
Terminal terminal = TerminalHelper.Terminal;
terminal.groupCredits -= 40;
TerminalHelper.Terminal.SyncGroupCreditsServerRpc(TerminalHelper.Terminal.groupCredits, TerminalHelper.Terminal.numberOfItemsInDropship);
}
public bool CanBeApplied()
{
return true;
}
}
internal class InsultsCurse : ICurse
{
public float Weight => 0.25f;
public void ApplyCurse()
{
}
public bool CanBeApplied()
{
return true;
}
}
internal class TeleportCurse : ICurse
{
public float Weight => 1f;
public void ApplyCurse()
{
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0049: Unknown result type (might be due to invalid IL or missing references)
//IL_0051: Unknown result type (might be due to invalid IL or missing references)
//IL_0056: Unknown result type (might be due to invalid IL or missing references)
//IL_0058: Unknown result type (might be due to invalid IL or missing references)
//IL_0068: Unknown result type (might be due to invalid IL or missing references)
List<PlayerControllerB> availablePlayers = GetAvailablePlayers();
int index = Random.Range(0, availablePlayers.Count);
PlayerControllerB obj = availablePlayers[index];
availablePlayers.RemoveAt(index);
int index2 = Random.Range(0, availablePlayers.Count);
PlayerControllerB val = availablePlayers[index2];
availablePlayers.RemoveAt(index2);
Vector3 position = ((Component)obj).transform.position;
Vector3 position2 = ((Component)val).transform.position;
obj.TeleportPlayer(position2, false, 0f, false, true);
val.TeleportPlayer(position, false, 0f, false, true);
}
public bool CanBeApplied()
{
return GetAvailablePlayers().Count >= 2;
}
private List<PlayerControllerB> GetAvailablePlayers()
{
List<PlayerControllerB> list = HuraHelper.Players.ToList();
for (int num = list.Count - 1; num >= 0; num--)
{
PlayerControllerB val = list[num];
if (val.isPlayerDead)
{
list.RemoveAt(num);
}
else if (val.inTerminalMenu)
{
list.RemoveAt(num);
}
else if (val.isClimbingLadder)
{
list.RemoveAt(num);
}
}
return list;
}
}
internal static class CurseManager
{
private static List<ICurse> curses;
static CurseManager()
{
curses = new List<ICurse>();
curses.Add(new MoneyCurse());
}
public static ICurse GetRandomCurse()
{
List<ICurse> list = new List<ICurse>();
foreach (ICurse curse2 in curses)
{
if (curse2.CanBeApplied())
{
list.Add(curse2);
}
}
foreach (ICurse item in list)
{
LethalQuiz.logger.LogMessage((object)$"Generating random curse... Available curse: {item.GetType()}");
}
float num = 0f;
float num2 = 0f;
foreach (ICurse item2 in list)
{
num2 += item2.Weight;
}
float num3 = Random.Range(0f, num2);
list.ShuffleSelf();
for (int i = 0; i < list.Count; i++)
{
ICurse curse = list[i];
if (i == list.Count - 1)
{
return curse;
}
num += curse.Weight;
if (num3 < num)
{
return curse;
}
}
return null;
}
}
internal enum QuizResult
{
Success,
Failure,
NoQuiz
}
internal struct QuizResultInfo
{
public QuizResult result;
public Quiz? quiz;
}
public struct Quiz
{
public enum Answer
{
A,
B,
C,
D
}
public string question;
public string[] answers;
public byte rightAnswerIndex;
public string RightAnswer => answers[rightAnswerIndex];
public Quiz(string question, string[] answers, byte rightAnswerIndex)
{
this.question = question;
this.answers = answers;
this.rightAnswerIndex = rightAnswerIndex;
}
}
internal class QuizImporter
{
public static List<Quiz> ImportQuiz(string filePath)
{
List<Quiz> list = new List<Quiz>();
try
{
using (StreamReader streamReader = new StreamReader(filePath))
{
while (!streamReader.EndOfStream)
{
string[] array = streamReader.ReadLine().Split(new char[1] { ';' });
string question = array[0];
string[] answers = new string[4]
{
array[1],
array[2],
array[3],
array[4]
};
byte rightAnswerIndex = byte.Parse(array[5]);
list.Add(new Quiz(question, answers, rightAnswerIndex));
}
}
LethalQuiz.logger.LogMessage((object)$"Successfuly loaded quiz file {Path.GetFileName(filePath)}. Loaded quiz count: {list.Count}.");
}
catch (Exception ex)
{
LethalQuiz.logger.LogError((object)("Error occurred while parsing the CSV: " + ex.Message));
}
return list;
}
public static List<Quiz> ImportAllQuiz()
{
List<Quiz> list = new List<Quiz>();
string[] directories = Directory.GetDirectories(Paths.PluginPath, "lethalquiz", SearchOption.AllDirectories);
LethalQuiz.logger.LogMessage((object)$"Directories found: {directories.Length}. Plugin path = {Paths.PluginPath}");
string[] array = directories;
for (int i = 0; i < array.Length; i++)
{
string[] files = Directory.GetFiles(array[i], "*.csv");
foreach (string filePath in files)
{
list.AddRange(ImportQuiz(filePath));
}
}
if (list.Count == 0)
{
LethalQuiz.logger.LogMessage((object)"No quiz files has been found.");
}
return list;
}
}
[HarmonyPatch(typeof(Terminal))]
internal static class QuizManager
{
public const string EVENT_ON_START_QUIZ = "quiz.start_quiz";
public const string EVENT_ON_ANSWER_A = "quiz.answer_a";
public const string EVENT_ON_ANSWER_B = "quiz.answer_b";
public const string EVENT_ON_ANSWER_C = "quiz.answer_c";
public const string EVENT_ON_ANSWER_D = "quiz.answer_d";
private const string ATTRIBUTE_QUESTION = "[quiz]";
private const string ATTRIBUTE_ANSWER_A = "[answerA]";
private const string ATTRIBUTE_ANSWER_B = "[answerB]";
private const string ATTRIBUTE_ANSWER_C = "[answerC]";
private const string ATTRIBUTE_ANSWER_D = "[answerD]";
private const string ATTRIBUTE_ANSWER_HELP = "[answerHelp]";
private const string ATTRIBUTE_RESULT = "[quiz_result]";
private const int MONEY_REWARD_AMOUNT = 20;
private static StringBuilder builder = new StringBuilder();
private static Random random = new Random();
private static Quiz? currentQuiz;
private static QuizResultInfo? currentResult;
private static TerminalNode[] answerNodes = (TerminalNode[])(object)new TerminalNode[4];
private static TerminalNode helpNode;
private static TerminalNode quizNode;
private static TerminalNode answerANode;
private static TerminalNode answerBNode;
private static TerminalNode answerCNode;
private static TerminalNode answerDNode;
private static TerminalNode shipPhaseNode;
private static List<Quiz> quizList = new List<Quiz>();
[HarmonyPatch("Start")]
[HarmonyPostfix]
public static void Initialize()
{
AddQuizCommandsToTerminal();
}
public static void ImportQuiz()
{
quizList = QuizImporter.ImportAllQuiz();
}
public static void AddQuizCommandsToTerminal()
{
helpNode = TerminalHelper.CreateNode(CreateHelpText());
quizNode = TerminalHelper.CreateNode(CreateQuizTextTemplate());
answerANode = TerminalHelper.CreateNode(CreateResultTextTemplate());
answerBNode = TerminalHelper.CreateNode(CreateResultTextTemplate());
answerCNode = TerminalHelper.CreateNode(CreateResultTextTemplate());
answerDNode = TerminalHelper.CreateNode(CreateResultTextTemplate());
shipPhaseNode = TerminalHelper.CreateNode("You cannot start a quiz while in the ship phase.");
answerNodes = (TerminalNode[])(object)new TerminalNode[4] { answerANode, answerBNode, answerCNode, answerDNode };
quizNode.terminalEvent = "quiz.start_quiz";
answerANode.terminalEvent = "quiz.answer_a";
answerBNode.terminalEvent = "quiz.answer_b";
answerCNode.terminalEvent = "quiz.answer_c";
answerDNode.terminalEvent = "quiz.answer_d";
TerminalKeyword obj = TerminalHelper.CreateKeyword("quiz", isVerb: true, helpNode);
TerminalKeyword val = TerminalHelper.CreateKeyword("start", isVerb: false, null);
TerminalKeyword val2 = TerminalHelper.CreateKeyword("answera", isVerb: false, answerANode);
TerminalKeyword val3 = TerminalHelper.CreateKeyword("answerb", isVerb: false, answerBNode);
TerminalKeyword val4 = TerminalHelper.CreateKeyword("answerc", isVerb: false, answerCNode);
TerminalKeyword val5 = TerminalHelper.CreateKeyword("answerd", isVerb: false, answerDNode);
TerminalHelper.AddCompatibleNounToVerb(obj, val, quizNode);
TerminalHelper.AddCompatibleNounToVerb(obj, val2, answerANode);
TerminalHelper.AddCompatibleNounToVerb(obj, val3, answerBNode);
TerminalHelper.AddCompatibleNounToVerb(obj, val4, answerCNode);
TerminalHelper.AddCompatibleNounToVerb(obj, val5, answerDNode);
TerminalHelper.AddKeyworkToTerminal(val);
TerminalHelper.AddKeyworkToTerminal(obj);
TerminalHelper.AddKeyworkToTerminal(val2);
TerminalHelper.AddKeyworkToTerminal(val3);
TerminalHelper.AddKeyworkToTerminal(val4);
TerminalHelper.AddKeyworkToTerminal(val5);
}
[HarmonyPatch("TextPostProcess")]
[HarmonyPostfix]
public static void PostProcessQuizText(ref string __result, string modifiedDisplayText, TerminalNode node)
{
if (currentQuiz.HasValue)
{
Quiz value = currentQuiz.Value;
__result = __result.Replace("[quiz]", value.question);
__result = __result.Replace("[answerA]", "A: " + value.answers[0]);
__result = __result.Replace("[answerB]", "B: " + value.answers[1]);
__result = __result.Replace("[answerC]", "C: " + value.answers[2]);
__result = __result.Replace("[answerD]", "D: " + value.answers[3]);
__result = __result.Replace("[answerHelp]", "Reply with \"answera\", \"answerb\"...");
}
if (currentResult.HasValue)
{
QuizResultInfo value2 = currentResult.Value;
switch (value2.result)
{
case QuizResult.Success:
__result = __result.Replace("[quiz_result]", TerminalHelper.CreateNodeText("Good Answer!", "", $"You have earned {20}$."));
break;
case QuizResult.Failure:
__result = __result.Replace("[quiz_result]", TerminalHelper.CreateNodeText("Bad Answer!", "", $"The correct answer was {value2.quiz.Value.RightAnswer}. You have lost {40}$."));
break;
case QuizResult.NoQuiz:
__result = __result.Replace("[quiz_result]", TerminalHelper.CreateNodeText("No quiz has started yet. Use \"quiz start\" to start a quiz."));
break;
}
}
else
{
__result = __result.Replace("[quiz_result]", TerminalHelper.CreateNodeText("No quiz has started yet. Use \"quiz start\" to start a quiz."));
}
}
[HarmonyPatch("RunTerminalEvents")]
[HarmonyPostfix]
public static void RunQuizTerminalEvents(TerminalNode node)
{
if (!string.IsNullOrEmpty(node.terminalEvent))
{
switch (node.terminalEvent)
{
case "quiz.start_quiz":
OnStartQuiz();
break;
case "quiz.answer_a":
OnAnswerA();
break;
case "quiz.answer_b":
OnAnswerB();
break;
case "quiz.answer_c":
OnAnswerC();
break;
case "quiz.answer_d":
OnAnswerD();
break;
}
}
}
[HarmonyPatch("ParsePlayerSentence")]
[HarmonyPostfix]
private static void ParsePlayerSentence(ref TerminalNode __result)
{
if (StartOfRound.Instance.inShipPhase && ((Object)(object)__result == (Object)(object)quizNode || answerNodes.Contains(__result)))
{
__result = shipPhaseNode;
}
}
[HarmonyPatch(typeof(StartOfRound), "ShipLeave")]
[HarmonyPostfix]
private static void ResetQuizWhenShipLeave()
{
currentQuiz = null;
}
private static void OnStartQuiz()
{
if (!currentQuiz.HasValue)
{
currentQuiz = GetRandomQuiz();
}
ResetCurrentResult();
}
private static Quiz GetRandomQuiz()
{
int index = random.Next(quizList.Count);
return quizList[index];
}
private static void OnAnswerA()
{
OnAnswer(0);
}
private static void OnAnswerB()
{
OnAnswer(1);
}
private static void OnAnswerC()
{
OnAnswer(2);
}
private static void OnAnswerD()
{
OnAnswer(3);
}
private static void OnAnswer(int answerIndex)
{
QuizResultInfo value;
if (!currentQuiz.HasValue)
{
value = default(QuizResultInfo);
value.result = QuizResult.NoQuiz;
currentResult = value;
return;
}
QuizResult quizResult = ((currentQuiz.Value.rightAnswerIndex != answerIndex) ? QuizResult.Failure : QuizResult.Success);
value = default(QuizResultInfo);
value.quiz = currentQuiz;
value.result = quizResult;
currentResult = value;
switch (quizResult)
{
case QuizResult.Success:
OnCorrectAnswer();
break;
case QuizResult.Failure:
OnWrongAnswer();
break;
}
ResetCurrentQuiz();
}
private static void OnCorrectAnswer()
{
HUDManager.Instance.UIAudio.PlayOneShot(HUDManager.Instance.displayCollectedScrapSFX);
Terminal terminal = TerminalHelper.Terminal;
terminal.groupCredits += 20;
TerminalHelper.Terminal.SyncGroupCreditsServerRpc(TerminalHelper.Terminal.groupCredits, TerminalHelper.Terminal.numberOfItemsInDropship);
}
private static void OnWrongAnswer()
{
HUDManager.Instance.UIAudio.PlayOneShot(HUDManager.Instance.levelIncreaseSFX);
LaunchRandomBadThing();
}
private static void LaunchRandomBadThing()
{
ICurse randomCurse = CurseManager.GetRandomCurse();
randomCurse.ApplyCurse();
LethalQuiz.logger.LogMessage((object)$"Curse applied: {randomCurse.GetType()}");
}
public static Quiz.Answer ParseAnswer(TerminalNode nodeResult)
{
for (int i = 0; i < 4; i++)
{
if ((Object)(object)answerNodes[i] == (Object)(object)nodeResult)
{
return (Quiz.Answer)i;
}
}
LethalQuiz.logger.LogError((object)"Error while parsing answer with given node. Given node was not one of the answer's node.");
return Quiz.Answer.A;
}
public static TerminalNode CreateQuizNode(Quiz quiz)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_0011: Expected O, but got Unknown
return new TerminalNode
{
displayText = CreateQuizTextTemplate()
};
}
private static string CreateHelpText()
{
return TerminalHelper.CreateNodeText("Welcome to the Lethal Quiz mod!", "", "Answer to questions to gain money. Be careful some bad things can happen if you give the right answer...", "", "To start a quiz, type \"quiz start\". Then answer with \"answera\" for answer A, \"answerb\" for answer B...", "", "");
}
private static string CreateQuizTextTemplate()
{
builder.Clear();
builder.Append("[quiz]").Append("\n").Append("\n")
.Append("[answerA]")
.Append("\n")
.Append("[answerB]")
.Append("\n")
.Append("[answerC]")
.Append("\n")
.Append("[answerD]")
.Append("\n")
.Append("\n\n")
.Append("[answerHelp]")
.Append("\n")
.Append("\n\n");
return builder.ToString();
}
private static string CreateResultTextTemplate()
{
builder.Clear();
builder.Append("[quiz_result]").Append("\n").Append("\n\n");
return builder.ToString();
}
private static void ResetCurrentQuiz()
{
currentQuiz = null;
}
private static void ResetCurrentResult()
{
currentResult = null;
}
}
}
namespace Hura.Patches
{
[HarmonyPatch(typeof(HUDManager))]
internal class HUDManagerPatch
{
[HarmonyPatch("ApplyPenalty")]
[HarmonyPrefix]
private static void NoMoneyLossPatch(ref int playersDead, ref int bodiesInsured)
{
}
}
}