Decompiled source of PVPVEserver v6.9.6

BepInEx/plugins/JewelcraftingKarmaDrops.dll

Decompiled 2 weeks ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Jewelcrafting;
using Jewelcrafting.GemEffects;
using Jewelcrafting.LootSystem;
using Microsoft.CodeAnalysis;
using ServerSync;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8", FrameworkDisplayName = ".NET Framework 4.8")]
[assembly: AssemblyCompany("sighsorry")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyDescription("Restricts Jewelcrafting creature-death loot and applies narrow Jewelcrafting compatibility fixes.")]
[assembly: AssemblyFileVersion("1.2.0.0")]
[assembly: AssemblyInformationalVersion("1.2.0+efbdeeb3b6eb978567aa8af3378051ffbbfacb07")]
[assembly: AssemblyProduct("Jewelcrafting Karma Drops")]
[assembly: AssemblyTitle("JewelcraftingKarmaDrops")]
[assembly: AssemblyVersion("1.2.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

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

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

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace JewelcraftingKarmaDrops
{
	[HarmonyPatch(typeof(Character), "RPC_Damage", new Type[]
	{
		typeof(long),
		typeof(HitData)
	})]
	internal static class CharacterRpcDamagePatch
	{
		[HarmonyPriority(800)]
		private static void Prefix(Character __instance, HitData hit, out KarmaDeathAttribution.RpcDamageScope __state)
		{
			__state = KarmaDeathAttribution.BeginRpcDamage(__instance, hit);
		}

		private static Exception? Finalizer(Exception? __exception, KarmaDeathAttribution.RpcDamageScope __state)
		{
			KarmaDeathAttribution.EndRpcDamage(__state);
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Character), "ApplyDamage", new Type[]
	{
		typeof(HitData),
		typeof(bool),
		typeof(bool),
		typeof(DamageModifier)
	})]
	internal static class CharacterApplyDamagePatch
	{
		private static void Prefix(Character __instance, HitData hit, out KarmaDeathAttribution.ApplyDamageScope __state)
		{
			__state = KarmaDeathAttribution.BeginApplyDamage(__instance, hit);
		}

		private static void Postfix(Character __instance, HitData hit, KarmaDeathAttribution.ApplyDamageScope __state)
		{
			KarmaDeathAttribution.CompleteApplyDamage(__instance, hit, __state);
		}
	}
	[HarmonyPatch(typeof(SE_Poison), "AddDamage", new Type[] { typeof(float) })]
	internal static class PoisonSourcePatch
	{
		private static void Prefix(SE_Poison __instance, float damage, out bool __state)
		{
			__state = damage >= ValheimPrivateFields.GetPoisonDamageLeft(__instance);
		}

		private static void Postfix(SE_Poison __instance, bool __state)
		{
			KarmaDeathAttribution.RecordPoisonSource(__instance, __state);
		}
	}
	[HarmonyPatch(typeof(SE_Burning), "AddFireDamage", new Type[] { typeof(float) })]
	internal static class FireSourcePatch
	{
		private static void Prefix(SE_Burning __instance, out bool __state)
		{
			__state = ValheimPrivateFields.GetFireDamageLeft(__instance) <= 0f;
		}

		private static void Postfix(SE_Burning __instance, bool __state, bool __result)
		{
			KarmaDeathAttribution.RecordFireSource(__instance, __state, __result);
		}
	}
	[HarmonyPatch(typeof(SE_Burning), "AddSpiritDamage", new Type[] { typeof(float) })]
	internal static class SpiritSourcePatch
	{
		private static void Prefix(SE_Burning __instance, out bool __state)
		{
			__state = ValheimPrivateFields.GetSpiritDamageLeft(__instance) <= 0f;
		}

		private static void Postfix(SE_Burning __instance, bool __state, bool __result)
		{
			KarmaDeathAttribution.RecordSpiritSource(__instance, __state, __result);
		}
	}
	[HarmonyPatch(typeof(SE_Poison), "UpdateStatusEffect", new Type[] { typeof(float) })]
	internal static class PoisonTickPatch
	{
		private static void Prefix(SE_Poison __instance, out KarmaDeathAttribution.DelayedTickScope __state)
		{
			__state = KarmaDeathAttribution.BeginPoisonTick(__instance);
		}

		private static Exception? Finalizer(Exception? __exception, KarmaDeathAttribution.DelayedTickScope __state)
		{
			KarmaDeathAttribution.EndDelayedTick(__state);
			return __exception;
		}
	}
	[HarmonyPatch(typeof(SE_Burning), "UpdateStatusEffect", new Type[] { typeof(float) })]
	internal static class BurningTickPatch
	{
		private static void Prefix(SE_Burning __instance, out KarmaDeathAttribution.DelayedTickScope __state)
		{
			__state = KarmaDeathAttribution.BeginBurningTick(__instance);
		}

		private static Exception? Finalizer(Exception? __exception, KarmaDeathAttribution.DelayedTickScope __state)
		{
			KarmaDeathAttribution.EndDelayedTick(__state);
			return __exception;
		}
	}
	[HarmonyPatch(typeof(Character), "Heal")]
	internal static class CharacterHealPatch
	{
		private static void Postfix(Character __instance)
		{
			KarmaDeathAttribution.ClearRecoveredDeathCredit(__instance);
		}
	}
	[HarmonyPatch(typeof(Character), "SetHealth", new Type[] { typeof(float) })]
	internal static class CharacterSetHealthPatch
	{
		private static void Postfix(Character __instance)
		{
			KarmaDeathAttribution.ClearRecoveredDeathCredit(__instance);
		}
	}
	[HarmonyPatch(typeof(Character), "OnDestroy")]
	internal static class CharacterOnDestroyPatch
	{
		private static void Prefix(Character __instance)
		{
			JewelcraftingDropGate.EndDeath(__instance);
			KarmaDeathAttribution.ForgetCharacter(__instance);
		}
	}
	[HarmonyPatch(typeof(ZNetScene), "OnDestroy")]
	internal static class ZNetSceneOnDestroyPatch
	{
		private static void Prefix()
		{
			JewelcraftingDropGate.ResetRuntimeState();
			KarmaDeathAttribution.Reset();
		}
	}
	internal static class JewelcraftingDropGate
	{
		private sealed class DeathEligibility
		{
			internal Character Target;

			internal bool Allowed;
		}

		[HarmonyPatch(typeof(Character), "OnDeath")]
		private static class DeathScopePatch
		{
			[HarmonyPriority(800)]
			private static void Prefix(Character __instance)
			{
				BeginDeath(__instance);
			}

			[HarmonyPriority(0)]
			private static Exception? Finalizer(Character __instance, Exception? __exception)
			{
				EndDeath(__instance);
				KarmaDeathAttribution.ForgetCharacter(__instance);
				return __exception;
			}
		}

		[HarmonyPatch(typeof(EquipmentDrops), "DoDrop")]
		private static class RequireKarmaKillForEquipmentDrops
		{
			private static bool Prefix(CharacterDrop characterDrop)
			{
				return IsAllowed(characterDrop);
			}
		}

		[HarmonyPatch]
		private static class RequireKarmaKillForChestDrops
		{
			private static MethodBase TargetMethod()
			{
				return RequireMethod("Jewelcrafting.LootSystem.ChestDrops+AddGemChestDrop", "Postfix");
			}

			private static bool Prefix(object[] __args)
			{
				if (__args.Length != 0)
				{
					object obj = __args[0];
					CharacterDrop val = (CharacterDrop)((obj is CharacterDrop) ? obj : null);
					if (val != null)
					{
						return IsAllowed(val);
					}
				}
				return false;
			}
		}

		private static readonly Dictionary<Func<Character, IEnumerable<Drop>>, Func<Character, IEnumerable<Drop>>> ownWrappers = new Dictionary<Func<Character, IEnumerable<Drop>>, Func<Character, IEnumerable<Drop>>>();

		private static readonly Dictionary<int, DeathEligibility> currentDeaths = new Dictionary<int, DeathEligibility>();

		internal static void Register()
		{
			WrapLootDelegates();
		}

		internal static void Reset()
		{
			List<Func<Character, IEnumerable<Drop>>> loot = LootAdder.Loot;
			for (int i = 0; i < loot.Count; i++)
			{
				Func<Character, IEnumerable<Drop>> func = loot[i];
				for (int j = 0; j < 8; j++)
				{
					if (!ownWrappers.TryGetValue(func, out Func<Character, IEnumerable<Drop>> value))
					{
						break;
					}
					func = value;
				}
				loot[i] = func;
			}
			ownWrappers.Clear();
			currentDeaths.Clear();
		}

		internal static void ResetRuntimeState()
		{
			currentDeaths.Clear();
		}

		internal static void BeginDeath(Character character)
		{
			WrapLootDelegates();
			currentDeaths[((Object)character).GetInstanceID()] = new DeathEligibility
			{
				Target = character,
				Allowed = KarmaDeathAttribution.IsKarmaEligibleKill(character)
			};
		}

		internal static void EndDeath(Character? character)
		{
			if (!((Object)(object)character == (Object)null))
			{
				int instanceID = ((Object)character).GetInstanceID();
				if (currentDeaths.TryGetValue(instanceID, out DeathEligibility value) && value.Target == character)
				{
					currentDeaths.Remove(instanceID);
				}
			}
		}

		internal static bool IsDeathInProgress(Character? character)
		{
			if ((Object)(object)character != (Object)null && currentDeaths.TryGetValue(((Object)character).GetInstanceID(), out DeathEligibility value))
			{
				return value.Target == character;
			}
			return false;
		}

		private static bool IsAllowed(Character? character)
		{
			if ((Object)(object)character == (Object)null)
			{
				return false;
			}
			if (currentDeaths.TryGetValue(((Object)character).GetInstanceID(), out DeathEligibility value) && value.Target == character)
			{
				return value.Allowed;
			}
			return KarmaDeathAttribution.IsKarmaEligibleKill(character);
		}

		private static bool IsAllowed(CharacterDrop? characterDrop)
		{
			if ((Object)(object)characterDrop != (Object)null)
			{
				Character character = ValheimPrivateFields.GetCharacter(characterDrop);
				if (character != null)
				{
					return IsAllowed(character);
				}
			}
			return false;
		}

		private static void WrapLootDelegates()
		{
			List<Func<Character, IEnumerable<Drop>>> loot = LootAdder.Loot;
			for (int i = 0; i < loot.Count; i++)
			{
				Func<Character, IEnumerable<Drop>> original = loot[i];
				if (!ownWrappers.ContainsKey(original))
				{
					Func<Character, IEnumerable<Drop>> key = (loot[i] = (Character character) => (!IsAllowed(character)) ? Enumerable.Empty<Drop>() : original(character));
					ownWrappers[key] = original;
				}
			}
		}

		private static MethodInfo RequireMethod(string typeName, string methodName)
		{
			return AccessTools.DeclaredMethod(AccessTools.TypeByName(typeName) ?? throw new MissingMethodException(typeName, methodName), methodName, (Type[])null, (Type[])null) ?? throw new MissingMethodException(typeName, methodName);
		}
	}
	internal static class KarmaDeathAttribution
	{
		internal enum DelayedAttributionKind
		{
			Unknown,
			Unattributed,
			Exact,
			Ambiguous
		}

		internal readonly struct DelayedAttribution
		{
			internal DelayedAttributionKind Kind { get; }

			internal ZDOID Source { get; }

			internal bool SourceWasPlayer { get; }

			internal bool IsExact
			{
				get
				{
					//IL_000a: Unknown result type (might be due to invalid IL or missing references)
					//IL_000f: Unknown result type (might be due to invalid IL or missing references)
					if (Kind == DelayedAttributionKind.Exact)
					{
						return Source != ZDOID.None;
					}
					return false;
				}
			}

			internal DelayedAttribution(DelayedAttributionKind kind, ZDOID source, bool sourceWasPlayer = false)
			{
				//IL_0008: 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)
				Kind = kind;
				Source = source;
				SourceWasPlayer = sourceWasPlayer;
			}

			internal static DelayedAttribution FromSource(ZDOID source, bool sourceWasPlayer)
			{
				//IL_0000: Unknown result type (might be due to invalid IL or missing references)
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0017: Unknown result type (might be due to invalid IL or missing references)
				//IL_000e: Unknown result type (might be due to invalid IL or missing references)
				if (!(source == ZDOID.None))
				{
					return new DelayedAttribution(DelayedAttributionKind.Exact, source, sourceWasPlayer);
				}
				return new DelayedAttribution(DelayedAttributionKind.Unattributed, ZDOID.None);
			}
		}

		private sealed class DelayedSourceLedger
		{
			internal Character Target;

			internal SE_Poison? PoisonStatus;

			internal DelayedAttribution Poison;

			internal SE_Burning? FireStatus;

			internal DelayedAttribution Fire;

			internal SE_Burning? SpiritStatus;

			internal DelayedAttribution Spirit;
		}

		private readonly struct DelayedDeathCredit
		{
			internal Character Target { get; }

			internal ZDOID Source { get; }

			internal bool SourceWasPlayer { get; }

			internal DelayedDeathCredit(Character target, ZDOID source, bool sourceWasPlayer)
			{
				//IL_0008: 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)
				Target = target;
				Source = source;
				SourceWasPlayer = sourceWasPlayer;
			}
		}

		internal readonly struct RpcDamageScope
		{
			private RpcDamageContext Previous { get; }

			internal bool Changed { get; }

			internal RpcDamageScope(RpcDamageContext previous)
			{
				Previous = previous;
				Changed = true;
			}

			internal void Restore()
			{
				if (Changed)
				{
					currentRpcDamage = Previous;
				}
			}
		}

		internal readonly struct DelayedTickScope
		{
			private DelayedTickContext Previous { get; }

			internal bool Changed { get; }

			internal DelayedTickScope(DelayedTickContext previous)
			{
				Previous = previous;
				Changed = true;
			}

			internal void Restore()
			{
				if (Changed)
				{
					currentDelayedTick = Previous;
				}
			}
		}

		internal readonly struct ApplyDamageScope
		{
			internal bool EligibleAtEntry { get; }

			internal float HealthBefore { get; }

			internal ApplyDamageScope(bool eligibleAtEntry, float healthBefore)
			{
				EligibleAtEntry = eligibleAtEntry;
				HealthBefore = healthBefore;
			}
		}

		internal struct RpcDamageContext
		{
			internal Character? Target;

			internal HitData? Hit;
		}

		internal struct DelayedTickContext
		{
			internal Character? Target;

			internal HitType HitType;

			internal DelayedAttribution Attribution;
		}

		private readonly struct FinalAttribution
		{
			internal ZDOID Source { get; }

			internal bool SourceWasPlayer { get; }

			internal bool HasSource => Source != ZDOID.None;

			internal FinalAttribution(ZDOID source, bool sourceWasPlayer)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0002: Unknown result type (might be due to invalid IL or missing references)
				Source = source;
				SourceWasPlayer = sourceWasPlayer;
			}
		}

		private static readonly Dictionary<int, DelayedSourceLedger> delayedSourceLedgers = new Dictionary<int, DelayedSourceLedger>();

		private static readonly Dictionary<int, DelayedDeathCredit> pendingDelayedDeathCredits = new Dictionary<int, DelayedDeathCredit>();

		[ThreadStatic]
		private static RpcDamageContext currentRpcDamage;

		[ThreadStatic]
		private static DelayedTickContext currentDelayedTick;

		internal static RpcDamageScope BeginRpcDamage(Character target, HitData hit)
		{
			RpcDamageContext previous = currentRpcDamage;
			currentRpcDamage = new RpcDamageContext
			{
				Target = target,
				Hit = hit
			};
			return new RpcDamageScope(previous);
		}

		internal static void EndRpcDamage(RpcDamageScope scope)
		{
			scope.Restore();
		}

		internal static void RecordPoisonSource(SE_Poison status, bool accepted)
		{
			if (accepted && !((Object)(object)status == (Object)null) && !((Object)(object)((StatusEffect)status).m_character == (Object)null))
			{
				Character character = ((StatusEffect)status).m_character;
				DelayedSourceLedger ledger = GetLedger(character);
				ledger.PoisonStatus = status;
				ledger.Poison = GetCurrentDelayedAttribution(character);
			}
		}

		internal static void RecordFireSource(SE_Burning status, bool poolWasEmpty, bool accepted)
		{
			RecordAccumulatingSource(status, poolWasEmpty, accepted, spirit: false);
		}

		internal static void RecordSpiritSource(SE_Burning status, bool poolWasEmpty, bool accepted)
		{
			RecordAccumulatingSource(status, poolWasEmpty, accepted, spirit: true);
		}

		internal static DelayedTickScope BeginPoisonTick(SE_Poison status)
		{
			//IL_0063: Unknown result type (might be due to invalid IL or missing references)
			DelayedTickContext previous = currentDelayedTick;
			Character val = ((StatusEffect)(status?)).m_character;
			DelayedAttribution attribution = default(DelayedAttribution);
			if ((Object)(object)val != (Object)null && delayedSourceLedgers.TryGetValue(((Object)val).GetInstanceID(), out DelayedSourceLedger value) && value.Target == val && value.PoisonStatus == status)
			{
				attribution = value.Poison;
			}
			currentDelayedTick = new DelayedTickContext
			{
				Target = val,
				HitType = (HitType)7,
				Attribution = attribution
			};
			return new DelayedTickScope(previous);
		}

		internal static DelayedTickScope BeginBurningTick(SE_Burning status)
		{
			//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
			DelayedTickContext previous = currentDelayedTick;
			Character val = ((StatusEffect)(status?)).m_character;
			DelayedAttribution fire = default(DelayedAttribution);
			DelayedAttribution spirit = default(DelayedAttribution);
			if ((Object)(object)val != (Object)null && delayedSourceLedgers.TryGetValue(((Object)val).GetInstanceID(), out DelayedSourceLedger value) && value.Target == val)
			{
				if (value.FireStatus == status)
				{
					fire = value.Fire;
				}
				if (value.SpiritStatus == status)
				{
					spirit = value.Spirit;
				}
			}
			bool hasFire = (Object)(object)status != (Object)null && ValheimPrivateFields.GetFireDamagePerHit(status) > 0f;
			bool hasSpirit = (Object)(object)status != (Object)null && ValheimPrivateFields.GetSpiritDamagePerHit(status) > 0f;
			currentDelayedTick = new DelayedTickContext
			{
				Target = val,
				HitType = (HitType)5,
				Attribution = CombineTickAttribution(fire, hasFire, spirit, hasSpirit)
			};
			return new DelayedTickScope(previous);
		}

		internal static void EndDelayedTick(DelayedTickScope scope)
		{
			scope.Restore();
		}

		internal static ApplyDamageScope BeginApplyDamage(Character target, HitData hit)
		{
			bool eligibleAtEntry = (Object)(object)target != (Object)null && hit != null && !target.IsDebugFlying() && !target.IsDead() && !target.IsTeleporting() && !target.InCutscene();
			float healthBefore = (((Object)(object)target == (Object)null) ? 0f : Mathf.Max(0f, target.GetHealth()));
			return new ApplyDamageScope(eligibleAtEntry, healthBefore);
		}

		internal static void CompleteApplyDamage(Character target, HitData hit, ApplyDamageScope scope)
		{
			if (scope.EligibleAtEntry)
			{
				CaptureDelayedDeathCredit(target, hit, scope.HealthBefore);
			}
		}

		internal static void ClearRecoveredDeathCredit(Character character)
		{
			if ((Object)(object)character != (Object)null && character.GetHealth() > 0f)
			{
				pendingDelayedDeathCredits.Remove(((Object)character).GetInstanceID());
			}
		}

		internal static bool IsKarmaEligibleKill(Character? dead)
		{
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)dead == (Object)null || dead.IsPlayer() || dead.IsTamed())
			{
				return false;
			}
			FinalAttribution finalAttribution = CaptureFinalAttribution(dead);
			bool sourceIsPlayer;
			if (finalAttribution.HasSource && finalAttribution.Source != dead.GetZDOID())
			{
				return TryValidatePlayerSideSource(finalAttribution.Source, out sourceIsPlayer);
			}
			return false;
		}

		internal static void ForgetCharacter(Character? character)
		{
			if (!((Object)(object)character == (Object)null))
			{
				int instanceID = ((Object)character).GetInstanceID();
				delayedSourceLedgers.Remove(instanceID);
				pendingDelayedDeathCredits.Remove(instanceID);
			}
		}

		internal static void Reset()
		{
			delayedSourceLedgers.Clear();
			pendingDelayedDeathCredits.Clear();
			currentRpcDamage = default(RpcDamageContext);
			currentDelayedTick = default(DelayedTickContext);
		}

		private static void RecordAccumulatingSource(SE_Burning status, bool poolWasEmpty, bool accepted, bool spirit)
		{
			if (accepted && !((Object)(object)status == (Object)null) && !((Object)(object)((StatusEffect)status).m_character == (Object)null))
			{
				Character character = ((StatusEffect)status).m_character;
				DelayedSourceLedger ledger = GetLedger(character);
				DelayedAttribution currentDelayedAttribution = GetCurrentDelayedAttribution(character);
				if (spirit)
				{
					bool flag = ledger.SpiritStatus == status;
					ledger.Spirit = (poolWasEmpty ? currentDelayedAttribution : MergeAttribution(flag ? ledger.Spirit : default(DelayedAttribution), currentDelayedAttribution));
					ledger.SpiritStatus = status;
				}
				else
				{
					bool flag2 = ledger.FireStatus == status;
					ledger.Fire = (poolWasEmpty ? currentDelayedAttribution : MergeAttribution(flag2 ? ledger.Fire : default(DelayedAttribution), currentDelayedAttribution));
					ledger.FireStatus = status;
				}
			}
		}

		private static DelayedAttribution MergeAttribution(DelayedAttribution current, DelayedAttribution incoming)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0016: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			if (current.Kind == DelayedAttributionKind.Exact && incoming.Kind == DelayedAttributionKind.Exact && current.Source == incoming.Source)
			{
				return DelayedAttribution.FromSource(current.Source, current.SourceWasPlayer || incoming.SourceWasPlayer);
			}
			if (current.Kind == DelayedAttributionKind.Unattributed && incoming.Kind == DelayedAttributionKind.Unattributed)
			{
				return current;
			}
			return new DelayedAttribution(DelayedAttributionKind.Ambiguous, ZDOID.None);
		}

		private static DelayedSourceLedger GetLedger(Character target)
		{
			int instanceID = ((Object)target).GetInstanceID();
			if (!delayedSourceLedgers.TryGetValue(instanceID, out DelayedSourceLedger value) || value.Target != target)
			{
				value = new DelayedSourceLedger
				{
					Target = target
				};
				delayedSourceLedgers[instanceID] = value;
			}
			return value;
		}

		private static DelayedAttribution GetCurrentDelayedAttribution(Character target)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			if (currentRpcDamage.Target != target || currentRpcDamage.Hit == null)
			{
				return DelayedAttribution.FromSource(ZDOID.None, sourceWasPlayer: false);
			}
			HitData? hit = currentRpcDamage.Hit;
			Character attacker = hit.GetAttacker();
			return DelayedAttribution.FromSource(hit.m_attacker, (Object)(object)attacker != (Object)null && attacker.IsPlayer());
		}

		private static DelayedAttribution CombineTickAttribution(DelayedAttribution fire, bool hasFire, DelayedAttribution spirit, bool hasSpirit)
		{
			//IL_0077: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: 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_0042: Unknown result type (might be due to invalid IL or missing references)
			if (!hasFire)
			{
				if (!hasSpirit)
				{
					return default(DelayedAttribution);
				}
				return spirit;
			}
			if (!hasSpirit)
			{
				return fire;
			}
			if (fire.Kind == DelayedAttributionKind.Exact && spirit.Kind == DelayedAttributionKind.Exact && fire.Source == spirit.Source)
			{
				return DelayedAttribution.FromSource(fire.Source, fire.SourceWasPlayer || spirit.SourceWasPlayer);
			}
			if (fire.Kind == DelayedAttributionKind.Unattributed && spirit.Kind == DelayedAttributionKind.Unattributed)
			{
				return fire;
			}
			return new DelayedAttribution(DelayedAttributionKind.Ambiguous, ZDOID.None);
		}

		private static void CaptureDelayedDeathCredit(Character target, HitData hit, float healthBefore)
		{
			//IL_0044: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)target == (Object)null) && hit != null && !(healthBefore <= 0f) && !(target.GetHealth() > 0f) && ValheimPrivateFields.GetLastHit(target) == hit)
			{
				int instanceID = ((Object)target).GetInstanceID();
				if (currentDelayedTick.Target != target || currentDelayedTick.HitType != hit.m_hitType)
				{
					pendingDelayedDeathCredits.Remove(instanceID);
					return;
				}
				DelayedAttribution attribution = currentDelayedTick.Attribution;
				pendingDelayedDeathCredits[instanceID] = new DelayedDeathCredit(target, attribution.IsExact ? attribution.Source : ZDOID.None, attribution.IsExact && attribution.SourceWasPlayer);
			}
		}

		private static FinalAttribution CaptureFinalAttribution(Character dead)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_0077: 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_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			int instanceID = ((Object)dead).GetInstanceID();
			if (pendingDelayedDeathCredits.TryGetValue(instanceID, out var value) && value.Target == dead)
			{
				if (!(value.Source == ZDOID.None))
				{
					return new FinalAttribution(value.Source, value.SourceWasPlayer);
				}
				return default(FinalAttribution);
			}
			HitData? lastHit = ValheimPrivateFields.GetLastHit(dead);
			Character val = ((lastHit != null) ? lastHit.GetAttacker() : null);
			ZDOID val2 = lastHit?.m_attacker ?? ZDOID.None;
			if (val2 == ZDOID.None && (Object)(object)val != (Object)null)
			{
				val2 = val.GetZDOID();
			}
			if (!(val2 == ZDOID.None))
			{
				return new FinalAttribution(val2, (Object)(object)val != (Object)null && val.IsPlayer());
			}
			return default(FinalAttribution);
		}

		private static bool TryValidatePlayerSideSource(ZDOID sourceId, out bool sourceIsPlayer)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0004: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
			//IL_0160: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Invalid comparison between Unknown and I4
			//IL_0126: Unknown result type (might be due to invalid IL or missing references)
			//IL_012b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Invalid comparison between Unknown and I4
			//IL_00c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			sourceIsPlayer = false;
			if (sourceId == ZDOID.None || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return false;
			}
			if ((Object)(object)Player.m_localPlayer != (Object)null && ((Character)Player.m_localPlayer).GetZDOID() == sourceId)
			{
				sourceIsPlayer = true;
				return true;
			}
			ZNet instance = ZNet.instance;
			if ((Object)(object)instance != (Object)null)
			{
				foreach (ZNetPeer peer in instance.GetPeers())
				{
					if (peer != null && peer.IsReady() && peer.m_characterID == sourceId)
					{
						sourceIsPlayer = true;
						return true;
					}
				}
				if (!instance.IsServer())
				{
					foreach (PlayerInfo player in instance.GetPlayerList())
					{
						if (player.m_characterID == sourceId)
						{
							sourceIsPlayer = true;
							return true;
						}
					}
				}
			}
			Character val = TryFindCharacter(sourceId);
			if ((Object)(object)val != (Object)null)
			{
				if (val.IsPlayer())
				{
					return sourceIsPlayer = (Object)(object)instance != (Object)null && !instance.IsServer() && val.GetZDOID() == sourceId;
				}
				if (!val.IsTamed())
				{
					return (int)val.GetFaction() == 11;
				}
				return true;
			}
			if (ZDOMan.instance == null)
			{
				return false;
			}
			ZDO zDO = ZDOMan.instance.GetZDO(sourceId);
			if (zDO == null)
			{
				return false;
			}
			if (zDO.GetBool(ZDOVars.s_tamed, false))
			{
				return true;
			}
			GameObject prefab = ZNetScene.instance.GetPrefab(zDO.GetPrefab());
			Character val2 = (((Object)(object)prefab != (Object)null) ? prefab.GetComponent<Character>() : null);
			if ((Object)(object)val2 != (Object)null && !val2.IsPlayer())
			{
				return (int)val2.GetFaction() == 11;
			}
			return false;
		}

		private static Character? TryFindCharacter(ZDOID sourceId)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0001: 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)
			if (sourceId == ZDOID.None || (Object)(object)ZNetScene.instance == (Object)null)
			{
				return null;
			}
			GameObject val = ZNetScene.instance.FindInstance(sourceId);
			if (!((Object)(object)val != (Object)null))
			{
				return null;
			}
			return val.GetComponent<Character>();
		}
	}
	internal static class LuckyCompat
	{
		private enum LifecyclePhase
		{
			Idle,
			Installing,
			Installed,
			CleanupPending
		}

		private sealed class LuckyDropRollState
		{
			private readonly List<DropFieldSnapshot> snapshots = new List<DropFieldSnapshot>();

			private bool restored;

			internal void Capture(Drop drop)
			{
				snapshots.Add(new DropFieldSnapshot(drop));
			}

			internal void Restore()
			{
				if (!restored)
				{
					for (int num = snapshots.Count - 1; num >= 0; num--)
					{
						snapshots[num].Restore();
					}
					restored = true;
				}
			}
		}

		private readonly struct DropFieldSnapshot
		{
			private readonly Drop drop;

			private readonly float chance;

			private readonly int amountMin;

			private readonly int amountMax;

			internal DropFieldSnapshot(Drop drop)
			{
				this.drop = drop;
				chance = drop.m_chance;
				amountMin = drop.m_amountMin;
				amountMax = drop.m_amountMax;
			}

			internal void Restore()
			{
				drop.m_chance = chance;
				drop.m_amountMin = amountMin;
				drop.m_amountMax = amountMax;
			}
		}

		private sealed class OriginalPatchRegistration
		{
			internal string Owner { get; }

			private int Priority { get; }

			private string[] Before { get; }

			private string[] After { get; }

			private bool Debug { get; }

			private string? DebugEmitPath { get; }

			private bool WrapTryCatch { get; }

			internal OriginalPatchRegistration(Patch patch)
			{
				if (string.IsNullOrWhiteSpace(patch.owner))
				{
					throw new InvalidOperationException("Jewelcrafting's Lucky creature-kill postfix has no Harmony owner.");
				}
				Owner = patch.owner;
				Priority = patch.priority;
				Before = patch.before?.ToArray() ?? Array.Empty<string>();
				After = patch.after?.ToArray() ?? Array.Empty<string>();
				Debug = patch.debug;
				DebugEmitPath = patch.debugEmitPath;
				WrapTryCatch = patch.wrapTryCatch;
			}

			internal HarmonyMethod CreateHarmonyMethod(MethodInfo patchMethod)
			{
				//IL_0001: Unknown result type (might be due to invalid IL or missing references)
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0012: Unknown result type (might be due to invalid IL or missing references)
				//IL_0023: 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_0045: Unknown result type (might be due to invalid IL or missing references)
				//IL_0051: Unknown result type (might be due to invalid IL or missing references)
				//IL_0063: Expected O, but got Unknown
				return new HarmonyMethod(patchMethod)
				{
					priority = Priority,
					before = Before.ToArray(),
					after = After.ToArray(),
					debug = Debug,
					debugEmitPath = DebugEmitPath,
					wrapTryCatch = WrapTryCatch
				};
			}

			internal bool MatchesMetadata(Patch patch)
			{
				if (patch.priority == Priority && (patch.before ?? Array.Empty<string>()).SequenceEqual(Before) && (patch.after ?? Array.Empty<string>()).SequenceEqual(After) && patch.debug == Debug && patch.debugEmitPath == DebugEmitPath)
				{
					return patch.wrapTryCatch == WrapTryCatch;
				}
				return false;
			}
		}

		private const string HarmonyOwner = "sighsorry.jewelcraftingkarmadrops.luckycompat";

		private const string JewelcraftingGuid = "org.bepinex.plugins.jewelcrafting";

		private const string DropNSpawnHarmonyOwner = "sighsorry.DropNSpawn";

		private const string LuckyZdoPrefix = "Jewelcrafting Lucky ";

		private const string TooltipMarker = "$jc_lucky_luck:";

		private const string DamageDoneTooltipMarker = "$jc_lucky_damage_done:";

		private const string DamageReceivedTooltipMarker = "$jc_lucky_damage_received:";

		private const float OriginalMaximumDropBonusPercent = 250f;

		private const float OriginalMaximumDamageDealtReductionPercent = 80f;

		private const float OriginalMaximumDamageTakenIncreasePercent = 300f;

		private static readonly Version minimumCompatibleJewelcraftingVersion = new Version(2, 0, 1);

		private static readonly Version latestVerifiedJewelcraftingVersion = new Version(2, 0, 5);

		private static readonly HashSet<string> emptyBlacklist = new HashSet<string>(StringComparer.OrdinalIgnoreCase);

		private static readonly object lifecycleLock = new object();

		private static Harmony? harmony;

		private static LifecyclePhase lifecyclePhase;

		private static MethodInfo[] auxiliaryPatchTargets = Array.Empty<MethodInfo>();

		private static FieldRef<Character, ZNetView>? characterNetView;

		private static MethodInfo? characterOnDeathTarget;

		private static MethodInfo? originalKillPostfixTarget;

		private static MethodInfo? replacementKillPostfixTarget;

		private static OriginalPatchRegistration? originalKillPatchRegistration;

		private static bool criticalWrapperUncertain;

		private static HashSet<string> dropPrefabBlacklist = emptyBlacklist;

		private static bool blacklistHandlerRegistered;

		internal static void TryInstall(ManualLogSource logger)
		{
			lock (lifecycleLock)
			{
				TryInstallLocked(logger);
			}
		}

		private static void TryInstallLocked(ManualLogSource logger)
		{
			//IL_0284: Unknown result type (might be due to invalid IL or missing references)
			//IL_0289: Unknown result type (might be due to invalid IL or missing references)
			//IL_028f: Expected O, but got Unknown
			//IL_028f: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_02eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0332: Unknown result type (might be due to invalid IL or missing references)
			//IL_033d: Unknown result type (might be due to invalid IL or missing references)
			if (lifecyclePhase == LifecyclePhase.Installed || lifecyclePhase == LifecyclePhase.Installing)
			{
				return;
			}
			if (lifecyclePhase == LifecyclePhase.CleanupPending)
			{
				RemoveBlacklistHandler();
				dropPrefabBlacklist = emptyBlacklist;
				if (!CleanupPatches(logger))
				{
					logger.LogWarning((object)"Lucky compatibility cleanup is still pending; a second installation was not attempted.");
					return;
				}
				lifecyclePhase = LifecyclePhase.Idle;
				characterNetView = null;
			}
			try
			{
				Version version = ValidateSupportedJewelcraftingVersion();
				MethodInfo methodInfo = RequireMethod(AccessTools.Inner(typeof(Lucky), "TrackCreatureKills") ?? throw new TypeLoadException("Jewelcrafting.GemEffects.Lucky+TrackCreatureKills was not found."), "Postfix", typeof(void), typeof(Character));
				MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(Character), "OnDeath", (Type[])null, (Type[])null) ?? throw new MissingMethodException(typeof(Character).FullName, "OnDeath");
				MethodInfo methodInfo3 = RequireMethod(typeof(CharacterDrop), "GenerateDropList", typeof(List<KeyValuePair<GameObject, int>>));
				MethodInfo methodInfo4 = RequireMethod(typeof(Lucky), "Chance", typeof(float), typeof(Character), typeof(string));
				MethodInfo methodInfo5 = RequireMethod(typeof(Lucky), "ChanceMultiplier", typeof(float), typeof(Player));
				MethodInfo methodInfo6 = RequireMethod(typeof(Lucky), "DamageDoneMultiplier", typeof(float));
				MethodInfo methodInfo7 = RequireMethod(typeof(Lucky), "DamageReceivedMultiplier", typeof(float));
				MethodInfo methodInfo8 = RequireMethod(typeof(Lucky), "GetTooltipString", typeof(string));
				MethodInfo methodInfo9 = RequirePatchMethod("ReplaceTrackCreatureKills");
				ValidateOriginalKillPostfixIsUnpatched(methodInfo);
				ValidateOriginalKillPostfixContract(methodInfo, methodInfo5);
				characterNetView = AccessTools.FieldRefAccess<Character, ZNetView>("m_nview");
				OriginalPatchRegistration originalPatchRegistration = CaptureOriginalKillPatch(methodInfo2, methodInfo, methodInfo9);
				if (originalPatchRegistration.Owner == "sighsorry.jewelcraftingkarmadrops.luckycompat")
				{
					throw new InvalidOperationException("Jewelcrafting's Lucky postfix unexpectedly uses the compatibility Harmony owner.");
				}
				ValidateConfiguration();
				RefreshDropPrefabBlacklist();
				lifecyclePhase = LifecyclePhase.Installing;
				characterOnDeathTarget = methodInfo2;
				originalKillPostfixTarget = methodInfo;
				replacementKillPostfixTarget = methodInfo9;
				originalKillPatchRegistration = originalPatchRegistration;
				criticalWrapperUncertain = false;
				auxiliaryPatchTargets = new MethodInfo[6] { methodInfo3, methodInfo4, methodInfo5, methodInfo6, methodInfo7, methodInfo8 };
				Harmony val = new Harmony("sighsorry.jewelcraftingkarmadrops.luckycompat");
				harmony = val;
				val.Patch((MethodBase)methodInfo4, (HarmonyMethod)null, PatchMethod("ApplyDropBonusCap"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				val.Patch((MethodBase)methodInfo5, PatchMethod("RejectMissingPlayerChanceMultiplier"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				val.Patch((MethodBase)methodInfo6, (HarmonyMethod)null, PatchMethod("ApplyDamageDealtReductionCap"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				val.Patch((MethodBase)methodInfo7, (HarmonyMethod)null, PatchMethod("ApplyDamageTakenIncreaseCap"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				val.Patch((MethodBase)methodInfo8, (HarmonyMethod)null, PatchMethod("DisplayCurrentAndMaximumInItemTooltip"), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				val.Patch((MethodBase)methodInfo3, DropRollPatchMethod("ApplyLuckyToCurrentDropRoll"), (HarmonyMethod)null, (HarmonyMethod)null, DropRollPatchMethod("RestoreDropsAfterCurrentRoll"), (HarmonyMethod)null);
				ValidateCriticalStateBeforeReplacement(methodInfo2, methodInfo, methodInfo9, originalPatchRegistration);
				Harmony val2 = new Harmony(originalPatchRegistration.Owner);
				criticalWrapperUncertain = true;
				val2.Unpatch((MethodBase)methodInfo2, methodInfo);
				criticalWrapperUncertain = false;
				criticalWrapperUncertain = true;
				val2.Patch((MethodBase)methodInfo2, (HarmonyMethod)null, originalPatchRegistration.CreateHarmonyMethod(methodInfo9), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				criticalWrapperUncertain = false;
				ValidateInstalledPatches(methodInfo2, methodInfo, originalPatchRegistration, methodInfo3, methodInfo4, methodInfo5, methodInfo6, methodInfo7, methodInfo8);
				Plugin.LuckyDropPrefabBlacklist.SettingChanged += OnBlacklistSettingChanged;
				blacklistHandlerRegistered = true;
				lifecyclePhase = LifecyclePhase.Installed;
				if (version.CompareTo(latestVerifiedJewelcraftingVersion) > 0)
				{
					logger.LogWarning((object)($"Jewelcrafting {version} is newer than the directly verified " + $"{latestVerifiedJewelcraftingVersion}; its Lucky runtime contract matched, " + "so forward-compatible Lucky support was enabled."));
				}
				logger.LogInfo((object)"Applied Lucky caps, item tooltip, tame-drop, creature-prefab, drop-blacklist, and current-roll drop compatibility patches.");
			}
			catch (Exception arg)
			{
				lifecyclePhase = LifecyclePhase.CleanupPending;
				RemoveBlacklistHandler();
				dropPrefabBlacklist = emptyBlacklist;
				if (CleanupPatches(logger))
				{
					lifecyclePhase = LifecyclePhase.Idle;
					characterNetView = null;
				}
				logger.LogWarning((object)$"Lucky compatibility patch was not applied; Karma Drops remains active: {arg}");
			}
		}

		internal static void Shutdown(ManualLogSource? logger = null)
		{
			lock (lifecycleLock)
			{
				RemoveBlacklistHandler();
				dropPrefabBlacklist = emptyBlacklist;
				if (lifecyclePhase != LifecyclePhase.Idle || harmony != null || HasCriticalPatchState())
				{
					lifecyclePhase = LifecyclePhase.CleanupPending;
					if (CleanupPatches(logger))
					{
						lifecyclePhase = LifecyclePhase.Idle;
						characterNetView = null;
					}
					else if (logger != null)
					{
						logger.LogWarning((object)"Lucky compatibility cleanup remains pending and will be retried on the next shutdown call.");
					}
				}
			}
		}

		private static Version ValidateSupportedJewelcraftingVersion()
		{
			if (!Chainloader.PluginInfos.TryGetValue("org.bepinex.plugins.jewelcrafting", out var value))
			{
				throw new InvalidOperationException("Jewelcrafting plugin metadata was not available for Lucky compatibility validation.");
			}
			Version version = value.Metadata.Version;
			if (version.CompareTo(minimumCompatibleJewelcraftingVersion) < 0)
			{
				throw new NotSupportedException("Lucky compatibility requires Jewelcrafting " + $"{minimumCompatibleJewelcraftingVersion} or newer; found {version}.");
			}
			return version;
		}

		private static void ValidateOriginalKillPostfixIsUnpatched(MethodInfo originalKillPostfix)
		{
			Patches patchInfo = Harmony.GetPatchInfo((MethodBase)originalKillPostfix);
			Patch[] array = ((patchInfo == null) ? Array.Empty<Patch>() : patchInfo.Prefixes.Concat(patchInfo.Postfixes).Concat(patchInfo.Transpilers).Concat(patchInfo.Finalizers)
				.Concat(patchInfo.ILManipulators)
				.ToArray());
			if (array.Length == 0)
			{
				return;
			}
			string text = string.Join(", ", from owner in (from patch in array
					select patch.owner into owner
					where !string.IsNullOrWhiteSpace(owner)
					select owner).Distinct()
				orderby owner
				select owner);
			throw new InvalidOperationException("Jewelcrafting's private Lucky creature-kill postfix is already patched" + ((text.Length == 0) ? "." : (" by: " + text + ".")));
		}

		private static void ValidateOriginalKillPostfixContract(MethodInfo originalKillPostfix, MethodInfo chanceMultiplier)
		{
			if (!originalKillPostfix.IsStatic)
			{
				throw new InvalidOperationException("Jewelcrafting's Lucky creature-kill postfix is no longer static.");
			}
			FieldInfo lastHit = RequireField(typeof(Character), "m_lastHit");
			FieldInfo netView = RequireField(typeof(Character), "m_nview");
			FieldInfo dropList = RequireField(typeof(CharacterDrop), "m_drops");
			FieldInfo chance = RequireField(typeof(Drop), "m_chance");
			FieldInfo amountMin = RequireField(typeof(Drop), "m_amountMin");
			FieldInfo amountMax = RequireField(typeof(Drop), "m_amountMax");
			MethodInfo getAttacker = RequireMethod(typeof(HitData), "GetAttacker", typeof(Character));
			MethodInfo invokeRpc = RequireMethod(typeof(ZNetView), "InvokeRPC", typeof(void), typeof(string), typeof(object[]));
			MethodInfo floorToInt = RequireMethod(typeof(Mathf), "FloorToInt", typeof(int), typeof(float));
			MethodInfo randomValue = AccessTools.PropertyGetter(typeof(Random), "value") ?? throw new MissingMethodException(typeof(Random).FullName, "get_value");
			List<CodeInstruction> originalInstructions = PatchProcessor.GetOriginalInstructions((MethodBase)originalKillPostfix, (ILGenerator)null);
			if (originalInstructions.Count == 0)
			{
				throw new InvalidOperationException("Jewelcrafting's Lucky creature-kill postfix has no readable IL body.");
			}
			int num = RequireSingleInstruction(originalInstructions, (CodeInstruction instruction) => IsParameterlessGetComponent<CharacterDrop>(instruction.operand), "GetComponent<CharacterDrop>() call");
			int num2 = RequireSingleInstruction(originalInstructions, (CodeInstruction instruction) => instruction.opcode == OpCodes.Ldfld && object.Equals(instruction.operand, lastHit), "Character.m_lastHit load");
			int num3 = RequireSingleInstruction(originalInstructions, (CodeInstruction instruction) => Calls(instruction, getAttacker), "HitData.GetAttacker() call");
			int num4 = RequireSingleInstruction(originalInstructions, (CodeInstruction instruction) => instruction.opcode == OpCodes.Isinst && object.Equals(instruction.operand, typeof(Player)), "Player type test");
			int num5 = RequireSingleInstruction(originalInstructions, (CodeInstruction instruction) => Calls(instruction, chanceMultiplier), "Lucky.ChanceMultiplier(Player) call");
			int num6 = RequireSingleInstruction(originalInstructions, (CodeInstruction instruction) => instruction.opcode == OpCodes.Ldfld && object.Equals(instruction.operand, netView), "Character.m_nview load");
			int num7 = RequireSingleInstruction(originalInstructions, (CodeInstruction instruction) => instruction.opcode == OpCodes.Ldstr && object.Equals(instruction.operand, "Jewelcrafting Lucky"), "Lucky RPC name");
			int num8 = RequireSingleInstruction(originalInstructions, (CodeInstruction instruction) => IsEmptyObjectArrayCall(instruction.operand), "Array.Empty<object>() call");
			int num9 = RequireSingleInstruction(originalInstructions, (CodeInstruction instruction) => Calls(instruction, invokeRpc), "ZNetView.InvokeRPC(string, object[]) call");
			int num10 = RequireSingleInstruction(originalInstructions, (CodeInstruction instruction) => instruction.opcode == OpCodes.Ldfld && object.Equals(instruction.operand, dropList), "CharacterDrop.m_drops load");
			int num11 = RequireSingleInstruction(originalInstructions, (CodeInstruction instruction) => Calls(instruction, floorToInt), "Mathf.FloorToInt(float) call");
			int num12 = RequireSingleInstruction(originalInstructions, (CodeInstruction instruction) => Calls(instruction, randomValue), "UnityEngine.Random.value read");
			int num13 = RequireSingleInstruction(originalInstructions, (CodeInstruction instruction) => IsDropListEnumeratorCall(instruction, "GetEnumerator"), "CharacterDrop.Drop list GetEnumerator() call");
			int num14 = RequireSingleInstruction(originalInstructions, (CodeInstruction instruction) => IsDropEnumeratorCall(instruction, "get_Current", typeof(Drop)), "CharacterDrop.Drop enumerator Current read");
			int num15 = RequireSingleInstruction(originalInstructions, (CodeInstruction instruction) => IsDropEnumeratorCall(instruction, "MoveNext", typeof(bool)), "CharacterDrop.Drop enumerator MoveNext() call");
			int num16 = RequireSingleInstruction(originalInstructions, (CodeInstruction instruction) => IsInterfaceDisposeCall(instruction), "CharacterDrop.Drop enumerator Dispose() call");
			int[] array = RequireInstructionCount(originalInstructions, (CodeInstruction instruction) => instruction.opcode == OpCodes.Cgt, 2, "greater-than comparisons");
			int[] array2 = RequireInstructionCount(originalInstructions, (CodeInstruction instruction) => instruction.opcode == OpCodes.Ldfld && object.Equals(instruction.operand, chance), 4, "CharacterDrop.Drop.m_chance loads");
			int[] array3 = RequireInstructionCount(originalInstructions, (CodeInstruction instruction) => instruction.opcode == OpCodes.Stfld && object.Equals(instruction.operand, chance), 2, "CharacterDrop.Drop.m_chance writes");
			int[] array4 = RequireInstructionCount(originalInstructions, (CodeInstruction instruction) => instruction.opcode == OpCodes.Ldfld && object.Equals(instruction.operand, amountMin), 1, "CharacterDrop.Drop.m_amountMin loads");
			int[] array5 = RequireInstructionCount(originalInstructions, (CodeInstruction instruction) => instruction.opcode == OpCodes.Stfld && object.Equals(instruction.operand, amountMin), 1, "CharacterDrop.Drop.m_amountMin writes");
			int[] array6 = RequireInstructionCount(originalInstructions, (CodeInstruction instruction) => instruction.opcode == OpCodes.Ldfld && object.Equals(instruction.operand, amountMax), 1, "CharacterDrop.Drop.m_amountMax loads");
			int[] array7 = RequireInstructionCount(originalInstructions, (CodeInstruction instruction) => instruction.opcode == OpCodes.Stfld && object.Equals(instruction.operand, amountMax), 1, "CharacterDrop.Drop.m_amountMax writes");
			RequireInstructionCount(originalInstructions, (CodeInstruction instruction) => instruction.opcode == OpCodes.Mul, 3, "drop amount/chance multiplications");
			RequireInstructionCount(originalInstructions, (CodeInstruction instruction) => instruction.opcode == OpCodes.Stfld || instruction.opcode == OpCodes.Stsfld, 4, "total field writes");
			RequireInstructionCount(originalInstructions, (CodeInstruction instruction) => instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt, 11, "total method calls");
			int[] array8 = new int[23]
			{
				num,
				num2,
				num3,
				num4,
				num5,
				array[0],
				num6,
				num7,
				num8,
				num9,
				num10,
				num13,
				num14,
				array3[0],
				array2[1],
				array[1],
				num11,
				num12,
				array5[0],
				array7[0],
				array3[1],
				num15,
				num16
			};
			if (array8.Zip(array8.Skip(1), (int left, int right) => left < right).Any((bool inOrder) => !inOrder))
			{
				throw new InvalidOperationException("Jewelcrafting's Lucky creature-kill postfix changed its core execution order.");
			}
			if (array2[0] >= array3[0] || array3[0] >= array2[1] || array4[0] >= array5[0] || array6[0] >= array7[0])
			{
				throw new InvalidOperationException("Jewelcrafting's Lucky creature-kill postfix changed its drop field read/write shape.");
			}
			if (!PreviousMeaningfulInstructionIs(originalInstructions, array3[0], OpCodes.Mul) || !PreviousMeaningfulInstructionIs(originalInstructions, array5[0], OpCodes.Mul) || !PreviousMeaningfulInstructionIs(originalInstructions, array7[0], OpCodes.Mul) || !PreviousMeaningfulInstructionLoadsOne(originalInstructions, array[0]) || !PreviousMeaningfulInstructionLoadsOne(originalInstructions, array[1]) || !PreviousMeaningfulInstructionLoadsOne(originalInstructions, array3[1]))
			{
				throw new InvalidOperationException("Jewelcrafting's Lucky creature-kill postfix changed its drop mutation semantics.");
			}
		}

		private static FieldInfo RequireField(Type type, string fieldName)
		{
			return AccessTools.DeclaredField(type, fieldName) ?? throw new MissingFieldException(type.FullName, fieldName);
		}

		private static int RequireSingleInstruction(IReadOnlyList<CodeInstruction> instructions, Func<CodeInstruction, bool> predicate, string description)
		{
			return RequireInstructionCount(instructions, predicate, 1, description)[0];
		}

		private static int[] RequireInstructionCount(IReadOnlyList<CodeInstruction> instructions, Func<CodeInstruction, bool> predicate, int expectedCount, string description)
		{
			int[] array = (from index in Enumerable.Range(0, instructions.Count)
				where predicate(instructions[index])
				select index).ToArray();
			if (array.Length != expectedCount)
			{
				throw new InvalidOperationException($"Jewelcrafting's Lucky creature-kill postfix expected {expectedCount} " + $"{description}, but found {array.Length}.");
			}
			return array;
		}

		private static bool Calls(CodeInstruction instruction, MethodInfo method)
		{
			if (instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt)
			{
				return object.Equals(instruction.operand, method);
			}
			return false;
		}

		private static bool IsParameterlessGetComponent<T>(object? operand)
		{
			if (operand is MethodInfo methodInfo && methodInfo.DeclaringType == typeof(Component) && methodInfo.Name == "GetComponent" && methodInfo.IsGenericMethod && methodInfo.GetGenericArguments().SequenceEqual(new Type[1] { typeof(T) }))
			{
				return methodInfo.GetParameters().Length == 0;
			}
			return false;
		}

		private static bool IsEmptyObjectArrayCall(object? operand)
		{
			if (operand is MethodInfo methodInfo && methodInfo.DeclaringType == typeof(Array) && methodInfo.Name == "Empty" && methodInfo.IsGenericMethod && methodInfo.GetGenericArguments().SequenceEqual(new Type[1] { typeof(object) }))
			{
				return methodInfo.GetParameters().Length == 0;
			}
			return false;
		}

		private static bool IsDropListEnumeratorCall(CodeInstruction instruction, string methodName)
		{
			if ((instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) && instruction.operand is MethodInfo methodInfo && methodInfo.DeclaringType == typeof(List<Drop>) && methodInfo.Name == methodName && methodInfo.ReturnType == typeof(List<Drop>.Enumerator))
			{
				return methodInfo.GetParameters().Length == 0;
			}
			return false;
		}

		private static bool IsDropEnumeratorCall(CodeInstruction instruction, string methodName, Type returnType)
		{
			if ((instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) && instruction.operand is MethodInfo methodInfo && methodInfo.DeclaringType == typeof(List<Drop>.Enumerator) && methodInfo.Name == methodName && methodInfo.ReturnType == returnType)
			{
				return methodInfo.GetParameters().Length == 0;
			}
			return false;
		}

		private static bool IsInterfaceDisposeCall(CodeInstruction instruction)
		{
			if ((instruction.opcode == OpCodes.Call || instruction.opcode == OpCodes.Callvirt) && instruction.operand is MethodInfo methodInfo && methodInfo.DeclaringType == typeof(IDisposable) && methodInfo.Name == "Dispose" && methodInfo.ReturnType == typeof(void))
			{
				return methodInfo.GetParameters().Length == 0;
			}
			return false;
		}

		private static bool PreviousMeaningfulInstructionIs(IReadOnlyList<CodeInstruction> instructions, int index, OpCode expectedOpcode)
		{
			int num = PreviousMeaningfulInstructionIndex(instructions, index);
			if (num >= 0)
			{
				return instructions[num].opcode == expectedOpcode;
			}
			return false;
		}

		private static bool PreviousMeaningfulInstructionLoadsOne(IReadOnlyList<CodeInstruction> instructions, int index)
		{
			int num = PreviousMeaningfulInstructionIndex(instructions, index);
			if (num < 0)
			{
				return false;
			}
			CodeInstruction val = instructions[num];
			if (val.opcode == OpCodes.Ldc_R4 && val.operand is float num2)
			{
				return Math.Abs(num2 - 1f) < float.Epsilon;
			}
			return false;
		}

		private static int PreviousMeaningfulInstructionIndex(IReadOnlyList<CodeInstruction> instructions, int index)
		{
			for (int num = index - 1; num >= 0; num--)
			{
				if (instructions[num].opcode != OpCodes.Nop)
				{
					return num;
				}
			}
			return -1;
		}

		private static MethodInfo RequireMethod(Type type, string methodName, Type returnType, params Type[] argumentTypes)
		{
			MethodInfo methodInfo = AccessTools.DeclaredMethod(type, methodName, argumentTypes, (Type[])null) ?? throw new MissingMethodException(type.FullName, methodName);
			if (methodInfo.ReturnType != returnType)
			{
				throw new MissingMethodException(type.FullName + "." + methodName + " has return type " + methodInfo.ReturnType.FullName + "; expected " + returnType.FullName + ".");
			}
			return methodInfo;
		}

		private static HarmonyMethod PatchMethod(string methodName)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			return new HarmonyMethod(RequirePatchMethod(methodName));
		}

		private static HarmonyMethod DropRollPatchMethod(string methodName)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Expected O, but got Unknown
			HarmonyMethod val = new HarmonyMethod(RequirePatchMethod(methodName));
			val.priority = 0;
			val.after = new string[1] { "sighsorry.DropNSpawn" };
			return val;
		}

		private static MethodInfo RequirePatchMethod(string methodName)
		{
			return AccessTools.DeclaredMethod(typeof(LuckyCompat), methodName, (Type[])null, (Type[])null) ?? throw new MissingMethodException(typeof(LuckyCompat).FullName, methodName);
		}

		private static OriginalPatchRegistration CaptureOriginalKillPatch(MethodInfo characterOnDeath, MethodInfo originalKillPostfix, MethodInfo replacementKillPostfix)
		{
			Patches patchInfo = Harmony.GetPatchInfo((MethodBase)characterOnDeath);
			Patch[] array = patchInfo?.Postfixes.Where((Patch patch) => patch.PatchMethod == originalKillPostfix).ToArray() ?? Array.Empty<Patch>();
			if (array.Length != 1)
			{
				throw new InvalidOperationException("Expected exactly one Jewelcrafting Lucky creature-kill postfix on " + $"Character.OnDeath, but found {array.Length}.");
			}
			if (patchInfo.Postfixes.Any((Patch patch) => patch.PatchMethod == replacementKillPostfix))
			{
				throw new InvalidOperationException("The Lucky replacement postfix is already installed on Character.OnDeath.");
			}
			return new OriginalPatchRegistration(array[0]);
		}

		private static void ValidateCriticalStateBeforeReplacement(MethodInfo characterOnDeath, MethodInfo originalKillPostfix, MethodInfo replacementKillPostfix, OriginalPatchRegistration originalRegistration)
		{
			ValidateOriginalKillPostfixIsUnpatched(originalKillPostfix);
			Patch[] array = FindPostfixes(characterOnDeath, originalKillPostfix);
			if (array.Length != 1 || array[0].owner != originalRegistration.Owner || !originalRegistration.MatchesMetadata(array[0]))
			{
				throw new InvalidOperationException("Jewelcrafting's Lucky creature-kill postfix changed before replacement.");
			}
			if (FindPostfixes(characterOnDeath, replacementKillPostfix).Length != 0)
			{
				throw new InvalidOperationException("The Lucky replacement postfix appeared before installation completed.");
			}
		}

		private static void ValidateInstalledPatches(MethodInfo characterOnDeath, MethodInfo originalKillPostfix, OriginalPatchRegistration originalRegistration, MethodInfo generateDropList, MethodInfo chance, MethodInfo chanceMultiplier, MethodInfo damageDoneMultiplier, MethodInfo damageReceivedMultiplier, MethodInfo tooltip)
		{
			Patches patchInfo = Harmony.GetPatchInfo((MethodBase)characterOnDeath);
			if (patchInfo != null && patchInfo.Postfixes.Any((Patch val) => val.PatchMethod == originalKillPostfix))
			{
				throw new InvalidOperationException("Jewelcrafting's original Lucky creature-kill postfix remained installed.");
			}
			Patch patch = RequireOwnedPatch(characterOnDeath, RequirePatchMethod("ReplaceTrackCreatureKills"), prefix: false, originalRegistration.Owner);
			if (!originalRegistration.MatchesMetadata(patch))
			{
				throw new InvalidOperationException("The Lucky replacement postfix did not retain Jewelcrafting's original patch metadata.");
			}
			ValidateDropRollPatchOrdering(RequireOwnedPatch(generateDropList, RequirePatchMethod("ApplyLuckyToCurrentDropRoll"), prefix: true, "sighsorry.jewelcraftingkarmadrops.luckycompat"), "prefix");
			ValidateDropRollPatchOrdering(RequireOwnedFinalizer(generateDropList, RequirePatchMethod("RestoreDropsAfterCurrentRoll"), "sighsorry.jewelcraftingkarmadrops.luckycompat"), "finalizer");
			RequireOwnedPatch(chance, RequirePatchMethod("ApplyDropBonusCap"), prefix: false, "sighsorry.jewelcraftingkarmadrops.luckycompat");
			RequireOwnedPatch(chanceMultiplier, RequirePatchMethod("RejectMissingPlayerChanceMultiplier"), prefix: true, "sighsorry.jewelcraftingkarmadrops.luckycompat");
			RequireOwnedPatch(damageDoneMultiplier, RequirePatchMethod("ApplyDamageDealtReductionCap"), prefix: false, "sighsorry.jewelcraftingkarmadrops.luckycompat");
			RequireOwnedPatch(damageReceivedMultiplier, RequirePatchMethod("ApplyDamageTakenIncreaseCap"), prefix: false, "sighsorry.jewelcraftingkarmadrops.luckycompat");
			RequireOwnedPatch(tooltip, RequirePatchMethod("DisplayCurrentAndMaximumInItemTooltip"), prefix: false, "sighsorry.jewelcraftingkarmadrops.luckycompat");
		}

		private static Patch RequireOwnedPatch(MethodBase target, MethodInfo patchMethod, bool prefix, string owner)
		{
			Patches patchInfo = Harmony.GetPatchInfo(target);
			object source;
			if (!prefix)
			{
				IEnumerable<Patch> enumerable = patchInfo?.Postfixes;
				source = enumerable ?? Enumerable.Empty<Patch>();
			}
			else
			{
				IEnumerable<Patch> enumerable = patchInfo?.Prefixes;
				source = enumerable ?? Enumerable.Empty<Patch>();
			}
			Patch[] array = ((IEnumerable<Patch>)source).Where((Patch patch) => patch.PatchMethod == patchMethod).ToArray();
			if (array.Length != 1 || array[0].owner != owner)
			{
				throw new InvalidOperationException("Harmony did not retain exactly one " + owner + " patch " + patchMethod.Name + " on " + target.DeclaringType?.FullName + "." + target.Name + ".");
			}
			return array[0];
		}

		private static Patch RequireOwnedFinalizer(MethodBase target, MethodInfo patchMethod, string owner)
		{
			Patch[] array = Harmony.GetPatchInfo(target)?.Finalizers.Where((Patch patch) => patch.PatchMethod == patchMethod).ToArray() ?? Array.Empty<Patch>();
			if (array.Length != 1 || array[0].owner != owner)
			{
				throw new InvalidOperationException("Harmony did not retain exactly one " + owner + " finalizer " + patchMethod.Name + " on " + target.DeclaringType?.FullName + "." + target.Name + ".");
			}
			return array[0];
		}

		private static void ValidateDropRollPatchOrdering(Patch patch, string patchKind)
		{
			if (patch.priority != 0 || !(patch.after ?? Array.Empty<string>()).Contains("sighsorry.DropNSpawn"))
			{
				throw new InvalidOperationException("The Lucky current-roll " + patchKind + " did not retain its DropNSpawn-late ordering metadata.");
			}
		}

		private static bool CleanupPatches(ManualLogSource? logger)
		{
			if (!ReconcileCriticalKillPatch(logger))
			{
				return false;
			}
			return RemoveAuxiliaryPatches(logger);
		}

		private static bool ReconcileCriticalKillPatch(ManualLogSource? logger)
		{
			//IL_011a: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			if (!HasCriticalPatchState())
			{
				return true;
			}
			MethodInfo methodInfo = characterOnDeathTarget;
			MethodInfo methodInfo2 = originalKillPostfixTarget;
			MethodInfo methodInfo3 = replacementKillPostfixTarget;
			OriginalPatchRegistration originalPatchRegistration = originalKillPatchRegistration;
			if (methodInfo == null || methodInfo2 == null || methodInfo3 == null || originalPatchRegistration == null)
			{
				if (logger != null)
				{
					logger.LogWarning((object)"Cannot reconcile Jewelcrafting's Lucky creature-kill postfix because its saved registration is incomplete.");
				}
				return false;
			}
			try
			{
				Patch[] array = FindPostfixes(methodInfo, methodInfo2);
				if (array.Length != 0 && (array.Length != 1 || array[0].owner != originalPatchRegistration.Owner || !originalPatchRegistration.MatchesMetadata(array[0])))
				{
					if (logger != null)
					{
						logger.LogWarning((object)"Did not alter Lucky creature-kill patches because the current original registration conflicts with the saved registration.");
					}
					return false;
				}
				if (FindPostfixes(methodInfo, methodInfo3).Length != 0)
				{
					criticalWrapperUncertain = true;
					try
					{
						new Harmony(originalPatchRegistration.Owner).Unpatch((MethodBase)methodInfo, methodInfo3);
						criticalWrapperUncertain = false;
					}
					catch (Exception arg)
					{
						if (logger != null)
						{
							logger.LogWarning((object)$"Could not remove the exact Lucky replacement postfix: {arg}");
						}
					}
				}
				if (FindPostfixes(methodInfo, methodInfo3).Length != 0)
				{
					if (logger != null)
					{
						logger.LogWarning((object)"The Lucky replacement postfix is still registered; the original postfix was not restored alongside it.");
					}
					return false;
				}
				Patch[] array2 = FindPostfixes(methodInfo, methodInfo2);
				if (array2.Length == 0)
				{
					criticalWrapperUncertain = true;
					try
					{
						new Harmony(originalPatchRegistration.Owner).Patch((MethodBase)methodInfo, (HarmonyMethod)null, originalPatchRegistration.CreateHarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						criticalWrapperUncertain = false;
					}
					catch (Exception arg2)
					{
						if (logger != null)
						{
							logger.LogWarning((object)$"Could not restore Jewelcrafting's Lucky creature-kill postfix: {arg2}");
						}
					}
				}
				array2 = FindPostfixes(methodInfo, methodInfo2);
				if (array2.Length != 1 || array2[0].owner != originalPatchRegistration.Owner || !originalPatchRegistration.MatchesMetadata(array2[0]))
				{
					if (logger != null)
					{
						logger.LogWarning((object)"Jewelcrafting's Lucky creature-kill postfix is not restored exactly once with its saved metadata.");
					}
					return false;
				}
				if (criticalWrapperUncertain)
				{
					try
					{
						new Harmony(originalPatchRegistration.Owner).Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						criticalWrapperUncertain = false;
					}
					catch (Exception arg3)
					{
						if (logger != null)
						{
							logger.LogWarning((object)$"Could not rebuild Character.OnDeath after a Lucky patch mutation failure: {arg3}");
						}
						return false;
					}
				}
				array2 = FindPostfixes(methodInfo, methodInfo2);
				if (FindPostfixes(methodInfo, methodInfo3).Length != 0 || array2.Length != 1 || array2[0].owner != originalPatchRegistration.Owner || !originalPatchRegistration.MatchesMetadata(array2[0]))
				{
					if (logger != null)
					{
						logger.LogWarning((object)"Character.OnDeath did not settle into the expected original-only Lucky patch state.");
					}
					return false;
				}
				ClearCriticalPatchState();
				return true;
			}
			catch (Exception arg4)
			{
				if (logger != null)
				{
					logger.LogWarning((object)$"Could not reconcile Jewelcrafting's Lucky creature-kill postfix: {arg4}");
				}
				return false;
			}
		}

		private static bool RemoveAuxiliaryPatches(ManualLogSource? logger)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				if (Harmony.HasAnyPatches("sighsorry.jewelcraftingkarmadrops.luckycompat") && auxiliaryPatchTargets.Length == 0)
				{
					if (logger != null)
					{
						logger.LogWarning((object)"Cannot safely rebuild Lucky auxiliary wrappers because their saved targets are missing.");
					}
					return false;
				}
				Harmony val = (harmony = (Harmony?)(((object)harmony) ?? ((object)new Harmony("sighsorry.jewelcraftingkarmadrops.luckycompat"))));
				try
				{
					val.UnpatchSelf();
				}
				catch (Exception arg)
				{
					if (logger != null)
					{
						logger.LogWarning((object)$"Could not fully remove Lucky auxiliary patches by owner: {arg}");
					}
				}
				bool flag = true;
				foreach (MethodInfo item in auxiliaryPatchTargets.Distinct())
				{
					try
					{
						val.Patch((MethodBase)item, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
					}
					catch (Exception arg2)
					{
						flag = false;
						if (logger != null)
						{
							logger.LogWarning((object)$"Could not rebuild Lucky auxiliary target {item.DeclaringType?.FullName}.{item.Name}: {arg2}");
						}
					}
				}
				if (!flag || Harmony.HasAnyPatches("sighsorry.jewelcraftingkarmadrops.luckycompat"))
				{
					if (logger != null)
					{
						logger.LogWarning((object)"Lucky auxiliary patches are not fully removed; cleanup state was retained for retry.");
					}
					return false;
				}
				auxiliaryPatchTargets = Array.Empty<MethodInfo>();
				harmony = null;
				return true;
			}
			catch (Exception arg3)
			{
				if (logger != null)
				{
					logger.LogWarning((object)$"Could not verify Lucky auxiliary patch cleanup: {arg3}");
				}
				return false;
			}
		}

		private static Patch[] FindPostfixes(MethodBase target, MethodInfo patchMethod)
		{
			return Harmony.GetPatchInfo(target)?.Postfixes.Where((Patch patch) => patch.PatchMethod == patchMethod).ToArray() ?? Array.Empty<Patch>();
		}

		private static bool HasCriticalPatchState()
		{
			if (!(characterOnDeathTarget != null) && !(originalKillPostfixTarget != null) && !(replacementKillPostfixTarget != null) && originalKillPatchRegistration == null)
			{
				return criticalWrapperUncertain;
			}
			return true;
		}

		private static void ClearCriticalPatchState()
		{
			characterOnDeathTarget = null;
			originalKillPostfixTarget = null;
			replacementKillPostfixTarget = null;
			originalKillPatchRegistration = null;
			criticalWrapperUncertain = false;
		}

		private static void ValidateConfiguration()
		{
			_ = Plugin.MaximumLuckyDropBonusPercent.Value;
			_ = Plugin.MaximumLuckyDamageDealtReductionPercent.Value;
			_ = Plugin.MaximumLuckyDamageTakenIncreasePercent.Value;
			_ = Plugin.LuckyDropPrefabBlacklist.Value;
		}

		private static void ReplaceTrackCreatureKills(Character __instance)
		{
			if (!Object.op_Implicit((Object)(object)__instance) || __instance.IsTamed() || !Object.op_Implicit((Object)(object)((Component)__instance).GetComponent<CharacterDrop>()))
			{
				return;
			}
			HitData? lastHit = ValheimPrivateFields.GetLastHit(__instance);
			Character obj = ((lastHit != null) ? lastHit.GetAttacker() : null);
			Player val = (Player)(object)((obj is Player) ? obj : null);
			if (val != null && HasActiveLuckyEffect(val))
			{
				ZNetView? netView = GetNetView((Character?)(object)val);
				if (netView != null)
				{
					netView.InvokeRPC("Jewelcrafting Lucky", Array.Empty<object>());
				}
			}
		}

		private static void ApplyLuckyToCurrentDropRoll(CharacterDrop __instance, out LuckyDropRollState? __state)
		{
			__state = null;
			if (!Object.op_Implicit((Object)(object)__instance) || __instance.m_drops == null)
			{
				return;
			}
			Character character = ValheimPrivateFields.GetCharacter(__instance);
			if (!Object.op_Implicit((Object)(object)character) || character.IsTamed() || !JewelcraftingDropGate.IsDeathInProgress(character))
			{
				return;
			}
			HitData? lastHit = ValheimPrivateFields.GetLastHit(character);
			Character obj = ((lastHit != null) ? lastHit.GetAttacker() : null);
			Player val = (Player)(object)((obj is Player) ? obj : null);
			if (val == null)
			{
				return;
			}
			float num = Lucky.ChanceMultiplier(val);
			if (num <= 1f)
			{
				return;
			}
			LuckyDropRollState luckyDropRollState = (__state = new LuckyDropRollState());
			foreach (Drop drop in __instance.m_drops)
			{
				if (!CanMultiplyDrop(drop))
				{
					continue;
				}
				luckyDropRollState.Capture(drop);
				drop.m_chance *= num;
				if (drop.m_chance > 1f)
				{
					int num2 = Mathf.FloorToInt(drop.m_chance);
					if (Random.value < drop.m_chance - (float)num2)
					{
						num2++;
					}
					drop.m_amountMin *= num2;
					drop.m_amountMax *= num2;
					drop.m_chance = 1f;
				}
			}
		}

		private static Exception? RestoreDropsAfterCurrentRoll(LuckyDropRollState? __state, Exception? __exception)
		{
			__state?.Restore();
			return __exception;
		}

		private static bool RejectMissingPlayerChanceMultiplier(Player? __0, ref float __result)
		{
			if (Object.op_Implicit((Object)(object)__0))
			{
				return true;
			}
			__result = 1f;
			return false;
		}

		private static void ApplyDropBonusCap(ref float __result)
		{
			__result = Mathf.Min(__result, DropBonusCap());
		}

		private static void ApplyDamageDealtReductionCap(ref float __result)
		{
			__result = Mathf.Max(__result, 1f - DamageDealtReductionCap());
		}

		private static void ApplyDamageTakenIncreaseCap(ref float __result)
		{
			__result = Mathf.Min(__result, 1f + DamageTakenIncreaseCap());
		}

		private static void DisplayCurrentAndMaximumInItemTooltip(Lucky __instance, ref string __result)
		{
			if (Object.op_Implicit((Object)(object)__instance) && !((Object)(object)((StatusEffect)__instance).m_character != (Object)null) && __result.LastIndexOf("$jc_lucky_luck:", StringComparison.Ordinal) >= 0 && __result.IndexOf("$jc_lucky_damage_done:", StringComparison.Ordinal) >= 0 && __result.IndexOf("$jc_lucky_damage_received:", StringComparison.Ordinal) >= 0)
			{
				Player localPlayer = Player.m_localPlayer;
				int luckyStacks = GetLuckyStacks(localPlayer, ((Object)__instance).name);
				int num = Lucky.MaxStacks((Character)(object)localPlayer);
				float value = Mathf.Min((float)Mathf.Min(luckyStacks, num) * 0.02f, DropBonusCap());
				float value2 = Mathf.Min((float)num * 0.02f, DropBonusCap());
				float value3 = Mathf.Min((float)luckyStacks * 0.01f, DamageDealtReductionCap());
				float value4 = DamageDealtReductionCap();
				float value5 = Mathf.Min((float)luckyStacks * 0.03f, DamageTakenIncreaseCap());
				float value6 = DamageTakenIncreaseCap();
				__result = ReplaceTooltipLine(__result, "$jc_lucky_luck:", "$jc_lucky_luck: <color=orange>+" + FormatPercent(value) + " / +" + FormatPercent(value2) + "</color>");
				__result = ReplaceTooltipLine(__result, "$jc_lucky_damage_done:", "$jc_lucky_damage_done: <color=orange>-" + FormatPercent(value3) + " / -" + FormatPercent(value4) + "</color>");
				__result = ReplaceTooltipLine(__result, "$jc_lucky_damage_received:", "$jc_lucky_damage_received: <color=orange>+" + FormatPercent(value5) + " / +" + FormatPercent(value6) + "</color>");
			}
		}

		private static string ReplaceTooltipLine(string tooltip, string marker, string replacement)
		{
			int num = tooltip.IndexOf(marker, StringComparison.Ordinal);
			if (num < 0)
			{
				return tooltip;
			}
			int num2 = tooltip.IndexOf('\n', num);
			if (num2 < 0)
			{
				return tooltip.Substring(0, num) + replacement;
			}
			string text = ((num2 > num && tooltip[num2 - 1] == '\r') ? "\r\n" : "\n");
			return tooltip.Substring(0, num) + replacement + text + tooltip.Substring(num2 + 1);
		}

		private static string FormatPercent(float value)
		{
			return value.ToString("0.##%", CultureInfo.CurrentCulture);
		}

		private static bool HasActiveLuckyEffect(Player player)
		{
			ZNetView netView = GetNetView((Character?)(object)player);
			if (!Object.op_Implicit((Object)(object)player) || !Object.op_Implicit((Object)(object)netView))
			{
				return false;
			}
			ZDO zdo = netView.GetZDO();
			if (zdo != null)
			{
				return Lucky.SeNames.Any((string seName) => zdo.GetInt("Jewelcrafting Lucky " + seName, 0) > 0);
			}
			return false;
		}

		private static int GetLuckyStacks(Player? player, string statusEffectName)
		{
			ZNetView netView = GetNetView((Character?)(object)player);
			if (!Object.op_Implicit((Object)(object)player) || !Object.op_Implicit((Object)(object)netView))
			{
				return 0;
			}
			ZDO zDO = netView.GetZDO();
			if (zDO != null)
			{
				return Math.Max(0, zDO.GetInt("Jewelcrafting Lucky " + statusEffectName, 0));
			}
			return 0;
		}

		private static ZNetView? GetNetView(Character? character)
		{
			if (!((Object)(object)character == (Object)null) && characterNetView != null)
			{
				return characterNetView.Invoke(character);
			}
			return null;
		}

		private static bool CanMultiplyDrop(Drop? drop)
		{
			GameObject val = drop?.m_prefab;
			if (!Object.op_Implicit((Object)(object)val) || (Object)(object)val.GetComponentInChildren<Character>(true) != (Object)null)
			{
				return false;
			}
			string name = ((Object)val).name;
			return !dropPrefabBlacklist.Contains(name);
		}

		private static float DropBonusCap()
		{
			return ReadPercentCap(Plugin.MaximumLuckyDropBonusPercent.Value, 250f);
		}

		private static float DamageDealtReductionCap()
		{
			return ReadPercentCap(Plugin.MaximumLuckyDamageDealtReductionPercent.Value, 80f);
		}

		private static float DamageTakenIncreaseCap()
		{
			return ReadPercentCap(Plugin.MaximumLuckyDamageTakenIncreasePercent.Value, 300f);
		}

		private static float ReadPercentCap(float configuredPercent, float originalMaximumPercent)
		{
			if (float.IsNaN(configuredPercent))
			{
				configuredPercent = originalMaximumPercent;
			}
			return Mathf.Clamp(configuredPercent, 0f, originalMaximumPercent) / 100f;
		}

		private static void OnBlacklistSettingChanged(object? sender, EventArgs eventArgs)
		{
			RefreshDropPrefabBlacklist();
		}

		private static void RefreshDropPrefabBlacklist()
		{
			dropPrefabBlacklist = new HashSet<string>(from entry in (Plugin.LuckyDropPrefabBlacklist.Value ?? string.Empty).Split(new char[4] { ',', ';', '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
				select entry.Trim() into entry
				where entry.Length > 0
				select entry, StringComparer.OrdinalIgnoreCase);
		}

		private static void RemoveBlacklistHandler()
		{
			if (blacklistHandlerRegistered)
			{
				Plugin.LuckyDropPrefabBlacklist.SettingChanged -= OnBlacklistSettingChanged;
				blacklistHandlerRegistered = false;
			}
		}
	}
	internal static class MagicRepairCompat
	{
		private const float RepairInterval = 60f;

		private const string HarmonyOwner = "sighsorry.jewelcraftingkarmadrops.magicrepaircompat";

		internal static void TryInstall(ManualLogSource logger)
		{
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Expected O, but got Unknown
			//IL_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Expected O, but got Unknown
			Harmony val = null;
			try
			{
				Type type = AccessTools.TypeByName("Jewelcrafting.GemEffects.MagicRepair") ?? throw new TypeLoadException("Jewelcrafting.GemEffects.MagicRepair was not found.");
				MethodInfo methodInfo = AccessTools.DeclaredMethod(type, "UpdateStatusEffect", new Type[1] { typeof(float) }, (Type[])null) ?? throw new MissingMethodException(type.FullName, "UpdateStatusEffect");
				MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(MagicRepairCompat), "BeforeUpdateStatusEffect", (Type[])null, (Type[])null) ?? throw new MissingMethodException(typeof(MagicRepairCompat).FullName, "BeforeUpdateStatusEffect");
				val = new Harmony("sighsorry.jewelcraftingkarmadrops.magicrepaircompat");
				val.Patch((MethodBase)methodInfo, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
				logger.LogInfo((object)"Applied the Jewelcrafting Magic Repair lifecycle compatibility patch.");
			}
			catch (Exception arg)
			{
				if (val != null)
				{
					try
					{
						val.UnpatchSelf();
					}
					catch (Exception arg2)
					{
						logger.LogWarning((object)$"Could not fully remove a partial Magic Repair compatibility patch: {arg2}");
					}
				}
				logger.LogWarning((object)$"Magic Repair compatibility patch was not applied; Karma Drops remains active: {arg}");
			}
		}

		private static void BeforeUpdateStatusEffect(StatusEffect __instance, float dt, ref float ___m_tickTimer, GameObject[]? ___m_startEffectInstances)
		{
			if (!(___m_tickTimer + dt < 60f))
			{
				Player localPlayer = Player.m_localPlayer;
				if (!Object.op_Implicit((Object)(object)localPlayer) || (Object)(object)__instance.m_character != (Object)(object)localPlayer || ___m_startEffectInstances == null || !Object.op_Implicit((Object)(object)ZNetScene.instance))
				{
					___m_tickTimer = 0f - dt;
				}
			}
		}
	}
	[BepInPlugin("sighsorry.jewelcraftingkarmadrops", "Jewelcrafting Karma Drops", "1.2.0")]
	[BepInDependency("org.bepinex.plugins.jewelcrafting", "2.0.0")]
	public sealed class Plugin : BaseUnityPlugin
	{
		public const string PluginGuid = "sighsorry.jewelcraftingkarmadrops";

		public const string PluginName = "Jewelcrafting Karma Drops";

		public const string PluginVersion = "1.2.0";

		private const string JewelcraftingGuid = "org.bepinex.plugins.jewelcrafting";

		private static readonly ConfigSync ConfigSynchronizer = new ConfigSync("sighsorry.jewelcraftingkarmadrops")
		{
			DisplayName = "Jewelcrafting Karma Drops",
			CurrentVersion = "1.2.0",
			MinimumRequiredVersion = "1.2.0",
			ModRequired = true
		};

		private Harmony? harmony;

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

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

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

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

		private void Awake()
		{
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			try
			{
				BindSyncedConfiguration();
				ValheimPrivateFields.Validate();
				harmony = new Harmony("sighsorry.jewelcraftingkarmadrops");
				harmony.PatchAll(typeof(Plugin).Assembly);
				JewelcraftingDropGate.Register();
				((BaseUnityPlugin)this).Logger.LogInfo((object)"Jewelcrafting creature-death loot now requires a CreatureManager Karma-style player-side kill (player, tamed creature, PlayerSpawned creature, or an exact attributed poison/fire/spirit DoT).");
			}
			catch (Exception arg)
			{
				CleanupCore("rolling back partial initialization");
				((BaseUnityPlugin)this).Logger.LogError((object)string.Format("Failed to initialize {0}: {1}", "Jewelcrafting Karma Drops", arg));
				throw;
			}
			TryInstallCompatibility("Magic Repair", delegate
			{
				MagicRepairCompat.TryInstall(((BaseUnityPlugin)this).Logger);
			});
			TryInstallCompatibility("Lucky", delegate
			{
				LuckyCompat.TryInstall(((BaseUnityPlugin)this).Logger);
			});
			TryInstallCompatibility("socket-limit", delegate
			{
				SocketLimitCompat.TryInstall(((BaseUnityPlugin)this).Logger);
			});
			TryInstallCompatibility("synergy YAML", delegate
			{
				SynergyYamlCompat.TryInstall(((BaseUnityPlugin)this).Logger);
			});
		}

		private void OnDestroy()
		{
			TryCleanup("Magic Repair compatibility", delegate
			{
				Harmony.UnpatchID("sighsorry.jewelcraftingkarmadrops.magicrepaircompat");
			});
			TryCleanup("Lucky compatibility", delegate
			{
				LuckyCompat.Shutdown(((BaseUnityPlugin)this).Logger);
			});
			TryCleanup("socket-limit compatibility", delegate
			{
				SocketLimitCompat.Shutdown(((BaseUnityPlugin)this).Logger);
			});
			TryCleanup("synergy YAML compatibility", delegate
			{
				SynergyYamlCompat.Shutdown(((BaseUnityPlugin)this).Logger);
			});
			CleanupCore("shutting down");
		}

		private void BindSyncedConfiguration()
		{
			ConfigEntry<bool> val = ((BaseUnityPlugin)this).Config.Bind<bool>("1 - General", "Lock Configuration", true, "Lock synchronized gameplay settings so that only server administrators can change them.");
			ConfigSynchronizer.AddLockingConfigEntry<bool>(val);
			MaximumLuckyDropBonusPercent = BindSynced("2 - Lucky", "Maximum Lucky Drop Bonus (%)", 250f, 0f, 250f, "Maximum drop bonus contributed by each active Lucky status effect.");
			MaximumLuckyDamageDealtReductionPercent = BindSynced("2 - Lucky", "Maximum Lucky Damage Dealt Reduction (%)", 80f, 0f, 80f, "Maximum outgoing-damage reduction applied by each active Lucky status effect.");
			MaximumLuckyDamageTakenIncreasePercent = BindSynced("2 - Lucky", "Maximum Lucky Damage Taken Increase (%)", 300f, 0f, 300f, "Maximum incoming-damage increase applied by each active Lucky status effect.");
			LuckyDropPrefabBlacklist = ((BaseUnityPlugin)this).Config.Bind<string>("2 - Lucky", "Lucky Drop Prefab Blacklist", string.Empty, "Comma-separated internal prefab names whose CharacterDrop entries must not receive the Lucky multiplier. Creature prefabs are always excluded.");
			ConfigSynchronizer.AddConfigEntry<string>(LuckyDropPrefabBlacklist);
		}

		private ConfigEntry<float> BindSynced(string section, string key, float defaultValue, float minimum, float maximum, string description)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			ConfigEntry<float> val = ((BaseUnityPlugin)this).Config.Bind<float>(section, key, defaultValue, new ConfigDescription(description, (AcceptableValueBase)(object)new AcceptableValueRange<float>(minimum, maximum), Array.Empty<object>()));
			ConfigSynchronizer.AddConfigEntry<float>(val);
			return val;
		}

		private void TryInstallCompatibility(string name, Action install)
		{
			try
			{
				install();
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"{name} compatibility was not installed; Karma Drops remains active: {arg}");
			}
		}

		private void CleanupCore(string operation)
		{
			TryCleanup("Jewelcrafting drop gate", JewelcraftingDropGate.Reset, operation);
			TryCleanup("core Harmony patches", delegate
			{
				Harmony? obj = harmony;
				if (obj != null)
				{
					obj.UnpatchSelf();
				}
				harmony = null;
			}, operation);
			TryCleanup("Karma death attribution", KarmaDeathAttribution.Reset, operation);
		}

		private void TryCleanup(string name, Action cleanup, string operation = "shutting down")
		{
			try
			{
				cleanup();
			}
			catch (Exception arg)
			{
				((BaseUnityPlugin)this).Logger.LogWarning((object)$"Failed to clean up {name} while {operation}: {arg}");
			}
		}
	}
	internal static class SocketLimitCompat
	{
		private enum PatchResult
		{
			None,
			Patched,
			AlreadyFixed
		}

		private const string HarmonyOwner = "sighsorry.jewelcraftingkarmadrops.socketlimitcompat";

		private const int CorrectLastTableLevelIndex = 3;

		private static PatchResult patchResult;

		private static bool installed;

		internal static void TryInstall(ManualLogSource logger)
		{
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Expected O, but got Unknown
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Expected O, but got Unknown
			if (installed)
			{
				return;
			}
			Harmony val = null;
			try
			{
				MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(GemStones), "CanAddMoreSockets", new Type[1] { typeof(ItemData) }, (Type[])null) ?? throw new MissingMethodException(typeof(GemStones).FullName, "CanAddMoreSockets(ItemDrop.ItemData)");
				MethodInfo methodInfo2 = AccessTools.DeclaredMethod(typeof(SocketLimitCompat), "FixTableLevelIndexCap", (Type[])null, (Type[])null) ?? throw new MissingMethodException(typeof(SocketLimitCompat).FullName, "FixTableLevelIndexCap");
				patchResult = PatchResult.None;
				val = new Harmony("sighsorry.jewelcraftingkarmadrops.socketlimitcompat");
				val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, (HarmonyMethod)null, new HarmonyMethod(methodInfo2), (HarmonyMethod)null, (HarmonyMethod)null);
				switch (patchResult)
				{
				case PatchResult.Patched:
					installed = true;
					logger.LogInfo((object)"Applied the Jewelcrafting level-4 Gemcutters Table socket-limit compatibility patch.");
					break;
				case PatchResult.AlreadyFixed:
					val.UnpatchSelf();
					logger.LogInfo((object)"Jewelcrafting already uses the level-4 Gemcutters Table socket-limit setting; the compatibility patch was not needed.");
					break;
				default:
					throw new InvalidOperationException("The socket-limit transpiler completed without reporting a result.");
				}
			}
			catch (Exception arg)
			{
				if (val != null)
				{
					try
					{
						val.UnpatchSelf();
						installed = false;
					}
					catch (Exception arg2)
					{
						installed = true;
						logger.LogWarning((object)$"Could not fully remove a partial socket-limit compatibility patch: {arg2}");
					}
				}
				else
				{
					installed = false;
				}
				logger.LogWarning((object)$"Socket-limit compatibility patch was not applied; Karma Drops remains active: {arg}");
			}
		}

		internal static void Shutdown(ManualLogSource logger)
		{
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			if (!installed)
			{
				return;
			}
			try
			{
				new Harmony("sighsorry.jewelcraftingkarmadrops.socketlimitcompat").UnpatchSelf();
				installed = false;
			}
			catch (Exception arg)
			{
				logger.LogWarning((object)$"Could not remove the socket-limit compatibility patch during shutdown: {arg}");
			}
		}

		private static IEnumerable<CodeInstruction> FixTableLevelIndexCap(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			FieldInfo fieldInfo = AccessTools.Field(typeof(Jewelcrafting), "maxSocketsTableLevel") ?? throw new MissingFieldException(typeof(Jewelcrafting).FullName, "maxSocketsTableLevel");
			MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(Math), "Min", new Type[2]
			{
				typeof(int),
				typeof(int)
			}, (Type[])null) ?? throw new MissingMethodException(typeof(Math).FullName, "Min(Int32, Int32)");
			int num = -1;
			int num2 = -1;
			for (int i = 0; i < list.Count; i++)
			{
				if (!CodeInstructionExtensions.LoadsField(list[i], fieldInfo, false))
				{
					continue;
				}
				int num3 = NextNonNop(list, i + 1);
				if (num3 < 0 || (!CodeInstructionExtensions.LoadsConstant(list[num3], 2L) && !CodeInstructionExtensions.LoadsConstant(list[num3], 3L)))
				{
					continue;
				}
				bool flag = false;
				int num4 = Math.Min(list.Count, num3 + 10);
				for (int j = num3 + 1; j < num4; j++)
				{
					if (CodeInstructionExtensions.Calls(list[j], methodInfo))
					{
						flag = true;
						break;
					}
				}
				if (flag)
				{
					if (num >= 0)
					{
						throw new InvalidOperationException("Found more than one maxSocketsTableLevel index-cap pattern.");
					}
					num = num3;
					num2 = (CodeInstructionExtensions.LoadsConstant(list[num3], 2L) ? 2 : 3);
				}
			}
			if (num < 0)
			{
				throw new InvalidOperationException("Could not find the maxSocketsTableLevel Math.Min index-cap pattern.");
			}
			if (num2 == 3)
			{
				patchResult = PatchResult.AlreadyFixed;
				return list;
			}
			list[num].opcode = OpCodes.Ldc_I4_3;
			list[num].operand = null;
			patchResult = PatchResult.Patched;
			return list;
		}

		private static int NextNonNop(IReadOnlyList<CodeInstruction> codes, int start)
		{
			for (int i = start; i < codes.Count; i++)
			{
				if (codes[i].opcode != OpCodes.Nop)
				{
					return i;
				}
			}
			return -1;
		}
	}
	internal static class SynergyYamlCompat
	{
		private enum ProbeState
		{
			BugPresent,
			Fixed,
			Incompatible
		}

		private const string HarmonyOwner = "sighsorry.jewelcraftingkarmadrops.synergyyamlcompat";

		private const string ProbeKey = "__karmadrops_synergy_unset_probe_9f67b5c1__";

		private static readonly object Sync = new object();

		private static Harmony? harmony;

		private static ManualLogSource? logger;

		private static bool installed;

		private static bool warnedPostfixFailure;

		internal static void TryInstall(ManualLogSource log)
		{
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_015c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Expected O, but got Unknown
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c4: Expected O, but got Unknown
			lock (Sync)
			{
				if (installed)
				{
					return;
				}
				logger = log;
				warnedPostfixFailure = false;
				if (Harmony.HasAnyPatches("sighsorry.jewelcraftingkarmadrops.synergyyamlcompat"))
				{
					new Harmony("sighsorry.jewelcraftingkarmadrops.synergyyamlcompat").UnpatchSelf();
					if (Harmony.HasAnyPatches("sighsorry.jewelcraftingkarmadrops.synergyyamlcompat"))
					{
						throw new InvalidOperationException("Failed to remove stale synergy YAML compatibility patches before installation.");
					}
				}
				MethodInfo methodInfo = AccessTools.DeclaredMethod(typeof(EffectDef), "Parse", new Type[2]
				{
					typeof(object),
					typeof(List<string>).MakeByRefType()
				}, (Type[])null) ?? throw new MissingMethodException(typeof(EffectDef).FullName, "Parse");
				if (methodInfo.ReturnType != typeof(ParseResult))
				{
					throw new InvalidOperationException("Unexpected " + typeof(EffectDef).FullName + ".Parse return type '" + methodInfo.ReturnType.FullName + "'.");
				}
				if (EffectDef.ValidEffects.ContainsKey("__karmadrops_synergy_unset_probe_9f67b5c1__"))
				{
					throw new InvalidOperationException("The synergy compatibility probe key '__karmadrops_synergy_unset_probe_9f67b5c1__' is already registered as an effect.");
				}
				switch (Probe())
				{
				case ProbeState.Fixed:
					log.LogInfo((object)"Jewelcrafting already accepts synergy 'unset' markers without a false effect-name error; the synergy YAML compatibility patch is not needed.");
					break;
				default:
					throw new InvalidOperationException("Jewelcrafting synergy 'unset' parsing does not match the supported runtime contract.");
				case ProbeState.BugPresent:
				{
					Harmony val = new Harmony("sighsorry.jewelcraftingkarmadrops.synergyyamlcompat");
					try
					{
						MethodInfo postfix = AccessTools.DeclaredMethod(typeof(SynergyYamlCompat), "AfterParse", (Type[])null, (Type[])null) ?? throw new MissingMethodException(typeof(SynergyYamlCompat).FullName, "AfterParse");
						val.Patch((MethodBase)methodInfo, (HarmonyMethod)null, new HarmonyMethod(postfix)
						{
							priority = 0
						}, (HarmonyMethod)null, (HarmonyMethod)null, (HarmonyMethod)null);
						Patches patchInfo = Harmony.GetPatchInfo((MethodBase)methodInfo);
						if (patchInfo == null || !patchInfo.Postfixes.Any((Patch patch) => patch.owner == "sighsorry.jewelcraftingkarmadrops.synergyyamlcompat" && patch.PatchMethod == postfix))
						{
							throw new InvalidOperationException("Harmony did not register the synergy YAML compatibility postfix.");
						}
						if (Probe() != ProbeState.Fixed)
						{
							throw new InvalidOperationException("Jewelcrafting synergy 'unset' parsing did not pass its post-install semantic probe.");
						}
						harmony = val;
						installed = true;
						log.LogInfo((object)"Jewelcrafting synergy YAML compatibility enabled: exact '<synergy id>: unset' definitions now survive hot reload and server synchronization.");
						break;
					}
					catch (Exception ex)
					{
						Exception ex2 = null;
						try
						{
							val.UnpatchSelf();
							if (Harmony.HasAnyPatches("sighsorry.jewelcraftingkarmadrops.synergyyamlcompat"))
							{
								ex2 = new InvalidOperationException("Harmony patches remain after rolling back synergy YAML compatibility.");
							}
						}
						catch (Exception ex3)
						{
							ex2 = ex3;
						}
						harmony = null;
						installed = false;
						if (ex2 != null)
						{
							throw new AggregateException("Failed to install and fully roll back synergy YAML compatibility.", ex, ex2);
						}
						throw;
					}
				}
				}
			}
		}

		internal static void Shutdown(ManualLogSource? log = null)
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			lock (Sync)
			{
				if (!installed && !Harmony.HasAnyPatches("sighsorry.jewelcraftingkarmadrops.synergyyamlcompat"))
				{
					logger = null;
					warnedPostfixFailure = false;
					return;
				}
				((Harmony)(((object)harmony) ?? ((object)new Harmony("sighsorry.jewelcraftingkarmadrops.synergyyamlcompat")))).UnpatchSelf();
				if (Harmony.HasAnyPatches("sighsorry.jewelcraftingkarmadrops.synergyyamlcompat"))
				{
					throw new InvalidOperationException("Failed to remove all synergy YAML compatibility patches.");
				}
				harmony = null;
				installed = false;
				logger = null;
				warnedPostfixFailure = false;
				if (log != null)
				{
					log.LogInfo((object)"Jewelcrafting synergy YAML compatibility disabled.");
				}
			}
		}

		private static void AfterParse(object? __0, ref List<string> __1, ref ParseResult __result)
		{
			try
			{
				if (!(__0 is Dictionary<object, object> dictionary) || __1 == null || __result.Synergy == null)
				{
					return;
				}
				foreach (KeyValuePair<object, object> item in dictionary)
				{
					if (item.Value is string text && !(text != "unset"))
					{
						string text2 = item.Key?.ToString() ?? string.Empty;
						if (text2.Length != 0 && __result.Synergy.TryGetValue(text2, out var value) && !(value.Name != string.Empty))
						{
							__1.Remove(BuildInvalidEffectError(text2));
						}
					}
				}
			}
			catch (Exception arg)
			{
				if (!warnedPostfixFailure)
				{
					warnedPostfixFailure = true;
					ManualLogSource? obj = logger;
					if (obj != null)
					{
						obj.LogWarning((object)("Failed to inspect a Jewelcrafting synergy 'unset' definition; " + $"the original parser result was preserved: {arg}"));
					}
				}
			}
		}

		private static ProbeState Probe()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			List<string> list = default(List<string>);
			if (!EffectDef.Parse((object)new Dictionary<object, object> { ["__karmadrops_synergy_unset_probe_9f67b5c1__"] = "unset" }, ref list).Synergy.TryGetValue("__karmadrops_synergy_unset_probe_9f67b5c1__", out var value) || !(value.Name == string.Empty))
			{
				return ProbeState.Incompatible;
			}
			if (list.Count == 0)
			{
				return ProbeState.Fixed;
			}
			if (list.Count != 1 || !string.Equals(list[0], BuildInvalidEffectError("__karmadrops_synergy_unset_probe_9f67b5c1__"), StringComparison.Ordinal))
			{
				return ProbeState.Incompatible;
			}
			return ProbeState.BugPresent;
		}

		private static string BuildInvalidEffectError(string key)
		{
			return "'" + key + "' is not a valid effect name. Valid effects are: '" + string.Join("', '", EffectDef.ValidEffects.Keys) + "'. There also can be a 'gems' section to define gem spawn chances.";
		}
	}
	internal static class ValheimPrivateFields
	{
		private static readonly FieldRef<Character, HitData> characterLastHit;

		private static readonly FieldRef<SE_Poison, float> poisonDamageLeft;

		private static readonly FieldRef<SE_Burning, float> burningFireDamageLeft;

		private static readonly FieldRef<SE_Burning, float> burningFireDamagePerHit;

		private static readonly FieldRef<SE