Decompiled source of HolyCasinoStoneExtra v0.1.2

HolyCasinoStoneExtra.dll

Decompiled 7 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Krild_Holy_Casino_Stone.Behaviours;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using REPOLib.Modules;
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: AssemblyCompany("HolyCasinoStoneExtra")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.2.0")]
[assembly: AssemblyInformationalVersion("0.1.2")]
[assembly: AssemblyProduct("HolyCasinoStoneExtra")]
[assembly: AssemblyTitle("HolyCasinoStoneExtra")]
[assembly: AssemblyVersion("0.1.2.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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 HolyCasinoStoneExtra
{
	[HarmonyPatch(typeof(Krild_Holy_Casino_Stone), "Impact")]
	internal static class CasinoImpactPatch
	{
		[HarmonyPrefix]
		private static bool ReplaceOriginalImpact(Krild_Holy_Casino_Stone __instance)
		{
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				return false;
			}
			ValuableObject component = ((Component)__instance).GetComponent<ValuableObject>();
			if ((Object)(object)component == (Object)null || !CasinoStoneRuntime.HasEstablishedValue(component))
			{
				Plugin.Log.LogWarning((object)"Skipped a Holy Casino Stone roll because its ValuableObject value is not initialized.");
				return false;
			}
			if (CasinoRollScheduler.HasPendingRoll(__instance))
			{
				Plugin.Log.LogDebug((object)"Ignored a duplicate Holy Casino Stone impact while its prior roll was waiting for physical damage.");
				return false;
			}
			Plugin instance = Plugin.Instance;
			float num = Random.Range(0f, 100f);
			bool good = num < (float)instance.GoodChance.Value;
			CasinoRollScheduler.QueueRoll(__instance, component, num, good);
			return false;
		}
	}
	[HarmonyPatch(typeof(ValuableObject), "DollarValueSetLogic")]
	internal static class ValuableInitializationTrackingPatch
	{
		[HarmonyPostfix]
		[HarmonyAfter(new string[] { "Swaggies.LevelScaling" })]
		[HarmonyPriority(0)]
		private static void TrackInitializedValue(ValuableObject __instance)
		{
			Krild_Holy_Casino_Stone val = CasinoStoneRuntime.FindOriginalBehaviour(__instance);
			if ((Object)(object)val != (Object)null)
			{
				StoneProgressionRuntime.ApplyConfiguredStartingValue(val, __instance);
			}
			LevelScalingCompatibility.ObserveKnownValue(__instance);
		}
	}
	[HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "Break")]
	internal static class StoneBreakRequestDamagePatch
	{
		[HarmonyPrefix]
		[HarmonyBefore(new string[] { "Swaggies.LevelScaling" })]
		[HarmonyPriority(800)]
		private static void ApplyStoneDamageToggle(PhysGrabObjectImpactDetector __instance, ref float valueLost)
		{
			if (!Plugin.Instance.StoneTakesDamages.Value && (Object)(object)((Component)__instance).GetComponent<Krild_Holy_Casino_Stone>() != (Object)null)
			{
				valueLost = 0f;
			}
		}
	}
	[HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "BreakRPC")]
	internal static class StoneBreakRpcDamagePatch
	{
		[HarmonyPrefix]
		[HarmonyBefore(new string[] { "Swaggies.LevelScaling" })]
		[HarmonyPriority(800)]
		private static void ApplyStoneDamageToggle(PhysGrabObjectImpactDetector __instance, ref float valueLost, out bool __state)
		{
			__state = false;
			if (!Plugin.Instance.StoneTakesDamages.Value && (Object)(object)((Component)__instance).GetComponent<Krild_Holy_Casino_Stone>() != (Object)null)
			{
				valueLost = 0f;
				StoneZeroDamageFeedbackSuppression.Enter();
				__state = true;
			}
		}

		[HarmonyPostfix]
		[HarmonyPriority(0)]
		private static void EndZeroDamageFeedbackSuppression(bool __state)
		{
			if (__state)
			{
				StoneZeroDamageFeedbackSuppression.Exit();
			}
		}
	}
	[HarmonyPatch(typeof(WorldSpaceUIParent), "ValueLostCreate", new Type[]
	{
		typeof(Vector3),
		typeof(int)
	})]
	internal static class StoneZeroDamageValueLostUiPatch
	{
		[HarmonyPrefix]
		private static bool SuppressZeroDamageText(int _value)
		{
			if (StoneZeroDamageFeedbackSuppression.Active)
			{
				return _value != 0;
			}
			return true;
		}
	}
	internal static class StoneZeroDamageFeedbackSuppression
	{
		[ThreadStatic]
		private static int _depth;

		internal static bool Active => _depth > 0;

		internal static void Enter()
		{
			_depth++;
		}

		internal static void Exit()
		{
			if (_depth > 0)
			{
				_depth--;
			}
		}
	}
	[HarmonyPatch(typeof(ValuableObject), "DollarValueSetRPC")]
	internal static class ValueReplacementPatch
	{
		internal struct ReplacementState
		{
			internal Krild_Holy_Casino_Stone Stone;

			internal bool WasSet;

			internal bool WasSurplus;

			internal float OldOriginal;

			internal float OldCurrent;
		}

		[HarmonyPrefix]
		private static void CapturePriorValue(ValuableObject __instance, out ReplacementState __state)
		{
			Krild_Holy_Casino_Stone stone = CasinoStoneRuntime.FindOriginalBehaviour(__instance);
			__state = new ReplacementState
			{
				Stone = stone,
				WasSet = CasinoStoneRuntime.HasEstablishedValue(__instance),
				WasSurplus = ((Object)(object)((Component)__instance).GetComponent<SurplusValuable>() != (Object)null),
				OldOriginal = CasinoStoneRuntime.GetOriginalValue(__instance),
				OldCurrent = CasinoStoneRuntime.GetCurrentValue(__instance)
			};
		}

		[HarmonyPostfix]
		[HarmonyAfter(new string[] { "Swaggies.LevelScaling" })]
		[HarmonyPriority(0)]
		private static void ReconcileReplacementAndPlayCasinoSound(ValuableObject __instance, float value, ReplacementState __state)
		{
			if (__state.WasSet)
			{
				LevelScalingCompatibility.OnValueReplaced(__instance, __state.OldOriginal, __state.OldCurrent, CasinoStoneRuntime.GetOriginalValue(__instance), CasinoStoneRuntime.GetCurrentValue(__instance), value, __state.WasSurplus);
			}
			else
			{
				LevelScalingCompatibility.ObserveKnownValue(__instance);
			}
			if ((Object)(object)__state.Stone != (Object)null && __state.WasSet)
			{
				float currentValue = CasinoStoneRuntime.GetCurrentValue(__instance);
				CasinoStoneRuntime.SetOriginalCounter(__state.Stone, currentValue);
				bool good;
				bool won = (CasinoRollScheduler.TryConsumeResultSound(__state.Stone, out good) ? good : (currentValue >= __state.OldCurrent));
				CasinoStoneRuntime.PlayResultSound(__state.Stone, won);
			}
		}
	}
	[HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "BreakRPC")]
	internal static class ValuableBreakTrackingPatch
	{
		[HarmonyPostfix]
		[HarmonyAfter(new string[] { "Swaggies.LevelScaling" })]
		[HarmonyPriority(0)]
		private static void TrackValueAfterPhysicalDamage(PhysGrabObjectImpactDetector __instance)
		{
			ValuableObject component = ((Component)__instance).GetComponent<ValuableObject>();
			if ((Object)(object)component != (Object)null)
			{
				LevelScalingCompatibility.ObserveKnownValue(component);
			}
		}
	}
	[HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "DestroyObjectRPC")]
	internal static class ValuableDestructionTrackingPatch
	{
		[HarmonyPostfix]
		[HarmonyAfter(new string[] { "Swaggies.LevelScaling" })]
		[HarmonyPriority(0)]
		private static void ForgetDestroyedValue(PhysGrabObjectImpactDetector __instance)
		{
			ValuableObject component = ((Component)__instance).GetComponent<ValuableObject>();
			Krild_Holy_Casino_Stone component2 = ((Component)__instance).GetComponent<Krild_Holy_Casino_Stone>();
			if ((Object)(object)component != (Object)null)
			{
				LevelScalingCompatibility.ForgetValue(component);
			}
			if ((Object)(object)component2 != (Object)null)
			{
				CasinoRollScheduler.Forget(component2);
				StoneProgressionRuntime.Forget(component2);
			}
		}
	}
	internal static class CasinoRollScheduler
	{
		private sealed class PendingRoll
		{
			internal Krild_Holy_Casino_Stone Stone;

			internal ValuableObject Valuable;

			internal float Roll;

			internal bool Good;
		}

		private static readonly Dictionary<int, PendingRoll> PendingRolls = new Dictionary<int, PendingRoll>();

		private static readonly Dictionary<int, bool> PendingResultSounds = new Dictionary<int, bool>();

		internal static bool HasPendingRoll(Krild_Holy_Casino_Stone stone)
		{
			if ((Object)(object)stone != (Object)null)
			{
				return PendingRolls.ContainsKey(((Object)stone).GetInstanceID());
			}
			return false;
		}

		internal static void QueueRoll(Krild_Holy_Casino_Stone stone, ValuableObject valuable, float roll, bool good)
		{
			int instanceID = ((Object)stone).GetInstanceID();
			PendingRoll pendingRoll = new PendingRoll
			{
				Stone = stone,
				Valuable = valuable,
				Roll = roll,
				Good = good
			};
			PendingRolls[instanceID] = pendingRoll;
			((MonoBehaviour)Plugin.Instance).StartCoroutine(ApplyAfterPhysicalDamage(instanceID, pendingRoll));
		}

		internal static void Clear()
		{
			PendingRolls.Clear();
			PendingResultSounds.Clear();
		}

		internal static bool TryConsumeResultSound(Krild_Holy_Casino_Stone stone, out bool good)
		{
			good = false;
			if ((Object)(object)stone == (Object)null)
			{
				return false;
			}
			int instanceID = ((Object)stone).GetInstanceID();
			if (!PendingResultSounds.TryGetValue(instanceID, out good))
			{
				return false;
			}
			PendingResultSounds.Remove(instanceID);
			return true;
		}

		internal static void Forget(Krild_Holy_Casino_Stone stone)
		{
			if (!((Object)(object)stone == (Object)null))
			{
				int instanceID = ((Object)stone).GetInstanceID();
				PendingRolls.Remove(instanceID);
				PendingResultSounds.Remove(instanceID);
			}
		}

		private static IEnumerator ApplyAfterPhysicalDamage(int key, PendingRoll pending)
		{
			yield return null;
			if (!PendingRolls.TryGetValue(key, out var value) || value != pending)
			{
				yield break;
			}
			PendingRolls.Remove(key);
			if ((Object)(object)pending.Stone == (Object)null || (Object)(object)pending.Valuable == (Object)null || !SemiFunc.IsMasterClientOrSingleplayer() || !CasinoStoneRuntime.HasEstablishedValue(pending.Valuable))
			{
				yield break;
			}
			float currentValue = CasinoStoneRuntime.GetCurrentValue(pending.Valuable);
			if (!StoneProgressionRuntime.TryCalculateOutcome(pending.Stone, currentValue, pending.Good, out var newValue, out var outcome, out var setValue))
			{
				Plugin.Log.LogDebug((object)$"Holy Casino Stone rolled {pending.Roll:0.00}: {outcome}.");
				yield break;
			}
			if (!setValue)
			{
				Plugin.Log.LogDebug((object)$"Holy Casino Stone rolled {pending.Roll:0.00}: {outcome}; value unchanged at {currentValue:0}.");
				yield break;
			}
			newValue = CasinoStoneRuntime.SanitizeDollarValue(newValue, currentValue);
			PendingResultSounds[key] = pending.Good;
			if (!CasinoStoneRuntime.SetNetworkedDollarValue(pending.Valuable, newValue))
			{
				PendingResultSounds.Remove(key);
				yield break;
			}
			CasinoStoneRuntime.SetOriginalCounter(pending.Stone, newValue);
			Plugin.Log.LogDebug((object)$"Holy Casino Stone rolled {pending.Roll:0.00}: {outcome}; {currentValue:0} -> {newValue:0} after physical damage.");
		}
	}
	internal static class CasinoStoneRuntime
	{
		private const float MaximumSafeDollarValue = 2E+09f;

		private static readonly FieldInfo DollarValueOriginalField = AccessTools.Field(typeof(ValuableObject), "dollarValueOriginal");

		private static readonly FieldInfo DollarValueCurrentField = AccessTools.Field(typeof(ValuableObject), "dollarValueCurrent");

		private static readonly FieldInfo DollarValueSetField = AccessTools.Field(typeof(ValuableObject), "dollarValueSet");

		private static readonly FieldInfo OriginalCounterField = AccessTools.Field(typeof(Krild_Holy_Casino_Stone), "valueDollar");

		private static readonly FieldInfo WinSoundField = AccessTools.Field(typeof(Krild_Holy_Casino_Stone), "winSound");

		private static readonly FieldInfo LoseSoundField = AccessTools.Field(typeof(Krild_Holy_Casino_Stone), "noSound");

		internal static bool Validate(out string error)
		{
			if (AccessTools.Method(typeof(Krild_Holy_Casino_Stone), "Impact", (Type[])null, (Type[])null) == null)
			{
				error = "the original Holy Casino Stone Impact method was not found.";
				return false;
			}
			if (AccessTools.Method(typeof(ValuableObject), "DollarValueSetRPC", new Type[2]
			{
				typeof(float),
				typeof(PhotonMessageInfo)
			}, (Type[])null) == null)
			{
				error = "ValuableObject.DollarValueSetRPC(float, PhotonMessageInfo) was not found.";
				return false;
			}
			if (DollarValueOriginalField == null || DollarValueOriginalField.FieldType != typeof(float))
			{
				error = "ValuableObject.dollarValueOriginal was not found as a float.";
				return false;
			}
			if (DollarValueCurrentField == null || DollarValueCurrentField.FieldType != typeof(float))
			{
				error = "ValuableObject.dollarValueCurrent was not found as a float.";
				return false;
			}
			if (DollarValueSetField == null || DollarValueSetField.FieldType != typeof(bool))
			{
				error = "ValuableObject.dollarValueSet was not found as a bool.";
				return false;
			}
			if (OriginalCounterField == null || OriginalCounterField.FieldType != typeof(float))
			{
				error = "the original Holy Casino Stone valueDollar field was not found as a float.";
				return false;
			}
			if (WinSoundField == null || LoseSoundField == null)
			{
				error = "the original Holy Casino Stone sound fields were not found.";
				return false;
			}
			error = null;
			return true;
		}

		internal static float GetOriginalValue(ValuableObject valuable)
		{
			return (float)DollarValueOriginalField.GetValue(valuable);
		}

		internal static float GetCurrentValue(ValuableObject valuable)
		{
			return (float)DollarValueCurrentField.GetValue(valuable);
		}

		internal static bool HasEstablishedValue(ValuableObject valuable)
		{
			return (bool)DollarValueSetField.GetValue(valuable);
		}

		internal static void SetLocalDollarValue(ValuableObject valuable, float value)
		{
			DollarValueOriginalField.SetValue(valuable, value);
			DollarValueCurrentField.SetValue(valuable, value);
			DollarValueSetField.SetValue(valuable, true);
		}

		internal static void SetOriginalCounter(Krild_Holy_Casino_Stone stone, float value)
		{
			OriginalCounterField.SetValue(stone, value);
		}

		internal static float SanitizeDollarValue(float value, float fallback)
		{
			if (float.IsNaN(value) || float.IsInfinity(value))
			{
				value = fallback;
			}
			return Mathf.Round(Mathf.Clamp(value, 0f, 2E+09f));
		}

		internal static bool SetNetworkedDollarValue(ValuableObject valuable, float value)
		{
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			if (SemiFunc.IsMultiplayer())
			{
				PhotonView component = ((Component)valuable).GetComponent<PhotonView>();
				if ((Object)(object)component == (Object)null || component.ViewID == 0)
				{
					Plugin.Log.LogWarning((object)"Skipped a Holy Casino Stone value change because its PhotonView is not ready.");
					return false;
				}
				component.RPC("DollarValueSetRPC", (RpcTarget)0, new object[1] { value });
				return true;
			}
			valuable.DollarValueSetRPC(value, default(PhotonMessageInfo));
			return true;
		}

		internal static Krild_Holy_Casino_Stone FindOriginalBehaviour(ValuableObject valuable)
		{
			if (!((Object)(object)valuable == (Object)null))
			{
				return ((Component)valuable).GetComponent<Krild_Holy_Casino_Stone>();
			}
			return null;
		}

		internal static void PlayResultSound(Krild_Holy_Casino_Stone stone, bool won)
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)stone == (Object)null))
			{
				Sound val = (Sound)(won ? WinSoundField.GetValue(stone) : LoseSoundField.GetValue(stone));
				if ((int)val != 0)
				{
					val.Play(((Component)stone).transform.position, 1f, 1f, 1f, 1f);
				}
			}
		}
	}
	internal static class ConfigOrderingRuntime
	{
		private sealed class ParsedSection
		{
			internal readonly Dictionary<string, string[]> Blocks = new Dictionary<string, string[]>(StringComparer.Ordinal);

			internal readonly List<string> OriginalKeys = new List<string>();

			internal string[] TrailingLines = Array.Empty<string>();
		}

		private static readonly string[] SectionOrder = new string[6] { "SPAWN", "STONE", "CHANCES", "GOOD", "BAD", "COMPATIBILITY" };

		private static readonly Dictionary<string, string[]> KeyOrder = new Dictionary<string, string[]>(StringComparer.Ordinal)
		{
			["SPAWN"] = new string[2] { "Minimum Spawn(s)", "Maximum Spawn(s)" },
			["STONE"] = new string[2] { "Stone Takes Damages", "Stone Start Value" },
			["CHANCES"] = new string[1] { "Good Chance" },
			["GOOD"] = new string[11]
			{
				"Multiply", "Minimum Multiplier", "Maximum Multiplier", "Incremental", "Minimum Start", "Maximum Start", "Minimum Step", "Maximum Step", "Addition", "Minimum Addition",
				"Maximum Addition"
			},
			["BAD"] = new string[13]
			{
				"Back To Starting Value", "Divide", "Minimum Divide By", "Maximum Divide By", "Decremental", "Minimum Start", "Maximum Start", "Minimum Step", "Maximum Step", "Subtract",
				"Minimum Subtraction", "Maximum Subtraction", "Reset Incremental"
			},
			["COMPATIBILITY"] = new string[1] { "Fix LevelScaling" }
		};

		private static bool _writing;

		private static bool _reportedFailure;

		internal static ConfigEntryBase[] OrderEntries(ConfigEntryBase[] entries)
		{
			if (entries == null)
			{
				return Array.Empty<ConfigEntryBase>();
			}
			string[] value;
			return (from item in entries.Select((ConfigEntryBase entry, int index) => new
				{
					Entry = entry,
					OriginalIndex = index,
					SectionRank = Rank(SectionOrder, entry.Definition.Section),
					KeyRank = (KeyOrder.TryGetValue(entry.Definition.Section, out value) ? Rank(value, entry.Definition.Key) : int.MaxValue)
				})
				orderby item.SectionRank, item.KeyRank, item.OriginalIndex
				select item.Entry).ToArray();
		}

		internal static void RewriteConfigFile(string path)
		{
			if (_writing || string.IsNullOrEmpty(path) || !File.Exists(path))
			{
				return;
			}
			try
			{
				_writing = true;
				string[] array = File.ReadAllLines(path);
				int num = Array.FindIndex(array, IsSectionHeader);
				if (num < 0)
				{
					return;
				}
				List<string> list = new List<string>();
				AppendTrimmed(list, array.Take(num));
				list.Add(string.Empty);
				Dictionary<string, ParsedSection> dictionary = ParseSections(array, num);
				HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
				string[] sectionOrder = SectionOrder;
				foreach (string text in sectionOrder)
				{
					if (dictionary.TryGetValue(text, out var value))
					{
						AppendSection(list, text, value);
						hashSet.Add(text);
					}
				}
				foreach (KeyValuePair<string, ParsedSection> item in dictionary)
				{
					if (!hashSet.Contains(item.Key))
					{
						AppendSection(list, item.Key, item.Value);
					}
				}
				while (list.Count > 0 && string.IsNullOrWhiteSpace(list[list.Count - 1]))
				{
					list.RemoveAt(list.Count - 1);
				}
				list.Add(string.Empty);
				File.WriteAllLines(path, list, new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
			}
			catch (Exception ex)
			{
				if (!_reportedFailure)
				{
					_reportedFailure = true;
					Plugin.Log.LogError((object)("Failed to preserve Holy Casino Stone Extra config ordering: " + ex));
				}
			}
			finally
			{
				_writing = false;
			}
		}

		private static Dictionary<string, ParsedSection> ParseSections(string[] lines, int start)
		{
			Dictionary<string, ParsedSection> dictionary = new Dictionary<string, ParsedSection>(StringComparer.Ordinal);
			int i = start;
			while (i < lines.Length)
			{
				if (!TryGetSectionName(lines[i], out var section))
				{
					i++;
					continue;
				}
				i++;
				int num = i;
				for (; i < lines.Length && !IsSectionHeader(lines[i]); i++)
				{
				}
				dictionary[section] = ParseSection(lines.Skip(num).Take(i - num));
			}
			return dictionary;
		}

		private static ParsedSection ParseSection(IEnumerable<string> bodyLines)
		{
			ParsedSection parsedSection = new ParsedSection();
			List<string> list = new List<string>();
			foreach (string bodyLine in bodyLines)
			{
				list.Add(bodyLine);
				if (TryGetSettingKey(bodyLine, out var key))
				{
					string[] value = TrimBlankLines(list).ToArray();
					parsedSection.Blocks[key] = value;
					parsedSection.OriginalKeys.Add(key);
					list.Clear();
				}
			}
			parsedSection.TrailingLines = TrimBlankLines(list).ToArray();
			return parsedSection;
		}

		private static void AppendSection(List<string> output, string name, ParsedSection section)
		{
			output.Add("[" + name + "]");
			output.Add(string.Empty);
			HashSet<string> hashSet = new HashSet<string>(StringComparer.Ordinal);
			if (KeyOrder.TryGetValue(name, out var value))
			{
				string[] array = value;
				foreach (string text in array)
				{
					if (section.Blocks.TryGetValue(text, out var value2))
					{
						AppendBlock(output, value2);
						hashSet.Add(text);
					}
				}
			}
			foreach (string originalKey in section.OriginalKeys)
			{
				if (!hashSet.Contains(originalKey) && section.Blocks.TryGetValue(originalKey, out var value3))
				{
					AppendBlock(output, value3);
					hashSet.Add(originalKey);
				}
			}
			if (section.TrailingLines.Length != 0)
			{
				AppendBlock(output, section.TrailingLines);
			}
		}

		private static void AppendBlock(List<string> output, IEnumerable<string> lines)
		{
			AppendTrimmed(output, lines);
			output.Add(string.Empty);
		}

		private static void AppendTrimmed(List<string> output, IEnumerable<string> lines)
		{
			output.AddRange(TrimBlankLines(lines));
		}

		private static IEnumerable<string> TrimBlankLines(IEnumerable<string> lines)
		{
			string[] array = lines.ToArray();
			int i;
			for (i = 0; i < array.Length && string.IsNullOrWhiteSpace(array[i]); i++)
			{
			}
			int end = array.Length - 1;
			while (end >= i && string.IsNullOrWhiteSpace(array[end]))
			{
				end--;
			}
			for (int index = i; index <= end; index++)
			{
				yield return array[index];
			}
		}

		private static bool IsSectionHeader(string line)
		{
			string section;
			return TryGetSectionName(line, out section);
		}

		private static bool TryGetSectionName(string line, out string section)
		{
			string text = line?.Trim();
			if (!string.IsNullOrEmpty(text) && text.Length > 2 && text[0] == '[' && text[text.Length - 1] == ']')
			{
				section = text.Substring(1, text.Length - 2);
				return true;
			}
			section = null;
			return false;
		}

		private static bool TryGetSettingKey(string line, out string key)
		{
			string text = line?.Trim();
			if (string.IsNullOrEmpty(text) || text[0] == '#' || text[0] == '[')
			{
				key = null;
				return false;
			}
			int num = text.IndexOf(" = ", StringComparison.Ordinal);
			if (num <= 0)
			{
				key = null;
				return false;
			}
			key = text.Substring(0, num).Trim();
			return true;
		}

		private static int Rank(string[] order, string value)
		{
			int num = Array.IndexOf(order, value);
			if (num >= 0)
			{
				return num;
			}
			return int.MaxValue;
		}
	}
	[HarmonyPatch(typeof(ConfigFile), "Save")]
	internal static class ConfigFileSaveOrderingPatch
	{
		[HarmonyPostfix]
		private static void PreserveRequestedOrder(ConfigFile __instance)
		{
			Plugin instance = Plugin.Instance;
			if ((Object)(object)instance != (Object)null && __instance == ((BaseUnityPlugin)instance).Config)
			{
				ConfigOrderingRuntime.RewriteConfigFile(__instance.ConfigFilePath);
			}
		}
	}
	[HarmonyPatch]
	internal static class RepoConfigOrderingPatch
	{
		[HarmonyPrepare]
		private static bool Prepare()
		{
			if (Chainloader.PluginInfos.ContainsKey("nickklmao.repoconfig"))
			{
				return TargetMethod() != null;
			}
			return false;
		}

		[HarmonyTargetMethod]
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method("REPOConfig.ConfigMenu:GetModConfigEntries", (Type[])null, (Type[])null);
		}

		[HarmonyPostfix]
		private static void ReorderHolyCasinoStoneExtra(ref Dictionary<string, ConfigEntryBase[]> __result)
		{
			if (__result != null && __result.TryGetValue("Holy Casino Stone Extra", out var value))
			{
				__result["Holy Casino Stone Extra"] = ConfigOrderingRuntime.OrderEntries(value);
			}
		}
	}
	internal static class LevelScalingCompatibility
	{
		private sealed class TrackedValue
		{
			internal ValuableObject Valuable;

			internal float Original;

			internal float Current;

			internal bool IsSurplus;
		}

		private static FieldInfo _clientTotalMapValueField;

		private static FieldInfo _valueFromSurplusField;

		private static FieldInfo _levelTotalValueBrokenField;

		private static FieldInfo _runStatsInstanceField;

		private static FieldInfo _runTotalValueBrokenField;

		private static MethodInfo _globalAddBrokenValueMethod;

		private static readonly Dictionary<int, TrackedValue> TrackedValues = new Dictionary<int, TrackedValue>();

		private static readonly List<int> DeadValueKeys = new List<int>();

		private static bool _available;

		private static bool _reportedRuntimeFailure;

		private static float _nextReconcileTime;

		internal static void Initialize()
		{
			if (!Chainloader.PluginInfos.TryGetValue("Swaggies.LevelScaling", out var value) || (Object)(object)((value != null) ? value.Instance : null) == (Object)null)
			{
				Plugin.Log.LogInfo((object)"LevelScaling is not installed; compatibility toggles will remain idle.");
				return;
			}
			Assembly assembly = ((object)value.Instance).GetType().Assembly;
			Type type = assembly.GetType("LevelScaling.LevelStats.LevelStatsManager");
			Type type2 = assembly.GetType("LevelScaling.LevelStats.RunStatsManager");
			Type type3 = assembly.GetType("LevelScaling.LevelStats.GlobalStatsManager");
			_clientTotalMapValueField = FindField(type, "_clientTotalMapValue", isStatic: true);
			_valueFromSurplusField = FindField(type, "_valueFromSurplus", isStatic: true);
			_levelTotalValueBrokenField = FindField(type, "totalValueBroken", isStatic: true);
			_runStatsInstanceField = FindAnyField(type2, "instance", isStatic: true);
			_runTotalValueBrokenField = FindField(type2, "totalValueBroken", isStatic: false);
			_globalAddBrokenValueMethod = ((type3 == null) ? null : type3.GetMethod("AddBrokenValue", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(float) }, null));
			_available = _clientTotalMapValueField != null && _valueFromSurplusField != null && _levelTotalValueBrokenField != null && _runStatsInstanceField != null && _runTotalValueBrokenField != null && _globalAddBrokenValueMethod != null;
			if (_available)
			{
				Plugin.Log.LogInfo((object)$"LevelScaling {value.Metadata.Version} compatibility hooks resolved successfully.");
			}
			else
			{
				Plugin.Log.LogWarning((object)$"LevelScaling {value.Metadata.Version} was found, but one or more stats fields changed. Holy Casino Stone Extra will not mutate LevelScaling stats.");
			}
		}

		internal static void OnValueReplaced(ValuableObject valuable, float oldOriginal, float oldCurrent, float newOriginal, float newCurrent, float valueAddedByLevelScaling, bool wasSurplus)
		{
			if (!_available || !SemiFunc.RunIsLevel())
			{
				return;
			}
			try
			{
				if (Plugin.Instance.FixLevelScaling.Value)
				{
					float num = ReadFloat(_clientTotalMapValueField, null);
					if (wasSurplus)
					{
						WriteFloat(_clientTotalMapValueField, null, Mathf.Max(0f, num - valueAddedByLevelScaling));
						float num2 = ReadFloat(_valueFromSurplusField, null);
						WriteFloat(_valueFromSurplusField, null, Mathf.Max(0f, num2 - (float)Mathf.FloorToInt(newCurrent)));
					}
					else
					{
						float num3 = newOriginal - oldOriginal - valueAddedByLevelScaling;
						WriteFloat(_clientTotalMapValueField, null, Mathf.Max(0f, num + num3));
					}
				}
				if (Plugin.Instance.FixLevelScaling.Value)
				{
					float num4 = OutstandingDamage(oldOriginal, oldCurrent);
					ApplyBrokenDelta(OutstandingDamage(newOriginal, newCurrent) - num4);
				}
				ObserveKnownValue(valuable);
			}
			catch (Exception exception)
			{
				DisableAfterRuntimeFailure(exception);
			}
		}

		internal static void ObserveKnownValue(ValuableObject valuable)
		{
			if (_available && !((Object)(object)valuable == (Object)null) && SemiFunc.RunIsLevel() && CasinoStoneRuntime.HasEstablishedValue(valuable))
			{
				int instanceID = ((Object)valuable).GetInstanceID();
				TrackedValues[instanceID] = new TrackedValue
				{
					Valuable = valuable,
					Original = CasinoStoneRuntime.GetOriginalValue(valuable),
					Current = CasinoStoneRuntime.GetCurrentValue(valuable),
					IsSurplus = ((Object)(object)((Component)valuable).GetComponent<SurplusValuable>() != (Object)null)
				};
			}
		}

		internal static void ForgetValue(ValuableObject valuable)
		{
			if ((Object)(object)valuable != (Object)null)
			{
				TrackedValues.Remove(((Object)valuable).GetInstanceID());
			}
		}

		internal static void ReconcileTrackedValues()
		{
			if (!_available)
			{
				return;
			}
			if (!SemiFunc.RunIsLevel())
			{
				TrackedValues.Clear();
				DeadValueKeys.Clear();
			}
			else
			{
				if (Time.unscaledTime < _nextReconcileTime)
				{
					return;
				}
				_nextReconcileTime = Time.unscaledTime + 0.1f;
				try
				{
					DeadValueKeys.Clear();
					foreach (KeyValuePair<int, TrackedValue> trackedValue in TrackedValues)
					{
						TrackedValue value = trackedValue.Value;
						if ((Object)(object)value.Valuable == (Object)null)
						{
							DeadValueKeys.Add(trackedValue.Key);
						}
						else
						{
							if (!CasinoStoneRuntime.HasEstablishedValue(value.Valuable))
							{
								continue;
							}
							float originalValue = CasinoStoneRuntime.GetOriginalValue(value.Valuable);
							float currentValue = CasinoStoneRuntime.GetCurrentValue(value.Valuable);
							bool flag = (Object)(object)((Component)value.Valuable).GetComponent<SurplusValuable>() != (Object)null;
							if (!Mathf.Approximately(originalValue, value.Original) || !Mathf.Approximately(currentValue, value.Current) || flag != value.IsSurplus)
							{
								float num = 0f;
								float num2 = 0f;
								if (Plugin.Instance.FixLevelScaling.Value && !value.IsSurplus && !flag)
								{
									num = originalValue - value.Original;
									float num3 = ReadFloat(_clientTotalMapValueField, null);
									WriteFloat(_clientTotalMapValueField, null, Mathf.Max(0f, num3 + num));
								}
								if (Plugin.Instance.FixLevelScaling.Value)
								{
									num2 = OutstandingDamage(originalValue, currentValue) - OutstandingDamage(value.Original, value.Current);
									ApplyBrokenDelta(num2);
								}
								value.Original = originalValue;
								value.Current = currentValue;
								value.IsSurplus = flag;
								if (!Mathf.Approximately(num, 0f) || !Mathf.Approximately(num2, 0f))
								{
									Plugin.Log.LogDebug((object)$"Reconciled an external valuable change: map delta {num:0}, broken delta {num2:0}.");
								}
							}
						}
					}
					foreach (int deadValueKey in DeadValueKeys)
					{
						TrackedValues.Remove(deadValueKey);
					}
				}
				catch (Exception exception)
				{
					DisableAfterRuntimeFailure(exception);
				}
			}
		}

		internal static void Reset()
		{
			TrackedValues.Clear();
			DeadValueKeys.Clear();
			_available = false;
		}

		private static float OutstandingDamage(float original, float current)
		{
			return Mathf.Max(0f, original - current);
		}

		private static void ApplyBrokenDelta(float requestedDelta)
		{
			if (Mathf.Approximately(requestedDelta, 0f))
			{
				return;
			}
			float num = ReadFloat(_levelTotalValueBrokenField, null);
			float num2 = Mathf.Max(0f, num + requestedDelta);
			float num3 = num2 - num;
			if (!Mathf.Approximately(num3, 0f))
			{
				WriteFloat(_levelTotalValueBrokenField, null, num2);
				object value = _runStatsInstanceField.GetValue(null);
				if (value != null)
				{
					float num4 = ReadFloat(_runTotalValueBrokenField, value);
					WriteFloat(_runTotalValueBrokenField, value, Mathf.Max(0f, num4 + num3));
				}
				_globalAddBrokenValueMethod.Invoke(null, new object[1] { num3 });
			}
		}

		private static void DisableAfterRuntimeFailure(Exception exception)
		{
			if (!_reportedRuntimeFailure)
			{
				_reportedRuntimeFailure = true;
				Plugin.Log.LogError((object)("LevelScaling compatibility correction failed and has been disabled for this process: " + exception));
			}
			_available = false;
			TrackedValues.Clear();
			DeadValueKeys.Clear();
		}

		private static FieldInfo FindField(Type type, string name, bool isStatic)
		{
			if (type == null)
			{
				return null;
			}
			FieldInfo field = type.GetField(name, (BindingFlags)(0x30 | (isStatic ? 8 : 4)));
			if (!(field != null) || (!(field.FieldType == typeof(float)) && !(field.FieldType == typeof(int))))
			{
				return null;
			}
			return field;
		}

		private static FieldInfo FindAnyField(Type type, string name, bool isStatic)
		{
			return type?.GetField(name, (BindingFlags)(0x30 | (isStatic ? 8 : 4)));
		}

		private static float ReadFloat(FieldInfo field, object instance)
		{
			return Convert.ToSingle(field.GetValue(instance));
		}

		private static void WriteFloat(FieldInfo field, object instance, float value)
		{
			if (field.FieldType == typeof(int))
			{
				field.SetValue(instance, Mathf.RoundToInt(value));
			}
			else
			{
				field.SetValue(instance, value);
			}
		}
	}
	internal static class OriginalLoopSoundCompatibility
	{
		private static readonly MethodInfo OriginalStartMethod = AccessTools.DeclaredMethod(typeof(Krild_Holy_Casino_Stone), "Start", Type.EmptyTypes, (Type[])null);

		private static readonly MethodInfo OriginalUpdateMethod = AccessTools.DeclaredMethod(typeof(Krild_Holy_Casino_Stone), "Update", Type.EmptyTypes, (Type[])null);

		private static readonly FieldInfo PhysGrabObjectField = AccessTools.DeclaredField(typeof(Krild_Holy_Casino_Stone), "physgrabObject");

		private static readonly FieldInfo LoopSoundField = AccessTools.DeclaredField(typeof(Krild_Holy_Casino_Stone), "loopKrild");

		private static readonly MethodInfo CurrentPlayLoopMethod = AccessTools.DeclaredMethod(typeof(Sound), "PlayLoop", new Type[5]
		{
			typeof(bool),
			typeof(float),
			typeof(float),
			typeof(float),
			typeof(float)
		}, (Type[])null);

		internal static MethodBase StartTarget => OriginalStartMethod;

		internal static bool Validate(out string error)
		{
			if (!ValidateInstanceVoidMethod(OriginalStartMethod, "Start", 0, out error) || !ValidateInstanceVoidMethod(OriginalUpdateMethod, "Update", 0, out error))
			{
				return false;
			}
			if (!ValidateField(PhysGrabObjectField, "physgrabObject", typeof(PhysGrabObject), out error) || !ValidateField(LoopSoundField, "loopKrild", typeof(Sound), out error))
			{
				return false;
			}
			if (!ValidateInstanceVoidMethod(CurrentPlayLoopMethod, "Sound.PlayLoop", 5, out error))
			{
				return false;
			}
			ParameterInfo[] parameters = CurrentPlayLoopMethod.GetParameters();
			Type[] array = new Type[5]
			{
				typeof(bool),
				typeof(float),
				typeof(float),
				typeof(float),
				typeof(float)
			};
			for (int i = 0; i < array.Length; i++)
			{
				if (parameters[i].ParameterType != array[i])
				{
					error = $"Sound.PlayLoop parameter {i + 1} is {parameters[i].ParameterType.FullName}, " + "expected " + array[i].FullName + ".";
					return false;
				}
			}
			error = null;
			return true;
		}

		internal static void AttachReplacement(Krild_Holy_Casino_Stone original)
		{
			if (!((Object)(object)original == (Object)null))
			{
				((Behaviour)original).enabled = false;
				object? value = PhysGrabObjectField.GetValue(original);
				PhysGrabObject physGrabObject = (PhysGrabObject)((value is PhysGrabObject) ? value : null);
				object? value2 = LoopSoundField.GetValue(original);
				Sound loopSound = (Sound)((value2 is Sound) ? value2 : null);
				OriginalLoopSoundReplacement originalLoopSoundReplacement = ((Component)original).GetComponent<OriginalLoopSoundReplacement>();
				if ((Object)(object)originalLoopSoundReplacement == (Object)null)
				{
					originalLoopSoundReplacement = ((Component)original).gameObject.AddComponent<OriginalLoopSoundReplacement>();
				}
				originalLoopSoundReplacement.Initialize(physGrabObject, loopSound);
			}
		}

		private static bool ValidateInstanceVoidMethod(MethodInfo method, string name, int parameterCount, out string error)
		{
			if (method == null)
			{
				error = "Required method " + name + " was not found.";
				return false;
			}
			if (method.IsStatic || method.ReturnType != typeof(void) || method.GetParameters().Length != parameterCount)
			{
				error = "Required method " + name + " has an unexpected signature.";
				return false;
			}
			error = null;
			return true;
		}

		private static bool ValidateField(FieldInfo field, string name, Type expectedType, out string error)
		{
			if (field == null)
			{
				error = "Required original-mod field " + name + " was not found.";
				return false;
			}
			if (field.IsStatic || field.FieldType != expectedType)
			{
				error = "Original-mod field " + name + " is " + field.FieldType.FullName + ", expected instance field " + expectedType.FullName + ".";
				return false;
			}
			error = null;
			return true;
		}
	}
	internal sealed class OriginalLoopSoundReplacement : MonoBehaviour
	{
		private PhysGrabObject _physGrabObject;

		private Sound _loopSound;

		internal void Initialize(PhysGrabObject physGrabObject, Sound loopSound)
		{
			_physGrabObject = physGrabObject;
			_loopSound = loopSound;
			((Behaviour)this).enabled = (Object)(object)_physGrabObject != (Object)null && _loopSound != null;
		}

		private void Update()
		{
			if (!((Object)(object)_physGrabObject == (Object)null) && _loopSound != null && !((Object)(object)_loopSound.Source == (Object)null) && _loopSound.Sounds != null && _loopSound.Sounds.Length != 0)
			{
				_loopSound.PlayLoop(_physGrabObject.grabbed, 1f, 1f, 1f, 1f);
			}
		}
	}
	[HarmonyPatch]
	internal static class OriginalCasinoStoneStartPatch
	{
		[HarmonyTargetMethod]
		private static MethodBase TargetMethod()
		{
			return OriginalLoopSoundCompatibility.StartTarget;
		}

		[HarmonyPostfix]
		private static void Postfix(Krild_Holy_Casino_Stone __instance)
		{
			OriginalLoopSoundCompatibility.AttachReplacement(__instance);
		}
	}
	[BepInPlugin("mathe.holycasinostoneextra", "Holy Casino Stone Extra", "0.1.2")]
	[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 sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "mathe.holycasinostoneextra";

		public const string PluginName = "Holy Casino Stone Extra";

		public const string PluginVersion = "0.1.2";

		public const string OriginalPluginGuid = "KrildHolyCasinoStone";

		public const string RepoLibGuid = "REPOLib";

		public const string LevelScalingGuid = "Swaggies.LevelScaling";

		public const string RepoConfigGuid = "nickklmao.repoconfig";

		private Harmony _harmony;

		internal static Plugin Instance { get; private set; }

		internal static ManualLogSource Log { get; private set; }

		internal ConfigEntry<int> MinimumSpawnInLevel { get; private set; }

		internal ConfigEntry<int> MaximumSpawnInLevel { get; private set; }

		internal ConfigEntry<bool> StoneTakesDamages { get; private set; }

		internal ConfigEntry<string> StoneStartValue { get; private set; }

		internal ConfigEntry<int> GoodChance { get; private set; }

		internal ConfigEntry<bool> GoodMultiply { get; private set; }

		internal ConfigEntry<string> GoodMinimumMultiplier { get; private set; }

		internal ConfigEntry<string> GoodMaximumMultiplier { get; private set; }

		internal ConfigEntry<bool> GoodIncremental { get; private set; }

		internal ConfigEntry<string> GoodIncrementalMinimumStart { get; private set; }

		internal ConfigEntry<string> GoodIncrementalMaximumStart { get; private set; }

		internal ConfigEntry<string> GoodIncrementalMinimumStep { get; private set; }

		internal ConfigEntry<string> GoodIncrementalMaximumStep { get; private set; }

		internal ConfigEntry<bool> GoodAddition { get; private set; }

		internal ConfigEntry<string> GoodMinimumAddition { get; private set; }

		internal ConfigEntry<string> GoodMaximumAddition { get; private set; }

		internal ConfigEntry<bool> BadBackToStartingValue { get; private set; }

		internal ConfigEntry<bool> BadDivide { get; private set; }

		internal ConfigEntry<string> BadMinimumDivideBy { get; private set; }

		internal ConfigEntry<string> BadMaximumDivideBy { get; private set; }

		internal ConfigEntry<bool> BadDecremental { get; private set; }

		internal ConfigEntry<string> BadDecrementalMinimumStart { get; private set; }

		internal ConfigEntry<string> BadDecrementalMaximumStart { get; private set; }

		internal ConfigEntry<string> BadDecrementalMinimumStep { get; private set; }

		internal ConfigEntry<string> BadDecrementalMaximumStep { get; private set; }

		internal ConfigEntry<bool> BadSubtract { get; private set; }

		internal ConfigEntry<string> BadMinimumSubtraction { get; private set; }

		internal ConfigEntry<string> BadMaximumSubtraction { get; private set; }

		internal ConfigEntry<bool> BadResetIncremental { get; private set; }

		internal ConfigEntry<bool> FixLevelScaling { get; private set; }

		private void Awake()
		{
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Expected O, but got Unknown
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			BindConfiguration();
			if (!CasinoStoneRuntime.Validate(out var error) || !OriginalLoopSoundCompatibility.Validate(out error) || !SpawnController.Validate(out error))
			{
				((BaseUnityPlugin)this).Logger.LogError((object)("Holy Casino Stone Extra is disabled because the installed game/original mod does not match its required runtime fields: " + error));
				return;
			}
			LevelScalingCompatibility.Initialize();
			_harmony = new Harmony("mathe.holycasinostoneextra");
			_harmony.PatchAll();
			ConfigOrderingRuntime.RewriteConfigFile(((BaseUnityPlugin)this).Config.ConfigFilePath);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Holy Casino Stone Extra 0.1.2 loaded. Host-authoritative casino and spawn patches are active.");
		}

		private void LateUpdate()
		{
			StoneProgressionRuntime.UpdateLifecycle();
			LevelScalingCompatibility.ReconcileTrackedValues();
		}

		private void OnDestroy()
		{
			CasinoRollScheduler.Clear();
			StoneProgressionRuntime.Clear();
			LevelScalingCompatibility.Reset();
			Harmony harmony = _harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
			if ((Object)(object)Instance == (Object)(object)this)
			{
				Instance = null;
				Log = null;
			}
		}

		private void BindConfiguration()
		{
			//IL_02e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Expected O, but got Unknown
			//IL_031a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0324: Expected O, but got Unknown
			//IL_0395: Unknown result type (might be due to invalid IL or missing references)
			//IL_039f: Expected O, but got Unknown
			bool saveOnConfigSet = ((BaseUnityPlugin)this).Config.SaveOnConfigSet;
			((BaseUnityPlugin)this).Config.SaveOnConfigSet = false;
			try
			{
				ConfigEntry<int> val = ((BaseUnityPlugin)this).Config.Bind<int>("SPAWN", "Minimum Spawn In Level", 0, (ConfigDescription)null);
				ConfigEntry<int> val2 = ((BaseUnityPlugin)this).Config.Bind<int>("SPAWN", "Maximum Spawn In Level", 2, (ConfigDescription)null);
				ConfigEntry<bool> val3 = ((BaseUnityPlugin)this).Config.Bind<bool>("GENERAL", "Item Damages", true, (ConfigDescription)null);
				ConfigEntry<int> val4 = ((BaseUnityPlugin)this).Config.Bind<int>("CHANCES", "Multiplier Chance", 80, (ConfigDescription)null);
				ConfigEntry<float> val5 = ((BaseUnityPlugin)this).Config.Bind<float>("CHANCES", "Minimum Multiplier", 1f, (ConfigDescription)null);
				ConfigEntry<float> val6 = ((BaseUnityPlugin)this).Config.Bind<float>("CHANCES", "Maximum Multiplier", 2f, (ConfigDescription)null);
				ConfigEntry<int> val7 = ((BaseUnityPlugin)this).Config.Bind<int>("CHANCES", "Reset Chance", 20, (ConfigDescription)null);
				ConfigEntry<bool> val8 = ((BaseUnityPlugin)this).Config.Bind<bool>("CHANCES", "Toggle Reset", true, (ConfigDescription)null);
				ConfigEntry<float> val9 = ((BaseUnityPlugin)this).Config.Bind<float>("CHANCES", "Minimum Divider", 2f, (ConfigDescription)null);
				ConfigEntry<float> val10 = ((BaseUnityPlugin)this).Config.Bind<float>("CHANCES", "Maximum Divider", 2f, (ConfigDescription)null);
				ConfigEntry<bool> val11 = ((BaseUnityPlugin)this).Config.Bind<bool>("COMPATIBILITY", "Fix LevelScaling Level Max Value", true, (ConfigDescription)null);
				ConfigEntry<bool> val12 = ((BaseUnityPlugin)this).Config.Bind<bool>("COMPATIBILITY", "Fix LevelScaling Item Damages From Cube", true, (ConfigDescription)null);
				int value = val.Value;
				int value2 = val2.Value;
				bool value3 = val3.Value;
				int value4 = val4.Value;
				float defaultValue = ClampOneToTen(val5.Value);
				float defaultValue2 = ClampOneToTen(val6.Value);
				bool value5 = val8.Value;
				bool flag = !val8.Value && val7.Value > 0;
				float defaultValue3 = ClampOneToTen(val9.Value);
				float defaultValue4 = ClampOneToTen(val10.Value);
				bool flag2 = val11.Value && val12.Value;
				((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val).Definition);
				((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val2).Definition);
				((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val3).Definition);
				((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val4).Definition);
				((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val5).Definition);
				((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val6).Definition);
				((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val7).Definition);
				((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val8).Definition);
				((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val9).Definition);
				((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val10).Definition);
				((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val11).Definition);
				((BaseUnityPlugin)this).Config.Remove(((ConfigEntryBase)val12).Definition);
				MinimumSpawnInLevel = ((BaseUnityPlugin)this).Config.Bind<int>("SPAWN", "Minimum Spawn(s)", value, new ConfigDescription("Minimum number of Holy Casino Stones selected for each generated level. Capped at Maximum Spawn(s).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 5), Array.Empty<object>()));
				MaximumSpawnInLevel = ((BaseUnityPlugin)this).Config.Bind<int>("SPAWN", "Maximum Spawn(s)", value2, new ConfigDescription("Maximum number of Holy Casino Stones selected for each generated level.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 5), Array.Empty<object>()));
				StoneTakesDamages = ((BaseUnityPlugin)this).Config.Bind<bool>("STONE", "Stone Takes Damages", value3, "When enabled, physical breaks reduce the stone's value. When disabled, its break value loss is forced to zero before it is networked or recorded by LevelScaling.");
				StoneStartValue = ((BaseUnityPlugin)this).Config.Bind<string>("STONE", "Stone Start Value", "100", "Dollar value assigned to every Holy Casino Stone when it initializes.");
				GoodChance = ((BaseUnityPlugin)this).Config.Bind<int>("CHANCES", "Good Chance", value4, new ConfigDescription("Chance that an eligible impact selects a GOOD operation. BAD chance is automatically 100 minus this value.", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 100), Array.Empty<object>()));
				GoodMultiply = ((BaseUnityPlugin)this).Config.Bind<bool>("GOOD", "Multiply", true, "Adds Multiply to the random pool of enabled GOOD operations.");
				GoodMinimumMultiplier = QuarterStepSlider("GOOD", "Minimum Multiplier", defaultValue, "Minimum random multiplier for Multiply.");
				GoodMaximumMultiplier = QuarterStepSlider("GOOD", "Maximum Multiplier", defaultValue2, "Maximum random multiplier for Multiply. Bounds are reordered when necessary.");
				GoodIncremental = ((BaseUnityPlugin)this).Config.Bind<bool>("GOOD", "Incremental", false, "Adds Incremental to the random GOOD pool. It multiplies by its current factor, then multiplies that factor by a random step for its next selection.");
				GoodIncrementalMinimumStart = QuarterStepSlider("GOOD", "Minimum Start", 1.5f, "Minimum initial Incremental multiplier.");
				GoodIncrementalMaximumStart = QuarterStepSlider("GOOD", "Maximum Start", 1.5f, "Maximum initial Incremental multiplier.");
				GoodIncrementalMinimumStep = QuarterStepSlider("GOOD", "Minimum Step", 2f, "Minimum factor applied to the Incremental multiplier after it is selected.");
				GoodIncrementalMaximumStep = QuarterStepSlider("GOOD", "Maximum Step", 2f, "Maximum factor applied to the Incremental multiplier after it is selected.");
				GoodAddition = ((BaseUnityPlugin)this).Config.Bind<bool>("GOOD", "Addition", false, "Adds Addition to the random pool of enabled GOOD operations.");
				GoodMinimumAddition = ((BaseUnityPlugin)this).Config.Bind<string>("GOOD", "Minimum Addition", "100", "Minimum random dollar amount added by Addition.");
				GoodMaximumAddition = ((BaseUnityPlugin)this).Config.Bind<string>("GOOD", "Maximum Addition", "1000", "Maximum random dollar amount added by Addition. Bounds are reordered when necessary.");
				BadBackToStartingValue = ((BaseUnityPlugin)this).Config.Bind<bool>("BAD", "Back To Starting Value", value5, "Adds a reset to Stone Start Value to the random pool of enabled BAD operations.");
				BadDivide = ((BaseUnityPlugin)this).Config.Bind<bool>("BAD", "Divide", flag, "Adds Divide to the random pool of enabled BAD operations.");
				BadMinimumDivideBy = QuarterStepSlider("BAD", "Minimum Divide By", defaultValue3, "Minimum random divisor for Divide.");
				BadMaximumDivideBy = QuarterStepSlider("BAD", "Maximum Divide By", defaultValue4, "Maximum random divisor for Divide. Bounds are reordered when necessary.");
				BadDecremental = ((BaseUnityPlugin)this).Config.Bind<bool>("BAD", "Decremental", false, "Adds Decremental to the random BAD pool. It divides by its current factor, then multiplies that factor by a random step for its next selection.");
				BadDecrementalMinimumStart = QuarterStepSlider("BAD", "Minimum Start", 1f, "Minimum initial Decremental divisor.");
				BadDecrementalMaximumStart = QuarterStepSlider("BAD", "Maximum Start", 2f, "Maximum initial Decremental divisor.");
				BadDecrementalMinimumStep = QuarterStepSlider("BAD", "Minimum Step", 1f, "Minimum factor applied to the Decremental divisor after it is selected.");
				BadDecrementalMaximumStep = QuarterStepSlider("BAD", "Maximum Step", 2f, "Maximum factor applied to the Decremental divisor after it is selected.");
				BadSubtract = ((BaseUnityPlugin)this).Config.Bind<bool>("BAD", "Subtract", false, "Adds Subtract to the random pool of enabled BAD operations.");
				BadMinimumSubtraction = ((BaseUnityPlugin)this).Config.Bind<string>("BAD", "Minimum Subtraction", "100", "Minimum random dollar amount removed by Subtract.");
				BadMaximumSubtraction = ((BaseUnityPlugin)this).Config.Bind<string>("BAD", "Maximum Subtraction", "2000", "Maximum random dollar amount removed by Subtract. Bounds are reordered when necessary.");
				BadResetIncremental = ((BaseUnityPlugin)this).Config.Bind<bool>("BAD", "Reset Incremental", false, "When enabled, every BAD roll resets Incremental so its next selection chooses a new starting multiplier.");
				FixLevelScaling = ((BaseUnityPlugin)this).Config.Bind<bool>("COMPATIBILITY", "Fix LevelScaling", flag2, "Reconciles both LevelScaling's map maximum and outstanding Broken value when valuables lose, regain, or replace value.");
				((BaseUnityPlugin)this).Config.Save();
			}
			finally
			{
				((BaseUnityPlugin)this).Config.SaveOnConfigSet = saveOnConfigSet;
			}
		}

		private ConfigEntry<string> QuarterStepSlider(string section, string key, float defaultValue, string description)
		{
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Expected O, but got Unknown
			return ((BaseUnityPlugin)this).Config.Bind<string>(section, key, FormatQuarterStep(defaultValue), new ConfigDescription(description + " Selectable from 1 to 10 in 0.25 steps.", (AcceptableValueBase)(object)new AcceptableValueList<string>(CreateQuarterStepOptions()), Array.Empty<object>()));
		}

		internal static float ReadQuarterStep(ConfigEntry<string> entry)
		{
			return ParseConfigNumber(entry?.Value, 1f);
		}

		internal static float ReadNumberInput(ConfigEntry<string> entry, float fallback)
		{
			return Math.Max(0f, ParseConfigNumber(entry?.Value, fallback));
		}

		private static string[] CreateQuarterStepOptions()
		{
			string[] array = new string[37];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = FormatQuarterStep(1f + (float)i * 0.25f);
			}
			return array;
		}

		private static string FormatQuarterStep(float value)
		{
			return ((float)Math.Round(Math.Max(1f, Math.Min(10f, value)) * 4f) / 4f).ToString("0.##", CultureInfo.InvariantCulture);
		}

		private static float ParseConfigNumber(string text, float fallback)
		{
			string text2 = text?.Trim().Replace(',', '.');
			if (!string.IsNullOrEmpty(text2) && float.TryParse(text2, NumberStyles.Float, CultureInfo.InvariantCulture, out var result) && !float.IsNaN(result) && !float.IsInfinity(result))
			{
				return result;
			}
			return fallback;
		}

		private static float ClampOneToTen(float value)
		{
			return Math.Max(1f, Math.Min(10f, value));
		}

		internal static void OrderedRange(float configuredMinimum, float configuredMaximum, out float minimum, out float maximum)
		{
			minimum = Math.Min(configuredMinimum, configuredMaximum);
			maximum = Math.Max(configuredMinimum, configuredMaximum);
		}
	}
	internal static class SpawnController
	{
		private const string CasinoPrefabName = "Holy Casino Stone";

		private static readonly Dictionary<string, FieldInfo> ValuableListFields = new Dictionary<string, FieldInfo>(StringComparer.Ordinal)
		{
			["01 Tiny"] = AccessTools.Field(typeof(ValuableDirector), "tinyValuables"),
			["02 Small"] = AccessTools.Field(typeof(ValuableDirector), "smallValuables"),
			["03 Medium"] = AccessTools.Field(typeof(ValuableDirector), "mediumValuables"),
			["04 Big"] = AccessTools.Field(typeof(ValuableDirector), "bigValuables"),
			["05 Wide"] = AccessTools.Field(typeof(ValuableDirector), "wideValuables"),
			["06 Tall"] = AccessTools.Field(typeof(ValuableDirector), "tallValuables"),
			["07 Very Tall"] = AccessTools.Field(typeof(ValuableDirector), "veryTallValuables")
		};

		private static PrefabRef _casinoPrefab;

		private static int _targetCount;

		private static int _spawnedCount;

		private static bool _warnedNoReplacement;

		internal static bool Validate(out string error)
		{
			if (AccessTools.Method(typeof(ValuableDirector), "SpawnValuable", new Type[3]
			{
				typeof(PrefabRef),
				typeof(ValuableVolume),
				typeof(string)
			}, (Type[])null) == null)
			{
				error = "ValuableDirector.SpawnValuable(PrefabRef, ValuableVolume, string) was not found.";
				return false;
			}
			if (AccessTools.Method(typeof(ValuableDirector), "SetupHost", (Type[])null, (Type[])null) == null || AccessTools.Method(typeof(ValuableDirector), "VolumesAndSwitchSetup", (Type[])null, (Type[])null) == null)
			{
				error = "the current ValuableDirector setup methods were not found.";
				return false;
			}
			foreach (KeyValuePair<string, FieldInfo> valuableListField in ValuableListFields)
			{
				if (valuableListField.Value == null || valuableListField.Value.FieldType != typeof(List<PrefabRef>))
				{
					error = "the ValuableDirector list for " + valuableListField.Key + " was not found as List<PrefabRef>.";
					return false;
				}
			}
			error = null;
			return true;
		}

		internal static void BeginLevel()
		{
			_casinoPrefab = ((IEnumerable<PrefabRef>)Valuables.RegisteredValuables).FirstOrDefault((Func<PrefabRef, bool>)IsCasinoPrefab) ?? ((IEnumerable<PrefabRef>)Valuables.AllValuables).FirstOrDefault((Func<PrefabRef, bool>)IsCasinoPrefab);
			_spawnedCount = 0;
			_warnedNoReplacement = false;
			int value = Plugin.Instance.MaximumSpawnInLevel.Value;
			int num = Math.Min(Plugin.Instance.MinimumSpawnInLevel.Value, value);
			_targetCount = Random.Range(num, value + 1);
			if (_casinoPrefab == null)
			{
				_targetCount = 0;
				Plugin.Log.LogError((object)"Could not find REPOLib's registered Holy Casino Stone prefab. Spawn limits cannot be applied this level.");
			}
			else
			{
				Plugin.Log.LogInfo((object)($"Holy Casino Stone spawn target for this level: {_targetCount} " + $"(configured {num}-{value})."));
			}
		}

		internal static bool PrepareSpawn(ValuableDirector director, ref PrefabRef selectedPrefab, string path)
		{
			if (_casinoPrefab == null)
			{
				return false;
			}
			if (_spawnedCount < _targetCount)
			{
				selectedPrefab = _casinoPrefab;
				return true;
			}
			if (!IsCasinoPrefab(selectedPrefab))
			{
				return false;
			}
			PrefabRef val = FindReplacement(director, path);
			if (val != null)
			{
				selectedPrefab = val;
				return false;
			}
			if (!_warnedNoReplacement)
			{
				_warnedNoReplacement = true;
				Plugin.Log.LogWarning((object)"The game selected an extra Holy Casino Stone, but no same-size replacement was available. The configured maximum may be exceeded for this level.");
			}
			return true;
		}

		internal static void CountCasinoSpawn(bool casinoSpawn)
		{
			if (casinoSpawn)
			{
				_spawnedCount++;
			}
		}

		internal static void EndLevelSetup()
		{
			if (_casinoPrefab != null)
			{
				if (_spawnedCount < _targetCount)
				{
					Plugin.Log.LogWarning((object)$"Only {_spawnedCount} of the {_targetCount} targeted Holy Casino Stones could be spawned because the level generated too few valuable slots.");
				}
				else
				{
					Plugin.Log.LogInfo((object)$"Holy Casino Stone level spawn count finalized at {_spawnedCount}.");
				}
			}
		}

		private static PrefabRef FindReplacement(ValuableDirector director, string path)
		{
			if ((Object)(object)director == (Object)null || path == null || !ValuableListFields.TryGetValue(path, out var value) || !(value?.GetValue(director) is List<PrefabRef> source))
			{
				return null;
			}
			List<PrefabRef> list = source.Where((PrefabRef candidate) => candidate != null && !IsCasinoPrefab(candidate)).ToList();
			if (list.Count != 0)
			{
				return list[Random.Range(0, list.Count)];
			}
			return null;
		}

		private static bool IsCasinoPrefab(PrefabRef prefab)
		{
			if (prefab != null)
			{
				return string.Equals(((PrefabRef<GameObject>)(object)prefab).PrefabName, "Holy Casino Stone", StringComparison.OrdinalIgnoreCase);
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(ValuableDirector))]
	internal static class ValuableDirectorSpawnPatches
	{
		[HarmonyPatch("SetupHost")]
		[HarmonyPrefix]
		private static void BeginHostSetup()
		{
			SpawnController.BeginLevel();
		}

		[HarmonyPatch("SpawnValuable")]
		[HarmonyPrefix]
		private static void ControlCasinoSelection(ValuableDirector __instance, ref PrefabRef _valuable, string _path, out bool __state)
		{
			__state = SpawnController.PrepareSpawn(__instance, ref _valuable, _path);
		}

		[HarmonyPatch("SpawnValuable")]
		[HarmonyPostfix]
		private static void CountCasinoSelection(bool __state)
		{
			SpawnController.CountCasinoSpawn(__state);
		}

		[HarmonyPatch("VolumesAndSwitchSetup")]
		[HarmonyPrefix]
		private static void ReportFinalSpawnCount()
		{
			SpawnController.EndLevelSetup();
		}
	}
	internal static class StoneProgressionRuntime
	{
		private sealed class StoneState
		{
			internal bool IncrementalInitialized;

			internal float IncrementalMultiplier;

			internal bool DecrementalInitialized;

			internal float DecrementalDivisor;
		}

		private static readonly Dictionary<int, StoneState> States = new Dictionary<int, StoneState>();

		private static bool _wasInLevel;

		internal static void ApplyConfiguredStartingValue(Krild_Holy_Casino_Stone stone, ValuableObject valuable)
		{
			if (!((Object)(object)stone == (Object)null) && !((Object)(object)valuable == (Object)null) && SemiFunc.IsMasterClientOrSingleplayer())
			{
				GetState(stone);
				float originalValue = CasinoStoneRuntime.GetOriginalValue(valuable);
				float currentValue = CasinoStoneRuntime.GetCurrentValue(valuable);
				float num = CasinoStoneRuntime.SanitizeDollarValue(Plugin.ReadNumberInput(Plugin.Instance.StoneStartValue, 500f), 500f);
				CasinoStoneRuntime.SetLocalDollarValue(valuable, num);
				CasinoStoneRuntime.SetOriginalCounter(stone, num);
				LevelScalingCompatibility.OnValueReplaced(valuable, originalValue, currentValue, num, num, 0f, (Object)(object)((Component)valuable).GetComponent<SurplusValuable>() != (Object)null);
			}
		}

		internal static bool TryCalculateOutcome(Krild_Holy_Casino_Stone stone, float baseValue, bool good, out float newValue, out string outcome, out bool setValue)
		{
			StoneState state = GetState(stone);
			Plugin instance = Plugin.Instance;
			if (good)
			{
				return TryCalculateGood(state, instance, baseValue, out newValue, out outcome, out setValue);
			}
			return TryCalculateBad(state, instance, baseValue, out newValue, out outcome, out setValue);
		}

		internal static void Forget(Krild_Holy_Casino_Stone stone)
		{
			if ((Object)(object)stone != (Object)null)
			{
				States.Remove(((Object)stone).GetInstanceID());
			}
		}

		internal static void UpdateLifecycle()
		{
			bool num = SemiFunc.RunIsLevel();
			if (!num && _wasInLevel)
			{
				States.Clear();
			}
			_wasInLevel = num;
		}

		internal static void Clear()
		{
			States.Clear();
			_wasInLevel = false;
		}

		private static bool TryCalculateGood(StoneState state, Plugin config, float baseValue, out float newValue, out string outcome, out bool setValue)
		{
			int num = (config.GoodMultiply.Value ? 1 : 0) + (config.GoodIncremental.Value ? 1 : 0) + (config.GoodAddition.Value ? 1 : 0);
			if (num == 0)
			{
				newValue = baseValue;
				outcome = "no enabled GOOD operation";
				setValue = false;
				return false;
			}
			int num2 = Random.Range(0, num);
			if (config.GoodMultiply.Value && num2-- == 0)
			{
				float num3 = RandomConfiguredRange(Plugin.ReadQuarterStep(config.GoodMinimumMultiplier), Plugin.ReadQuarterStep(config.GoodMaximumMultiplier));
				newValue = baseValue * num3;
				outcome = $"GOOD multiply {num3:0.###}x";
				setValue = true;
				return true;
			}
			if (config.GoodIncremental.Value && num2-- == 0)
			{
				if (!state.IncrementalInitialized)
				{
					state.IncrementalMultiplier = RandomConfiguredRange(Plugin.ReadQuarterStep(config.GoodIncrementalMinimumStart), Plugin.ReadQuarterStep(config.GoodIncrementalMaximumStart));
					state.IncrementalInitialized = true;
				}
				float incrementalMultiplier = state.IncrementalMultiplier;
				float num4 = RandomConfiguredRange(Plugin.ReadQuarterStep(config.GoodIncrementalMinimumStep), Plugin.ReadQuarterStep(config.GoodIncrementalMaximumStep));
				state.IncrementalMultiplier = SanitizeProgressionFactor(incrementalMultiplier * num4);
				newValue = baseValue * incrementalMultiplier;
				outcome = $"GOOD incremental {incrementalMultiplier:0.###}x; next {state.IncrementalMultiplier:0.###}x";
				setValue = true;
				return true;
			}
			float num5 = Mathf.Max(0f, RandomConfiguredRange(Plugin.ReadNumberInput(config.GoodMinimumAddition, 100f), Plugin.ReadNumberInput(config.GoodMaximumAddition, 1000f)));
			newValue = baseValue + num5;
			outcome = $"GOOD addition ${num5:0}";
			setValue = true;
			return true;
		}

		private static bool TryCalculateBad(StoneState state, Plugin config, float baseValue, out float newValue, out string outcome, out bool setValue)
		{
			bool value = config.BadResetIncremental.Value;
			if (value)
			{
				state.IncrementalInitialized = false;
				state.IncrementalMultiplier = 0f;
			}
			int num = (config.BadBackToStartingValue.Value ? 1 : 0) + (config.BadDivide.Value ? 1 : 0) + (config.BadDecremental.Value ? 1 : 0) + (config.BadSubtract.Value ? 1 : 0);
			if (num == 0)
			{
				newValue = baseValue;
				outcome = (value ? "BAD reset incremental; no enabled value operation" : "no enabled BAD operation");
				setValue = false;
				return value;
			}
			int num2 = Random.Range(0, num);
			if (config.BadBackToStartingValue.Value && num2-- == 0)
			{
				newValue = CasinoStoneRuntime.SanitizeDollarValue(Plugin.ReadNumberInput(config.StoneStartValue, 500f), 500f);
				outcome = "BAD back to starting value";
			}
			else if (config.BadDivide.Value && num2-- == 0)
			{
				float num3 = RandomConfiguredRange(Plugin.ReadQuarterStep(config.BadMinimumDivideBy), Plugin.ReadQuarterStep(config.BadMaximumDivideBy));
				newValue = baseValue / num3;
				outcome = $"BAD divide by {num3:0.###}";
			}
			else if (config.BadDecremental.Value && num2-- == 0)
			{
				if (!state.DecrementalInitialized)
				{
					state.DecrementalDivisor = RandomConfiguredRange(Plugin.ReadQuarterStep(config.BadDecrementalMinimumStart), Plugin.ReadQuarterStep(config.BadDecrementalMaximumStart));
					state.DecrementalInitialized = true;
				}
				float decrementalDivisor = state.DecrementalDivisor;
				float num4 = RandomConfiguredRange(Plugin.ReadQuarterStep(config.BadDecrementalMinimumStep), Plugin.ReadQuarterStep(config.BadDecrementalMaximumStep));
				state.DecrementalDivisor = SanitizeProgressionFactor(decrementalDivisor * num4);
				newValue = baseValue / decrementalDivisor;
				outcome = $"BAD decremental divide by {decrementalDivisor:0.###}; next {state.DecrementalDivisor:0.###}";
			}
			else
			{
				float num5 = Mathf.Max(0f, RandomConfiguredRange(Plugin.ReadNumberInput(config.BadMinimumSubtraction, 100f), Plugin.ReadNumberInput(config.BadMaximumSubtraction, 1000f)));
				newValue = baseValue - num5;
				outcome = $"BAD subtraction ${num5:0}";
			}
			if (value)
			{
				outcome += "; reset incremental";
			}
			setValue = true;
			return true;
		}

		private static StoneState GetState(Krild_Holy_Casino_Stone stone)
		{
			int instanceID = ((Object)stone).GetInstanceID();
			if (!States.TryGetValue(instanceID, out var value))
			{
				value = new StoneState();
				States[instanceID] = value;
			}
			return value;
		}

		private static float RandomConfiguredRange(float configuredMinimum, float configuredMaximum)
		{
			Plugin.OrderedRange(configuredMinimum, configuredMaximum, out var minimum, out var maximum);
			return Random.Range(minimum, maximum);
		}

		private static float SanitizeProgressionFactor(float value)
		{
			if (float.IsNaN(value) || float.IsInfinity(value))
			{
				return 2E+09f;
			}
			return Mathf.Clamp(value, 1f, 2E+09f);
		}
	}
}