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 RecentIntents = new Queue(); 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 list = new List(); 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 _animatedBoxes = new List(); 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("■"); } private static void SyncAnimatedBoxes(CosmeticBoxCounts counts, float now, ConsumedAnimationIntentState intentState) { CleanupRemovedBoxes(now); CosmeticBoxCounts previousCounts = CloneCounts(_lastAnimatedCounts); List list = BuildDesiredBoxSequence(counts); List list2 = new List(); 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 BuildDesiredBoxSequence(CosmeticBoxCounts counts) { List list = new List(); 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 OverlayVisibility { get; private set; } = null; internal static ConfigEntry HostSync { get; private set; } = null; internal static ConfigEntry ShowMapValue { get; private set; } = null; internal static ConfigEntry ShowCartValue { get; private set; } = null; internal static ConfigEntry ShowLootboxes { get; private set; } = null; internal static ConfigEntry CosmeticBoxesScale { get; private set; } = null; internal static ConfigEntry YellowCartText { get; private set; } = null; internal static ConfigEntry LabelColorHex { get; private set; } = null; internal static ConfigEntry ValueColorHex { get; private set; } = null; internal static ConfigEntry DollarColorHex { get; private set; } = null; internal static ConfigEntry CartDollarColorHex { get; private set; } = null; internal static ConfigEntry 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("General", "OverlayVisibility", OverlayVisibilityMode.Always, new ConfigDescription("Overlay visibility mode. Available values: Off, ShowWithMap, Always.", (AcceptableValueBase)null, Array.Empty())); HostSync = ((BaseUnityPlugin)this).Config.Bind("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())); ShowMapValue = ((BaseUnityPlugin)this).Config.Bind("General", "Map Value", true, new ConfigDescription("Show map value on the level in the overlay.", (AcceptableValueBase)null, Array.Empty())); ShowCartValue = ((BaseUnityPlugin)this).Config.Bind("General", "C.A.R.T. Value", true, new ConfigDescription("Show C.A.R.T. value in the overlay.", (AcceptableValueBase)null, Array.Empty())); ShowLootboxes = ((BaseUnityPlugin)this).Config.Bind("General", "Cosmetic Boxes", true, new ConfigDescription("Show Cosmetic Boxes in the overlay.", (AcceptableValueBase)null, Array.Empty())); CosmeticBoxesScale = ((BaseUnityPlugin)this).Config.Bind("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(0.35f, 1f), Array.Empty())); YellowCartText = ((BaseUnityPlugin)this).Config.Bind("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())); LabelColorHex = ((BaseUnityPlugin)this).Config.Bind("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())); ValueColorHex = ((BaseUnityPlugin)this).Config.Bind("Advanced", "Value color", "#ffffff", new ConfigDescription("HEX color for numeric values in the overlay. Format: #RRGGBB", (AcceptableValueBase)null, Array.Empty())); DollarColorHex = ((BaseUnityPlugin)this).Config.Bind("Advanced", "Dollar color", "#66c94f", new ConfigDescription("HEX color for the $ symbol in money lines. Format: #RRGGBB", (AcceptableValueBase)null, Array.Empty())); CartDollarColorHex = ((BaseUnityPlugin)this).Config.Bind("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())); CartValueColorHex = ((BaseUnityPlugin)this).Config.Bind("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())); if (Harmony == null) { Harmony val = new Harmony(((BaseUnityPlugin)this).Info.Metadata.GUID); Harmony val2 = val; Harmony = val; } Harmony.PatchAll(); } internal static string GetValidatedHexColor(ConfigEntry 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(); 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 TrackedValuables = new Dictionary(); private static readonly Dictionary TrackedCarts = new Dictionary(); 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 ValuableAccessorsByType = new Dictionary(); 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 list = null; foreach (KeyValuePair 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(); } 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(); _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 list = null; foreach (KeyValuePair 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(); } 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(); _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(); if ((Object)(object)component != (Object)null) { UnregisterCart(component); } ValuableObject component2 = ((Component)__instance).GetComponent(); 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 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 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 AmountMemberCache = new Dictionary(); 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 + "+$" + FormatCompactWholeMoney(num3) + ""; 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 = "-$" + FormatMoney(mapDisplay.DeltaAmount, compact: false) + ""; string text5 = ((mapDisplay.DeltaOffsetX >= 0f) ? new string(' ', Mathf.Clamp(Mathf.RoundToInt(mapDisplay.DeltaOffsetX / 2f), 0, 4)) : string.Empty); string prefixRichText = text5 + text4; return new OverlayLinePresentation(OverlayLineMode.CompositeMoney, OverlayRevealTrack.Primary, "Map Value", mapDisplay.ValueText, prefixRichText); } return new OverlayLinePresentation(OverlayLineMode.Money, OverlayRevealTrack.Primary, "Map Value", mapDisplay.ValueText); } internal static OverlayLinePresentation BuildCartPresentation(CartAnimationDisplay cartDisplay) { if (cartDisplay.IsAnimating) { return new OverlayLinePresentation(OverlayLineMode.AlertMoney, OverlayRevealTrack.Cart, "C.A.R.T.", cartDisplay.Text, "", forceWhiteMoney: false, null, null, cartDisplay.IsIncrease ? "#ffffff" : "#EF3338"); } return new OverlayLinePresentation(OverlayLineMode.Money, OverlayRevealTrack.Cart, "C.A.R.T.", cartDisplay.Text, "", forceWhiteMoney: false, DenisUIPlugin.GetCartDollarColorHex(), DenisUIPlugin.GetCartValueColorHex()); } internal static OverlayLinePresentation BuildBoxesPresentation(CosmeticBoxSnapshot snapshot, ConsumedAnimationIntentState intentState) { string text = CosmeticBoxTracker.BuildLine(snapshot, intentState); if (string.IsNullOrEmpty(text)) { return default(OverlayLinePresentation); } return new OverlayLinePresentation(OverlayLineMode.RawText, OverlayRevealTrack.Tertiary, string.Empty, text); } private static string FormatMoney(float value, bool compact) { int num = Mathf.RoundToInt(value); if (!compact) { return num.ToString("N0"); } if (num >= 1000000) { return $"{(float)num / 1000000f:0.#}M"; } if (num >= 1000) { return $"{(float)num / 1000f:0.#}K"; } return num.ToString("N0"); } private static string FormatCompactWholeMoney(float value) { int num = Mathf.RoundToInt(value); if (num >= 1000000) { return $"{Mathf.RoundToInt((float)num / 1000000f)}M"; } if (num >= 1000) { return $"{Mathf.RoundToInt((float)num / 1000f)}K"; } return num.ToString("N0"); } private static string ToAlphaHex(float alpha) { return Mathf.Clamp(Mathf.RoundToInt(alpha * 255f), 0, 255).ToString("X2"); } private static float GetFloatingDeltaAlpha(float startedAt, float now, float showSeconds) { float num = now - startedAt; float num2 = Mathf.Min(0.1f, showSeconds * 0.2f); float num3 = Mathf.Min(0.18f, showSeconds * 0.28f); float num4 = ((num2 <= 0.01f) ? 1f : Mathf.Clamp01(num / num2)); float num5 = startedAt + showSeconds; float num6 = num5 - num3; float num7 = ((now < num6) ? 1f : Mathf.Clamp01((num5 - now) / Mathf.Max(0.01f, num3))); return Mathf.Clamp01(Mathf.Min(num4, num7)); } private static float GetFloatingDeltaOffsetX(float startedAt, float now, float showSeconds) { float num = now - startedAt; float num2 = Mathf.Min(0.14f, showSeconds * 0.24f); float num3 = Mathf.Min(0.16f, showSeconds * 0.24f); if (num < num2) { float num4 = Mathf.Clamp01(num / Mathf.Max(0.01f, num2)); float num5 = 1f - Mathf.Pow(1f - num4, 3f); return Mathf.Lerp(8f, 0f, num5); } float num6 = startedAt + showSeconds; float num7 = num6 - num3; if (now > num7) { float num8 = Mathf.Clamp01((now - num7) / Mathf.Max(0.01f, num3)); float num9 = num8 * num8 * (3f - 2f * num8); return Mathf.Lerp(0f, -6f, num9); } return 0f; } } internal enum OverlayRevealTrack { Primary, Cart, Tertiary } internal sealed class OverlayRevealController { private const float MapRevealSeconds = 0.44f; private const float DefaultRevealSeconds = 0.34f; private const float DefaultOffsetY = 10f; private const float CartDelaySeconds = 0.22f; private const float TertiaryDelaySeconds = 0.4f; private float _startedAt = -100000f; internal void Reset() { _startedAt = -100000f; } internal void Begin() { _startedAt = Time.unscaledTime; } internal string Apply(string content, OverlayRevealTrack track) { if (string.IsNullOrEmpty(content)) { return content; } if (_startedAt < 0f) { return content; } if (1 == 0) { } float num = track switch { OverlayRevealTrack.Cart => 0.22f, OverlayRevealTrack.Tertiary => 0.4f, _ => 0f, }; if (1 == 0) { } float num2 = num; float num3 = ((track == OverlayRevealTrack.Primary || track == OverlayRevealTrack.Cart) ? 0.44f : 0.34f); float num4 = Time.unscaledTime - _startedAt - num2; if (num4 <= 0f) { return string.Empty; } if (num4 >= num3) { return content; } float num5 = Mathf.Clamp01(num4 / Mathf.Max(0.01f, num3)); float num6 = 1f - Mathf.Pow(1f - num5, 2.2f); string arg = ToAlphaHex(num5); float num7 = Mathf.Lerp(10f, 0f, num6); return $"{content}"; } private static string ToAlphaHex(float alpha) { return Mathf.Clamp(Mathf.RoundToInt(alpha * 255f), 0, 255).ToString("X2"); } } [Serializable] internal sealed class OverlaySyncSnapshot { public bool Active; public bool ExtractionClosed; public int MapValue; public int CartValue; public int HaulValue; public bool HaulTransitionActive; public int BoxCommon; public int BoxUncommon; public int BoxRare; public int BoxUltraRare; public AnimationIntentSyncPayload MapIntent = new AnimationIntentSyncPayload(); public AnimationIntentSyncPayload CartIntent = new AnimationIntentSyncPayload(); public AnimationIntentSyncPayload BoxesIntent = new AnimationIntentSyncPayload(); public AnimationIntentSyncPayload HaulIntent = new AnimationIntentSyncPayload(); public int Revision; } internal static class OverlaySyncBridge { private const string RoomPropertyKey = "denis.ui.overlay.snapshot"; private const float KeepAlivePublishSeconds = 2.5f; private const float LocalFallbackDelaySeconds = 2.5f; private static float _lastPublishAt = -100000f; private static int _nextRevision = 1; private static string _lastPublishedPayloadSignature = string.Empty; private static string _lastReceivedPayloadSignature = string.Empty; private static float _remoteWaitStartedAt = -100000f; private static bool _fallbackActive; private static int _lastOpenMapValue; private static int _lastOpenCartValue; private static int _lastPublishedHaulValue; private static OverlaySyncSnapshot? _lastLocalSnapshot; private static bool _hasLocalSnapshot; internal static bool IsPassiveModeEnabled() { return DenisUIPlugin.HostSync != null && DenisUIPlugin.HostSync.Value; } internal static bool ShouldUseRemoteSnapshot() { if (!IsPassiveModeEnabled()) { return false; } if (!PhotonNetwork.InRoom) { return false; } return !PhotonNetwork.IsMasterClient; } internal static bool ShouldFallbackToLocal() { if (!ShouldUseRemoteSnapshot()) { return false; } if (_remoteWaitStartedAt < 0f) { _remoteWaitStartedAt = Time.unscaledTime; } if (_fallbackActive) { return true; } float num = Time.unscaledTime - _remoteWaitStartedAt; if (num < 2.5f) { return false; } _fallbackActive = true; DenisUIPerfTelemetry.RecordPassiveFallbackActivation(); return true; } internal static void TickHostPublish(bool mapOpen, bool overlayVisible) { if (IsPassiveModeEnabled() && PhotonNetwork.InRoom && PhotonNetwork.IsMasterClient) { float unscaledTime = Time.unscaledTime; bool flag = unscaledTime - _lastPublishAt >= 2.5f; OverlaySyncSnapshot snapshot = (_lastLocalSnapshot = CaptureLocalSnapshot(mapOpen, overlayVisible)); _hasLocalSnapshot = true; DenisUIPerfTelemetry.RecordOverlayPublishAttempt(flag); PublishSnapshot(snapshot, flag); DenisUIPerfTelemetry.MaybeLogSummary(); } } internal static void UpdateLocalSnapshotCache(bool mapOpen, bool overlayVisible) { if ((!IsPassiveModeEnabled() || !PhotonNetwork.InRoom || !PhotonNetwork.IsMasterClient) && (!ShouldUseRemoteSnapshot() || ShouldFallbackToLocal())) { _lastLocalSnapshot = CaptureLocalSnapshot(mapOpen, overlayVisible); _hasLocalSnapshot = true; } } internal static bool TryGetCachedLocalSnapshot(out OverlaySyncSnapshot snapshot) { if (_hasLocalSnapshot && _lastLocalSnapshot != null) { snapshot = _lastLocalSnapshot; return true; } snapshot = new OverlaySyncSnapshot(); return false; } internal static OverlaySyncSnapshot CaptureLocalSnapshot(bool mapOpen, bool overlayVisible = false) { DenisUIPerfTelemetry.RecordLocalSnapshot(GetLocalSnapshotMode()); ClosedHaulStageState closedHaulStageState = HaulTracker.CaptureClosedStageState(); NativeHaulSnapshot nativeHaulSnapshot = NativeHaulTracker.CaptureSnapshot(); AnimationIntentConsumer.Update(Time.unscaledTime); int num = 0; int num2 = 0; bool flag = nativeHaulSnapshot.Valid && nativeHaulSnapshot.CurrentHaul > 0f; if (!closedHaulStageState.Closed) { MapValueTracker.MapTrackerSnapshot mapTrackerSnapshot = MapValueTracker.CapturePreparedSnapshot(mapOpen, overlayVisible); num = mapTrackerSnapshot.MapValue; num2 = mapTrackerSnapshot.CartValue; if (!flag) { _lastOpenMapValue = num; _lastOpenCartValue = num2; } } CosmeticBoxSnapshot snapshot = CosmeticBoxTracker.GetSnapshot(); AnimationIntentSyncPayload haulIntent = AnimationIntentConsumer.CaptureHaulPayload(); if (closedHaulStageState.Closed && closedHaulStageState.HaulValue > 0 && closedHaulStageState.HaulValue != _lastPublishedHaulValue) { int num3 = Mathf.Max(0, _lastPublishedHaulValue); int haulValue = closedHaulStageState.HaulValue; haulIntent = AnimationIntentSyncPayload.FromIntent(new AnimationIntent(AnimationIntentKind.HaulChanged, num3, haulValue, (haulValue >= num3) ? AnimationDirection.Up : AnimationDirection.Down, (haulValue > num3) ? "settle_gain_sync" : "settle_sync", "host-sync-haul")); } return new OverlaySyncSnapshot { Active = SemiFunc.RunIsLevel(), ExtractionClosed = closedHaulStageState.Closed, MapValue = num, CartValue = num2, HaulValue = closedHaulStageState.HaulValue, HaulTransitionActive = flag, BoxCommon = snapshot.Common, BoxUncommon = snapshot.Uncommon, BoxRare = snapshot.Rare, BoxUltraRare = snapshot.UltraRare, MapIntent = AnimationIntentConsumer.CaptureMapPayload(), CartIntent = AnimationIntentConsumer.CaptureCartPayload(), BoxesIntent = AnimationIntentConsumer.CaptureBoxesPayload(), HaulIntent = haulIntent, Revision = 0 }; } internal static bool TryGetRemoteSnapshot(out OverlaySyncSnapshot snapshot) { snapshot = new OverlaySyncSnapshot(); if (!ShouldUseRemoteSnapshot()) { return false; } try { Room currentRoom = PhotonNetwork.CurrentRoom; if (((currentRoom != null) ? ((RoomInfo)currentRoom).CustomProperties : null) == null) { return false; } if (!((Dictionary)(object)((RoomInfo)currentRoom).CustomProperties).TryGetValue((object)"denis.ui.overlay.snapshot", out object value) || !(value is string text) || string.IsNullOrWhiteSpace(text)) { DenisUIPerfTelemetry.RecordPassiveRemoteSnapshot(success: false); return false; } OverlaySyncSnapshot overlaySyncSnapshot = JsonUtility.FromJson(text); if (overlaySyncSnapshot == null) { DenisUIPerfTelemetry.RecordPassiveRemoteSnapshot(success: false); return false; } if (!overlaySyncSnapshot.Active) { DenisUIPerfTelemetry.RecordPassiveRemoteSnapshot(success: false); return false; } _remoteWaitStartedAt = -100000f; _fallbackActive = false; snapshot = overlaySyncSnapshot; string payloadSignature = GetPayloadSignature(snapshot); _lastReceivedPayloadSignature = payloadSignature; DenisUIPerfTelemetry.RecordPassiveRemoteSnapshot(success: true); DenisUIPerfTelemetry.MaybeLogSummary(); return true; } catch { DenisUIPerfTelemetry.RecordPassiveRemoteSnapshot(success: false); return false; } } internal static void ClearPublishedSnapshot() { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_009a: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Expected O, but got Unknown _lastPublishedPayloadSignature = string.Empty; _lastReceivedPayloadSignature = string.Empty; _lastPublishAt = -100000f; _remoteWaitStartedAt = -100000f; _fallbackActive = false; _lastOpenMapValue = 0; _lastOpenCartValue = 0; _lastPublishedHaulValue = 0; _lastLocalSnapshot = null; _hasLocalSnapshot = false; if (!PhotonNetwork.InRoom || !PhotonNetwork.IsMasterClient) { return; } try { OverlaySyncSnapshot overlaySyncSnapshot = new OverlaySyncSnapshot { Active = false, Revision = _nextRevision++ }; string text = JsonUtility.ToJson((object)overlaySyncSnapshot); Hashtable val = new Hashtable { [(object)"denis.ui.overlay.snapshot"] = text }; Room currentRoom = PhotonNetwork.CurrentRoom; if (currentRoom != null) { currentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null); } } catch { } } private static void PublishSnapshot(OverlaySyncSnapshot snapshot, bool forceRepublish = false) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0059: Expected O, but got Unknown try { string payloadSignature = GetPayloadSignature(snapshot); if (!forceRepublish && string.Equals(payloadSignature, _lastPublishedPayloadSignature, StringComparison.Ordinal)) { DenisUIPerfTelemetry.RecordOverlayPublishDuplicateSkip(); return; } snapshot.Revision = _nextRevision++; string text = JsonUtility.ToJson((object)snapshot); Hashtable val = new Hashtable { [(object)"denis.ui.overlay.snapshot"] = text }; Room currentRoom = PhotonNetwork.CurrentRoom; if (currentRoom != null) { currentRoom.SetCustomProperties(val, (Hashtable)null, (WebFlags)null); } _lastPublishedPayloadSignature = payloadSignature; _lastPublishAt = Time.unscaledTime; _lastPublishedHaulValue = snapshot.HaulValue; DenisUIPerfTelemetry.RecordOverlayPublishSuccess(text.Length); } catch { } } private static DenisUIPerfTelemetry.LocalSnapshotMode GetLocalSnapshotMode() { if (IsPassiveModeEnabled() && PhotonNetwork.InRoom) { if (PhotonNetwork.IsMasterClient) { return DenisUIPerfTelemetry.LocalSnapshotMode.Host; } return DenisUIPerfTelemetry.LocalSnapshotMode.PassiveFallback; } return DenisUIPerfTelemetry.LocalSnapshotMode.Standalone; } private static string GetPayloadSignature(OverlaySyncSnapshot snapshot) { return $"{snapshot.Active}|{snapshot.ExtractionClosed}|{snapshot.HaulTransitionActive}|{snapshot.MapValue}|{snapshot.CartValue}|{snapshot.HaulValue}|{snapshot.BoxCommon}|{snapshot.BoxUncommon}|{snapshot.BoxRare}|{snapshot.BoxUltraRare}|{GetIntentSignature(snapshot.MapIntent)}|{GetIntentSignature(snapshot.CartIntent)}|{GetIntentSignature(snapshot.BoxesIntent)}|{GetIntentSignature(snapshot.HaulIntent)}"; } private static string GetIntentSignature(AnimationIntentSyncPayload? payload) { if (payload == null || !payload.Active) { return "0"; } return $"1:{payload.Kind}:{payload.PreviousValue}:{payload.NextValue}:{payload.Direction}:{payload.AnimationCode}:{payload.ReasonCode}"; } } internal static class RunCurrencyTracker { private static MethodInfo? _statGetRunCurrencyMethod; private static bool _discovered; internal static int ReadBalanceMoney() { DiscoverIfNeeded(); if (_statGetRunCurrencyMethod == null) { return 0; } try { object obj = ResolveInvocationTarget(_statGetRunCurrencyMethod); object obj2 = _statGetRunCurrencyMethod.Invoke(obj, Array.Empty()); if (obj2 == null) { return 0; } int num = Convert.ToInt32(obj2); return Mathf.Max(0, num * 1000); } catch { return 0; } } private static void DiscoverIfNeeded() { if (_discovered) { return; } _discovered = true; try { Assembly assembly = typeof(StatsManager).Assembly; Type[] types = assembly.GetTypes(); foreach (Type type in types) { MethodInfo[] methods = type.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); foreach (MethodInfo methodInfo in methods) { if (string.Equals(methodInfo.Name, "StatGetRunCurrency", StringComparison.OrdinalIgnoreCase)) { _statGetRunCurrencyMethod = methodInfo; return; } } } } catch { } } private static object? ResolveInvocationTarget(MethodInfo method) { if (method.IsStatic) { return null; } Type declaringType = method.DeclaringType; PropertyInfo propertyInfo = AccessTools.Property(declaringType, "instance") ?? AccessTools.Property(declaringType, "Instance"); if (propertyInfo != null) { return propertyInfo.GetValue(null, null); } FieldInfo fieldInfo = AccessTools.Field(declaringType, "instance") ?? AccessTools.Field(declaringType, "Instance"); if (fieldInfo != null) { return fieldInfo.GetValue(null); } return null; } } [HarmonyPatch] internal static class TabOverlayPatches { [HarmonyPatch(typeof(RoundDirector), "Update")] [HarmonyPostfix] private static void RoundDirector_Update_Postfix() { TabOverlay.OnRoundDirectorUpdate(); } [HarmonyPatch(typeof(SemiFunc), "OnSceneSwitch")] [HarmonyPrefix] private static void OnSceneSwitch_Prefix() { TabOverlay.OnSceneSwitch(); } } internal static class TabOverlay { private const float MapValueIntroCountUpSeconds = 1.8f; private const float MapValueDeltaShowSeconds = 3f; private const float MapValueDecreaseCountDownSeconds = 0.22f; private const float MapValueDeltaWhiteSeconds = 0.22f; private const float CartValueScrambleSeconds = 0.38f; private const float CartValueScrambleFrameSeconds = 0.05f; private const float OverlayLineGap = 2f; private const float OverlayTopPadding = 22f; private const float SharedRevealSeconds = 0.34f; private const float MapRevealDelaySeconds = 0.22f; private const float CartRevealDelaySeconds = 0.4f; private const float SharedRevealStartOffsetX = 18f; private const float CloseToHaulFadeOutSeconds = 0.18f; private const float CloseToHaulFadeInSeconds = 0.18f; private const float CloseToHaulSwitchDelaySeconds = 0.14f; private const float HaulLatchedCartDrainSeconds = 0.42f; private static readonly MapValueAnimationController MapValueAnimation = new MapValueAnimationController(1.8f, 3f, 0.22f, 0.22f); private static readonly CartValueAnimationController CartValueAnimation = new CartValueAnimationController(0.38f, 0.05f); private static readonly OverlayRevealController OverlayReveal = new OverlayRevealController(); private static GameObject? _overlayRoot; private static TextMeshProUGUI? _primaryText; private static TextMeshProUGUI? _cartText; private static TextMeshProUGUI? _boxesText; private static string _lastRenderedPrimaryContent = string.Empty; private static string _lastRenderedCartContent = string.Empty; private static string _lastRenderedBoxesContent = string.Empty; private static string _lastOpenPrimaryContent = string.Empty; private static string _lastOpenCartContent = string.Empty; private static string _transitionOutPrimaryContent = string.Empty; private static string _transitionOutCartContent = string.Empty; private static string _transitionInHaulContent = string.Empty; private static float _overlayRevealStartedAt = -100000f; private static float _closeToHaulStartedAt = -100000f; private static bool _wasExtractionClosed; private static float _lastHandledHaulIntentAt = -100000f; private static bool _haulPresentationLatched; private static float _haulCartDrainStartedAt = -100000f; private static int _haulCartDrainStartValue; private static int _lastObservedHaulValue = -1; private static int _haulAnimationFromValue = -1; private static int _haulAnimationToValue = -1; private static float _haulAnimationStartedAt = -100000f; private static bool _updateErrorLogged; private static bool _runStartIntroTriggered; internal static void OnRoundDirectorUpdate() { if (!SemiFunc.RunIsLevel()) { return; } try { if (EnsureOverlayReady()) { bool mapOpen = IsMapOpen(); bool flag = ShouldShowOverlay(mapOpen); OverlaySyncBridge.TickHostPublish(mapOpen, flag); OverlaySyncBridge.UpdateLocalSnapshotCache(mapOpen, flag); CheckRunStartIntroTrigger(); OverlaySyncSnapshot overlaySyncSnapshot = CaptureDisplaySnapshot(mapOpen); bool flag2 = flag || ShouldShowBoxesOnly(overlaySyncSnapshot); ApplyOverlayVisibility(flag2); if (flag2) { UpdateContent(mapOpen, overlaySyncSnapshot, OverlaySyncBridge.ShouldUseRemoteSnapshot() && overlaySyncSnapshot != null && !OverlaySyncBridge.ShouldFallbackToLocal()); } } } catch (Exception arg) { if (!_updateErrorLogged) { _updateErrorLogged = true; DenisUIPlugin.Logger.LogWarning((object)$"TabOverlay update failed: {arg}"); } } } internal static void OnSceneSwitch() { OverlaySyncBridge.ClearPublishedSnapshot(); if ((Object)(object)_overlayRoot != (Object)null) { Object.Destroy((Object)(object)_overlayRoot); _overlayRoot = null; _primaryText = null; _cartText = null; _boxesText = null; _lastRenderedPrimaryContent = string.Empty; _lastRenderedCartContent = string.Empty; _lastRenderedBoxesContent = string.Empty; _lastOpenPrimaryContent = string.Empty; _lastOpenCartContent = string.Empty; _transitionOutPrimaryContent = string.Empty; _transitionOutCartContent = string.Empty; _transitionInHaulContent = string.Empty; } MapValueAnimation.Reset(); CartValueAnimation.Reset(); OverlayReveal.Reset(); AnimationIntentRecorder.Reset(); AnimationIntentConsumer.Reset(); HaulTracker.Reset(); _runStartIntroTriggered = false; _overlayRevealStartedAt = -100000f; _closeToHaulStartedAt = -100000f; _lastHandledHaulIntentAt = -100000f; _wasExtractionClosed = false; _haulPresentationLatched = false; _haulCartDrainStartedAt = -100000f; _haulCartDrainStartValue = 0; _lastObservedHaulValue = -1; _haulAnimationFromValue = -1; _haulAnimationToValue = -1; _haulAnimationStartedAt = -100000f; } private static void CreateOverlay() { //IL_0059: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown //IL_009c: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0120: Unknown result type (might be due to invalid IL or missing references) GameObject val = GameObject.Find("Game Hud"); GameObject val2 = GameObject.Find("Tax Haul"); if (!((Object)(object)val == (Object)null) && !((Object)(object)val2 == (Object)null)) { TMP_FontAsset font = val2.GetComponent().font; _overlayRoot = new GameObject("Denis UI Overlay", new Type[1] { typeof(RectTransform) }); _overlayRoot.SetActive(false); RectTransform component = _overlayRoot.GetComponent(); _overlayRoot.transform.SetParent(val.transform, false); component.pivot = new Vector2(1f, 1f); component.anchoredPosition = new Vector2(1f, -1f); component.anchorMin = new Vector2(0f, 0f); component.anchorMax = new Vector2(1f, 0f); component.sizeDelta = new Vector2(0f, 0f); component.offsetMax = new Vector2(0f, 315f); component.offsetMin = new Vector2(400f, 145f); _primaryText = CreateOverlayText("Primary", font); _cartText = CreateOverlayText("Cart", font); _boxesText = CreateOverlayText("Boxes", font); } } private static TextMeshProUGUI CreateOverlayText(string name, TMP_FontAsset font) { //IL_000c: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Expected O, but got Unknown //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_00d2: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) //IL_00f4: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Denis UI " + name); val.transform.SetParent(_overlayRoot.transform, false); TextMeshProUGUI val2 = val.AddComponent(); ((TMP_Text)val2).font = font; ((Graphic)val2).color = new Color(0.79f, 0.91f, 0.9f, 1f); ((TMP_Text)val2).fontSize = 18f; ((TMP_Text)val2).enableWordWrapping = false; ((TMP_Text)val2).alignment = (TextAlignmentOptions)260; ((TMP_Text)val2).horizontalAlignment = (HorizontalAlignmentOptions)4; ((TMP_Text)val2).verticalAlignment = (VerticalAlignmentOptions)256; ((TMP_Text)val2).richText = true; RectTransform rectTransform = ((TMP_Text)val2).rectTransform; rectTransform.anchorMin = new Vector2(0f, 1f); rectTransform.anchorMax = new Vector2(1f, 1f); rectTransform.pivot = new Vector2(1f, 1f); rectTransform.anchoredPosition = Vector2.zero; rectTransform.sizeDelta = new Vector2(0f, 26f); return val2; } private static bool EnsureOverlayReady() { if ((Object)(object)_overlayRoot != (Object)null) { return true; } _overlayRoot = null; _primaryText = null; _cartText = null; _boxesText = null; CreateOverlay(); return (Object)(object)_overlayRoot != (Object)null; } private static bool IsMapOpen() { bool flag = false; if ((Object)(object)MapToolController.instance != (Object)null) { flag = Traverse.Create((object)MapToolController.instance).Field("mapToggled").GetValue(); } bool flag2 = SemiFunc.InputHold((InputKey)8); return flag2 || flag; } private static bool ShouldShowOverlay(bool mapOpen) { if (!_runStartIntroTriggered) { return false; } DenisUIPlugin.OverlayVisibilityMode value = DenisUIPlugin.OverlayVisibility.Value; if (1 == 0) { } bool result = value switch { DenisUIPlugin.OverlayVisibilityMode.Off => false, DenisUIPlugin.OverlayVisibilityMode.ShowWithMap => mapOpen, _ => true, }; if (1 == 0) { } return result; } private static void ApplyOverlayVisibility(bool shouldShow) { if (!((Object)(object)_overlayRoot == (Object)null)) { bool activeSelf = _overlayRoot.activeSelf; if (activeSelf != shouldShow) { _overlayRoot.SetActive(shouldShow); } if (!activeSelf && shouldShow) { OverlayReveal.Begin(); _overlayRevealStartedAt = Time.unscaledTime; } } } private static void CheckRunStartIntroTrigger() { if (!_runStartIntroTriggered) { RoundDirector instance = RoundDirector.instance; if (!((Object)(object)instance == (Object)null) && instance.extractionPointActive && !((Object)(object)instance.extractionPointCurrent == (Object)null)) { _runStartIntroTriggered = true; OverlayReveal.Begin(); _overlayRevealStartedAt = Time.unscaledTime; MapValueAnimation.TriggerIntro(); } } } private static void UpdateContent(bool mapOpen, OverlaySyncSnapshot? snapshot = null, bool usingRemoteSnapshot = false) { if ((Object)(object)_primaryText == (Object)null || (Object)(object)_cartText == (Object)null || (Object)(object)_boxesText == (Object)null) { return; } if (snapshot == null) { snapshot = CaptureDisplaySnapshot(mapOpen); } if (usingRemoteSnapshot && snapshot != null) { AnimationIntentConsumer.UpdateFromRemoteSnapshot(snapshot, Time.unscaledTime); } else { AnimationIntentConsumer.Update(Time.unscaledTime); } if (snapshot != null && snapshot.ExtractionClosed && snapshot.HaulValue > 0) { ObserveLocalHaulAnimation(snapshot.HaulValue, Time.unscaledTime); } ConsumedAnimationIntentState haulState = AnimationIntentConsumer.GetHaulState(); if (!_haulPresentationLatched && snapshot != null && snapshot.ExtractionClosed && snapshot.HaulValue > 0 && (usingRemoteSnapshot || !_wasExtractionClosed || (haulState.IsActive && haulState.Intent.Kind == AnimationIntentKind.HaulChanged))) { _haulPresentationLatched = true; _haulCartDrainStartedAt = Time.unscaledTime; _haulCartDrainStartValue = Mathf.Max(snapshot.CartValue, 0); } if (_haulPresentationLatched && snapshot != null && snapshot.HaulValue > 0) { string text = RenderLatchedHaulContent(snapshot); string text2 = RenderLatchedCartDrainContent(); ApplyTextIfChanged(_primaryText, text, ref _lastRenderedPrimaryContent); ApplyTextIfChanged(_cartText, text2, ref _lastRenderedCartContent); ApplyTextIfChanged(_boxesText, string.Empty, ref _lastRenderedBoxesContent); ApplyLayoutAndVisibility(text, text2, string.Empty); return; } string primaryContent = RenderPrimaryContent(snapshot); string cartContent = RenderCartContent(snapshot); string text3 = RenderBoxesContent(snapshot); FreezeOpenValuesDuringHaulTransition(snapshot, ref primaryContent, ref cartContent); HandleExtractionClosedTransition(snapshot, haulState, ref primaryContent, ref cartContent); if (snapshot != null && !snapshot.ExtractionClosed) { _lastOpenPrimaryContent = primaryContent; _lastOpenCartContent = cartContent; } ApplyTextIfChanged(_primaryText, primaryContent, ref _lastRenderedPrimaryContent); ApplyTextIfChanged(_cartText, cartContent, ref _lastRenderedCartContent); ApplyTextIfChanged(_boxesText, text3, ref _lastRenderedBoxesContent); ApplyLayoutAndVisibility(primaryContent, cartContent, text3); } private static void HandleExtractionClosedTransition(OverlaySyncSnapshot? snapshot, ConsumedAnimationIntentState haulIntentState, ref string primaryContent, ref string cartContent) { bool flag = snapshot?.ExtractionClosed ?? false; bool flag2 = haulIntentState.IsActive && haulIntentState.Intent.Kind == AnimationIntentKind.HaulChanged && haulIntentState.RecordedAt > _lastHandledHaulIntentAt; if (flag && (flag2 || !_wasExtractionClosed)) { _closeToHaulStartedAt = (flag2 ? haulIntentState.RecordedAt : Time.unscaledTime); _lastHandledHaulIntentAt = _closeToHaulStartedAt; _transitionOutPrimaryContent = _lastOpenPrimaryContent; _transitionOutCartContent = _lastOpenCartContent; _transitionInHaulContent = primaryContent; } _wasExtractionClosed = flag; if (flag && !(_closeToHaulStartedAt < 0f)) { float num = Time.unscaledTime - _closeToHaulStartedAt; if (num < 0.14f) { primaryContent = _transitionOutPrimaryContent; cartContent = _transitionOutCartContent; } else { primaryContent = _transitionInHaulContent; cartContent = string.Empty; } } } private static void FreezeOpenValuesDuringHaulTransition(OverlaySyncSnapshot? snapshot, ref string primaryContent, ref string cartContent) { if (snapshot != null && snapshot.HaulTransitionActive && !snapshot.ExtractionClosed) { primaryContent = _lastOpenPrimaryContent; cartContent = _lastOpenCartContent; } } private static void ApplyTextIfChanged(TextMeshProUGUI text, string content, ref string lastContent) { if (!string.Equals(content, lastContent, StringComparison.Ordinal)) { lastContent = content; ((TMP_Text)text).text = content; } } private static void ApplyLayoutAndVisibility(string primaryContent, string cartContent, string boxesContent) { if ((Object)(object)_primaryText == (Object)null || (Object)(object)_cartText == (Object)null || (Object)(object)_boxesText == (Object)null) { return; } bool flag = !string.IsNullOrEmpty(primaryContent); bool flag2 = !string.IsNullOrEmpty(cartContent); bool flag3 = !string.IsNullOrEmpty(boxesContent); bool flag4 = _runStartIntroTriggered && DenisUIPlugin.ShowCartValue.Value; bool flag5 = _wasExtractionClosed && _closeToHaulStartedAt >= 0f; ((Component)_primaryText).gameObject.SetActive(flag); ((Component)_cartText).gameObject.SetActive(flag2); ((Component)_boxesText).gameObject.SetActive(flag3); float num = -22f; if (flag) { if (flag5) { ApplyCloseToHaulPrimaryLine(_primaryText, num); } else { ApplyRevealLine(_primaryText, num, 0.22f); } num -= ((TMP_Text)_primaryText).preferredHeight + 2f; } if (flag2) { if (flag5) { ApplyCloseToHaulCartLine(_cartText, num); } else { ApplyRevealLine(_cartText, num, 0.4f); } num -= GetLineHeight(_cartText) + 2f; } else if (flag4) { num -= GetLineHeight(_cartText) + 2f; } if (flag3) { SetLineY(_boxesText, num); } } private static float GetLineHeight(TextMeshProUGUI text) { //IL_000d: Unknown result type (might be due to invalid IL or missing references) return Mathf.Max(((TMP_Text)text).preferredHeight, ((TMP_Text)text).rectTransform.sizeDelta.y); } private static void SetLineY(TextMeshProUGUI text, float y) { SetLinePosition(text, 0f, y); SetTextAlpha(text, 1f); } private static void ApplyRevealLine(TextMeshProUGUI text, float y, float delay) { float revealProgress = GetRevealProgress(delay); float num = 1f - Mathf.Pow(1f - revealProgress, 2.2f); float x = Mathf.Lerp(18f, 0f, num); SetLinePosition(text, x, y); SetTextAlpha(text, revealProgress); } private static void ApplyCloseToHaulPrimaryLine(TextMeshProUGUI text, float y) { float num = Time.unscaledTime - _closeToHaulStartedAt; if (num < 0.14f) { float num2 = Mathf.Clamp01(num / Mathf.Max(0.01f, 0.18f)); float num3 = 1f - Mathf.Pow(1f - num2, 2f); SetLinePosition(text, Mathf.Lerp(0f, -10f, num3), y); SetTextAlpha(text, 1f - num2); } else { float num4 = num - 0.14f; float num5 = Mathf.Clamp01(num4 / Mathf.Max(0.01f, 0.18f)); float num6 = 1f - Mathf.Pow(1f - num5, 2.2f); SetLinePosition(text, Mathf.Lerp(10f, 0f, num6), y); SetTextAlpha(text, num5); } } private static void ApplyCloseToHaulCartLine(TextMeshProUGUI text, float y) { float num = Time.unscaledTime - _closeToHaulStartedAt; float num2 = Mathf.Clamp01(num / Mathf.Max(0.01f, 0.18f)); float num3 = 1f - Mathf.Pow(1f - num2, 2f); SetLinePosition(text, Mathf.Lerp(0f, -10f, num3), y); SetTextAlpha(text, 1f - num2); } private static float GetRevealProgress(float delay) { if (!_runStartIntroTriggered) { return 0f; } if (_overlayRevealStartedAt < 0f) { return 1f; } float num = Time.unscaledTime - _overlayRevealStartedAt - delay; if (num <= 0f) { return 0f; } if (num >= 0.34f) { return 1f; } return Mathf.Clamp01(num / Mathf.Max(0.01f, 0.34f)); } private static void SetLinePosition(TextMeshProUGUI text, float x, float y) { //IL_0009: Unknown result type (might be due to invalid IL or missing references) ((TMP_Text)text).rectTransform.anchoredPosition = new Vector2(x, y); } private static void SetTextAlpha(TextMeshProUGUI text, float alpha) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_0016: Unknown result type (might be due to invalid IL or missing references) Color color = ((Graphic)text).color; color.a = Mathf.Clamp01(alpha); ((Graphic)text).color = color; } private static OverlaySyncSnapshot? CaptureDisplaySnapshot(bool mapOpen) { if (OverlaySyncBridge.ShouldUseRemoteSnapshot()) { if (OverlaySyncBridge.TryGetRemoteSnapshot(out OverlaySyncSnapshot snapshot)) { return snapshot; } if (OverlaySyncBridge.ShouldFallbackToLocal()) { if (OverlaySyncBridge.TryGetCachedLocalSnapshot(out OverlaySyncSnapshot snapshot2)) { return snapshot2; } OverlaySyncBridge.UpdateLocalSnapshotCache(mapOpen, overlayVisible: true); return OverlaySyncBridge.TryGetCachedLocalSnapshot(out snapshot2) ? snapshot2 : null; } return null; } if (OverlaySyncBridge.TryGetCachedLocalSnapshot(out OverlaySyncSnapshot snapshot3)) { return snapshot3; } OverlaySyncBridge.UpdateLocalSnapshotCache(mapOpen, overlayVisible: true); return OverlaySyncBridge.TryGetCachedLocalSnapshot(out snapshot3) ? snapshot3 : null; } private static bool ShouldShowBoxesOnly(OverlaySyncSnapshot? snapshot) { if (snapshot == null || snapshot.ExtractionClosed || !DenisUIPlugin.ShowLootboxes.Value) { return false; } return snapshot.BoxCommon > 0 || snapshot.BoxUncommon > 0 || snapshot.BoxRare > 0 || snapshot.BoxUltraRare > 0; } private static string RenderPrimaryContent(OverlaySyncSnapshot? snapshot) { if (snapshot == null || !_runStartIntroTriggered) { return string.Empty; } StringBuilder stringBuilder = new StringBuilder(); AppendPrimaryLines(stringBuilder, snapshot, includeCart: false, IsMapOpen()); return stringBuilder.ToString().TrimEnd('\r', '\n'); } private static string RenderLatchedHaulContent(OverlaySyncSnapshot snapshot) { float unscaledTime = Time.unscaledTime; ConsumedAnimationIntentState preferredHaulAnimationState = GetPreferredHaulAnimationState(unscaledTime); OverlaySyncSnapshot snapshot2 = new OverlaySyncSnapshot { Active = snapshot.Active, ExtractionClosed = true, HaulValue = snapshot.HaulValue }; OverlayLinePresentation presentation = OverlayPresentationPolicy.BuildHaulPresentation(snapshot2, preferredHaulAnimationState, unscaledTime); return BuildPresentationLine(presentation); } private static string RenderLatchedCartDrainContent() { if (!DenisUIPlugin.ShowCartValue.Value || _haulCartDrainStartValue <= 0 || _haulCartDrainStartedAt < 0f) { return string.Empty; } float num = Time.unscaledTime - _haulCartDrainStartedAt; if (num >= 0.42f) { return string.Empty; } float num2 = Mathf.Clamp01(num / Mathf.Max(0.01f, 0.42f)); float num3 = 1f - Mathf.Pow(1f - num2, 3f); int num4 = Mathf.RoundToInt(Mathf.Lerp((float)_haulCartDrainStartValue, 0f, num3)); num4 = Mathf.Clamp(num4, 0, _haulCartDrainStartValue); OverlayLinePresentation presentation = new OverlayLinePresentation(OverlayLineMode.Money, OverlayRevealTrack.Cart, "C.A.R.T.", FormatMoney(num4), "", forceWhiteMoney: false, DenisUIPlugin.GetCartDollarColorHex(), DenisUIPlugin.GetCartValueColorHex()); return BuildPresentationLine(presentation); } private static string RenderCartContent(OverlaySyncSnapshot? snapshot) { if (snapshot == null || !_runStartIntroTriggered) { return string.Empty; } if (!DenisUIPlugin.ShowCartValue.Value || snapshot.ExtractionClosed) { return string.Empty; } float unscaledTime = Time.unscaledTime; CartAnimationDisplay display = CartValueAnimation.GetDisplay(snapshot.CartValue, unscaledTime, (int value) => FormatMoney(value)); OverlayLinePresentation presentation = OverlayPresentationPolicy.BuildCartPresentation(display); return BuildPresentationLine(presentation, IsMapOpen()); } private static string RenderBoxesContent(OverlaySyncSnapshot? snapshot) { if (snapshot == null) { return string.Empty; } if (snapshot.ExtractionClosed || !DenisUIPlugin.ShowLootboxes.Value) { return string.Empty; } ConsumedAnimationIntentState boxesState = AnimationIntentConsumer.GetBoxesState(); OverlayLinePresentation overlayLinePresentation = OverlayPresentationPolicy.BuildBoxesPresentation(new CosmeticBoxSnapshot(snapshot.BoxCommon, snapshot.BoxUncommon, snapshot.BoxRare, snapshot.BoxUltraRare), boxesState); return overlayLinePresentation.HasContent ? overlayLinePresentation.ValueText : string.Empty; } private static void AppendPrimaryLines(StringBuilder sb, OverlaySyncSnapshot snapshot, bool includeCart, bool mapOpen) { bool value2 = DenisUIPlugin.ShowMapValue.Value; bool value3 = DenisUIPlugin.ShowCartValue.Value; if (snapshot.ExtractionClosed) { float unscaledTime = Time.unscaledTime; ConsumedAnimationIntentState preferredHaulAnimationState = GetPreferredHaulAnimationState(unscaledTime); AppendPresentationLine(sb, OverlayPresentationPolicy.BuildHaulPresentation(snapshot, preferredHaulAnimationState, unscaledTime), mapOpen: false); return; } float unscaledTime2 = Time.unscaledTime; if (value2) { MapAnimationDisplay display = MapValueAnimation.GetDisplay(snapshot.MapValue, unscaledTime2, (int value) => FormatMoney(value)); AppendPresentationLine(sb, OverlayPresentationPolicy.BuildMapPresentation(display), mapOpen); } if (includeCart && value3) { CartAnimationDisplay display2 = CartValueAnimation.GetDisplay(snapshot.CartValue, unscaledTime2, (int value) => FormatMoney(value)); AppendPresentationLine(sb, OverlayPresentationPolicy.BuildCartPresentation(display2), mapOpen); } } private static void AppendPresentationLine(StringBuilder sb, OverlayLinePresentation presentation, bool mapOpen) { string value = BuildPresentationLine(presentation, mapOpen); if (!string.IsNullOrEmpty(value)) { sb.AppendLine(value); } } private static string BuildPresentationLine(OverlayLinePresentation presentation, bool mapOpen = false) { if (!presentation.HasContent) { return string.Empty; } OverlayLineMode mode = presentation.Mode; if (1 == 0) { } string text = mode switch { OverlayLineMode.Money => FormatMoneyTextLine(presentation.Label, presentation.ValueText, presentation.ForceWhiteMoney, presentation.DollarColorOverride, presentation.ValueColorOverride), OverlayLineMode.AlertMoney => FormatAlertMoneyTextLine(presentation.Label, presentation.ValueText, presentation.AlertColorHex ?? "#ffffff"), OverlayLineMode.CompositeMoney => FormatCompositeMoneyLine(presentation.PrefixRichText, presentation.Label, presentation.ValueText, presentation.ForceWhiteMoney, presentation.DollarColorOverride, presentation.ValueColorOverride), OverlayLineMode.RawText => presentation.ValueText, _ => string.Empty, }; if (1 == 0) { } string text2 = text; if (string.IsNullOrEmpty(text2)) { return string.Empty; } return (presentation.Track == OverlayRevealTrack.Tertiary) ? OverlayReveal.Apply(text2, presentation.Track) : text2; } private static void ObserveLocalHaulAnimation(int haulValue, float now) { haulValue = Mathf.Max(0, haulValue); if (_lastObservedHaulValue < 0) { _haulAnimationFromValue = 0; _haulAnimationToValue = haulValue; _haulAnimationStartedAt = now; _lastObservedHaulValue = haulValue; } else if (haulValue != _lastObservedHaulValue) { _haulAnimationFromValue = _lastObservedHaulValue; _haulAnimationToValue = haulValue; _haulAnimationStartedAt = now; _lastObservedHaulValue = haulValue; } } private static ConsumedAnimationIntentState GetPreferredHaulAnimationState(float now) { if (_haulAnimationStartedAt >= 0f && _haulAnimationFromValue >= 0 && _haulAnimationToValue > _haulAnimationFromValue && now - _haulAnimationStartedAt <= 0.85f) { AnimationIntent intent = new AnimationIntent(AnimationIntentKind.HaulChanged, _haulAnimationFromValue, _haulAnimationToValue, AnimationDirection.Up, "haul_local_delta", "local-haul-delta"); return new ConsumedAnimationIntentState(isActive: true, intent, _haulAnimationStartedAt); } return AnimationIntentConsumer.GetHaulState(); } private static string FormatMoneyLine(string label, float value, bool compactValue = false, bool forceWhiteMoney = false, string? dollarColorOverride = null, string? valueColorOverride = null) { return FormatMoneyTextLine(label, FormatMoney(value, compactValue), forceWhiteMoney, dollarColorOverride, valueColorOverride); } private static string FormatAlertMoneyLine(string label, float value, string alertColorHex) { return FormatAlertMoneyTextLine(label, FormatMoney(value), alertColorHex); } private static string FormatMoneyTextLine(string label, string valueText, bool forceWhiteMoney = false, string? dollarColorOverride = null, string? valueColorOverride = null) { string labelColorHex = DenisUIPlugin.GetLabelColorHex(); string text = (forceWhiteMoney ? "#ffffff" : (dollarColorOverride ?? DenisUIPlugin.GetDollarColorHex())); string text2 = (forceWhiteMoney ? "#ffffff" : (valueColorOverride ?? DenisUIPlugin.GetValueColorHex())); return "" + label + ": $" + valueText + ""; } private static string FormatCompositeMoneyLine(string prefixRichText, string label, string baseValueText, bool forceWhiteMoney = false, string? dollarColorOverride = null, string? valueColorOverride = null) { string labelColorHex = DenisUIPlugin.GetLabelColorHex(); string text = (forceWhiteMoney ? "#ffffff" : (dollarColorOverride ?? DenisUIPlugin.GetDollarColorHex())); string text2 = (forceWhiteMoney ? "#ffffff" : (valueColorOverride ?? DenisUIPlugin.GetValueColorHex())); return prefixRichText + " " + label + ": $" + baseValueText + ""; } private static string FormatAlertMoneyTextLine(string label, string valueText, string alertColorHex) { string labelColorHex = DenisUIPlugin.GetLabelColorHex(); return "" + label + ": $" + valueText + ""; } private static string FormatMoney(float value, bool compact = false) { 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"); } } }