Decompiled source of DeathHeadHopperFix v0.2.4

plugins\DeathHeadHopperFix.dll

Decompiled a month ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using DeathHeadHopper;
using DeathHeadHopper.Abilities;
using DeathHeadHopper.Abilities.Charge;
using DeathHeadHopper.DeathHead;
using DeathHeadHopper.DeathHead.Handlers;
using DeathHeadHopper.Items;
using DeathHeadHopper.Managers;
using DeathHeadHopper.UI;
using DeathHeadHopperFix.Modules.Battery;
using DeathHeadHopperFix.Modules.Config;
using DeathHeadHopperFix.Modules.Gameplay.Core.Abilities;
using DeathHeadHopperFix.Modules.Gameplay.Core.Audio;
using DeathHeadHopperFix.Modules.Gameplay.Core.Bootstrap;
using DeathHeadHopperFix.Modules.Gameplay.Core.Input;
using DeathHeadHopperFix.Modules.Gameplay.Core.Interop;
using DeathHeadHopperFix.Modules.Gameplay.Core.Runtime;
using DeathHeadHopperFix.Modules.Gameplay.Stun;
using DeathHeadHopperFix.Modules.Stamina;
using DeathHeadHopperFix.Modules.Utilities;
using ExitGames.Client.Photon;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Photon.Pun;
using Photon.Realtime;
using REPOLib.Modules;
using TMPro;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.SceneManagement;
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(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

		public NullableAttribute(byte P_0)
		{
			NullableFlags = new byte[1] { P_0 };
		}

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableContextAttribute : Attribute
	{
		public readonly byte Flag;

		public NullableContextAttribute(byte P_0)
		{
			Flag = P_0;
		}
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace DeathHeadHopperFix
{
	[BepInPlugin("AdrenSnyder.DeathHeadHopperFix", "Death Head Hopper - Fix", "0.2.4")]
	public sealed class Plugin : BaseUnityPlugin
	{
		private const string TargetAssemblyName = "DeathHeadHopper";

		private Harmony? _harmony;

		private bool _patched;

		private bool _patchAttempted;

		private Assembly? _targetAssembly;

		private static ManualLogSource? _log;

		private void Awake()
		{
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Expected O, but got Unknown
			_log = ((BaseUnityPlugin)this).Logger;
			ConfigManager.Initialize(((BaseUnityPlugin)this).Config);
			WarnUnsafeDebugFlagsInRelease();
			_harmony = new Harmony("AdrenSnyder.DeathHeadHopperFix");
			_harmony.PatchAll(typeof(Plugin).Assembly);
			DHHApiGuardModule.DetectGameApiChanges();
			ApplyEarlyPatches();
			AppDomain.CurrentDomain.AssemblyLoad += OnAssemblyLoad;
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly asm in assemblies)
			{
				TryPatchIfTargetAssembly(asm);
			}
		}

		private void OnDestroy()
		{
			AppDomain.CurrentDomain.AssemblyLoad -= OnAssemblyLoad;
		}

		private void OnAssemblyLoad(object sender, AssemblyLoadEventArgs args)
		{
			TryPatchIfTargetAssembly(args.LoadedAssembly);
		}

		private void ApplyEarlyPatches()
		{
			if (_harmony != null)
			{
				StatsModule.ApplyHooks(_harmony);
				ItemUpgradeModule.Apply(_harmony);
			}
		}

		private void TryPatchIfTargetAssembly(Assembly asm)
		{
			if (_patched || _patchAttempted || asm == null)
			{
				return;
			}
			string name = asm.GetName().Name;
			if (!string.Equals(name, "DeathHeadHopper", StringComparison.OrdinalIgnoreCase))
			{
				return;
			}
			_patchAttempted = true;
			try
			{
				ManualLogSource? log = _log;
				if (log != null)
				{
					log.LogInfo((object)"Detected DeathHeadHopper assembly load. Applying patches...");
				}
				Harmony harmony = _harmony;
				if (harmony == null)
				{
					throw new InvalidOperationException("Harmony instance is null.");
				}
				DHHStatsBootstrapModule.Apply(harmony, asm, _log);
				PrefabModule.Apply(harmony, asm, _log);
				AudioModule.Apply(harmony, asm, _log);
				DHHShopVanillaPoolModule.Apply(harmony, asm, _log);
				_targetAssembly = asm;
				DHHApiGuardModule.Apply(harmony, asm);
				BatteryJumpPatchModule.Apply(harmony, asm);
				JumpForceModule.Apply(harmony, asm, _log);
				ChargeAbilityTuningModule.Apply(harmony, asm);
				ChargeHoldReleaseModule.Apply(harmony, asm, _log);
				InputModule.Apply(harmony, asm, _log);
				AbilityModule.ApplyAbilitySpotLabelOverlay(harmony, asm);
				AbilityModule.ApplyAbilityManagerHooks(harmony, asm);
				_patched = true;
				ManualLogSource? log2 = _log;
				if (log2 != null)
				{
					log2.LogInfo((object)"Patches applied successfully.");
				}
			}
			catch (Exception ex)
			{
				ManualLogSource? log3 = _log;
				if (log3 != null)
				{
					log3.LogError((object)ex);
				}
			}
		}

		private void WarnUnsafeDebugFlagsInRelease()
		{
			if (!Debug.isDebugBuild && (InternalDebugFlags.DisableBatteryModule || InternalDebugFlags.DisableAbilityPatches || InternalDebugFlags.DisableSpectateChecks))
			{
				ManualLogSource? log = _log;
				if (log != null)
				{
					log.LogWarning((object)("[DebugSafety] Internal debug bypass flags are enabled in a non-debug build. " + $"DisableBatteryModule={InternalDebugFlags.DisableBatteryModule}, " + $"DisableAbilityPatches={InternalDebugFlags.DisableAbilityPatches}, " + $"DisableSpectateChecks={InternalDebugFlags.DisableSpectateChecks}"));
				}
			}
		}
	}
}
namespace DeathHeadHopperFix.Modules.Utilities
{
	internal static class LastChanceInteropBridge
	{
		private const BindingFlags StaticAny = BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic;

		private static Type? s_timerControllerType;

		private static Type? s_spectateHelperType;

		private static Type? s_featureFlagsType;

		private static Type? s_imageAssetLoaderType;

		private static PropertyInfo? s_isActiveProperty;

		private static PropertyInfo? s_directionUiVisibleProperty;

		private static MethodInfo? s_isDirectionEnergySufficientMethod;

		private static MethodInfo? s_getDirectionPenaltyPreviewMethod;

		private static MethodInfo? s_getDirectionEnergyDebugSnapshotMethod;

		private static MethodInfo? s_isPlayerSurrenderedForDataMethod;

		private static MethodInfo? s_allPlayersDisabledMethod;

		private static MethodInfo? s_resetForceStateMethod;

		private static MethodInfo? s_shouldForceLocalDeathHeadSpectateMethod;

		private static MethodInfo? s_ensureSpectatePlayerLocalMethod;

		private static MethodInfo? s_forceDeathHeadSpectateIfPossibleMethod;

		private static MethodInfo? s_debugLogStateMethod;

		private static MethodInfo? s_isManualSwitchInputDownMethod;

		private static FieldInfo? s_lastChanceModeField;

		private static FieldInfo? s_spectateDeadPlayersField;

		private static FieldInfo? s_spectateDeadPlayersModeField;

		private static FieldInfo? s_lastChanceIndicatorsField;

		private static MethodInfo? s_tryLoadSpriteMethod;

		internal static bool IsLastChanceActive()
		{
			return TryGetBoolProperty(ref s_isActiveProperty, "DeathHeadHopperFix.Modules.Gameplay.LastChance.Runtime.LastChanceTimerController", "IsActive");
		}

		internal static bool IsDirectionIndicatorUiVisible()
		{
			return TryGetBoolProperty(ref s_directionUiVisibleProperty, "DeathHeadHopperFix.Modules.Gameplay.LastChance.Runtime.LastChanceTimerController", "IsDirectionIndicatorUiVisible");
		}

		internal static bool IsDirectionIndicatorEnergySufficientPreview()
		{
			ResolveMembers();
			if (s_isDirectionEnergySufficientMethod == null)
			{
				return false;
			}
			return (s_isDirectionEnergySufficientMethod.Invoke(null, null) as bool?).GetValueOrDefault();
		}

		internal static float GetDirectionIndicatorPenaltySecondsPreview()
		{
			ResolveMembers();
			if (s_getDirectionPenaltyPreviewMethod == null)
			{
				return 0f;
			}
			return (s_getDirectionPenaltyPreviewMethod.Invoke(null, null) as float?).GetValueOrDefault();
		}

		internal static void GetDirectionIndicatorEnergyDebugSnapshot(out bool visible, out float timerRemaining, out float penaltyPreview, out bool hasEnoughEnergy)
		{
			visible = false;
			timerRemaining = 0f;
			penaltyPreview = 0f;
			hasEnoughEnergy = false;
			ResolveMembers();
			if (!(s_getDirectionEnergyDebugSnapshotMethod == null))
			{
				object[] array = new object[4] { false, 0f, 0f, false };
				s_getDirectionEnergyDebugSnapshotMethod.Invoke(null, array);
				visible = (array[0] as bool?).GetValueOrDefault();
				timerRemaining = (array[1] as float?).GetValueOrDefault();
				penaltyPreview = (array[2] as float?).GetValueOrDefault();
				hasEnoughEnergy = (array[3] as bool?).GetValueOrDefault();
			}
		}

		internal static bool IsPlayerSurrenderedForData(PlayerAvatar? player)
		{
			ResolveMembers();
			if (s_isPlayerSurrenderedForDataMethod == null)
			{
				return false;
			}
			return (s_isPlayerSurrenderedForDataMethod.Invoke(null, new object[1] { player }) as bool?).GetValueOrDefault();
		}

		internal static bool AllPlayersDisabled()
		{
			ResolveMembers();
			if (s_allPlayersDisabledMethod == null)
			{
				return false;
			}
			return (s_allPlayersDisabledMethod.Invoke(null, null) as bool?).GetValueOrDefault();
		}

		internal static void ResetSpectateForceState()
		{
			ResolveMembers();
			s_resetForceStateMethod?.Invoke(null, null);
		}

		internal static bool ShouldForceLocalDeathHeadSpectate()
		{
			ResolveMembers();
			if (s_shouldForceLocalDeathHeadSpectateMethod == null)
			{
				return false;
			}
			return (s_shouldForceLocalDeathHeadSpectateMethod.Invoke(null, null) as bool?).GetValueOrDefault();
		}

		internal static void EnsureSpectatePlayerLocal(SpectateCamera? spectate)
		{
			ResolveMembers();
			s_ensureSpectatePlayerLocalMethod?.Invoke(null, new object[1] { spectate });
		}

		internal static void ForceDeathHeadSpectateIfPossible()
		{
			ResolveMembers();
			s_forceDeathHeadSpectateIfPossibleMethod?.Invoke(null, null);
		}

		internal static void DebugLogState(SpectateCamera? spectate)
		{
			ResolveMembers();
			s_debugLogStateMethod?.Invoke(null, new object[1] { spectate });
		}

		internal static bool IsManualSwitchInputDown()
		{
			ResolveMembers();
			if (s_isManualSwitchInputDownMethod == null)
			{
				return false;
			}
			return (s_isManualSwitchInputDownMethod.Invoke(null, null) as bool?).GetValueOrDefault();
		}

		internal static bool IsLastChanceModeEnabled()
		{
			ResolveMembers();
			if (s_lastChanceModeField == null)
			{
				return false;
			}
			return (s_lastChanceModeField.GetValue(null) as bool?).GetValueOrDefault();
		}

		internal static bool IsSpectateDeadPlayersEnabled()
		{
			ResolveMembers();
			if (s_spectateDeadPlayersField == null)
			{
				return false;
			}
			return (s_spectateDeadPlayersField.GetValue(null) as bool?).GetValueOrDefault();
		}

		internal static string GetSpectateDeadPlayersMode()
		{
			ResolveMembers();
			if (s_spectateDeadPlayersModeField == null)
			{
				return string.Empty;
			}
			return (s_spectateDeadPlayersModeField.GetValue(null) as string) ?? string.Empty;
		}

		internal static string GetLastChanceIndicatorsMode()
		{
			ResolveMembers();
			if (s_lastChanceIndicatorsField == null)
			{
				return string.Empty;
			}
			return (s_lastChanceIndicatorsField.GetValue(null) as string) ?? string.Empty;
		}

		internal static bool TryGetDirectionSlotSprite(out Sprite? sprite)
		{
			sprite = null;
			ResolveMembers();
			if (s_tryLoadSpriteMethod == null)
			{
				return false;
			}
			object[] array = new object[5]
			{
				"Direction.png",
				null,
				null,
				string.Empty,
				100f
			};
			if (!(s_tryLoadSpriteMethod.Invoke(null, array) as bool?).GetValueOrDefault())
			{
				return false;
			}
			object obj = array[2];
			sprite = (Sprite?)((obj is Sprite) ? obj : null);
			return (Object)(object)sprite != (Object)null;
		}

		private static bool TryGetBoolProperty(ref PropertyInfo? property, string typeName, string propertyName)
		{
			ResolveMembers();
			if (property == null)
			{
				property = ResolveType(typeName)?.GetProperty(propertyName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			}
			return (property?.GetValue(null) as bool?).GetValueOrDefault();
		}

		private static void ResolveMembers()
		{
			if ((object)s_timerControllerType == null)
			{
				s_timerControllerType = ResolveType("DeathHeadHopperFix.Modules.Gameplay.LastChance.Runtime.LastChanceTimerController");
			}
			if ((object)s_spectateHelperType == null)
			{
				s_spectateHelperType = ResolveType("DeathHeadHopperFix.Modules.Gameplay.LastChance.Spectate.LastChanceSpectateHelper");
			}
			if ((object)s_featureFlagsType == null)
			{
				s_featureFlagsType = ResolveType("DHHFLastChanceMode.Modules.Config.FeatureFlags");
			}
			if ((object)s_imageAssetLoaderType == null)
			{
				s_imageAssetLoaderType = ResolveType("DeathHeadHopperFix.Modules.Utilities.ImageAssetLoader");
			}
			if (s_timerControllerType != null)
			{
				if ((object)s_isDirectionEnergySufficientMethod == null)
				{
					s_isDirectionEnergySufficientMethod = s_timerControllerType.GetMethod("IsDirectionIndicatorEnergySufficientPreview", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if ((object)s_getDirectionPenaltyPreviewMethod == null)
				{
					s_getDirectionPenaltyPreviewMethod = s_timerControllerType.GetMethod("GetDirectionIndicatorPenaltySecondsPreview", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if ((object)s_getDirectionEnergyDebugSnapshotMethod == null)
				{
					s_getDirectionEnergyDebugSnapshotMethod = s_timerControllerType.GetMethod("GetDirectionIndicatorEnergyDebugSnapshot", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if ((object)s_isPlayerSurrenderedForDataMethod == null)
				{
					s_isPlayerSurrenderedForDataMethod = s_timerControllerType.GetMethod("IsPlayerSurrenderedForData", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
			}
			if (s_spectateHelperType != null)
			{
				if ((object)s_allPlayersDisabledMethod == null)
				{
					s_allPlayersDisabledMethod = s_spectateHelperType.GetMethod("AllPlayersDisabled", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if ((object)s_resetForceStateMethod == null)
				{
					s_resetForceStateMethod = s_spectateHelperType.GetMethod("ResetForceState", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if ((object)s_shouldForceLocalDeathHeadSpectateMethod == null)
				{
					s_shouldForceLocalDeathHeadSpectateMethod = s_spectateHelperType.GetMethod("ShouldForceLocalDeathHeadSpectate", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if ((object)s_ensureSpectatePlayerLocalMethod == null)
				{
					s_ensureSpectatePlayerLocalMethod = s_spectateHelperType.GetMethod("EnsureSpectatePlayerLocal", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if ((object)s_forceDeathHeadSpectateIfPossibleMethod == null)
				{
					s_forceDeathHeadSpectateIfPossibleMethod = s_spectateHelperType.GetMethod("ForceDeathHeadSpectateIfPossible", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if ((object)s_debugLogStateMethod == null)
				{
					s_debugLogStateMethod = s_spectateHelperType.GetMethod("DebugLogState", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if ((object)s_isManualSwitchInputDownMethod == null)
				{
					s_isManualSwitchInputDownMethod = s_spectateHelperType.GetMethod("IsManualSwitchInputDown", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
			}
			if (s_featureFlagsType != null)
			{
				if ((object)s_lastChanceModeField == null)
				{
					s_lastChanceModeField = s_featureFlagsType.GetField("LastChangeMode", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if ((object)s_spectateDeadPlayersField == null)
				{
					s_spectateDeadPlayersField = s_featureFlagsType.GetField("SpectateDeadPlayers", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if ((object)s_spectateDeadPlayersModeField == null)
				{
					s_spectateDeadPlayersModeField = s_featureFlagsType.GetField("SpectateDeadPlayersMode", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if ((object)s_lastChanceIndicatorsField == null)
				{
					s_lastChanceIndicatorsField = s_featureFlagsType.GetField("LastChanceIndicators", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
			}
			if (s_imageAssetLoaderType != null && (object)s_tryLoadSpriteMethod == null)
			{
				s_tryLoadSpriteMethod = s_imageAssetLoaderType.GetMethod("TryLoadSprite", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
			}
		}

		private static Type? ResolveType(string fullName)
		{
			Type type = Type.GetType(fullName, throwOnError: false);
			if (type != null)
			{
				return type;
			}
			Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
			foreach (Assembly assembly in assemblies)
			{
				type = assembly.GetType(fullName, throwOnError: false);
				if (type != null)
				{
					return type;
				}
			}
			return null;
		}
	}
	internal static class LogLimiter
	{
		public const int DefaultFrameInterval = 240;

		private static readonly Dictionary<string, int> _lastFrameByKey = new Dictionary<string, int>(StringComparer.Ordinal);

		public static bool ShouldLog(string key, int frameInterval = 240)
		{
			if (string.IsNullOrEmpty(key))
			{
				return true;
			}
			if (frameInterval <= 0)
			{
				return true;
			}
			int frameCount = Time.frameCount;
			if (_lastFrameByKey.TryGetValue(key, out var value) && frameCount - value < frameInterval)
			{
				return false;
			}
			_lastFrameByKey[key] = frameCount;
			return true;
		}

		public static void Reset(string key)
		{
			if (!string.IsNullOrEmpty(key))
			{
				_lastFrameByKey.Remove(key);
			}
		}

		public static void Clear()
		{
			_lastFrameByKey.Clear();
		}
	}
	internal static class NetworkProtocol
	{
		internal const string ModId = "DeathHeadHopperFix";

		internal const int ProtocolVersion = 1;

		internal const string RoomKeyPrefix = "DeathHeadHopperFix.Room.";

		internal static string BuildRoomKey(string localKey)
		{
			return string.IsNullOrWhiteSpace(localKey) ? "DeathHeadHopperFix.Room." : ("DeathHeadHopperFix.Room." + localKey.Trim());
		}
	}
	internal readonly struct NetworkEnvelope
	{
		internal string ModId { get; }

		internal int ProtocolVersion { get; }

		internal string MessageType { get; }

		internal int MessageSeq { get; }

		internal object? Payload { get; }

		internal NetworkEnvelope(string modId, int protocolVersion, string messageType, int messageSeq, object? payload)
		{
			ModId = (string.IsNullOrWhiteSpace(modId) ? string.Empty : modId.Trim());
			ProtocolVersion = protocolVersion;
			MessageType = (string.IsNullOrWhiteSpace(messageType) ? string.Empty : messageType.Trim());
			MessageSeq = messageSeq;
			Payload = payload;
		}

		internal object?[] ToEventPayload()
		{
			return new object[5] { ModId, ProtocolVersion, MessageType, MessageSeq, Payload };
		}

		internal bool IsExpectedSource()
		{
			return ModId == "DeathHeadHopperFix" && ProtocolVersion == 1 && !string.IsNullOrWhiteSpace(MessageType);
		}

		internal static bool TryParse(object? customData, out NetworkEnvelope envelope)
		{
			envelope = default(NetworkEnvelope);
			if (!(customData is object[] array) || array.Length < 5)
			{
				return false;
			}
			if (array[0] is string modId && array[1] is int protocolVersion && array[2] is string messageType)
			{
				object obj = array[3];
				int messageSeq = default(int);
				int num;
				if (obj is int)
				{
					messageSeq = (int)obj;
					num = 1;
				}
				else
				{
					num = 0;
				}
				if (num != 0)
				{
					envelope = new NetworkEnvelope(modId, protocolVersion, messageType, messageSeq, array[4]);
					return true;
				}
			}
			return false;
		}
	}
	internal static class PhotonEventCodes
	{
		internal const byte ConfigSync = 79;
	}
	internal static class PlayerStateExtractionHelper
	{
		internal readonly struct PlayerStateSnapshot
		{
			internal int ActorNumber { get; }

			internal int SteamIdShort { get; }

			internal string Name { get; }

			internal Color Color { get; }

			internal bool IsAlive { get; }

			internal bool IsDead { get; }

			internal bool IsInTruck { get; }

			internal bool IsSurrendered { get; }

			internal int SourceOrder { get; }

			internal PlayerStateSnapshot(int actorNumber, int steamIdShort, string name, Color color, bool isAlive, bool isDead, bool isInTruck, bool isSurrendered, int sourceOrder)
			{
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_0019: Unknown result type (might be due to invalid IL or missing references)
				ActorNumber = actorNumber;
				SteamIdShort = steamIdShort;
				Name = name;
				Color = color;
				IsAlive = isAlive;
				IsDead = isDead;
				IsInTruck = isInTruck;
				IsSurrendered = isSurrendered;
				SourceOrder = sourceOrder;
			}
		}

		private static readonly FieldInfo? s_playerNameField = AccessTools.Field(typeof(PlayerAvatar), "playerName");

		private static readonly FieldInfo? s_playerDeadSetField = AccessTools.Field(typeof(PlayerAvatar), "deadSet");

		private static readonly FieldInfo? s_playerIsDisabledField = AccessTools.Field(typeof(PlayerAvatar), "isDisabled");

		private static readonly FieldInfo? s_playerRoomVolumeCheckField = AccessTools.Field(typeof(PlayerAvatar), "RoomVolumeCheck");

		private static readonly FieldInfo? s_playerSteamIdShortField = AccessTools.Field(typeof(PlayerAvatar), "steamIDshort");

		private static readonly FieldInfo? s_visualColorField = AccessTools.Field(typeof(PlayerAvatarVisuals), "color");

		private static FieldInfo? s_roomVolumeCheckInTruckField;

		private static FieldInfo? s_deathHeadInTruckField;

		private static FieldInfo? s_deathHeadRoomVolumeCheckField;

		internal static List<PlayerStateSnapshot> GetPlayersStateSnapshot()
		{
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fe: Unknown result type (might be due to invalid IL or missing references)
			List<PlayerStateSnapshot> list = new List<PlayerStateSnapshot>();
			GameDirector instance = GameDirector.instance;
			if ((Object)(object)instance == (Object)null || instance.PlayerList == null || instance.PlayerList.Count == 0)
			{
				return list;
			}
			for (int i = 0; i < instance.PlayerList.Count; i++)
			{
				PlayerAvatar val = instance.PlayerList[i];
				if (!((Object)(object)val == (Object)null))
				{
					PhotonView photonView = val.photonView;
					int? obj;
					if (photonView == null)
					{
						obj = null;
					}
					else
					{
						Player owner = photonView.Owner;
						obj = ((owner != null) ? new int?(owner.ActorNumber) : null);
					}
					int? num = obj;
					int valueOrDefault = num.GetValueOrDefault();
					int steamIdShort = GetSteamIdShort(val);
					string playerName = GetPlayerName(val);
					Color playerColor = GetPlayerColor(val);
					bool flag = IsDeadSet(val);
					bool flag2 = IsDisabled(val);
					bool flag3 = flag || flag2;
					bool isAlive = !flag3;
					bool isInTruck = IsPlayerInTruck(val, flag2);
					bool isSurrendered = LastChanceInteropBridge.IsPlayerSurrenderedForData(val);
					list.Add(new PlayerStateSnapshot(valueOrDefault, steamIdShort, playerName, playerColor, isAlive, flag3, isInTruck, isSurrendered, i));
				}
			}
			list.Sort(CompareSnapshotOrder);
			return list;
		}

		internal static List<PlayerStateSnapshot> GetPlayersStillInLastChance()
		{
			List<PlayerStateSnapshot> playersStateSnapshot = GetPlayersStateSnapshot();
			List<PlayerStateSnapshot> list = new List<PlayerStateSnapshot>(playersStateSnapshot.Count);
			for (int i = 0; i < playersStateSnapshot.Count; i++)
			{
				PlayerStateSnapshot item = playersStateSnapshot[i];
				if (!item.IsSurrendered)
				{
					list.Add(item);
				}
			}
			return list;
		}

		private static int CompareSnapshotOrder(PlayerStateSnapshot left, PlayerStateSnapshot right)
		{
			if (left.ActorNumber > 0 && right.ActorNumber > 0)
			{
				return left.ActorNumber.CompareTo(right.ActorNumber);
			}
			return left.SourceOrder.CompareTo(right.SourceOrder);
		}

		private static string GetPlayerName(PlayerAvatar player)
		{
			if (s_playerNameField != null && s_playerNameField.GetValue(player) is string text && !string.IsNullOrWhiteSpace(text))
			{
				return text;
			}
			return "unknown";
		}

		private static Color GetPlayerColor(PlayerAvatar player)
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			PlayerAvatarVisuals playerAvatarVisuals = player.playerAvatarVisuals;
			if ((Object)(object)playerAvatarVisuals == (Object)null)
			{
				return Color.black;
			}
			if (s_visualColorField != null && s_visualColorField.GetValue(playerAvatarVisuals) is Color result)
			{
				return result;
			}
			return Color.black;
		}

		private static bool IsDeadSet(PlayerAvatar player)
		{
			bool flag = default(bool);
			int num;
			if (s_playerDeadSetField != null)
			{
				object value = s_playerDeadSetField.GetValue(player);
				if (value is bool)
				{
					flag = (bool)value;
					num = 1;
				}
				else
				{
					num = 0;
				}
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		private static bool IsDisabled(PlayerAvatar player)
		{
			bool flag = default(bool);
			int num;
			if (s_playerIsDisabledField != null)
			{
				object value = s_playerIsDisabledField.GetValue(player);
				if (value is bool)
				{
					flag = (bool)value;
					num = 1;
				}
				else
				{
					num = 0;
				}
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		private static bool IsPlayerInTruck(PlayerAvatar player, bool isDisabled)
		{
			if (!isDisabled)
			{
				object roomVolumeCheck = GetRoomVolumeCheck(player);
				return roomVolumeCheck != null && IsRoomVolumeInTruck(roomVolumeCheck);
			}
			PlayerDeathHead playerDeathHead = player.playerDeathHead;
			if ((Object)(object)playerDeathHead == (Object)null)
			{
				return false;
			}
			object deathHeadRoomVolumeCheck = GetDeathHeadRoomVolumeCheck(playerDeathHead);
			if (deathHeadRoomVolumeCheck != null)
			{
				return IsRoomVolumeInTruck(deathHeadRoomVolumeCheck);
			}
			FieldInfo deathHeadInTruckField = GetDeathHeadInTruckField(((object)playerDeathHead).GetType());
			bool flag = default(bool);
			int num;
			if (deathHeadInTruckField != null)
			{
				object value = deathHeadInTruckField.GetValue(playerDeathHead);
				if (value is bool)
				{
					flag = (bool)value;
					num = 1;
				}
				else
				{
					num = 0;
				}
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		private static object? GetRoomVolumeCheck(PlayerAvatar player)
		{
			if (s_playerRoomVolumeCheckField == null)
			{
				return null;
			}
			return s_playerRoomVolumeCheckField.GetValue(player);
		}

		private static bool IsRoomVolumeInTruck(object roomVolumeCheck)
		{
			if (roomVolumeCheck == null)
			{
				return false;
			}
			FieldInfo fieldInfo = s_roomVolumeCheckInTruckField;
			if (fieldInfo == null || fieldInfo.DeclaringType != roomVolumeCheck.GetType())
			{
				fieldInfo = (s_roomVolumeCheckInTruckField = AccessTools.Field(roomVolumeCheck.GetType(), "inTruck"));
			}
			bool flag = default(bool);
			int num;
			if (fieldInfo != null)
			{
				object value = fieldInfo.GetValue(roomVolumeCheck);
				if (value is bool)
				{
					flag = (bool)value;
					num = 1;
				}
				else
				{
					num = 0;
				}
			}
			else
			{
				num = 0;
			}
			return (byte)((uint)num & (flag ? 1u : 0u)) != 0;
		}

		private static FieldInfo? GetDeathHeadInTruckField(Type deathHeadType)
		{
			FieldInfo fieldInfo = s_deathHeadInTruckField;
			if (fieldInfo == null || fieldInfo.DeclaringType != deathHeadType)
			{
				fieldInfo = (s_deathHeadInTruckField = AccessTools.Field(deathHeadType, "inTruck"));
			}
			return fieldInfo;
		}

		private static object? GetDeathHeadRoomVolumeCheck(PlayerDeathHead deathHead)
		{
			FieldInfo fieldInfo = s_deathHeadRoomVolumeCheckField;
			if (fieldInfo == null || fieldInfo.DeclaringType != ((object)deathHead).GetType())
			{
				fieldInfo = (s_deathHeadRoomVolumeCheckField = AccessTools.Field(((object)deathHead).GetType(), "roomVolumeCheck"));
			}
			return fieldInfo?.GetValue(deathHead);
		}

		private static int GetSteamIdShort(PlayerAvatar player)
		{
			return (s_playerSteamIdShortField != null && s_playerSteamIdShortField.GetValue(player) is int num) ? num : 0;
		}
	}
	internal static class SpectateContextHelper
	{
		private static readonly FieldInfo? s_spectatePlayerField = AccessTools.Field(typeof(SpectateCamera), "player");

		private static readonly FieldInfo? s_playerDeathHeadSpectatedField = AccessTools.Field(typeof(PlayerDeathHead), "spectated");

		internal static bool IsSpectatingLocalPlayerTarget()
		{
			SpectateCamera instance = SpectateCamera.instance;
			PlayerAvatar instance2 = PlayerAvatar.instance;
			if ((Object)(object)instance == (Object)null || (Object)(object)instance2 == (Object)null || s_spectatePlayerField == null)
			{
				return false;
			}
			object? value = s_spectatePlayerField.GetValue(instance);
			PlayerAvatar val = (PlayerAvatar)((value is PlayerAvatar) ? value : null);
			return (Object)(object)val != (Object)null && val == instance2;
		}

		internal static bool IsSpectatingLocalDeathHead()
		{
			SpectateCamera instance = SpectateCamera.instance;
			if ((Object)(object)instance == (Object)null || !instance.CheckState((State)2))
			{
				return false;
			}
			PlayerDeathHead val = PlayerController.instance?.playerAvatarScript?.playerDeathHead;
			if ((Object)(object)val == (Object)null || s_playerDeathHeadSpectatedField == null)
			{
				return false;
			}
			return (s_playerDeathHeadSpectatedField.GetValue(val) as bool?).GetValueOrDefault();
		}

		internal static bool IsLocalDeathHeadSpectated()
		{
			PlayerDeathHead val = PlayerController.instance?.playerAvatarScript?.playerDeathHead;
			if ((Object)(object)val == (Object)null || s_playerDeathHeadSpectatedField == null)
			{
				return false;
			}
			return (s_playerDeathHeadSpectatedField.GetValue(val) as bool?).GetValueOrDefault();
		}
	}
}
namespace DeathHeadHopperFix.Modules.Stamina
{
	internal sealed class StaminaRechargeModule : MonoBehaviour
	{
		private static readonly Type? s_deathHeadControllerType = AccessTools.TypeByName("DeathHeadHopper.DeathHead.DeathHeadController");

		private object? _controllerInstance;

		private PhotonView? _photonView;

		private bool _isOwner;

		private float _rechargeAccumulator;

		private Rigidbody? _rb;

		private void Awake()
		{
			if (s_deathHeadControllerType == null)
			{
				((Behaviour)this).enabled = false;
				return;
			}
			_controllerInstance = ((Component)this).GetComponent(s_deathHeadControllerType);
			if (_controllerInstance == null)
			{
				((Behaviour)this).enabled = false;
				return;
			}
			_photonView = ((Component)this).GetComponent<PhotonView>();
			_isOwner = !SemiFunc.IsMultiplayer() || ((Object)(object)_photonView != (Object)null && _photonView.IsMine);
			_rb = ((Component)this).GetComponent<Rigidbody>();
		}

		private void Update()
		{
			if (!_isOwner)
			{
				return;
			}
			if (!FeatureFlags.RechargeWithStamina)
			{
				_rechargeAccumulator = 0f;
			}
			else
			{
				if (_controllerInstance == null)
				{
					return;
				}
				_rechargeAccumulator += Time.deltaTime;
				if (!(_rechargeAccumulator < FeatureFlags.RechargeTickInterval))
				{
					bool flag = !FeatureFlags.RechargeStaminaOnlyStationary || IsHeadStationary();
					(bool, bool?, float, float) tuple = DHHBatteryHelper.EvaluateJumpAllowance();
					if (flag || !tuple.Item1)
					{
						DHHBatteryHelper.RechargeDhhAbilityEnergy(_controllerInstance, _rechargeAccumulator);
					}
					_rechargeAccumulator = 0f;
				}
			}
		}

		private bool IsHeadStationary()
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)_rb == (Object)null)
			{
				return true;
			}
			Vector3 velocity = _rb.velocity;
			return ((Vector3)(ref velocity)).sqrMagnitude < FeatureFlags.HeadStationaryVelocitySqrThreshold;
		}
	}
}
namespace DeathHeadHopperFix.Modules.Gameplay.Stun
{
	internal static class ChargeHoldReleaseModule
	{
		private sealed class ChargeHoldState
		{
			public float StartTime;

			public bool IsHolding;

			public float LaunchScale = 1f;
		}

		private readonly struct DiminishingReturnsResult
		{
			public float BaseValue { get; }

			public float IncreasePerLevel { get; }

			public int AppliedLevel { get; }

			public int ThresholdLevel { get; }

			public float DiminishingFactor { get; }

			public int LinearLevels { get; }

			public int ExtraLevels { get; }

			public float LinearContribution { get; }

			public float DiminishingContribution { get; }

			public float DiminishingComponent { get; }

			public float FinalValue { get; }

			public DiminishingReturnsResult(float baseValue, float increasePerLevel, int appliedLevel, int thresholdLevel, float diminishingFactor, int linearLevels, int extraLevels, float linearContribution, float diminishingContribution, float diminishingComponent, float finalValue)
			{
				BaseValue = baseValue;
				IncreasePerLevel = increasePerLevel;
				AppliedLevel = appliedLevel;
				ThresholdLevel = thresholdLevel;
				DiminishingFactor = diminishingFactor;
				LinearLevels = linearLevels;
				ExtraLevels = extraLevels;
				LinearContribution = linearContribution;
				DiminishingContribution = diminishingContribution;
				DiminishingComponent = diminishingComponent;
				FinalValue = finalValue;
			}
		}

		private const string ChargeStrengthLogKey = "Fix:Charge.Strength";

		private const string ChargePermissiveFallbackLogKey = "Fix:Charge.PermissiveFallback";

		private const float RemoteReleaseCommandTag = -777f;

		private const float RemoteCancelCommandTag = -778f;

		private static ManualLogSource? s_log;

		private static readonly Dictionary<int, ChargeHoldState> s_chargeHoldStates = new Dictionary<int, ChargeHoldState>();

		private static float s_lastLocalHoldInputStartTime;

		private static bool s_localHoldUiActive;

		private static bool s_localHoldInputPending;

		internal static void Apply(Harmony harmony, Assembly asm, ManualLogSource? log)
		{
			s_log = log;
			PatchChargeHandlerDamageModeIfPossible(harmony, asm);
			PatchChargeAbilityHoldReleaseIfPossible(harmony, asm);
			PatchStunHandlerHoldScalingIfPossible(harmony, asm);
		}

		private static void PatchChargeHandlerDamageModeIfPossible(Harmony harmony, Assembly asm)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Expected O, but got Unknown
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Expected O, but got Unknown
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Expected O, but got Unknown
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_021a: Expected O, but got Unknown
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			//IL_0261: Expected O, but got Unknown
			//IL_029b: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a8: Expected O, but got Unknown
			//IL_02e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ef: Expected O, but got Unknown
			//IL_0328: Unknown result type (might be due to invalid IL or missing references)
			//IL_0336: Expected O, but got Unknown
			//IL_0371: Unknown result type (might be due to invalid IL or missing references)
			//IL_037f: Expected O, but got Unknown
			//IL_0394: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a1: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(ChargeHandler), "ChargeWindup", new Type[1] { typeof(Vector3) }, (Type[])null);
			MethodInfo method = typeof(ChargeHoldReleaseModule).GetMethod("ChargeHandler_ChargeWindup_Prefix", BindingFlags.Static | BindingFlags.NonPublic);
			if (methodInfo != null && method != null)
			{
				harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo method2 = typeof(ChargeHoldReleaseModule).GetMethod("ChargeHandler_ChargeWindup_Postfix", BindingFlags.Static | BindingFlags.NonPublic);
			if (methodInfo != null && method2 != null)
			{
				harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(method2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo methodInfo2 = AccessTools.Method(typeof(ChargeHandler), "ResetState", Type.EmptyTypes, (Type[])null);
			MethodInfo methodInfo3 = AccessTools.Method(typeof(ChargeHandler), "FixedUpdate", Type.EmptyTypes, (Type[])null);
			MethodInfo methodInfo4 = AccessTools.Method(typeof(ChargeHandler), "CancelCharge", Type.EmptyTypes, (Type[])null);
			MethodInfo methodInfo5 = AccessTools.Method(typeof(ChargeHandler), "EnemyHit", (Type[])null, (Type[])null);
			MethodInfo methodInfo6 = AccessTools.Method(typeof(ChargeHandler), "UpdateWindupDirection", new Type[1] { typeof(Vector3) }, (Type[])null);
			MethodInfo methodInfo7 = AccessTools.Method(typeof(ChargeHandler), "SyncChargeStateRPC", (Type[])null, (Type[])null);
			MethodInfo method3 = typeof(ChargeHoldReleaseModule).GetMethod("ChargeHandler_SyncChargeStateRPC_Prefix", BindingFlags.Static | BindingFlags.NonPublic);
			MethodInfo method4 = typeof(ChargeHoldReleaseModule).GetMethod("ChargeHandler_ResetState_Postfix", BindingFlags.Static | BindingFlags.NonPublic);
			if (methodInfo2 != null && method4 != null)
			{
				harmony.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, new HarmonyMethod(method4), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo methodInfo8 = AccessTools.Method(typeof(ChargeHandler), "EndCharge", Type.EmptyTypes, (Type[])null);
			MethodInfo method5 = typeof(ChargeHoldReleaseModule).GetMethod("ChargeHandler_EndCharge_Postfix", BindingFlags.Static | BindingFlags.NonPublic);
			if (methodInfo8 != null && method5 != null)
			{
				harmony.Patch((MethodBase)methodInfo8, (HarmonyMethod)null, new HarmonyMethod(method5), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo method6 = typeof(ChargeHoldReleaseModule).GetMethod("ChargeHandler_FixedUpdate_Postfix", BindingFlags.Static | BindingFlags.NonPublic);
			if (methodInfo3 != null && method6 != null)
			{
				harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(method6), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo method7 = typeof(ChargeHoldReleaseModule).GetMethod("ChargeHandler_CancelCharge_Postfix", BindingFlags.Static | BindingFlags.NonPublic);
			if (methodInfo4 != null && method7 != null)
			{
				harmony.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, new HarmonyMethod(method7), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo method8 = typeof(ChargeHoldReleaseModule).GetMethod("ChargeHandler_EnemyHit_Prefix", BindingFlags.Static | BindingFlags.NonPublic);
			if (methodInfo5 != null && method8 != null)
			{
				harmony.Patch((MethodBase)methodInfo5, new HarmonyMethod(method8), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo method9 = typeof(ChargeHoldReleaseModule).GetMethod("ChargeHandler_UpdateWindupDirection_Prefix", BindingFlags.Static | BindingFlags.NonPublic);
			if (methodInfo6 != null && method9 != null)
			{
				harmony.Patch((MethodBase)methodInfo6, new HarmonyMethod(method9), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
			}
			MethodInfo method10 = typeof(ChargeHoldReleaseModule).GetMethod("ChargeHandler_SyncChargeStateRPC_Postfix", BindingFlags.Static | BindingFlags.NonPublic);
			if (methodInfo7 != null)
			{
				if (method3 != null)
				{
					harmony.Patch((MethodBase)methodInfo7, new HarmonyMethod(method3), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				if (method10 != null)
				{
					harmony.Patch((MethodBase)methodInfo7, (HarmonyMethod)null, new HarmonyMethod(method10), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
		}

		private static bool ChargeHandler_ChargeWindup_Prefix(ChargeHandler __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (IsChargeHandlerHeadGrabbed(__instance))
			{
				if (FeatureFlags.DebugLogging && LogLimiter.ShouldLog("Fix:Charge.Grabbed", 30))
				{
					Debug.Log((object)"[Fix:Charge] Charge windup blocked because the head is grabbed.");
				}
				return false;
			}
			return true;
		}

		private static void ChargeHandler_ChargeWindup_Postfix(ChargeHandler __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && IsChargeState(__instance, "Windup"))
			{
				int unityObjectInstanceId = GetUnityObjectInstanceId((Object)(object)__instance);
				if (unityObjectInstanceId != 0)
				{
					ChargeHoldState orCreateChargeHoldState = GetOrCreateChargeHoldState(unityObjectInstanceId);
					orCreateChargeHoldState.StartTime = Time.time;
					orCreateChargeHoldState.IsHolding = true;
					orCreateChargeHoldState.LaunchScale = 1f;
					s_localHoldUiActive = true;
					AbilityModule.SetChargeSlotActivationProgress(0f);
				}
			}
		}

		private static void ChargeHandler_FixedUpdate_Postfix(ChargeHandler __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			int unityObjectInstanceId = GetUnityObjectInstanceId((Object)(object)__instance);
			if (unityObjectInstanceId == 0)
			{
				return;
			}
			if (!IsLocalChargeHandler(__instance))
			{
				if (s_chargeHoldStates.TryGetValue(unityObjectInstanceId, out ChargeHoldState value))
				{
					if (!IsChargeState(__instance, "Windup"))
					{
						s_chargeHoldStates.Remove(unityObjectInstanceId);
					}
					else if (value.IsHolding)
					{
						__instance.windupTimer = Mathf.Max(0.01f, __instance.windupTime);
					}
				}
				return;
			}
			if (!s_chargeHoldStates.TryGetValue(unityObjectInstanceId, out ChargeHoldState value2))
			{
				if (!IsChargeState(__instance, "Windup") || !s_localHoldInputPending)
				{
					if (!s_localHoldUiActive)
					{
						AbilityModule.SetChargeSlotActivationProgress(0f);
					}
					return;
				}
				value2 = GetOrCreateChargeHoldState(unityObjectInstanceId);
				value2.StartTime = ((s_lastLocalHoldInputStartTime > 0f) ? s_lastLocalHoldInputStartTime : Time.time);
				value2.IsHolding = true;
				value2.LaunchScale = 1f;
				s_localHoldUiActive = true;
				AbilityModule.SetChargeSlotActivationProgress(0f);
			}
			if (!IsChargeState(__instance, "Windup"))
			{
				s_chargeHoldStates.Remove(unityObjectInstanceId);
				s_localHoldUiActive = false;
				AbilityModule.SetChargeSlotActivationProgress(0f);
				if (!s_localHoldUiActive)
				{
					AbilityModule.SetChargeSlotActivationProgress(0f);
				}
				return;
			}
			if (s_localHoldUiActive && value2.IsHolding)
			{
				float num = Mathf.Max(0.2f, FeatureFlags.ChargeAbilityHoldSeconds);
				float progress = Mathf.Clamp01((Time.time - value2.StartTime) / num);
				float minimumChargeReleaseScale = GetMinimumChargeReleaseScale(__instance);
				AbilityModule.SetChargeSlotActivationProgress(progress, minimumChargeReleaseScale);
			}
			else
			{
				if (!value2.IsHolding)
				{
					if (!s_localHoldUiActive)
					{
						AbilityModule.SetChargeSlotActivationProgress(0f);
					}
					if (!IsChargeState(__instance, "Windup"))
					{
						s_chargeHoldStates.Remove(unityObjectInstanceId);
					}
					return;
				}
				if (!IsChargeState(__instance, "Windup"))
				{
					s_chargeHoldStates.Remove(unityObjectInstanceId);
					AbilityModule.SetChargeSlotActivationProgress(0f);
					return;
				}
				float num2 = Mathf.Max(0.2f, FeatureFlags.ChargeAbilityHoldSeconds);
				float progress2 = Mathf.Clamp01((Time.time - value2.StartTime) / num2);
				float minimumChargeReleaseScale2 = GetMinimumChargeReleaseScale(__instance);
				AbilityModule.SetChargeSlotActivationProgress(progress2, minimumChargeReleaseScale2);
			}
			if (value2.IsHolding)
			{
				__instance.windupTimer = Mathf.Max(0.01f, __instance.windupTime);
			}
		}

		private static void PatchChargeAbilityHoldReleaseIfPossible(Harmony harmony, Assembly asm)
		{
			//IL_00ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fb: Expected O, but got Unknown
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e9: Expected O, but got Unknown
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.Method(typeof(ChargeAbility), "OnAbilityDown", Type.EmptyTypes, (Type[])null);
			MethodInfo methodInfo2 = AccessTools.Method(typeof(ChargeAbility), "OnAbilityUp", Type.EmptyTypes, (Type[])null);
			MethodInfo methodInfo3 = AccessTools.Method(typeof(ChargeAbility), "OnAbilityCancel", Type.EmptyTypes, (Type[])null);
			if (methodInfo2 == null)
			{
				return;
			}
			MethodInfo method = typeof(ChargeHoldReleaseModule).GetMethod("ChargeAbility_OnAbilityUp_Prefix", BindingFlags.Static | BindingFlags.NonPublic);
			if (!(method == null))
			{
				MethodInfo method2 = typeof(ChargeHoldReleaseModule).GetMethod("ChargeAbility_OnAbilityDown_Postfix", BindingFlags.Static | BindingFlags.NonPublic);
				MethodInfo method3 = typeof(ChargeHoldReleaseModule).GetMethod("ChargeAbility_OnAbilityCancel_Postfix", BindingFlags.Static | BindingFlags.NonPublic);
				if (methodInfo != null && method2 != null)
				{
					harmony.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(method2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
				harmony.Patch((MethodBase)methodInfo2, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				if (methodInfo3 != null && method3 != null)
				{
					harmony.Patch((MethodBase)methodInfo3, (HarmonyMethod)null, new HarmonyMethod(method3), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
		}

		private static void PatchStunHandlerHoldScalingIfPossible(Harmony harmony, Assembly asm)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Expected O, but got Unknown
			MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(StunHandler), "StunDuration");
			if (!(methodInfo == null))
			{
				MethodInfo method = typeof(ChargeHoldReleaseModule).GetMethod("StunHandler_StunDuration_Prefix", BindingFlags.Static | BindingFlags.NonPublic);
				if (!(method == null))
				{
					harmony.Patch((MethodBase)methodInfo, new HarmonyMethod(method), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				}
			}
		}

		private static void ChargeHandler_CancelCharge_Postfix(ChargeHandler __instance)
		{
			StopChargeWindupLoop(__instance);
			ClearChargeHoldState(__instance);
		}

		private static void ChargeHandler_SyncChargeStateRPC_Prefix(ChargeHandler __instance, ChargeState state)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Invalid comparison between Unknown and I4
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Invalid comparison between Unknown and I4
			if (!((Object)(object)__instance == (Object)null) && (int)state != 1 && (int)state != 2)
			{
				StopChargeWindupLoop(__instance);
				ClearChargeHoldState(__instance);
			}
		}

		private static void ChargeHandler_SyncChargeStateRPC_Postfix(ChargeHandler __instance)
		{
			if (!((Object)(object)__instance == (Object)null) && !IsChargeState(__instance, "Windup") && !IsChargeState(__instance, "Charging"))
			{
				StopChargeWindupLoop(__instance);
				ClearChargeHoldState(__instance);
			}
		}

		private static bool ChargeHandler_EnemyHit_Prefix(ChargeHandler __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			int unityObjectInstanceId = GetUnityObjectInstanceId((Object)(object)__instance);
			if (unityObjectInstanceId == 0 || !s_chargeHoldStates.TryGetValue(unityObjectInstanceId, out ChargeHoldState value))
			{
				return true;
			}
			__instance.enemiesHit++;
			int abilityLevel = __instance.AbilityLevel;
			int num = Mathf.FloorToInt(EvaluateStatWithDiminishingReturns(1f, 0.5f, abilityLevel, 20, 0.9f).FinalValue);
			int num2 = Mathf.Max(1, Mathf.RoundToInt((float)num * Mathf.Clamp01(value.LaunchScale)));
			if (__instance.enemiesHit >= num2)
			{
				__instance.EndCharge();
			}
			return false;
		}

		private static bool StunHandler_StunDuration_Prefix(StunHandler __instance, ref float __result)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			ChargeHandler chargeHandler = __instance.chargeHandler;
			if ((Object)(object)chargeHandler == (Object)null)
			{
				return true;
			}
			int unityObjectInstanceId = GetUnityObjectInstanceId((Object)(object)chargeHandler);
			if (unityObjectInstanceId == 0 || !s_chargeHoldStates.TryGetValue(unityObjectInstanceId, out ChargeHoldState value))
			{
				return true;
			}
			int abilityLevel = chargeHandler.AbilityLevel;
			float num = 5f + 1f * (float)abilityLevel;
			__result = num * Mathf.Clamp01(value.LaunchScale);
			return false;
		}

		private static bool ChargeAbility_OnAbilityUp_Prefix()
		{
			return TryReleaseHeldCharge();
		}

		private static void ChargeAbility_OnAbilityDown_Postfix()
		{
			s_lastLocalHoldInputStartTime = Time.time;
			s_localHoldInputPending = true;
			AbilityModule.SetChargeSlotActivationProgress(0f);
			ChargeHandler localChargeHandler = GetLocalChargeHandler();
			if ((Object)(object)localChargeHandler != (Object)null && !IsChargeState(localChargeHandler, "Windup"))
			{
				ClearChargeHoldState(localChargeHandler);
			}
		}

		private static void ChargeAbility_OnAbilityCancel_Postfix()
		{
			s_localHoldInputPending = false;
			s_localHoldUiActive = false;
			AbilityModule.SetChargeSlotActivationProgress(0f);
			ChargeHandler localChargeHandler = GetLocalChargeHandler();
			if (!((Object)(object)localChargeHandler == (Object)null))
			{
				ClearChargeHoldState(localChargeHandler);
			}
		}

		private static bool TryReleaseHeldCharge()
		{
			ChargeHandler localChargeHandler = GetLocalChargeHandler();
			if ((Object)(object)localChargeHandler == (Object)null)
			{
				s_localHoldInputPending = false;
				s_localHoldUiActive = false;
				AbilityModule.SetChargeSlotActivationProgress(0f);
				return true;
			}
			if (!IsChargeState(localChargeHandler, "Windup"))
			{
				s_localHoldInputPending = false;
				s_localHoldUiActive = false;
				AbilityModule.SetChargeSlotActivationProgress(0f);
				return true;
			}
			int unityObjectInstanceId = GetUnityObjectInstanceId((Object)(object)localChargeHandler);
			if (unityObjectInstanceId == 0)
			{
				s_localHoldInputPending = false;
				s_localHoldUiActive = false;
				AbilityModule.SetChargeSlotActivationProgress(0f);
				return true;
			}
			if (!s_chargeHoldStates.TryGetValue(unityObjectInstanceId, out ChargeHoldState value))
			{
				value = GetOrCreateChargeHoldState(unityObjectInstanceId);
				value.StartTime = ((s_lastLocalHoldInputStartTime > 0f) ? s_lastLocalHoldInputStartTime : Time.time);
				value.IsHolding = true;
				value.LaunchScale = 1f;
			}
			if (!value.IsHolding)
			{
				return true;
			}
			if (SemiFunc.IsMultiplayer() && !SemiFunc.IsMasterClientOrSingleplayer())
			{
				if (!ConfigSyncManager.IsRemoteHostFixCompatible())
				{
					value.IsHolding = false;
					value.LaunchScale = 0f;
					s_localHoldInputPending = false;
					s_localHoldUiActive = false;
					AbilityModule.SetChargeSlotActivationProgress(0f);
					StopChargeWindupLoop(localChargeHandler);
					if (FeatureFlags.DebugLogging && LogLimiter.ShouldLog("Fix:Charge.PermissiveFallback", 120))
					{
						Debug.Log((object)"[Fix:DHHCharge][PermissiveGate] Host fix marker missing. Sending authoritative cancel fallback.");
					}
					TrySendVanillaRemoteCancelCommand(localChargeHandler);
					return false;
				}
				value.IsHolding = false;
				s_localHoldInputPending = false;
				s_localHoldUiActive = false;
				AbilityModule.SetChargeSlotActivationProgress(0f);
				if (!TrySendRemoteReleaseCommand(localChargeHandler))
				{
					StopChargeWindupLoop(localChargeHandler);
					TrySendVanillaRemoteCancelCommand(localChargeHandler);
				}
				return false;
			}
			float num = Mathf.Max(0.2f, FeatureFlags.ChargeAbilityHoldSeconds);
			float num2 = Mathf.Clamp01((Time.time - value.StartTime) / num);
			float minimumChargeReleaseScale = GetMinimumChargeReleaseScale(localChargeHandler);
			if (num2 < minimumChargeReleaseScale)
			{
				value.IsHolding = false;
				value.LaunchScale = 0f;
				s_localHoldInputPending = false;
				s_localHoldUiActive = false;
				AbilityModule.SetChargeSlotActivationProgress(0f);
				StopChargeWindupLoop(localChargeHandler);
				localChargeHandler.CancelCharge();
				return false;
			}
			value.IsHolding = false;
			value.LaunchScale = num2;
			localChargeHandler.chargeStrength *= num2;
			localChargeHandler.maxBounces = Mathf.Max(0f, localChargeHandler.maxBounces * num2);
			localChargeHandler.windupTimer = -1f;
			s_localHoldInputPending = false;
			s_localHoldUiActive = false;
			AbilityModule.SetChargeSlotActivationProgress(0f);
			return true;
		}

		private static bool ChargeHandler_UpdateWindupDirection_Prefix(ChargeHandler __instance, Vector3 chargeDirection)
		{
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if (!SemiFunc.IsMasterClientOrSingleplayer())
			{
				return true;
			}
			if (Mathf.Abs(chargeDirection.x - -778f) < 0.001f)
			{
				__instance.CancelCharge();
				return false;
			}
			if (Mathf.Abs(chargeDirection.x - -777f) > 0.001f)
			{
				return true;
			}
			int unityObjectInstanceId = GetUnityObjectInstanceId((Object)(object)__instance);
			if (unityObjectInstanceId == 0 || !s_chargeHoldStates.TryGetValue(unityObjectInstanceId, out ChargeHoldState value))
			{
				return false;
			}
			if (!IsChargeState(__instance, "Windup"))
			{
				return false;
			}
			float num = Mathf.Max(0.2f, FeatureFlags.ChargeAbilityHoldSeconds);
			float num2 = Mathf.Clamp01((Time.time - value.StartTime) / num);
			float minimumChargeReleaseScale = GetMinimumChargeReleaseScale(__instance);
			value.IsHolding = false;
			if (num2 < minimumChargeReleaseScale)
			{
				value.LaunchScale = 0f;
				__instance.CancelCharge();
				return false;
			}
			value.LaunchScale = num2;
			__instance.chargeStrength *= num2;
			__instance.maxBounces = Mathf.Max(0f, __instance.maxBounces * num2);
			__instance.windupTimer = -1f;
			return false;
		}

		private static bool TrySendRemoteReleaseCommand(ChargeHandler chargeHandler)
		{
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			PhotonView chargePhotonView = GetChargePhotonView(chargeHandler);
			if ((Object)(object)chargePhotonView == (Object)null || chargePhotonView.ViewID <= 0)
			{
				return false;
			}
			if (!PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient)
			{
				return false;
			}
			chargePhotonView.RPC("UpdateWindupDirection", (RpcTarget)2, new object[1] { (object)new Vector3(-777f, 0f, 0f) });
			return true;
		}

		private static bool TrySendVanillaRemoteCancelCommand(ChargeHandler chargeHandler)
		{
			PhotonView chargePhotonView = GetChargePhotonView(chargeHandler);
			if ((Object)(object)chargePhotonView == (Object)null || chargePhotonView.ViewID <= 0)
			{
				return false;
			}
			if (!PhotonNetwork.InRoom || PhotonNetwork.IsMasterClient)
			{
				return false;
			}
			chargePhotonView.RPC("CancelCharge", (RpcTarget)2, Array.Empty<object>());
			return true;
		}

		private static PhotonView? GetChargePhotonView(ChargeHandler chargeHandler)
		{
			if (chargeHandler != null)
			{
				PhotonView component = ((Component)chargeHandler).GetComponent<PhotonView>();
				if ((Object)(object)component != (Object)null && component.ViewID > 0)
				{
					return component;
				}
			}
			return GetDhhInputManagerHeadPhotonView();
		}

		private static PhotonView? GetDhhInputManagerHeadPhotonView()
		{
			return ((Object)(object)DHHInputManager.instance != (Object)null) ? DHHInputManager.instance.headPhotonView : null;
		}

		private static float GetMinimumChargeReleaseScale(ChargeHandler chargeHandler)
		{
			if ((Object)(object)chargeHandler == (Object)null)
			{
				return 0f;
			}
			float num = 0f;
			TryGetEffectiveChargeAbilityLevel(chargeHandler, out var abilityLevel);
			float effectiveChargeStrengthForThreshold = GetEffectiveChargeStrengthForThreshold(chargeHandler, abilityLevel);
			if (effectiveChargeStrengthForThreshold > 0f)
			{
				num = Mathf.Max(num, RequiredScaleForMinimumOne(effectiveChargeStrengthForThreshold));
			}
			float effectiveMaxBouncesForThreshold = GetEffectiveMaxBouncesForThreshold(chargeHandler, abilityLevel);
			if (effectiveMaxBouncesForThreshold > 0f)
			{
				num = Mathf.Max(num, RequiredScaleForMinimumOne(effectiveMaxBouncesForThreshold));
			}
			if (abilityLevel > 0)
			{
				float baseValue = 5f + 1f * (float)abilityLevel;
				num = Mathf.Max(num, RequiredScaleForMinimumOne(baseValue));
			}
			if (float.IsNaN(num) || float.IsInfinity(num))
			{
				return 1f;
			}
			return Mathf.Clamp01(num);
		}

		private static float GetEffectiveChargeStrengthForThreshold(ChargeHandler chargeHandler, int abilityLevel)
		{
			if (chargeHandler.chargeStrength > 0f)
			{
				return chargeHandler.chargeStrength;
			}
			if (abilityLevel <= 0)
			{
				return 0f;
			}
			return EvaluateStatWithDiminishingReturns(FeatureFlags.DHHChargeStrengthBaseValue, FeatureFlags.DHHChargeStrengthIncreasePerLevel, abilityLevel, FeatureFlags.DHHChargeStrengthThresholdLevel, FeatureFlags.DHHChargeStrengthDiminishingFactor).FinalValue;
		}

		private static float GetEffectiveMaxBouncesForThreshold(ChargeHandler chargeHandler, int abilityLevel)
		{
			if (chargeHandler.maxBounces > 0f)
			{
				return chargeHandler.maxBounces;
			}
			if (abilityLevel <= 0)
			{
				return 0f;
			}
			float baseValue = ((chargeHandler.baseMaxBounces > 0) ? ((float)chargeHandler.baseMaxBounces) : 3f);
			return Mathf.FloorToInt(EvaluateStatWithDiminishingReturns(baseValue, 0.5f, abilityLevel, 20, 0.9f).FinalValue);
		}

		private static bool TryGetEffectiveChargeAbilityLevel(ChargeHandler chargeHandler, out int abilityLevel)
		{
			abilityLevel = Mathf.Max(0, chargeHandler.AbilityLevel);
			if (abilityLevel > 0)
			{
				return true;
			}
			if (!SemiFunc.IsMasterClientOrSingleplayer() && IsLocalChargeHandler(chargeHandler) && TryGetLocalPlayerChargeUpgrade(out var upgrade))
			{
				abilityLevel = Mathf.Max(0, upgrade);
				return true;
			}
			return abilityLevel >= 0;
		}

		private static bool TryGetLocalPlayerChargeUpgrade(out int upgrade)
		{
			upgrade = 0;
			PlayerAvatar instance = PlayerAvatar.instance;
			string text = (((Object)(object)instance != (Object)null) ? SemiFunc.PlayerGetSteamID(instance) : null);
			if (string.IsNullOrWhiteSpace(text))
			{
				return false;
			}
			try
			{
				upgrade = DHHStatsManager.GetHeadChargeUpgrade(text);
				return true;
			}
			catch
			{
				return false;
			}
		}

		private static float RequiredScaleForMinimumOne(float baseValue)
		{
			if (baseValue <= 0f)
			{
				return float.PositiveInfinity;
			}
			return 1f / baseValue;
		}

		private static bool IsChargeHandlerHeadGrabbed(ChargeHandler chargeHandler)
		{
			PhysGrabObjectImpactDetector impactDetector = chargeHandler.impactDetector;
			if ((Object)(object)impactDetector == (Object)null)
			{
				return false;
			}
			PhysGrabObject physGrabObject = impactDetector.physGrabObject;
			if ((Object)(object)physGrabObject == (Object)null)
			{
				return false;
			}
			return physGrabObject.grabbed;
		}

		private static int GetUnityObjectInstanceId(Object obj)
		{
			return (obj != (Object)null) ? obj.GetInstanceID() : 0;
		}

		private static ChargeHoldState GetOrCreateChargeHoldState(int id)
		{
			if (!s_chargeHoldStates.TryGetValue(id, out ChargeHoldState value))
			{
				value = new ChargeHoldState();
				s_chargeHoldStates[id] = value;
			}
			return value;
		}

		private static bool IsChargeState(ChargeHandler chargeHandler, string stateName)
		{
			//IL_0011: 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)
			if ((Object)(object)chargeHandler == (Object)null)
			{
				return false;
			}
			ChargeState state = chargeHandler.State;
			return string.Equals(((object)(ChargeState)(ref state)).ToString(), stateName, StringComparison.Ordinal);
		}

		private static bool IsLocalChargeHandler(ChargeHandler chargeHandler)
		{
			ChargeHandler localChargeHandler = GetLocalChargeHandler();
			if ((Object)(object)localChargeHandler == (Object)null)
			{
				return false;
			}
			return localChargeHandler == chargeHandler;
		}

		private static ChargeHandler? GetLocalChargeHandler()
		{
			PlayerAvatar instance = PlayerAvatar.instance;
			if ((Object)(object)instance?.playerDeathHead == (Object)null)
			{
				return null;
			}
			return ((Component)instance.playerDeathHead).GetComponent<DeathHeadController>()?.chargeHandler;
		}

		private static void ClearChargeHoldState(ChargeHandler? chargeHandler)
		{
			if (!((Object)(object)chargeHandler == (Object)null))
			{
				int unityObjectInstanceId = GetUnityObjectInstanceId((Object)(object)chargeHandler);
				if (unityObjectInstanceId != 0)
				{
					s_chargeHoldStates.Remove(unityObjectInstanceId);
				}
				AbilityModule.SetChargeSlotActivationProgress(0f);
			}
		}

		private static void ChargeHandler_ResetState_Postfix(ChargeHandler __instance)
		{
			StopChargeWindupLoop(__instance);
			ClearChargeHoldState(__instance);
			if (!((Object)(object)__instance == (Object)null))
			{
				int abilityLevel = __instance.AbilityLevel;
				DiminishingReturnsResult stat = EvaluateStatWithDiminishingReturns(FeatureFlags.DHHChargeStrengthBaseValue, FeatureFlags.DHHChargeStrengthIncreasePerLevel, abilityLevel, FeatureFlags.DHHChargeStrengthThresholdLevel, FeatureFlags.DHHChargeStrengthDiminishingFactor);
				__instance.chargeStrength = stat.FinalValue;
				LogChargeStrength(__instance, stat);
			}
		}

		private static void ChargeHandler_EndCharge_Postfix(ChargeHandler __instance)
		{
			StopChargeWindupLoop(__instance);
			ClearChargeHoldState(__instance);
		}

		private static void StopChargeWindupLoop(ChargeHandler? chargeHandler)
		{
			if ((Object)(object)chargeHandler == (Object)null)
			{
				return;
			}
			try
			{
				DeathHeadController controller = chargeHandler.controller;
				if ((Object)(object)controller == (Object)null)
				{
					return;
				}
				ChargeEffects componentInChildren = ((Component)controller).GetComponentInChildren<ChargeEffects>(true);
				if ((Object)(object)componentInChildren != (Object)null)
				{
					try
					{
						componentInChildren.StopWindupState();
					}
					catch
					{
					}
					try
					{
						componentInChildren.StopChargeState();
						return;
					}
					catch
					{
						return;
					}
				}
				AudioHandler audioHandler = controller.audioHandler;
				if (!((Object)(object)audioHandler == (Object)null))
				{
					audioHandler.StopWindupSound();
				}
			}
			catch
			{
			}
		}

		private static DiminishingReturnsResult EvaluateStatWithDiminishingReturns(float baseValue, float increasePerLevel, int currentLevel, int thresholdLevel, float diminishingFactor)
		{
			int num = Math.Max(0, currentLevel - 1);
			int num2 = Math.Max(0, thresholdLevel - 1);
			int num3 = Mathf.Min(num, num2);
			int num4 = Mathf.Max(0, num - num2);
			float num5 = (float)num4 * Mathf.Pow(diminishingFactor, (float)num4);
			float num6 = increasePerLevel * (float)num3;
			float num7 = increasePerLevel * num5;
			float finalValue = baseValue + num6 + num7;
			return new DiminishingReturnsResult(baseValue, increasePerLevel, currentLevel, thresholdLevel, diminishingFactor, num3, num4, num6, num7, num5, finalValue);
		}

		private static string GetHandlerLabel(object? handler, string fallback)
		{
			Component val = (Component)((handler is Component) ? handler : null);
			if (val != null)
			{
				return ((Object)val).name ?? ((object)val).GetType().Name;
			}
			return handler?.GetType().Name ?? fallback;
		}

		private static void LogChargeStrength(object chargeHandler, DiminishingReturnsResult stat)
		{
			if (FeatureFlags.DebugLogging && LogLimiter.ShouldLog("Fix:Charge.Strength", 60))
			{
				string handlerLabel = GetHandlerLabel(chargeHandler, "ChargeHandler");
				string text = $"[Fix:Charge] {handlerLabel} Strength={stat.FinalValue:F3} base={stat.BaseValue:F3} inc={stat.IncreasePerLevel:F3} level={stat.AppliedLevel} fullUpgrades={stat.LinearLevels} dimUpgrades={stat.ExtraLevels} linearDelta={stat.LinearContribution:F3} dimDelta={stat.DiminishingContribution:F3} thresh={stat.ThresholdLevel} dimFactor={stat.DiminishingFactor:F3}";
				ManualLogSource? obj = s_log;
				if (obj != null)
				{
					obj.LogInfo((object)text);
				}
				Debug.Log((object)text);
			}
		}

		private static Vector3 CalculateEnemyBounceNormal(Transform? self, Vector3 enemyCenterPoint)
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0049: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)self == (Object)null)
			{
				return Vector3.up;
			}
			Vector3 val = self.TransformPoint(Vector3.up * 0.3f);
			Vector3 val2 = val - enemyCenterPoint;
			Vector3 val3 = Vector3.ProjectOnPlane(val2, Vector3.up);
			return ((Vector3)(ref val3)).normalized;
		}
	}
	[HarmonyPatch(typeof(StunHandler), "HandleStun")]
	internal static class StunHandlerReleasePatch
	{
		[CompilerGenerated]
		private sealed class <Transpiler>d__3 : IEnumerable<CodeInstruction>, IEnumerable, IEnumerator<CodeInstruction>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private CodeInstruction <>2__current;

			private int <>l__initialThreadId;

			private IEnumerable<CodeInstruction> instructions;

			public IEnumerable<CodeInstruction> <>3__instructions;

			private IEnumerator<CodeInstruction> <>s__1;

			private CodeInstruction <instruction>5__2;

			CodeInstruction IEnumerator<CodeInstruction>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <Transpiler>d__3(int <>1__state)
			{
				this.<>1__state = <>1__state;
				<>l__initialThreadId = Environment.CurrentManagedThreadId;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || (uint)(num - 1) <= 1u)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<>s__1 = null;
				<instruction>5__2 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ae: Expected O, but got Unknown
				try
				{
					switch (<>1__state)
					{
					default:
						return false;
					case 0:
						<>1__state = -1;
						<>s__1 = instructions.GetEnumerator();
						<>1__state = -3;
						break;
					case 1:
						<>1__state = -3;
						break;
					case 2:
						<>1__state = -3;
						<instruction>5__2 = null;
						break;
					}
					if (<>s__1.MoveNext())
					{
						<instruction>5__2 = <>s__1.Current;
						if (s_targetCall != null && s_replacement != null && CodeInstructionExtensions.Calls(<instruction>5__2, s_targetCall))
						{
							<>2__current = new CodeInstruction(OpCodes.Call, (object)s_replacement);
							<>1__state = 1;
							return true;
						}
						<>2__current = <instruction>5__2;
						<>1__state = 2;
						return true;
					}
					<>m__Finally1();
					<>s__1 = null;
					return false;
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			private void <>m__Finally1()
			{
				<>1__state = -1;
				if (<>s__1 != null)
				{
					<>s__1.Dispose();
				}
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}

			[DebuggerHidden]
			IEnumerator<CodeInstruction> IEnumerable<CodeInstruction>.GetEnumerator()
			{
				<Transpiler>d__3 <Transpiler>d__;
				if (<>1__state == -2 && <>l__initialThreadId == Environment.CurrentManagedThreadId)
				{
					<>1__state = 0;
					<Transpiler>d__ = this;
				}
				else
				{
					<Transpiler>d__ = new <Transpiler>d__3(0);
				}
				<Transpiler>d__.instructions = <>3__instructions;
				return <Transpiler>d__;
			}

			[DebuggerHidden]
			IEnumerator IEnumerable.GetEnumerator()
			{
				return ((IEnumerable<CodeInstruction>)this).GetEnumerator();
			}
		}

		private const int ReleaseObjectViewId = -1;

		private static readonly MethodInfo? s_targetCall = AccessTools.Method(typeof(StunHandler), "PhysObjectHurt", new Type[3]
		{
			typeof(PhysGrabObject),
			typeof(BreakImpact),
			typeof(float)
		}, (Type[])null);

		private static readonly MethodInfo? s_replacement = AccessTools.Method(typeof(StunHandlerReleasePatch), "CustomPhysObjectHurt", new Type[4]
		{
			typeof(StunHandler),
			typeof(PhysGrabObject),
			typeof(BreakImpact),
			typeof(float)
		}, (Type[])null);

		[IteratorStateMachine(typeof(<Transpiler>d__3))]
		[HarmonyTranspiler]
		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <Transpiler>d__3(-2)
			{
				<>3__instructions = instructions
			};
		}

		private static void CustomPhysObjectHurt(StunHandler self, PhysGrabObject physGrabObject, BreakImpact impact, float hitForce)
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Expected I4, but got Unknown
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)physGrabObject == (Object)null)
			{
				return;
			}
			switch (impact - 1)
			{
			case 0:
				physGrabObject.lightBreakImpulse = true;
				break;
			case 1:
				physGrabObject.mediumBreakImpulse = true;
				break;
			case 2:
				physGrabObject.heavyBreakImpulse = true;
				break;
			}
			if (!(hitForce >= 5f) || physGrabObject.playerGrabbing.Count <= 0)
			{
				return;
			}
			foreach (PhysGrabber item in physGrabObject.playerGrabbing.ToList())
			{
				if (!((Object)(object)item == (Object)null))
				{
					if (!SemiFunc.IsMultiplayer())
					{
						item.ReleaseObjectRPC(true, 2f, -1, default(PhotonMessageInfo));
						continue;
					}
					item.photonView.RPC("ReleaseObjectRPC", (RpcTarget)0, new object[3] { false, 1f, -1 });
				}
			}
		}
	}
}
namespace DeathHeadHopperFix.Modules.Gameplay.Spectate
{
	public static class AbilityBarVisibilityAnchor
	{
		private static readonly HashSet<string> ActiveDemands = new HashSet<string>();

		public static bool HasExternalDemand()
		{
			lock (ActiveDemands)
			{
				return ActiveDemands.Count > 0;
			}
		}

		public static void SetExternalDemand(string sourceId, bool active)
		{
			if (string.IsNullOrWhiteSpace(sourceId))
			{
				return;
			}
			lock (ActiveDemands)
			{
				if (active)
				{
					ActiveDemands.Add(sourceId);
				}
				else
				{
					ActiveDemands.Remove(sourceId);
				}
			}
		}

		public static void ClearExternalDemands()
		{
			lock (ActiveDemands)
			{
				ActiveDemands.Clear();
			}
		}
	}
	[HarmonyPatch(typeof(SpectateCamera), "LateUpdate")]
	internal static class AbilityBarVisibilityModule
	{
		private static Type? s_dhhAbilityManagerType;

		private static PropertyInfo? s_dhhAbilityManagerInstanceProperty;

		private static MethodInfo? s_hasEquippedAbilityMethod;

		private static Type? s_abilityUiType;

		private static FieldInfo? s_abilityUiInstanceField;

		private static MethodInfo? s_abilityUiShowMethod;

		private static Type? s_abilityEnergyUiType;

		private static FieldInfo? s_abilityEnergyUiInstanceField;

		private static MethodInfo? s_abilityEnergyUiShowMethod;

		private static bool? s_lastShouldShow;

		[HarmonyPostfix]
		private static void LateUpdatePostfix()
		{
			if (InternalDebugFlags.DisableAbilityPatches)
			{
				return;
			}
			if (!ShouldEvaluateInCurrentContext())
			{
				s_lastShouldShow = null;
				return;
			}
			if (!ShouldShowAbilityBar())
			{
				if (FeatureFlags.DebugLogging && s_lastShouldShow.GetValueOrDefault())
				{
					Debug.Log((object)"[Fix:AbilityBar] hidden by policy (native=false, external=false).");
				}
				s_lastShouldShow = false;
				return;
			}
			if (FeatureFlags.DebugLogging && !s_lastShouldShow.GetValueOrDefault())
			{
				bool flag = HasNativeEquippedAbility();
				bool flag2 = HasExternalAbilityUiDemand();
				Debug.Log((object)$"[Fix:AbilityBar] show by policy (native={flag}, external={flag2}).");
			}
			TryShowAbilityUi();
			TryShowAbilityEnergyUi();
			s_lastShouldShow = true;
		}

		private static bool ShouldShowAbilityBar()
		{
			return HasNativeEquippedAbility() || HasExternalAbilityUiDemand();
		}

		private static bool HasExternalAbilityUiDemand()
		{
			return AbilityBarVisibilityAnchor.HasExternalDemand();
		}

		private static bool ShouldEvaluateInCurrentContext()
		{
			if (!SemiFunc.RunIsLevel() && !SemiFunc.RunIsShop())
			{
				return false;
			}
			if (HasExternalAbilityUiDemand())
			{
				return true;
			}
			return SpectateContextHelper.IsLocalDeathHeadSpectated();
		}

		private static bool HasNativeEquippedAbility()
		{
			ResolveDhhAbilityManagerReflection();
			if (s_dhhAbilityManagerInstanceProperty == null || s_hasEquippedAbilityMethod == null)
			{
				return false;
			}
			object value = s_dhhAbilityManagerInstanceProperty.GetValue(null);
			if (value == null)
			{
				return false;
			}
			return (s_hasEquippedAbilityMethod.Invoke(value, null) as bool?).GetValueOrDefault();
		}

		private static void TryShowAbilityUi()
		{
			ResolveAbilityUiReflection();
			object obj = s_abilityUiInstanceField?.GetValue(null);
			if (obj == null || s_abilityUiShowMethod == null)
			{
				return;
			}
			try
			{
				s_abilityUiShowMethod.Invoke(obj, null);
			}
			catch
			{
			}
		}

		private static void TryShowAbilityEnergyUi()
		{
			ResolveAbilityEnergyUiReflection();
			object obj = s_abilityEnergyUiInstanceField?.GetValue(null);
			if (obj == null || s_abilityEnergyUiShowMethod == null)
			{
				return;
			}
			try
			{
				s_abilityEnergyUiShowMethod.Invoke(obj, null);
			}
			catch
			{
			}
		}

		private static void ResolveDhhAbilityManagerReflection()
		{
			if ((object)s_dhhAbilityManagerType == null)
			{
				s_dhhAbilityManagerType = AccessTools.TypeByName("DeathHeadHopper.Managers.DHHAbilityManager");
			}
			if (!(s_dhhAbilityManagerType == null))
			{
				if ((object)s_dhhAbilityManagerInstanceProperty == null)
				{
					s_dhhAbilityManagerInstanceProperty = s_dhhAbilityManagerType.GetProperty("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if ((object)s_hasEquippedAbilityMethod == null)
				{
					s_hasEquippedAbilityMethod = s_dhhAbilityManagerType.GetMethod("HasEquippedAbility", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				}
			}
		}

		private static void ResolveAbilityUiReflection()
		{
			if ((object)s_abilityUiType == null)
			{
				s_abilityUiType = AccessTools.TypeByName("DeathHeadHopper.UI.AbilityUI");
			}
			if (!(s_abilityUiType == null))
			{
				if ((object)s_abilityUiInstanceField == null)
				{
					s_abilityUiInstanceField = s_abilityUiType.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if ((object)s_abilityUiShowMethod == null)
				{
					s_abilityUiShowMethod = s_abilityUiType.GetMethod("Show", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				}
			}
		}

		private static void ResolveAbilityEnergyUiReflection()
		{
			if ((object)s_abilityEnergyUiType == null)
			{
				s_abilityEnergyUiType = AccessTools.TypeByName("DeathHeadHopper.UI.AbilityEnergyUI");
			}
			if (!(s_abilityEnergyUiType == null))
			{
				if ((object)s_abilityEnergyUiInstanceField == null)
				{
					s_abilityEnergyUiInstanceField = s_abilityEnergyUiType.GetField("instance", BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				}
				if ((object)s_abilityEnergyUiShowMethod == null)
				{
					s_abilityEnergyUiShowMethod = s_abilityEnergyUiType.GetMethod("Show", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
				}
			}
		}
	}
	internal static class DeathMinimapBridge
	{
		internal static void Reset()
		{
		}

		internal static void Tick()
		{
		}
	}
	[HarmonyPatch(typeof(SpectateCamera), "PlayerSwitch")]
	internal static class SpectateDeadPlayersModule
	{
		private sealed class CameraSnapshot
		{
			public string Reason = string.Empty;

			public bool LastChanceEnabled;

			public bool LastChanceActive;

			public bool AllPlayersDisabled;

			public string CurrentState = string.Empty;

			public string PlayerName = string.Empty;

			public string OverrideName = string.Empty;

			public bool CurrentPlayerDisabled;

			public bool LocalPlayerDisabled;

			public bool LocalPlayerDeadSet;

			public string? MainCameraName;

			public string? SpectateCameraName;

			public float? MainNearClip;

			public float? SpectateNearClip;

			public float? MainFov;

			public float? SpectateFov;

			public Rect? MainRect;

			public string? MainRectKey;

			public int MainCullingMask;

			public string MainClearFlags = string.Empty;

			public string? MainParentName;

			public float? FogStartDistance;

			public float? MainMinusSpectateNearClip;

			public bool HasMeaningfulDelta(CameraSnapshot other)
			{
				return !NullableFloatEquals(MainNearClip, other.MainNearClip) || !NullableFloatEquals(SpectateNearClip, other.SpectateNearClip) || !NullableFloatEquals(MainFov, other.MainFov) || !NullableRectEquals(MainRect, other.MainRect) || MainCullingMask != other.MainCullingMask || !string.Equals(MainClearFlags, other.MainClearFlags, StringComparison.Ordinal) || !string.Equals(MainParentName, other.MainParentName, StringComparison.Ordinal) || !NullableFloatEquals(FogStartDistance, other.FogStartDistance) || !NullableFloatEquals(MainMinusSpectateNearClip, other.MainMinusSpectateNearClip) || !string.Equals(MainCameraName, other.MainCameraName, StringComparison.Ordinal) || !string.Equals(SpectateCameraName, other.SpectateCameraName, StringComparison.Ordinal);
			}

			private static bool NullableFloatEquals(float? left, float? right)
			{
				if (!left.HasValue && !right.HasValue)
				{
					return true;
				}
				if (!left.HasValue || !right.HasValue)
				{
					return false;
				}
				return Mathf.Abs(left.Value - right.Value) < 0.0001f;
			}

			private static bool NullableRectEquals(Rect? left, Rect? right)
			{
				//IL_0049: 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_0051: Unknown result type (might be due to invalid IL or missing references)
				//IL_0056: Unknown result type (might be due to invalid IL or missing references)
				if (!left.HasValue && !right.HasValue)
				{
					return true;
				}
				if (!left.HasValue || !right.HasValue)
				{
					return false;
				}
				Rect value = left.Value;
				Rect value2 = right.Value;
				return Mathf.Abs(((Rect)(ref value)).x - ((Rect)(ref value2)).x) < 0.0001f && Mathf.Abs(((Rect)(ref value)).y - ((Rect)(ref value2)).y) < 0.0001f && Mathf.Abs(((Rect)(ref value)).width - ((Rect)(ref value2)).width) < 0.0001f && Mathf.Abs(((Rect)(ref value)).height - ((Rect)(ref value2)).height) < 0.0001f;
			}
		}

		private sealed class NearClipSnapshot
		{
			public string Reason = string.Empty;

			public bool LastChanceEnabled;

			public bool LastChanceActive;

			public bool AllPlayersDisabled;

			public string CurrentState = string.Empty;

			public string PlayerName = string.Empty;

			public string OverrideName = string.Empty;

			public bool CurrentPlayerDisabled;

			public bool LocalPlayerDisabled;

			public bool LocalPlayerDeadSet;

			public string? MainCameraName;

			public string? SpectateCameraName;

			public float? MainNearClip;

			public float? SpectateNearClip;

			public float? FogStartDistance;

			public float? MainMinusSpectateNearClip;

			public bool HasMeaningfulDelta(NearClipSnapshot other)
			{
				return !NullableFloatEquals(MainNearClip, other.MainNearClip) || !NullableFloatEquals(SpectateNearClip, other.SpectateNearClip) || !NullableFloatEquals(FogStartDistance, other.FogStartDistance) || !NullableFloatEquals(MainMinusSpectateNearClip, other.MainMinusSpectateNearClip) || !string.Equals(MainCameraName, other.MainCameraName, StringComparison.Ordinal) || !string.Equals(SpectateCameraName, other.SpectateCameraName, StringComparison.Ordinal);
			}

			private static bool NullableFloatEquals(float? left, float? right)
			{
				if (!left.HasValue && !right.HasValue)
				{
					return true;
				}
				if (!left.HasValue || !right.HasValue)
				{
					return false;
				}
				return Mathf.Abs(left.Value - right.Value) < 0.0001f;
			}
		}

		[CompilerGenerated]
		private sealed class <LogCameraDelayedFollowUp>d__31 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public SpectateCamera spectate;

			public CameraSnapshot baseline;

			private CameraSnapshot <current>5__1;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <LogCameraDelayedFollowUp>d__31(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<current>5__1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSecondsRealtime(3f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if ((Object)(object)spectate == (Object)null || !FeatureFlags.DebugLogging)
					{
						return false;
					}
					<current>5__1 = CaptureCameraSnapshot(spectate, baseline.Reason);
					if (!<current>5__1.HasMeaningfulDelta(baseline))
					{
						return false;
					}
					if (!LogCameraSnapshot("delayed", <current>5__1))
					{
						return false;
					}
					LogCameraDelta(baseline, <current>5__1);
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <LogCameraFollowUp>d__30 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public SpectateCamera spectate;

			public CameraSnapshot baseline;

			private CameraSnapshot <current>5__1;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <LogCameraFollowUp>d__30(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<current>5__1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = null;
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if ((Object)(object)spectate == (Object)null || !FeatureFlags.DebugLogging)
					{
						return false;
					}
					<current>5__1 = CaptureCameraSnapshot(spectate, baseline.Reason);
					if (!<current>5__1.HasMeaningfulDelta(baseline))
					{
						return false;
					}
					LogCameraDelta(baseline, <current>5__1);
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <LogNearClipDelayedFollowUp>d__39 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public SpectateCamera spectate;

			public NearClipSnapshot baseline;

			private NearClipSnapshot <current>5__1;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <LogNearClipDelayedFollowUp>d__39(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<current>5__1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSecondsRealtime(3f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if ((Object)(object)spectate == (Object)null || !FeatureFlags.DebugLogging)
					{
						return false;
					}
					<current>5__1 = CaptureNearClipSnapshot(spectate, baseline.Reason);
					if (!<current>5__1.HasMeaningfulDelta(baseline))
					{
						return false;
					}
					if (!LogNearClipSnapshot("delayed", <current>5__1))
					{
						return false;
					}
					LogNearClipDelta(baseline, <current>5__1);
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <LogNearClipFollowUp>d__38 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public SpectateCamera spectate;

			public NearClipSnapshot baseline;

			private NearClipSnapshot <current>5__1;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <LogNearClipFollowUp>d__38(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<current>5__1 = null;
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = null;
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if ((Object)(object)spectate == (Object)null || !FeatureFlags.DebugLogging)
					{
						return false;
					}
					<current>5__1 = CaptureNearClipSnapshot(spectate, baseline.Reason);
					if (!<current>5__1.HasMeaningfulDelta(baseline))
					{
						return false;
					}
					LogNearClipDelta(baseline, <current>5__1);
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		[CompilerGenerated]
		private sealed class <RunDhhFovRecovery>d__25 : IEnumerator<object>, IDisposable, IEnumerator
		{
			private int <>1__state;

			private object <>2__current;

			public SpectateCamera spectate;

			public string reason;

			private int <targetFov>5__1;

			object IEnumerator<object>.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			object IEnumerator.Current
			{
				[DebuggerHidden]
				get
				{
					return <>2__current;
				}
			}

			[DebuggerHidden]
			public <RunDhhFovRecovery>d__25(int <>1__state)
			{
				this.<>1__state = <>1__state;
			}

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0026: Unknown result type (might be due to invalid IL or missing references)
				//IL_0030: Expected O, but got Unknown
				switch (<>1__state)
				{
				default:
					return false;
				case 0:
					<>1__state = -1;
					<>2__current = (object)new WaitForSecondsRealtime(3f);
					<>1__state = 1;
					return true;
				case 1:
					<>1__state = -1;
					if ((Object)(object)spectate == (Object)null || !IsDeadPlayersSpectateEnabledNow() || !ShouldApplyDhhFovRecovery())
					{
						return false;
					}
					<targetFov>5__1 = FeatureFlags.DHHSpectateDefaultFov;
					TryRestoreDhhFov(spectate, <targetFov>5__1, reason);
					return false;
				}
			}

			bool IEnumerator.MoveNext()
			{
				//ILSpy generated this explicit interface implementation from .override directive in MoveNext
				return this.MoveNext();
			}

			[DebuggerHidden]
			void IEnumerator.Reset()
			{
				throw new NotSupportedException();
			}
		}

		private const string ModuleId = "DeathHeadHopperFix.Spectate.DeadPlayers";

		private static PlayerAvatar? s_stateNormalPatchedPlayer;

		private static Transform? s_stateNormalOriginalSpectatePoint;

		private static Transform? s_stateNormalOrbitProxy;

		private static string? s_lastNearClipSnapshotKey;

		private static string? s_lastCameraSnapshotKey;

		private static int s_lastFovRecoveryFrame = -1;

		[HarmonyPrefix]
		private static bool PlayerSwitchPrefix(SpectateCamera __instance, bool _next)
		{
			if (ShouldBlockJumpDrivenPlayerSwitch(_next))
			{
				return false;
			}
			if (ShouldBlockPlayerSwitchForLastChance())
			{
				return false;
			}
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			List<PlayerAvatar> list = GameDirector.instance?.PlayerList;
			if (list == null || list.Count == 0)
			{
				return true;
			}
			if (IsDeadPlayersSpectateEnabledNow())
			{
				TraceCameraActivation(__instance, "PlayerSwitchPrefix");
				return HandleDeadPlayersSpectateSwitch(__instance, list, _next);
			}
			return HandleVanillaEquivalentPlayerSwitch(__instance, list, _next);
		}

		private static bool HandleDeadPlayersSpectateSwitch(SpectateCamera spectate, IList<PlayerAvatar> playerList, bool next)
		{
			TraceCameraActivation(spectate, "HandleDeadPlayersSpectateSwitch");
			bool flag = true;
			foreach (PlayerAvatar player in playerList)
			{
				if ((Object)(object)player == (Object)null || player.isDisabled)
				{
					continue;
				}
				flag = false;
				break;
			}
			if (!flag)
			{
				return true;
			}
			TryPlayerSwitch(spectate, playerList, next, includeDisabled: true);
			return false;
		}

		private static bool HandleVanillaEquivalentPlayerSwitch(SpectateCamera spectate, IList<PlayerAvatar> playerList, bool next)
		{
			if (playerList.All((PlayerAvatar p) => (Object)(object)p == (Object)null || p.isDisabled))
			{
				return false;
			}
			if (TryPlayerSwitch(spectate, playerList, next, includeDisabled: false))
			{
				return false;
			}
			return true;
		}

		private static bool ShouldBlockJumpDrivenPlayerSwitch(bool next)
		{
			return next && SemiFunc.InputDown((InputKey)1) && !SemiFunc.InputDown((InputKey)23);
		}

		[HarmonyPatch(typeof(SpectateCamera), "StateNormal")]
		[HarmonyPrefix]
		private static void StateNormalPrefix(SpectateCamera __instance)
		{
			//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: 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_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)__instance == (Object)null || !IsDeadPlayersSpectateEnabledNow())
			{
				return;
			}
			PlayerAvatar player = __instance.player;
			if ((Object)(object)player == (Object)null || player == PlayerAvatar.instance || !player.isDisabled || !TryGetDeathHeadAnchor(player, out var anchor))
			{
				return;
			}
			Transform spectatePoint = player.spectatePoint;
			if ((Object)(object)spectatePoint != (Object)null)
			{
				Transform val = EnsureStateNormalOrbitProxy();
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				Vector3 val2 = Vector3.zero;
				if ((Object)(object)((Component)player).transform != (Object)null)
				{
					val2 = spectatePoint.position - ((Component)player).transform.position;
				}
				val.position = anchor + val2;
				val.rotation = spectatePoint.rotation;
				s_stateNormalPatchedPlayer = player;
				s_stateNormalOriginalSpectatePoint = spectatePoint;
				player.spectatePoint = val;
			}
			if (FeatureFlags.DebugLogging && LogLimiter.ShouldLog("Spectate.DeadPlayers.Anchor", 120))
			{
				Debug.Log((object)("[SpectateDeadPlayers] Orbit source moved to DeathHead center for " + GetPlayerName(player)));
			}
		}

		[HarmonyPatch(typeof(SpectateCamera), "StateNormal")]
		[HarmonyPostfix]
		private static void StateNormalPostfix(SpectateCamera __instance)
		{
			if ((Object)(object)s_stateNormalPatchedPlayer == (Object)null)
			{
				HandleLastChanceStateNormalPostfix(__instance);
				MaintainDhhFovState(__instance);
				return;
			}
			if ((Object)(object)s_stateNormalOriginalSpectatePoint != (Object)null)
			{
				s_stateNormalPatchedPlayer.spectatePoint = s_stateNormalOriginalSpectatePoint;
			}
			s_stateNormalPatchedPlayer = null;
			s_stateNormalOriginalSpectatePoint = null;
			HandleLastChanceStateNormalPostfix(__instance);
			MaintainDhhFovState(__instance);
		}

		[HarmonyPatch(typeof(SpectateCamera), "UpdateState")]
		[HarmonyPrefix]
		private static bool UpdateStatePrefix(SpectateCamera __instance, State _state)
		{
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Invalid comparison between Unknown and I4
			if (!LastChanceInteropBridge.IsLastChanceModeEnabled() || (Object)(object)__instance == (Object)null)
			{
				return true;
			}
			if ((int)_state != 2)
			{
				return true;
			}
			if (LastChanceInteropBridge.IsLastChanceActive())
			{
				return false;
			}
			return !LastChanceInteropBridge.AllPlayersDisabled();
		}

		private static bool IsDeadPlayersSpectateEnabledNow()
		{
			if (!LastChanceInteropBridge.IsSpectateDeadPlayersEnabled())
			{
				return false;
			}
			string text = LastChanceInteropBridge.GetSpectateDeadPlayersMode().Trim();
			if (text.Equals("Disabled", StringComparison.OrdinalIgnoreCase))
			{
				return false;
			}
			if (text.Equals("LastChanceOnly", StringComparison.OrdinalIgnoreCase))
			{
				return LastChanceInteropBridge.IsLastChanceModeEnabled() && LastChanceInteropBridge.IsLastChanceActive() && IsLocalPlayerDeadOrDisabled();
			}
			return true;
		}

		private static bool TryPlayerSwitch(SpectateCamera spectate, IList<PlayerAvatar> players, bool next, bool includeDisabled)
		{
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Unknown result type (might be due to invalid IL or missing references)
			//IL_015a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0167: Unknown result type (might be due to invalid IL or missing references)
			//IL_016c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid