using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CycleRandomizer.Patches;
using DunGen;
using HarmonyLib;
using LethalLevelLoader;
using TMPro;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Video;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("CycleRandomizer")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("CycleRandomizer")]
[assembly: AssemblyCopyright("Copyright © 2024")]
[assembly: AssemblyTrademark("")]
[assembly: ComVisible(false)]
[assembly: Guid("3f8d7100-f4c7-4c78-86cc-49272a0208d5")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace CycleRandomizer
{
internal class ConfigManager
{
public static ConfigEntry<bool> isCycleDungeon;
public static ConfigEntry<bool> isMoonScreenHided;
public static ConfigEntry<string> moonDefaultExclusions;
public static ConfigEntry<string> dungeonDefaultExclusions;
internal static void Load()
{
isCycleDungeon = CycleRandomizer.configFile.Bind<bool>("_Global_", "Cycle Dungeon", true, "Activate dungeon cycling.");
isMoonScreenHided = CycleRandomizer.configFile.Bind<bool>("_Global_", "Hide Infos", true, "Hide information on moon screen.");
moonDefaultExclusions = CycleRandomizer.configFile.Bind<string>("_Global_", "Default moon exclusion list", (string)null, "Moons assigned to the list by default at the start of the game.");
dungeonDefaultExclusions = CycleRandomizer.configFile.Bind<string>("_Global_", "Default dungeon exclusion list", (string)null, "Dungeons assigned to the list by default at the start of the game.");
}
}
[BepInPlugin("Lega.CycleRandomizer", "Cycle Randomizer", "1.0.3")]
internal class CycleRandomizer : BaseUnityPlugin
{
private const string modGUID = "Lega.CycleRandomizer";
private const string modName = "Cycle Randomizer";
private const string modVersion = "1.0.3";
private readonly Harmony harmony = new Harmony("Lega.CycleRandomizer");
internal static ManualLogSource mls;
public static ConfigFile configFile;
public static List<string> cycleMoons = new List<string>();
public static List<string> cycleDungeons = new List<string>();
public static List<Dictionary<string, int>> planetWeights = new List<Dictionary<string, int>>();
private void Awake()
{
mls = Logger.CreateLogSource("CycleRandomizer");
configFile = ((BaseUnityPlugin)this).Config;
ConfigManager.Load();
harmony.PatchAll(typeof(CycleRandomizer));
harmony.PatchAll(typeof(StartOfRoundPatch));
harmony.PatchAll(typeof(TerminalPatch));
harmony.PatchAll(typeof(MoonPatch));
harmony.PatchAll(typeof(DungeonPatch));
}
}
}
namespace CycleRandomizer.Patches
{
internal class DungeonPatch
{
[HarmonyPatch(typeof(DungeonGenerator), "Generate")]
[HarmonyPostfix]
private static void GenerateDungeon()
{
AddCycleDungeon(DungeonManager.CurrentExtendedDungeonFlow.DungeonName, autoClear: true);
}
[HarmonyPatch(typeof(DungeonManager), "GetValidExtendedDungeonFlows")]
[HarmonyPostfix]
private static void PreventDungeon(ref List<ExtendedDungeonFlowWithRarity> __result)
{
if (!ConfigManager.isCycleDungeon.Value)
{
return;
}
List<ExtendedDungeonFlowWithRarity> list = new List<ExtendedDungeonFlowWithRarity>();
foreach (ExtendedDungeonFlowWithRarity item in __result)
{
if (!CycleRandomizer.cycleDungeons.Contains(item.extendedDungeonFlow.DungeonName))
{
list.Add(item);
}
}
if (list.Count > 0)
{
__result = list;
}
}
internal static bool AddCycleDungeon(string dungeonName, bool autoClear = false)
{
if (!CycleRandomizer.cycleDungeons.Contains(dungeonName))
{
CycleRandomizer.cycleDungeons.Add(dungeonName);
if (autoClear && CycleRandomizer.cycleDungeons.Count == PatchedContent.ExtendedDungeonFlows.Select((ExtendedDungeonFlow d) => d.DungeonName).Distinct().Count())
{
CycleRandomizer.cycleDungeons.Clear();
}
RefreshTerminalCycleDungeons();
return true;
}
return false;
}
internal static bool RemoveCycleDungeon(string dungeonName)
{
if (!CycleRandomizer.cycleDungeons.Contains(dungeonName))
{
return false;
}
CycleRandomizer.cycleDungeons.Remove(dungeonName);
RefreshTerminalCycleDungeons();
return true;
}
internal static void RefreshTerminalCycleDungeons()
{
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
string text = "List of dungeons that cannot be randomly selected:\n\n";
foreach (string cycleDungeon in CycleRandomizer.cycleDungeons)
{
text = text + "* " + cycleDungeon + "\n";
}
text += "\n\n";
((Terminal)AccessTools.Field(typeof(HUDManager), "terminalScript").GetValue(HUDManager.Instance)).terminalNodes.allKeywords.First((TerminalKeyword k) => ((Object)k).name.Equals("CycleDisplayDungeons")).specialKeywordResult.displayText = text;
}
}
internal class MoonPatch
{
[HarmonyPatch(typeof(StartOfRound), "StartGame")]
[HarmonyPostfix]
private static void StartGame(ref SelectableLevel ___currentLevel)
{
AddCycleMoon(___currentLevel.PlanetName, autoClear: true);
}
internal static bool AddCycleMoon(string planetName, bool autoClear = false)
{
if (!CycleRandomizer.cycleMoons.Contains(planetName))
{
CycleRandomizer.cycleMoons.Add(planetName);
if (autoClear && CycleRandomizer.cycleMoons.Count == StartOfRound.Instance.levels.Where((SelectableLevel l) => l.planetHasTime).Count())
{
CycleRandomizer.cycleMoons.Clear();
}
RefreshTerminalCycleMoons();
return true;
}
return false;
}
internal static bool RemoveCycleMoon(string planetName)
{
if (!CycleRandomizer.cycleMoons.Contains(planetName))
{
return false;
}
CycleRandomizer.cycleMoons.Remove(planetName);
RefreshTerminalCycleMoons();
return true;
}
internal static void RefreshTerminalCycleMoons()
{
//IL_0070: Unknown result type (might be due to invalid IL or missing references)
string text = "List of moons that cannot be randomly selected:\n\n";
foreach (string cycleMoon in CycleRandomizer.cycleMoons)
{
text = text + "* " + cycleMoon + "\n";
}
text += "\n\n";
((Terminal)AccessTools.Field(typeof(HUDManager), "terminalScript").GetValue(HUDManager.Instance)).terminalNodes.allKeywords.First((TerminalKeyword k) => ((Object)k).name.Equals("CycleDisplayMoons")).specialKeywordResult.displayText = text;
}
}
internal class StartOfRoundPatch
{
[HarmonyPatch(typeof(StartOfRound), "Start")]
[HarmonyPostfix]
private static void BindMoonsConfig(ref StartOfRound __instance)
{
//IL_0073: Unknown result type (might be due to invalid IL or missing references)
//IL_007d: Expected O, but got Unknown
foreach (string item in from l in __instance.levels
where l.planetHasTime
select l.PlanetName)
{
if (!CycleRandomizer.configFile.ContainsKey(new ConfigDefinition("Moons", item + " Weight")))
{
ConfigEntry<int> val = CycleRandomizer.configFile.Bind<int>("Moons", item + " Weight", 1, "Weighting value for " + item + " to be randomly selected");
CycleRandomizer.planetWeights.Add(new Dictionary<string, int> { { item, val.Value } });
}
}
}
[HarmonyPatch(typeof(StartOfRound), "SetMapScreenInfoToCurrentLevel")]
[HarmonyAfter(new string[] { "mrov.WeatherRegistry" })]
[HarmonyPostfix]
private static void HideMapScreenInfo(ref StartOfRound __instance, ref VideoPlayer ___screenLevelVideoReel, ref TextMeshProUGUI ___screenLevelDescription)
{
if (ConfigManager.isMoonScreenHided.Value && !((Object)__instance.currentLevel).name.Equals("CompanyBuildingLevel"))
{
((TMP_Text)___screenLevelDescription).text = "Orbiting: Unknown\nPOPULATION: Unknown\nCONDITIONS: Unknown\nFAUNA: Unknown\nWeather: Unknown";
((Behaviour)___screenLevelVideoReel).enabled = false;
___screenLevelVideoReel.clip = null;
((Component)___screenLevelVideoReel).gameObject.SetActive(false);
___screenLevelVideoReel.Stop();
}
}
}
internal class TerminalPatch
{
[HarmonyPatch(typeof(Terminal), "Awake")]
[HarmonyPostfix]
private static void AddCommands(ref Terminal __instance)
{
AddCycleRandomCommand(ref __instance);
AddCycleDisplayCommands(ref __instance);
AddCycleClearFillCommands(ref __instance);
AddCycleAddRemoveCommands(ref __instance, "Moon", "cam", "crm", from l in StartOfRound.Instance.levels
where l.planetHasTime
select l.PlanetName);
AddCycleAddRemoveCommands(ref __instance, "Dungeon", "cad", "crd", PatchedContent.ExtendedDungeonFlows.Select((ExtendedDungeonFlow d) => d.DungeonName).Distinct());
}
[HarmonyPatch(typeof(Terminal), "ParsePlayerSentence")]
[HarmonyPostfix]
private static void RunCommands(ref TerminalNode __result, ref Terminal __instance)
{
if ((Object)(object)__result == (Object)null)
{
return;
}
if (!((NetworkBehaviour)GameNetworkManager.Instance.localPlayerController).IsServer && !((NetworkBehaviour)GameNetworkManager.Instance.localPlayerController).IsHost && (((Object)__result).name.Contains("cycle") || ((Object)__result).name.Contains("Cycle")))
{
__result = CreateTerminalNode("Unauthorized", "Command not found or usable only by host.\n\n\n");
}
else if (((NetworkBehaviour)GameNetworkManager.Instance.localPlayerController).IsServer || ((NetworkBehaviour)GameNetworkManager.Instance.localPlayerController).IsHost)
{
if (((Object)__result).name.Equals("cyclerandomNode"))
{
HandleCycleRandomNode(ref __result, ref __instance);
}
else if (((Object)__result).name.Contains("CycleClear"))
{
HandleCycleClearNode(((Object)__result).name);
}
else if (((Object)__result).name.Contains("CycleFill"))
{
HandleCycleFillNode(((Object)__result).name);
}
else if (((Object)__result).name.Contains("cycleaddmoon") || ((Object)__result).name.Contains("cycleremovemoon"))
{
HandleCycleAddRemoveNode(ref __result, "moon", ((Object)__result).name.Contains("add"));
}
else if (((Object)__result).name.Contains("cycleadddungeon") || ((Object)__result).name.Contains("cycleremovedungeon"))
{
HandleCycleAddRemoveNode(ref __result, "dungeon", ((Object)__result).name.Contains("add"));
}
}
}
private static void AddCycleRandomCommand(ref Terminal __instance)
{
//IL_0069: Unknown result type (might be due to invalid IL or missing references)
//IL_006e: Unknown result type (might be due to invalid IL or missing references)
//IL_0075: Unknown result type (might be due to invalid IL or missing references)
//IL_008e: Expected O, but got Unknown
TerminalKeyword val = ((IEnumerable<TerminalKeyword>)__instance.terminalNodes.allKeywords).FirstOrDefault((Func<TerminalKeyword, bool>)((TerminalKeyword k) => ((Object)k).name == "Route"));
TerminalKeyword val2 = CreateTerminalKeyword("CycleRandom", "cyclerandom", isVerb: false, val);
__instance.terminalNodes.allKeywords = CollectionExtensions.AddToArray<TerminalKeyword>(__instance.terminalNodes.allKeywords, val2);
val.compatibleNouns = CollectionExtensions.AddToArray<CompatibleNoun>(val.compatibleNouns, new CompatibleNoun
{
noun = val2,
result = CreateTerminalNode("cyclerandomNode", null)
});
}
private static void AddCycleDisplayCommands(ref Terminal __instance)
{
string text = "List of moons that cannot be randomly selected:\n\n";
if (!string.IsNullOrEmpty(ConfigManager.moonDefaultExclusions.Value))
{
string[] array = ConfigManager.moonDefaultExclusions.Value.Split(new char[1] { ',' });
foreach (string moonName in array)
{
string text2 = StartOfRound.Instance.levels.Where((SelectableLevel l) => GetNameOnlyWithLetters(l.PlanetName).ToLower().Contains(moonName.ToLower())).FirstOrDefault()?.PlanetName;
if (!string.IsNullOrEmpty(text2) && !CycleRandomizer.cycleMoons.Contains(text2))
{
CycleRandomizer.cycleMoons.Add(text2);
text = text + "* " + text2 + "\n";
}
}
text += "\n\n";
}
string text3 = "List of dungeons that cannot be randomly selected:\n\n";
if (!string.IsNullOrEmpty(ConfigManager.dungeonDefaultExclusions.Value))
{
string[] array = ConfigManager.dungeonDefaultExclusions.Value.Split(new char[1] { ',' });
foreach (string dungeonName in array)
{
string text4 = (from d in PatchedContent.ExtendedDungeonFlows.Select((ExtendedDungeonFlow d) => d.DungeonName).Distinct()
where GetNameOnlyWithLetters(d).ToLower().Contains(dungeonName.ToLower())
select d).FirstOrDefault();
if (!string.IsNullOrEmpty(text4) && !CycleRandomizer.cycleDungeons.Contains(text4))
{
CycleRandomizer.cycleDungeons.Add(text4);
text3 = text3 + "* " + text4 + "\n";
}
}
text3 += "\n\n";
}
AddCycleDisplayCommand(ref __instance, "CycleDisplayMoons", "cdm", "CycleMCatalogue", text);
AddCycleDisplayCommand(ref __instance, "CycleDisplayDungeons", "cdd", "CycleDCatalogue", text3);
}
private static void AddCycleDisplayCommand(ref Terminal __instance, string name, string word, string nodeName, string displayText)
{
TerminalKeyword val = CreateTerminalKeyword(name, word, isVerb: false, null, CreateTerminalNode(nodeName, displayText));
__instance.terminalNodes.allKeywords = CollectionExtensions.AddToArray<TerminalKeyword>(__instance.terminalNodes.allKeywords, val);
}
private static void AddCycleClearFillCommands(ref Terminal __instance)
{
AddCycleClearFillCommand(ref __instance, "CycleClearMoons", "ccm", "CycleClearM", "Moon list cleared.\n\n");
AddCycleClearFillCommand(ref __instance, "CycleClearDungeons", "ccd", "CycleClearD", "Dungeon list cleared.\n\n");
AddCycleClearFillCommand(ref __instance, "CycleFillMoons", "cfm", "CycleFillM", "Moon list filled.\n\n");
AddCycleClearFillCommand(ref __instance, "CycleFillDungeons", "cfd", "CycleFillD", "Dungeon list filled.\n\n");
}
private static void AddCycleClearFillCommand(ref Terminal __instance, string name, string word, string nodeName, string displayText)
{
TerminalKeyword val = CreateTerminalKeyword(name, word, isVerb: false, null, CreateTerminalNode(nodeName, displayText));
__instance.terminalNodes.allKeywords = CollectionExtensions.AddToArray<TerminalKeyword>(__instance.terminalNodes.allKeywords, val);
}
private static void AddCycleAddRemoveCommands(ref Terminal __instance, string type, string addWord, string removeWord, IEnumerable<string> names)
{
TerminalKeyword parentKeyword = CreateTerminalKeyword("CycleAdd" + type, addWord, isVerb: true);
__instance.terminalNodes.allKeywords = CollectionExtensions.AddToArray<TerminalKeyword>(__instance.terminalNodes.allKeywords, parentKeyword);
TerminalKeyword parentKeyword2 = CreateTerminalKeyword("CycleRemove" + type, removeWord, isVerb: true);
__instance.terminalNodes.allKeywords = CollectionExtensions.AddToArray<TerminalKeyword>(__instance.terminalNodes.allKeywords, parentKeyword2);
foreach (string name3 in names)
{
string name = GetNameOnlyWithLetters(name3);
string name2 = GetFirstPart(name3);
if (!string.IsNullOrEmpty(name2))
{
AddCycleAddRemoveCommand(ref __instance, ref parentKeyword, "cycleadd" + type.ToLower() + name.ToLower() + "Node", ref name2);
AddCycleAddRemoveCommand(ref __instance, ref parentKeyword2, "cycleremove" + type.ToLower() + name.ToLower() + "Node", ref name2);
}
AddCycleAddRemoveCommand(ref __instance, ref parentKeyword, "cycleadd" + type.ToLower() + name.ToLower() + "NodeFull", ref name);
AddCycleAddRemoveCommand(ref __instance, ref parentKeyword2, "cycleremove" + type.ToLower() + name.ToLower() + "NodeFull", ref name);
}
}
private static void AddCycleAddRemoveCommand(ref Terminal __instance, ref TerminalKeyword parentKeyword, string nodeWord, ref string name)
{
//IL_0046: Unknown result type (might be due to invalid IL or missing references)
//IL_004b: Unknown result type (might be due to invalid IL or missing references)
//IL_0052: Unknown result type (might be due to invalid IL or missing references)
//IL_0067: Expected O, but got Unknown
TerminalKeyword val = CreateTerminalKeyword(((Object)parentKeyword).name + name, name.ToLower(), isVerb: false, parentKeyword);
__instance.terminalNodes.allKeywords = CollectionExtensions.AddToArray<TerminalKeyword>(__instance.terminalNodes.allKeywords, val);
parentKeyword.compatibleNouns = CollectionExtensions.AddToArray<CompatibleNoun>(parentKeyword.compatibleNouns, new CompatibleNoun
{
noun = val,
result = CreateTerminalNode(nodeWord, null)
});
}
private static void HandleCycleRandomNode(ref TerminalNode __result, ref Terminal __instance)
{
List<CompatibleNoun> compatibleNouns = __instance.terminalNodes.allKeywords.First((TerminalKeyword k) => ((Object)k).name.Equals("Route")).compatibleNouns.Where((CompatibleNoun n) => n.result.buyRerouteToMoon == -2 && !CycleRandomizer.cycleMoons.Contains(StartOfRound.Instance.levels[n.result.displayPlanetInfo].PlanetName)).ToList();
if (compatibleNouns.Count <= 0)
{
__result = CreateTerminalNode("NoMoonFound", "No moon found.\n\n\n");
return;
}
CompatibleNoun val = GetRandomMoon(ref compatibleNouns);
if (val == null)
{
CycleRandomizer.mls.LogInfo((object)"No moons could be recovered from the weight configurations.");
val = compatibleNouns[new Random().Next(compatibleNouns.Count)];
}
TerminalNode result = val.result.terminalOptions.First((CompatibleNoun c) => ((Object)c.noun).name == "Confirm").result;
__result = CreateTerminalNode("NoMoonInfo", "Routing on a random moon.\n\n\n", result.buyRerouteToMoon);
}
private static CompatibleNoun GetRandomMoon(ref List<CompatibleNoun> compatibleNouns)
{
List<CompatibleNoun> list = new List<CompatibleNoun>();
foreach (CompatibleNoun compatibleNoun in compatibleNouns)
{
for (int i = 0; i < FindWeightByMoon(StartOfRound.Instance.levels[compatibleNoun.result.displayPlanetInfo].PlanetName); i++)
{
list.Add(compatibleNoun);
}
}
if (list.Count > 0)
{
return list[new Random().Next(list.Count)];
}
return null;
}
public static int? FindWeightByMoon(string planetName)
{
foreach (Dictionary<string, int> planetWeight in CycleRandomizer.planetWeights)
{
if (planetWeight.TryGetValue(planetName, out var value))
{
return value;
}
}
return null;
}
private static void HandleCycleClearNode(string name)
{
if (name.Equals("CycleClearM"))
{
CycleRandomizer.cycleMoons.Clear();
MoonPatch.RefreshTerminalCycleMoons();
}
else if (name.Equals("CycleClearD"))
{
CycleRandomizer.cycleDungeons.Clear();
DungeonPatch.RefreshTerminalCycleDungeons();
}
}
private static void HandleCycleFillNode(string name)
{
if (name.Equals("CycleFillM"))
{
foreach (string item in from l in StartOfRound.Instance.levels
where l.planetHasTime
select l.PlanetName)
{
MoonPatch.AddCycleMoon(item);
}
return;
}
if (!name.Equals("CycleFillD"))
{
return;
}
foreach (string item2 in PatchedContent.ExtendedDungeonFlows.Select((ExtendedDungeonFlow d) => d.DungeonName).Distinct())
{
DungeonPatch.AddCycleDungeon(item2);
}
}
private static void HandleCycleAddRemoveNode(ref TerminalNode __result, string type, bool isAdd)
{
int length = ((isAdd ? "cycleadd" : "cycleremove") + type).Length;
string typeNode = ((Object)__result).name.Substring(length, ((Object)__result).name.IndexOf("Node") - length);
string text = ((!(type == "moon")) ? (from d in PatchedContent.ExtendedDungeonFlows.Select((ExtendedDungeonFlow d) => d.DungeonName).Distinct()
where GetNameOnlyWithLetters(d).ToLower().Contains(typeNode)
select d).FirstOrDefault() : StartOfRound.Instance.levels.Where((SelectableLevel l) => GetNameOnlyWithLetters(l.PlanetName).ToLower().Contains(typeNode)).FirstOrDefault()?.PlanetName);
if (string.IsNullOrEmpty(text))
{
__result = CreateTerminalNode("Cycle" + (isAdd ? "Add" : "Remove") + type + "NotFound", ((type == "moon") ? "Moon" : "Dungeon") + " not found.\n\n\n");
return;
}
bool flag = ((!(type == "moon")) ? (isAdd ? DungeonPatch.AddCycleDungeon(text) : DungeonPatch.RemoveCycleDungeon(text)) : (isAdd ? MoonPatch.AddCycleMoon(text) : MoonPatch.RemoveCycleMoon(text)));
__result = CreateTerminalNode("Cycle" + (isAdd ? "Add" : "Remove") + type + "Info" + (flag ? "Success" : "Fail"), text + (flag ? (" has been " + (isAdd ? "added to" : "removed from") + " the list.\n\n\n") : (isAdd ? " is already in the list.\n\n\n" : " is not in the list.\n\n\n")));
}
private static TerminalKeyword CreateTerminalKeyword(string name, string word, bool isVerb = false, TerminalKeyword defaultVerb = null, TerminalNode specialKeywordResult = null)
{
TerminalKeyword obj = ScriptableObject.CreateInstance<TerminalKeyword>();
((Object)obj).name = name;
obj.word = word;
obj.isVerb = isVerb;
obj.defaultVerb = defaultVerb;
obj.specialKeywordResult = specialKeywordResult;
return obj;
}
private static TerminalNode CreateTerminalNode(string name, string displayText, int buyRerouteToMoon = -1, int itemCost = 0, bool clearPreviousText = true)
{
TerminalNode obj = ScriptableObject.CreateInstance<TerminalNode>();
((Object)obj).name = name;
obj.displayText = displayText;
obj.buyRerouteToMoon = buyRerouteToMoon;
obj.itemCost = itemCost;
obj.clearPreviousText = clearPreviousText;
return obj;
}
private static string GetFirstPart(string name)
{
Match match = Regex.Match(name, "^[^a-zA-Z]*([a-zA-Z]+)[^a-zA-Z]+[a-zA-Z]");
if (!match.Success)
{
return string.Empty;
}
return match.Groups[1].Value;
}
private static string GetNameOnlyWithLetters(string name)
{
return new string(name.Where((char c) => char.IsLetter(c)).ToArray());
}
}
}