Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of RandomCosmetic v4.0.0
RandomCosmetic.dll
Decompiled 19 hours agousing System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: IgnoresAccessChecksTo("")] [assembly: AssemblyCompany("REPOJP")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("zabuMod")] [assembly: AssemblyTitle("zabuMod")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.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; } } } [BepInPlugin("REPOJP.RandomCosmetic", "RandomCosmetic", "4.0.0")] public class RandomCosmeticPlugin : BaseUnityPlugin { private enum CosmeticCategoryGroup { Head, Face, Body, Back, Other } private sealed class SaveGuardState { public bool Restore; public List<int> RuntimeEquipped; public int[] RuntimeColors; } [CompilerGenerated] private sealed class <ApplyWhenReadyCoroutine>d__91 : IEnumerator<object>, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string reason; public int sequence; private float <elapsed>5__2; private float <retryInterval>5__3; private float <maxRetry>5__4; object IEnumerator<object>.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public <ApplyWhenReadyCoroutine>d__91(int <>1__state) { this.<>1__state = <>1__state; } [DebuggerHidden] void IDisposable.Dispose() { <>1__state = -2; } private bool MoveNext() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0046: Expected O, but got Unknown //IL_00fd: Unknown result type (might be due to invalid IL or missing references) //IL_0107: Expected O, but got Unknown switch (<>1__state) { default: return false; case 0: { <>1__state = -1; float configFloat = GetConfigFloat(ConfigApplyDelaySeconds, 0.25f); if (configFloat > 0f) { <>2__current = (object)new WaitForSeconds(configFloat); <>1__state = 1; return true; } goto IL_0056; } case 1: <>1__state = -1; goto IL_0056; case 2: { <>1__state = -1; break; } IL_0056: <elapsed>5__2 = 0f; <retryInterval>5__3 = Mathf.Max(0.05f, GetConfigFloat(ConfigRetryIntervalSeconds, 0.25f)); <maxRetry>5__4 = Mathf.Max(1f, GetConfigFloat(ConfigMaxRetrySeconds, 10f)); break; } if (pendingRandomize && sequence == pendingSequence && <elapsed>5__2 <= <maxRetry>5__4) { if (!IsReadyForApply()) { <elapsed>5__2 += <retryInterval>5__3; <>2__current = (object)new WaitForSeconds(<retryInterval>5__3); <>1__state = 2; return true; } if (TryApplyRandomLoadout()) { pendingRandomize = false; lastAppliedLevelKey = pendingLevelKey; WriteDebugLog("Applied random loadout: " + lastAppliedLevelKey + " / trigger=" + reason); } } if (pendingRandomize && sequence == pendingSequence) { WriteDebugLog("Apply timeout: " + pendingLevelKey); } applyCoroutineRunning = false; if (pendingRandomize && sequence != pendingSequence) { StartApplyCoroutine("reschedule"); } return false; } bool IEnumerator.MoveNext() { //ILSpy generated this explicit interface implementation from .override directive in MoveNext return this.MoveNext(); } [DebuggerHidden] void IEnumerator.Reset() { throw new NotSupportedException(); } } public const string PluginGuid = "REPOJP.RandomCosmetic"; public const string PluginName = "RandomCosmetic"; public const string PluginVersion = "4.0.0"; private static RandomCosmeticPlugin Instance = null; private static ManualLogSource Log = null; private Harmony harmony; private static ConfigEntry<bool> ConfigEnable = null; private static ConfigEntry<bool> ConfigShowDebugLog = null; private static ConfigEntry<bool> ConfigRandomizeOnChangeLevel = null; private static ConfigEntry<bool> ConfigRandomizeOnNormalLevelEnter = null; private static ConfigEntry<bool> ConfigRandomizeOnShopEnter = null; private static ConfigEntry<bool> ConfigRandomizeOnArenaEnter = null; private static ConfigEntry<bool> ConfigUseUnlockedOnly = null; private static ConfigEntry<bool> ConfigAllowEmptySlot = null; private static ConfigEntry<int> ConfigEmptySlotWeight = null; private static ConfigEntry<bool> ConfigRandomizeHead = null; private static ConfigEntry<bool> ConfigRandomizeFace = null; private static ConfigEntry<bool> ConfigRandomizeBody = null; private static ConfigEntry<bool> ConfigRandomizeBack = null; private static ConfigEntry<bool> ConfigRandomizeOther = null; private static ConfigEntry<bool> ConfigDoNotOverwriteSavedPreset = null; private static ConfigEntry<bool> ConfigSaveRandomizedLoadout = null; private static ConfigEntry<bool> ConfigSaveRandomizedLoadoutAsPreset = null; private static ConfigEntry<int> ConfigPresetSlot = null; private static ConfigEntry<float> ConfigApplyDelaySeconds = null; private static ConfigEntry<float> ConfigRetryIntervalSeconds = null; private static ConfigEntry<float> ConfigMaxRetrySeconds = null; private static Type TypeRunManager = null; private static Type TypeLevelGenerator = null; private static Type TypePlayerAvatar = null; private static Type TypePlayerCosmetics = null; private static Type TypeMeta = null; private static Type TypeCosmeticAsset = null; private static Type TypeSemiFunc = null; private static FieldInfo FieldRunInstance = null; private static FieldInfo FieldRunLevelCurrent = null; private static FieldInfo FieldRunLevelMainMenu = null; private static FieldInfo FieldRunLevelLobbyMenu = null; private static FieldInfo FieldRunLevelLobby = null; private static FieldInfo FieldRunLevelSplashScreen = null; private static FieldInfo FieldRunLevelTutorial = null; private static FieldInfo FieldRunLevelRecording = null; private static FieldInfo FieldRunLevelShop = null; private static FieldInfo FieldRunLevelArena = null; private static FieldInfo FieldLevelGeneratorInstance = null; private static FieldInfo FieldLevelGeneratorGenerated = null; private static FieldInfo FieldPlayerAvatarInstance = null; private static FieldInfo FieldPlayerAvatarCosmetics = null; private static FieldInfo FieldPlayerCosmeticsFirstSetup = null; private static FieldInfo FieldMetaInstance = null; private static FieldInfo FieldMetaSaveReady = null; private static FieldInfo FieldMetaCosmeticUnlocks = null; private static FieldInfo FieldMetaCosmeticEquipped = null; private static FieldInfo FieldMetaColorsEquipped = null; private static FieldInfo FieldMetaCosmeticAssets = null; private static FieldInfo FieldMetaCosmeticTypeAssets = null; private static FieldInfo FieldAssetType = null; private static FieldInfo FieldAssetPrefab = null; private static FieldInfo FieldAssetName = null; private static FieldInfo FieldTypeAssetType = null; private static FieldInfo FieldTypeAssetTypeName = null; private static MethodInfo MethodSemiFuncPlayerGetLocal = null; private static MethodInfo MethodMetaCosmeticEquip = null; private static MethodInfo MethodMetaCosmeticUnequip = null; private static MethodInfo MethodMetaCosmeticPreviewSet = null; private static MethodInfo MethodMetaCosmeticPlayerUpdateLocal = null; private static MethodInfo MethodMetaCosmeticPresetSet = null; private static MethodInfo MethodPrefabIsValid = null; private static bool pendingRandomize = false; private static bool applyCoroutineRunning = false; private static bool applyingRandomize = false; private static bool randomLoadoutActive = false; private static bool saveGuardActive = false; private static bool presetGuardActive = false; private static int pendingSequence = 0; private static string pendingLevelKey = string.Empty; private static string lastAppliedLevelKey = string.Empty; private static List<int> baseEquippedSnapshot = null; private static int[] baseColorsSnapshot = null; private void Awake() { //IL_0046: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Expected O, but got Unknown try { Instance = this; Log = ((BaseUnityPlugin)this).Logger; ((Component)this).transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); BindConfig(); InitializeReflection(); harmony = new Harmony("REPOJP.RandomCosmetic"); PatchRuntimeMethods(); WriteDebugLog("Loaded RandomCosmetic v4.0.0"); } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failure: Awake\n" + ex)); } } private void OnDestroy() { try { if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception ex) { ((BaseUnityPlugin)this).Logger.LogError((object)("Failure: OnDestroy\n" + ex)); } } private void BindConfig() { //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Expected O, but got Unknown //IL_0255: Unknown result type (might be due to invalid IL or missing references) //IL_025f: Expected O, but got Unknown //IL_0292: Unknown result type (might be due to invalid IL or missing references) //IL_029c: Expected O, but got Unknown //IL_02cf: Unknown result type (might be due to invalid IL or missing references) //IL_02d9: Expected O, but got Unknown //IL_030c: Unknown result type (might be due to invalid IL or missing references) //IL_0316: Expected O, but got Unknown ConfigEnable = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Enable", true, "Enable this mod.このMODを有効化します。"); ConfigShowDebugLog = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "ShowDebugLog", false, "Show debug logs.デバッグログを出力します。"); ConfigRandomizeOnChangeLevel = ((BaseUnityPlugin)this).Config.Bind<bool>("Randomize", "RandomizeOnChangeLevel", true, "Randomize cosmetics on level changes.レベル変更時にコスメをランダム化します。"); ConfigRandomizeOnNormalLevelEnter = ((BaseUnityPlugin)this).Config.Bind<bool>("Randomize", "RandomizeOnNormalLevelEnter", true, "Randomize cosmetics when entering normal levels.通常レベル進入時にコスメをランダム化します。"); ConfigRandomizeOnShopEnter = ((BaseUnityPlugin)this).Config.Bind<bool>("Randomize", "RandomizeOnShopEnter", false, "Randomize cosmetics when entering shop levels.ショップ進入時にコスメをランダム化します。"); ConfigRandomizeOnArenaEnter = ((BaseUnityPlugin)this).Config.Bind<bool>("Randomize", "RandomizeOnArenaEnter", true, "Randomize cosmetics when entering arena levels.アリーナ進入時にコスメをランダム化します。"); ConfigUseUnlockedOnly = ((BaseUnityPlugin)this).Config.Bind<bool>("Randomize", "UseUnlockedOnly", true, "Use unlocked cosmetics only.所持済みコスメのみ使用します。"); ConfigAllowEmptySlot = ((BaseUnityPlugin)this).Config.Bind<bool>("Randomize", "AllowEmptySlot", true, "Allow empty equipment slots in random results.空装備をランダム候補に含めます。"); ConfigEmptySlotWeight = ((BaseUnityPlugin)this).Config.Bind<int>("Randomize", "EmptySlotWeight", 1, new ConfigDescription("Weight for empty equipment slots.空装備候補の重みです。", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>())); ConfigRandomizeHead = ((BaseUnityPlugin)this).Config.Bind<bool>("Category", "RandomizeHead", true, "Randomize head cosmetics.頭部系コスメをランダム化します。"); ConfigRandomizeFace = ((BaseUnityPlugin)this).Config.Bind<bool>("Category", "RandomizeFace", true, "Randomize face cosmetics.顔系コスメをランダム化します。"); ConfigRandomizeBody = ((BaseUnityPlugin)this).Config.Bind<bool>("Category", "RandomizeBody", true, "Randomize body cosmetics.体・腕・脚・足系コスメをランダム化します。"); ConfigRandomizeBack = ((BaseUnityPlugin)this).Config.Bind<bool>("Category", "RandomizeBack", true, "Randomize back-like cosmetics detected by names.背中系として判定できるコスメをランダム化します。"); ConfigRandomizeOther = ((BaseUnityPlugin)this).Config.Bind<bool>("Category", "RandomizeOther", true, "Randomize other cosmetics.その他コスメをランダム化します。"); ConfigDoNotOverwriteSavedPreset = ((BaseUnityPlugin)this).Config.Bind<bool>("Preset", "DoNotOverwriteSavedPreset", true, "Do not overwrite saved presets while a random loadout is active.ランダム衣装中は保存済みプリセットを上書きしません。"); ConfigSaveRandomizedLoadout = ((BaseUnityPlugin)this).Config.Bind<bool>("Preset", "SaveRandomizedLoadout", false, "Save randomized loadouts to the normal equipped cosmetic save.ランダム衣装を通常装備として保存します。"); ConfigSaveRandomizedLoadoutAsPreset = ((BaseUnityPlugin)this).Config.Bind<bool>("Preset", "SaveRandomizedLoadoutAsPreset", false, "Save randomized loadouts to a preset when allowed.許可時にランダム衣装をプリセットへ保存します。"); ConfigPresetSlot = ((BaseUnityPlugin)this).Config.Bind<int>("Preset", "PresetSlot", 0, new ConfigDescription("Preset slot index used when saving randomized loadouts.ランダム衣装保存時のプリセット番号です。", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 27), Array.Empty<object>())); ConfigApplyDelaySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "ApplyDelaySeconds", 0.25f, new ConfigDescription("Delay before applying a random loadout after the game becomes ready.準備完了後にランダム衣装を適用するまでの遅延秒数です。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>())); ConfigRetryIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "RetryIntervalSeconds", 0.25f, new ConfigDescription("Retry interval while waiting for the local player and cosmetics.ローカルプレイヤーとコスメ準備待機中の再試行間隔です。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 5f), Array.Empty<object>())); ConfigMaxRetrySeconds = ((BaseUnityPlugin)this).Config.Bind<float>("Timing", "MaxRetrySeconds", 10f, new ConfigDescription("Maximum retry time for applying a random loadout.ランダム衣装適用の最大再試行秒数です。", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 60f), Array.Empty<object>())); } private static void InitializeReflection() { TypeRunManager = AccessTools.TypeByName("RunManager"); TypeLevelGenerator = AccessTools.TypeByName("LevelGenerator"); TypePlayerAvatar = AccessTools.TypeByName("PlayerAvatar"); TypePlayerCosmetics = AccessTools.TypeByName("PlayerCosmetics"); TypeMeta = AccessTools.TypeByName("MetaManager"); TypeCosmeticAsset = AccessTools.TypeByName("CosmeticAsset"); TypeSemiFunc = AccessTools.TypeByName("SemiFunc"); if (TypeRunManager != null) { FieldRunInstance = GetField(TypeRunManager, "instance"); FieldRunLevelCurrent = GetField(TypeRunManager, "levelCurrent"); FieldRunLevelMainMenu = GetField(TypeRunManager, "levelMainMenu"); FieldRunLevelLobbyMenu = GetField(TypeRunManager, "levelLobbyMenu"); FieldRunLevelLobby = GetField(TypeRunManager, "levelLobby"); FieldRunLevelSplashScreen = GetField(TypeRunManager, "levelSplashScreen"); FieldRunLevelTutorial = GetField(TypeRunManager, "levelTutorial"); FieldRunLevelRecording = GetField(TypeRunManager, "levelRecording"); FieldRunLevelShop = GetField(TypeRunManager, "levelShop"); FieldRunLevelArena = GetField(TypeRunManager, "levelArena"); } if (TypeLevelGenerator != null) { FieldLevelGeneratorInstance = GetField(TypeLevelGenerator, "Instance"); FieldLevelGeneratorGenerated = GetField(TypeLevelGenerator, "Generated"); } if (TypePlayerAvatar != null) { FieldPlayerAvatarInstance = GetField(TypePlayerAvatar, "instance"); FieldPlayerAvatarCosmetics = GetField(TypePlayerAvatar, "playerCosmetics"); } if (TypePlayerCosmetics != null) { FieldPlayerCosmeticsFirstSetup = GetField(TypePlayerCosmetics, "firstSetup"); } if (TypeMeta != null) { FieldMetaInstance = GetField(TypeMeta, "instance"); FieldMetaSaveReady = GetField(TypeMeta, "saveReady"); FieldMetaCosmeticUnlocks = GetField(TypeMeta, "cosmeticUnlocks"); FieldMetaCosmeticEquipped = GetField(TypeMeta, "cosmeticEquipped"); FieldMetaColorsEquipped = GetField(TypeMeta, "colorsEquipped"); FieldMetaCosmeticAssets = GetField(TypeMeta, "cosmeticAssets"); FieldMetaCosmeticTypeAssets = GetField(TypeMeta, "cosmeticTypeAssets"); MethodMetaCosmeticEquip = GetMethod(TypeMeta, "CosmeticEquip"); MethodMetaCosmeticUnequip = GetMethod(TypeMeta, "CosmeticUnequip"); MethodMetaCosmeticPreviewSet = GetMethod(TypeMeta, "CosmeticPreviewSet"); MethodMetaCosmeticPlayerUpdateLocal = GetMethod(TypeMeta, "CosmeticPlayerUpdateLocal"); MethodMetaCosmeticPresetSet = GetMethod(TypeMeta, "CosmeticPresetSet"); } if (TypeCosmeticAsset != null) { FieldAssetType = GetField(TypeCosmeticAsset, "type"); FieldAssetPrefab = GetField(TypeCosmeticAsset, "prefab"); FieldAssetName = GetField(TypeCosmeticAsset, "assetName"); } Type type = AccessTools.TypeByName("CosmeticTypeAsset"); if (type != null) { FieldTypeAssetType = GetField(type, "type"); FieldTypeAssetTypeName = GetField(type, "typeName"); } if (TypeSemiFunc != null) { MethodSemiFuncPlayerGetLocal = GetMethod(TypeSemiFunc, "PlayerGetLocal"); } } private void PatchRuntimeMethods() { try { PatchPostfix(TypeRunManager, "ChangeLevel", "RunManagerChangeLevelPostfix"); PatchPostfix(TypeRunManager, "UpdateLevel", "RunManagerUpdateLevelPostfix"); PatchPostfix(TypeLevelGenerator, "GenerateDone", "LevelGeneratorGenerateDonePostfix"); PatchPostfix(TypePlayerAvatar, "Start", "PlayerAvatarStartPostfix"); PatchPostfix(TypePlayerCosmetics, "SetupCosmetics", "PlayerCosmeticsSetupCosmeticsPostfix"); PatchPrefixPostfix(TypeMeta, "Save", "MetaSavePrefix", "MetaSavePostfix"); PatchPrefix(TypeMeta, "CosmeticPresetSet", "MetaCosmeticPresetSetPrefix"); } catch (Exception ex) { WriteErrorLog("Failure: PatchRuntimeMethods\n" + ex); } } private void PatchPostfix(Type targetType, string targetMethodName, string postfixName) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown if (targetType == null) { WriteDebugLog("Skip patch: type not found / " + targetMethodName); return; } MethodBase methodBase = AccessTools.Method(targetType, targetMethodName, (Type[])null, (Type[])null); MethodInfo methodInfo = AccessTools.Method(typeof(RandomCosmeticPlugin), postfixName, (Type[])null, (Type[])null); if (methodBase == null || methodInfo == null) { WriteDebugLog("Skip patch: method not found / " + targetType.Name + "." + targetMethodName); return; } PatchProcessor val = harmony.CreateProcessor(methodBase); val.AddPostfix(new HarmonyMethod(methodInfo)); val.Patch(); WriteDebugLog("Patched postfix: " + targetType.Name + "." + targetMethodName); } private void PatchPrefix(Type targetType, string targetMethodName, string prefixName) { //IL_0074: Unknown result type (might be due to invalid IL or missing references) //IL_007e: Expected O, but got Unknown if (targetType == null) { WriteDebugLog("Skip patch: type not found / " + targetMethodName); return; } MethodBase methodBase = AccessTools.Method(targetType, targetMethodName, (Type[])null, (Type[])null); MethodInfo methodInfo = AccessTools.Method(typeof(RandomCosmeticPlugin), prefixName, (Type[])null, (Type[])null); if (methodBase == null || methodInfo == null) { WriteDebugLog("Skip patch: method not found / " + targetType.Name + "." + targetMethodName); return; } PatchProcessor val = harmony.CreateProcessor(methodBase); val.AddPrefix(new HarmonyMethod(methodInfo)); val.Patch(); WriteDebugLog("Patched prefix: " + targetType.Name + "." + targetMethodName); } private void PatchPrefixPostfix(Type targetType, string targetMethodName, string prefixName, string postfixName) { //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Expected O, but got Unknown //IL_009e: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Expected O, but got Unknown if (targetType == null) { WriteDebugLog("Skip patch: type not found / " + targetMethodName); return; } MethodBase methodBase = AccessTools.Method(targetType, targetMethodName, (Type[])null, (Type[])null); MethodInfo methodInfo = AccessTools.Method(typeof(RandomCosmeticPlugin), prefixName, (Type[])null, (Type[])null); MethodInfo methodInfo2 = AccessTools.Method(typeof(RandomCosmeticPlugin), postfixName, (Type[])null, (Type[])null); if (methodBase == null || methodInfo == null || methodInfo2 == null) { WriteDebugLog("Skip patch: method not found / " + targetType.Name + "." + targetMethodName); return; } PatchProcessor val = harmony.CreateProcessor(methodBase); val.AddPrefix(new HarmonyMethod(methodInfo)); val.AddPostfix(new HarmonyMethod(methodInfo2)); val.Patch(); WriteDebugLog("Patched prefix/postfix: " + targetType.Name + "." + targetMethodName); } private static FieldInfo GetField(Type type, string name) { if (type == null) { return null; } return type.GetField(name, BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); } private static MethodInfo GetMethod(Type type, string name) { if (type == null) { return null; } return AccessTools.Method(type, name, (Type[])null, (Type[])null); } private static void ScheduleRandomize(string reason) { if (!IsModEnabled() || ConfigRandomizeOnChangeLevel == null || !ConfigRandomizeOnChangeLevel.Value) { return; } if (!ShouldRandomizeCurrentLevel(out var levelKind)) { WriteDebugLog("Skip schedule: disabled level kind / " + reason); return; } string currentLevelKey = GetCurrentLevelKey(levelKind); if (!string.IsNullOrEmpty(currentLevelKey)) { if (currentLevelKey == lastAppliedLevelKey) { WriteDebugLog("Skip schedule: already applied / " + currentLevelKey); return; } pendingRandomize = true; pendingSequence++; pendingLevelKey = currentLevelKey; WriteDebugLog("Pending randomize: " + reason + " / " + pendingLevelKey); StartApplyCoroutine("schedule"); } } private static void StartApplyCoroutine(string reason) { if (!((Object)(object)Instance == (Object)null) && !applyCoroutineRunning) { applyCoroutineRunning = true; ((MonoBehaviour)Instance).StartCoroutine(Instance.ApplyWhenReadyCoroutine(pendingSequence, reason)); } } [IteratorStateMachine(typeof(<ApplyWhenReadyCoroutine>d__91))] private IEnumerator ApplyWhenReadyCoroutine(int sequence, string reason) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new <ApplyWhenReadyCoroutine>d__91(0) { sequence = sequence, reason = reason }; } private static bool TryApplyRandomLoadout() { try { if (applyingRandomize) { return false; } applyingRandomize = true; object metaInstance = GetMetaInstance(); if (metaInstance == null) { return false; } IList cosmeticAssets = GetCosmeticAssets(metaInstance); IList cosmeticUnlocks = GetCosmeticUnlocks(metaInstance); List<int> cosmeticEquipped = GetCosmeticEquipped(metaInstance); int[] colorsEquipped = GetColorsEquipped(metaInstance); if (cosmeticAssets == null || cosmeticEquipped == null) { return false; } if (!randomLoadoutActive) { baseEquippedSnapshot = new List<int>(cosmeticEquipped); baseColorsSnapshot = CopyColors(colorsEquipped); } Dictionary<int, List<object>> dictionary = BuildCandidatesByType(metaInstance, cosmeticAssets, cosmeticUnlocks); List<int> allCosmeticTypes = GetAllCosmeticTypes(metaInstance, dictionary); if (allCosmeticTypes.Count == 0) { WriteDebugLog("No cosmetic types found"); return false; } int num = 0; for (int i = 0; i < allCosmeticTypes.Count; i++) { int num2 = allCosmeticTypes[i]; if (!IsCategoryEnabled(num2, metaInstance)) { continue; } if (!dictionary.TryGetValue(num2, out var value)) { value = new List<object>(); } object obj = SelectCandidate(value); bool flag = ConfigAllowEmptySlot != null && ConfigAllowEmptySlot.Value && ConfigEmptySlotWeight != null && ConfigEmptySlotWeight.Value > 0; if (obj == null && value.Count == 0 && !flag) { continue; } UnequipCurrentType(metaInstance, cosmeticAssets, num2); if (obj != null) { if (InvokeCosmeticEquip(metaInstance, obj, preview: false, save: false)) { num++; WriteDebugLog("Selected: " + GetTypeName(metaInstance, num2) + " / " + GetAssetDisplayName(obj)); } } else { num++; WriteDebugLog("Selected: " + GetTypeName(metaInstance, num2) + " / Empty"); } } InvokeCosmeticPreviewSet(metaInstance, state: false); InvokeCosmeticPlayerUpdateLocal(metaInstance, synced: true, forced: true); randomLoadoutActive = true; if (ConfigSaveRandomizedLoadoutAsPreset != null && ConfigSaveRandomizedLoadoutAsPreset.Value) { SaveRandomizedPreset(metaInstance); } WriteDebugLog("Randomize complete / changed types=" + num); return true; } catch (Exception ex) { WriteErrorLog("Failure: TryApplyRandomLoadout\n" + ex); return false; } finally { applyingRandomize = false; } } private static Dictionary<int, List<object>> BuildCandidatesByType(object meta, IList cosmeticAssets, IList unlocks) { Dictionary<int, List<object>> dictionary = new Dictionary<int, List<object>>(); bool flag = ConfigUseUnlockedOnly == null || ConfigUseUnlockedOnly.Value; for (int i = 0; i < cosmeticAssets.Count; i++) { object obj = cosmeticAssets[i]; if (!IsUnityObjectAlive(obj) || (flag && !ListContainsInt(unlocks, i)) || !IsPrefabValid(obj)) { continue; } int assetTypeKey = GetAssetTypeKey(obj); if (assetTypeKey >= 0) { if (!dictionary.TryGetValue(assetTypeKey, out var value)) { value = (dictionary[assetTypeKey] = new List<object>()); } value.Add(obj); } } return dictionary; } private static List<int> GetAllCosmeticTypes(object meta, Dictionary<int, List<object>> candidatesByType) { List<int> list = new List<int>(); IList cosmeticTypeAssets = GetCosmeticTypeAssets(meta); if (cosmeticTypeAssets != null) { for (int i = 0; i < cosmeticTypeAssets.Count; i++) { object typeAsset = cosmeticTypeAssets[i]; int typeAssetKey = GetTypeAssetKey(typeAsset); if (typeAssetKey >= 0 && !list.Contains(typeAssetKey)) { list.Add(typeAssetKey); } } } foreach (int key in candidatesByType.Keys) { if (!list.Contains(key)) { list.Add(key); } } list.Sort(); return list; } private static object SelectCandidate(List<object> candidates) { int num = 0; if (ConfigAllowEmptySlot != null && ConfigAllowEmptySlot.Value && ConfigEmptySlotWeight != null) { num = Mathf.Max(0, ConfigEmptySlotWeight.Value); } int num2 = candidates.Count + num; if (num2 <= 0) { return null; } int num3 = Random.Range(0, num2); if (num3 < num) { return null; } int num4 = num3 - num; if (num4 < 0 || num4 >= candidates.Count) { return null; } return candidates[num4]; } private static void UnequipCurrentType(object meta, IList cosmeticAssets, int typeKey) { List<int> cosmeticEquipped = GetCosmeticEquipped(meta); if (cosmeticEquipped == null) { return; } List<object> list = new List<object>(); for (int i = 0; i < cosmeticEquipped.Count; i++) { int num = cosmeticEquipped[i]; if (num >= 0 && num < cosmeticAssets.Count) { object obj = cosmeticAssets[num]; if (IsUnityObjectAlive(obj) && GetAssetTypeKey(obj) == typeKey) { list.Add(obj); } } } for (int j = 0; j < list.Count; j++) { InvokeCosmeticUnequip(meta, list[j], preview: false, save: false, resetColor: true); } } private static bool IsReadyForApply() { if (!IsModEnabled()) { return false; } object metaInstance = GetMetaInstance(); if (metaInstance == null || !GetBoolField(FieldMetaSaveReady, metaInstance, defaultValue: false)) { return false; } object levelGeneratorInstance = GetLevelGeneratorInstance(); if (levelGeneratorInstance == null || !GetBoolField(FieldLevelGeneratorGenerated, levelGeneratorInstance, defaultValue: false)) { return false; } object localPlayerCosmetics = GetLocalPlayerCosmetics(); if (localPlayerCosmetics == null) { return false; } if (GetBoolField(FieldPlayerCosmeticsFirstSetup, localPlayerCosmetics, defaultValue: false)) { return false; } string levelKind; return ShouldRandomizeCurrentLevel(out levelKind); } private static bool ShouldRandomizeCurrentLevel(out string levelKind) { levelKind = "Unknown"; object runManagerInstance = GetRunManagerInstance(); if (runManagerInstance == null) { return false; } object fieldValue = GetFieldValue(FieldRunLevelCurrent, runManagerInstance); if (!IsUnityObjectAlive(fieldValue)) { return false; } if (IsSameLevel(fieldValue, GetFieldValue(FieldRunLevelMainMenu, runManagerInstance)) || IsSameLevel(fieldValue, GetFieldValue(FieldRunLevelLobbyMenu, runManagerInstance)) || IsSameLevel(fieldValue, GetFieldValue(FieldRunLevelLobby, runManagerInstance)) || IsSameLevel(fieldValue, GetFieldValue(FieldRunLevelSplashScreen, runManagerInstance)) || IsSameLevel(fieldValue, GetFieldValue(FieldRunLevelTutorial, runManagerInstance)) || IsSameLevel(fieldValue, GetFieldValue(FieldRunLevelRecording, runManagerInstance))) { levelKind = "MenuOrLobby"; return false; } if (IsLevelInList(fieldValue, GetFieldValue(FieldRunLevelShop, runManagerInstance))) { levelKind = "Shop"; if (ConfigRandomizeOnShopEnter != null) { return ConfigRandomizeOnShopEnter.Value; } return false; } if (IsLevelInList(fieldValue, GetFieldValue(FieldRunLevelArena, runManagerInstance))) { levelKind = "Arena"; if (ConfigRandomizeOnArenaEnter != null) { return ConfigRandomizeOnArenaEnter.Value; } return false; } levelKind = "Normal"; if (ConfigRandomizeOnNormalLevelEnter != null) { return ConfigRandomizeOnNormalLevelEnter.Value; } return true; } private static string GetCurrentLevelKey(string levelKind) { object runManagerInstance = GetRunManagerInstance(); if (runManagerInstance == null) { return string.Empty; } object fieldValue = GetFieldValue(FieldRunLevelCurrent, runManagerInstance); string unityObjectName = GetUnityObjectName(fieldValue); if (string.IsNullOrEmpty(unityObjectName)) { return string.Empty; } return levelKind + ":" + unityObjectName; } private static bool IsCategoryEnabled(int typeKey, object meta) { switch (GetCategoryGroup(typeKey, meta)) { case CosmeticCategoryGroup.Head: if (ConfigRandomizeHead != null) { return ConfigRandomizeHead.Value; } return true; case CosmeticCategoryGroup.Face: if (ConfigRandomizeFace != null) { return ConfigRandomizeFace.Value; } return true; case CosmeticCategoryGroup.Body: if (ConfigRandomizeBody != null) { return ConfigRandomizeBody.Value; } return true; case CosmeticCategoryGroup.Back: if (ConfigRandomizeBack != null) { return ConfigRandomizeBack.Value; } return true; default: if (ConfigRandomizeOther != null) { return ConfigRandomizeOther.Value; } return true; } } private static CosmeticCategoryGroup GetCategoryGroup(int typeKey, object meta) { string text = GetTypeName(meta, typeKey).ToLowerInvariant(); if (text.Contains("back") || text.Contains("wing") || text.Contains("cape") || text.Contains("pack")) { return CosmeticCategoryGroup.Back; } if (text.Contains("hat") || text.Contains("head") || text.Contains("ear") || text.Contains("eyelid")) { return CosmeticCategoryGroup.Head; } if (text.Contains("face") || text.Contains("eye") || text.Contains("glass")) { return CosmeticCategoryGroup.Face; } if (text.Contains("body") || text.Contains("arm") || text.Contains("leg") || text.Contains("foot") || text.Contains("grabber")) { return CosmeticCategoryGroup.Body; } return CosmeticCategoryGroup.Other; } private static string GetTypeName(object meta, int typeKey) { IList cosmeticTypeAssets = GetCosmeticTypeAssets(meta); if (cosmeticTypeAssets != null) { for (int i = 0; i < cosmeticTypeAssets.Count; i++) { object obj = cosmeticTypeAssets[i]; if (GetTypeAssetKey(obj) == typeKey) { string stringField = GetStringField(FieldTypeAssetTypeName, obj); if (!string.IsNullOrEmpty(stringField)) { return stringField; } } } } object enumValueFromTypeAssets = GetEnumValueFromTypeAssets(meta, typeKey); if (enumValueFromTypeAssets != null) { return enumValueFromTypeAssets.ToString(); } return "Type" + typeKey; } private static object GetEnumValueFromTypeAssets(object meta, int typeKey) { IList cosmeticTypeAssets = GetCosmeticTypeAssets(meta); if (cosmeticTypeAssets == null) { return null; } for (int i = 0; i < cosmeticTypeAssets.Count; i++) { object instance = cosmeticTypeAssets[i]; object fieldValue = GetFieldValue(FieldTypeAssetType, instance); if (EnumToInt(fieldValue) == typeKey) { return fieldValue; } } return null; } private static object GetRunManagerInstance() { return GetStaticFieldValue(FieldRunInstance); } private static object GetLevelGeneratorInstance() { return GetStaticFieldValue(FieldLevelGeneratorInstance); } private static object GetMetaInstance() { return GetStaticFieldValue(FieldMetaInstance); } private static object GetLocalPlayerAvatar() { try { if (MethodSemiFuncPlayerGetLocal != null) { object obj = MethodSemiFuncPlayerGetLocal.Invoke(null, null); if (obj != null) { return obj; } } } catch { } return GetStaticFieldValue(FieldPlayerAvatarInstance); } private static object GetLocalPlayerCosmetics() { object localPlayerAvatar = GetLocalPlayerAvatar(); if (!IsUnityObjectAlive(localPlayerAvatar)) { return null; } object fieldValue = GetFieldValue(FieldPlayerAvatarCosmetics, localPlayerAvatar); if (IsUnityObjectAlive(fieldValue)) { return fieldValue; } Component val = (Component)((localPlayerAvatar is Component) ? localPlayerAvatar : null); if ((Object)(object)val != (Object)null && TypePlayerCosmetics != null) { Component componentInChildren = val.GetComponentInChildren(TypePlayerCosmetics, true); if ((Object)(object)componentInChildren != (Object)null) { return componentInChildren; } } return null; } private static IList GetCosmeticAssets(object meta) { return GetFieldValue(FieldMetaCosmeticAssets, meta) as IList; } private static IList GetCosmeticTypeAssets(object meta) { return GetFieldValue(FieldMetaCosmeticTypeAssets, meta) as IList; } private static IList GetCosmeticUnlocks(object meta) { return GetFieldValue(FieldMetaCosmeticUnlocks, meta) as IList; } private static List<int> GetCosmeticEquipped(object meta) { if (!(GetFieldValue(FieldMetaCosmeticEquipped, meta) is IList list)) { return null; } List<int> list2 = new List<int>(); for (int i = 0; i < list.Count; i++) { list2.Add(ConvertToInt(list[i], -1)); } return list2; } private static void SetCosmeticEquipped(object meta, List<int> value) { if (!(FieldMetaCosmeticEquipped == null) && meta != null && value != null) { FieldMetaCosmeticEquipped.SetValue(meta, value); } } private static int[] GetColorsEquipped(object meta) { return GetFieldValue(FieldMetaColorsEquipped, meta) as int[]; } private static void SetColorsEquipped(object meta, int[] value) { if (!(FieldMetaColorsEquipped == null) && meta != null && value != null) { FieldMetaColorsEquipped.SetValue(meta, value); } } private static int GetAssetTypeKey(object asset) { object fieldValue = GetFieldValue(FieldAssetType, asset); return EnumToInt(fieldValue); } private static int GetTypeAssetKey(object typeAsset) { object fieldValue = GetFieldValue(FieldTypeAssetType, typeAsset); return EnumToInt(fieldValue); } private static bool IsPrefabValid(object asset) { object fieldValue = GetFieldValue(FieldAssetPrefab, asset); if (fieldValue == null) { return false; } try { if (MethodPrefabIsValid == null) { MethodPrefabIsValid = AccessTools.Method(fieldValue.GetType(), "IsValid", (Type[])null, (Type[])null); } if (MethodPrefabIsValid != null) { object obj = MethodPrefabIsValid.Invoke(fieldValue, null); if (obj is bool) { return (bool)obj; } } } catch { } return true; } private static bool InvokeCosmeticEquip(object meta, object asset, bool preview, bool save) { if (MethodMetaCosmeticEquip == null || meta == null || asset == null) { return false; } try { object obj = MethodMetaCosmeticEquip.Invoke(meta, new object[3] { asset, preview, save }); return obj is bool && (bool)obj; } catch (Exception ex) { WriteErrorLog("Failure: CosmeticEquip " + GetAssetDisplayName(asset) + "\n" + ex); return false; } } private static bool InvokeCosmeticUnequip(object meta, object asset, bool preview, bool save, bool resetColor) { if (MethodMetaCosmeticUnequip == null || meta == null || asset == null) { return false; } try { object obj = MethodMetaCosmeticUnequip.Invoke(meta, new object[4] { asset, preview, save, resetColor }); return obj is bool && (bool)obj; } catch (Exception ex) { WriteErrorLog("Failure: CosmeticUnequip " + GetAssetDisplayName(asset) + "\n" + ex); return false; } } private static void InvokeCosmeticPreviewSet(object meta, bool state) { if (MethodMetaCosmeticPreviewSet == null || meta == null) { return; } try { MethodMetaCosmeticPreviewSet.Invoke(meta, new object[1] { state }); } catch (Exception ex) { WriteErrorLog("Failure: CosmeticPreviewSet\n" + ex); } } private static void InvokeCosmeticPlayerUpdateLocal(object meta, bool synced, bool forced) { if (MethodMetaCosmeticPlayerUpdateLocal == null || meta == null) { return; } try { MethodMetaCosmeticPlayerUpdateLocal.Invoke(meta, new object[2] { synced, forced }); } catch (Exception ex) { WriteErrorLog("Failure: CosmeticPlayerUpdateLocal\n" + ex); } } private static void SaveRandomizedPreset(object meta) { if (MethodMetaCosmeticPresetSet == null || meta == null) { return; } List<int> cosmeticEquipped = GetCosmeticEquipped(meta); int[] colorsEquipped = GetColorsEquipped(meta); if (cosmeticEquipped == null || colorsEquipped == null) { return; } List<int> list = new List<int>(); for (int i = 0; i < colorsEquipped.Length; i++) { list.Add(colorsEquipped[i]); } try { presetGuardActive = true; int num = ((ConfigPresetSlot != null) ? ConfigPresetSlot.Value : 0); MethodMetaCosmeticPresetSet.Invoke(meta, new object[3] { num, cosmeticEquipped, list }); WriteDebugLog("Saved randomized preset: " + num); } catch (Exception ex) { WriteErrorLog("Failure: SaveRandomizedPreset\n" + ex); } finally { presetGuardActive = false; } } private static string GetAssetDisplayName(object asset) { string stringField = GetStringField(FieldAssetName, asset); if (!string.IsNullOrEmpty(stringField)) { return stringField; } Object val = (Object)((asset is Object) ? asset : null); if (val != (Object)null) { return val.name; } return "Unknown"; } private static bool IsLevelInList(object currentLevel, object levelListObject) { if (!(levelListObject is IList list)) { return IsSameLevel(currentLevel, levelListObject); } for (int i = 0; i < list.Count; i++) { if (IsSameLevel(currentLevel, list[i])) { return true; } } return false; } private static bool IsSameLevel(object a, object b) { if (!IsUnityObjectAlive(a) || !IsUnityObjectAlive(b)) { return false; } if (a == b) { return true; } Object val = (Object)((a is Object) ? a : null); Object val2 = (Object)((b is Object) ? b : null); if (val != (Object)null && val2 != (Object)null) { return val == val2; } return false; } private static bool IsUnityObjectAlive(object obj) { if (obj == null) { return false; } Object val = (Object)((obj is Object) ? obj : null); if (val != (Object)null) { return val != (Object)null; } return true; } private static bool ListContainsInt(IList list, int value) { if (list == null) { return false; } for (int i = 0; i < list.Count; i++) { if (ConvertToInt(list[i], -999999) == value) { return true; } } return false; } private static object GetStaticFieldValue(FieldInfo field) { if (field == null) { return null; } try { return field.GetValue(null); } catch { return null; } } private static object GetFieldValue(FieldInfo field, object instance) { if (field == null || instance == null) { return null; } try { return field.GetValue(instance); } catch { return null; } } private static bool GetBoolField(FieldInfo field, object instance, bool defaultValue) { object fieldValue = GetFieldValue(field, instance); if (fieldValue is bool) { return (bool)fieldValue; } return defaultValue; } private static string GetStringField(FieldInfo field, object instance) { object fieldValue = GetFieldValue(field, instance); if (fieldValue == null) { return string.Empty; } return fieldValue.ToString(); } private static int EnumToInt(object value) { if (value == null) { return -1; } try { return Convert.ToInt32(value); } catch { return -1; } } private static int ConvertToInt(object value, int defaultValue) { if (value == null) { return defaultValue; } try { return Convert.ToInt32(value); } catch { return defaultValue; } } private static int[] CopyColors(int[] source) { if (source == null) { return null; } int[] array = new int[source.Length]; Array.Copy(source, array, source.Length); return array; } private static string GetUnityObjectName(object obj) { Object val = (Object)((obj is Object) ? obj : null); if (val == (Object)null) { return string.Empty; } return val.name; } private static float GetConfigFloat(ConfigEntry<float> config, float defaultValue) { return config?.Value ?? defaultValue; } private static bool IsModEnabled() { if (ConfigEnable != null) { return ConfigEnable.Value; } return false; } private static void WriteDebugLog(string message) { if (ConfigShowDebugLog != null && ConfigShowDebugLog.Value && Log != null) { Log.LogInfo((object)message); } } private static void WriteErrorLog(string message) { if (Log != null) { Log.LogError((object)message); } } private static void RunManagerChangeLevelPostfix() { ScheduleRandomize("RunManager.ChangeLevel"); } private static void RunManagerUpdateLevelPostfix() { ScheduleRandomize("RunManager.UpdateLevel"); } private static void LevelGeneratorGenerateDonePostfix() { if (pendingRandomize) { StartApplyCoroutine("LevelGenerator.GenerateDone"); } } private static void PlayerAvatarStartPostfix(object __instance) { object localPlayerAvatar = GetLocalPlayerAvatar(); if (__instance != null && localPlayerAvatar != null && __instance == localPlayerAvatar && pendingRandomize) { StartApplyCoroutine("PlayerAvatar.Start"); } } private static void PlayerCosmeticsSetupCosmeticsPostfix() { if (!applyingRandomize && pendingRandomize) { StartApplyCoroutine("PlayerCosmetics.SetupCosmetics"); } } private static void MetaSavePrefix(object __instance, ref SaveGuardState __state) { __state = new SaveGuardState(); if (__instance == null || ConfigSaveRandomizedLoadout == null || ConfigSaveRandomizedLoadout.Value || !randomLoadoutActive || applyingRandomize || baseEquippedSnapshot == null || saveGuardActive) { return; } List<int> cosmeticEquipped = GetCosmeticEquipped(__instance); int[] colorsEquipped = GetColorsEquipped(__instance); if (cosmeticEquipped != null) { __state.Restore = true; __state.RuntimeEquipped = new List<int>(cosmeticEquipped); __state.RuntimeColors = CopyColors(colorsEquipped); saveGuardActive = true; SetCosmeticEquipped(__instance, new List<int>(baseEquippedSnapshot)); if (baseColorsSnapshot != null) { SetColorsEquipped(__instance, CopyColors(baseColorsSnapshot)); } } } private static void MetaSavePostfix(object __instance, SaveGuardState __state) { if (__instance == null || __state == null || !__state.Restore) { saveGuardActive = false; return; } SetCosmeticEquipped(__instance, __state.RuntimeEquipped); if (__state.RuntimeColors != null) { SetColorsEquipped(__instance, __state.RuntimeColors); } saveGuardActive = false; } private static bool MetaCosmeticPresetSetPrefix() { if (presetGuardActive) { return true; } if (ConfigDoNotOverwriteSavedPreset == null || !ConfigDoNotOverwriteSavedPreset.Value) { return true; } if (!randomLoadoutActive) { return true; } WriteDebugLog("Blocked preset overwrite while random loadout is active"); return false; } }