Decompiled source of FriendinaBox v1.0.0

BepInEx/plugins/FriendinaBox.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Pigeon.Math;
using Pigeon.Movement;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[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("Sparroh")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("FriendinaBox")]
[assembly: AssemblyTitle("FriendinaBox")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[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;
		}
	}
}
[HarmonyPatch]
internal static class CoopUnlockHook
{
	private static MethodBase TargetMethod()
	{
		MethodInfo methodInfo = AccessTools.Method(typeof(PlayerData), "FilterUpgrades", (Type[])null, (Type[])null);
		if (methodInfo == null)
		{
			ManualLogSource logger = FriendinaBoxPlugin.Logger;
			if (logger != null)
			{
				logger.LogError((object)"[FriendinaBox] Could not find PlayerData.FilterUpgrades.");
				return methodInfo;
			}
			return methodInfo;
		}
		ManualLogSource logger2 = FriendinaBoxPlugin.Logger;
		if (logger2 != null)
		{
			logger2.LogInfo((object)"[FriendinaBox] Patching PlayerData.FilterUpgrades for Coop unlock.");
			return methodInfo;
		}
		return methodInfo;
	}

	[HarmonyTranspiler]
	private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
	{
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fa: Expected O, but got Unknown
		MethodInfo methodInfo = AccessTools.PropertyGetter(typeof(List<Player>), "Count");
		FieldInfo fieldInfo = AccessTools.Field(typeof(GameManager), "players");
		MethodInfo methodInfo2 = AccessTools.Method(typeof(CoopUnlockHook), "GetEffectivePlayerCount", (Type[])null, (Type[])null);
		if (methodInfo == null || fieldInfo == null || methodInfo2 == null)
		{
			ManualLogSource logger = FriendinaBoxPlugin.Logger;
			if (logger != null)
			{
				logger.LogError((object)"[FriendinaBox] CoopUnlockHook transpiler missing members — coop unlock disabled.");
			}
			return instructions;
		}
		List<CodeInstruction> list = new List<CodeInstruction>(instructions);
		int num = 0;
		for (int i = 0; i < list.Count - 1; i++)
		{
			if (list[i].opcode == OpCodes.Ldsfld && object.Equals(list[i].operand, fieldInfo) && list[i + 1].opcode == OpCodes.Callvirt && object.Equals(list[i + 1].operand, methodInfo))
			{
				list[i + 1] = new CodeInstruction(OpCodes.Call, (object)methodInfo2);
				num++;
			}
		}
		ManualLogSource logger2 = FriendinaBoxPlugin.Logger;
		if (logger2 != null)
		{
			logger2.LogInfo((object)$"[FriendinaBox] CoopUnlockHook transpiler applied {num} player-count replacement(s).");
		}
		return list;
	}

	public static int GetEffectivePlayerCount(List<Player> players)
	{
		int num = players?.Count ?? 0;
		if (num <= 1 && FriendinaBoxBehaviour.IsEquippedOnLocalPlayer())
		{
			return 2;
		}
		return num;
	}
}
public static class FriendBulletCache
{
	private static bool _attempted;

	private static GameObject _railPrefab;

	private static GameObject _mortarPrefab;

	public static GameObject RailPrefab
	{
		get
		{
			EnsureCached();
			return _railPrefab;
		}
	}

	public static GameObject MortarPrefab
	{
		get
		{
			EnsureCached();
			return _mortarPrefab;
		}
	}

	public static void EnsureCached()
	{
		if (_attempted)
		{
			return;
		}
		_attempted = true;
		TryCacheRailFromCycler();
		TryCacheMortar();
		if ((Object)(object)_railPrefab != (Object)null)
		{
			ManualLogSource logger = FriendinaBoxPlugin.Logger;
			if (logger != null)
			{
				logger.LogInfo((object)("[FriendinaBox] Cached RailBullet prefab '" + ((Object)_railPrefab).name + "'."));
			}
		}
		else
		{
			ManualLogSource logger2 = FriendinaBoxPlugin.Logger;
			if (logger2 != null)
			{
				logger2.LogWarning((object)"[FriendinaBox] Could not cache RailBullet — turret fire will use explosion fallback.");
			}
		}
		if ((Object)(object)_mortarPrefab != (Object)null)
		{
			ManualLogSource logger3 = FriendinaBoxPlugin.Logger;
			if (logger3 != null)
			{
				logger3.LogInfo((object)("[FriendinaBox] Cached MortarBullet prefab '" + ((Object)_mortarPrefab).name + "'."));
			}
		}
		else
		{
			ManualLogSource logger4 = FriendinaBoxPlugin.Logger;
			if (logger4 != null)
			{
				logger4.LogWarning((object)"[FriendinaBox] Could not cache MortarBullet — mortar fire will use explosion fallback.");
			}
		}
	}

	private static void TryCacheRailFromCycler()
	{
		//IL_00f7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00fc: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		if (Global.Instance?.AllGear != null)
		{
			for (int i = 0; i < Global.Instance.AllGear.Length; i++)
			{
				IUpgradable obj = Global.Instance.AllGear[i];
				CartridgeSMG val = (CartridgeSMG)(object)((obj is CartridgeSMG) ? obj : null);
				if (val != null && TryReadGunBulletPrefab((Gun)(object)val, out var prefab) && (Object)(object)prefab != (Object)null && ((Object)(object)prefab.GetComponent<RailBullet>() != (Object)null || prefab.GetComponent<IBullet>() != null))
				{
					_railPrefab = prefab;
					return;
				}
			}
			for (int j = 0; j < Global.Instance.AllGear.Length; j++)
			{
				IUpgradable obj2 = Global.Instance.AllGear[j];
				Gun val2 = (Gun)(object)((obj2 is Gun) ? obj2 : null);
				if (val2 != null && TryReadGunBulletPrefab(val2, out var prefab2) && (Object)(object)prefab2 != (Object)null && (Object)(object)prefab2.GetComponent<RailBullet>() != (Object)null)
				{
					_railPrefab = prefab2;
					return;
				}
			}
		}
		RailBullet[] array = Resources.FindObjectsOfTypeAll<RailBullet>();
		foreach (RailBullet val3 in array)
		{
			if ((Object)(object)val3 == (Object)null)
			{
				continue;
			}
			GameObject gameObject = ((Component)val3).gameObject;
			Scene scene = gameObject.scene;
			if (((Scene)(ref scene)).IsValid())
			{
				scene = gameObject.scene;
				if (((Scene)(ref scene)).isLoaded && gameObject.activeInHierarchy)
				{
					continue;
				}
			}
			_railPrefab = gameObject;
			return;
		}
		if (array.Length != 0 && (Object)(object)array[0] != (Object)null)
		{
			_railPrefab = ((Component)array[0]).gameObject;
		}
	}

	private static void TryCacheMortar()
	{
		//IL_0024: Unknown result type (might be due to invalid IL or missing references)
		//IL_0029: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: Unknown result type (might be due to invalid IL or missing references)
		MortarBullet[] array = Resources.FindObjectsOfTypeAll<MortarBullet>();
		foreach (MortarBullet val in array)
		{
			if ((Object)(object)val == (Object)null)
			{
				continue;
			}
			GameObject gameObject = ((Component)val).gameObject;
			Scene scene = gameObject.scene;
			if (((Scene)(ref scene)).IsValid())
			{
				scene = gameObject.scene;
				if (((Scene)(ref scene)).isLoaded && gameObject.activeInHierarchy)
				{
					continue;
				}
			}
			_mortarPrefab = gameObject;
			return;
		}
		if (array.Length != 0 && (Object)(object)array[0] != (Object)null)
		{
			_mortarPrefab = ((Component)array[0]).gameObject;
			return;
		}
		RocketSalvoBullet[] array2 = Resources.FindObjectsOfTypeAll<RocketSalvoBullet>();
		foreach (RocketSalvoBullet val2 in array2)
		{
			if (!((Object)(object)val2 == (Object)null))
			{
				_ = val2 is MortarBullet;
			}
		}
		if (Global.Instance?.AllGear == null)
		{
			return;
		}
		ProjectileGunArmTip[] array3 = Resources.FindObjectsOfTypeAll<ProjectileGunArmTip>();
		for (int k = 0; k < array3.Length; k++)
		{
			if (TryReadArmTipBulletPrefab(array3[k], out var prefab) && (Object)(object)prefab != (Object)null && (Object)(object)prefab.GetComponent<MortarBullet>() != (Object)null)
			{
				_mortarPrefab = prefab;
				break;
			}
		}
	}

	private static bool TryReadGunBulletPrefab(Gun gun, out GameObject prefab)
	{
		prefab = null;
		if ((Object)(object)gun == (Object)null)
		{
			return false;
		}
		object? obj = typeof(Gun).GetField("_bulletPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)?.GetValue(gun);
		GameObject val = (GameObject)((obj is GameObject) ? obj : null);
		if (val != null && (Object)(object)val != (Object)null)
		{
			prefab = val;
			return true;
		}
		object? obj2 = (typeof(Gun).GetField("bulletPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? typeof(Gun).GetField("defaultBulletPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(gun);
		IBullet val2 = (IBullet)((obj2 is IBullet) ? obj2 : null);
		if (val2 != null)
		{
			Component val3 = (Component)(object)((val2 is Component) ? val2 : null);
			if (val3 != null && (Object)(object)val3 != (Object)null)
			{
				prefab = val3.gameObject;
				return true;
			}
		}
		RailBullet componentInChildren = ((Component)gun).GetComponentInChildren<RailBullet>(true);
		if ((Object)(object)componentInChildren != (Object)null)
		{
			prefab = ((Component)componentInChildren).gameObject;
			return true;
		}
		return false;
	}

	private static bool TryReadArmTipBulletPrefab(ProjectileGunArmTip tip, out GameObject prefab)
	{
		prefab = null;
		if ((Object)(object)tip == (Object)null)
		{
			return false;
		}
		object? obj = (typeof(GunArmTip).GetField("bulletPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) ?? ((object)tip).GetType().GetField("bulletPrefab", BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))?.GetValue(tip);
		GameObject val = (GameObject)((obj is GameObject) ? obj : null);
		if (val != null && (Object)(object)val != (Object)null)
		{
			prefab = val;
			return true;
		}
		return false;
	}
}
public static class FriendCombatHooks
{
	[CompilerGenerated]
	private static class <>O
	{
		public static DamageCallback <0>__OnFriendDamage;

		public static KillCallback <1>__OnFriendKill;

		public static MutableDamageCallback <2>__OnPlayerBeforeDamageOutgoing;

		public static RefAction<DamageData, IDamageSource> <3>__OnPlayerTookDamage;
	}

	private static bool _playerHooksBound;

	private static DamageCallback _onGearDamage;

	private static KillCallback _onGearKill;

	private static MutableDamageCallback _onPlayerBeforeDamage;

	private static RefAction<DamageData, IDamageSource> _onPlayerAfterTakeDamage;

	private static float _nextOverhealTime;

	private static IGear _hookedGear;

	public static void EnsureBound(IGear gear, FriendinaBoxBehaviour.Data data)
	{
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003c: Expected O, but got Unknown
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_005c: Expected O, but got Unknown
		//IL_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_007c: Expected O, but got Unknown
		//IL_008d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0097: Expected O, but got Unknown
		IDamageSource val = (IDamageSource)(object)((gear is IDamageSource) ? gear : null);
		if (val == null)
		{
			return;
		}
		if (_hookedGear != gear)
		{
			UnbindGear();
			_hookedGear = gear;
			object obj = <>O.<0>__OnFriendDamage;
			if (obj == null)
			{
				DamageCallback val2 = OnFriendDamage;
				<>O.<0>__OnFriendDamage = val2;
				obj = (object)val2;
			}
			_onGearDamage = (DamageCallback)obj;
			object obj2 = <>O.<1>__OnFriendKill;
			if (obj2 == null)
			{
				KillCallback val3 = OnFriendKill;
				<>O.<1>__OnFriendKill = val3;
				obj2 = (object)val3;
			}
			_onGearKill = (KillCallback)obj2;
			try
			{
				val.OnDamageTarget = (DamageCallback)Delegate.Combine((Delegate?)(object)val.OnDamageTarget, (Delegate?)(object)_onGearDamage);
				val.OnKillTarget = (KillCallback)Delegate.Combine((Delegate?)(object)val.OnKillTarget, (Delegate?)(object)_onGearKill);
			}
			catch (Exception ex)
			{
				ManualLogSource logger = FriendinaBoxPlugin.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)("[FriendinaBox] Gear combat bind failed: " + ex.Message));
				}
			}
		}
		EnsurePlayerHooks();
	}

	public static void UnbindGear()
	{
		//IL_002f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Expected O, but got Unknown
		//IL_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Expected O, but got Unknown
		if (_hookedGear == null)
		{
			return;
		}
		try
		{
			IGear hookedGear = _hookedGear;
			IDamageSource val = (IDamageSource)(object)((hookedGear is IDamageSource) ? hookedGear : null);
			if (val != null)
			{
				if (_onGearDamage != null)
				{
					val.OnDamageTarget = (DamageCallback)Delegate.Remove((Delegate?)(object)val.OnDamageTarget, (Delegate?)(object)_onGearDamage);
				}
				if (_onGearKill != null)
				{
					val.OnKillTarget = (KillCallback)Delegate.Remove((Delegate?)(object)val.OnKillTarget, (Delegate?)(object)_onGearKill);
				}
			}
		}
		catch
		{
		}
		_hookedGear = null;
	}

	private static void EnsurePlayerHooks()
	{
		//IL_0028: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Expected O, but got Unknown
		//IL_0069: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Expected O, but got Unknown
		if (_playerHooksBound)
		{
			return;
		}
		Player localPlayer = Player.LocalPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return;
		}
		object obj = <>O.<2>__OnPlayerBeforeDamageOutgoing;
		if (obj == null)
		{
			MutableDamageCallback val = OnPlayerBeforeDamageOutgoing;
			<>O.<2>__OnPlayerBeforeDamageOutgoing = val;
			obj = (object)val;
		}
		_onPlayerBeforeDamage = (MutableDamageCallback)obj;
		_onPlayerAfterTakeDamage = OnPlayerTookDamage;
		try
		{
			localPlayer.OnBeforeDamage = (MutableDamageCallback)Delegate.Combine((Delegate?)(object)localPlayer.OnBeforeDamage, (Delegate?)(object)_onPlayerBeforeDamage);
			localPlayer.OnAfterTakeDamage += _onPlayerAfterTakeDamage;
			_playerHooksBound = true;
		}
		catch (Exception ex)
		{
			ManualLogSource logger = FriendinaBoxPlugin.Logger;
			if (logger != null)
			{
				logger.LogWarning((object)("[FriendinaBox] Player combat bind failed: " + ex.Message));
			}
		}
	}

	private static bool TryGetLiveData(out FriendinaBoxBehaviour.Data data, out IGear gear, out Player player)
	{
		data = default(FriendinaBoxBehaviour.Data);
		gear = null;
		player = Player.LocalPlayer;
		if (!FriendinaBoxBehaviour.TryGetEquipped(out var behaviour, out gear) || (Object)(object)behaviour == (Object)null)
		{
			return false;
		}
		data = behaviour.GrenadeData;
		EnsureBound(gear, data);
		return true;
	}

	private static bool IsFriendSourced(IDamageSource source)
	{
		if (source == null)
		{
			return false;
		}
		if (_hookedGear != null && (object)source == _hookedGear)
		{
			return true;
		}
		for (IDamageSource val = source; val != null; val = val.ParentSource)
		{
			if (_hookedGear != null && (object)val == _hookedGear)
			{
				return true;
			}
			IGear val2 = (IGear)(object)((val is IGear) ? val : null);
			if (val2 != null && (Object)(object)((IUpgradable)val2).Info != (Object)null && (((IUpgradable)val2).Info.APIName == "friend_in_a_box" || ((IUpgradable)val2).Info.ID == 92100))
			{
				return true;
			}
		}
		return false;
	}

	private static void GrantBlueOverhealth(Player player, IDamageSource source, float amount)
	{
		//IL_002b: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null || !player.IsAlive || amount <= 0f)
		{
			return;
		}
		try
		{
			player.HealWithOverhealth(amount, source);
		}
		catch
		{
			try
			{
				IDamageSource.HealTarget(source, (ITarget)(object)player, 0f - amount, player.InterpolatedPosition);
			}
			catch (Exception ex)
			{
				ManualLogSource logger = FriendinaBoxPlugin.Logger;
				if (logger != null)
				{
					logger.LogWarning((object)("[FriendinaBox] Overheal failed: " + ex.Message));
				}
			}
		}
	}

	private static void OnFriendDamage(in DamageCallbackData data)
	{
		if (!TryGetLiveData(out var data2, out var gear, out var player) || !IsFriendSourced(data.source))
		{
			return;
		}
		float damage = data.damageData.damage;
		if (damage <= 0f)
		{
			return;
		}
		if (data2.lifestealFraction > 0f && (Object)(object)player != (Object)null && ((NetworkBehaviour)player).IsLocalPlayer)
		{
			float num = damage * data2.lifestealFraction;
			if (num > 0f)
			{
				IDamageSource val = (IDamageSource)(object)((gear is IDamageSource) ? gear : null);
				if (val != null)
				{
					GrantBlueOverhealth(player, val, num);
				}
			}
		}
		if (data2.markDamageBonus > 0f && data2.markDuration > 0f && data.target != null)
		{
			FriendMarkedTarget.Apply(data.target, data2.markDamageBonus, data2.markDuration);
		}
	}

	private static void OnFriendKill(in KillCallbackData data)
	{
		if (!TryGetLiveData(out var data2, out var gear, out var _) || !IsFriendSourced(data.source))
		{
			return;
		}
		if (data2.chargeOnKill > 0f)
		{
			Throwable val = (Throwable)(object)((gear is Throwable) ? gear : null);
			if (val != null)
			{
				try
				{
					((CooldownData)(ref val.CooldownData)).AddCharge(data2.chargeOnKill);
				}
				catch
				{
					try
					{
						val.CooldownData.charge = Mathf.Min((float)val.CooldownData.maxCharges, val.CooldownData.charge + data2.chargeOnKill);
					}
					catch
					{
					}
				}
			}
		}
		if (data2.durationOnKill > 0f)
		{
			FriendDeployTracker.ExtendAllDurations(data2.durationOnKill);
		}
	}

	private static void OnPlayerBeforeDamageOutgoing(ref DamageCallbackData data)
	{
		if (data.target != null && !(data.damageData.damage <= 0f))
		{
			if (TryGetLiveData(out var data2, out var _, out var _) && data2.sharePlayerTarget && data2.sharedTargetDuration > 0f && !IsFriendSourced(data.source))
			{
				FriendSharedTarget.Set(data.target, data2.sharedTargetDuration);
			}
			if (FriendMarkedTarget.TryGetBonus(data.target, out var bonus) && bonus > 0f)
			{
				data.damageData.damage *= 1f + bonus;
			}
		}
	}

	private static void OnPlayerTookDamage(ref DamageData damage, ref IDamageSource source)
	{
		if (!TryGetLiveData(out var data, out var gear, out var player) || data.overhealOnDamaged <= 0f || (Object)(object)player == (Object)null || !((NetworkBehaviour)player).IsLocalPlayer || !player.IsAlive || Time.time < _nextOverhealTime)
		{
			return;
		}
		_nextOverhealTime = Time.time + Mathf.Max(0.5f, data.overhealOnDamagedCooldown);
		IDamageSource val = (IDamageSource)(object)((gear is IDamageSource) ? gear : null);
		if (val != null)
		{
			GrantBlueOverhealth(player, val, data.overhealOnDamaged);
			ManualLogSource logger = FriendinaBoxPlugin.Logger;
			if (logger != null)
			{
				logger.LogInfo((object)$"[FriendinaBox] Reactive Shell overheal +{data.overhealOnDamaged:0.#} blue");
			}
		}
	}

	public static void TickShootToDetonate()
	{
		//IL_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		//IL_0096: 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_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		if (!TryGetLiveData(out var data, out var _, out var player) || !data.shootToDetonate || (Object)(object)player == (Object)null || !((NetworkBehaviour)player).IsLocalPlayer || !FriendDeployTracker.HasActive)
		{
			return;
		}
		try
		{
			if (PlayerInput.Controls == null)
			{
				return;
			}
			PlayerActions player2 = PlayerInput.Controls.Player;
			if (!((PlayerActions)(ref player2)).Fire.IsPressed())
			{
				return;
			}
		}
		catch
		{
			return;
		}
		Camera val = Camera.main;
		if ((Object)(object)val == (Object)null && (Object)(object)PlayerLook.Instance != (Object)null)
		{
			val = ((Component)PlayerLook.Instance).GetComponentInChildren<Camera>();
		}
		RaycastHit val2 = default(RaycastHit);
		if (!((Object)(object)val == (Object)null) && Physics.Raycast(new Ray(((Component)val).transform.position, ((Component)val).transform.forward), ref val2, 200f, -1, (QueryTriggerInteraction)2))
		{
			FriendDeployable friendDeployable = FriendDeployTracker.FindNearest(((RaycastHit)(ref val2)).point, 1.75f);
			if ((Object)(object)friendDeployable != (Object)null)
			{
				friendDeployable.DetonateFromPlayerShot();
			}
		}
	}
}
public sealed class FriendCombatRunner : MonoBehaviour
{
	private static FriendCombatRunner _instance;

	public static void Ensure()
	{
		//IL_0013: 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_001e: Expected O, but got Unknown
		if (!((Object)(object)_instance != (Object)null))
		{
			GameObject val = new GameObject("[FriendinaBox] CombatRunner");
			Object.DontDestroyOnLoad((Object)val);
			_instance = val.AddComponent<FriendCombatRunner>();
		}
	}

	private void Update()
	{
		try
		{
			FriendCombatHooks.TickShootToDetonate();
		}
		catch
		{
		}
	}
}
[HarmonyPatch(typeof(Player), "OnNetworkSpawn")]
internal static class FriendPlayerSpawnHook
{
	[HarmonyPostfix]
	private static void Postfix(Player __instance)
	{
		if (!((Object)(object)__instance == (Object)null) && ((NetworkBehaviour)__instance).IsLocalPlayer)
		{
			FriendCombatRunner.Ensure();
			if (FriendinaBoxBehaviour.TryGetEquipped(out var behaviour, out var gear))
			{
				FriendCombatHooks.EnsureBound(gear, behaviour.GrenadeData);
			}
		}
	}
}
public static class FriendDebugVisual
{
	public static readonly Color DefaultColor = Color.magenta;

	private static readonly Dictionary<int, Material> MaterialsByColor = new Dictionary<int, Material>();

	public static GameObject Attach(Transform parent, Color? color = null, float scale = 0.75f)
	{
		//IL_0040: 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_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_0086: 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_00a6: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)parent == (Object)null)
		{
			return null;
		}
		Transform val = parent.Find("FriendDebugVisual");
		if ((Object)(object)val != (Object)null)
		{
			Object.Destroy((Object)(object)((Component)val).gameObject);
		}
		Color color2 = (Color)(((??)color) ?? DefaultColor);
		GameObject obj = GameObject.CreatePrimitive((PrimitiveType)3);
		((Object)obj).name = "FriendDebugVisual";
		obj.transform.SetParent(parent, false);
		obj.transform.localPosition = Vector3.up * (scale * 0.5f);
		obj.transform.localRotation = Quaternion.identity;
		obj.transform.localScale = Vector3.one * Mathf.Max(0.1f, scale);
		Collider component = obj.GetComponent<Collider>();
		if ((Object)(object)component != (Object)null)
		{
			Object.Destroy((Object)(object)component);
		}
		Renderer component2 = obj.GetComponent<Renderer>();
		if ((Object)(object)component2 != (Object)null)
		{
			component2.sharedMaterial = GetOrCreateMaterial(color2);
			component2.shadowCastingMode = (ShadowCastingMode)0;
			component2.receiveShadows = false;
		}
		return obj;
	}

	private unsafe static Material GetOrCreateMaterial(Color color)
	{
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Expected O, but got Unknown
		//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		int hashCode = ((object)(*(Color*)(&color))/*cast due to .constrained prefix*/).GetHashCode();
		if (MaterialsByColor.TryGetValue(hashCode, out var value) && (Object)(object)value != (Object)null)
		{
			return value;
		}
		Shader val = Shader.Find("Unlit/Color") ?? Shader.Find("Universal Render Pipeline/Unlit") ?? Shader.Find("Sprites/Default") ?? Shader.Find("Standard");
		Material val2 = new Material(((Object)(object)val != (Object)null) ? val : Shader.Find("Hidden/InternalErrorShader"));
		((Object)val2).name = $"FriendinaBox_Debug_{hashCode:X8}";
		((Object)val2).hideFlags = (HideFlags)61;
		if (val2.HasProperty("_Color"))
		{
			val2.color = color;
		}
		else if (val2.HasProperty("_BaseColor"))
		{
			val2.SetColor("_BaseColor", color);
		}
		MaterialsByColor[hashCode] = val2;
		return val2;
	}
}
public sealed class FriendDeployable : MonoBehaviour
{
	private IGear ownerGear;

	private Player ownerPlayer;

	private FriendinaBoxBehaviour.Data friendData;

	private FriendDeployMode mode;

	private float remainingDuration;

	private float detectRadius;

	private float explosionRadius;

	private DamageData damage;

	private float explosionShake;

	private bool finished;

	private float nextScanTime;

	private float nextFireTime;

	private bool droneDiving;

	private Vector3 droneDiveTarget;

	private float maxDuration;

	private SphereCollider hitProxy;

	private float effectiveFireRate;

	private const float ScanInterval = 0.1f;

	private const float HoverHeight = 2.4f;

	private const float ModeEngageRange = 50f;

	private static readonly RangeData LongRangeData = new RangeData
	{
		falloffStartDistance = 799f,
		falloffEndDistance = 800f,
		maxFalloffDamageMultiplier = 1f,
		maxDamageRange = 800f
	};

	private const int SurfaceMask = 10241;

	private const int TargetMaskNonPlayer = 345216;

	public FriendDeployMode Mode => mode;

	public float RemainingDuration => remainingDuration;

	public float MaxDuration => maxDuration;

	public static FriendDeployable Spawn(Vector3 position, IGear gear, FriendinaBoxBehaviour.Data friendData, float explosionRadius, DamageData damage, float explosionShake, float selfEffectMultiplier)
	{
		//IL_0005: 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_0010: 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)
		GameObject val = new GameObject("[FriendinaBox] Deployable");
		val.transform.position = position;
		FriendDeployable friendDeployable = val.AddComponent<FriendDeployable>();
		friendDeployable.Arm(gear, friendData, explosionRadius, damage, explosionShake);
		return friendDeployable;
	}

	private void Arm(IGear gear, FriendinaBoxBehaviour.Data friendData, float explosionRadius, DamageData damage, float explosionShake)
	{
		//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_0150: Unknown result type (might be due to invalid IL or missing references)
		//IL_011c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0121: Unknown result type (might be due to invalid IL or missing references)
		//IL_0138: Unknown result type (might be due to invalid IL or missing references)
		//IL_01b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_01bb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
		ownerGear = gear;
		Throwable val = (Throwable)(object)((gear is Throwable) ? gear : null);
		ownerPlayer = ((val != null) ? val.Player : Player.LocalPlayer);
		this.friendData = friendData;
		mode = friendData.deployMode;
		remainingDuration = Mathf.Max(0.5f, friendData.deployDuration);
		maxDuration = remainingDuration;
		this.explosionRadius = Mathf.Max(0.1f, explosionRadius);
		if (friendData.maxConcurrentDeploys > FriendDeployTracker.MaxConcurrentDeploys)
		{
			FriendDeployTracker.MaxConcurrentDeploys = friendData.maxConcurrentDeploys;
		}
		float num = ((friendData.detectRadius > 0f) ? friendData.detectRadius : (this.explosionRadius * 0.75f));
		detectRadius = num * Mathf.Max(0.01f, friendData.detectRadiusMultiplier);
		this.damage = damage;
		this.explosionShake = explosionShake;
		finished = false;
		nextScanTime = 0f;
		nextFireTime = Time.time + 0.35f;
		droneDiving = false;
		effectiveFireRate = Mathf.Max(0.25f, friendData.fireRate);
		if ((mode & FriendDeployMode.Drone) != FriendDeployMode.None)
		{
			Vector3 position = ((Component)this).transform.position;
			position.y += 2.4f;
			((Component)this).transform.position = position;
		}
		FriendDeployTracker.Register(this);
		FriendDebugVisual.Attach(((Component)this).transform, FriendDeployModeUtil.GetDebugColor(mode), ((mode & FriendDeployMode.Drone) != FriendDeployMode.None) ? 0.55f : 0.75f);
		if (friendData.shootToDetonate)
		{
			hitProxy = ((Component)this).gameObject.AddComponent<SphereCollider>();
			((Collider)hitProxy).isTrigger = true;
			hitProxy.radius = 0.9f;
			hitProxy.center = Vector3.up * 0.4f;
		}
		if (gear != null)
		{
			FriendCombatHooks.EnsureBound(gear, friendData);
		}
		FriendCombatRunner.Ensure();
		ManualLogSource logger = FriendinaBoxPlugin.Logger;
		if (logger != null)
		{
			logger.LogInfo((object)($"[FriendinaBox] {FriendDeployModeUtil.GetLabel(mode)} armed at {((Component)this).transform.position} " + $"duration={remainingDuration:0.#}s detect={detectRadius:0.##} explode={this.explosionRadius:0.##}"));
		}
	}

	public void ExtendDuration(float seconds)
	{
		if (!finished && !(seconds <= 0f))
		{
			remainingDuration += seconds;
			maxDuration = Mathf.Max(maxDuration, remainingDuration);
			ManualLogSource logger = FriendinaBoxPlugin.Logger;
			if (logger != null)
			{
				logger.LogInfo((object)$"[FriendinaBox] Duration +{seconds:0.#}s → {remainingDuration:0.#}s remaining");
			}
		}
	}

	public void DetonateFromPlayerShot()
	{
		if (!finished)
		{
			float num = ((maxDuration > 0.01f) ? Mathf.Clamp01(remainingDuration / maxDuration) : 1f);
			float num2 = Mathf.Lerp(0.35f, 1f, num);
			explosionRadius *= num2;
			damage.damage *= num2;
			damage.effectAmount *= num2;
			ManualLogSource logger = FriendinaBoxPlugin.Logger;
			if (logger != null)
			{
				logger.LogInfo((object)$"[FriendinaBox] Scuttle shot detonate power={num2:P0} remaining={remainingDuration:0.#}s");
			}
			Detonate("player shot");
		}
	}

	private void OnDestroy()
	{
		FriendDeployTracker.Unregister(this);
	}

	public void ForceDespawn()
	{
		//IL_0031: Unknown result type (might be due to invalid IL or missing references)
		if (!finished)
		{
			finished = true;
			ManualLogSource logger = FriendinaBoxPlugin.Logger;
			if (logger != null)
			{
				logger.LogInfo((object)$"[FriendinaBox] {FriendDeployModeUtil.GetLabel(mode)} force-despawned at {((Component)this).transform.position}");
			}
			FriendDeployTracker.Unregister(this);
			Object.Destroy((Object)(object)((Component)this).gameObject);
		}
	}

	private void Update()
	{
		if (finished)
		{
			return;
		}
		remainingDuration -= Time.deltaTime;
		if (remainingDuration <= 0f)
		{
			Expire();
			return;
		}
		switch (FriendDeployModeUtil.GetPrimary(mode))
		{
		case FriendDeployMode.None:
			TickMine();
			break;
		case FriendDeployMode.Turret:
			TickTurret();
			break;
		case FriendDeployMode.Mortar:
			TickMortar();
			break;
		case FriendDeployMode.Drone:
			TickDrone();
			break;
		case FriendDeployMode.Turret | FriendDeployMode.Mortar:
			break;
		}
	}

	private void TickMine()
	{
		if (!(Time.time < nextScanTime))
		{
			nextScanTime = Time.time + 0.1f;
			if (TryFindEnemy(detectRadius, out var _))
			{
				Detonate("proximity");
			}
		}
	}

	private float GetTurretRange()
	{
		return Mathf.Max(detectRadius, 50f);
	}

	private float GetMortarRange()
	{
		return Mathf.Max(detectRadius * Mathf.Max(1f, friendData.mortarRangeMultiplier), 50f);
	}

	private void TickTurret()
	{
		if (!(Time.time < nextFireTime))
		{
			float num = 1f / Mathf.Max(0.25f, effectiveFireRate);
			if (!TryFindEnemy(GetTurretRange(), out var found))
			{
				nextFireTime = Time.time + 0.1f;
				return;
			}
			nextFireTime = Time.time + num;
			FireAtTarget(found, lobbed: false);
		}
	}

	private void TickMortar()
	{
		if (!(Time.time < nextFireTime))
		{
			float num = 1f / Mathf.Max(0.15f, effectiveFireRate * 0.55f);
			if (!TryFindEnemy(GetMortarRange(), out var found))
			{
				nextFireTime = Time.time + 0.1f;
				return;
			}
			nextFireTime = Time.time + num;
			FireAtTarget(found, lobbed: true);
		}
	}

	private void TickDrone()
	{
		//IL_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_026b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0271: Unknown result type (might be due to invalid IL or missing references)
		//IL_027d: Unknown result type (might be due to invalid IL or missing references)
		//IL_028d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0293: Unknown result type (might be due to invalid IL or missing references)
		//IL_0298: Unknown result type (might be due to invalid IL or missing references)
		//IL_029d: 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_0076: Unknown result type (might be due to invalid IL or missing references)
		//IL_0214: Unknown result type (might be due to invalid IL or missing references)
		//IL_0219: Unknown result type (might be due to invalid IL or missing references)
		//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b9: Unknown result type (might be due to invalid IL or missing references)
		//IL_00be: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: 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_00d4: 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)
		//IL_00de: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
		//IL_010a: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0119: Unknown result type (might be due to invalid IL or missing references)
		//IL_022e: Unknown result type (might be due to invalid IL or missing references)
		if (!droneDiving && (Object)(object)ownerPlayer != (Object)null)
		{
			Vector3 interpolatedPosition = ownerPlayer.InterpolatedPosition;
			FriendDeployTracker.GetDroneFormationSlot(this, out var index, out var count);
			float num = Mathf.Clamp(friendData.droneFollowDistance, 0.75f, 1.6f);
			if (count > 1)
			{
				num = Mathf.Max(num, 1.1f + 0.15f * (float)(count - 1));
			}
			Vector3 droneFormationOffset = FriendDeployTracker.GetDroneFormationOffset(index, count, num);
			float num2 = ((count > 1) ? (((float)index - (float)(count - 1) * 0.5f) * 0.2f) : 0f);
			Vector3 val = Quaternion.Euler(0f, ((Component)ownerPlayer).transform.eulerAngles.y, 0f) * droneFormationOffset;
			Vector3 val2 = interpolatedPosition + Vector3.up * (2.4f + num2) + val;
			float num3 = Mathf.Max(1f, friendData.droneMoveSpeed);
			((Component)this).transform.position = Vector3.MoveTowards(((Component)this).transform.position, val2, num3 * Time.deltaTime);
		}
		bool flag = (mode & FriendDeployMode.Turret) != 0;
		bool flag2 = (mode & FriendDeployMode.Mortar) != 0;
		if (flag || flag2)
		{
			if (Time.time >= nextFireTime)
			{
				float radius = (flag2 ? GetMortarRange() : GetTurretRange());
				if (TryFindEnemy(radius, out var found))
				{
					float num4 = effectiveFireRate * ((flag2 && !flag) ? 0.55f : 1f);
					nextFireTime = Time.time + 1f / Mathf.Max(0.25f, num4);
					FireAtTarget(found, flag2 && !flag);
				}
				else
				{
					nextFireTime = Time.time + 0.1f;
				}
			}
		}
		else if (!droneDiving)
		{
			if (Time.time < nextScanTime)
			{
				return;
			}
			nextScanTime = Time.time + 0.1f;
			if (TryFindEnemy(detectRadius * 1.5f, out var found2))
			{
				droneDiving = true;
				droneDiveTarget = ((IFollowable)found2).GetHealthbarPosition();
				ManualLogSource logger = FriendinaBoxPlugin.Logger;
				if (logger != null)
				{
					logger.LogInfo((object)$"[FriendinaBox] Drone diving toward {droneDiveTarget}");
				}
			}
		}
		else
		{
			float num5 = Mathf.Max(1f, friendData.droneMoveSpeed * 1.75f);
			((Component)this).transform.position = Vector3.MoveTowards(((Component)this).transform.position, droneDiveTarget, num5 * Time.deltaTime);
			Vector3 val3 = ((Component)this).transform.position - droneDiveTarget;
			if (((Vector3)(ref val3)).sqrMagnitude <= 0.122499995f)
			{
				Detonate("suicide dive");
			}
		}
	}

	private void FireAtTarget(ITarget target, bool lobbed)
	{
		//IL_0036: Unknown result type (might be due to invalid IL or missing references)
		//IL_003b: 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_004a: 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_0051: Unknown result type (might be due to invalid IL or missing references)
		//IL_0056: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_0058: Unknown result type (might be due to invalid IL or missing references)
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_005e: Unknown result type (might be due to invalid IL or missing references)
		//IL_02cb: 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_007c: 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_0083: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: 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_00cc: Unknown result type (might be due to invalid IL or missing references)
		//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cd: Unknown result type (might be due to invalid IL or missing references)
		//IL_01cf: Unknown result type (might be due to invalid IL or missing references)
		//IL_01d5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0146: Unknown result type (might be due to invalid IL or missing references)
		//IL_0149: Invalid comparison between Unknown and I4
		//IL_0293: Unknown result type (might be due to invalid IL or missing references)
		//IL_014b: 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_0213: Unknown result type (might be due to invalid IL or missing references)
		//IL_0215: Unknown result type (might be due to invalid IL or missing references)
		//IL_0257: Unknown result type (might be due to invalid IL or missing references)
		//IL_0259: Unknown result type (might be due to invalid IL or missing references)
		//IL_025e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0263: Unknown result type (might be due to invalid IL or missing references)
		//IL_0267: Unknown result type (might be due to invalid IL or missing references)
		//IL_0268: Unknown result type (might be due to invalid IL or missing references)
		//IL_026a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0270: Unknown result type (might be due to invalid IL or missing references)
		//IL_0240: Unknown result type (might be due to invalid IL or missing references)
		//IL_024b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0250: Unknown result type (might be due to invalid IL or missing references)
		//IL_0255: Unknown result type (might be due to invalid IL or missing references)
		if (target == null)
		{
			return;
		}
		IGear obj = ownerGear;
		IDamageSource val = (IDamageSource)(object)((obj is IDamageSource) ? obj : null);
		if (val == null)
		{
			FireExplosionFallback(target, lobbed);
			return;
		}
		Vector3 val2 = ((Component)this).transform.position + Vector3.up * 0.35f;
		Vector3 healthbarPosition = ((IFollowable)target).GetHealthbarPosition();
		Vector3 val3 = healthbarPosition - val2;
		float magnitude = ((Vector3)(ref val3)).magnitude;
		if (magnitude < 0.05f)
		{
			return;
		}
		Vector3 val4 = val3 / magnitude;
		Quaternion val5 = Quaternion.LookRotation(val4, Vector3.up);
		float num = Mathf.Max(0.05f, friendData.modeDamageScale);
		float num2 = damage.damage * num;
		float num3 = damage.effectAmount * num;
		EffectType val6 = damage.effect;
		int num4 = 1;
		if (friendData.copyPlayerWeaponStats && friendData.weaponStatPortion > 0f && TryGetPrimaryWeaponStats(out var primaryDamage, out var primaryEffect, out var primaryEffectAmount, out var primaryFireInterval, out var primaryBulletsPerShot))
		{
			float num5 = Mathf.Clamp01(friendData.weaponStatPortion);
			num2 = Mathf.Lerp(num2, primaryDamage * num, num5);
			num3 = Mathf.Lerp(num3, primaryEffectAmount * num, num5);
			if (num5 >= 0.25f && (int)primaryEffect > 0)
			{
				val6 = primaryEffect;
			}
			if (primaryFireInterval > 0.01f)
			{
				float num6 = 1f / primaryFireInterval;
				float num7 = Mathf.Lerp(Mathf.Max(0.25f, friendData.fireRate), num6, num5 * 0.65f);
				effectiveFireRate = Mathf.Clamp(num7, 0.5f, 12f);
			}
			if (!lobbed && primaryBulletsPerShot > 1)
			{
				num4 = Mathf.Clamp(Mathf.RoundToInt(Mathf.Lerp(1f, (float)primaryBulletsPerShot, num5)), 1, 8);
			}
		}
		try
		{
			bool flag;
			if (lobbed)
			{
				flag = TryFireMortar(val, val2, val5, val4, healthbarPosition, target, num2, num3, val6);
			}
			else
			{
				flag = false;
				float num8 = ((num4 > 1) ? Mathf.Lerp(1.2f, 4.5f, (float)(num4 - 1) / 7f) : 0f);
				for (int i = 0; i < num4; i++)
				{
					Quaternion val7 = val5;
					if (num4 > 1)
					{
						float num9 = Random.Range(0f - num8, num8);
						float num10 = Random.Range((0f - num8) * 0.65f, num8 * 0.65f);
						val7 = val5 * Quaternion.Euler(num10, num9, 0f);
					}
					Vector3 dir = val7 * Vector3.forward;
					if (TryFireRail(val, val2, val7, dir, num2, num3, val6))
					{
						flag = true;
					}
				}
			}
			if (!flag)
			{
				FireExplosionFallback(target, lobbed, num2, num3, val6);
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = FriendinaBoxPlugin.Logger;
			if (logger != null)
			{
				logger.LogWarning((object)("[FriendinaBox] Mode fire failed: " + ex.Message));
			}
			FireExplosionFallback(target, lobbed, num2, num3, val6);
		}
	}

	private bool TryGetPrimaryWeaponStats(out float primaryDamage, out EffectType primaryEffect, out float primaryEffectAmount, out float primaryFireInterval, out int primaryBulletsPerShot)
	{
		//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c5: Expected I4, but got Unknown
		primaryDamage = 0f;
		primaryEffect = (EffectType)0;
		primaryEffectAmount = 0f;
		primaryFireInterval = 0f;
		primaryBulletsPerShot = 1;
		Player val = (((Object)(object)ownerPlayer != (Object)null) ? ownerPlayer : Player.LocalPlayer);
		if (((val != null) ? val.Gear : null) == null)
		{
			return false;
		}
		for (int i = 0; i < val.Gear.Length; i++)
		{
			IGear val2 = val.Gear[i];
			if (val2 != null && !(val2 is Throwable) && (!((Object)(object)((IUpgradable)val2).Info != (Object)null) || (!(((IUpgradable)val2).Info.APIName == "friend_in_a_box") && ((IUpgradable)val2).Info.ID != 92100)))
			{
				IWeapon val3 = (IWeapon)(object)((val2 is IWeapon) ? val2 : null);
				if (val3 != null)
				{
					ref GunData gunData = ref val3.GunData;
					primaryDamage = gunData.damage;
					primaryEffect = (EffectType)(int)gunData.damageEffect;
					primaryEffectAmount = gunData.damageEffectAmount;
					primaryFireInterval = gunData.fireInterval;
					primaryBulletsPerShot = Mathf.Max(1, gunData.bulletsPerShot);
					return primaryDamage > 0f;
				}
			}
		}
		return false;
	}

	private bool TryFireRail(IDamageSource source, Vector3 origin, Quaternion rot, Vector3 dir, float shotDamage, float shotEffect, EffectType shotEffectType)
	{
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_005a: Unknown result type (might be due to invalid IL or missing references)
		//IL_005b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Unknown result type (might be due to invalid IL or missing references)
		//IL_006a: Unknown result type (might be due to invalid IL or missing references)
		//IL_006c: 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_0096: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00f5: Unknown result type (might be due to invalid IL or missing references)
		//IL_0112: Unknown result type (might be due to invalid IL or missing references)
		//IL_0114: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: Unknown result type (might be due to invalid IL or missing references)
		//IL_0128: Unknown result type (might be due to invalid IL or missing references)
		//IL_012c: Unknown result type (might be due to invalid IL or missing references)
		GameObject prefab = FriendBulletCache.RailPrefab;
		if ((Object)(object)prefab == (Object)null)
		{
			return false;
		}
		GameObject val = SimplePool.Get(prefab);
		if ((Object)(object)val == (Object)null)
		{
			return false;
		}
		IBullet component = val.GetComponent<IBullet>();
		if (component == null)
		{
			SimplePool.Release(prefab, val);
			return false;
		}
		BulletData val2 = new BulletData
		{
			position = origin,
			rotation = rot,
			direction = dir,
			gravity = 0f,
			speed = 400f,
			damage = shotDamage,
			damageEffect = shotEffectType,
			damageEffectAmount = shotEffect,
			damageFlags = damage.damageFlags,
			maxBounces = 0,
			surfaceCollisionMask = 10241,
			targetCollisionMask = 345216,
			surfaceMagnetism = 0f,
			targetMagnetism = 0f,
			range = LongRangeData,
			force = 0f,
			impactSize = 1f
		};
		BulletFlags val3 = (BulletFlags)73;
		Action<IBullet> action = delegate(IBullet b)
		{
			Component val4 = (Component)(object)((b is Component) ? b : null);
			if (val4 != null && (Object)(object)val4 != (Object)null)
			{
				SimplePool.Release(prefab, val4.gameObject);
			}
		};
		component.Initialize(val2, source, action, val3);
		return true;
	}

	private bool TryFireMortar(IDamageSource source, Vector3 origin, Quaternion rot, Vector3 dir, Vector3 aimPoint, ITarget target, float shotDamage, float shotEffect, EffectType shotEffectType)
	{
		//IL_0069: 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_0072: Unknown result type (might be due to invalid IL or missing references)
		//IL_0079: Unknown result type (might be due to invalid IL or missing references)
		//IL_007a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0081: Unknown result type (might be due to invalid IL or missing references)
		//IL_0083: 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_00ad: Unknown result type (might be due to invalid IL or missing references)
		//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
		//IL_00cb: Unknown result type (might be due to invalid IL or missing references)
		//IL_010a: Unknown result type (might be due to invalid IL or missing references)
		//IL_010f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0128: Unknown result type (might be due to invalid IL or missing references)
		//IL_012a: Unknown result type (might be due to invalid IL or missing references)
		//IL_012e: Unknown result type (might be due to invalid IL or missing references)
		//IL_013f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0144: Unknown result type (might be due to invalid IL or missing references)
		//IL_015b: Unknown result type (might be due to invalid IL or missing references)
		GameObject prefab = FriendBulletCache.MortarPrefab;
		if ((Object)(object)prefab == (Object)null)
		{
			return false;
		}
		GameObject val = SimplePool.Get(prefab);
		if ((Object)(object)val == (Object)null)
		{
			return false;
		}
		IBullet component = val.GetComponent<IBullet>();
		if (component == null)
		{
			SimplePool.Release(prefab, val);
			return false;
		}
		float force = Mathf.Max(0.75f, explosionRadius * 0.65f);
		BulletData val2 = new BulletData
		{
			position = origin,
			rotation = rot,
			direction = dir,
			gravity = 18f,
			speed = 28f,
			damage = shotDamage,
			damageEffect = shotEffectType,
			damageEffectAmount = shotEffect,
			damageFlags = (DamageFlags)(damage.damageFlags | 0x10),
			maxBounces = 0,
			surfaceCollisionMask = 10241,
			targetCollisionMask = 345216,
			surfaceMagnetism = 0f,
			targetMagnetism = 1f,
			range = LongRangeData,
			force = force,
			impactSize = 1f
		};
		BulletFlags val3 = (BulletFlags)73;
		Action<IBullet> action = delegate(IBullet b)
		{
			Component val5 = (Component)(object)((b is Component) ? b : null);
			if (val5 != null && (Object)(object)val5 != (Object)null)
			{
				SimplePool.Release(prefab, val5.gameObject);
			}
		};
		component.Initialize(val2, source, action, val3);
		RocketSalvoBullet val4 = (RocketSalvoBullet)(object)((component is RocketSalvoBullet) ? component : null);
		if (val4 != null)
		{
			val4.SetTarget(target, aimPoint, -1f);
		}
		return true;
	}

	private void FireExplosionFallback(ITarget target, bool lobbed, float shotDamageOverride = -1f, float shotEffectOverride = -1f, EffectType? shotEffectTypeOverride = null)
	{
		//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_007e: 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_0083: Unknown result type (might be due to invalid IL or missing references)
		//IL_0088: 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_00dc: Unknown result type (might be due to invalid IL or missing references)
		//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
		//IL_011e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0122: Unknown result type (might be due to invalid IL or missing references)
		if (target == null || (Object)(object)GameManager.Instance == (Object)null)
		{
			return;
		}
		Vector3 healthbarPosition = ((IFollowable)target).GetHealthbarPosition();
		float num = Mathf.Max(0.05f, friendData.modeDamageScale);
		float num2 = ((shotDamageOverride >= 0f) ? shotDamageOverride : (damage.damage * num));
		float num3 = ((shotEffectOverride >= 0f) ? shotEffectOverride : (damage.effectAmount * num));
		EffectType val = (EffectType)(((??)shotEffectTypeOverride) ?? damage.effect);
		DamageData val2 = default(DamageData);
		((DamageData)(ref val2))..ctor(num2, val, num3, damage.damageFlags);
		float num4 = (lobbed ? (explosionRadius * 0.65f) : Mathf.Max(0.4f, explosionRadius * 0.2f));
		try
		{
			IGear obj = ownerGear;
			Throwable val3 = (Throwable)(object)((obj is Throwable) ? obj : null);
			if (val3 != null)
			{
				GameManager.Instance.SpawnExplosionFirstPerson((IDamageSource)(object)val3, healthbarPosition, num4, (TargetType)5, val2, lobbed ? (explosionShake * 0.35f) : (explosionShake * 0.15f));
				return;
			}
			IGear obj2 = ownerGear;
			IDamageSource val4 = (IDamageSource)(object)((obj2 is IDamageSource) ? obj2 : null);
			if (val4 != null)
			{
				GameManager.Instance.SpawnExplosionFirstPerson(val4, healthbarPosition, num4, (TargetType)5, val2, lobbed ? (explosionShake * 0.35f) : (explosionShake * 0.15f));
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = FriendinaBoxPlugin.Logger;
			if (logger != null)
			{
				logger.LogWarning((object)("[FriendinaBox] Fallback fire failed: " + ex.Message));
			}
		}
	}

	private bool TryFindEnemy(float radius, out ITarget found)
	{
		//IL_000f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_0093: 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_003d: 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_0045: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
		//IL_00d5: 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_00db: Unknown result type (might be due to invalid IL or missing references)
		//IL_0066: Unknown result type (might be due to invalid IL or missing references)
		//IL_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)
		found = null;
		float num = float.MaxValue;
		Vector3 position = ((Component)this).transform.position;
		if (friendData.sharePlayerTarget && FriendSharedTarget.TryGet(position, radius, out var target) && IsValidEnemy(target))
		{
			found = target;
			return true;
		}
		Vector3 val2;
		try
		{
			TargetEnumerator val = default(TargetEnumerator);
			try
			{
				if (((TargetEnumerator)(ref val)).GetTargetsInSphere(position, radius, -1, (TargetType)1))
				{
					while (((TargetEnumerator)(ref val)).MoveNext())
					{
						ITarget current = ((TargetEnumerator)(ref val)).Current;
						if (IsValidEnemy(current))
						{
							val2 = ((IFollowable)current).GetHealthbarPosition() - position;
							float sqrMagnitude = ((Vector3)(ref val2)).sqrMagnitude;
							if (sqrMagnitude < num)
							{
								num = sqrMagnitude;
								found = current;
							}
						}
					}
				}
			}
			finally
			{
				((IDisposable)val).Dispose();
			}
		}
		catch
		{
		}
		if (found != null)
		{
			return true;
		}
		Collider[] array = Physics.OverlapSphere(position, radius, -1, (QueryTriggerInteraction)2);
		for (int i = 0; i < array.Length; i++)
		{
			ITarget target2 = IDamageSource.GetTarget((Component)(object)array[i]);
			if (IsValidEnemy(target2))
			{
				val2 = ((IFollowable)target2).GetHealthbarPosition() - position;
				float sqrMagnitude2 = ((Vector3)(ref val2)).sqrMagnitude;
				if (sqrMagnitude2 < num)
				{
					num = sqrMagnitude2;
					found = target2;
				}
			}
		}
		return found != null;
	}

	private static bool IsValidEnemy(ITarget target)
	{
		//IL_0018: 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_0022: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Invalid comparison between Unknown and I4
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		if (target == null || !target.IsAlive)
		{
			return false;
		}
		if (target.IsPlayer())
		{
			return false;
		}
		if ((((IFollowable)target).Type & 1) != 0 || (int)((IFollowable)target).Type == 1)
		{
			return true;
		}
		if ((((IFollowable)target).Type & 2) == 0 && target.MaxHealth > 0f)
		{
			return true;
		}
		return false;
	}

	private void Detonate(string reason)
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0038: 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_0074: Unknown result type (might be due to invalid IL or missing references)
		//IL_00a8: 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)
		if (finished)
		{
			return;
		}
		finished = true;
		Vector3 position = ((Component)this).transform.position;
		ManualLogSource logger = FriendinaBoxPlugin.Logger;
		if (logger != null)
		{
			logger.LogInfo((object)$"[FriendinaBox] {FriendDeployModeUtil.GetLabel(mode)} detonated ({reason}) at {position}");
		}
		try
		{
			if (!((Object)(object)GameManager.Instance != (Object)null))
			{
				goto IL_0086;
			}
			IGear obj = ownerGear;
			Throwable val = (Throwable)(object)((obj is Throwable) ? obj : null);
			if (val == null)
			{
				goto IL_0086;
			}
			GameManager.Instance.SpawnExplosionFirstPerson((IDamageSource)(object)val, position, explosionRadius, (TargetType)5, damage, explosionShake);
			goto end_IL_0049;
			IL_0086:
			if ((Object)(object)GameManager.Instance != (Object)null)
			{
				IGear obj2 = ownerGear;
				IDamageSource val2 = (IDamageSource)(object)((obj2 is IDamageSource) ? obj2 : null);
				if (val2 != null)
				{
					GameManager.Instance.SpawnExplosionFirstPerson(val2, position, explosionRadius, (TargetType)5, damage, explosionShake);
				}
			}
			end_IL_0049:;
		}
		catch (Exception arg)
		{
			ManualLogSource logger2 = FriendinaBoxPlugin.Logger;
			if (logger2 != null)
			{
				logger2.LogError((object)$"[FriendinaBox] Detonate failed: {arg}");
			}
		}
		FriendDeployTracker.Unregister(this);
		Object.Destroy((Object)(object)((Component)this).gameObject);
	}

	private void Expire()
	{
		//IL_0016: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0037: 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)
		if (!finished)
		{
			finished = true;
			Vector3 position = ((Component)this).transform.position;
			ManualLogSource logger = FriendinaBoxPlugin.Logger;
			if (logger != null)
			{
				logger.LogInfo((object)$"[FriendinaBox] {FriendDeployModeUtil.GetLabel(mode)} expired at {position}");
			}
			TrySpawnAcidPuddle(position);
			TryGrantExpireMoveSpeed();
			FriendDeployTracker.Unregister(this);
			Object.Destroy((Object)(object)((Component)this).gameObject);
		}
	}

	private void TrySpawnAcidPuddle(Vector3 pos)
	{
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: Unknown result type (might be due to invalid IL or missing references)
		//IL_005d: 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)
		if (friendData.acidPuddleOnExpireDuration <= 0f)
		{
			return;
		}
		try
		{
			if (!((Object)(object)GameManager.Instance == (Object)null) && ownerGear is IDamageSource)
			{
				float num = Mathf.Max(1f, explosionRadius * 0.85f);
				IGear obj = ownerGear;
				NetworkBehaviourReference networkRef = IDamageSource.GetNetworkRef((IDamageSource)(object)((obj is IDamageSource) ? obj : null));
				GameManager.Instance.SpawnAcidPuddle_Rpc(networkRef, pos, friendData.acidPuddleOnExpireDuration, (TargetType)5, num);
			}
		}
		catch (Exception ex)
		{
			ManualLogSource logger = FriendinaBoxPlugin.Logger;
			if (logger != null)
			{
				logger.LogWarning((object)("[FriendinaBox] Acid puddle spawn failed: " + ex.Message));
			}
		}
	}

	private void TryGrantExpireMoveSpeed()
	{
		if (!(friendData.expireMoveSpeedBonus <= 0f) && !(friendData.expireMoveSpeedDuration <= 0f))
		{
			Player val = (((Object)(object)ownerPlayer != (Object)null) ? ownerPlayer : Player.LocalPlayer);
			if (!((Object)(object)val == (Object)null) && ((NetworkBehaviour)val).IsLocalPlayer)
			{
				FriendExpireSpeedBuff.Apply(val, friendData.expireMoveSpeedBonus, friendData.expireMoveSpeedDuration);
			}
		}
	}
}
[Flags]
public enum FriendDeployMode : byte
{
	None = 0,
	Turret = 1,
	Mortar = 2,
	Drone = 4
}
public static class FriendDeployModeUtil
{
	public static FriendDeployMode GetPrimary(FriendDeployMode flags)
	{
		if ((flags & FriendDeployMode.Drone) != FriendDeployMode.None)
		{
			return FriendDeployMode.Drone;
		}
		if ((flags & FriendDeployMode.Mortar) != FriendDeployMode.None)
		{
			return FriendDeployMode.Mortar;
		}
		if ((flags & FriendDeployMode.Turret) != FriendDeployMode.None)
		{
			return FriendDeployMode.Turret;
		}
		return FriendDeployMode.None;
	}

	public static string GetLabel(FriendDeployMode flags)
	{
		switch (GetPrimary(flags))
		{
		case FriendDeployMode.Drone:
			if ((flags & FriendDeployMode.Turret) != FriendDeployMode.None && (flags & FriendDeployMode.Mortar) != FriendDeployMode.None)
			{
				return "Drone+Turret+Mortar";
			}
			if ((flags & FriendDeployMode.Turret) != FriendDeployMode.None)
			{
				return "Drone+Turret";
			}
			if ((flags & FriendDeployMode.Mortar) != FriendDeployMode.None)
			{
				return "Drone+Mortar";
			}
			return "Drone";
		case FriendDeployMode.Mortar:
			return "Mortar";
		case FriendDeployMode.Turret:
			return "Turret";
		default:
			return "Mine";
		}
	}

	public static Color GetDebugColor(FriendDeployMode flags)
	{
		//IL_005a: 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_0060: 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)
		return (Color)(GetPrimary(flags) switch
		{
			FriendDeployMode.Drone => new Color(0.2f, 0.85f, 1f), 
			FriendDeployMode.Mortar => new Color(1f, 0.55f, 0.1f), 
			FriendDeployMode.Turret => new Color(0.3f, 1f, 0.35f), 
			_ => Color.magenta, 
		});
	}
}
public static class FriendDeployTracker
{
	private static readonly List<FriendDeployable> Active = new List<FriendDeployable>();

	public static int MaxConcurrentDeploys { get; set; } = 1;

	public static int ActiveCount
	{
		get
		{
			PruneNulls();
			return Active.Count;
		}
	}

	public static bool HasActive => ActiveCount > 0;

	public static void Register(FriendDeployable deployable)
	{
		//IL_0061: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)deployable == (Object)null)
		{
			return;
		}
		PruneNulls();
		if (Active.Contains(deployable))
		{
			return;
		}
		int num = Mathf.Max(1, MaxConcurrentDeploys);
		while (Active.Count >= num)
		{
			FriendDeployable friendDeployable = Active[0];
			Active.RemoveAt(0);
			if ((Object)(object)friendDeployable != (Object)null)
			{
				ManualLogSource logger = FriendinaBoxPlugin.Logger;
				if (logger != null)
				{
					logger.LogInfo((object)$"[FriendinaBox] Replacing oldest deployable at {((Component)friendDeployable).transform.position} (max concurrent={num}).");
				}
				friendDeployable.ForceDespawn();
			}
		}
		Active.Add(deployable);
	}

	public static void Unregister(FriendDeployable deployable)
	{
		if (!((Object)(object)deployable == (Object)null))
		{
			Active.Remove(deployable);
		}
	}

	public static void Clear()
	{
		for (int num = Active.Count - 1; num >= 0; num--)
		{
			FriendDeployable friendDeployable = Active[num];
			if ((Object)(object)friendDeployable != (Object)null)
			{
				friendDeployable.ForceDespawn();
			}
		}
		Active.Clear();
	}

	public static void ExtendAllDurations(float seconds)
	{
		if (!(seconds <= 0f))
		{
			PruneNulls();
			for (int i = 0; i < Active.Count; i++)
			{
				Active[i]?.ExtendDuration(seconds);
			}
		}
	}

	public static FriendDeployable FindNearest(Vector3 point, float maxDistance)
	{
		//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_0030: Unknown result type (might be due to invalid IL or missing references)
		//IL_0035: Unknown result type (might be due to invalid IL or missing references)
		PruneNulls();
		FriendDeployable result = null;
		float num = maxDistance * maxDistance;
		for (int i = 0; i < Active.Count; i++)
		{
			FriendDeployable friendDeployable = Active[i];
			if (!((Object)(object)friendDeployable == (Object)null))
			{
				Vector3 val = ((Component)friendDeployable).transform.position - point;
				float sqrMagnitude = ((Vector3)(ref val)).sqrMagnitude;
				if (sqrMagnitude <= num)
				{
					num = sqrMagnitude;
					result = friendDeployable;
				}
			}
		}
		return result;
	}

	public static void GetDroneFormationSlot(FriendDeployable self, out int index, out int count)
	{
		index = 0;
		count = 0;
		if ((Object)(object)self == (Object)null)
		{
			return;
		}
		PruneNulls();
		int num = 0;
		for (int i = 0; i < Active.Count; i++)
		{
			FriendDeployable friendDeployable = Active[i];
			if (!((Object)(object)friendDeployable == (Object)null) && (friendDeployable.Mode & FriendDeployMode.Drone) != FriendDeployMode.None)
			{
				if ((Object)(object)friendDeployable == (Object)(object)self)
				{
					num = count;
				}
				count++;
			}
		}
		index = num;
		if (count < 1)
		{
			count = 1;
		}
	}

	public static void GetAlliesInRadius(Vector3 origin, float radius, List<FriendDeployable> buffer)
	{
		//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_003d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0042: Unknown result type (might be due to invalid IL or missing references)
		buffer.Clear();
		if (radius <= 0f)
		{
			return;
		}
		PruneNulls();
		float num = radius * radius;
		for (int i = 0; i < Active.Count; i++)
		{
			FriendDeployable friendDeployable = Active[i];
			if (!((Object)(object)friendDeployable == (Object)null))
			{
				Vector3 val = ((Component)friendDeployable).transform.position - origin;
				if (((Vector3)(ref val)).sqrMagnitude <= num)
				{
					buffer.Add(friendDeployable);
				}
			}
		}
	}

	public static void GetDronesInRadius(Vector3 origin, float radius, List<FriendDeployable> buffer)
	{
		//IL_0000: Unknown result type (might be due to invalid IL or missing references)
		GetAlliesInRadius(origin, radius, buffer);
	}

	public static Vector3 GetDroneFormationOffset(int index, int count, float radius)
	{
		//IL_005b: 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)
		count = Mathf.Max(1, count);
		radius = Mathf.Max(0.5f, radius);
		if (count == 1)
		{
			return new Vector3(0.45f, 0f, 0f - radius);
		}
		float num = (float)index / (float)count * MathF.PI * 2f + MathF.PI;
		float num2 = Mathf.Sin(num) * radius;
		float num3 = Mathf.Cos(num) * radius;
		return new Vector3(num2, 0f, num3);
	}

	private static void PruneNulls()
	{
		for (int num = Active.Count - 1; num >= 0; num--)
		{
			if ((Object)(object)Active[num] == (Object)null)
			{
				Active.RemoveAt(num);
			}
		}
	}
}
public sealed class FriendExpireSpeedBuff : MonoBehaviour
{
	private Player player;

	private float bonus;

	private float remaining;

	private RefAction<float> onSetSpeed;

	private bool ended;

	public static void Apply(Player target, float speedBonus, float duration)
	{
		if (!((Object)(object)target == (Object)null) && !(speedBonus <= 0f) && !(duration <= 0f))
		{
			FriendExpireSpeedBuff component = ((Component)target).GetComponent<FriendExpireSpeedBuff>();
			if ((Object)(object)component != (Object)null)
			{
				component.bonus = Mathf.Max(component.bonus, speedBonus);
				component.remaining = Mathf.Max(component.remaining, duration);
			}
			else
			{
				((Component)target).gameObject.AddComponent<FriendExpireSpeedBuff>().StartBuff(target, speedBonus, duration);
			}
		}
	}

	private void StartBuff(Player target, float speedBonus, float duration)
	{
		//IL_002e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0039: Unknown result type (might be due to invalid IL or missing references)
		//IL_003e: Unknown result type (might be due to invalid IL or missing references)
		player = target;
		bonus = speedBonus;
		remaining = duration;
		onSetSpeed = ModifyMoveSpeed;
		Player obj = player;
		obj.OnSetMovementSpeed += onSetSpeed;
		ManualLogSource logger = FriendinaBoxPlugin.Logger;
		if (logger != null)
		{
			logger.LogInfo((object)$"[FriendinaBox] Expire speed buff +{bonus:P0} for {duration:0.#}s");
		}
	}

	private void ModifyMoveSpeed(ref float speed)
	{
		speed *= 1f + bonus;
	}

	private void Update()
	{
		remaining -= Time.deltaTime;
		if (remaining <= 0f)
		{
			EndBuff();
		}
	}

	private void OnDestroy()
	{
		EndBuff();
	}

	private void EndBuff()
	{
		//IL_002d: 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)
		if (ended)
		{
			return;
		}
		ended = true;
		if ((Object)(object)player != (Object)null && onSetSpeed != null)
		{
			try
			{
				Player obj = player;
				obj.OnSetMovementSpeed -= onSetSpeed;
			}
			catch
			{
			}
		}
		player = null;
		onSetSpeed = null;
		if ((Object)(object)this != (Object)null)
		{
			Object.Destroy((Object)(object)this);
		}
	}
}
[HarmonyPatch(typeof(GrenadeBullet), "Detonate")]
internal static class FriendGrenadeHooks
{
	[HarmonyPrefix]
	private static bool Prefix(GrenadeBullet __instance)
	{
		//IL_0087: Unknown result type (might be due to invalid IL or missing references)
		//IL_0077: Unknown result type (might be due to invalid IL or missing references)
		//IL_007d: Unknown result type (might be due to invalid IL or missing references)
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
		//IL_0102: Unknown result type (might be due to invalid IL or missing references)
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_015f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0164: Unknown result type (might be due to invalid IL or missing references)
		//IL_0169: Unknown result type (might be due to invalid IL or missing references)
		//IL_0178: Unknown result type (might be due to invalid IL or missing references)
		try
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return true;
			}
			IDamageSource parentSource = ((SimpleProjectileBullet)__instance).ParentSource;
			IGear val = (IGear)(object)((parentSource is IGear) ? parentSource : null);
			if (val == null)
			{
				return true;
			}
			if (!FriendinaBoxBehaviour.TryGet(val, out var behaviour))
			{
				return true;
			}
			Throwable val2 = (Throwable)(object)((val is Throwable) ? val : null);
			if (val2 != null && ((Object)(object)val2.Player == (Object)null || !((NetworkBehaviour)val2.Player).IsLocalPlayer))
			{
				return false;
			}
			Vector3 pos = (Vector3)(((Object)(object)((Component)__instance).transform != (Object)null) ? ((Component)__instance).transform.position : default(Vector3));
			TryReadBulletPosition(__instance, ref pos);
			ref FriendinaBoxBehaviour.Data grenadeData = ref behaviour.GrenadeData;
			IWeapon val3 = (IWeapon)(object)((val is IWeapon) ? val : null);
			if (val3 == null)
			{
				return true;
			}
			float num = Mathf.Max(0.01f, grenadeData.explosionRadiusMultiplier);
			float explosionRadius = val3.GunData.hitForce * num;
			DamageData damage = default(DamageData);
			((DamageData)(ref damage))..ctor(val3.GunData.damage, val3.GunData.damageEffect, val3.GunData.damageEffectAmount, (DamageFlags)(val3.GunData.damageFlags | 0x10));
			float explosionShake = 12f;
			float selfEffectMultiplier = 1f;
			GrenadeGear val4 = (GrenadeGear)(object)((val is GrenadeGear) ? val : null);
			if (val4 != null)
			{
				explosionShake = val4.ExplosionShake;
				selfEffectMultiplier = val4.SelfEffectMultiplier;
			}
			int num2 = Mathf.Max(1, grenadeData.maxConcurrentDeploys);
			if (num2 > FriendDeployTracker.MaxConcurrentDeploys)
			{
				FriendDeployTracker.MaxConcurrentDeploys = num2;
			}
			for (int i = 0; i < num2; i++)
			{
				FriendDeployable.Spawn(pos + GetSquadSpreadOffset(i, num2), val, grenadeData, explosionRadius, damage, explosionShake, selfEffectMultiplier);
			}
			try
			{
				((SimpleProjectileBullet)__instance).Kill();
			}
			catch
			{
			}
			return false;
		}
		catch (Exception arg)
		{
			ManualLogSource logger = FriendinaBoxPlugin.Logger;
			if (logger != null)
			{
				logger.LogError((object)$"[FriendinaBox] Detonate prefix failed: {arg}");
			}
			return true;
		}
	}

	private static void TryReadBulletPosition(GrenadeBullet bullet, ref Vector3 pos)
	{
		//IL_0043: 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_004a: 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)
		FieldInfo fieldInfo = AccessTools.Field(((object)bullet).GetType(), "positionNext") ?? AccessTools.Field(typeof(SimpleProjectileBullet), "positionNext");
		if (fieldInfo != null && fieldInfo.GetValue(bullet) is Vector3 val)
		{
			pos = val;
		}
	}

	private static Vector3 GetSquadSpreadOffset(int index, int count)
	{
		//IL_0008: 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)
		if (count <= 1 || index <= 0)
		{
			return Vector3.zero;
		}
		float num = Mathf.Clamp(1.6f + 0.35f * (float)(count - 2), 1.4f, 3.2f);
		float num2 = (float)index / (float)count * MathF.PI * 2f + 0.35f;
		float num3 = (float)index * 0.17f % 0.25f;
		float num4 = num + num3;
		return new Vector3(Mathf.Cos(num2) * num4, 0f, Mathf.Sin(num2) * num4);
	}
}
public sealed class FriendinaBoxBehaviour : MonoBehaviour
{
	[Serializable]
	public struct Data
	{
		public float deployDuration;

		public float detectRadius;

		public float detectRadiusMultiplier;

		public float explosionRadiusMultiplier;

		public float acidPuddleOnExpireDuration;

		public float expireMoveSpeedBonus;

		public float expireMoveSpeedDuration;

		public FriendDeployMode deployMode;

		public float fireRate;

		public float modeDamageScale;

		public float mortarRangeMultiplier;

		public float droneFollowDistance;

		public float droneMoveSpeed;

		public int maxConcurrentDeploys;

		public float lifestealFraction;

		public float chargeOnKill;

		public float durationOnKill;

		public float markDamageBonus;

		public float markDuration;

		public bool shootToDetonate;

		public float overhealOnDamaged;

		public float overhealOnDamagedCooldown;

		public bool copyPlayerWeaponStats;

		public float weaponStatPortion;

		public bool sharePlayerTarget;

		public float sharedTargetDuration;

		public bool countsAsSwarmAlly;
	}

	[SerializeField]
	private Data data = CreateDefaultData();

	private Data prefabSnapshot = CreateDefaultData();

	private string description = "Friend in a Box";

	public ref Data GrenadeData => ref data;

	public string Description => description;

	public static Data CreateDefaultData()
	{
		return new Data
		{
			deployDuration = 20f,
			detectRadius = 0f,
			detectRadiusMultiplier = 1f,
			explosionRadiusMultiplier = 1f,
			acidPuddleOnExpireDuration = 0f,
			expireMoveSpeedBonus = 0f,
			expireMoveSpeedDuration = 0f,
			deployMode = FriendDeployMode.None,
			fireRate = 2f,
			modeDamageScale = 0.35f,
			mortarRangeMultiplier = 1f,
			droneFollowDistance = 0.85f,
			droneMoveSpeed = 14f,
			maxConcurrentDeploys = 1,
			lifestealFraction = 0f,
			chargeOnKill = 0f,
			durationOnKill = 0f,
			markDamageBonus = 0f,
			markDuration = 0f,
			shootToDetonate = false,
			overhealOnDamaged = 0f,
			overhealOnDamagedCooldown = 3f,
			copyPlayerWeaponStats = false,
			weaponStatPortion = 0f,
			sharePlayerTarget = false,
			sharedTargetDuration = 0f,
			countsAsSwarmAlly = false
		};
	}

	public void InitializeAsPrefab(string desc)
	{
		description = desc ?? "Friend in a Box";
		data = CreateDefaultData();
		prefabSnapshot = data;
	}

	public void RestoreFromPrefab()
	{
		data = prefabSnapshot;
	}

	public void CapturePrefabSnapshot()
	{
		prefabSnapshot = data;
	}

	public void CopySnapshotFrom(FriendinaBoxBehaviour template)
	{
		if (!((Object)(object)template == (Object)null))
		{
			prefabSnapshot = template.prefabSnapshot;
			data = prefabSnapshot;
			description = template.description;
		}
	}

	public static bool TryGet(IGear gear, out FriendinaBoxBehaviour behaviour)
	{
		behaviour = null;
		if ((Object)(object)((gear != null) ? gear.gameObject : null) == (Object)null)
		{
			return false;
		}
		behaviour = gear.gameObject.GetComponent<FriendinaBoxBehaviour>();
		if ((Object)(object)behaviour != (Object)null)
		{
			return true;
		}
		bool num = (Object)(object)((IUpgradable)gear).Info != (Object)null && ((IUpgradable)gear).Info.APIName == "friend_in_a_box";
		FriendinaBoxBehaviour friendinaBoxBehaviour = null;
		IUpgradable prefab = ((IUpgradable)gear).Prefab;
		Component val = (Component)(object)((prefab is Component) ? prefab : null);
		if (val != null)
		{
			friendinaBoxBehaviour = val.GetComponent<FriendinaBoxBehaviour>();
		}
		if (!num && (Object)(object)friendinaBoxBehaviour == (Object)null)
		{
			return false;
		}
		string desc = (((Object)(object)friendinaBoxBehaviour != (Object)null) ? friendinaBoxBehaviour.Description : "Deployable ally grenade. Lands as a proximity mine. While equipped, enables multiplayer-only upgrades in Ouroboros.");
		behaviour = gear.gameObject.AddComponent<FriendinaBoxBehaviour>();
		behaviour.InitializeAsPrefab(desc);
		if ((Object)(object)friendinaBoxBehaviour != (Object)null)
		{
			behaviour.data = friendinaBoxBehaviour.prefabSnapshot;
		}
		behaviour.CapturePrefabSnapshot();
		return true;
	}

	public static bool IsEquippedOnLocalPlayer()
	{
		Player localPlayer = Player.LocalPlayer;
		if (((localPlayer != null) ? localPlayer.Gear : null) == null)
		{
			return false;
		}
		for (int i = 0; i < localPlayer.Gear.Length; i++)
		{
			IGear val = localPlayer.Gear[i];
			if (val != null)
			{
				if ((Object)(object)((IUpgradable)val).Info != (Object)null && ((IUpgradable)val).Info.APIName == "friend_in_a_box")
				{
					return true;
				}
				if ((Object)(object)((IUpgradable)val).Info != (Object)null && ((IUpgradable)val).Info.ID == 92100)
				{
					return true;
				}
				if ((Object)(object)val.gameObject != (Object)null && (Object)(object)val.gameObject.GetComponent<FriendinaBoxBehaviour>() != (Object)null)
				{
					return true;
				}
			}
		}
		return false;
	}

	public static bool TryGetEquipped(out FriendinaBoxBehaviour behaviour, out IGear gear)
	{
		behaviour = null;
		gear = null;
		Player localPlayer = Player.LocalPlayer;
		if (((localPlayer != null) ? localPlayer.Gear : null) == null)
		{
			return false;
		}
		for (int i = 0; i < localPlayer.Gear.Length; i++)
		{
			IGear val = localPlayer.Gear[i];
			if (val != null && TryGet(val, out var behaviour2))
			{
				if ((Object)(object)((IUpgradable)val).Info != (Object)null && (((IUpgradable)val).Info.APIName == "friend_in_a_box" || ((IUpgradable)val).Info.ID == 92100))
				{
					behaviour = behaviour2;
					gear = val;
					return true;
				}
				if ((Object)(object)behaviour == (Object)null)
				{
					behaviour = behaviour2;
					gear = val;
				}
			}
		}
		return (Object)(object)behaviour != (Object)null;
	}
}
public sealed class FriendMarkedTarget : MonoBehaviour
{
	private static readonly List<FriendMarkedTarget> Active = new List<FriendMarkedTarget>();

	private ITarget target;

	private float bonus;

	private float remaining;

	public static void Apply(ITarget enemy, float damageBonus, float duration)
	{
		if (enemy == null || !enemy.IsAlive || damageBonus <= 0f || duration <= 0f)
		{
			return;
		}
		Component val = (Component)(object)((enemy is Component) ? enemy : null);
		if (!((Object)(object)val == (Object)null))
		{
			FriendMarkedTarget component = val.GetComponent<FriendMarkedTarget>();
			if ((Object)(object)component != (Object)null)
			{
				component.bonus = Mathf.Max(component.bonus, damageBonus);
				component.remaining = Mathf.Max(component.remaining, duration);
				return;
			}
			FriendMarkedTarget friendMarkedTarget = val.gameObject.AddComponent<FriendMarkedTarget>();
			friendMarkedTarget.target = enemy;
			friendMarkedTarget.bonus = damageBonus;
			friendMarkedTarget.remaining = duration;
			Active.Add(friendMarkedTarget);
		}
	}

	public static bool TryGetBonus(ITarget enemy, out float bonus)
	{
		bonus = 0f;
		if (enemy == null)
		{
			return false;
		}
		for (int num = Active.Count - 1; num >= 0; num--)
		{
			FriendMarkedTarget friendMarkedTarget = Active[num];
			if ((Object)(object)friendMarkedTarget == (Object)null)
			{
				Active.RemoveAt(num);
			}
			else if (friendMarkedTarget.target == enemy && friendMarkedTarget.remaining > 0f)
			{
				bonus = friendMarkedTarget.bonus;
				return true;
			}
		}
		return false;
	}

	private void Update()
	{
		remaining -= Time.deltaTime;
		if (remaining <= 0f || target == null || !target.IsAlive)
		{
			Object.Destroy((Object)(object)this);
		}
	}

	private void OnDestroy()
	{
		Active.Remove(this);
	}
}
public static class FriendSharedTarget
{
	private static ITarget current;

	private static float expireTime;

	public static bool IsActive
	{
		get
		{
			if (current != null && current.IsAlive)
			{
				return Time.time <= expireTime;
			}
			return false;
		}
	}

	public static void Set(ITarget target, float duration)
	{
		if (target != null && target.IsAlive && !(duration <= 0f) && !target.IsPlayer())
		{
			current = target;
			expireTime = Time.time + duration;
		}
	}

	public static void Clear()
	{
		current = null;
		expireTime = 0f;
	}

	public static bool TryGet(Vector3 origin, float maxRange, out ITarget target)
	{
		//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_003e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0043: Unknown result type (might be due to invalid IL or missing references)
		target = null;
		if (current == null)
		{
			return false;
		}
		if (Time.time > expireTime || !current.IsAlive)
		{
			Clear();
			return false;
		}
		if (maxRange > 0f)
		{
			Vector3 val = ((IFollowable)current).GetHealthbarPosition() - origin;
			if (((Vector3)(ref val)).sqrMagnitude > maxRange * maxRange)
			{
				return false;
			}
		}
		target = current;
		return true;
	}
}
[Serializable]
public class FriendWiderNetProperty : UpgradeProperty
{
	public Range<float> radiusBonus = new Range<float>(0.2f, 0.35f);

	public override IEnumerator<StatData> GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade)
	{
		//IL_000e: 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)
		yield return StatData.Create("Mine radius:", radiusBonus, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range<float>?)null);
	}

	public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			float value = radiusBonus.GetValue(ref rand, upgrade, default(BoostParams));
			float num = 1f + value;
			behaviour.GrenadeData.explosionRadiusMultiplier *= num;
			behaviour.GrenadeData.detectRadiusMultiplier *= num;
			IWeapon val = (IWeapon)(object)((gear is IWeapon) ? gear : null);
			if (val != null)
			{
				val.GunData.hitForce *= num;
			}
		}
	}

	public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand)
	{
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			behaviour.RestoreFromPrefab();
		}
		IWeapon val = (IWeapon)(object)((gear is IWeapon) ? gear : null);
		if (val != null)
		{
			IWeapon val2 = (IWeapon)(object)((prefab is IWeapon) ? prefab : null);
			if (val2 != null)
			{
				val.GunData.hitForce = val2.GunData.hitForce;
			}
		}
	}
}
[Serializable]
public class FriendLongWatchProperty : UpgradeProperty
{
	public Range<float> durationBonus = new Range<float>(0.35f, 0.5f);

	public Range<float> cooldownPenalty = new Range<float>(0.2f, 0.3f);

	public override IEnumerator<StatData> GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade)
	{
		//IL_000e: 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)
		yield return StatData.Create("Mine duration:", durationBonus, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range<float>?)null);
		yield return StatData.Create("Cooldown:", cooldownPenalty, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range<float>?)null);
	}

	public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			float value = durationBonus.GetValue(ref rand, upgrade, default(BoostParams));
			float value2 = cooldownPenalty.GetValue(ref rand, upgrade, default(BoostParams));
			behaviour.GrenadeData.deployDuration *= 1f + value;
			Throwable val = (Throwable)(object)((gear is Throwable) ? gear : null);
			if (val != null)
			{
				val.CooldownData.rechargeDuration *= 1f + value2;
			}
		}
	}

	public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand)
	{
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			behaviour.RestoreFromPrefab();
		}
		Throwable val = (Throwable)(object)((gear is Throwable) ? gear : null);
		if (val != null)
		{
			Throwable val2 = (Throwable)(object)((prefab is Throwable) ? prefab : null);
			if (val2 != null)
			{
				val.CooldownData.rechargeDuration = val2.CooldownData.rechargeDuration;
			}
		}
	}
}
[Serializable]
public class FriendQuickDeployProperty : UpgradeProperty
{
	public Range<float> cooldownBonus = new Range<float>(0.2f, 0.3f);

	public Range<float> durationPenalty = new Range<float>(0.15f, 0.25f);

	public override IEnumerator<StatData> GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade)
	{
		//IL_000e: 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)
		yield return StatData.Create("Cooldown:", cooldownBonus, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range<float>?)null);
		yield return StatData.Create("Mine duration:", durationPenalty, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range<float>?)null);
	}

	public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_002d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0033: Unknown result type (might be due to invalid IL or missing references)
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			float value = cooldownBonus.GetValue(ref rand, upgrade, default(BoostParams));
			float value2 = durationPenalty.GetValue(ref rand, upgrade, default(BoostParams));
			behaviour.GrenadeData.deployDuration *= Mathf.Max(0.25f, 1f - value2);
			Throwable val = (Throwable)(object)((gear is Throwable) ? gear : null);
			if (val != null)
			{
				val.CooldownData.rechargeDuration *= Mathf.Max(0.25f, 1f - value);
			}
		}
	}

	public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand)
	{
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			behaviour.RestoreFromPrefab();
		}
		Throwable val = (Throwable)(object)((gear is Throwable) ? gear : null);
		if (val != null)
		{
			Throwable val2 = (Throwable)(object)((prefab is Throwable) ? prefab : null);
			if (val2 != null)
			{
				val.CooldownData.rechargeDuration = val2.CooldownData.rechargeDuration;
			}
		}
	}
}
[Serializable]
public class FriendLingeringGiftProperty : UpgradeProperty
{
	public Range<float> puddleDuration = new Range<float>(4f, 6f);

	public override IEnumerator<StatData> GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade)
	{
		//IL_000e: 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)
		yield return StatData.Create("Expire acid puddle:", puddleDuration, upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams), (Range<float>?)null);
	}

	public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			float value = puddleDuration.GetValue(ref rand, upgrade, default(BoostParams));
			behaviour.GrenadeData.acidPuddleOnExpireDuration = Mathf.Max(behaviour.GrenadeData.acidPuddleOnExpireDuration, value);
		}
	}

	public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand)
	{
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			behaviour.RestoreFromPrefab();
		}
	}
}
[Serializable]
public class FriendPartingBoostProperty : UpgradeProperty
{
	public Range<float> speedBonus = new Range<float>(0.2f, 0.3f);

	public Range<float> buffDuration = new Range<float>(4f, 6f);

	public override IEnumerator<StatData> GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade)
	{
		//IL_000e: 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)
		yield return StatData.Create("Expire move speed:", speedBonus, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range<float>?)null);
		yield return StatData.Create("Boost duration:", buffDuration, upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams), (Range<float>?)null);
	}

	public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		//IL_002c: 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)
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			float value = speedBonus.GetValue(ref rand, upgrade, default(BoostParams));
			float value2 = buffDuration.GetValue(ref rand, upgrade, default(BoostParams));
			behaviour.GrenadeData.expireMoveSpeedBonus = Mathf.Max(behaviour.GrenadeData.expireMoveSpeedBonus, value);
			behaviour.GrenadeData.expireMoveSpeedDuration = Mathf.Max(behaviour.GrenadeData.expireMoveSpeedDuration, value2);
		}
	}

	public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand)
	{
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			behaviour.RestoreFromPrefab();
		}
	}
}
[Serializable]
public class FriendTurretModeProperty : UpgradeProperty
{
	public override IEnumerator<StatData> GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade)
	{
		//IL_0007: 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)
		yield return StatData.Create("Deploy mode:", new Range<int>(1, 1), upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams));
	}

	public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand)
	{
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			behaviour.GrenadeData.deployMode |= FriendDeployMode.Turret;
		}
	}

	public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand)
	{
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			behaviour.RestoreFromPrefab();
		}
	}
}
[Serializable]
public class FriendMortarModeProperty : UpgradeProperty
{
	public override IEnumerator<StatData> GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade)
	{
		//IL_0007: 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)
		yield return StatData.Create("Deploy mode:", new Range<int>(1, 1), upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams));
	}

	public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand)
	{
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			behaviour.GrenadeData.deployMode |= FriendDeployMode.Mortar;
		}
	}

	public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand)
	{
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			behaviour.RestoreFromPrefab();
		}
	}
}
[Serializable]
public class FriendDroneModeProperty : UpgradeProperty
{
	public override IEnumerator<StatData> GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade)
	{
		//IL_0007: 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)
		yield return StatData.Create("Deploy mode:", new Range<int>(1, 1), upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams));
	}

	public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand)
	{
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			behaviour.GrenadeData.deployMode |= FriendDeployMode.Drone;
		}
	}

	public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand)
	{
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			behaviour.RestoreFromPrefab();
		}
	}
}
[Serializable]
public class FriendSquadDropProperty : UpgradeProperty
{
	public Range<int> extraDeploys = new Range<int>(1, 1);

	public override IEnumerator<StatData> GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade)
	{
		//IL_000e: 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)
		yield return StatData.Create("Deploys per throw:", extraDeploys, upgrade, ref rand, (OverrideType)1, (LabelType)1, default(BoostParams));
	}

	public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			int value = extraDeploys.GetValue(ref rand, upgrade, default(BoostParams));
			behaviour.GrenadeData.maxConcurrentDeploys = Mathf.Max(1, behaviour.GrenadeData.maxConcurrentDeploys + value);
			FriendDeployTracker.MaxConcurrentDeploys = Mathf.Max(FriendDeployTracker.MaxConcurrentDeploys, behaviour.GrenadeData.maxConcurrentDeploys);
			FriendCombatHooks.EnsureBound(gear, behaviour.GrenadeData);
		}
	}

	public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand)
	{
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			behaviour.RestoreFromPrefab();
		}
		FriendDeployTracker.MaxConcurrentDeploys = 1;
	}
}
[Serializable]
public class FriendSympatheticLinkProperty : UpgradeProperty
{
	public Range<float> lifesteal = new Range<float>(0.08f, 0.15f);

	public override IEnumerator<StatData> GetStatData(Random rand, IUpgradable gear, UpgradeInstance upgrade)
	{
		//IL_000e: 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)
		yield return StatData.Create("Overheal from damage:", lifesteal, upgrade, ref rand, (OverrideType)2, (LabelType)1, default(BoostParams), (Range<float>?)null);
	}

	public override void Apply(IGear gear, UpgradeInstance upgrade, ref Random rand)
	{
		//IL_0015: Unknown result type (might be due to invalid IL or missing references)
		//IL_001b: Unknown result type (might be due to invalid IL or missing references)
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			float value = lifesteal.GetValue(ref rand, upgrade, default(BoostParams));
			behaviour.GrenadeData.lifestealFraction = Mathf.Max(behaviour.GrenadeData.lifestealFraction, value);
			FriendCombatHooks.EnsureBound(gear, behaviour.GrenadeData);
		}
	}

	public override void Remove(IGear gear, IGear prefab, UpgradeInstance upgrade, ref Random rand)
	{
		if (FriendinaBoxBehaviour.TryGet(gear, out var behaviour))
		{
			behaviour.RestoreFromPrefab();
		}
	}
}
[Serializable]
public class FriendFieldRechargeProperty : UpgradeProper