using System; using System.Collections; using System.Collections.Concurrent; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Numerics; using System.Reflection; using System.Reflection.Emit; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using BepInEx; using BepInEx.Bootstrap; using BepInEx.Configuration; using BepInEx.Logging; using CloverAPI.Assets; using CloverAPI.Classes; using CloverAPI.Classes.Interfaces; using CloverAPI.Content.Audio; using CloverAPI.Content.Builders; using CloverAPI.Content.Charms; using CloverAPI.Content.Data; using CloverAPI.Content.Settings; using CloverAPI.Content.Strings; using CloverAPI.Content.Textures; using CloverAPI.Internal; using CloverAPI.SaveData; using CloverAPI.Utils; using Cysharp.Threading.Tasks; using Cysharp.Threading.Tasks.CompilerServices; using HarmonyLib; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Panik; using TMPro; using UnityEngine; using UnityEngine.Networking; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("CloverAPI")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+8e6ab96d26dcf86060e469d366417958aab3e1b4")] [assembly: AssemblyProduct("CloverAPI")] [assembly: AssemblyTitle("CloverAPI")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [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 CloverAPI { public static class Logging { public static ManualLogSource Logger => Plugin.Log; public static void Log(LogLevel level, object message) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) Logger.Log(level, message); } public static void LogInfo(object message) { Logger.LogInfo(message); } public static void LogDebug(object message) { Logger.LogDebug(message); } public static void LogWarning(object message) { Logger.LogWarning(message); } public static void LogError(object message) { Logger.LogError(message); } public static void LogFatal(object message) { Logger.LogFatal(message); } } [BepInPlugin("ModdingAPIs.cloverpit.CloverAPI", "Clover API", "0.2.1")] [HarmonyPatch] public class Plugin : BaseUnityPlugin { public const string PluginGuid = "ModdingAPIs.cloverpit.CloverAPI"; public const string PluginName = "Clover API"; public const string PluginVer = "0.2.1"; internal const string MainContentFolder = "CloverAPI_Content"; private const int FONT_SIZE = 16; internal static ManualLogSource Log; internal static readonly Harmony Harmony = new Harmony("ModdingAPIs.cloverpit.CloverAPI"); internal static string PluginPath; internal static ConfigEntry EnableDebugKeys; internal static ConfigEntry UseFullQualityTextures; internal static ConfigEntry OverrideOrdering; public static string DataPath { get; private set; } public static string ImagePath { get; private set; } private void Awake() { Log = ((BaseUnityPlugin)this).Logger; PluginPath = Path.GetDirectoryName(((BaseUnityPlugin)this).Info.Location); DataPath = Path.Combine(PluginPath, "CloverAPI_Content", "Data"); ImagePath = Path.Combine(PluginPath, "CloverAPI_Content", "Images"); MakeConfig(); LoadAssets(); GameUtils.OnGameReady += OnReady; PersistentDataManager.RegisterData("CharmMappings", CharmMappings.Instance); PersistentDataManager.RegisterData("CharmData", AllCharmData.Instance); } private void Update() { if (!EnableDebugKeys.Value) { return; } int num = 1; if (Input.GetKey((KeyCode)304) || Input.GetKey((KeyCode)303)) { num *= 10; } if (Input.GetKey((KeyCode)306) || Input.GetKey((KeyCode)305)) { num *= 100; } if (Input.GetKey((KeyCode)308) || Input.GetKey((KeyCode)307)) { num *= 1000; } if (Input.GetKeyDown((KeyCode)282)) { GameplayData.CoinsAdd((BigInteger)num, false); } if (Input.GetKeyDown((KeyCode)283)) { GameplayData.CloverTicketsAdd((long)num, false); } if (Input.GetKeyDown((KeyCode)284)) { PowerupScript[] array = (from x in CharmManager.CustomCharms.Pick(4) select PowerupScript.GetPowerup_Quick(x.id)).ToArray(); if (array.Length < 4) { array = array.Concat(Enumerable.Repeat(null, 4 - array.Length)).ToArray(); } StoreCapsuleScript.Restock(false, true, array, false, false); } } private void OnReady() { LoadData(); } private void OnEnable() { Harmony.PatchAll(); Logging.LogInfo("Loaded Clover API!"); } private void OnDisable() { Harmony.UnpatchSelf(); Logging.LogInfo("Unloaded Clover API!"); } private void MakeConfig() { EnableDebugKeys = ((BaseUnityPlugin)this).Config.Bind("General", "EnableDebugKeys", false, "If true, enables debug keybinds for testing purposes."); UseFullQualityTextures = ((BaseUnityPlugin)this).Config.Bind("General", "UseFullQualityTextures", true, "If true, uses full quality textures for modded content. If false, resizes them to match the size of the original texture. Some textures require a restart to take effect."); ModSettingsManager.RegisterPageFromConfig((BaseUnityPlugin)(object)this, "Clover API Settings"); OverrideOrdering = ((BaseUnityPlugin)this).Config.Bind("General", "OverrideOrdering", "", "A comma-separated list of mod GUIDs that specifies the order in which overrides are applied. The leftmost mod in the list has the highest priority. Mods that have conflicting overrides will automatically be added to the end."); ResourceOrdering._ConfigRef(OverrideOrdering); } private void LoadAssets() { string text = Path.Combine(PluginPath, DataPath, "templatemodel"); if (File.Exists(text)) { APIAssets._assetBundle = AssetBundle.LoadFromFile(text); return; } Logging.LogError("AssetBundle not found at " + text + "!"); Logging.LogError("Make sure you have the '" + DataPath + "' folder in the same directory as the plugin DLL with the 'templatemodel' AssetBundle inside it."); } private void LoadData() { LoadTranslations(); } private void LoadTranslations() { //IL_001a: Unknown result type (might be due to invalid IL or missing references) string[] files = Directory.GetFiles(PluginPath, "*.loc", SearchOption.AllDirectories); string[] array = files; for (int i = 0; i < array.Length; i++) { LanguageManager.LoadFromFile(array[i]); } if (files.Length != 0) { Logging.LogInfo($"Loaded {files.Length} translation files."); } } } } namespace CloverAPI.Utils { public static class AssetBundleUtils { private static readonly Dictionary LoadedAssetBundles = new Dictionary(); public static GameObject InstantiateCustomModelCharm(string assetBundleName, string prefabName = null) { return Object.Instantiate(AssetBundleUtils.LoadFromAssetBundle(assetBundleName, prefabName)); } public static T LoadFromAssetBundle(string assetBundleName, string assetName = null) where T : Object { string text = FileUtils.FindFile(assetBundleName); AssetBundle val = LoadAssetBundle(text); if ((Object)(object)val == (Object)null) { Logging.LogError("Could not load AssetBundle from path: " + text); return default(T); } if (string.IsNullOrEmpty(assetName)) { T[] array = val.LoadAllAssets(); if (array.Length != 0) { if (array.Length > 1) { Logging.LogWarning($"Multiple assets of type {typeof(T)} found in AssetBundle '{assetBundleName}'. Using the first one found: '{((Object)array[0]).name}'."); } return array[0]; } Logging.LogError($"No assets of type {typeof(T)} found in AssetBundle '{assetBundleName}'."); PrintContentsOfAssetBundle(val); return default(T); } T val2 = val.LoadAsset(assetName); if ((Object)(object)val2 == (Object)null) { Logging.LogError($"Could not find asset '{assetName}' of type {typeof(T)} in AssetBundle '{assetBundleName}'."); PrintContentsOfAssetBundle(val); return default(T); } return val2; } public static AssetBundle LoadAssetBundle(string path) { if (LoadedAssetBundles.TryGetValueNoCase(path, out var value)) { return value; } AssetBundle val = AssetBundle.LoadFromFile(path); if ((Object)(object)val == (Object)null) { Logging.LogError("Failed to load AssetBundle from path: " + path); return null; } LoadedAssetBundles[path] = val; return val; } public static void PrintContentsOfAssetBundle(AssetBundle assetBundle) { string[] allAssetNames = assetBundle.GetAllAssetNames(); Logging.LogInfo("AssetBundle contains the following assets:"); string[] array = allAssetNames; foreach (string text in array) { Logging.LogInfo("- " + text); } } } public static class AudioUtils { public static AudioClip LoadAudioFromFile(string filePath, string onNotFound = "error", string onMultipleFound = "warn") { //IL_0049: Unknown result type (might be due to invalid IL or missing references) //IL_004f: Invalid comparison between Unknown and I4 string text = FileUtils.FindFile(filePath, onNotFound, onMultipleFound); if (!File.Exists(text)) { throw new FileNotFoundException("File '" + filePath + "' not found."); } UnityWebRequest audioClip = UnityWebRequestMultimedia.GetAudioClip("file://" + text, (AudioType)0); audioClip.SendWebRequest(); while (!audioClip.isDone) { } if ((int)audioClip.result == 1) { AudioClip content = DownloadHandlerAudioClip.GetContent(audioClip); audioClip.Dispose(); return content; } Logging.LogError("Failed to load audio clip from file '" + filePath + "': " + audioClip.error); audioClip.Dispose(); return null; } } public class FileUtils { public static string[] FindFiles(string fileName) { if (Path.IsPathRooted(fileName)) { if (File.Exists(fileName)) { return new string[1] { fileName }; } return Array.Empty(); } return Directory.GetFiles(Paths.PluginPath, fileName, SearchOption.AllDirectories); } public static string FindFile(string fileName, string onNotFound = "error", string onMultipleFound = "warn") { string[] array = FindFiles(fileName); if (array.Length < 1) { if (onNotFound == "error") { throw new FileNotFoundException("File '" + fileName + "' not found in plugin directory."); } if (onNotFound == "warn") { Logging.LogWarning("File '" + fileName + "' not found in plugin directory."); } return null; } if (array.Length > 1) { if (onMultipleFound == "error") { throw new Exception("Multiple files named '" + fileName + "' found in plugin directory."); } if (onMultipleFound == "warn") { Logging.LogWarning("Multiple files named '" + fileName + "' found. Using the first one found at '" + array[0] + "'."); } } return array[0]; } public static byte[] LoadFileBytes(string filePath, string onNotFound = "error", string onMultipleFound = "warn") { string path = FindFile(filePath, onNotFound, onMultipleFound); if (!File.Exists(path)) { throw new FileNotFoundException("File '" + filePath + "' not found."); } return File.ReadAllBytes(path); } } public class GameUtils { public static bool GameReady { get; internal set; } = false; public static event Action OnGameReady; internal static void TriggerGameReady() { GameReady = true; GameUtils.OnGameReady(); } static GameUtils() { GameUtils.OnGameReady = delegate { }; } } public static class LinqExtensions { public static T MaxBy(this IEnumerable source, Func selector) where TKey : IComparable { using IEnumerator enumerator = source.GetEnumerator(); if (!enumerator.MoveNext()) { throw new InvalidOperationException("Sequence contains no elements"); } T val = enumerator.Current; TKey other = selector(val); while (enumerator.MoveNext()) { T current = enumerator.Current; TKey val2 = selector(current); if (val2.CompareTo(other) > 0) { val = current; other = val2; } } return val; } public static bool TryGetValueNoCase(this IDictionary dictionary, string key, out TValue value) { foreach (KeyValuePair item in dictionary) { if (string.Equals(item.Key, key, StringComparison.OrdinalIgnoreCase)) { value = item.Value; return true; } } value = default(TValue); return false; } } public class MathUtils { public static float RoundToMultipleOf(float value, float multiple) { if (multiple == 0f) { throw new ArgumentException("Multiple cannot be zero.", "multiple"); } return (float)(Math.Round(value / multiple) * (double)multiple); } public static float RoundToNearestSignificantFive(float value, int unroundedSignificantDigits = 1) { if (value == 0f) { return 0f; } float num = (float)Math.Pow(10.0, Math.Floor(Math.Log10(Math.Abs(value))) + 1.0 - (double)unroundedSignificantDigits); return RoundToMultipleOf(value, 0.5f * num); } public static float RoundToNearestSignificant(float value, int significantDigits = 1) { if (value == 0f) { return 0f; } float num = (float)Math.Pow(10.0, Math.Floor(Math.Log10(Math.Abs(value))) + 1.0 - (double)significantDigits); return (float)(Math.Round(value / num) * (double)num); } } public static class RandomUtils { public static IList Pick(this IList list, int count) { if (count == 1) { return new List { list.Pick() }; } if (list == null) { throw new ArgumentNullException("list", "The list cannot be null."); } if (list.Count == 0) { throw new ArgumentException("The list cannot be empty.", "list"); } if (count > list.Count) { count = list.Count; } HashSet hashSet = new HashSet(); while (hashSet.Count < count) { int item = Random.Range(0, list.Count); hashSet.Add(item); } List list2 = new List(); foreach (int item2 in hashSet) { list2.Add(list[item2]); } return list2; } public static T Pick(this IList list) { if (list == null) { throw new ArgumentNullException("list", "The list cannot be null."); } if (list.Count == 0) { throw new ArgumentException("The list cannot be empty.", "list"); } int index = Random.Range(0, list.Count); return list[index]; } } public static class TextureUtils { public static Texture2D LoadTextureFromFile(string filePath, FilterMode filterMode = (FilterMode)0, string onNotFound = "error", string onMultipleFound = "warn") { //IL_0004: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) //IL_0019: Expected O, but got Unknown //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Expected O, but got Unknown Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false); byte[] array = FileUtils.LoadFileBytes(filePath, onNotFound, onMultipleFound); if (!ImageConversion.LoadImage(val, array)) { throw new Exception("Failed to load texture from file '" + filePath + "'. The file may not be a valid image."); } ((Texture)val).filterMode = filterMode; ((Object)val).name = Path.GetFileNameWithoutExtension(filePath); return val; } public static Texture2D ResizeTexture(Texture2D original, int width, int height) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown RenderTexture val = (RenderTexture.active = RenderTexture.GetTemporary(width, height)); Graphics.Blit((Texture)(object)original, val); Texture2D val2 = new Texture2D(width, height); val2.ReadPixels(new Rect(0f, 0f, (float)width, (float)height), 0, 0); val2.Apply(); RenderTexture.active = null; RenderTexture.ReleaseTemporary(val); return val2; } public static Texture2D CopyTexture(Texture2D original) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Expected O, but got Unknown //IL_0075: Unknown result type (might be due to invalid IL or missing references) Texture2D val = new Texture2D(((Texture)original).width, ((Texture)original).height, original.format, ((Texture)original).mipmapCount > 1); if (((Texture)original).isReadable) { val.SetPixels(original.GetPixels()); val.Apply(); return val; } RenderTexture val2 = (RenderTexture.active = RenderTexture.GetTemporary(((Texture)original).width, ((Texture)original).height)); Graphics.Blit((Texture)(object)original, val2); val.ReadPixels(new Rect(0f, 0f, (float)((Texture)original).width, (float)((Texture)original).height), 0, 0); val.Apply(); RenderTexture.active = null; RenderTexture.ReleaseTemporary(val2); return val; } } } namespace CloverAPI.SaveData { public class AllCharmData : JsonPersistentData { public static AllCharmData Instance = new AllCharmData(); [JsonProperty] internal Dictionary Entries = new Dictionary(); public CharmData this[string guid] { get { return GetCharmDataByGUID(guid); } set { SetCharmDataByGuid(guid, value); } } public CharmData this[Identifier id] { get { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return GetCharmDataByID(id); } set { //IL_0001: Unknown result type (might be due to invalid IL or missing references) SetCharmDataById(id, value); } } public CharmData GetCharmDataByGUID(string guid) { if (!Entries.TryGetValueNoCase(guid, out var value)) { value = new CharmData(); Entries[guid] = value; } return value; } public CharmData GetCharmDataByID(Identifier id) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (CharmManager.IdToGUID.TryGetValue(id, out var value)) { return GetCharmDataByGUID(value); } return null; } public bool HasCharmDataByGuid(string guid) { return Entries.ContainsKey(guid); } public bool HasCharmDataById(Identifier id) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (CharmManager.IdToGUID.TryGetValue(id, out var value)) { return HasCharmDataByGuid(value); } return false; } public void RemoveCharmDataByGuid(string guid) { Entries.Remove(guid); } public void RemoveCharmDataById(Identifier id) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (CharmManager.IdToGUID.TryGetValue(id, out var value)) { RemoveCharmDataByGuid(value); } } public void SetCharmDataByGuid(string guid, CharmData data) { Entries[guid] = data; } public void SetCharmDataById(Identifier id, CharmData data) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (CharmManager.IdToGUID.TryGetValue(id, out var value)) { SetCharmDataByGuid(value, data); } } public override void OnReset() { Entries.Clear(); } } public class CharmData { [JsonProperty] internal Dictionary Data = new Dictionary(); public T Get(string key, T defaultValue = default(T)) { if (Data.TryGetValueNoCase(key, out var value)) { if (value is T) { return (T)value; } T val = JsonConvert.DeserializeObject(JsonConvert.SerializeObject(value)); if (val != null) { return val; } return defaultValue; } return defaultValue; } public string GetString(string key, string defaultValue = null) { return Get(key, defaultValue); } public int GetInt(string key, int defaultValue = 0) { return Get(key, defaultValue); } public float GetFloat(string key, float defaultValue = 0f) { return Get(key, defaultValue); } public bool GetBool(string key, bool defaultValue = false) { return Get(key, defaultValue); } public BigInteger GetBigInteger(string key, BigInteger defaultValue = default(BigInteger)) { return Get(key, defaultValue); } public List GetList(string key, List defaultValue = null) { return Get(key, defaultValue ?? new List()); } public T[] GetArray(string key, T[] defaultValue = null) { return Get(key, defaultValue ?? Array.Empty()); } public Dictionary GetDictionary(string key, Dictionary defaultValue = null) { return Get(key, defaultValue ?? new Dictionary()); } public void Set(string key, T value) { Data[key] = value; } public void Remove(string key) { Data.Remove(key); } public bool ContainsKey(string key) { return Data.ContainsKey(key); } } } namespace CloverAPI.Patches { [HarmonyPatch] public class AudioPatcher { [HarmonyPatch(typeof(AssetMaster), "GetSound")] [HarmonyPrefix] internal static bool AssetMaster_GetSound_Prefix(ref AudioClip __result, ref string clipName) { if (AudioManager.TryGetSoundOverride(clipName, out var audioClip)) { __result = audioClip; return false; } return true; } [HarmonyPatch(typeof(AssetMaster), "GetMusic")] [HarmonyPrefix] internal static bool AssetMaster_GetMusic_Prefix(ref AudioClip __result, ref string clipName) { if (AudioManager.TryGetMusicOverride(clipName, out var audioClip)) { __result = audioClip; return false; } return true; } } [HarmonyPatch] internal class CharmPatcher { [HarmonyPatch(typeof(PowerupScript), "InitializeAll")] [HarmonyTranspiler] internal static IEnumerable PowerupScript_InitializeAll_Transpiler(IEnumerable instructions) { IEnumerator enumerator = instructions.GetEnumerator(); bool done = false; while (enumerator.MoveNext()) { CodeInstruction instruction = enumerator.Current; yield return instruction; if (instruction.opcode == OpCodes.Ldc_I4 && instruction.operand is int num && num == 204) { break; } } while (enumerator.MoveNext()) { CodeInstruction instruction = enumerator.Current; yield return instruction; if (instruction.opcode == OpCodes.Callvirt && instruction.operand is MethodInfo methodInfo && methodInfo == AccessTools.Method(typeof(PowerupScript), "Initialize", (Type[])null, (Type[])null)) { yield return new CodeInstruction(OpCodes.Ldarg_1, (object)null); yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.Method(typeof(CharmPatcher), "InitCustomCharms", (Type[])null, (Type[])null)); done = true; break; } } while (enumerator.MoveNext()) { yield return enumerator.Current; } if (!done) { Logging.LogError("Failed to apply PowerupScript.InitializeAll transpiler patch!"); } enumerator.Dispose(); } [HarmonyPatch(typeof(GameplayData), "_EnsurePowerupDataArray")] [HarmonyTranspiler] internal static IEnumerable GameplayData__EnsurePowerupDataArray_Transpiler(IEnumerable instructions) { return ReplaceILCharmCounts(instructions, "error", "warn", 1, 1, "GameplayData._EnsurePowerupDataArray"); } [HarmonyPatch(typeof(TerminalScript), "Initialize")] [HarmonyTranspiler] internal static IEnumerable TerminalScript_Initialize_Transpiler(IEnumerable instructions) { return ReplaceILCharmCounts(instructions, "error", "warn", 1, 2, "TerminalScript.Initialize"); } private static IEnumerable ReplaceILCharmCounts(IEnumerable instructions, string onNotFound = "error", string onMultipleFound = "warn", int replaceAmount = 1, int expectedAmountSeen = 1, string caller = "unknown") { if (replaceAmount < 1 || expectedAmountSeen < 1) { Logging.LogError("Invalid arguments to ReplaceILCharmCounts in '" + caller + "'. Both replaceAmount and expectedAmountSeen must be at least 1."); replaceAmount = Mathf.Max(1, replaceAmount); expectedAmountSeen = Mathf.Max(1, expectedAmountSeen); } int seen = 0; foreach (CodeInstruction instruction in instructions) { if (instruction.opcode == OpCodes.Ldc_I4 && instruction.operand is int num && num == 205) { if (seen < replaceAmount) { yield return new CodeInstruction(OpCodes.Call, (object)AccessTools.PropertyGetter(typeof(CharmManager), "NewCount")); } else { yield return instruction; } seen++; } else { yield return instruction; } } if (seen < expectedAmountSeen) { if (expectedAmountSeen == 1) { if (onNotFound == "error") { Logging.LogError($"Transpiler patch in '{caller}' failed to replace any instances of 'ldc.i4 {205}'!"); } else if (onNotFound == "warn") { Logging.LogWarning($"Transpiler patch in '{caller}' failed to replace any instances of 'ldc.i4 {205}'!"); } } else if (onNotFound == "error") { Logging.LogError($"Transpiler patch in '{caller}' replaced only {seen} instances of 'ldc.i4 {205}'. Expected to replace exactly {expectedAmountSeen} instances, but could be fine if an update changed this."); } else if (onNotFound == "warn") { Logging.LogWarning($"Transpiler patch in '{caller}' replaced only {seen} instances of 'ldc.i4 {205}'. Expected to replace exactly {expectedAmountSeen} instances, but could be fine if an update changed this."); } } if (seen > expectedAmountSeen) { if (onMultipleFound == "error") { Logging.LogError(string.Format("Transpiler patch in '{0}' replaced {1} instances of 'ldc.i4 {2}'. Expected to replace exactly {3} instance{4}!", caller, seen, 205, expectedAmountSeen, (expectedAmountSeen == 1) ? "" : "s")); } else if (onMultipleFound == "warn") { Logging.LogWarning(string.Format("Transpiler patch in '{0}' replaced {1} instances of 'ldc.i4 {2}'. Expected to replace exactly {3} instance{4}!", caller, seen, 205, expectedAmountSeen, (expectedAmountSeen == 1) ? "" : "s")); } } } private static void InitCustomCharms(bool isNewGame) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) foreach (CharmBuilder customCharm in CharmManager.CustomCharms) { CharmManager.SpawnCustomCharm(customCharm.id, isNewGame); } } } [HarmonyPatch] public class GamePatcher { [HarmonyPatch(typeof(IntroScript), "Start")] [HarmonyPrefix] internal static void IntroScript_Start_Prefix() { GameUtils.TriggerGameReady(); } [HarmonyPatch(/*Could not decode attribute arguments.*/)] [HarmonyPrefix] internal static bool Master__PlatformKind_Get_Prefix(ref PlatformKind __result) { __result = (PlatformKind)0; return false; } } [HarmonyPatch] internal class SavePatcher { [HarmonyPatch(typeof(Data), "SaveGame")] [HarmonyPrefix] internal static void Data_SaveGame_Postfix() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) UniTaskExtensions.Forget(PersistentDataManager.SaveData()); } [HarmonyPatch(typeof(Data), "LoadGame")] [HarmonyPostfix] internal static void Data_LoadGame_Postfix() { //IL_0000: Unknown result type (might be due to invalid IL or missing references) UniTaskExtensions.Forget(PersistentDataManager.LoadData()); } [HarmonyPatch(typeof(GameData), "GameplayDataReset")] [HarmonyPostfix] internal static void Data_GameData_GameplayDataReset_Postfix(GameData __instance) { PersistentDataManager.ResetAll(); } [HarmonyPatch(typeof(PlatformDataMaster), "PathGet_GameDataFile")] [HarmonyPostfix] internal static void PlatformDataMaster_PathGet_GameDataFile_Postfix(ref string __result, string extraAppendix) { string text = PlatformDataMaster.GameFolderPath + "GameDataModded" + extraAppendix + ".json"; if (!File.Exists(text) && File.Exists(__result)) { File.Copy(__result, text); } __result = text; } } internal static class HubState { public enum View { None, HubIndex, Page } public static View Current = View.None; public static int HubPageOffset = 0; public static int PageItemOffset = 0; public static int ActivePage = -1; public static void Reset() { Current = View.None; HubPageOffset = 0; PageItemOffset = 0; ActivePage = -1; } } [HarmonyPatch] internal static class MainMenu_Patches { private readonly struct HubLayout { public int Capacity { get; } public int ListSlots { get; } public int NextRow { get; } public int BackRow { get; } public int Step { get; } public HubLayout(int capacity) { Capacity = capacity; BackRow = capacity - 1; NextRow = ((capacity >= 2) ? (capacity - 2) : (-1)); ListSlots = Mathf.Max(0, capacity - 2); Step = Math.Max(1, ListSlots); } } private const int ModsSlotWithoutTwitch = 4; private const int ModsSlotWithTwitch = 5; private static bool _rowValidationLogged; private static readonly FieldInfo DesiredNavigationIndexField = AccessTools.Field(typeof(MainMenuScript), "desiredNavigationIndex"); private static readonly FieldInfo ControllerElementsField = AccessTools.Field(typeof(DiegeticMenuController), "elements"); private static readonly FieldInfo RightNavigationPressField = AccessTools.Field(typeof(MainMenuScript), "rightNavigationPress"); private static readonly FieldInfo LeftNavigationPressField = AccessTools.Field(typeof(MainMenuScript), "leftNavigationPress"); private static readonly FieldInfo MenuIndexField = AccessTools.Field(typeof(MainMenuScript), "menuIndex"); private static bool IsInHub() { return HubState.Current != HubState.View.None; } private static bool HasPages() { return ModSettingsManager.Pages.Count > 0; } private static int GetModsSlotIndex() { if (!TwitchMaster.IsTwitchSupported()) { return 4; } return 5; } private static int GetBackSlotIndex() { return GetModsSlotIndex() + 1; } private static void SetDesiredNavigationIndex(MainMenuScript menu, int value) { DesiredNavigationIndexField?.SetValue(menu, value); } private static MenuIndex GetMenuIndex(MainMenuScript menu) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) if (MenuIndexField == null) { return (MenuIndex)(-1); } object value = MenuIndexField.GetValue(menu); if (value is MenuIndex) { return (MenuIndex)value; } return (MenuIndex)(-1); } private static void SetMenuIndex(MainMenuScript menu, MenuIndex value) { //IL_000b: Unknown result type (might be due to invalid IL or missing references) MenuIndexField?.SetValue(menu, value); } private static List? GetElementsList(DiegeticMenuController? controller) { if ((Object)(object)controller == (Object)null || ControllerElementsField == null) { return null; } return ControllerElementsField.GetValue(controller) as List; } private static bool GetFlag(FieldInfo field, MainMenuScript menu) { bool flag = default(bool); int num; if (field != null && (Object)(object)menu != (Object)null) { object value = field.GetValue(menu); if (value is bool) { flag = (bool)value; num = 1; } else { num = 0; } } else { num = 0; } return (byte)((uint)num & (flag ? 1u : 0u)) != 0; } private static void SetTextSafe(TextMeshProUGUI[] texts, int index, string? value) { if (texts != null && index >= 0 && index < texts.Length && (Object)(object)texts[index] != (Object)null) { ((TMP_Text)texts[index]).text = value ?? string.Empty; } } private static DiegeticMenuElement? GetMenuElement(MainMenuScript? menu, int index) { if (menu?.menuElements == null || index < 0 || index >= menu.menuElements.Length) { return null; } return menu.menuElements[index]; } private static bool TryGetOptionTexts(MainMenuScript menu, out TextMeshProUGUI[] optionTexts) { if (menu?.optionTexts == null || menu.optionTexts.Length == 0) { optionTexts = Array.Empty(); return false; } optionTexts = menu.optionTexts; return true; } private static bool TryGetHubLayout(MainMenuScript menu, out HubLayout layout) { layout = default(HubLayout); if ((Object)(object)menu == (Object)null) { return false; } int num = VisibleCapacity(menu); if (num <= 0) { return false; } layout = new HubLayout(num); return true; } private static bool TryGetActivePage(out ModSettingsManager.Page page) { int activePage = HubState.ActivePage; if (activePage >= 0 && activePage < ModSettingsManager.Pages.Count) { page = ModSettingsManager.Pages[activePage]; return true; } page = null; return false; } private static void RenderListEntries(TextMeshProUGUI[] optionTexts, int listSlots, int start, int total, Func labelSelector) { int i = 0; int num = start; while (num < total && i < listSlots) { SetTextSafe(optionTexts, i, labelSelector(num)); num++; i++; } for (; i < listSlots; i++) { SetTextSafe(optionTexts, i, string.Empty); } } private static void RenderPaginationFooter(TextMeshProUGUI[] optionTexts, HubLayout layout, int renderedEnd, int total) { if (layout.NextRow >= 0) { SetTextSafe(optionTexts, layout.NextRow, (renderedEnd < total) ? "NEXT" : string.Empty); } SetTextSafe(optionTexts, layout.BackRow, "BACK"); } private static int ClampPageOffset(int requested, int total) { if (total <= 0) { return 0; } int num = Math.Max(0, total - 1); return Mathf.Clamp(requested, 0, num); } private static int GetDesiredNavigationIndex(MainMenuScript menu) { if (DesiredNavigationIndexField == null) { return -1; } object value = DesiredNavigationIndexField.GetValue(menu); if (value is int) { return (int)value; } return -1; } private static void EnsureSlotState(MainMenuScript menu, int index, bool enabled) { if ((Object)(object)menu?.menuController == (Object)null) { return; } DiegeticMenuElement menuElement = GetMenuElement(menu, index); if ((Object)(object)menuElement == (Object)null) { return; } Transform transform = ((Component)menuElement).transform; object obj; if (transform == null) { obj = null; } else { Transform parent = transform.parent; obj = ((parent != null) ? ((Component)parent).gameObject : null); } if (obj != null) { ((GameObject)obj).SetActive(enabled); } List elementsList = GetElementsList(menu.menuController); if (elementsList == null) { return; } if (enabled) { if (!elementsList.Contains(menuElement)) { int index2 = Mathf.Clamp(index, 0, elementsList.Count); elementsList.Insert(index2, menuElement); menuElement.SetMyController(menu.menuController); } if (!VirtualCursors.IsCursorVisible(0, true) && GetDesiredNavigationIndex(menu) == index) { menu.menuController.HoveredElement = menuElement; } } else { elementsList.Remove(menuElement); if (menu.menuController.HoveredElement == menuElement) { menu.menuController.HoveredElement = null; } } } private static void EnsureSlotEnabled(MainMenuScript menu, int index) { EnsureSlotState(menu, index, enabled: true); } private static void EnsureSlotDisabled(MainMenuScript menu, int index) { EnsureSlotState(menu, index, enabled: false); } private static bool ValidateMenuRows(MainMenuScript menu, int requiredIndex) { if ((Object)(object)menu == (Object)null) { return false; } DiegeticMenuElement[] menuElements = menu.menuElements; int num = ((menuElements != null) ? menuElements.Length : 0); TextMeshProUGUI[] optionTexts = menu.optionTexts; int num2 = ((optionTexts != null) ? optionTexts.Length : 0); bool num3 = num > requiredIndex; bool flag = num2 > requiredIndex; if ((!num3 || !flag) && !_rowValidationLogged) { _rowValidationLogged = true; Logging.LogFatal($"ModSettingsExtender detected missing menu rows. Expected index {requiredIndex} but found menuElements={num}, optionTexts={num2}. The base menu prefab likely changed; mods menu injection is disabled."); } return num3 && flag; } private static int VisibleCapacity(MainMenuScript menu) { return Mathf.Max(0, GetElementsList(menu?.menuController)?.Count ?? 0); } private static bool InputSuppressed(MainMenuScript menu) { bool num = Controls.MouseButton_PressedGet(0, (MouseElement)1); bool flag = GetFlag(RightNavigationPressField, menu); bool flag2 = GetFlag(LeftNavigationPressField, menu); return num || flag || flag2; } [HarmonyPostfix] [HarmonyPatch(typeof(MainMenuScript), "OptionsUpdateText_Desktop")] private static void OptionsUpdateText_Desktop_Postfix(MainMenuScript __instance) { AfterOptionsUpdateText(__instance); } private static void AfterOptionsUpdateText(MainMenuScript menu) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Invalid comparison between Unknown and I4 if ((Object)(object)menu == (Object)null) { return; } try { if ((int)GetMenuIndex(menu) == 1) { EnsureModsRow(menu); } else { DisableExtraRow(menu); } if (IsInHub()) { RenderHub(menu); } else if (HubState.Current != HubState.View.None && !HasPages()) { HubState.Reset(); } } catch (Exception arg) { Logging.LogError($"OptionsUpdate postfix failed: {arg}"); } } private static void DisableExtraRow(MainMenuScript menu) { if (!((Object)(object)menu == (Object)null)) { int backSlotIndex = GetBackSlotIndex(); if (ValidateMenuRows(menu, backSlotIndex)) { SetTextSafe(menu.optionTexts, backSlotIndex, string.Empty); EnsureSlotDisabled(menu, backSlotIndex); } } } private static void EnsureModsRow(MainMenuScript menu) { TextMeshProUGUI[] optionTexts = menu.optionTexts; if (optionTexts == null) { return; } int modsSlotIndex = GetModsSlotIndex(); int backSlotIndex = GetBackSlotIndex(); bool flag = HasPages(); if (!ValidateMenuRows(menu, Math.Max(modsSlotIndex, backSlotIndex))) { return; } if (!flag) { SetTextSafe(optionTexts, modsSlotIndex, Translation.Get("MENU_OPTION_BACK")); SetTextSafe(optionTexts, backSlotIndex, string.Empty); EnsureSlotDisabled(menu, backSlotIndex); } else if (modsSlotIndex < optionTexts.Length && backSlotIndex < optionTexts.Length) { TextMeshProUGUI obj = optionTexts[modsSlotIndex]; string value = ((obj != null) ? ((TMP_Text)obj).text : null) ?? Translation.Get("MENU_OPTION_BACK"); if (string.IsNullOrEmpty(value)) { value = Translation.Get("MENU_OPTION_BACK"); } EnsureSlotEnabled(menu, modsSlotIndex); EnsureSlotEnabled(menu, backSlotIndex); SetTextSafe(optionTexts, backSlotIndex, value); SetTextSafe(optionTexts, modsSlotIndex, "MODS"); } } private static void RenderHub(MainMenuScript menu) { if (TryGetOptionTexts(menu, out TextMeshProUGUI[] optionTexts) && TryGetHubLayout(menu, out var layout)) { ModSettingsManager.Page page; if (HubState.Current == HubState.View.HubIndex) { RenderHubIndex(menu, optionTexts, layout); } else if (HubState.Current == HubState.View.Page && TryGetActivePage(out page)) { RenderHubPage(menu, optionTexts, layout, page); } } } private static void RenderHubIndex(MainMenuScript menu, TextMeshProUGUI[] optionTexts, HubLayout layout) { if ((Object)(object)menu.titleText != (Object)null) { ((TMP_Text)menu.titleText).text = "MOD SETTINGS"; } int hubPageOffset = HubState.HubPageOffset; int count = ModSettingsManager.Pages.Count; int renderedEnd = Mathf.Min(hubPageOffset + layout.ListSlots, count); RenderListEntries(optionTexts, layout.ListSlots, hubPageOffset, count, (int index) => ModSettingsManager.GetDisplayTitle(ModSettingsManager.Pages[index])); RenderPaginationFooter(optionTexts, layout, renderedEnd, count); } private static void RenderHubPage(MainMenuScript menu, TextMeshProUGUI[] optionTexts, HubLayout layout, ModSettingsManager.Page page) { if ((Object)(object)menu.titleText != (Object)null) { ((TMP_Text)menu.titleText).text = ModSettingsManager.GetDisplayTitle(page); } int pageItemOffset = HubState.PageItemOffset; int count = page.Items.Count; int renderedEnd = Mathf.Min(pageItemOffset + layout.ListSlots, count); RenderListEntries(optionTexts, layout.ListSlots, pageItemOffset, count, (int index) => page.Items[index].Label?.Invoke() ?? "(item)"); RenderPaginationFooter(optionTexts, layout, renderedEnd, count); } [HarmonyPrefix] [HarmonyPatch(typeof(MainMenuScript), "Select_Desktop")] private static bool Select_Desktop_Prefix(MainMenuScript __instance, MenuIndex _menuIndex, int selectionIndex) { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_007d: Invalid comparison between Unknown and I4 if (IsInHub() && (int)_menuIndex == 0 && selectionIndex == 0) { if (HubState.Current == HubState.View.Page) { Sound.Play("SoundMenuBack", 1f, 1f); HubState.Current = HubState.View.HubIndex; HubState.PageItemOffset = 0; } else { Sound.Play("SoundMenuBack", 1f, 1f); HubState.Reset(); SetDesiredNavigationIndex(__instance, GetModsSlotIndex()); } __instance.OptionsUpdate(); return false; } if (IsInHub()) { HandleHubSelection(__instance, selectionIndex); __instance.OptionsUpdate(); return false; } if ((int)_menuIndex == 1 && HasPages()) { int modsSlotIndex = GetModsSlotIndex(); int backSlotIndex = GetBackSlotIndex(); bool flag = InputSuppressed(__instance); if (selectionIndex == modsSlotIndex && !flag) { Sound.Play("SoundMenuSelect", 1f, 1f); HubState.Current = HubState.View.HubIndex; HubState.HubPageOffset = 0; HubState.ActivePage = -1; HubState.PageItemOffset = 0; SetDesiredNavigationIndex(__instance, 0); __instance.OptionsUpdate(); return false; } if (selectionIndex == backSlotIndex && !flag) { Sound.Play("SoundMenuBack", 1f, 1f); SetMenuIndex(__instance, (MenuIndex)0); int value = ((!Master.IsDemo) ? 2 : 3); SetDesiredNavigationIndex(__instance, value); HubState.Reset(); __instance.OptionsUpdate(); return false; } } return true; } private static void HandleHubSelection(MainMenuScript menu, int selectionIndex) { if (TryGetHubLayout(menu, out var layout)) { if (HubState.Current == HubState.View.HubIndex) { HandleHubIndexSelection(menu, selectionIndex, layout); } else if (HubState.Current == HubState.View.Page) { int navigationDirection = GetNavigationDirection(menu); HandleHubPageSelection(menu, selectionIndex, layout, navigationDirection); } } } private static void HandleHubIndexSelection(MainMenuScript menu, int selectionIndex, HubLayout layout) { int hubPageOffset = HubState.HubPageOffset; int count = ModSettingsManager.Pages.Count; int num = Mathf.Min(hubPageOffset + layout.ListSlots, count); if (selectionIndex >= 0 && selectionIndex < layout.ListSlots) { int num2 = hubPageOffset + selectionIndex; if (num2 >= hubPageOffset && num2 < num) { Sound.Play("SoundMenuSelect", 1f, 1f); HubState.ActivePage = num2; HubState.PageItemOffset = 0; HubState.Current = HubState.View.Page; SetDesiredNavigationIndex(menu, 0); } } else if (selectionIndex == layout.NextRow && num < count) { Sound.Play("SoundMenuSelect", 1f, 1f); HubState.HubPageOffset = ClampPageOffset(HubState.HubPageOffset + layout.Step, count); } else if (selectionIndex == layout.BackRow) { Sound.Play("SoundMenuBack", 1f, 1f); HubState.Reset(); SetDesiredNavigationIndex(menu, GetModsSlotIndex()); } } private static void HandleHubPageSelection(MainMenuScript menu, int selectionIndex, HubLayout layout, int direction) { if (!TryGetActivePage(out ModSettingsManager.Page page)) { return; } int pageItemOffset = HubState.PageItemOffset; int count = page.Items.Count; int num = Mathf.Min(pageItemOffset + layout.ListSlots, count); if (selectionIndex >= 0 && selectionIndex < layout.ListSlots) { int num2 = pageItemOffset + selectionIndex; if (num2 >= pageItemOffset && num2 < num) { ModSettingsManager.Item item = page.Items[num2]; if ((direction != 0) ? TryInvokeAdjust(page, num2, item, direction) : TryInvokeSelect(page, num2, item)) { Sound.Play("SoundMenuSelect", 1f, 1f); } } } else if (selectionIndex == layout.NextRow && num < count) { Sound.Play("SoundMenuSelect", 1f, 1f); HubState.PageItemOffset = ClampPageOffset(HubState.PageItemOffset + layout.Step, count); } else if (selectionIndex == layout.BackRow) { Sound.Play("SoundMenuBack", 1f, 1f); HubState.Current = HubState.View.HubIndex; HubState.PageItemOffset = 0; HubState.ActivePage = -1; SetDesiredNavigationIndex(menu, 0); } } private static int GetNavigationDirection(MainMenuScript menu) { bool flag = GetFlag(RightNavigationPressField, menu); bool flag2 = GetFlag(LeftNavigationPressField, menu); int num = (flag ? 1 : (flag2 ? (-1) : 0)); if (num == 0 && Controls.MouseButton_PressedGet(0, (MouseElement)1)) { num = -1; } return num; } private static bool TryInvokeSelect(ModSettingsManager.Page page, int itemIndex, ModSettingsManager.Item item) { if (item == null || item.OnSelect == null) { return true; } return TryInvokeItemHandler(page, itemIndex, "OnSelect", item.OnSelect); } private static bool TryInvokeAdjust(ModSettingsManager.Page page, int itemIndex, ModSettingsManager.Item item, int direction) { if (item == null || item.OnAdjust == null) { return true; } return TryInvokeItemHandler(page, itemIndex, "OnAdjust", delegate { item.OnAdjust(direction); }); } private static bool TryInvokeItemHandler(ModSettingsManager.Page page, int itemIndex, string handler, Action invoke) { try { invoke(); return true; } catch (Exception ex) { LogHandlerException(handler, page, itemIndex, ex); return false; } } private static void LogHandlerException(string handler, ModSettingsManager.Page page, int itemIndex, Exception ex) { string text = page?.Name ?? "(unnamed page)"; Logging.LogError($"Exception during {handler} for page '{text}' item index {itemIndex}: {ex}"); } } [HarmonyPatch] public class TexturePatcher { [HarmonyPatch(typeof(Object), "Internal_CloneSingle", new Type[] { typeof(Object) })] [HarmonyPostfix] public static void Object_CloneSingle(ref Object __result) { Object obj = __result; GameObject val = (GameObject)(object)((obj is GameObject) ? obj : null); if (val == null) { return; } Renderer[] componentsInChildren = val.GetComponentsInChildren(true); foreach (Renderer val2 in componentsInChildren) { Material[] materials = val2.materials; foreach (Material val3 in materials) { if (!val3.HasProperty("_MainTex") || (Object)(object)val3.mainTexture == (Object)null) { continue; } string text = ((Object)val3.mainTexture).name.ToLower(); string text2 = ((Object)val).name.Replace("(Clone)", "").Trim().ToLower() + "_" + ((Object)val3.mainTexture).name.ToLower(); string text3 = ((Object)val).name.Replace("(Clone)", "").Trim().ToLower() + "_" + ((Object)val2).name.Trim().ToLower() + "_" + ((Object)val3.mainTexture).name.ToLower(); if (TextureManager.TryGetTextureOverride(out var texture, text, text2, text3)) { if ((val3.mainTexture.width != ((Texture)texture).width || val3.mainTexture.height != ((Texture)texture).height) && !Plugin.UseFullQualityTextures.Value) { texture = TextureUtils.ResizeTexture(texture, val3.mainTexture.width, val3.mainTexture.height); } val3.mainTexture = (Texture)(object)texture; } } } AudioSource[] componentsInChildren2 = val.GetComponentsInChildren(true); foreach (AudioSource val4 in componentsInChildren2) { if (!((Object)(object)val4.clip == (Object)null) && AudioManager.TryGetSoundOverride(out var audioClip, ((Object)val4.clip).name)) { val4.clip = audioClip; } } } } [HarmonyPatch] internal class TranslationPatcher { [HarmonyPatch(typeof(Translation), "Get")] [HarmonyPrefix] internal static bool Translation_Get_Prefix(ref string key, ref string __result) { if (string.IsNullOrEmpty(key)) { return true; } bool result = true; if (LocalizationManager.TryGetValueNoCase(key, out var value)) { __result = value; result = false; } if (LanguageManager.IsCustomLanguage && LanguageManager.TryGetTerm(key, out var termValue)) { __result = termValue; result = false; } return result; } [HarmonyPatch(typeof(Strings), "Sanitize")] [HarmonyPrefix] internal static void Strings_Sanitize_Prefix(ref string input, SantizationKind santizationKind, SanitizationSubKind subKind) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) StringManager.Sanitize(ref input, santizationKind, subKind); } [HarmonyPatch(typeof(Strings), "Sanitize")] [HarmonyPostfix] internal static void Strings_Sanitize_Postfix(ref string __result, SantizationKind santizationKind, SanitizationSubKind subKind) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) StringManager.SanitizeLate(ref __result, santizationKind, subKind); } [HarmonyPatch(typeof(Translation), "LanguageGet")] [HarmonyPrefix] internal static bool Translation_LanguageGet_Prefix(ref Language __result) { if (LanguageManager.IsCustomLanguage) { __result = (Language)0; return false; } return true; } } } namespace CloverAPI.Internal { internal class CharmMappings : JsonPersistentData { internal static CharmMappings Instance = new CharmMappings(); [JsonProperty] internal Dictionary Mappings = new Dictionary(); public override void BeforeSave() { Mappings = CharmManager.GUIDToId; } public unsafe override void AfterLoad() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_00dd: Unknown result type (might be due to invalid IL or missing references) //IL_00e2: Unknown result type (might be due to invalid IL or missing references) //IL_00e5: Unknown result type (might be due to invalid IL or missing references) //IL_0133: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013b: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = CharmManager.GenerateRemaps(Mappings); if (dictionary.Count == 0) { Logging.LogInfo("No Charm mappings to apply."); return; } Logging.LogInfo("Current Charm mappings:"); foreach (KeyValuePair item in dictionary) { Logging.LogInfo($" {item.Key} -> {item.Value}"); } for (int i = 0; i < GameplayData.Instance.equippedPowerups.Length; i++) { Identifier key = PlatformDataMaster.EnumEntryFromString(GameplayData.Instance.equippedPowerups[i], (Identifier)(-1)); if (dictionary.TryGetValue(key, out var value)) { GameplayData.Instance.equippedPowerups[i] = ((object)(*(Identifier*)(&value))/*cast due to .constrained prefix*/).ToString(); } } for (int j = 0; j < GameplayData.Instance.equippedPowerups_Skeleton.Length; j++) { Identifier key2 = PlatformDataMaster.EnumEntryFromString(GameplayData.Instance.equippedPowerups_Skeleton[j], (Identifier)(-1)); if (dictionary.TryGetValue(key2, out var value2)) { GameplayData.Instance.equippedPowerups_Skeleton[j] = ((object)(*(Identifier*)(&value2))/*cast due to .constrained prefix*/).ToString(); } } for (int k = 0; k < GameplayData.Instance.drawerPowerups.Length; k++) { Identifier key3 = PlatformDataMaster.EnumEntryFromString(GameplayData.Instance.drawerPowerups[k], (Identifier)(-1)); if (dictionary.TryGetValue(key3, out var value3)) { GameplayData.Instance.drawerPowerups[k] = ((object)(*(Identifier*)(&value3))/*cast due to .constrained prefix*/).ToString(); } } } } public static class ResourceOrdering { private static readonly object _lock = new object(); private static ConfigEntry _overrideOrdering; public static List OrderedPluginUuids { get; private set; } = new List(); public static int GetPriority(string pluginUuid) { lock (_lock) { if (!OrderedPluginUuids.Contains(pluginUuid)) { OrderedPluginUuids.Add(pluginUuid); ApplyToConfig(); } return OrderedPluginUuids.IndexOf(pluginUuid); } } internal static void _ConfigRef(ConfigEntry overrideOrdering) { lock (_lock) { if (overrideOrdering == null) { throw new ArgumentNullException("overrideOrdering"); } _overrideOrdering = overrideOrdering; OrderedPluginUuids = new List((from s in overrideOrdering.Value.Split(new char[1] { ',' }) select s.Trim() into s where !string.IsNullOrEmpty(s) select s).ToArray()); } } internal static void ApplyToConfig() { lock (_lock) { if (_overrideOrdering == null) { throw new InvalidOperationException("Config reference not set. Call _ConfigRef first."); } _overrideOrdering.Value = string.Join(", ", OrderedPluginUuids); } } } } namespace CloverAPI.Content.Textures { public class TextureManager { internal class TextureOverride : IPriority { public Texture2D Texture; public string OwnerGuid; public int Priority => ResourceOrdering.GetPriority(OwnerGuid); } internal static ConcurrentDictionary> TextureOverrides = new ConcurrentDictionary>(); public static void RegisterTextureOverride(string name, Texture2D texture, ModGuid guid) { _RegisterOverride(name, TextureOverrides, texture, guid); } private static void _RegisterOverride(string name, ConcurrentDictionary> dict, Texture2D texture, ModGuid guid) { if (!dict.ContainsKey(name)) { dict[name] = new OrderedList(); } TextureOverride textureOverride = dict[name].FirstOrDefault((TextureOverride o) => o.OwnerGuid == guid); if (textureOverride != null) { textureOverride.Texture = texture; return; } dict[name].Add(new TextureOverride { Texture = texture, OwnerGuid = guid }); } public static bool TryGetTextureOverride(string name, out Texture2D texture) { return _TryGetOverride(name, TextureOverrides, out texture); } public static bool TryGetTextureOverride(out Texture2D texture, params string[] names) { for (int i = 0; i < names.Length; i++) { if (_TryGetOverride(names[i], TextureOverrides, out texture)) { return true; } } texture = null; return false; } private static bool _TryGetOverride(string name, ConcurrentDictionary> dict, out Texture2D texture) { if (dict.TryGetValueNoCase(name, out var value) && value.Count > 0) { TextureOverride textureOverride = value[0]; texture = textureOverride.Texture; return true; } texture = null; return false; } } } namespace CloverAPI.Content.Strings { public static class LanguageManager { internal static List CustomLanguages = new List(); internal static Dictionary LangMap = new Dictionary(); internal static Dictionary LangMapT = new Dictionary(); internal static Dictionary> TermsByLanguage = new Dictionary>(); private static int NextId => Enum.GetValues(typeof(Language)).Length + CustomLanguages.Count - 1; public static bool IsCustomLanguage => (int)Data.settings.language >= Enum.GetValues(typeof(Language)).Length - 1; public static Language LoadFromFile(string filePath) { //IL_0115: Unknown result type (might be due to invalid IL or missing references) string[] array = File.ReadAllText(filePath).Replace("\\;", "").Split(new char[2] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries); Dictionary dictionary = new Dictionary(); string text = ""; string[] array2 = array; foreach (string text2 in array2) { if (!text2.StartsWith("#") && !text2.StartsWith("//") && !string.IsNullOrWhiteSpace(text2)) { string[] array3 = text2.Split(new char[1] { ';' }, 2); if (array3.Length == 2) { text = array3[0].Trim(); dictionary[text] = array3[1].Replace("", ";").Trim(); } else { Dictionary dictionary2 = dictionary; string key = text; dictionary2[key] = dictionary2[key] + "\n" + text2.Replace("", ";"); } } } if (!dictionary.ContainsKey("YOUR_LANGUAGE_TRANSLATED")) { dictionary["YOUR_LANGUAGE_TRANSLATED"] = Path.GetFileNameWithoutExtension(filePath); } return RegisterLanguage(dictionary); } public static Language RegisterLanguage(params (string termKey, string termValue)[] terms) { //IL_006a: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = new Dictionary(); for (int i = 0; i < terms.Length; i++) { (string termKey, string termValue) tuple = terms[i]; string item = tuple.termKey; string item2 = tuple.termValue; dictionary[item] = item2; } string languageName = $"UnnamedLanguage{CustomLanguages.Count + 1}"; if (dictionary.TryGetValue("YOUR_LANGUAGE_TRANSLATED", out var value)) { languageName = value; } return RegisterLanguage(languageName, dictionary); } public static Language RegisterLanguage(Dictionary terms) { //IL_002f: Unknown result type (might be due to invalid IL or missing references) string languageName = $"UnnamedLanguage{CustomLanguages.Count + 1}"; if (terms.TryGetValue("YOUR_LANGUAGE_TRANSLATED", out var value)) { languageName = value; } return RegisterLanguage(languageName, terms); } public static Language RegisterLanguage(string languageName, params (string termKey, string termValue)[] terms) { //IL_0036: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = new Dictionary(); for (int i = 0; i < terms.Length; i++) { (string termKey, string termValue) tuple = terms[i]; string item = tuple.termKey; string item2 = tuple.termValue; dictionary[item] = item2; } return RegisterLanguage(languageName, dictionary); } public static Language RegisterLanguage(string languageName, Dictionary terms) { //IL_0015: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_0055: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) if (!CustomLanguages.Contains(languageName)) { Language val = (Language)NextId; LangMap[val] = languageName; LangMapT[languageName] = val; TermsByLanguage[languageName] = new Dictionary(terms); CustomLanguages.Add(languageName); _ = Translation.LanguagesInOrder; Translation._languagesInOrder = Translation._languagesInOrder.Append(val).ToArray(); Translation.languageNamesTranslated = Translation.languageNamesTranslated.Append(languageName).ToArray(); Translation.languagesI2Names = Translation.languagesI2Names.Append("English").ToArray(); return val; } throw new Exception("Language " + languageName + " is already registered."); } public static bool TryGetTerm(string termKey, out string termValue) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0010: Unknown result type (might be due to invalid IL or missing references) Language language = Data.settings.language; if (LangMap.TryGetValue(language, out var value) && TermsByLanguage.TryGetValue(value, out var value2)) { return value2.TryGetValue(termKey, out termValue); } termValue = null; return false; } public static Language GetLanguageByName(string languageName) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (LangMapT.TryGetValue(languageName, out var value)) { return value; } throw new Exception("Language " + languageName + " is not registered."); } private static bool TryGetLanguageByName(string languageName, out Language lang) { return LangMapT.TryGetValue(languageName, out lang); } } public static class LocalizationManager { public static Dictionary Translations = new Dictionary(); public static string RegisterTranslation(string key, StringSource value) { value.SetKey(key); Translations[key] = value; return key; } public static string Get(string key) { if (Translations.TryGetValue(key, out var value)) { return value.GetString(); } return key; } public static bool TryGetValueNoCase(string key, out string value) { if (Translations.TryGetValueNoCase(key, out var value2)) { if (value2 is VanillaLocalizedString) { value = null; return false; } value = value2.GetString(); return true; } value = null; return false; } } public class StringManager { private static readonly Dictionary Strings = new Dictionary(); private static readonly Dictionary> ConditionalStrings = new Dictionary>(); private static readonly Dictionary StringsLate = new Dictionary(); private static readonly Dictionary> ConditionalStringsLate = new Dictionary>(); public static void RegisterString(string key, StringSource value, bool late = false) { if (late) { StringsLate[key] = value; } else { Strings[key] = value; } } public static void RegisterConditional(string key, StringSource value, SantizationKind kind, SanitizationSubKind subKind, bool late = false) { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_000b: 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) if (late) { ConditionalStringsLate[key] = new Tuple(value, kind, subKind); } else { ConditionalStrings[key] = new Tuple(value, kind, subKind); } } public static void Sanitize(ref string s, SantizationKind santizationKind, SanitizationSubKind subKind) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(s); foreach (string key in Strings.Keys) { stringBuilder.Replace(key, Strings[key].GetString()); } foreach (string key2 in ConditionalStrings.Keys) { var (stringSource2, val3, val4) = ConditionalStrings[key2]; if (((int)santizationKind == 0 || santizationKind == val3) && ((int)subKind == 0 || subKind == val4)) { stringBuilder.Replace(key2, stringSource2.GetString()); } } s = stringBuilder.ToString(); } public static void SanitizeLate(ref string s, SantizationKind santizationKind, SanitizationSubKind subKind) { //IL_0089: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_0099: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_009d: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(s); foreach (string key in StringsLate.Keys) { stringBuilder.Replace(key, StringsLate[key].GetString()); } foreach (string key2 in ConditionalStringsLate.Keys) { var (stringSource2, val3, val4) = ConditionalStringsLate[key2]; if (((int)santizationKind == 0 || santizationKind == val3) && ((int)subKind == 0 || subKind == val4)) { stringBuilder.Replace(key2, stringSource2.GetString()); } } s = stringBuilder.ToString(); } } } namespace CloverAPI.Content.Settings { public class ModSettingsManager { public enum ToggleAdjustMode { Toggle, Directional } public sealed class Item { public Func? Label { get; set; } public Action? OnSelect { get; set; } public Action? OnAdjust { get; set; } public Item() { } internal Item(Func? label, Action? onSelect, Action? onAdjust) { Label = label; OnSelect = onSelect; OnAdjust = onAdjust; } } public sealed class Page { private readonly List items = new List(); public string Name { get; } internal string OwnerGuid { get; } internal string OwnerName { get; } internal string NormalizedName { get; } public List Items => items; internal Page(string name, ModGuid ownerGuid, ModName ownerName, string normalizedName) { if (string.IsNullOrWhiteSpace(name)) { throw new ArgumentException("Page name must not be empty.", "name"); } if (string.IsNullOrWhiteSpace(ownerGuid)) { throw new ArgumentException("Page owner GUID must not be empty.", "ownerGuid"); } if (string.IsNullOrWhiteSpace(normalizedName)) { throw new ArgumentException("Normalized page name must not be empty.", "normalizedName"); } Name = name; OwnerGuid = ownerGuid; OwnerName = ownerName; NormalizedName = normalizedName; } internal Item AddItem(Func? label, Action? onSelect, Action? onAdjust) { return AddItem(new Item(label, onSelect, onAdjust)); } internal Item AddItem(Item item) { if (item == null) { throw new ArgumentNullException("item"); } items.Add(item); return item; } } public sealed class PageBuilder { private static readonly IReadOnlyList DefaultMultiplierSteps = Array.AsReadOnly(new float[6] { 0f, 0.5f, 1f, 2f, 3f, 4f }); private readonly Page page; public Page BuiltPage => page; internal PageBuilder(Page page) { this.page = page ?? throw new ArgumentNullException("page"); } public PageBuilder AddItem(Func? label, Action? onSelect = null, Action? onAdjust = null) { page.AddItem(label, onSelect, onAdjust); return this; } public PageBuilder AddToggle(string label, Func getter, Action setter, string onLabel = "On", string offLabel = "Off", Action? onChanged = null, ToggleAdjustMode adjustMode = ToggleAdjustMode.Toggle) { if (label == null) { throw new ArgumentNullException("label"); } if (getter == null) { throw new ArgumentNullException("getter"); } if (setter == null) { throw new ArgumentNullException("setter"); } return AddItem(Format, delegate { SetValue(!getter()); }, delegate(int dir) { int num = NormalizeDirection(dir); if (num != 0) { if (adjustMode == ToggleAdjustMode.Directional) { SetValue(num > 0); } else { SetValue(!getter()); } } }); string Format() { return label + ": " + (getter() ? onLabel : offLabel); } void SetValue(bool value) { UpdateIfChanged(getter, setter, value, onChanged); } } public PageBuilder AddToggle(string label, ConfigEntry entry, string onLabel = "On", string offLabel = "Off", Action? onChanged = null, ToggleAdjustMode adjustMode = ToggleAdjustMode.Toggle) { if (entry == null) { throw new ArgumentNullException("entry"); } return AddToggle(label, () => entry.Value, delegate(bool value) { entry.Value = value; }, onLabel, offLabel, onChanged, adjustMode); } public PageBuilder OnOff(string label, Func getter, Action setter, string onLabel = "On", string offLabel = "Off", Action? onChanged = null, ToggleAdjustMode adjustMode = ToggleAdjustMode.Toggle) { return AddToggle(label, getter, setter, onLabel, offLabel, onChanged, adjustMode); } public PageBuilder OnOff(string label, ConfigEntry entry, string onLabel = "On", string offLabel = "Off", Action? onChanged = null, ToggleAdjustMode adjustMode = ToggleAdjustMode.Toggle) { return OnOff(label, () => entry.Value, delegate(bool value) { entry.Value = value; }, onLabel, offLabel, onChanged, adjustMode); } public PageBuilder AddIntStepper(string label, Func getter, Action setter, int step = 1, int? min = null, int? max = null, bool wrap = false, Func? normalizer = null, Func? valueFormatter = null, Action? onChanged = null) { if (label == null) { throw new ArgumentNullException("label"); } if (getter == null) { throw new ArgumentNullException("getter"); } if (setter == null) { throw new ArgumentNullException("setter"); } if (step <= 0) { throw new ArgumentOutOfRangeException("step", "Step must be positive."); } if (min.HasValue && max.HasValue && min.Value > max.Value) { throw new ArgumentOutOfRangeException("max", "Max must be greater than or equal to min."); } return AddItem(Format, delegate { SetValue(getter() + step); }, delegate(int dir) { int num = NormalizeDirection(dir); if (num != 0) { SetValue(getter() + num * step); } }); int ApplyBounds(int value) { if (wrap && min.HasValue && max.HasValue && min.Value <= max.Value) { int num = Math.Max(1, step); int num2 = max.Value - min.Value + num; if (num2 > 0) { int num3 = ((value - min.Value) % num2 + num2) % num2; return min.Value + num3; } } if (min.HasValue && value < min.Value) { value = min.Value; } if (max.HasValue && value > max.Value) { value = max.Value; } return value; } string Format() { int arg = getter(); string text = valueFormatter?.Invoke(arg) ?? arg.ToString(); return label + ": " + text; } int Normalize(int value) { int num = ApplyBounds(value); if (normalizer != null) { num = ApplyBounds(normalizer(num)); } return num; } void SetValue(int rawValue) { int value = Normalize(rawValue); UpdateIfChanged(getter, setter, value, onChanged); } } public PageBuilder AddIntStepper(string label, ConfigEntry entry, int step = 1, int? min = null, int? max = null, bool wrap = false, Func? normalizer = null, Func? valueFormatter = null, Action? onChanged = null) { if (entry == null) { throw new ArgumentNullException("entry"); } return AddIntStepper(label, () => entry.Value, delegate(int value) { entry.Value = value; }, step, min, max, wrap, normalizer, valueFormatter, onChanged); } public PageBuilder Int(string label, Func getter, Action setter, int? min = null, int? max = null, int step = 1, bool wrap = false, Func? normalizer = null, Func? valueFormatter = null, Action? onChanged = null) { return AddIntStepper(label, getter, setter, step, min, max, wrap, normalizer, valueFormatter, onChanged); } public PageBuilder Int(string label, ConfigEntry entry, int? min = null, int? max = null, int step = 1, bool wrap = false, Func? normalizer = null, Func? valueFormatter = null, Action? onChanged = null) { if (entry == null) { throw new ArgumentNullException("entry"); } return Int(label, () => entry.Value, delegate(int value) { entry.Value = value; }, min, max, step, wrap, normalizer, valueFormatter, onChanged); } public PageBuilder Percent(string label, Func getter, Action setter, int minPercent = 0, int maxPercent = 100, int step = 5, bool wrap = false, bool showPercent = true, float scale = 1f, Action? onChanged = null) { if (maxPercent < minPercent) { throw new ArgumentOutOfRangeException("maxPercent", "maxPercent must be greater than or equal to minPercent."); } return AddIntStepper(label, getter, setter, step, minPercent, maxPercent, wrap, null, Formatter, onChanged); string Formatter(int value) { int num = (int)((float)value * scale); if (!showPercent) { return num.ToString(); } return $"{num}%"; } } public PageBuilder Percent(string label, ConfigEntry entry, int minPercent = 0, int maxPercent = 100, int step = 5, bool wrap = false, bool showPercent = true, float scale = 1f, Action? onChanged = null) { if (entry == null) { throw new ArgumentNullException("entry"); } return Percent(label, () => entry.Value, delegate(int value) { entry.Value = value; }, minPercent, maxPercent, step, wrap, showPercent, scale, onChanged); } public PageBuilder Percent(string label, Func getter, Action setter, float minPercent = 0f, float maxPercent = 100f, float step = 5f, bool wrap = false, int decimalPlaces = 1, bool showPercent = true, float scale = 1f, Action? onChanged = null) { if (label == null) { throw new ArgumentNullException("label"); } if (getter == null) { throw new ArgumentNullException("getter"); } if (setter == null) { throw new ArgumentNullException("setter"); } if (step <= 0f) { throw new ArgumentOutOfRangeException("step", "Step must be positive."); } if (decimalPlaces < 0) { throw new ArgumentOutOfRangeException("decimalPlaces", "decimalPlaces must be zero or greater."); } float num = getter(); int requiredDecimalPlaces = GetRequiredDecimalPlaces(minPercent, maxPercent, step, num); int effectiveDecimalPlaces = Math.Max(decimalPlaces, requiredDecimalPlaces); int localScale = Pow10(effectiveDecimalPlaces); int num2 = Scale(step); if (num2 <= 0) { throw new ArgumentOutOfRangeException("step", "Step is too small for the configured decimalPlaces."); } int minScaled = Scale(minPercent); int maxScaled = Scale(maxPercent); if (maxScaled < minScaled) { throw new ArgumentOutOfRangeException("maxPercent", "maxPercent must be greater than or equal to minPercent."); } Action onChanged2 = null; if (onChanged != null) { onChanged2 = delegate(int value) { onChanged(Unscale(value)); }; } return AddIntStepper(label, () => Scale(getter()), delegate(int value) { setter(Unscale(value)); }, num2, minScaled, maxScaled, wrap, RoundOnNonBounds, FormatPercentLabel, onChanged2); string FormatPercentLabel(int scaledValue) { float num3 = Unscale(scaledValue) * scale; string text = ((effectiveDecimalPlaces > 0) ? $"F{effectiveDecimalPlaces}" : "F0"); string text2 = num3.ToString(text, CultureInfo.InvariantCulture); if (effectiveDecimalPlaces > 0) { text2 = text2.TrimEnd(new char[1] { '0' }).TrimEnd(new char[1] { '.' }); } if (!showPercent) { return text2; } return text2 + "%"; } int RoundOnNonBounds(int value) { if (value == minScaled || value == maxScaled) { return value; } return Scale(MathUtils.RoundToMultipleOf(Unscale(value), step)); } int Scale(float value) { return (int)Math.Round((decimal)value * (decimal)localScale, MidpointRounding.AwayFromZero); } float Unscale(int value) { return (float)value / (float)localScale; } } public PageBuilder Percent(string label, ConfigEntry entry, float minPercent = 0f, float maxPercent = 100f, float step = 5f, bool wrap = false, int decimalPlaces = 1, bool showPercent = true, float scale = 1f, Action? onChanged = null) { if (entry == null) { throw new ArgumentNullException("entry"); } return Percent(label, () => entry.Value, delegate(float value) { entry.Value = value; }, minPercent, maxPercent, step, wrap, decimalPlaces, showPercent, scale, onChanged); } public PageBuilder Multiplier(string label, Func getter, Action setter, float minMultiplier, float maxMultiplier, float step, bool wrap = false, int decimalPlaces = 2, Func? valueFormatter = null, Action? onChanged = null) { if (label == null) { throw new ArgumentNullException("label"); } if (getter == null) { throw new ArgumentNullException("getter"); } if (setter == null) { throw new ArgumentNullException("setter"); } if (step <= 0f) { throw new ArgumentOutOfRangeException("step", "Step must be positive."); } if (decimalPlaces < 0) { throw new ArgumentOutOfRangeException("decimalPlaces", "decimalPlaces must be zero or greater."); } float num = getter(); int requiredDecimalPlaces = GetRequiredDecimalPlaces(minMultiplier, maxMultiplier, step, num); int effectiveDecimalPlaces = Math.Max(decimalPlaces, requiredDecimalPlaces); int scale = Pow10(effectiveDecimalPlaces); int num2 = Scale(step); if (num2 <= 0) { throw new ArgumentOutOfRangeException("step", "Step is too small for the configured decimalPlaces."); } int num3 = Scale(minMultiplier); int num4 = Scale(maxMultiplier); if (num4 < num3) { throw new ArgumentOutOfRangeException("maxMultiplier", "maxMultiplier must be greater than or equal to minMultiplier."); } Action onChanged2 = null; if (onChanged != null) { onChanged2 = delegate(int value) { onChanged(Unscale(value)); }; } return AddIntStepper(label, () => Scale(getter()), delegate(int value) { setter(Unscale(value)); }, num2, num3, num4, wrap, null, FormatMultiplierLabel, onChanged2); string FormatMultiplierLabel(int scaledValue) { float arg = Unscale(scaledValue); if (valueFormatter != null) { return valueFormatter(arg); } string text = ((effectiveDecimalPlaces > 0) ? $"F{effectiveDecimalPlaces}" : "F0"); string text2 = arg.ToString(text, CultureInfo.InvariantCulture); if (effectiveDecimalPlaces > 0) { text2 = text2.TrimEnd(new char[1] { '0' }).TrimEnd(new char[1] { '.' }); } return text2 + "x"; } int Scale(float value) { return (int)Math.Round((decimal)value * (decimal)scale, MidpointRounding.AwayFromZero); } float Unscale(int value) { return (float)value / (float)scale; } } public PageBuilder Multiplier(string label, ConfigEntry entry, float minMultiplier, float maxMultiplier, float step, bool wrap = false, int decimalPlaces = 2, Func? valueFormatter = null, Action? onChanged = null) { if (entry == null) { throw new ArgumentNullException("entry"); } return Multiplier(label, () => entry.Value, delegate(float value) { entry.Value = value; }, minMultiplier, maxMultiplier, step, wrap, decimalPlaces, valueFormatter, onChanged); } public PageBuilder Multiplier(string label, Func getter, Action setter, IReadOnlyList? options = null, Func? valueFormatter = null, Action? onChanged = null) { IReadOnlyList values = options ?? DefaultMultiplierSteps; int optionDecimalPlaces = ((valueFormatter == null) ? GetRequiredDecimalPlaces(values) : 0); return Cycle(label, getter, setter, values, formatter, onChanged); string formatter(float value) { if (valueFormatter != null) { return valueFormatter(value); } string text = ((optionDecimalPlaces > 0) ? $"F{optionDecimalPlaces}" : "F0"); string text2 = value.ToString(text, CultureInfo.InvariantCulture); if (optionDecimalPlaces > 0) { text2 = text2.TrimEnd(new char[1] { '0' }).TrimEnd(new char[1] { '.' }); } return text2 + "x"; } } public PageBuilder Multiplier(string label, ConfigEntry entry, IReadOnlyList? options = null, Func? valueFormatter = null, Action? onChanged = null) { if (entry == null) { throw new ArgumentNullException("entry"); } return Multiplier(label, () => entry.Value, delegate(float value) { entry.Value = value; }, options, valueFormatter, onChanged); } public PageBuilder Cycle(string label, Func getter, Action setter, IReadOnlyList values, Func? valueFormatter = null, Action? onChanged = null, IEqualityComparer? comparer = null) { return CycleInternal(label, getter, setter, values, valueFormatter, onChanged, comparer); } public PageBuilder Cycle(string label, Func getter, Action setter, params T[] values) { if (values == null) { throw new ArgumentNullException("values"); } return CycleInternal(label, getter, setter, Array.AsReadOnly(values), null, null, null); } public PageBuilder Cycle(string label, ConfigEntry entry, IReadOnlyList values, Func? valueFormatter = null, Action? onChanged = null, IEqualityComparer? comparer = null) { if (entry == null) { throw new ArgumentNullException("entry"); } return Cycle(label, () => entry.Value, delegate(T value) { entry.Value = value; }, values, valueFormatter, onChanged, comparer); } public PageBuilder Cycle(string label, ConfigEntry entry, params T[] values) { if (values == null) { throw new ArgumentNullException("values"); } return Cycle(label, entry, (IReadOnlyList)Array.AsReadOnly(values), (Func?)null, (Action?)null, (IEqualityComparer?)null); } public void Build() { } private PageBuilder CycleInternal(string label, Func getter, Action setter, IReadOnlyList values, Func? valueFormatter, Action? onChanged, IEqualityComparer? comparer) { if (label == null) { throw new ArgumentNullException("label"); } if (getter == null) { throw new ArgumentNullException("getter"); } if (setter == null) { throw new ArgumentNullException("setter"); } if (values == null) { throw new ArgumentNullException("values"); } if (values.Count == 0) { throw new ArgumentException("At least one value must be supplied.", "values"); } IEqualityComparer equalityComparer = comparer ?? EqualityComparer.Default; return AddItem(Format, delegate { Advance(1); }, delegate(int dir) { Advance(dir); }); void Advance(int direction) { int num = NormalizeDirection(direction); if (num != 0 && values.Count != 1) { T candidate = getter(); int num2 = FindIndex(candidate); int index = ((num2 >= 0) ? ((num2 + num + values.Count) % values.Count) : ((num <= 0) ? (values.Count - 1) : 0)); T value = values[index]; UpdateIfChanged(getter, setter, value, onChanged); } } int FindIndex(T candidate) { for (int i = 0; i < values.Count; i++) { if (equalityComparer.Equals(values[i], candidate)) { return i; } } return -1; } string Format() { T value = getter(); return label + ": " + RenderValue(value); } string RenderValue(T value) { if (valueFormatter != null) { return valueFormatter(value); } if (!(value is IFormattable formattable)) { object obj = value?.ToString(); if (obj == null) { obj = string.Empty; } return (string)obj; } return formattable.ToString(null, CultureInfo.InvariantCulture); } } private static int NormalizeDirection(int value) { if (value == 0) { return 0; } if (value <= 0) { return -1; } return 1; } private static int Pow10(int exponent) { if (exponent < 0) { throw new ArgumentOutOfRangeException("exponent"); } int num = 1; for (int i = 0; i < exponent; i++) { num = checked(num * 10); } return num; } private static int GetRequiredDecimalPlaces(IReadOnlyList values) { if (values == null || values.Count == 0) { return 0; } int num = 0; for (int i = 0; i < values.Count; i++) { int num2 = CountDecimalPlaces(values[i]); if (num2 > num) { num = num2; } } return num; } private static int GetRequiredDecimalPlaces(params float[] values) { if (values == null || values.Length == 0) { return 0; } int num = 0; for (int i = 0; i < values.Length; i++) { int num2 = CountDecimalPlaces(values[i]); if (num2 > num) { num = num2; } } return num; } private static int CountDecimalPlaces(float value) { decimal value2 = decimal.Round((decimal)value, 6, MidpointRounding.AwayFromZero); value2 = Math.Abs(value2); for (int i = 0; i <= 6; i++) { decimal num = value2 * (decimal)Pow10(i); if (decimal.Truncate(num) == num) { return i; } } return 6; } private static void UpdateIfChanged(Func getter, Action setter, T value, Action? onChanged) { if (getter == null) { throw new ArgumentNullException("getter"); } if (setter == null) { throw new ArgumentNullException("setter"); } T x = getter(); if (!EqualityComparer.Default.Equals(x, value)) { setter(value); onChanged?.Invoke(value); } } } private const int MaxAutoDecimalPlaces = 6; private static readonly List pages = new List(); private static readonly Dictionary> pagesByOwner = new Dictionary>(StringComparer.OrdinalIgnoreCase); private static readonly Dictionary> ownersByDisplayName = new Dictionary>(StringComparer.OrdinalIgnoreCase); public static IReadOnlyList Pages => pages; internal static string GetDisplayTitle(Page page) { if (page == null) { return string.Empty; } string text = page.Name ?? string.Empty; string key = page.NormalizedName ?? NormalizePageName(text); if (!ownersByDisplayName.TryGetValueNoCase>(key, out var value) || value.Count <= 1) { return text; } string text2 = page.OwnerName; if (string.IsNullOrWhiteSpace(text2)) { text2 = page.OwnerGuid; } if (!string.IsNullOrEmpty(text2) && text2.Length > 8) { text2 = text2.Substring(0, 8); } if (!string.IsNullOrWhiteSpace(text2)) { return text + " (" + text2 + ")"; } return text; } private static Page AddOrReplacePage(Page page) { if (page == null) { throw new ArgumentNullException("page"); } if (string.IsNullOrWhiteSpace(page.OwnerGuid)) { throw new ArgumentException("Page must have an owner GUID.", "page"); } if (string.IsNullOrWhiteSpace(page.NormalizedName)) { throw new ArgumentException("Page must have a normalized name.", "page"); } TrackDisplayName(page); string ownerGuid = page.OwnerGuid; string normalizedName = page.NormalizedName; if (!pagesByOwner.TryGetValueNoCase>(ownerGuid, out var value)) { value = new Dictionary(StringComparer.OrdinalIgnoreCase); pagesByOwner[ownerGuid] = value; } if (value.TryGetValueNoCase(normalizedName, out var value2)) { int num = pages.IndexOf(value2); if (num >= 0) { pages.RemoveAt(num); pages.Insert(num, page); } else { pages.Add(page); } value[normalizedName] = page; return page; } value[normalizedName] = page; pages.Add(page); return page; } private static void TrackDisplayName(Page page) { string key = page.NormalizedName ?? NormalizePageName(page.Name ?? string.Empty); string text = page.OwnerGuid ?? string.Empty; if (!ownersByDisplayName.TryGetValueNoCase>(key, out var value)) { value = new HashSet(StringComparer.OrdinalIgnoreCase); ownersByDisplayName[key] = value; } if (!string.IsNullOrWhiteSpace(text)) { value.Add(text); } } private static string NormalizePageName(string name) { if (name == null) { throw new ArgumentNullException("name"); } string text = name.Trim(); if (text.Length == 0) { throw new ArgumentException("Page name must not be empty.", "name"); } return text; } private static Page CreatePageShell(BaseUnityPlugin owner, string name) { if ((Object)(object)owner == (Object)null) { throw new ArgumentNullException("owner"); } if (name == null) { throw new ArgumentNullException("name"); } string normalizedName = NormalizePageName(name); return new Page(name.Trim(), owner, owner, normalizedName); } public static PageBuilder RegisterPage(BaseUnityPlugin owner, string name, Action? configure = null) { if ((Object)(object)owner == (Object)null) { throw new ArgumentNullException("owner"); } if (name == null) { throw new ArgumentNullException("name"); } Page page = CreatePageShell(owner, name); PageBuilder pageBuilder = new PageBuilder(page); try { configure?.Invoke(pageBuilder); } catch (Exception arg) { Logging.LogError($"Exception while configuring settings page '{page.Name}': {arg}"); throw; } AddOrReplacePage(page); return pageBuilder; } [Obsolete("Use RegisterPage(BaseUnityPlugin, string, Action) instead. This overload infers the owner from the calling assembly, which is fragile and may break in certain scenarios.")] public static PageBuilder RegisterPage(string name, Action configure) { return RegisterPage(ResolveOwnerForLegacyCall(Assembly.GetCallingAssembly()), name, configure); } public static PageBuilder RegisterPageFromConfig(BaseUnityPlugin owner, string name, string[]? ignoredKeys = null, string[]? ignoredCategories = null, Action? configure = null) { if ((Object)(object)owner == (Object)null) { throw new ArgumentNullException("owner"); } if (name == null) { throw new ArgumentNullException("name"); } Page page = CreatePageShell(owner, name); PageBuilder pageBuilder = new PageBuilder(page); PopulateFromConfig(pageBuilder, owner, ignoredKeys, ignoredCategories); try { configure?.Invoke(pageBuilder); } catch (Exception arg) { Logging.LogError($"Exception while configuring settings page '{page.Name}': {arg}"); throw; } AddOrReplacePage(page); return pageBuilder; } [Obsolete("Use RegisterPageFromConfig(BaseUnityPlugin, string, string[], string[], Action) instead. This overload infers the owner from the calling assembly, which is fragile and may break in certain scenarios.")] public static PageBuilder RegisterPageFromConfig(string name, string[] ignoredKeys, string[] ignoredCategories, Action? configure = null) { return RegisterPageFromConfig(ResolveOwnerForLegacyCall(Assembly.GetCallingAssembly()), name, ignoredKeys, ignoredCategories, configure); } private static void PopulateFromConfig(PageBuilder builder, BaseUnityPlugin owner, string[]? ignoredKeys, string[]? ignoredCategories) { if (builder == null) { throw new ArgumentNullException("builder"); } if ((Object)(object)owner == (Object)null) { throw new ArgumentNullException("owner"); } List> list = ((IEnumerable>)owner.Config)?.Where(delegate(KeyValuePair e) { ConfigDefinition key2 = e.Key; if (((key2 != null) ? key2.Key : null) == null) { return false; } if (ignoredKeys != null && ignoredKeys.Contains(e.Key.Key, StringComparer.OrdinalIgnoreCase)) { return false; } return (ignoredCategories == null || !ignoredCategories.Contains(e.Key.Section, StringComparer.OrdinalIgnoreCase)) ? true : false; }).ToList(); if (list == null || !list.Any()) { return; } foreach (KeyValuePair item in list) { try { object boxedValue = item.Value.BoxedValue; if (boxedValue is bool) { builder.OnOff(item.Key.Key, (ConfigEntry)(object)item.Value); } else if (boxedValue is int) { int? min = null; int? max = null; if (item.Value.Description.AcceptableValues is AcceptableValueRange val) { min = val.MinValue; max = val.MaxValue; } builder.Int(item.Key.Key, (ConfigEntry)(object)item.Value, min, max); } else if (boxedValue is float) { float num = 0f; float num2 = 100f; if (item.Value.Description.AcceptableValues is AcceptableValueRange val2) { num = val2.MinValue; num2 = val2.MaxValue; } GetScaleValues(num, num2, out var step, out var decimalPlaces, out var isPercent, out var scale); string key = item.Key.Key; ConfigEntry entry = (ConfigEntry)(object)item.Value; float minPercent = num; float maxPercent = num2; float step2 = step; int decimalPlaces2 = decimalPlaces; float scale2 = scale; builder.Percent(key, entry, minPercent, maxPercent, step2, wrap: false, decimalPlaces2, isPercent, scale2); } else if (boxedValue is string) { ConfigEntry val3 = (ConfigEntry)(object)item.Value; ConfigDescription description = ((ConfigEntryBase)val3).Description; if (((description != null) ? description.AcceptableValues : null) is AcceptableValueList val4 && val4.AcceptableValues != null && val4.AcceptableValues.Length != 0) { builder.Cycle(item.Key.Key, val3, val4.AcceptableValues); } } else if (boxedValue is Enum) { ConfigEntryBase configEntry = item.Value; Type settingType = item.Value.SettingType; builder.Cycle(item.Key.Key, () => (Enum)configEntry.BoxedValue, delegate(Enum value) { configEntry.BoxedValue = value; }, Enum.GetValues(settingType).Cast().ToArray()); } } catch (Exception ex) { Logging.LogError($"Exception while adding config entry '{item.Value.Definition.Section}.{item.Value.Definition.Key}' to settings page '{builder.BuiltPage.Name}': {ex}"); } } } private static void GetScaleValues(float min, float max, out float step, out int decimalPlaces, out bool isPercent, out float scale) { if (max < min) { throw new ArgumentOutOfRangeException("max", "max must be greater than or equal to min."); } isPercent = min >= 0f && min < 0.5f && max > 0.5f && max <= 5f; scale = (isPercent ? 100f : 1f); float value = (max - min) / 100f; step = MathUtils.RoundToNearestSignificantFive(value); decimalPlaces = Math.Max(0, Math.Min(4, Mathf.RoundToInt(Mathf.Log10(1f / step)))); } public static Item RegisterItem(Page page, Func? label, Action? onSelect = null, Action? onAdjust = null) { if (page == null) { throw new ArgumentNullException("page"); } return page.AddItem(label, onSelect, onAdjust); } private static BaseUnityPlugin ResolveOwnerForLegacyCall(Assembly caller) { if (caller == null) { throw new ArgumentNullException("caller"); } BaseUnityPlugin val = TryResolveOwnerFromAssembly(caller); if ((Object)(object)val != (Object)null) { return val; } throw new InvalidOperationException("Unable to infer the plugin owning this settings page. Call the overload that accepts a BaseUnityPlugin instance."); } private static BaseUnityPlugin? TryResolveOwnerFromAssembly(Assembly assembly) { if (assembly == null) { return null; } foreach (KeyValuePair pluginInfo in Chainloader.PluginInfos) { PluginInfo value = pluginInfo.Value; if (!((Object)(object)((value != null) ? value.Instance : null) == (Object)null) && ((object)value.Instance).GetType().Assembly == assembly) { return value.Instance; } } return null; } } } namespace CloverAPI.Content.Data { public class PersistentDataManager { [StructLayout(LayoutKind.Auto)] [CompilerGenerated] private struct d__3 : IAsyncStateMachine { public int <>1__state; public AsyncUniTaskMethodBuilder <>t__builder; private Dictionary.KeyCollection.Enumerator <>7__wrap1; private string 5__3; private bool <>7__wrap3; private Awaiter <>u__1; private void MoveNext() { //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) int num = <>1__state; bool result2; try { bool flag = default(bool); if (num != 0) { flag = false; <>7__wrap1 = PersistentDataItems.Keys.GetEnumerator(); } try { if (num != 0) { goto IL_00fe; } Awaiter val = <>u__1; <>u__1 = default(Awaiter); num = (<>1__state = -1); goto IL_00cc; IL_00cc: bool result = val.GetResult(); flag = <>7__wrap3 || !result; PersistentDataItems[5__3].AfterLoad(); 5__3 = null; goto IL_00fe; IL_00fe: if (<>7__wrap1.MoveNext()) { 5__3 = <>7__wrap1.Current; PersistentDataItems[5__3].BeforeLoad(); bool flag2 = flag; <>7__wrap3 = flag2; val = _Load(5__3, PersistentDataItems[5__3]).GetAwaiter(); if (!val.IsCompleted) { num = (<>1__state = 0); <>u__1 = val; <>t__builder.AwaitUnsafeOnCompleted, d__3>(ref val, ref this); return; } goto IL_00cc; } } finally { if (num < 0) { ((IDisposable)<>7__wrap1/*cast due to .constrained prefix*/).Dispose(); } } <>7__wrap1 = default(Dictionary.KeyCollection.Enumerator); result2 = !flag; } catch (Exception exception) { <>1__state = -2; <>t__builder.SetException(exception); return; } <>1__state = -2; <>t__builder.SetResult(result2); } void IAsyncStateMachine.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext this.MoveNext(); } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine stateMachine) { <>t__builder.SetStateMachine(stateMachine); } void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine this.SetStateMachine(stateMachine); } } [StructLayout(LayoutKind.Auto)] [CompilerGenerated] private struct d__2 : IAsyncStateMachine { public int <>1__state; public AsyncUniTaskMethodBuilder <>t__builder; private Dictionary.KeyCollection.Enumerator <>7__wrap1; private string 5__3; private bool <>7__wrap3; private Awaiter <>u__1; private void MoveNext() { //IL_00b0: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0077: Unknown result type (might be due to invalid IL or missing references) //IL_007b: Unknown result type (might be due to invalid IL or missing references) //IL_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0095: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) int num = <>1__state; bool result2; try { bool flag = default(bool); if (num != 0) { flag = false; <>7__wrap1 = PersistentDataItems.Keys.GetEnumerator(); } try { if (num != 0) { goto IL_00fe; } Awaiter val = <>u__1; <>u__1 = default(Awaiter); num = (<>1__state = -1); goto IL_00cc; IL_00cc: bool result = val.GetResult(); flag = <>7__wrap3 || !result; PersistentDataItems[5__3].AfterSave(); 5__3 = null; goto IL_00fe; IL_00fe: if (<>7__wrap1.MoveNext()) { 5__3 = <>7__wrap1.Current; PersistentDataItems[5__3].BeforeSave(); bool flag2 = flag; <>7__wrap3 = flag2; val = _Save(5__3, PersistentDataItems[5__3]).GetAwaiter(); if (!val.IsCompleted) { num = (<>1__state = 0); <>u__1 = val; <>t__builder.AwaitUnsafeOnCompleted, d__2>(ref val, ref this); return; } goto IL_00cc; } } finally { if (num < 0) { ((IDisposable)<>7__wrap1/*cast due to .constrained prefix*/).Dispose(); } } <>7__wrap1 = default(Dictionary.KeyCollection.Enumerator); result2 = !flag; } catch (Exception exception) { <>1__state = -2; <>t__builder.SetException(exception); return; } <>1__state = -2; <>t__builder.SetResult(result2); } void IAsyncStateMachine.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext this.MoveNext(); } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine stateMachine) { <>t__builder.SetStateMachine(stateMachine); } void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine this.SetStateMachine(stateMachine); } } [StructLayout(LayoutKind.Auto)] [CompilerGenerated] private struct <_Load>d__5 : IAsyncStateMachine { public int <>1__state; public AsyncUniTaskMethodBuilder <>t__builder; public string name; public PersistentData data; private Awaiter <>u__1; private void MoveNext() { //IL_0063: 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) //IL_006f: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) int num = <>1__state; bool result2; try { Awaiter val; if (num != 0) { val = PlatformDataMaster.Load(name, PlatformDataMaster.GameFolderPath + "ModData_" + name + ".json").GetAwaiter(); if (!val.IsCompleted) { num = (<>1__state = 0); <>u__1 = val; <>t__builder.AwaitUnsafeOnCompleted, <_Load>d__5>(ref val, ref this); return; } } else { val = <>u__1; <>u__1 = default(Awaiter); num = (<>1__state = -1); } string result = val.GetResult(); if (string.IsNullOrEmpty(result)) { result2 = false; } else { data.FromString(result); result2 = true; } } catch (Exception exception) { <>1__state = -2; <>t__builder.SetException(exception); return; } <>1__state = -2; <>t__builder.SetResult(result2); } void IAsyncStateMachine.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext this.MoveNext(); } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine stateMachine) { <>t__builder.SetStateMachine(stateMachine); } void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine this.SetStateMachine(stateMachine); } } [StructLayout(LayoutKind.Auto)] [CompilerGenerated] private struct <_Save>d__4 : IAsyncStateMachine { public int <>1__state; public AsyncUniTaskMethodBuilder <>t__builder; public string name; public PersistentData data; private Awaiter <>u__1; private void MoveNext() { //IL_006d: Unknown result type (might be due to invalid IL or missing references) //IL_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0042: 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_0057: Unknown result type (might be due to invalid IL or missing references) int num = <>1__state; bool result; try { Awaiter val; if (num != 0) { val = PlatformDataMaster.Save(name, PlatformDataMaster.GameFolderPath + "ModData_" + name + ".json", data.ToString()).GetAwaiter(); if (!val.IsCompleted) { num = (<>1__state = 0); <>u__1 = val; <>t__builder.AwaitUnsafeOnCompleted, <_Save>d__4>(ref val, ref this); return; } } else { val = <>u__1; <>u__1 = default(Awaiter); num = (<>1__state = -1); } result = val.GetResult(); } catch (Exception exception) { <>1__state = -2; <>t__builder.SetException(exception); return; } <>1__state = -2; <>t__builder.SetResult(result); } void IAsyncStateMachine.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext this.MoveNext(); } [DebuggerHidden] private void SetStateMachine(IAsyncStateMachine stateMachine) { <>t__builder.SetStateMachine(stateMachine); } void IAsyncStateMachine.SetStateMachine(IAsyncStateMachine stateMachine) { //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine this.SetStateMachine(stateMachine); } } internal static Dictionary PersistentDataItems = new Dictionary(); public static void RegisterData(string name, PersistentData data) { PersistentDataItems[name] = data; } [AsyncStateMachine(typeof(d__2))] internal static UniTask SaveData() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) d__2 d__ = default(d__2); d__.<>t__builder = AsyncUniTaskMethodBuilder.Create(); d__.<>1__state = -1; d__.<>t__builder.Start<d__2>(ref d__); return d__.<>t__builder.Task; } [AsyncStateMachine(typeof(d__3))] internal static UniTask LoadData() { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) d__3 d__ = default(d__3); d__.<>t__builder = AsyncUniTaskMethodBuilder.Create(); d__.<>1__state = -1; d__.<>t__builder.Start<d__3>(ref d__); return d__.<>t__builder.Task; } [AsyncStateMachine(typeof(<_Save>d__4))] private static UniTask _Save(string name, PersistentData data) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) <_Save>d__4 <_Save>d__ = default(<_Save>d__4); <_Save>d__.<>t__builder = AsyncUniTaskMethodBuilder.Create(); <_Save>d__.name = name; <_Save>d__.data = data; <_Save>d__.<>1__state = -1; <_Save>d__.<>t__builder.Start<<_Save>d__4>(ref <_Save>d__); return <_Save>d__.<>t__builder.Task; } [AsyncStateMachine(typeof(<_Load>d__5))] private static UniTask _Load(string name, PersistentData data) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) <_Load>d__5 <_Load>d__ = default(<_Load>d__5); <_Load>d__.<>t__builder = AsyncUniTaskMethodBuilder.Create(); <_Load>d__.name = name; <_Load>d__.data = data; <_Load>d__.<>1__state = -1; <_Load>d__.<>t__builder.Start<<_Load>d__5>(ref <_Load>d__); return <_Load>d__.<>t__builder.Task; } internal static void ResetAll() { foreach (string key in PersistentDataItems.Keys) { PersistentDataItems[key].OnReset(); } } } } namespace CloverAPI.Content.Charms { public static class CharmManager { public static int CurrentId => CustomCharms.Count; public static int NextId => CurrentId + 1; public static int MaxId { get { if (CustomCharms.Count != 0) { return 205 + CustomCharms.Count; } return 204; } } public static int NewCount => MaxId + 1; public static int TotalCharms => 204 + CustomCharms.Count; internal static List CustomCharms { get; } = new List(); internal static Dictionary GUIDToId { get; } = new Dictionary(); internal static Dictionary IdToGUID { get; } = new Dictionary(); internal static Dictionary CustomCharmsById { get; } = new Dictionary(); public static CharmBuilder Builder(string nameSpace, string identifier) { return CharmBuilder.Create(nameSpace, identifier); } public static Identifier RegisterCharm(CharmBuilder charm) { //IL_006e: Unknown result type (might be due to invalid IL or missing references) //IL_0074: Invalid comparison between Unknown and I4 //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0067: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) if (!CustomCharms.Contains(charm)) { charm.id = (Identifier)(205 + NextId); GUIDToId[charm.guid] = charm.id; IdToGUID[charm.id] = charm.guid; CustomCharmsById[charm.id] = charm; CustomCharms.Add(charm); return charm.id; } if ((int)charm.id != -1) { Logging.LogWarning("Charm " + charm.GetName() + " is already registered."); return charm.id; } throw new Exception("Charm " + charm.GetName() + " is already registered but has no identifier set."); } internal static PowerupScript SpawnCustomCharm(Identifier identifier, bool isNewGame) { //IL_0000: Unknown result type (might be due to invalid IL or missing references) //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Expected I4, but got Unknown //IL_0020: 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_007e: Unknown result type (might be due to invalid IL or missing references) //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Expected O, but got Unknown int num = identifier - 205 - 1; if (num < 0 || num >= CustomCharms.Count) { Logging.LogError($"Invalid custom charm identifier: {identifier}"); return null; } CharmBuilder charmBuilder = CustomCharms[num]; GameObject val; if (charmBuilder.GetModelMode() == CharmBuilder.ModelMode.GenericModel) { val = APIAssets.InstantiateGenericCharm(); } else if (charmBuilder.GetModelMode() == CharmBuilder.ModelMode.ExistingModel) { val = Object.Instantiate(PowerupScript.GetPrefab(charmBuilder.GetSourceModel())); } else { if (charmBuilder.GetModelMode() != CharmBuilder.ModelMode.CustomModel) { throw new InvalidOperationException($"Unknown model mode for charm {charmBuilder.GetName()} (ID: {identifier})."); } val = AssetBundleUtils.InstantiateCustomModelCharm(charmBuilder.GetCustomModelAssetBundlePath(), charmBuilder.GetCustomModelName()); } if ((Object)(object)val == (Object)null) { Logging.LogError($"Could not instantiate charm {charmBuilder.GetName()} (ID: {identifier})."); return null; } PowerupScript component = val.GetComponent(); Texture2D texture = charmBuilder.GetTexture(); if ((Object)(object)texture != (Object)null) { component.materialDefault = new Material(component.materialDefault); component.materialDefault.mainTexture = (Texture)(object)texture; } charmBuilder._Initialize(component, isNewGame); return component; } internal static Dictionary GenerateRemaps(Dictionary oldMap) { return GenerateRemaps(oldMap, GUIDToId); } internal static Dictionary GenerateRemaps(Dictionary oldMap, Dictionary newMap) { //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0028: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) Dictionary dictionary = new Dictionary(); foreach (KeyValuePair item in oldMap) { if (newMap.TryGetValueNoCase(item.Key, out var value)) { if (value != item.Value) { dictionary[item.Value] = value; } } else { dictionary[item.Value] = (Identifier)(-1); } } foreach (KeyValuePair item2 in newMap) { if (!oldMap.ContainsKey(item2.Key)) { dictionary[item2.Value] = (Identifier)(-1); } } return dictionary; } public static CharmBuilder GetCharmByGUID(string guid) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (GUIDToId.TryGetValueNoCase(guid, out var value)) { return GetCharmByID(value); } return null; } public static CharmBuilder GetCharmByID(Identifier id) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (CustomCharmsById.TryGetValue(id, out var value)) { return value; } return null; } public static CharmData GetCharmDataByGUID(string guid) { return AllCharmData.Instance.GetCharmDataByGUID(guid); } public static CharmData GetCharmDataByID(Identifier id) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) return AllCharmData.Instance.GetCharmDataByID(id); } public static Identifier GetCharmIDByGUID(string guid) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) if (GUIDToId.TryGetValueNoCase(guid, out var value)) { return value; } return (Identifier)(-1); } public static string GetCharmGUIDByID(Identifier id) { //IL_0005: Unknown result type (might be due to invalid IL or missing references) if (IdToGUID.TryGetValue(id, out var value)) { return value; } return null; } } public class CharmScript { private CharmBuilder charmReference; public CharmData Data { get { //IL_0010: Unknown result type (might be due to invalid IL or missing references) if (charmReference == null) { return null; } return CharmManager.GetCharmDataByID(charmReference.id); } } public virtual void OnEquip(PowerupScript powerup) { } public virtual void OnUnequip(PowerupScript powerup) { } public virtual void OnPutInDrawer(PowerupScript powerup) { } public virtual void OnThrowAway(PowerupScript powerup) { } public CharmBuilder ToBuilder(string nameSpace, string name) { return CharmBuilder.Create(nameSpace, name, this); } internal void SetCharmReference(CharmBuilder charmBuilder) { charmReference = charmBuilder; } } } namespace CloverAPI.Content.Builders { public class CharmBuilder { public enum ModelMode { ExistingModel, GenericModel, CustomModel } private Archetype _archetype; private Category _category = (Category)1; private string _customModelAssetBundlePath; private string _customModelName; private StringSource _description = "No description set."; private bool _isInstantPowerup; private int _maxBuyTimes = -1; private ModelMode _modelMode = ModelMode.GenericModel; private StringSource _name = "Unnamed Lucky Charm"; private PowerupEvent _onEquip; private PowerupEvent _onPutInDrawer; private PowerupEvent _onThrowAway; private PowerupEvent _onUnequip; private Identifier _sourceModel = (Identifier)(-1); private int _startingPrice = 3; private float _storeRerollChance; private TextureSource _texture; private StringSource _unlockMission = new VanillaLocalizedString("POWERUP_UNLOCK_MISSION_NONE"); private BigInteger _unlockPrice = -1L; internal string guid; internal Identifier id = (Identifier)(-1); private CharmBuilder(string nameSpace, string identifier) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_0062: Unknown result type (might be due to invalid IL or missing references) if (string.IsNullOrEmpty(nameSpace)) { throw new ArgumentException("Namespace cannot be null or empty.", "nameSpace"); } if (string.IsNullOrEmpty(identifier)) { throw new ArgumentException("Identifier cannot be null or empty.", "identifier"); } if (identifier.Contains(".")) { throw new ArgumentException("Identifier cannot contain dots.", "identifier"); } guid = nameSpace + "." + identifier; } public static CharmBuilder Create(string nameSpace, string identifier) { return new CharmBuilder(nameSpace, identifier); } public static CharmBuilder Create(string nameSpace, string identifier, CharmScript script) { return Create(nameSpace, identifier).WithScript(script); } public CharmBuilder WithCategory(Category category) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) _category = category; return this; } public CharmBuilder WithArchetype(Archetype archetype) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) _archetype = archetype; return this; } public CharmBuilder WithIsInstantPowerup(bool isInstantPowerup = true) { _isInstantPowerup = isInstantPowerup; return this; } public CharmBuilder WithMaxBuyTimes(int maxBuyTimes) { _maxBuyTimes = maxBuyTimes; return this; } public CharmBuilder WithStoreRerollChance(float storeRerollChance) { _storeRerollChance = storeRerollChance; return this; } public CharmBuilder WithStartingPrice(int startingPrice) { _startingPrice = startingPrice; return this; } public CharmBuilder WithUnlockPrice(BigInteger unlockPrice) { _unlockPrice = unlockPrice; return this; } public CharmBuilder WithUnlockPrice(long unlockPrice) { _unlockPrice = unlockPrice; return this; } public CharmBuilder WithName(StringSource name) { _name = name; return this; } public CharmBuilder WithName(string name) { _name = name; return this; } public CharmBuilder WithDescription(StringSource description) { _description = description; return this; } public CharmBuilder WithDescription(string description) { _description = description; return this; } public CharmBuilder WithUnlockMission(StringSource unlockMission) { _unlockMission = unlockMission; return this; } public CharmBuilder WithUnlockMission(string unlockMission) { _unlockMission = unlockMission; return this; } public CharmBuilder WithOnEquipEvent(PowerupEvent onEquip) { _onEquip = onEquip; return this; } public CharmBuilder WithOnUnequipEvent(PowerupEvent onUnequip) { _onUnequip = onUnequip; return this; } public CharmBuilder WithOnPutInDrawerEvent(PowerupEvent onPutInDrawer) { _onPutInDrawer = onPutInDrawer; return this; } public CharmBuilder WithOnThrowAwayEvent(PowerupEvent onThrowAway) { _onThrowAway = onThrowAway; return this; } public CharmBuilder WithScript(CharmScript script) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Expected O, but got Unknown //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Expected O, but got Unknown //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Expected O, but got Unknown //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004c: Expected O, but got Unknown _onEquip = new PowerupEvent(script.OnEquip); _onUnequip = new PowerupEvent(script.OnUnequip); _onPutInDrawer = new PowerupEvent(script.OnPutInDrawer); _onThrowAway = new PowerupEvent(script.OnThrowAway); script.SetCharmReference(this); return this; } public CharmBuilder WithScript() where T : CharmScript, new() { return WithScript(new T()); } public CharmBuilder WithTextureModel(TextureSource texture) { _modelMode = ModelMode.GenericModel; _texture = texture; return this; } public CharmBuilder WithExistingModel(Identifier sourceModel, TextureSource texture = null) { //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) _modelMode = ModelMode.ExistingModel; _sourceModel = sourceModel; _texture = texture; return this; } public CharmBuilder WithCustomModel(string assetBundlePath, string modelName = null, TextureSource texture = null) { _modelMode = ModelMode.CustomModel; _customModelAssetBundlePath = assetBundlePath; _customModelName = modelName; _texture = texture; return this; } public string GetName() { return _name; } public string GetDescription() { return _description; } public Category GetCategory() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return _category; } public Archetype GetArchetype() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return _archetype; } public bool IsInstantPowerup() { return _isInstantPowerup; } public int GetMaxBuyTimes() { return _maxBuyTimes; } public float GetStoreRerollChance() { return _storeRerollChance; } public int GetStartingPrice() { return _startingPrice; } public BigInteger GetUnlockPrice() { return _unlockPrice; } public string GetUnlockMission() { return _unlockMission; } public PowerupEvent GetOnEquipEvent() { return _onEquip; } public PowerupEvent GetOnUnequipEvent() { return _onUnequip; } public PowerupEvent GetOnPutInDrawerEvent() { return _onPutInDrawer; } public PowerupEvent GetOnThrowAwayEvent() { return _onThrowAway; } public ModelMode GetModelMode() { return _modelMode; } public string GetCustomModelAssetBundlePath() { return _customModelAssetBundlePath; } public string GetCustomModelName() { return _customModelName; } public Identifier GetSourceModel() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return _sourceModel; } public Texture2D GetTexture() { return _texture?.GetTexture(); } public Identifier GetID() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return id; } public string GetGUID() { return guid; } public string GetNamespace() { int length = guid.LastIndexOf('.'); return guid.Substring(0, length); } public string GetIdentifier() { int num = guid.LastIndexOf('.'); return guid.Substring(num + 1); } internal void _Initialize(PowerupScript obj, bool isNewGame) { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Invalid comparison between Unknown and I4 //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) if ((int)id == -1) { throw new InvalidOperationException(string.Concat("Charm ", _name, " is not registered. Call BuildAndRegister() before using the charm.")); } obj.Initialize(isNewGame, _category, id, _archetype, _isInstantPowerup, _maxBuyTimes, _storeRerollChance, _startingPrice, _unlockPrice, LocalizationManager.RegisterTranslation("MODDED_CHARM_" + guid + "_NAME", _name), LocalizationManager.RegisterTranslation("MODDED_CHARM_" + guid + "_DESC", _description), LocalizationManager.RegisterTranslation("MODDED_CHARM_" + guid + "_MISSION", _unlockMission), _onEquip, _onUnequip, _onPutInDrawer, _onThrowAway); } [Obsolete("Use BuildAndRegister() for clarity.")] public Identifier Build() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return BuildAndRegister(); } public Identifier BuildAndRegister() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) return CharmManager.RegisterCharm(this); } } } namespace CloverAPI.Content.Audio { public static class AudioManager { internal class AudioClipOverride : IPriority { public AudioClip Clip; public string OwnerGuid; public int Priority => ResourceOrdering.GetPriority(OwnerGuid); } internal static ConcurrentDictionary> SoundOverrides = new ConcurrentDictionary>(); internal static ConcurrentDictionary> MusicOverrides = new ConcurrentDictionary>(); public static void RegisterSoundOverride(string name, AudioClip clip, ModGuid guid) { _RegisterOverride(name, SoundOverrides, clip, guid); } public static void RegisterMusicOverride(string name, AudioClip clip, ModGuid guid) { _RegisterOverride(name, MusicOverrides, clip, guid); } private static void _RegisterOverride(string name, ConcurrentDictionary> dict, AudioClip clip, ModGuid guid) { if (!dict.ContainsKey(name)) { dict[name] = new OrderedList(); } AudioClipOverride audioClipOverride = dict[name].FirstOrDefault((AudioClipOverride o) => o.OwnerGuid == guid); if (audioClipOverride != null) { audioClipOverride.Clip = clip; return; } dict[name].Add(new AudioClipOverride { Clip = clip, OwnerGuid = guid }); } public static bool TryGetSoundOverride(string name, out AudioClip audioClip) { return _TryGetOverride(name, SoundOverrides, out audioClip); } public static bool TryGetMusicOverride(string name, out AudioClip audioClip) { return _TryGetOverride(name, MusicOverrides, out audioClip); } public static bool TryGetSoundOverride(out AudioClip audioClip, params string[] names) { for (int i = 0; i < names.Length; i++) { if (TryGetSoundOverride(names[i], out audioClip)) { return true; } } audioClip = null; return false; } public static bool TryGetMusicOverride(out AudioClip audioClip, params string[] names) { for (int i = 0; i < names.Length; i++) { if (TryGetMusicOverride(names[i], out audioClip)) { return true; } } audioClip = null; return false; } private static bool _TryGetOverride(string name, ConcurrentDictionary> dict, out AudioClip audioClip) { if (dict.TryGetValueNoCase(name, out var value) && value.Count > 0) { AudioClipOverride audioClipOverride = value[0]; audioClip = audioClipOverride.Clip; return true; } audioClip = null; return false; } } } namespace CloverAPI.Classes { public class JsonPersistentData : PersistentData { public override string ToString() { return JsonConvert.SerializeObject((object)this); } public override void FromString(string data) { JsonConvert.PopulateObject(data, (object)this); } } public enum OnMissingLanguage { UseDefault, UseFirst, ReturnKey, ReturnNull, ReturnEmpty, ReturnErrorAsString, ThrowError } public class LocalizedString : StringSource { private readonly string _defaultValue; private readonly Dictionary _localizedValues; private readonly OnMissingLanguage _onMissingLanguage; public string _key = "(Key Undefined)"; public LocalizedString(Dictionary localizedValues, string defaultValue = null, OnMissingLanguage onMissingLanguage = OnMissingLanguage.ReturnErrorAsString) { if (localizedValues == null || localizedValues.Count == 0) { throw new ArgumentException("localizedValues must contain at least one entry."); } _localizedValues = localizedValues; _defaultValue = defaultValue; _onMissingLanguage = onMissingLanguage; } public override string GetString() { //IL_0005: Unknown result type (might be due to invalid IL or missing references) //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_009e: Unknown result type (might be due to invalid IL or missing references) Language language = Data.settings.language; if (_localizedValues.TryGetValue(language, out var value)) { return value; } switch (_onMissingLanguage) { case OnMissingLanguage.UseDefault: if (!string.IsNullOrEmpty(_defaultValue)) { return _defaultValue; } goto case OnMissingLanguage.UseFirst; case OnMissingLanguage.UseFirst: return new List(_localizedValues.Values)[0]; case OnMissingLanguage.ReturnKey: return _key; case OnMissingLanguage.ReturnNull: return null; case OnMissingLanguage.ReturnEmpty: return string.Empty; case OnMissingLanguage.ReturnErrorAsString: return $"[Error: Key '{_key}' missing for language '{language}']"; case OnMissingLanguage.ThrowError: throw new Exception($"Missing localization for language: {language}"); default: throw new ArgumentOutOfRangeException(); } } public override void SetKey(string key) { _key = key; } } public readonly struct ModGuid { public string Guid { get; } public ModGuid(string guid) { Guid = guid ?? throw new ArgumentNullException("guid"); } public override string ToString() { return Guid; } public static implicit operator string(ModGuid modGuid) { return modGuid.Guid; } public static implicit operator ModGuid(string str) { return new ModGuid(str); } public static implicit operator ModGuid(BaseUnityPlugin plugin) { PluginInfo info = plugin.Info; object obj; if (info == null) { obj = null; } else { BepInPlugin metadata = info.Metadata; obj = ((metadata != null) ? metadata.GUID : null); } if (obj == null) { throw new ArgumentNullException("plugin"); } return new ModGuid((string)obj); } } public readonly struct ModName { public string Name { get; } public ModName(string name) { Name = name ?? throw new ArgumentNullException("name"); } public override string ToString() { return Name; } public static implicit operator string(ModName modName) { return modName.Name; } public static implicit operator ModName(string str) { return new ModName(str); } public static implicit operator ModName(BaseUnityPlugin plugin) { PluginInfo info = plugin.Info; object obj; if (info == null) { obj = null; } else { BepInPlugin metadata = info.Metadata; obj = ((metadata != null) ? metadata.Name : null); } if (obj == null) { obj = ((object)plugin).GetType().Name; } return new ModName((string)obj); } } public class OrderedList : IEnumerable, IEnumerable where TComparer : IComparer, new() { private List items; private TComparer comparer; private bool isSorted; private bool losesOrderOnRemove; public int Count => items.Count; public T this[int index] { get { EnsureSorted(); return items[index]; } set { items[index] = value; isSorted = false; } } public OrderedList(bool losesOrderOnRemove = false) { items = new List(); comparer = new TComparer(); isSorted = true; this.losesOrderOnRemove = losesOrderOnRemove; } public void Add(T item) { items.Add(item); isSorted = false; } public bool Remove(T item) { bool num = items.Remove(item); if (num && losesOrderOnRemove) { isSorted = false; } return num; } public void Clear() { items.Clear(); isSorted = true; } public List ToList() { EnsureSorted(); return new List(items); } private void EnsureSorted() { if (!isSorted) { items.Sort(comparer); isSorted = true; } } public IEnumerator GetEnumerator() { EnsureSorted(); return items.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } } public abstract class PersistentData { public new abstract string ToString(); public abstract void FromString(string data); public virtual void BeforeLoad() { } public virtual void AfterLoad() { } public virtual void BeforeSave() { } public virtual void AfterSave() { } public virtual void OnReset() { } } internal class PriorityComparer : IComparer { public int Compare(IPriority x, IPriority y) { if (x == null && y == null) { return 0; } if (x == null) { return 1; } if (y == null) { return -1; } return x.Priority.CompareTo(y.Priority); } } public class RawString : StringSource { private readonly string _value; public RawString(string value) { _value = value; } public override string GetString() { return _value; } public static implicit operator RawString(string value) { return new RawString(value); } public static implicit operator string(RawString src) { return src._value; } } public class StringFromCallable : StringSource { private delegate string StringProvider(); private readonly StringProvider _provider; public StringFromCallable(Func provider) { _provider = provider.Invoke; } public override string GetString() { return _provider(); } } public abstract class StringSource { public abstract string GetString(); public static implicit operator StringSource(string value) { return new RawString(value); } public static implicit operator string(StringSource src) { return src.GetString(); } public static implicit operator StringSource(Func func) { return new StringFromCallable(func); } public virtual void SetKey(string key) { } } public abstract class TextureSource { public abstract Texture2D GetTexture(); public static implicit operator TextureSource(string filePath) { return new TextureSourceFromFile(filePath); } public static implicit operator TextureSource(Texture2D texture) { return new TextureSourceFromTexture(texture); } public static implicit operator Texture2D(TextureSource source) { return source.GetTexture(); } } public class TextureSourceFromFile : TextureSource { private readonly string _filePath; private Texture2D _texture; public TextureSourceFromFile(string filePath) { _filePath = filePath; LoadTexture(); } private void LoadTexture() { _texture = TextureUtils.LoadTextureFromFile(_filePath, (FilterMode)0); } public override Texture2D GetTexture() { return _texture; } public static implicit operator TextureSourceFromFile(string filePath) { return new TextureSourceFromFile(filePath); } public static implicit operator string(TextureSourceFromFile source) { return source._filePath; } } public class TextureSourceFromTexture : TextureSource { private readonly Texture2D _texture; public TextureSourceFromTexture(Texture2D texture) { _texture = texture; } public override Texture2D GetTexture() { return _texture; } public static implicit operator TextureSourceFromTexture(Texture2D texture) { return new TextureSourceFromTexture(texture); } public static implicit operator Texture2D(TextureSourceFromTexture source) { return source._texture; } } public class VanillaLocalizedString : StringSource { public string Key { get; } public VanillaLocalizedString(string key) { Key = key; } public override string GetString() { return Translation.Get(Key); } } } namespace CloverAPI.Classes.Interfaces { internal interface IPriority { int Priority { get; } } } namespace CloverAPI.Assets { internal class APIAssets { private const string GENERIC_CHARM_OBJ_NAME = "Powerup Generic Editable"; internal static AssetBundle _assetBundle; internal static GameObject _genericCharmPrefab; internal static GameObject InstantiateGenericCharm() { return Object.Instantiate(GetGenericCharmPrefab()); } internal static GameObject GetGenericCharmPrefab() { if ((Object)(object)_assetBundle == (Object)null) { Logging.LogError("AssetBundle not loaded!"); return null; } if ((Object)(object)_genericCharmPrefab == (Object)null) { _genericCharmPrefab = _assetBundle.LoadAsset("Powerup Generic Editable"); if ((Object)(object)_genericCharmPrefab == (Object)null) { Logging.LogError("Could not find Powerup Generic Editable in AssetBundle!"); AssetBundleUtils.PrintContentsOfAssetBundle(_assetBundle); return null; } } return _genericCharmPrefab; } } }