Decompiled source of 666PityPoster v0.1.3

BepInEx/plugins/pharmacomaniac-666Poster/666Poster.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CloverPitMods.Bridge;
using CloverPitMods.Configuration;
using CloverPitMods.Diagnostics;
using CloverPitMods.Patches;
using CloverPitMods.Poster;
using CloverPitMods.Tracking;
using CloverPitMods.Utils;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using TMPro;
using UnityEngine;
using UnityEngine.Profiling;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("666Poster")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.1.3.0")]
[assembly: AssemblyInformationalVersion("0.1.3+b20661ea92f2b044aa2b6b8f4e2d67f0c2da4be1")]
[assembly: AssemblyProduct("666Poster")]
[assembly: AssemblyTitle("666Poster")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.1.3.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.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]
	[Microsoft.CodeAnalysis.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]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace CloverPitMods
{
	[BepInPlugin("com.pharmacomaniac.cloverpit.666PityPoster", "666PityPoster", "0.1.3")]
	public class Plugin : BaseUnityPlugin
	{
		private Harmony? _harmony;

		private void Awake()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			ModConfig.Initialize(((BaseUnityPlugin)this).Config);
			_harmony = new Harmony("com.pharmacomaniac.cloverpit.666PityPoster.harmony");
			_harmony.PatchAll();
			LogShim.Attach(((BaseUnityPlugin)this).Logger);
			PerfTracker.AttachLogger(((BaseUnityPlugin)this).Logger);
			SixSixSixTracker.AttachLogger(((BaseUnityPlugin)this).Logger);
			SixSixSixTracker.Install();
			GameObject val = new GameObject("PosterCloneController");
			Object.DontDestroyOnLoad((Object)(object)val);
			val.AddComponent<PosterCloneController>();
			val.AddComponent<SixSixSixBridge>();
			((BaseUnityPlugin)this).Logger.LogInfo((object)"[PosterClone] Initialized");
		}

		private void OnDestroy()
		{
			try
			{
				Harmony? harmony = _harmony;
				if (harmony != null)
				{
					harmony.UnpatchSelf();
				}
			}
			catch
			{
			}
		}
	}
}
namespace CloverPitMods.Utils
{
	internal static class HarmonyIds
	{
		public const string PluginGuid = "com.pharmacomaniac.cloverpit.666PityPoster";

		public const string PluginName = "666PityPoster";

		public const string PluginVersion = "0.1.3";

		public const string PosterHarmonyId = "com.pharmacomaniac.cloverpit.666PityPoster.harmony";

		public const string TrackerHarmonyId = "com.pharmacomaniac.cloverpit.666PityPoster.tracker";
	}
	internal static class LogShim
	{
		private static ManualLogSource? _logger;

		public static ManualLogSource? Logger => _logger;

		public static void Attach(ManualLogSource logger)
		{
			_logger = logger;
		}

		public static void Debug(string message)
		{
			ManualLogSource? logger = _logger;
			if (logger != null)
			{
				logger.LogDebug((object)message);
			}
		}

		public static void Info(string message)
		{
			ManualLogSource? logger = _logger;
			if (logger != null)
			{
				logger.LogInfo((object)message);
			}
		}

		public static void Warn(string message)
		{
			ManualLogSource? logger = _logger;
			if (logger != null)
			{
				logger.LogWarning((object)message);
			}
		}

		public static void Error(string message)
		{
			ManualLogSource? logger = _logger;
			if (logger != null)
			{
				logger.LogError((object)message);
			}
		}
	}
	internal static class ReflectionUtil
	{
		private const BindingFlags MemberFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;

		internal static object? TryGetMemberValue(object target, params string[] memberNames)
		{
			if (target == null || memberNames == null || memberNames.Length == 0)
			{
				return null;
			}
			Type type = target.GetType();
			foreach (string text in memberNames)
			{
				if (!string.IsNullOrEmpty(text))
				{
					FieldInfo field = type.GetField(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null)
					{
						return field.GetValue(target);
					}
					PropertyInfo property = type.GetProperty(text, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (property != null && property.CanRead)
					{
						return property.GetValue(target);
					}
				}
			}
			return null;
		}

		internal static bool TrySetMemberValue(object target, object? value, params string[] memberNames)
		{
			if (target == null || memberNames == null || memberNames.Length == 0)
			{
				return false;
			}
			foreach (string text in memberNames)
			{
				if (!string.IsNullOrEmpty(text) && TrySetMemberValueInternal(target, value, text))
				{
					return true;
				}
			}
			return false;
		}

		internal static void CopyMemberValue(object source, object destination, bool cloneLists, params string[] memberNames)
		{
			if (source == null || destination == null || memberNames == null || memberNames.Length == 0)
			{
				return;
			}
			object obj = TryGetMemberValue(source, memberNames);
			if (obj != null)
			{
				if (cloneLists && obj is IList list)
				{
					IList list2 = CloneList(list);
					TrySetMemberValue(destination, list2 ?? list, memberNames);
				}
				else
				{
					TrySetMemberValue(destination, obj, memberNames);
				}
			}
		}

		internal static IList? CloneList(IList original)
		{
			if (original == null)
			{
				return null;
			}
			try
			{
				Type type = original.GetType();
				if (type.IsArray)
				{
					Type elementType = type.GetElementType() ?? typeof(object);
					Array array = Array.CreateInstance(elementType, original.Count);
					for (int i = 0; i < original.Count; i++)
					{
						array.SetValue(original[i], i);
					}
					return array;
				}
				IList list = Activator.CreateInstance(type) as IList;
				if (list == null)
				{
					Type type2 = type.GetInterfaces().FirstOrDefault((Type it) => it.IsGenericType && it.GetGenericTypeDefinition() == typeof(IList<>));
					if (type2 != null)
					{
						Type type3 = type2.GetGenericArguments()[0];
						Type type4 = typeof(List<>).MakeGenericType(type3);
						list = Activator.CreateInstance(type4) as IList;
					}
				}
				if (list != null)
				{
					foreach (object item in original)
					{
						list.Add(item);
					}
					return list;
				}
			}
			catch
			{
			}
			return original;
		}

		internal static bool ListContainsType(IList list, Type targetType)
		{
			if (list == null || targetType == null)
			{
				return false;
			}
			foreach (object item in list)
			{
				if (item != null && targetType.IsInstanceOfType(item))
				{
					return true;
				}
			}
			return false;
		}

		internal static object? CreateInstanceFlexible(Type type)
		{
			try
			{
				if (typeof(ScriptableObject).IsAssignableFrom(type))
				{
					return ScriptableObject.CreateInstance(type);
				}
				return Activator.CreateInstance(type);
			}
			catch
			{
				return null;
			}
		}

		internal static bool TryConvertValue(object? value, Type targetType, out object? converted)
		{
			Type type = Nullable.GetUnderlyingType(targetType) ?? targetType;
			if (value == null)
			{
				if (!type.IsValueType || Nullable.GetUnderlyingType(targetType) != null)
				{
					converted = null;
					return true;
				}
				converted = null;
				return false;
			}
			if (type.IsInstanceOfType(value))
			{
				converted = value;
				return true;
			}
			try
			{
				if (value is IConvertible && typeof(IConvertible).IsAssignableFrom(type))
				{
					converted = Convert.ChangeType(value, type, CultureInfo.InvariantCulture);
					return true;
				}
			}
			catch
			{
			}
			if (type.IsAssignableFrom(value.GetType()))
			{
				converted = value;
				return true;
			}
			converted = null;
			return false;
		}

		private static bool TrySetMemberValueInternal(object target, object? value, string memberName)
		{
			Type type = target.GetType();
			FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null && TryConvertValue(value, field.FieldType, out object converted))
			{
				field.SetValue(target, converted);
				return true;
			}
			PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (property != null && property.CanWrite && TryConvertValue(value, property.PropertyType, out object converted2))
			{
				property.SetValue(target, converted2);
				return true;
			}
			return false;
		}
	}
	internal static class TmpUtil
	{
		internal static void MirrorStyle(TextMeshProUGUI source, TextMeshProUGUI destination, bool forceCenter = true)
		{
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_0109: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)source == (Object)null) && !((Object)(object)destination == (Object)null))
			{
				if ((Object)(object)((TMP_Text)source).font != (Object)null)
				{
					((TMP_Text)destination).font = ((TMP_Text)source).font;
				}
				Material val = ((TMP_Text)source).fontSharedMaterial ?? ((TMP_Text)source).fontMaterial;
				if ((Object)(object)val != (Object)null)
				{
					Material fontMaterial = (((TMP_Text)destination).fontSharedMaterial = new Material(val));
					((TMP_Text)destination).fontMaterial = fontMaterial;
				}
				((TMP_Text)destination).outlineWidth = ((TMP_Text)source).outlineWidth;
				((TMP_Text)destination).outlineColor = ((TMP_Text)source).outlineColor;
				((TMP_Text)destination).enableAutoSizing = ((TMP_Text)source).enableAutoSizing;
				((TMP_Text)destination).fontSizeMin = ((TMP_Text)source).fontSizeMin;
				((TMP_Text)destination).fontSizeMax = ((TMP_Text)source).fontSizeMax;
				((TMP_Text)destination).fontSize = ((TMP_Text)source).fontSize;
				((TMP_Text)destination).fontStyle = ((TMP_Text)source).fontStyle;
				((TMP_Text)destination).fontWeight = ((TMP_Text)source).fontWeight;
				((TMP_Text)destination).richText = ((TMP_Text)source).richText;
				((TMP_Text)destination).parseCtrlCharacters = ((TMP_Text)source).parseCtrlCharacters;
				((TMP_Text)destination).overrideColorTags = ((TMP_Text)source).overrideColorTags;
				((TMP_Text)destination).textWrappingMode = ((TMP_Text)source).textWrappingMode;
				((TMP_Text)destination).overflowMode = ((TMP_Text)source).overflowMode;
				((TMP_Text)destination).geometrySortingOrder = ((TMP_Text)source).geometrySortingOrder;
				((TMP_Text)destination).alignment = (TextAlignmentOptions)(forceCenter ? 514 : ((int)((TMP_Text)source).alignment));
				((TMP_Text)destination).horizontalMapping = ((TMP_Text)source).horizontalMapping;
				((TMP_Text)destination).ignoreVisibility = ((TMP_Text)source).ignoreVisibility;
			}
		}

		internal static void StripPosterTextExtras(GameObject textObject)
		{
			if ((Object)(object)textObject == (Object)null)
			{
				return;
			}
			TMP_SpriteAnimator component = textObject.GetComponent<TMP_SpriteAnimator>();
			if ((Object)(object)component != (Object)null)
			{
				Object.DestroyImmediate((Object)(object)component, true);
			}
			UnityUtil.RemoveComponentsByTypeName(textObject, "TextAccessibilityController");
			TMP_SubMeshUI[] componentsInChildren = textObject.GetComponentsInChildren<TMP_SubMeshUI>(true);
			foreach (TMP_SubMeshUI val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)((Component)val).gameObject, true);
				}
			}
		}

		internal static void CleanupExtraChildren(Transform textTransform)
		{
			if ((Object)(object)textTransform == (Object)null)
			{
				return;
			}
			for (int num = textTransform.childCount - 1; num >= 0; num--)
			{
				Transform child = textTransform.GetChild(num);
				if ((Object)(object)child != (Object)null)
				{
					Object.DestroyImmediate((Object)(object)((Component)child).gameObject, true);
				}
			}
		}

		internal static void EnsureTextAnimator(TextMeshProUGUI? source, TextMeshProUGUI target)
		{
			if ((Object)(object)target == (Object)null)
			{
				return;
			}
			GameObject gameObject = ((Component)target).gameObject;
			if ((Object)(object)gameObject == (Object)null)
			{
				return;
			}
			try
			{
				Type type = UnityUtil.FindTypeAnyAssembly("Febucci.UI.TextAnimator");
				if (type == null)
				{
					Debug.LogWarning((object)"[PosterClone] Febucci.UI.TextAnimator type not found; skipping animator setup.");
					return;
				}
				Component val = gameObject.GetComponent(type) ?? gameObject.AddComponent(type);
				if ((Object)(object)val == (Object)null)
				{
					Debug.LogWarning((object)"[PosterClone] Failed to attach Febucci TextAnimator component.");
					return;
				}
				Component val2 = (((Object)(object)source != (Object)null) ? ((Component)source).gameObject.GetComponent(type) : null);
				if ((Object)(object)val2 != (Object)null)
				{
					CopyAnimatorSettings(val2, val);
				}
				ConfigureTextAnimator(val);
			}
			catch (Exception arg)
			{
				Debug.LogWarning((object)$"[PosterClone] Failed to configure Febucci TextAnimator: {arg}");
			}
		}

		private static void CopyAnimatorSettings(Component source, Component destination)
		{
			ReflectionUtil.CopyMemberValue(source, destination, true, "appearanceEffects", "AppearanceEffects");
			ReflectionUtil.CopyMemberValue(source, destination, true, "behaviorEffects", "behaviourEffects", "BehaviorEffects", "BehaviourEffects");
			ReflectionUtil.CopyMemberValue(source, destination, true, "behaviorValues", "BehaviourValues", "BehaviorValues");
			ReflectionUtil.CopyMemberValue(source, destination, false, "behaviorEffects_Enabled", "behaviourEffects_Enabled", "behaviorEffectsEnabled", "behaviourEffectsEnabled", "BehaviorEffectsEnabled", "BehaviourEffectsEnabled");
			ReflectionUtil.CopyMemberValue(source, destination, false, "enabled_globalAppearances", "enabledGlobalAppearances", "EnabledGlobalAppearances");
			ReflectionUtil.CopyMemberValue(source, destination, false, "enabled_globalBehaviors", "enabledGlobalBehaviours", "enabledGlobalBehaviors", "enabledGlobalBehaviours", "EnabledGlobalBehaviors", "EnabledGlobalBehaviours");
			ReflectionUtil.CopyMemberValue(source, destination, false, "forceMeshRefresh", "ForceMeshRefresh");
			ReflectionUtil.CopyMemberValue(source, destination, false, "hasParentCanvas", "HasParentCanvas");
			ReflectionUtil.CopyMemberValue(source, destination, false, "hasText", "HasText");
			ReflectionUtil.CopyMemberValue(source, destination, false, "effectIntensityMultiplier", "EffectIntensityMultiplier");
			ReflectionUtil.CopyMemberValue(source, destination, false, "useDynamicScaling", "UseDynamicScaling");
		}

		private static void ConfigureTextAnimator(Component animator)
		{
			if ((Object)(object)animator == (Object)null)
			{
				return;
			}
			try
			{
				Type type = UnityUtil.FindTypeAnyAssembly("Febucci.UI.Core.BehaviorBase");
				Type type2 = UnityUtil.FindTypeAnyAssembly("Febucci.UI.Core.ShakeBehavior");
				IList list = ReflectionUtil.TryGetMemberValue(animator, "behaviorEffects", "behaviourEffects", "BehaviorEffects", "BehaviourEffects") as IList;
				if (list == null && type != null)
				{
					Type type3 = typeof(List<>).MakeGenericType(type);
					list = Activator.CreateInstance(type3) as IList;
				}
				if (list != null)
				{
					if (type2 != null)
					{
						object obj = null;
						foreach (object item in list)
						{
							if (item != null && type2.IsInstanceOfType(item))
							{
								obj = item;
								break;
							}
						}
						if (obj == null)
						{
							obj = ReflectionUtil.CreateInstanceFlexible(type2);
							if (obj != null)
							{
								list.Add(obj);
							}
						}
						if (obj != null)
						{
							ReflectionUtil.TrySetMemberValue(obj, 0.04f, "shakeDelay", "shake_delay");
							ReflectionUtil.TrySetMemberValue(obj, 0.16f, "shakeStrength", "shake_strength");
						}
					}
					ReflectionUtil.TrySetMemberValue(animator, list, "behaviorEffects", "behaviourEffects", "BehaviorEffects", "BehaviourEffects");
				}
				Type type4 = UnityUtil.FindTypeAnyAssembly("Febucci.UI.Core.BehaviorDefaultValues");
				object obj2 = ((type4 != null) ? ReflectionUtil.CreateInstanceFlexible(type4) : null);
				if (obj2 != null)
				{
					ReflectionUtil.TrySetMemberValue(animator, obj2, "behaviorValues", "BehaviourValues", "BehaviorValues");
				}
				ReflectionUtil.TrySetMemberValue(animator, true, "behaviorEffects_Enabled", "behaviourEffects_Enabled", "behaviorEffectsEnabled", "behaviourEffectsEnabled", "BehaviorEffectsEnabled", "BehaviourEffectsEnabled");
				ReflectionUtil.TrySetMemberValue(animator, true, "enabled_globalAppearances", "enabledGlobalAppearances", "EnabledGlobalAppearances");
				ReflectionUtil.TrySetMemberValue(animator, true, "enabled_globalBehaviors", "enabledGlobalBehaviours", "enabledGlobalBehaviors", "enabledGlobalBehaviours", "EnabledGlobalBehaviors", "EnabledGlobalBehaviours");
				ReflectionUtil.TrySetMemberValue(animator, false, "forceMeshRefresh", "ForceMeshRefresh");
				ReflectionUtil.TrySetMemberValue(animator, true, "hasParentCanvas", "HasParentCanvas");
				ReflectionUtil.TrySetMemberValue(animator, true, "hasText", "HasText");
				ReflectionUtil.TrySetMemberValue(animator, 0.02f, "effectIntensityMultiplier", "EffectIntensityMultiplier");
				ReflectionUtil.TrySetMemberValue(animator, true, "useDynamicScaling", "UseDynamicScaling");
			}
			catch (Exception arg)
			{
				Debug.LogWarning((object)$"[PosterClone] Failed while configuring TextAnimator values: {arg}");
			}
		}
	}
	internal static class UnityUtil
	{
		internal static Type? FindTypeAnyAssembly(string typeName)
		{
			Type type = Type.GetType(typeName);
			if (type != null)
			{
				return type;
			}
			type = Type.GetType(typeName + ", Assembly-CSharp");
			if (type != null)
			{
				return type;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				try
				{
					type = assembly.GetType(typeName);
					if (type != null)
					{
						return type;
					}
				}
				catch
				{
				}
			}
			return null;
		}

		internal static Object[] FindObjectsOfTypeAllSafe(Type type)
		{
			return Resources.FindObjectsOfTypeAll(type);
		}

		internal static void RemoveComponentsByTypeName(GameObject root, string fullNameStartsWith)
		{
			if ((Object)(object)root == (Object)null)
			{
				return;
			}
			Component[] componentsInChildren = root.GetComponentsInChildren<Component>(true);
			Component[] array = componentsInChildren;
			foreach (Component val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					string fullName = ((object)val).GetType().FullName;
					if (!string.IsNullOrEmpty(fullName) && fullName.StartsWith(fullNameStartsWith, StringComparison.Ordinal))
					{
						Object.DestroyImmediate((Object)(object)val, true);
					}
				}
			}
		}
	}
}
namespace CloverPitMods.Tracking
{
	public enum ForcedKind
	{
		None,
		BookedTriple,
		PhoneTriple,
		Suppressed,
		OtherForcedPreRng
	}
	public sealed class ForcedPrediction
	{
		public ForcedKind Kind;

		public int SpinsLeftAfterConsume;

		public int BookedTargetSpinsLeft;
	}
	internal static class ReflectionCache
	{
		private static Type? _gameplayData;

		private static bool _gameplayDataResolved;

		private static MethodInfo? _gameplayDataDebtIndexGet;

		private static bool _gameplayDataDebtIndexGetResolved;

		private static MethodInfo? _gameplayDataMinimumDebtIndexGet;

		private static bool _gameplayDataMinimumDebtIndexGetResolved;

		private static MethodInfo? _gameplayDataSpinsLeftGet;

		private static bool _gameplayDataSpinsLeftGetResolved;

		private static MethodInfo? _gameplayDataBookedSpinGet;

		private static bool _gameplayDataBookedSpinGetResolved;

		private static MethodInfo? _gameplayDataPhoneTriggersCountGet;

		private static bool _gameplayDataPhoneTriggersCountGetResolved;

		private static MethodInfo? _gameplayDataSuppressedSpinsGet;

		private static bool _gameplayDataSuppressedSpinsGetResolved;

		internal static Type? GameplayData
		{
			get
			{
				if (!_gameplayDataResolved)
				{
					_gameplayData = AccessTools.TypeByName("GameplayData");
					_gameplayDataResolved = true;
				}
				return _gameplayData;
			}
		}

		internal static MethodInfo? GameplayDataDebtIndexGet => GetCachedMethod(ref _gameplayDataDebtIndexGet, ref _gameplayDataDebtIndexGetResolved, GameplayData, "DebtIndexGet");

		internal static MethodInfo? GameplayDataMinimumDebtIndexGet => GetCachedMethod(ref _gameplayDataMinimumDebtIndexGet, ref _gameplayDataMinimumDebtIndexGetResolved, GameplayData, "SixSixSix_GetMinimumDebtIndex");

		internal static MethodInfo? GameplayDataSpinsLeftGet => GetCachedMethod(ref _gameplayDataSpinsLeftGet, ref _gameplayDataSpinsLeftGetResolved, GameplayData, "SpinsLeftGet");

		internal static MethodInfo? GameplayDataBookedSpinGet => GetCachedMethod(ref _gameplayDataBookedSpinGet, ref _gameplayDataBookedSpinGetResolved, GameplayData, "SixSixSix_BookedSpinGet");

		internal static MethodInfo? GameplayDataPhoneTriggersCountGet => GetCachedMethod(ref _gameplayDataPhoneTriggersCountGet, ref _gameplayDataPhoneTriggersCountGetResolved, GameplayData, "Powerup_PossessedPhone_TriggersCount_Get");

		internal static MethodInfo? GameplayDataSuppressedSpinsGet => GetCachedMethod(ref _gameplayDataSuppressedSpinsGet, ref _gameplayDataSuppressedSpinsGetResolved, GameplayData, "SixSixSix_SuppressedSpinsGet");

		internal static void Prewarm()
		{
			_ = GameplayData;
			_ = GameplayDataDebtIndexGet;
			_ = GameplayDataMinimumDebtIndexGet;
			_ = GameplayDataSpinsLeftGet;
			_ = GameplayDataBookedSpinGet;
			_ = GameplayDataPhoneTriggersCountGet;
			_ = GameplayDataSuppressedSpinsGet;
		}

		private static MethodInfo? GetCachedMethod(ref MethodInfo? storage, ref bool resolved, Type? type, string methodName)
		{
			if (!resolved)
			{
				storage = ((type != null) ? AccessTools.Method(type, methodName, (Type[])null, (Type[])null) : null);
				resolved = true;
			}
			return storage;
		}
	}
	public static class SixSixSixTracker
	{
		private static ManualLogSource? _log;

		private static bool _loggedEligibilityTypeMissing;

		private static bool _loggedEligibilityMethodMissing;

		private static bool _loggedEligibilityConversionFailure;

		private static bool _loggedFirstSpin;

		private static bool _lastEligibilityState;

		private static bool _installed;

		public static double CurrentLiveTripleChance { get; private set; }

		public static int EligibleSpinCount { get; private set; }

		public static int SpinsSinceLastAny666 { get; private set; }

		public static int SpinsSinceLastTriple666 { get; private set; }

		public static int SessionId { get; private set; }

		public static event Action<double>? OnLiveTripleUpdated;

		public static event Action<ForcedPrediction>? OnForcedPrediction;

		public static event Action<SpinSnapshot>? OnSpinEvaluated;

		public static event Action? OnSessionStarted;

		public static event Action? OnSessionEnded;

		public static void AttachLogger(ManualLogSource log)
		{
			_log = log;
		}

		public static void Install()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			if (_installed)
			{
				return;
			}
			_installed = true;
			Harmony val = new Harmony("com.pharmacomaniac.cloverpit.666PityPoster.tracker");
			try
			{
				val.CreateClassProcessor(typeof(GameplayLifecyclePatches)).Patch();
				val.CreateClassProcessor(typeof(LiveTriplePatch)).Patch();
				val.CreateClassProcessor(typeof(SpinPredictionPatch)).Patch();
				val.CreateClassProcessor(typeof(GetSpinCountPatch)).Patch();
				LogDebug("SixSixSix tracker patches applied.");
			}
			catch (Exception arg)
			{
				LogError($"Failed to install SixSixSix tracker patches: {arg}");
			}
		}

		internal static void ResetSessionCounters()
		{
			CurrentLiveTripleChance = 0.0;
			EligibleSpinCount = 0;
			SpinsSinceLastAny666 = 0;
			SpinsSinceLastTriple666 = 0;
			SpinScratch.ResetFrame();
		}

		internal static void HandleSessionStart()
		{
			if (IsPlatformInitialized())
			{
				SessionId++;
				ResetSessionCounters();
				Prewarm();
				SixSixSixTracker.OnSessionStarted?.Invoke();
			}
		}

		internal static void HandleSessionEnd()
		{
			SixSixSixTracker.OnSessionEnded?.Invoke();
		}

		internal static void Prewarm()
		{
			ReflectionCache.Prewarm();
		}

		internal static void PublishPrediction(bool calledByAutoSpin, bool eligible, ForcedKind kind, int nextSpinsLeft, int bookedTarget)
		{
			LogDebug($"Spin prediction → Eligible={eligible}, ForcedKind={kind}, Auto={calledByAutoSpin}, SpinId={SpinScratch.CurrentSpinId}");
			SixSixSixTracker.OnForcedPrediction?.Invoke(new ForcedPrediction
			{
				Kind = kind,
				SpinsLeftAfterConsume = nextSpinsLeft,
				BookedTargetSpinsLeft = bookedTarget
			});
		}

		internal static void NotifyLiveTriple(double chance)
		{
			CurrentLiveTripleChance = chance;
			SixSixSixTracker.OnLiveTripleUpdated?.Invoke(chance);
		}

		internal static void NotifySpinEvaluated(SpinSnapshot snapshot, bool updateValues, int spinId)
		{
			if (snapshot.EligibleForTripleGate)
			{
				EligibleSpinCount++;
				if (snapshot.Outcome == 1 || snapshot.Outcome == 2 || snapshot.Outcome == 3)
				{
					SpinsSinceLastAny666 = 0;
				}
				else
				{
					SpinsSinceLastAny666++;
				}
				if (snapshot.Outcome == 3)
				{
					SpinsSinceLastTriple666 = 0;
				}
				else
				{
					SpinsSinceLastTriple666++;
				}
			}
			SixSixSixTracker.OnSpinEvaluated?.Invoke(snapshot);
			if (!_loggedFirstSpin)
			{
				LogDebug($"First spin observed. Eligible={snapshot.EligibleForTripleGate}, Forced={snapshot.Forced}, Outcome={snapshot.Outcome}");
				_loggedFirstSpin = true;
			}
			LogDebug($"Spin snapshot → Eligible={snapshot.EligibleForTripleGate}, Forced={snapshot.ForcedWhy}, Outcome={snapshot.Outcome}, TripleChance={snapshot.Triple:0.0000}, SpinId={spinId}, updateValues={updateValues}");
		}

		internal static bool Is666EligibleNow()
		{
			using (PerfTracker.Track("Spin.Reflection"))
			{
				try
				{
					LogDebug("Is666EligibleNow invoked.");
					Type gameplayData = ReflectionCache.GameplayData;
					if (gameplayData == null)
					{
						if (!_loggedEligibilityTypeMissing)
						{
							LogWarning("GameplayData type not found; cannot evaluate 666 eligibility.");
							_loggedEligibilityTypeMissing = true;
						}
						return false;
					}
					MethodInfo gameplayDataDebtIndexGet = ReflectionCache.GameplayDataDebtIndexGet;
					MethodInfo gameplayDataMinimumDebtIndexGet = ReflectionCache.GameplayDataMinimumDebtIndexGet;
					if (gameplayDataDebtIndexGet == null || gameplayDataMinimumDebtIndexGet == null)
					{
						if (!_loggedEligibilityMethodMissing)
						{
							LogWarning("Could not locate DebtIndexGet / SixSixSix_GetMinimumDebtIndex; 666 eligibility will always be false.");
							_loggedEligibilityMethodMissing = true;
						}
						return false;
					}
					object obj = gameplayDataDebtIndexGet.Invoke(null, null);
					object obj2 = gameplayDataMinimumDebtIndexGet.Invoke(null, null);
					LogDebug("Eligibility sample → DebtIndex=" + FormatNumeric(obj) + " (" + (obj?.GetType().FullName ?? "null") + ") vs Min=" + FormatNumeric(obj2) + " (" + (obj2?.GetType().FullName ?? "null") + ")");
					if (TryCompareNumeric(obj, obj2, out var comparison))
					{
						bool flag = comparison >= 0;
						LogDebug($"Eligibility compare result → comparison={comparison} (eligible={flag})");
						if (flag != _lastEligibilityState)
						{
							string text = ((comparison == 0) ? "eligible (equal to minimum)" : (flag ? "eligible" : "ineligible"));
							LogInfo("Eligibility toggled → " + text + " (DebtIndex: " + FormatNumeric(obj) + " vs Min: " + FormatNumeric(obj2) + ")");
							_lastEligibilityState = flag;
						}
						return flag;
					}
					if (!_loggedEligibilityConversionFailure)
					{
						LogWarning("Unable to interpret DebtIndex values (types: " + (obj?.GetType().FullName ?? "null") + " vs " + (obj2?.GetType().FullName ?? "null") + "); treating as ineligible.");
						_loggedEligibilityConversionFailure = true;
					}
				}
				catch (Exception arg)
				{
					LogDebug($"Is666EligibleNow threw: {arg}");
				}
				return false;
			}
		}

		internal static void LogDebug(string message)
		{
			ManualLogSource? log = _log;
			if (log != null)
			{
				log.LogDebug((object)message);
			}
		}

		internal static void LogInfo(string message)
		{
			ManualLogSource? log = _log;
			if (log != null)
			{
				log.LogInfo((object)message);
			}
		}

		internal static void LogWarning(string message)
		{
			ManualLogSource? log = _log;
			if (log != null)
			{
				log.LogWarning((object)message);
			}
		}

		internal static void LogError(string message)
		{
			ManualLogSource? log = _log;
			if (log != null)
			{
				log.LogError((object)message);
			}
		}

		private static bool IsPlatformInitialized()
		{
			try
			{
				Type type = AccessTools.TypeByName("Panik.PlatformMaster") ?? AccessTools.TypeByName("PlatformMaster");
				return (bool)(((type != null) ? AccessTools.Method(type, "IsInitialized", (Type[])null, (Type[])null) : null)?.Invoke(null, null) ?? ((object)true));
			}
			catch
			{
				return true;
			}
		}

		private static bool TryCompareNumeric(object? left, object? right, out int comparison)
		{
			if (TryToBigInteger(left, out var result) && TryToBigInteger(right, out var result2))
			{
				comparison = result.CompareTo(result2);
				return true;
			}
			if (TryToDouble(left, out var result3) && TryToDouble(right, out var result4))
			{
				comparison = result3.CompareTo(result4);
				return true;
			}
			comparison = 0;
			return false;
		}

		private static bool TryToBigInteger(object? value, out BigInteger result)
		{
			if (value != null)
			{
				if (!(value is BigInteger bigInteger))
				{
					if (!(value is int num))
					{
						if (!(value is uint num2))
						{
							if (!(value is long num3))
							{
								if (!(value is ulong num4))
								{
									if (!(value is short num5))
									{
										if (!(value is ushort num6))
										{
											if (!(value is byte b))
											{
												if (!(value is sbyte b2))
												{
													if (!(value is decimal num7))
													{
														if (!(value is double num8))
														{
															if (!(value is float num9))
															{
																if (value is string text)
																{
																	string value2 = text;
																	if (BigInteger.TryParse(value2, NumberStyles.Integer, CultureInfo.InvariantCulture, out var result2))
																	{
																		result = result2;
																		return true;
																	}
																}
															}
															else
															{
																float num10 = num9;
																if (!float.IsNaN(num10) && !float.IsInfinity(num10))
																{
																	result = new BigInteger(Math.Round(num10));
																	return true;
																}
															}
														}
														else
														{
															double num11 = num8;
															if (!double.IsNaN(num11) && !double.IsInfinity(num11))
															{
																result = new BigInteger(Math.Round(num11));
																return true;
															}
														}
														try
														{
															if (value is IConvertible convertible)
															{
																double num12 = convertible.ToDouble(CultureInfo.InvariantCulture);
																if (!double.IsNaN(num12) && !double.IsInfinity(num12))
																{
																	result = new BigInteger(Math.Round(num12));
																	return true;
																}
															}
														}
														catch
														{
														}
														result = default(BigInteger);
														return false;
													}
													decimal value3 = num7;
													result = new BigInteger(value3);
													return true;
												}
												sbyte value4 = b2;
												result = new BigInteger(value4);
												return true;
											}
											byte value5 = b;
											result = new BigInteger(value5);
											return true;
										}
										ushort value6 = num6;
										result = new BigInteger(value6);
										return true;
									}
									short value7 = num5;
									result = new BigInteger(value7);
									return true;
								}
								ulong value8 = num4;
								result = new BigInteger(value8);
								return true;
							}
							long value9 = num3;
							result = new BigInteger(value9);
							return true;
						}
						uint value10 = num2;
						result = new BigInteger(value10);
						return true;
					}
					int value11 = num;
					result = new BigInteger(value11);
					return true;
				}
				BigInteger bigInteger2 = bigInteger;
				result = bigInteger2;
				return true;
			}
			result = default(BigInteger);
			return false;
		}

		private static bool TryToDouble(object? value, out double result)
		{
			if (value != null)
			{
				if (!(value is double num))
				{
					if (!(value is float num2))
					{
						if (value is BigInteger bigInteger)
						{
							result = (double)bigInteger;
							if (!double.IsNaN(result))
							{
								return !double.IsInfinity(result);
							}
							return false;
						}
					}
					else if (!float.IsNaN(num2) && !float.IsInfinity(num2))
					{
						result = num2;
						return true;
					}
				}
				else if (!double.IsNaN(num) && !double.IsInfinity(num))
				{
					result = num;
					return true;
				}
				try
				{
					if (value is IConvertible convertible)
					{
						result = convertible.ToDouble(CultureInfo.InvariantCulture);
						return !double.IsNaN(result) && !double.IsInfinity(result);
					}
				}
				catch
				{
				}
				result = 0.0;
				return false;
			}
			result = 0.0;
			return false;
		}

		private static string FormatNumeric(object? value)
		{
			if (value != null)
			{
				if (!(value is double num))
				{
					if (!(value is float num2))
					{
						if (!(value is decimal num3))
						{
							if (!(value is BigInteger bigInteger))
							{
								if (value is IFormattable formattable)
								{
									return formattable.ToString(null, CultureInfo.InvariantCulture);
								}
								return value?.ToString() ?? "<?>";
							}
							return bigInteger.ToString();
						}
						return num3.ToString("0.######", CultureInfo.InvariantCulture);
					}
					return num2.ToString("0.######", CultureInfo.InvariantCulture);
				}
				return num.ToString("0.######", CultureInfo.InvariantCulture);
			}
			return "null";
		}
	}
	internal static class SpinScratch
	{
		[ThreadStatic]
		private static double _tripleCheck;

		[ThreadStatic]
		private static double _doubleCheck;

		[ThreadStatic]
		private static double _singleCheck;

		[ThreadStatic]
		private static int _seenMask;

		[ThreadStatic]
		private static bool _forcedPreRng;

		[ThreadStatic]
		private static bool _forcedBooked;

		[ThreadStatic]
		private static ForcedKind _forcedKind;

		[ThreadStatic]
		private static bool _eligibleThisSpin;

		[ThreadStatic]
		private static bool _queuedEligibility;

		[ThreadStatic]
		private static bool _hasQueuedEligibility;

		[ThreadStatic]
		private static int _currentSpinId;

		[ThreadStatic]
		private static int _publishedSpinId;

		private static int _spinSequence;

		private static int _skippedPreviewCount;

		private static readonly object _skipLock = new object();

		internal static int CurrentSpinId => _currentSpinId;

		internal static void ResetFrame()
		{
			_tripleCheck = (_doubleCheck = (_singleCheck = 0.0));
			_seenMask = 0;
			_forcedPreRng = false;
			_forcedBooked = false;
			_forcedKind = ForcedKind.None;
			_eligibleThisSpin = _hasQueuedEligibility && _queuedEligibility;
			_hasQueuedEligibility = false;
		}

		internal static void BeginSpin(bool eligible)
		{
			_currentSpinId = Interlocked.Increment(ref _spinSequence);
			_publishedSpinId = 0;
			_queuedEligibility = eligible;
			_hasQueuedEligibility = true;
			SixSixSixTracker.LogDebug($"Scratch.BeginSpin → spinId={_currentSpinId}, queuedEligibility={eligible}");
		}

		internal static void MarkForcedPreRng(ForcedKind why = ForcedKind.OtherForcedPreRng)
		{
			_forcedPreRng = true;
			if (_forcedKind == ForcedKind.None || _forcedKind == ForcedKind.OtherForcedPreRng)
			{
				_forcedKind = why;
			}
		}

		internal static void MarkForcedBooked()
		{
			_forcedBooked = true;
			_forcedKind = ForcedKind.BookedTriple;
		}

		internal static void RecordThreshold(double passProb, int ordinal)
		{
			switch (ordinal)
			{
			case 1:
				_tripleCheck = passProb;
				_seenMask |= 1;
				break;
			case 2:
				_doubleCheck = passProb;
				_seenMask |= 2;
				break;
			case 3:
				_singleCheck = passProb;
				_seenMask |= 4;
				break;
			}
		}

		internal static void PublishResult(int outcome, bool updateValues)
		{
			PerfTracker.Scope scope = PerfTracker.Track("Spin.Evaluate");
			bool flag = updateValues;
			try
			{
				if (_currentSpinId == 0)
				{
					_currentSpinId = Interlocked.Increment(ref _spinSequence);
				}
				if (!updateValues)
				{
					EnsureQueuedEligibility();
					IncrementSkippedPreviewCount();
					SixSixSixTracker.LogDebug($"Preview SixSixSix_GetSpinCount ignored (spinId={_currentSpinId}, outcome={outcome}, updateValues=false, skippedPreviews={_skippedPreviewCount})");
					flag = false;
					return;
				}
				if (_currentSpinId != 0 && _publishedSpinId == _currentSpinId)
				{
					SixSixSixTracker.LogDebug($"Duplicate SixSixSix_GetSpinCount publish suppressed (spinId={_currentSpinId}, outcome={outcome}, updateValues=true)");
					ResetFrame();
					flag = false;
					return;
				}
				_publishedSpinId = _currentSpinId;
				SpinSnapshot spinSnapshot = new SpinSnapshot();
				bool flag2 = (spinSnapshot.Forced = _forcedPreRng || _forcedBooked || _seenMask == 0);
				spinSnapshot.ForcedWhy = _forcedKind;
				bool flag3 = _eligibleThisSpin;
				if (!flag3 && updateValues)
				{
					SixSixSixTracker.LogDebug($"Spin {_currentSpinId} requesting eligibility fallback (queued={_eligibleThisSpin}).");
					flag3 = SixSixSixTracker.Is666EligibleNow();
					SixSixSixTracker.LogDebug($"Spin {_currentSpinId} fallback result → eligible={flag3}");
					if (flag3)
					{
						_eligibleThisSpin = true;
						SixSixSixTracker.LogDebug($"Eligibility fallback activated for spin {_currentSpinId}.");
					}
				}
				spinSnapshot.EligibleForTripleGate = flag3;
				if (flag2)
				{
					spinSnapshot.Triple = ((outcome == 3) ? 1 : 0);
					spinSnapshot.Double = ((outcome == 2) ? 1 : 0);
					spinSnapshot.Single = ((outcome == 1) ? 1 : 0);
				}
				else
				{
					double num = Clamp01(_tripleCheck);
					double num2 = Clamp01(_doubleCheck);
					double num3 = Clamp01(_singleCheck);
					spinSnapshot.Triple = num;
					spinSnapshot.Double = (1.0 - num) * num2;
					spinSnapshot.Single = (1.0 - num) * (1.0 - num2) * num3;
				}
				spinSnapshot.Outcome = outcome;
				SixSixSixTracker.NotifySpinEvaluated(spinSnapshot, updateValues, _currentSpinId);
				ResetFrame();
			}
			finally
			{
				scope.Dispose();
				if (scope.WasActive && flag)
				{
					PerfTracker.ReportSpinPressure("Spin.Evaluate", scope.ElapsedMilliseconds, _currentSpinId);
				}
			}
		}

		internal static void PublishLiveTriple(float value)
		{
			double chance = ((value < 0f) ? 0.0 : ((value > 1f) ? 1.0 : ((double)value)));
			SixSixSixTracker.NotifyLiveTriple(chance);
		}

		private static double Clamp01(double value)
		{
			if (!(value < 0.0))
			{
				if (!(value > 1.0))
				{
					return value;
				}
				return 1.0;
			}
			return 0.0;
		}

		private static void EnsureQueuedEligibility()
		{
			if (!_hasQueuedEligibility)
			{
				_queuedEligibility = _eligibleThisSpin;
				_hasQueuedEligibility = true;
			}
		}

		private static void IncrementSkippedPreviewCount()
		{
			lock (_skipLock)
			{
				_skippedPreviewCount++;
			}
		}
	}
	public sealed class SpinSnapshot
	{
		public double Triple;

		public double Double;

		public double Single;

		public int Outcome;

		public bool Forced;

		public ForcedKind ForcedWhy;

		public bool EligibleForTripleGate;
	}
}
namespace CloverPitMods.Poster
{
	internal class PosterCloneController : MonoBehaviour
	{
		private const string SourcePath = "Room/Poster Patterns";

		private const string CloneName = "Poster Patterns (Custom Clone)";

		private GameObject? _clone;

		private Transform? _source;

		private bool _sceneHooked;

		private TextMeshProUGUI? _cloneText;

		public TextMeshProUGUI? PosterText => _cloneText;

		public event Action<TextMeshProUGUI>? PosterReady;

		public event Action? PosterRemoved;

		private void OnEnable()
		{
			SceneManager.sceneLoaded += OnSceneLoaded;
			_sceneHooked = true;
			((MonoBehaviour)this).StartCoroutine(SpawnerLoop());
		}

		private void OnDisable()
		{
			if (_sceneHooked)
			{
				SceneManager.sceneLoaded -= OnSceneLoaded;
				_sceneHooked = false;
			}
			ClearClone();
		}

		private void OnSceneLoaded(Scene _, LoadSceneMode __)
		{
			ClearClone();
			_source = null;
		}

		private void ClearClone()
		{
			if ((Object)(object)_cloneText != (Object)null)
			{
				try
				{
					this.PosterRemoved?.Invoke();
				}
				catch (Exception arg)
				{
					Debug.LogError((object)$"[PosterClone] PosterRemoved handler failed: {arg}");
				}
				_cloneText = null;
			}
			if ((Object)(object)_clone != (Object)null)
			{
				Object.Destroy((Object)(object)_clone);
				_clone = null;
			}
		}

		private IEnumerator SpawnerLoop()
		{
			WaitForSeconds wait = new WaitForSeconds(0.5f);
			while (true)
			{
				if ((Object)(object)_source == (Object)null)
				{
					GameObject val = GameObject.Find("Room/Poster Patterns");
					if ((Object)(object)val != (Object)null && val.activeInHierarchy)
					{
						_source = val.transform;
					}
					else
					{
						Type type = UnityUtil.FindTypeAnyAssembly("PosterScript");
						if (type != null)
						{
							Component val2 = UnityUtil.FindObjectsOfTypeAllSafe(type).OfType<Component>().FirstOrDefault((Func<Component, bool>)delegate(Component comp)
							{
								if (!comp.gameObject.activeInHierarchy)
								{
									return false;
								}
								Transform transform = comp.transform;
								return ((Object)transform).name.IndexOf("Poster", StringComparison.OrdinalIgnoreCase) >= 0 && ((Object)(object)transform.parent == (Object)null || ((Object)transform.parent).name.IndexOf("Room", StringComparison.OrdinalIgnoreCase) >= 0);
							});
							if ((Object)(object)val2 != (Object)null)
							{
								_source = val2.transform;
							}
						}
					}
				}
				if ((Object)(object)_source != (Object)null && (Object)(object)_clone == (Object)null && ((Component)_source).gameObject.activeInHierarchy)
				{
					TryCloneAndCustomize(((Component)_source).gameObject);
				}
				if ((Object)(object)_source != (Object)null && !((Component)_source).gameObject.activeInHierarchy)
				{
					_source = null;
					ClearClone();
				}
				yield return wait;
			}
		}

		private void TryCloneAndCustomize(GameObject source)
		{
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				_clone = Object.Instantiate<GameObject>(source, source.transform.parent);
				((Object)_clone).name = "Poster Patterns (Custom Clone)";
				Vector3 targetWorldPosition = ModConfig.TargetWorldPosition;
				Vector3 targetEulerAngles = ModConfig.TargetEulerAngles;
				Vector3 targetLocalScale = ModConfig.TargetLocalScale;
				_clone.transform.position = targetWorldPosition;
				_clone.transform.rotation = Quaternion.Euler(targetEulerAngles);
				_clone.transform.localScale = targetLocalScale;
				Type type = UnityUtil.FindTypeAnyAssembly("PosterScript");
				Component val = null;
				if (type != null)
				{
					Component component = _clone.GetComponent(type);
					val = source.GetComponent(type);
					if ((Object)(object)component != (Object)null)
					{
						Object.DestroyImmediate((Object)(object)component, true);
					}
					FieldInfo field = type.GetField("instance", BindingFlags.Static | BindingFlags.Public);
					if (field != null && (Object)(object)val != (Object)null)
					{
						field.SetValue(null, val);
					}
				}
				TextMeshProUGUI sourceText = ((IEnumerable<TextMeshProUGUI>)source.GetComponentsInChildren<TextMeshProUGUI>(true)).FirstOrDefault((Func<TextMeshProUGUI, bool>)((TextMeshProUGUI t) => ((Object)t).name.IndexOf("Text", StringComparison.OrdinalIgnoreCase) >= 0)) ?? source.GetComponentInChildren<TextMeshProUGUI>(true);
				TextMeshProUGUI val2 = ((IEnumerable<TextMeshProUGUI>)_clone.GetComponentsInChildren<TextMeshProUGUI>(true)).FirstOrDefault((Func<TextMeshProUGUI, bool>)((TextMeshProUGUI t) => ((Object)t).name.IndexOf("Text", StringComparison.OrdinalIgnoreCase) >= 0)) ?? _clone.GetComponentInChildren<TextMeshProUGUI>(true);
				if ((Object)(object)val2 == (Object)null)
				{
					throw new Exception("Could not find TextMeshProUGUI on clone.");
				}
				PosterStyle.ApplyCloneStyle(sourceText, val2);
				TextMeshProUGUI[] componentsInChildren = _clone.GetComponentsInChildren<TextMeshProUGUI>(true);
				foreach (TextMeshProUGUI val3 in componentsInChildren)
				{
					if ((Object)(object)val3 != (Object)(object)val2)
					{
						((TMP_Text)val3).text = string.Empty;
					}
				}
				UnityUtil.RemoveComponentsByTypeName(_clone, "TextAccessibilityController");
				PosterStyle.AdjustCloneCanvas(_clone);
				_cloneText = val2;
				try
				{
					this.PosterReady?.Invoke(val2);
				}
				catch (Exception arg)
				{
					Debug.LogError((object)$"[PosterClone] PosterReady handler failed: {arg}");
				}
				Debug.Log((object)"[PosterClone] Clone created & customized.");
			}
			catch (Exception arg2)
			{
				Debug.LogError((object)$"[PosterClone] Failed to clone/customize: {arg2}");
				ClearClone();
			}
		}
	}
	internal static class PosterStyle
	{
		internal static void ApplyCloneStyle(TextMeshProUGUI? sourceText, TextMeshProUGUI destination)
		{
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)destination == (Object)null)
			{
				throw new ArgumentNullException("destination");
			}
			TmpUtil.StripPosterTextExtras(((Component)destination).gameObject);
			if ((Object)(object)sourceText != (Object)null)
			{
				TmpUtil.MirrorStyle(sourceText, destination);
			}
			((TMP_Text)destination).renderMode = (TextRenderFlags)255;
			((TMP_Text)destination).enableAutoSizing = false;
			((TMP_Text)destination).fontSize = ModConfig.PosterFontSize;
			((TMP_Text)destination).fontSizeMin = ModConfig.PosterFontSizeMin;
			((TMP_Text)destination).fontSizeMax = ModConfig.PosterFontSizeMax;
			((TMP_Text)destination).textWrappingMode = (TextWrappingModes)0;
			((TMP_Text)destination).overflowMode = (TextOverflowModes)0;
			((TMP_Text)destination).lineSpacing = ModConfig.PosterLineSpacing;
			((TMP_Text)destination).characterSpacing = ModConfig.PosterCharacterSpacing;
			((TMP_Text)destination).alpha = ModConfig.PosterTextAlpha;
			((TMP_Text)destination).outlineWidth = ModConfig.PosterOutlineWidth;
			((TMP_Text)destination).outlineColor = ModConfig.PosterOutlineColor;
			((TMP_Text)destination).alignment = (TextAlignmentOptions)258;
			((TMP_Text)destination).richText = true;
			((TMP_Text)destination).overrideColorTags = false;
			RectTransform rectTransform = ((TMP_Text)destination).rectTransform;
			if ((Object)(object)rectTransform != (Object)null)
			{
				rectTransform.anchoredPosition += new Vector2(0f, 0.1f);
			}
			((TMP_Text)destination).text = string.Empty;
			TmpUtil.CleanupExtraChildren(((TMP_Text)destination).transform);
			TmpUtil.EnsureTextAnimator(sourceText, destination);
			UnityUtil.RemoveComponentsByTypeName(((Component)destination).gameObject, "TextAccessibilityController");
			((TMP_Text)destination).ForceMeshUpdate(false, false);
		}

		internal static void AdjustCloneCanvas(GameObject clone)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0112: Unknown result type (might be due to invalid IL or missing references)
			//IL_008c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)clone == (Object)null)
			{
				return;
			}
			Canvas componentInChildren = clone.GetComponentInChildren<Canvas>(true);
			if ((Object)(object)componentInChildren == (Object)null)
			{
				return;
			}
			componentInChildren.overrideSorting = true;
			componentInChildren.sortingOrder = 4000;
			GraphicRaycaster component = ((Component)componentInChildren).GetComponent<GraphicRaycaster>();
			if ((Object)(object)component != (Object)null)
			{
				((Behaviour)component).enabled = false;
			}
			CanvasScaler component2 = ((Component)componentInChildren).GetComponent<CanvasScaler>();
			if ((Object)(object)component2 != (Object)null)
			{
				component2.dynamicPixelsPerUnit = 1f;
			}
			Transform transform = ((Component)componentInChildren).transform;
			Transform val = clone.transform.Find("Mesh");
			Quaternion rotation = transform.rotation;
			if ((Object)(object)val != (Object)null)
			{
				Vector3 val2 = transform.position - val.position;
				float num = ((Vector3)(ref val2)).magnitude;
				Vector3 val3;
				if (num > 0.0001f)
				{
					val3 = val2 / num;
				}
				else
				{
					val3 = val.forward;
					num = 0f;
				}
				transform.position = val.position + val3 * (num + 0.0025f);
			}
			else
			{
				Vector3 val4 = rotation * Vector3.forward;
				transform.position += val4 * 0.0025f;
			}
			transform.rotation = rotation;
		}
	}
}
namespace CloverPitMods.Diagnostics
{
	[Flags]
	internal enum PerfFlags
	{
		None = 0,
		LogAlways = 1,
		LogSlow = 2,
		Summary = 4,
		SkipRecord = 8
	}
	internal static class PerfTracker
	{
		private struct PerfStats
		{
			public int Count;

			public double TotalMs;

			public double MaxMs;

			public double LastMs;
		}

		private struct SpinPressureSnapshot
		{
			public int Gc0;

			public int Gc1;

			public int Gc2;

			public long MonoUsed;

			public long TotalAllocated;

			public float PreviousFrameDelta;

			public float CurrentFrameDelta;

			public float LatestFrameDelta;
		}

		internal struct Scope : IDisposable
		{
			private readonly string _name;

			private readonly PerfFlags _flags;

			private readonly long _startTicks;

			private readonly bool _active;

			private bool _disposed;

			private double _elapsedMs;

			public double ElapsedMilliseconds => _elapsedMs;

			public bool IsActive
			{
				get
				{
					if (_active)
					{
						return !_disposed;
					}
					return false;
				}
			}

			public bool WasActive => _active;

			internal Scope(string name, PerfFlags flags)
			{
				_name = name;
				_flags = ((flags == PerfFlags.None) ? (PerfFlags.LogSlow | PerfFlags.Summary) : flags);
				if (!Enabled)
				{
					_active = false;
					_startTicks = 0L;
					_elapsedMs = 0.0;
					_disposed = true;
				}
				else
				{
					_active = true;
					_startTicks = Stopwatch.GetTimestamp();
					_elapsedMs = 0.0;
					_disposed = false;
				}
			}

			public void Dispose()
			{
				if (_active && !_disposed)
				{
					_disposed = true;
					_elapsedMs = (double)(Stopwatch.GetTimestamp() - _startTicks) * 1000.0 / (double)Stopwatch.Frequency;
					if ((_flags & PerfFlags.SkipRecord) == 0)
					{
						Record(_name, _elapsedMs, _flags);
					}
				}
			}
		}

		private const double SlowThresholdMs = 0.5;

		private const double WarnThresholdMs = 4.0;

		private const int SummaryInterval = 60;

		private static ManualLogSource? _log;

		private static readonly Dictionary<string, PerfStats> _stats = new Dictionary<string, PerfStats>();

		private static readonly object _statsLock = new object();

		private static readonly object _frameLock = new object();

		private static float _framePrev;

		private static float _frameCurrent;

		private static float _frameLatest;

		private static readonly object _spinLock = new object();

		private static SpinPressureSnapshot? _spinBaseline;

		private static bool Enabled
		{
			get
			{
				if (ModConfig.IsInitialized)
				{
					return ModConfig.PerfTrackerEnabled;
				}
				return false;
			}
		}

		internal static void AttachLogger(ManualLogSource logger)
		{
			if (_log == null)
			{
				_log = logger;
			}
		}

		internal static void RegisterFrameDelta(float delta)
		{
			if (!Enabled)
			{
				return;
			}
			lock (_frameLock)
			{
				_framePrev = _frameCurrent;
				_frameCurrent = _frameLatest;
				_frameLatest = delta;
			}
		}

		internal static void MarkSpinBaseline()
		{
			if (!Enabled)
			{
				return;
			}
			lock (_spinLock)
			{
				_spinBaseline = CaptureSpinSnapshot();
			}
		}

		internal static void ReportSpinPressure(string label, double durationMs, int spinId)
		{
			if (Enabled && _log != null)
			{
				SpinPressureSnapshot? spinBaseline;
				lock (_spinLock)
				{
					spinBaseline = _spinBaseline;
					_spinBaseline = null;
				}
				SpinPressureSnapshot spinPressureSnapshot = CaptureSpinSnapshot();
				if (!spinBaseline.HasValue)
				{
					_log.LogDebug((object)$"[Perf][{label}] spin={spinId} {durationMs:F3}ms (no baseline)");
					return;
				}
				SpinPressureSnapshot value = spinBaseline.Value;
				int num = spinPressureSnapshot.Gc0 - value.Gc0;
				int num2 = spinPressureSnapshot.Gc1 - value.Gc1;
				int num3 = spinPressureSnapshot.Gc2 - value.Gc2;
				long bytes = spinPressureSnapshot.MonoUsed - value.MonoUsed;
				long bytes2 = spinPressureSnapshot.TotalAllocated - value.TotalAllocated;
				float previousFrameDelta = value.PreviousFrameDelta;
				float latestFrameDelta = value.LatestFrameDelta;
				float latestFrameDelta2 = spinPressureSnapshot.LatestFrameDelta;
				string text = $"[Perf][{label}] spin={spinId} {durationMs:F3}ms | GCΔ=[{num},{num2},{num3}] | MonoΔ={FormatBytes(bytes)} | TotalΔ={FormatBytes(bytes2)} | frameΔ prev={previousFrameDelta * 1000f:F1}ms spin={latestFrameDelta * 1000f:F1}ms next={latestFrameDelta2 * 1000f:F1}ms";
				_log.LogInfo((object)text);
			}
		}

		internal static Scope Track(string name, PerfFlags flags = PerfFlags.LogSlow | PerfFlags.Summary)
		{
			return new Scope(name, flags);
		}

		private static void Record(string name, double elapsedMs, PerfFlags flags)
		{
			if (!Enabled)
			{
				return;
			}
			bool flag = (flags & PerfFlags.LogSlow) != 0 && elapsedMs >= 0.5;
			bool flag2 = (flags & PerfFlags.LogAlways) != 0;
			bool flag3 = elapsedMs >= 4.0;
			bool flag4 = false;
			PerfStats value;
			double num;
			lock (_statsLock)
			{
				if (!_stats.TryGetValue(name, out value))
				{
					value = default(PerfStats);
				}
				value.Count++;
				value.TotalMs += elapsedMs;
				value.LastMs = elapsedMs;
				if (elapsedMs > value.MaxMs)
				{
					value.MaxMs = elapsedMs;
				}
				_stats[name] = value;
				num = value.TotalMs / (double)value.Count;
				flag4 = (flags & PerfFlags.Summary) != 0 && value.Count % 60 == 0;
			}
			if (_log == null)
			{
				return;
			}
			if (flag2 || flag3 || flag)
			{
				string text = (flag3 ? "[Perf][WARN]" : "[Perf]");
				if (flag3 || flag2)
				{
					_log.LogInfo((object)$"{text}[{name}] {elapsedMs:F3}ms (max {value.MaxMs:F3} / avg {num:F3} over {value.Count})");
				}
				else
				{
					_log.LogDebug((object)$"{text}[{name}] {elapsedMs:F3}ms (max {value.MaxMs:F3} / avg {num:F3} over {value.Count})");
				}
			}
			if (flag4 && !flag3 && !flag2 && !flag)
			{
				_log.LogDebug((object)$"[Perf][{name}] avg {num:F3}ms over {value.Count} (max {value.MaxMs:F3}ms)");
			}
		}

		private static SpinPressureSnapshot CaptureSpinSnapshot()
		{
			lock (_frameLock)
			{
				SpinPressureSnapshot result = default(SpinPressureSnapshot);
				result.Gc0 = GC.CollectionCount(0);
				result.Gc1 = GC.CollectionCount(1);
				result.Gc2 = GC.CollectionCount(2);
				result.MonoUsed = Profiler.GetMonoUsedSizeLong();
				result.TotalAllocated = Profiler.GetTotalAllocatedMemoryLong();
				result.PreviousFrameDelta = _framePrev;
				result.CurrentFrameDelta = _frameCurrent;
				result.LatestFrameDelta = _frameLatest;
				return result;
			}
		}

		private static string FormatBytes(long bytes)
		{
			if (bytes == 0L)
			{
				return "0B";
			}
			bool flag = bytes < 0;
			double num = Math.Abs((double)bytes);
			double num2;
			string text;
			if (num >= 1048576.0)
			{
				num2 = num / 1048576.0;
				text = "MB";
			}
			else if (num >= 1024.0)
			{
				num2 = num / 1024.0;
				text = "KB";
			}
			else
			{
				num2 = num;
				text = "B";
			}
			string text2 = ((num2 >= 10.0) ? num2.ToString("0", CultureInfo.InvariantCulture) : num2.ToString("0.0", CultureInfo.InvariantCulture));
			if (!flag)
			{
				return "+" + text2 + text;
			}
			return "-" + text2 + text;
		}
	}
}
namespace CloverPitMods.Patches
{
	[HarmonyPatch]
	internal static class GameplayLifecyclePatches
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(AccessTools.TypeByName("GameplayMaster"), "Start", (Type[])null, (Type[])null);
		}

		[HarmonyPostfix]
		private static void Postfix()
		{
			SixSixSixTracker.HandleSessionStart();
		}
	}
	[HarmonyPatch]
	internal static class GameplayLifecycleOnDestroyPatch
	{
		private static MethodBase TargetMethod()
		{
			return AccessTools.Method(AccessTools.TypeByName("GameplayMaster"), "OnDestroy", (Type[])null, (Type[])null);
		}

		[HarmonyPrefix]
		private static void Prefix()
		{
			SixSixSixTracker.HandleSessionEnd();
		}
	}
	[HarmonyPatch]
	internal static class GetSpinCountPatch
	{
		private static class SpinScratchBridge
		{
			public static void ResetFrame()
			{
				SpinScratch.ResetFrame();
			}

			public static void MarkForcedPreRng()
			{
				SpinScratch.MarkForcedPreRng();
			}

			public static void RecordThreshold(double passProb, int ordinal)
			{
				SpinScratch.RecordThreshold(passProb, ordinal);
			}

			public static void PublishResult(int outcome, bool updateValues)
			{
				SpinScratch.PublishResult(outcome, updateValues);
			}
		}

		private static MethodBase TargetMethod()
		{
			Type type = AccessTools.TypeByName("GameplayData");
			return AccessTools.Method(type, "SixSixSix_GetSpinCount", new Type[1] { typeof(bool) }, (Type[])null);
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions, ILGenerator il)
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Expected O, but got Unknown
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Expected O, but got Unknown
			//IL_025b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Expected O, but got Unknown
			//IL_026f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Expected O, but got Unknown
			//IL_027e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0284: Expected O, but got Unknown
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			Type type = AccessTools.TypeByName("Panik.Rng") ?? AccessTools.TypeByName("Rng");
			MethodInfo getValue = ((type != null) ? AccessTools.PropertyGetter(type, "Value") : null);
			MethodInfo methodInfo = AccessTools.Method(typeof(SpinScratchBridge), "ResetFrame", (Type[])null, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(SpinScratchBridge), "MarkForcedPreRng", (Type[])null, (Type[])null);
			MethodInfo methodInfo3 = AccessTools.Method(typeof(SpinScratchBridge), "RecordThreshold", (Type[])null, (Type[])null);
			list.Insert(0, new CodeInstruction(OpCodes.Call, (object)methodInfo));
			if (getValue != null)
			{
				int num = list.FindIndex((CodeInstruction ci) => CodeInstructionExtensions.Calls(ci, getValue));
				if (num >= 0)
				{
					for (int i = 0; i < num; i++)
					{
						if (list[i].opcode == OpCodes.Ret)
						{
							list.Insert(i, new CodeInstruction(OpCodes.Call, (object)methodInfo2));
							i++;
						}
					}
				}
				int num2 = 0;
				for (int j = 0; j < list.Count; j++)
				{
					if (!CodeInstructionExtensions.Calls(list[j], getValue))
					{
						continue;
					}
					int num3 = -1;
					for (int k = j + 1; k < list.Count && k < j + 24; k++)
					{
						OpCode opcode = list[k].opcode;
						if (opcode == OpCodes.Ldc_R4 || opcode == OpCodes.Ldc_R8)
						{
							if (list[k].operand is float num4 && num4 == 1f)
							{
								num3 = k;
								break;
							}
							if (list[k].operand is double num5 && num5 == 1.0)
							{
								num3 = k;
								break;
							}
						}
					}
					if (num3 < 0)
					{
						continue;
					}
					int num6 = -1;
					for (int l = num3 + 1; l < list.Count && l < num3 + 24; l++)
					{
						if (list[l].opcode == OpCodes.Sub)
						{
							num6 = l;
							break;
						}
					}
					if (num6 < 0)
					{
						continue;
					}
					int num7 = num6 - 1;
					if (num7 > num3)
					{
						num2++;
						list.InsertRange(num7 + 1, (IEnumerable<CodeInstruction>)(object)new CodeInstruction[3]
						{
							new CodeInstruction(OpCodes.Dup, (object)null),
							new CodeInstruction(OpCodes.Ldc_I4, (object)num2),
							new CodeInstruction(OpCodes.Call, (object)methodInfo3)
						});
						if (num2 >= 3)
						{
							break;
						}
					}
				}
			}
			return list;
		}

		[HarmonyPostfix]
		private static void Postfix(bool updateValues, int __result)
		{
			PerfTracker.Scope scope = PerfTracker.Track("Spin.Evaluate", PerfFlags.SkipRecord);
			try
			{
				SpinScratchBridge.PublishResult(__result, updateValues);
			}
			finally
			{
				scope.Dispose();
			}
		}
	}
	[HarmonyPatch]
	internal static class LiveTriplePatch
	{
		private static MethodBase TargetMethod()
		{
			Type type = AccessTools.TypeByName("GameplayData");
			return AccessTools.Method(type, "SixSixSix_ChanceGet", new Type[1] { typeof(bool) }, (Type[])null);
		}

		[HarmonyPostfix]
		private static void Postfix(bool considerMaximum, ref float __result)
		{
			if (considerMaximum)
			{
				SpinScratch.PublishLiveTriple(__result);
			}
		}
	}
	[HarmonyPatch]
	internal static class SpinPredictionPatch
	{
		private static MethodBase TargetMethod()
		{
			Type type = AccessTools.TypeByName("GameplayMaster");
			return AccessTools.Method(type, "FCall_SlotSpinTry", new Type[1] { typeof(bool) }, (Type[])null);
		}

		[HarmonyPrefix]
		private static void Prefix(bool calledByAutoSpin)
		{
			PerfTracker.MarkSpinBaseline();
			using (PerfTracker.Track("Spin.Predict"))
			{
				bool eligible = SixSixSixTracker.Is666EligibleNow();
				SpinScratch.BeginSpin(eligible);
				ForcedKind forcedKind = ForcedKind.None;
				int num = -1;
				int num2 = -1;
				using (PerfTracker.Track("Spin.Reflection"))
				{
					try
					{
						MethodInfo gameplayDataSpinsLeftGet = ReflectionCache.GameplayDataSpinsLeftGet;
						MethodInfo gameplayDataBookedSpinGet = ReflectionCache.GameplayDataBookedSpinGet;
						MethodInfo gameplayDataPhoneTriggersCountGet = ReflectionCache.GameplayDataPhoneTriggersCountGet;
						MethodInfo gameplayDataSuppressedSpinsGet = ReflectionCache.GameplayDataSuppressedSpinsGet;
						int num3 = (int)(gameplayDataSpinsLeftGet?.Invoke(null, null) ?? ((object)0));
						num = ((num3 > 0) ? (num3 - 1) : 0);
						num2 = (int)(gameplayDataBookedSpinGet?.Invoke(null, null) ?? ((object)(-1)));
						if (num2 >= 0 && num2 == num)
						{
							forcedKind = ForcedKind.BookedTriple;
						}
						int num4 = (int)(gameplayDataPhoneTriggersCountGet?.Invoke(null, null) ?? ((object)0));
						int num5 = (int)(gameplayDataSuppressedSpinsGet?.Invoke(null, null) ?? ((object)0));
						SixSixSixTracker.LogDebug($"Spin prediction diagnostics → curSpinsLeft={num3}, nextSpinsLeft={num}, bookedTarget={num2}, phoneTriggers={num4}, suppressedSpins={num5}");
						if (forcedKind == ForcedKind.None)
						{
							if (num4 > 0)
							{
								forcedKind = ForcedKind.PhoneTriple;
							}
							else if (num5 > 0)
							{
								forcedKind = ForcedKind.Suppressed;
							}
						}
					}
					catch
					{
					}
				}
				SixSixSixTracker.PublishPrediction(calledByAutoSpin, eligible, forcedKind, num, num2);
				switch (forcedKind)
				{
				case ForcedKind.BookedTriple:
					SpinScratch.MarkForcedBooked();
					break;
				case ForcedKind.PhoneTriple:
				case ForcedKind.Suppressed:
					SpinScratch.MarkForcedPreRng(forcedKind);
					break;
				}
			}
		}
	}
}
namespace CloverPitMods.Configuration
{
	internal static class ModConfig
	{
		private const int CurrentSchemaVersion = 2;

		private const string SchemaSection = "Internal";

		private const string SchemaKey = "SchemaVersion";

		private const float CurrentLineSpacingDefault = 0f;

		private static ConfigEntry<int>? _schemaVersion;

		private static ConfigEntry<bool>? _perfTrackerEnabled;

		private static ConfigEntry<float>? _targetWorldPosX;

		private static ConfigEntry<float>? _targetWorldPosY;

		private static ConfigEntry<float>? _targetWorldPosZ;

		private static ConfigEntry<float>? _targetEulerX;

		private static ConfigEntry<float>? _targetEulerY;

		private static ConfigEntry<float>? _targetEulerZ;

		private static ConfigEntry<float>? _targetScaleX;

		private static ConfigEntry<float>? _targetScaleY;

		private static ConfigEntry<float>? _targetScaleZ;

		private static ConfigEntry<float>? _posterFontSize;

		private static ConfigEntry<float>? _posterFontSizeMin;

		private static ConfigEntry<float>? _posterFontSizeMax;

		private static ConfigEntry<float>? _posterLineSpacing;

		private static ConfigEntry<float>? _posterCharacterSpacing;

		private static ConfigEntry<float>? _posterTextAlpha;

		private static ConfigEntry<float>? _posterOutlineWidth;

		private static ConfigEntry<int>? _posterOutlineColorR;

		private static ConfigEntry<int>? _posterOutlineColorG;

		private static ConfigEntry<int>? _posterOutlineColorB;

		private static ConfigEntry<int>? _posterOutlineColorA;

		internal static bool IsInitialized { get; private set; }

		internal static bool PerfTrackerEnabled => _perfTrackerEnabled?.Value ?? false;

		internal static Vector3 TargetWorldPosition => new Vector3(_targetWorldPosX?.Value ?? (-2.78125f), _targetWorldPosY?.Value ?? 6f, _targetWorldPosZ?.Value ?? 4.8125f);

		internal static Vector3 TargetEulerAngles => new Vector3(_targetEulerX?.Value ?? 0f, _targetEulerY?.Value ?? 180f, _targetEulerZ?.Value ?? 0f);

		internal static Vector3 TargetLocalScale => new Vector3(_targetScaleX?.Value ?? 0.22f, _targetScaleY?.Value ?? 0.22f, _targetScaleZ?.Value ?? 0.22f);

		internal static float PosterFontSize => _posterFontSize?.Value ?? 0.2f;

		internal static float PosterFontSizeMin => _posterFontSizeMin?.Value ?? 0.2f;

		internal static float PosterFontSizeMax => _posterFontSizeMax?.Value ?? 0.2f;

		internal static float PosterLineSpacing => _posterLineSpacing?.Value ?? 0f;

		internal static float PosterCharacterSpacing => _posterCharacterSpacing?.Value ?? (-12f);

		internal static float PosterTextAlpha => Mathf.Clamp01(_posterTextAlpha?.Value ?? 0.5f);

		internal static float PosterOutlineWidth => Mathf.Max(0f, _posterOutlineWidth?.Value ?? 0.02f);

		internal static Color32 PosterOutlineColor
		{
			get
			{
				//IL_0080: Unknown result type (might be due to invalid IL or missing references)
				byte b = (byte)Mathf.Clamp(_posterOutlineColorR?.Value ?? 0, 0, 255);
				byte b2 = (byte)Mathf.Clamp(_posterOutlineColorG?.Value ?? 0, 0, 255);
				byte b3 = (byte)Mathf.Clamp(_posterOutlineColorB?.Value ?? 0, 0, 255);
				byte b4 = (byte)Mathf.Clamp(_posterOutlineColorA?.Value ?? 255, 0, 255);
				return new Color32(b, b2, b3, b4);
			}
		}

		internal static void Initialize(ConfigFile config)
		{
			if (IsInitialized)
			{
				return;
			}
			int existingSchemaVersion = GetExistingSchemaVersion(config);
			_schemaVersion = config.Bind<int>("Internal", "SchemaVersion", 2, "Internal configuration schema version. Do not modify manually.");
			_perfTrackerEnabled = config.Bind<bool>("Performance", "PerfTrackerEnabled", false, "Enable detailed performance tracking and logging.");
			_targetWorldPosX = config.Bind<float>("Poster.Transform", "TargetWorldPosX", -2.78125f, "Poster world position X component. Round nearest multiple of 0.03125");
			_targetWorldPosY = config.Bind<float>("Poster.Transform", "TargetWorldPosY", 6f, "Poster world position Y component. Round nearest multiple of 0.03125");
			_targetWorldPosZ = config.Bind<float>("Poster.Transform", "TargetWorldPosZ", 4.8125f, "Poster world position Z component. Round nearest multiple of 0.03125");
			_targetEulerX = config.Bind<float>("Poster.Transform", "TargetEulerX", 0f, "Poster rotation X (degrees).");
			_targetEulerY = config.Bind<float>("Poster.Transform", "TargetEulerY", 180f, "Poster rotation Y (degrees).");
			_targetEulerZ = config.Bind<float>("Poster.Transform", "TargetEulerZ", 0f, "Poster rotation Z (degrees).");
			_targetScaleX = config.Bind<float>("Poster.Transform", "TargetScaleX", 0.22f, "Poster local scale X component.");
			_targetScaleY = config.Bind<float>("Poster.Transform", "TargetScaleY", 0.22f, "Poster local scale Y component.");
			_targetScaleZ = config.Bind<float>("Poster.Transform", "TargetScaleZ", 0.22f, "Poster local scale Z component.");
			_posterFontSize = config.Bind<float>("Poster.Text", "FontSize", 0.2f, "Poster font size.");
			_posterFontSizeMin = config.Bind<float>("Poster.Text", "FontSizeMin", 0.2f, "Poster minimum font size.");
			_posterFontSizeMax = config.Bind<float>("Poster.Text", "FontSizeMax", 0.2f, "Poster maximum font size.");
			_posterLineSpacing = config.Bind<float>("Poster.Text", "LineSpacing", 0f, "Poster line spacing.");
			_posterCharacterSpacing = config.Bind<float>("Poster.Text", "CharacterSpacing", -12f, "Poster character spacing.");
			_posterTextAlpha = config.Bind<float>("Poster.Text", "Alpha", 0.5f, "Poster text alpha in range [0,1].");
			_posterOutlineWidth = config.Bind<float>("Poster.Text", "OutlineWidth", 0.02f, "Poster text outline width.");
			_posterOutlineColorR = config.Bind<int>("Poster.Text", "OutlineColorR", 0, "Poster text outline color red channel (0-255).");
			_posterOutlineColorG = config.Bind<int>("Poster.Text", "OutlineColorG", 0, "Poster text outline color green channel (0-255).");
			_posterOutlineColorB = config.Bind<int>("Poster.Text", "OutlineColorB", 0, "Poster text outline color blue channel (0-255).");
			_posterOutlineColorA = config.Bind<int>("Poster.Text", "OutlineColorA", 255, "Poster text outline color alpha channel (0-255).");
			if (existingSchemaVersion < 2)
			{
				bool flag = RunConfigMigrations(existingSchemaVersion);
				if (_schemaVersion != null)
				{
					if (_schemaVersion.Value != 2)
					{
						_schemaVersion.Value = 2;
					}
					flag = true;
				}
				if (flag)
				{
					config.Save();
				}
			}
			IsInitialized = true;
		}

		private static int GetExistingSchemaVersion(ConfigFile config)
		{
			ConfigEntry<int> val = default(ConfigEntry<int>);
			if (!config.TryGetEntry<int>("Internal", "SchemaVersion", ref val))
			{
				return 1;
			}
			return val.Value;
		}

		private static bool RunConfigMigrations(int previousSchemaVersion)
		{
			bool result = false;
			if (previousSchemaVersion < 2)
			{
				ConfigEntry<float> posterLineSpacing = _posterLineSpacing;
				if (posterLineSpacing != null)
				{
					posterLineSpacing.Value = 0f;
					result = true;
				}
			}
			return result;
		}
	}
}
namespace CloverPitMods.Bridge
{
	internal static class SixSixSixPosterMath
	{
		internal readonly record struct RunLuckSummary(double CalibratedZ, double LuckScore, string LuckDisplay, string LuckLabel, string LuckColor);

		internal readonly record struct RunLuckComputation(RunLuckSummary Summary, bool HasData, bool OnUnluckySide, bool OnLuckySide, int CappedHits, double TailProbability, double Magnitude, double Quantile, double ProbabilityAtHits);

		internal const double SurvivalFloor = 1E-09;

		private const double LuckSideTolerance = 1E-06;

		private static readonly double[] InverseNormalA = new double[6] { -39.69683028665376, 220.9460984245205, -275.9285104469687, 138.357751867269, -30.66479806614716, 2.506628277459239 };

		private static readonly double[] InverseNormalB = new double[5] { -54.47609879822406, 161.5858368580409, -155.6989798598866, 66.80131188771972, -13.28068155288572 };

		private static readonly double[] InverseNormalC = new double[6] { -0.007784894002430293, -0.3223964580411365, -2.400758277161838, -2.549732539343734, 4.374664141464968, 2.938163982698783 };

		private static readonly double[] InverseNormalD = new double[4] { 0.007784695709041462, 0.3224671290700398, 2.445134137142996, 3.754408661907416 };

		internal static double Clamp01(double value)
		{
			if (value < 0.0)
			{
				return 0.0;
			}
			if (value > 1.0)
			{
				return 1.0;
			}
			return value;
		}

		internal static double ClampProbability(double value)
		{
			if (double.IsNaN(value) || double.IsInfinity(value))
			{
				return 0.0;
			}
			return Clamp01(value);
		}

		internal static string FormatPercentPrecise(double value)
		{
			double num = Clamp01(value) * 100.0;
			if (num >= 10.0)
			{
				return num.ToString("0", CultureInfo.InvariantCulture) + "%";
			}
			if (num >= 1.0)
			{
				return num.ToString("0.0", CultureInfo.InvariantCulture) + "%";
			}
			if (num >= 0.1)
			{
				return num.ToString("0.00", CultureInfo.InvariantCulture) + "%";
			}
			if (num <= 0.0)
			{
				return "0%";
			}
			return num.ToString("0.000", CultureInfo.InvariantCulture) + "%";
		}

		internal static string FormatOneIn(double percentile)
		{
			percentile = Clamp01(percentile);
			if (percentile <= 0.0)
			{
				return "1-in-∞";
			}
			double num = 1.0 / percentile;
			if (double.IsInfinity(num) || num > 1000000.0)
			{
				return "1-in-1M+";
			}
			double num2 = Math.Max(1.0, Math.Round(num, 1, MidpointRounding.AwayFromZero));
			string text = ((Math.Abs(num2 - Math.Round(num2)) < 1E-09) ? num2.ToString("0", CultureInfo.InvariantCulture) : num2.ToString("0.0", CultureInfo.InvariantCulture));
			return "1-in-" + text;
		}

		internal static string FormatOneInSpaced(double percentile)
		{
			percentile = Clamp01(percentile);
			if (percentile <= 0.0)
			{
				return "1 in ∞";
			}
			double num = 1.0 / percentile;
			if (double.IsInfinity(num) || num > 1000000.0)
			{
				return "1 in 1M+";
			}
			double num2 = Math.Max(1.0, Math.Round(num, 1, MidpointRounding.AwayFromZero));
			string text = ((Math.Abs(num2 - Math.Round(num2)) < 1E-09) ? num2.ToString("0", CultureInfo.InvariantCulture) : num2.ToString("0.0", CultureInfo.InvariantCulture));
			return "1 in " + text;
		}

		internal static string FormatLuckMeter(double luckScore)
		{
			double value = 50.0 + 0.5 * luckScore;
			value = Math.Clamp(value, 0.0, 100.0);
			string text = Math.Round(value, 0, MidpointRounding.AwayFromZero).ToString("0", CultureInfo.InvariantCulture);
			return text + "/100";
		}

		internal static string FormatOrdinal(int value)
		{
			if (value < 0)
			{
				value = 0;
			}
			if (value > 100)
			{
				value = 100;
			}
			int num = value % 100;
			return string.Format(arg1: (num >= 11 && num <= 13) ? "th" : ((value % 10) switch
			{
				1 => "st", 
				2 => "nd", 
				3 => "rd", 
				_ => "th", 
			}), format: "{0}{1}", arg0: value);
		}

		internal static (string Label, string Color) DescribeRunLuck(double z)
		{
			if (z >= 2.0)
			{
				return ("Super Unlucky!", "#FF6B6B");
			}
			if (z >= 1.0)
			{
				return ("Unlucky!", "#FFB580");
			}
			if (z > -1.0 && z < 1.0)
			{
				return ("Typical odds", "#FFDF80");
			}
			if (z > -2.0 && z <= -1.0)
			{
				return ("Lucky!", "#80CAFF");
			}
			return ("Super Lucky!", "#0096FF");
		}

		internal static (string Label, string Color) DescribeSurvival(double percentile)
		{
			percentile = Clamp01(percentile);
			if (percentile < 0.25)
			{
				return ("Unlucky", "#FF2200");
			}
			if (percentile < 0.5)
			{
				return ("Typical", "#FF7E0d");
			}
			if (percentile < 0.75)
			{
				return ("Fortunate", "#FFF829");
			}
			return ("Lucky!", "#80CAFF");
		}

		internal static RunLuckSummary CalculateRunLuck(double expectedHits, int hits, IReadOnlyList<double> pmf)
		{
			return CalculateRunLuckInternal(expectedHits, hits, pmf).Summary;
		}

		internal static RunLuckComputation CalculateRunLuckDiagnostics(double expectedHits, int hits, IReadOnlyList<double> pmf)
		{
			return CalculateRunLuckInternal(expectedHits, hits, pmf);
		}

		private static RunLuckComputation CalculateRunLuckInternal(double expectedHits, int hits, IReadOnlyList<double> pmf)
		{
			(string Label, string Color) tuple = DescribeRunLuck(0.0);
			string item = tuple.Label;
			string item2 = tuple.Color;
			RunLuckSummary summary = new RunLuckSummary(0.0, 0.0, FormatLuckMeter(0.0), item, item2);
			if (pmf == null || pmf.Count == 0)
			{
				return new RunLuckComputation(summary, HasData: false, OnUnluckySide: false, OnLuckySide: false, 0, double.NaN, 0.0, 0.5, double.NaN);
			}
			bool flag = (double)hits > expectedHits + 1E-06;
			bool flag2 = (double)hits < expectedHits - 1E-06;
			int num = hits;
			if (num < 0)
			{
				num = 0;
			}
			int num2 = pmf.Count - 1;
			if (num > num2)
			{
				num = num2;
			}
			double probabilityAtHits = ClampProbability(pmf[num]);
			if (!flag && !flag2)
			{
				return new RunLuckComputation(summary, HasData: true, OnUnluckySide: false, OnLuckySide: false, num, double.NaN, 0.0, 0.5, probabilityAtHits);
			}
			double num3 = 0.0;
			if (flag)
			{
				for (int i = num; i < pmf.Count; i++)
				{
					num3 += ClampProbability(pmf[i]);
				}
			}
			else
			{
				for (int j = 0; j <= num; j++)
				{
					num3 += ClampProbability(pmf[j]);
				}
			}
			num3 = ClampProbability(num3);
			if (num3 < 1E-09)
			{
				num3 = 1E-09;
			}
			if (num3 > 0.999999999)
			{
				num3 = 0.999999999;
			}
			double num4 = Math.Clamp(Math.Abs(num3 - 0.5) * 2.0, 0.0, 1.0);
			if (num4 <= 0.0)
			{
				return new RunLuckComputation(summary, HasData: true, flag, flag2, num, num3, num4, 1.0 - num3, probabilityAtHits);
			}
			double num5 = 1.0 - num3;
			if (num5 < 1E-09)
			{
				num5 = 1E-09;
			}
			else if (num5 > 0.999999999)
			{
				num5 = 0.999999999;
			}
			double num6 = Math.Abs(InverseNormalCdf(num5));
			if (double.IsNaN(num6) || double.IsInfinity(num6))
			{
				num6 = 0.0;
			}
			double num7 = (flag ? num6 : (0.0 - num6));
			double luckScore = ((num7 < 0.0) ? (num4 * 100.0) : ((num7 > 0.0) ? ((0.0 - num4) * 100.0) : 0.0));
			string luckDisplay = FormatLuckMeter(luckScore);
			(string Label, string Color) tuple2 = DescribeRunLuck(num7);
			string item3 = tuple2.Label;
			string item4 = tuple2.Color;
			RunLuckSummary summary2 = new RunLuckSummary(num7, luckScore, luckDisplay, item3, item4);
			return new RunLuckComputation(summary2, HasData: true, flag, flag2, num, num3, num4, num5, probabilityAtHits);
		}

		internal static double InverseNormalCdf(double p)
		{
			if (double.IsNaN(p))
			{
				return double.NaN;
			}
			if (p <= 0.0)
			{
				return double.NegativeInfinity;
			}
			if (p >= 1.0)
			{
				return double.PositiveInfinity;
			}
			if (p < 0.02425)
			{
				double num = Math.Sqrt(-2.0 * Math.Log(p));
				double num2 = ((((InverseNormalC[0] * num + InverseNormalC[1]) * num + InverseNormalC[2]) * num + InverseNormalC[3]) * num + InverseNormalC[4]) * num + InverseNormalC[5];
				double num3 = (((InverseNormalD[0] * num + InverseNormalD[1]) * num + InverseNormalD[2]) * num + InverseNormalD[3]) * num + 1.0;
				return num2 / num3;
			}
			if (p > 0.97575)
			{
				double num4 = Math.Sqrt(-2.0 * Math.Log(1.0 - p));
				double num5 = ((((InverseNormalC[0] * num4 + InverseNormalC[1]) * num4 + InverseNormalC[2]) * num4 + InverseNormalC[3]) * num4 + InverseNormalC[4]) * num4 + InverseNormalC[5];
				double num6 = (((InverseNormalD[0] * num4 + InverseNormalD[1]) * num4 + InverseNormalD[2]) * num4 + InverseNormalD[3]) * num4 + 1.0;
				return (0.0 - num5) / num6;
			}
			double num7 = p - 0.5;
			double num8 = num7 * num7;
			double num9 = (((((InverseNormalA[0] * num8 + InverseNormalA[1]) * num8 + InverseNormalA[2]) * num8 + InverseNormalA[3]) * num8 + InverseNormalA[4]) * num8 + InverseNormalA[5]) * num7;
			double num10 = ((((InverseNormalB[0] * num8 + InverseNormalB[1]) * num8 + InverseNormalB[2]) * num8 + InverseNormalB[3]) * num8 + InverseNormalB[4]) * num8 + 1.0;
			return num9 / num10;
		}
	}
	internal sealed class SixSixSixBridge : MonoBehaviour
	{
		private enum FontPreset
		{
			VerySmall,
			Small,
			Default,
			Large,
			VeryLarge
		}

		private const double DefaultFontSize = 0.32;

		private const double VeryLargeFontScale = 1.35;

		private const double LargeFontScale = 1.1;

		private const double SmallFontScale = 0.9;

		private const double VerySmallFontScale = 0.85;

		private const string TitleColor = "#FF3C00";

		private const string HeaderColor = "#FFFFFF";

		private PosterCloneController? _posterController;

		private TextMeshProUGUI? _posterText;

		private double _logSurvival;

		private int _tripleCount;

		private int _eligibleSpinStreakAll;

		private double _currentTripleChance;

		private bool _hasTripleChance;

		private bool _hasSurvivalSample;

		private bool _eligibilityReached;

		private bool _hitOverlayActive;

		private long _spinSerial;

		private long _overlayClearSerial = -1L;

		private double _hitOddsProbability = double.NaN;

		private string _hitOddsDisplay = string.Empty;

		private int _streakBeforeHit;

		private string _statusMessage = "Waiting for spins";

		private string _cachedPosterText = string.Empty;

		private bool _posterDirty;

		private bool _posterStateDirty;

		private int _cachedEligibleSpinCount;

		private int _cachedSpinsSinceLastTriple;

		private double _runExpectedHits;

		private readonly List<double> _runPmf = new List<double>();

		private int _runUnforcedHits;

		private double _runCalibratedZ;

		private double _runLuckScore;

		private string _runLuckDisplay = SixSixSixPosterMath.FormatLuckMeter(0.0);

		private string _runLuckLabel = "Typical odds";

		private string _runLuckColor = "#FFFFFF";

		private double _lastRunLuckLogExpected = double.NaN;

		private int _lastRunLuckLogHits = -1;

		private string _lastRunLuckLogDisplay = string.Empty;

		private void Awake()
		{
			_posterController = ((Component)this).GetComponent<PosterCloneController>();
			if ((Object)(object)_posterController == (Object)null)
			{
				Debug.LogError((object)"[SixSixSixHud] PosterCloneController missing; disabling bridge.");
				((Behaviour)this).enabled = false;
				return;
			}
			_posterController.PosterReady += OnPosterReady;
			_posterController.PosterRemoved += OnPosterRemoved;
			SixSixSixTracker.OnSessionStarted += HandleSessionStarted;
			SixSixSixTracker.OnSessionEnded += HandleSessionEnded;
			SixSixSixTracker.OnSpinEvaluated += HandleSpinEvaluated;
			SixSixSixTracker.OnLiveTripleUpdated += HandleLiveTripleUpdated;
			ResetHudState("Waiting for spins");
			RefreshAggregateCounters();
			UpdatePosterText();
		}

		private void OnDestroy()
		{
			if ((Object)(object)_posterController != (Object)null)
			{
				_posterController.PosterReady -= OnPosterReady;
				_posterController.PosterRemoved -= OnPosterRemoved;
			}
			SixSixSixTracker.OnSessionStarted -= HandleSessionStarted;
			SixSixSixTracker.OnSessionEnded -= HandleSessionEnded;
			SixSixSixTracker.OnSpinEvaluated -= HandleSpinEvaluated;
			SixSixSixTracker.OnLiveTripleUpdated -= HandleLiveTripleUpdated;
		}

		private void HandleSessionStarted()
		{
			ResetHudState("Waiting for spins");
			RefreshAggregateCounters();
			UpdatePosterText();
		}

		private void HandleSessionEnded()
		{
			bool flag = false;
			flag |= UpdateField(ref _statusMessage, "Session ended");
			flag |= UpdateField(ref _hitOverlayActive, value: false);
			_overlayClearSerial = -1L;
			flag |= UpdateDouble(ref _logSurvival, 0.0);
			flag |= UpdateField(ref _eligibleSpinStreakAll, 0);
			flag |= UpdateDouble(ref _currentTripleChance, 0.0);
			flag |= UpdateField(ref _hasTripleChance, value: false);
			flag |= UpdateField(ref _hasSurvivalSample, value: false);
			flag |= UpdateField(ref _eligibilityReached, value: false);
			flag |= UpdateDouble(ref _hitOddsProbability, double.NaN);
			flag |= UpdateField(ref _hitOddsDisplay, string.Empty);
			flag |= UpdateDouble(ref _runExpectedHits, 0.0);
			ResetRunDistribution();
			flag |= UpdateField(ref _runUnforcedHits, 0);
			flag |= UpdateDouble(ref _runCalibratedZ, 0.0);
			flag |= UpdateDouble(ref _runLuckScore, 0.0);
			flag |= UpdateField(ref _runLuckDisplay, SixSixSixPosterMath.FormatLuckMeter(0.0));
			flag |= UpdateField(ref _runLuckLabel, "Typical odds");
			flag |= UpdateField(ref _runLuckColor, "#FFFFFF");
			if (flag | RefreshAggregateCounters())
			{
				UpdatePosterText();
			}
		}

		private void HandleLiveTripleUpdated(double chance)
		{
			using (PerfTracker.Track("HUD.LiveTriple"))
			{
				double value = SixSixSixPosterMath.ClampProbability(chance);
				bool flag = false;
				flag |= UpdateDouble(ref _currentTripleChance, value, 0.0001);
				flag |= UpdateField(ref _hasTripleChance, value: true);
				flag |= RefreshAggregateCounters();
				if (_eligibilityReached && !_hitOverlayActive && flag)
				{
					UpdatePosterText();
				}
			}
		}

		private void LateUpdate()
		{
			PerfTracker.RegisterFrameDelta(Time.unscaledDeltaTime);
			if (!_posterDirty || (Object)(object)_posterText == (Object)null)
			{
				return;
			}
			PerfTracker.Scope scope = PerfTracker.Track("HUD.ApplyText");
			try
			{
				using (PerfTracker.Track("HUD.SetText"))
				{
					((TMP_Text)_posterText).SetText(_cachedPosterText);
					_posterDirty = false;
				}
			}
			finally
			{
				scope.Dispose();
			}
		}

		private void HandleSpinEvaluated(SpinSnapshot snap)
		{
			using (PerfTracker.Track("HUD.HandleSpin"))
			{
				_spinSerial++;
				bool flag = RefreshAggregateCounters();
				if (_hitOverlayActive && _overlayClearSerial == _spinSerial)
				{
					flag |= UpdateField(ref _hitOverlayActive, value: false);
					_overlayClearSerial = -1L;
				}
				if (!snap.EligibleForTripleGate)
				{
					if (flag)
					{
						UpdatePosterText();
					}
					return;
				}
				flag |= UpdateField(ref _eligibilityReached, value: true);
				double num = SixSixSixPosterMath.Clamp01(Math.Exp(_logSurvival));
				double num2 = SixSixSixPosterMath.ClampProbability(snap.Triple);
				if (!snap.Forced)
				{
					double d = Math.Max(1E-09, 1.0 - num2);
					double value = _logSurvival + Math.Log(d);
					flag |= UpdateDouble(ref _logSurvival, value);
					flag |= UpdateField(ref _hasSurvivalSample, value: true);
					flag |= UpdateDouble(ref _currentTripleChance, num2, 0.0001);
					flag |= UpdateField(ref _hasTripleChance, value: true);
					if (!string.IsNullOrEmpty(_statusMessage))
					{
						flag |= UpdateField(ref _statusMessage, string.Empty);
					}
					flag |= UpdateDouble(ref _runExpectedHits, _runExpectedHits + num2);
					UpdateRunDistribution(num2);
					if (snap.Outcome == 3)
					{
						flag |= UpdateField(ref _runUnforcedHits, _runUnforcedHits + 1);
					}
					flag |= RecalculateRunLuck();
				}
				if (snap.Outcome == 3)
				{
					double num3 = SixSixSixPosterMath.Clamp01(1.0 - num);
					int value2 = _tripleCount + 1;
					flag |= UpdateField(ref _tripleCount, value2);
					flag |= UpdateField(ref _streakBeforeHit, _eligibleSpinStreakAll);
					flag |= UpdateDouble(ref _hitOddsProbability, num3, 1E-06);
					flag |= UpdateField(ref _hitOddsDisplay, SixSixSixPosterMath.FormatOneInSpaced(num3));
					flag |= UpdateField(ref _hitOverlayActive, value: true);
					_overlayClearSerial = _spinSerial + 1;
					flag |= UpdateField(ref _eligibleSpinStreakAll, 0);
					flag |= UpdateDouble(ref _logSurvival, 0.0);
					flag |= UpdateField(ref _hasSurvivalSample, value: false);
				}
				else
				{
					int value3 = _eligibleSpinStreakAll + 1;
					flag |= UpdateField(ref _eligibleSpinStreakAll, value3);
				}
				if (flag)
				{
					UpdatePosterText();
				}
			}
		}

		private void OnPosterReady(TextMeshProUGUI text)
		{
			_posterText = text;
			TryApplyPosterText();
		}

		private void OnPosterRemoved()
		{
			_posterText = null;
			_posterDirty = false;
		}

		private void UpdatePosterText()
		{
			if (_posterStateDirty)
			{
				_cachedPosterText = BuildPosterText();
				_posterStateDirty = false;
				TryApplyPosterText();
			}
		}

		private string BuildPosterText()
		{
			using (PerfTracker.Track("HUD.BuildText"))
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine(ApplyColor(ApplyFont("666 Tracker", FontPreset.Large), "#FF3C00"));
				if (!_eligibilityReached)
				{
					stringBuilder.AppendLine();
					stringBuilder.AppendLine();
					stringBuilder.AppendLine(ApplyColor(ApplyFont("Waiting for 666", FontPreset.VeryLarge), "#FFFFFF"));
					stringBuilder.AppendLine();
					stringBuilder.AppendLine(ApplyColor(ApplyFont("To Be Active...", FontPreset.VeryLarge), "#FFFFFF"));
					if (!string.IsNullOrEmpty(_statusMessage) && !string.Equals(_statusMessage, "Waiting for spins", StringComparison.OrdinalIgnoreCase))
					{
						stringBuilder.AppendLine();
						stringBuilder.AppendLine(_statusMessage);
					}
					return stringBuilder.ToString();
				}
				stringBuilder.AppendLine(ApplyFont($"Spin Count: {_cachedEligibleSpinCount}", FontPreset.Default));
				stringBuilder.AppendLine(ApplyFont($"666 Count: {_tripleCount}", FontPreset.Default));
				stringBuilder.AppendLine(ApplyFont($"Spins Since Last 666: {_cachedSpinsSinceLastTriple}", FontPreset.Default));
				stringBuilder.AppendLine();
				if (_hitOverlayActive)
				{
					AppendHitOverlaySection(stringBuilder);
				}
				else
				{
					AppendByNowSection(stringBuilder);
				}
				if (!string.IsNullOrEmpty(_statusMessage) && !string.Equals(_statusMessage, "Waiting for spins", StringComparison.OrdinalIgnoreCase))
				{
					stringBuilder.AppendLine();
					stringBuilder.AppendLine(_statusMessage);
				}
				return stringBuilder.ToString();
			}
		}

		private void AppendByNowSection(StringBuilder sb)
		{
			if (sb != null)
			{
				sb.AppendLine(ApplyColor(ApplyFont("% Runs That Hit 666 By Now:", FontPreset.VerySmall), "#FFFFFF"));
				if (!_hasSurvivalSample)
				{
					sb.AppendLine("");
					sb.AppendLine("(1-in-∞)");
					sb.AppendLine("Luck If 666 Hits Now:");
					sb.AppendLine("---");
					return;
				}
				double num = SixSixSixPosterMath.Clamp01(Math.Exp(_logSurvival));
				double num2 = SixSixSixPosterMath.Clamp01(1.0 - num);
				(string Label, string Color) tuple = SixSixSixPosterMath.DescribeSurvival(num2);
				string item = tuple.Label;
				string item2 = tuple.Color;
				string value = ApplyColor(SixSixSixPosterMath.FormatPercentPrecise(num2), item2);
				string text = ApplyColor(SixSixSixPosterMath.FormatOneIn(num2), item2);
				string text2 = ApplyColor(item, item2);
				sb.AppendLine(ApplyFont(value, FontPreset.Large));
				sb.AppendLine(ApplyFont("(" + text + ")", FontPreset.Small));
			}
		}

		private void AppendHitOverlaySection(StringBuilder sb)
		{
			sb.AppendLine(ApplyColor(ApplyFont("Odds For This 666", FontPreset.Small), "#FFFFFF"));
			string text = (string.IsNullOrEmpty(_hitOddsDisplay) ? "--" : _hitOddsDisplay);
			sb.AppendLine(ApplyFont("(" + text + ")", FontPreset.Default));
			sb.AppendLine(ApplyFont("Your 666 Luck This Run:", FontPreset.Small));
			sb.AppendLine(ApplyFont("Luck: " + _runLuckDisplay, FontPreset.Default));
			sb.AppendLine(ApplyFont(ApplyColor(_runLuckLabel, _runLuckColor), FontPreset.Default));
		}

		private bool RecalculateRunLuck()
		{
			SixSixSixPosterMath.RunLuckComputation computation = SixSixSixPosterMath.CalculateRunLuckDiagnostics(_runExpectedHits, _runUnforcedHits, _runPmf);
			SixSixSixPosterMath.RunLuckSummary summary = computation.Summary;
			bool flag = false;
			flag |= UpdateDouble(ref _runCalibratedZ, summary.CalibratedZ, 0.0001);
			flag |= UpdateDouble(ref _runLuckScore, summary.LuckScore, 0.001);
			flag |= UpdateField(ref _runLuckDisplay, summary.LuckDisplay);
			flag |= UpdateField(ref _runLuckLabel, summary.LuckLabel);
			flag |= UpdateField(ref _runLuckColor, summary.LuckColor);
			LogRunLuckDiagnostics(computation, flag);
			return flag;
		}

		private void ResetHudState(string status)
		{
			_logSurvival = 0.0;
			_tripleCount = 0;
			_eligibleSpinStreakAll = 0;
			_currentTripleChance = 0.0;
			_hasTripleChance = false;
			_hasSurvivalSample = false;
			_eligibilityReached = false;
			_hitOverlayActive = false;
			_spinSerial = 0L;
			_overlayClearSerial = -1L;
			_hitOddsProbability = double.NaN;
			_hitOddsDisplay = string.Empty;
			_streakBeforeHit = 0;
			_statusMessage = status;
			_posterDirty = false;
			_posterStateDirty = true;
			_cachedPosterText = string.Empty;
			_runExpectedHits = 0.0;
			ResetRunDistribution();
			_runUnforcedHits = 0;
			_runCalibratedZ = 0.0;
			_runLuckScore = 0.0;
			_runLuckDisplay = SixSixSixPosterMath.FormatLuckMeter(0.0);
			_runLuckLabel = "Typical odds";
			_runLuckColor = "#FFFFFF";
			_lastRunLuckLogExpected = double.NaN;
			_lastRunLuckLogHits = -1;
			_lastRunLuckLogDisplay = string.Empty;
		}

		private void TryApplyPosterText()
		{
			if (!((Object)(object)_posterText == (Object)null))
			{
				_posterDirty = true;
			}
		}

		private bool RefreshAggregateCounters()
		{
			bool flag = false;
			flag |= UpdateField(ref _cachedEligibleSpinCount, SixSixSixTracker.EligibleSpinCount);
			return flag | UpdateField(ref _cachedSpinsSinceLastTriple, SixSixSixTracker.SpinsSinceLastTriple666);
		}

		private bool UpdateField<T>(ref T field, T value)
		{
			if (EqualityComparer<T>.Default.Equals(field, value))
			{
				return false;
			}
			field = value;
			_posterStateDirty = true;
			return true;
		}

		private bool UpdateDouble(ref double field, double value, double epsilon = 1E-09)
		{
			if (Math.Abs(field - value) <= epsilon)
			{
				return false;
			}
			field = value;
			_posterStateDirty = true;
			return true;
		}

		private void ResetRunDistribution()
		{
			_runPmf.Clear();
			_runPmf.Add(1.0);
		}

		private void UpdateRunDistribution(double hitProbability)
		{
			if (_runPmf.Count == 0)
			{
				_runPmf.Add(1.0);
			}
			double num = SixSixSixPosterMath.ClampProbability(hitProbability);
			double num2 = SixSixSixPosterMath.ClampProbability(1.0 - num);
			int num3 = _runPmf.Count - 1;
			_runPmf.Add(0.0);
			for (int num4 = num3; num4 >= 0; num4--)
			{
				double num5 = _runPmf[num4];
				double value = num5 * num2;
				double num6 = num5 * num;
				_runPmf[num4] = value;
				_runPmf[num4 + 1] += num6;
			}
			double num7 = 0.0;
			for (int i = 0; i < _runPmf.Count; i++)
			{
				num7 += _runPmf[i];
			}
			if (!(Math.Abs(num7 - 1.0) > 1E-09))
			{
				return;
			}
			if (num7 <= 0.0)
			{
				ResetRunDistribution();
				return;
			}
			double num8 = 1.0 / num7;
			for (int j = 0; j < _runPmf.Count; j++)
			{
				_runPmf[j] *= num8;
			}
		}

		private static string ApplyFont(string value, FontPreset preset)
		{
			string text = (0.32 * preset switch
			{
				FontPreset.VeryLarge => 1.35, 
				FontPreset.Large => 1.1, 
				FontPreset.Small => 0.9, 
				FontPreset.VerySmall => 0.85, 
				_ => 1.0, 
			}).ToString("0.###", CultureInfo.InvariantCulture);
			return "<size=" + text + ">" + value + "</size>";
		}

		private static string ApplyColor(string value, string colorHex)
		{
			if (string.IsNullOrEmpty(colorHex))
			{
				return value;
			}
			return "<color=" + colorHex + ">" + value + "</color>";
		}

		private void LogRunLuckDiagnostics(SixSixSixPosterMath.RunLuckComputation computation, bool forceLog)
		{
			if (LogShim.Logger == null)
			{
				return;
			}
			if (!computation.HasData)
			{
				if (forceLog || SignificantDiff(_lastRunLuckLogExpected, _runExpectedHits, 1E-06) || _lastRunLuckLogHits != _runUnforcedHits)
				{
					_lastRunLuckLogExpected = _runExpectedHits;
					_lastRunLuckLogHits = _runUnforcedHits;
					_lastRunLuckLogDisplay = _runLuckDisplay;
				}
			}
			else if (forceLog || SignificantDiff(_lastRunLuckLogExpected, _runExpectedHits, 1E-06) || _lastRunLuckLogHits != _runUnforcedHits || !string.Equals(_lastRunLuckLogDisplay, _runLuckDisplay, StringComparison.OrdinalIgnoreCase))
			{
				SixSixSixPosterMath.RunLuckSummary summary = computation.Summary;
				string text = (computation.OnUnluckySide ? "unluck