Decompiled source of ExoticUpgrades v0.4.0

plugins/ArceDev.ExoticUpgrades.dll

Decompiled 2 months ago
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using FishNet;
using FishNet.Broadcast;
using FishNet.Connection;
using FishNet.Object;
using FishNet.Serializing;
using FishNet.Transporting;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using StatusCore;
using TMPro;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.UI;
using UpgradeCore;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("ArceDev.ExoticUpgrades")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("0.4.0.0")]
[assembly: AssemblyInformationalVersion("0.4.0")]
[assembly: AssemblyProduct("ArceDev.ExoticUpgrades")]
[assembly: AssemblyTitle("Exotic Upgrades")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.4.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
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 BepInEx
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	[Embedded]
	internal sealed class BepInAutoPluginAttribute : Attribute
	{
		public BepInAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace BepInEx.Preloader.Core.Patching
{
	[AttributeUsage(AttributeTargets.Class, Inherited = false, AllowMultiple = false)]
	[Conditional("CodeGeneration")]
	[Embedded]
	internal sealed class PatcherAutoPluginAttribute : Attribute
	{
		public PatcherAutoPluginAttribute(string? id = null, string? name = null, string? version = null)
		{
		}
	}
}
namespace Microsoft.CodeAnalysis
{
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace ExoticUpgrades
{
	internal static class ExoticGacha
	{
		private readonly record struct PendingReward(Player Player, byte ItemId, int UpgradeIndex, byte SlotIndex);

		[HarmonyPatch(typeof(SlotMachineManager), "SendRoll")]
		private static class SendRollPatch
		{
			private static void Prefix(byte[] itemIDs, byte[] itemSkins)
			{
				float num = Mathf.Clamp01(_exoticChance.Value);
				for (int i = 0; i < itemIDs.Length; i++)
				{
					Item val = ((itemIDs[i] == byte.MaxValue) ? null : GameInfo.IDToItem(itemIDs[i]));
					if (Object.op_Implicit((Object)(object)val))
					{
						int[] array = UpgradeState.CompatibleUpgradeIndices(val);
						if (array.Length != 0 && Random.value < num)
						{
							itemSkins[i] = Encode(array[Random.Range(0, array.Length)]);
						}
					}
				}
			}
		}

		[HarmonyPatch(typeof(SlotMachine), "Roll")]
		private static class RollPatch
		{
			private static void Prefix(Player roller, byte[] itemIDs, byte[] itemSkins, byte rolled)
			{
				_pending = (TryDecode(itemSkins[rolled], out var upgradeIndex) ? new PendingReward?(new PendingReward(roller, itemIDs[rolled], upgradeIndex, rolled)) : ((PendingReward?)null));
			}

			private static void Postfix()
			{
				PendingReward? pending = _pending;
				if (pending.HasValue)
				{
					RolledSkinField.SetValue(null, (byte)0);
				}
			}
		}

		[HarmonyPatch(typeof(MachineSlot), "SetIndex")]
		private static class SetIndexPatch
		{
			private static void Prefix(MachineSlot __instance, ref byte skinIndex)
			{
				if (TryDecode(skinIndex, out var upgradeIndex))
				{
					SlotUpgrades[__instance] = upgradeIndex;
					skinIndex = 0;
				}
				else
				{
					SlotUpgrades.Remove(__instance);
				}
			}

			private static void Postfix(MachineSlot __instance, TextMeshProUGUI ____itemText, Image ____outline)
			{
				((TMP_Text)____itemText).text = "";
				((Behaviour)____outline).enabled = true;
				HideUpgradeIcon(__instance);
				if (SlotUpgrades.TryGetValue(__instance, out var value))
				{
					UpgradeDefinition upgrade = UpgradeState.GetUpgrade(value);
					((Behaviour)____outline).enabled = false;
					ShowUpgradeIcon(__instance, ((Component)____outline).transform.parent, upgrade);
				}
			}
		}

		[HarmonyPatch(typeof(SlotMachine), "OnRollFinished")]
		private static class RollFinishedPatch
		{
			private static void Prefix(MachineSlot[] ____machineSlots)
			{
				PendingReward? pending = _pending;
				if (pending.HasValue)
				{
					PendingReward valueOrDefault = pending.GetValueOrDefault();
					if (valueOrDefault.SlotIndex < ____machineSlots.Length && UpgradeIcons.TryGetValue(____machineSlots[valueOrDefault.SlotIndex], out Image value) && Object.op_Implicit((Object)(object)value))
					{
						SetWinnerIconLayout(((Graphic)value).rectTransform);
					}
					if (Object.op_Implicit((Object)(object)valueOrDefault.Player) && ((NetworkBehaviour)valueOrDefault.Player).Owner.IsLocalClient)
					{
						UpgradeState.Unlock(valueOrDefault.Player.SteamID, valueOrDefault.ItemId, valueOrDefault.UpgradeIndex);
					}
				}
			}

			private static void Postfix(TextMeshProUGUI ____rollerText)
			{
				PendingReward? pending = _pending;
				if (pending.HasValue)
				{
					PendingReward valueOrDefault = pending.GetValueOrDefault();
					string name = GameInfo.IDToItem(valueOrDefault.ItemId).GetName();
					string displayName = UpgradeState.GetUpgrade(valueOrDefault.UpgradeIndex).DisplayName;
					((TMP_Text)____rollerText).text = "EXOTIC: " + displayName + " — " + name;
				}
				_pending = null;
			}
		}

		private const byte EncodedUpgradeStart = 128;

		private const float DefaultExoticChance = 0.25f;

		private static readonly FieldInfo RolledSkinField = AccessTools.Field(typeof(SlotMachine), "_skinIndex");

		private static readonly Dictionary<MachineSlot, int> SlotUpgrades = new Dictionary<MachineSlot, int>();

		private static readonly Dictionary<MachineSlot, Image> UpgradeIcons = new Dictionary<MachineSlot, Image>();

		private static readonly Dictionary<string, Sprite> IconSprites = new Dictionary<string, Sprite>();

		private static ConfigEntry<float> _exoticChance = null;

		private static PendingReward? _pending;

		internal static void Initialize(ConfigFile config)
		{
			_exoticChance = config.Bind<float>("Gacha", "ExoticChance", 0.25f, "Chance from 0 to 1 that each eligible weapon-skin slot becomes an exotic-upgrade slot.");
		}

		internal static void Validate()
		{
			for (int i = 0; i < UpgradeState.UpgradeCount; i++)
			{
				if (!TryDecode(Encode(i), out var upgradeIndex) || upgradeIndex != i)
				{
					throw new InvalidOperationException("Exotic gacha encoding validation failed.");
				}
				byte[] array = ReadIconPng(UpgradeState.GetUpgrade(i));
				if (array.Length < 8 || array[0] != 137 || array[1] != 80 || array[2] != 78 || array[3] != 71)
				{
					throw new InvalidOperationException("Exotic gacha icon validation failed.");
				}
			}
			float num = 0.54999995f;
			float num2 = 0.54999995f;
			if (Mathf.Abs(num - num2) > 0.001f)
			{
				throw new InvalidOperationException("Exotic winner layout must remain square at 2:1.");
			}
		}

		private static void HideUpgradeIcon(MachineSlot slot)
		{
			if (UpgradeIcons.TryGetValue(slot, out Image value) && Object.op_Implicit((Object)(object)value))
			{
				((Component)value).gameObject.SetActive(false);
			}
		}

		private static void ShowUpgradeIcon(MachineSlot slot, Transform parent, UpgradeDefinition upgrade)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: Expected O, but got Unknown
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Expected O, but got Unknown
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			if (!UpgradeIcons.TryGetValue(slot, out Image value) || !Object.op_Implicit((Object)(object)value))
			{
				GameObject val = new GameObject("ExoticUpgradeIcon", new Type[3]
				{
					typeof(RectTransform),
					typeof(CanvasRenderer),
					typeof(Image)
				});
				val.layer = ((Component)parent).gameObject.layer;
				val.transform.SetParent(parent, false);
				RectTransform val2 = (RectTransform)val.transform;
				((Transform)val2).SetAsLastSibling();
				value = val.GetComponent<Image>();
				value.preserveAspect = true;
				((Graphic)value).raycastTarget = false;
				UpgradeIcons[slot] = value;
			}
			SetPreviewIconLayout(((Graphic)value).rectTransform);
			value.sprite = GetUpgradeIconSprite(upgrade);
			((Graphic)value).color = upgrade.BadgeColor;
			((Component)value).gameObject.SetActive(true);
			((Component)value).transform.SetAsLastSibling();
		}

		private static void SetPreviewIconLayout(RectTransform rect)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			SetIconLayout(rect, Vector2.one / 6f, Vector2.one * 5f / 6f, 0.005f);
		}

		private static void SetWinnerIconLayout(RectTransform rect)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			SetIconLayout(rect, new Vector2(0.3625f, 0.225f), new Vector2(0.6375f, 0.775f), 0f);
		}

		private static void SetIconLayout(RectTransform rect, Vector2 anchorMin, Vector2 anchorMax, float inset)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			rect.anchorMin = anchorMin;
			rect.anchorMax = anchorMax;
			rect.offsetMin = new Vector2(inset, inset);
			rect.offsetMax = new Vector2(0f - inset, 0f - inset);
		}

		private static Sprite GetUpgradeIconSprite(UpgradeDefinition upgrade)
		{
			//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_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			if (IconSprites.TryGetValue(upgrade.Id, out Sprite value) && Object.op_Implicit((Object)(object)value))
			{
				return value;
			}
			Texture2D val = new Texture2D(2, 2, (TextureFormat)4, false)
			{
				name = "ExoticUpgrades_" + upgrade.Id,
				filterMode = (FilterMode)1,
				wrapMode = (TextureWrapMode)1
			};
			if (!ImageConversion.LoadImage(val, ReadIconPng(upgrade), true))
			{
				Object.Destroy((Object)(object)val);
				throw new InvalidOperationException("Exotic icon for '" + upgrade.Id + "' is not a valid PNG.");
			}
			value = Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f), 100f);
			((Object)value).name = "ExoticUpgrades_" + upgrade.Id;
			IconSprites[upgrade.Id] = value;
			return value;
		}

		private static byte[] ReadIconPng(UpgradeDefinition upgrade)
		{
			return upgrade.GetIconPng();
		}

		private static byte Encode(int upgradeIndex)
		{
			return (byte)(128 + upgradeIndex);
		}

		private static bool TryDecode(byte encoded, out int upgradeIndex)
		{
			upgradeIndex = encoded - 128;
			if (encoded >= 128)
			{
				return upgradeIndex < UpgradeState.UpgradeCount;
			}
			return false;
		}
	}
	internal static class ExplosiveBullets
	{
		private const int DefaultDamage = 50;

		private const float DefaultRadius = 5f;

		private const float DefaultItemForce = 800f;

		private static readonly Dictionary<Projectile, ProjectileProfile> Tracked = new Dictionary<Projectile, ProjectileProfile>();

		private static readonly MethodInfo ExplodeEffects = AccessTools.Method(typeof(ExplosionManager), "ExplodeEffects", (Type[])null, (Type[])null);

		private static ConfigEntry<int> _damage = null;

		private static ConfigEntry<float> _radius = null;

		private static ConfigEntry<float> _itemForce = null;

		private static ExplosionInfo? _nativeExplosion;

		private static bool _warnedMissingEffects;

		internal static int Damage => Mathf.Max(0, _damage.Value);

		internal static float Radius => Mathf.Max(0f, _radius.Value);

		internal static float ItemForce => Mathf.Max(0f, _itemForce.Value);

		internal static void Initialize(ConfigFile config)
		{
			_damage = config.Bind<int>("Explosive Bullets", "Damage", 50, "Explosion damage dealt to each creature in range, in addition to the direct bullet hit.");
			_radius = config.Bind<float>("Explosive Bullets", "Radius", 5f, "Damage and item-force radius in metres.");
			_itemForce = config.Bind<float>("Explosive Bullets", "ItemForce", 800f, "Explosion force applied to nearby loose items.");
		}

		internal static void Track(Projectile projectile, ProjectileProfile profile)
		{
			if (profile.Effects.HasFlag(ProjectileEffects.Explosive))
			{
				Tracked[projectile] = profile;
			}
		}

		internal static void Clear()
		{
			Tracked.Clear();
		}

		internal static void Explode(Projectile projectile, Vector3 position)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			if (Tracked.Remove(projectile) && projectile.IsLocal)
			{
				ProjectileNetwork.RequestExplosion(projectile, position);
			}
		}

		internal static void RemoveExpired(ProjectileManager manager)
		{
			List<Projectile> list = null;
			foreach (Projectile key in Tracked.Keys)
			{
				if (!manager.GetType(key.TypeId).Projectiles.Contains(key))
				{
					(list ?? (list = new List<Projectile>())).Add(key);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (Projectile item in list)
			{
				Tracked.Remove(item);
			}
		}

		internal static void DamageOnServer(Player owner, Vector3 position, ProjectileProfile profile)
		{
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: 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_0090: Unknown result type (might be due to invalid IL or missing references)
			float num = profile.ExplosionRadius * profile.ExplosionRadius;
			HashSet<Creature> hashSet = new HashSet<Creature>();
			foreach (Item value in ItemManager.Items.Values)
			{
				Creature val = (Object.op_Implicit((Object)(object)value) ? value.Creature : null);
				if (Object.op_Implicit((Object)(object)val) && !val.IsDead && hashSet.Add(val))
				{
					Vector3 val2 = ((Item)val).Rig.worldCenterOfMass - position;
					if (((Vector3)(ref val2)).sqrMagnitude <= num)
					{
						val.ServerChangeHp(profile.ExplosionDamage);
						val.ObserverExplosionHit(owner, ((Vector3)(ref val2)).normalized, profile.ExplosionDamage);
					}
				}
			}
		}

		internal static void PlayExplosion(Vector3 position, float radius, float force)
		{
			//IL_0000: 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)
			PlayNativeEffects(position);
			PushItems(position, radius, force);
		}

		internal static void Validate()
		{
			if ((object)ExplodeEffects == null)
			{
				throw new InvalidOperationException("Explosive Bullets validation failed.");
			}
		}

		private static void PlayNativeEffects(Vector3 position)
		{
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			if (_nativeExplosion == null)
			{
				_nativeExplosion = (from explosive in Resources.FindObjectsOfTypeAll<Explosive>()
					orderby ((Object)explosive).name.IndexOf("dynamite", StringComparison.OrdinalIgnoreCase) >= 0 descending
					select ((Item)explosive).GetExplosionInfo()).FirstOrDefault((Func<ExplosionInfo, bool>)((ExplosionInfo info) => info != null));
			}
			if (_nativeExplosion != null)
			{
				ExplodeEffects.Invoke(null, new object[2] { position, _nativeExplosion });
			}
			else if (!_warnedMissingEffects)
			{
				_warnedMissingEffects = true;
				Plugin.Log.LogWarning((object)"Could not find a native dynamite explosion for Explosive Bullets effects.");
			}
		}

		private static void PushItems(Vector3 position, float radius, float force)
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_006c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: 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_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			radius = Mathf.Max(0f, radius);
			force = Mathf.Max(0f, force);
			HashSet<Item> hashSet = new HashSet<Item>();
			Collider[] array = Physics.OverlapSphere(position, radius, LayerMask.op_Implicit(GameInfo.AffectedByExplosionLayer));
			foreach (Collider val in array)
			{
				Item val2 = ItemManager.Get(val);
				if (Object.op_Implicit((Object)(object)val2) && !Object.op_Implicit((Object)(object)val2.Holder) && hashSet.Add(val2))
				{
					val2.RigidbodySync.StartSimulateLocal(default(Vector3), default(Quaternion));
					val2.Rig.AddExplosionForce(force, position, radius);
				}
			}
		}
	}
	internal static class HomingBullets
	{
		private readonly record struct HomingProjectile(Projectile Projectile, ProjectileProfile Profile, int TargetObjectId);

		private const float AggressivePrototypeTurnDegreesPerSecond = 720f;

		internal const float DefaultTurnDegreesPerSecond = 180f;

		private const float DefaultRange = 60f;

		private const float DefaultMinimumForwardDot = -0.25f;

		private static readonly Dictionary<ProjectileKey, HomingProjectile> Tracked = new Dictionary<ProjectileKey, HomingProjectile>();

		private static readonly Dictionary<ProjectileKey, int> Targets = new Dictionary<ProjectileKey, int>();

		private static ConfigEntry<float> _range = null;

		private static ConfigEntry<float> _minimumForwardDot = null;

		private static ConfigEntry<float> _turnDegreesPerSecond = null;

		internal static float Range => Mathf.Max(0f, _range.Value);

		internal static float MinimumForwardDot => Mathf.Clamp(_minimumForwardDot.Value, -1f, 1f);

		internal static float TurnDegreesPerSecond => Mathf.Max(0f, _turnDegreesPerSecond.Value);

		internal static void Initialize(ConfigFile config)
		{
			_range = config.Bind<float>("Homing Bullets", "TargetRange", 60f, "Maximum target acquisition distance in metres.");
			_minimumForwardDot = config.Bind<float>("Homing Bullets", "MinimumForwardDot", -0.25f, "Target cone: 1 is straight ahead, 0 includes side targets, and -1 includes targets behind.");
			_turnDegreesPerSecond = config.Bind<float>("Homing Bullets", "TurnDegreesPerSecond", 180f, "Maximum projectile steering speed in degrees per second.");
			if (Mathf.Approximately(_turnDegreesPerSecond.Value, 720f))
			{
				_turnDegreesPerSecond.Value = 180f;
			}
		}

		internal static void Track(Projectile projectile, ProjectileKey key, ProjectileProfile profile)
		{
			if (profile.Effects.HasFlag(ProjectileEffects.Homing))
			{
				Tracked[key] = new HomingProjectile(projectile, profile, Targets.TryGetValue(key, out var value) ? value : (-1));
			}
		}

		internal static void SetTarget(ProjectileKey key, int targetObjectId)
		{
			Targets[key] = targetObjectId;
			if (Tracked.TryGetValue(key, out var value))
			{
				Tracked[key] = value with
				{
					TargetObjectId = targetObjectId
				};
			}
		}

		internal static void Clear()
		{
			Tracked.Clear();
			Targets.Clear();
		}

		internal static void Steer(ProjectileManager manager)
		{
			if (Tracked.Count == 0)
			{
				return;
			}
			List<ProjectileKey> list = null;
			List<(Projectile, Creature)> list2 = null;
			foreach (var (item, tracked) in Tracked)
			{
				if (!UpdateProjectile(manager, tracked, out Creature acquired))
				{
					(list ?? (list = new List<ProjectileKey>())).Add(item);
				}
				else if (Object.op_Implicit((Object)(object)acquired))
				{
					(list2 ?? (list2 = new List<(Projectile, Creature)>())).Add((tracked.Projectile, acquired));
				}
			}
			ShareTargets(list2);
			RemoveExpired(list);
		}

		private static bool UpdateProjectile(ProjectileManager manager, HomingProjectile tracked, out Creature? acquired)
		{
			//IL_0062: Unknown result type (might be due to invalid IL or missing references)
			//IL_0068: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0072: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0096: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Unknown result type (might be due to invalid IL or missing references)
			Projectile projectile = tracked.Projectile;
			acquired = null;
			if (!manager.GetType(projectile.TypeId).Projectiles.Contains(projectile))
			{
				return false;
			}
			Creature val = CreatureFor(tracked.TargetObjectId);
			if (!Object.op_Implicit((Object)(object)val) && projectile.IsLocal)
			{
				val = (acquired = NearestTarget(projectile, tracked.Profile));
			}
			if (Object.op_Implicit((Object)(object)val))
			{
				Vector3 desired = ((Item)val).Rig.worldCenterOfMass - projectile.Position;
				projectile.Velocity = TurnTowards(projectile.Velocity, desired, tracked.Profile.HomingTurnSpeed * (MathF.PI / 180f) * Time.fixedDeltaTime);
			}
			return true;
		}

		private static void ShareTargets(List<(Projectile Projectile, Creature Target)>? acquired)
		{
			if (acquired == null)
			{
				return;
			}
			foreach (var (projectile, target) in acquired)
			{
				ProjectileNetwork.ShareTarget(projectile, target);
			}
		}

		private static void RemoveExpired(List<ProjectileKey>? expired)
		{
			if (expired == null)
			{
				return;
			}
			foreach (ProjectileKey item in expired)
			{
				Tracked.Remove(item);
				Targets.Remove(item);
			}
		}

		internal static void Validate()
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//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)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = TurnTowards(Vector3.forward * 10f, Vector3.right, 0.25f);
			if (!Mathf.Approximately(((Vector3)(ref val)).magnitude, 10f) || val.x <= 0f || val.z <= 0f)
			{
				throw new InvalidOperationException("Homing steering validation failed.");
			}
		}

		private static Creature? NearestTarget(Projectile projectile, ProjectileProfile profile)
		{
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0091: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: 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_00b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			Creature result = null;
			float homingRange = profile.HomingRange;
			float num = homingRange * homingRange;
			float homingMinimumDot = profile.HomingMinimumDot;
			Vector3 normalized = ((Vector3)(ref projectile.Velocity)).normalized;
			HashSet<Creature> hashSet = new HashSet<Creature>();
			foreach (Item value in ItemManager.Items.Values)
			{
				if (value == null || !Object.op_Implicit((Object)(object)value))
				{
					continue;
				}
				Creature creature = value.Creature;
				if (Object.op_Implicit((Object)(object)creature) && !creature.IsDead && !Object.op_Implicit((Object)(object)value.Holder) && hashSet.Add(creature))
				{
					Vector3 val = ((Item)creature).Rig.worldCenterOfMass - projectile.Position;
					float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
					if (sqrMagnitude < num && Vector3.Dot(normalized, ((Vector3)(ref val)).normalized) >= homingMinimumDot)
					{
						result = creature;
						num = sqrMagnitude;
					}
				}
			}
			return result;
		}

		private static Creature? CreatureFor(int objectId)
		{
			if (objectId < 0)
			{
				return null;
			}
			foreach (Item value in ItemManager.Items.Values)
			{
				if (Object.op_Implicit((Object)(object)value) && ((NetworkBehaviour)value).ObjectId == objectId && Object.op_Implicit((Object)(object)value.Creature) && !value.Creature.IsDead)
				{
					return value.Creature;
				}
			}
			return null;
		}

		private static Vector3 TurnTowards(Vector3 velocity, Vector3 desired, float maxRadians)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_001e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			float magnitude = ((Vector3)(ref velocity)).magnitude;
			if (!(magnitude <= Mathf.Epsilon) && !(((Vector3)(ref desired)).sqrMagnitude <= Mathf.Epsilon))
			{
				return Vector3.RotateTowards(velocity, ((Vector3)(ref desired)).normalized * magnitude, maxRadians, 0f);
			}
			return velocity;
		}
	}
	internal static class MeleeEffects
	{
		private sealed class Pursuit
		{
			internal Player Player { get; }

			internal Transform Transform { get; set; }

			internal Vector3 WorldPoint { get; set; }

			internal bool Attacked { get; set; }

			internal Pursuit(Player player, Transform transform, Vector3 worldPoint)
			{
				//IL_0015: Unknown result type (might be due to invalid IL or missing references)
				Player = player;
				Transform = transform;
				WorldPoint = worldPoint;
			}
		}

		private readonly record struct TargetCandidate(Transform Transform, Vector3 WorldPoint, float Distance);

		private readonly record struct SecondAttack(Melee Melee, Transform Target, Vector3 LocalHitPoint, int Side, float ExecuteAt);

		internal readonly record struct MovementState(Vector2 Input, float WalkSpeed, float SprintSpeed, float CurrentSpeed, float TargetSpeed, float TransitionStart, float TransitionPercent);

		private static readonly MethodInfo CanAttackMethod = AccessTools.Method(typeof(Melee), "CanAttack", (Type[])null, (Type[])null);

		private static readonly MethodInfo StartAttackingMethod = AccessTools.Method(typeof(Melee), "StartAttacking", (Type[])null, (Type[])null);

		private static readonly FieldRef<Melee, float> RangeRef = AccessTools.FieldRefAccess<Melee, float>("_range");

		private static readonly HashSet<Melee> PrimedOneTwo = new HashSet<Melee>();

		private static readonly HashSet<Melee> SecondStrikes = new HashSet<Melee>();

		private static readonly Dictionary<Melee, Pursuit> Pursuits = new Dictionary<Melee, Pursuit>();

		private static readonly List<SecondAttack> PendingSeconds = new List<SecondAttack>();

		private static ConfigEntry<float> _cleaveDamage = null;

		private static ConfigEntry<float> _cleaveRadius = null;

		private static ConfigEntry<int> _cleaveTargets = null;

		private static ConfigEntry<float> _heavyForce = null;

		private static ConfigEntry<float> _oneTwoDamage = null;

		private static ConfigEntry<float> _oneTwoDelay = null;

		private static ConfigEntry<float> _pursuitRange = null;

		private static ConfigEntry<float> _pursuitStopDistance = null;

		private static ConfigEntry<float> _pursuitSpeed = null;

		internal static float CleaveDamageMultiplier => Mathf.Clamp(_cleaveDamage.Value, 0f, 2f);

		internal static float CleaveRadius => Mathf.Clamp(_cleaveRadius.Value, 0f, 8f);

		internal static int CleaveTargetCount => Mathf.Clamp(_cleaveTargets.Value, 0, 8);

		internal static float HeavyForceMultiplier => Mathf.Clamp(_heavyForce.Value, 1f, 10f);

		internal static float PursuitSpeedMultiplier => Mathf.Clamp(_pursuitSpeed.Value, 1f, 100f);

		internal static float NativeRange(Melee melee)
		{
			return RangeRef.Invoke(melee);
		}

		internal static void Initialize(ConfigFile config)
		{
			_cleaveDamage = config.Bind<float>("Melee.Cleave", "DamageMultiplier", 0.65f, "Damage dealt to each extra target relative to the normal strike.");
			_cleaveRadius = config.Bind<float>("Melee.Cleave", "Radius", 2.5f, "Radius in metres around the original impact.");
			_cleaveTargets = config.Bind<int>("Melee.Cleave", "ExtraTargets", 2, "Maximum extra targets hit by one strike.");
			_heavyForce = config.Bind<float>("Melee.HeavyHitter", "KnockbackMultiplier", 3f, "Knuckles knockback multiplier. Damage is unchanged.");
			_oneTwoDamage = config.Bind<float>("Melee.OneTwo", "SecondHitDamageMultiplier", 0.6f, "Damage multiplier for the second punch.");
			_oneTwoDelay = config.Bind<float>("Melee.OneTwo", "SecondPunchDelay", 0.08f, "Minimum delay before the follow-up punch.");
			_pursuitRange = config.Bind<float>("Melee.CoDLike", "TargetRange", 8f, "Maximum distance for acquiring a target inside the current camera FOV.");
			_pursuitStopDistance = config.Bind<float>("Melee.CoDLike", "AttackDistanceMultiplier", 0.8f, "Fraction of the Knife's native range at which pursuit attacks.");
			_pursuitSpeed = config.Bind<float>("Melee.CoDLike", "PursuitSpeedMultiplier", 20f, "Native movement speed multiplier while pursuing a target.");
		}

		internal static void Update()
		{
			UpdatePursuits();
			UpdateSecondAttacks();
		}

		internal static bool TryBeginPursuit(Melee melee)
		{
			//IL_0071: Unknown result type (might be due to invalid IL or missing references)
			if (!LocalHeld(melee) || !UpgradeState.IsKnife(melee) || !UpgradeState.IsEnabled((Item)(object)melee, UpgradeState.CodLike))
			{
				return false;
			}
			Player holder = ((Item)melee).Holder;
			TargetCandidate? targetCandidate = FindTarget(holder, Mathf.Clamp(_pursuitRange.Value, 1f, 40f));
			if (!targetCandidate.HasValue)
			{
				return false;
			}
			Pursuits[melee] = new Pursuit(holder, targetCandidate.Value.Transform, targetCandidate.Value.WorldPoint);
			return true;
		}

		internal static void EndPursuit(Melee melee)
		{
			Pursuits.Remove(melee);
		}

		private static bool ApplyPursuitInput(PlayerMovement movement, ref Vector2 input)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			//IL_0067: Unknown result type (might be due to invalid IL or missing references)
			//IL_006d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: 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_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			Pursuit pursuit = FindPursuit(movement);
			if (pursuit == null)
			{
				return false;
			}
			if (pursuit.Attacked)
			{
				input = Vector2.zero;
				return false;
			}
			Vector3 val = pursuit.WorldPoint - pursuit.Player.Transform.position;
			val.y = 0f;
			Vector3 val2 = Quaternion.Inverse(pursuit.Player.CurPlayerRot) * ((Vector3)(ref val)).normalized;
			input = new Vector2(val2.x, val2.z);
			return true;
		}

		internal static MovementState ApplyPursuitMovement(PlayerMovement movement, ref Vector2 input, ref float walkSpeed, ref float sprintSpeed, ref float currentSpeed, ref float targetSpeed, ref float transitionStart, ref float transitionPercent)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			MovementState result = new MovementState(input, walkSpeed, sprintSpeed, currentSpeed, targetSpeed, transitionStart, transitionPercent);
			if (!ApplyPursuitInput(movement, ref input))
			{
				return result;
			}
			float num = Mathf.Max(walkSpeed, sprintSpeed) * PursuitSpeedMultiplier;
			walkSpeed = (sprintSpeed = (currentSpeed = (targetSpeed = (transitionStart = num))));
			transitionPercent = 1f;
			return result;
		}

		internal static void RestoreMovement(ref Vector2 input, ref float walkSpeed, ref float sprintSpeed, ref float currentSpeed, ref float targetSpeed, ref float transitionStart, ref float transitionPercent, MovementState state)
		{
			//IL_0003: Unknown result type (might be due to invalid IL or missing references)
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			input = state.Input;
			walkSpeed = state.WalkSpeed;
			sprintSpeed = state.SprintSpeed;
			currentSpeed = state.CurrentSpeed;
			targetSpeed = state.TargetSpeed;
			transitionStart = state.TransitionStart;
			transitionPercent = state.TransitionPercent;
		}

		internal static void PrimeOneTwo(Melee melee)
		{
			if (LocalHeld(melee) && UpgradeState.IsKnuckles(melee) && UpgradeState.IsEnabled((Item)(object)melee, UpgradeState.OneTwo))
			{
				PrimedOneTwo.Add(melee);
			}
		}

		internal static void OnHit(Melee melee, Transform target, Vector3 localHitPoint, int side)
		{
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: 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)
			if (!LocalHeld(melee) || !Object.op_Implicit((Object)(object)target))
			{
				SecondStrikes.Remove(melee);
				return;
			}
			bool flag = SecondStrikes.Remove(melee);
			if (!flag)
			{
				MeleeNetwork.ReportHit(melee, target, target.TransformPoint(localHitPoint));
			}
			if (!flag && PrimedOneTwo.Remove(melee))
			{
				PendingSeconds.Add(new SecondAttack(melee, target, localHitPoint, 1 - side, Time.time + Mathf.Clamp(_oneTwoDelay.Value, 0f, 1f)));
			}
		}

		internal static void ModifyHit(Player attacker, ref int damage, ref Vector3 force)
		{
			//IL_003c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)attacker))
			{
				return;
			}
			Item heldItem = attacker.Holding.HeldItem;
			Melee val = (Melee)(object)((heldItem is Melee) ? heldItem : null);
			if (val != null && LocalHeld(val))
			{
				if (UpgradeState.IsKnuckles(val) && UpgradeState.IsEnabled((Item)(object)val, UpgradeState.HeavyHitter))
				{
					force *= HeavyForceMultiplier;
				}
				if (SecondStrikes.Contains(val))
				{
					damage = Mathf.Max(1, Mathf.RoundToInt((float)damage * Mathf.Clamp(_oneTwoDamage.Value, 0f, 2f)));
				}
			}
		}

		internal static void Reset()
		{
			PrimedOneTwo.Clear();
			SecondStrikes.Clear();
			Pursuits.Clear();
			PendingSeconds.Clear();
		}

		internal static void Validate()
		{
			if (RangeRef == null || (object)CanAttackMethod == null || (object)StartAttackingMethod == null)
			{
				throw new InvalidOperationException("Required native melee methods were not found.");
			}
		}

		private static void UpdatePursuits()
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_005e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			List<Melee> list = null;
			foreach (var (val2, pursuit2) in Pursuits)
			{
				if (!LocalHeld(val2) || !RefreshTarget(pursuit2))
				{
					(list ?? (list = new List<Melee>())).Add(val2);
					continue;
				}
				Vector3 val3 = (pursuit2.WorldPoint = TargetPoint(pursuit2.Transform));
				float num = RangeRef.Invoke(val2) * Mathf.Clamp(_pursuitStopDistance.Value, 0.25f, 1f);
				if (!pursuit2.Attacked && Vector3.Distance(pursuit2.Player.Transform.position, val3) <= num)
				{
					pursuit2.Attacked = StartAttack(val2, pursuit2.Transform, val3);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (Melee item in list)
			{
				Pursuits.Remove(item);
			}
		}

		private static bool RefreshTarget(Pursuit pursuit)
		{
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)pursuit.Transform) && TargetIsAlive(pursuit.Transform) && HasLineOfSight(pursuit.Player, TargetPoint(pursuit.Transform)))
			{
				return true;
			}
			TargetCandidate? targetCandidate = FindTarget(pursuit.Player, Mathf.Clamp(_pursuitRange.Value, 1f, 40f));
			if (!targetCandidate.HasValue)
			{
				return false;
			}
			pursuit.Transform = targetCandidate.Value.Transform;
			pursuit.WorldPoint = targetCandidate.Value.WorldPoint;
			return true;
		}

		private static void UpdateSecondAttacks()
		{
			//IL_00db: Unknown result type (might be due to invalid IL or missing references)
			for (int num = PendingSeconds.Count - 1; num >= 0; num--)
			{
				SecondAttack secondAttack = PendingSeconds[num];
				if (!Object.op_Implicit((Object)(object)secondAttack.Melee) || !Object.op_Implicit((Object)(object)secondAttack.Target) || !LocalHeld(secondAttack.Melee))
				{
					PendingSeconds.RemoveAt(num);
				}
				else if (!(Time.time < secondAttack.ExecuteAt) && (bool)CanAttackMethod.Invoke(secondAttack.Melee, Array.Empty<object>()))
				{
					PendingSeconds.RemoveAt(num);
					SecondStrikes.Add(secondAttack.Melee);
					StartAttackingMethod.Invoke(secondAttack.Melee, new object[4] { secondAttack.Target, secondAttack.Side, true, secondAttack.LocalHitPoint });
				}
			}
		}

		private static bool StartAttack(Melee melee, Transform target, Vector3 worldPoint)
		{
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			if (!(bool)CanAttackMethod.Invoke(melee, Array.Empty<object>()))
			{
				return false;
			}
			StartAttackingMethod.Invoke(melee, new object[4]
			{
				target,
				0,
				true,
				target.InverseTransformPoint(worldPoint)
			});
			return true;
		}

		private static Pursuit? FindPursuit(PlayerMovement movement)
		{
			foreach (Pursuit value in Pursuits.Values)
			{
				if ((Object)(object)value.Player.Movement == (Object)(object)movement)
				{
					return value;
				}
			}
			return null;
		}

		private static TargetCandidate? FindTarget(Player player, float range)
		{
			//IL_003f: 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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			TargetCandidate? result = null;
			float minimumDot = Mathf.Cos((Object.op_Implicit((Object)(object)player.CurCam) ? player.CurCam.fieldOfView : 70f) * 0.5f * (MathF.PI / 180f));
			Collider[] array = Physics.OverlapSphere(player.CamObject.position, range, LayerMask.op_Implicit(GameInfo.ItemPartLayer) | LayerMask.op_Implicit(GameInfo.PlayerLayers));
			foreach (Collider val in array)
			{
				TargetCandidate? targetCandidate = CandidateFor(player, ((Component)val).transform);
				if (targetCandidate.HasValue && InsideFov(player, targetCandidate.Value.WorldPoint, minimumDot) && HasLineOfSight(player, targetCandidate.Value.WorldPoint) && (!result.HasValue || targetCandidate.Value.Distance < result.Value.Distance))
				{
					result = targetCandidate;
				}
			}
			return result;
		}

		private static TargetCandidate? CandidateFor(Player attacker, Transform transform)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			//IL_002f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: 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_0099: Unknown result type (might be due to invalid IL or missing references)
			Item val = ItemManager.Get(transform);
			if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.Creature) && !val.Creature.IsDead)
			{
				Vector3 val2 = TargetPoint(transform);
				return new TargetCandidate(transform, val2, Vector3.Distance(attacker.CamObject.position, val2));
			}
			Player playerFromBodyPart = PlayerManager.GetPlayerFromBodyPart(transform);
			if (!Object.op_Implicit((Object)(object)playerFromBodyPart) || (Object)(object)playerFromBodyPart == (Object)(object)attacker || playerFromBodyPart.Dying.IsDead || !ServerSettings.UseFriendlyFire)
			{
				return null;
			}
			Vector3 val3 = TargetPoint(transform);
			return new TargetCandidate(transform, val3, Vector3.Distance(attacker.CamObject.position, val3));
		}

		private static bool InsideFov(Player player, Vector3 point, float minimumDot)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: 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_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			Vector3 forward = player.CamObject.forward;
			Vector3 val = point - player.CamObject.position;
			return Vector3.Dot(forward, ((Vector3)(ref val)).normalized) >= minimumDot;
		}

		private static bool HasLineOfSight(Player player, Vector3 point)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = point - player.CamObject.position;
			return !Physics.Raycast(player.CamObject.position, ((Vector3)(ref val)).normalized, ((Vector3)(ref val)).magnitude, LayerMask.op_Implicit(GameInfo.LevelLayer));
		}

		private static Vector3 TargetPoint(Transform target)
		{
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			Collider component = ((Component)target).GetComponent<Collider>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				return target.position;
			}
			Bounds bounds = component.bounds;
			return ((Bounds)(ref bounds)).center;
		}

		private static bool TargetIsAlive(Transform target)
		{
			Item val = ItemManager.Get(target);
			if (Object.op_Implicit((Object)(object)val) && Object.op_Implicit((Object)(object)val.Creature))
			{
				return !val.Creature.IsDead;
			}
			Player playerFromBodyPart = PlayerManager.GetPlayerFromBodyPart(target);
			if (Object.op_Implicit((Object)(object)playerFromBodyPart))
			{
				return !playerFromBodyPart.Dying.IsDead;
			}
			return false;
		}

		private static bool LocalHeld(Melee melee)
		{
			if (Object.op_Implicit((Object)(object)melee) && Object.op_Implicit((Object)(object)((Item)melee).Holder))
			{
				return ((NetworkBehaviour)((Item)melee).Holder).Owner.IsLocalClient;
			}
			return false;
		}
	}
	[HarmonyPatch(typeof(Melee), "PrimaryInput")]
	internal static class MeleePrimaryPatch
	{
		private static bool Prefix(Melee __instance, CallbackContext context)
		{
			return !MeleeEffects.TryBeginPursuit(__instance);
		}
	}
	[HarmonyPatch(typeof(Melee), "FindAttackTarget")]
	internal static class MeleeFindTargetPatch
	{
		private static void Prefix(Melee __instance, bool extraCheckMidAttack)
		{
			if (!extraCheckMidAttack)
			{
				MeleeEffects.PrimeOneTwo(__instance);
			}
		}
	}
	[HarmonyPatch(typeof(Melee), "PrimaryInputCancel")]
	internal static class MeleePrimaryCancelPatch
	{
		private static void Prefix(Melee __instance)
		{
			MeleeEffects.EndPursuit(__instance);
		}
	}
	[HarmonyPatch(typeof(Melee), "HitTarget")]
	internal static class MeleeHitPatch
	{
		private static void Postfix(Melee __instance, int side, Transform[] ____curTarget, Vector3[] ____targetHitPoint)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			MeleeEffects.OnHit(__instance, ____curTarget[side], ____targetHitPoint[side], side);
		}
	}
	[HarmonyPatch(typeof(Item), "LocalHit")]
	internal static class MeleeItemDamagePatch
	{
		private static void Prefix(Player player, ref int damage, ref Vector3 force)
		{
			MeleeEffects.ModifyHit(player, ref damage, ref force);
		}
	}
	[HarmonyPatch(typeof(PlayerVitals), "LocalHit")]
	internal static class MeleePlayerDamagePatch
	{
		private static void Prefix(Player playerWhoHit, ref int damage, ref Vector3 force)
		{
			MeleeEffects.ModifyHit(playerWhoHit, ref damage, ref force);
		}
	}
	[HarmonyPatch(typeof(PlayerMovement), "Move")]
	internal static class MeleeMovementPatch
	{
		private static void Prefix(PlayerMovement __instance, ref Vector2 ____moveInput, ref float ____walkSpeed, ref float ____sprintSpeed, ref float ____curMoveSpeed, ref float ____targetMoveSpeed, ref float ____moveSpeedTransitionStart, ref float ____moveSpeedTransitionPercent, out MeleeEffects.MovementState __state)
		{
			__state = MeleeEffects.ApplyPursuitMovement(__instance, ref ____moveInput, ref ____walkSpeed, ref ____sprintSpeed, ref ____curMoveSpeed, ref ____targetMoveSpeed, ref ____moveSpeedTransitionStart, ref ____moveSpeedTransitionPercent);
		}

		private static void Postfix(ref Vector2 ____moveInput, ref float ____walkSpeed, ref float ____sprintSpeed, ref float ____curMoveSpeed, ref float ____targetMoveSpeed, ref float ____moveSpeedTransitionStart, ref float ____moveSpeedTransitionPercent, MeleeEffects.MovementState __state)
		{
			MeleeEffects.RestoreMovement(ref ____moveInput, ref ____walkSpeed, ref ____sprintSpeed, ref ____curMoveSpeed, ref ____targetMoveSpeed, ref ____moveSpeedTransitionStart, ref ____moveSpeedTransitionPercent, __state);
		}
	}
	[Flags]
	internal enum MeleeHitEffects : byte
	{
		None = 0,
		Cleave = 1,
		Bleed = 2
	}
	internal readonly record struct MeleeHitRequest(int AttackerObjectId, int MeleeObjectId, int TargetObjectId, bool TargetIsPlayer, Vector3 Position, MeleeHitEffects Effects) : IBroadcast;
	internal readonly record struct CleaveFeedbackBroadcast(Vector3 Position, int Damage, bool Killed) : IBroadcast;
	internal readonly record struct BleedParticleBroadcast(Vector3 Position) : IBroadcast;
	internal static class MeleeNetwork
	{
		private const string StatusId = "ArceDev.ExoticUpgrades.Bleeding";

		private static readonly Action<NetworkConnection, MeleeHitRequest, Channel> ServerHitHandler = OnServerHit;

		private static readonly Action<CleaveFeedbackBroadcast, Channel> ClientCleaveHandler = OnClientCleave;

		private static readonly Action<BleedParticleBroadcast, Channel> ClientBleedParticleHandler = OnClientBleedParticle;

		private static ConfigEntry<int> _bleedDamage = null;

		private static ConfigEntry<float> _bleedDuration = null;

		private static ConfigEntry<float> _bleedInterval = null;

		private static bool _clientRegistered;

		private static bool _serverRegistered;

		internal static void Initialize(ConfigFile config)
		{
			//IL_0148: Unknown result type (might be due to invalid IL or missing references)
			//IL_014d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_0193: Expected O, but got Unknown
			_bleedDamage = config.Bind<int>("Melee.Bleed", "DamagePerTick", 2, "Damage dealt by each bleeding tick.");
			_bleedDuration = config.Bind<float>("Melee.Bleed", "Duration", 6f, "Bleeding duration in seconds. Repeated hits refresh it.");
			_bleedInterval = config.Bind<float>("Melee.Bleed", "TickInterval", 1f, "Seconds between bleeding damage ticks.");
			GenericWriter<MeleeHitRequest>.SetWrite((Action<Writer, MeleeHitRequest>)WriteHit);
			GenericReader<MeleeHitRequest>.SetRead((Func<Reader, MeleeHitRequest>)ReadHit);
			GenericWriter<CleaveFeedbackBroadcast>.SetWrite((Action<Writer, CleaveFeedbackBroadcast>)delegate(Writer writer, CleaveFeedbackBroadcast message)
			{
				//IL_0003: Unknown result type (might be due to invalid IL or missing references)
				writer.WriteVector3(message.Position);
				writer.WriteInt32(message.Damage);
				writer.WriteBoolean(message.Killed);
			});
			GenericReader<CleaveFeedbackBroadcast>.SetRead((Func<Reader, CleaveFeedbackBroadcast>)((Reader reader) => new CleaveFeedbackBroadcast(reader.ReadVector3(), reader.ReadInt32(), reader.ReadBoolean())));
			GenericWriter<BleedParticleBroadcast>.SetWrite((Action<Writer, BleedParticleBroadcast>)delegate(Writer writer, BleedParticleBroadcast message)
			{
				//IL_0003: Unknown result type (might be due to invalid IL or missing references)
				writer.WriteVector3(message.Position);
			});
			GenericReader<BleedParticleBroadcast>.SetRead((Func<Reader, BleedParticleBroadcast>)((Reader reader) => new BleedParticleBroadcast(reader.ReadVector3())));
			StatusApi.Register(new StatusDefinition("ArceDev.ExoticUpgrades.Bleeding", "Bleeding", LoadPng("bleeding.png"), Color32.op_Implicit(new Color32((byte)126, (byte)20, (byte)46, byte.MaxValue)), Mathf.Clamp(_bleedInterval.Value, 0.1f, 10f), (Action<StatusContext>)null, (Action<StatusContext>)TickBleeding, (Action<StatusContext, StatusRemovalReason>)null, true));
		}

		internal static void RegisterClient()
		{
			if (!_clientRegistered && Object.op_Implicit((Object)(object)InstanceFinder.ClientManager))
			{
				InstanceFinder.ClientManager.RegisterBroadcast<CleaveFeedbackBroadcast>(ClientCleaveHandler);
				InstanceFinder.ClientManager.RegisterBroadcast<BleedParticleBroadcast>(ClientBleedParticleHandler);
				_clientRegistered = true;
			}
		}

		internal static void RegisterServer()
		{
			if (!_serverRegistered && Object.op_Implicit((Object)(object)InstanceFinder.ServerManager))
			{
				InstanceFinder.ServerManager.RegisterBroadcast<MeleeHitRequest>(ServerHitHandler, true);
				_serverRegistered = true;
			}
		}

		internal static void ReportHit(Melee melee, Transform target, Vector3 position)
		{
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)melee) && Object.op_Implicit((Object)(object)((Item)melee).Holder) && ((NetworkBehaviour)((Item)melee).Holder).Owner.IsLocalClient && Object.op_Implicit((Object)(object)InstanceFinder.ClientManager) && TryResolveTarget(target, out var objectId, out var isPlayer))
			{
				MeleeHitEffects meleeHitEffects = CaptureEffects(melee);
				if (meleeHitEffects != MeleeHitEffects.None)
				{
					InstanceFinder.ClientManager.Broadcast<MeleeHitRequest>(new MeleeHitRequest(((NetworkBehaviour)((Item)melee).Holder).ObjectId, ((NetworkBehaviour)melee).ObjectId, objectId, isPlayer, position, meleeHitEffects), (Channel)0);
				}
			}
		}

		internal static void ResetSession()
		{
			if (_clientRegistered && Object.op_Implicit((Object)(object)InstanceFinder.ClientManager))
			{
				InstanceFinder.ClientManager.UnregisterBroadcast<CleaveFeedbackBroadcast>(ClientCleaveHandler);
				InstanceFinder.ClientManager.UnregisterBroadcast<BleedParticleBroadcast>(ClientBleedParticleHandler);
			}
			if (_serverRegistered && Object.op_Implicit((Object)(object)InstanceFinder.ServerManager))
			{
				InstanceFinder.ServerManager.UnregisterBroadcast<MeleeHitRequest>(ServerHitHandler);
			}
			_clientRegistered = false;
			_serverRegistered = false;
		}

		private static void OnServerHit(NetworkConnection connection, MeleeHitRequest message, Channel channel)
		{
			Player val = PlayerFor(message.AttackerObjectId);
			Melee val2 = MeleeFor(message.MeleeObjectId);
			if (ValidHitRequest(connection, val, val2, message))
			{
				Player attacker = val;
				Melee melee = val2;
				MeleeHitEffects meleeHitEffects = (UpgradeState.IsKnuckles(melee) ? MeleeHitEffects.Cleave : (MeleeHitEffects.Cleave | MeleeHitEffects.Bleed));
				MeleeHitEffects meleeHitEffects2 = message.Effects & meleeHitEffects;
				if (meleeHitEffects2.HasFlag(MeleeHitEffects.Cleave))
				{
					ApplyCleave(attacker, melee, message);
				}
				if (meleeHitEffects2.HasFlag(MeleeHitEffects.Bleed) && message.TargetIsPlayer)
				{
					ApplyBleed(attacker, message.TargetObjectId);
				}
			}
		}

		private static void ApplyCleave(Player attacker, Melee melee, MeleeHitRequest message)
		{
			//IL_005a: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_014c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_0156: Unknown result type (might be due to invalid IL or missing references)
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_016d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0184: Unknown result type (might be due to invalid IL or missing references)
			//IL_0189: Unknown result type (might be due to invalid IL or missing references)
			//IL_019b: Unknown result type (might be due to invalid IL or missing references)
			int num = MeleeEffects.CleaveTargetCount;
			if (num == 0)
			{
				return;
			}
			float cleaveRadius = MeleeEffects.CleaveRadius;
			int num2 = Mathf.Max(1, Mathf.RoundToInt((float)melee.GetCurSharpness().Damage * MeleeEffects.CleaveDamageMultiplier));
			HashSet<int> seen = new HashSet<int> { message.TargetObjectId };
			List<Creature> list = LivingCreaturesNear(message.Position, cleaveRadius, seen);
			list.Sort((Creature left, Creature right) => Distance(left).CompareTo(Distance(right)));
			foreach (Creature item in list)
			{
				item.ServerChangeHp(num2);
				item.ObserverHit(attacker, ((Component)item).transform.position, Direction(attacker, ((Component)item).transform.position), num2);
				SendCleaveFeedback(attacker, ((Component)item).transform.position, num2, item.IsDead);
				if (--num == 0)
				{
					return;
				}
			}
			if (!ServerSettings.UseFriendlyFire)
			{
				return;
			}
			foreach (Player item2 in PlayersNear(attacker, message.Position, cleaveRadius, seen))
			{
				int num3 = Mathf.Max(1, Mathf.RoundToInt((float)num2 * 0.25f));
				Vector3 val = Direction(attacker, item2.Transform.position);
				item2.Vitals.TakeDamage(num3, item2.Transform.position, val, true);
				item2.Vitals.ObserverHit(attacker, item2.Transform.position, val, num3, (DamageType)1);
				SendCleaveFeedback(attacker, item2.Transform.position, num3, item2.Dying.IsDead);
				if (--num == 0)
				{
					break;
				}
			}
			float Distance(Creature creature)
			{
				//IL_0006: Unknown result type (might be due to invalid IL or missing references)
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_0016: Unknown result type (might be due to invalid IL or missing references)
				return Vector3.SqrMagnitude(((Component)creature).transform.position - message.Position);
			}
		}

		private static void ApplyBleed(Player attacker, int targetObjectId)
		{
			Player val = PlayerFor(targetObjectId);
			if (Object.op_Implicit((Object)(object)val) && !((Object)(object)val == (Object)(object)attacker) && !val.Dying.IsDead && ServerSettings.UseFriendlyFire)
			{
				float num = Mathf.Clamp(_bleedDuration.Value, 0.1f, 60f);
				StatusApi.Apply(val, "ArceDev.ExoticUpgrades.Bleeding", num, attacker);
			}
		}

		private static List<Creature> LivingCreaturesNear(Vector3 position, float radius, HashSet<int> seen)
		{
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			List<Creature> list = new List<Creature>();
			foreach (Item value in ItemManager.Items.Values)
			{
				if (Object.op_Implicit((Object)(object)value) && Object.op_Implicit((Object)(object)value.Creature) && !value.Creature.IsDead && seen.Add(((NetworkBehaviour)value).ObjectId) && Vector3.Distance(((Component)value).transform.position, position) <= radius)
				{
					list.Add(value.Creature);
				}
			}
			return list;
		}

		private static IEnumerable<Player> PlayersNear(Player attacker, Vector3 position, float radius, HashSet<int> seen)
		{
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			foreach (Player alivePlayer in PlayerManager.AlivePlayers)
			{
				if (Object.op_Implicit((Object)(object)alivePlayer) && (Object)(object)alivePlayer != (Object)(object)attacker && seen.Add(((NetworkBehaviour)alivePlayer).ObjectId) && Vector3.Distance(alivePlayer.Transform.position, position) <= radius)
				{
					yield return alivePlayer;
				}
			}
		}

		private static Player? PlayerFor(int objectId)
		{
			foreach (Player player in PlayerManager.Players)
			{
				if (Object.op_Implicit((Object)(object)player) && ((NetworkBehaviour)player).ObjectId == objectId)
				{
					return player;
				}
			}
			return null;
		}

		private static bool TryResolveTarget(Transform target, out int objectId, out bool isPlayer)
		{
			Player playerFromBodyPart = PlayerManager.GetPlayerFromBodyPart(target);
			if (Object.op_Implicit((Object)(object)playerFromBodyPart))
			{
				objectId = ((NetworkBehaviour)playerFromBodyPart).ObjectId;
				isPlayer = true;
				return true;
			}
			Item val = ItemManager.Get(target);
			objectId = (Object.op_Implicit((Object)(object)val) ? ((NetworkBehaviour)val).ObjectId : (-1));
			isPlayer = false;
			return Object.op_Implicit((Object)(object)val);
		}

		private static MeleeHitEffects CaptureEffects(Melee melee)
		{
			MeleeHitEffects meleeHitEffects = MeleeHitEffects.None;
			if (UpgradeState.IsEnabled((Item)(object)melee, UpgradeState.Cleave))
			{
				meleeHitEffects |= MeleeHitEffects.Cleave;
			}
			if (UpgradeState.IsKnife(melee) && UpgradeState.IsEnabled((Item)(object)melee, UpgradeState.Bleed))
			{
				meleeHitEffects |= MeleeHitEffects.Bleed;
			}
			return meleeHitEffects;
		}

		private static Melee? MeleeFor(int objectId)
		{
			foreach (Item value in ItemManager.Items.Values)
			{
				if (Object.op_Implicit((Object)(object)value) && ((NetworkBehaviour)value).ObjectId == objectId)
				{
					Melee val = (Melee)(object)((value is Melee) ? value : null);
					if (val != null)
					{
						return val;
					}
				}
			}
			return null;
		}

		private static bool ValidHitRequest(NetworkConnection connection, Player? attacker, Melee? melee, MeleeHitRequest message)
		{
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0099: 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)
			//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d9: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)attacker) || ((NetworkBehaviour)attacker).Owner != connection || !Object.op_Implicit((Object)(object)melee) || (Object)(object)((Item)melee).Holder != (Object)(object)attacker)
			{
				return false;
			}
			float num = MeleeEffects.NativeRange(melee) + 2f;
			if (Vector3.Distance(attacker.CamObject.position, message.Position) > num)
			{
				return false;
			}
			Player val = PlayerFor(message.TargetObjectId);
			Item val2 = ItemFor(message.TargetObjectId);
			Vector3 val3 = (Vector3)((message.TargetIsPlayer && Object.op_Implicit((Object)(object)val)) ? val.Transform.position : (Object.op_Implicit((Object)(object)val2) ? ((Component)val2).transform.position : new Vector3(float.PositiveInfinity, 0f, 0f)));
			if (message.TargetIsPlayer ? Object.op_Implicit((Object)(object)val) : Object.op_Implicit((Object)(object)val2))
			{
				return Vector3.Distance(val3, message.Position) <= 4f;
			}
			return false;
		}

		private static Item? ItemFor(int objectId)
		{
			foreach (Item value in ItemManager.Items.Values)
			{
				if (Object.op_Implicit((Object)(object)value) && ((NetworkBehaviour)value).ObjectId == objectId)
				{
					return value;
				}
			}
			return null;
		}

		private static Vector3 Direction(Player attacker, Vector3 target)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0011: Unknown result type (might be due to invalid IL or missing references)
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = target - attacker.CamObject.position;
			return ((Vector3)(ref val)).normalized;
		}

		private static void OnClientCleave(CleaveFeedbackBroadcast message, Channel channel)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			PlayerUI.AddDamageNumber(message.Position, message.Damage, message.Killed);
		}

		private static void OnClientBleedParticle(BleedParticleBroadcast message, Channel channel)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			ParticleManager.Play("BloodSmall", message.Position, Vector3.up);
		}

		private static void SendCleaveFeedback(Player attacker, Vector3 position, int damage, bool killed)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			NetworkConnection owner = ((NetworkBehaviour)attacker).Owner;
			if (owner != null && owner.IsValid)
			{
				InstanceFinder.ServerManager.Broadcast<CleaveFeedbackBroadcast>(owner, new CleaveFeedbackBroadcast(position, damage, killed), true, (Channel)0);
			}
		}

		private static void SendBleedParticle(Player attacker, Vector3 position)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			NetworkConnection owner = ((NetworkBehaviour)attacker).Owner;
			if (owner != null && owner.IsValid)
			{
				InstanceFinder.ServerManager.Broadcast<BleedParticleBroadcast>(owner, new BleedParticleBroadcast(position), true, (Channel)0);
			}
		}

		private static void TickBleeding(StatusContext context)
		{
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			if (Object.op_Implicit((Object)(object)((StatusContext)(ref context)).Target) && !((StatusContext)(ref context)).Target.Dying.IsDead && Object.op_Implicit((Object)(object)((StatusContext)(ref context)).Source))
			{
				int num = Mathf.Clamp(_bleedDamage.Value, 0, 100);
				((StatusContext)(ref context)).Target.Vitals.TakeDamage(num, ((StatusContext)(ref context)).Target.Transform.position, Vector3.zero, true);
				((StatusContext)(ref context)).Target.Vitals.ObserverHit(((StatusContext)(ref context)).Source, ((StatusContext)(ref context)).Target.Transform.position, Vector3.zero, num, (DamageType)0);
				SendBleedParticle(((StatusContext)(ref context)).Source, ((StatusContext)(ref context)).Target.Transform.position);
			}
		}

		private static byte[] LoadPng(string resource)
		{
			using Stream stream = typeof(MeleeNetwork).Assembly.GetManifestResourceStream("ExoticUpgrades." + resource) ?? throw new InvalidOperationException("Embedded status icon '" + resource + "' was not found.");
			using MemoryStream memoryStream = new MemoryStream();
			stream.CopyTo(memoryStream);
			return memoryStream.ToArray();
		}

		private static void WriteHit(Writer writer, MeleeHitRequest message)
		{
			//IL_0037: Unknown result type (might be due to invalid IL or missing references)
			writer.WriteInt32(message.AttackerObjectId);
			writer.WriteInt32(message.MeleeObjectId);
			writer.WriteInt32(message.TargetObjectId);
			writer.WriteBoolean(message.TargetIsPlayer);
			writer.WriteVector3(message.Position);
			writer.WriteUInt8Unpacked((byte)message.Effects);
		}

		private static MeleeHitRequest ReadHit(Reader reader)
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			return new MeleeHitRequest(reader.ReadInt32(), reader.ReadInt32(), reader.ReadInt32(), reader.ReadBoolean(), reader.ReadVector3(), (MeleeHitEffects)reader.ReadUInt8Unpacked());
		}
	}
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	[BepInPlugin("ArceDev.ExoticUpgrades", "Exotic Upgrades", "0.4.0")]
	public class Plugin : BaseUnityPlugin
	{
		[HarmonyPatch(typeof(PlayerHolding), "ChangeSkinInput")]
		private static class ChangeSkinInputPatch
		{
			private static bool Prefix(Player ____player, Item ____heldItem, ref CallbackContext context)
			{
				if (____player.BlockInputs || !InspectHeld() || !Object.op_Implicit((Object)(object)____heldItem) || !UpgradeState.SupportsExoticUpgrades(____heldItem))
				{
					return true;
				}
				return !UpgradeState.TrySelect(____heldItem, ((CallbackContext)(ref context)).ReadValue<float>() > 0f);
			}
		}

		[HarmonyPatch(typeof(PlayerHolding), "ReloadInput")]
		private static class ReloadInputPatch
		{
			private static bool Prefix(Player ____player, Item ____heldItem)
			{
				if (____player.BlockInputs || !InspectHeld() || !Object.op_Implicit((Object)(object)____heldItem) || !UpgradeState.SupportsExoticUpgrades(____heldItem))
				{
					return true;
				}
				return !UpgradeState.TryToggleSelected(____heldItem);
			}
		}

		[HarmonyPatch(typeof(WeaponUI), "UpdateInspectedItemText")]
		private static class InspectionTextPatch
		{
			private static void Prefix(Item ____inspectedItem, ref string ____extraDescription)
			{
				if (Object.op_Implicit((Object)(object)____inspectedItem) && UpgradeState.SupportsExoticUpgrades(____inspectedItem))
				{
					____extraDescription = UpgradeState.WithInspection(____extraDescription, ____inspectedItem);
				}
			}
		}

		[HarmonyPatch(typeof(ProjectileManager), "AddProjectile")]
		private static class AddProjectilePatch
		{
			private static bool Prefix(ProjectileManager __instance, Player owner, WeaponInfo weaponInfo, bool isLocal, Vector3 pos, ref Vector3 velocity, uint catchingUpToDo, uint id, bool fromNpc, out int __state)
			{
				//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
				//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
				//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
				//IL_007f: Unknown result type (might be due to invalid IL or missing references)
				//IL_008b: Unknown result type (might be due to invalid IL or missing references)
				//IL_0090: Unknown result type (might be due to invalid IL or missing references)
				__state = __instance.GetType(weaponInfo.ProjectileType).Projectiles.Count;
				if (!isLocal || fromNpc || !Object.op_Implicit((Object)(object)weaponInfo.Weapon))
				{
					return true;
				}
				ProjectileProfile profile = ProjectileProfile.Capture(weaponInfo.Weapon).Clamped();
				if (profile.Effects == ProjectileEffects.None)
				{
					return true;
				}
				if (profile.Effects.HasFlag(ProjectileEffects.SplitShot) && profile.SplitCount > 1)
				{
					__state = ~__state;
					__instance.AddProjectiles(owner, weaponInfo, true, pos, (Vector3[])(object)new Vector3[1] { velocity }, catchingUpToDo, id, false);
					return false;
				}
				velocity = SlowMoBullets.ApplySpeed(velocity, profile);
				ProjectileNetwork.SendShot(owner, ProjectileNetwork.NextId(__instance), 1, profile);
				return true;
			}

			private static void Postfix(ProjectileManager __instance, WeaponInfo weaponInfo, int __state)
			{
				if (__state >= 0)
				{
					ProjectileNetwork.RegisterNew(__instance, weaponInfo, __state);
				}
			}
		}

		[HarmonyPatch(typeof(ProjectileManager), "AddProjectiles")]
		private static class AddProjectilesPatch
		{
			private static void Prefix(ProjectileManager __instance, Player owner, WeaponInfo weaponInfo, bool isLocal, ref Vector3[] velocities, out int __state)
			{
				__state = __instance.GetType(weaponInfo.ProjectileType).Projectiles.Count;
				if (isLocal && Object.op_Implicit((Object)(object)weaponInfo.Weapon))
				{
					ProjectileProfile profile = ProjectileProfile.Capture(weaponInfo.Weapon).Clamped();
					if (profile.Effects != ProjectileEffects.None)
					{
						velocities = SplitShotBullets.Expand(velocities, profile);
						ApplySlowSpeed(velocities, profile);
						ProjectileNetwork.SendShot(owner, ProjectileNetwork.NextId(__instance), velocities.Length, profile);
					}
				}
			}

			private static void Postfix(ProjectileManager __instance, WeaponInfo weaponInfo, int __state)
			{
				ProjectileNetwork.RegisterNew(__instance, weaponInfo, __state);
			}
		}

		[HarmonyPatch(typeof(ProjectileManager), "FixedUpdate")]
		private static class ProjectileUpdatePatch
		{
			private static void Prefix(ProjectileManager __instance)
			{
				HomingBullets.Steer(__instance);
				ExplosiveBullets.RemoveExpired(__instance);
				RicochetBullets.RemoveExpired(__instance);
				ProjectileNetwork.Cleanup();
			}
		}

		[HarmonyPatch(typeof(ProjectileManager), "Hit")]
		private static class ProjectileHitPatch
		{
			private static bool Prefix(Projectile projectile, ref RaycastHit hit, out bool __state)
			{
				//IL_0003: Unknown result type (might be due to invalid IL or missing references)
				__state = RicochetBullets.TryBounce(projectile, hit);
				return !__state;
			}

			private static void Postfix(Projectile projectile, ref RaycastHit hit, bool __state)
			{
				//IL_0005: Unknown result type (might be due to invalid IL or missing references)
				if (!__state)
				{
					ExplosiveBullets.Explode(projectile, ((RaycastHit)(ref hit)).point);
				}
			}
		}

		[HarmonyPatch(typeof(Player), "OnStartClient")]
		private static class PlayerClientStartPatch
		{
			private static void Postfix()
			{
				ProjectileNetwork.RegisterClient();
				ProjectileNetwork.RegisterServer();
				MeleeNetwork.RegisterClient();
				MeleeNetwork.RegisterServer();
			}
		}

		[HarmonyPatch(typeof(Server), "OnStartClient")]
		private static class NetworkStartPatch
		{
			private static void Postfix()
			{
				ProjectileNetwork.RegisterClient();
				ProjectileNetwork.RegisterServer();
				MeleeNetwork.RegisterClient();
				MeleeNetwork.RegisterServer();
			}
		}

		[HarmonyPatch(typeof(Server), "OnStopClient")]
		private static class NetworkStopPatch
		{
			private static void Prefix()
			{
				ProjectileNetwork.ResetSession();
				MeleeNetwork.ResetSession();
				MeleeEffects.Reset();
			}
		}

		public const string Id = "ArceDev.ExoticUpgrades";

		internal static ManualLogSource Log { get; private set; }

		public static string Name => "Exotic Upgrades";

		public static string Version => "0.4.0";

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			UpgradeState.Initialize(((BaseUnityPlugin)this).Config);
			ProjectileNetwork.Initialize();
			MeleeNetwork.Initialize(((BaseUnityPlugin)this).Config);
			MeleeEffects.Initialize(((BaseUnityPlugin)this).Config);
			HomingBullets.Initialize(((BaseUnityPlugin)this).Config);
			ExplosiveBullets.Initialize(((BaseUnityPlugin)this).Config);
			RicochetBullets.Initialize(((BaseUnityPlugin)this).Config);
			SlowMoBullets.Initialize(((BaseUnityPlugin)this).Config);
			SplitShotBullets.Initialize(((BaseUnityPlugin)this).Config);
			ExoticGacha.Initialize(((BaseUnityPlugin)this).Config);
			ExoticGacha.Validate();
			UpgradeState.Validate();
			HomingBullets.Validate();
			ExplosiveBullets.Validate();
			RicochetBullets.Validate();
			SlowMoBullets.Validate();
			SplitShotBullets.Validate();
			ProjectileNetwork.Validate();
			MeleeEffects.Validate();
			Harmony.CreateAndPatchAll(typeof(Plugin).Assembly, "ArceDev.ExoticUpgrades");
			Log.LogInfo((object)("Plugin " + Name + " is loaded!"));
			Log.LogInfo((object)"Exotic upgrades coexist with native skins in each island's slot-machine weapon pool.");
		}

		private void Update()
		{
			MeleeEffects.Update();
		}

		private static bool InspectHeld()
		{
			if (Object.op_Implicit((Object)(object)GameInfo.Input))
			{
				return GameInfo.Input.actions["WeaponInspect"].IsPressed();
			}
			return false;
		}

		private static void ApplySlowSpeed(Vector3[] velocities, ProjectileProfile profile)
		{
			//IL_0008: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			for (int i = 0; i < velocities.Length; i++)
			{
				velocities[i] = SlowMoBullets.ApplySpeed(velocities[i], profile);
			}
		}
	}
	[Flags]
	internal enum ProjectileEffects : byte
	{
		None = 0,
		Homing = 1,
		Explosive = 2,
		Ricochet = 4,
		SlowMo = 8,
		SplitShot = 0x10
	}
	internal readonly record struct ProjectileKey(int OwnerObjectId, uint ProjectileId);
	internal readonly record struct ProjectileProfile(ProjectileEffects Effects, float HomingRange, float HomingMinimumDot, float HomingTurnSpeed, int ExplosionDamage, float ExplosionRadius, float ExplosionForce, int RicochetBounces, float RicochetRetention, float RicochetOffset, float SlowSpeed, float SlowGravity, int SplitCount, float SplitSpread, float SplitDamage)
	{
		internal static ProjectileProfile Capture(Weapon weapon)
		{
			ProjectileEffects projectileEffects = ProjectileEffects.None;
			if (UpgradeState.IsEnabled((Item)(object)weapon, UpgradeState.Homing))
			{
				projectileEffects |= ProjectileEffects.Homing;
			}
			if (UpgradeState.IsEnabled((Item)(object)weapon, UpgradeState.Explosive))
			{
				projectileEffects |= ProjectileEffects.Explosive;
			}
			if (UpgradeState.IsEnabled((Item)(object)weapon, UpgradeState.Ricochet))
			{
				projectileEffects |= ProjectileEffects.Ricochet;
			}
			if (UpgradeState.IsEnabled((Item)(object)weapon, UpgradeState.SlowMo))
			{
				projectileEffects |= ProjectileEffects.SlowMo;
			}
			if (UpgradeState.IsEnabled((Item)(object)weapon, UpgradeState.SplitShot))
			{
				projectileEffects |= ProjectileEffects.SplitShot;
			}
			return new ProjectileProfile(projectileEffects, HomingBullets.Range, HomingBullets.MinimumForwardDot, HomingBullets.TurnDegreesPerSecond, ExplosiveBullets.Damage, ExplosiveBullets.Radius, ExplosiveBullets.ItemForce, RicochetBullets.MaxBounces, RicochetBullets.SpeedRetention, RicochetBullets.SurfaceOffset, SlowMoBullets.ProjectileSpeed, SlowMoBullets.GravityMultiplier, SplitShotBullets.ProjectileCount, SplitShotBullets.SpreadDegrees, SplitShotBullets.DamageMultiplier);
		}

		internal ProjectileProfile Clamped()
		{
			return this with
			{
				Effects = (Effects & (ProjectileEffects.Homing | ProjectileEffects.Explosive | ProjectileEffects.Ricochet | ProjectileEffects.SlowMo | ProjectileEffects.SplitShot)),
				HomingRange = Mathf.Clamp(HomingRange, 0f, 250f),
				HomingMinimumDot = Mathf.Clamp(HomingMinimumDot, -1f, 1f),
				HomingTurnSpeed = Mathf.Clamp(HomingTurnSpeed, 0f, 1440f),
				ExplosionDamage = Mathf.Clamp(ExplosionDamage, 0, 1000),
				ExplosionRadius = Mathf.Clamp(ExplosionRadius, 0f, 25f),
				ExplosionForce = Mathf.Clamp(ExplosionForce, 0f, 10000f),
				RicochetBounces = Mathf.Clamp(RicochetBounces, 0, 12),
				RicochetRetention = Mathf.Clamp(RicochetRetention, 0f, 3f),
				RicochetOffset = Mathf.Clamp(RicochetOffset, 0f, 1f),
				SlowSpeed = Mathf.Clamp(SlowSpeed, 0.01f, 500f),
				SlowGravity = Mathf.Clamp(SlowGravity, 0f, 5f),
				SplitCount = Mathf.Clamp(SplitCount, 1, 12),
				SplitSpread = Mathf.Clamp(SplitSpread, 0f, 180f),
				SplitDamage = Mathf.Clamp(SplitDamage, 0f, 5f)
			};
		}
	}
	internal readonly record struct ShotBroadcast(int OwnerObjectId, uint FirstProjectileId, ushort ProjectileCount, ProjectileProfile Profile) : IBroadcast;
	internal readonly record struct HomingTargetBroadcast(int OwnerObjectId, uint ProjectileId, int TargetObjectId) : IBroadcast;
	internal readonly record struct ExplosionRequestBroadcast(int OwnerObjectId, uint ProjectileId, Vector3 Position) : IBroadcast;
	internal readonly record struct ExplosionEventBroadcast(Vector3 Position, float Radius, float Force) : IBroadcast;
	internal static class ProjectileNetwork
	{
		private readonly record struct ServerShot(NetworkConnection Connection, ProjectileProfile Profile, float ExpiresAt);

		private const int MaxProjectilesPerShot = 192;

		private const float ServerShotLifetime = 120f;

		private static readonly FieldRef<ProjectileManager, uint> NextIdRef = AccessTools.FieldRefAccess<ProjectileManager, uint>("_nextId");

		private static readonly Action<ShotBroadcast, Channel> ClientShotHandler = OnClientShot;

		private static readonly Action<HomingTargetBroadcast, Channel> ClientTargetHandler = OnClientTarget;

		private static readonly Action<ExplosionEventBroadcast, Channel> ClientExplosionHandler = OnClientExplosion;

		private static readonly Action<NetworkConnection, ShotBroadcast, Channel> ServerShotHandler = OnServerShot;

		private static readonly Action<NetworkConnection, HomingTargetBroadcast, Channel> ServerTargetHandler = OnServerTarget;

		private static readonly Action<NetworkConnection, ExplosionRequestBroadcast, Channel> ServerExplosionHandler = OnServerExplosion;

		private static readonly Dictionary<ProjectileKey, ProjectileProfile> PendingProfiles = new Dictionary<ProjectileKey, ProjectileProfile>();

		private static readonly Dictionary<ProjectileKey, Projectile> PendingProjectiles = new Dictionary<ProjectileKey, Projectile>();

		private static readonly HashSet<ProjectileKey> Applied = new HashSet<ProjectileKey>();

		private static readonly Dictionary<ProjectileKey, ServerShot> ServerShots = new Dictionary<ProjectileKey, ServerShot>();

		private static bool _clientRegistered;

		private static bool _serverRegistered;

		internal static void Initialize()
		{
			GenericWriter<ShotBroadcast>.SetWrite((Action<Writer, ShotBroadcast>)WriteShot);
			GenericReader<ShotBroadcast>.SetRead((Func<Reader, ShotBroadcast>)ReadShot);
			GenericWriter<HomingTargetBroadcast>.SetWrite((Action<Writer, HomingTargetBroadcast>)delegate(Writer writer, HomingTargetBroadcast message)
			{
				writer.WriteInt32(message.OwnerObjectId);
				writer.WriteUInt32(message.ProjectileId);
				writer.WriteInt32(message.TargetObjectId);
			});
			GenericReader<HomingTargetBroadcast>.SetRead((Func<Reader, HomingTargetBroadcast>)((Reader reader) => new HomingTargetBroadcast(reader.ReadInt32(), reader.ReadUInt32(), reader.ReadInt32())));
			GenericWriter<ExplosionRequestBroadcast>.SetWrite((Action<Writer, ExplosionRequestBroadcast>)delegate(Writer writer, ExplosionRequestBroadcast message)
			{
				//IL_001d: Unknown result type (might be due to invalid IL or missing references)
				writer.WriteInt32(message.OwnerObjectId);
				writer.WriteUInt32(message.ProjectileId);
				writer.WriteVector3(message.Position);
			});
			GenericReader<ExplosionRequestBroadcast>.SetRead((Func<Reader, ExplosionRequestBroadcast>)((Reader reader) => new ExplosionRequestBroadcast(reader.ReadInt32(), reader.ReadUInt32(), reader.ReadVector3())));
			GenericWriter<ExplosionEventBroadcast>.SetWrite((Action<Writer, ExplosionEventBroadcast>)delegate(Writer writer, ExplosionEventBroadcast message)
			{
				//IL_0003: Unknown result type (might be due to invalid IL or missing references)
				writer.WriteVector3(message.Position);
				writer.WriteSingle(message.Radius);
				writer.WriteSingle(message.Force);
			});
			GenericReader<ExplosionEventBroadcast>.SetRead((Func<Reader, ExplosionEventBroadcast>)((Reader reader) => new ExplosionEventBroadcast(reader.ReadVector3(), reader.ReadSingle(), reader.ReadSingle())));
		}

		internal static void RegisterClient()
		{
			if (!_clientRegistered && Object.op_Implicit((Object)(object)InstanceFinder.ClientManager))
			{
				InstanceFinder.ClientManager.RegisterBroadcast<ShotBroadcast>(ClientShotHandler);
				InstanceFinder.ClientManager.RegisterBroadcast<HomingTargetBroadcast>(ClientTargetHandler);
				InstanceFinder.ClientManager.RegisterBroadcast<ExplosionEventBroadcast>(ClientExplosionHandler);
				_clientRegistered = true;
				Plugin.Log.LogInfo((object)"Exotic projectile multiplayer client handlers registered.");
			}
		}

		internal static void RegisterServer()
		{
			if (!_serverRegistered && Object.op_Implicit((Object)(object)InstanceFinder.ServerManager))
			{
				InstanceFinder.ServerManager.RegisterBroadcast<ShotBroadcast>(ServerShotHandler, true);
				InstanceFinder.ServerManager.RegisterBroadcast<HomingTargetBroadcast>(ServerTargetHandler, true);
				InstanceFinder.ServerManager.RegisterBroadcast<ExplosionRequestBroadcast>(ServerExplosionHandler, true);
				_serverRegistered = true;
				Plugin.Log.LogInfo((object)"Exotic projectile multiplayer server handlers registered.");
			}
		}

		internal static void ResetSession()
		{
			if (_clientRegistered && Object.op_Implicit((Object)(object)InstanceFinder.ClientManager))
			{
				InstanceFinder.ClientManager.UnregisterBroadcast<ShotBroadcast>(ClientShotHandler);
				InstanceFinder.ClientManager.UnregisterBroadcast<HomingTargetBroadcast>(ClientTargetHandler);
				InstanceFinder.ClientManager.UnregisterBroadcast<ExplosionEventBroadcast>(ClientExplosionHandler);
			}
			if (_serverRegistered && Object.op_Implicit((Object)(object)InstanceFinder.ServerManager))
			{
				InstanceFinder.ServerManager.UnregisterBroadcast<ShotBroadcast>(ServerShotHandler);
				InstanceFinder.ServerManager.UnregisterBroadcast<HomingTargetBroadcast>(ServerTargetHandler);
				InstanceFinder.ServerManager.UnregisterBroadcast<ExplosionRequestBroadcast>(ServerExplosionHandler);
			}
			_clientRegistered = false;
			_serverRegistered = false;
			PendingProfiles.Clear();
			PendingProjectiles.Clear();
			Applied.Clear();
			ServerShots.Clear();
			HomingBullets.Clear();
			ExplosiveBullets.Clear();
			RicochetBullets.Clear();
		}

		internal static uint NextId(ProjectileManager manager)
		{
			return NextIdRef.Invoke(manager);
		}

		internal static void SendShot(Player owner, uint firstId, int count, ProjectileProfile profile)
		{
			if (Object.op_Implicit((Object)(object)owner) && count > 0 && count <= 192 && Object.op_Implicit((Object)(object)InstanceFinder.ClientManager))
			{
				ShotBroadcast shotBroadcast = new ShotBroadcast(((NetworkBehaviour)owner).ObjectId, firstId, (ushort)count, profile.Clamped());
				ApplyShot(shotBroadcast);
				InstanceFinder.ClientManager.Broadcast<ShotBroadcast>(shotBroadcast, (Channel)0);
			}
		}

		internal static void RegisterNew(ProjectileManager manager, WeaponInfo weaponInfo, int firstNewIndex)
		{
			List<Projectile> projectiles = manager.GetType(weaponInfo.ProjectileType).Projectiles;
			for (int i = firstNewIndex; i < projectiles.Count; i++)
			{
				Projectile val = projectiles[i];
				ProjectileKey key = Key(val);
				PendingProjectiles[key] = val;
				TryApply(key);
			}
		}

		internal static void ShareTarget(Projectile projectile, Creature target)
		{
			if (projectile.IsLocal && Object.op_Implicit((Object)(object)target) && Object.op_Implicit((Object)(object)InstanceFinder.ClientManager))
			{
				HomingTargetBroadcast homingTargetBroadcast = new HomingTargetBroadcast(((NetworkBehaviour)projectile.Owner).ObjectId, projectile.Id, ((NetworkBehaviour)target).ObjectId);
				HomingBullets.SetTarget(Key(projectile), ((NetworkBehaviour)target).ObjectId);
				InstanceFinder.ClientManager.Broadcast<HomingTargetBroadcast>(homingTargetBroadcast, (Channel)0);
			}
		}

		internal static void RequestExplosion(Projectile projectile, Vector3 position)
		{
			//IL_002a: Unknown result type (might be due to invalid IL or missing references)
			if (projectile.IsLocal && Object.op_Implicit((Object)(object)InstanceFinder.ClientManager))
			{
				InstanceFinder.ClientManager.Broadcast<ExplosionRequestBroadcast>(new ExplosionRequestBroadcast(((NetworkBehaviour)projectile.Owner).ObjectId, projectile.Id, position), (Channel)0);
			}
		}

		internal static void Cleanup()
		{
			CleanupProjectiles();
			CleanupServerShots(Time.time);
		}

		private static void CleanupProjectiles()
		{
			List<ProjectileKey> list = null;
			foreach (var (item, val2) in PendingProjectiles)
			{
				if (!Object.op_Implicit((Object)(object)val2.Owner) || !Object.op_Implicit((Object)(object)ProjectileManager.Instance) || !ProjectileManager.Instance.GetType(val2.TypeId).Projectiles.Contains(val2))
				{
					(list ?? (list = new List<ProjectileKey>())).Add(item);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (ProjectileKey item2 in list)
			{
				PendingProjectiles.Remove(item2);
				PendingProfiles.Remove(item2);
				Applied.Remove(item2);
			}
		}

		private static void CleanupServerShots(float now)
		{
			List<ProjectileKey> list = null;
			foreach (var (item, serverShot2) in ServerShots)
			{
				if (now >= serverShot2.ExpiresAt)
				{
					(list ?? (list = new List<ProjectileKey>())).Add(item);
				}
			}
			if (list == null)
			{
				return;
			}
			foreach (ProjectileKey item2 in list)
			{
				ServerShots.Remove(item2);
			}
		}

		internal static void Validate()
		{
			ProjectileProfile projectileProfile = new ProjectileProfile((ProjectileEffects)255, -1f, -2f, 9999f, -1, 999f, -4f, -2, 9f, -1f, 0f, -1f, 99, 999f, -1f).Clamped();
			if (projectileProfile.Effects == ProjectileEffects.None || projectileProfile.HomingRange < 0f || projectileProfile.ExplosionDamage < 0 || projectileProfile.SplitCount != 12 || projectileProfile.SlowSpeed < 0.01f)
			{
				throw new InvalidOperationException("Projectile network profile validation failed.");
			}
		}

		private static void OnServerShot(NetworkConnection connection, ShotBroadcast message, Channel channel)
		{
			Player player = PlayerFor(message.OwnerObjectId);
			if (!Owns(connection, player) || message.ProjectileCount == 0 || message.ProjectileCount > 192)
			{
				Plugin.Log.LogWarning((object)"Rejected invalid exotic projectile metadata from a client.");
				return;
			}
			ShotBroadcast message2 = message with
			{
				Profile = message.Profile.Clamped()
			};
			for (uint num = 0u; num < message2.ProjectileCount; num++)
			{
				ServerShots[new ProjectileKey(message2.OwnerObjectId, message2.FirstProjectileId + num)] = new ServerShot(connection, message2.Profile, Time.time + 120f);
			}
			BroadcastAuthenticated<ShotBroadcast>(message2);
		}

		private static void OnServerTarget(NetworkConnection connection, HomingTargetBroadcast message, Channel channel)
		{
			ProjectileKey key = new ProjectileKey(message.OwnerObjectId, message.ProjectileId);
			if (ServerShots.TryGetValue(key, out var value) && !(value.Connection != connection) && value.Profile.Effects.HasFlag(ProjectileEffects.Homing) && IsLivingCreature(message.TargetObjectId))
			{
				BroadcastAuthenticated<HomingTargetBroadcast>(message);
			}
		}

		private static void OnServerExplosion(NetworkConnection connection, ExplosionRequestBroadcast message, Channel channel)
		{
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			ProjectileKey key = new ProjectileKey(message.OwnerObjectId, message.ProjectileId);
			if (ServerShots.TryGetValue(key, out var value) && !(value.Connection != connection) && value.Profile.Effects.HasFlag(ProjectileEffects.Explosive))
			{
				ServerShots.Remove(key);
				Player val = PlayerFor(message.OwnerObjectId);
				if (Object.op_Implicit((Object)(object)val))
				{
					ExplosiveBullets.DamageOnServer(val, message.Position, value.Profile);
					BroadcastAuthenticated<ExplosionEventBroadcast>(new ExplosionEventBroadcast(message.Position, value.Profile.ExplosionRadius, value.Profile.ExplosionForce));
				}
			}
		}

		private static void OnClientShot(ShotBroadcast message, Channel channel)
		{
			ApplyShot(message);
		}

		private static void OnClientTarget(HomingTargetBroadcast message, Channel channel)
		{
			HomingBullets.SetTarget(new ProjectileKey(message.OwnerObjectId, message.ProjectileId), message.TargetObjectId);
		}

		private static void OnClientExplosion(ExplosionEventBroadcast message, Channel channel)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			ExplosiveBullets.PlayExplosion(message.Position, message.Radius, message.Force);
		}

		private static void ApplyShot(ShotBroadcast message)
		{
			ProjectileProfile value = message.Profile.Clamped();
			for (uint num = 0u; num < message.ProjectileCount; num++)
			{
				ProjectileKey key = new ProjectileKey(message.OwnerObjectId, message.FirstProjectileId + num);
				PendingProfiles[key] = value;
				TryApply(key);
			}
		}

		private static void TryApply(ProjectileKey key)
		{
			if (PendingProfiles.TryGetValue(key, out var value) && PendingProjectiles.TryGetValue(key, out Projectile value2))
			{
				if (!Applied.Add(key))
				{
					PendingProfiles.Remove(key);
					return;
				}
				HomingBullets.Track(value2, key, value);
				ExplosiveBullets.Track(value2, value);
				RicochetBullets.Track(value2, value);
				SlowMoBullets.Apply(value2, value);
				SplitShotBullets.ApplyDamage(value2, value);
				PendingProfiles.Remove(key);
			}
		}

		private static ProjectileKey Key(Projectile projectile)
		{
			return new ProjectileKey(((NetworkBehaviour)projectile.Owner).ObjectId, projectile.Id);
		}

		private static Player? PlayerFor(int objectId)
		{
			foreach (Player player in PlayerManager.Players)
			{
				if (Object.op_Implicit((Object)(object)player) && ((NetworkBehaviour)player).ObjectId == objectId)
				{
					return player;
				}
			}
			return null;
		}

		private static bool Owns(NetworkConnection connection, Player? player)
		{
			if (Object.op_Implicit((Object)(object)player))
			{
				return ((NetworkBehaviour)player).Owner == connection;
			}
			return false;
		}

		private static bool IsLivingCreature(int objectId)
		{
			foreach (Item value in ItemManager.Items.Values)
			{
				if (Object.op_Implicit((Object)(object)value) && ((NetworkBehaviour)value).ObjectId == objectId && Object.op_Implicit((Object)(object)value.Creature) && !value.Creature.IsDead)
				{
					return true;
				}
			}
			return false;
		}

		private static void BroadcastAuthenticated<T>(T message) where T : struct, IBroadcast
		{
			foreach (NetworkConnection value in InstanceFinder.ServerManager.Clients.Values)
			{
				if (value.IsAuthenticated)
				{
					InstanceFinder.ServerManager.Broadcast<T>(value, message, true, (Channel)0);
				}
			}
		}

		private static void WriteShot(Writer writer, ShotBroadcast message)
		{
			writer.WriteInt32(message.OwnerObjectId);
			writer.WriteUInt32(message.FirstProjectileId);
			writer.WriteUInt16(message.ProjectileCount);
			writer.WriteUInt8Unpacked((byte)message.Profile.Effects);
			writer.WriteSingle(message.Profile.HomingRange);
			writer.WriteSingle(message.Profile.HomingMinimumDot);
			writer.WriteSingle(message.Profile.HomingTurnSpeed);
			writer.WriteInt32(message.Profile.ExplosionDamage);
			writer.WriteSingle(message.Profile.ExplosionRadius);
			writer.WriteSingle(message.Profile.ExplosionForce);
			writer.WriteInt32(message.Profile.RicochetBounces);
			writer.WriteSingle(message.Profile.RicochetRetention);
			writer.WriteSingle(message.Profile.RicochetOffset);
			writer.WriteSingle(message.Profile.SlowSpeed);
			writer.WriteSingle(message.Profile.SlowGravity);
			writer.WriteInt32(message.Profile.SplitCount);
			writer.WriteSingle(message.Profile.SplitSpread);
			writer.WriteSingle(message.Profile.SplitDamage);
		}

		private static ShotBroadcast ReadShot(Reader reader)
		{
			return new ShotBroadcast(reader.ReadInt32(), reader.ReadUInt32(), reader.ReadUInt16(), new ProjectileProfile((ProjectileEffects)reader.ReadUInt8Unpacked(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadInt32(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadInt32(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadSingle(), reader.ReadInt32(), reader.ReadSingle(), reader.ReadSingle()));
		}
	}
	internal static class RicochetBullets
	{
		private readonly record struct RicochetState(int Remaining, ProjectileProfile Profile);

		private const int DefaultMaxBounces = 3;

		private const float DefaultSpeedRetention = 0.85f;

		private const float DefaultSurfaceOffset = 0.08f;

		private static readonly Dictionary<Projectile, RicochetState> Tracked = new Dictionary<Projectile, RicochetState>();

		private static ConfigEntry<int> _maxBounces = null;

		private static ConfigEntry<float> _speedRetention = null;

		private static ConfigEntry<float> _surfaceOffset = null;

		internal static int MaxBounces => Mathf.Max(0, _maxBounces.Value);

		internal static float SpeedRetention => Mathf.Max(0f, _speedRetention.Value);

		internal static float SurfaceOffset => Mathf.Max(0f, _surfaceOffset.Value);

		internal static void Initialize(ConfigFile config)
		{
			_maxBounces = config.Bind<int>("Ricochet", "MaxBounces", 3, "Maximum number of level or boat surface bounces per projectile.");
			_speedRetention = config.Bind<float>("Ricochet", "SpeedRetention", 0.85f, "Projectile speed multiplier after each bounce. Values above 1 accelerate it.");
			_surfaceOffset = config.Bind<float>("Ricochet", "SurfaceOffset", 0.08f, "Distance used to move a reflected projectile away from the hit surface.");
		}

		internal static void Track(Projectile projectile, ProjectileProfile profile)
		{
			if (profile.Effects.HasFlag(ProjectileEffects.Ricochet))
			{
				Tracked[projectile] = new RicochetState(profile.RicochetBounces, profile);
			}
		}

		internal static void Clear()
		{
			Tracked.Clear();
		}

		internal static bool TryBounce(Projectile projectile, RaycastHit hit)
		{
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0076: Unknown result type (might be due to invalid IL or missing references)
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ad: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			if (!Tracked.TryGetValue(projectile, out var value))
			{
				return false;
			}
			if ((!((Component)((RaycastHit)(ref hit)).transform).CompareTag("Level") && !((Component)((RaycastHit)(ref hit)).transform).CompareTag("Boat")) || value.Remaining <= 0)
			{
				Tracked.Remove(projectile);
				return false;
			}
			Vector3 val = Vector3.Reflect(projectile.Velocity, ((RaycastHit)(ref hit)).normal) * value.Profile.RicochetRetention;
			projectile.Position = (projectile.PreviousPosition = ((RaycastHit)(ref hit)).point + ((RaycastHit)(ref hit)).normal * value.Profile.RicochetOffset);
			projectile.Prev