using System; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using System.Threading.Tasks; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("ExpandedWinTracker.Plugin")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("0.0.1.0")] [assembly: AssemblyInformationalVersion("0.0.1+04db90a217b61f64020e47044550e36ca413dff6")] [assembly: AssemblyProduct("ExpandedWinTracker.Plugin")] [assembly: AssemblyTitle("ExpandedWinTracker.Plugin")] [assembly: AssemblyVersion("0.0.1.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace ExpandedWinTracker.Plugin { internal class ConfigDescriptionBuilder { public string English { get; set; } = ""; public string French { get; set; } = ""; public string German { get; set; } = ""; public string Russian { get; set; } = ""; public string Portuguese { get; set; } = ""; public string Chinese { get; set; } = ""; public string Spanish { get; set; } = ""; public string ChineseTraditional { get; set; } = ""; public string Korean { get; set; } = ""; public string Japanese { get; set; } = ""; public override string ToString() { StringBuilder stringBuilder = new StringBuilder(); if (!string.IsNullOrEmpty(English)) { stringBuilder.AppendLine(English); } if (!string.IsNullOrEmpty(French)) { stringBuilder.AppendLine(French); } if (!string.IsNullOrEmpty(German)) { stringBuilder.AppendLine(German); } if (!string.IsNullOrEmpty(Russian)) { stringBuilder.AppendLine(Russian); } if (!string.IsNullOrEmpty(Portuguese)) { stringBuilder.AppendLine(Portuguese); } if (!string.IsNullOrEmpty(Chinese)) { stringBuilder.AppendLine(Chinese); } if (!string.IsNullOrEmpty(Spanish)) { stringBuilder.AppendLine(Spanish); } if (!string.IsNullOrEmpty(ChineseTraditional)) { stringBuilder.AppendLine(ChineseTraditional); } if (!string.IsNullOrEmpty(Korean)) { stringBuilder.AppendLine(Korean); } if (!string.IsNullOrEmpty(Japanese)) { stringBuilder.AppendLine(Japanese); } return stringBuilder.ToString().TrimEnd(); } } [Serializable] public sealed class SoulSaviorClassCombination { [JsonProperty("classID")] public string ClassID { get; set; } = ""; [JsonProperty("subclassID")] public string SubclassID { get; set; } = ""; [JsonProperty("championIndex")] public int ChampionIndex { get; set; } [JsonProperty("highestGameLevel")] public int HighestGameLevel { get; set; } } [Serializable] public class SoulSaviorSoulWin { [JsonProperty("name")] public string Name { get; set; } = ""; [JsonProperty("tier")] public int Tier { get; set; } [JsonProperty("highestGameLevel")] public int HighestGameLevel { get; set; } } [Serializable] public class ExtraMetagameSaveData { [JsonProperty("soulSaviorClassWins")] public List SoulSaviorClassWins { get; set; } = new List(); [JsonProperty("soulWins")] public List SoulWins { get; set; } = new List(); } [BepInPlugin("ExpandedWinTracker.Plugin", "ExpandedWinTracker.Plugin", "0.0.1")] public class Plugin : BaseUnityPlugin { internal static ManualLogSource Logger = new ManualLogSource("ExpandedWinTracker.Plugin"); public static ConfigEntry? soulSaviorWinTracker; public static ConfigEntry? scanRunHistory; public static ExtraMetagameSaveData SaveData = new ExtraMetagameSaveData(); public static string SaveFilePath = string.Empty; public static string ModConfigDir = string.Empty; public void Awake() { //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Expected O, but got Unknown //IL_0092: Unknown result type (might be due to invalid IL or missing references) Logger = ((BaseUnityPlugin)this).Logger; ModConfigDir = Path.Combine(Paths.ConfigPath, "ExpandedWinTracker.Plugin"); SaveFilePath = Path.Combine(ModConfigDir, "extraMetagameSave.json"); ReadSaveData(); scanRunHistory = ((BaseUnityPlugin)this).Config.Bind("Options", "Scan Run History", true, new ConfigDescription(new ConfigDescriptionBuilder { English = "Scan the Run History JSON to populate unrecorded wins. This option will re-set itself to false the next time the game is ran.", Chinese = "" }.ToString(), (AcceptableValueBase)null, Array.Empty())); Logger.LogInfo((object)"Plugin ExpandedWinTracker.Plugin is loaded!"); new Harmony("ExpandedWinTracker.Plugin").PatchAll(); } public static void ReadSaveData() { CreateSaveDir(); if (!File.Exists(SaveFilePath)) { Logger.LogWarning((object)("No save data found at " + SaveFilePath)); WriteSaveData(); return; } ExtraMetagameSaveData extraMetagameSaveData = JsonConvert.DeserializeObject(File.ReadAllText(SaveFilePath)); if (extraMetagameSaveData == null) { Logger.LogWarning((object)("Failed to read save data at " + SaveFilePath)); extraMetagameSaveData = new ExtraMetagameSaveData(); } SaveData = extraMetagameSaveData; } public static void CreateSaveDir() { if (!Directory.Exists(ModConfigDir)) { Directory.CreateDirectory(ModConfigDir); } } public static void WriteSaveData() { CreateSaveDir(); string contents = JsonConvert.SerializeObject((object)SaveData, (Formatting)1); Logger.LogDebug((object)("Writing to " + SaveFilePath)); File.WriteAllText(SaveFilePath, contents); } internal static SoulSaviorSoulWin FindSoulWin(string name) { return SaveData.SoulWins.Find((SoulSaviorSoulWin x) => x.Name == name); } internal static void AddOrUpdateSoulWin(string name, int tier, int regionLevel) { SoulSaviorSoulWin soulSaviorSoulWin = FindSoulWin(name); if (soulSaviorSoulWin != null) { if (soulSaviorSoulWin.HighestGameLevel < regionLevel) { soulSaviorSoulWin.HighestGameLevel = regionLevel; Logger.LogInfo((object)$"Updated {name}({tier}) win to {regionLevel}"); } if (soulSaviorSoulWin.Tier < tier) { soulSaviorSoulWin.Tier = tier; Logger.LogInfo((object)$"Updated {name} win to {tier} level"); } } else { SaveData.SoulWins.Add(new SoulSaviorSoulWin { Name = name, Tier = tier, HighestGameLevel = regionLevel }); Logger.LogInfo((object)$"Added {name}({tier}) win at {regionLevel}"); } } internal static SoulSaviorClassCombination FindSoulSaviorWin(string classID, string subclassID, int championIndex) { return SaveData.SoulSaviorClassWins.Find((SoulSaviorClassCombination x) => x.ClassID == classID && x.SubclassID == subclassID && x.ChampionIndex == championIndex); } internal static void AddOrUpdateSoulSaviorWin(string classID, string subclassID, int championIndex, int regionLevel) { SoulSaviorClassCombination soulSaviorClassCombination = FindSoulSaviorWin(classID, subclassID, championIndex); if (soulSaviorClassCombination != null) { if (soulSaviorClassCombination.HighestGameLevel < regionLevel) { soulSaviorClassCombination.HighestGameLevel = regionLevel; Logger.LogInfo((object)$"Updated {classID}-({championIndex})/{subclassID} win to {regionLevel}"); } } else { SaveData.SoulSaviorClassWins.Add(new SoulSaviorClassCombination { ClassID = classID, SubclassID = subclassID, ChampionIndex = championIndex, HighestGameLevel = regionLevel }); Logger.LogInfo((object)$"Added {classID}-({championIndex})/{subclassID} win at {regionLevel}"); } } } public static class MyPluginInfo { public const string PLUGIN_GUID = "ExpandedWinTracker.Plugin"; public const string PLUGIN_NAME = "ExpandedWinTracker.Plugin"; public const string PLUGIN_VERSION = "0.0.1"; } } namespace ExpandedWinTracker.Plugin.patches { [HarmonyPatch(typeof(RunHistoryDataJson), "InitUserAsync")] public static class RunHistoryDataJson_InitUserAsync_Patch { [HarmonyPostfix] public static void Postfix(RunHistoryDataJson __instance, ref Task __result) { __result = AfterInit(__result, __instance); } private static async Task AfterInit(Task originalTask, RunHistoryDataJson instance) { await originalTask; if (Plugin.scanRunHistory.Value && AccessTools.Field(typeof(RunHistoryDataJson), "_runEntries").GetValue(instance) is List runEntries) { ScanRunHistory(runEntries); Plugin.scanRunHistory.Value = false; } } private static void ScanRunHistory(List runEntries) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Invalid comparison between Unknown and I4 foreach (RunEntry runEntry in runEntries) { RunAggregateData run = runEntry.GetRun(); Plugin.Logger.LogDebug((object)("Scanning run: " + runEntry.runId)); if ((int)((BaseSaveData)run).GetRunTypeEnum() != 6 || !run.GetVictory()) { continue; } IReadonly startingConditions = ((BaseSaveData)run).GetStartingConditions(); string classId = startingConditions.ClassId; string subclassId = startingConditions.SubclassId; int mainChampionIndex = startingConditions.MainChampionIndex; int regionRunDifficultyTier = startingConditions.GetRegionRunDifficultyTier(); Plugin.AddOrUpdateSoulSaviorWin(classId, subclassId, mainChampionIndex, regionRunDifficultyTier); foreach (SoulState soul in run.GetSouls()) { int tierLevel = soul.TierLevel; string name = ((Object)soul.GetSoulData()).name; Plugin.AddOrUpdateSoulWin(name.Substring(0, name.Length - 2), tierLevel, regionRunDifficultyTier); } } Plugin.WriteSaveData(); } } [HarmonyPatch(typeof(SaveManager), "TrackClassVictory")] public class TrackClassVictoryPatch { public static void Postfix(SaveManager __instance) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Invalid comparison between Unknown and I4 RunType runType = __instance.GetRunType(); string iD = ((GameData)__instance.GetMainClass()).GetID(); string iD2 = ((GameData)__instance.GetSubClass()).GetID(); int mainChampionIndex = __instance.GetMainChampionIndex(); int regionRunDifficultyTier = __instance.GetStartingConditions().GetRegionRunDifficultyTier(); if ((int)runType == 6) { Plugin.AddOrUpdateSoulSaviorWin(iD, iD2, mainChampionIndex, regionRunDifficultyTier); foreach (SoulState selectedSoul in __instance.GetSelectedSouls()) { string name = ((Object)selectedSoul.GetSoulData()).name; Plugin.AddOrUpdateSoulWin(name.Substring(0, name.Length - 2), selectedSoul.TierLevel, regionRunDifficultyTier); } } Plugin.WriteSaveData(); } } }