Decompiled source of Denis UI v1.2.2

plugins/DenisUI/DenisUI.dll

Decompiled 2 days 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.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: IgnoresAccessChecksTo("")]
[assembly: AssemblyCompany("denism")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyFileVersion("1.2.2.0")]
[assembly: AssemblyInformationalVersion("1.2.2+0d77aba4ef99a87d6be4daeb81b0de1f2e0c0dca")]
[assembly: AssemblyProduct("DenisUI")]
[assembly: AssemblyTitle("DenisUI")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.2.2.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)
		{
			return (!state.IsActive) ? new AnimationIntentSyncPayload() : AnimationIntentSyncPayload.FromIntent(state.Intent);
		}

		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;
			}
			return (now - state.RecordedAt > Mathf.Max(0.01f, lifetimeSeconds)) ? default(ConsumedAnimationIntentState) : state;
		}
	}
	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;

		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;
			}
		}

		private static bool Resolve()
		{
			if (_resolved)
			{
				return _pluginType != null || _bridgeType != null;
			}
			_resolved = true;
			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;
						_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;
						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;
				}
			}
			return (_pluginType != null || _bridgeType != null) && (_getOverlayLockedBountyTotalMethod != null || _getFinalShopTargetMoneyMethod != null);
		}

		private static bool ShouldUseOverlayExport()
		{
			if (_hasOverlayExportProperty != null && _hasOverlayExportProperty.GetValue(null, null) is bool flag && !flag)
			{
				return false;
			}
			return true;
		}
	}
	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)
			{
				return IsRemoving && now - RemovingStartedAt >= destroySeconds;
			}
		}

		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 DenisUIPerfTelemetry
	{
		internal enum LocalSnapshotMode
		{
			Standalone,
			Host,
			PassiveFallback
		}

		private static float _lastLogAt = -100000f;

		private static int _valuableSnapshotScans;

		private static int _valuableSnapshotObjects;

		private static int _cartSnapshotScans;

		private static int _cartSnapshotObjects;

		private static int _totalValueRecomputes;

		private static double _totalValueRecomputeMs;

		private static double _totalValueRecomputeMaxMs;

		private static int _lastTrackedValuableCount;

		private static int _cartValueRecomputes;

		private static double _cartValueRecomputeMs;

		private static double _cartValueRecomputeMaxMs;

		private static int _lastTrackedCartCount;

		private static int _hostLocalSnapshots;

		private static int _standaloneLocalSnapshots;

		private static int _passiveFallbackLocalSnapshots;

		private static int _passiveRemoteSnapshots;

		private static int _passiveRemoteMisses;

		private static int _passiveFallbackActivations;

		private static int _overlayPublishAttempts;

		private static int _overlayPublishSuccesses;

		private static int _overlayPublishKeepAlives;

		private static int _overlayPublishDuplicateSkips;

		private static int _overlayPublishedBytesTotal;

		private static int _overlayPublishedBytesMax;

		internal static void Reset()
		{
			_lastLogAt = -100000f;
			ResetWindow();
		}

		internal static void RecordValuableSnapshotScan(int count)
		{
			_valuableSnapshotScans++;
			_valuableSnapshotObjects += Mathf.Max(0, count);
		}

		internal static void RecordCartSnapshotScan(int count)
		{
			_cartSnapshotScans++;
			_cartSnapshotObjects += Mathf.Max(0, count);
		}

		internal static void RecordTotalValueRecompute(double elapsedMs, int trackedCount)
		{
			_totalValueRecomputes++;
			_totalValueRecomputeMs += elapsedMs;
			if (elapsedMs > _totalValueRecomputeMaxMs)
			{
				_totalValueRecomputeMaxMs = elapsedMs;
			}
			_lastTrackedValuableCount = Mathf.Max(0, trackedCount);
		}

		internal static void RecordCartValueRecompute(double elapsedMs, int trackedCount)
		{
			_cartValueRecomputes++;
			_cartValueRecomputeMs += elapsedMs;
			if (elapsedMs > _cartValueRecomputeMaxMs)
			{
				_cartValueRecomputeMaxMs = elapsedMs;
			}
			_lastTrackedCartCount = Mathf.Max(0, trackedCount);
		}

		internal static void RecordLocalSnapshot(LocalSnapshotMode mode)
		{
			switch (mode)
			{
			case LocalSnapshotMode.Host:
				_hostLocalSnapshots++;
				break;
			case LocalSnapshotMode.PassiveFallback:
				_passiveFallbackLocalSnapshots++;
				break;
			default:
				_standaloneLocalSnapshots++;
				break;
			}
		}

		internal static void RecordPassiveRemoteSnapshot(bool success)
		{
			if (success)
			{
				_passiveRemoteSnapshots++;
			}
			else
			{
				_passiveRemoteMisses++;
			}
		}

		internal static void RecordPassiveFallbackActivation()
		{
			_passiveFallbackActivations++;
		}

		internal static void RecordOverlayPublishAttempt(bool keepAlive)
		{
			_overlayPublishAttempts++;
			if (keepAlive)
			{
				_overlayPublishKeepAlives++;
			}
		}

		internal static void RecordOverlayPublishSuccess(int payloadBytes)
		{
			_overlayPublishSuccesses++;
			_overlayPublishedBytesTotal += Mathf.Max(0, payloadBytes);
			if (payloadBytes > _overlayPublishedBytesMax)
			{
				_overlayPublishedBytesMax = payloadBytes;
			}
		}

		internal static void RecordOverlayPublishDuplicateSkip()
		{
			_overlayPublishDuplicateSkips++;
		}

		internal static void MaybeLogSummary()
		{
			if (DenisUIPlugin.IsPerfDebugLoggingEnabled())
			{
				float unscaledTime = Time.unscaledTime;
				float perfDebugLogIntervalSeconds = DenisUIPlugin.GetPerfDebugLogIntervalSeconds();
				if (!(unscaledTime - _lastLogAt < perfDebugLogIntervalSeconds))
				{
					_lastLogAt = unscaledTime;
					double num = ((_totalValueRecomputes > 0) ? (_totalValueRecomputeMs / (double)_totalValueRecomputes) : 0.0);
					double num2 = ((_cartValueRecomputes > 0) ? (_cartValueRecomputeMs / (double)_cartValueRecomputes) : 0.0);
					double num3 = ((_overlayPublishSuccesses > 0) ? ((double)_overlayPublishedBytesTotal / (double)_overlayPublishSuccesses) : 0.0);
					DenisUIPlugin.Logger.LogInfo((object)("[DenisUIPerf] " + $"valuableScans={_valuableSnapshotScans} valuableScanObjs={_valuableSnapshotObjects} " + $"cartScans={_cartSnapshotScans} cartScanObjs={_cartSnapshotObjects} " + $"totalRecomputes={_totalValueRecomputes} totalAvgMs={num:0.###} totalMaxMs={_totalValueRecomputeMaxMs:0.###} trackedValuables={_lastTrackedValuableCount} " + $"cartRecomputes={_cartValueRecomputes} cartAvgMs={num2:0.###} cartMaxMs={_cartValueRecomputeMaxMs:0.###} trackedCarts={_lastTrackedCartCount} " + $"hostLocalSnapshots={_hostLocalSnapshots} standaloneLocalSnapshots={_standaloneLocalSnapshots} passiveRemoteSnapshots={_passiveRemoteSnapshots} passiveRemoteMisses={_passiveRemoteMisses} passiveFallbackActivations={_passiveFallbackActivations} passiveFallbackLocalSnapshots={_passiveFallbackLocalSnapshots} " + $"publishAttempts={_overlayPublishAttempts} publishSuccesses={_overlayPublishSuccesses} publishKeepAlives={_overlayPublishKeepAlives} publishDuplicateSkips={_overlayPublishDuplicateSkips} publishAvgBytes={num3:0.#} publishMaxBytes={_overlayPublishedBytesMax}"));
					ResetWindow();
				}
			}
		}

		private static void ResetWindow()
		{
			_valuableSnapshotScans = 0;
			_valuableSnapshotObjects = 0;
			_cartSnapshotScans = 0;
			_cartSnapshotObjects = 0;
			_totalValueRecomputes = 0;
			_totalValueRecomputeMs = 0.0;
			_totalValueRecomputeMaxMs = 0.0;
			_lastTrackedValuableCount = 0;
			_cartValueRecomputes = 0;
			_cartValueRecomputeMs = 0.0;
			_cartValueRecomputeMaxMs = 0.0;
			_lastTrackedCartCount = 0;
			_hostLocalSnapshots = 0;
			_standaloneLocalSnapshots = 0;
			_passiveFallbackLocalSnapshots = 0;
			_passiveRemoteSnapshots = 0;
			_passiveRemoteMisses = 0;
			_passiveFallbackActivations = 0;
			_overlayPublishAttempts = 0;
			_overlayPublishSuccesses = 0;
			_overlayPublishKeepAlives = 0;
			_overlayPublishDuplicateSkips = 0;
			_overlayPublishedBytesTotal = 0;
			_overlayPublishedBytesMax = 0;
		}
	}
	[BepInPlugin("denis.repo.denisui", "Denis UI", "1.2.2")]
	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.2.2";

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

		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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Expected O, but got Unknown
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Expected O, but got Unknown
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Expected O, but got Unknown
			//IL_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Expected O, but got Unknown
			//IL_0132: Unknown result type (might be due to invalid IL or missing references)
			//IL_013c: Expected O, but got Unknown
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Expected O, but got Unknown
			//IL_018e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0198: Expected O, but got Unknown
			//IL_01be: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c8: Expected O, but got Unknown
			//IL_01ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f8: Expected O, but got Unknown
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Expected O, but got Unknown
			//IL_024e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0258: Expected O, but got Unknown
			//IL_0277: Unknown result type (might be due to invalid IL or missing references)
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_027e: Expected O, but got Unknown
			//IL_0283: 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();
		}

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

		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()
		{
			return YellowCartText.Value ? GetValidatedHexColor(CartDollarColorHex, "#feb740") : GetDollarColorHex();
		}

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

		internal static bool IsPerfDebugLoggingEnabled()
		{
			return false;
		}

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

		private void OnDestroy()
		{
			Harmony? harmony = Harmony;
			if (harmony != null)
			{
				harmony.UnpatchSelf();
			}
		}
	}
	internal static class HaulTracker
	{
		private static bool _closedHaulLatched;

		private static int _latchedClosedHaulValue;

		private static int _lastIntentValue;

		internal static void Reset()
		{
			_closedHaulLatched = false;
			_latchedClosedHaulValue = 0;
			_lastIntentValue = 0;
		}

		internal static ClosedHaulStageState CaptureClosedStageState()
		{
			bool flag = (Object)(object)RoundDirector.instance != (Object)null && Traverse.Create((object)RoundDirector.instance).Field("allExtractionPointsCompleted").GetValue<bool>();
			int total;
			bool flag2 = BountySupportBridge.TryGetFinalShopTargetMoney(out total);
			if (flag2)
			{
				bool closedHaulLatched = _closedHaulLatched;
				int latchedClosedHaulValue = _latchedClosedHaulValue;
				_closedHaulLatched = true;
				_latchedClosedHaulValue = total;
				RecordHaulIntentIfNeeded(closedHaulLatched ? latchedClosedHaulValue : 0, total, "settle", "final-shop-target");
			}
			else if (!_closedHaulLatched && flag)
			{
				int num = RunCurrencyTracker.ReadBalanceMoney();
				if (num > 0)
				{
					_closedHaulLatched = true;
					_latchedClosedHaulValue = num;
					RecordHaulIntentIfNeeded(0, num, "settle", "run-currency-fallback");
				}
			}
			ClosedHaulStageState result = default(ClosedHaulStageState);
			result.Closed = flag2 || _closedHaulLatched;
			result.HaulValue = (_closedHaulLatched ? _latchedClosedHaulValue : 0);
			return result;
		}

		private static void RecordHaulIntentIfNeeded(int previousValue, int nextValue, string animationCode, string reasonCode)
		{
			if (nextValue > 0 && nextValue != _lastIntentValue)
			{
				_lastIntentValue = nextValue;
				AnimationIntentRecorder.Record(new AnimationIntent(AnimationIntentKind.HaulChanged, previousValue, nextValue, (nextValue >= previousValue) ? AnimationDirection.Up : AnimationDirection.Down, animationCode, reasonCode));
			}
		}
	}
	internal struct ClosedHaulStageState
	{
		public bool Closed;

		public int HaulValue;
	}
	public static class MapValueTracker
	{
		private sealed class TrackedValuableState
		{
			public ValuableObject Valuable;

			public float CurrentValue;

			public float OriginalValue;

			public TrackedValuableState(ValuableObject valuable, float currentValue, float originalValue)
			{
				Valuable = valuable;
				CurrentValue = currentValue;
				OriginalValue = originalValue;
			}
		}

		private sealed class TrackedCartState
		{
			public PhysGrabCart Cart;

			public int LastKnownHaul;

			public TrackedCartState(PhysGrabCart cart, int lastKnownHaul)
			{
				Cart = cart;
				LastKnownHaul = lastKnownHaul;
			}
		}

		internal enum TrackerHealth
		{
			Uninitialized,
			EventDriven,
			RecoveryPending
		}

		private enum ValueRecoveryReason
		{
			None,
			Startup,
			GenerateDone,
			RegistryEmpty,
			TotalDroppedToZero,
			ExplicitDriftRecovery,
			EventDrivenVerification
		}

		private enum CartRecoveryReason
		{
			None,
			Startup,
			GenerateDone,
			RegistryEmpty,
			ExplicitDriftRecovery,
			EventDrivenVerification
		}

		internal readonly struct MapTrackerSnapshot
		{
			public readonly int MapValue;

			public readonly int CartValue;

			public readonly TrackerHealth Health;

			public MapTrackerSnapshot(int mapValue, int cartValue, TrackerHealth health)
			{
				MapValue = mapValue;
				CartValue = cartValue;
				Health = health;
			}
		}

		private sealed class Accessors
		{
			private readonly MemberInfo? _currentMember;

			private readonly MemberInfo? _originalMember;

			private Accessors(MemberInfo? currentMember, MemberInfo? originalMember)
			{
				_currentMember = currentMember;
				_originalMember = originalMember;
			}

			public float ReadCurrent(ValuableObject valuable)
			{
				return ReadMember(valuable, _currentMember);
			}

			public float ReadOriginal(ValuableObject valuable)
			{
				return ReadMember(valuable, _originalMember);
			}

			public static Accessors Create(Type valuableType)
			{
				return new Accessors(FindFirstMember(valuableType, "dollarValueCurrent", "dollarValue", "value", "Value", "currentValue", "CurrentValue"), FindFirstMember(valuableType, "dollarValueOriginal", "dollarValueStart", "originalValue", "OriginalValue", "baseValue", "BaseValue"));
			}

			private static MemberInfo? FindFirstMember(Type type, params string[] names)
			{
				foreach (string text in names)
				{
					FieldInfo fieldInfo = AccessTools.Field(type, text);
					if (fieldInfo != null)
					{
						return fieldInfo;
					}
					PropertyInfo propertyInfo = AccessTools.Property(type, text);
					if (propertyInfo != null)
					{
						return propertyInfo;
					}
				}
				return null;
			}

			private static float ReadMember(object target, MemberInfo? member)
			{
				if (target == null || member == null)
				{
					return 0f;
				}
				if (1 == 0)
				{
				}
				object obj = ((member is FieldInfo fieldInfo) ? fieldInfo.GetValue(target) : ((!(member is PropertyInfo propertyInfo)) ? null : propertyInfo.GetValue(target, null)));
				if (1 == 0)
				{
				}
				object obj2 = obj;
				float result;
				return (obj2 != null && TryConvertToFloat(obj2, out result)) ? result : 0f;
			}
		}

		private const float SnapshotCacheSeconds = 2f;

		private const float StartupRecoveryScanInterval = 0.35f;

		private const float RecoveryPendingScanInterval = 15f;

		private const float MapEventDrivenRescanOpenSeconds = 2.5f;

		private const float MapEventDrivenRescanClosedSeconds = 6f;

		private const float CartStartupRecoverySeconds = 2f;

		private const float CartLiveValueRefreshSeconds = 0.08f;

		private const float CartEventDrivenRecoveryOpenSeconds = 2.5f;

		private const float CartEventDrivenRecoveryClosedSeconds = 6f;

		private static float _lastCartRefreshTime = -100000f;

		private static float _lastCartVerificationTime = -100000f;

		private static float _lastMapValueRefreshTime = -100000f;

		private static float _lastMapVerificationTime = -100000f;

		private static bool _lastMapOpen;

		private static float _lastFullRefreshTime = -100000f;

		private static TrackerHealth _mapHealth;

		private static TrackerHealth _cartHealth;

		private static ValueRecoveryReason _pendingValueRecoveryReason;

		private static CartRecoveryReason _pendingCartRecoveryReason;

		private static readonly Dictionary<int, TrackedValuableState> TrackedValuables = new Dictionary<int, TrackedValuableState>();

		private static readonly Dictionary<int, TrackedCartState> TrackedCarts = new Dictionary<int, TrackedCartState>();

		private static ValuableObject[]? _cachedValuables;

		private static float _cachedValuablesAt = -100000f;

		private static readonly FieldInfo CartHaulCurrentField = AccessTools.Field(typeof(PhysGrabCart), "haulCurrent");

		private static readonly FieldInfo AllExtractionPointsCompletedField = AccessTools.Field(typeof(RoundDirector), "allExtractionPointsCompleted");

		private static bool _cartFieldWarningLogged;

		private static bool _extractionCompletedFieldWarningLogged;

		private static readonly Dictionary<Type, Accessors> ValuableAccessorsByType = new Dictionary<Type, Accessors>();

		private static int _mapRecoveryCount;

		private static int _cartRecalcCount;

		private static int _valuableScanCount;

		private static int _cartScanCount;

		private static int _trackedValueRefreshCount;

		private static int _trackedCartRefreshCount;

		private static double _mapRecoveryMsTotal;

		private static double _cartRecalcMsTotal;

		private static double _mapRecoveryMsMax;

		private static double _cartRecalcMsMax;

		private static int _lastValuableCount;

		private static int _lastCartCount;

		public static float TotalValue { get; private set; }

		public static float InitialValue { get; private set; }

		public static float CachedCartsValue { get; private set; }

		public static void Reset()
		{
			if (!SemiFunc.RunIsLevel())
			{
				TotalValue = 0f;
			}
			CachedCartsValue = 0f;
			InvalidateValueCaches();
			TrackedValuables.Clear();
			TrackedCarts.Clear();
			_lastFullRefreshTime = -100000f;
			_lastMapValueRefreshTime = -100000f;
			_lastMapVerificationTime = -100000f;
			_lastCartRefreshTime = -100000f;
			_lastCartVerificationTime = -100000f;
			_lastMapOpen = false;
			_mapHealth = TrackerHealth.Uninitialized;
			_cartHealth = TrackerHealth.Uninitialized;
			_pendingValueRecoveryReason = ValueRecoveryReason.None;
			_pendingCartRecoveryReason = CartRecoveryReason.None;
			ValuableAccessorsByType.Clear();
			ResetPerfCounters();
		}

		internal static MapTrackerSnapshot CapturePreparedSnapshot(bool mapOpen, bool overlayVisible)
		{
			PrepareMapValueState(mapOpen, overlayVisible);
			PrepareCartState(mapOpen, overlayVisible);
			DenisUIPerfTelemetry.MaybeLogSummary();
			return new MapTrackerSnapshot(Mathf.RoundToInt(TotalValue), Mathf.RoundToInt(CachedCartsValue), _mapHealth);
		}

		public static void PrepareMapValueState(bool mapOpen, bool overlayVisible)
		{
			if (!ShouldFreezeMapValueForHaulTransition())
			{
				float unscaledTime = Time.unscaledTime;
				bool interactive = mapOpen || overlayVisible;
				if (_mapHealth == TrackerHealth.EventDriven)
				{
					MaybeRunEventDrivenValueVerification(unscaledTime, interactive);
				}
				else if (ShouldRecoverMapValue(unscaledTime))
				{
					RunValueRecoveryScan((_pendingValueRecoveryReason == ValueRecoveryReason.None) ? ValueRecoveryReason.Startup : _pendingValueRecoveryReason, unscaledTime);
				}
			}
		}

		private static void RunValueRecoveryScan(ValueRecoveryReason reason, float now)
		{
			Stopwatch stopwatch = Stopwatch.StartNew();
			RebuildValueRegistryFromScene();
			stopwatch.Stop();
			RecordMapRecovery(stopwatch.Elapsed.TotalMilliseconds);
			_lastMapValueRefreshTime = now;
			_lastMapVerificationTime = now;
			_lastFullRefreshTime = now;
			_pendingValueRecoveryReason = ValueRecoveryReason.None;
			UpdateMapHealthAfterRefresh();
			LogValueRecoveryReason(reason);
		}

		public static void PrepareCartState(bool mapOpen, bool overlayVisible)
		{
			float unscaledTime = Time.unscaledTime;
			bool flag = mapOpen || overlayVisible;
			bool flag2 = mapOpen && !_lastMapOpen;
			TrackerHealth cartHealth = _cartHealth;
			if (1 == 0)
			{
			}
			float num = cartHealth switch
			{
				TrackerHealth.Uninitialized => 2f, 
				TrackerHealth.RecoveryPending => (!flag) ? 6f : 2.5f, 
				_ => flag ? 2.5f : 6f, 
			};
			if (1 == 0)
			{
			}
			float num2 = num;
			float num3 = (flag ? 0.08f : Mathf.Min(0.5f, num2));
			bool flag3 = unscaledTime - _lastCartRefreshTime >= num3;
			bool flag4 = unscaledTime - _lastCartVerificationTime >= num2;
			if ((flag3 || flag2 || _cartHealth != TrackerHealth.EventDriven) && TryUseNativeCartTotal(unscaledTime, out var _))
			{
				_lastMapOpen = mapOpen;
			}
			else if (_cartHealth == TrackerHealth.EventDriven)
			{
				if (TrackedCarts.Count == 0)
				{
					if (flag4)
					{
						RunCartRecoveryScan(CartRecoveryReason.RegistryEmpty, unscaledTime);
					}
				}
				else if (flag3 || flag2)
				{
					RefreshTrackedCartValues();
					_lastCartRefreshTime = unscaledTime;
				}
				_lastMapOpen = mapOpen;
			}
			else
			{
				if (flag4)
				{
					RunCartRecoveryScan((_pendingCartRecoveryReason == CartRecoveryReason.None) ? CartRecoveryReason.Startup : _pendingCartRecoveryReason, unscaledTime);
				}
				_lastMapOpen = mapOpen;
			}
		}

		private static bool TryUseNativeCartTotal(float now, out int sourceCount)
		{
			sourceCount = 0;
			if (!NativeCartTracker.TryGetDirectCartTotal(out var totalValue, out sourceCount))
			{
				return false;
			}
			CachedCartsValue = Mathf.Max(0f, (float)totalValue);
			_lastCartRefreshTime = now;
			_lastCartVerificationTime = now;
			_cartHealth = TrackerHealth.EventDriven;
			_pendingCartRecoveryReason = CartRecoveryReason.None;
			_lastCartCount = Math.Max(sourceCount, TrackedCarts.Count);
			DenisUIPerfTelemetry.RecordCartValueRecompute(0.0, _lastCartCount);
			return true;
		}

		private static void RebuildValueRegistryFromScene()
		{
			Stopwatch stopwatch = Stopwatch.StartNew();
			ValuableObject[] valuablesSnapshot = GetValuablesSnapshot(forceRefresh: true);
			TrackedValuables.Clear();
			float num = 0f;
			foreach (ValuableObject val in valuablesSnapshot)
			{
				if (!((Object)(object)val == (Object)null))
				{
					float valuableCurrent = GetValuableCurrent(val);
					float valuableOriginal = GetValuableOriginal(val);
					TrackedValuables[((Object)val).GetInstanceID()] = new TrackedValuableState(val, valuableCurrent, valuableOriginal);
					num += valuableCurrent;
				}
			}
			_lastValuableCount = TrackedValuables.Count;
			TotalValue = Mathf.Max(0f, num);
			stopwatch.Stop();
			DenisUIPerfTelemetry.RecordTotalValueRecompute(stopwatch.Elapsed.TotalMilliseconds, _lastValuableCount);
		}

		private static void ReconcileTrackedValueRegistry()
		{
			if (TrackedValuables.Count == 0)
			{
				return;
			}
			Stopwatch stopwatch = Stopwatch.StartNew();
			float num = 0f;
			List<int> list = null;
			foreach (KeyValuePair<int, TrackedValuableState> trackedValuable in TrackedValuables)
			{
				trackedValuable.Deconstruct(out var key, out var value);
				int item = key;
				TrackedValuableState trackedValuableState = value;
				ValuableObject valuable = trackedValuableState.Valuable;
				if ((Object)(object)valuable == (Object)null)
				{
					if (list == null)
					{
						list = new List<int>();
					}
					list.Add(item);
					continue;
				}
				float num2 = (trackedValuableState.CurrentValue = GetValuableCurrent(valuable));
				float valuableOriginal = GetValuableOriginal(valuable);
				if (valuableOriginal > 0f)
				{
					trackedValuableState.OriginalValue = valuableOriginal;
				}
				num += num2;
			}
			if (list != null)
			{
				for (int i = 0; i < list.Count; i++)
				{
					TrackedValuables.Remove(list[i]);
				}
			}
			_trackedValueRefreshCount++;
			_lastValuableCount = TrackedValuables.Count;
			TotalValue = Mathf.Max(0f, num);
			stopwatch.Stop();
			DenisUIPerfTelemetry.RecordTotalValueRecompute(stopwatch.Elapsed.TotalMilliseconds, _lastValuableCount);
		}

		private static void RebuildCartRegistryFromScene()
		{
			TrackedCarts.Clear();
			PhysGrabCart[] array = Object.FindObjectsOfType<PhysGrabCart>();
			_cartScanCount++;
			DenisUIPerfTelemetry.RecordCartSnapshotScan(array.Length);
			float num = 0f;
			foreach (PhysGrabCart val in array)
			{
				if (!((Object)(object)val == (Object)null))
				{
					int value = 0;
					TryGetCartHaulCurrent(val, out value);
					TrackedCarts[((Object)val).GetInstanceID()] = new TrackedCartState(val, value);
					num += (float)value;
				}
			}
			_lastCartCount = TrackedCarts.Count;
			CachedCartsValue = Mathf.Max(0f, num);
		}

		private static void RefreshTrackedCartValues()
		{
			if (TrackedCarts.Count == 0)
			{
				CachedCartsValue = 0f;
				DenisUIPerfTelemetry.RecordCartValueRecompute(0.0, 0);
				return;
			}
			Stopwatch stopwatch = Stopwatch.StartNew();
			List<int> list = null;
			foreach (KeyValuePair<int, TrackedCartState> trackedCart in TrackedCarts)
			{
				trackedCart.Deconstruct(out var key, out var value);
				int item = key;
				TrackedCartState trackedCartState = value;
				PhysGrabCart cart = trackedCartState.Cart;
				if ((Object)(object)cart == (Object)null)
				{
					if (list == null)
					{
						list = new List<int>();
					}
					list.Add(item);
					continue;
				}
				if (!TryGetCartHaulCurrent(cart, out var value2))
				{
					value2 = trackedCartState.LastKnownHaul;
				}
				int num = value2 - trackedCartState.LastKnownHaul;
				if (num != 0)
				{
					trackedCartState.LastKnownHaul = value2;
					CachedCartsValue = Mathf.Max(0f, CachedCartsValue + (float)num);
				}
			}
			if (list != null)
			{
				for (int i = 0; i < list.Count; i++)
				{
					UnregisterCartById(list[i]);
				}
			}
			_trackedCartRefreshCount++;
			_lastCartCount = TrackedCarts.Count;
			stopwatch.Stop();
			DenisUIPerfTelemetry.RecordCartValueRecompute(stopwatch.Elapsed.TotalMilliseconds, _lastCartCount);
		}

		private static void InvalidateValueCaches()
		{
			_cachedValuables = null;
			_cachedValuablesAt = -100000f;
		}

		private static void ApplyTotalDelta(float delta)
		{
			if (!Mathf.Approximately(delta, 0f) && !ShouldFreezeMapValueForHaulTransition())
			{
				TotalValue = Mathf.Max(0f, TotalValue + delta);
				_mapHealth = TrackerHealth.EventDriven;
				if (TotalValue <= 0f && SemiFunc.RunIsLevel() && !AreAllExtractionPointsCompleted())
				{
					RequestRecoveryScan(ValueRecoveryReason.TotalDroppedToZero);
				}
			}
		}

		private static void RequestRecoveryScan(ValueRecoveryReason reason = ValueRecoveryReason.ExplicitDriftRecovery)
		{
			_mapHealth = TrackerHealth.RecoveryPending;
			_pendingValueRecoveryReason = reason;
		}

		private static void RequestCartRecoveryScan(CartRecoveryReason reason = CartRecoveryReason.ExplicitDriftRecovery)
		{
			_cartHealth = TrackerHealth.RecoveryPending;
			_pendingCartRecoveryReason = reason;
		}

		private static bool ShouldFreezeMapValueForHaulTransition()
		{
			if (!SemiFunc.RunIsLevel())
			{
				return false;
			}
			if (AreAllExtractionPointsCompleted())
			{
				return true;
			}
			ClosedHaulStageState closedHaulStageState = HaulTracker.CaptureClosedStageState();
			return closedHaulStageState.Closed && closedHaulStageState.HaulValue > 0;
		}

		private static void MaybeRunEventDrivenValueVerification(float now, bool interactive)
		{
			if (TrackedValuables.Count == 0)
			{
				if (now - _lastMapVerificationTime >= 0.35f)
				{
					RunValueRecoveryScan(ValueRecoveryReason.RegistryEmpty, now);
				}
				return;
			}
			float num = (interactive ? 2.5f : 6f);
			if (now - _lastMapVerificationTime >= num)
			{
				RunValueRecoveryScan(ValueRecoveryReason.EventDrivenVerification, now);
			}
		}

		private static void RunCartRecoveryScan(CartRecoveryReason reason, float now)
		{
			Stopwatch stopwatch = Stopwatch.StartNew();
			RebuildCartRegistryFromScene();
			stopwatch.Stop();
			RecordCartRecalc(stopwatch.Elapsed.TotalMilliseconds);
			DenisUIPerfTelemetry.RecordCartValueRecompute(stopwatch.Elapsed.TotalMilliseconds, TrackedCarts.Count);
			_lastCartRefreshTime = now;
			_lastCartVerificationTime = now;
			_lastFullRefreshTime = now;
			_cartHealth = TrackerHealth.EventDriven;
			_pendingCartRecoveryReason = CartRecoveryReason.None;
			LogCartRecoveryReason(reason);
		}

		private static bool ShouldRecoverMapValue(float now)
		{
			TrackerHealth mapHealth = _mapHealth;
			if (1 == 0)
			{
			}
			float num = mapHealth switch
			{
				TrackerHealth.Uninitialized => 0.35f, 
				TrackerHealth.RecoveryPending => 15f, 
				_ => 0f, 
			};
			if (1 == 0)
			{
			}
			float num2 = num;
			TrackerHealth mapHealth2 = _mapHealth;
			if (1 == 0)
			{
			}
			bool result = mapHealth2 switch
			{
				TrackerHealth.Uninitialized => now - _lastFullRefreshTime >= num2, 
				TrackerHealth.RecoveryPending => now - _lastFullRefreshTime >= num2, 
				_ => false, 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		private static void UpdateMapHealthAfterRefresh()
		{
			_mapHealth = ((_lastValuableCount > 0 || TotalValue > 0f) ? TrackerHealth.EventDriven : TrackerHealth.Uninitialized);
			if (_mapHealth == TrackerHealth.Uninitialized && SemiFunc.RunIsLevel() && !AreAllExtractionPointsCompleted())
			{
				_pendingValueRecoveryReason = ValueRecoveryReason.RegistryEmpty;
			}
		}

		private static ValuableObject[] GetValuablesSnapshot(bool forceRefresh = false)
		{
			float unscaledTime = Time.unscaledTime;
			if (!forceRefresh && _cachedValuables != null && unscaledTime - _cachedValuablesAt < 2f)
			{
				return _cachedValuables;
			}
			_cachedValuables = Object.FindObjectsOfType<ValuableObject>();
			_cachedValuablesAt = unscaledTime;
			_valuableScanCount++;
			_lastValuableCount = _cachedValuables.Length;
			DenisUIPerfTelemetry.RecordValuableSnapshotScan(_cachedValuables.Length);
			return _cachedValuables;
		}

		private static void RecordMapRecovery(double elapsedMs)
		{
			_mapRecoveryCount++;
			_mapRecoveryMsTotal += elapsedMs;
			if (elapsedMs > _mapRecoveryMsMax)
			{
				_mapRecoveryMsMax = elapsedMs;
			}
		}

		private static void RecordCartRecalc(double elapsedMs)
		{
			_cartRecalcCount++;
			_cartRecalcMsTotal += elapsedMs;
			if (elapsedMs > _cartRecalcMsMax)
			{
				_cartRecalcMsMax = elapsedMs;
			}
		}

		private static void MaybeLogPerfSummary()
		{
			DenisUIPerfTelemetry.MaybeLogSummary();
		}

		private static void LogValueRecoveryReason(ValueRecoveryReason reason)
		{
			if (DenisUIPlugin.IsPerfDebugLoggingEnabled())
			{
				DenisUIPlugin.Logger.LogInfo((object)$"[DenisUITracker] mapRecovery reason={reason} trackedValuables={TrackedValuables.Count} total={Mathf.RoundToInt(TotalValue)}");
			}
		}

		private static void LogCartRecoveryReason(CartRecoveryReason reason)
		{
			if (DenisUIPlugin.IsPerfDebugLoggingEnabled())
			{
				DenisUIPlugin.Logger.LogInfo((object)$"[DenisUITracker] cartRecovery reason={reason} trackedCarts={TrackedCarts.Count} cart={Mathf.RoundToInt(CachedCartsValue)}");
			}
		}

		private static void ResetPerfCounters(bool keepLogTimestamp = false)
		{
			DenisUIPerfTelemetry.Reset();
			_mapRecoveryCount = 0;
			_cartRecalcCount = 0;
			_valuableScanCount = 0;
			_cartScanCount = 0;
			_trackedValueRefreshCount = 0;
			_trackedCartRefreshCount = 0;
			_mapRecoveryMsTotal = 0.0;
			_cartRecalcMsTotal = 0.0;
			_mapRecoveryMsMax = 0.0;
			_cartRecalcMsMax = 0.0;
			_lastValuableCount = 0;
			_lastCartCount = 0;
		}

		private static bool AreAllExtractionPointsCompleted()
		{
			if ((Object)(object)RoundDirector.instance == (Object)null)
			{
				return false;
			}
			if (AllExtractionPointsCompletedField == null)
			{
				if (!_extractionCompletedFieldWarningLogged)
				{
					_extractionCompletedFieldWarningLogged = true;
					DenisUIPlugin.Logger.LogWarning((object)"Unable to access RoundDirector.allExtractionPointsCompleted. Falling back to assuming extraction is still open.");
				}
				return false;
			}
			object value = AllExtractionPointsCompletedField.GetValue(RoundDirector.instance);
			if (value is bool result)
			{
				return result;
			}
			if (value is IConvertible value2)
			{
				try
				{
					return Convert.ToBoolean(value2);
				}
				catch
				{
					return false;
				}
			}
			return false;
		}

		private static bool TryGetCartHaulCurrent(PhysGrabCart cart, out int value)
		{
			value = 0;
			if ((Object)(object)cart == (Object)null)
			{
				return false;
			}
			if (CartHaulCurrentField == null)
			{
				LogMissingCartFieldWarning("haulCurrent");
				return false;
			}
			object value2 = CartHaulCurrentField.GetValue(cart);
			if (value2 is int num)
			{
				value = num;
				return true;
			}
			if (value2 is IConvertible value3)
			{
				try
				{
					value = Convert.ToInt32(value3);
					return true;
				}
				catch
				{
					return false;
				}
			}
			return false;
		}

		private static void LogMissingCartFieldWarning(string fieldName)
		{
			if (!_cartFieldWarningLogged)
			{
				_cartFieldWarningLogged = true;
				DenisUIPlugin.Logger.LogWarning((object)("Unable to access PhysGrabCart." + fieldName + ". Cart values will be treated as $0."));
			}
		}

		public static float GetValuableCurrent(ValuableObject vo)
		{
			Accessors accessors = GetAccessors(vo);
			return accessors.ReadCurrent(vo);
		}

		public static float GetValuableOriginal(ValuableObject vo)
		{
			Accessors accessors = GetAccessors(vo);
			float num = accessors.ReadOriginal(vo);
			if (num <= 0f)
			{
				num = accessors.ReadCurrent(vo);
			}
			return num;
		}

		private static Accessors GetAccessors(ValuableObject vo)
		{
			Type type = ((object)vo).GetType();
			if (ValuableAccessorsByType.TryGetValue(type, out Accessors value))
			{
				return value;
			}
			value = Accessors.Create(type);
			ValuableAccessorsByType[type] = value;
			return value;
		}

		private static bool TryConvertToFloat(object value, out float result)
		{
			if (value == null)
			{
				result = 0f;
				return false;
			}
			if (value is float num)
			{
				result = num;
				return true;
			}
			if (value is int num2)
			{
				result = num2;
				return true;
			}
			if (value is double num3)
			{
				result = (float)num3;
				return true;
			}
			if (value is long num4)
			{
				result = num4;
				return true;
			}
			if (value is IConvertible)
			{
				try
				{
					result = Convert.ToSingle(value);
					return true;
				}
				catch
				{
					result = 0f;
					return false;
				}
			}
			result = 0f;
			return false;
		}

		[HarmonyPatch(typeof(LevelGenerator), "StartRoomGeneration")]
		[HarmonyPrefix]
		private static void OnGenerationStart()
		{
			Reset();
		}

		[HarmonyPatch(typeof(LevelGenerator), "GenerateDone")]
		[HarmonyPostfix]
		private static void OnGenerationDone()
		{
			RebuildValueRegistryFromScene();
			RebuildCartRegistryFromScene();
			if (TotalValue > 0f)
			{
				InitialValue = TotalValue;
			}
			_lastMapValueRefreshTime = Time.unscaledTime;
			_lastMapVerificationTime = Time.unscaledTime;
			_lastFullRefreshTime = Time.unscaledTime;
			_lastCartRefreshTime = Time.unscaledTime;
			_lastCartVerificationTime = Time.unscaledTime;
			UpdateMapHealthAfterRefresh();
			_cartHealth = TrackerHealth.EventDriven;
			_pendingValueRecoveryReason = ValueRecoveryReason.None;
			_pendingCartRecoveryReason = CartRecoveryReason.None;
			LogValueRecoveryReason(ValueRecoveryReason.GenerateDone);
			LogCartRecoveryReason(CartRecoveryReason.GenerateDone);
		}

		[HarmonyPatch(typeof(ValuableObject), "DollarValueSetRPC")]
		[HarmonyPostfix]
		private static void OnDollarValueSetRpc(ValuableObject __instance, float value)
		{
			RegisterOrUpdateValuable(__instance, value);
			InvalidateValueCaches();
		}

		[HarmonyPatch(typeof(ValuableObject), "DollarValueSetLogic")]
		[HarmonyPostfix]
		private static void OnDollarValueSetLogic(ValuableObject __instance)
		{
			if (SemiFunc.IsMasterClientOrSingleplayer())
			{
				RegisterOrUpdateValuable(__instance);
				InvalidateValueCaches();
			}
		}

		[HarmonyPatch(typeof(PhysGrabObjectImpactDetector), "BreakRPC")]
		[HarmonyPostfix]
		private static void OnBreak(float valueLost, bool _loseValue)
		{
			if (_loseValue)
			{
				ApplyTotalDelta(0f - valueLost);
				InvalidateValueCaches();
			}
		}

		[HarmonyPatch(typeof(PhysGrabObject), "DestroyPhysGrabObjectRPC")]
		[HarmonyPostfix]
		private static void OnDestroy(PhysGrabObject __instance)
		{
			if (!SemiFunc.RunIsLevel())
			{
				return;
			}
			PhysGrabCart component = ((Component)__instance).GetComponent<PhysGrabCart>();
			if ((Object)(object)component != (Object)null)
			{
				UnregisterCart(component);
			}
			ValuableObject component2 = ((Component)__instance).GetComponent<ValuableObject>();
			if (!((Object)(object)component2 == (Object)null))
			{
				int instanceID = ((Object)component2).GetInstanceID();
				float value;
				float num = (TryGetTrackedCurrentValue(instanceID, out value) ? value : GetValuableCurrent(component2));
				float value2;
				float num2 = (TryGetTrackedOriginalValue(instanceID, out value2) ? value2 : GetValuableOriginal(component2));
				if (!(num < num2 * 0.15f))
				{
					ApplyTotalDelta(0f - num);
					TrackedValuables.Remove(instanceID);
					InvalidateValueCaches();
				}
			}
		}

		[HarmonyPatch(typeof(RoundDirector), "ExtractionCompleted")]
		[HarmonyPostfix]
		private static void OnExtractionCompleted()
		{
			if (SemiFunc.RunIsLevel())
			{
				TotalValue = 0f;
				CachedCartsValue = 0f;
				InvalidateValueCaches();
				TrackedValuables.Clear();
				TrackedCarts.Clear();
				_lastFullRefreshTime = Time.unscaledTime;
				_lastMapValueRefreshTime = Time.unscaledTime;
				_lastMapVerificationTime = Time.unscaledTime;
				_lastCartRefreshTime = Time.unscaledTime;
				_lastCartVerificationTime = Time.unscaledTime;
				_mapHealth = TrackerHealth.Uninitialized;
				_cartHealth = TrackerHealth.Uninitialized;
				_pendingValueRecoveryReason = ValueRecoveryReason.None;
				_pendingCartRecoveryReason = CartRecoveryReason.None;
			}
		}

		[HarmonyPatch(typeof(PhysGrabCart), "Start")]
		[HarmonyPostfix]
		private static void OnCartStart(PhysGrabCart __instance)
		{
			RegisterOrUpdateCart(__instance);
		}

		private static void RegisterOrUpdateValuable(ValuableObject valuable, float? currentValueOverride = null)
		{
			if ((Object)(object)valuable == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)valuable).GetInstanceID();
			float num = currentValueOverride ?? GetValuableCurrent(valuable);
			float valuableOriginal = GetValuableOriginal(valuable);
			if (TrackedValuables.TryGetValue(instanceID, out TrackedValuableState value))
			{
				float delta = num - value.CurrentValue;
				value.Valuable = valuable;
				value.CurrentValue = num;
				if (valuableOriginal > 0f)
				{
					value.OriginalValue = valuableOriginal;
				}
				ApplyTotalDelta(delta);
			}
			else
			{
				TrackedValuables[instanceID] = new TrackedValuableState(valuable, num, valuableOriginal);
				_lastValuableCount = TrackedValuables.Count;
				ApplyTotalDelta(num);
			}
		}

		private static void RegisterOrUpdateCart(PhysGrabCart cart, int? haulCurrentOverride = null)
		{
			if ((Object)(object)cart == (Object)null)
			{
				return;
			}
			int instanceID = ((Object)cart).GetInstanceID();
			int value;
			int num = haulCurrentOverride ?? (TryGetCartHaulCurrent(cart, out value) ? value : 0);
			if (TrackedCarts.TryGetValue(instanceID, out TrackedCartState value2))
			{
				int num2 = num - value2.LastKnownHaul;
				value2.Cart = cart;
				value2.LastKnownHaul = num;
				if (num2 != 0)
				{
					CachedCartsValue = Mathf.Max(0f, CachedCartsValue + (float)num2);
				}
			}
			else
			{
				TrackedCarts[instanceID] = new TrackedCartState(cart, num);
				CachedCartsValue = Mathf.Max(0f, CachedCartsValue + (float)num);
			}
			_lastCartCount = TrackedCarts.Count;
			_cartHealth = TrackerHealth.EventDriven;
		}

		private static void UnregisterCart(PhysGrabCart cart)
		{
			if (!((Object)(object)cart == (Object)null))
			{
				UnregisterCartById(((Object)cart).GetInstanceID());
			}
		}

		private static void UnregisterCartById(int id)
		{
			if (TrackedCarts.TryGetValue(id, out TrackedCartState value))
			{
				CachedCartsValue = Mathf.Max(0f, CachedCartsValue - (float)value.LastKnownHaul);
				TrackedCarts.Remove(id);
				_lastCartCount = TrackedCarts.Count;
				if (TrackedCarts.Count == 0 && SemiFunc.RunIsLevel() && !AreAllExtractionPointsCompleted())
				{
					RequestCartRecoveryScan(CartRecoveryReason.RegistryEmpty);
				}
			}
		}

		private static bool TryGetTrackedCurrentValue(int id, out float value)
		{
			if (TrackedValuables.TryGetValue(id, out TrackedValuableState value2))
			{
				value = value2.CurrentValue;
				return true;
			}
			value = 0f;
			return false;
		}

		private static bool TryGetTrackedOriginalValue(int id, out float value)
		{
			if (TrackedValuables.TryGetValue(id, out TrackedValuableState value2))
			{
				value = value2.OriginalValue;
				return true;
			}
			value = 0f;
			return false;
		}
	}
	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;
			}
			NativeHaulSnapshot nativeHaulSnapshot = NativeHaulTracker.CaptureSnapshot();
			return nativeHaulSnapshot.Valid && nativeHaulSnapshot.CurrentHaul > 0f;
		}

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

		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 static class NativeCartTracker
	{
		private static FieldInfo? _itemsCartsField;

		private static FieldInfo? _cartSnapshotsField;

		private static readonly Dictionary<Type, MemberInfo?> AmountMemberCache = new Dictionary<Type, MemberInfo>();

		private static bool _resolved;

		private static bool _warningLogged;

		internal static bool TryGetDirectCartTotal(out int totalValue, out int sourceCount)
		{
			totalValue = 0;
			sourceCount = 0;
			try
			{
				if (!Resolve())
				{
					return false;
				}
				RoundDirector instance = RoundDirector.instance;
				if ((Object)(object)instance == (Object)null)
				{
					return false;
				}
				if (TrySumEnumerableField(_itemsCartsField, instance, out totalValue, out sourceCount))
				{
					return true;
				}
				if (TrySumEnumerableField(_cartSnapshotsField, instance, out totalValue, out sourceCount))
				{
					return true;
				}
				return false;
			}
			catch (Exception ex)
			{
				LogWarningOnce("Native cart snapshot failed, falling back safely: " + ex.Message);
				return false;
			}
		}

		private static bool Resolve()
		{
			if (_resolved)
			{
				return _itemsCartsField != null || _cartSnapshotsField != null;
			}
			_resolved = true;
			try
			{
				_itemsCartsField = AccessTools.Field(typeof(RoundDirector), "itemsCarts");
				_cartSnapshotsField = AccessTools.Field(typeof(RoundDirector), "cartSnapshots");
			}
			catch (Exception ex)
			{
				LogWarningOnce("Native cart field resolve failed, falling back safely: " + ex.Message);
			}
			return _itemsCartsField != null || _cartSnapshotsField != null;
		}

		private static bool TrySumEnumerableField(FieldInfo? field, object target, out int totalValue, out int sourceCount)
		{
			totalValue = 0;
			sourceCount = 0;
			if (field == null || target == null)
			{
				return false;
			}
			object value = field.GetValue(target);
			if (!(value is IEnumerable enumerable))
			{
				return false;
			}
			bool flag = false;
			foreach (object item in enumerable)
			{
				if (item != null && TryReadAmount(item, out var amount))
				{
					totalValue += amount;
					sourceCount++;
					flag = true;
				}
			}
			return flag || value != null;
		}

		private static bool TryReadAmount(object entry, out int amount)
		{
			amount = 0;
			PhysGrabCart val = (PhysGrabCart)((entry is PhysGrabCart) ? entry : null);
			if (val != null)
			{
				return TryReadIntMember(val, typeof(PhysGrabCart), out amount, "haulCurrent", "currentHaul", "currentHaulValue", "AmountCurrent", "amountCurrent");
			}
			Type type = entry.GetType();
			return TryReadIntMember(entry, type, out amount, "haulCurrent", "currentHaul", "currentHaulValue", "AmountCurrent", "amountCurrent", "valueCurrent", "ValueCurrent");
		}

		private static bool TryReadIntMember(object target, Type type, out int amount, params string[] names)
		{
			amount = 0;
			if (!AmountMemberCache.TryGetValue(type, out MemberInfo value))
			{
				value = FindFirstMember(type, names);
				AmountMemberCache[type] = value;
			}
			if (value == null)
			{
				return false;
			}
			if (1 == 0)
			{
			}
			object obj = ((value is FieldInfo fieldInfo) ? fieldInfo.GetValue(target) : ((!(value is PropertyInfo propertyInfo)) ? null : propertyInfo.GetValue(target, null)));
			if (1 == 0)
			{
			}
			object obj2 = obj;
			if (obj2 == null)
			{
				return false;
			}
			try
			{
				amount = Convert.ToInt32(obj2);
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static MemberInfo? FindFirstMember(Type type, params string[] names)
		{
			foreach (string text in names)
			{
				FieldInfo fieldInfo = AccessTools.Field(type, text);
				if (fieldInfo != null)
				{
					return fieldInfo;
				}
				PropertyInfo propertyInfo = AccessTools.Property(type, text);
				if (propertyInfo != null)
				{
					return propertyInfo;
				}
			}
			return null;
		}

		private static void LogWarningOnce(string message)
		{
			if (!_warningLogged)
			{
				_warningLogged = true;
				DenisUIPlugin.Logger.LogWarning((object)message);
			}
		}
	}
	internal static class NativeHaulTracker
	{
		private static FieldInfo? _currentHaulField;

		private static FieldInfo? _extractionPointsField;

		private static FieldInfo? _extractionPointsCompletedField;

		private static bool _resolved;

		private static bool _warningLogged;

		internal static NativeHaulSnapshot CaptureSnapshot()
		{
			try
			{
				if (!Resolve())
				{
					return default(NativeHaulSnapshot);
				}
				RoundDirector instance = RoundDirector.instance;
				if ((Object)(object)instance == (Object)null)
				{
					return default(NativeHaulSnapshot);
				}
				NativeHaulSnapshot result = default(NativeHaulSnapshot);
				result.Valid = true;
				result.CurrentHaul = ReadFloat(_currentHaulField, instance);
				result.ExtractionPoints = ReadInt(_extractionPointsField, instance);
				result.ExtractionPointsCompleted = ReadInt(_extractionPointsCompletedField, instance);
				return result;
			}
			catch (Exception ex)
			{
				LogWarningOnce("Native haul snapshot failed, falling back safely: " + ex.Message);
				return default(NativeHaulSnapshot);
			}
		}

		private static bool Resolve()
		{
			if (_resolved)
			{
				return _currentHaulField != null;
			}
			_resolved = true;
			try
			{
				_currentHaulField = AccessTools.Field(typeof(RoundDirector), "currentHaul");
				_extractionPointsField = AccessTools.Field(typeof(RoundDirector), "extractionPoints");
				_extractionPointsCompletedField = AccessTools.Field(typeof(RoundDirector), "extractionPointsCompleted");
			}
			catch (Exception ex)
			{
				LogWarningOnce("Native haul field resolve failed, falling back safely: " + ex.Message);
			}
			return _currentHaulField != null;
		}

		private static int ReadInt(FieldInfo? field, object target)
		{
			if (field == null || target == null)
			{
				return 0;
			}
			object value = field.GetValue(target);
			if (value == null)
			{
				return 0;
			}
			try
			{
				return Convert.ToInt32(value);
			}
			catch
			{
				return 0;
			}
		}

		private static float ReadFloat(FieldInfo? field, object target)
		{
			if (field == null || target == null)
			{
				return 0f;
			}
			object value = field.GetValue(target);
			if (value == null)
			{
				return 0f;
			}
			try
			{
				return Convert.ToSingle(value);
			}
			catch
			{
				return 0f;
			}
		}

		private static void LogWarningOnce(string message)
		{
			if (!_warningLogged)
			{
				_warningLogged = true;
				DenisUIPlugin.Logger.LogWarning((object)message);
			}
		}
	}
	internal struct NativeHaulSnapshot
	{
		public bool Valid;

		public float CurrentHaul;

		public int ExtractionPoints;

		public int ExtractionPointsCompleted;
	}
	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 => Mode != 0 && !string.IsNullOrEmpty(ValueText);

		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