Decompiled source of Denis UI v1.3.0

plugins/DenisUI/DenisUI.dll

Decompiled 3 weeks ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Text.RegularExpressions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using TMPro;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("denism")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.3.0.0")]
[assembly: AssemblyInformationalVersion("1.3.0+0d77aba4ef99a87d6be4daeb81b0de1f2e0c0dca")]
[assembly: AssemblyProduct("DenisUI")]
[assembly: AssemblyTitle("DenisUI")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.3.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.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 DenisUI
{
	internal readonly struct ConsumedAnimationIntentState
	{
		public readonly bool IsActive;

		public readonly AnimationIntent Intent;

		public readonly float RecordedAt;

		public ConsumedAnimationIntentState(bool isActive, AnimationIntent intent, float recordedAt)
		{
			IsActive = isActive;
			Intent = intent;
			RecordedAt = recordedAt;
		}
	}
	internal static class AnimationIntentConsumer
	{
		private const float MapStateLifetimeSeconds = 3.1f;

		private const float CartStateLifetimeSeconds = 0.45f;

		private const float BoxesStateLifetimeSeconds = 0.55f;

		private const float HaulStateLifetimeSeconds = 0.75f;

		private static int _lastConsumedSequence;

		private static int _lastRemoteRevision;

		private static string _lastRemoteMapSignature = string.Empty;

		private static string _lastRemoteCartSignature = string.Empty;

		private static string _lastRemoteBoxesSignature = string.Empty;

		private static string _lastRemoteHaulSignature = string.Empty;

		private static ConsumedAnimationIntentState _mapState;

		private static ConsumedAnimationIntentState _cartState;

		private static ConsumedAnimationIntentState _boxesState;

		private static ConsumedAnimationIntentState _haulState;

		internal static void Reset()
		{
			_lastConsumedSequence = 0;
			_lastRemoteRevision = 0;
			_lastRemoteMapSignature = string.Empty;
			_lastRemoteCartSignature = string.Empty;
			_lastRemoteBoxesSignature = string.Empty;
			_lastRemoteHaulSignature = string.Empty;
			_mapState = default(ConsumedAnimationIntentState);
			_cartState = default(ConsumedAnimationIntentState);
			_boxesState = default(ConsumedAnimationIntentState);
			_haulState = default(ConsumedAnimationIntentState);
		}

		internal static void Update(float now)
		{
			TimedAnimationIntent[] recentSince = AnimationIntentRecorder.GetRecentSince(_lastConsumedSequence);
			for (int i = 0; i < recentSince.Length; i++)
			{
				TimedAnimationIntent timedIntent = recentSince[i];
				Consume(timedIntent);
				if (timedIntent.Sequence > _lastConsumedSequence)
				{
					_lastConsumedSequence = timedIntent.Sequence;
				}
			}
			ExpireIfNeeded(now);
		}

		internal static void UpdateFromRemoteSnapshot(OverlaySyncSnapshot snapshot, float now)
		{
			if (snapshot == null)
			{
				ExpireIfNeeded(now);
				return;
			}
			if (snapshot.Revision != _lastRemoteRevision)
			{
				ConsumeRemotePayload(snapshot.MapIntent, now, ref _mapState, ref _lastRemoteMapSignature);
				ConsumeRemotePayload(snapshot.CartIntent, now, ref _cartState, ref _lastRemoteCartSignature);
				ConsumeRemotePayload(snapshot.BoxesIntent, now, ref _boxesState, ref _lastRemoteBoxesSignature);
				ConsumeRemotePayload(snapshot.HaulIntent, now, ref _haulState, ref _lastRemoteHaulSignature);
				_lastRemoteRevision = snapshot.Revision;
			}
			ExpireIfNeeded(now);
		}

		internal static ConsumedAnimationIntentState GetMapState()
		{
			return _mapState;
		}

		internal static ConsumedAnimationIntentState GetCartState()
		{
			return _cartState;
		}

		internal static ConsumedAnimationIntentState GetBoxesState()
		{
			return _boxesState;
		}

		internal static ConsumedAnimationIntentState GetHaulState()
		{
			return _haulState;
		}

		internal static AnimationIntentSyncPayload CaptureMapPayload()
		{
			return CapturePayload(_mapState);
		}

		internal static AnimationIntentSyncPayload CaptureCartPayload()
		{
			return CapturePayload(_cartState);
		}

		internal static AnimationIntentSyncPayload CaptureBoxesPayload()
		{
			return CapturePayload(_boxesState);
		}

		internal static AnimationIntentSyncPayload CaptureHaulPayload()
		{
			return CapturePayload(_haulState);
		}

		private static void Consume(TimedAnimationIntent timedIntent)
		{
			ConsumedAnimationIntentState consumedAnimationIntentState = new ConsumedAnimationIntentState(isActive: true, timedIntent.Intent, timedIntent.RecordedAt);
			switch (timedIntent.Intent.Kind)
			{
			case AnimationIntentKind.MapValueChanged:
				_mapState = consumedAnimationIntentState;
				break;
			case AnimationIntentKind.CartValueChanged:
				_cartState = consumedAnimationIntentState;
				break;
			case AnimationIntentKind.CosmeticBoxesChanged:
				_boxesState = consumedAnimationIntentState;
				break;
			case AnimationIntentKind.HaulChanged:
				_haulState = consumedAnimationIntentState;
				break;
			}
		}

		private static AnimationIntentSyncPayload CapturePayload(ConsumedAnimationIntentState state)
		{
			if (state.IsActive)
			{
				return AnimationIntentSyncPayload.FromIntent(state.Intent);
			}
			return new AnimationIntentSyncPayload();
		}

		private static void ConsumeRemotePayload(AnimationIntentSyncPayload? payload, float now, ref ConsumedAnimationIntentState state, ref string lastSignature)
		{
			string payloadSignature = GetPayloadSignature(payload);
			if (string.Equals(payloadSignature, lastSignature, StringComparison.Ordinal))
			{
				return;
			}
			lastSignature = payloadSignature;
			if (payload != null && payload.Active)
			{
				AnimationIntent intent = payload.ToIntent();
				if (intent.Kind != 0)
				{
					state = new ConsumedAnimationIntentState(isActive: true, intent, now);
				}
			}
		}

		private static string GetPayloadSignature(AnimationIntentSyncPayload? payload)
		{
			if (payload == null || !payload.Active)
			{
				return "0";
			}
			return $"1:{payload.Kind}:{payload.PreviousValue}:{payload.NextValue}:{payload.Direction}:{payload.AnimationCode}:{payload.ReasonCode}";
		}

		private static void ExpireIfNeeded(float now)
		{
			_mapState = Expire(_mapState, now, 3.1f);
			_cartState = Expire(_cartState, now, 0.45f);
			_boxesState = Expire(_boxesState, now, 0.55f);
			_haulState = Expire(_haulState, now, 0.75f);
		}

		private static ConsumedAnimationIntentState Expire(ConsumedAnimationIntentState state, float now, float lifetimeSeconds)
		{
			if (!state.IsActive)
			{
				return state;
			}
			if (!(now - state.RecordedAt > Mathf.Max(0.01f, lifetimeSeconds)))
			{
				return state;
			}
			return default(ConsumedAnimationIntentState);
		}
	}
	internal enum AnimationDirection
	{
		None,
		Up,
		Down
	}
	internal enum AnimationIntentKind
	{
		None,
		MapValueChanged,
		CartValueChanged,
		CosmeticBoxesChanged,
		HaulChanged
	}
	internal readonly struct AnimationIntent
	{
		public readonly AnimationIntentKind Kind;

		public readonly int PreviousValue;

		public readonly int NextValue;

		public readonly AnimationDirection Direction;

		public readonly string AnimationCode;

		public readonly string? ReasonCode;

		public AnimationIntent(AnimationIntentKind kind, int previousValue, int nextValue, AnimationDirection direction, string animationCode, string? reasonCode = null)
		{
			Kind = kind;
			PreviousValue = previousValue;
			NextValue = nextValue;
			Direction = direction;
			AnimationCode = animationCode;
			ReasonCode = reasonCode;
		}
	}
	[Serializable]
	internal sealed class AnimationIntentSyncPayload
	{
		public bool Active;

		public int Kind;

		public int PreviousValue;

		public int NextValue;

		public int Direction;

		public string AnimationCode = string.Empty;

		public string ReasonCode = string.Empty;

		public static AnimationIntentSyncPayload FromIntent(AnimationIntent intent)
		{
			return new AnimationIntentSyncPayload
			{
				Active = (intent.Kind != AnimationIntentKind.None),
				Kind = (int)intent.Kind,
				PreviousValue = intent.PreviousValue,
				NextValue = intent.NextValue,
				Direction = (int)intent.Direction,
				AnimationCode = (intent.AnimationCode ?? string.Empty),
				ReasonCode = (intent.ReasonCode ?? string.Empty)
			};
		}

		public AnimationIntent ToIntent()
		{
			if (!Active)
			{
				return default(AnimationIntent);
			}
			AnimationIntentKind kind = (Enum.IsDefined(typeof(AnimationIntentKind), Kind) ? ((AnimationIntentKind)Kind) : AnimationIntentKind.None);
			AnimationDirection direction = (Enum.IsDefined(typeof(AnimationDirection), Direction) ? ((AnimationDirection)Direction) : AnimationDirection.None);
			return new AnimationIntent(kind, PreviousValue, NextValue, direction, AnimationCode ?? string.Empty, string.IsNullOrWhiteSpace(ReasonCode) ? null : ReasonCode);
		}
	}
	internal readonly struct TimedAnimationIntent
	{
		public readonly AnimationIntent Intent;

		public readonly float RecordedAt;

		public readonly int Sequence;

		public TimedAnimationIntent(AnimationIntent intent, float recordedAt, int sequence)
		{
			Intent = intent;
			RecordedAt = recordedAt;
			Sequence = sequence;
		}
	}
	internal static class AnimationIntentRecorder
	{
		private const int MaxRecentIntents = 24;

		private static readonly Queue<TimedAnimationIntent> RecentIntents = new Queue<TimedAnimationIntent>();

		private static int _nextSequence = 1;

		internal static void Reset()
		{
			RecentIntents.Clear();
			_nextSequence = 1;
		}

		internal static void Record(AnimationIntent intent)
		{
			if (intent.Kind != 0)
			{
				RecentIntents.Enqueue(new TimedAnimationIntent(intent, Time.unscaledTime, _nextSequence++));
				while (RecentIntents.Count > 24)
				{
					RecentIntents.Dequeue();
				}
			}
		}

		internal static TimedAnimationIntent[] GetRecentSince(int lastSequence)
		{
			List<TimedAnimationIntent> list = new List<TimedAnimationIntent>();
			foreach (TimedAnimationIntent recentIntent in RecentIntents)
			{
				if (recentIntent.Sequence > lastSequence)
				{
					list.Add(recentIntent);
				}
			}
			return list.ToArray();
		}
	}
	internal static class BountySupportBridge
	{
		private static readonly string[] BountyPluginTypeNames = new string[2] { "BountyHunters.Plugin", "BountyHuntersUI.Plugin" };

		private static readonly string[] BountyBridgeTypeNames = new string[2] { "BountyHunters.BountyBridge", "BountyHuntersUI.BountyBridge" };

		private static Type? _pluginType;

		private static Type? _bridgeType;

		private static MethodInfo? _getOverlayLockedBountyTotalMethod;

		private static MethodInfo? _getFinalShopTargetMoneyMethod;

		private static MethodInfo? _shouldOverlayShowBountyMethod;

		private static PropertyInfo? _hasOverlayExportProperty;

		private static bool _resolved;

		private static Type? _roundDirectorType;

		private static object? _roundDirectorInstance;

		private static FieldInfo? _currentHaulField;

		private static FieldInfo? _allExtractionPointsCompletedField;

		private static bool _roundDirectorInitialized;

		private static bool _bountyEventsSubscribed = false;

		internal static bool TryGetLockedBountyTotal(out int total)
		{
			total = 0;
			if (!Resolve())
			{
				return false;
			}
			try
			{
				if (!ShouldUseOverlayExport())
				{
					return false;
				}
				if (_getOverlayLockedBountyTotalMethod == null)
				{
					return false;
				}
				object obj = _getOverlayLockedBountyTotalMethod.Invoke(null, null);
				if (obj == null)
				{
					return false;
				}
				total = Convert.ToInt32(obj);
				return total > 0;
			}
			catch
			{
				return false;
			}
		}

		internal static bool TryGetFinalShopTargetMoney(out int total)
		{
			total = 0;
			if (!Resolve())
			{
				return false;
			}
			try
			{
				if (!ShouldUseOverlayExport())
				{
					return false;
				}
				if (_getFinalShopTargetMoneyMethod == null)
				{
					return false;
				}
				object obj = _getFinalShopTargetMoneyMethod.Invoke(null, null);
				if (obj == null)
				{
					return false;
				}
				total = Convert.ToInt32(obj);
				return total > 0;
			}
			catch
			{
				return false;
			}
		}

		internal static bool TryGetCurrentHaulValue(out int haulValue)
		{
			haulValue = 0;
			try
			{
				InitializeRoundDirector();
				if (_currentHaulField == null)
				{
					return false;
				}
				if (_roundDirectorInstance == null)
				{
					return false;
				}
				object value = _currentHaulField.GetValue(_roundDirectorInstance);
				haulValue = (int)(value ?? ((object)0));
				return haulValue >= 0;
			}
			catch (Exception ex)
			{
				DenisUIPlugin.DebugLog("[BountySupportBridge.TryGetCurrentHaulValue] ❌ Exception: " + ex.Message);
				return false;
			}
		}

		internal static bool IsInExtractionClosingPhase()
		{
			try
			{
				InitializeRoundDirector();
				if (_allExtractionPointsCompletedField == null || _roundDirectorInstance == null)
				{
					return false;
				}
				return (bool)(_allExtractionPointsCompletedField.GetValue(_roundDirectorInstance) ?? ((object)false));
			}
			catch
			{
				return false;
			}
		}

		internal static bool IsInExtractionPhase()
		{
			try
			{
				if (SemiFunc.RunIsLevel())
				{
					return !IsInExtractionClosingPhase();
				}
				return false;
			}
			catch
			{
				return false;
			}
		}

		private static void InitializeRoundDirector()
		{
			if (_roundDirectorInitialized)
			{
				return;
			}
			_roundDirectorInitialized = true;
			try
			{
				_roundDirectorType = Type.GetType("RoundDirector");
				if (_roundDirectorType == null)
				{
					DenisUIPlugin.DebugLog("[BountySupportBridge] ❌ RoundDirector type not found!");
					return;
				}
				_roundDirectorInstance = _roundDirectorType.GetField("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
				if (_roundDirectorInstance == null)
				{
					DenisUIPlugin.DebugLog("[BountySupportBridge] ❌ RoundDirector.instance is null!");
					return;
				}
				_currentHaulField = _roundDirectorType.GetField("currentHaul", BindingFlags.Instance | BindingFlags.NonPublic);
				if (_currentHaulField == null)
				{
					DenisUIPlugin.DebugLog("[BountySupportBridge] ⚠\ufe0f currentHaul field not found!");
					return;
				}
				_allExtractionPointsCompletedField = _roundDirectorType.GetField("allExtractionPointsCompleted", BindingFlags.Instance | BindingFlags.NonPublic);
				DenisUIPlugin.DebugLog("[BountySupportBridge] ✅ RoundDirector initialized (currentHaul field available)");
			}
			catch (Exception ex)
			{
				DenisUIPlugin.DebugLog("[BountySupportBridge] ❌ RoundDirector init failed: " + ex.Message);
			}
		}

		private static bool Resolve()
		{
			if (_resolved)
			{
				if (!(_pluginType != null))
				{
					return _bridgeType != null;
				}
				return true;
			}
			_resolved = true;
			DenisUIPlugin.DebugLog("[BountySupportBridge.Resolve] Starting resolution of Bounty Hunters types");
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				for (int j = 0; j < BountyPluginTypeNames.Length; j++)
				{
					Type type = assembly.GetType(BountyPluginTypeNames[j], throwOnError: false, ignoreCase: false);
					if (!(type == null))
					{
						_pluginType = type;
						DenisUIPlugin.DebugLog("[BountySupportBridge.Resolve] ✓ Found Bounty Plugin type: " + type.FullName);
						_getOverlayLockedBountyTotalMethod = type.GetMethod("GetOverlayLockedBountyTotal", BindingFlags.Static | BindingFlags.Public);
						_getFinalShopTargetMoneyMethod = type.GetMethod("GetFinalShopTargetMoney", BindingFlags.Static | BindingFlags.Public);
						_shouldOverlayShowBountyMethod = type.GetMethod("ShouldOverlayShowBounty", BindingFlags.Static | BindingFlags.Public);
						_hasOverlayExportProperty = type.GetProperty("HasOverlayExport", BindingFlags.Static | BindingFlags.Public);
						break;
					}
				}
				if (_pluginType != null)
				{
					break;
				}
			}
			Assembly[] assemblies2 = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly2 in assemblies2)
			{
				for (int l = 0; l < BountyBridgeTypeNames.Length; l++)
				{
					Type type2 = assembly2.GetType(BountyBridgeTypeNames[l], throwOnError: false, ignoreCase: false);
					if (!(type2 == null))
					{
						_bridgeType = type2;
						DenisUIPlugin.DebugLog("[BountySupportBridge.Resolve] ✓ Found Bounty Bridge type: " + type2.FullName);
						if ((object)_getOverlayLockedBountyTotalMethod == null)
						{
							_getOverlayLockedBountyTotalMethod = type2.GetMethod("GetOverlayLockedBountyTotal", BindingFlags.Static | BindingFlags.Public);
						}
						if ((object)_getFinalShopTargetMoneyMethod == null)
						{
							_getFinalShopTargetMoneyMethod = type2.GetMethod("GetFinalShopTargetMoney", BindingFlags.Static | BindingFlags.Public);
						}
						if ((object)_shouldOverlayShowBountyMethod == null)
						{
							_shouldOverlayShowBountyMethod = type2.GetMethod("ShouldOverlayShowBounty", BindingFlags.Static | BindingFlags.Public);
						}
						if ((object)_hasOverlayExportProperty == null)
						{
							_hasOverlayExportProperty = type2.GetProperty("HasOverlayExport", BindingFlags.Static | BindingFlags.Public);
						}
						break;
					}
				}
				if (_bridgeType != null)
				{
					break;
				}
			}
			bool flag = (_pluginType != null || _bridgeType != null) && (_getOverlayLockedBountyTotalMethod != null || _getFinalShopTargetMoneyMethod != null);
			DenisUIPlugin.DebugLog("[BountySupportBridge.Resolve] Resolution complete: " + (flag ? "✅ SUCCESS" : "❌ FAILED"));
			if (_pluginType != null)
			{
				DenisUIPlugin.DebugLog("[BountySupportBridge.Resolve]   ✓ Plugin type resolved");
			}
			if (_bridgeType != null)
			{
				DenisUIPlugin.DebugLog("[BountySupportBridge.Resolve]   ✓ Bridge type resolved");
			}
			if (_getOverlayLockedBountyTotalMethod != null)
			{
				DenisUIPlugin.DebugLog("[BountySupportBridge.Resolve]   ✓ GetOverlayLockedBountyTotal method resolved");
			}
			if (_getFinalShopTargetMoneyMethod != null)
			{
				DenisUIPlugin.DebugLog("[BountySupportBridge.Resolve]   ✓ GetFinalShopTargetMoney method resolved");
			}
			return flag;
		}

		private static bool ShouldUseOverlayExport()
		{
			if (_hasOverlayExportProperty != null)
			{
				object value = _hasOverlayExportProperty.GetValue(null, null);
				if (value is bool && !(bool)value)
				{
					return false;
				}
			}
			return true;
		}

		internal static void SubscribeToBountyEvents()
		{
			if (_bountyEventsSubscribed)
			{
				DenisUIPlugin.DebugLog("[BountySupportBridge] SubscribeToBountyEvents: Already subscribed, skipping");
				return;
			}
			_bountyEventsSubscribed = true;
			DenisUIPlugin.DebugLog("[BountySupportBridge] SubscribeToBountyEvents: Starting event subscription");
			try
			{
				Type type = Type.GetType("BountyHunters.BountyEvents");
				if (type == null)
				{
					DenisUIPlugin.DebugLog("[BountySupportBridge] ERROR: BountyEvents type not found! Bounty Hunters v1.3.1+ may not be installed");
					return;
				}
				DenisUIPlugin.DebugLog("[BountySupportBridge] ✓ BountyEvents type found: " + type.FullName);
				EventInfo @event = type.GetEvent("OnHaulUpdated", BindingFlags.Static | BindingFlags.Public);
				if (@event != null)
				{
					MethodInfo method = type.GetMethod("add_OnHaulUpdated", BindingFlags.Static | BindingFlags.Public);
					if (method != null)
					{
						Action<int> action = OnBountyHaulUpdated;
						method.Invoke(null, new object[1] { action });
						DenisUIPlugin.DebugLog("[BountySupportBridge] Subscribed to OnHaulUpdated event");
					}
				}
				else
				{
					FieldInfo field = type.GetField("OnHaulUpdated", BindingFlags.Static | BindingFlags.Public);
					if (field != null)
					{
						Action<int> b = OnBountyHaulUpdated;
						Delegate a = (Delegate)field.GetValue(null);
						Delegate value = Delegate.Combine(a, b);
						field.SetValue(null, value);
						DenisUIPlugin.DebugLog("[BountySupportBridge] Subscribed to OnHaulUpdated event (via field)");
					}
				}
				EventInfo event2 = type.GetEvent("OnBountyPhaseChanged", BindingFlags.Static | BindingFlags.Public);
				if (event2 != null)
				{
					MethodInfo method2 = type.GetMethod("add_OnBountyPhaseChanged", BindingFlags.Static | BindingFlags.Public);
					if (method2 != null)
					{
						Action action2 = OnBountyPhaseChanged;
						method2.Invoke(null, new object[1] { action2 });
						DenisUIPlugin.DebugLog("[BountySupportBridge] Subscribed to OnBountyPhaseChanged event");
					}
				}
				else
				{
					FieldInfo field2 = type.GetField("OnBountyPhaseChanged", BindingFlags.Static | BindingFlags.Public);
					if (field2 != null)
					{
						Action b2 = OnBountyPhaseChanged;
						Delegate a2 = (Delegate)field2.GetValue(null);
						Delegate value2 = Delegate.Combine(a2, b2);
						field2.SetValue(null, value2);
						DenisUIPlugin.DebugLog("[BountySupportBridge] Subscribed to OnBountyPhaseChanged event (via field)");
					}
				}
				EventInfo event3 = type.GetEvent("OnBountyRewardGranted", BindingFlags.Static | BindingFlags.Public);
				if (event3 != null)
				{
					MethodInfo method3 = type.GetMethod("add_OnBountyRewardGranted", BindingFlags.Static | BindingFlags.Public);
					if (method3 != null)
					{
						Action<int> action3 = OnBountyRewardGranted;
						method3.Invoke(null, new object[1] { action3 });
						DenisUIPlugin.DebugLog("[BountySupportBridge] Subscribed to OnBountyRewardGranted event");
					}
					return;
				}
				FieldInfo field3 = type.GetField("OnBountyRewardGranted", BindingFlags.Static | BindingFlags.Public);
				if (field3 != null)
				{
					Action<int> b3 = OnBountyRewardGranted;
					Delegate a3 = (Delegate)field3.GetValue(null);
					Delegate value3 = Delegate.Combine(a3, b3);
					field3.SetValue(null, value3);
					DenisUIPlugin.DebugLog("[BountySupportBridge] Subscribed to OnBountyRewardGranted event (via field)");
				}
			}
			catch (Exception ex)
			{
				DenisUIPlugin.DebugLog("[BountySupportBridge] Failed to subscribe to Bounty events: " + ex.Message);
			}
		}

		private static void OnBountyHaulUpdated(int newHaul)
		{
			DenisUIPlugin.DebugLog($"[BountySupportBridge] OnHaulUpdated fired: {newHaul}");
			DataSnapshotBuilder.InvalidateSnapshot();
		}

		private static void OnBountyPhaseChanged()
		{
			DenisUIPlugin.DebugLog("[BountySupportBridge] OnBountyPhaseChanged fired");
			DataSnapshotBuilder.InvalidateSnapshot();
		}

		private static void OnBountyRewardGranted(int rewardAmount)
		{
			DenisUIPlugin.DebugLog($"[BountySupportBridge] OnBountyRewardGranted fired: {rewardAmount}");
			DataSnapshotBuilder.InvalidateSnapshot();
		}
	}
	internal readonly struct CosmeticBoxSnapshot
	{
		internal int Common { get; }

		internal int Uncommon { get; }

		internal int Rare { get; }

		internal int UltraRare { get; }

		internal CosmeticBoxSnapshot(int common, int uncommon, int rare, int ultraRare)
		{
			Common = common;
			Uncommon = uncommon;
			Rare = rare;
			UltraRare = ultraRare;
		}
	}
	internal static class CosmeticBoxTracker
	{
		private sealed class AnimatedBoxGlyph
		{
			public string StyleName { get; }

			public string ColorHex { get; }

			public float StartedAt { get; }

			public float RemovingStartedAt { get; private set; }

			public bool IsRemoving => RemovingStartedAt > -99999f;

			public AnimatedBoxGlyph(string styleName, string colorHex, float startedAt)
			{
				StyleName = styleName;
				ColorHex = colorHex;
				StartedAt = startedAt;
				RemovingStartedAt = -100000f;
			}

			public void BeginRemoving(float now)
			{
				if (!IsRemoving)
				{
					RemovingStartedAt = now;
				}
			}

			public bool IsExpired(float now, float destroySeconds)
			{
				if (IsRemoving)
				{
					return now - RemovingStartedAt >= destroySeconds;
				}
				return false;
			}
		}

		private sealed class CosmeticBoxStyle
		{
			public string Name { get; }

			public string ColorHex { get; }

			public CosmeticBoxStyle(string name, string colorHex)
			{
				Name = name;
				ColorHex = colorHex;
			}
		}

		private sealed class CosmeticBoxCounts
		{
			public int Common;

			public int Uncommon;

			public int Rare;

			public int UltraRare;

			public int Total => Common + Uncommon + Rare + UltraRare;

			public void Add(string rarityName)
			{
				if (string.Equals(rarityName, "Common", StringComparison.OrdinalIgnoreCase))
				{
					Common++;
				}
				else if (string.Equals(rarityName, "Uncommon", StringComparison.OrdinalIgnoreCase))
				{
					Uncommon++;
				}
				else if (string.Equals(rarityName, "Rare", StringComparison.OrdinalIgnoreCase))
				{
					Rare++;
				}
				else if (string.Equals(rarityName, "UltraRare", StringComparison.OrdinalIgnoreCase) || string.Equals(rarityName, "Ultra Rare", StringComparison.OrdinalIgnoreCase))
				{
					UltraRare++;
				}
			}

			public int GetCount(string rarityName)
			{
				if (string.Equals(rarityName, "Common", StringComparison.OrdinalIgnoreCase))
				{
					return Common;
				}
				if (string.Equals(rarityName, "Uncommon", StringComparison.OrdinalIgnoreCase))
				{
					return Uncommon;
				}
				if (string.Equals(rarityName, "Rare", StringComparison.OrdinalIgnoreCase))
				{
					return Rare;
				}
				if (string.Equals(rarityName, "Ultra Rare", StringComparison.OrdinalIgnoreCase))
				{
					return UltraRare;
				}
				return 0;
			}
		}

		private const float RefreshInterval = 1f;

		private const float BoxSlideInSeconds = 0.26f;

		private const float BoxStaggerSeconds = 0.1f;

		private const float BoxStartOffsetY = 8f;

		private const float BoxBlinkRevealSeconds = 0.1f;

		private const float BoxDestroySeconds = 0.3f;

		private const float BoxDestroyWhiteSeconds = 0.1f;

		private const string DestroyColorHex = "#EF3338";

		private static readonly CosmeticBoxStyle[] CosmeticBoxStyles = new CosmeticBoxStyle[4]
		{
			new CosmeticBoxStyle("Common", "#15E803"),
			new CosmeticBoxStyle("Uncommon", "#027BE7"),
			new CosmeticBoxStyle("Rare", "#EA00E7"),
			new CosmeticBoxStyle("Ultra Rare", "#E7D301")
		};

		private static Type? _cosmeticWorldObjectType;

		private static bool _typeResolved;

		private static FieldInfo? _rarityField;

		private static bool _rarityFieldResolved;

		private static float _lastRefreshAt = -100000f;

		private static CosmeticBoxCounts _cachedCounts = new CosmeticBoxCounts();

		private static CosmeticBoxCounts _lastAnimatedCounts = new CosmeticBoxCounts();

		private static string _cachedLine = string.Empty;

		private static readonly List<AnimatedBoxGlyph> _animatedBoxes = new List<AnimatedBoxGlyph>();

		internal static string BuildLine()
		{
			RefreshIfNeeded();
			return _cachedLine;
		}

		internal static CosmeticBoxSnapshot GetSnapshot()
		{
			RefreshIfNeeded();
			return new CosmeticBoxSnapshot(_cachedCounts.Common, _cachedCounts.Uncommon, _cachedCounts.Rare, _cachedCounts.UltraRare);
		}

		internal static string BuildLine(CosmeticBoxSnapshot snapshot)
		{
			return BuildLine(snapshot, default(ConsumedAnimationIntentState));
		}

		internal static string BuildLine(CosmeticBoxSnapshot snapshot, ConsumedAnimationIntentState intentState)
		{
			CosmeticBoxCounts counts = new CosmeticBoxCounts
			{
				Common = snapshot.Common,
				Uncommon = snapshot.Uncommon,
				Rare = snapshot.Rare,
				UltraRare = snapshot.UltraRare
			};
			return BuildAnimatedLineFromCounts(counts, Time.unscaledTime, intentState);
		}

		private static void RefreshIfNeeded()
		{
			float unscaledTime = Time.unscaledTime;
			if (!(unscaledTime - _lastRefreshAt < 1f))
			{
				_lastRefreshAt = unscaledTime;
				_cachedCounts = GetCounts();
				_cachedLine = BuildAnimatedLineFromCounts(_cachedCounts, unscaledTime, default(ConsumedAnimationIntentState));
			}
		}

		private static CosmeticBoxCounts GetCounts()
		{
			CosmeticBoxCounts cosmeticBoxCounts = new CosmeticBoxCounts();
			Type cosmeticWorldObjectType = GetCosmeticWorldObjectType();
			if (cosmeticWorldObjectType == null)
			{
				return cosmeticBoxCounts;
			}
			FieldInfo rarityField = GetRarityField(cosmeticWorldObjectType);
			if (rarityField == null)
			{
				return cosmeticBoxCounts;
			}
			Object[] array = Object.FindObjectsOfType(cosmeticWorldObjectType);
			foreach (Object val in array)
			{
				if (!(val == (Object)null))
				{
					object value;
					try
					{
						value = rarityField.GetValue(val);
					}
					catch
					{
						continue;
					}
					cosmeticBoxCounts.Add(value?.ToString() ?? string.Empty);
				}
			}
			return cosmeticBoxCounts;
		}

		private static string BuildAnimatedLineFromCounts(CosmeticBoxCounts counts, float now, ConsumedAnimationIntentState intentState)
		{
			SyncAnimatedBoxes(counts, now, intentState);
			if (_animatedBoxes.Count == 0)
			{
				return string.Empty;
			}
			StringBuilder stringBuilder = new StringBuilder();
			int glyphSize = Mathf.RoundToInt(26f * DenisUIPlugin.GetCosmeticBoxesScale());
			for (int i = 0; i < _animatedBoxes.Count; i++)
			{
				AnimatedBoxGlyph glyph = _animatedBoxes[i];
				if (i > 0)
				{
					stringBuilder.Append(' ');
				}
				RenderGlyph(stringBuilder, glyph, glyphSize, now);
			}
			return stringBuilder.ToString();
		}

		private static void RenderGlyph(StringBuilder sb, AnimatedBoxGlyph glyph, int glyphSize, float now)
		{
			float alpha;
			float num4;
			string text;
			if (glyph.IsRemoving)
			{
				float num = now - glyph.RemovingStartedAt;
				float num2 = Mathf.Clamp01(num / Mathf.Max(0.01f, 0.3f));
				float num3 = num2 * num2 * (3f - 2f * num2);
				alpha = 1f - num2;
				num4 = Mathf.Lerp(0f, -5f, num3);
				text = ((num < 0.1f) ? "#FFFFFF" : "#EF3338");
			}
			else
			{
				alpha = 1f;
				num4 = 0f;
				text = glyph.ColorHex;
			}
			string value = text + ToAlphaHex(alpha);
			sb.Append("<voffset=");
			sb.Append(num4.ToString("0.##"));
			sb.Append("px><size=");
			sb.Append(glyphSize);
			sb.Append("><color=");
			sb.Append(value);
			sb.Append(">■</color></size></voffset>");
		}

		private static void SyncAnimatedBoxes(CosmeticBoxCounts counts, float now, ConsumedAnimationIntentState intentState)
		{
			CleanupRemovedBoxes(now);
			CosmeticBoxCounts previousCounts = CloneCounts(_lastAnimatedCounts);
			List<CosmeticBoxStyle> list = BuildDesiredBoxSequence(counts);
			List<AnimatedBoxGlyph> list2 = new List<AnimatedBoxGlyph>();
			for (int i = 0; i < _animatedBoxes.Count; i++)
			{
				if (!_animatedBoxes[i].IsRemoving)
				{
					list2.Add(_animatedBoxes[i]);
				}
			}
			int j;
			for (j = 0; j < list2.Count && j < list.Count && string.Equals(list2[j].StyleName, list[j].Name, StringComparison.Ordinal); j++)
			{
			}
			for (int k = j; k < list2.Count; k++)
			{
				list2[k].BeginRemoving(now);
			}
			RecordIntentIfCountsChanged(previousCounts, counts);
			for (int l = j; l < list.Count; l++)
			{
				CosmeticBoxStyle cosmeticBoxStyle = list[l];
				float startedAt = now + (float)(l - j) * 0.1f;
				_animatedBoxes.Add(new AnimatedBoxGlyph(cosmeticBoxStyle.Name, cosmeticBoxStyle.ColorHex, startedAt));
			}
			_lastAnimatedCounts = CloneCounts(counts);
		}

		private static void CleanupRemovedBoxes(float now)
		{
			for (int num = _animatedBoxes.Count - 1; num >= 0; num--)
			{
				if (_animatedBoxes[num].IsExpired(now, 0.3f))
				{
					_animatedBoxes.RemoveAt(num);
				}
			}
		}

		private static void RecordIntentIfCountsChanged(CosmeticBoxCounts previousCounts, CosmeticBoxCounts nextCounts)
		{
			int total = previousCounts.Total;
			int total2 = nextCounts.Total;
			if (total != total2)
			{
				AnimationIntentRecorder.Record(new AnimationIntent(AnimationIntentKind.CosmeticBoxesChanged, total, total2, (total2 > total) ? AnimationDirection.Up : AnimationDirection.Down, (total2 > total) ? "boxes_spawn" : "boxes_destroy"));
			}
		}

		private static CosmeticBoxCounts CloneCounts(CosmeticBoxCounts counts)
		{
			return new CosmeticBoxCounts
			{
				Common = counts.Common,
				Uncommon = counts.Uncommon,
				Rare = counts.Rare,
				UltraRare = counts.UltraRare
			};
		}

		private static List<CosmeticBoxStyle> BuildDesiredBoxSequence(CosmeticBoxCounts counts)
		{
			List<CosmeticBoxStyle> list = new List<CosmeticBoxStyle>();
			CosmeticBoxStyle[] cosmeticBoxStyles = CosmeticBoxStyles;
			foreach (CosmeticBoxStyle cosmeticBoxStyle in cosmeticBoxStyles)
			{
				int count = counts.GetCount(cosmeticBoxStyle.Name);
				for (int j = 0; j < count; j++)
				{
					list.Add(cosmeticBoxStyle);
				}
			}
			return list;
		}

		private static Type? GetCosmeticWorldObjectType()
		{
			if (_typeResolved)
			{
				return _cosmeticWorldObjectType;
			}
			_typeResolved = true;
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				Type type = assembly.GetType("CosmeticWorldObject", throwOnError: false, ignoreCase: false);
				if (type != null)
				{
					_cosmeticWorldObjectType = type;
					break;
				}
			}
			return _cosmeticWorldObjectType;
		}

		private static FieldInfo? GetRarityField(Type cosmeticWorldObjectType)
		{
			if (_rarityFieldResolved)
			{
				return _rarityField;
			}
			_rarityFieldResolved = true;
			_rarityField = cosmeticWorldObjectType.GetField("rarity", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			return _rarityField;
		}

		private static string ToAlphaHex(float alpha)
		{
			return Mathf.Clamp(Mathf.RoundToInt(alpha * 255f), 0, 255).ToString("X2");
		}
	}
	internal static class DataSnapshotBuilder
	{
		private const float SnapshotCacheIntervalSeconds = 0.05f;

		private static float _lastSnapshotBuiltAt = -100000f;

		private static OverlaySyncSnapshot? _lastSnapshot;

		private static int _cachedHaulForDisplay = 0;

		private static bool _extractionWasComplete = false;

		private static bool _snapshotInvalidated = false;

		private static int _cachedMapValue = 0;

		private static int _cachedCartValue = 0;

		private static CosmeticBoxSnapshot _cachedBoxes = new CosmeticBoxSnapshot(0, 0, 0, 0);

		private static int _lastLoggedHaulValue = -1;

		private static int _cachedSecondaryHaul = -1;

		private static float _lastSecondaryCheckTime = -100f;

		internal static OverlaySyncSnapshot BuildSnapshot(bool mapOpen, bool overlayVisible)
		{
			float unscaledTime = Time.unscaledTime;
			if (!_snapshotInvalidated && !(unscaledTime - _lastSnapshotBuiltAt >= 0.05f) && _lastSnapshot != null)
			{
				return _lastSnapshot;
			}
			string text = (_snapshotInvalidated ? "EVENT INVALIDATED" : "CACHE TIMEOUT");
			if (_snapshotInvalidated)
			{
				DenisUIPlugin.DebugLog("[BuildSnapshot] \ud83d\udd28 REBUILDING SNAPSHOT (reason: " + text + ")");
			}
			_snapshotInvalidated = false;
			_lastSnapshotBuiltAt = unscaledTime;
			bool flag = ReadExtractionClosed();
			int num = ReadMapValue(flag);
			int num2 = ReadCartValue(flag);
			if (!flag)
			{
				if (num > 0)
				{
					_cachedMapValue = num;
				}
				if (num2 > 0)
				{
					_cachedCartValue = num2;
				}
				if (num == 0 && _cachedMapValue > 0)
				{
					num = _cachedMapValue;
				}
				if (num2 == 0 && _cachedCartValue > 0)
				{
					num2 = _cachedCartValue;
				}
			}
			else
			{
				_cachedMapValue = 0;
				_cachedCartValue = 0;
				_cachedBoxes = new CosmeticBoxSnapshot(0, 0, 0, 0);
			}
			if (flag && !_extractionWasComplete)
			{
				_extractionWasComplete = true;
				int cachedHaulForDisplay = ReadHaulValue();
				_cachedHaulForDisplay = cachedHaulForDisplay;
				DenisUIPlugin.DebugLog($"[BuildSnapshot] Extraction completed - captured haul: {_cachedHaulForDisplay}");
				DenisUIEvents.RaiseHaulCaptured(_cachedHaulForDisplay);
			}
			if (!flag && _extractionWasComplete)
			{
				_extractionWasComplete = false;
				_cachedHaulForDisplay = 0;
			}
			if (!flag)
			{
				_extractionWasComplete = false;
			}
			int num3 = ReadHaulValue();
			bool haulTransitionActive = ReadHaulTransitionActive();
			CosmeticBoxSnapshot cachedBoxes = ReadCosmeticBoxes();
			if (!flag)
			{
				if (cachedBoxes.Common > 0 || cachedBoxes.Uncommon > 0 || cachedBoxes.Rare > 0 || cachedBoxes.UltraRare > 0)
				{
					_cachedBoxes = cachedBoxes;
				}
				else if (_cachedBoxes.Common > 0 || _cachedBoxes.Uncommon > 0 || _cachedBoxes.Rare > 0 || _cachedBoxes.UltraRare > 0)
				{
					cachedBoxes = _cachedBoxes;
				}
			}
			_lastSnapshot = new OverlaySyncSnapshot
			{
				Active = SemiFunc.RunIsLevel(),
				MapValue = num,
				CartValue = num2,
				HaulValue = num3,
				ExtractionClosed = flag,
				HaulTransitionActive = haulTransitionActive,
				BoxCommon = cachedBoxes.Common,
				BoxUncommon = cachedBoxes.Uncommon,
				BoxRare = cachedBoxes.Rare,
				BoxUltraRare = cachedBoxes.UltraRare,
				MapIntent = new AnimationIntentSyncPayload(),
				CartIntent = new AnimationIntentSyncPayload(),
				BoxesIntent = new AnimationIntentSyncPayload(),
				HaulIntent = new AnimationIntentSyncPayload(),
				Revision = 0
			};
			if (Math.Abs(num3 - _lastLoggedHaulValue) > 100)
			{
				DenisUIPlugin.DebugLog($"[BuildSnapshot] Snapshot haul updated: {num3}");
				_lastLoggedHaulValue = num3;
			}
			DenisUIEvents.RaiseSnapshotUpdated(_lastSnapshot);
			return _lastSnapshot;
		}

		private static int ReadMapValue(bool extractionClosed)
		{
			if (extractionClosed)
			{
				return 0;
			}
			float num = 0f;
			try
			{
				foreach (ValuableObject item in GameStateAccessors.GetValuablesOnMap())
				{
					if ((Object)(object)item != (Object)null)
					{
						num += GetValuableCurrent(item);
					}
				}
			}
			catch
			{
			}
			return Mathf.RoundToInt(Mathf.Max(0f, num));
		}

		private static int ReadCartValue(bool extractionClosed)
		{
			if (extractionClosed)
			{
				return 0;
			}
			float num = 0f;
			try
			{
				foreach (PhysGrabCart item in GameStateAccessors.GetCartsOnMap())
				{
					if ((Object)(object)item != (Object)null)
					{
						num += GetCartHaulCurrent(item);
					}
				}
			}
			catch
			{
			}
			return Mathf.RoundToInt(Mathf.Max(0f, num));
		}

		private static int ReadHaulValue()
		{
			if (BountySupportBridge.TryGetCurrentHaulValue(out var haulValue) && haulValue > 0)
			{
				return haulValue;
			}
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime - _lastSecondaryCheckTime >= 0.1f)
			{
				if (BountySupportBridge.TryGetFinalShopTargetMoney(out var total) && total > 0)
				{
					_cachedSecondaryHaul = total;
					_lastSecondaryCheckTime = unscaledTime;
					if (Math.Abs(total - _lastLoggedHaulValue) > 100)
					{
						DenisUIPlugin.DebugLog($"[ReadHaulValue] Bounty API returned: {total}");
						_lastLoggedHaulValue = total;
					}
					return total;
				}
			}
			else if (_cachedSecondaryHaul > 0)
			{
				return _cachedSecondaryHaul;
			}
			try
			{
				int num = SemiFunc.StatGetRunCurrency();
				if (num > 0)
				{
					return num;
				}
			}
			catch
			{
			}
			return RunCurrencyTracker.ReadBalanceMoney();
		}

		private static bool ReadExtractionClosed()
		{
			if (!GameStateAccessors.AreAllExtractionPointsCompleted())
			{
				return false;
			}
			int num = ReadHaulValue();
			return num > 0;
		}

		private static bool ReadHaulTransitionActive()
		{
			try
			{
				return GameStateAccessors.GetCurrentHaul() > 0f;
			}
			catch
			{
				return false;
			}
		}

		private static CosmeticBoxSnapshot ReadCosmeticBoxes()
		{
			try
			{
				return CosmeticBoxTracker.GetSnapshot();
			}
			catch
			{
				return new CosmeticBoxSnapshot(0, 0, 0, 0);
			}
		}

		private static float GetValuableCurrent(ValuableObject valuable)
		{
			try
			{
				Type type = ((object)valuable).GetType();
				FieldInfo field = type.GetField("dollarValueCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null && field.GetValue(valuable) is float result)
				{
					return result;
				}
				string[] array = new string[4] { "currentValue", "CurrentValue", "value", "_value" };
				foreach (string name in array)
				{
					field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null && field.GetValue(valuable) is float result2)
					{
						return result2;
					}
				}
				return 0f;
			}
			catch (Exception ex)
			{
				DenisUIPlugin.Logger.LogError((object)("[GetValuableCurrent] Exception: " + ex.Message));
				return 0f;
			}
		}

		private static float GetCartHaulCurrent(PhysGrabCart cart)
		{
			try
			{
				Type typeFromHandle = typeof(PhysGrabCart);
				FieldInfo field = typeFromHandle.GetField("haulCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					object value = field.GetValue(cart);
					if (value is int num)
					{
						return num;
					}
					if (value is float num2)
					{
						return Mathf.Max(0f, num2);
					}
				}
				return 0f;
			}
			catch (Exception ex)
			{
				DenisUIPlugin.Logger.LogError((object)("[GetCartHaulCurrent] Exception: " + ex.Message));
				return 0f;
			}
		}

		internal static void Reset()
		{
			_lastSnapshotBuiltAt = -100000f;
			_lastSnapshot = null;
			_cachedHaulForDisplay = 0;
			_extractionWasComplete = false;
			_cachedMapValue = 0;
			_cachedCartValue = 0;
			_cachedBoxes = new CosmeticBoxSnapshot(0, 0, 0, 0);
		}

		internal static void InvalidateSnapshot()
		{
			DenisUIPlugin.DebugLog("[DataSnapshotBuilder.InvalidateSnapshot] ⚡ SNAPSHOT INVALIDATED - will rebuild on next frame");
			_snapshotInvalidated = true;
		}
	}
	internal static class DenisUIEvents
	{
		internal static event Action<OverlaySyncSnapshot>? OnSnapshotUpdated;

		internal static event Action<int, int>? OnMapValueChanged;

		internal static event Action<int, int>? OnCartValueChanged;

		internal static event Action<int>? OnHaulCaptured;

		internal static event Action? OnExtractionClosing;

		internal static void RaiseSnapshotUpdated(OverlaySyncSnapshot snapshot)
		{
			try
			{
				DenisUIEvents.OnSnapshotUpdated?.Invoke(snapshot);
			}
			catch (Exception ex)
			{
				DenisUIPlugin.ErrorLog("[DenisUIEvents] Error in OnSnapshotUpdated subscriber: " + ex.Message);
			}
		}

		internal static void RaiseMapValueChanged(int oldValue, int newValue)
		{
			try
			{
				DenisUIEvents.OnMapValueChanged?.Invoke(oldValue, newValue);
			}
			catch (Exception ex)
			{
				DenisUIPlugin.ErrorLog("[DenisUIEvents] Error in OnMapValueChanged subscriber: " + ex.Message);
			}
		}

		internal static void RaiseCartValueChanged(int oldValue, int newValue)
		{
			try
			{
				DenisUIEvents.OnCartValueChanged?.Invoke(oldValue, newValue);
			}
			catch (Exception ex)
			{
				DenisUIPlugin.ErrorLog("[DenisUIEvents] Error in OnCartValueChanged subscriber: " + ex.Message);
			}
		}

		internal static void RaiseHaulCaptured(int haulValue)
		{
			try
			{
				DenisUIPlugin.DebugLog($"[DenisUIEvents] OnHaulCaptured fired: {haulValue}");
				DenisUIEvents.OnHaulCaptured?.Invoke(haulValue);
			}
			catch (Exception ex)
			{
				DenisUIPlugin.ErrorLog("[DenisUIEvents] Error in OnHaulCaptured subscriber: " + ex.Message);
			}
		}

		internal static void RaiseExtractionClosing()
		{
			try
			{
				DenisUIPlugin.DebugLog("[DenisUIEvents] OnExtractionClosing fired");
				DenisUIEvents.OnExtractionClosing?.Invoke();
			}
			catch (Exception ex)
			{
				DenisUIPlugin.ErrorLog("[DenisUIEvents] Error in OnExtractionClosing subscriber: " + ex.Message);
			}
		}
	}
	[BepInPlugin("denis.repo.denisui", "Denis UI", "1.3.0")]
	public class DenisUIPlugin : BaseUnityPlugin
	{
		internal enum OverlayVisibilityMode
		{
			Off,
			ShowWithMap,
			Always
		}

		private const string PluginGuid = "denis.repo.denisui";

		private const string PluginName = "Denis UI";

		private const string PluginVersion = "1.3.0";

		private static readonly Regex HexColorRegex = new Regex("^#[0-9A-Fa-f]{6}$", RegexOptions.Compiled);

		private static readonly bool VerboseDebugLogs = false;

		internal static DenisUIPlugin Instance { get; private set; } = null;


		internal static ManualLogSource Logger => Instance._logger;

		internal static ConfigEntry<OverlayVisibilityMode> OverlayVisibility { get; private set; } = null;


		internal static ConfigEntry<bool> HostSync { get; private set; } = null;


		internal static ConfigEntry<bool> ShowMapValue { get; private set; } = null;


		internal static ConfigEntry<bool> ShowCartValue { get; private set; } = null;


		internal static ConfigEntry<bool> ShowLootboxes { get; private set; } = null;


		internal static ConfigEntry<float> CosmeticBoxesScale { get; private set; } = null;


		internal static ConfigEntry<bool> YellowCartText { get; private set; } = null;


		internal static ConfigEntry<string> LabelColorHex { get; private set; } = null;


		internal static ConfigEntry<string> ValueColorHex { get; private set; } = null;


		internal static ConfigEntry<string> DollarColorHex { get; private set; } = null;


		internal static ConfigEntry<string> CartDollarColorHex { get; private set; } = null;


		internal static ConfigEntry<string> CartValueColorHex { get; private set; } = null;


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

		internal Harmony? Harmony { get; set; }

		private void Awake()
		{
			//IL_0040: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Expected O, but got Unknown
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Expected O, but got Unknown
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Expected O, but got Unknown
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Expected O, but got Unknown
			//IL_0129: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Expected O, but got Unknown
			//IL_0154: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Expected O, but got Unknown
			//IL_0183: Unknown result type (might be due to invalid IL or missing references)
			//IL_018d: Expected O, but got Unknown
			//IL_01b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bc: Expected O, but got Unknown
			//IL_01e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Expected O, but got Unknown
			//IL_0210: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Expected O, but got Unknown
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0249: Expected O, but got Unknown
			//IL_0267: Unknown result type (might be due to invalid IL or missing references)
			//IL_026c: Unknown result type (might be due to invalid IL or missing references)
			//IL_026e: Expected O, but got Unknown
			//IL_0273: Expected O, but got Unknown
			Instance = this;
			((Component)this).gameObject.transform.parent = null;
			((Object)((Component)this).gameObject).hideFlags = (HideFlags)61;
			OverlayVisibility = ((BaseUnityPlugin)this).Config.Bind<OverlayVisibilityMode>("General", "OverlayVisibility", OverlayVisibilityMode.Always, new ConfigDescription("Overlay visibility mode. Available values: Off, ShowWithMap, Always.", (AcceptableValueBase)null, Array.Empty<object>()));
			HostSync = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced", "Host sync", true, new ConfigDescription("When enabled, the host publishes overlay data and clients with this option enabled render the host snapshot instead of relying on their own local calculations.", (AcceptableValueBase)null, Array.Empty<object>()));
			ShowMapValue = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Map Value", true, new ConfigDescription("Show map value on the level in the overlay.", (AcceptableValueBase)null, Array.Empty<object>()));
			ShowCartValue = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "C.A.R.T. Value", true, new ConfigDescription("Show C.A.R.T. value in the overlay.", (AcceptableValueBase)null, Array.Empty<object>()));
			ShowLootboxes = ((BaseUnityPlugin)this).Config.Bind<bool>("General", "Cosmetic Boxes", true, new ConfigDescription("Show Cosmetic Boxes in the overlay.", (AcceptableValueBase)null, Array.Empty<object>()));
			CosmeticBoxesScale = ((BaseUnityPlugin)this).Config.Bind<float>("Advanced", "Icons size", 0.35f, new ConfigDescription("Scale the Cosmetic Boxes row size. 1.0 equals 26 px and is the maximum size.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.35f, 1f), Array.Empty<object>()));
			YellowCartText = ((BaseUnityPlugin)this).Config.Bind<bool>("Advanced", "Yellow C.A.R.T. text", true, new ConfigDescription("Use the C.A.R.T.-specific colors. If disabled, C.A.R.T. uses the same colors as Map Value.", (AcceptableValueBase)null, Array.Empty<object>()));
			LabelColorHex = ((BaseUnityPlugin)this).Config.Bind<string>("Advanced", "Label color", "#aaaaaa", new ConfigDescription("HEX color for labels like Map Value, C.A.R.T., and Haul. Format: #RRGGBB", (AcceptableValueBase)null, Array.Empty<object>()));
			ValueColorHex = ((BaseUnityPlugin)this).Config.Bind<string>("Advanced", "Value color", "#ffffff", new ConfigDescription("HEX color for numeric values in the overlay. Format: #RRGGBB", (AcceptableValueBase)null, Array.Empty<object>()));
			DollarColorHex = ((BaseUnityPlugin)this).Config.Bind<string>("Advanced", "Dollar color", "#66c94f", new ConfigDescription("HEX color for the $ symbol in money lines. Format: #RRGGBB", (AcceptableValueBase)null, Array.Empty<object>()));
			CartDollarColorHex = ((BaseUnityPlugin)this).Config.Bind<string>("Advanced", "C.A.R.T. dollar color", "#feb740", new ConfigDescription("HEX color for the $ symbol in the C.A.R.T. line. Format: #RRGGBB", (AcceptableValueBase)null, Array.Empty<object>()));
			CartValueColorHex = ((BaseUnityPlugin)this).Config.Bind<string>("Advanced", "C.A.R.T. value color", "#f4dd9c", new ConfigDescription("HEX color for the numeric value in the C.A.R.T. line. Format: #RRGGBB", (AcceptableValueBase)null, Array.Empty<object>()));
			if (Harmony == null)
			{
				Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID);
				Harmony val2 = val;
				Harmony = val;
			}
			Harmony.PatchAll();
			BountySupportBridge.SubscribeToBountyEvents();
		}

		internal static string GetValidatedHexColor(ConfigEntry<string> entry, string fallback)
		{
			string text = (entry.Value ?? string.Empty).Trim();
			if (!HexColorRegex.IsMatch(text))
			{
				return fallback;
			}
			return text;
		}

		internal static float GetCosmeticBoxesScale()
		{
			return Mathf.Clamp(CosmeticBoxesScale.Value, 0.35f, 1f);
		}

		internal static string GetLabelColorHex()
		{
			return GetValidatedHexColor(LabelColorHex, "#aaaaaa");
		}

		internal static string GetValueColorHex()
		{
			return GetValidatedHexColor(ValueColorHex, "#ffffff");
		}

		internal static string GetDollarColorHex()
		{
			return GetValidatedHexColor(DollarColorHex, "#66c94f");
		}

		internal static string GetCartDollarColorHex()
		{
			if (!YellowCartText.Value)
			{
				return GetDollarColorHex();
			}
			return GetValidatedHexColor(CartDollarColorHex, "#feb740");
		}

		internal static string GetCartValueColorHex()
		{
			if (!YellowCartText.Value)
			{
				return GetValueColorHex();
			}
			return GetValidatedHexColor(CartValueColorHex, "#f4dd9c");
		}

		internal static bool IsPerfDebugLoggingEnabled()
		{
			return false;
		}

		internal static float GetPerfDebugLogIntervalSeconds()
		{
			return 15f;
		}

		internal static void DebugLog(string message)
		{
			if (VerboseDebugLogs)
			{
				Logger.LogInfo((object)message);
			}
		}

		internal static void WarningLog(string message)
		{
			Logger.LogWarning((object)message);
		}

		internal static void ErrorLog(string message)
		{
			Logger.LogError((object)message);
		}

		private void OnDestroy()
		{
			Harmony? harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class GameStateAccessors
	{
		private static FieldInfo? _currentHaulField;

		private static FieldInfo? _allExtractionPointsCompletedField;

		private static FieldInfo? _itemsCartsField;

		private static bool _resolved;

		private static bool _warningLogged;

		internal static float GetCurrentHaul()
		{
			if (!Resolve())
			{
				return 0f;
			}
			if (_currentHaulField == null || (Object)(object)RoundDirector.instance == (Object)null)
			{
				return 0f;
			}
			try
			{
				return (_currentHaulField.GetValue(RoundDirector.instance) is float num) ? Mathf.Max(0f, num) : 0f;
			}
			catch
			{
				return 0f;
			}
		}

		internal static bool AreAllExtractionPointsCompleted()
		{
			if (!Resolve())
			{
				return false;
			}
			if (_allExtractionPointsCompletedField == null || (Object)(object)RoundDirector.instance == (Object)null)
			{
				return false;
			}
			try
			{
				object value = _allExtractionPointsCompletedField.GetValue(RoundDirector.instance);
				bool flag = default(bool);
				int num;
				if (value is bool)
				{
					flag = (bool)value;
					num = 1;
				}
				else
				{
					num = 0;
				}
				return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
			}
			catch
			{
				return false;
			}
		}

		internal static IEnumerable<ValuableObject> GetValuablesOnMap()
		{
			if (TryGetValuableDirectorList(out IEnumerable<ValuableObject> list))
			{
				return list;
			}
			try
			{
				return Object.FindObjectsOfType<ValuableObject>();
			}
			catch
			{
				return (IEnumerable<ValuableObject>)(object)new ValuableObject[0];
			}
		}

		internal static IEnumerable<PhysGrabCart> GetCartsOnMap()
		{
			if (Resolve() && _itemsCartsField != null && (Object)(object)RoundDirector.instance != (Object)null)
			{
				try
				{
					object value = _itemsCartsField.GetValue(RoundDirector.instance);
					if (value is IEnumerable<PhysGrabCart> result)
					{
						return result;
					}
					if (value is IEnumerable enumerable)
					{
						List<PhysGrabCart> list = new List<PhysGrabCart>();
						foreach (object item in enumerable)
						{
							PhysGrabCart val = (PhysGrabCart)((item is PhysGrabCart) ? item : null);
							if (val != null)
							{
								list.Add(val);
							}
						}
						return list;
					}
				}
				catch
				{
				}
			}
			try
			{
				return Object.FindObjectsOfType<PhysGrabCart>();
			}
			catch
			{
				return (IEnumerable<PhysGrabCart>)(object)new PhysGrabCart[0];
			}
		}

		private static bool TryGetValuableDirectorList(out IEnumerable<ValuableObject> list)
		{
			list = (IEnumerable<ValuableObject>)(object)new ValuableObject[0];
			try
			{
				object obj = typeof(ValuableDirector).GetProperty("instance", BindingFlags.Static | BindingFlags.Public)?.GetValue(null);
				if (obj == null)
				{
					return false;
				}
				FieldInfo field = typeof(ValuableDirector).GetField("valuableList", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field == null)
				{
					return false;
				}
				object value = field.GetValue(obj);
				if (value is List<ValuableObject> list2)
				{
					list = list2;
					return true;
				}
				return false;
			}
			catch
			{
				return false;
			}
		}

		private static bool Resolve()
		{
			if (_resolved)
			{
				return true;
			}
			_resolved = true;
			try
			{
				_currentHaulField = AccessTools.Field(typeof(RoundDirector), "currentHaul");
				_allExtractionPointsCompletedField = AccessTools.Field(typeof(RoundDirector), "allExtractionPointsCompleted");
				_itemsCartsField = AccessTools.Field(typeof(RoundDirector), "itemsCarts");
			}
			catch (Exception ex)
			{
				LogWarningOnce("GameStateAccessors resolve failed: " + ex.Message);
			}
			if (!(_currentHaulField != null))
			{
				return _allExtractionPointsCompletedField != null;
			}
			return true;
		}

		private static void LogWarningOnce(string message)
		{
			if (!_warningLogged)
			{
				_warningLogged = true;
				DenisUIPlugin.WarningLog(message);
			}
		}
	}
	internal readonly struct CartAnimationDisplay
	{
		public readonly string Text;

		public readonly bool IsAnimating;

		public readonly bool IsIncrease;

		public CartAnimationDisplay(string text, bool isAnimating, bool isIncrease)
		{
			Text = text;
			IsAnimating = isAnimating;
			IsIncrease = isIncrease;
		}
	}
	internal readonly struct MapAnimationDisplay
	{
		public readonly int Value;

		public readonly string ValueText;

		public readonly bool IsDeltaActive;

		public readonly int DeltaAmount;

		public readonly bool IsIncrease;

		public readonly bool UseWhiteDeltaColor;

		public readonly float DeltaAlpha;

		public readonly float DeltaOffsetX;

		public MapAnimationDisplay(int value, string valueText, bool isDeltaActive, int deltaAmount, bool isIncrease, bool useWhiteDeltaColor, float deltaAlpha, float deltaOffsetX)
		{
			Value = value;
			ValueText = valueText;
			IsDeltaActive = isDeltaActive;
			DeltaAmount = deltaAmount;
			IsIncrease = isIncrease;
			UseWhiteDeltaColor = useWhiteDeltaColor;
			DeltaAlpha = deltaAlpha;
			DeltaOffsetX = deltaOffsetX;
		}
	}
	internal sealed class MapValueAnimationController
	{
		private const float IntroBaselineValue = 30000f;

		private const float IntroMaxScale = 2.5f;

		private readonly float _introSeconds;

		private readonly float _deltaShowSeconds;

		private readonly float _decreaseCountDownSeconds;

		private readonly float _deltaWhiteSeconds;

		private bool _introStarted;

		private bool _introCompleted;

		private bool _introArmed;

		private float _introStartedAt = -100000f;

		private int _introTarget;

		private int _lastObservedValue = -1;

		private int _changeFromValue = -1;

		private int _changeToValue = -1;

		private int _deltaAnchorValue = -1;

		private int _deltaCurrentTargetValue = -1;

		private float _deltaStartedAt = -100000f;

		private float _deltaShowUntil = -100000f;

		private float _deltaWhiteUntil = -100000f;

		private float _decreaseCountDownStartedAt = -100000f;

		internal MapValueAnimationController(float introSeconds, float deltaShowSeconds, float decreaseCountDownSeconds, float deltaWhiteSeconds)
		{
			_introSeconds = introSeconds;
			_deltaShowSeconds = deltaShowSeconds;
			_decreaseCountDownSeconds = decreaseCountDownSeconds;
			_deltaWhiteSeconds = deltaWhiteSeconds;
		}

		internal void Reset()
		{
			_introStarted = false;
			_introCompleted = false;
			_introArmed = false;
			_introStartedAt = -100000f;
			_introTarget = 0;
			_lastObservedValue = -1;
			_changeFromValue = -1;
			_changeToValue = -1;
			_deltaAnchorValue = -1;
			_deltaCurrentTargetValue = -1;
			_deltaStartedAt = -100000f;
			_deltaShowUntil = -100000f;
			_deltaWhiteUntil = -100000f;
			_decreaseCountDownStartedAt = -100000f;
		}

		internal void ArmIntro()
		{
			_introArmed = true;
		}

		internal void TriggerIntro()
		{
			_introStarted = false;
			_introCompleted = false;
			_introArmed = true;
			_introStartedAt = -100000f;
			_introTarget = 0;
		}

		internal MapAnimationDisplay GetDisplay(int target, float now, Func<int, string> formatter)
		{
			target = Mathf.Max(0, target);
			ObserveChange(target, now);
			if (target <= 0)
			{
				return new MapAnimationDisplay(0, formatter(0), isDeltaActive: false, 0, isIncrease: false, useWhiteDeltaColor: false, 1f, 0f);
			}
			int animatedValue = GetAnimatedValue(target, now);
			int num = animatedValue;
			if (!_introCompleted && _introArmed)
			{
				if (!_introStarted)
				{
					_introStarted = true;
					_introStartedAt = now;
					_introTarget = target;
				}
				else if (target > _introTarget)
				{
					_introTarget = target;
				}
				float scaledIntroDurationSeconds = GetScaledIntroDurationSeconds();
				float num2 = Mathf.Clamp01((now - _introStartedAt) / scaledIntroDurationSeconds);
				float num3 = EaseOutIntroValue(num2);
				int num4 = Mathf.RoundToInt(Mathf.Lerp(0f, (float)_introTarget, num3));
				if (num2 >= 1f)
				{
					_introCompleted = true;
					_introArmed = false;
					num4 = target;
				}
				num = Mathf.Min(num4, animatedValue);
			}
			bool flag = IsDeltaActive(now);
			int deltaAmount = (flag ? Mathf.Abs(_deltaCurrentTargetValue - _deltaAnchorValue) : 0);
			bool isIncrease = _deltaCurrentTargetValue > _deltaAnchorValue;
			bool useWhiteDeltaColor = flag && now < _deltaWhiteUntil;
			float deltaAlpha = (flag ? GetDeltaAlpha(now) : 1f);
			float deltaOffsetX = (flag ? GetDeltaOffsetX(now) : 0f);
			bool flag2 = IsValueAnimationActive(now);
			string text = formatter(num);
			if (flag2)
			{
				float num5 = now - _decreaseCountDownStartedAt;
				int frame = Mathf.Max(0, Mathf.FloorToInt(num5 / 0.05f));
				text = ScrambleDisplay(text, frame, allowDollar: false);
			}
			return new MapAnimationDisplay(num, text, flag, deltaAmount, isIncrease, useWhiteDeltaColor, deltaAlpha, deltaOffsetX);
		}

		private float GetScaledIntroDurationSeconds()
		{
			float num = ((_introSeconds <= 0f) ? 1f : _introSeconds);
			if ((float)_introTarget <= 30000f)
			{
				return num;
			}
			float num2 = Mathf.Max(1f, (float)_introTarget / 30000f);
			float num3 = 1f + (Mathf.Sqrt(num2) - 1f) * 1.35f;
			float num4 = Mathf.Clamp(num3, 1f, 2.5f);
			return num * num4;
		}

		private static AnimationDirection GetDirection(int previousValue, int nextValue)
		{
			if (nextValue > previousValue)
			{
				return AnimationDirection.Up;
			}
			if (nextValue < previousValue)
			{
				return AnimationDirection.Down;
			}
			return AnimationDirection.None;
		}

		private float EaseOutIntroValue(float t)
		{
			t = Mathf.Clamp01(t);
			if (t <= 0.46f)
			{
				float num = t / 0.46f;
				float num2 = 1f - Mathf.Pow(1f - num, 2.6f);
				return num2 * 0.66f;
			}
			if (t <= 0.82f)
			{
				float num3 = (t - 0.46f) / 0.35999998f;
				float num4 = 1f - Mathf.Pow(1f - num3, 3.4f);
				return Mathf.Lerp(0.66f, 0.91f, num4);
			}
			float num5 = (t - 0.82f) / 0.18f;
			float num6 = 1f - Mathf.Pow(1f - num5, 5.6f);
			return Mathf.Lerp(0.91f, 1f, num6);
		}

		private void ObserveChange(int target, float now)
		{
			if (target <= 0)
			{
				return;
			}
			if (_lastObservedValue >= 0 && target != _lastObservedValue)
			{
				int lastObservedValue = _lastObservedValue;
				if (target < lastObservedValue && ShouldSuppressExtractionLossDelta(lastObservedValue, target))
				{
					_changeFromValue = lastObservedValue;
					_changeToValue = target;
					_deltaAnchorValue = target;
					_deltaCurrentTargetValue = target;
					_deltaStartedAt = now;
					_deltaShowUntil = now;
					_deltaWhiteUntil = now;
					_decreaseCountDownStartedAt = now;
					_lastObservedValue = target;
					return;
				}
				bool flag = IsDeltaActive(now);
				_changeFromValue = lastObservedValue;
				_changeToValue = target;
				AnimationIntentRecorder.Record(new AnimationIntent(AnimationIntentKind.MapValueChanged, lastObservedValue, target, GetDirection(lastObservedValue, target), (target < lastObservedValue) ? "map_loss_delta" : "map_gain_count", (target < lastObservedValue) ? "destroyed" : "revealed"));
				if (!flag)
				{
					_deltaAnchorValue = _lastObservedValue;
					_deltaStartedAt = now;
				}
				else if (_deltaAnchorValue < 0)
				{
					_deltaAnchorValue = _lastObservedValue;
					_deltaStartedAt = now;
				}
				_deltaCurrentTargetValue = target;
				_deltaShowUntil = now + _deltaShowSeconds;
				_deltaWhiteUntil = now + _deltaWhiteSeconds;
				_decreaseCountDownStartedAt = now;
			}
			_lastObservedValue = target;
		}

		private static bool ShouldSuppressExtractionLossDelta(int previousValue, int nextValue)
		{
			int num = previousValue - nextValue;
			if (num < 1000)
			{
				return false;
			}
			return GameStateAccessors.GetCurrentHaul() > 0f;
		}

		private bool IsDeltaActive(float now)
		{
			if (_deltaAnchorValue >= 0 && _deltaCurrentTargetValue >= 0 && _deltaAnchorValue != _deltaCurrentTargetValue)
			{
				return now < _deltaShowUntil;
			}
			return false;
		}

		private float GetDeltaAlpha(float now)
		{
			float num = ((_deltaStartedAt <= -99999f) ? _deltaShowSeconds : (now - _deltaStartedAt));
			float num2 = Mathf.Min(0.1f, _deltaShowSeconds * 0.2f);
			float num3 = Mathf.Min(0.18f, _deltaShowSeconds * 0.28f);
			float num4 = ((num2 <= 0.01f) ? 1f : Mathf.Clamp01(num / num2));
			float num5 = _deltaShowUntil - num3;
			float num6 = ((now < num5) ? 1f : Mathf.Clamp01((_deltaShowUntil - now) / Mathf.Max(0.01f, num3)));
			return Mathf.Clamp01(Mathf.Min(num4, num6));
		}

		private float GetDeltaOffsetX(float now)
		{
			float num = ((_deltaStartedAt <= -99999f) ? _deltaShowSeconds : (now - _deltaStartedAt));
			float num2 = Mathf.Min(0.14f, _deltaShowSeconds * 0.24f);
			float num3 = Mathf.Min(0.16f, _deltaShowSeconds * 0.24f);
			if (num < num2)
			{
				float num4 = Mathf.Clamp01(num / Mathf.Max(0.01f, num2));
				float num5 = 1f - Mathf.Pow(1f - num4, 3f);
				return Mathf.Lerp(8f, 0f, num5);
			}
			float num6 = _deltaShowUntil - num3;
			if (now > num6)
			{
				float num7 = Mathf.Clamp01((now - num6) / Mathf.Max(0.01f, num3));
				float num8 = num7 * num7 * (3f - 2f * num7);
				return Mathf.Lerp(0f, -6f, num8);
			}
			return 0f;
		}

		private bool IsValueAnimationActive(float now)
		{
			if (_changeFromValue < 0 || _changeToValue < 0)
			{
				return false;
			}
			if (_changeFromValue == _changeToValue)
			{
				return false;
			}
			float num = ((_decreaseCountDownSeconds <= 0f) ? 0.01f : _decreaseCountDownSeconds);
			return now - _decreaseCountDownStartedAt < num;
		}

		private int GetAnimatedValue(int target, float now)
		{
			if (_changeFromValue < 0 || _changeToValue < 0)
			{
				return target;
			}
			if (_changeFromValue == _changeToValue)
			{
				return target;
			}
			float num = ((_decreaseCountDownSeconds <= 0f) ? 0.01f : _decreaseCountDownSeconds);
			float num2 = now - _decreaseCountDownStartedAt;
			if (num2 >= num)
			{
				return target;
			}
			float num3 = Mathf.Clamp01(num2 / num);
			float num4 = 1f - Mathf.Pow(1f - num3, 3f);
			float num5 = Mathf.Lerp((float)_changeFromValue, (float)_changeToValue, num4);
			if (_changeToValue < _changeFromValue)
			{
				return Mathf.Max(target, Mathf.RoundToInt(num5));
			}
			return Mathf.Min(target, Mathf.RoundToInt(num5));
		}

		private static string ScrambleDisplay(string numericText, int frame, bool allowDollar)
		{
			if (string.IsNullOrEmpty(numericText))
			{
				return numericText;
			}
			char[] array = numericText.ToCharArray();
			for (int i = 0; i < array.Length; i++)
			{
				char c = array[i];
				if (char.IsDigit(c))
				{
					int num = (i + frame) % 5;
					if (allowDollar && num == 0)
					{
						array[i] = '$';
					}
					else if (num == 1 || num == 2)
					{
						array[i] = "TAX$"[(i + frame) % 3];
					}
				}
				else if (allowDollar && c == ',' && frame % 4 == 2)
				{
					array[i] = '$';
				}
			}
			string text = new string(array);
			if (frame % 3 == 1 && text.Length >= 3)
			{
				int num2 = Mathf.Clamp((frame + text.Length) % text.Length, 0, text.Length - 1);
				string text2 = "TAX$".Substring(0, Mathf.Min(3, text.Length));
				char[] array2 = text.ToCharArray();
				for (int j = 0; j < text2.Length && num2 + j < array2.Length; j++)
				{
					array2[num2 + j] = text2[j];
				}
				text = new string(array2);
			}
			return text;
		}
	}
	internal sealed class CartValueAnimationController
	{
		private readonly float _scrambleSeconds;

		private readonly float _scrambleFrameSeconds;

		private int _lastObservedValue = -1;

		private int _animationFromValue = -1;

		private int _animationToValue = -1;

		private float _animationStartedAt = -100000f;

		internal CartValueAnimationController(float scrambleSeconds, float scrambleFrameSeconds)
		{
			_scrambleSeconds = scrambleSeconds;
			_scrambleFrameSeconds = scrambleFrameSeconds;
		}

		internal void Reset()
		{
			_lastObservedValue = -1;
			_animationFromValue = -1;
			_animationToValue = -1;
			_animationStartedAt = -100000f;
		}

		internal CartAnimationDisplay GetDisplay(int target, float now, Func<int, string> formatter)
		{
			target = Mathf.Max(0, target);
			ObserveChange(target, now);
			if (!IsAnimationActive(now))
			{
				return new CartAnimationDisplay(formatter(target), isAnimating: false, isIncrease: false);
			}
			float num = ((_scrambleSeconds <= 0f) ? 0.01f : _scrambleSeconds);
			float num2 = now - _animationStartedAt;
			float num3 = Mathf.Clamp01(num2 / num);
			float num4 = 1f - Mathf.Pow(1f - num3, 3f);
			int num5 = Mathf.RoundToInt(Mathf.Lerp((float)_animationFromValue, (float)_animationToValue, num4));
			num5 = Mathf.Clamp(num5, Mathf.Min(_animationFromValue, _animationToValue), Mathf.Max(_animationFromValue, _animationToValue));
			string numericText = formatter(num5);
			int frame = Mathf.Max(0, Mathf.FloorToInt(num2 / Mathf.Max(0.01f, _scrambleFrameSeconds)));
			return new CartAnimationDisplay(ScrambleDisplay(numericText, frame), isAnimating: true, _animationToValue > _animationFromValue);
		}

		private static AnimationDirection GetDirection(int previousValue, int nextValue)
		{
			if (nextValue > previousValue)
			{
				return AnimationDirection.Up;
			}
			if (nextValue < previousValue)
			{
				return AnimationDirection.Down;
			}
			return AnimationDirection.None;
		}

		private void ObserveChange(int target, float now)
		{
			if (_lastObservedValue >= 0 && target != _lastObservedValue)
			{
				_animationFromValue = _lastObservedValue;
				_animationToValue = target;
				_animationStartedAt = now;
				AnimationIntentRecorder.Record(new AnimationIntent(AnimationIntentKind.CartValueChanged, _animationFromValue, _animationToValue, GetDirection(_animationFromValue, _animationToValue), (_animationToValue > _animationFromValue) ? "cart_scramble_gain" : "cart_scramble_loss"));
			}
			_lastObservedValue = target;
		}

		private bool IsAnimationActive(float now)
		{
			if (_animationStartedAt < 0f)
			{
				return false;
			}
			if (_animationFromValue == _animationToValue)
			{
				return false;
			}
			return now - _animationStartedAt < _scrambleSeconds;
		}

		private static string ScrambleDisplay(string numericText, int frame)
		{
			return ScrambleDisplay(numericText, frame, allowDollar: true);
		}

		private static string ScrambleDisplay(string numericText, int frame, bool allowDollar)
		{
			if (string.IsNullOrEmpty(numericText))
			{
				return numericText;
			}
			char[] array = numericText.ToCharArray();
			string text = "TAX$";
			for (int i = 0; i < array.Length; i++)
			{
				char c = array[i];
				if (char.IsDigit(c))
				{
					int num = (i + frame) % 5;
					if (allowDollar && num == 0)
					{
						array[i] = '$';
					}
					else if (num == 1 || num == 2)
					{
						array[i] = text[(i + frame) % 3];
					}
				}
				else if (allowDollar && c == ',' && frame % 4 == 2)
				{
					array[i] = '$';
				}
			}
			string text2 = new string(array);
			if (frame % 3 == 1 && text2.Length >= 3)
			{
				int num2 = Mathf.Clamp((frame + text2.Length) % text2.Length, 0, text2.Length - 1);
				string text3 = text.Substring(0, Mathf.Min(3, text2.Length));
				char[] array2 = text2.ToCharArray();
				for (int j = 0; j < text3.Length && num2 + j < array2.Length; j++)
				{
					array2[num2 + j] = text3[j];
				}
				text2 = new string(array2);
			}
			return text2;
		}
	}
	internal enum OverlayLineMode
	{
		None,
		Money,
		AlertMoney,
		CompositeMoney,
		RawText
	}
	internal readonly struct OverlayLinePresentation
	{
		public readonly OverlayLineMode Mode;

		public readonly OverlayRevealTrack Track;

		public readonly string Label;

		public readonly string ValueText;

		public readonly string PrefixRichText;

		public readonly bool ForceWhiteMoney;

		public readonly string? DollarColorOverride;

		public readonly string? ValueColorOverride;

		public readonly string? AlertColorHex;

		public bool HasContent
		{
			get
			{
				if (Mode != 0)
				{
					return !string.IsNullOrEmpty(ValueText);
				}
				return false;
			}
		}

		public OverlayLinePresentation(OverlayLineMode mode, OverlayRevealTrack track, string label, string valueText, string prefixRichText = "", bool forceWhiteMoney = false, string? dollarColorOverride = null, string? valueColorOverride = null, string? alertColorHex = null)
		{
			Mode = mode;
			Track = track;
			Label = label;
			ValueText = valueText;
			PrefixRichText = prefixRichText;
			ForceWhiteMoney = forceWhiteMoney;
			DollarColorOverride = dollarColorOverride;
			ValueColorOverride = valueColorOverride;
			AlertColorHex = alertColorHex;
		}
	}
	internal static class OverlayPresentationPolicy
	{
		private const string LossColorHex = "#EF3338";

		private const string GainColorHex = "#66c94f";

		internal static OverlayLinePresentation BuildHaulPresentation(OverlaySyncSnapshot snapshot, ConsumedAnimationIntentState intentState, float now)
		{
			if (!snapshot.ExtractionClosed || snapshot.HaulValue <= 0)
			{
				return default(OverlayLinePresentation);
			}
			if (intentState.IsActive && intentState.Intent.Kind == AnimationIntentKind.HaulChanged && intentState.Intent.Direction == AnimationDirection.Up && intentState.Intent.NextValue > intentState.Intent.PreviousValue)
			{
				int num = Mathf.Max(0, intentState.Intent.PreviousValue);
				int num2 = Mathf.Max(num, intentState.Intent.NextValue);
				int num3 = Mathf.Max(0, num2 - num);
				float num4 = Mathf.Max(0f, now - intentState.RecordedAt);
				float num5 = Mathf.Clamp01(num4 / 0.34f);
				float num6 = 1f - Mathf.Pow(1f - num5, 3f);
				int num7 = Mathf.RoundToInt(Mathf.Lerp((float)num, (float)num2, num6));
				num7 = Mathf.Clamp(num7, num, num2);
				float floatingDeltaAlpha = GetFloatingDeltaAlpha(intentState.RecordedAt, now, 0.75f);
				float floatingDeltaOffsetX = GetFloatingDeltaOffsetX(intentState.RecordedAt, now, 0.75f);
				string text = ToAlphaHex(floatingDeltaAlpha);
				string text2 = "#66c94f" + text;
				string text3 = ((floatingDeltaOffsetX >= 0f) ? new string(' ', Mathf.Clamp(Mathf.RoundToInt(floatingDeltaOffsetX / 2f), 0, 4)) : string.Empty);
				string prefixRichText = text3 + "<color=" + text2 + ">+$</color><color=" + text2 + ">" + FormatCompactWholeMoney(num3) + "</color>";
				return new OverlayLinePresentation(OverlayLineMode.CompositeMoney, OverlayRevealTrack.Primary, "Haul", FormatCompactWholeMoney(num7), prefixRichText, forceWhiteMoney: true);
			}
			return new OverlayLinePresentation(OverlayLineMode.Money, OverlayRevealTrack.Primary, "Haul", FormatCompactWholeMoney(snapshot.HaulValue), "", forceWhiteMoney: true);
		}

		internal static OverlayLinePresentation BuildMapPresentation(MapAnimationDisplay mapDisplay)
		{
			if (mapDisplay.IsDeltaActive && !mapDisplay.IsIncrease)
			{
				string text = (mapDisplay.UseWhiteDeltaColor ? "#ffffff" : "#EF3338");
				string text2 = ToAlphaHex(mapDisplay.DeltaAlpha);
				string text3 = text + text2;
				string text4 = "<color=" + text3 + ">-$</color><color=" + text3 + ">" + FormatMoney(mapDisplay.DeltaAmount, compact: false) + "</color>";
				string text5 = ((mapDisplay.DeltaOffsetX >= 0f) ? new string(' ', Mathf.Clamp(Mathf.RoundToInt(mapDisplay.DeltaOffsetX / 2f), 0, 4)) : string.Empty);
				string prefixRichText = text5 + text4;
				return new OverlayLinePresentation(OverlayLineMode.CompositeMoney, OverlayRevealTrack.Primary, "Map Value", mapDisplay.ValueText, prefixRichText);
			}
			return new OverlayLinePresentation(OverlayLineMode.Money, OverlayRevealTrack.Primary, "Map Value", mapDisplay.ValueText);
		}

		internal static OverlayLinePresentation BuildCartPresentation(CartAnimationDisplay cartDisplay)
		{
			if (cartDisplay.IsAnimating)
			{
				return new OverlayLinePresentation(OverlayLineMode.AlertMoney, OverlayRevealTrack.Cart, "C.A.R.T.", cartDisplay.Text, "", forceWhiteMoney: false, null, null, cartDisplay.IsIncrease ? "#ffffff" : "#EF3338");
			}
			return new OverlayLinePresentation(OverlayLineMode.Money, OverlayRevealTrack.Cart, "C.A.R.T.", cartDisplay.Text, "", forceWhiteMoney: false, DenisUIPlugin.GetCartDollarColorHex(), DenisUIPlugin.GetCartValueColorHex());
		}

		internal static OverlayLinePresentation BuildBoxesPresentation(CosmeticBoxSnapshot snapshot, ConsumedAnimationIntentState intentState)
		{
			string text = CosmeticBoxTracker.BuildLine(snapshot, intentState);
			if (string.IsNullOrEmpty(text))
			{
				return default(OverlayLinePresentation);
			}
			return new OverlayLinePresentation(OverlayLineMode.RawText, OverlayRevealTrack.Tertiary, string.Empty, text);
		}

		private static string FormatMoney(float value, bool compact)
		{
			int num = Mathf.RoundToInt(value);
			if (!compact)
			{
				return num.ToString("N0");
			}
			if (num >= 1000000)
			{
				return $"{(float)num / 1000000f:0.#}M";
			}
			if (num >= 1000)
			{
				return $"{(float)num / 1000f:0.#}K";
			}
			return num.ToString("N0");
		}

		private static string FormatCompactWholeMoney(float value)
		{
			int num = Mathf.RoundToInt(value);
			if (num >= 1000000)
			{
				return $"{Mathf.RoundToInt((float)num / 1000000f)}M";
			}
			if (num >= 1000)
			{
				return $"{Mathf.RoundToInt((float)num / 1000f)}K";
			}
			return num.ToString("N0");
		}

		private static string ToAlphaHex(float alpha)
		{
			return Mathf.Clamp(Mathf.RoundToInt(alpha * 255f), 0, 255).ToString("X2");
		}

		private static float GetFloatingDeltaAlpha(float startedAt, float now, float showSeconds)
		{
			float num = now - startedAt;
			float num2 = Mathf.Min(0.1f, showSeconds * 0.2f);
			float num3 = Mathf.Min(0.18f, showSeconds * 0.28f);
			float num4 = ((num2 <= 0.01f) ? 1f : Mathf.Clamp01(num / num2));
			float num5 = startedAt + showSeconds;
			float num6 = num5 - num3;
			float num7 = ((now < num6) ? 1f : Mathf.Clamp01((num5 - now) / Mathf.Max(0.01f, num3)));
			return Mathf.Clamp01(Mathf.Min(num4, num7));
		}

		private static float GetFloatingDeltaOffsetX(float startedAt, float now, float showSeconds)
		{
			float num = now - startedAt;
			float num2 = Mathf.Min(0.14f, showSeconds * 0.24f);
			float num3 = Mathf.Min(0.16f, showSeconds * 0.24f);
			if (num < num2)
			{
				float num4 = Mathf.Clamp01(num / Mathf.Max(0.01f, num2));
				float num5 = 1f - Mathf.Pow(1f - num4, 3f);
				return Mathf.Lerp(8f, 0f, num5);
			}
			float num6 = startedAt + showSeconds;
			float num7 = num6 - num3;
			if (now > num7)
			{
				float num8 = Mathf.Clamp01((now - num7) / Mathf.Max(0.01f, num3));
				float num9 = num8 * num8 * (3f - 2f * num8);
				return Mathf.Lerp(0f, -6f, num9);
			}
			return 0f;
		}
	}
	internal enum OverlayRevealTrack
	{
		Primary,
		Cart,
		Tertiary
	}
	internal sealed class OverlayRevealController
	{
		private const float MapRevealSeconds = 0.44f;

		private const float DefaultRevealSeconds = 0.34f;

		private const float DefaultOffsetY = 10f;

		private const float CartDelaySeconds = 0.22f;

		private const float TertiaryDelaySeconds = 0.4f;

		private float _startedAt = -100000f;

		internal void Reset()
		{
			_startedAt = -100000f;
		}

		internal void Begin()
		{
			_startedAt = Time.unscaledTime;
		}

		internal string Apply(string content, OverlayRevealTrack track)
		{
			if (string.IsNullOrEmpty(content))
			{
				return content;
			}
			if (_startedAt < 0f)
			{
				return content;
			}
			float num = track switch
			{
				OverlayRevealTrack.Cart => 0.22f, 
				OverlayRevealTrack.Tertiary => 0.4f, 
				_ => 0f, 
			};
			float num2 = ((track == OverlayRevealTrack.Primary || track == OverlayRevealTrack.Cart) ? 0.44f : 0.34f);
			float num3 = Time.unscaledTime - _startedAt - num;
			if (num3 <= 0f)
			{
				return string.Empty;
			}
			if (num3 >= num2)
			{
				return content;
			}
			float num4 = Mathf.Clamp01(num3 / Mathf.Max(0.01f, num2));
			float num5 = 1f - Mathf.Pow(1f - num4, 2.2f);
			string arg = ToAlphaHex(num4);
			float num6 = Mathf.Lerp(10f, 0f, num5);
			return $"<alpha=#{arg}><voffset={num6:0.##}px>{content}</voffset><alpha=#FF>";
		}

		private static string ToAlphaHex(float alpha)
		{
			return Mathf.Clamp(Mathf.RoundToInt(alpha * 255f), 0, 255).ToString("X2");
		}
	}
	[Serializable]
	internal sealed class OverlaySyncSnapshot
	{
		public bool Active;

		public bool ExtractionClosed;

		public int MapValue;

		public int CartValue;

		public int HaulValue;

		public bool HaulTransitionActive;

		public int BoxCommon;

		public int BoxUncommon;

		public int BoxRare;

		public int BoxUltraRare;

		public AnimationIntentSyncPayload MapIntent = new AnimationIntentSyncPayload();

		public AnimationIntentSyncPayload CartIntent = new AnimationIntentSyncPayload();

		public AnimationIntentSyncPayload BoxesIntent = new AnimationIntentSyncPayload();

		public AnimationIntentSyncPayload HaulIntent = new AnimationIntentSyncPayload();

		public int Revision;
	}
	internal static class OverlaySyncBridge
	{
		private const string RoomPropertyKey = "denis.ui.overlay.snapshot";

		private const float KeepAlivePublishSeconds = 2.5f;

		private const float LocalFallbackDelaySeconds = 2.5f;

		private static float _lastPublishAt = -100000f;

		private static int _nextRevision = 1;

		private static string _lastPublishedPayloadSignature = string.Empty;

		private static string _lastReceivedPayloadSignature = string.Empty;

		private static float _remoteWaitStartedAt = -100000f;

		private static bool _fallbackActive;

		private static int _lastOpenMapValue;

		private static int _lastOpenCartValue;

		private static int _lastPublishedHaulValue;

		private static OverlaySyncSnapshot? _lastLocalSnapshot;

		private static bool _hasLocalSnapshot;

		internal static bool IsPassiveModeEnabled()
		{
			if (DenisUIPlugin.HostSync != null)
			{
				return DenisUIPlugin.HostSync.Value;
			}
			return false;
		}

		internal static bool ShouldUseRemoteSnapshot()
		{
			if (!IsPassiveModeEnabled())
			{
				return false;
			}
			if (!PhotonNetwork.InRoom)
			{
				return false;
			}
			return !PhotonNetwork.IsMasterClient;
		}

		internal static bool ShouldFallbackToLocal()
		{
			if (!ShouldUseRemoteSnapshot())
			{
				return false;
			}
			if (_remoteWaitStartedAt < 0f)
			{
				_remoteWaitStartedAt = Time.unscaledTime;
			}
			if (_fallbackActive)
			{
				return true;
			}
			float num = Time.unscaledTime - _remoteWaitStartedAt;
			if (num < 2.5f)
			{
				return false;
			}
			_fallbackActive = true;
			return true;
		}

		internal static void TickHostPublish(bool mapOpen, bool overlayVisible)
		{
			if (IsPassiveModeEnabled() && PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient)
			{
				float unscaledTime = Time.unscaledTime;
				bool forceRepublish = unscaledTime - _lastPublishAt >= 2.5f;
				OverlaySyncSnapshot snapshot = (_lastLocalSnapshot = CaptureLocalSnapshot(mapOpen, overlayVisible));
				_hasLocalSnapshot = true;
				PublishSnapshot(snapshot, forceRepublish);
			}
		}

		internal static void UpdateLocalSnapshotCache(bool mapOpen, bool overlayVisible)
		{
			if ((!IsPassiveModeEnabled() || !PhotonNetwork.InRoom || !PhotonNetwork.IsMasterClient) && (!ShouldUseRemoteSnapshot() || ShouldFallbackToLocal()))
			{
				_lastLocalSnapshot = CaptureLocalSnapshot(mapOpen, overlayVisible);
				_hasLocalSnapshot = true;
			}
		}

		internal static bool TryGetCachedLocalSnapshot(out OverlaySyncSnapshot snapshot)
		{
			if (_hasLocalSnapshot && _lastLocalSnapshot != null)
			{
				snapshot = _lastLocalSnapshot;
				return true;
			}
			snapshot = new OverlaySyncSnapshot();
			return false;
		}

		internal static OverlaySyncSnapshot CaptureLocalSnapshot(bool mapOpen, bool overlayVisible = false)
		{
			AnimationIntentConsumer.Update(Time.unscaledTime);
			OverlaySyncSnapshot overlaySyncSnapshot = DataSnapshotBuilder.BuildSnapshot(mapOpen, overlayVisible);
			if (!overlaySyncSnapshot.ExtractionClosed)
			{
				_lastOpenMapValue = overlaySyncSnapshot.MapValue;
				_lastOpenCartValue = overlaySyncSnapshot.CartValue;
			}
			AnimationIntentSyncPayload haulIntent = AnimationIntentConsumer.CaptureHaulPayload();
			if (overlaySyncSnapshot.ExtractionClosed && overlaySyncSnapshot.HaulValue > 0 && overlaySyncSnapshot.HaulValue != _lastPublishedHaulValue)
			{
				int num = Mathf.Max(0, _lastPublishedHaulValue);
				int haulValue = overlaySyncSnapshot.HaulValue;
				haulIntent = AnimationIntentSyncPayload.FromIntent(new AnimationIntent(AnimationIntentKind.HaulChanged, num, haulValue, (haulValue >= num) ? AnimationDirection.Up : AnimationDirection.Down, (haulValue > num) ? "settle_gain_sync" : "settle_sync", "host-sync-haul"));
			}
			overlaySyncSnapshot.HaulIntent = haulIntent;
			if (overlaySyncSnapshot.ExtractionClosed && overlaySyncSnapshot.HaulValue > 0)
			{
				_lastPublishedHaulValue = overlaySyncSnapshot.HaulValue;
			}
			return overlaySyncSnapshot;
		}

		internal static bool TryGetRemoteSnapshot(out OverlaySyncSnapshot snapshot)
		{
			snapshot = new OverlaySyncSnapshot();
			if (!ShouldUseRemoteSnapshot())
			{
				return false;
			}
			try
			{
				Room currentRoom = PhotonNetwork.CurrentRoom;
				if (((currentRoom != null) ? ((RoomInfo)currentRoom).CustomProperties : null) == null)
				{
					return false;
				}
				if (!((Dictionary<object, object>)(object)((RoomInfo)currentRoom).CustomProperties).TryGetValue((object)"denis.ui.overlay.snapshot", out object value) || !(value is string text) || string.IsNullOrWhiteSpace(text))
				{
					return false;
				}
				OverlaySyncSnapshot overlaySyncSnapshot = JsonUtility.FromJson<OverlaySyncSnapshot>(text);
				if (overlaySyncSnapshot == null)
				{
					return false;
				}
				if (!overlaySyncSnapshot.Active)
				{
					return false;
				}
				_remoteWaitStartedAt = -100000f;
				_fallbackActive = false;
				snapshot = overlaySyncSnapshot;
				string payloadSignature = GetPayloadSignature(snapshot);
				_lastReceivedPayloadSignature = payloadSignature;
				return true;
			}
			catch
			{
				return false;
			}
		}

		internal static void ClearPublishedSnapshot()
		{
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			_lastPublishedPayloadSignature = string.Empty;
			_lastReceivedPayloadSignature = string.Empty;
			_lastPublishAt = -100000f;
			_remoteWaitStartedAt = -100000f;
			_fallbackActive = false;
			_lastOpenMapValue = 0;
			_lastOpenCartValue = 0;
			_lastPublishedHaulValue = 0;
			_lastLocalSnapshot = null;
			_hasLocalSnapshot = false;
			if (!PhotonNetwork.InRoom || !PhotonNetwork.IsMasterClient)
			{
				return;
			}
			try
			{
				OverlaySyncSnapshot overlaySyncSnapshot = new OverlaySyncSnapshot
				{
					Active = false,
					Revision = _nextRevision++
				};
				string text = JsonUtility.ToJson((object)overlaySyncSnapshot);
				Hashtable val = new Hashtable { [(object)"denis.ui.overlay.snapshot"] = text };
				Room currentRoom = PhotonNetwork.CurrentRoom;
				if (currentRoom != null)
				{
					currentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
				}
			}
			catch
			{
			}
		}

		private static void PublishSnapshot(OverlaySyncSnapshot snapshot, bool forceRepublish = false)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Expected O, but got Unknown
			try
			{
				string payloadSignature = GetPayloadSignature(snapshot);
				if (forceRepublish || !string.Equals(payloadSignature, _lastPublishedPayloadSignature, StringComparison.Ordinal))
				{
					snapshot.Revision = _nextRevision++;
					string text = JsonUtility.ToJson((object)snapshot);
					Hashtable val = new Hashtable { [(object)"denis.ui.overlay.snapshot"] = text };
					Room currentRoom = PhotonNetwork.CurrentRoom;
					if (currentRoom != null)
					{
						currentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null);
					}
					_lastPublishedPayloadSignature = payloadSignature;
					_lastPublishAt = Time.unscaledTime;
					_lastPublishedHaulValue = snapshot.HaulValue;
				}
			}
			catch
			{
			}
		}

		private static string GetPayloadSignature(OverlaySyncSnapshot snapshot)
		{
			return $"{snapshot.Active}|{snapshot.ExtractionClosed}|{snapshot.HaulTransitionActive}|{snapshot.MapValue}|{snapshot.CartValue}|{snapshot.HaulValue}|{snapshot.BoxCommon}|{snapshot.BoxUncommon}|{snapshot.BoxRare}|{snapshot.BoxUltraRare}|{GetIntentSignature(snapshot.MapIntent)}|{GetIntentSignature(snapshot.CartIntent)}|{GetIntentSignature(snapshot.BoxesIntent)}|{GetIntentSignature(snapshot.HaulIntent)}";
		}

		private static string GetIntentSignature(AnimationIntentSyncPayload? payload)
		{
			if (payload == null || !payload.Active)
			{
				return "0";
			}
			return $"1:{payload.Kind}:{payload.PreviousValue}:{payload.NextValue}:{payload.Direction}:{payload.AnimationCode}:{payload.ReasonCode}";
		}
	}
	internal static class RunCurrencyTracker
	{
		private static MethodInfo? _statGetRunCurrencyMethod;

		private static bool _discovered;

		internal static int ReadBalanceMoney()
		{
			DiscoverIfNeeded();
			if (_statGetRunCurrencyMethod == null)
			{
				DenisUIPlugin.Logger.LogWarning((object)"[RunCurrencyTracker] StatGetRunCurrency method not found!");
				return 0;
			}
			try
			{
				object obj = ResolveInvocationTarget(_statGetRunCurrencyMethod);
				object obj2 = _statGetRunCurrencyMethod.Invoke(obj, Array.Empty<object>());
				if (obj2 == null)
				{
					DenisUIPlugin.Logger.LogWarning((object)"[RunCurrencyTracker] StatGetRunCurrency returned null");
					return 0;
				}
				int num = Convert.ToInt32(obj2);
				return Mathf.Max(0, num * 1000);
			}
			catch (Exception ex)
			{
				DenisUIPlugin.Logger.LogError((object)("[RunCurrencyTracker] Exception: " + ex.Message));
				return 0;
			}
		}

		private static void DiscoverIfNeeded()
		{
			if (_discovered)
			{
				return;
			}
			_discovered = true;
			try
			{
				Assembly assembly = typeof(StatsManager).Assembly;
				Type[] types = assembly.GetTypes();
				foreach (Type type in types)
				{
					MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (MethodInfo methodInfo in methods)
					{
						if (string.Equals(methodInfo.Name, "StatGetRunCurrency", StringComparison.OrdinalIgnoreCase))
						{
							_statGetRunCurrencyMethod = methodInfo;
							return;
						}
					}
				}
			}
			catch
			{
			}
		}

		private static object? ResolveInvocationTarget(MethodInfo method)
		{
			if (method.IsStatic)
			{
				return null;
			}
			Type declaringType = method.DeclaringType;
			PropertyInfo propertyInfo = AccessTools.Property(declaringType, "instance") ?? AccessTools.Property(declaringType, "Instance");
			if (propertyInfo != null)
			{
				return propertyInfo.GetValue(null, null);
			}
			FieldInfo fieldInfo = AccessTools.Field(declaringType, "instance") ?? AccessTools.Field(declaringType, "Instance");
			if (fieldInfo != null)
			{
				return fieldInfo.GetValue(null);
			}
			return null;
		}
	}
	internal static class SimplifiedMapValueTracker
	{
		private const float ReadIntervalSeconds = 0.1f;

		private static float _cachedMapValue = 0f;

		private static float _lastReadAt = -100000f;

		internal static int GetCurrentMapValue()
		{
			float unscaledTime = Time.unscaledTime;
			if (unscaledTime - _lastReadAt >= 0.1f)
			{
				_lastReadAt = unscaledTime;
				_cachedMapValue = 0f;
				try
				{
					foreach (ValuableObject item in GameStateAccessors.GetValuablesOnMap())
					{
						if ((Object)(object)item != (Object)null)
						{
							_cachedMapValue += GetValuableCurrent(item);
						}
					}
				}
				catch
				{
				}
			}
			return Mathf.RoundToInt(Mathf.Max(0f, _cachedMapValue));
		}

		internal static void Reset()
		{
			_cachedMapValue = 0f;
			_lastReadAt = -100000f;
		}

		private static float GetValuableCurrent(ValuableObject valuable)
		{
			try
			{
				Type type = ((object)valuable).GetType();
				FieldInfo field = type.GetField("dollarValueCurrent", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				if (field != null)
				{
					return (field.GetValue(valuable) is float num) ? num : 0f;
				}
				string[] array = new string[5] { "currentValue", "CurrentValue", "value", "_currentValue", "haulCurrent" };
				foreach (string name in array)
				{
					field = type.GetField(name, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
					if (field != null)
					{
						return (field.GetValue(valuable) is float num2) ? num2 : 0f;
					}
				}
				return 0f;
			}
			catch
			{
				return 0f;
			}
		}
	}
	internal static class TabOverlay
	{
		private const float MapValueIntroCountUpSeconds = 1.8f;

		private const float MapValueDeltaShowSeconds = 3f;

		private const float MapValueDecreaseCountDownSeconds = 0.22f;

		private const float MapValueDeltaWhiteSeconds = 0.22f;

		private const float CartValueScrambleSeconds = 0.38f;

		private const float CartValueScrambleFrameSeconds = 0.05f;

		private const float OverlayLineGap = 2f;

		private const float OverlayTopPadding = 22f;

		private const float SharedRevealSeconds = 0.34f;

		private const float MapRevealDelaySeconds = 0.22f;

		private const float CartRevealDelaySeconds = 0.4f;

		private const float SharedRevealStartOffsetX = 18f;

		private const float CloseToHaulFadeOutSeconds = 0.18f;

		private const float CloseToHaulFadeInSeconds = 0.18f;

		private const float CloseToHaulSwitchDelaySeconds = 0.14f;

		private const float HaulLatchedCartDrainSeconds = 0.42f;

		private static readonly MapValueAnimationController MapValueAnimation = new MapValueAnimationController(1.8f, 3f, 0.22f, 0.22f);

		private static readonly CartValueAnimationController CartValueAnimation = new CartValueAnimationController(0.38f, 0.05f);

		private static readonly OverlayRevealController OverlayReveal = new OverlayRevealController();

		private static GameObject? _overlayRoot;

		private static TextMeshProUGUI? _primaryText;

		private static TextMeshProUGUI? _cartText;

		private static TextMeshProUGUI? _boxesText;

		private static string _lastRenderedPrimaryContent = string.Empty;

		private static string _lastRenderedCartContent = string.Empty;

		private static string _lastRenderedBoxesContent = string.Empty;

		private static string _lastOpenPrimaryContent = string.Empty;

		private static string _lastOpenCartContent = string.Empty;

		private static string _transitionOutPrimaryContent = string.Empty;

		private static string _transitionOutCartContent = string.Empty;

		private static string _transitionInHaulContent = string.Empty;

		private static OverlaySyncSnapshot? _lastOpenSnapshot;

		private static float _overlayRevealStartedAt = -100000f;

		private static float _closeToHaulStartedAt = -100000f;

		private static bool _wasExtractionClosed;

		private static float _lastHandledHaulIntentAt = -100000f;

		private static bool _haulPresentationLatched;

		private static float _haulCartDrainStartedAt = -100000f;

		private static int _haulCartDrainStartValue;

		private static int _lastObservedHaulValue = -1;

		private static int _haulAnimationFromValue = -1;

		private static int _haulAnimationToValue = -1;

		private static float _haulAnimationStartedAt = -100000f;

		private static bool _updateErrorLogged;

		private static bool _runStartIntroTriggered;

		private static int _lastEventMapValue = -1;

		private static int _lastEventCartValue = -1;

		internal static void OnRoundDirectorUpdate()
		{
			if (!SemiFunc.RunIsLevel())
			{
				return;
			}
			try
			{
				if (EnsureOverlayReady())
				{
					bool mapOpen = IsMapOpen();
					bool flag = ShouldShowOverlay(mapOpen);
					OverlaySyncBridge.TickHostPublish(mapOpen, flag);
					OverlaySyncBridge.UpdateLocalSnapshotCache(mapOpen, flag);
					CheckRunStartIntroTrigger();
					OverlaySyncSnapshot overlaySyncSnapshot = CaptureDisplaySnapshot(mapOpen);
					bool flag2 = flag || ShouldShowBoxesOnly(overlaySyncSnapshot);
					ApplyOverlayVisibility(flag2);
					if (flag2)
					{
						UpdateContent(mapOpen, overlaySyncSnapshot, OverlaySyncBridge.ShouldUseRemoteSnapshot() && overlaySyncSnapshot != null && !OverlaySyncBridge.ShouldFallbackToLocal());
					}
				}
			}
			catch (Exception arg)
			{
				if (!_updateErrorLogged)
				{
					_updateErrorLogged = true;
					DenisUIPlugin.Logger.LogWarning((object)$"TabOverlay update failed: {arg}");
				}
			}
		}

		internal static void OnSceneSwitch()
		{
			OverlaySyncBridge.ClearPublishedSnapshot();
			if ((Object)(object)_overlayRoot != (Object)null)
			{
				Object.Destroy((Object)(object)_overlayRoot);
				_overlayRoot = null;
				_primaryText = null;
				_cartText = null;
				_boxesText = null;
				_lastRenderedPrimaryContent = string.Empty;
				_lastRenderedCartContent = string.Empty;
				_lastRenderedBoxesContent = string.Empty;
				_lastOpenPrimaryContent = string.Empty;
				_lastOpenCartContent = string.Empty;
				_transitionOutPrimaryContent = string.Empty;
				_transitionOutCartContent = string.Empty;
				_transitionInHaulContent = string.Empty;
			}
			MapValueAnimation.Reset();
			CartValueAnimation.Reset();
			OverlayReveal.Reset();
			AnimationIntentRecorder.Reset();
			AnimationIntentConsumer.Reset();
			DataSnapshotBuilder.Reset();
			SimplifiedMapValueTracker.Reset();
			_runStartIntroTriggered = false;
			_overlayRevealStartedAt = -100000f;
			_closeToHaulStartedAt = -100000f;
			_lastHandledHaulIntentAt = -100000f;
			_wasExtractionClosed = false;
			_haulPresentationLatched = false;
			_haulCartDrainStartedAt = -100000f;
			_haulCartDrainStartValue = 0;
			_lastObservedHaulValue = -1;
			_haulAnimationFromValue = -1;
			_haulAnimationToValue = -1;
			_haulAnimationStartedAt = -100000f;
			_lastOpenSnapshot = null;
		}

		private static void CreateOverlay()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cd: 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_00f7: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Unknown result type (might be due to invalid IL or missing references)
			GameObject val = GameObject.Find("Game Hud");
			GameObject val2 = GameObject.Find("Tax Haul");
			if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null))
			{
				TMP_FontAsset font = val2.GetComponent<TMP_Text>().font;
				_overlayRoot = new GameObject("Denis UI Overlay", new Type[1] { typeof(RectTransform) });
				_overlayRoot.SetActive(false);
				RectTransform component = _overlayRoot.GetComponent<RectTransform>();
				_overlayRoot.transform.SetParent(val.transform, false);
				component.pivot = new Vector2(1f, 1f);
				component.anchoredPosition = new Vector2(1f, -1f);
				component.anchorMin = new Vector2(0f, 0f);
				component.anchorMax = new Vector2(1f, 0f);
				component.sizeDelta = new Vector2(0f, 0f);
				component.offsetMax = new Vector2(0f, 315f);
				component.offsetMin = new Vector2(400f, 145f);
				_primaryText = CreateOverlayText("Primary", font);
				_cartText = CreateOverlayText("Cart", font);
				_boxesText = CreateOverlayText("Boxes", font);
			}
		}

		private static TextMeshProUGUI CreateOverlayText(string name, TMP_FontAsset font)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Expected O, but got Unknown
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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_00c6: 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_00e6: Unknown result type (might be due to invalid IL or missin