Decompiled source of RandomCosmetic v4.0.0

RandomCosmetic.dll

Decompiled 19 hours ago
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<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;
	}
}