Decompiled source of LoopStabilizer v1.0.0

plugins/LoopStabilizer/LoopStabilizer.dll

Decompiled a day ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using Microsoft.CodeAnalysis;
using On.RoR2;
using On.RoR2.Items;
using On.RoR2.Orbs;
using On.RoR2.Projectile;
using RiskOfOptions;
using RiskOfOptions.OptionConfigs;
using RiskOfOptions.Options;
using RoR2;
using RoR2.Orbs;
using RoR2.Projectile;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("LoopStabilizer")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0+7e6f2311f0a7348b69474ff1980a93033ebd9d34")]
[assembly: AssemblyProduct("LoopStabilizer")]
[assembly: AssemblyTitle("LoopStabilizer")]
[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;
		}
	}
}
namespace LoopStabilizer
{
	internal static class BannerHooks
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static hook_OnCharacterLevelUp <0>__OnCharacterLevelUp;

			public static hook_Awake <1>__Awake;
		}

		private static readonly List<BuffWard> banners = new List<BuffWard>();

		private static bool placingBanner;

		private static BuffWard placedBanner;

		public static void Init()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//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)
			//IL_003b: Expected O, but got Unknown
			object obj = <>O.<0>__OnCharacterLevelUp;
			if (obj == null)
			{
				hook_OnCharacterLevelUp val = OnCharacterLevelUp;
				<>O.<0>__OnCharacterLevelUp = val;
				obj = (object)val;
			}
			WardOnLevelManager.OnCharacterLevelUp += (hook_OnCharacterLevelUp)obj;
			object obj2 = <>O.<1>__Awake;
			if (obj2 == null)
			{
				hook_Awake val2 = Awake;
				<>O.<1>__Awake = val2;
				obj2 = (object)val2;
			}
			BuffWard.Awake += (hook_Awake)obj2;
		}

		private static void OnCharacterLevelUp(orig_OnCharacterLevelUp orig, CharacterBody characterBody)
		{
			placingBanner = true;
			placedBanner = null;
			try
			{
				orig.Invoke(characterBody);
			}
			finally
			{
				placingBanner = false;
			}
			if (Object.op_Implicit((Object)(object)placedBanner) && LoopStabilizer.limitBanners.Value)
			{
				RemoveBannersCloseTo(placedBanner);
				banners.Add(placedBanner);
			}
		}

		private static void Awake(orig_Awake orig, BuffWard self)
		{
			orig.Invoke(self);
			if (placingBanner)
			{
				placedBanner = self;
			}
		}

		private static void RemoveBannersCloseTo(BuffWard banner)
		{
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: 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_0088: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			Vector3 position = ((Component)banner).transform.position;
			TeamIndex teamIndex = banner.teamFilter.teamIndex;
			float num = banner.radius * (float)LoopStabilizer.minBannerDistance.Value / 100f;
			for (int num2 = banners.Count - 1; num2 >= 0; num2--)
			{
				BuffWard val = banners[num2];
				if (!Object.op_Implicit((Object)(object)val))
				{
					banners.RemoveAt(num2);
				}
				else if (!(val.radius > banner.radius) && val.teamFilter.teamIndex == teamIndex)
				{
					Vector3 val2 = ((Component)val).transform.position - position;
					if (!(((Vector3)(ref val2)).sqrMagnitude > num * num))
					{
						Object.Destroy((Object)(object)((Component)val).gameObject);
						banners.RemoveAt(num2);
					}
				}
			}
		}
	}
	internal static class BoomerangHooks
	{
		private struct Throw
		{
			public bool crit;

			public ProcChainMask procChainMask;
		}

		private class MergedBoomerang : MonoBehaviour
		{
			public readonly List<Throw> others = new List<Throw>();

			private void Awake()
			{
				mergedBoomerangs++;
			}

			private void OnDestroy()
			{
				mergedBoomerangs--;
			}
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static hook_Awake <0>__Awake;

			public static hook_FireProjectile_FireProjectileInfo <1>__FireProjectile;

			public static hook_ProcessHits <2>__ProcessHits;
		}

		private const string ELECTRIC_BOOMERANG_PATH = "Prefabs/Projectiles/StunAndPierceBoomerang";

		private static int mergedBoomerangs;

		private static BoomerangProjectile lastBoomerang;

		private static MergedBoomerang lastMerged;

		private static FireProjectileInfo lastThrow;

		private static float lastThrowTime;

		private static bool throwing;

		private static BoomerangProjectile thrown;

		private static GameObject electricBoomerangPrefab;

		private static bool lookedForPrefab;

		public static int BoomerangsMerged;

		public static void Init()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//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)
			//IL_003b: Expected O, but got Unknown
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			object obj = <>O.<0>__Awake;
			if (obj == null)
			{
				hook_Awake val = Awake;
				<>O.<0>__Awake = val;
				obj = (object)val;
			}
			BoomerangProjectile.Awake += (hook_Awake)obj;
			object obj2 = <>O.<1>__FireProjectile;
			if (obj2 == null)
			{
				hook_FireProjectile_FireProjectileInfo val2 = FireProjectile;
				<>O.<1>__FireProjectile = val2;
				obj2 = (object)val2;
			}
			ProjectileManager.FireProjectile_FireProjectileInfo += (hook_FireProjectile_FireProjectileInfo)obj2;
			object obj3 = <>O.<2>__ProcessHits;
			if (obj3 == null)
			{
				hook_ProcessHits val3 = ProcessHits;
				<>O.<2>__ProcessHits = val3;
				obj3 = (object)val3;
			}
			OverlapAttack.ProcessHits += (hook_ProcessHits)obj3;
		}

		private static void Awake(orig_Awake orig, BoomerangProjectile self)
		{
			orig.Invoke(self);
			if (throwing)
			{
				thrown = self;
			}
			if (NetworkServer.active && LoopStabilizer.limitBoomerangs.Value)
			{
				float num = self.maxFlyStopwatch + self.transitionDuration + (float)LoopStabilizer.maxBoomerangReturnTime.Value;
				if (LoopStabilizer.boomerangLimitInRealTime.Value)
				{
					((MonoBehaviour)self).StartCoroutine(DestroyAfterRealTime(((Component)self).gameObject, num));
				}
				else
				{
					Object.Destroy((Object)(object)((Component)self).gameObject, num);
				}
			}
		}

		private static IEnumerator DestroyAfterRealTime(GameObject boomerang, float seconds)
		{
			yield return (object)new WaitForSecondsRealtime(seconds);
			Object.Destroy((Object)(object)boomerang);
		}

		private static void FireProjectile(orig_FireProjectile_FireProjectileInfo orig, ProjectileManager self, FireProjectileInfo fireProjectileInfo)
		{
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: 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_0082: Unknown result type (might be due to invalid IL or missing references)
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			if (!NetworkServer.active || !LoopStabilizer.mergeBoomerangs.Value || !IsElectricBoomerang(fireProjectileInfo.projectilePrefab))
			{
				orig.Invoke(self, fireProjectileInfo);
				return;
			}
			if (Object.op_Implicit((Object)(object)lastBoomerang) && Time.fixedTime == lastThrowTime && IsSameThrow(fireProjectileInfo, lastThrow))
			{
				if (!Object.op_Implicit((Object)(object)lastMerged))
				{
					lastMerged = ((Component)lastBoomerang).gameObject.AddComponent<MergedBoomerang>();
				}
				lastMerged.others.Add(new Throw
				{
					crit = fireProjectileInfo.crit,
					procChainMask = fireProjectileInfo.procChainMask
				});
				BoomerangsMerged++;
				return;
			}
			throwing = true;
			thrown = null;
			try
			{
				orig.Invoke(self, fireProjectileInfo);
			}
			finally
			{
				throwing = false;
			}
			lastBoomerang = thrown;
			lastMerged = null;
			lastThrow = fireProjectileInfo;
			lastThrowTime = Time.fixedTime;
		}

		private static bool IsSameThrow(FireProjectileInfo a, FireProjectileInfo b)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_0035: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: 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)
			if ((Object)(object)a.owner == (Object)(object)b.owner && a.damage == b.damage && a.position == b.position)
			{
				return a.rotation == b.rotation;
			}
			return false;
		}

		private static void ProcessHits(orig_ProcessHits orig, OverlapAttack self, List<OverlapInfo> hitList)
		{
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_008e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			orig.Invoke(self, hitList);
			if (mergedBoomerangs == 0 || hitList.Count == 0 || !Object.op_Implicit((Object)(object)self.inflictor))
			{
				return;
			}
			MergedBoomerang component = self.inflictor.GetComponent<MergedBoomerang>();
			if (Object.op_Implicit((Object)(object)component))
			{
				bool isCrit = self.isCrit;
				ProcChainMask procChainMask = self.procChainMask;
				GameObject hitEffectPrefab = self.hitEffectPrefab;
				self.hitEffectPrefab = null;
				int i = 0;
				for (int count = component.others.Count; i < count; i++)
				{
					self.isCrit = component.others[i].crit;
					self.procChainMask = component.others[i].procChainMask;
					orig.Invoke(self, hitList);
				}
				self.isCrit = isCrit;
				self.procChainMask = procChainMask;
				self.hitEffectPrefab = hitEffectPrefab;
			}
		}

		private static bool IsElectricBoomerang(GameObject prefab)
		{
			if (!lookedForPrefab)
			{
				lookedForPrefab = true;
				electricBoomerangPrefab = FindElectricBoomerangPrefab();
			}
			if (Object.op_Implicit((Object)(object)electricBoomerangPrefab))
			{
				return (Object)(object)prefab == (Object)(object)electricBoomerangPrefab;
			}
			return false;
		}

		private static GameObject FindElectricBoomerangPrefab()
		{
			GameObject val = LegacyResourcesAPI.Load<GameObject>("Prefabs/Projectiles/StunAndPierceBoomerang");
			if (!Object.op_Implicit((Object)(object)val))
			{
				Log.Warning("Couldn't find the Electric Boomerang, so its boomerangs are not merged");
				return null;
			}
			bool num = Object.op_Implicit((Object)(object)val.GetComponent<ProjectileOverlapAttack>()) || Object.op_Implicit((Object)(object)val.GetComponent<ProjectileDotZone>());
			bool flag = Object.op_Implicit((Object)(object)val.GetComponent<ProjectileExplosion>()) || Object.op_Implicit((Object)(object)val.GetComponent<ProjectileSingleTargetImpact>());
			if (!num || flag)
			{
				List<string> list = new List<string>();
				Component[] componentsInChildren = val.GetComponentsInChildren<Component>(true);
				foreach (Component val2 in componentsInChildren)
				{
					list.Add(((object)val2).GetType().Name);
				}
				Log.Warning("The Electric Boomerang isn't what this mod expects, so its boomerangs are not merged. It is made of: " + string.Join(", ", list));
				return null;
			}
			return val;
		}
	}
	internal static class DebugKeys
	{
		private const int STAGES_PER_LOOP = 5;

		private const float SECONDS_PER_MINUTE = 60f;

		private const float MIN_SPAWN_DISTANCE = 15f;

		private const float MAX_SPAWN_DISTANCE = 50f;

		public static void Update()
		{
			if (LoopStabilizer.debugKeys.Value)
			{
				if (IsPressedByHost(LoopStabilizer.addStagesKey))
				{
					AddStages();
				}
				if (IsPressedByHost(LoopStabilizer.addTimeKey))
				{
					AddTime();
				}
				if (IsPressedByHost(LoopStabilizer.giveItemsKey))
				{
					GiveItems();
				}
				if (IsPressedByHost(LoopStabilizer.lateGameKey))
				{
					AddStages();
					AddTime();
					GiveItems();
				}
				if (IsPressedByHost(LoopStabilizer.spawnEnemiesKey))
				{
					SpawnEnemies();
				}
			}
		}

		private static bool IsPressedByHost(ConfigEntry<KeyboardShortcut> key)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			KeyboardShortcut value = key.Value;
			if (!((KeyboardShortcut)(ref value)).IsDown() || !Object.op_Implicit((Object)(object)Run.instance))
			{
				return false;
			}
			if (!NetworkServer.active)
			{
				Log.Warning("The debug keys only work for the host");
			}
			return NetworkServer.active;
		}

		private static void AddStages()
		{
			Run instance = Run.instance;
			int num = (instance.NetworkstageClearCount = instance.stageClearCount + LoopStabilizer.debugStagesPerPress.Value);
			instance.Network_loopClearCount = num / 5;
			Log.Info($"Debug key pressed, the run counts as {num} stages cleared now");
		}

		private static void AddTime()
		{
			Run instance = Run.instance;
			int value = LoopStabilizer.debugMinutesPerPress.Value;
			instance.SetRunStopwatch(instance.GetRunStopwatch() + (float)value * 60f);
			Log.Info($"Debug key pressed, moved the timer of the run {value} minutes forward");
		}

		private static void GiveItems()
		{
			Run instance = Run.instance;
			foreach (PlayerCharacterMasterController instance2 in PlayerCharacterMasterController.instances)
			{
				Inventory val = (Object.op_Implicit((Object)(object)instance2.master) ? instance2.master.inventory : null);
				if (Object.op_Implicit((Object)(object)val))
				{
					GiveAll(val, instance.availableTier1DropList, LoopStabilizer.debugCommonItems.Value);
					GiveAll(val, instance.availableTier2DropList, LoopStabilizer.debugUncommonItems.Value);
					GiveAll(val, instance.availableTier3DropList, LoopStabilizer.debugLegendaryItems.Value);
					if (LoopStabilizer.debugPlasmaShrimps.Value > 0)
					{
						val.GiveItemPermanent(Items.MissileVoid, LoopStabilizer.debugPlasmaShrimps.Value);
					}
				}
			}
			Log.Info("Debug key pressed, gave everyone items");
		}

		private static void GiveAll(Inventory inventory, List<PickupIndex> pickups, int count)
		{
			//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)
			//IL_0025: Invalid comparison between Unknown and I4
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			if (count <= 0)
			{
				return;
			}
			foreach (PickupIndex pickup in pickups)
			{
				PickupDef pickupDef = PickupCatalog.GetPickupDef(pickup);
				if (pickupDef != null && (int)pickupDef.itemIndex != -1)
				{
					inventory.GiveItemPermanent(pickupDef.itemIndex, count);
				}
			}
		}

		private static void SpawnEnemies()
		{
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_008a: 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_0091: 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_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_009d: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Expected O, but got Unknown
			//IL_00ba: 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_00cb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d4: Expected O, but got Unknown
			LocalUser firstLocalUser = LocalUserManager.GetFirstLocalUser();
			CharacterBody val = ((firstLocalUser != null) ? firstLocalUser.cachedBody : null);
			ClassicStageInfo instance = ClassicStageInfo.instance;
			if (!Object.op_Implicit((Object)(object)val) || !Object.op_Implicit((Object)(object)DirectorCore.instance) || !Object.op_Implicit((Object)(object)instance) || instance.monsterSelection == null || instance.monsterSelection.Count == 0)
			{
				Log.Warning("Nothing to spawn enemies from or around here");
				return;
			}
			Xoroshiro128Plus rng = RoR2Application.rng;
			int num = 0;
			for (int i = 0; i < LoopStabilizer.debugEnemiesPerPress.Value; i++)
			{
				SpawnCard spawnCard = instance.monsterSelection.Evaluate(rng.nextNormalizedFloat).GetSpawnCard();
				if (Object.op_Implicit((Object)(object)spawnCard))
				{
					DirectorPlacementRule val2 = new DirectorPlacementRule
					{
						placementMode = (PlacementMode)1,
						position = val.corePosition,
						minDistance = 15f,
						maxDistance = 50f
					};
					DirectorSpawnRequest val3 = new DirectorSpawnRequest(spawnCard, val2, rng)
					{
						teamIndexOverride = (TeamIndex)2,
						ignoreTeamMemberLimit = true
					};
					if (Object.op_Implicit((Object)(object)DirectorCore.instance.TrySpawnObject(val3)))
					{
						num++;
					}
				}
			}
			Log.Info($"Debug key pressed, spawned {num} enemies");
		}
	}
	internal static class EffectBudget
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static hook_SpawnEffect_EffectIndex_EffectData_bool <0>__SpawnEffect;
		}

		private static int frame = -1;

		private static int total;

		private static int[] perEffect = Array.Empty<int>();

		public static int Shown;

		public static int Skipped;

		public static void Init()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			object obj = <>O.<0>__SpawnEffect;
			if (obj == null)
			{
				hook_SpawnEffect_EffectIndex_EffectData_bool val = SpawnEffect;
				<>O.<0>__SpawnEffect = val;
				obj = (object)val;
			}
			EffectManager.SpawnEffect_EffectIndex_EffectData_bool += (hook_SpawnEffect_EffectIndex_EffectData_bool)obj;
		}

		private static void SpawnEffect(orig_SpawnEffect_EffectIndex_EffectData_bool orig, EffectIndex effectIndex, EffectData effectData, bool transmit)
		{
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_000f: Unknown result type (might be due to invalid IL or missing references)
			if (transmit || !LoopStabilizer.limitEffects.Value || CanAfford(effectIndex))
			{
				orig.Invoke(effectIndex, effectData, transmit);
			}
			else
			{
				Skipped++;
			}
		}

		private static bool CanAfford(EffectIndex effectIndex)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: Invalid comparison between Unknown and I4
			//IL_0045: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Expected I4, but got Unknown
			EffectDef effectDef = EffectCatalog.GetEffectDef(effectIndex);
			if (effectDef == null)
			{
				return true;
			}
			if ((int)effectDef.vfxPriority == 2)
			{
				return true;
			}
			if (frame != Time.frameCount)
			{
				frame = Time.frameCount;
				total = 0;
				Array.Clear(perEffect, 0, perEffect.Length);
			}
			int num = (int)effectIndex;
			if (num >= perEffect.Length)
			{
				Array.Resize(ref perEffect, Math.Max(num + 1, EffectCatalog.effectCount));
			}
			if (total >= LoopStabilizer.maxEffectsPerFrame.Value || perEffect[num] >= LoopStabilizer.maxSameEffectsPerFrame.Value)
			{
				return false;
			}
			total++;
			perEffect[num]++;
			Shown++;
			return true;
		}
	}
	internal static class ExperienceOrbBudget
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static hook_HandleCreateExpEffectInternal <0>__HandleCreateExpEffectInternal;
		}

		private static float budget;

		private static float lastTime;

		public static int Shown;

		public static int Skipped;

		public static void Init()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			object obj = <>O.<0>__HandleCreateExpEffectInternal;
			if (obj == null)
			{
				hook_HandleCreateExpEffectInternal val = HandleCreateExpEffectInternal;
				<>O.<0>__HandleCreateExpEffectInternal = val;
				obj = (object)val;
			}
			ExperienceManager.HandleCreateExpEffectInternal += (hook_HandleCreateExpEffectInternal)obj;
		}

		private static void HandleCreateExpEffectInternal(orig_HandleCreateExpEffectInternal orig, ExperienceManager self, NetworkMessage netMsg)
		{
			if (!LoopStabilizer.limitEffects.Value)
			{
				orig.Invoke(self, netMsg);
				return;
			}
			int value = LoopStabilizer.maxExperienceOrbsPerSecond.Value;
			float unscaledTime = Time.unscaledTime;
			budget = Math.Min(value, budget + (unscaledTime - lastTime) * (float)value);
			lastTime = unscaledTime;
			if (budget <= 0f)
			{
				Skipped++;
				return;
			}
			orig.Invoke(self, netMsg);
			Shown++;
			budget -= self.CalculateDenominations(ExperienceManager.currentIncomingCreateExpEffectMessage.awardAmount).Count;
		}
	}
	internal static class KillChainLimiter
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static hook_OnCharacterDeath <0>__OnCharacterDeath;
		}

		private const int MAX_NESTED_KILLS = 16;

		private static readonly Queue<DamageReport> waiting = new Queue<DamageReport>();

		private static int depth;

		public static int KillsDelayed;

		public static void Init()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			object obj = <>O.<0>__OnCharacterDeath;
			if (obj == null)
			{
				hook_OnCharacterDeath val = OnCharacterDeath;
				<>O.<0>__OnCharacterDeath = val;
				obj = (object)val;
			}
			GlobalEventManager.OnCharacterDeath += (hook_OnCharacterDeath)obj;
		}

		private static void OnCharacterDeath(orig_OnCharacterDeath orig, GlobalEventManager self, DamageReport damageReport)
		{
			if (!LoopStabilizer.limitKillChains.Value)
			{
				orig.Invoke(self, damageReport);
				return;
			}
			if (depth >= 16)
			{
				waiting.Enqueue(damageReport);
				KillsDelayed++;
				return;
			}
			WorkOff(orig, self, damageReport);
			while (depth == 0 && waiting.Count > 0)
			{
				WorkOff(orig, self, waiting.Dequeue());
			}
		}

		private static void WorkOff(orig_OnCharacterDeath orig, GlobalEventManager self, DamageReport damageReport)
		{
			depth++;
			try
			{
				orig.Invoke(self, damageReport);
			}
			finally
			{
				depth--;
			}
		}
	}
	internal static class Log
	{
		private static ManualLogSource _logSource;

		internal static void Init(ManualLogSource logSource)
		{
			_logSource = logSource;
		}

		internal static void Debug(object data)
		{
			_logSource.LogDebug(data);
		}

		internal static void Error(object data)
		{
			_logSource.LogError(data);
		}

		internal static void Fatal(object data)
		{
			_logSource.LogFatal(data);
		}

		internal static void Info(object data)
		{
			_logSource.LogInfo(data);
		}

		internal static void Message(object data)
		{
			_logSource.LogMessage(data);
		}

		internal static void Warning(object data)
		{
			_logSource.LogWarning(data);
		}
	}
	[BepInPlugin("360gaggi.LoopStabilizer", "LoopStabilizer", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public class LoopStabilizer : BaseUnityPlugin
	{
		public const string PLUGIN_GUID = "360gaggi.LoopStabilizer";

		public const string PLUGIN_AUTHOR = "360gaggi";

		public const string PLUGIN_NAME = "LoopStabilizer";

		public const string PLUGIN_VERSION = "1.0.0";

		public const int MIN_TICK_TIME_BUDGET = 1;

		public const int MIN_EFFECTS_PER_FRAME = 1;

		public const int MAX_EFFECTS_PER_FRAME = 500;

		public const int MAX_SAME_EFFECTS_PER_FRAME = 100;

		public const int MAX_EXPERIENCE_ORBS_PER_SECOND = 1000;

		public const int MAX_TICK_TIME_BUDGET = 100;

		public const int MAX_STICKY_BOMB_MERGE_TIME = 2000;

		public const int MAX_BOOMERANG_RETURN_TIME = 60;

		public const int MIN_BANNER_DISTANCE = 1;

		public const int MAX_BANNER_DISTANCE = 100;

		public const int MAX_DEBUG_ITEMS = 200;

		public const int MAX_DEBUG_STAGES = 500;

		public const int MAX_DEBUG_MINUTES = 1000;

		public const int MAX_DEBUG_ENEMIES = 100;

		public static ConfigEntry<bool> limitTicks;

		public static ConfigEntry<int> tickTimeBudget;

		public static ConfigEntry<bool> limitEffects;

		public static ConfigEntry<int> maxEffectsPerFrame;

		public static ConfigEntry<int> maxSameEffectsPerFrame;

		public static ConfigEntry<int> maxExperienceOrbsPerSecond;

		public static ConfigEntry<bool> mergeIcbmMissiles;

		public static ConfigEntry<bool> mergedMissilesHitThreeTimes;

		public static ConfigEntry<bool> mergeStickyBombs;

		public static ConfigEntry<int> stickyBombMergeTime;

		public static ConfigEntry<bool> mergeBoomerangs;

		public static ConfigEntry<bool> limitBoomerangs;

		public static ConfigEntry<int> maxBoomerangReturnTime;

		public static ConfigEntry<bool> boomerangLimitInRealTime;

		public static ConfigEntry<bool> mergeDaggers;

		public static ConfigEntry<bool> limitKillChains;

		public static ConfigEntry<bool> limitBanners;

		public static ConfigEntry<int> minBannerDistance;

		public static ConfigEntry<bool> showOverlay;

		public static ConfigEntry<KeyboardShortcut> toggleOverlayKey;

		public static ConfigEntry<bool> debugKeys;

		public static ConfigEntry<KeyboardShortcut> addStagesKey;

		public static ConfigEntry<KeyboardShortcut> addTimeKey;

		public static ConfigEntry<KeyboardShortcut> giveItemsKey;

		public static ConfigEntry<KeyboardShortcut> lateGameKey;

		public static ConfigEntry<KeyboardShortcut> spawnEnemiesKey;

		public static ConfigEntry<int> debugStagesPerPress;

		public static ConfigEntry<int> debugMinutesPerPress;

		public static ConfigEntry<int> debugCommonItems;

		public static ConfigEntry<int> debugUncommonItems;

		public static ConfigEntry<int> debugLegendaryItems;

		public static ConfigEntry<int> debugPlasmaShrimps;

		public static ConfigEntry<int> debugEnemiesPerPress;

		private PerfOverlay overlay;

		public void Awake()
		{
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Expected O, but got Unknown
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Expected O, but got Unknown
			//IL_010e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0118: Expected O, but got Unknown
			//IL_01a7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b1: Expected O, but got Unknown
			//IL_0219: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: Expected O, but got Unknown
			//IL_02cc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d6: Expected O, but got Unknown
			//IL_0315: Unknown result type (might be due to invalid IL or missing references)
			//IL_0363: Unknown result type (might be due to invalid IL or missing references)
			//IL_0391: Unknown result type (might be due to invalid IL or missing references)
			//IL_03bf: Unknown result type (might be due to invalid IL or missing references)
			//IL_03ed: Unknown result type (might be due to invalid IL or missing references)
			//IL_041b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0456: Unknown result type (might be due to invalid IL or missing references)
			//IL_0460: Expected O, but got Unknown
			//IL_048f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0499: Expected O, but got Unknown
			//IL_04c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_04cf: Expected O, but got Unknown
			//IL_04fb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0505: Expected O, but got Unknown
			//IL_0531: Unknown result type (might be due to invalid IL or missing references)
			//IL_053b: Expected O, but got Unknown
			//IL_0566: Unknown result type (might be due to invalid IL or missing references)
			//IL_0570: Expected O, but got Unknown
			//IL_0599: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a3: Expected O, but got Unknown
			Log.Init(((BaseUnityPlugin)this).Logger);
			limitTicks = ((BaseUnityPlugin)this).Config.Bind<bool>("Ticks", "Limit ticks per frame", true, "Lets the game run in slow motion when it can't keep up, instead of dropping to 1-2 FPS. Only changes something while the FPS are low");
			tickTimeBudget = ((BaseUnityPlugin)this).Config.Bind<int>("Ticks", "Tick time budget", 33, new ConfigDescription("How many milliseconds the ticks of a single frame may take together. The game simulates in ticks of 1/60 of a second and vanilla runs up to 20 of them in a frame to catch up, no matter how long they take. Here a frame gets as many as fit into this time, but always at least one. Lower is smoother, higher keeps the game at full speed for longer", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			limitEffects = ((BaseUnityPlugin)this).Config.Bind<bool>("Effects", "Limit effects", true, "Leaves out effects and their sounds once there are more of them in a single frame than the limits below allow. Only what you see and hear, damage and procs are not touched");
			maxEffectsPerFrame = ((BaseUnityPlugin)this).Config.Bind<int>("Effects", "Max effects per frame", 60, new ConfigDescription("How many effects can spawn in a single frame, all of them together (vanilla has no limit)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 500), Array.Empty<object>()));
			maxSameEffectsPerFrame = ((BaseUnityPlugin)this).Config.Bind<int>("Effects", "Max of the same effect per frame", 10, new ConfigDescription("How many times the very same effect can spawn in a single frame (vanilla has no limit)", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			maxExperienceOrbsPerSecond = ((BaseUnityPlugin)this).Config.Bind<int>("Effects", "Max XP orbs per second", 60, new ConfigDescription("How many XP orbs can show up per second, the orbs of further kills are left out (vanilla has no limit). The XP itself doesn't come from the orbs and is not touched", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 1000), Array.Empty<object>()));
			mergeIcbmMissiles = ((BaseUnityPlugin)this).Config.Bind<bool>("Missiles", "Merge ICBM missiles", true, "With a Pocket I.C.B.M. one missile is fired for all three instead of three missiles. Only the host's setting counts");
			mergedMissilesHitThreeTimes = ((BaseUnityPlugin)this).Config.Bind<bool>("Missiles", "Merged missiles hit three times", true, "On: a merged missile hits three times in a row when it arrives, so damage numbers and procs stay exactly like vanilla. Off: it hits once with three times the damage, which is less work for the game but only rolls for procs once. Missiles that explode (fireworks) always do the latter. Only the host's setting counts");
			mergeStickyBombs = ((BaseUnityPlugin)this).Config.Bind<bool>("Sticky Bombs", "Merge sticky bombs", true, "A Sticky Bomb takes the ones along that get attached to the same enemy shortly after it, and goes off once for each of them. Same damage numbers, far fewer bombs. Only the host's setting counts");
			stickyBombMergeTime = ((BaseUnityPlugin)this).Config.Bind<int>("Sticky Bombs", "Sticky bomb merge time", 250, new ConfigDescription("For how many milliseconds a bomb takes others along. The ones riding along go off together with it, so up to this much earlier than they would have on their own. Higher means fewer bombs, but more explosions at the same moment. 0 only merges the bombs of the very same tick. Only the host's setting counts", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 2000), Array.Empty<object>()));
			mergeBoomerangs = ((BaseUnityPlugin)this).Config.Bind<bool>("Boomerangs", "Merge boomerangs", true, "All the Electric Boomerangs that are thrown in the same instant fly the very same path, so only one of them flies and hits once for each of them. Same damage numbers and the same rolls for procs, far fewer boomerangs. Only the host's setting counts");
			limitBoomerangs = ((BaseUnityPlugin)this).Config.Bind<bool>("Boomerangs", "Limit boomerangs", true, "Boomerangs (Electric Boomerang, Sawmerang) disappear when they take too long to get back to their owner. Vanilla has no limit, so if you are faster than your boomerangs they follow you forever and pile up. Only the host's setting counts");
			maxBoomerangReturnTime = ((BaseUnityPlugin)this).Config.Bind<int>("Boomerangs", "Max boomerang return time", 2, new ConfigDescription("How many seconds a boomerang has to make it back to its owner. It keeps hitting what it flies through on the way back, so with a 0 it is gone the moment it turns around and those hits are lost. Only the host's setting counts", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 60), Array.Empty<object>()));
			boomerangLimitInRealTime = ((BaseUnityPlugin)this).Config.Bind<bool>("Boomerangs", "Boomerang limit in real time", false, "Counts the time a boomerang has in real seconds instead of game seconds. In slow motion the boomerangs are then gone as quickly as they would be at full speed, but they don't get as far and hit less for it. Only the host's setting counts");
			mergeDaggers = ((BaseUnityPlugin)this).Config.Bind<bool>("Daggers", "Merge daggers", true, "The three daggers that a kill with a Ceremonial Dagger throws go out as one that hits three times, each time with the roll for a crit that dagger would have had. Same damage numbers and the same rolls for procs, a third of the daggers. Only the host's setting counts");
			limitKillChains = ((BaseUnityPlugin)this).Config.Bind<bool>("Kills", "Limit kill chains", true, "What a kill sets off (Gasoline for one) is worked off inside of the hit that killed, and a kill caused by that inside of it again. In a big enough crowd where everyone dies to the explosion of the one before, vanilla nests that until the game crashes. With this a kill that is nested too deep waits for the ones before it, still in the same instant. Only the host's setting counts");
			limitBanners = ((BaseUnityPlugin)this).Config.Bind<bool>("Banners", "Limit banners", true, "A Warbanner that gets put down for a level up takes the place of the older ones standing close to it. Vanilla keeps every single one until the stage is over, so levelling up a lot covers the map in hundreds of them. Only the host's setting counts");
			minBannerDistance = ((BaseUnityPlugin)this).Config.Bind<int>("Banners", "Min distance between banners", 50, new ConfigDescription("How close an older banner has to stand to a new one to be replaced by it, in percent of the range of the new one. The buff of banners doesn't stack, all that can get lost is the outer edge of the area the older one covered. Only the host's setting counts", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			showOverlay = ((BaseUnityPlugin)this).Config.Bind<bool>("Overlay", "Show overlay", false, "Shows FPS, ticks per frame and how much is going on in the game");
			toggleOverlayKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Overlay", "Toggle overlay", new KeyboardShortcut((KeyCode)292, Array.Empty<KeyCode>()), "Shows and hides the overlay");
			debugKeys = ((BaseUnityPlugin)this).Config.Bind<bool>("Debug", "Debug keys", false, "Turns on the keys below, for trying things out. They only work for the host");
			addStagesKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Debug", "Add stages", new KeyboardShortcut((KeyCode)287, Array.Empty<KeyCode>()), "Makes the run count as a later one, by as many stages as set below. The stage itself stays the one you are on");
			addTimeKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Debug", "Add time", new KeyboardShortcut((KeyCode)288, Array.Empty<KeyCode>()), "Moves the timer of the run forward, by as many minutes as set below");
			giveItemsKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Debug", "Give items", new KeyboardShortcut((KeyCode)289, Array.Empty<KeyCode>()), "Gives every player the items set below. Every press gives them again");
			lateGameKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Debug", "Late game", new KeyboardShortcut((KeyCode)290, Array.Empty<KeyCode>()), "Does what the three keys above do, all at once");
			spawnEnemiesKey = ((BaseUnityPlugin)this).Config.Bind<KeyboardShortcut>("Debug", "Spawn enemies", new KeyboardShortcut((KeyCode)291, Array.Empty<KeyCode>()), "Spawns enemies of the current stage around you, on top of the ones the game spawns and past its limit of 40");
			debugStagesPerPress = ((BaseUnityPlugin)this).Config.Bind<int>("Debug", "Stages per press", 60, new ConfigDescription("How many stages the add stages key adds", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 500), Array.Empty<object>()));
			debugMinutesPerPress = ((BaseUnityPlugin)this).Config.Bind<int>("Debug", "Minutes per press", 300, new ConfigDescription("How many minutes the add time key adds. The difficulty of a run comes from its time as much as from its stages", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 1000), Array.Empty<object>()));
			debugCommonItems = ((BaseUnityPlugin)this).Config.Bind<int>("Debug", "Common items", 50, new ConfigDescription("How many of every common item the give items key gives", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 200), Array.Empty<object>()));
			debugUncommonItems = ((BaseUnityPlugin)this).Config.Bind<int>("Debug", "Uncommon items", 25, new ConfigDescription("How many of every uncommon item the give items key gives", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 200), Array.Empty<object>()));
			debugLegendaryItems = ((BaseUnityPlugin)this).Config.Bind<int>("Debug", "Legendary items", 10, new ConfigDescription("How many of every legendary item the give items key gives", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 200), Array.Empty<object>()));
			debugPlasmaShrimps = ((BaseUnityPlugin)this).Config.Bind<int>("Debug", "Plasma Shrimps", 0, new ConfigDescription("How many Plasma Shrimps the give items key gives. They turn all ATGs into Plasma Shrimps, so leave this at 0 to try out the ATGs", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 200), Array.Empty<object>()));
			debugEnemiesPerPress = ((BaseUnityPlugin)this).Config.Bind<int>("Debug", "Enemies per press", 20, new ConfigDescription("How many enemies the spawn enemies key spawns", (AcceptableValueBase)(object)new AcceptableValueRange<int>(1, 100), Array.Empty<object>()));
			TickLimiter.Init();
			EffectBudget.Init();
			ExperienceOrbBudget.Init();
			MissileHooks.Init();
			StickyBombHooks.Init();
			BoomerangHooks.Init();
			KillChainLimiter.Init();
			BannerHooks.Init();
			overlay = ((Component)this).gameObject.AddComponent<PerfOverlay>();
			((Behaviour)overlay).enabled = showOverlay.Value;
			if (RiskOfOptionsCompat.Enabled)
			{
				RiskOfOptionsCompat.Init(((BaseUnityPlugin)this).Info.Location);
			}
		}

		public void FixedUpdate()
		{
			TickLimiter.FixedUpdate();
		}

		public void Update()
		{
			//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)
			TickLimiter.Update();
			DebugKeys.Update();
			KeyboardShortcut value = toggleOverlayKey.Value;
			if (((KeyboardShortcut)(ref value)).IsDown())
			{
				showOverlay.Value = !showOverlay.Value;
			}
			((Behaviour)overlay).enabled = showOverlay.Value;
		}
	}
	internal static class MissileHooks
	{
		private class MergedMissile : MonoBehaviour
		{
			public int hits;

			public List<bool> crits;
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static hook_FireMissile_Vector3_CharacterBody_ProcChainMask_GameObject_float_bool_GameObject_DamageColorIndex_Vector3_float_bool <0>__FireMissile;

			public static hook_FireProjectile_FireProjectileInfo <1>__FireProjectile;

			public static hook_InitializeProjectile <2>__InitializeProjectile;

			public static hook_OnProjectileImpact <3>__OnProjectileImpact;

			public static hook_AddOrb <4>__AddOrb;

			public static hook_GetOrbEffect <5>__GetOrbEffect;
		}

		private const int ICBM_MISSILES = 3;

		private static GameObject volleyPrefab;

		private static int volleyMissiles;

		private const int DAGGERS_PER_KILL = 3;

		private static int mergedHits;

		private static List<bool> mergedCrits;

		private static MergedMissile lastMerged;

		private static bool firingOnward;

		private static MergedMissile volleyDagger;

		private static float volleyDaggerTime;

		private static int volleyDaggersLeft;

		private static MissileVoidOrb volleyOrb;

		private static int volleyOrbsLeft;

		private static MissileVoidOrb hiddenOrb;

		private static GameObject missileVoidOrbEffect;

		public static int ProjectilesFired;

		public static int MissilesMerged;

		public static int DaggersMerged;

		public static int OrbsAdded;

		public static void Init()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//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)
			//IL_003b: Expected O, but got Unknown
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			//IL_0070: 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_007b: Expected O, but got Unknown
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Unknown result type (might be due to invalid IL or missing references)
			//IL_009b: Expected O, but got Unknown
			//IL_00b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bb: Expected O, but got Unknown
			object obj = <>O.<0>__FireMissile;
			if (obj == null)
			{
				hook_FireMissile_Vector3_CharacterBody_ProcChainMask_GameObject_float_bool_GameObject_DamageColorIndex_Vector3_float_bool val = FireMissile;
				<>O.<0>__FireMissile = val;
				obj = (object)val;
			}
			MissileUtils.FireMissile_Vector3_CharacterBody_ProcChainMask_GameObject_float_bool_GameObject_DamageColorIndex_Vector3_float_bool += (hook_FireMissile_Vector3_CharacterBody_ProcChainMask_GameObject_float_bool_GameObject_DamageColorIndex_Vector3_float_bool)obj;
			object obj2 = <>O.<1>__FireProjectile;
			if (obj2 == null)
			{
				hook_FireProjectile_FireProjectileInfo val2 = FireProjectile;
				<>O.<1>__FireProjectile = val2;
				obj2 = (object)val2;
			}
			ProjectileManager.FireProjectile_FireProjectileInfo += (hook_FireProjectile_FireProjectileInfo)obj2;
			object obj3 = <>O.<2>__InitializeProjectile;
			if (obj3 == null)
			{
				hook_InitializeProjectile val3 = InitializeProjectile;
				<>O.<2>__InitializeProjectile = val3;
				obj3 = (object)val3;
			}
			ProjectileManager.InitializeProjectile += (hook_InitializeProjectile)obj3;
			object obj4 = <>O.<3>__OnProjectileImpact;
			if (obj4 == null)
			{
				hook_OnProjectileImpact val4 = OnProjectileImpact;
				<>O.<3>__OnProjectileImpact = val4;
				obj4 = (object)val4;
			}
			ProjectileSingleTargetImpact.OnProjectileImpact += (hook_OnProjectileImpact)obj4;
			object obj5 = <>O.<4>__AddOrb;
			if (obj5 == null)
			{
				hook_AddOrb val5 = AddOrb;
				<>O.<4>__AddOrb = val5;
				obj5 = (object)val5;
			}
			OrbManager.AddOrb += (hook_AddOrb)obj5;
			object obj6 = <>O.<5>__GetOrbEffect;
			if (obj6 == null)
			{
				hook_GetOrbEffect val6 = GetOrbEffect;
				<>O.<5>__GetOrbEffect = val6;
				obj6 = (object)val6;
			}
			MissileVoidOrb.GetOrbEffect += (hook_GetOrbEffect)obj6;
		}

		private static bool HasIcbm(CharacterBody body)
		{
			if (Object.op_Implicit((Object)(object)body) && Object.op_Implicit((Object)(object)body.inventory))
			{
				return body.inventory.GetItemCountEffective(Items.MoreMissile) > 0;
			}
			return false;
		}

		private static void FireMissile(orig_FireMissile_Vector3_CharacterBody_ProcChainMask_GameObject_float_bool_GameObject_DamageColorIndex_Vector3_float_bool orig, Vector3 position, CharacterBody attackerBody, ProcChainMask procChainMask, GameObject victim, float missileDamage, bool isCrit, GameObject projectilePrefab, DamageColorIndex damageColorIndex, Vector3 initialDirection, float force, bool addMissileProc)
		{
			//IL_0058: 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_0063: Unknown result type (might be due to invalid IL or missing references)
			//IL_0065: Unknown result type (might be due to invalid IL or missing references)
			int num;
			object obj;
			if (LoopStabilizer.mergeIcbmMissiles.Value && NetworkServer.active)
			{
				num = (HasIcbm(attackerBody) ? 1 : 0);
				if (num != 0)
				{
					obj = projectilePrefab;
					goto IL_0024;
				}
			}
			else
			{
				num = 0;
			}
			obj = null;
			goto IL_0024;
			IL_0024:
			volleyPrefab = (GameObject)obj;
			volleyMissiles = 0;
			mergedHits = ((num != 0 && LoopStabilizer.mergedMissilesHitThreeTimes.Value && Object.op_Implicit((Object)(object)projectilePrefab.GetComponent<ProjectileSingleTargetImpact>())) ? 3 : 0);
			try
			{
				orig.Invoke(position, attackerBody, procChainMask, victim, missileDamage, isCrit, projectilePrefab, damageColorIndex, initialDirection, force, addMissileProc);
			}
			finally
			{
				volleyPrefab = null;
				mergedHits = 0;
			}
		}

		private static void FireProjectile(orig_FireProjectile_FireProjectileInfo orig, ProjectileManager self, FireProjectileInfo fireProjectileInfo)
		{
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: 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)
			if (Object.op_Implicit((Object)(object)volleyPrefab) && (Object)(object)fireProjectileInfo.projectilePrefab == (Object)(object)volleyPrefab)
			{
				if (volleyMissiles++ > 0)
				{
					MissilesMerged++;
					return;
				}
				if (mergedHits == 0)
				{
					fireProjectileInfo.damage *= 3f;
				}
			}
			else if (!firingOnward && IsMergedDagger(fireProjectileInfo.projectilePrefab))
			{
				FireDagger(orig, self, fireProjectileInfo);
				return;
			}
			ProjectilesFired++;
			orig.Invoke(self, fireProjectileInfo);
		}

		private static bool IsMergedDagger(GameObject prefab)
		{
			if (LoopStabilizer.mergeDaggers.Value && NetworkServer.active && (Object)(object)prefab == (Object)(object)CommonAssets.daggerPrefab)
			{
				return Object.op_Implicit((Object)(object)prefab.GetComponent<ProjectileSingleTargetImpact>());
			}
			return false;
		}

		private static void FireDagger(orig_FireProjectile_FireProjectileInfo orig, ProjectileManager self, FireProjectileInfo fireProjectileInfo)
		{
			//IL_0084: 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 (volleyDaggersLeft > 0 && Object.op_Implicit((Object)(object)volleyDagger) && Time.fixedTime == volleyDaggerTime)
			{
				volleyDaggersLeft--;
				volleyDagger.hits++;
				volleyDagger.crits.Add(fireProjectileInfo.crit);
				DaggersMerged++;
				return;
			}
			mergedHits = 1;
			mergedCrits = new List<bool>();
			lastMerged = null;
			try
			{
				ProjectilesFired++;
				orig.Invoke(self, fireProjectileInfo);
			}
			finally
			{
				mergedHits = 0;
				mergedCrits = null;
			}
			volleyDagger = lastMerged;
			volleyDaggerTime = Time.fixedTime;
			volleyDaggersLeft = 2;
		}

		private static void InitializeProjectile(orig_InitializeProjectile orig, ProjectileController projectileController, FireProjectileInfo fireProjectileInfo)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			if (mergedHits > 0)
			{
				lastMerged = ((Component)projectileController).gameObject.AddComponent<MergedMissile>();
				lastMerged.hits = mergedHits;
				lastMerged.crits = mergedCrits;
			}
			orig.Invoke(projectileController, fireProjectileInfo);
		}

		private static void OnProjectileImpact(orig_OnProjectileImpact orig, ProjectileSingleTargetImpact self, ProjectileImpactInfo impactInfo)
		{
			//IL_0008: 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)
			bool alive = self.alive;
			orig.Invoke(self, impactInfo);
			if (!alive || self.alive || !Object.op_Implicit((Object)(object)self.healthComponentTarget))
			{
				return;
			}
			MergedMissile component = ((Component)self).GetComponent<MergedMissile>();
			if (!Object.op_Implicit((Object)(object)component))
			{
				return;
			}
			for (int i = 1; i < component.hits; i++)
			{
				if (!self.healthComponentTarget.alive)
				{
					FireOnward(self, component, i);
					break;
				}
				if (component.crits != null)
				{
					self.projectileDamage.crit = component.crits[i - 1];
				}
				self.alive = true;
				orig.Invoke(self, impactInfo);
			}
		}

		private static void FireOnward(ProjectileSingleTargetImpact missile, MergedMissile merged, int hit)
		{
			//IL_008f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: 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)
			ProjectileController projectileController = missile.projectileController;
			ProjectileDamage projectileDamage = missile.projectileDamage;
			GameObject projectilePrefab = ProjectileCatalog.GetProjectilePrefab(projectileController.catalogIndex);
			if (!Object.op_Implicit((Object)(object)projectilePrefab) || !Object.op_Implicit((Object)(object)projectileController.owner) || !Object.op_Implicit((Object)(object)ProjectileManager.instance))
			{
				return;
			}
			int num = merged.hits - hit;
			if (merged.crits != null)
			{
				projectileDamage.crit = merged.crits[hit - 1];
				mergedCrits = merged.crits.GetRange(hit, num - 1);
			}
			mergedHits = ((num > 1) ? num : 0);
			firingOnward = true;
			try
			{
				ProjectileManager.instance.FireProjectile(new FireProjectileInfo
				{
					projectilePrefab = projectilePrefab,
					position = ((Component)missile).transform.position,
					rotation = ((Component)missile).transform.rotation,
					owner = projectileController.owner,
					damage = projectileDamage.damage,
					crit = projectileDamage.crit,
					force = projectileDamage.force,
					damageColorIndex = projectileDamage.damageColorIndex,
					procChainMask = projectileController.procChainMask
				});
			}
			finally
			{
				mergedHits = 0;
				mergedCrits = null;
				firingOnward = false;
			}
		}

		private static void AddOrb(orig_AddOrb orig, OrbManager self, Orb orb)
		{
			MissileVoidOrb val = (MissileVoidOrb)(object)((orb is MissileVoidOrb) ? orb : null);
			if (val != null && LoopStabilizer.mergeIcbmMissiles.Value && FollowsVolley(val))
			{
				MissilesMerged++;
				if (!LoopStabilizer.mergedMissilesHitThreeTimes.Value)
				{
					MissileVoidOrb obj = volleyOrb;
					((GenericDamageOrb)obj).damageValue = ((GenericDamageOrb)obj).damageValue + ((GenericDamageOrb)val).damageValue;
					return;
				}
				hiddenOrb = val;
			}
			OrbsAdded++;
			orig.Invoke(self, orb);
		}

		private static bool FollowsVolley(MissileVoidOrb orb)
		{
			if (volleyOrbsLeft > 0 && (Object)(object)((GenericDamageOrb)orb).attacker == (Object)(object)((GenericDamageOrb)volleyOrb).attacker && (Object)(object)((Orb)orb).target == (Object)(object)((Orb)volleyOrb).target)
			{
				volleyOrbsLeft--;
				return true;
			}
			int num;
			object obj;
			if (Object.op_Implicit((Object)(object)((GenericDamageOrb)orb).attacker))
			{
				num = (HasIcbm(((GenericDamageOrb)orb).attacker.GetComponent<CharacterBody>()) ? 1 : 0);
				if (num != 0)
				{
					obj = orb;
					goto IL_006b;
				}
			}
			else
			{
				num = 0;
			}
			obj = null;
			goto IL_006b;
			IL_006b:
			volleyOrb = (MissileVoidOrb)obj;
			volleyOrbsLeft = ((num != 0) ? 2 : 0);
			return false;
		}

		private static GameObject GetOrbEffect(orig_GetOrbEffect orig, MissileVoidOrb self)
		{
			if (self == hiddenOrb)
			{
				return null;
			}
			if (!Object.op_Implicit((Object)(object)missileVoidOrbEffect))
			{
				missileVoidOrbEffect = orig.Invoke(self);
			}
			return missileVoidOrbEffect;
		}
	}
	internal class PerfOverlay : MonoBehaviour
	{
		private const float REFRESH_INTERVAL = 0.5f;

		private const float REFERENCE_SCREEN_HEIGHT = 1080f;

		private const float MARGIN = 10f;

		private const int MOST_COMMON_PROJECTILES = 3;

		private int[] projectileCounts = Array.Empty<int>();

		private readonly StringBuilder builder = new StringBuilder();

		private readonly GUIContent content = new GUIContent();

		private GUIStyle style;

		private float elapsed;

		private float worstFrame;

		private int frames;

		private int ticks;

		private int hits;

		private int killsDelayed;

		private float tickSeconds;

		private int tickFrames;

		private int effectsShown;

		private int effectsSkipped;

		private int projectilesFired;

		private int missilesMerged;

		private int bombsMerged;

		private int boomerangsMerged;

		private int daggersMerged;

		private int orbKillsShown;

		private int orbKillsSkipped;

		private int orbsAdded;

		private int collections;

		private void Awake()
		{
			((MonoBehaviour)this).useGUILayout = false;
		}

		private void OnEnable()
		{
			GlobalEventManager.onServerDamageDealt += OnServerDamageDealt;
			GlobalEventManager.onClientDamageNotified += OnClientDamageNotified;
			Restart();
		}

		private void OnDisable()
		{
			GlobalEventManager.onServerDamageDealt -= OnServerDamageDealt;
			GlobalEventManager.onClientDamageNotified -= OnClientDamageNotified;
		}

		private void OnServerDamageDealt(DamageReport damageReport)
		{
			hits++;
		}

		private void OnClientDamageNotified(DamageDealtMessage message)
		{
			if (!NetworkServer.active)
			{
				hits++;
			}
		}

		private void FixedUpdate()
		{
			ticks++;
		}

		private void Update()
		{
			frames++;
			elapsed += Time.unscaledDeltaTime;
			worstFrame = Math.Max(worstFrame, Time.unscaledDeltaTime);
			if (TickLimiter.TickSeconds > 0f)
			{
				tickSeconds += TickLimiter.TickSeconds;
				tickFrames++;
			}
			if (elapsed >= 0.5f)
			{
				Refresh();
				Restart();
			}
		}

		private void Restart()
		{
			elapsed = 0f;
			worstFrame = 0f;
			frames = 0;
			ticks = 0;
			hits = 0;
			killsDelayed = KillChainLimiter.KillsDelayed;
			tickSeconds = 0f;
			tickFrames = 0;
			effectsShown = EffectBudget.Shown;
			effectsSkipped = EffectBudget.Skipped;
			projectilesFired = MissileHooks.ProjectilesFired;
			missilesMerged = MissileHooks.MissilesMerged;
			bombsMerged = StickyBombHooks.BombsMerged;
			boomerangsMerged = BoomerangHooks.BoomerangsMerged;
			daggersMerged = MissileHooks.DaggersMerged;
			orbKillsShown = ExperienceOrbBudget.Shown;
			orbKillsSkipped = ExperienceOrbBudget.Skipped;
			orbsAdded = MissileHooks.OrbsAdded;
			collections = GC.CollectionCount(0);
		}

		private void Refresh()
		{
			float num = 1f / elapsed;
			builder.Clear();
			builder.Append("LoopStabilizer\n");
			builder.Append($"FPS: {(float)frames * num:0} (frame {elapsed / (float)frames * 1000f:0.0} ms, worst {worstFrame * 1000f:0.0} ms)\n");
			builder.Append($"Ticks per frame: {(float)ticks / (float)frames:0.0}, game speed: {(float)ticks * Time.fixedDeltaTime * num:P0}\n");
			AppendTickTime();
			builder.Append($"Characters: {CharacterBody.readOnlyInstancesList.Count}, projectiles: {InstanceTracker.GetInstancesList<ProjectileController>().Count}\n");
			AppendMostCommonProjectiles();
			builder.Append($"Hits per second: {(float)hits * num:0}, kills that had to wait: {(float)(KillChainLimiter.KillsDelayed - killsDelayed) * num:0}\n");
			builder.Append($"Effects per second: {(float)(EffectBudget.Shown - effectsShown) * num:0} shown, {(float)(EffectBudget.Skipped - effectsSkipped) * num:0} skipped\n");
			builder.Append($"Kills with XP orbs per second: {(float)(ExperienceOrbBudget.Shown - orbKillsShown) * num:0} shown, {(float)(ExperienceOrbBudget.Skipped - orbKillsSkipped) * num:0} skipped\n");
			builder.Append($"Projectiles per second: {(float)(MissileHooks.ProjectilesFired - projectilesFired) * num:0}, orbs: {(float)(MissileHooks.OrbsAdded - orbsAdded) * num:0}\n");
			builder.Append($"Merged per second: {(float)(MissileHooks.MissilesMerged - missilesMerged) * num:0} missiles, {(float)(StickyBombHooks.BombsMerged - bombsMerged) * num:0} bombs, {(float)(BoomerangHooks.BoomerangsMerged - boomerangsMerged) * num:0} boomerangs, {(float)(MissileHooks.DaggersMerged - daggersMerged) * num:0} daggers\n");
			builder.Append($"Garbage collections per second: {(float)(GC.CollectionCount(0) - collections) * num:0.0}, memory: {GC.GetTotalMemory(forceFullCollection: false) >> 20} MB");
			content.text = builder.ToString();
		}

		private void AppendTickTime()
		{
			if (tickFrames == 0)
			{
				builder.Append("Tick: none to measure\n");
				return;
			}
			float num = tickSeconds / (float)tickFrames * 1000f;
			float num2 = elapsed / (float)frames * 1000f;
			builder.Append($"Tick: {num:0.0} ms, all ticks of a frame: {num * (float)ticks / (float)frames:0.0} of {num2:0.0} ms\n");
		}

		private void AppendMostCommonProjectiles()
		{
			List<ProjectileController> instancesList = InstanceTracker.GetInstancesList<ProjectileController>();
			if (projectileCounts.Length < ProjectileCatalog.projectilePrefabCount)
			{
				projectileCounts = new int[ProjectileCatalog.projectilePrefabCount];
			}
			Array.Clear(projectileCounts, 0, projectileCounts.Length);
			for (int i = 0; i < instancesList.Count; i++)
			{
				int catalogIndex = instancesList[i].catalogIndex;
				if (catalogIndex >= 0 && catalogIndex < projectileCounts.Length)
				{
					projectileCounts[catalogIndex]++;
				}
			}
			for (int j = 0; j < 3; j++)
			{
				int num = 0;
				for (int k = 1; k < projectileCounts.Length; k++)
				{
					if (projectileCounts[k] > projectileCounts[num])
					{
						num = k;
					}
				}
				if (projectileCounts.Length == 0 || projectileCounts[num] == 0)
				{
					break;
				}
				builder.Append((j == 0) ? "Most of them: " : ", ");
				builder.Append($"{projectileCounts[num]} {((Object)ProjectileCatalog.GetProjectilePrefab(num)).name}");
				projectileCounts[num] = 0;
			}
			builder.Append('\n');
		}

		private void OnGUI()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_007f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0084: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Unknown result type (might be due to invalid IL or missing references)
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_0021: Unknown result type (might be due to invalid IL or missing references)
			//IL_0026: 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_0035: 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_0041: Unknown result type (might be due to invalid IL or missing references)
			//IL_004b: Expected O, but got Unknown
			//IL_0050: Expected O, but got Unknown
			if ((int)Event.current.type == 7)
			{
				if (style == null)
				{
					style = new GUIStyle(GUI.skin.box)
					{
						alignment = (TextAnchor)0,
						fontSize = 14,
						wordWrap = false,
						padding = new RectOffset(8, 8, 6, 6)
					};
				}
				float num = (float)Screen.height / 1080f;
				GUI.matrix = Matrix4x4.Scale(new Vector3(num, num, 1f));
				Vector2 val = style.CalcSize(content);
				float num2 = (float)Screen.width / num - val.x - 10f;
				float num3 = (1080f - val.y) / 2f;
				GUI.Box(new Rect(num2, num3, val.x, val.y), content, style);
			}
		}
	}
	internal static class RiskOfOptionsCompat
	{
		public const string GUID = "com.rune580.riskofoptions";

		private const string ICON_FILE = "icon.png";

		public static bool Enabled => Chainloader.PluginInfos.ContainsKey("com.rune580.riskofoptions");

		[MethodImpl(MethodImplOptions.NoInlining)]
		public static void Init(string pluginLocation)
		{
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Expected O, but got Unknown
			//IL_0056: Unknown result type (might be due to invalid IL or missing references)
			//IL_0060: Expected O, but got Unknown
			//IL_0065: 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_0071: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Expected O, but got Unknown
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0090: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Expected O, but got Unknown
			//IL_009f: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Expected O, but got Unknown
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ba: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Expected O, but got Unknown
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Expected O, but got Unknown
			//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00de: Expected O, but got Unknown
			//IL_00e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ed: Expected O, but got Unknown
			//IL_00f2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fc: Expected O, but got Unknown
			//IL_0101: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: Expected O, but got Unknown
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Expected O, but got Unknown
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_0131: Expected O, but got Unknown
			//IL_0136: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Expected O, but got Unknown
			//IL_0145: Unknown result type (might be due to invalid IL or missing references)
			//IL_014a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0151: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Expected O, but got Unknown
			//IL_0159: Unknown result type (might be due to invalid IL or missing references)
			//IL_0163: Expected O, but got Unknown
			//IL_0168: Unknown result type (might be due to invalid IL or missing references)
			//IL_0172: Expected O, but got Unknown
			//IL_0177: Unknown result type (might be due to invalid IL or missing references)
			//IL_0181: Expected O, but got Unknown
			//IL_0186: Unknown result type (might be due to invalid IL or missing references)
			//IL_0190: Expected O, but got Unknown
			//IL_0195: Unknown result type (might be due to invalid IL or missing references)
			//IL_019f: Expected O, but got Unknown
			//IL_01a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01bd: Expected O, but got Unknown
			//IL_01b8: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c2: Expected O, but got Unknown
			//IL_01c7: Unknown result type (might be due to invalid IL or missing references)
			//IL_01d1: Expected O, but got Unknown
			//IL_01d6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01e0: Expected O, but got Unknown
			//IL_01e5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01ef: Expected O, but got Unknown
			//IL_01f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fe: Expected O, but got Unknown
			//IL_0203: Unknown result type (might be due to invalid IL or missing references)
			//IL_020d: Expected O, but got Unknown
			//IL_0212: Unknown result type (might be due to invalid IL or missing references)
			//IL_021c: Expected O, but got Unknown
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_022b: Expected O, but got Unknown
			//IL_0230: Unknown result type (might be due to invalid IL or missing references)
			//IL_023a: Expected O, but got Unknown
			//IL_023f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: 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_025b: Expected O, but got Unknown
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Expected O, but got Unknown
			//IL_0265: 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_0271: Unknown result type (might be due to invalid IL or missing references)
			//IL_0281: Expected O, but got Unknown
			//IL_027c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0286: Expected O, but got Unknown
			//IL_028b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0290: Unknown result type (might be due to invalid IL or missing references)
			//IL_0297: Unknown result type (might be due to invalid IL or missing references)
			//IL_02a7: Expected O, but got Unknown
			//IL_02a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_02ac: Expected O, but got Unknown
			//IL_02b1: Unknown result type (might be due to invalid IL or missing references)
			//IL_02b6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_02cd: Expected O, but got Unknown
			//IL_02c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d2: Expected O, but got Unknown
			//IL_02d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_02dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_02e3: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f3: Expected O, but got Unknown
			//IL_02ee: Unknown result type (might be due to invalid IL or missing references)
			//IL_02f8: Expected O, but got Unknown
			//IL_02fd: Unknown result type (might be due to invalid IL or missing references)
			//IL_0302: Unknown result type (might be due to invalid IL or missing references)
			//IL_0309: Unknown result type (might be due to invalid IL or missing references)
			//IL_0319: Expected O, but got Unknown
			//IL_0314: Unknown result type (might be due to invalid IL or missing references)
			//IL_031e: Expected O, but got Unknown
			//IL_0323: Unknown result type (might be due to invalid IL or missing references)
			//IL_0328: Unknown result type (might be due to invalid IL or missing references)
			//IL_032f: Unknown result type (might be due to invalid IL or missing references)
			//IL_033c: Expected O, but got Unknown
			//IL_0337: Unknown result type (might be due to invalid IL or missing references)
			//IL_0341: Expected O, but got Unknown
			ModSettingsManager.SetModDescription("Keeps long runs playable: slow motion instead of 1-2 FPS, fewer effects when there are too many of them and fewer missiles for the same damage and procs.");
			Sprite val = LoadIcon(pluginLocation);
			if (Object.op_Implicit((Object)(object)val))
			{
				ModSettingsManager.SetModIcon(val);
			}
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LoopStabilizer.limitTicks));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(LoopStabilizer.tickTimeBudget, new IntSliderConfig
			{
				min = 1,
				max = 100
			}));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LoopStabilizer.limitEffects));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(LoopStabilizer.maxEffectsPerFrame, new IntSliderConfig
			{
				min = 1,
				max = 500
			}));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(LoopStabilizer.maxSameEffectsPerFrame, new IntSliderConfig
			{
				min = 1,
				max = 100
			}));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(LoopStabilizer.maxExperienceOrbsPerSecond, new IntSliderConfig
			{
				min = 1,
				max = 1000
			}));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LoopStabilizer.mergeIcbmMissiles));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LoopStabilizer.mergedMissilesHitThreeTimes));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LoopStabilizer.mergeStickyBombs));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(LoopStabilizer.stickyBombMergeTime, new IntSliderConfig
			{
				min = 0,
				max = 2000
			}));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LoopStabilizer.mergeBoomerangs));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LoopStabilizer.limitBoomerangs));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(LoopStabilizer.maxBoomerangReturnTime, new IntSliderConfig
			{
				min = 0,
				max = 60
			}));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LoopStabilizer.boomerangLimitInRealTime));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LoopStabilizer.mergeDaggers));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LoopStabilizer.limitKillChains));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LoopStabilizer.limitBanners));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(LoopStabilizer.minBannerDistance, new IntSliderConfig
			{
				min = 1,
				max = 100
			}));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LoopStabilizer.showOverlay));
			ModSettingsManager.AddOption((BaseOption)new KeyBindOption(LoopStabilizer.toggleOverlayKey));
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(LoopStabilizer.debugKeys));
			ModSettingsManager.AddOption((BaseOption)new KeyBindOption(LoopStabilizer.addStagesKey));
			ModSettingsManager.AddOption((BaseOption)new KeyBindOption(LoopStabilizer.addTimeKey));
			ModSettingsManager.AddOption((BaseOption)new KeyBindOption(LoopStabilizer.giveItemsKey));
			ModSettingsManager.AddOption((BaseOption)new KeyBindOption(LoopStabilizer.lateGameKey));
			ModSettingsManager.AddOption((BaseOption)new KeyBindOption(LoopStabilizer.spawnEnemiesKey));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(LoopStabilizer.debugStagesPerPress, new IntSliderConfig
			{
				min = 0,
				max = 500
			}));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(LoopStabilizer.debugMinutesPerPress, new IntSliderConfig
			{
				min = 0,
				max = 1000
			}));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(LoopStabilizer.debugCommonItems, new IntSliderConfig
			{
				min = 0,
				max = 200
			}));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(LoopStabilizer.debugUncommonItems, new IntSliderConfig
			{
				min = 0,
				max = 200
			}));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(LoopStabilizer.debugLegendaryItems, new IntSliderConfig
			{
				min = 0,
				max = 200
			}));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(LoopStabilizer.debugPlasmaShrimps, new IntSliderConfig
			{
				min = 0,
				max = 200
			}));
			ModSettingsManager.AddOption((BaseOption)new IntSliderOption(LoopStabilizer.debugEnemiesPerPress, new IntSliderConfig
			{
				min = 1,
				max = 100
			}));
		}

		private static Sprite LoadIcon(string pluginLocation)
		{
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Expected O, but got Unknown
			//IL_0067: 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)
			string directoryName = Path.GetDirectoryName(pluginLocation);
			string path = Path.Combine(directoryName, "icon.png");
			if (!File.Exists(path))
			{
				path = Path.Combine(Path.GetDirectoryName(directoryName), "icon.png");
			}
			if (!File.Exists(path))
			{
				return null;
			}
			Texture2D val = new Texture2D(2, 2);
			if (!ImageConversion.LoadImage(val, File.ReadAllBytes(path)))
			{
				return null;
			}
			return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
		}
	}
	internal static class StickyBombHooks
	{
		private struct Bomb
		{
			public float damage;

			public bool crit;

			public ProcChainMask procChainMask;
		}

		private class MergedBomb : MonoBehaviour
		{
			public int victimId;

			public GameObject owner;

			public float collectsUntil;

			public List<Bomb> others;

			private void OnDestroy()
			{
				Forget(this);
			}
		}

		[CompilerGenerated]
		private static class <>O
		{
			public static hook_ProcessHitEnemy <0>__ProcessHitEnemy;

			public static hook_FireProjectile_FireProjectileInfo <1>__FireProjectile;

			public static hook_InitializeProjectile <2>__InitializeProjectile;

			public static hook_PerformDamage <3>__PerformDamage;
		}

		private const string STICKY_BOMB_PATH = "Prefabs/Projectiles/StickyBomb";

		private const float MILLISECONDS_PER_SECOND = 1000f;

		private static readonly Dictionary<int, MergedBomb> collectors = new Dictionary<int, MergedBomb>();

		private static GameObject currentVictim;

		private static bool creatingCollector;

		private static int collectorVictimId;

		private static GameObject stickyBombPrefab;

		private static bool lookedForPrefab;

		public static int BombsMerged;

		public static void Init()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//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)
			//IL_003b: Expected O, but got Unknown
			//IL_0050: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			//IL_0070: 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_007b: Expected O, but got Unknown
			object obj = <>O.<0>__ProcessHitEnemy;
			if (obj == null)
			{
				hook_ProcessHitEnemy val = ProcessHitEnemy;
				<>O.<0>__ProcessHitEnemy = val;
				obj = (object)val;
			}
			GlobalEventManager.ProcessHitEnemy += (hook_ProcessHitEnemy)obj;
			object obj2 = <>O.<1>__FireProjectile;
			if (obj2 == null)
			{
				hook_FireProjectile_FireProjectileInfo val2 = FireProjectile;
				<>O.<1>__FireProjectile = val2;
				obj2 = (object)val2;
			}
			ProjectileManager.FireProjectile_FireProjectileInfo += (hook_FireProjectile_FireProjectileInfo)obj2;
			object obj3 = <>O.<2>__InitializeProjectile;
			if (obj3 == null)
			{
				hook_InitializeProjectile val3 = InitializeProjectile;
				<>O.<2>__InitializeProjectile = val3;
				obj3 = (object)val3;
			}
			ProjectileManager.InitializeProjectile += (hook_InitializeProjectile)obj3;
			object obj4 = <>O.<3>__PerformDamage;
			if (obj4 == null)
			{
				hook_PerformDamage val4 = PerformDamage;
				<>O.<3>__PerformDamage = val4;
				obj4 = (object)val4;
			}
			ProjectileExplosion.PerformDamage += (hook_PerformDamage)obj4;
		}

		private static void ProcessHitEnemy(orig_ProcessHitEnemy orig, GlobalEventManager self, DamageInfo damageInfo, GameObject victim)
		{
			GameObject val = currentVictim;
			currentVictim = victim;
			try
			{
				orig.Invoke(self, damageInfo, victim);
			}
			finally
			{
				currentVictim = val;
			}
		}

		private static void FireProjectile(orig_FireProjectile_FireProjectileInfo orig, ProjectileManager self, FireProjectileInfo fireProjectileInfo)
		{
			//IL_002e: Unknown result type (might be due to invalid IL or missing references)
			//IL_001f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_00da: 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_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_00af: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
			if (!Object.op_Implicit((Object)(object)currentVictim) || !NetworkServer.active || !LoopStabilizer.mergeStickyBombs.Value || !IsStickyBomb(fireProjectileInfo.projectilePrefab))
			{
				orig.Invoke(self, fireProjectileInfo);
				return;
			}
			int instanceID = ((Object)currentVictim).GetInstanceID();
			if (collectors.TryGetValue(instanceID, out var value) && (Object)(object)value.owner == (Object)(object)fireProjectileInfo.owner && Time.fixedTime <= value.collectsUntil)
			{
				MergedBomb mergedBomb = value;
				if (mergedBomb.others == null)
				{
					mergedBomb.others = new List<Bomb>();
				}
				value.others.Add(new Bomb
				{
					damage = fireProjectileInfo.damage,
					crit = fireProjectileInfo.crit,
					procChainMask = fireProjectileInfo.procChainMask
				});
				BombsMerged++;
				return;
			}
			creatingCollector = true;
			collectorVictimId = instanceID;
			try
			{
				orig.Invoke(self, fireProjectileInfo);
			}
			finally
			{
				creatingCollector = false;
			}
		}

		private static void InitializeProjectile(orig_InitializeProjectile orig, ProjectileController projectileController, FireProjectileInfo fireProjectileInfo)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0025: Unknown result type (might be due to invalid IL or missing references)
			if (creatingCollector)
			{
				creatingCollector = false;
				MergedBomb mergedBomb = ((Component)projectileController).gameObject.AddComponent<MergedBomb>();
				mergedBomb.victimId = collectorVictimId;
				mergedBomb.owner = fireProjectileInfo.owner;
				mergedBomb.collectsUntil = Time.fixedTime + (float)LoopStabilizer.stickyBombMergeTime.Value / 1000f;
				collectors[collectorVictimId] = mergedBomb;
			}
			orig.Invoke(projectileController, fireProjectileInfo);
		}

		private static void PerformDamage(orig_PerformDamage orig, ProjectileExplosion self)
		{
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			MergedBomb mergedBomb = ((collectors.Count > 0) ? ((Component)self).GetComponent<MergedBomb>() : null);
			if (Object.op_Implicit((Object)(object)mergedBomb))
			{
				Forget(mergedBomb);
			}
			orig.Invoke(self);
			if (!Object.op_Implicit((Object)(object)mergedBomb) || mergedBomb.others == null)
			{
				return;
			}
			foreach (Bomb other in mergedBomb.others)
			{
				self.projectileDamage.damage = other.damage;
				self.projectileDamage.crit = other.crit;
				self.projectileController.procChainMask = other.procChainMask;
				orig.Invoke(self);
			}
		}

		private static void Forget(MergedBomb collector)
		{
			if (collectors.TryGetValue(collector.victimId, out var value) && (Object)(object)value == (Object)(object)collector)
			{
				collectors.Remove(collector.victimId);
			}
		}

		private static bool IsStickyBomb(GameObject prefab)
		{
			if (!lookedForPrefab)
			{
				lookedForPrefab = true;
				stickyBombPrefab = FindStickyBombPrefab();
			}
			if (Object.op_Implicit((Object)(object)stickyBombPrefab))
			{
				return (Object)(object)prefab == (Object)(object)stickyBombPrefab;
			}
			return false;
		}

		private static GameObject FindStickyBombPrefab()
		{
			GameObject val = LegacyResourcesAPI.Load<GameObject>("Prefabs/Projectiles/StickyBomb");
			ProjectileController val2 = (Object.op_Implicit((Object)(object)val) ? val.GetComponent<ProjectileController>() : null);
			if (!Object.op_Implicit((Object)(object)val2) || val2.procCoefficient != 0f || !Object.op_Implicit((Object)(object)val.GetComponent<ProjectileExplosion>()))
			{
				Log.Warning("The Sticky Bomb isn't what this mod expects, so its bombs are not merged");
				return null;
			}
			return val;
		}
	}
	internal static class TickLimiter
	{
		private const float MILLISECONDS_PER_SECOND = 1000f;

		private static float vanillaMaximumDeltaTime;

		private static int ticks;

		private static long firstTick;

		public static float TickSeconds { get; private set; }

		public static void Init()
		{
			vanillaMaximumDeltaTime = Time.maximumDeltaTime;
			Log.Info($"The game runs up to {Mathf.RoundToInt(vanillaMaximumDeltaTime / Time.fixedDeltaTime)} ticks per frame on its own");
		}

		public static void FixedUpdate()
		{
			if (ticks++ == 0)
			{
				firstTick = Stopwatch.GetTimestamp();
			}
		}

		public static void Update()
		{
			TickSeconds = ((ticks > 0) ? ((float)(Stopwatch.GetTimestamp() - firstTick) / (float)Stopwatch.Frequency / (float)ticks) : 0f);
			ticks = 0;
			float num = (LoopStabilizer.limitTicks.Value ? (Time.fixedDeltaTime * (float)GetMaxTicks()) : vanillaMaximumDeltaTime);
			if (Time.maximumDeltaTime != num)
			{
				Time.maximumDeltaTime = num;
			}
		}

		private static int GetMaxTicks()
		{
			int num = Mathf.RoundToInt(vanillaMaximumDeltaTime / Time.fixedDeltaTime);
			if (TickSeconds <= 0f)
			{
				return num;
			}
			return Mathf.Clamp((int)((float)LoopStabilizer.tickTimeBudget.Value / 1000f / TickSeconds), 1, num);
		}
	}
}