Decompiled source of RepoGamblingConfigure v0.2.0

RepoGamblingConfigure.dll

Decompiled 4 days ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using MenuLib;
using MenuLib.MonoBehaviors;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using REPOGambling;
using REPOLib.Modules;
using TMPro;
using UnityEngine;
using UnityEngine.Events;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyVersion("0.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace repo_gambling_configure_nezzy
{
	[HarmonyPatch(typeof(EnvironmentPlacementPatch), "ApplyUpgradeDirect")]
	internal static class ApplyUpgradeDirectPatch
	{
		private static bool Prefix(int upgradeIndex, string steamID)
		{
			if (upgradeIndex >= 8)
			{
				return !UpgradeDropConfig.Apply(upgradeIndex, steamID);
			}
			return true;
		}
	}
	[HarmonyPatch]
	internal static class RepoConfigMenuPatch
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static ScrollViewBuilderDelegate <>9__2_0;

			internal RectTransform <AddSectionSpacer>b__2_0(Transform parent)
			{
				//IL_0014: Unknown result type (might be due to invalid IL or missing references)
				//IL_001a: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Unknown result type (might be due to invalid IL or missing references)
				Vector2 val = default(Vector2);
				((Vector2)(ref val))..ctor(0f, 30f);
				return ((REPOElement)MenuAPI.CreateREPOSpacer(parent, default(Vector2), val)).rectTransform;
			}
		}

		private static MethodBase? TargetMethod()
		{
			return AccessTools.Method("REPOConfig.ConfigMenu:CreateModEntries", new Type[2]
			{
				typeof(REPOPopupPage),
				typeof(ConfigEntryBase[])
			}, (Type[])null);
		}

		private static bool Prefix(REPOPopupPage modPage, ConfigEntryBase[] configEntryBases)
		{
			bool flag = false;
			foreach (ConfigEntryBase val in configEntryBases)
			{
				if (val.Definition.Section == "QOL" && val.Definition.Key == "Remove Player Count Repeat")
				{
					flag = true;
					break;
				}
			}
			if (!flag)
			{
				return true;
			}
			AddHeader(modPage, "QOL");
			AddToggle(modPage, "Remove Player\nCount Repeat", UpgradeDropConfig.SetRemovePlayerCountRepeat, UpgradeDropConfig.RemovePlayerCountRepeat);
			AddToggle(modPage, "Replace Range At\nMax Range Upgrade", UpgradeDropConfig.SetRemoveRangeAtTen, UpgradeDropConfig.RemoveRangeAtTen);
			AddToggle(modPage, "Try To Spawn Shop\nSecret (Host Controlled)", UpgradeDropConfig.SetTrySpawnShopSecret, UpgradeDropConfig.TrySpawnShopSecret);
			AddSectionSpacer(modPage);
			AddUpgrades(modPage, "Yellow Upgrade");
			AddUpgrades(modPage, "Orange Upgrade");
			AddUpgrades(modPage, "Blue Upgrade");
			AddUpgrades(modPage, "Green Upgrade");
			AddUpgrades(modPage, "Death");
			AddUpgrades(modPage, "Custom");
			AddHeader(modPage, "Developer");
			AddToggle(modPage, "Extended Logging", UpgradeDropConfig.SetExtendedLogging, UpgradeDropConfig.ExtendedLogging);
			UpgradeDropConfig.LogDetailed("Configure menu section spacing applied (30 px).");
			return false;
		}

		private static void AddSectionSpacer(REPOPopupPage page)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Expected O, but got Unknown
			object obj = <>c.<>9__2_0;
			if (obj == null)
			{
				ScrollViewBuilderDelegate val = delegate(Transform parent)
				{
					//IL_0014: Unknown result type (might be due to invalid IL or missing references)
					//IL_001a: Unknown result type (might be due to invalid IL or missing references)
					//IL_001b: Unknown result type (might be due to invalid IL or missing references)
					Vector2 val2 = default(Vector2);
					((Vector2)(ref val2))..ctor(0f, 30f);
					return ((REPOElement)MenuAPI.CreateREPOSpacer(parent, default(Vector2), val2)).rectTransform;
				};
				<>c.<>9__2_0 = val;
				obj = (object)val;
			}
			page.AddElementToScrollView((ScrollViewBuilderDelegate)obj, 0f, 0f);
		}

		private static void AddHeader(REPOPopupPage page, string text)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected O, but got Unknown
			page.AddElementToScrollView((ScrollViewBuilderDelegate)((Transform parent) => (RectTransform)((Component)MenuAPI.CreateREPOLabel(text, parent, default(Vector2))).transform), 0f, 4f);
		}

		private static void AddUpgrades(REPOPopupPage page, string section)
		{
			AddHeader(page, section);
			for (int i = 0; i < UpgradeDropConfig.Upgrades.Count; i++)
			{
				UpgradeDropConfig.Upgrade upgrade = UpgradeDropConfig.Upgrades[i];
				if (upgrade.Section == section)
				{
					int index = i;
					AddToggle(page, upgrade.Label, delegate(bool enabled)
					{
						UpgradeDropConfig.SetAllowed(index, enabled);
					}, UpgradeDropConfig.IsAllowed(index));
				}
			}
			AddSectionSpacer(page);
		}

		private static void AddToggle(REPOPopupPage page, string text, Action<bool> onToggle, bool value)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Expected O, but got Unknown
			page.AddElementToScrollView((ScrollViewBuilderDelegate)delegate(Transform parent)
			{
				//IL_0018: Unknown result type (might be due to invalid IL or missing references)
				//IL_001e: Unknown result type (might be due to invalid IL or missing references)
				//IL_0034: Unknown result type (might be due to invalid IL or missing references)
				//IL_003a: Expected O, but got Unknown
				string text2 = text;
				Action<bool> action = onToggle;
				bool flag = value;
				return (RectTransform)((Component)MenuAPI.CreateREPOToggle(text2, action, parent, default(Vector2), "ON", "OFF", flag)).transform;
			}, 0f, 2f);
		}
	}
	[BepInPlugin("nezzy.RepoGamblingConfigure", "REPO Gambling Configure", "0.2.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class RepoGamblingConfigurePlugin : BaseUnityPlugin
	{
		internal static RepoGamblingConfigurePlugin Instance { get; private set; }

		internal static ManualLogSource Logger => Instance._logger;

		private ManualLogSource _logger => ((BaseUnityPlugin)this).Logger;

		private void Awake()
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			Instance = this;
			((Component)this).gameObject.transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID).PatchAll();
			Logger.LogInfo((object)$"{((BaseUnityPlugin)this).Info.Metadata.GUID} v{((BaseUnityPlugin)this).Info.Metadata.Version} loaded.");
		}

		private void Start()
		{
			UpgradeDropConfig.Bind(((BaseUnityPlugin)this).Config);
			UpgradeDropRollTranspiler.LogPatchCounts();
		}
	}
	[HarmonyPatch(typeof(WheelMachineRPCHandler), "RPC_ApplyUpgrade")]
	internal static class RpcApplyUpgradePatch
	{
		private static bool Prefix(int upgradeIndex, string steamID)
		{
			if (upgradeIndex >= 8)
			{
				return !UpgradeDropConfig.Apply(upgradeIndex, steamID);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(LevelGenerator), "Start")]
	internal static class ShopDeadEndCapturePatch
	{
		internal static List<PrefabRef>? OriginalDeadEnds;

		internal static PrefabRef? GamblingRoom;

		[HarmonyPrefix]
		[HarmonyPriority(800)]
		private static void CaptureOriginalDeadEnds()
		{
			OriginalDeadEnds = null;
			GamblingRoom = null;
			if ((Object)(object)RunManager.instance != (Object)null && RunManager.instance.levelCurrent.NarrativeName == "Service Station")
			{
				OriginalDeadEnds = new List<PrefabRef>(RunManager.instance.levelCurrent.ModulesDeadEnd1);
			}
		}
	}
	[HarmonyPatch(typeof(LevelGenerator), "Start")]
	internal static class ShopGamblingRoomPatch
	{
		[HarmonyPrefix]
		[HarmonyPriority(0)]
		private static void GuaranteeGamblingRoom()
		{
			List<PrefabRef> original = ShopDeadEndCapturePatch.OriginalDeadEnds;
			if (original == null || (Object)(object)RunManager.instance == (Object)null || RunManager.instance.levelCurrent.NarrativeName != "Service Station")
			{
				return;
			}
			List<PrefabRef> modulesDeadEnd = RunManager.instance.levelCurrent.ModulesDeadEnd1;
			PrefabRef val = ((IEnumerable<PrefabRef>)modulesDeadEnd).FirstOrDefault((Func<PrefabRef, bool>)((PrefabRef prefab) => !original.Any((PrefabRef originalPrefab) => ((PrefabRef<GameObject>)(object)originalPrefab).ResourcePath == ((PrefabRef<GameObject>)(object)prefab).ResourcePath)));
			string text = "REPOGambling default selection";
			if (val == null)
			{
				REPOGambling val2 = Object.FindObjectOfType<REPOGambling>();
				if ((Object)(object)val2 == (Object)null)
				{
					RepoGamblingConfigurePlugin.Logger.LogWarning((object)"Guaranteed Gambling room skipped: REPOGambling was unavailable.");
					return;
				}
				List<GameObject> list = val2.LoadModulePrefabsFromBundle();
				if (list.Count == 0)
				{
					RepoGamblingConfigurePlugin.Logger.LogWarning((object)"Guaranteed Gambling room skipped: no Gambling room prefab was available.");
					return;
				}
				GameObject val3 = list[Random.Range(0, list.Count)];
				val = NetworkPrefabs.RegisterNetworkPrefab("Level/Shop/Modules/" + ((Object)val3).name, val3);
				text = "guarantee fallback";
			}
			modulesDeadEnd.Clear();
			modulesDeadEnd.Add(val);
			ShopDeadEndCapturePatch.GamblingRoom = val;
			UpgradeDropConfig.LogShopInfo("Guaranteed Gambling room selected via " + text + ": " + ((PrefabRef<GameObject>)(object)val).ResourcePath);
		}
	}
	[HarmonyPatch(typeof(LevelGenerator), "GenerateConnectObjects")]
	internal static class ShopSecretPlacementPatch
	{
		private static readonly FieldInfo? LevelGrid = AccessTools.Field(typeof(LevelGenerator), "LevelGrid");

		[HarmonyPrefix]
		private static void TryAddSecretDeadEnd(LevelGenerator __instance)
		{
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			List<PrefabRef> originalDeadEnds = ShopDeadEndCapturePatch.OriginalDeadEnds;
			if (!UpgradeDropConfig.TrySpawnShopSecret || originalDeadEnds == null || originalDeadEnds.Count == 0 || ShopDeadEndCapturePatch.GamblingRoom == null || !SemiFunc.IsMasterClientOrSingleplayer())
			{
				return;
			}
			if (!(LevelGrid?.GetValue(__instance) is Tile[,] array))
			{
				UpgradeDropConfig.LogShopInfo("Shop secret skipped: the store grid was unavailable.");
				return;
			}
			List<Tile> list = new List<Tile>();
			for (int i = 0; i < array.GetLength(0); i++)
			{
				for (int j = 0; j < array.GetLength(1); j++)
				{
					Tile val = array[i, j];
					if (!val.active && HasOneOpenConnection(array, i, j) && !TouchesReservedTile(array, i, j))
					{
						list.Add(val);
					}
				}
			}
			if (list.Count == 0)
			{
				UpgradeDropConfig.LogShopInfo("Shop secret skipped: no eligible second dead-end socket.");
				return;
			}
			Tile obj = list[Random.Range(0, list.Count)];
			obj.active = true;
			obj.type = (Type)2;
			PrefabRef val2 = originalDeadEnds[Random.Range(0, originalDeadEnds.Count)];
			RunManager.instance.levelCurrent.ModulesDeadEnd1.Add(val2);
			UpgradeDropConfig.LogShopInfo("Shop secret spawned: " + ((PrefabRef<GameObject>)(object)val2).ResourcePath);
		}

		private static bool HasOneOpenConnection(Tile[,] grid, int x, int y)
		{
			int num = 0;
			(int, int)[] array = new(int, int)[4]
			{
				(x - 1, y),
				(x + 1, y),
				(x, y - 1),
				(x, y + 1)
			};
			for (int i = 0; i < array.Length; i++)
			{
				var (num2, num3) = array[i];
				if (num2 >= 0 && num2 < grid.GetLength(0) && num3 >= 0 && num3 < grid.GetLength(1) && grid[num2, num3].active)
				{
					num++;
				}
			}
			return num == 1;
		}

		private static bool TouchesReservedTile(Tile[,] grid, int x, int y)
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Invalid comparison between Unknown and I4
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Invalid comparison between Unknown and I4
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Invalid comparison between Unknown and I4
			(int, int)[] array = new(int, int)[4]
			{
				(x - 1, y),
				(x + 1, y),
				(x, y - 1),
				(x, y + 1)
			};
			for (int i = 0; i < array.Length; i++)
			{
				var (num, num2) = array[i];
				if (num >= 0 && num < grid.GetLength(0) && num2 >= 0 && num2 < grid.GetLength(1))
				{
					Type type = grid[num, num2].type;
					if ((int)type == 2 || (int)type == 3 || (int)type == 4)
					{
						return true;
					}
				}
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(EnvironmentPlacementPatch), "SetupSlotMachines")]
	internal static class SlotBetTextFontPatch
	{
		[HarmonyPostfix]
		private static void RepairBetTextFont()
		{
			GameObject[] array = Object.FindObjectsByType<GameObject>((FindObjectsSortMode)0);
			foreach (GameObject val in array)
			{
				if (!((Object)val).name.Contains("Slot Machine"))
				{
					continue;
				}
				Transform val2 = val.transform.Find("Bet Text");
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				TextMeshPro component = ((Component)val2).GetComponent<TextMeshPro>();
				if ((Object)(object)component == (Object)null)
				{
					continue;
				}
				TMP_FontAsset font = ((TMP_Text)component).font;
				Material fontSharedMaterial = ((TMP_Text)component).fontSharedMaterial;
				Texture val3 = (Texture)(object)(((Object)(object)font != (Object)null) ? font.atlasTexture : null);
				TMP_FontAsset defaultFontAsset = TMP_Settings.defaultFontAsset;
				string[] obj = new string[13]
				{
					"Slot bet text before font repair: font=",
					((font != null) ? ((Object)font).name : null) ?? "null",
					", material=",
					((fontSharedMaterial != null) ? ((Object)fontSharedMaterial).name : null) ?? "null",
					", shader=",
					null,
					null,
					null,
					null,
					null,
					null,
					null,
					null
				};
				object obj2;
				if (fontSharedMaterial == null)
				{
					obj2 = null;
				}
				else
				{
					Shader shader = fontSharedMaterial.shader;
					obj2 = ((shader != null) ? ((Object)shader).name : null);
				}
				if (obj2 == null)
				{
					obj2 = "null";
				}
				obj[5] = (string)obj2;
				obj[6] = ", ";
				bool? obj3;
				if (fontSharedMaterial == null)
				{
					obj3 = null;
				}
				else
				{
					Shader shader2 = fontSharedMaterial.shader;
					obj3 = ((shader2 != null) ? new bool?(shader2.isSupported) : ((bool?)null));
				}
				obj[7] = string.Format("supported={0}, atlas={1}, ", obj3, ((val3 != null) ? ((Object)val3).name : null) ?? "null");
				obj[8] = string.Format("size={0}x{1}, gameFont={2}, ", (val3 != null) ? new int?(val3.width) : ((int?)null), (val3 != null) ? new int?(val3.height) : ((int?)null), ((defaultFontAsset != null) ? ((Object)defaultFontAsset).name : null) ?? "null");
				obj[9] = "gameShader=";
				object obj4;
				if (defaultFontAsset == null)
				{
					obj4 = null;
				}
				else
				{
					Material material = ((TMP_Asset)defaultFontAsset).material;
					if (material == null)
					{
						obj4 = null;
					}
					else
					{
						Shader shader3 = material.shader;
						obj4 = ((shader3 != null) ? ((Object)shader3).name : null);
					}
				}
				if (obj4 == null)
				{
					obj4 = "null";
				}
				obj[10] = (string)obj4;
				obj[11] = ", text=";
				obj[12] = ((TMP_Text)component).text;
				UpgradeDropConfig.LogDetailed(string.Concat(obj));
				if ((Object)(object)defaultFontAsset == (Object)null || (Object)(object)((TMP_Asset)defaultFontAsset).material == (Object)null || !defaultFontAsset.HasCharacter('$', false, false))
				{
					RepoGamblingConfigurePlugin.Logger.LogError((object)"Slot bet text font repair failed: the game font, material, or dollar-sign glyph is unavailable.");
					continue;
				}
				((TMP_Text)component).font = defaultFontAsset;
				((TMP_Text)component).fontSharedMaterial = ((TMP_Asset)defaultFontAsset).material;
				((TMP_Text)component).ForceMeshUpdate(false, false);
				UpgradeDropConfig.LogDetailed("Slot bet text font replaced: font=" + ((Object)((TMP_Text)component).font).name + ", shader=" + ((Object)((TMP_Text)component).fontSharedMaterial.shader).name + ", atlas=" + ((Object)((TMP_Text)component).font.atlasTexture).name + ", " + $"characters={((TMP_Text)component).textInfo.characterCount}.");
			}
		}
	}
	[HarmonyPatch(typeof(EnvironmentPlacementPatch), "SpinAndStop")]
	internal static class SlotSpinPatch
	{
		private static readonly PropertyInfo GamblingInstance = AccessTools.Property(typeof(REPOGambling), "Instance") ?? throw new MissingMemberException(typeof(REPOGambling).FullName, "Instance");

		private static readonly MethodInfo SmoothSpinRoll = AccessTools.Method(typeof(EnvironmentPlacementPatch), "SmoothSpinRoll", new Type[3]
		{
			typeof(Transform),
			typeof(float),
			typeof(float)
		}, (Type[])null) ?? throw new MissingMethodException(typeof(EnvironmentPlacementPatch).FullName, "SmoothSpinRoll");

		private static readonly MethodInfo SpecialPrize = AccessTools.Method(typeof(EnvironmentPlacementPatch), "SpecialPrize", new Type[1] { typeof(Transform) }, (Type[])null) ?? throw new MissingMethodException(typeof(EnvironmentPlacementPatch).FullName, "SpecialPrize");

		private static bool Prefix(Transform[] rolls, int[] results, float[] fruitAngles, string[] fruitNames, AudioSource audio, SlotMachineState state, Transform nadeOrigin, ref IEnumerator __result)
		{
			__result = SpinAndStopCompatible(rolls, results, fruitAngles, fruitNames, audio, state, nadeOrigin);
			return false;
		}

		private static IEnumerator SpinAndStopCompatible(Transform[] rolls, int[] results, float[] fruitAngles, string[] fruitNames, AudioSource audio, SlotMachineState state, Transform nadeOrigin)
		{
			object? value = GamblingInstance.GetValue(null);
			REPOGambling gambling = (REPOGambling)(((value is REPOGambling) ? value : null) ?? throw new InvalidOperationException("REPOGambling instance was unavailable for slot spin."));
			float elapsed = 0f;
			bool specialCombo = results.Count((int r) => fruitNames[r] == "Cherry") == 1 && results.Count((int r) => fruitNames[r] == "Spade") == 2;
			UpgradeDropConfig.LogDetailed("Sunstone slot spin coroutine started.");
			PlaySlotSound(rolls, audio, gambling, (GamblingSound)0, 0);
			while (elapsed < 6.38f)
			{
				foreach (Transform val in rolls)
				{
					if ((Object)(object)val != (Object)null)
					{
						float num2 = Random.Range(0f, 360f);
						val.localEulerAngles = new Vector3(num2, val.localEulerAngles.y, val.localEulerAngles.z);
					}
				}
				elapsed += Time.deltaTime;
				yield return null;
			}
			IEnumerator[] array = new IEnumerator[rolls.Length];
			for (int num3 = 0; num3 < rolls.Length; num3++)
			{
				if ((Object)(object)rolls[num3] != (Object)null && results[num3] != -1)
				{
					array[num3] = (SmoothSpinRoll.Invoke(null, new object[3]
					{
						rolls[num3],
						fruitAngles[results[num3]],
						0.1f
					}) as IEnumerator) ?? throw new InvalidOperationException("Base SmoothSpinRoll did not return a coroutine.");
				}
			}
			IEnumerator[] array2 = array;
			foreach (IEnumerator enumerator in array2)
			{
				if (enumerator != null)
				{
					((MonoBehaviour)gambling).StartCoroutine(enumerator);
				}
			}
			yield return (object)new WaitForSeconds(0.1f);
			bool num4 = results.All((int r) => r == results[0]) && results[0] != -1;
			bool flag = num4 && fruitNames[results[0]] == "7";
			int num5 = Mathf.RoundToInt((num4 ? MatchMultiplier(gambling, fruitNames[results[0]]) : 0f) * (float)gambling.slotBet);
			if (specialCombo)
			{
				SpecialPrize.Invoke(null, new object[1] { nadeOrigin });
			}
			if (num5 > 0)
			{
				SemiFunc.StatSetRunCurrency(SemiFunc.StatGetRunCurrency() + num5);
				if (flag)
				{
					ChatManager.instance.PossessChatScheduleStart(-1);
					ChatManager.instance.PossessChat((PossessChatID)3, $"Jackpot {num5}k from the slots!", 2f, Color.yellow, 0f, true, 1, (UnityEvent)null, (string)null);
					ChatManager.instance.PossessChatScheduleEnd();
					PlaySlotSound(rolls, audio, gambling, (GamblingSound)5, 5);
				}
				else
				{
					PlaySlotSound(rolls, audio, gambling, (GamblingSound)3, 3);
				}
			}
			else
			{
				PlaySlotSound(rolls, audio, gambling, (GamblingSound)4, 4);
			}
			state.IsSpinning = false;
			UpgradeDropConfig.LogDetailed($"Sunstone slot spin completed: payout={num5}, jackpot={flag}, special={specialCombo}.");
		}

		private static float MatchMultiplier(REPOGambling gambling, string fruit)
		{
			return fruit switch
			{
				"7" => gambling.slotRewardJackpotMultiplier.Value, 
				"Cherry" => gambling.slotRewardCherryMatchMultiplier.Value, 
				"Strawberry" => gambling.slotRewardBerryMatchMultiplier.Value, 
				"Spade" => gambling.slotRewardSpadeMatchMultiplier.Value, 
				"Clove" => gambling.slotRewardCloveMatchMultiplier.Value, 
				_ => 0f, 
			};
		}

		private static void PlaySlotSound(Transform[] rolls, AudioSource audio, REPOGambling gambling, GamblingSound sound, int rpcSound)
		{
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			if (SemiFunc.IsMultiplayer())
			{
				((Component)rolls[0].parent).GetComponent<PhotonView>().RPC("RPC_PlaySound", (RpcTarget)0, new object[1] { rpcSound });
			}
			else if ((Object)(object)audio != (Object)null)
			{
				audio.PlayOneShot(gambling.GetClip(sound));
			}
		}
	}
	internal static class UpgradeAnnouncePatch
	{
		private static readonly FieldInfo IsDisabledField = AccessTools.Field(typeof(PlayerAvatar), "isDisabled") ?? throw new MissingFieldException(typeof(PlayerAvatar).FullName, "isDisabled");

		internal static void Announce(int index, PlayerAvatar avatar)
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)avatar == (Object)null)
			{
				return;
			}
			object value = IsDisabledField.GetValue(avatar);
			if ((value is bool && (bool)value) || index < 0 || index >= UpgradeDropConfig.Upgrades.Count)
			{
				return;
			}
			ChatManager instance = ChatManager.instance;
			if ((Object)(object)instance == (Object)null)
			{
				return;
			}
			try
			{
				string text = ((Random.value > 0.5f) ? "Upgraded" : "Just what I wanted, a");
				instance.PossessChatScheduleStart(-1);
				instance.PossessChat((PossessChatID)3, text + " " + UpgradeDropConfig.Upgrades[index].Label, 2f, Color.blue, 0f, false, 0, (UnityEvent)null, (string)null);
				instance.PossessChatScheduleEnd();
				UpgradeDropConfig.LogDetailed("Sunstone upgrade announcement returned successfully.");
			}
			catch (Exception arg)
			{
				RepoGamblingConfigurePlugin.Logger.LogWarning((object)$"Failed to show upgrade announcement: {arg}");
			}
		}
	}
	internal static class UpgradeDropConfig
	{
		internal sealed class Upgrade
		{
			internal string Key { get; }

			internal string Label { get; }

			internal string Section { get; }

			internal Action<string> Apply { get; }

			internal Upgrade(string key, string label, string section, Action<string> apply)
			{
				Key = key;
				Label = label;
				Section = section;
				Apply = apply;
			}
		}

		internal static readonly List<Upgrade> Upgrades = new List<Upgrade>
		{
			new Upgrade("MapPlayerCount", "Map Player Count", "Green Upgrade", delegate(string steamId)
			{
				PunManager.instance.UpgradeMapPlayerCount(steamId, 1);
			}),
			new Upgrade("PlayerStamina", "Player Stamina", "Yellow Upgrade", delegate(string steamId)
			{
				PunManager.instance.UpgradePlayerEnergy(steamId, 1);
			}),
			new Upgrade("ExtraJump", "Extra Jump", "Blue Upgrade", delegate(string steamId)
			{
				PunManager.instance.UpgradePlayerExtraJump(steamId, 1);
			}),
			new Upgrade("GrabRange", "Grab Range", "Orange Upgrade", delegate(string steamId)
			{
				PunManager.instance.UpgradePlayerGrabRange(steamId, 1);
			}),
			new Upgrade("GrabStrength", "Grab Strength", "Orange Upgrade", delegate(string steamId)
			{
				PunManager.instance.UpgradePlayerGrabStrength(steamId, 1);
			}),
			new Upgrade("Health", "Health", "Green Upgrade", delegate(string steamId)
			{
				PunManager.instance.UpgradePlayerHealth(steamId, 1);
			}),
			new Upgrade("SprintSpeed", "Sprint Speed", "Yellow Upgrade", delegate(string steamId)
			{
				PunManager.instance.UpgradePlayerSprintSpeed(steamId, 1);
			}),
			new Upgrade("TumbleLaunch", "Tumble Launch", "Blue Upgrade", delegate(string steamId)
			{
				PunManager.instance.UpgradePlayerTumbleLaunch(steamId, 1);
			}),
			new Upgrade("TumbleClimb", "Tumble Climb", "Blue Upgrade", delegate(string steamId)
			{
				PunManager.instance.UpgradePlayerTumbleClimb(steamId, 1);
			}),
			new Upgrade("DeathHeadBattery", "Death Head Battery", "Death", delegate(string steamId)
			{
				PunManager.instance.UpgradeDeathHeadBattery(steamId, 1);
			}),
			new Upgrade("TumbleWings", "Tumble Wings", "Blue Upgrade", delegate(string steamId)
			{
				PunManager.instance.UpgradePlayerTumbleWings(steamId, 1);
			}),
			new Upgrade("CrouchRest", "Crouch Rest", "Yellow Upgrade", delegate(string steamId)
			{
				PunManager.instance.UpgradePlayerCrouchRest(steamId, 1);
			})
		};

		private static readonly Dictionary<string, ConfigEntry<bool>> Entries = new Dictionary<string, ConfigEntry<bool>>();

		private static readonly FieldInfo SteamIdField = AccessTools.Field(typeof(PlayerAvatar), "steamID") ?? throw new MissingFieldException(typeof(PlayerAvatar).FullName, "steamID");

		private static ConfigEntry<bool> removePlayerCountRepeat = null;

		private static ConfigEntry<bool> removeRangeAtTen = null;

		private static ConfigEntry<bool> trySpawnShopSecret = null;

		private static ConfigEntry<bool> extendedLogging = null;

		internal static bool RemovePlayerCountRepeat => removePlayerCountRepeat.Value;

		internal static bool RemoveRangeAtTen => removeRangeAtTen.Value;

		internal static bool TrySpawnShopSecret => trySpawnShopSecret.Value;

		internal static bool ExtendedLogging => extendedLogging?.Value ?? false;

		internal static void BindLogging(ConfigFile config)
		{
			extendedLogging = config.Bind<bool>("Developer", "Extended Shop Logging", false, "Log detailed Configure patch, wheel, slot, font, and Shop events.");
		}

		internal static void Bind(ConfigFile config)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Expected O, but got Unknown
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Expected O, but got Unknown
			//IL_01f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0200: Expected O, but got Unknown
			removePlayerCountRepeat = config.Bind<bool>("QOL", "Remove Player Count Repeat", true, "Remove Map Player Count when the player already has it.");
			ConfigDefinition val = new ConfigDefinition("QOL", "Remove Range At 10");
			bool flag = true;
			ConfigEntry<bool> val2 = null;
			if (config.TryGetEntry<bool>(val, ref val2))
			{
				flag = val2.Value;
			}
			config.Remove(val);
			removeRangeAtTen = config.Bind<bool>("QOL", "Replace Range At Max Range Upgrade", flag, "Remove Grab Range when the player has more than 9 levels.");
			trySpawnShopSecret = config.Bind<bool>("QOL", "Try To Spawn Shop Secret (Host Controlled)", true, "Try to add a second Shop dead-end for the original secret-room pool. The host controls this setting.");
			foreach (PlayerUpgrade upgrade in Upgrades.PlayerUpgrades)
			{
				string key = "REPOLib." + upgrade.UpgradeId;
				if (!Upgrades.Exists((Upgrade existing) => existing.Key == key))
				{
					Upgrades.Add(new Upgrade(key, upgrade.UpgradeId, "Custom", delegate(string steamId)
					{
						upgrade.AddLevel(steamId, 1);
					}));
				}
			}
			string[] obj = new string[6] { "Yellow Upgrade", "Orange Upgrade", "Blue Upgrade", "Green Upgrade", "Death", "Custom" };
			ConfigEntry<bool> val3 = null;
			ConfigEntry<bool> val4 = null;
			string[] array = obj;
			foreach (string text in array)
			{
				foreach (Upgrade upgrade2 in Upgrades)
				{
					if (!(upgrade2.Section != text))
					{
						ConfigDefinition val5 = new ConfigDefinition("Upgrade Drops", upgrade2.Key);
						bool flag2 = true;
						if (config.TryGetEntry<bool>(val5, ref val3))
						{
							flag2 = val3.Value;
						}
						config.Remove(val5);
						ConfigDefinition val6 = new ConfigDefinition(upgrade2.Section.Replace(" Upgrade", string.Empty), upgrade2.Key);
						if (config.TryGetEntry<bool>(val6, ref val4))
						{
							flag2 = val4.Value;
						}
						config.Remove(val6);
						Entries[upgrade2.Key] = config.Bind<bool>(upgrade2.Section, upgrade2.Key, flag2, "Allow REPOGambling to award " + upgrade2.Label + ".");
					}
				}
			}
			BindLogging(config);
			LogDetailed($"Configured {Upgrades.Count} upgrade drops ({Upgrades.Count - 12} REPOLib custom).");
		}

		internal static bool IsAllowed(int index)
		{
			if (index >= 0 && index < Upgrades.Count)
			{
				return Entries[Upgrades[index].Key].Value;
			}
			return false;
		}

		internal static void SetAllowed(int index, bool allowed)
		{
			if (index >= 0 && index < Upgrades.Count)
			{
				Entries[Upgrades[index].Key].Value = allowed;
				((BaseUnityPlugin)RepoGamblingConfigurePlugin.Instance).Config.Save();
			}
		}

		internal static int PickUpgradeIndex(PlayerAvatar avatar)
		{
			string text = SteamIdField.GetValue(avatar) as string;
			if (string.IsNullOrEmpty(text))
			{
				throw new InvalidOperationException("PlayerAvatar steamID was unavailable.");
			}
			List<int> list = new List<int>();
			for (int i = 0; i < Upgrades.Count; i++)
			{
				if (IsAllowed(i) && IsEligible(Upgrades[i], text))
				{
					list.Add(i);
				}
			}
			if (list.Count == 0)
			{
				RepoGamblingConfigurePlugin.Logger.LogWarning((object)"All eligible upgrade drops disabled; falling back to eligible upgrades.");
				for (int j = 0; j < Upgrades.Count; j++)
				{
					if (IsEligible(Upgrades[j], text))
					{
						list.Add(j);
					}
				}
			}
			return list[Random.Range(0, list.Count)];
		}

		private static bool IsEligible(Upgrade upgrade, string steamId)
		{
			if ((Object)(object)StatsManager.instance == (Object)null)
			{
				return true;
			}
			if (upgrade.Key == "MapPlayerCount" && removePlayerCountRepeat.Value && StatsManager.instance.playerUpgradeMapPlayerCount.TryGetValue(steamId, out var value) && value > 0)
			{
				return false;
			}
			if (!(upgrade.Key != "GrabRange") && removeRangeAtTen.Value && StatsManager.instance.playerUpgradeRange.TryGetValue(steamId, out var value2))
			{
				return value2 <= 9;
			}
			return true;
		}

		internal static void SetRemovePlayerCountRepeat(bool enabled)
		{
			removePlayerCountRepeat.Value = enabled;
			((BaseUnityPlugin)RepoGamblingConfigurePlugin.Instance).Config.Save();
		}

		internal static void SetRemoveRangeAtTen(bool enabled)
		{
			removeRangeAtTen.Value = enabled;
			((BaseUnityPlugin)RepoGamblingConfigurePlugin.Instance).Config.Save();
		}

		internal static void SetTrySpawnShopSecret(bool enabled)
		{
			trySpawnShopSecret.Value = enabled;
			((BaseUnityPlugin)RepoGamblingConfigurePlugin.Instance).Config.Save();
		}

		internal static void SetExtendedLogging(bool enabled)
		{
			extendedLogging.Value = enabled;
			((BaseUnityPlugin)RepoGamblingConfigurePlugin.Instance).Config.Save();
		}

		internal static void LogShopInfo(string message)
		{
			LogDetailed(message);
		}

		internal static void LogDetailed(string message)
		{
			if (ExtendedLogging)
			{
				RepoGamblingConfigurePlugin.Logger.LogInfo((object)message);
			}
		}

		internal static bool Apply(int index, string steamId)
		{
			if (index < 0 || index >= Upgrades.Count)
			{
				RepoGamblingConfigurePlugin.Logger.LogWarning((object)$"Invalid upgrade index: {index}");
				return false;
			}
			Upgrades[index].Apply(steamId);
			return true;
		}
	}
	[HarmonyPatch(typeof(EnvironmentPlacementPatch), "HandleWheelPrize")]
	internal static class UpgradeDropRollTranspiler
	{
		private static int upgradeRollCount;

		private static int announcementCount;

		private static int wheelDeathCount;

		private static readonly MethodInfo RandomRangeInt = AccessTools.Method(typeof(Random), "Range", new Type[2]
		{
			typeof(int),
			typeof(int)
		}, (Type[])null);

		private static readonly MethodInfo PickUpgradeIndex = AccessTools.Method(typeof(UpgradeDropConfig), "PickUpgradeIndex", new Type[1] { typeof(PlayerAvatar) }, (Type[])null);

		private static readonly MethodInfo UpgradeAnnounce = AccessTools.Method(typeof(EnvironmentPlacementPatch), "UpgradeAnnounce", new Type[2]
		{
			typeof(int),
			typeof(PlayerAvatar)
		}, (Type[])null);

		private static readonly MethodInfo CompatibleAnnounce = AccessTools.Method(typeof(UpgradeAnnouncePatch), "Announce", new Type[2]
		{
			typeof(int),
			typeof(PlayerAvatar)
		}, (Type[])null);

		private static readonly MethodInfo WheelSelfDestruction = AccessTools.Method(typeof(EnvironmentPlacementPatch), "WheelSelfDestruction", new Type[1] { typeof(PlayerAvatar) }, (Type[])null);

		private static readonly MethodInfo CompatibleWheelDeath = AccessTools.Method(typeof(WheelDeathPatch), "AnnounceAndKill", new Type[1] { typeof(PlayerAvatar) }, (Type[])null);

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = instructions.ToList();
			int num = 0;
			for (int i = 0; i <= list.Count - 3; i++)
			{
				if (CodeInstructionExtensions.LoadsConstant(list[i], 0L) && CodeInstructionExtensions.LoadsConstant(list[i + 1], 8L) && CodeInstructionExtensions.Calls(list[i + 2], RandomRangeInt))
				{
					list[i].opcode = OpCodes.Ldarg_3;
					list[i + 1].opcode = OpCodes.Nop;
					list[i + 1].operand = null;
					list[i + 2].opcode = OpCodes.Call;
					list[i + 2].operand = PickUpgradeIndex;
					num++;
				}
			}
			int num2 = 0;
			int num3 = 0;
			foreach (CodeInstruction item in list)
			{
				if (CodeInstructionExtensions.Calls(item, UpgradeAnnounce))
				{
					item.opcode = OpCodes.Call;
					item.operand = CompatibleAnnounce;
					num2++;
				}
				else if (CodeInstructionExtensions.Calls(item, WheelSelfDestruction))
				{
					item.opcode = OpCodes.Call;
					item.operand = CompatibleWheelDeath;
					num3++;
				}
			}
			if (num != 2 || num2 != 2 || num3 != 2)
			{
				throw new InvalidOperationException($"Unsupported REPOGambling 1.7.1 wheel IL: {num} upgrade rolls, {num2} announcement calls, {num3} wheel death calls.");
			}
			upgradeRollCount = num;
			announcementCount = num2;
			wheelDeathCount = num3;
			return list;
		}

		internal static void LogPatchCounts()
		{
			UpgradeDropConfig.LogDetailed($"Patched {upgradeRollCount} REPOGambling upgrade roll(s).");
			UpgradeDropConfig.LogDetailed($"Replaced {announcementCount} obsolete upgrade announcement call(s).");
			UpgradeDropConfig.LogDetailed($"Replaced {wheelDeathCount} wheel death call(s).");
		}

		private static void Postfix(PrizeEntry prize)
		{
			UpgradeDropConfig.LogDetailed("REPOGambling wheel prize handler returned: " + prize.name + ".");
		}
	}
	internal static class WheelDeathPatch
	{
		private static readonly FieldInfo IsDisabledField = AccessTools.Field(typeof(PlayerAvatar), "isDisabled") ?? throw new MissingFieldException(typeof(PlayerAvatar).FullName, "isDisabled");

		private static readonly PropertyInfo GamblingInstance = AccessTools.Property(typeof(REPOGambling), "Instance") ?? throw new MissingMemberException(typeof(REPOGambling).FullName, "Instance");

		private static readonly MethodInfo DelayedDeath = AccessTools.Method(typeof(EnvironmentPlacementPatch), "DelayedDeath", new Type[3]
		{
			typeof(PlayerAvatar),
			typeof(int),
			typeof(float)
		}, (Type[])null) ?? throw new MissingMethodException(typeof(EnvironmentPlacementPatch).FullName, "DelayedDeath");

		internal static void AnnounceAndKill(PlayerAvatar avatar)
		{
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)avatar == (Object)null)
			{
				return;
			}
			object value = IsDisabledField.GetValue(avatar);
			if (value is bool && (bool)value)
			{
				return;
			}
			ChatManager val = ChatManager.instance ?? throw new InvalidOperationException("ChatManager was unavailable for wheel death.");
			object? value2 = GamblingInstance.GetValue(null);
			REPOGambling val2 = (REPOGambling)(((value2 is REPOGambling) ? value2 : null) ?? throw new InvalidOperationException("REPOGambling instance was unavailable for wheel death."));
			if (val2.deathLines == null || val2.deathLines.Count == 0)
			{
				throw new InvalidOperationException("REPOGambling death lines were unavailable.");
			}
			string text = val2.deathLines[Random.Range(0, val2.deathLines.Count)];
			try
			{
				val.PossessChatScheduleStart(-1);
				val.PossessChat((PossessChatID)3, text, 2f, Color.red, 0f, true, 2, (UnityEvent)null, (string)null);
				val.PossessChatScheduleEnd();
				if (!(DelayedDeath.Invoke(null, new object[3] { avatar, text.Length, 2f }) is IEnumerator enumerator))
				{
					throw new InvalidOperationException("Wheel death coroutine was unavailable.");
				}
				((MonoBehaviour)val2).StartCoroutine(enumerator);
				UpgradeDropConfig.LogDetailed("Sunstone wheel death voice line played and original delayed death scheduled.");
			}
			catch (Exception arg)
			{
				RepoGamblingConfigurePlugin.Logger.LogError((object)$"Wheel death failed: {arg}");
				throw;
			}
		}
	}
}