Decompiled source of HaldorExpansion v1.0.0

BepInEx/plugins/aveasura-HaldorExpansion/HaldorExpansion.dll

Decompiled 2 months ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jotunn.Configs;
using Jotunn.Entities;
using Jotunn.Managers;
using Jotunn.Utils;
using UnityEngine;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("HaldorExpansion")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("aveasura")]
[assembly: AssemblyProduct("HaldorExpansion")]
[assembly: AssemblyCopyright("Copyright © aveasura 2026")]
[assembly: AssemblyTrademark("aveasura")]
[assembly: ComVisible(false)]
[assembly: Guid("8A5C36D6-1908-4277-91DF-24E5D1B9B7BA")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyVersion("1.0.0.0")]
namespace HaldorExpansion
{
	[BepInPlugin("aveasura.haldor.expansion", "Haldor Expansion", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[NetworkCompatibility(/*Could not decode attribute arguments.*/)]
	public class HaldorExpansionPlugin : BaseUnityPlugin
	{
		private sealed class CestusState
		{
			public Player Player;

			public float Charge;

			public float ShieldRemaining;

			public float ShieldMax;

			public float ShieldDecayPerTick;

			public int ShieldTicksLeft;

			public float CooldownUntil;

			public bool IsCasting;

			public float InputLockedUntil;

			public Coroutine ShieldRoutine;

			public Coroutine CastRoutine;

			public readonly List<AudioSource> PreparedExplosionAudios = new List<AudioSource>();

			public float LastChargeGainTime;

			public Coroutine ChargeDecayRoutine;

			public float LastObservedHealth;

			public float LastObservedMaxHealth;

			public bool HasObservedHealth;

			public bool WasEquippedLastFrame;

			public bool ForceHideHudBar;

			public float LastSyncedShieldRemaining = -1f;

			public float LastSyncedShieldMax = -1f;

			public bool LastSyncedShieldActive;
		}

		private sealed class CestusAnimatorSpeedState
		{
			public bool Applied;

			public float LastBaseSpeed = 1f;

			public float LastAppliedSpeed = 1f;
		}

		[HarmonyPatch(typeof(CharacterAnimEvent), "CustomFixedUpdate")]
		private static class CestusAnimatorSpeedPatch
		{
			private static readonly FieldInfo CurrentAttackField = AccessTools.Field(typeof(Humanoid), "m_currentAttack");

			private static void Postfix(Character ___m_character, Animator ___m_animator)
			{
				if ((Object)(object)___m_character == (Object)null || (Object)(object)___m_animator == (Object)null)
				{
					return;
				}
				Player val = (Player)(object)((___m_character is Player) ? ___m_character : null);
				if ((Object)(object)val == (Object)null)
				{
					return;
				}
				int instanceID = ((Object)___m_animator).GetInstanceID();
				if (!CestusAnimatorStates.TryGetValue(instanceID, out var value))
				{
					value = new CestusAnimatorSpeedState();
					CestusAnimatorStates[instanceID] = value;
				}
				float speed = ___m_animator.speed;
				float num = speed;
				if (value.Applied && Mathf.Abs(speed - value.LastAppliedSpeed) < 0.0001f)
				{
					num = value.LastBaseSpeed;
				}
				ItemData currentWeapon = ((Humanoid)val).GetCurrentWeapon();
				Attack val2 = null;
				if (CurrentAttackField != null)
				{
					object? value2 = CurrentAttackField.GetValue(val);
					val2 = (Attack)((value2 is Attack) ? value2 : null);
				}
				if (!((Character)val).InAttack() || !IsCestusWeapon(currentWeapon) || val2 == null || string.IsNullOrEmpty(val2.m_attackAnimation) || val2.m_attackAnimation.IndexOf("unarmed", StringComparison.OrdinalIgnoreCase) < 0)
				{
					value.Applied = false;
					value.LastBaseSpeed = speed;
					value.LastAppliedSpeed = speed;
					return;
				}
				float num2 = Mathf.Clamp(1.9f, 0.01f, 10f);
				float num3 = num * num2;
				if (Mathf.Abs(___m_animator.speed - num3) > 0.0001f)
				{
					___m_animator.speed = num3;
					DebugLog("Cestus animator speed applied: " + num.ToString("0.###") + " -> " + num3.ToString("0.###"));
				}
				value.Applied = true;
				value.LastBaseSpeed = num;
				value.LastAppliedSpeed = num3;
			}
		}

		[HarmonyPatch(typeof(Player), "GetTotalFoodValue")]
		private static class CestusMaxHealthPatch
		{
			private static void Postfix(Player __instance, ref float hp, ref float stamina, ref float eitr)
			{
				if (!((Object)(object)__instance == (Object)null))
				{
					if (HasEquippedCestus(__instance))
					{
						hp += 30f;
					}
					DebugLog($"[HaldorExpansion] Cestus HP bonus: equipped={HasEquippedCestus(__instance)}, hpBeforeBonus={hp}");
				}
			}
		}

		[HarmonyPatch(typeof(Player), "GetBodyArmor")]
		private static class CestusArmorPenaltyPatch
		{
			private static void Postfix(Player __instance, ref float __result)
			{
				if (!((Object)(object)__instance == (Object)null))
				{
					bool flag = HasEquippedCestus(__instance);
					if (flag)
					{
						__result = Mathf.Max(0f, __result - 15f);
					}
					DebugLog($"[HaldorExpansion] Cestus armor penalty: equipped={flag}, armorAfterPenalty={__result}");
				}
			}
		}

		private sealed class DelayedDoomDotState
		{
			public Player Player;

			public float RemainingDamage;

			public int TicksLeft;

			public Coroutine Routine;
		}

		[HarmonyPatch(typeof(Player), "GetTotalFoodValue")]
		private static class PitKingChestMaxHealthPatch
		{
			private static void Postfix(Player __instance, ref float hp, ref float stamina, ref float eitr)
			{
				if (!((Object)(object)__instance == (Object)null))
				{
					bool flag = HasPitKingChestEquipped(__instance);
					if (flag)
					{
						hp += 70f;
					}
					DebugLog($"[HaldorExpansion] Pit King chest HP bonus: equipped={flag}, hpAfterBonus={hp:0.##}");
				}
			}
		}

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

			private object <>2__current;

			public CestusState state;

			public float shieldAmount;

			private Player <player>5__2;

			private long <playerId>5__3;

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

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

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

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

			private bool MoveNext()
			{
				//IL_0189: Unknown result type (might be due to invalid IL or missing references)
				//IL_0193: Expected O, but got Unknown
				bool result;
				try
				{
					switch (<>1__state)
					{
					default:
						result = false;
						goto end_IL_0000;
					case 0:
						<>1__state = -1;
						if (state == null)
						{
							result = false;
						}
						else
						{
							<player>5__2 = state.Player;
							<playerId>5__3 = (((Object)(object)<player>5__2 != (Object)null) ? GetCestusStateKey(<player>5__2) : 0);
							state.IsCasting = true;
							state.InputLockedUntil = Time.time + 1.05f;
							state.PreparedExplosionAudios.Clear();
							<>1__state = -3;
							if ((Object)(object)<player>5__2 == (Object)null || ((Character)<player>5__2).IsDead())
							{
								result = false;
								break;
							}
							<player>5__2.StartEmote("point", true);
							ClearHumanoidBlockState((Humanoid)(object)<player>5__2);
							state.ShieldRemaining = shieldAmount;
							state.ShieldMax = shieldAmount;
							state.ShieldTicksLeft = 40;
							state.ShieldDecayPerTick = shieldAmount / 40f;
							SyncCestusShieldState(<player>5__2, state, force: true);
							if (state.ShieldRoutine != null)
							{
								((MonoBehaviour)Instance).StopCoroutine(state.ShieldRoutine);
								state.ShieldRoutine = null;
							}
							PlayCestusCastFx(<player>5__2, state, shieldAmount);
							PlayCestusCastSfx(<player>5__2);
							<>2__current = (object)new WaitForSeconds(1.05f);
							<>1__state = 1;
							result = true;
						}
						goto end_IL_0000;
					case 1:
						<>1__state = -3;
						if ((Object)(object)<player>5__2 == (Object)null || ((Character)<player>5__2).IsDead())
						{
							result = false;
							break;
						}
						state.ShieldRoutine = ((MonoBehaviour)Instance).StartCoroutine(CestusShieldRoutine(state));
						TriggerCestusNova(<player>5__2, shieldAmount);
						PlayPreparedCestusExplosionAudio(state);
						DebugLog("[HaldorExpansion] Cestus cast resolved: " + $"shield={shieldAmount:0.##}, " + $"duration={1.05f:0.##}");
						<>m__Finally1();
						result = false;
						goto end_IL_0000;
					}
					<>m__Finally1();
					end_IL_0000:;
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
				return result;
			}

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

			private void <>m__Finally1()
			{
				<>1__state = -1;
				state.IsCasting = false;
				state.InputLockedUntil = 0f;
				state.CastRoutine = null;
				if (<playerId>5__3 != 0L && ActiveCestusStates.TryGetValue(<playerId>5__3, out var value) && value == state)
				{
					value.IsCasting = false;
					value.InputLockedUntil = 0f;
					value.CastRoutine = null;
				}
			}

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

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

			private object <>2__current;

			public CestusState state;

			private long <playerId>5__2;

			private float <wait>5__3;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_007f: Unknown result type (might be due to invalid IL or missing references)
				//IL_0089: Expected O, but got Unknown
				bool result;
				try
				{
					int num = <>1__state;
					if (num != 0)
					{
						if (num == 1)
						{
							<>1__state = -3;
							Player player = state.Player;
							if ((Object)(object)player == (Object)null || ((Character)player).IsDead())
							{
								result = false;
							}
							else
							{
								if (!(state.Charge <= 0.001f))
								{
									if (!state.IsCasting && !(state.ShieldRemaining > 0.001f) && !(Time.time < state.CooldownUntil) && !(Time.time < state.LastChargeGainTime + 7f))
									{
										float charge = state.Charge;
										float num2 = 6f * <wait>5__3;
										state.Charge = Mathf.Max(0f, state.Charge - num2);
										DebugLog("[HaldorExpansion] Cestus charge decay: " + $"before={charge:0.##}, " + $"decay={num2:0.##}, " + $"after={state.Charge:0.##}");
										if (state.Charge <= 0.001f)
										{
											state.Charge = 0f;
											result = false;
											goto IL_01dc;
										}
									}
									goto IL_0078;
								}
								result = false;
							}
							goto IL_01dc;
						}
						result = false;
					}
					else
					{
						<>1__state = -1;
						if (state != null)
						{
							<playerId>5__2 = (((Object)(object)state.Player != (Object)null) ? GetCestusStateKey(state.Player) : 0);
							<wait>5__3 = Mathf.Max(0.02f, 0.2f);
							<>1__state = -3;
							goto IL_0078;
						}
						result = false;
					}
					goto end_IL_0000;
					IL_01dc:
					<>m__Finally1();
					goto end_IL_0000;
					IL_0078:
					<>2__current = (object)new WaitForSeconds(<wait>5__3);
					<>1__state = 1;
					result = true;
					end_IL_0000:;
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
				return result;
			}

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

			private void <>m__Finally1()
			{
				<>1__state = -1;
				state.ChargeDecayRoutine = null;
				if (<playerId>5__2 != 0L && ActiveCestusStates.TryGetValue(<playerId>5__2, out var value) && value == state)
				{
					value.ChargeDecayRoutine = null;
				}
			}

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

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

			private object <>2__current;

			public CestusState state;

			private float <waitPerTick>5__2;

			private long <playerId>5__3;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0072: Unknown result type (might be due to invalid IL or missing references)
				//IL_007c: Expected O, but got Unknown
				bool result;
				try
				{
					int num = <>1__state;
					if (num != 0)
					{
						if (num != 1)
						{
							result = false;
						}
						else
						{
							<>1__state = -3;
							Player player = state.Player;
							if ((Object)(object)player == (Object)null || ((Character)player).IsDead())
							{
								result = false;
							}
							else if (state.ShieldRemaining <= 0.001f || state.ShieldTicksLeft <= 0)
							{
								result = false;
							}
							else
							{
								float num2 = Mathf.Min(state.ShieldRemaining, state.ShieldDecayPerTick);
								state.ShieldRemaining = Mathf.Max(0f, state.ShieldRemaining - num2);
								state.ShieldTicksLeft--;
								if (state.ShieldRemaining <= 0.001f)
								{
									ClearCestusShieldState(player, state, stopRoutine: false, syncNow: true);
								}
								else
								{
									SyncCestusShieldState(player, state);
								}
								DebugLog("[HaldorExpansion] Cestus shield decay: " + $"decay={num2:0.##}, " + $"remaining={state.ShieldRemaining:0.##}, " + $"ticksLeft={state.ShieldTicksLeft}");
								if (!(state.ShieldRemaining <= 0.001f))
								{
									goto IL_006b;
								}
								result = false;
							}
							<>m__Finally1();
						}
					}
					else
					{
						<>1__state = -1;
						if (state != null)
						{
							<waitPerTick>5__2 = 0.15f;
							<playerId>5__3 = (((Object)(object)state.Player != (Object)null) ? GetCestusStateKey(state.Player) : 0);
							<>1__state = -3;
							goto IL_006b;
						}
						result = false;
					}
					goto end_IL_0000;
					IL_006b:
					<>2__current = (object)new WaitForSeconds(<waitPerTick>5__2);
					<>1__state = 1;
					result = true;
					end_IL_0000:;
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
				return result;
			}

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

			private void <>m__Finally1()
			{
				<>1__state = -1;
				state.ShieldRoutine = null;
				ClearCestusShieldState(state.Player, state, stopRoutine: false, syncNow: true);
				if (<playerId>5__3 != 0L && ActiveCestusStates.TryGetValue(<playerId>5__3, out var value) && value == state)
				{
					value.ShieldRoutine = null;
					ClearCestusShieldState(value.Player, value, stopRoutine: false, syncNow: true);
				}
			}

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

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

			private object <>2__current;

			public DelayedDoomDotState state;

			private float <waitPerTick>5__2;

			private int <playerId>5__3;

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

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

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

			[DebuggerHidden]
			void IDisposable.Dispose()
			{
				int num = <>1__state;
				if (num == -3 || num == 1)
				{
					try
					{
					}
					finally
					{
						<>m__Finally1();
					}
				}
				<>1__state = -2;
			}

			private bool MoveNext()
			{
				//IL_0071: Unknown result type (might be due to invalid IL or missing references)
				//IL_007b: Expected O, but got Unknown
				bool result;
				try
				{
					int num = <>1__state;
					if (num != 0)
					{
						if (num != 1)
						{
							result = false;
						}
						else
						{
							<>1__state = -3;
							Player player = state.Player;
							if ((Object)(object)player == (Object)null)
							{
								DebugLog("[HaldorExpansion] Delayed Doom routine stop: player null");
								result = false;
							}
							else if (((Character)player).IsDead())
							{
								DebugLog("[HaldorExpansion] Delayed Doom routine stop: player dead, " + GetCestusDebugPlayerTag(player));
								result = false;
							}
							else if (state.RemainingDamage <= 0f || state.TicksLeft <= 0)
							{
								DebugLog("[HaldorExpansion] Delayed Doom routine stop: exhausted, " + $"remaining={state.RemainingDamage:0.##}, " + $"ticksLeft={state.TicksLeft}, " + GetCestusDebugPlayerTag(player));
								result = false;
							}
							else
							{
								float num2 = state.RemainingDamage / (float)state.TicksLeft;
								num2 = Mathf.Max(0f, num2);
								state.RemainingDamage = Mathf.Max(0f, state.RemainingDamage - num2);
								state.TicksLeft--;
								DebugLog("[HaldorExpansion] Delayed Doom pooled tick: " + $"tick={num2:0.##}, " + $"remaining={state.RemainingDamage:0.##}, " + $"ticksLeft={state.TicksLeft}, " + GetCestusDebugPlayerTag(player));
								if (!ApplyDelayedDoomHealthTick(player, num2))
								{
									result = false;
								}
								else
								{
									if (!(state.RemainingDamage <= 0.001f))
									{
										goto IL_006a;
									}
									result = false;
								}
							}
							<>m__Finally1();
						}
					}
					else
					{
						<>1__state = -1;
						if (state != null)
						{
							<waitPerTick>5__2 = 0.5f;
							<playerId>5__3 = (((Object)(object)state.Player != (Object)null) ? ((Object)state.Player).GetInstanceID() : 0);
							<>1__state = -3;
							goto IL_006a;
						}
						result = false;
					}
					goto end_IL_0000;
					IL_006a:
					<>2__current = (object)new WaitForSeconds(<waitPerTick>5__2);
					<>1__state = 1;
					result = true;
					end_IL_0000:;
				}
				catch
				{
					//try-fault
					((IDisposable)this).Dispose();
					throw;
				}
				return result;
			}

			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 ((Object)(object)state.Player != (Object)null)
				{
					DebugLog("[HaldorExpansion] Delayed Doom routine finally: " + $"remainingBeforeClear={state.RemainingDamage:0.##}, " + $"ticksLeftBeforeClear={state.TicksLeft}, " + GetCestusDebugPlayerTag(state.Player));
				}
				state.Routine = null;
				state.RemainingDamage = 0f;
				state.TicksLeft = 0;
				if (<playerId>5__3 != 0 && ActiveDelayedDoomDots.TryGetValue(<playerId>5__3, out var value) && value == state)
				{
					ActiveDelayedDoomDots.Remove(<playerId>5__3);
				}
			}

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

		private static readonly HashSet<string> CestusItemKeys = new HashSet<string> { "$item_weapon_gritcestus" };

		internal const float CestusSetBonusMultiplier = 1.5f;

		internal static ConfigEntry<KeyCode> CestusAbilityKeyConfig;

		internal static ButtonConfig CestusAbilityButton;

		internal const float CestusChargeMax = 100f;

		internal const float CestusShieldMaxHpRatio = 0.4f;

		internal const float CestusShieldMinFlat = 20f;

		internal const float CestusShieldDuration = 6f;

		internal const int CestusShieldTicks = 40;

		internal const float CestusCooldown = 15f;

		private const string CestusShieldZdoRemainingKey = "he_cestus_shield_remaining";

		private const string CestusShieldZdoMaxKey = "he_cestus_shield_max";

		private const string CestusShieldZdoActiveKey = "he_cestus_shield_active";

		internal const string CestusActivationEmote = "point";

		internal const float CestusCastDuration = 1.05f;

		internal const float CestusNovaDamageMultiplier = 2.7f;

		internal const float CestusNovaRadius = 4f;

		internal const float CestusNovaPushForce = 20f;

		internal const string CestusCastFxPrefabName = "vfx_StaminaUpgrade";

		internal const string CestusCastSfxPrefabName = "";

		internal const float CestusCastFxAudioPitch = 1.25f;

		internal const float CestusCastFxAudioStartOffset = 0.08f;

		internal const int CestusCastFxAmount = 2;

		internal const float CestusCastFxForwardOffset = 1f;

		internal const float CestusCastFxUpOffset = 0.8f;

		internal const float CestusCastFxBaseScale = 1f;

		internal const float CestusCastFxMaxScale = 1.6f;

		internal const float CestusCastFxReferenceShield = 100f;

		internal const float CestusCastFxPlaybackSpeed = 2f;

		internal const float CestusChargePerPostArmorDamage = 0.8f;

		internal const float CestusChargeDecayDelay = 7f;

		internal const float CestusChargeDecayPerSecond = 6f;

		internal const float CestusChargeDecayTickInterval = 0.2f;

		private static readonly Dictionary<long, CestusState> ActiveCestusStates = new Dictionary<long, CestusState>();

		private static Image CestusShieldOverlayImage;

		private static RectTransform CestusShieldOverlayRect;

		private static RectTransform CestusShieldReferenceRect;

		private static Text CestusShieldText;

		private static RectTransform CestusShieldTextRect;

		private static bool CestusShieldOverlayLoggedMissing;

		private static bool _adrenalineReflectionDumped;

		private static readonly Dictionary<int, CestusAnimatorSpeedState> CestusAnimatorStates = new Dictionary<int, CestusAnimatorSpeedState>();

		internal const string GritCestusPrefabName = "Bone_crushers";

		private const string CestusEffectNameKey = "$se_weapon_gritcestus";

		private const string CestusEffectDescKey = "$se_weapon_gritcestus_desc";

		internal const string GritCestusSourcePrefab = "FistFenrirClaw";

		internal const int GritCestusPrice = 2500;

		private const string CestusItemKey = "$item_weapon_gritcestus";

		private const string CestusItemDescKey = "$item_weapon_gritcestus_desc";

		private const float GritCestusWeight = 5f;

		private const int GritCestusMaxQuality = 1;

		private const float GritCestusMaxDurability = 1800f;

		private const float GritCestusDurabilityPerLevel = 0f;

		private const float GritCestusBluntDamage = 24f;

		private const float GritCestusLightningDamage = 4f;

		internal const float GritCestusBonusMaxHealth = 30f;

		private const float GritCestusPrimaryStaggerMultiplier = 1.35f;

		private const float GritCestusSecondaryStaggerMultiplier = 2.2f;

		private const float GritCestusAttackSpeedMultiplier = 1.9f;

		private const float GritCestusArmorPenalty = 15f;

		private const float GritCestusBlockPower = 2f;

		private const float GritCestusBlockPowerPerLevel = 0f;

		private const float GritCestusDeflectionForce = 15f;

		private const float GritCestusDeflectionForcePerLevel = 0f;

		private const float GritCestusTimedBlockBonus = 2f;

		internal static GameObject GritCestusPrefab;

		internal static ItemDrop GritCestusItemDrop;

		private const string ModGuid = "aveasura.haldor.expansion";

		private const string ModName = "Haldor Expansion";

		private const string ModVersion = "1.0.0";

		internal static ManualLogSource Log;

		internal static HaldorExpansionPlugin Instance;

		private static readonly bool DebugLogging = false;

		internal const string DelayedDoomChestPrefabName = "ArmorDelayedDoomChest";

		internal const string DelayedDoomChestSourcePrefab = "ArmorPaddedCuirass";

		internal const int DelayedDoomChestPrice = 4000;

		internal const float DelayedDoomChestFoodRegenBonus = 5f;

		internal static GameObject DelayedDoomChestPrefab;

		internal static ItemDrop DelayedDoomChestItemDrop;

		private static readonly HashSet<string> DelayedDoomItemKeys = new HashSet<string> { "$item_chest_delayeddoom" };

		internal const float DelayedDoomTriggerRatio = 0.5f;

		internal const float DelayedDoomImmediateRatio = 0.5f;

		internal const float DelayedDoomDebtRatio = 0.7f;

		internal const float DelayedDoomDuration = 6f;

		internal const int DelayedDoomTicks = 12;

		private static readonly HashSet<int> DelayedDoomInternalDamageVictims = new HashSet<int>();

		private static readonly Dictionary<int, DelayedDoomDotState> ActiveDelayedDoomDots = new Dictionary<int, DelayedDoomDotState>();

		private const string EmbeddedIconPrefix = "HaldorExpansion.Assets.Icons.";

		internal const string PitKingChestPrefabName = "ArmorPitKingChest";

		internal const string PitKingChestSourcePrefab = "ArmorMageChest";

		internal const int PitKingChestPrice = 4000;

		internal const float PitKingChestBonusMaxHealth = 70f;

		private const string PitKingChestItemKey = "$item_chest_pitking";

		private const string PitKingChestItemDescKey = "$item_chest_pitking_desc";

		private const string PitKingChestEffectNameKey = "$se_pitking_vigor";

		private const string PitKingChestEffectDescKey = "$se_pitking_vigor_desc";

		private const string SteelHeartSetNameKey = "$itemset_steelheart";

		private const string SteelHeartSetDescKey = "$itemset_steelheart_desc";

		private static readonly HashSet<string> PitKingChestItemKeys = new HashSet<string> { "$item_chest_pitking" };

		internal static GameObject PitKingChestPrefab;

		internal static ItemDrop PitKingChestItemDrop;

		internal const string RingPrefabName = "BrisingamenRing";

		internal const int RingPrice = 1600;

		internal static GameObject RingPrefab;

		internal static ItemDrop RingItemDrop;

		private static bool _ringReadyLogged;

		internal const string ShadowCrossbowPrefabName = "CrossbowSilentHunt";

		internal const string ShadowCrossbowSourcePrefab = "CrossbowArbalest";

		internal const int ShadowCrossbowPrice = 2500;

		internal const string ShadowCrossbowItemKey = "$item_crossbow_silenthunt";

		internal const string ShadowCrossbowItemDescKey = "$item_crossbow_silenthunt_desc";

		internal const float ShadowCrossbowBasePierceDamage = 210f;

		internal const float ShadowCrossbowMovementModifier = -0.35f;

		internal const float ShadowCrossbowBackstabBonus = 6f;

		internal static GameObject ShadowCrossbowPrefab;

		internal static ItemDrop ShadowCrossbowItemDrop;

		private const int DefaultTrophyValue = 10;

		private static readonly Dictionary<string, int> ExtraSellableItems = new Dictionary<string, int>
		{
			{ "SurtlingCore", 30 },
			{ "BlackCore", 80 },
			{ "MoltenCore", 130 }
		};

		private static readonly Dictionary<string, int> TrophyValueOverrides = new Dictionary<string, int>
		{
			{ "TrophyDeer", 15 },
			{ "TrophyBoar", 15 },
			{ "TrophyNeck", 25 },
			{ "TrophyEikthyr", 70 },
			{ "TrophyGreydwarf", 15 },
			{ "TrophySkeleton", 15 },
			{ "TrophyGreydwarfShaman", 20 },
			{ "TrophyGhost", 25 },
			{ "TrophyGreydwarfBrute", 20 },
			{ "TrophyBjorn", 50 },
			{ "TrophyFrostTroll", 50 },
			{ "TrophySkeletonPoison", 50 },
			{ "TrophySkeletonHildir", 80 },
			{ "TrophyTheElder", 100 },
			{ "TrophySerpent", 500 },
			{ "TrophyBonemawSerpent", 500 },
			{ "TrophyBlob", 15 },
			{ "TrophyDraugr", 15 },
			{ "TrophyLeech", 15 },
			{ "TrophySurtling", 15 },
			{ "TrophyDraugrElite", 30 },
			{ "TrophyWraith", 35 },
			{ "TrophyAbomination", 75 },
			{ "TrophyKvastur", 80 },
			{ "TrophyBonemass", 150 },
			{ "TrophyWolf", 15 },
			{ "TrophyHatchling", 15 },
			{ "TrophyUlv", 20 },
			{ "TrophyFenring", 25 },
			{ "TrophyCultist", 40 },
			{ "TrophySGolem", 70 },
			{ "TrophyCultist_Hildir", 80 },
			{ "TrophyDragonQueen", 200 },
			{ "TrophyDeathsquito", 15 },
			{ "TrophyGoblin", 15 },
			{ "TrophyGrowth", 15 },
			{ "TrophyBjornUndead", 50 },
			{ "TrophyLox", 35 },
			{ "TrophyGoblinShaman", 30 },
			{ "TrophyGoblinBrute", 70 },
			{ "TrophyGoblinBruteBrosShaman", 80 },
			{ "TrophyGoblinBruteBrosBrute", 80 },
			{ "TrophyGoblinKing", 350 },
			{ "TrophySeeker", 20 },
			{ "TrophyTick", 20 },
			{ "TrophyDvergr", 20 },
			{ "TrophyHare", 20 },
			{ "TrophyGjall", 70 },
			{ "TrophySeekerBrute", 80 },
			{ "TrophySeekerQueen", 500 },
			{ "TrophyCharredArcher", 20 },
			{ "TrophyVolture", 20 },
			{ "TrophyAsksvin", 20 },
			{ "TrophyCharredMage", 25 },
			{ "TrophyCharredMelee", 25 },
			{ "TrophyMorgen", 100 },
			{ "TrophyFallenValkyrie", 120 },
			{ "TrophyFader", 1000 }
		};

		private const float WoundedBeastCapeRegenWithDelayedDoomMultiplier = 0.5f;

		internal const string WoundedBeastCapePrefabName = "CapeWoundedBeast";

		internal const string WoundedBeastCapeSourcePrefab = "CapeWolf";

		internal const int WoundedBeastCapePrice = 2500;

		internal const string WoundedBeastCapeItemKey = "$item_cape_woundedbeast";

		private const string WoundedBeastCapeItemDescKey = "$item_cape_woundedbeast_desc";

		private const string WoundedBeastCapeEffectNameKey = "$se_cape_woundedbeast";

		private const string WoundedBeastCapeEffectDescKey = "$se_cape_woundedbeast_desc";

		internal const float WoundedBeastCapeIncomingDamageMultiplier = 1.15f;

		internal const float WoundedBeastCapeDotRegenSuppressDuration = 2f;

		private const float WoundedBeastCapeRegenTickInterval = 1f;

		internal static GameObject WoundedBeastCapePrefab;

		internal static ItemDrop WoundedBeastCapeItemDrop;

		private static readonly Dictionary<long, float> WoundedBeastCapeNextRegenTickTimes = new Dictionary<long, float>();

		private static readonly Dictionary<long, float> WoundedBeastCapeRegenSuppressedUntil = new Dictionary<long, float>();

		private static long GetCestusStateKey(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return 0L;
			}
			long playerID = player.GetPlayerID();
			if (playerID != 0L)
			{
				return playerID;
			}
			return ((Object)player).GetInstanceID();
		}

		private static CestusState GetOrCreateCestusState(Player player)
		{
			long cestusStateKey = GetCestusStateKey(player);
			if (!ActiveCestusStates.TryGetValue(cestusStateKey, out var value))
			{
				value = new CestusState
				{
					Player = player,
					LastChargeGainTime = 0f,
					ChargeDecayRoutine = null,
					LastObservedHealth = ((Character)player).GetHealth(),
					LastObservedMaxHealth = ((Character)player).GetMaxHealth(),
					HasObservedHealth = true,
					WasEquippedLastFrame = false,
					ForceHideHudBar = false,
					Charge = 0f,
					ShieldRemaining = 0f,
					ShieldMax = 0f,
					ShieldDecayPerTick = 0f,
					ShieldTicksLeft = 0,
					CooldownUntil = 0f,
					IsCasting = false,
					InputLockedUntil = 0f,
					ShieldRoutine = null,
					CastRoutine = null
				};
				ActiveCestusStates[cestusStateKey] = value;
			}
			value.Player = player;
			return value;
		}

		internal static bool HasCestusEquipped(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			if (IsCestusWeapon(((Humanoid)player).GetCurrentWeapon()))
			{
				return true;
			}
			if (IsCestusWeapon(GetPlayerHandItem(player, "m_rightItem")))
			{
				return true;
			}
			if (IsCestusWeapon(GetPlayerHandItem(player, "m_leftItem")))
			{
				return true;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return false;
			}
			List<ItemData> allItems = inventory.GetAllItems();
			if (allItems == null)
			{
				return false;
			}
			foreach (ItemData item in allItems)
			{
				if (item == null || !item.m_equipped)
				{
					continue;
				}
				if (IsCestusWeapon(item))
				{
					return true;
				}
				if (item.m_shared != null)
				{
					string name = item.m_shared.m_name;
					if (!string.IsNullOrEmpty(name) && CestusItemKeys.Contains(name))
					{
						return true;
					}
				}
			}
			return false;
		}

		internal static bool ShouldProcessCestusLocally(Player player)
		{
			if ((Object)(object)player != (Object)null && (Object)(object)Player.m_localPlayer != (Object)null)
			{
				return (Object)(object)player == (Object)(object)Player.m_localPlayer;
			}
			return false;
		}

		internal static string GetCestusDebugPlayerTag(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return "player=null";
			}
			ItemData currentWeapon = ((Humanoid)player).GetCurrentWeapon();
			ItemData playerHandItem = GetPlayerHandItem(player, "m_rightItem");
			ItemData playerHandItem2 = GetPlayerHandItem(player, "m_leftItem");
			bool flag = (Object)(object)player == (Object)(object)Player.m_localPlayer;
			ZNetView playerNView = GetPlayerNView(player);
			bool flag2 = (Object)(object)playerNView != (Object)null && playerNView.IsValid() && playerNView.IsOwner();
			return "name=" + ((Object)player).name + ", " + $"local={flag}, " + $"owner={flag2}, " + $"pid={player.GetPlayerID()}, " + $"iid={((Object)player).GetInstanceID()}, " + "current=" + GetCestusItemDebugName(currentWeapon) + ", right=" + GetCestusItemDebugName(playerHandItem) + ", left=" + GetCestusItemDebugName(playerHandItem2);
		}

		private static ZNetView GetPlayerNView(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return null;
			}
			object? obj = typeof(Character).GetField("m_nview", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(player);
			return (ZNetView)((obj is ZNetView) ? obj : null);
		}

		private static bool IsPlayerNViewOwner(Player player)
		{
			ZNetView playerNView = GetPlayerNView(player);
			if ((Object)(object)playerNView != (Object)null && playerNView.IsValid())
			{
				return playerNView.IsOwner();
			}
			return false;
		}

		private static ZDO GetPlayerZdo(Player player)
		{
			ZNetView playerNView = GetPlayerNView(player);
			if (!((Object)(object)playerNView != (Object)null) || !playerNView.IsValid())
			{
				return null;
			}
			return playerNView.GetZDO();
		}

		internal static bool IsCestusShieldAuthorityRuntime(Player player)
		{
			if (!ShouldProcessCestusLocally(player))
			{
				return false;
			}
			ZNetView playerNView = GetPlayerNView(player);
			if (!((Object)(object)playerNView == (Object)null) && playerNView.IsValid())
			{
				return playerNView.IsOwner();
			}
			return true;
		}

		internal static float GetSyncedCestusShieldRemaining(Player player)
		{
			ZDO playerZdo = GetPlayerZdo(player);
			if (playerZdo == null)
			{
				return 0f;
			}
			return Mathf.Max(0f, playerZdo.GetFloat("he_cestus_shield_remaining", 0f));
		}

		internal static float GetSyncedCestusShieldMax(Player player)
		{
			ZDO playerZdo = GetPlayerZdo(player);
			if (playerZdo == null)
			{
				return 0f;
			}
			return Mathf.Max(0f, playerZdo.GetFloat("he_cestus_shield_max", 0f));
		}

		internal static float GetCestusEffectiveShieldRemaining(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return 0f;
			}
			CestusState orCreateCestusState = GetOrCreateCestusState(player);
			return Mathf.Max(Mathf.Max(0f, orCreateCestusState.ShieldRemaining), GetSyncedCestusShieldRemaining(player));
		}

		private static void SyncCestusShieldState(Player player, CestusState state, bool force = false)
		{
			if ((Object)(object)player == (Object)null || state == null || !IsPlayerNViewOwner(player))
			{
				return;
			}
			float num = Mathf.Max(0f, state.ShieldRemaining);
			float num2 = Mathf.Max(0f, state.ShieldMax);
			bool flag = num > 0.001f;
			if (force || !(Mathf.Abs(state.LastSyncedShieldRemaining - num) <= 0.01f) || !(Mathf.Abs(state.LastSyncedShieldMax - num2) <= 0.01f) || state.LastSyncedShieldActive != flag)
			{
				ZDO playerZdo = GetPlayerZdo(player);
				if (playerZdo != null)
				{
					playerZdo.Set("he_cestus_shield_remaining", num);
					playerZdo.Set("he_cestus_shield_max", num2);
					playerZdo.Set("he_cestus_shield_active", flag);
					state.LastSyncedShieldRemaining = num;
					state.LastSyncedShieldMax = num2;
					state.LastSyncedShieldActive = flag;
				}
			}
		}

		private static void ClearCestusShieldState(Player player, CestusState state, bool stopRoutine, bool syncNow)
		{
			if (state != null)
			{
				if (stopRoutine && state.ShieldRoutine != null && (Object)(object)Instance != (Object)null)
				{
					((MonoBehaviour)Instance).StopCoroutine(state.ShieldRoutine);
					state.ShieldRoutine = null;
				}
				state.ShieldRemaining = 0f;
				state.ShieldMax = 0f;
				state.ShieldDecayPerTick = 0f;
				state.ShieldTicksLeft = 0;
				if (syncNow)
				{
					SyncCestusShieldState(player, state, force: true);
					return;
				}
				state.LastSyncedShieldRemaining = -1f;
				state.LastSyncedShieldMax = -1f;
				state.LastSyncedShieldActive = false;
			}
		}

		private static ItemData GetPlayerHandItem(Player player, string fieldName)
		{
			if ((Object)(object)player == (Object)null || string.IsNullOrWhiteSpace(fieldName))
			{
				return null;
			}
			object? obj = typeof(Humanoid).GetField(fieldName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(player);
			return (ItemData)((obj is ItemData) ? obj : null);
		}

		private static string GetCestusItemDebugName(ItemData item)
		{
			if (item == null)
			{
				return "null";
			}
			if ((Object)(object)item.m_dropPrefab != (Object)null && !string.IsNullOrWhiteSpace(((Object)item.m_dropPrefab).name))
			{
				return ((Object)item.m_dropPrefab).name;
			}
			if (item.m_shared != null && !string.IsNullOrWhiteSpace(item.m_shared.m_name))
			{
				return item.m_shared.m_name;
			}
			return "unknown";
		}

		internal static bool HasFullCestusCharge(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			return GetOrCreateCestusState(player).Charge >= 99.999f;
		}

		internal static float GetCestusCharge(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return 0f;
			}
			return Mathf.Clamp(GetOrCreateCestusState(player).Charge, 0f, 100f);
		}

		internal static bool ShouldMirrorCestusToAdrenaline(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			CestusState orCreateCestusState = GetOrCreateCestusState(player);
			if (!HasCestusEquipped(player) && !(orCreateCestusState.Charge > 0.001f) && !(orCreateCestusState.ShieldRemaining > 0.001f))
			{
				return orCreateCestusState.IsCasting;
			}
			return true;
		}

		internal static bool ShouldForceHideCestusHudBar(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			CestusState orCreateCestusState = GetOrCreateCestusState(player);
			if (!HasCestusEquipped(player) && orCreateCestusState.ForceHideHudBar && orCreateCestusState.Charge <= 0.001f && orCreateCestusState.ShieldRemaining <= 0.001f)
			{
				return !orCreateCestusState.IsCasting;
			}
			return false;
		}

		internal static bool IsCestusShieldActive(Player player)
		{
			return GetCestusEffectiveShieldRemaining(player) > 0.001f;
		}

		internal static bool IsCestusCasting(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			return GetOrCreateCestusState(player).IsCasting;
		}

		internal static bool IsCestusInputLocked(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			CestusState orCreateCestusState = GetOrCreateCestusState(player);
			if (orCreateCestusState.IsCasting)
			{
				return Time.time < orCreateCestusState.InputLockedUntil;
			}
			return false;
		}

		internal static void UpdateCestusLocalEquipState(Player player)
		{
			if (ShouldProcessCestusLocally(player))
			{
				CestusState orCreateCestusState = GetOrCreateCestusState(player);
				bool flag = HasCestusEquipped(player);
				if (flag)
				{
					orCreateCestusState.ForceHideHudBar = false;
				}
				else if (orCreateCestusState.WasEquippedLastFrame)
				{
					ResetCestusState(player, orCreateCestusState, "unequipped");
					orCreateCestusState.ForceHideHudBar = true;
				}
				orCreateCestusState.WasEquippedLastFrame = flag;
			}
		}

		private static void ResetCestusState(Player player, CestusState state, string reason)
		{
			if (state != null)
			{
				if (state.ChargeDecayRoutine != null && (Object)(object)Instance != (Object)null)
				{
					((MonoBehaviour)Instance).StopCoroutine(state.ChargeDecayRoutine);
				}
				if (state.ShieldRoutine != null && (Object)(object)Instance != (Object)null)
				{
					((MonoBehaviour)Instance).StopCoroutine(state.ShieldRoutine);
				}
				if (state.CastRoutine != null && (Object)(object)Instance != (Object)null)
				{
					((MonoBehaviour)Instance).StopCoroutine(state.CastRoutine);
				}
				state.ChargeDecayRoutine = null;
				state.ShieldRoutine = null;
				state.CastRoutine = null;
				state.Charge = 0f;
				state.LastChargeGainTime = 0f;
				ClearCestusShieldState(player, state, stopRoutine: false, syncNow: true);
				state.IsCasting = false;
				state.InputLockedUntil = 0f;
				if ((Object)(object)player != (Object)null)
				{
					state.LastObservedHealth = Mathf.Max(0f, ((Character)player).GetHealth());
					state.HasObservedHealth = true;
				}
				DebugLog("[HaldorExpansion] Cestus state reset: reason=" + reason + ", " + GetCestusDebugPlayerTag(player));
			}
		}

		internal static void UpdateCestusLocalDamageCharge(Player player)
		{
			if (!ShouldProcessCestusLocally(player))
			{
				return;
			}
			CestusState orCreateCestusState = GetOrCreateCestusState(player);
			float num = Mathf.Max(0f, ((Character)player).GetHealth());
			float num2 = Mathf.Max(0f, ((Character)player).GetMaxHealth());
			if (!orCreateCestusState.HasObservedHealth)
			{
				orCreateCestusState.LastObservedHealth = num;
				orCreateCestusState.LastObservedMaxHealth = num2;
				orCreateCestusState.HasObservedHealth = true;
				return;
			}
			float lastObservedHealth = orCreateCestusState.LastObservedHealth;
			float lastObservedMaxHealth = orCreateCestusState.LastObservedMaxHealth;
			orCreateCestusState.LastObservedHealth = num;
			orCreateCestusState.LastObservedMaxHealth = num2;
			if (!HasCestusEquipped(player))
			{
				return;
			}
			float num3 = lastObservedHealth - num;
			float num4 = Mathf.Max(0f, lastObservedMaxHealth - num2);
			float num5 = num3 - num4;
			if (num5 <= 0.05f)
			{
				return;
			}
			DebugLog("[HaldorExpansion] Observed local Cestus damage: " + GetCestusDebugPlayerTag(player) + ", " + $"prevHp={lastObservedHealth:0.##}, " + $"currentHp={num:0.##}, " + $"prevMaxHp={lastObservedMaxHealth:0.##}, " + $"currentMaxHp={num2:0.##}, " + $"effectiveDelta={num5:0.##}");
			if (orCreateCestusState.ShieldRemaining > 0.001f && !((Character)player).IsDead())
			{
				float num6 = Mathf.Min(orCreateCestusState.ShieldRemaining, num5);
				if (num6 > 0.001f)
				{
					orCreateCestusState.ShieldRemaining = Mathf.Max(0f, orCreateCestusState.ShieldRemaining - num6);
					float num7 = Mathf.Min(num2, Mathf.Max(0f, num + num6));
					SetCharacterHealth((Character)(object)player, num7);
					orCreateCestusState.LastObservedHealth = num7;
					if (orCreateCestusState.ShieldRemaining <= 0.001f)
					{
						ClearCestusShieldState(player, orCreateCestusState, stopRoutine: false, syncNow: true);
					}
					else
					{
						SyncCestusShieldState(player, orCreateCestusState);
					}
					DebugLog("[HaldorExpansion] Cestus shield corrected via local observer: " + $"observedDamage={num5:0.##}, " + $"absorbed={num6:0.##}, " + $"correctedHealth={num7:0.##}, " + $"remaining={orCreateCestusState.ShieldRemaining:0.##}, " + GetCestusDebugPlayerTag(player));
					num5 = Mathf.Max(0f, num5 - num6);
				}
			}
			AddCestusCharge(player, num5);
		}

		internal static void AddCestusCharge(Player player, float postArmorDamage)
		{
			if ((Object)(object)player == (Object)null || postArmorDamage <= 0f)
			{
				return;
			}
			if (!ShouldProcessCestusLocally(player))
			{
				DebugLog("[HaldorExpansion] Skip Cestus charge on non-local player: " + GetCestusDebugPlayerTag(player));
				return;
			}
			if (!HasCestusEquipped(player))
			{
				DebugLog("[HaldorExpansion] Skip Cestus charge because weapon not detected: " + GetCestusDebugPlayerTag(player));
				return;
			}
			CestusState orCreateCestusState = GetOrCreateCestusState(player);
			if (!(Time.time < orCreateCestusState.CooldownUntil) && !(orCreateCestusState.ShieldRemaining > 0.001f) && !orCreateCestusState.IsCasting)
			{
				float charge = orCreateCestusState.Charge;
				float num = postArmorDamage * 0.8f;
				orCreateCestusState.Charge = Mathf.Clamp(orCreateCestusState.Charge + num, 0f, 100f);
				orCreateCestusState.LastChargeGainTime = Time.time;
				EnsureCestusChargeDecayRoutine(orCreateCestusState);
				DebugLog("[HaldorExpansion] Cestus charge added: " + $"damage={postArmorDamage:0.##}, " + $"added={num:0.##}, " + $"before={charge:0.##}, " + $"after={orCreateCestusState.Charge:0.##}");
			}
		}

		private static void EnsureCestusChargeDecayRoutine(CestusState state)
		{
			if (state != null && !((Object)(object)Instance == (Object)null) && state.ChargeDecayRoutine == null)
			{
				state.ChargeDecayRoutine = ((MonoBehaviour)Instance).StartCoroutine(CestusChargeDecayRoutine(state));
			}
		}

		[IteratorStateMachine(typeof(<CestusChargeDecayRoutine>d__63))]
		private static IEnumerator CestusChargeDecayRoutine(CestusState state)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <CestusChargeDecayRoutine>d__63(0)
			{
				state = state
			};
		}

		internal static float AbsorbCestusShield(Player player, float incomingDamage)
		{
			if ((Object)(object)player == (Object)null || incomingDamage <= 0f)
			{
				return 0f;
			}
			if (!IsCestusShieldAuthorityRuntime(player))
			{
				return 0f;
			}
			CestusState orCreateCestusState = GetOrCreateCestusState(player);
			if (orCreateCestusState.ShieldRemaining <= 0.001f)
			{
				return 0f;
			}
			float num = Mathf.Min(orCreateCestusState.ShieldRemaining, incomingDamage);
			orCreateCestusState.ShieldRemaining = Mathf.Max(0f, orCreateCestusState.ShieldRemaining - num);
			if (orCreateCestusState.ShieldRemaining <= 0.001f)
			{
				ClearCestusShieldState(player, orCreateCestusState, stopRoutine: false, syncNow: true);
			}
			else
			{
				SyncCestusShieldState(player, orCreateCestusState);
			}
			DebugLog("[HaldorExpansion] Cestus shield absorbed: " + $"incoming={incomingDamage:0.##}, " + $"absorbed={num:0.##}, " + $"remaining={orCreateCestusState.ShieldRemaining:0.##}, " + $"syncedRemaining={GetSyncedCestusShieldRemaining(player):0.##}, " + GetCestusDebugPlayerTag(player));
			return num;
		}

		internal static float CorrectCestusShieldAfterDamage(Player player, float beforeHealth, float afterHealth)
		{
			if ((Object)(object)player == (Object)null)
			{
				return 0f;
			}
			if (!IsCestusShieldAuthorityRuntime(player))
			{
				return 0f;
			}
			if (((Character)player).IsDead())
			{
				return 0f;
			}
			float num = beforeHealth - afterHealth;
			if (num <= 0.001f)
			{
				return 0f;
			}
			CestusState orCreateCestusState = GetOrCreateCestusState(player);
			if (orCreateCestusState.ShieldRemaining <= 0.001f)
			{
				return 0f;
			}
			float num2 = Mathf.Min(orCreateCestusState.ShieldRemaining, num);
			if (num2 <= 0.001f)
			{
				return 0f;
			}
			orCreateCestusState.ShieldRemaining = Mathf.Max(0f, orCreateCestusState.ShieldRemaining - num2);
			float num3 = Mathf.Min(GetCharacterMaxHealth((Character)(object)player), Mathf.Max(0f, afterHealth + num2));
			SetCharacterHealth((Character)(object)player, num3);
			if (orCreateCestusState.ShieldRemaining <= 0.001f)
			{
				ClearCestusShieldState(player, orCreateCestusState, stopRoutine: false, syncNow: true);
			}
			else
			{
				SyncCestusShieldState(player, orCreateCestusState);
			}
			DebugLog("[HaldorExpansion] Cestus shield corrected post-damage: " + $"before={beforeHealth:0.##}, " + $"after={afterHealth:0.##}, " + $"absorbed={num2:0.##}, " + $"newHealth={num3:0.##}, " + $"remaining={orCreateCestusState.ShieldRemaining:0.##}, " + GetCestusDebugPlayerTag(player));
			return num2;
		}

		internal static bool TryActivateCestusShield(Player player)
		{
			if ((Object)(object)Instance == (Object)null || (Object)(object)player == (Object)null)
			{
				return false;
			}
			if (!ShouldProcessCestusLocally(player))
			{
				return false;
			}
			if (!HasCestusEquipped(player))
			{
				return false;
			}
			CestusState orCreateCestusState = GetOrCreateCestusState(player);
			if (orCreateCestusState.IsCasting)
			{
				return false;
			}
			if (orCreateCestusState.ShieldRemaining > 0.001f)
			{
				return false;
			}
			if (orCreateCestusState.Charge < 99.999f)
			{
				return false;
			}
			if (Time.time < orCreateCestusState.CooldownUntil)
			{
				return false;
			}
			float characterMaxHealth = GetCharacterMaxHealth((Character)(object)player);
			if (characterMaxHealth <= 0f)
			{
				return false;
			}
			float num = Mathf.Max(20f, characterMaxHealth * 0.4f);
			bool flag = HasCestusSetBonus(player);
			float num2 = (flag ? 1.5f : 1f);
			float num3 = num * num2;
			orCreateCestusState.Charge = 0f;
			orCreateCestusState.CooldownUntil = Time.time + 15f;
			if (orCreateCestusState.CastRoutine != null)
			{
				((MonoBehaviour)Instance).StopCoroutine(orCreateCestusState.CastRoutine);
				orCreateCestusState.CastRoutine = null;
			}
			orCreateCestusState.CastRoutine = ((MonoBehaviour)Instance).StartCoroutine(CestusCastRoutine(orCreateCestusState, num3));
			DebugLog("[HaldorExpansion] Cestus cast started: " + $"baseShield={num:0.##}, " + $"shieldMultiplier={num2:0.##}, " + $"shield={num3:0.##}, " + $"setBonus={flag}, " + $"maxHealth={characterMaxHealth:0.##}, " + $"cooldownUntil={orCreateCestusState.CooldownUntil:0.##}");
			return true;
		}

		[IteratorStateMachine(typeof(<CestusCastRoutine>d__67))]
		private static IEnumerator CestusCastRoutine(CestusState state, float shieldAmount)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <CestusCastRoutine>d__67(0)
			{
				state = state,
				shieldAmount = shieldAmount
			};
		}

		private static void TriggerCestusNova(Player player, float shieldAmount)
		{
			//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_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || shieldAmount <= 0f)
			{
				return;
			}
			float num = shieldAmount * 2.7f;
			if (num <= 0f)
			{
				return;
			}
			Vector3 cestusNovaOrigin = GetCestusNovaOrigin(player);
			int num2 = 0;
			List<Character> allCharacters = Character.GetAllCharacters();
			if (allCharacters == null)
			{
				return;
			}
			foreach (Character item in allCharacters)
			{
				if (ShouldHitCestusNovaTarget(player, item, cestusNovaOrigin))
				{
					ApplyCestusNovaDamage(player, item, cestusNovaOrigin, num);
					num2++;
				}
			}
			DebugLog("[HaldorExpansion] Cestus nova triggered: " + $"shield={shieldAmount:0.##}, " + $"damage={num:0.##}, " + $"radius={4f:0.##}, " + $"hits={num2}");
		}

		private static bool ShouldHitCestusNovaTarget(Player player, Character target, Vector3 origin)
		{
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || (Object)(object)target == (Object)null)
			{
				return false;
			}
			if (player == target)
			{
				return false;
			}
			if (target.IsDead() || target.IsPlayer())
			{
				return false;
			}
			if (!BaseAI.IsEnemy((Character)(object)player, target))
			{
				return false;
			}
			float num = 4f + Mathf.Max(0f, target.GetRadius());
			return Vector3.Distance(origin, GetCestusNovaTargetPoint(target)) <= num;
		}

		private static void ApplyCestusNovaDamage(Player player, Character target, Vector3 origin, float damage)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: 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_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Expected O, but got Unknown
			//IL_0057: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)player == (Object)null) && !((Object)(object)target == (Object)null) && !(damage <= 0f))
			{
				Vector3 cestusNovaTargetPoint = GetCestusNovaTargetPoint(target);
				Vector3 val = cestusNovaTargetPoint - origin;
				if (((Vector3)(ref val)).sqrMagnitude <= 0.0001f)
				{
					val = ((Component)player).transform.forward;
				}
				HitData val2 = new HitData();
				val2.m_damage.m_blunt = damage;
				val2.m_point = cestusNovaTargetPoint;
				val2.m_dir = ((Vector3)(ref val)).normalized;
				val2.m_pushForce = 20f;
				val2.m_skill = (SkillType)11;
				TryAssignHitAttacker(val2, (Character)(object)player);
				target.Damage(val2);
			}
		}

		private static Vector3 GetCestusNovaOrigin(Player player)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return Vector3.zero;
			}
			return ((Character)player).GetCenterPoint();
		}

		private static Vector3 GetCestusNovaTargetPoint(Character target)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)target == (Object)null)
			{
				return Vector3.zero;
			}
			return target.GetCenterPoint();
		}

		private static void TryAssignHitAttacker(HitData hit, Character attacker)
		{
			if (hit == null || (Object)(object)attacker == (Object)null)
			{
				return;
			}
			MethodInfo method = typeof(HitData).GetMethod("SetAttacker", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic, null, new Type[1] { typeof(Character) }, null);
			if (method != null)
			{
				method.Invoke(hit, new object[1] { attacker });
				return;
			}
			FieldInfo field = typeof(HitData).GetField("m_attacker", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			if (field != null && field.FieldType.IsAssignableFrom(((object)attacker).GetType()))
			{
				field.SetValue(hit, attacker);
			}
		}

		private static void PlayCestusCastFx(Player player, CestusState state, float shieldAmount)
		{
			//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_0055: 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)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null || (Object)(object)ZNetScene.instance == (Object)null || string.IsNullOrWhiteSpace("vfx_StaminaUpgrade"))
			{
				return;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab("vfx_StaminaUpgrade");
			if ((Object)(object)prefab == (Object)null)
			{
				DebugLog("[HaldorExpansion] Missing Cestus FX prefab: vfx_StaminaUpgrade");
				return;
			}
			Vector3 cestusCastFxOrigin = GetCestusCastFxOrigin(player);
			Quaternion val = Quaternion.LookRotation(((Component)player).transform.forward, Vector3.up);
			float num = Mathf.Clamp01(shieldAmount / Mathf.Max(1f, 100f));
			float num2 = Mathf.Lerp(1f, 1.6f, num);
			int num3 = Mathf.Max(1, 2);
			for (int i = 0; i < num3; i++)
			{
				GameObject obj = Object.Instantiate<GameObject>(prefab, cestusCastFxOrigin, val);
				Transform transform = obj.transform;
				transform.localScale *= num2;
				TrySpeedUpCestusFx(obj, state);
			}
		}

		private static void TrySpeedUpCestusFx(GameObject fxInstance, CestusState state)
		{
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)fxInstance == (Object)null)
			{
				return;
			}
			Animator[] componentsInChildren = fxInstance.GetComponentsInChildren<Animator>(true);
			foreach (Animator val in componentsInChildren)
			{
				if ((Object)(object)val != (Object)null)
				{
					val.speed *= 2f;
				}
			}
			ParticleSystem[] componentsInChildren2 = fxInstance.GetComponentsInChildren<ParticleSystem>(true);
			foreach (ParticleSystem val2 in componentsInChildren2)
			{
				if (!((Object)(object)val2 == (Object)null))
				{
					MainModule main = val2.main;
					((MainModule)(ref main)).simulationSpeed = ((MainModule)(ref main)).simulationSpeed * 2f;
				}
			}
			ZSFX[] componentsInChildren3 = fxInstance.GetComponentsInChildren<ZSFX>(true);
			DebugLog($"[HaldorExpansion] ZSFX found: {componentsInChildren3.Length}");
			ZSFX[] array = componentsInChildren3;
			foreach (ZSFX val3 in array)
			{
				if ((Object)(object)val3 == (Object)null)
				{
					continue;
				}
				AudioSource cestusFxAudioSource = GetCestusFxAudioSource(val3);
				AudioClip[] cestusFxAudioClips = GetCestusFxAudioClips(val3);
				if ((Object)(object)cestusFxAudioSource == (Object)null)
				{
					continue;
				}
				string text = ((Object)((Component)val3).gameObject).name.ToLowerInvariant();
				DebugLog("[HaldorExpansion] ZSFX name=" + ((Object)((Component)val3).gameObject).name + ", " + $"clips={((cestusFxAudioClips != null) ? cestusFxAudioClips.Length : 0)}, " + "clip=" + (((Object)(object)cestusFxAudioSource.clip != (Object)null) ? ((Object)cestusFxAudioSource.clip).name : "null") + ", " + $"isPlaying={cestusFxAudioSource.isPlaying}, playOnAwake={cestusFxAudioSource.playOnAwake}, " + $"pitch={cestusFxAudioSource.pitch:0.##}");
				((Behaviour)val3).enabled = false;
				cestusFxAudioSource.playOnAwake = false;
				cestusFxAudioSource.Stop();
				if (cestusFxAudioClips != null && cestusFxAudioClips.Length != 0 && !((Object)(object)cestusFxAudioClips[0] == (Object)null))
				{
					cestusFxAudioSource.clip = cestusFxAudioClips[0];
					if (text.Contains("expl"))
					{
						state?.PreparedExplosionAudios.Add(cestusFxAudioSource);
						DebugLog("[HaldorExpansion] Prepared explosion audio: " + ((Object)((Component)val3).gameObject).name + ", clip=" + ((Object)cestusFxAudioSource.clip).name);
						continue;
					}
					cestusFxAudioSource.pitch *= 1.25f;
					float num = 0f;
					num = (cestusFxAudioSource.time = Mathf.Clamp(0.08f, 0f, Mathf.Max(0f, cestusFxAudioSource.clip.length - 0.01f)));
					cestusFxAudioSource.Play();
					DebugLog("[HaldorExpansion] Manual FX sound play: name=" + ((Object)((Component)val3).gameObject).name + ", clip=" + ((Object)cestusFxAudioSource.clip).name + ", " + $"pitch={cestusFxAudioSource.pitch:0.##}, startOffset={num:0.##}");
				}
			}
		}

		internal static bool HasCestusSetBonus(Player player)
		{
			if ((Object)(object)player != (Object)null && HasCestusEquipped(player))
			{
				return HasPitKingChestEquipped(player);
			}
			return false;
		}

		private static void PlayPreparedCestusExplosionAudio(CestusState state)
		{
			if (state == null || state.PreparedExplosionAudios == null || state.PreparedExplosionAudios.Count == 0)
			{
				return;
			}
			foreach (AudioSource preparedExplosionAudio in state.PreparedExplosionAudios)
			{
				if (!((Object)(object)preparedExplosionAudio == (Object)null) && !((Object)(object)preparedExplosionAudio.clip == (Object)null))
				{
					preparedExplosionAudio.Stop();
					preparedExplosionAudio.time = 0f;
					preparedExplosionAudio.Play();
					DebugLog("[HaldorExpansion] Explosion audio played: " + $"clip={((Object)preparedExplosionAudio.clip).name}, pitch={preparedExplosionAudio.pitch:0.##}");
				}
			}
			state.PreparedExplosionAudios.Clear();
		}

		private static AudioSource GetCestusFxAudioSource(ZSFX sfx)
		{
			if ((Object)(object)sfx == (Object)null)
			{
				return null;
			}
			object? obj = typeof(ZSFX).GetField("m_audioSource", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(sfx);
			return (AudioSource)((obj is AudioSource) ? obj : null);
		}

		private static AudioClip[] GetCestusFxAudioClips(ZSFX sfx)
		{
			if ((Object)(object)sfx == (Object)null)
			{
				return null;
			}
			return typeof(ZSFX).GetField("m_audioClips", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(sfx) as AudioClip[];
		}

		private static void PlayCestusCastSfx(Player player)
		{
			//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_0055: 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)
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)player == (Object)null) && !((Object)(object)ZNetScene.instance == (Object)null) && !string.IsNullOrWhiteSpace(""))
			{
				GameObject prefab = ZNetScene.instance.GetPrefab("");
				if ((Object)(object)prefab == (Object)null)
				{
					DebugLog("[HaldorExpansion] Missing Cestus SFX prefab: ");
					return;
				}
				Vector3 cestusCastFxOrigin = GetCestusCastFxOrigin(player);
				Quaternion val = Quaternion.LookRotation(((Component)player).transform.forward, Vector3.up);
				Object.Instantiate<GameObject>(prefab, cestusCastFxOrigin, val);
			}
		}

		private static Vector3 GetCestusCastFxOrigin(Player player)
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: 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_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0009: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)player == (Object)null)
			{
				return Vector3.zero;
			}
			return ((Character)player).GetCenterPoint() + ((Component)player).transform.forward * 1f + Vector3.up * 0.8f;
		}

		[IteratorStateMachine(typeof(<CestusShieldRoutine>d__82))]
		private static IEnumerator CestusShieldRoutine(CestusState state)
		{
			//yield-return decompiler failed: Unexpected instruction in Iterator.Dispose()
			return new <CestusShieldRoutine>d__82(0)
			{
				state = state
			};
		}

		internal static void ClearHumanoidAttackState(Humanoid humanoid)
		{
			if (!((Object)(object)humanoid == (Object)null))
			{
				SetFieldIfExists(humanoid, "m_attack", false);
				SetFieldIfExists(humanoid, "m_secondaryAttack", false);
				SetFieldIfExists(humanoid, "m_attackHold", false);
			}
		}

		internal static void ClearHumanoidBlockState(Humanoid humanoid)
		{
			if (!((Object)(object)humanoid == (Object)null))
			{
				SetFieldIfExists(humanoid, "m_blocking", false);
				SetFieldIfExists(humanoid, "m_blockTimer", 0f);
			}
		}

		internal static float GetCestusShieldNormalized(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return 0f;
			}
			CestusState orCreateCestusState = GetOrCreateCestusState(player);
			if (orCreateCestusState.ShieldRemaining <= 0.001f || orCreateCestusState.ShieldMax <= 0.001f)
			{
				return 0f;
			}
			return Mathf.Clamp01(orCreateCestusState.ShieldRemaining / orCreateCestusState.ShieldMax);
		}

		internal static float GetCestusShieldBarNormalized(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return 0f;
			}
			CestusState orCreateCestusState = GetOrCreateCestusState(player);
			if (orCreateCestusState.ShieldRemaining <= 0.001f)
			{
				return 0f;
			}
			float characterMaxHealth = GetCharacterMaxHealth((Character)(object)player);
			if (characterMaxHealth <= 0f)
			{
				return 0f;
			}
			return Mathf.Clamp01(orCreateCestusState.ShieldRemaining / characterMaxHealth);
		}

		internal static float GetCestusHealthNormalized(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return 0f;
			}
			float characterMaxHealth = GetCharacterMaxHealth((Character)(object)player);
			if (characterMaxHealth <= 0f)
			{
				return 0f;
			}
			return Mathf.Clamp01(((Character)player).GetHealth() / characterMaxHealth);
		}

		private void RegisterCestusInput()
		{
			//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_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			CestusAbilityKeyConfig = ((BaseUnityPlugin)this).Config.Bind<KeyCode>("Cestus", "Ability Key", (KeyCode)325, "Key for activating the Cestus ability");
			CestusAbilityButton = new ButtonConfig
			{
				Name = "CestusAbility",
				Config = CestusAbilityKeyConfig,
				HintToken = "$cestus_ability",
				BlockOtherInputs = true
			};
			InputManager.Instance.AddButton("aveasura.haldor.expansion", CestusAbilityButton);
		}

		internal static void UpdateCestusShieldOverlay(Hud hud, Player player)
		{
			//IL_0120: Unknown result type (might be due to invalid IL or missing references)
			//IL_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0138: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0170: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)hud == (Object)null)
			{
				return;
			}
			EnsureCestusShieldOverlay(hud);
			if ((Object)(object)CestusShieldOverlayImage == (Object)null || (Object)(object)CestusShieldOverlayRect == (Object)null || (Object)(object)CestusShieldReferenceRect == (Object)null || (Object)(object)CestusShieldText == (Object)null || (Object)(object)CestusShieldTextRect == (Object)null)
			{
				return;
			}
			if ((Object)(object)player == (Object)null || ((Character)player).IsDead())
			{
				if (((Component)CestusShieldOverlayImage).gameObject.activeSelf)
				{
					((Component)CestusShieldOverlayImage).gameObject.SetActive(false);
				}
				if (((Component)CestusShieldText).gameObject.activeSelf)
				{
					((Component)CestusShieldText).gameObject.SetActive(false);
				}
				return;
			}
			CestusState orCreateCestusState = GetOrCreateCestusState(player);
			float cestusShieldBarNormalized = GetCestusShieldBarNormalized(player);
			bool flag = cestusShieldBarNormalized > 0.001f;
			if (((Component)CestusShieldOverlayImage).gameObject.activeSelf != flag)
			{
				((Component)CestusShieldOverlayImage).gameObject.SetActive(flag);
			}
			if (((Component)CestusShieldText).gameObject.activeSelf != flag)
			{
				((Component)CestusShieldText).gameObject.SetActive(flag);
			}
			if (flag)
			{
				float cestusHealthNormalized = GetCestusHealthNormalized(player);
				CopyRectTransform(CestusShieldReferenceRect, CestusShieldOverlayRect);
				Rect rect = CestusShieldReferenceRect.rect;
				float width = ((Rect)(ref rect)).width;
				Vector2 anchoredPosition = CestusShieldReferenceRect.anchoredPosition;
				float num = width * cestusShieldBarNormalized;
				float num2 = width * cestusHealthNormalized;
				CestusShieldOverlayRect.anchoredPosition = anchoredPosition + new Vector2(num2, 0f);
				CestusShieldOverlayImage.fillAmount = cestusShieldBarNormalized;
				((Transform)CestusShieldTextRect).rotation = Quaternion.identity;
				CestusShieldText.text = Mathf.CeilToInt(orCreateCestusState.ShieldRemaining).ToString();
				CestusShieldTextRect.sizeDelta = new Vector2(Mathf.Max(36f, num), 18f);
				CestusShieldTextRect.anchoredPosition = new Vector2(num * 0.5f, 0f);
			}
		}

		private static void EnsureCestusShieldOverlay(Hud hud)
		{
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_019d: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0201: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_0225: Unknown result type (might be due to invalid IL or missing references)
			//IL_022f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_030c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0320: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)CestusShieldOverlayImage != (Object)null && (Object)(object)CestusShieldOverlayRect != (Object)null && (Object)(object)CestusShieldReferenceRect != (Object)null && (Object)(object)CestusShieldText != (Object)null && (Object)(object)CestusShieldTextRect != (Object)null)
			{
				return;
			}
			Component val = FindReferenceHealthBarComponent(hud);
			if ((Object)(object)val == (Object)null)
			{
				if (!CestusShieldOverlayLoggedMissing)
				{
					CestusShieldOverlayLoggedMissing = true;
					DebugLog("[HaldorExpansion] Cestus segmented HUD: health bar component not found");
				}
				return;
			}
			RectTransform component = val.GetComponent<RectTransform>();
			if ((Object)(object)component == (Object)null || (Object)(object)((Transform)component).parent == (Object)null)
			{
				if (!CestusShieldOverlayLoggedMissing)
				{
					CestusShieldOverlayLoggedMissing = true;
					DebugLog("[HaldorExpansion] Cestus segmented HUD: reference RectTransform not found");
				}
				return;
			}
			GameObject val2 = new GameObject("HaldorExpansion_CestusShieldSegment", new Type[1] { typeof(RectTransform) });
			RectTransform component2 = val2.GetComponent<RectTransform>();
			((Transform)component2).SetParent(((Transform)component).parent, false);
			CopyRectTransform(component, component2);
			Image val3 = val2.AddComponent<Image>();
			((Graphic)val3).raycastTarget = false;
			val3.type = (Type)3;
			val3.fillMethod = (FillMethod)0;
			val3.fillOrigin = 0;
			val3.fillAmount = 0f;
			((Graphic)val3).color = new Color(0.62f, 0.62f, 0.62f, 0.78f);
			Image val4 = val.GetComponent<Image>();
			if ((Object)(object)val4 == (Object)null)
			{
				val4 = val.GetComponentInChildren<Image>(true);
			}
			if ((Object)(object)val4 != (Object)null)
			{
				val3.sprite = val4.sprite;
				((Graphic)val3).material = ((Graphic)val4).material;
				val3.preserveAspect = val4.preserveAspect;
			}
			((Transform)component2).SetSiblingIndex(((Transform)component).GetSiblingIndex() + 1);
			val2.SetActive(false);
			GameObject val5 = new GameObject("HaldorExpansion_CestusShieldText", new Type[1] { typeof(RectTransform) });
			RectTransform component3 = val5.GetComponent<RectTransform>();
			((Transform)component3).SetParent((Transform)(object)component2, false);
			component3.anchorMin = new Vector2(0f, 0.5f);
			component3.anchorMax = new Vector2(0f, 0.5f);
			component3.pivot = new Vector2(0.5f, 0.5f);
			component3.sizeDelta = new Vector2(64f, 18f);
			((Transform)component3).localScale = Vector3.one;
			((Transform)component3).rotation = Quaternion.identity;
			component3.anchoredPosition = Vector2.zero;
			Text val6 = val5.AddComponent<Text>();
			((Graphic)val6).raycastTarget = false;
			val6.alignment = (TextAnchor)4;
			val6.horizontalOverflow = (HorizontalWrapMode)1;
			val6.verticalOverflow = (VerticalWrapMode)1;
			val6.resizeTextForBestFit = false;
			val6.fontSize = 14;
			val6.fontStyle = (FontStyle)1;
			((Graphic)val6).color = new Color(1f, 1f, 1f, 0.95f);
			val6.text = string.Empty;
			Text val7 = val.GetComponent<Text>();
			if ((Object)(object)val7 == (Object)null)
			{
				val7 = val.GetComponentInChildren<Text>(true);
			}
			if ((Object)(object)val7 != (Object)null && (Object)(object)val7.font != (Object)null)
			{
				val6.font = val7.font;
			}
			else
			{
				val6.font = Resources.GetBuiltinResource<Font>("Arial.ttf");
			}
			Outline obj = val5.AddComponent<Outline>();
			((Shadow)obj).effectColor = new Color(0f, 0f, 0f, 0.9f);
			((Shadow)obj).effectDistance = new Vector2(1f, -1f);
			val5.SetActive(false);
			CestusShieldOverlayImage = val3;
			CestusShieldOverlayRect = component2;
			CestusShieldReferenceRect = component;
			CestusShieldText = val6;
			CestusShieldTextRect = component3;
			CestusShieldOverlayLoggedMissing = false;
			DebugLog("[HaldorExpansion] Cestus segmented HUD with text created");
		}

		private static Component FindReferenceHealthBarComponent(Hud hud)
		{
			FieldInfo[] fields = typeof(Hud).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic);
			Component val = null;
			FieldInfo[] array = fields;
			foreach (FieldInfo fieldInfo in array)
			{
				if (fieldInfo.FieldType == null || fieldInfo.FieldType.Name != "GuiBar")
				{
					continue;
				}
				object? value = fieldInfo.GetValue(hud);
				Component val2 = (Component)((value is Component) ? value : null);
				if ((Object)(object)val2 == (Object)null)
				{
					continue;
				}
				string text = fieldInfo.Name.ToLowerInvariant();
				if (text.Contains("health") || text.Contains("hp"))
				{
					if (text.Contains("fast"))
					{
						return val2;
					}
					if ((Object)(object)val == (Object)null)
					{
						val = val2;
					}
				}
			}
			return val;
		}

		private static void CopyRectTransform(RectTransform source, RectTransform target)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			target.anchorMin = source.anchorMin;
			target.anchorMax = source.anchorMax;
			target.pivot = source.pivot;
			target.anchoredPosition = source.anchoredPosition;
			target.sizeDelta = source.sizeDelta;
			((Transform)target).localScale = ((Transform)source).localScale;
			((Transform)target).localRotation = ((Transform)source).localRotation;
			target.offsetMin = source.offsetMin;
			target.offsetMax = source.offsetMax;
		}

		internal static void DumpAdrenalineReflection()
		{
			if (_adrenalineReflectionDumped)
			{
				return;
			}
			_adrenalineReflectionDumped = true;
			try
			{
				Assembly assembly = typeof(Player).Assembly;
				DebugLog("[HaldorExpansion] ===== ADRENALINE / TRINKET REFLECTION DUMP START =====");
				string[] needles = new string[2] { "adren", "trinket" };
				Type[] array = (from t in assembly.GetTypes()
					where needles.Any((string n) => (t.FullName ?? string.Empty).IndexOf(n, StringComparison.OrdinalIgnoreCase) >= 0 || t.Name.IndexOf(n, StringComparison.OrdinalIgnoreCase) >= 0)
					orderby t.FullName
					select t).ToArray();
				foreach (Type type in array)
				{
					DebugLog("[HaldorExpansion] [Reflect] TYPE: " + type.FullName);
					FieldInfo[] fields = type.GetFields(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (FieldInfo fieldInfo in fields)
					{
						DebugLog("[HaldorExpansion] [Reflect]   FIELD: " + fieldInfo.FieldType.Name + " " + fieldInfo.Name);
					}
					PropertyInfo[] properties = type.GetProperties(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (PropertyInfo propertyInfo in properties)
					{
						DebugLog("[HaldorExpansion] [Reflect]   PROP: " + propertyInfo.PropertyType.Name + " " + propertyInfo.Name);
					}
					MethodInfo[] methods = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
					foreach (MethodInfo i in methods)
					{
						if (needles.Any((string n) => i.Name.IndexOf(n, StringComparison.OrdinalIgnoreCase) >= 0))
						{
							string text = string.Join(", ", from p in i.GetParameters()
								select p.ParameterType.Name + " " + p.Name);
							DebugLog("[HaldorExpansion] [Reflect]   METHOD: " + i.ReturnType.Name + " " + i.Name + "(" + text + ")");
						}
					}
				}
				DumpRelevantMembers(typeof(Player), "Player");
				DumpRelevantMembers(typeof(Hud), "Hud");
				DumpRelevantMembers(typeof(Humanoid), "Humanoid");
				DumpRelevantMembers(typeof(InventoryGui), "InventoryGui");
				DebugLog("[HaldorExpansion] ===== ADRENALINE / TRINKET REFLECTION DUMP END =====");
			}
			catch (Exception arg)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogError((object)$"[HaldorExpansion] Adrenaline reflection dump failed: {arg}");
				}
			}
		}

		private static void DumpRelevantMembers(Type t, string label)
		{
			try
			{
				string[] source = new string[2] { "adren", "trinket" };
				DebugLog("[HaldorExpansion] [Reflect] ===== " + label + " relevant members =====");
				FieldInfo[] fields = t.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (FieldInfo f in fields)
				{
					if (source.Any((string n) => f.Name.IndexOf(n, StringComparison.OrdinalIgnoreCase) >= 0))
					{
						DebugLog("[HaldorExpansion] [Reflect]   FIELD: " + f.FieldType.Name + " " + f.Name);
					}
				}
				PropertyInfo[] properties = t.GetProperties(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (PropertyInfo p2 in properties)
				{
					if (source.Any((string n) => p2.Name.IndexOf(n, StringComparison.OrdinalIgnoreCase) >= 0))
					{
						DebugLog("[HaldorExpansion] [Reflect]   PROP: " + p2.PropertyType.Name + " " + p2.Name);
					}
				}
				MethodInfo[] methods = t.GetMethods(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic);
				foreach (MethodInfo i in methods)
				{
					if (source.Any((string n) => i.Name.IndexOf(n, StringComparison.OrdinalIgnoreCase) >= 0))
					{
						string text = string.Join(", ", from p in i.GetParameters()
							select p.ParameterType.Name + " " + p.Name);
						DebugLog("[HaldorExpansion] [Reflect]   METHOD: " + i.ReturnType.Name + " " + i.Name + "(" + text + ")");
					}
				}
			}
			catch (Exception arg)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogError((object)$"[HaldorExpansion] DumpRelevantMembers failed for {label}: {arg}");
				}
			}
		}

		internal static bool EnsureCestusReady()
		{
			//IL_0172: Unknown result type (might be due to invalid IL or missing references)
			//IL_017c: Expected O, but got Unknown
			try
			{
				if ((Object)(object)ObjectDB.instance == (Object)null)
				{
					return false;
				}
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Bone_crushers");
				if ((Object)(object)itemPrefab != (Object)null)
				{
					GritCestusPrefab = itemPrefab;
					GritCestusItemDrop = itemPrefab.GetComponent<ItemDrop>();
					if ((Object)(object)GritCestusItemDrop != (Object)null)
					{
						ConfigureCestusShared(GritCestusItemDrop.m_itemData.m_shared);
						ApplyBoneCrushersIcon(GritCestusItemDrop.m_itemData.m_shared);
						GritCestusItemDrop.m_itemData.m_dropPrefab = itemPrefab;
					}
					DebugLog("[HaldorExpansion] Cestus already in ObjectDB: Bone_crushers");
					return (Object)(object)GritCestusItemDrop != (Object)null;
				}
				GameObject itemPrefab2 = ObjectDB.instance.GetItemPrefab("FistFenrirClaw");
				if ((Object)(object)itemPrefab2 == (Object)null)
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogWarning((object)"[HaldorExpansion] Source cestus prefab not found: FistFenrirClaw");
					}
					return false;
				}
				GameObject val = PrefabManager.Instance.CreateClonedPrefab("Bone_crushers", ((Object)itemPrefab2).name);
				if ((Object)(object)val == (Object)null)
				{
					ManualLogSource log2 = Log;
					if (log2 != null)
					{
						log2.LogWarning((object)"[HaldorExpansion] Failed to clone cestus prefab");
					}
					return false;
				}
				ItemDrop component = val.GetComponent<ItemDrop>();
				if ((Object)(object)component == (Object)null)
				{
					ManualLogSource log3 = Log;
					if (log3 != null)
					{
						log3.LogWarning((object)"[HaldorExpansion] ItemDrop missing on cloned cestus prefab");
					}
					return false;
				}
				ConfigureCestusShared(component.m_itemData.m_shared);
				ApplyBoneCrushersIcon(component.m_itemData.m_shared);
				component.m_itemData.m_dropPrefab = val;
				ItemManager.Instance.AddItem(new CustomItem(val, true));
				GritCestusPrefab = val;
				GritCestusItemDrop = component;
				DebugLog("[HaldorExpansion] Cestus registered: Bone_crushers (source=" + ((Object)itemPrefab2).name + ")");
				return true;
			}
			catch (Exception arg)
			{
				ManualLogSource log4 = Log;
				if (log4 != null)
				{
					log4.LogError((object)$"[HaldorExpansion] EnsureCestusReady error: {arg}");
				}
				return false;
			}
		}

		private static void ConfigureCestusShared(SharedData shared)
		{
			if (shared != null)
			{
				shared.m_name = "$item_weapon_gritcestus";
				shared.m_description = "$item_weapon_gritcestus_desc";
				shared.m_maxStackSize = 1;
				shared.m_weight = 5f;
				shared.m_value = 0;
				shared.m_maxQuality = 1;
				shared.m_canBeReparied = true;
				shared.m_useDurability = true;
				shared.m_maxDurability = 1800f;
				shared.m_durabilityPerLevel = 0f;
				shared.m_damages.m_slash = 0f;
				shared.m_damages.m_blunt = 24f;
				shared.m_damages.m_pierce = 0f;
				shared.m_damages.m_fire = 0f;
				shared.m_damages.m_frost = 0f;
				shared.m_damages.m_lightning = 4f;
				shared.m_damages.m_poison = 0f;
				shared.m_damages.m_spirit = 0f;
				shared.m_damagesPerLevel.m_slash = 0f;
				shared.m_damagesPerLevel.m_blunt = 0f;
				shared.m_damagesPerLevel.m_pierce = 0f;
				shared.m_damagesPerLevel.m_fire = 0f;
				shared.m_damagesPerLevel.m_frost = 0f;
				shared.m_damagesPerLevel.m_lightning = 0f;
				shared.m_damagesPerLevel.m_poison = 0f;
				shared.m_damagesPerLevel.m_spirit = 0f;
				shared.m_blockPower = 2f;
				shared.m_blockPowerPerLevel = 0f;
				shared.m_deflectionForce = 15f;
				shared.m_deflectionForcePerLevel = 0f;
				shared.m_timedBlockBonus = 2f;
				if (shared.m_attack != null)
				{
					shared.m_attack.m_attackStamina = 12f;
					shared.m_attack.m_staggerMultiplier = 1.35f;
				}
				if (shared.m_secondaryAttack != null)
				{
					shared.m_secondaryAttack.m_attackStamina = 20f;
					shared.m_secondaryAttack.m_staggerMultiplier = 2.2f;
				}
				shared.m_backstabBonus = 3f;
				ApplyCestusEquipBonuses(shared);
				shared.m_canBeReparied = true;
				shared.m_useDurability = true;
				ApplySteelHeartSet(shared);
			}
		}

		private static void ApplyCestusEquipBonuses(SharedData shared)
		{
			if (shared != null)
			{
				ApplyCestusVisualEffect(shared);
			}
		}

		private static bool IsCestusWeapon(ItemData item)
		{
			if (item == null)
			{
				return false;
			}
			if ((Object)(object)item.m_dropPrefab != (Object)null && ((Object)item.m_dropPrefab).name == "Bone_crushers")
			{
				return true;
			}
			if (item.m_shared != null && item.m_shared.m_name == "$item_weapon_gritcestus")
			{
				return true;
			}
			return false;
		}

		internal static void RegisterCestusRecipe()
		{
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Unknown result type (might be due to invalid IL or missing references)
			//IL_012e: Expected O, but got Unknown
			//IL_0135: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Expected O, but got Unknown
			try
			{
				string text = "Recipe_Bone_crushers";
				if (ItemManager.Instance.GetRecipe(text) != null)
				{
					DebugLog("[HaldorExpansion] Recipe already registered: " + text);
					return;
				}
				ObjectDB instance = ObjectDB.instance;
				GameObject val = ((instance != null) ? instance.GetItemPrefab("FistFenrirClaw") : null);
				if ((Object)(object)val == (Object)null)
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogWarning((object)"[HaldorExpansion] Source prefab not found for cestus recipe");
					}
					return;
				}
				ItemDrop component = val.GetComponent<ItemDrop>();
				if ((Object)(object)component == (Object)null)
				{
					ManualLogSource log2 = Log;
					if (log2 != null)
					{
						log2.LogWarning((object)"[HaldorExpansion] Source ItemDrop not found for cestus recipe");
					}
					return;
				}
				Recipe recipe = ObjectDB.instance.GetRecipe(component.m_itemData);
				if ((Object)(object)recipe == (Object)null)
				{
					ManualLogSource log3 = Log;
					if (log3 != null)
					{
						log3.LogWarning((object)"[HaldorExpansion] Source recipe not found for cestus");
					}
					return;
				}
				string craftingStation = (((Object)(object)recipe.m_craftingStation != (Object)null) ? ((Object)recipe.m_craftingStation).name : "forge");
				RecipeConfig val2 = new RecipeConfig
				{
					Name = text,
					Item = "Bone_crushers",
					Amount = 1,
					CraftingStation = craftingStation,
					RepairStation = "forge",
					MinStationLevel = 1,
					Enabled = false,
					Requirements = Array.Empty<RequirementConfig>()
				};
				ItemManager.Instance.AddRecipe(new CustomRecipe(val2));
				DebugLog("[HaldorExpansion] Registered recipe via Jotunn for Bone_crushers");
			}
			catch (Exception arg)
			{
				ManualLogSource log4 = Log;
				if (log4 != null)
				{
					log4.LogError((object)$"[HaldorExpansion] RegisterCestusRecipe error: {arg}");
				}
			}
		}

		internal static bool FixCestusItemsInInventory(Player player)
		{
			try
			{
				if ((Object)(object)player == (Object)null || (Object)(object)ObjectDB.instance == (Object)null)
				{
					return false;
				}
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("Bone_crushers");
				if ((Object)(object)itemPrefab == (Object)null)
				{
					return false;
				}
				ItemDrop component = itemPrefab.GetComponent<ItemDrop>();
				if ((Object)(object)component == (Object)null)
				{
					return false;
				}
				Inventory inventory = ((Humanoid)player).GetInventory();
				if (inventory == null)
				{
					return false;
				}
				bool result = false;
				List<ItemData> allItems = inventory.GetAllItems();
				if (allItems == null)
				{
					return false;
				}
				foreach (ItemData item in allItems)
				{
					if (item != null && item.m_shared != null && !(item.m_shared.m_name != "$item_weapon_gritcestus"))
					{
						result = true;
						int num = Mathf.Max(1, item.m_quality);
						float durability = item.m_durability;
						bool equipped = item.m_equipped;
						int variant = item.m_variant;
						long crafterID = item.m_crafterID;
						string crafterName = item.m_crafterName;
						item.m_dropPrefab = itemPrefab;
						item.m_shared = component.m_itemData.m_shared;
						item.m_quality = Mathf.Clamp(num, 1, Mathf.Max(1, item.m_shared.m_maxQuality));
						item.m_variant = variant;
						item.m_crafterID = crafterID;
						item.m_crafterName = crafterName;
						item.m_equipped = equipped;
						float num2 = item.m_shared.m_maxDurability + item.m_shared.m_durabilityPerLevel * (float)(item.m_quality - 1);
						item.m_durability = Mathf.Clamp(durability, 0f, Mathf.Max(1f, num2));
						item.m_shared.m_canBeReparied = true;
						item.m_shared.m_useDurability = true;
					}
				}
				return result;
			}
			catch (Exception arg)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogError((object)$"[HaldorExpansion] FixCestusItemsInInventory error: {arg}");
				}
				return false;
			}
		}

		private static bool HasEquippedCestus(Player player)
		{
			if ((Object)(object)player == (Object)null)
			{
				return false;
			}
			Inventory inventory = ((Humanoid)player).GetInventory();
			if (inventory == null)
			{
				return false;
			}
			List<ItemData> allItems = inventory.GetAllItems();
			if (allItems == null)
			{
				return false;
			}
			foreach (ItemData item in allItems)
			{
				if (item != null && item.m_equipped && IsCestusWeapon(item))
				{
					return true;
				}
			}
			return false;
		}

		private static void ApplyCestusVisualEffect(SharedData shared)
		{
			if (shared != null)
			{
				SE_Stats val = ScriptableObject.CreateInstance<SE_Stats>();
				((Object)val).name = "SE_GritCestusVisual";
				SetFieldIfExists(val, "m_name", "$se_weapon_gritcestus");
				SetFieldIfExists(val, "m_tooltip", "$se_weapon_gritcestus_desc");
				shared.m_equipStatusEffect = (StatusEffect)(object)val;
			}
		}

		private static void ApplyBoneCrushersIcon(SharedData shared)
		{
			if (shared != null && !TrySetEmbeddedIcon(shared, "Bone_crushers.png"))
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogWarning((object)"[HaldorExpansion] Embedded icon missing for Bone crushers: Bone_crushers.png");
				}
			}
		}

		internal static void DebugLog(string message)
		{
			if (DebugLogging)
			{
				ManualLogSource log = Log;
				if (log != null)
				{
					log.LogInfo((object)message);
				}
			}
		}

		private void Awake()
		{
			Instance = this;
			Log = ((BaseUnityPlugin)this).Logger;
			AddLocalizations();
			Harmony.CreateAndPatchAll(Assembly.GetExecutingAssembly(), "aveasura.haldor.expansion");
			if (DebugLogging)
			{
				DumpAdrenalineReflection();
			}
			RegisterCestusInput();
		}

		private static void ApplyDelayedDoomChestVisualEffect(SharedData shared)
		{
			if (shared != null)
			{
				SE_Stats val = ScriptableObject.CreateInstance<SE_Stats>();
				((Object)val).name = "SE_DelayedDoomChestVisual";
				SetFieldIfExists(val, "m_name", "$se_delayeddoom_regen");
				SetFieldIfExists(val, "m_tooltip", "$se_delayeddoom_regen_desc");
				shared.m_equipStatusEffect = (StatusEffect)(object)val;
			}
		}

		internal static bool EnsureDelayedDoomChestReady()
		{
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Expected O, but got Unknown
			try
			{
				if ((Object)(object)ObjectDB.instance == (Object)null)
				{
					return false;
				}
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("ArmorDelayedDoomChest");
				if ((Object)(object)itemPrefab != (Object)null)
				{
					DelayedDoomChestPrefab = itemPrefab;
					DelayedDoomChestItemDrop = itemPrefab.GetComponent<ItemDrop>();
					if ((Object)(object)DelayedDoomChestItemDrop != (Object)null)
					{
						SharedData shared = DelayedDoomChestItemDrop.m_itemData.m_shared;
						ConfigureDelayedDoomChestShared(shared);
						ApplyDelayedDoomChestIcon(shared);
						DelayedDoomChestItemDrop.m_itemData.m_dropPrefab = itemPrefab;
						shared.m_canBeReparied = true;
						shared.m_useDurability = true;
					}
					DebugLog("[HaldorExpansion] Delayed Doom chest already in ObjectDB: ArmorDelayedDoomChest");
					return (Object)(object)DelayedDoomChestItemDrop != (Object)null;
				}
				GameObject itemPrefab2 = ObjectDB.instance.GetItemPrefab("ArmorPaddedCuirass");
				if ((Object)(object)itemPrefab2 == (Object)null)
				{
					ManualLogSource log = Log;
					if (log != null)
					{
						log.LogWarning((object)"[HaldorExpansion] Source chest prefab not found: ArmorPaddedCuirass");
					}
					return false;
				}
				GameObject val = PrefabManager.Instance.CreateClonedPrefab("ArmorDelayedDoomChest", ((Object)itemPrefab2).name);
				if ((Object)(object)val == (Object)null)
				{
					ManualLogSource log2 = Log;
					if (log2 != null)
					{
						log2.LogWarning((object)"[HaldorExpansion] Failed to clone delayed doom chest prefab");
					}
					return false;
				}
				ItemDrop component = val.GetComponent<ItemDrop>();
				if ((Object)(object)component == (Object)null)
				{
					ManualLogSource log3 = Log;
					if (log3 != null)
					{
						log3.LogWarning((object)"[HaldorExpansion] ItemDrop missing on delayed doom chest");
					}
					return false;
				}
				SharedData shared2 = component.m_itemData.m_shared;
				ConfigureDelayedDoomChestShared(shared2);
				ApplyDelayedDoomChestIcon(shared2);
				component.m_itemData.m_dropPrefab = val;
				ItemManager.Instance.AddItem(new CustomItem(val, true));
				DelayedDoomChestPrefab = val;
				DelayedDoomChestItemDrop = component;
				DebugLog("[HaldorExpansion] Delayed Doom chest registered: ArmorDelayedDoomChest (source=" + ((Object)itemPrefab2).name + ")");
				return true;
			}
			catch (Exception arg)
			{
				ManualLogSource log4 = Log;
				if (log4 != null)
				{
					log4.LogError((object)$"[HaldorExpansion] EnsureDelayedDoomChestReady error: {arg}");
				}
				return false;
			}
		}

		private static void ConfigureDelayedDoomChestShared(SharedData shared)
		{
			if (shared != null)
			{
				shared.m_name = "$item_chest_delayeddoom";
				shared.m_description = "$item_chest_delayeddoom_desc";
				shared.m_armor = 32f;
				shared.m_maxQuality = 1;
				shared.m_weight = 5f;
				shared.m_value = 0;
				shared.m_maxDurability = 2100f;
				shared.m_durabilityPerLevel = 0f;
				shared.m_useDurability = true;
				shared.m_canBeReparied = true;
				shared.m_setName = "";
				shared.m_setSize = 0;
				shared.m_setStatusEffect = null;
				ApplyDelayedDoomChestVisualEffect(shared);
			}
		}

		internal static bool FixDelayedDoomItemsInInventory(Player player)
		{
			try
			{
				if ((Object)(object)player == (Object)null || (Object)(object)ObjectDB.instance == (Object)null)
				{
					return false;
				}
				GameObject itemPrefab = ObjectDB.instance.GetItemPrefab("ArmorDelayedDoomChest");
				if ((Object)(object)itemPrefab == (Object)null)
				{
					return false;
				}
				ItemDrop component = itemPrefab.GetComponent<ItemDrop>();
				if ((Object)(object)component == (Object)null)
				{
					return false;
				}
				Inventory inventory = ((Humanoid)player).GetInventory();
				if (inventory == null)
				{
					return false;
				}
				bool flag = false;
				bool flag2 = false;
				List<ItemData> allItems = inventory.GetAllItems();
				if (allItems == null)
				{
					return false;
				}
				foreach (ItemData item in allItems)
				{
					if (item != null && item.m_shared != null && !(item.m_shared.m_name != "$item_chest_delayeddoom"))
					{
						flag = true;
						int num = Mathf.Max(1, item.m_quality);
						float durability = item.m_durability;
						bool equipped = item.m_equipped;
						int variant = item.m_variant;
						long crafterID = item.m_crafterID;
						string crafterName = item.m_crafterName;
						item.m_dropPrefab = itemPrefab;
						item.m_shared = component.m_itemData.m_shared;
						item.m_quality = Mathf.Clamp(num, 1, Mathf.Max(1, item.m_shared.m_maxQuality));
						item.m_variant = variant;
						item.m_crafterID = crafterID;
						item.m_crafterName = crafterName;
						item.m_equipped = equipped;
						float num2 = item.m_shared.m_maxDurability + item.m_shared.m_durabilityPerLevel * (float)(item.m_quality - 1);
						item.m_durability = Mathf.Clamp(durability, 0f, Mathf.Max(1f, num2));
						item.m_shared.m_canBeReparied = true;
						item.m_shared.m_useDurability = true;
						flag2 = true;
					}
				}
				if (flag)
				{
					Recipe recipe = ObjectDB.instance.GetRecipe(component.m_itemData);
					string[] obj = new string[10]
					{
						"[HaldorExpansion] Inventory rebind checked: ",
						$"changed={flag2}, ",
						"recipe=",
						((Object)(object)recipe != (Object)null) ? ((Object)recipe).name : "null",
						", craft=",
						null,
						null,
						null,
						null,
						null
					};
					object obj2;
					if (!((Object)(object)recipe != (Object)null))
					{
						obj2 = "null";
					}
					else
					{
						CraftingStation craftingStation = recipe.m_craftingStation;
						obj2 = ((craftingStation != null) ? ((Object)craftingStation).name : null);
					}
					obj[5] = (string)obj2;
					obj[6] = ", repair=";
					object obj3;
					if (!((Object)(object)recipe != (Object)null))
					{
						obj3 = "null";
					}
					else
					{
						CraftingStation repairStation = recipe.m_repairStation;
						obj3 = ((repairStation != null) ? ((Object)repairStation).name : null);
					}
					obj[7] = (string)obj3;
					obj[8] = ", dropPrefab=";