using 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 RuntimeEquipped; public int[] RuntimeColors; } [CompilerGenerated] private sealed class d__91 : IEnumerator, IEnumerator, IDisposable { private int <>1__state; private object <>2__current; public string reason; public int sequence; private float 5__2; private float 5__3; private float 5__4; object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } object IEnumerator.Current { [DebuggerHidden] get { return <>2__current; } } [DebuggerHidden] public 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: 5__2 = 0f; 5__3 = Mathf.Max(0.05f, GetConfigFloat(ConfigRetryIntervalSeconds, 0.25f)); 5__4 = Mathf.Max(1f, GetConfigFloat(ConfigMaxRetrySeconds, 10f)); break; } if (pendingRandomize && sequence == pendingSequence && 5__2 <= 5__4) { if (!IsReadyForApply()) { 5__2 += 5__3; <>2__current = (object)new WaitForSeconds(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 ConfigEnable = null; private static ConfigEntry ConfigShowDebugLog = null; private static ConfigEntry ConfigRandomizeOnChangeLevel = null; private static ConfigEntry ConfigRandomizeOnNormalLevelEnter = null; private static ConfigEntry ConfigRandomizeOnShopEnter = null; private static ConfigEntry ConfigRandomizeOnArenaEnter = null; private static ConfigEntry ConfigUseUnlockedOnly = null; private static ConfigEntry ConfigAllowEmptySlot = null; private static ConfigEntry ConfigEmptySlotWeight = null; private static ConfigEntry ConfigRandomizeHead = null; private static ConfigEntry ConfigRandomizeFace = null; private static ConfigEntry ConfigRandomizeBody = null; private static ConfigEntry ConfigRandomizeBack = null; private static ConfigEntry ConfigRandomizeOther = null; private static ConfigEntry ConfigDoNotOverwriteSavedPreset = null; private static ConfigEntry ConfigSaveRandomizedLoadout = null; private static ConfigEntry ConfigSaveRandomizedLoadoutAsPreset = null; private static ConfigEntry ConfigPresetSlot = null; private static ConfigEntry ConfigApplyDelaySeconds = null; private static ConfigEntry ConfigRetryIntervalSeconds = null; private static ConfigEntry 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 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("General", "Enable", true, "Enable this mod.このMODを有効化します。"); ConfigShowDebugLog = ((BaseUnityPlugin)this).Config.Bind("General", "ShowDebugLog", false, "Show debug logs.デバッグログを出力します。"); ConfigRandomizeOnChangeLevel = ((BaseUnityPlugin)this).Config.Bind("Randomize", "RandomizeOnChangeLevel", true, "Randomize cosmetics on level changes.レベル変更時にコスメをランダム化します。"); ConfigRandomizeOnNormalLevelEnter = ((BaseUnityPlugin)this).Config.Bind("Randomize", "RandomizeOnNormalLevelEnter", true, "Randomize cosmetics when entering normal levels.通常レベル進入時にコスメをランダム化します。"); ConfigRandomizeOnShopEnter = ((BaseUnityPlugin)this).Config.Bind("Randomize", "RandomizeOnShopEnter", false, "Randomize cosmetics when entering shop levels.ショップ進入時にコスメをランダム化します。"); ConfigRandomizeOnArenaEnter = ((BaseUnityPlugin)this).Config.Bind("Randomize", "RandomizeOnArenaEnter", true, "Randomize cosmetics when entering arena levels.アリーナ進入時にコスメをランダム化します。"); ConfigUseUnlockedOnly = ((BaseUnityPlugin)this).Config.Bind("Randomize", "UseUnlockedOnly", true, "Use unlocked cosmetics only.所持済みコスメのみ使用します。"); ConfigAllowEmptySlot = ((BaseUnityPlugin)this).Config.Bind("Randomize", "AllowEmptySlot", true, "Allow empty equipment slots in random results.空装備をランダム候補に含めます。"); ConfigEmptySlotWeight = ((BaseUnityPlugin)this).Config.Bind("Randomize", "EmptySlotWeight", 1, new ConfigDescription("Weight for empty equipment slots.空装備候補の重みです。", (AcceptableValueBase)(object)new AcceptableValueRange(0, 100), Array.Empty())); ConfigRandomizeHead = ((BaseUnityPlugin)this).Config.Bind("Category", "RandomizeHead", true, "Randomize head cosmetics.頭部系コスメをランダム化します。"); ConfigRandomizeFace = ((BaseUnityPlugin)this).Config.Bind("Category", "RandomizeFace", true, "Randomize face cosmetics.顔系コスメをランダム化します。"); ConfigRandomizeBody = ((BaseUnityPlugin)this).Config.Bind("Category", "RandomizeBody", true, "Randomize body cosmetics.体・腕・脚・足系コスメをランダム化します。"); ConfigRandomizeBack = ((BaseUnityPlugin)this).Config.Bind("Category", "RandomizeBack", true, "Randomize back-like cosmetics detected by names.背中系として判定できるコスメをランダム化します。"); ConfigRandomizeOther = ((BaseUnityPlugin)this).Config.Bind("Category", "RandomizeOther", true, "Randomize other cosmetics.その他コスメをランダム化します。"); ConfigDoNotOverwriteSavedPreset = ((BaseUnityPlugin)this).Config.Bind("Preset", "DoNotOverwriteSavedPreset", true, "Do not overwrite saved presets while a random loadout is active.ランダム衣装中は保存済みプリセットを上書きしません。"); ConfigSaveRandomizedLoadout = ((BaseUnityPlugin)this).Config.Bind("Preset", "SaveRandomizedLoadout", false, "Save randomized loadouts to the normal equipped cosmetic save.ランダム衣装を通常装備として保存します。"); ConfigSaveRandomizedLoadoutAsPreset = ((BaseUnityPlugin)this).Config.Bind("Preset", "SaveRandomizedLoadoutAsPreset", false, "Save randomized loadouts to a preset when allowed.許可時にランダム衣装をプリセットへ保存します。"); ConfigPresetSlot = ((BaseUnityPlugin)this).Config.Bind("Preset", "PresetSlot", 0, new ConfigDescription("Preset slot index used when saving randomized loadouts.ランダム衣装保存時のプリセット番号です。", (AcceptableValueBase)(object)new AcceptableValueRange(0, 27), Array.Empty())); ConfigApplyDelaySeconds = ((BaseUnityPlugin)this).Config.Bind("Timing", "ApplyDelaySeconds", 0.25f, new ConfigDescription("Delay before applying a random loadout after the game becomes ready.準備完了後にランダム衣装を適用するまでの遅延秒数です。", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 10f), Array.Empty())); ConfigRetryIntervalSeconds = ((BaseUnityPlugin)this).Config.Bind("Timing", "RetryIntervalSeconds", 0.25f, new ConfigDescription("Retry interval while waiting for the local player and cosmetics.ローカルプレイヤーとコスメ準備待機中の再試行間隔です。", (AcceptableValueBase)(object)new AcceptableValueRange(0.05f, 5f), Array.Empty())); ConfigMaxRetrySeconds = ((BaseUnityPlugin)this).Config.Bind("Timing", "MaxRetrySeconds", 10f, new ConfigDescription("Maximum retry time for applying a random loadout.ランダム衣装適用の最大再試行秒数です。", (AcceptableValueBase)(object)new AcceptableValueRange(1f, 60f), Array.Empty())); } 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(d__91))] private IEnumerator ApplyWhenReadyCoroutine(int sequence, string reason) { //yield-return decompiler failed: Unexpected instruction in Iterator.Dispose() return new 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 cosmeticEquipped = GetCosmeticEquipped(metaInstance); int[] colorsEquipped = GetColorsEquipped(metaInstance); if (cosmeticAssets == null || cosmeticEquipped == null) { return false; } if (!randomLoadoutActive) { baseEquippedSnapshot = new List(cosmeticEquipped); baseColorsSnapshot = CopyColors(colorsEquipped); } Dictionary> dictionary = BuildCandidatesByType(metaInstance, cosmeticAssets, cosmeticUnlocks); List 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 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> BuildCandidatesByType(object meta, IList cosmeticAssets, IList unlocks) { Dictionary> dictionary = new Dictionary>(); 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()); } value.Add(obj); } } return dictionary; } private static List GetAllCosmeticTypes(object meta, Dictionary> candidatesByType) { List list = new List(); 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 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 cosmeticEquipped = GetCosmeticEquipped(meta); if (cosmeticEquipped == null) { return; } List list = new List(); 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 GetCosmeticEquipped(object meta) { if (!(GetFieldValue(FieldMetaCosmeticEquipped, meta) is IList list)) { return null; } List list2 = new List(); for (int i = 0; i < list.Count; i++) { list2.Add(ConvertToInt(list[i], -1)); } return list2; } private static void SetCosmeticEquipped(object meta, List 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 cosmeticEquipped = GetCosmeticEquipped(meta); int[] colorsEquipped = GetColorsEquipped(meta); if (cosmeticEquipped == null || colorsEquipped == null) { return; } List list = new List(); 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 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 cosmeticEquipped = GetCosmeticEquipped(__instance); int[] colorsEquipped = GetColorsEquipped(__instance); if (cosmeticEquipped != null) { __state.Restore = true; __state.RuntimeEquipped = new List(cosmeticEquipped); __state.RuntimeColors = CopyColors(colorsEquipped); saveGuardActive = true; SetCosmeticEquipped(__instance, new List(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; } }