Decompiled source of OVERKILL v1.3.8

BepInEx/plugins/OVERKILL.dll

Decompiled 14 hours ago
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using System.Threading.Tasks;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Newtonsoft.Json;
using OVERKILL.HakitaPls;
using OVERKILL.JSON;
using OVERKILL.Patches;
using OVERKILL.UI;
using OVERKILL.UI.Upgrades;
using OVERKILL.Upgrades;
using OVERKILL.Upgrades.Cybergrind;
using OVERKILL.Upgrades.Jackhammer;
using OVERKILL.Upgrades.Music;
using OVERKILL.Upgrades.Music.UI;
using OVERKILL.Upgrades.RocketLauncher;
using OVERKILL.Utils;
using Steamworks;
using Steamworks.Data;
using TMPro;
using UnityEngine;
using UnityEngine.AI;
using UnityEngine.AddressableAssets;
using UnityEngine.Audio;
using UnityEngine.EventSystems;
using UnityEngine.Events;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.Networking;
using UnityEngine.ResourceManagement.AsyncOperations;
using UnityEngine.SceneManagement;
using UnityEngine.UI;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)]
[assembly: TargetFramework(".NETStandard,Version=v2.0", FrameworkDisplayName = "")]
[assembly: AssemblyCompany("OVERKILL")]
[assembly: AssemblyConfiguration("Debug")]
[assembly: AssemblyDescription("My first plugin")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("OVERKILL")]
[assembly: AssemblyTitle("OVERKILL")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.0.0")]
[module: UnverifiableCode]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	internal sealed class IsReadOnlyAttribute : Attribute
	{
	}
	[CompilerGenerated]
	[Microsoft.CodeAnalysis.Embedded]
	[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
	internal sealed class NullableAttribute : Attribute
	{
		public readonly byte[] NullableFlags;

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

		public NullableAttribute(byte[] P_0)
		{
			NullableFlags = P_0;
		}
	}
}
namespace OVERKILL
{
	public class CustomSound
	{
		public static Dictionary<string, AudioClip> cache = new Dictionary<string, AudioClip>();

		public static async Task<AudioClip> LoadAsync(string name)
		{
			if (cache.TryGetValue(name, out var cachedClip))
			{
				return cachedClip;
			}
			string assPath = Path.GetDirectoryName(typeof(CustomSound).Assembly.Location);
			string soundsDir = Path.Combine(assPath, "Sounds");
			string soundFilePath = Path.Combine(soundsDir, name);
			if (!File.Exists(soundFilePath))
			{
				return null;
			}
			TaskCompletionSource<AudioClip> asyncSrc = new TaskCompletionSource<AudioClip>();
			((MonoBehaviour)MonoSingleton<NewMovement>.Instance).StartCoroutine(LoadCoroutine(soundFilePath, asyncSrc));
			AudioClip clip = await asyncSrc.Task;
			cache.Add(name, clip);
			return clip;
		}

		public static async Task<AudioClip> LoadAsyncFromFile(string soundFilePath)
		{
			if (!File.Exists(soundFilePath))
			{
				return null;
			}
			TaskCompletionSource<AudioClip> asyncSrc = new TaskCompletionSource<AudioClip>();
			((MonoBehaviour)MonoSingleton<NewMovement>.Instance).StartCoroutine(LoadCoroutine(soundFilePath, asyncSrc));
			return await asyncSrc.Task;
		}

		private static IEnumerator LoadCoroutine(string soundFilePath, TaskCompletionSource<AudioClip> asyncSrc)
		{
			WWW www = new WWW("file:\\\\" + soundFilePath);
			try
			{
				yield return www;
				asyncSrc.SetResult(www.GetAudioClip());
			}
			finally
			{
				((IDisposable)www)?.Dispose();
			}
		}
	}
	public class CybergrindInnerArenaTrigger : MonoBehaviour
	{
		public static CybergrindInnerArenaTrigger Instance { get; private set; }

		public BoxCollider Collider { get; private set; }

		private void Awake()
		{
			Instance = this;
		}

		private void Start()
		{
			Collider = ((Component)this).GetComponent<BoxCollider>();
		}

		private void OnTriggerEnter(Collider other)
		{
			if (((Component)other).CompareTag("Player"))
			{
				NewMovement component = ((Component)other).GetComponent<NewMovement>();
				if (!((Object)(object)component == (Object)null))
				{
					Events.OnPlayerEnterArena.Pre?.Invoke(component);
					Events.OnPlayerEnterArena.LatePre?.Invoke(component);
					Events.OnPlayerEnterArena.Post?.Invoke(component);
				}
			}
			else if (((Component)other).CompareTag("Enemy"))
			{
				EnemyIdentifier component2 = ((Component)other).GetComponent<EnemyIdentifier>();
				if (!((Object)(object)component2 == (Object)null))
				{
					Events.OnEnemyEnterArena.Pre?.Invoke(component2);
					Events.OnEnemyEnterArena.LatePre?.Invoke(component2);
					Events.OnEnemyEnterArena.Post?.Invoke(component2);
				}
			}
		}

		private void OnTriggerExit(Collider other)
		{
			if (((Component)other).CompareTag("Player"))
			{
				NewMovement component = ((Component)other).GetComponent<NewMovement>();
				if (!((Object)(object)component == (Object)null))
				{
					Events.OnPlayerLeaveArena.Pre?.Invoke(component);
					Events.OnPlayerLeaveArena.LatePre?.Invoke(component);
					Events.OnPlayerLeaveArena.Post?.Invoke(component);
				}
			}
			else if (((Component)other).CompareTag("Enemy"))
			{
				EnemyIdentifier component2 = ((Component)other).GetComponent<EnemyIdentifier>();
				if (!((Object)(object)component2 == (Object)null))
				{
					Events.OnEnemyLeaveArena.Pre?.Invoke(component2);
					Events.OnEnemyLeaveArena.LatePre?.Invoke(component2);
					Events.OnEnemyLeaveArena.Post?.Invoke(component2);
				}
			}
		}
	}
	[HarmonyPatch(typeof(NewMovement), "Update")]
	public class DebugGiveXP
	{
		public static void Postfix()
		{
			if (Time.frameCount % 2 == 0 && Input.GetKey((KeyCode)111) && Input.GetKey((KeyCode)118) && Input.GetKey((KeyCode)101) && Input.GetKey((KeyCode)114))
			{
				int okLevel = PlayerUpgradeStats.Instance.okLevel;
				int level = okLevel - 1;
				long xPAtLevel = StyleLevelupThresholds.GetXPAtLevel(level);
				long xPAtLevel2 = StyleLevelupThresholds.GetXPAtLevel(okLevel);
				if (okLevel < 2)
				{
					MonoSingleton<StyleHUD>.Instance.AddPoints((int)Math.Max(40L, xPAtLevel2 - xPAtLevel), "O.V.E.R", (GameObject)null, (EnemyIdentifier)null, -1, "", "");
				}
				else
				{
					MonoSingleton<StyleHUD>.Instance.AddPoints((int)Math.Max(40L, (xPAtLevel2 - xPAtLevel) / 10), "O.V.E.R", (GameObject)null, (EnemyIdentifier)null, -1, "", "");
				}
			}
		}
	}
	public static class EnemyMaxHP
	{
		private static Dictionary<EnemyIdentifier, float> maxHPs = new Dictionary<EnemyIdentifier, float>(128);

		public static bool TryGet(EnemyIdentifier enemy, out float value)
		{
			return maxHPs.TryGetValue(enemy, out value);
		}

		public static void Register(EnemyIdentifier enemy, float value)
		{
			maxHPs.Add(enemy, value);
		}

		public static bool Unregister(EnemyIdentifier enemy)
		{
			bool result = maxHPs.Remove(enemy);
			if (maxHPs.Count > 120)
			{
				OK.Log($"A lot of max hp trackers remain after removing {((Object)((Component)enemy).gameObject).name}. left now: {maxHPs.Count}", (LogLevel)4);
			}
			return result;
		}
	}
	[Serializable]
	[EnumFieldLabel("values")]
	public class EnumIndexedArray<TValue, TEnum> : IEnumerable where TEnum : struct, IConvertible
	{
		public static readonly int EnumSizeCached = Enum.GetValues(typeof(TEnum)).Length;

		public TValue[] values;

		protected virtual TValue DefaultValue => default(TValue);

		public int Length => values.Length;

		public TValue this[TEnum index]
		{
			get
			{
				return values[index.ToInt32(CultureInfo.InvariantCulture)];
			}
			set
			{
				values[index.ToInt32(CultureInfo.InvariantCulture)] = value;
			}
		}

		public TValue this[int index]
		{
			get
			{
				return values[index];
			}
			set
			{
				values[index] = value;
			}
		}

		protected int EnumSize => EnumSizeCached;

		public static T Copy<T>(T original) where T : EnumIndexedArray<TValue, TEnum>, new()
		{
			T val = new T();
			Array.Copy(original.values, val.values, original.Length);
			return val;
		}

		public EnumIndexedArray(TValue[] arr)
		{
			values = arr;
		}

		public EnumIndexedArray()
		{
			values = new TValue[EnumSize];
		}

		public static implicit operator EnumIndexedArray<TValue, TEnum>(TValue v)
		{
			TValue[] array = new TValue[EnumSizeCached];
			for (int i = 0; i < array.Length; i++)
			{
				array[i] = v;
			}
			return new EnumIndexedArray<TValue, TEnum>(array);
		}

		IEnumerator IEnumerable.GetEnumerator()
		{
			return values.GetEnumerator();
		}
	}
	public class EnumFieldLabelAttribute : Attribute
	{
		public string childValuesFieldName;

		public EnumFieldLabelAttribute(string childValuesFieldName)
		{
			this.childValuesFieldName = childValuesFieldName;
		}
	}
	public struct Event<TDelegate>
	{
		public TDelegate Pre;

		public TDelegate LatePre;

		public TDelegate Post;
	}
	public static class Events
	{
		public static Event<EventDelegates.DeliverDamage> OnDealDamage;

		public static Event<EventDelegates.Death> OnEnemyDeath;

		public static Event<EventDelegates.PlayerHurt> OnPlayerHurt;

		public static Event<EventDelegates.WithNone> OnPlayerRespawn;

		public static Event<EventDelegates.WithSender<RocketLauncher>> OnFreezeStart;

		public static Event<EventDelegates.WithSender<RocketLauncher>> OnFreezeEnd;

		public static EventDelegates.HudTypeChangedEvent OnHudTypeChanged;

		public static EventDelegates.SenderEnabledEvent<Speedometer> OnSpeedometerEnabledChanged;

		public static Dictionary<string, EventDelegates.StylePointsGained> OnGainStylePoints = new Dictionary<string, EventDelegates.StylePointsGained>();

		public static Event<EventDelegates.WithSender<EndlessGrid>> OnEndWave;

		public static Event<EventDelegates.WithSender<EndlessGrid>> OnStartNewWave;

		public static Event<EventDelegates.WithSender<EndlessGrid>> OnStartValueDriftWave;

		public static Event<EventDelegates.WithSender<Grenade>> OnRocketRideStart;

		public static Event<EventDelegates.WithSender<Grenade>> OnRocketRideEnd;

		public static Event<EventDelegates.GetMaxHP> OnPlayerGetMaxHP;

		public static Event<EventDelegates.WithSender<NewMovement>> OnPlayerLeaveArena;

		public static Event<EventDelegates.WithSender<NewMovement>> OnPlayerEnterArena;

		public static Event<EventDelegates.WithSender<EnemyIdentifier>> OnEnemyLeaveArena;

		public static Event<EventDelegates.WithSender<EnemyIdentifier>> OnEnemyEnterArena;

		public static Event<EventDelegates.WithNone> OnUpdate;

		public static Event<EventDelegates.WithNone> OnFixedUpdate;
	}
	public sealed class EventDelegates
	{
		public class DeliverDamageEventData
		{
			public bool isHeadshot = false;

			public float multiplier;

			public Vector3 force;

			public Vector3 hitPoint;

			public bool tryForExplode;

			public GameObject sourceWeapon;

			public float critMultiplier = 0f;

			public Color? damageNumberColorModifier;

			public string damageNumberFormatOverride;
		}

		public class PlayerHurtEventData
		{
			public int damage;

			public float hardDamageMultiplier;

			public float scoreLossMultiplier = 1f;

			public bool ignoreInvincibility;
		}

		public delegate void WithSender<T>(T sender);

		public delegate void HudTypeChangedEvent(GameObject sender, bool isDefaultHud);

		public delegate void SenderEnabledEvent<T>(T sender, bool enabled);

		public delegate void StylePointsGained(string pointId, ref int points, ref string prefix, ref string postfix);

		public delegate void WithNone();

		public delegate void DeliverDamage(DeliverDamageEventData evntData, EnemyIdentifier enemy, GameObject target, bool ignoreTotalDamageTakenMultiplier = false, bool fromExplosion = false);

		public delegate void PlayerHurt(PlayerHurtEventData evntData, EnemyIdentifier enemy, bool explosion);

		public delegate void Death(EnemyIdentifier enemy, DeliverDamageEventData lastDamage, bool fromExplosion);

		public delegate void GetMaxHP(ref double value);
	}
	[BepInPlugin("OVERKILL", "OVERKILL", "1.0.0")]
	public class OK : BaseUnityPlugin
	{
		public static readonly OKVersion Version = new OKVersion("1.0.0");

		private static OK instance;

		public static OK Instance => instance;

		public static void Log(object data, LogLevel level = 16)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			((BaseUnityPlugin)instance).Logger.Log(level, data);
		}

		public static void LogTraced(object data, LogLevel level = 16)
		{
			//IL_000b: Unknown result type (might be due to invalid IL or missing references)
			((BaseUnityPlugin)instance).Logger.Log(level, (object)(data.ToString() + "\n" + new StackTrace().ToString()));
		}

		public static Coroutine ExecuteDelayed(float delay = 1f, Action a = null)
		{
			if (a == null)
			{
				return null;
			}
			return ((MonoBehaviour)instance).StartCoroutine(ExecuteDelayedCoroutine(delay, a));
		}

		private static IEnumerator ExecuteDelayedCoroutine(float delay, Action a)
		{
			yield return (object)new WaitForSeconds(delay);
			a?.Invoke();
		}

		private void Awake()
		{
			//IL_0013: Unknown result type (might be due to invalid IL or missing references)
			//IL_0019: Expected O, but got Unknown
			instance = this;
			Application.runInBackground = true;
			Harmony val = new Harmony("com.jaydev.overkill");
			val.PatchAll();
			DamageNumbers.Initialize();
			PlayerUpgradeStats.Initialize();
			RandomUpgrade.Initialize();
			PatchCybergrindEnemySpawning.Initialize();
			((Behaviour)MonoSingleton<PlayerDeathHandler>.Instance).enabled = true;
			if ((Object)(object)MonoSingleton<StatsManager>.Instance != (Object)null)
			{
				MonoSingleton<StatsManager>.Instance.majorUsed = true;
			}
		}

		private void Update()
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			Events.OnUpdate.Pre?.Invoke();
			if (Options.config != null && Input.GetKeyDown(Options.config.OpenRewardScreenKey))
			{
				UpgradeScreen.Instance.TryShowIfUpgradeAvailable();
			}
		}

		private void LateUpdate()
		{
			Events.OnUpdate.LatePre?.Invoke();
			Events.OnUpdate.Post?.Invoke();
		}

		private void FixedUpdate()
		{
			Events.OnFixedUpdate.Pre?.Invoke();
			Events.OnFixedUpdate.LatePre?.Invoke();
			Events.OnFixedUpdate.Post?.Invoke();
		}

		public static float Gaussian(float mean = 0f, float deviation = 1f)
		{
			float num = 1f - Random.value;
			float num2 = 1f - Random.value;
			float num3 = Mathf.Sqrt(-2f * Mathf.Log(num)) * Mathf.Sin(MathF.PI * 2f * num2);
			return mean + deviation * num3;
		}
	}
	[RequireComponent(typeof(TextMeshProUGUI))]
	public sealed class DamageNumber : FloatingText
	{
		private static Dictionary<EnemyIdentifier, DamageNumber> damageNumbersByTarget = new Dictionary<EnemyIdentifier, DamageNumber>(64);

		private float value;

		public bool isDead = false;

		protected override float SizeMultiplier => value * base.SizeMultiplier;

		public static DamageNumber CreateOrAddToExistingHit(EventDelegates.DeliverDamageEventData evntData, Transform target, EnemyIdentifier enemyId, Vector3 hitPoint, float value)
		{
			//IL_0096: 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)
			FloatingText.EnsureCanvasExists();
			if (damageNumbersByTarget.TryGetValue(enemyId, out var damageNumber))
			{
				if (damageNumber.isDead)
				{
					return damageNumber;
				}
				damageNumber.value += value;
				damageNumber.target = target;
				damageNumber.lifeTime = Mathf.Clamp(damageNumber.lifeTime + 0.15f, 1f, 2.5f);
				SetDamageNumberText(evntData, damageNumber, enemyId);
				damageNumber.startTime = Time.time - damageNumber.lifeTime * 0.032f;
				damageNumber.isDead = enemyId.dead;
				return damageNumber;
			}
			DamageNumber damageNumber2 = FloatingText.CreateNew<DamageNumber>(target, enemyId, hitPoint, Vector2.zero, 1f, null);
			damageNumber2.value = value;
			SetDamageNumberText(evntData, damageNumber2, enemyId);
			damageNumbersByTarget.Add(enemyId, damageNumber2);
			return damageNumber2;
		}

		private static void SetDamageNumberText(EventDelegates.DeliverDamageEventData evntData, DamageNumber existing, EnemyIdentifier enemyId)
		{
			//IL_00a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f4: 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_011b: Unknown result type (might be due to invalid IL or missing references)
			if (string.IsNullOrEmpty(evntData.damageNumberFormatOverride))
			{
				existing.text.text = existing.value.ToString("0.");
			}
			else
			{
				existing.text.text = string.Format(evntData.damageNumberFormatOverride, existing.value.ToString("0."));
			}
			if (enemyId.blessed)
			{
				existing.text.text = "<b>RESIST</b> (" + existing.text.text + ")";
				((Graphic)existing.text).color = Color32.op_Implicit(new Color32((byte)215, (byte)234, byte.MaxValue, byte.MaxValue));
				return;
			}
			if (evntData.isHeadshot)
			{
				((Graphic)existing.text).color = Color.yellow;
				TMP_Text obj = existing.text;
				obj.text += "!";
			}
			else
			{
				((Graphic)existing.text).color = Color.white;
			}
			if (evntData.damageNumberColorModifier.HasValue)
			{
				((Graphic)existing.text).color = evntData.damageNumberColorModifier.Value;
			}
		}

		private void OnDestroy()
		{
			damageNumbersByTarget.Remove(enemyId);
		}
	}
	public static class DamageNumbers
	{
		private static bool enemyWasDead;

		public static void Initialize()
		{
			ref EventDelegates.DeliverDamage pre = ref Events.OnDealDamage.Pre;
			pre = (EventDelegates.DeliverDamage)Delegate.Combine(pre, new EventDelegates.DeliverDamage(OnPreDeliverDamage));
			ref EventDelegates.DeliverDamage post = ref Events.OnDealDamage.Post;
			post = (EventDelegates.DeliverDamage)Delegate.Combine(post, new EventDelegates.DeliverDamage(OnDeliverDamage));
		}

		private static void OnPreDeliverDamage(EventDelegates.DeliverDamageEventData evntData, EnemyIdentifier enemy, GameObject target, bool ignoretotaldamagetakenmultiplier, bool fromexplosion)
		{
			enemyWasDead = (Object)(object)enemy == (Object)null || enemy.dead;
		}

		private static void OnDeliverDamage(EventDelegates.DeliverDamageEventData evntData, EnemyIdentifier enemy, GameObject target, bool ignoretotaldamagetakenmultiplier, bool fromexplosion)
		{
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			if (evntData.multiplier > 0f && !enemyWasDead && !Options.config.hideDamageNumbers)
			{
				DamageNumber.CreateOrAddToExistingHit(evntData, target.transform, enemy, evntData.hitPoint, evntData.multiplier * 10f);
			}
		}
	}
	public static class Util
	{
		public static bool TryGetComponent<T>(this GameObject go, out T comp) where T : Component
		{
			comp = go.GetComponent<T>();
			return (Object)(object)comp == (Object)null;
		}

		public static bool TryGetComponent<T>(this Component go, out T comp) where T : Component
		{
			comp = go.GetComponent<T>();
			return (Object)(object)comp == (Object)null;
		}

		public static string GetGameObjectScenePath(this GameObject go)
		{
			string text = string.Empty;
			Transform val = go.transform;
			while ((Object)(object)val != (Object)null)
			{
				if (text.Length > 0)
				{
					text = "/" + text;
				}
				text = ((Object)((Component)val).gameObject).name + text;
				val = val.parent;
			}
			return text;
		}
	}
	public interface IRandomWeight
	{
		double Weight { get; }
	}
	public struct FixedRandomWeight : IRandomWeight
	{
		public double Weight { get; }

		public FixedRandomWeight(double weight = 1.0)
		{
			Weight = weight;
		}
	}
	public class WeightedRandom<T>
	{
		public struct Entry
		{
			public T value;

			public IRandomWeight weight;
		}

		private List<Entry> _entries;

		public double weightSum;

		public Random rnd;

		public static int numFuckedSounds;

		public WeightedRandom(Random rnd)
		{
			this.rnd = rnd;
			_entries = new List<Entry>();
		}

		public void Clear()
		{
			weightSum = 0.0;
			_entries.Clear();
		}

		public void AddEntry(T value, IRandomWeight weight)
		{
			_entries.Add(new Entry
			{
				value = value,
				weight = weight
			});
			weightSum += weight.Weight;
		}

		public bool Any()
		{
			return _entries.Any();
		}

		public T Get()
		{
			double num = rnd.NextDouble() * weightSum;
			foreach (Entry entry in _entries)
			{
				num -= entry.weight.Weight;
				if (num <= 0.0)
				{
					return entry.value;
				}
			}
			throw new Exception($"WTF, got rnd {num} left, {_entries.Count} entries");
		}
	}
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "OVERKILL";

		public const string PLUGIN_NAME = "OVERKILL";

		public const string PLUGIN_VERSION = "1.0.0";
	}
}
namespace OVERKILL.Utils
{
	public class DetectOnEnableDisableComponent : MonoBehaviour
	{
		public Action onEnable;

		public Action onDisable;

		private void OnEnable()
		{
			onEnable?.Invoke();
		}

		private void OnDisable()
		{
			onDisable?.Invoke();
		}
	}
	public static class EnemyTypeExtensions
	{
		public static PrefabID GetPrefabID(this EnemyType enemyType)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected I4, but got Unknown
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Invalid comparison between Unknown and I4
			if (1 == 0)
			{
			}
			PrefabID result = (int)enemyType switch
			{
				37 => PrefabID.BigJohnator, 
				23 => PrefabID.CancerousRodent, 
				35 => PrefabID.Centaur, 
				0 => PrefabID.Cerberus, 
				1 => PrefabID.Drone, 
				26 => PrefabID.Ferryman, 
				3 => PrefabID.Filth, 
				30 => PrefabID.FleshPanopticon, 
				17 => PrefabID.FleshPrison, 
				16 => PrefabID.Gabriel, 
				28 => PrefabID.GabrielSecond, 
				33 => PrefabID.Gutterman, 
				34 => PrefabID.Guttertank, 
				2 => PrefabID.HideousMass, 
				21 => PrefabID.Idol, 
				27 => PrefabID.Leviathan, 
				4 => PrefabID.MaliciousFace, 
				25 => PrefabID.Mandalore, 
				31 => PrefabID.Mannequin, 
				5 => PrefabID.Mindflayer, 
				11 => PrefabID.Minos, 
				18 => PrefabID.MinosPrime, 
				32 => PrefabID.Minotaur, 
				36 => PrefabID.Puppet, 
				14 => PrefabID.Schism, 
				19 => PrefabID.Sisyphus, 
				29 => PrefabID.SisyphusPrime, 
				15 => PrefabID.Soldier, 
				12 => PrefabID.Stalker, 
				13 => PrefabID.Stray, 
				6 => PrefabID.Streetcleaner, 
				7 => PrefabID.Swordsmachine, 
				20 => PrefabID.Turret, 
				8 => PrefabID.V2, 
				22 => PrefabID.V2Second, 
				24 => PrefabID.VeryCancerousRodent, 
				9 => PrefabID.Virtue, 
				10 => PrefabID.Wicked, 
				_ => ((int)enemyType != 1000) ? PrefabID.Filth : PrefabID.KITR, 
			};
			if (1 == 0)
			{
			}
			return result;
		}

		public static string GetName(this EnemyType enemyType)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a3: Expected I4, but got Unknown
			//IL_00a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Invalid comparison between Unknown and I4
			if (1 == 0)
			{
			}
			string result = (int)enemyType switch
			{
				37 => "Big Johninator", 
				23 => "Cancerous Rodent", 
				35 => "Centaur", 
				0 => "Cerberus", 
				1 => "Drone", 
				26 => "Ferryman", 
				3 => "Filth", 
				30 => "Flesh Panopticon", 
				17 => "Flesh Prison", 
				16 => "Gabriel", 
				28 => "GabrielSecond", 
				33 => "Gutterman", 
				34 => "Guttertank", 
				2 => "Hideous Mass", 
				21 => "Idol", 
				27 => "Leviathan", 
				4 => "Malicious Face", 
				25 => "Mandalore", 
				31 => "Mannequin", 
				5 => "Mindflayer", 
				11 => "Minos", 
				18 => "Minos Prime", 
				32 => "Minotaur", 
				36 => "Puppet", 
				14 => "Schism", 
				19 => "Sisyphus", 
				29 => "Sisyphus Prime", 
				15 => "Soldier", 
				12 => "Stalker", 
				13 => "Stray", 
				6 => "Streetcleaner", 
				7 => "Swordsmachine", 
				20 => "Sentry", 
				8 => "V2", 
				22 => "V2-2", 
				24 => "Very Cancerous Rodent", 
				9 => "Virtue", 
				10 => "Wicked", 
				_ => ((int)enemyType != 1000) ? "UNKNOWN" : "KITR", 
			};
			if (1 == 0)
			{
			}
			return result;
		}
	}
	public struct OKVersion : IEquatable<OKVersion>, IComparable<OKVersion>
	{
		public byte major;

		public byte minor;

		public byte revision;

		public override string ToString()
		{
			return $"{major}.{minor}.{revision}";
		}

		public OKVersion(string s)
		{
			string[] array = s.Split(new char[1] { '.' });
			major = byte.Parse(array[0]);
			minor = byte.Parse(array[1]);
			revision = byte.Parse(array[2]);
		}

		public OKVersion(byte major, byte minor = 0, byte revision = 0)
		{
			this.major = major;
			this.minor = minor;
			this.revision = revision;
		}

		public bool Equals(OKVersion other)
		{
			return major == other.major && minor == other.minor && revision == other.revision;
		}

		public override bool Equals(object obj)
		{
			return obj is OKVersion other && Equals(other);
		}

		public override int GetHashCode()
		{
			return revision + (minor << 8) + (major << 16);
		}

		public int CompareTo(OKVersion other)
		{
			return GetHashCode().CompareTo(other.GetHashCode());
		}
	}
	public enum PrefabID
	{
		Cerberus,
		Drone,
		DroneSkull,
		DroneFlesh,
		HideousMass,
		Filth,
		MaliciousFace,
		Mindflayer,
		Streetcleaner,
		Swordsmachine,
		V2,
		V2GreenArmVariant,
		Virtue,
		Wicked,
		Minos,
		Stalker,
		Stray,
		Schism,
		Soldier,
		Gabriel,
		FleshPrison,
		MinosPrime,
		Sisyphus,
		Turret,
		Idol,
		V2Second,
		CancerousRodent,
		VeryCancerousRodent,
		Mandalore,
		Ferryman,
		Leviathan,
		GabrielSecond,
		SisyphusPrime,
		FleshPanopticon,
		Mannequin,
		Minotaur,
		Gutterman,
		Guttertank,
		Centaur,
		Puppet,
		BigJohnator,
		DualWieldPowerup,
		SuperchargeHealth,
		Mine,
		KITR,
		Soap,
		Hampter,
		BlackHoleEnemy
	}
	public static class Prefabs
	{
		private static readonly List<IPrefabCategorizer> categorizers;

		public static Dictionary<PrefabID, List<GameObject>> DB { get; private set; }

		public static bool TryGet(PrefabID prefabID, out GameObject go)
		{
			if (DB.TryGetValue(prefabID, out var value))
			{
				go = value.First();
				return true;
			}
			go = null;
			return false;
		}

		public static IEnumerable<GameObject> GetAllRootPrefabs()
		{
			return from go in Resources.FindObjectsOfTypeAll<GameObject>()
				where (Object)(object)go.transform.parent == (Object)null
				select go;
		}

		static Prefabs()
		{
			OK.Log("discovering prefabs...", (LogLevel)16);
			Stopwatch stopwatch = Stopwatch.StartNew();
			categorizers = new List<IPrefabCategorizer>();
			Type[] types = typeof(Prefabs).Assembly.GetTypes();
			foreach (Type type in types)
			{
				if (typeof(IPrefabCategorizer).IsAssignableFrom(type) && !type.IsAbstract)
				{
					categorizers.Add((IPrefabCategorizer)Activator.CreateInstance(type));
				}
			}
			DB = new Dictionary<PrefabID, List<GameObject>>(512);
			foreach (GameObject allRootPrefab in GetAllRootPrefabs())
			{
				if ((Object)(object)allRootPrefab.transform.parent != (Object)null)
				{
					continue;
				}
				foreach (IPrefabCategorizer categorizer in categorizers)
				{
					PrefabID? prefabID = categorizer.Categorize(allRootPrefab);
					if (prefabID.HasValue)
					{
						if (!DB.TryGetValue(prefabID.Value, out var value))
						{
							value = new List<GameObject>();
							DB.Add(prefabID.Value, value);
						}
						value.Add(allRootPrefab);
					}
				}
			}
			stopwatch.Stop();
			OK.Log($"prefab discovery too {stopwatch.ElapsedMilliseconds}ms", (LogLevel)16);
		}
	}
	public interface IPrefabCategorizer
	{
		PrefabID? Categorize(GameObject prefab);
	}
	public abstract class PrefabCategorizer<TSelf> : IPrefabCategorizer where TSelf : PrefabCategorizer<TSelf>
	{
		public static readonly List<GameObject> prefabs = new List<GameObject>();

		public PrefabID? Categorize(GameObject prefab)
		{
			PrefabID? result = OnCategorize(prefab);
			if (result.HasValue)
			{
				prefabs.Add(prefab);
			}
			return result;
		}

		public abstract PrefabID? OnCategorize(GameObject prefab);
	}
	public static class TransformExtensions
	{
		public static void SetGlobalScale(this Transform transform, Vector3 globalScale)
		{
			//IL_0002: Unknown result type (might be due to invalid IL or missing references)
			//IL_000e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_0032: Unknown result type (might be due to invalid IL or missing references)
			//IL_0039: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			transform.localScale = Vector3.one;
			transform.localScale = new Vector3(globalScale.x / transform.lossyScale.x, globalScale.y / transform.lossyScale.y, globalScale.z / transform.lossyScale.z);
		}
	}
}
namespace OVERKILL.Utils.Categorizers
{
	public class EnemyCategorizer : PrefabCategorizer<EnemyCategorizer>
	{
		public const int KITREnemyTypeId = 1000;

		public override PrefabID? OnCategorize(GameObject prefab)
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Invalid comparison between Unknown and I4
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0089: Invalid comparison between Unknown and I4
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Invalid comparison between Unknown and I4
			//IL_0180: Unknown result type (might be due to invalid IL or missing references)
			//IL_0186: Invalid comparison between Unknown and I4
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			EnemyIdentifier component = prefab.GetComponent<EnemyIdentifier>();
			if ((Object)(object)component == (Object)null)
			{
				BlackHoleProjectile component2 = prefab.GetComponent<BlackHoleProjectile>();
				if ((Object)(object)component2 != (Object)null)
				{
					return PrefabID.BlackHoleEnemy;
				}
				return null;
			}
			if ((int)component.enemyType == 3)
			{
				if ((Object)(object)((Component)component).GetComponent<Zombie>() == (Object)null)
				{
					return PrefabID.CancerousRodent;
				}
				return PrefabID.Filth;
			}
			if ((int)component.enemyType == 8)
			{
				if (((Object)((Component)component).gameObject).name.Contains("Green Arm"))
				{
					return PrefabID.V2GreenArmVariant;
				}
				if (((Object)((Component)component).gameObject).name.Contains("Big Johninator"))
				{
					return PrefabID.BigJohnator;
				}
				return PrefabID.V2;
			}
			if ((int)component.enemyType == 1)
			{
				if (((Object)((Component)component).gameObject).name.Contains("Mandalore"))
				{
					return PrefabID.Mandalore;
				}
				if (((Object)((Component)component).gameObject).name.Contains("DroneSkull"))
				{
					return PrefabID.DroneSkull;
				}
				if (((Object)((Component)component).gameObject).name.Contains("DroneFlesh"))
				{
					return PrefabID.DroneFlesh;
				}
				return PrefabID.Drone;
			}
			if ((int)component.enemyType == 0 && ((Object)((Component)component).gameObject).name.Contains("Rodent"))
			{
				return PrefabID.VeryCancerousRodent;
			}
			return component.enemyType.GetPrefabID();
		}
	}
	public class MiscCategorizer : IPrefabCategorizer
	{
		public static GameObject mine;

		public static GameObject kitr;

		public static GameObject soap;

		public PrefabID? Categorize(GameObject prefab)
		{
			Landmine component = prefab.GetComponent<Landmine>();
			if ((Object)(object)component != (Object)null)
			{
				mine = prefab;
				return PrefabID.Mine;
			}
			if (((Object)prefab.gameObject).name == "KITR")
			{
				kitr = prefab;
				return PrefabID.KITR;
			}
			Soap component2 = prefab.GetComponent<Soap>();
			if ((Object)(object)component2 != (Object)null)
			{
				soap = prefab;
				return PrefabID.Soap;
			}
			return null;
		}
	}
	public class PowerupCategorizer : PrefabCategorizer<PowerupCategorizer>
	{
		public override PrefabID? OnCategorize(GameObject prefab)
		{
			DualWieldPickup component = prefab.GetComponent<DualWieldPickup>();
			if ((Object)(object)component != (Object)null)
			{
				return PrefabID.DualWieldPowerup;
			}
			Bonus component2 = prefab.GetComponent<Bonus>();
			if ((Object)(object)component2 != (Object)null && ((Object)((Component)component2).gameObject).name.Equals("BonusGhostSuperCharge"))
			{
				return PrefabID.SuperchargeHealth;
			}
			return null;
		}
	}
}
namespace OVERKILL.Upgrades
{
	public class BallEnjoyerUpgrade : LeveledUpgrade
	{
		public override string Name => "Ball Enjoyer";

		public override Rarity Rarity => Rarity.Rare;

		public override Rarity MinRarity => Rarity.Rare;

		public override Rarity MaxRarity => Rarity.Rare;

		public override int MaxLevel => 1;

		public override string Description => "The mindflayer will now go out of her way to teleport into the trajectory of a parried insurrectionist's... maurice ball?\n<i>Let's see what else there to say... ah yes, give her head.</i>\n\nI'll see myself out now.";

		public override double AppearChanceWeighting => base.AppearChanceWeighting * 0.25 * 0.800000011920929;

		public override void Apply()
		{
			BallEnjoyerPatches.enabled = true;
		}

		public override void Absolve()
		{
			BallEnjoyerPatches.enabled = false;
		}
	}
	[HarmonyPatch]
	public static class BallEnjoyerPatches
	{
		public static bool enabled = false;

		public static HashSet<Sisyphus> parriedSisyphi = new HashSet<Sisyphus>();

		[HarmonyPatch(typeof(Sisyphus), "GotParried")]
		[HarmonyPostfix]
		public static void OnParried(Sisyphus __instance)
		{
			if (enabled)
			{
				parriedSisyphi.Add(__instance);
			}
		}

		[HarmonyPatch(typeof(Sisyphus), "TryToRetractArm")]
		[HarmonyPostfix]
		public static void TryToRetractArm(Sisyphus __instance)
		{
			if (enabled)
			{
				parriedSisyphi.Remove(__instance);
			}
		}

		[HarmonyPatch(typeof(Sisyphus), "SwingStop")]
		[HarmonyPostfix]
		public static void SwingStop(Sisyphus __instance)
		{
			if (enabled)
			{
				parriedSisyphi.Remove(__instance);
			}
		}

		[HarmonyPatch(typeof(Mindflayer), "Start")]
		[HarmonyPostfix]
		public static void MindflayerStart(Mindflayer __instance)
		{
			ComponentExtensions.GetOrAddComponent<BallEnjoyer>((Component)(object)__instance);
		}
	}
	public class BallEnjoyer : MonoBehaviour
	{
		private float lastTeleportTime = 0f;

		private void Update()
		{
			if ((!BallEnjoyerPatches.enabled || BallEnjoyerPatches.parriedSisyphi.Count > 0) && Time.frameCount % (((Object)((Component)this).gameObject).GetInstanceID() % 30) == 0 && Time.time - lastTeleportTime > 1.5f)
			{
				TeleportToBall();
			}
		}

		private void TeleportToBall()
		{
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0052: 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_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: 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_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0085: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0094: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e8: Unknown result type (might be due to invalid IL or missing references)
			//IL_0108: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			lastTeleportTime = Time.time;
			EnemyIdentifier component = ((Component)this).GetComponent<EnemyIdentifier>();
			if (component.hooked)
			{
				return;
			}
			Sisyphus val = BallEnjoyerPatches.parriedSisyphi.ElementAt(Random.Range(0, BallEnjoyerPatches.parriedSisyphi.Count));
			Cannonball componentInChildren = ((Component)val).GetComponentInChildren<Cannonball>();
			Rigidbody component2 = ((Component)componentInChildren).GetComponent<Rigidbody>();
			Vector3 val2 = Vector3.zero;
			bool flag = false;
			for (int i = 1; i < 6; i++)
			{
				val2 = component2.position + component2.velocity * 0.25f * (float)i * 0.1f;
				if (!Physics.CheckSphere(val2, 0.1f, LayerMask.op_Implicit(LayerMaskDefaults.Get((LMD)2)), (QueryTriggerInteraction)1))
				{
					flag = true;
					break;
				}
			}
			if (flag)
			{
				Mindflayer component3 = ((Component)this).GetComponent<Mindflayer>();
				component3.StopAction();
				((Component)this).transform.position = val2;
				componentInChildren.damage = 35f;
				Object.Instantiate<GameObject>(component3.teleportSound, ((Component)this).transform.position, Quaternion.identity);
				MonoSingleton<StyleHUD>.Instance.AddPoints(150, "BALL ENJOYER", (GameObject)null, component, -1, "", "");
			}
		}
	}
	public class Arrival : Bane, IRandomizable
	{
		private PrefabID prefabID;

		[JsonIgnore]
		private EnemyIdentifier currBoss;

		private static readonly PrefabID[] availablePrefabs = new PrefabID[1] { PrefabID.Minos };

		public override string Name => "Arrival";

		public override string Description => $"{prefabID} attacks whenever you experience value drift.";

		public override double AppearChanceWeighting => base.AppearChanceWeighting * 0.75;

		public override bool IsObtainable => base.IsObtainable && (Object)(object)MonoSingleton<EndlessGrid>.Instance != (Object)null && MonoSingleton<EndlessGrid>.Instance.currentWave > MonoSingleton<EndlessGrid>.Instance.startWave;

		public override void Apply()
		{
			ref EventDelegates.WithSender<EndlessGrid> post = ref Events.OnStartValueDriftWave.Post;
			post = (EventDelegates.WithSender<EndlessGrid>)Delegate.Combine(post, new EventDelegates.WithSender<EndlessGrid>(OnValueDrift));
		}

		public override void Absolve()
		{
			ref EventDelegates.WithSender<EndlessGrid> post = ref Events.OnStartValueDriftWave.Post;
			post = (EventDelegates.WithSender<EndlessGrid>)Delegate.Remove(post, new EventDelegates.WithSender<EndlessGrid>(OnValueDrift));
		}

		private void OnValueDrift(EndlessGrid sender)
		{
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_0191: Unknown result type (might be due to invalid IL or missing references)
			//IL_0196: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
			//IL_01a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_01aa: Unknown result type (might be due to invalid IL or missing references)
			//IL_01fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0204: Unknown result type (might be due to invalid IL or missing references)
			//IL_0209: 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_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)currBoss != (Object)null && !((Component)currBoss).GetComponent<EnemyIdentifier>().dead)
			{
				return;
			}
			if (!Prefabs.TryGet(prefabID, out var go))
			{
				switch (prefabID)
				{
				case PrefabID.Minos:
				{
					MinosBoss val2 = default(MinosBoss);
					foreach (GameObject allRootPrefab in Prefabs.GetAllRootPrefabs())
					{
						if (allRootPrefab.TryGetComponent<MinosBoss>(ref val2))
						{
							go = allRootPrefab;
							List<GameObject> list2 = new List<GameObject>();
							list2.Add(go);
							Prefabs.DB.Add(PrefabID.Minos, list2);
						}
					}
					break;
				}
				case PrefabID.Leviathan:
				{
					LeviathanController val = default(LeviathanController);
					foreach (GameObject allRootPrefab2 in Prefabs.GetAllRootPrefabs())
					{
						if (allRootPrefab2.TryGetComponent<LeviathanController>(ref val))
						{
							go = allRootPrefab2;
							List<GameObject> list = new List<GameObject>();
							list.Add(go);
							Prefabs.DB.Add(PrefabID.Leviathan, list);
						}
					}
					break;
				}
				default:
					OK.Log($"Can't locate {prefabID}!", (LogLevel)2);
					return;
				}
			}
			if ((Object)(object)go != (Object)null)
			{
				switch (prefabID)
				{
				case PrefabID.Minos:
				{
					currBoss = Object.Instantiate<GameObject>(go, Vector3.down * 565f + Vector3.back * 120f, Quaternion.identity).GetComponent<EnemyIdentifier>();
					Statue statue = currBoss.statue;
					statue.health *= 2f;
					Statue statue2 = currBoss.statue;
					statue2.originalHealth *= 2f;
					break;
				}
				case PrefabID.Leviathan:
					currBoss = Object.Instantiate<GameObject>(go, Vector3.down * 565f + Vector3.back * 120f, Quaternion.identity).GetComponent<EnemyIdentifier>();
					break;
				}
			}
			else
			{
				OK.Log($"NO {prefabID} FOUND!", (LogLevel)2);
			}
		}

		public void Randomize(int seed)
		{
			prefabID = availablePrefabs[Random.Range(0, availablePrefabs.Length)];
		}
	}
	public class DelayBanes : Bane
	{
		public override string Name => "Delay the Inevitable";

		public override string Description => $"This value drift has no effect. Your next value drift is also delayed by {UpgradeScreen.valueDriftWaveInterval} waves.\nHowever the next time you do experience value drift you automatically have to choose all three options.";

		public override bool IsObtainable => base.IsObtainable && UpgradeScreen.Instance.numValueDriftSkips <= 0 && !UpgradeScreen.Instance.autoChooseAllBaneOptions;

		public override void Apply()
		{
			UpgradeScreen.Instance.autoChooseAllBaneOptions = true;
			UpgradeScreen.Instance.numValueDriftSkips = 1;
			PlayerUpgradeStats.Instance.upgrades.Remove(GetHashCode());
		}

		public override void Absolve()
		{
			UpgradeScreen.Instance.autoChooseAllBaneOptions = false;
			UpgradeScreen.Instance.numValueDriftSkips = 0;
		}
	}
	public class ForcedInvasionBane : Bane, IRandomizable
	{
		private SpawnBossUpgrade invasion;

		public override string Name => invasion.Name;

		public override string Description => invasion.Description;

		public override double AppearChanceWeighting => base.AppearChanceWeighting * 1.5;

		public override void Apply()
		{
			invasion.Apply();
		}

		public override void Absolve()
		{
			invasion.Absolve();
		}

		public void Randomize(int seed)
		{
			invasion = new SpawnBossUpgrade();
			invasion.level = 1;
			invasion.Randomize(seed);
		}

		public override int GetHashCode()
		{
			return (invasion != null) ? invasion.GetHashCode() : 0;
		}
	}
	public class GlassCannon : Bane
	{
		public override string Name => "Glass Cannon";

		public override string Description => $"Reduce your maximum HP by {GetReductionPct()}% (1% per wave) up to a maximum of 80%. You however deal additional damage equal to that percentage.\nThe HP reduction factors in any upgrades to your maximum HP. The modifier is applied after all your other upgrades.";

		public int GetReductionPct()
		{
			if ((Object)(object)MonoSingleton<EndlessGrid>.Instance != (Object)null)
			{
				return Math.Min(80, Math.Max(1, MonoSingleton<EndlessGrid>.Instance.currentWave));
			}
			return 80;
		}

		public override void Apply()
		{
			ref EventDelegates.WithSender<EndlessGrid> post = ref Events.OnEndWave.Post;
			post = (EventDelegates.WithSender<EndlessGrid>)Delegate.Combine(post, new EventDelegates.WithSender<EndlessGrid>(OnEndWave));
			ref EventDelegates.GetMaxHP post2 = ref Events.OnPlayerGetMaxHP.Post;
			post2 = (EventDelegates.GetMaxHP)Delegate.Combine(post2, new EventDelegates.GetMaxHP(OnGetMaxHP));
			ref EventDelegates.DeliverDamage latePre = ref Events.OnDealDamage.LatePre;
			latePre = (EventDelegates.DeliverDamage)Delegate.Combine(latePre, new EventDelegates.DeliverDamage(OnDealDamage));
		}

		private void OnDealDamage(EventDelegates.DeliverDamageEventData evntdata, EnemyIdentifier enemy, GameObject target, bool ignoretotaldamagetakenmultiplier, bool fromexplosion)
		{
			//IL_0023: 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)
			evntdata.multiplier *= 1f + (float)GetReductionPct() * 0.01f;
			evntdata.damageNumberColorModifier = RarityColor.Get(Rarity.ValueDrift) * 2.5f;
		}

		public override void Absolve()
		{
			ref EventDelegates.WithSender<EndlessGrid> post = ref Events.OnEndWave.Post;
			post = (EventDelegates.WithSender<EndlessGrid>)Delegate.Remove(post, new EventDelegates.WithSender<EndlessGrid>(OnEndWave));
			ref EventDelegates.GetMaxHP post2 = ref Events.OnPlayerGetMaxHP.Post;
			post2 = (EventDelegates.GetMaxHP)Delegate.Remove(post2, new EventDelegates.GetMaxHP(OnGetMaxHP));
			ref EventDelegates.DeliverDamage latePre = ref Events.OnDealDamage.LatePre;
			latePre = (EventDelegates.DeliverDamage)Delegate.Remove(latePre, new EventDelegates.DeliverDamage(OnDealDamage));
		}

		private void OnGetMaxHP(ref double value)
		{
			value *= 1.0 - (double)GetReductionPct() * 0.01;
		}

		private void OnEndWave(EndlessGrid sender)
		{
			OK.Log("WAVE END: " + sender.currentWave, (LogLevel)16);
			MonoSingleton<NewMovement>.Instance.hp = (int)PlayerUpgradeStats.Instance.GetMaxHP();
		}
	}
	public class IdolBuffBane : Bane
	{
		public static bool proccedPlayerSaveThisWave;

		public override double AppearChanceWeighting => base.AppearChanceWeighting * 0.95;

		public override string Name => "Unfinished Business";

		public override string Description => $"Whenever an enemy is damaged,\na {GetHurtTime():0.##} (2 - 0.02/wave, minimum 1.5) second timer starts. If they are damaged again after the timer ends, they gain an idol buff for 4 seconds.\n\nWhen you are about to die from incoming damage, gain the same buff and reset your hard damage. This effect can only occur once per wave.";

		public float GetHurtTime()
		{
			if ((Object)(object)MonoSingleton<EndlessGrid>.Instance == (Object)null)
			{
				return 2.5f;
			}
			return Mathf.Max(1f, 3f - (float)MonoSingleton<EndlessGrid>.Instance.currentWave * 0.03f);
		}

		public static async void PlaySound()
		{
			int num = Random.Range(1, 7);
			OneShotAudioExtension.PlayClipAtPoint(await CustomSound.LoadAsync($"deathimmanent{num}.wav"), (AudioMixerGroup)null, Vector3.zero, 128, 0f, 1f, 1f, (AudioRolloffMode)1, 0f, 1000f);
		}

		public override void Apply()
		{
			ref EventDelegates.DeliverDamage latePre = ref Events.OnDealDamage.LatePre;
			latePre = (EventDelegates.DeliverDamage)Delegate.Combine(latePre, new EventDelegates.DeliverDamage(OnDealDamage));
			ref EventDelegates.PlayerHurt pre = ref Events.OnPlayerHurt.Pre;
			pre = (EventDelegates.PlayerHurt)Delegate.Combine(pre, new EventDelegates.PlayerHurt(PreventDamageTakenIfBlessed));
			ref EventDelegates.PlayerHurt latePre2 = ref Events.OnPlayerHurt.LatePre;
			latePre2 = (EventDelegates.PlayerHurt)Delegate.Combine(latePre2, new EventDelegates.PlayerHurt(OnPlayerDamageTaken));
			ref EventDelegates.WithSender<EndlessGrid> post = ref Events.OnStartNewWave.Post;
			post = (EventDelegates.WithSender<EndlessGrid>)Delegate.Combine(post, new EventDelegates.WithSender<EndlessGrid>(OnStartNewWave));
		}

		public override void Absolve()
		{
			ref EventDelegates.DeliverDamage latePre = ref Events.OnDealDamage.LatePre;
			latePre = (EventDelegates.DeliverDamage)Delegate.Remove(latePre, new EventDelegates.DeliverDamage(OnDealDamage));
			ref EventDelegates.PlayerHurt pre = ref Events.OnPlayerHurt.Pre;
			pre = (EventDelegates.PlayerHurt)Delegate.Remove(pre, new EventDelegates.PlayerHurt(PreventDamageTakenIfBlessed));
			ref EventDelegates.PlayerHurt latePre2 = ref Events.OnPlayerHurt.LatePre;
			latePre2 = (EventDelegates.PlayerHurt)Delegate.Remove(latePre2, new EventDelegates.PlayerHurt(OnPlayerDamageTaken));
			ref EventDelegates.WithSender<EndlessGrid> post = ref Events.OnStartNewWave.Post;
			post = (EventDelegates.WithSender<EndlessGrid>)Delegate.Remove(post, new EventDelegates.WithSender<EndlessGrid>(OnStartNewWave));
		}

		private void PreventDamageTakenIfBlessed(EventDelegates.PlayerHurtEventData evntdata, EnemyIdentifier enemy, bool explosion)
		{
			if ((Object)(object)((Component)MonoSingleton<NewMovement>.Instance).GetComponent<TemporaryPlayerBless>() != (Object)null)
			{
				evntdata.damage = 0;
				MonoSingleton<NewMovement>.Instance.ResetHardDamage();
				evntdata.ignoreInvincibility = false;
				evntdata.hardDamageMultiplier = 0f;
				evntdata.scoreLossMultiplier = 0f;
			}
		}

		private void OnStartNewWave(EndlessGrid sender)
		{
			proccedPlayerSaveThisWave = false;
		}

		private void OnPlayerDamageTaken(EventDelegates.PlayerHurtEventData evntdata, EnemyIdentifier enemy, bool explosion)
		{
			//IL_005f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0064: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Unknown result type (might be due to invalid IL or missing references)
			if (MonoSingleton<NewMovement>.Instance.hp <= evntdata.damage && !proccedPlayerSaveThisWave)
			{
				proccedPlayerSaveThisWave = true;
				evntdata.damage = MonoSingleton<NewMovement>.Instance.hp - 1;
				TMP_Text val = PatchCybergrindEnemySpawning.DisplayInvasionText("<color=red><size=150%>DEATH IMMANENT!</size></color>", 3f, 5f, 0.5f);
				RectTransform rectTransform = val.rectTransform;
				rectTransform.anchoredPosition += Vector2.down * 160f;
				MonoSingleton<NewMovement>.Instance.ResetHardDamage();
				((Component)MonoSingleton<NewMovement>.Instance).gameObject.AddComponent<TemporaryPlayerBless>().time = 4f;
				PlaySound();
			}
		}

		private void OnDealDamage(EventDelegates.DeliverDamageEventData evntdata, EnemyIdentifier enemy, GameObject target, bool ignoretotaldamagetakenmultiplier, bool fromexplosion)
		{
			//IL_006e: Unknown result type (might be due to invalid IL or missing references)
			EnemyHurtTimer orAddComponent = ComponentExtensions.GetOrAddComponent<EnemyHurtTimer>((Component)(object)enemy);
			if (!enemy.blessed && !enemy.dead && (Object)(object)((Component)enemy).GetComponent<TemporaryBless>() == (Object)null)
			{
				if (orAddComponent.lastHurtTime != 0f && Time.time - orAddComponent.lastHurtTime > 2f)
				{
					evntdata.multiplier = 0.1f;
					evntdata.damageNumberFormatOverride = "BLESSED";
					evntdata.damageNumberColorModifier = Color.red;
					((Component)enemy).gameObject.AddComponent<TemporaryBless>().time = 4f;
					orAddComponent.lastHurtTime = float.MaxValue;
				}
				else
				{
					orAddComponent.lastHurtTime = Time.time;
				}
			}
		}
	}
	public class EnemyHurtTimer : MonoBehaviour
	{
		public float lastHurtTime = float.MaxValue;
	}
	public class TemporaryPlayerBless : MonoBehaviour
	{
		private GameObject blessGo;

		public float time;

		private void Start()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: 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_0060: Unknown result type (might be due to invalid IL or missing references)
			blessGo = Object.Instantiate<GameObject>(MonoSingleton<DefaultReferenceManager>.Instance.blessingGlow, ((Component)this).transform.position, ((Component)this).transform.rotation);
			Collider component = ((Component)this).gameObject.GetComponent<Collider>();
			if (Object.op_Implicit((Object)(object)component))
			{
				Transform transform = blessGo.transform;
				Bounds bounds = component.bounds;
				transform.localScale = ((Bounds)(ref bounds)).size * 2f;
			}
			blessGo.transform.SetParent(((Component)this).transform, true);
			((MonoBehaviour)this).Invoke("StopBless", time);
		}

		private void StopBless()
		{
			Object.Destroy((Object)(object)blessGo);
			Object.Destroy((Object)(object)this);
		}
	}
	public class TemporaryBless : MonoBehaviour
	{
		private EnemyIdentifier eid;

		public float time;

		private void Start()
		{
			eid = ((Component)this).GetComponent<EnemyIdentifier>();
			eid.Bless(false);
			((MonoBehaviour)this).Invoke("StopBless", time);
		}

		private void StopBless()
		{
			eid.Unbless(false);
			Object.Destroy((Object)(object)this);
		}
	}
	public class RogueArsenal : Bane, IRandomizable
	{
		[JsonIgnore]
		public static List<List<GameObject>> allWeaponsPrior;

		public HashSet<WeaponVariationType> chosenWeapons;

		public static readonly RogueArsenal Default = new RogueArsenal();

		public override string Name => "Rogue Arsenal";

		public override string Description => "Your arsenal is replaced with two random weapons. These two weapons deal 20% more damage.\nYou gain 25% more style and experience as well as <color=green>2 rerolls and banishes</color>.\nYou can unlock the rest of your arsenal through upgrades.\nWeapons you did not have equipped prior to choosing this Value Drift will not appear.\nThis upgrade has a high chance of occuring during the start of your run.";

		public override double AppearChanceWeighting => base.AppearChanceWeighting * (((Object)(object)MonoSingleton<EndlessGrid>.Instance == (Object)null) ? 1.0 : ((MonoSingleton<EndlessGrid>.Instance.currentWave <= MonoSingleton<EndlessGrid>.Instance.startWave) ? 10.0 : 0.95));

		public override void Apply()
		{
			allWeaponsPrior = new List<List<GameObject>>();
			foreach (List<GameObject> slot in MonoSingleton<GunControl>.Instance.slots)
			{
				allWeaponsPrior.Add(new List<GameObject>(slot));
			}
			foreach (List<GameObject> slot2 in MonoSingleton<GunControl>.Instance.slots)
			{
				slot2.Clear();
			}
			int num = 0;
			WeaponUpgrade.BuildEquippedWeaponsLookup();
			foreach (WeaponVariationType chosenWeapon in chosenWeapons)
			{
				if (!WeaponUpgrade.equipped.Has(chosenWeapon))
				{
					UpgradeScreen.Instance.forcedUpgradeQueue.Enqueue(new GainRogueWeaponUpgrade
					{
						type = chosenWeapon
					});
					num++;
				}
			}
			UpgradeScreen.Instance.ShowTimes(num);
			PlayerUpgradeStats.Instance.numRerollsAvailable += 2;
			PlayerUpgradeStats.Instance.numBanishesAvailable += 2;
			PlayerUpgradeStats.Instance.StylePointsMultiplier += 0.25;
			try
			{
				MonoSingleton<GunControl>.Instance.UpdateWeaponList(false);
			}
			catch (ArgumentException data)
			{
				OK.Log(data, (LogLevel)16);
			}
			WeaponUpgrade.BuildEquippedWeaponsLookup();
		}

		public override void Absolve()
		{
			allWeaponsPrior = null;
			PlayerUpgradeStats.Instance.StylePointsMultiplier -= 0.25;
			PlayerUpgradeStats.Instance.numRerollsAvailable -= 2;
			PlayerUpgradeStats.Instance.numBanishesAvailable -= 2;
			foreach (WeaponVariationType chosenWeapon in chosenWeapons)
			{
				PlayerUpgradeStats.Instance.weaponDamageMultplier.weaponVariationTypeNoAltMultiplier[chosenWeapon.GetVariantNoAlt()] -= 0.2;
			}
			WeaponUpgrade.BuildEquippedWeaponsLookup();
		}

		public void Randomize(int seed)
		{
			allWeaponsPrior = new List<List<GameObject>>();
			foreach (List<GameObject> slot in MonoSingleton<GunControl>.Instance.slots)
			{
				allWeaponsPrior.Add(new List<GameObject>(slot));
			}
			int num = allWeaponsPrior.Where((List<GameObject> l) => l != null).Sum((List<GameObject> l) => l.Count);
			chosenWeapons = new HashSet<WeaponVariationType>();
			while (chosenWeapons.Count < Mathf.Min(2, num))
			{
				int num2 = Random.Range(0, num);
				int i;
				for (i = 0; i < allWeaponsPrior.Count && num2 >= allWeaponsPrior[i].Count; i++)
				{
					num2 -= allWeaponsPrior[i].Count;
				}
				List<GameObject> list = allWeaponsPrior[i];
				GameObject val = list[num2];
				WeaponTypeComponent component = val.GetComponent<WeaponTypeComponent>();
				if (!chosenWeapons.Contains(component.value))
				{
					chosenWeapons.Add(component.value);
					PlayerUpgradeStats.Instance.weaponDamageMultplier.weaponVariationTypeNoAltMultiplier[component.value.GetVariantNoAlt()] += 0.2;
				}
			}
		}
	}
	public class GainRogueWeaponUpgrade : WeaponUpgrade, IRandomizable
	{
		public WeaponVariationType type;

		public override string Name => $"Arsenal: {type}";

		public override string Description
		{
			get
			{
				if (IsObtainable)
				{
					return "Gain that weapon.";
				}
				return "Gain that weapon.\n\n<color=red>Only obtainable if you have the \"" + RogueArsenal.Default.Name + "\" value drift.</color>";
			}
		}

		public override WeaponsEquippedFlags AffectedWeapons => type;

		public override Rarity Rarity => Rarity.Common;

		public override Rarity MinRarity => Rarity.Common;

		public override Rarity MaxRarity => Rarity.Common;

		public override double AppearChanceWeighting => IsObtainable ? (base.AppearChanceWeighting * 0.5 * 1.5) : 0.0;

		public override bool IsObtainable
		{
			get
			{
				if (PlayerUpgradeStats.Instance.upgrades.TryGetValue(GetHashCode(), out var value))
				{
					if (value is LeveledUpgrade leveledUpgrade)
					{
						return leveledUpgrade.level < leveledUpgrade.MaxLevel;
					}
					return false;
				}
				if (!PlayerUpgradeStats.Instance.upgrades.ContainsKey(RogueArsenal.Default.GetHashCode()))
				{
					return false;
				}
				if (RogueArsenal.allWeaponsPrior.Sum((List<GameObject> l) => l.Count) <= MonoSingleton<GunControl>.Instance.slots.Sum((List<GameObject> l) => l.Count))
				{
					return false;
				}
				if (WeaponUpgrade.equipped.value <= 0)
				{
					WeaponUpgrade.BuildEquippedWeaponsLookup();
				}
				return !WeaponUpgrade.equipped.Any(AffectedWeapons);
			}
		}

		public override void Apply()
		{
			WeaponVariationType weaponVariationType = type;
			if (1 == 0)
			{
			}
			int num = weaponVariationType switch
			{
				WeaponVariationType.UNKNOWN => 0, 
				WeaponVariationType.PiercerRevolver => 0, 
				WeaponVariationType.MarskmanRevolver => 0, 
				WeaponVariationType.SharpshooterRevolver => 0, 
				WeaponVariationType.PiercerSlabRevolver => 0, 
				WeaponVariationType.MarskmanSlabRevolver => 0, 
				WeaponVariationType.SharpshooterSlabRevolver => 0, 
				WeaponVariationType.CoreEjectShotgun => 1, 
				WeaponVariationType.PumpChargeShotgun => 1, 
				WeaponVariationType.SawedOnShotgun => 1, 
				WeaponVariationType.CoreEjectShotgunHammer => 1, 
				WeaponVariationType.PumpChargeShotgunHammer => 1, 
				WeaponVariationType.SawedOnShotgunHammer => 1, 
				WeaponVariationType.AttractorNailgun => 2, 
				WeaponVariationType.OverheatNailgun => 2, 
				WeaponVariationType.JumpstartNailgun => 2, 
				WeaponVariationType.AttractorSawgun => 2, 
				WeaponVariationType.OverheatSawgun => 2, 
				WeaponVariationType.JumpstartSawgun => 2, 
				WeaponVariationType.ElectricRailcannon => 3, 
				WeaponVariationType.ScrewdriverRailcannon => 3, 
				WeaponVariationType.MaliciousRailcannon => 3, 
				WeaponVariationType.FreezeframeRocketLauncher => 4, 
				WeaponVariationType.SRSRocketLauncher => 4, 
				WeaponVariationType.FirestarterRocketLauncher => 4, 
				_ => throw new ArgumentOutOfRangeException(), 
			};
			if (1 == 0)
			{
			}
			int index = num;
			GameObject val = null;
			foreach (GameObject item in RogueArsenal.allWeaponsPrior[index])
			{
				if (item.GetComponent<WeaponTypeComponent>().value == type)
				{
					OK.Log($"Found: {type}: {((Object)item.gameObject).name}", (LogLevel)16);
					val = item;
				}
			}
			if ((Object)(object)val != (Object)null)
			{
				MonoSingleton<GunControl>.Instance.slots[index].Add(val);
				MonoSingleton<GunControl>.Instance.SwitchWeapon(MonoSingleton<GunControl>.Instance.slots[index].Count - 1, MonoSingleton<GunControl>.Instance.slots[index], false, false, false, false);
				MonoSingleton<GunControl>.Instance.UpdateWeaponList(false);
				WeaponUpgrade.BuildEquippedWeaponsLookup();
			}
		}

		public override void Absolve()
		{
		}

		public void Randomize(int seed)
		{
			Random.InitState(seed);
			int num = 0;
			WeaponTypeComponent weaponTypeComponent = null;
			do
			{
				num = Random.Range(0, RogueArsenal.allWeaponsPrior.Sum((List<GameObject> l) => l.Count));
				int i;
				for (i = 0; i < RogueArsenal.allWeaponsPrior.Count && num >= RogueArsenal.allWeaponsPrior[i].Count; i++)
				{
					num -= RogueArsenal.allWeaponsPrior[i].Count;
				}
				List<GameObject> list = RogueArsenal.allWeaponsPrior[i];
				GameObject val = list[num];
				weaponTypeComponent = val.GetComponent<WeaponTypeComponent>();
				OK.Log($"Randomized: {type}, is equipped: {WeaponUpgrade.equipped.Has(weaponTypeComponent.value)} ({((WeaponsEquippedFlags)weaponTypeComponent.value).ToString()}) equipped: {WeaponUpgrade.equipped.ToString()}", (LogLevel)16);
			}
			while ((Object)(object)weaponTypeComponent == (Object)null || WeaponUpgrade.equipped.Has(weaponTypeComponent.value));
			type = weaponTypeComponent.value;
		}
	}
	public class SuddenDeath : Bane
	{
		private static bool enabled = true;

		public const float HPExecuteThreshold = 0.5f;

		[JsonIgnore]
		private bool playerInArena = true;

		[JsonIgnore]
		private int fixedFrame;

		public override string Name => "Sudden Hyperdeath";

		public override string Description => $"You accumulate {GetHardDamagePct():0.%} (2% + 0.4%/wave, max 20%) of your maximum HP in hard damage every second you spend outside or far above the arena.\nBut while you are inside the arena, your hard damage resets 20% faster.\nWhen hitting an enemy with the knuckleblaster and that enemy has less than {0.5f:0.%} of its maximum health, it is executed and dies instantly.";

		public override bool IsObtainable => base.IsObtainable && (Object)(object)MonoSingleton<EndlessGrid>.Instance != (Object)null;

		public override double AppearChanceWeighting => base.AppearChanceWeighting;

		public double GetHardDamagePctPerWave()
		{
			return 0.004;
		}

		public double GetHardDamagePct()
		{
			return Math.Min(0.2, 0.02 + GetHardDamagePctPerWave() * (double)(((Object)(object)MonoSingleton<EndlessGrid>.Instance != (Object)null) ? (MonoSingleton<EndlessGrid>.Instance.currentWave - 1) : 20));
		}

		public override void Apply()
		{
			ref EventDelegates.WithSender<NewMovement> post = ref Events.OnPlayerEnterArena.Post;
			post = (EventDelegates.WithSender<NewMovement>)Delegate.Combine(post, new EventDelegates.WithSender<NewMovement>(OnPlayerEnterArena));
			ref EventDelegates.WithSender<NewMovement> post2 = ref Events.OnPlayerLeaveArena.Post;
			post2 = (EventDelegates.WithSender<NewMovement>)Delegate.Combine(post2, new EventDelegates.WithSender<NewMovement>(OnPlayerLeaveArena));
			ref EventDelegates.WithNone post3 = ref Events.OnFixedUpdate.Post;
			post3 = (EventDelegates.WithNone)Delegate.Combine(post3, new EventDelegates.WithNone(OnFixedUpdate));
			ref EventDelegates.DeliverDamage pre = ref Events.OnDealDamage.Pre;
			pre = (EventDelegates.DeliverDamage)Delegate.Combine(pre, new EventDelegates.DeliverDamage(OnDealDamage));
			HardDamagePatches.hardDamageCooldownMultiplier -= 0.2;
		}

		public override void Absolve()
		{
			ref EventDelegates.WithSender<NewMovement> post = ref Events.OnPlayerEnterArena.Post;
			post = (EventDelegates.WithSender<NewMovement>)Delegate.Remove(post, new EventDelegates.WithSender<NewMovement>(OnPlayerEnterArena));
			ref EventDelegates.WithSender<NewMovement> post2 = ref Events.OnPlayerLeaveArena.Post;
			post2 = (EventDelegates.WithSender<NewMovement>)Delegate.Remove(post2, new EventDelegates.WithSender<NewMovement>(OnPlayerLeaveArena));
			ref EventDelegates.WithNone post3 = ref Events.OnFixedUpdate.Post;
			post3 = (EventDelegates.WithNone)Delegate.Remove(post3, new EventDelegates.WithNone(OnFixedUpdate));
			ref EventDelegates.DeliverDamage pre = ref Events.OnDealDamage.Pre;
			pre = (EventDelegates.DeliverDamage)Delegate.Remove(pre, new EventDelegates.DeliverDamage(OnDealDamage));
			HardDamagePatches.hardDamageCooldownMultiplier += 0.2;
			Disable();
		}

		private void OnDealDamage(EventDelegates.DeliverDamageEventData evntdata, EnemyIdentifier enemy, GameObject target, bool ignoretotaldamagetakenmultiplier, bool fromexplosion)
		{
			//IL_0089: 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)
			if (!((Object)(object)evntdata.sourceWeapon == (Object)null))
			{
				Punch component = evntdata.sourceWeapon.GetComponent<Punch>();
				if (!((Object)(object)component == (Object)null) && !((Object)(object)component.blastWave == (Object)null) && EnemyMaxHP.TryGet(enemy, out var value) && enemy.health / value < 0.5f)
				{
					evntdata.multiplier = enemy.health * (enemy.healthBuff ? enemy.healthBuffModifier : 1f);
					evntdata.damageNumberColorModifier = RarityColor.Get(Rarity.ValueDrift) * 2.5f;
					evntdata.damageNumberFormatOverride = "<size=150%>EXECUTED</size>";
				}
			}
		}

		private void OnFixedUpdate()
		{
			if (!playerInArena && Time.fixedUnscaledDeltaTime != 0f && !MonoSingleton<NewMovement>.Instance.dead && (!((Object)(object)MonoSingleton<EndlessGrid>.Instance != (Object)null) || enabled))
			{
				fixedFrame++;
				if (fixedFrame % (int)(1f / Time.fixedUnscaledDeltaTime) == 0)
				{
					MonoSingleton<NewMovement>.Instance.ForceAddAntiHP((float)(GetHardDamagePct() * PlayerUpgradeStats.Instance.GetMaxHP()), false, false, true);
				}
			}
		}

		private void OnPlayerEnterArena(NewMovement sender)
		{
			playerInArena = true;
		}

		private void OnPlayerLeaveArena(NewMovement sender)
		{
			fixedFrame = 0;
			playerInArena = false;
		}

		public static void Enable()
		{
			enabled = true;
		}

		public static void Disable()
		{
			enabled = false;
		}
	}
	public class BanisherUpgrade : RecurringUpgrade
	{
		public override string Name => "BANISHER";

		public override Rarity Rarity => Rarity.Overkill;

		public override Rarity MinRarity => Rarity.Overkill;

		public override Rarity MaxRarity => Rarity.Overkill;

		public override OKVersion Version => new OKVersion(1, 3, 0);

		public override double AppearChanceWeighting => base.AppearChanceWeighting * 0.02500000037252903 * 0.85;

		public override string NonRecurringDescription
		{
			get
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("You gain 3 random upgrades that you have banished.");
				int count = PlayerUpgradeStats.Instance.banishedUpgrades.Count;
				if (count > 0)
				{
					stringBuilder.AppendLine("If they have a rarity, you obtain them at their maximum rarity. Your banished upgrades are:");
					foreach (KeyValuePair<int, IUpgrade> item in PlayerUpgradeStats.Instance.banishedUpgrades.OrderByDescending((KeyValuePair<int, IUpgrade> u) => u.Value.Rarity).Take(Mathf.Min(count, 10)))
					{
						stringBuilder.AppendLine(item.Value.RTFName());
					}
					if (count > 10)
					{
						stringBuilder.AppendLine($"\n...and {count - 10} more.");
					}
					return stringBuilder.ToString();
				}
				stringBuilder.AppendLine("You haven't banished anything though, so this upgrade is useless. Please start using my banishing mechanic :(");
				return stringBuilder.ToString();
			}
		}

		public override void OnApply()
		{
			if (PlayerUpgradeStats.Instance.banishedUpgrades.Count <= 0)
			{
				return;
			}
			List<IUpgrade> list = new List<IUpgrade>();
			do
			{
				int index = Random.Range(0, PlayerUpgradeStats.Instance.banishedUpgrades.Count);
				KeyValuePair<int, IUpgrade> keyValuePair = PlayerUpgradeStats.Instance.banishedUpgrades.ElementAt(index);
				PlayerUpgradeStats.Instance.banishedUpgrades.Remove(keyValuePair.Key);
				list.Add(keyValuePair.Value);
			}
			while (PlayerUpgradeStats.Instance.banishedUpgrades.Count > 0 && list.Count < 3);
			foreach (IUpgrade item in list)
			{
				UpgradeScreen.Instance.forcedUpgradeQueue.Enqueue(item);
			}
			UpgradeScreen.Instance.ShowTimes(list.Count);
		}
	}
	public sealed class BanishesForRerollsUpgrade : RecurringUpgrade
	{
		public override string Name => "Banishes => Rerolls";

		public override string NonRecurringDescription => "Converts your banishes into rerolls at a ratio of 2:1.";

		public override bool IsObtainable => base.IsObtainable && PlayerUpgradeStats.Instance.numRerollsAvailable > 1;

		public override double AppearChanceWeighting => base.AppearChanceWeighting * 0.25 * 0.5;

		public override Rarity MinRarity => Rarity.Rare;

		public override Rarity MaxRarity => Rarity.Rare;

		public override Rarity Rarity => Rarity.Rare;

		public override OKVersion Version => new OKVersion(1, 3, 0);

		public override void OnApply()
		{
			PlayerUpgradeStats.Instance.numRerollsAvailable += PlayerUpgradeStats.Instance.numBanishesAvailable / 2;
			PlayerUpgradeStats.Instance.numBanishesAvailable = 0;
			if (Object.op_Implicit((Object)(object)UpgradeScreen.Instance))
			{
				UpgradeScreen.Instance.UpdateRerollsBanishesUI();
			}
		}
	}
	public class DropPowerupUpgrade : LeveledUpgrade, IRandomizable
	{
		public override OKVersion Version => new OKVersion(1, 2, 3);

		public override string Name => "EXCUSE ME";

		public override Rarity MinRarity => Rarity.Epic;

		public override Rarity MaxRarity => Rarity.Overkill;

		public override Rarity Rarity { get; set; }

		public override int MaxLevel => 3;

		public override double AppearChanceWeighting => base.AppearChanceWeighting * 0.10000000149011612 * 0.89;

		public override string Description
		{
			get
			{
				StringBuilder stringBuilder = new StringBuilder();
				stringBuilder.AppendLine("<i>...you dropped your wallet. Uhm, I mean your Dual Wield powerup!</i>\n\nKilling enemies now has a chance of dropping either a Dual Wield or Health Supercharge powerup. They might also drop mines instead. The chance of an enemy dropping something increases with their max HP and your style rank. Powerups stay until the end of the next round but will then despawn. Higher level of the upgrade increases the duration of the Dual Wield powerup as well as the HP gained from Health powerup.");
				if (Rarity == Rarity.Overkill)
				{
					stringBuilder.AppendLine("\nThere is also a tiny chance the dropped powerup is... <i>less violent</i>.");
				}
				return stringBuilder.ToString();
			}
		}

		public override void Apply()
		{
			ref EventDelegates.Death latePre = ref Events.OnEnemyDeath.LatePre;
			latePre = (EventDelegates.Death)Delegate.Combine(latePre, new EventDelegates.Death(OnEnemyDeath));
		}

		public override void Absolve()
		{
			ref EventDelegates.Death latePre = ref Events.OnEnemyDeath.LatePre;
			latePre = (EventDelegates.Death)Delegate.Remove(latePre, new EventDelegates.Death(OnEnemyDeath));
		}

		private void OnEnemyDeath(EnemyIdentifier enemy, EventDelegates.DeliverDamageEventData lastDamage, bool fromexplosion)
		{
			//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_00f4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f9: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_016e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0173: Unknown result type (might be due to invalid IL or missing references)
			//IL_017d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0182: Unknown result type (might be due to invalid IL or missing references)
			//IL_0187: Unknown result type (might be due to invalid IL or missing references)
			//IL_01c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0218: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0227: Unknown result type (might be due to invalid IL or missing references)
			//IL_022c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0231: Unknown result type (might be due to invalid IL or missing references)
			//IL_0254: Unknown result type (might be due to invalid IL or missing references)
			float value = Random.value;
			if (!EnemyMaxHP.TryGet(enemy, out var value2))
			{
				OK.Log("Failed to get maxhp for dying enemy: " + ((Object)((Component)enemy).gameObject).name + "!", (LogLevel)2);
			}
			else
			{
				if (value / value2 > 1f / 30f)
				{
					return;
				}
				value = Random.value;
				if (value > 0.998f && Rarity == Rarity.Overkill)
				{
					if (Prefabs.TryGet(PrefabID.KITR, out var go))
					{
						GameObject val = Object.Instantiate<GameObject>(go, ((Component)enemy).transform.position, Quaternion.identity, ((Component)enemy).transform.parent);
						val.AddComponent<DestroySelfAfterWaves>().numWaves = 4;
						val.AddComponent<ScaleFadeInBehaviour>();
					}
				}
				else if (value > 0.997f && Rarity == Rarity.Overkill)
				{
					if (Prefabs.TryGet(PrefabID.Soap, out var go2))
					{
						GameObject val = Object.Instantiate<GameObject>(go2, ((Component)enemy).transform.position, Quaternion.identity, ((Component)enemy).transform.parent);
						val.transform.SetGlobalScale(go2.transform.localScale);
						val.AddComponent<DestroySelfAfterWaves>().numWaves = 1;
						val.AddComponent<ScaleFadeInBehaviour>();
					}
				}
				else if (value > 0.75f)
				{
					if (Prefabs.TryGet(PrefabID.DualWieldPowerup, out var go3))
					{
						GameObject val = Object.Instantiate<GameObject>(go3, ((Component)enemy).transform.position + Vector3.up * 2f, Quaternion.identity, ((Component)enemy).transform.parent);
						DualWieldPickup component = val.GetComponent<DualWieldPickup>();
						component.juiceAmount = (float)level * 5f;
						val.transform.SetGlobalScale(go3.transform.localScale);
						val.AddComponent<ScaleFadeInBehaviour>();
						val.AddComponent<DestroySelfAfterWaves>().numWaves = 3;
					}
				}
				else if (value > 0.5f)
				{
					if (Prefabs.TryGet(PrefabID.SuperchargeHealth, out var go4))
					{
						GameObject val = Object.Instantiate<GameObject>(go4, ((Component)enemy).transform.position + Vector3.up * 2f, Quaternion.identity, ((Component)enemy).transform.parent);
						val.transform.SetGlobalScale(go4.transform.localScale);
						Bonus component2 = val.GetComponent<Bonus>();
						component2.superCharge = true;
						component2.ghost = true;
						SuperChargeHealthOverride superChargeHealthOverride = ((Component)component2).gameObject.AddComponent<SuperChargeHealthOverride>();
						superChargeHealthOverride.fillToMaxHP = false;
						superChargeHealthOverride.extraHealth = (level + 1) * 25;
						val.AddComponent<ScaleFadeInBehaviour>();
						val.AddComponent<DestroySelfAfterWaves>().numWaves = 3;
					}
				}
				else
				{
					((MonoBehaviour)MonoSingleton<NewMovement>.Instance).StartCoroutine(SpawnMine(enemy, 0.12f, Math.Min(8, Math.Max(1, Random.Range(1, (int)value2 / 2)))));
				}
			}
		}

		private static IEnumerator SpawnMine(EnemyIdentifier enemy, float delay, int num)
		{
			if (!Prefabs.TryGet(PrefabID.Mine, out var prefab))
			{
				yield break;
			}
			Vector3 pos = ((Component)enemy).transform.position;
			for (int i = 0; i < num; i++)
			{
				if (i != 0)
				{
					pos += Random.insideUnitSphere * 1f;
				}
				GameObject inst = Object.Instantiate<GameObject>(prefab, pos, Quaternion.identity, ((Component)enemy).transform.parent);
				inst.transform.SetGlobalScale(prefab.transform.localScale);
				Landmine mine = inst.GetComponent<Landmine>();
				mine.originEnemy = enemy;
				((Component)mine).GetComponent<Rigidbody>();
				yield return (object)new WaitForSeconds(delay * 0.5f);
				mine.Activate(Random.Range(0.8f, 1.2f));
				yield return (object)new WaitForSeconds(delay * 0.5f);
			}
		}

		public void Randomize(int seed)
		{
			Random.InitState(seed);
			float num = Random.value * 0.1f;
			if (1 == 0)
			{
			}
			Rarity rarity = ((!(num >= 0.025f)) ? Rarity.Epic : Rarity.Overkill);
			if (1 == 0)
			{
			}
			Rarity = rarity;
		}
	}
	public class SuperChargeHealthOverride : MonoBehaviour
	{
		public bool fillToMaxHP;

		public int extraHealth;
	}
	[HarmonyPatch]
	public class PatchPlayerSuperCharge
	{
		public static SuperChargeHealthOverride currentOverride;

		[HarmonyPatch(typeof(NewMovement), "SuperCharge")]
		[HarmonyPrefix]
		[HarmonyPriority(0)]
		public static bool SuperCharge(NewMovement __instance)
		{
			if ((Object)(object)currentOverride != (Object)null)
			{
				double maxHP = PlayerUpgradeStats.Instance.GetMaxHP();
				if (currentOverride.fillToMaxHP)
				{
					__instance.GetHealth((int)maxHP, true, false);
				}
				__instance.hp += currentOverride.extraHealth;
				if ((double)__instance.hp > maxHP * 2.0)
				{
					__instance.hp = (int)(maxHP * 2.0);
				}
				currentOverride = null;
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(Bonus), "OnTriggerEnter")]
		[HarmonyPrefix]
		[HarmonyPriority(0)]
		public static void OnTriggerEnter(Bonus __instance, Collider other)
		{
			currentOverride = ((Component)__instance).GetComponent<SuperChargeHealthOverride>();
		}
	}
	public class DestroySelfAfterWaves : MonoBehaviour
	{
		public int numWaves = 1;

		private void OnEnable()
		{
			ref EventDelegates.WithSender<EndlessGrid> post = ref Events.OnEndWave.Post;
			post = (EventDelegates.WithSender<EndlessGrid>)Delegate.Combine(post, new EventDelegates.WithSender<EndlessGrid>(OnEndWave));
		}

		private void OnDisable()
		{
			ref EventDelegates.WithSender<EndlessGrid> post = ref Events.OnEndWave.Post;
			post = (EventDelegates.WithSender<EndlessGrid>)Delegate.Remove(post, new EventDelegates.WithSender<EndlessGrid>(OnEndWave));
		}

		private void OnEndWave(EndlessGrid sender)
		{
			numWaves--;
			if (numWaves <= 0)
			{
				Object.Destroy((Object)(object)((Component)this).gameObject);
			}
		}
	}
	public class DestroySelfAfterSeconds : MonoBehaviour
	{
		public float seconds;

		public Action onRunOut = null;

		private void Update()
		{
			seconds -= Time.deltaTime;
			if (seconds <= 0f)
			{
				if (onRunOut != null)
				{
					onRunOut();
					((Behaviour)this).enabled = false;
				}
				else
				{
					Object.DestroyImmediate((Object)(object)((Component)this).gameObject);
				}
			}
		}
	}
	public class ScaleFadeInBehaviour : MonoBehaviour
	{
		private Vector3 targetScale;

		private Vector3 targetScaleNormalized;

		private float targetScaleLength;

		public float time = 0.25f;

		private void Start()
		{
			//IL_0008: 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_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_003a: Unknown result type (might be due to invalid IL or missing references)
			targetScale = ((Component)this).transform.localScale;
			targetScaleLength = ((Vector3)(ref targetScale)).magnitude;
			targetScaleNormalized = ((Vector3)(ref targetScale)).normalized;
			((Component)this).transform.localScale = Vector3.zero;
			if (time <= 0f)
			{
				time = 0.1f;
			}
		}

		private void Update()
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0069: 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_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_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0053: Unknown result type (might be due to invalid IL or missing references)
			Vector3 localScale = ((Component)this).transform.localScale;
			if (((Vector3)(ref localScale)).magnitude < targetScaleLength)
			{
				Transform transform = ((Component)this).transform;
				transform.localScale += targetScaleNormalized * targetScaleLength * Time.deltaTime / time;
			}
			else
			{
				((Component)this).transform.localScale = targetScale;
				Object.Destroy((Object)(object)this);
			}
		}
	}
	public class InvincibilityFramesUpgrade : LeveledUpgrade, IRandomizable
	{
		public override double AppearChanceWeighting => 0.026249999180436134 * base.AppearChanceWeightingOptionMultiplier;

		public override int MaxLevel => 3;

		public override string Name => "I'M INVINCIBLE!";

		public override string Description => "You gain full invincibilty for 0.35 seconds when dashing, but you lose 1 stamina bar permanently upon picking this upgrade. Dodging in this way reduces hard damage by 25.\n\nLevel 2: Dodging an attack in this way refills 1 bar of stamina.\nLevel 3: Dodging an attack in this way recharges your Railcannon by 20%.";

		public override Rarity MaxRarity => Rarity.Overkill;

		public override Rarity MinRarity => Rarity.Overkill;

		public override void Apply()
		{
			PatchDashInvincibility.effectEnabled = true;
			PatchStaminaRegenSpeed.maxStamina -= 100f;
			if (level > 1)
			{
				PatchDashInvincibility.staminaRefill = 100f;
			}
			if (level > 2)
			{
				PatchDashInvincibility.railRefill = 0.2f;
			}
		}

		public override void Absolve()
		{
			PatchDashInvincibility.effectEnabled = false;
			PatchStaminaRegenSpeed.maxStamina += 100f;
			if (level > 1)
			{
				PatchDashInvincibility.staminaRefill = 0f;
			}
			if (level > 2)
			{
				PatchDashInvincibility.railRefill = 0f;
			}
		}

		public void Randomize(int seed)
		{
			Random.InitState(seed);
			Rarity = Rarity.Overkill;
		}
	}
	[HarmonyPatch(typeof(NewMovement), "GetHurt")]
	public class PatchDashInvincibility
	{
		public static bool effectEnabled;

		public static bool forceByPassIframes;

		public static float staminaRefill;

		public static float railRefill;

		public static int currAudioIndex;

		private static float lastFlashDodgeTime;

		public const float invincibilityTime = 0.35f;

		public static bool dodgeTriggeredFlash;

		public static bool IsInIframes => PatchStaminaRegenSpeed.TimeSinceLastDash < 0.35f && effectEnabled && !forceByPassIframes;

		[HarmonyPriority(800)]
		public static bool Prefix(NewMovement __instance, int damage, bool invincible, float scoreLossMultiplier = 1f, bool explosion = false, bool instablack = false, float hardDamageMultiplier = 0.35f, bool ignoreInvincibility = false)
		{
			if (IsInIframes)
			{
				if (staminaRefill > 0f)
				{
					NewMovement instance = MonoSingleton<NewMovement>.Instance;
					instance.boostCharge += staminaRefill;
				}
				WeaponCharges instance2 = MonoSingleton<WeaponCharges>.Instance;
				instance2.raicharge += railRefill * 5f;
				if (MonoSingleton<NewMovement>.Instance.antiHp > 0f)
				{
					NewMovement instance3 = MonoSingleton<NewMovement>.Instance;
					instance3.antiHp -= 25f;
					if (MonoSingleton<NewMovement>.Instance.antiHp < 0f)
					{
						NewMovement instance4 = MonoSingleton<NewMovement>.Instance;
						instance4.antiHp -= 0f;
					}
				}
				if (!dodgeTriggeredFlash)
				{
					dodgeTriggeredFlash = true;
					lastFlashDodgeTime = Time.time - PatchStaminaRegenSpeed.TimeSinceLastDash;
					MonoSingleton<TimeController>.Instance.ParryFlash();
					PlayDodgeSoundAsync(__instance);
				}
				return false;
			}
			return true;
		}

		[HarmonyPatch(typeof(NewMovement), "Dodge")]
		[HarmonyPostfix]
		public static void OverrideVanillaInvincibility(NewMovement __instance)
		{
			if (effectEnabled && !__instance.sliding)
			{
				((Component)__instance).gameObject.layer = 2;
			}
		}

		private static async Task PlayDodgeSoundAsync(NewMovement __instance)
		{
			AudioClip clip = await CustomSound.LoadAsync($"doot{currAudioIndex % 8 + 1}.wav");
			currAudioIndex++;
			if (currAudioIndex > 1)
			{
				__instance.boostCharge = Mathf.Clamp(__instance.boostCharge + Mathf.Clamp(30f + (float)(currAudioIndex - 1) * 20f, 0f, 90f), 0f, PatchStaminaRegenSpeed.maxStamina);
				MonoSingleton<StyleHUD>.Instance.AddPoints(30 + Math.Min(7, currAudioIndex) * 10, $"PANIC ROLL #{currAudioIndex}", (GameObject)null, (EnemyIdentifier)null, -1, "", "");
			}
			else
			{
				MonoSingleton<StyleHUD>.Instance.AddPoints(40, "PANIC ROLL?", (GameObject)null, (EnemyIdentifier)null, -1, "", "");
			}
			OneShotAudioExtension.PlayClipAtPoint(clip, MonoSingleton<AudioMixerController>.Instance.allGroup, ((Component)__instance).transform.position, 128, 0f, 1f, 1f, (AudioRolloffMode)1, 0f, 1000f);
		}
	}
	public interface IUpgrade : IEquatable<IUpgrade>
	{
		OKVersion Version { get; }

		int OptionsSortPriority { get; }

		double AppearChanceWeightingOptionMultiplier { get; set; }

		[JsonIgnore]
		bool IsObtainable { get; }

		[JsonIgnore]
		double AppearChanceWeighting { get; }

		string Name { get; }

		string Description { get; }

		[JsonIgnore]
		Rarity Rarity { get; set; }

		[JsonIgnore]
		Rarity MaxRarity { get; }

		[JsonIgnore]
		Rarity MinRarity { get; }

		void Apply();

		void Absolve();
	}
	public interface IBoon : IUpgrade, IEquatable<IUpgrade>
	{
	}
	public abstract class Bane : IUpgrade, IEquatable<IUpgrade>
	{
		public virtual OKVersion Version => new OKVersion(1, 3, 0);

		[JsonIgnore]
		public virtual int OptionsSortPriority => 0;

		public double AppearChanceWeightingOptionMultiplier { get; set; } = 1.0;


		public virtual bool IsObtainable
		{
			get
			{
				if (PlayerUpgradeStats.Instance.upgrades.TryGetValue(GetHashCode(), out var value))
				{
					if (value is LeveledUpgrade leveledUpgrade)
					{
						return leveledUpgrade.level < leveledUpgrade.MaxLevel;
					}
					return false;
				}
				return true;
			}
		}

		[JsonIgnore]
		public virtual double AppearChanceWeighting => 1.0 * AppearChanceWeightingOptionMultiplier;

		public abstract string Name { get; }

		public abstract string Description { get; }

		[JsonIgnore]
		public Rarity Rarity
		{
			get
			{
				return Rarity.ValueDrift;
			}
			set
			{
			}
		}

		[JsonIgnore]
		public Rarity MaxRarity => Rarity.ValueDrift;

		[JsonIgnore]
		public Rarity MinRarity => Rarity.ValueDrift;

		public abstract void Apply();

		public abstract void Absolve();

		protected bool Equals(Bane other)
		{
			return GetHashCode() == other.GetHashCode();
		}

		public virtual bool Equals(IUpgrade other)
		{
			return other != null && GetHashCode() == other.GetHashCode();
		}

		public override bool Equals(object obj)
		{
			if (obj == null)
			{
				return false;
			}
			if (this == obj)
			{
				return true;
			}
			if (obj.GetType() != GetType())
			{
				return false;
			}
			return Equals((Bane)obj);
		}

		public override int GetHashCode()
		{
			return (Name != null) ? Name.GetHashCode() : 0;
		}
	}
	public interface IRandomizable
	{
		void Randomize(int seed);
	}
	public static class UpgradeExtensions
	{
		public static string RTFName(this IUpgrade upgrade)
		{
			//IL_003a: 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_001c: Unknown result type (might be due to invalid IL or missing references)
			if (upgrade is IBoon boon)
			{
				return upgrade.Name.ColoredRTF(RarityColor.Get(boon.Rarity));
			}
			return upgrade.Name.ColoredRTF(Color32.op_Implicit(new Color32((byte)102, (byte)85, (byte)102, byte.MaxValue)));
		}
	}
	public abstract class LeveledUpgrade : IBoon, IUpgrade, IEquatable<IUpgrade>
	{
		public int level = 0;

		public virtual OKVersion Version => new OKVersion(1, 1, 6);

		[JsonIgnore]
		public virtual int MaxLevel => 1;

		[JsonIgnore]
		public virtual int OptionsSortPriority => 0;

		public virtual bool IsObtainable
		{
			get
			{
				if (PlayerUpgradeStats.Instance.upgrades.TryGetValue(GetHashCode(), out var value))
				{
					if (value is LeveledUpgrade leveledUpgrade)
					{
						return leveledUpgrade.level < leveledUpgrade.MaxLevel;
					}
					return false;
				}
				return true;
			}
		}

		[JsonIgnore]
		public virtual double AppearChanceWeighting => 1.0 * AppearChanceWeightingOptionMultiplier;

		public double AppearChanceWeightingOptionMultiplier { get; set; } = 1.0;


		public abstract string Name { get; }

		[JsonIgnore]
		public abstract string Description { get; }

		[JsonIgnore]
		public virtual Rarity Rarity { get; set; }

		[JsonIgnore]
		public virtual Rarity MaxRarity => Rarity.Overkill;

		public virtual Rarity MinRarity => Rarity.Common;

		public abstract void Apply();

		public abstract void Absolve();

		protected bool Equals(LeveledUpgrade other)
		{
			return GetHashCode() == other.GetHashCode();
		}

		public bool Equals(IUpgrade other)
		{
			return other != null && GetHashCode() == other.GetHashCode();
		}

		public override bool Equals(object obj)
		{
			if (obj == null)
			{
				return false;
			}
			if (this == obj)
			{
				return true;
			}
			if (obj.GetType() != GetType())
			{
				return false;
			}
			return Equals((LeveledUpgrade)obj);
		}

		public override int GetHashCode()
		{
			return (Name != null) ? Name.GetHashCode() : 0;
		}
	}
	public class MaxHPOnKillUpgrade : LeveledUpgrade, IRandomizable
	{
		[JsonProperty]
		private Rarity rarity;

		public override OKVersion Version => new OKVersion(1, 1, 6);

		public new bool IsObtainable => !PlayerUpgradeStats.Instance.upgrades.ContainsKey(GetHashCode());

		public override double AppearChanceWeighting => 0.15 * base.AppearChanceWeightingOptionMultiplier;

		public override string Name => "OVERHEALTH";

		public override string Description => $"Whenever killing an enemy, increase your maximum HP by {pctHP[Rarity] / 10.0:0.0%} of that enemy's maximum HP (Overkill HP values, which are 10x base game to get rid of some decimals). After gaining {GetHPThreshold()} HP from this upgrade, the rate at which you gain more slows down.";

		[JsonIgnore]
		public override Rarity Rarity
		{
			get
			{
				return rarity;
			}
			set
			{
				rarity = value;
			}
		}

		public override Rarity MaxRarity => Rarity.Overkill;

		public override Rarity MinRarity => Rarity.Rare;

		public override int MaxLevel => 3;

		[JsonIgnore]
		public DoubleRarityValue pctHP => new DoubleRarityValue(0.0, 0.0, 0.04, 0.07, 0.1);

		[JsonIgnore]
		public DoubleRarityValue hpThreshold => new DoubleRarityValue(0.0, 0.0, 10.0, 10.0, 10.0);

		[JsonIgnore]
		public DoubleRarityValue hpThresholdLevelGain => new DoubleRarityValue(0.0, 0.0, 5.0, 10.0, 15.0);

		public double GetHPThreshold()
		{
			return hpThreshold[Rarity] + (double)Math.Max(0, level - 1) * hpThresholdLevelGain[Rarity];
		}

		public override void Apply()
		{
			PlayerUpgradeStats.Instance.PctMaxHPGainOnKill += pctHP[Rarity];
			PlayerUpgradeStats.Instance.PctHPGainFalloffThresholdOnKill = GetHPThreshold();
		}

		public override void Absolve()
		{
			PlayerUpgradeStats.Instance.PctMaxHPGainOnKill -= pctHP[Rarity];
			PlayerUpgradeStats.Instance.PctHPGainFalloffThresholdOnKill = 0.0;
		}

		public void Randomize(int seed)
		{
			Random.InitState(seed);
			float num = Random.value * 0.25f;
			if (1 == 0)
			{
			}
			Rarity rarity = ((num >= 0.1f) ? Rarity.Rare : ((!(num >= 0.025f)) ? Rarity.Overkill : Rarity.Epic));
			if (1 == 0)
			{
			}
			this.rarity = rarity;
		}

		public new bool Equals(IUpgrade other)
		{
			return Name.Equals(other.Name);
		}

		public override int GetHashCode()
		{
			return Name.GetHashCode();
		}
	}
	public sealed class MaxHPUpgrade : LeveledUpgrade, IRandomizable
	{
		public LongRarityValue flatBonus = new LongRarityValue(new long[5] { 5L, 10L, 15L, 20L, 0L });

		public override int MaxLevel => 5;

		public override double AppearChanceWeighting => 0.8 * base.AppearChanceWeightingOptionMultiplier;

		public override string Name => "Max HP Up";

		public override string Description => $"Increases your maximum HP by {flatBonus[Rarity] * level}, but increases the delay at which hard damage starts recovering by {(double)flatBonus[Rarity.Uncommon] * 0.01 * (double)level:0.%}";

		public override Rarity MaxRarity => Rarity.Epic;

		public override Rarity MinRarity => Rarity.Common;

		public override void Apply()
		{
			PlayerUpgradeStats.Instance.HPBonusFlat += flatBonus[Rarity] * level;
			HardDamagePatches.hardDamageCooldownMultiplier += (double)(flatBonus[Rarity.Uncommon] * level) * 0.01;
		}

		public override void Absolve()
		{
			PlayerUpgradeStats.Instance.HPBonusFlat -= flatBonus[Rarity] * level;
			HardDamagePatches.hardDamageCooldownMultiplier -= (double)(flatBonus[Rarity.Uncommon] * level) * 0.01;
		}

		public void Randomize(int seed)
		{
			Random.InitState(seed);
			float num = Random.value * 0.5f;
			if (1 == 0)
			{
			}
			Rarity rarity = ((num >= 0.15f) ? ((!(num >= 0.5f)) ? Rarity.Uncommon : Rarity.Common) : ((!(num >= 0.015000001f)) ? Rarity.Epic : Rarity.Rare));
			if (1 == 0)
			{
			}
			Rarity = rarity;
		}
	}
	public class ParryProjectileHomingUpgrade : LeveledUpgrade
	{
		public override int MaxLevel => 3;

		public override double AppearChanceWeighting => 0.06300000101327896 * base.AppearChanceWeightingOptionMultiplier;

		public override string Name => "PARRY THIS CASUL";

		public override string Description => $"Parried projectiles home to the closest target. Homing projectiles retarget if their current target dies. If no target is found, they turn hostile towards you again. When parrying a projectile, create {2 * level} clones of it.";

		public override Rarity Rarity => Rarity.Epic;

		public override Rarity MaxRarity => Rarity.Epic;

		public override Rarity MinRarity => Rarity.Epic;

		public override void Apply()
		{
			ParryPatcher.effectActive = true;
			ParryPatcher.numCopies += 2 * level;
		}

		public override void Absolve()
		{
			ParryPatcher.effectActive = false;
			ParryPatcher.numCopies -= 2 * level;
		}
	}
	[HarmonyPatch(typeof(Punch), "ParryProjectile")]
	public class ParryPatcher
	{
		public static bool effectActive;

		public static int numCopies;

		public static void Postfix(Punch __instance, Projectile proj)
		{
			//IL_0026: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Expected O, but got Unknown
			//IL_0052: 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_0099: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e7: 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_0114: Unknown result type (might be due to invalid IL or missing references)
			if (!effectActive)
			{
				return;
			}
			ProjectileSourceComponent projectileSourceComponent = default(ProjectileSourceComponent);
			if (((Component)proj).TryGetComponent<ProjectileSourceComponent>(ref projectileSourceComponent))
			{
				proj.target = new EnemyTarget(projectileSourceComponent.enemyId);
			}
			else
			{
				OK.Log("Unknown projectile for parry handler: " + ((Object)((Component)proj).gameObject).name, (LogLevel)16);
			}
			proj.homingType = (HomingType)1;
			for (int i = 0; i < numCopies; i++)
			{
				GameObject val = Object.Instantiate<GameObject>(((Component)proj).gameObject);
				Transform transform = val.transform;
				transform.localEulerAngles += new Vector3(Random.Range(-2f, 5f), Random.Range(-45f, 45f), 0f);
				if (val.TryGetComponent<ProjectileSourceComponent>(ref projectileSourceComponent))
				{
					val.GetComponent<Projectile>().target = proj.target;
				}
			}
			Transform transform2 = ((Component)proj).transform;
			transform2.localEulerAngles += new Vector3(Random.Range(-2f, 5f), Random.Range(-45f, 45f), 0f);
		}
	}
	public class ProjectileSourceComponent : MonoBehaviour
	{
		public EnemyIdentifier enemyId;

		private void Update()
		{
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_009e: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)enemyId == (Object)null || (Object)(object)this == (Object)null || !enemyId.dead)
			{
				return;
			}
			Collider[] array = Physics.OverlapSphere(((Component)this).transform.position, 500f, 1 << ((Component)enemyId).gameObject.layer, (QueryTriggerInteraction)1);
			if (array == null)
			{
				return;
			}
			if (array.Length == 0)
			{
				Projectile val = default(Projectile);
				if (((Component)this).gameObject.TryGetComponent<Projectile>(ref val))
				{
					val.homingType = (HomingType)4;
					val.friendly = false;
					val.target = EnemyTarget.TrackPlayer();
				}
				return;
			}
			Collider[] array2 = array;
			EnemyIdentifier val3 = default(EnemyIdentifier);
			foreach (Collider val2 in array2)
			{
				if (!((Object)(object)val2.attachedRigidbody == (Object)null) && ((Component)val2.attachedRigidbody).TryGetComponent<EnemyIdentifier>(ref val3) && !val3.dead)
				{
					enemyId = val3;
				}
			}
		}
	}
	[HarmonyPatch(typeof(Projectile), "Start")]
	public class ProjectileStartPatcher
	{
		public static bool hasEffect;

		public static void Postfix(Projectile __instance)
		{
			if (hasEffect)
			{
				__instance.canHitCoin = true;
			}
		}
	}
	[HarmonyPatch(typeof(Coin), "DelayedEnemyReflect")]
	public class CoinReflectPatcher
	{
		public static void Prefix(Coin __instance)
		{
			//IL_0007: 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_0094: Expected O, but got Unknown
			Collider[] array = Physics.OverlapSphere(((Component)__instance).transform.position, 500f, 4096, (QueryTriggerInteraction)1);
			if (array.Length == 0)
			{
				return;
			}
			Collider[] array2 = array;
			EnemyIdentifier val2 = default(EnemyIdentifier);
			foreach (Collider val in array2)
			{
				if (!((Object)(object)val.attachedRigidbody == (Object)null) && !((Object)(object)((Component)val).gameObject == (Object)null) && ((Component)val.attachedRigidbody).TryGetComponent<EnemyIdentifier>(ref val2) && !val2.dead)
				{
					__instance.customTarget = new EnemyTarget(val2);
					MonoSingleton<StyleHUD>.Instance.AddPoints(200, "ultrakill.ricoshot", (GameObject)null, (EnemyIdentifier)null, -1, "OVER", "");
				}
			}
		}
	}
	[HarmonyPatch(typeof(ZombieProjectiles), "ShootProjectile")]
	public class ZombieProjectilesShootPatcher
	{
		public static void Postfix(ZombieProjectiles __instance, int skipOnEasy)
		{
			GameObjectExtensions.GetOrAddComponent<ProjectileSourceComponent>(__instance.projectile).enemyId = ((Component)__instance).GetComponent<EnemyIdentifier>();
		}
	}
	[HarmonyPatch(typeof(ZombieProjectiles), "ThrowProjectile")]
	public class ZombieProjectilesThrowPatcher
	{
		public static void Postfix(ZombieProjectiles __instance)
		{
			GameObjectExtensions.GetOrAddComponent<ProjectileSourceComponent>(__instance.projectile).enemyId = ((Component)__instance).GetComponent<EnemyIdentifier>();
		}
	}
	public class PlayerUpgradeStats
	{
		public struct WeaponMultiplier
		{
			public double globalMultiplier;

			[JsonConverter(typeof(EnumIndexedArrayConverter))]
			public EnumIndexedArray<double, WeaponType> weaponTypeMultiplier;

			[JsonConverter(typeof(EnumIndexedArrayConverter))]
			public EnumIndexedArray<double, WeaponVariationType> weaponVariationTypeMultiplier;

			[JsonConverter(typeof(EnumIndexedArrayConverter))]
			public EnumIndexedArray<double, WeaponVariationTypeNoAlt> weaponVariationTypeNoAltMultiplier;

			public double GetMultiplier(WeaponTypeComponent wt)
			{
				if ((Object)(object)wt == (Object)null)
				{
					return globalMultiplier;
				}
				if (weaponVariationTypeMultiplier == null)
				{
					OK.Log("NO VAR MULT", (LogLevel)16);
				}
				if (weaponVariationTypeNoAltMultiplier == null)
				{
					OK.Log("NO ALT MULT", (LogLevel)16);
				}
				return globalMultiplier * weaponVariationTypeMultiplier[wt.value] * weaponTypeMultiplier[wt.WeaponTypeNoVariation] * weaponVariationTypeNoAltMultiplier[wt.WeaponTypeVariationNoAlt];
			}

			public double GetMultiplier(WeaponVariationType variant)
			{
				return globalMultiplier * weaponVariationTypeMultiplier[variant] * weaponTypeMultiplier[variant.GetWeaponType()] * weapo