Decompiled source of 666PityPoster v0.1.3
BepInEx/plugins/pharmacomaniac-666Poster/666Poster.dll
Decompiled a day ago
The result has been truncated due to the large size, download it to view full contents!
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