Decompiled source of JohnFister v1.1.1

JohnFister.dll

Decompiled an hour ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using System.Text;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using JohnFister.Body;
using JohnFister.Core;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.Utilities;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: AssemblyCompany("JohnFister")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.1.0")]
[assembly: AssemblyInformationalVersion("1.1.1")]
[assembly: AssemblyProduct("JohnFister")]
[assembly: AssemblyTitle("JohnFister")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.1.0")]
[module: UnverifiableCode]
namespace JohnFister
{
	internal static class Assets
	{
		private static string dir;

		private static readonly Dictionary<string, Texture2D> textures = new Dictionary<string, Texture2D>();

		private static Shader unlit;

		private static bool preloaded;

		private static Texture2D white;

		internal static string Dir
		{
			get
			{
				if (dir != null)
				{
					return dir;
				}
				string pluginDir = FisterPlugin.PluginDir;
				string text = (dir = Path.Combine(pluginDir, "assets"));
				if (!File.Exists(Path.Combine(text, "body.txt")))
				{
					try
					{
						string text2 = Directory.GetFiles(pluginDir, "body.txt", SearchOption.AllDirectories).FirstOrDefault();
						if (text2 != null)
						{
							dir = Path.GetDirectoryName(text2);
							FisterPlugin.Log.LogWarning((object)("assets are not in " + text + "; using " + dir));
						}
						else
						{
							FisterPlugin.Log.LogError((object)("John Fister's assets are missing: no body.txt under " + pluginDir + ". Reinstall the mod (the package's assets folder must sit next to JohnFister.dll)."));
						}
					}
					catch (Exception ex)
					{
						FisterPlugin.Log.LogError((object)("asset search failed: " + ex.Message));
					}
				}
				return dir;
			}
		}

		internal static Texture2D White
		{
			get
			{
				//IL_0011: Unknown result type (might be due to invalid IL or missing references)
				//IL_001b: Expected O, but got Unknown
				//IL_0029: 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)
				if ((Object)(object)white == (Object)null)
				{
					white = new Texture2D(4, 4, (TextureFormat)4, false);
					Color[] array = (Color[])(object)new Color[16];
					for (int i = 0; i < 16; i++)
					{
						array[i] = Color.white;
					}
					white.SetPixels(array);
					white.Apply();
					Object.DontDestroyOnLoad((Object)(object)white);
				}
				return white;
			}
		}

		internal static Shader Unlit
		{
			get
			{
				if ((Object)(object)unlit == (Object)null)
				{
					unlit = ((IEnumerable<Shader>)Resources.FindObjectsOfTypeAll<Shader>()).FirstOrDefault((Func<Shader, bool>)((Shader s) => (Object)(object)s != (Object)null && ((Object)s).name == "Particles/Standard Unlit"));
					if ((Object)(object)unlit == (Object)null)
					{
						unlit = Shader.Find("Particles/Standard Unlit") ?? Shader.Find("Unlit/Texture") ?? Shader.Find("Sprites/Default");
						FisterPlugin.Log.LogWarning((object)("Particles/Standard Unlit is not loaded; John is drawn with " + (((Object)(object)unlit != (Object)null) ? ((Object)unlit).name : "NO SHADER")));
					}
				}
				return unlit;
			}
		}

		internal static string Find(string sub, string file)
		{
			string text = Path.Combine(Path.Combine(Dir, sub), file);
			if (File.Exists(text))
			{
				return text;
			}
			string text2 = Path.Combine(Dir, file);
			if (File.Exists(text2))
			{
				return text2;
			}
			try
			{
				return Directory.GetFiles(FisterPlugin.PluginDir, file, SearchOption.AllDirectories).FirstOrDefault() ?? text;
			}
			catch (Exception)
			{
				return text;
			}
		}

		internal static Texture2D Tex(string name)
		{
			//IL_0034: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Expected O, but got Unknown
			if (textures.TryGetValue(name, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			string text = Find("tex", name + ".png");
			value = new Texture2D(2, 2, (TextureFormat)4, true);
			if (File.Exists(text))
			{
				ImageConversion.LoadImage(value, File.ReadAllBytes(text));
			}
			else
			{
				FisterPlugin.Log.LogError((object)("missing texture " + text));
			}
			((Object)value).name = "fister_" + name;
			((Texture)value).wrapMode = (TextureWrapMode)1;
			((Texture)value).filterMode = (FilterMode)1;
			Object.DontDestroyOnLoad((Object)(object)value);
			textures[name] = value;
			return value;
		}

		internal static void PreloadAll()
		{
			if (preloaded)
			{
				return;
			}
			preloaded = true;
			try
			{
				string path = Path.Combine(Dir, "tex");
				if (Directory.Exists(path))
				{
					string[] files = Directory.GetFiles(path, "*.png");
					for (int i = 0; i < files.Length; i++)
					{
						Tex(Path.GetFileNameWithoutExtension(files[i]));
					}
				}
				string path2 = Path.Combine(Dir, "sfx");
				if (Directory.Exists(path2))
				{
					string[] files = Directory.GetFiles(path2, "*.wav");
					for (int i = 0; i < files.Length; i++)
					{
						FisterAudio.Clip(Path.GetFileNameWithoutExtension(files[i]));
					}
				}
				_ = Unlit;
				Fx.Prewarm();
			}
			catch (Exception ex)
			{
				FisterPlugin.Log.LogWarning((object)("preload: " + ex.Message));
			}
		}

		internal static Material Material(Texture tex, int cull, bool transparent = false, bool additive = false)
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Expected O, but got Unknown
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			Material val = new Material(Unlit);
			val.mainTexture = tex;
			val.SetColor("_Color", Color.white);
			val.SetFloat("_Cull", (float)cull);
			if (transparent)
			{
				val.SetFloat("_Mode", 2f);
				val.SetFloat("_SrcBlend", 5f);
				val.SetFloat("_DstBlend", additive ? 1f : 10f);
				val.SetFloat("_ZWrite", 0f);
				val.renderQueue = 3000;
			}
			else
			{
				val.SetFloat("_Mode", 0f);
				val.SetFloat("_SrcBlend", 1f);
				val.SetFloat("_DstBlend", 0f);
				val.SetFloat("_ZWrite", 1f);
				val.renderQueue = 2000;
			}
			Object.DontDestroyOnLoad((Object)(object)val);
			return val;
		}
	}
	internal sealed class ContactProbe : MonoBehaviour
	{
		internal readonly List<Vector3> normals = new List<Vector3>(8);

		internal readonly List<Vector3> points = new List<Vector3>(8);

		private int frame = -1;

		private void Record(Collision c)
		{
			//IL_0036: Unknown result type (might be due to invalid IL or missing references)
			//IL_003b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: 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)
			if ((Object)(object)c.collider == (Object)null || c.collider.isTrigger)
			{
				return;
			}
			_ = frame;
			_ = Time.frameCount;
			int contactCount = c.contactCount;
			for (int i = 0; i < contactCount; i++)
			{
				if (normals.Count >= 8)
				{
					break;
				}
				ContactPoint contact = c.GetContact(i);
				normals.Add(((ContactPoint)(ref contact)).normal);
				points.Add(((ContactPoint)(ref contact)).point);
			}
		}

		private void OnCollisionEnter(Collision c)
		{
			Record(c);
		}

		private void OnCollisionStay(Collision c)
		{
			Record(c);
		}

		internal void Consume(List<Vector3> outNormals, List<Vector3> outPoints)
		{
			outNormals.Clear();
			outPoints.Clear();
			outNormals.AddRange(normals);
			outPoints.AddRange(points);
			normals.Clear();
			points.Clear();
		}
	}
	internal sealed class CooldownRing : MonoBehaviour
	{
		private static readonly Color ParryColor = new Color(8f / 51f, 0.5921569f, 1f);

		private static readonly Color GrabColor = new Color(1f, 0.83137256f, 0.23137255f);

		private const float FlashSeconds = 0.56f;

		private const int Segments = 28;

		private float parryFlash = -1f;

		private float grabFlash = -1f;

		private float lastParry;

		private float lastGrab;

		private float parryMax = 3f;

		private float grabMax = 2f;

		private void Update()
		{
			if (FisterMode.Active && !((Object)(object)FisterController.Instance == (Object)null))
			{
				Sim sim = FisterController.Instance.player.sim;
				float num = sim.ParryCooldown();
				float num2 = sim.GrabCooldown();
				if (num > lastParry + 0.01f)
				{
					parryMax = Mathf.Max(0.1f, num);
				}
				if (num2 > lastGrab + 0.01f)
				{
					grabMax = Mathf.Max(0.1f, num2);
				}
				if (lastParry > 0f && num <= 0f)
				{
					parryFlash = 0f;
				}
				if (lastGrab > 0f && num2 <= 0f)
				{
					grabFlash = 0f;
				}
				lastParry = num;
				lastGrab = num2;
				float unscaledDeltaTime = Time.unscaledDeltaTime;
				if (parryFlash >= 0f && (parryFlash += unscaledDeltaTime) > 0.56f)
				{
					parryFlash = -1f;
				}
				if (grabFlash >= 0f && (grabFlash += unscaledDeltaTime) > 0.56f)
				{
					grabFlash = -1f;
				}
			}
		}

		private static float FlashAlpha(float t)
		{
			float num = t / 0.56f;
			if (num < 0.14f)
			{
				return Mathf.Lerp(0f, 0.82f, num / 0.14f);
			}
			if (num < 0.46f)
			{
				return Mathf.Lerp(0.82f, 0.24f, (num - 0.14f) / 0.32f);
			}
			return Mathf.Lerp(0.24f, 0f, (num - 0.46f) / 0.54f);
		}

		private void OnGUI()
		{
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_000b: Invalid comparison between Unknown and I4
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			//IL_0098: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b3: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			if ((int)Event.current.type == 7 && FisterMode.Active && !((Object)(object)FisterController.Instance == (Object)null))
			{
				OptionsManager instance = MonoSingleton<OptionsManager>.Instance;
				NewMovement instance2 = MonoSingleton<NewMovement>.Instance;
				if ((!((Object)(object)instance != (Object)null) || !instance.paused) && !((Object)(object)instance2 == (Object)null) && !instance2.dead && !instance2.levelOver)
				{
					float num = Screen.height;
					float r = num * 0.028f;
					float thick = Mathf.Max(2f, Mathf.Round(num * 0.0035f));
					Vector2 c = new Vector2((float)Screen.width * 0.5f, num * 0.5f);
					Arc(c, r, thick, 180f, lastParry, parryMax, parryFlash, ParryColor);
					Arc(c, r, thick, 0f, lastGrab, grabMax, grabFlash, GrabColor);
				}
			}
		}

		private static void Arc(Vector2 c, float r, float thick, float centreDeg, float cooldown, float max, float flash, Color colour)
		{
			//IL_0078: Unknown result type (might be due to invalid IL or missing references)
			//IL_007d: Unknown result type (might be due to invalid IL or missing references)
			//IL_018c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_010d: Unknown result type (might be due to invalid IL or missing references)
			//IL_011c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_012c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0133: Unknown result type (might be due to invalid IL or missing references)
			//IL_0140: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_0157: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			bool flag = cooldown > 0f;
			if (flag || !(flash < 0f))
			{
				float num = (flag ? (1f - Mathf.Clamp01(cooldown / max)) : 1f);
				bool num2 = centreDeg > 90f;
				float num3 = (num2 ? (centreDeg - 55f) : (centreDeg + 55f));
				float num4 = (num2 ? 1f : (-1f));
				float num5 = (float)Math.PI * 2f * r * (11f / 36f) / 28f + 0.6f;
				Color color = GUI.color;
				for (int i = 0; i < 28; i++)
				{
					float num6 = ((float)i + 0.5f) / 28f;
					float num7 = (flag ? ((num6 <= num) ? 0.9f : 0.22f) : FlashAlpha(flash));
					Color color2 = ((flag && num6 > num) ? new Color(0f, 0f, 0f, num7) : new Color(colour.r, colour.g, colour.b, num7));
					float num8 = num3 + num4 * 110f * num6;
					float num9 = num8 * ((float)Math.PI / 180f);
					Vector2 val = c + new Vector2(Mathf.Cos(num9), Mathf.Sin(num9)) * r;
					GUI.color = color2;
					Matrix4x4 matrix = GUI.matrix;
					GUIUtility.RotateAroundPivot(num8 + 90f, val);
					GUI.DrawTexture(new Rect(val.x - num5 / 2f, val.y - thick / 2f, num5, thick), (Texture)(object)Assets.White);
					GUI.matrix = matrix;
				}
				GUI.color = color;
			}
		}
	}
	internal static class Defense
	{
		internal static Vector3? pendingDirection;

		internal static Sim Sim => FisterController.Instance?.player.sim;

		internal static bool Parrying
		{
			get
			{
				if (FisterMode.Active && Sim != null)
				{
					return Sim.IsParrying();
				}
				return false;
			}
		}

		internal static bool Invulnerable
		{
			get
			{
				if (FisterMode.Active && Sim != null)
				{
					return Sim.IsInvulnerable();
				}
				return false;
			}
		}

		internal static void MarkParry()
		{
			Sim.MarkParrySuccessful(0);
			FisterController.Instance.player.combat.HandleEvents(MonoSingleton<NewMovement>.Instance);
		}

		internal static void ParryProjectile(Projectile p)
		{
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00dc: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00eb: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f0: Unknown result type (might be due to invalid IL or missing references)
			Punch val = (((Object)(object)MonoSingleton<FistControl>.Instance != (Object)null) ? MonoSingleton<FistControl>.Instance.currentPunch : null);
			if ((Object)(object)val != (Object)null)
			{
				if ((Object)(object)val.fc == (Object)null)
				{
					val.fc = MonoSingleton<FistControl>.Instance;
				}
				if ((Object)(object)val.anim == (Object)null)
				{
					val.anim = ((Component)val).GetComponent<Animator>();
				}
				val.ParryProjectile(p);
			}
			else
			{
				p.friendly = true;
				p.parried = true;
				p.homingType = (HomingType)0;
				p.speed = Mathf.Max(p.speed * 2f, 100f);
				MonoSingleton<NewMovement>.Instance.Parry((EnemyIdentifier)null, "");
			}
			NewMovement instance = MonoSingleton<NewMovement>.Instance;
			CameraController cc = instance.cc;
			Vector3 johnAim = Quaternion.Euler(0f - cc.rotationX, cc.rotationY, 0f) * Vector3.forward;
			ParryReturn.Aim(p, ((Component)instance).transform.position + Vector3.up * 1.4f, johnAim);
		}

		internal static void Counter(EnemyIdentifier eid, Vector3 attackForward)
		{
			//IL_0023: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Unknown result type (might be due to invalid IL or missing references)
			//IL_005d: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)eid == (Object)null) && !eid.dead)
			{
				float num = 100f;
				Vec3 vec = FisterWorld.BodyCentre(eid).Sub(Units.BodyCentreFromFeet(FisterPlayer.Feet(MonoSingleton<NewMovement>.Instance)));
				Vec3 vec2 = new Vec3(vec.x, vec.y, 0f).Normalized();
				if (vec2.Length() < 0.5f)
				{
					vec2 = Units.DirToSim(-attackForward);
				}
				vec2.z = 0.3f;
				FisterWorld.Launch(eid, vec2.Normalized().Mul(Mathf.Min(60f, num)), 2f, 3f);
			}
		}
	}
	[HarmonyPatch(typeof(Projectile), "Collided")]
	internal static class ProjectileParryPatch
	{
		private static bool Prefix(Projectile __instance, Collider other)
		{
			//IL_008a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0092: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009a: Unknown result type (might be due to invalid IL or missing references)
			if (!FisterMode.Active || __instance.friendly || __instance.hittingPlayer || !((Component)other).gameObject.CompareTag("Player"))
			{
				return true;
			}
			if (Defense.Parrying && !__instance.unparryable && !__instance.undeflectable)
			{
				Defense.ParryProjectile(__instance);
				Defense.MarkParry();
				return false;
			}
			if (Defense.Invulnerable || Defense.Parrying)
			{
				return false;
			}
			Rigidbody component = ((Component)__instance).GetComponent<Rigidbody>();
			Vector3 value;
			if ((Object)(object)component != (Object)null)
			{
				Vector3 velocity = component.velocity;
				if (((Vector3)(ref velocity)).sqrMagnitude > 0.01f)
				{
					velocity = component.velocity;
					value = ((Vector3)(ref velocity)).normalized;
					goto IL_009f;
				}
			}
			value = ((Component)__instance).transform.forward;
			goto IL_009f;
			IL_009f:
			Defense.pendingDirection = value;
			return true;
		}
	}
	[HarmonyPatch(typeof(SwingCheck2), "CheckCollision")]
	internal static class MeleeParryPatch
	{
		private static bool Prefix(SwingCheck2 __instance, Collider other)
		{
			//IL_004a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			if (!FisterMode.Active || !((Component)other).gameObject.CompareTag("Player") || __instance.hitColliders.Contains(other))
			{
				return true;
			}
			if (Defense.Parrying)
			{
				__instance.hitColliders.Add(other);
				EnemyIdentifier eid = __instance.eid;
				Defense.Counter(eid, ((Component)__instance).transform.forward);
				MonoSingleton<NewMovement>.Instance.Parry(eid, "");
				Defense.MarkParry();
				return false;
			}
			if (Defense.Invulnerable)
			{
				return false;
			}
			Defense.pendingDirection = ((Component)__instance).transform.forward;
			return true;
		}
	}
	[HarmonyPatch(typeof(NewMovement), "GetHurt")]
	internal static class GetHurtPatch
	{
		private static bool Prefix(NewMovement __instance, int damage, bool invincible, bool instablack, bool ignoreInvincibility)
		{
			//IL_00b2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_00b8: 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_00c9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d2: Unknown result type (might be due to invalid IL or missing references)
			if (!FisterMode.Active || SelfDamageGuard.Active || __instance.dead)
			{
				return true;
			}
			bool flag = instablack || damage >= 999;
			if (!flag && (Defense.Parrying || Defense.Invulnerable))
			{
				if (Defense.Parrying)
				{
					Defense.MarkParry();
				}
				Defense.pendingDirection = null;
				return false;
			}
			if (damage > 0 && !flag && (!invincible || ((Component)__instance).gameObject.layer != 15 || ignoreInvincibility))
			{
				float num = Mathf.Clamp01((float)damage * 2f / 50f);
				Vector3 val = (Vector3)(((??)Defense.pendingDirection) ?? (-((Component)__instance).transform.forward));
				Vector3 val2 = new Vector3(val.x, 0f, val.z);
				Vec3 vec = Units.DirToSim(((Vector3)(ref val2)).normalized).Mul(2f + 4f * num);
				vec.z = 1f;
				Defense.Sim.OnHitByAttack(num, vec);
			}
			Defense.pendingDirection = null;
			return true;
		}
	}
	[HarmonyPatch(typeof(NewMovement), "LaunchFromPoint")]
	internal static class NoLaunchFromPoint
	{
		private static bool Prefix()
		{
			return !FisterMode.Active;
		}
	}
	[HarmonyPatch(typeof(NewMovement), "Slamdown")]
	internal static class NoSlamdown
	{
		private static bool Prefix()
		{
			return !FisterMode.Active;
		}
	}
	[HarmonyPatch(typeof(Enemy), "FixedUpdate")]
	internal static class EnemyFixedPatch
	{
		private static bool Prefix(Enemy __instance)
		{
			if (FisterWorld.Victims.Count != 0)
			{
				return !FisterWorld.IsVictim((Component)(object)__instance);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Zombie), "FixedUpdate")]
	internal static class ZombieFixedPatch
	{
		private static bool Prefix(Zombie __instance)
		{
			if (FisterWorld.Victims.Count != 0)
			{
				return !FisterWorld.IsVictim((Component)(object)__instance);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Machine), "FixedUpdate")]
	internal static class MachineFixedPatch
	{
		private static bool Prefix(Machine __instance)
		{
			if (FisterWorld.Victims.Count != 0)
			{
				return !FisterWorld.IsVictim((Component)(object)__instance);
			}
			return true;
		}
	}
	[HarmonyPatch(typeof(Drone), "FixedUpdate")]
	internal static class DroneFixedPatch
	{
		private static bool Prefix(Drone __instance)
		{
			if (FisterWorld.Victims.Count != 0)
			{
				return !FisterWorld.IsVictim((Component)(object)__instance);
			}
			return true;
		}
	}
	internal static class FisterAudio
	{
		internal const float Master = 0.5f;

		private static readonly Dictionary<string, AudioClip> clips = new Dictionary<string, AudioClip>();

		private static readonly List<AudioSource> pool = new List<AudioSource>();

		private static GameObject host;

		private static readonly Dictionary<string, AudioSource> loops = new Dictionary<string, AudioSource>();

		private static Action<string, float> Played;

		internal static float UserVolume => 0.5f * FisterConfig.Volume.Value;

		internal static void ObservePlays(Action<string, float> a)
		{
			Played = (Action<string, float>)Delegate.Combine(Played, a);
		}

		internal static AudioClip Clip(string name)
		{
			if (clips.TryGetValue(name, out var value) && (Object)(object)value != (Object)null)
			{
				return value;
			}
			string text = Assets.Find("sfx", name + ".wav");
			value = (File.Exists(text) ? LoadWav(text, name) : null);
			if ((Object)(object)value == (Object)null)
			{
				FisterPlugin.Log.LogWarning((object)("missing sound " + text));
			}
			clips[name] = value;
			return value;
		}

		private static AudioClip LoadWav(string path, string name)
		{
			byte[] array = File.ReadAllBytes(path);
			int num = 1;
			int num2 = 44100;
			int num3 = 16;
			int num4 = -1;
			int num5 = 0;
			int num6;
			for (int i = 12; i + 8 <= array.Length; i += 8 + num6 + (num6 & 1))
			{
				string text = Encoding.ASCII.GetString(array, i, 4);
				num6 = BitConverter.ToInt32(array, i + 4);
				if (text == "fmt ")
				{
					num = BitConverter.ToInt16(array, i + 10);
					num2 = BitConverter.ToInt32(array, i + 12);
					num3 = BitConverter.ToInt16(array, i + 22);
				}
				else if (text == "data")
				{
					num4 = i + 8;
					num5 = Mathf.Min(num6, array.Length - num4);
					break;
				}
			}
			if (num4 < 0 || num3 != 16)
			{
				return null;
			}
			int num7 = num5 / 2;
			float[] array2 = new float[num7];
			for (int j = 0; j < num7; j++)
			{
				array2[j] = (float)BitConverter.ToInt16(array, num4 + j * 2) / 32768f;
			}
			AudioClip obj = AudioClip.Create("fister_" + name, num7 / num, num, num2, false);
			obj.SetData(array2, 0);
			Object.DontDestroyOnLoad((Object)(object)obj);
			return obj;
		}

		private static void EnsureHost()
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_001c: Expected O, but got Unknown
			if ((Object)(object)host == (Object)null)
			{
				host = new GameObject("FisterAudio");
				Object.DontDestroyOnLoad((Object)(object)host);
				pool.Clear();
				loops.Clear();
			}
		}

		private static AudioSource NewSource()
		{
			AudioSource val = host.AddComponent<AudioSource>();
			val.playOnAwake = false;
			val.spatialBlend = 0f;
			AudioMixerController instance = MonoSingleton<AudioMixerController>.Instance;
			if ((Object)(object)instance != (Object)null)
			{
				val.outputAudioMixerGroup = instance.allGroup;
			}
			return val;
		}

		internal static void Play(string name, Vector3 at, float volume, float pitch = 1f)
		{
			//IL_00d0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d5: Unknown result type (might be due to invalid IL or missing references)
			AudioClip val = Clip(name);
			if ((Object)(object)val == (Object)null)
			{
				return;
			}
			EnsureHost();
			AudioSource val2 = null;
			foreach (AudioSource item in pool)
			{
				if ((Object)(object)item != (Object)null && !item.isPlaying)
				{
					val2 = item;
					break;
				}
			}
			if ((Object)(object)val2 == (Object)null)
			{
				if (pool.Count >= 24)
				{
					return;
				}
				val2 = NewSource();
				pool.Add(val2);
			}
			AudioMixerController instance = MonoSingleton<AudioMixerController>.Instance;
			if ((Object)(object)instance != (Object)null && (Object)(object)val2.outputAudioMixerGroup == (Object)null)
			{
				val2.outputAudioMixerGroup = instance.allGroup;
			}
			float num = 1f;
			NewMovement instance2 = MonoSingleton<NewMovement>.Instance;
			if ((Object)(object)instance2 != (Object)null)
			{
				float num2 = Vector3.Distance(((Component)instance2).transform.position, at) / 1.1798415f;
				num = Mathf.Clamp(1f - (num2 - 20f) / 100f, 0.3f, 1f);
			}
			float num3 = Mathf.Clamp01(volume * UserVolume * num);
			val2.pitch = pitch;
			val2.PlayOneShot(val, num3);
			Played?.Invoke(name, num3);
		}

		internal static void Loop(string name, float volume, float pitch)
		{
			EnsureHost();
			loops.TryGetValue(name, out var value);
			if (volume <= 0.001f)
			{
				if ((Object)(object)value != (Object)null && value.isPlaying)
				{
					value.Stop();
				}
				return;
			}
			if ((Object)(object)value == (Object)null)
			{
				value = NewSource();
				value.loop = true;
				value.clip = Clip(name);
				loops[name] = value;
			}
			if (!((Object)(object)value.clip == (Object)null))
			{
				value.volume = Mathf.Clamp01(volume * UserVolume);
				value.pitch = pitch;
				if (!value.isPlaying)
				{
					value.Play();
					Played?.Invoke(name + "(loop)", value.volume);
				}
			}
		}

		internal static void StopLoops()
		{
			foreach (AudioSource value in loops.Values)
			{
				if ((Object)(object)value != (Object)null)
				{
					value.Stop();
				}
			}
		}
	}
	internal static class FisterCamera
	{
		internal const float PivotAboveHead = 1.25f;

		internal const float HeadAboveFeet = 2.3f;

		internal const float ScreenFraction = 0.2f;

		internal static Vector3 FollowFeet;

		internal static bool HasFollow;

		internal static Vector3 Shake;

		internal static float FovPush;

		private static bool overriding;

		private static float smoothedDistance;

		private static Camera mainCam;

		private static bool hooked;

		private static bool moved;

		private static Vector3 savedLocalPos;

		private static Quaternion savedLocalRot;

		private static ParticleSystemRenderer[] heat;

		private static float[] heatMax;

		private static int heatScene = -1;

		internal static float HeatWaveMaxSize = 0.06f;

		internal static Vector3 Pivot { get; private set; }

		internal static Vector3 ViewPosition { get; private set; }

		internal static Quaternion ViewRotation { get; private set; }

		internal static float Distance { get; private set; }

		internal static float BodyAlpha { get; private set; } = 1f;

		internal static float DistanceFor(float verticalFov)
		{
			return Mathf.Clamp(2.6f / (0.4f * Mathf.Tan(verticalFov * 0.5f * ((float)Math.PI / 180f))), 4f, 12f);
		}

		internal static void Apply(CameraController cc)
		{
			//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)
			//IL_0049: 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_005a: Unknown result type (might be due to invalid IL or missing references)
			//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_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_006b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0070: Unknown result type (might be due to invalid IL or missing references)
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			//IL_007c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ec: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00fa: Unknown result type (might be due to invalid IL or missing references)
			//IL_0100: Unknown result type (might be due to invalid IL or missing references)
			//IL_0105: Unknown result type (might be due to invalid IL or missing references)
			//IL_010a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0113: Unknown result type (might be due to invalid IL or missing references)
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_015e: Unknown result type (might be due to invalid IL or missing references)
			//IL_015f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0164: Unknown result type (might be due to invalid IL or missing references)
			//IL_0169: Unknown result type (might be due to invalid IL or missing references)
			//IL_016f: 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_0125: Unknown result type (might be due to invalid IL or missing references)
			//IL_0127: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0144: Unknown result type (might be due to invalid IL or missing references)
			//IL_0149: Unknown result type (might be due to invalid IL or missing references)
			//IL_0221: Unknown result type (might be due to invalid IL or missing references)
			//IL_0223: 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_0236: Unknown result type (might be due to invalid IL or missing references)
			//IL_023b: Unknown result type (might be due to invalid IL or missing references)
			//IL_023d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0244: Unknown result type (might be due to invalid IL or missing references)
			//IL_024b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0268: Unknown result type (might be due to invalid IL or missing references)
			//IL_026d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_0274: Unknown result type (might be due to invalid IL or missing references)
			//IL_0275: Unknown result type (might be due to invalid IL or missing references)
			//IL_027a: Unknown result type (might be due to invalid IL or missing references)
			//IL_027f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0283: Unknown result type (might be due to invalid IL or missing references)
			//IL_0288: Unknown result type (might be due to invalid IL or missing references)
			Camera cam = cc.cam;
			if ((Object)(object)cam == (Object)null)
			{
				return;
			}
			RestoreTransform();
			if (!FisterMode.Active || !HasFollow)
			{
				if (overriding)
				{
					cam.ResetWorldToCameraMatrix();
					overriding = false;
				}
				return;
			}
			Quaternion val = cc.gravityRotation * Quaternion.AngleAxis(cc.rotationY, Vector3.up) * Quaternion.AngleAxis(0f - cc.rotationX, Vector3.right);
			Vector3 val2 = cc.gravityRotation * Vector3.up;
			int num = LayerMask.op_Implicit(LayerMaskDefaults.Get((LMD)1));
			float nearClipPlane = cam.nearClipPlane;
			float num2 = nearClipPlane * Mathf.Tan(cam.fieldOfView * 0.5f * ((float)Math.PI / 180f));
			float num3 = Mathf.Max(0.25f, Mathf.Sqrt(nearClipPlane * nearClipPlane + num2 * num2 * (1f + cam.aspect * cam.aspect)) + 0.05f);
			Vector3 val3 = FollowFeet + val2 * 2.7136354f;
			Vector3 val4 = val3 + val2 * 1.4748019f;
			float num4 = 1.4748019f;
			RaycastHit val5 = default(RaycastHit);
			if (Physics.SphereCast(val3, num3, val2, ref val5, num4, num, (QueryTriggerInteraction)1))
			{
				val4 = val3 + val2 * Mathf.Max(0f, ((RaycastHit)(ref val5)).distance - 0.05f);
			}
			float num5 = DistanceFor(cam.fieldOfView) * 1.1798415f;
			Vector3 val6 = val * Vector3.back;
			float num6 = num5;
			RaycastHit val7 = default(RaycastHit);
			if (Physics.SphereCast(val4, num3, val6, ref val7, num5, num, (QueryTriggerInteraction)1))
			{
				num6 = Mathf.Max(0.15f, ((RaycastHit)(ref val7)).distance - 0.05f);
			}
			float num7 = Mathf.Min(Time.unscaledDeltaTime, 0.1f);
			if (smoothedDistance <= 0f || num6 < smoothedDistance)
			{
				smoothedDistance = num6;
			}
			else
			{
				smoothedDistance = Mathf.Min(num6, Mathf.Lerp(smoothedDistance, num6, 1f - Mathf.Exp(-3f * num7)));
			}
			float num8 = smoothedDistance;
			BodyAlpha = Mathf.Lerp(0.35f, 1f, Mathf.InverseLerp(0.8f, 2.6f, num8));
			Vector3 val8 = val4 + val6 * num8 + Shake;
			Pivot = val4;
			ViewPosition = val8;
			ViewRotation = val;
			Distance = num8;
			Matrix4x4 val9 = Matrix4x4.Scale(new Vector3(1f, 1f, -1f));
			Matrix4x4 val10 = Matrix4x4.TRS(val8, val, Vector3.one);
			cam.worldToCameraMatrix = val9 * ((Matrix4x4)(ref val10)).inverse;
			overriding = true;
			mainCam = cam;
			Hook();
		}

		private static void Hook()
		{
			//IL_001a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Expected O, but got Unknown
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_002e: Expected O, but got Unknown
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0044: Expected O, but got Unknown
			//IL_0044: Unknown result type (might be due to invalid IL or missing references)
			//IL_004e: Expected O, but got Unknown
			if (!hooked)
			{
				hooked = true;
				Camera.onPreCull = (CameraCallback)Delegate.Combine((Delegate?)(object)Camera.onPreCull, (Delegate?)new CameraCallback(OnPreCull));
				Camera.onPostRender = (CameraCallback)Delegate.Combine((Delegate?)(object)Camera.onPostRender, (Delegate?)new CameraCallback(OnPostRender));
			}
		}

		private static void OnPreCull(Camera cam)
		{
			//IL_0033: Unknown result type (might be due to invalid IL or missing references)
			//IL_0038: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Unknown result type (might be due to invalid IL or missing references)
			if (!((Object)(object)cam == (Object)null) && !((Object)(object)cam != (Object)(object)mainCam) && overriding && !moved && FisterMode.Active)
			{
				Transform transform = ((Component)cam).transform;
				savedLocalPos = transform.localPosition;
				savedLocalRot = transform.localRotation;
				transform.SetPositionAndRotation(ViewPosition, ViewRotation);
				moved = true;
				TameHeatWaves(tame: true);
			}
		}

		private static void OnPostRender(Camera cam)
		{
			if ((Object)(object)cam == (Object)(object)mainCam)
			{
				RestoreTransform();
			}
		}

		private static void TameHeatWaves(bool tame)
		{
			//IL_0000: Unknown result type (might be due to invalid IL or missing references)
			//IL_0005: Unknown result type (might be due to invalid IL or missing references)
			//IL_0054: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			try
			{
				Scene val = SceneManager.GetActiveScene();
				int handle = ((Scene)(ref val)).handle;
				if (tame && (heat == null || heatScene != handle))
				{
					heatScene = handle;
					List<ParticleSystemRenderer> list = new List<ParticleSystemRenderer>();
					ParticleSystemRenderer[] array = Resources.FindObjectsOfTypeAll<ParticleSystemRenderer>();
					foreach (ParticleSystemRenderer val2 in array)
					{
						if ((Object)(object)val2 != (Object)null)
						{
							val = ((Component)val2).gameObject.scene;
							if (((Scene)(ref val)).IsValid() && (Object)(object)((Renderer)val2).sharedMaterial != (Object)null && (Object)(object)((Renderer)val2).sharedMaterial.shader != (Object)null && ((Object)((Renderer)val2).sharedMaterial.shader).name.Contains("HeatWave"))
							{
								list.Add(val2);
							}
						}
					}
					heat = list.ToArray();
					heatMax = new float[heat.Length];
					for (int j = 0; j < heat.Length; j++)
					{
						heatMax[j] = heat[j].maxParticleSize;
					}
				}
				if (heat == null)
				{
					return;
				}
				for (int k = 0; k < heat.Length; k++)
				{
					if ((Object)(object)heat[k] != (Object)null)
					{
						heat[k].maxParticleSize = (tame ? Mathf.Min(heatMax[k], HeatWaveMaxSize) : heatMax[k]);
					}
				}
			}
			catch (Exception)
			{
				heat = null;
			}
		}

		internal static void RestoreTransform()
		{
			//IL_002b: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			if (moved)
			{
				moved = false;
				TameHeatWaves(tame: false);
				if ((Object)(object)mainCam != (Object)null)
				{
					((Component)mainCam).transform.localPosition = savedLocalPos;
					((Component)mainCam).transform.localRotation = savedLocalRot;
				}
			}
		}
	}
	[HarmonyPatch(typeof(CameraController), "LateUpdate")]
	internal static class CameraLateUpdatePatch
	{
		private static void Postfix(CameraController __instance)
		{
			if ((Object)(object)FisterController.Instance != (Object)null && FisterMode.Active)
			{
				FisterController.Instance.OnCameraLateUpdate(__instance);
			}
			else
			{
				FisterCamera.Apply(__instance);
			}
		}
	}
	internal sealed class FisterCombat
	{
		private struct Hit
		{
			internal EnemyIdentifier ent;

			internal float distance;

			internal Vec3 centre;
		}

		private readonly FisterPlayer owner;

		private float pendingImpactDamage;

		private double pendingImpactAt;

		private static Action<Event> EventObserved;

		private bool diveHitAnyone;

		private EnemyIdentifier airKickHomingTarget;

		private EnemyIdentifier airKickLockTarget;

		private EnemyIdentifier grabbed;

		private float lastAirKickScale = 1f;

		private readonly HashSet<EnemyIdentifier> railingHit = new HashSet<EnemyIdentifier>();

		private readonly HashSet<EnemyIdentifier> ascentHit = new HashSet<EnemyIdentifier>();

		private readonly HashSet<EnemyIdentifier> grabStandIn = new HashSet<EnemyIdentifier>();

		private readonly Dictionary<EnemyIdentifier, double[]> taps = new Dictionary<EnemyIdentifier, double[]>();

		private static Action<EnemyIdentifier, Vec3> ThrowObserved;

		private readonly List<EnemyIdentifier> sweepBuffer = new List<EnemyIdentifier>(32);

		private int airKickChain;

		private float lastKickHitTime = -10f;

		private bool landedSinceKick = true;

		private Sim sim => owner.sim;

		internal EnemyIdentifier AirKickLockTarget => airKickLockTarget;

		private float Yaw => owner.yaw;

		internal int AirKickChain => airKickChain;

		internal FisterCombat(FisterPlayer owner)
		{
			this.owner = owner;
		}

		internal void Reset()
		{
			pendingImpactDamage = 0f;
			ResetTargets();
		}

		internal void HandleEvents(NewMovement nm)
		{
			List<Event> list = new List<Event>(sim.Events());
			sim.Events().Clear();
			for (int i = 0; i < list.Count; i++)
			{
				Event obj = list[i];
				try
				{
					HandleEvent(nm, obj);
				}
				catch (Exception ex)
				{
					FisterPlugin.Log.LogError((object)("event " + obj.type.ToString() + " handler failed: " + ex));
				}
				list.AddRange(sim.Events());
				sim.Events().Clear();
			}
		}

		internal static void ObserveEvents(Action<Event> a)
		{
			EventObserved = (Action<Event>)Delegate.Combine(EventObserved, a);
		}

		private void HandleEvent(NewMovement nm, Event e)
		{
			//IL_001d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0024: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bd: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Unknown result type (might be due to invalid IL or missing references)
			//IL_013f: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b7: Unknown result type (might be due to invalid IL or missing references)
			//IL_03d1: Unknown result type (might be due to invalid IL or missing references)
			//IL_03f6: Unknown result type (might be due to invalid IL or missing references)
			//IL_0128: Unknown result type (might be due to invalid IL or missing references)
			//IL_0179: Unknown result type (might be due to invalid IL or missing references)
			//IL_0272: Unknown result type (might be due to invalid IL or missing references)
			//IL_0253: 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)
			//IL_025e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0263: Unknown result type (might be due to invalid IL or missing references)
			//IL_029c: Unknown result type (might be due to invalid IL or missing references)
			//IL_042b: Unknown result type (might be due to invalid IL or missing references)
			//IL_040a: Unknown result type (might be due to invalid IL or missing references)
			//IL_035e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0220: 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_0386: Unknown result type (might be due to invalid IL or missing references)
			//IL_0387: Unknown result type (might be due to invalid IL or missing references)
			//IL_0391: Unknown result type (might be due to invalid IL or missing references)
			//IL_0396: Unknown result type (might be due to invalid IL or missing references)
			//IL_03b6: Unknown result type (might be due to invalid IL or missing references)
			EventObserved?.Invoke(e);
			Fx instance = Fx.Instance;
			Vector3 at = Units.PosToUnity(e.position);
			Vector3 val = FisterPlayer.Feet(nm);
			switch (e.type)
			{
			case EventType.ChargeReady:
				FisterAudio.Play("reload", val, 0.4f);
				FisterController.Instance.anim.TriggerChargeReady();
				break;
			case EventType.PunchThrown:
				FisterAudio.Play("punch", val, 1f);
				FisterController.Instance.anim.TriggerPunch(e.strength, owner.pitch);
				ResolvePunch(nm, e);
				break;
			case EventType.RocketPunchLaunch:
				ResetAscent();
				instance?.GroundHop(val, 0.6f);
				break;
			case EventType.DiveStart:
				diveHitAnyone = false;
				break;
			case EventType.DiveImpact:
			case EventType.DiveObstacle:
				ResolveDive(nm, e, at);
				break;
			case EventType.AirKickStart:
				airKickHomingTarget = (e.flag ? airKickLockTarget : null);
				break;
			case EventType.AirKickWhiff:
			{
				float num2 = FMath.Clamp(lastAirKickScale, 0.5f, 1.5f);
				instance?.AirKickWhiff(at, num2);
				FisterController.Instance.anim.TriggerImpactFrame(0.04f + 0.025f * num2);
				Shake(0.25f * num2);
				break;
			}
			case EventType.RailingStart:
				ResetRailing();
				FisterAudio.Play("overcharge-dash-screech", val, 1.2f);
				break;
			case EventType.RailingBounce:
			{
				sim.ScaleLaunch(Tuning.RailingBounce.Value);
				float num = (e.flag ? 2.35f : FMath.Clamp(e.value / 1.1f / 88f, 1f, 2.25f));
				instance?.RailingBounce(at, num);
				FisterAudio.Play("crack", at, 1f, 0.9f);
				Shake(0.2f * num);
				if (e.flag)
				{
					SelfKill(nm);
				}
				break;
			}
			case EventType.RailingMegaBoost:
				instance?.MegaBoost(val + Vector3.up * 1.1798415f);
				FisterAudio.Play("railing-sonic-boom", val, 1f);
				break;
			case EventType.GroundPunchHop:
				sim.ScaleLaunch(Tuning.GroundHop.Value);
				instance?.GroundHop(val, 1f);
				ResolveGroundHop(nm, e);
				break;
			case EventType.ParryStart:
				ParryReceivers(nm);
				break;
			case EventType.ParrySuccess:
				if (e.kind == 3)
				{
					pendingImpactDamage = 0f;
					sim.ScaleLaunch(Tuning.WallParry.Value);
				}
				else if (e.kind == 1)
				{
					sim.ScaleLaunch(Tuning.ParryJump.Value);
					owner.StartParryJumpFlight();
				}
				else if (e.kind == 2 && sim.IsOverchargeDashing())
				{
					sim.ScaleLaunch(Tuning.RailingReverse.Value);
				}
				else if (e.kind == 2)
				{
					sim.ScaleLaunch(Tuning.GroundParry.Value);
				}
				FisterAudio.Play("parry-success", val, 1.8f);
				FisterController.Instance.anim.TriggerImpactFrame(0.1f);
				instance?.Parry(val + Vector3.up * 1.1798415f);
				instance?.Flash(new Color(53f / 85f, 43f / 51f, 1f), 0.35f);
				break;
			case EventType.GrabStart:
				ClearGrabStandIns();
				FisterAudio.Play("grab-lunge", val, 0.45f);
				break;
			case EventType.GrabMiss:
			case EventType.GrabCancel:
				ReleaseGrabbed(breakSim: false);
				break;
			case EventType.GrabThrow:
				ResolveThrow(nm, e);
				break;
			case EventType.PiledriverImpact:
				ResolvePiledriver(nm, e, at);
				break;
			case EventType.OverchargeExplode:
				ResolveOverchargeExplode(nm, e);
				break;
			case EventType.RagdollImpact:
				instance?.Impact(at, Mathf.Min(1f, e.value / 15f));
				FisterAudio.Play("crack", at, Mathf.Min(1f, 0.3f + e.value / 20f));
				if (sim.parryLateWindow > 0f)
				{
					pendingImpactDamage += e.value;
					pendingImpactAt = owner.clock;
				}
				else
				{
					SelfDamage(nm, e.value);
				}
				break;
			case EventType.SelfDamage:
				SelfDamage(nm, e.value);
				break;
			case EventType.Ragdoll:
				ReleaseGrabbed(breakSim: false);
				break;
			case EventType.AirKickStop:
			case EventType.RailingStop:
			case EventType.ParryEndlag:
			case EventType.SlideStart:
			case EventType.SlideStop:
			case EventType.AirTech:
			case EventType.Flip:
			case EventType.OverchargeWarning:
			case EventType.Recover:
				break;
			}
		}

		private void ParryReceivers(NewMovement nm)
		{
			//IL_000d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			//IL_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_003e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0043: Unknown result type (might be due to invalid IL or missing references)
			//IL_0048: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: 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_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0055: Unknown result type (might be due to invalid IL or missing references)
			//IL_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)
			CameraController cc = nm.cc;
			Vector3 val = ((Component)nm).transform.position + ((Component)nm).transform.up * 1.4f;
			Vector3 val2 = Quaternion.Euler(0f - cc.rotationX, cc.rotationY, 0f) * Vector3.forward;
			Collider[] array = Physics.OverlapCapsule(val, val + val2 * 5f * 1.1798415f, 1.5f, -1, (QueryTriggerInteraction)2);
			foreach (Collider val3 in array)
			{
				ParryReceiver val4 = (((Object)(object)val3 != (Object)null) ? ((Component)val3).GetComponent<ParryReceiver>() : null);
				if (!((Object)(object)val4 == (Object)null) && ((Behaviour)val4).enabled)
				{
					if (val4.parryHeal)
					{
						nm.Parry((EnemyIdentifier)null, "");
					}
					else
					{
						MonoSingleton<TimeController>.Instance.ParryFlash();
					}
					val4.Parry();
					sim.MarkParrySuccessful(0);
					FisterPlugin.Log.LogInfo((object)("parried " + ((Object)val3).name));
					break;
				}
			}
		}

		internal static void Shake(float intensity)
		{
			if (FisterConfig.ScreenShake.Value)
			{
				FisterController.Instance?.AddShake(intensity);
			}
		}

		internal static void ObserveThrows(Action<EnemyIdentifier, Vec3> a)
		{
			ThrowObserved = (Action<EnemyIdentifier, Vec3>)Delegate.Combine(ThrowObserved, a);
		}

		private void ResetTargets()
		{
			diveHitAnyone = false;
			airKickHomingTarget = (airKickLockTarget = null);
			grabbed = null;
			railingHit.Clear();
			ascentHit.Clear();
			taps.Clear();
		}

		private void ClearGrabStandIns()
		{
			grabStandIn.Clear();
		}

		private void ResetAscent()
		{
			ascentHit.Clear();
		}

		private void ResetRailing()
		{
			railingHit.Clear();
		}

		private void ApplyPunchToTarget(EnemyIdentifier target, Vec3 direction, float velocity, float strength, bool critical)
		{
			bool flag = FisterWorld.IsRagdolled(target);
			bool flag2 = FisterWorld.IsHardKnocked(target);
			float num = FisterWorld.Health(target);
			float num2 = Mathf.Max(0.01f, FisterWorld.MaxHealth(target));
			float num3 = num2 * 0.2f;
			if (strength >= 0.95f && num > 0f && !target.isBoss && !FisterWorld.IsKillProtected(target) && (num <= num3 || (flag2 && num <= num2 * 0.5f)))
			{
				FisterWorld.KillAndLaunch(target, direction.Mul(velocity));
				return;
			}
			float hardKnockdown = 0f;
			if (strength >= 0.95f && flag2)
			{
				if (!critical)
				{
					velocity *= 10f;
				}
				hardKnockdown = 2f;
			}
			if (flag)
			{
				Vec3 vec = new Vec3(direction.x * velocity, direction.y * velocity, 0.5f * velocity);
				if (strength < 0.6f)
				{
					vec = vec.Mul(0.5f);
				}
				FisterWorld.Launch(target, vec, hardKnockdown, 0f);
			}
			else if (strength < 0.6f)
			{
				double clock = owner.clock;
				if (!taps.TryGetValue(target, out var value) || clock - value[2] > 0.65 || clock - value[1] > 1.8)
				{
					value = new double[3] { 0.0, clock, clock };
					taps[target] = value;
				}
				value[2] = clock;
				value[0] += 1.0;
				if (value[0] >= 4.0)
				{
					value[0] = 0.0;
					FisterWorld.Launch(target, direction.Mul(velocity * 0.5f), 0f, 1f);
				}
				else
				{
					FisterWorld.Stagger(target, strength, direction.Mul(velocity * 0.5f));
				}
			}
			else
			{
				FisterWorld.Launch(target, direction.Mul(velocity), hardKnockdown, 4f);
			}
		}

		private void ResolvePunch(NewMovement nm, Event e)
		{
			//IL_0348: Unknown result type (might be due to invalid IL or missing references)
			//IL_048d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0494: Unknown result type (might be due to invalid IL or missing references)
			//IL_0586: Unknown result type (might be due to invalid IL or missing references)
			//IL_058b: Unknown result type (might be due to invalid IL or missing references)
			//IL_055f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0594: Unknown result type (might be due to invalid IL or missing references)
			//IL_0598: Unknown result type (might be due to invalid IL or missing references)
			//IL_05a5: Unknown result type (might be due to invalid IL or missing references)
			//IL_050f: Unknown result type (might be due to invalid IL or missing references)
			//IL_05c1: Unknown result type (might be due to invalid IL or missing references)
			//IL_05df: Unknown result type (might be due to invalid IL or missing references)
			float strength = e.strength;
			int kind = e.kind;
			bool critical = e.critical;
			bool flag = kind == 2;
			Vec3 position = e.position;
			Vec3 vec = FMath.YawForward(Yaw);
			Vec3 direction = e.direction;
			Vec3 vec2 = sim.Velocity();
			float num = 15f + strength * 25f;
			float num2 = (critical ? 10f : 1f);
			float num3 = Mathf.Max(0f, vec.Dot(vec2));
			float num4 = 1f + num3 / 20f * 0.5f;
			float num5 = (flag ? 18f : 5f);
			float arcCos = FMath.Cos((flag ? 0.9424779f : ((float)Math.PI * 3f / 4f)) / 2f);
			List<Hit> list = new List<Hit>();
			foreach (EnemyIdentifier item in FisterWorld.Targets(position, num5 + 12f))
			{
				float hitRadius = (FisterWorld.IsRagdolled(item) ? 1f : 0.5f);
				Vec3 vec3 = FisterWorld.BodyCentre(item);
				float bodyAllowance = Mathf.Max(0f, BodyHalfHeight(item) - 1.3f);
				float hitDistance;
				bool flag2 = PunchReaches(position, vec, vec3, bodyAllowance, hitRadius, num5, arcCos, flag, out hitDistance);
				if (!flag2 && FisterWorld.IsHeavy(item))
				{
					Vec3 vec4 = FisterWorld.NearestBodyPoint(item, position);
					if (PunchReaches(position, vec, vec4, 0f, hitRadius, num5, arcCos, flag, out hitDistance))
					{
						flag2 = true;
						vec3 = vec4;
					}
				}
				if (flag2 && FisterWorld.ClearLine(position, vec3, item))
				{
					list.Add(new Hit
					{
						ent = item,
						distance = hitDistance,
						centre = vec3
					});
				}
			}
			list.Sort((Hit a, Hit b) => a.distance.CompareTo(b.distance));
			if (flag && list.Count > 1)
			{
				list.RemoveRange(1, list.Count - 1);
			}
			int num6 = 0;
			foreach (Hit item2 in list)
			{
				float num7 = Mathf.Max(0.2f, 1f - 0.2f * (float)num6);
				float velocity = num * num2 * num4 * num7;
				num6++;
				FisterWorld.PunchContact(item2.ent, strength, critical || flag);
				bool flag3 = false;
				if (e.flag)
				{
					Vec3 vec5 = item2.centre.Sub(position);
					float num8 = vec5.x * vec.x + vec5.y * vec.y;
					float num9 = FMath.Hypot(vec5.x - vec.x * num8, vec5.y - vec.y * num8);
					float num10 = vec5.z - 0.08f;
					if (num8 <= 4.25f && num9 <= 0.95f && num10 >= -0.75f && num10 <= 2.85f)
					{
						flag3 = true;
						if (FisterWorld.IsClearlyAirborne(item2.ent))
						{
							FisterWorld.KillAndLaunch(item2.ent, new Vec3(0f, 0f, 111.111115f));
							FisterAudio.Play("successful-100-punch", FisterPlayer.Feet(nm), 1f);
						}
						else if (kind == 3)
						{
							float num11 = (critical ? 0.45f : 0.3f);
							float num12 = (critical ? 6f : 3f);
							FisterWorld.Launch(item2.ent, new Vec3(vec2.x * num11, vec2.y * num11, Mathf.Max(0f, vec2.z) + num12), 0f, 3f);
						}
						else
						{
							ApplyPunchToTarget(item2.ent, new Vec3(direction.x * 0.3f, direction.y * 0.3f, direction.z), velocity, strength, critical);
						}
					}
				}
				if (!flag3)
				{
					ApplyPunchToTarget(item2.ent, direction, velocity, strength, critical);
				}
			}
			float s = FMath.Clamp01(strength);
			foreach (Hit item3 in list)
			{
				Vec3 vec6 = item3.centre.Sub(position).Normalized();
				if (vec6.Length() < 0.5f)
				{
					vec6 = vec;
				}
				Fx.Instance?.PunchHit(Units.PosToUnity(item3.centre), Units.DirToUnity(vec6), s);
			}
			if (num6 > 0 && strength > 0f && strength < 0.6f)
			{
				Shake(0.05f + 0.08f * Mathf.Min(1f, strength / 0.6f));
			}
			if (num6 > 0 && critical)
			{
				Fx.Instance?.CriticalIcon();
				FisterAudio.Play("criticalhit", FisterPlayer.Feet(nm), 1f);
			}
			float volume = Mathf.Max((0.5f + 0.5f * strength) * 0.6f, Mathf.Min(1f, 0.3f + 0.45f * strength));
			if (num6 > 0)
			{
				FisterAudio.Play("crack", FisterPlayer.Feet(nm), volume);
				if (strength >= 1f)
				{
					sim.GrantInstantPunchCharge();
				}
				return;
			}
			Vector3 val = Units.PosToUnity(position);
			RaycastHit val2 = default(RaycastHit);
			if (kind == 3 || !Physics.Raycast(val, Units.DirToUnity(direction), ref val2, 5.8992076f, LayerMask.op_Implicit(LayerMaskDefaults.Get((LMD)1)), (QueryTriggerInteraction)1))
			{
				return;
			}
			FisterAudio.Play("crack", ((RaycastHit)(ref val2)).point, volume);
			Fx.Instance?.WallPunch(((RaycastHit)(ref val2)).point, strength);
			Breakable componentInParent = ((Component)((RaycastHit)(ref val2)).collider).GetComponentInParent<Breakable>();
			if ((Object)(object)componentInParent != (Object)null && (componentInParent.weak || strength >= 0.6f) && !componentInParent.unbreakable && !componentInParent.precisionOnly && !componentInParent.specialCaseOnly)
			{
				componentInParent.Break();
				return;
			}
			Glass componentInParent2 = ((Component)((RaycastHit)(ref val2)).collider).GetComponentInParent<Glass>();
			if (strength >= 0.6f && (Object)(object)componentInParent2 != (Object)null && !componentInParent2.broken)
			{
				componentInParent2.Shatter();
			}
			else
			{
				sim.ApplyWallPunchRecoil(strength);
			}
		}

		private static bool PunchReaches(Vec3 origin, Vec3 forward, Vec3 centre, float bodyAllowance, float hitRadius, float range, float arcCos, bool criticalDash, out float hitDistance)
		{
			Vec3 vec = centre.Sub(origin);
			float num = vec.Length();
			float num2 = vec.x * forward.x + vec.y * forward.y;
			float num3 = FMath.Hypot(vec.x - forward.x * num2, vec.y - forward.y * num2);
			hitDistance = (criticalDash ? num2 : num);
			if (criticalDash)
			{
				float num4 = 2.2f + (hitRadius - 0.5f) + bodyAllowance;
				if (num2 > 0f && num2 <= range + bodyAllowance && Mathf.Abs(vec.z) <= 2.5f + bodyAllowance)
				{
					return num3 <= num4;
				}
				return false;
			}
			bool num5 = num > 0.001f && num <= range + (hitRadius - 0.5f) + bodyAllowance && num2 / num > arcCos;
			bool flag = num2 >= -0.2f && num2 <= range + bodyAllowance && num3 <= 1.3299999f + bodyAllowance && Mathf.Abs(vec.z) <= 2f + bodyAllowance;
			return num5 || flag;
		}

		private static float BodyHalfHeight(EnemyIdentifier eid)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_000c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0018: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = FisterWorld.CentreUnity(eid);
			return Mathf.Clamp(Mathf.Max(0.5f, val.y - ((Component)eid).transform.position.y) / 1.1798415f, 0.4f, 6f);
		}

		private bool ResolveShockwave(Vec3 centre, float charge)
		{
			float num = 5f + 7f * charge;
			bool result = false;
			foreach (EnemyIdentifier item in FisterWorld.Targets(centre, num + 4f))
			{
				Vec3 vec = FisterWorld.BodyCentre(item).Sub(centre);
				if (Mathf.Abs(vec.z) > 4f + BodyHalfHeight(item))
				{
					continue;
				}
				float num2 = FMath.Hypot(vec.x, vec.y);
				if (!(num2 >= num))
				{
					result = true;
					if (num2 < 0.75f)
					{
						FisterWorld.KillAndLaunch(item, new Vec3(0f, 0f, 10f));
						continue;
					}
					float num3 = 16.25f * (1f - num2 / num);
					Vec3 vec2 = new Vec3(vec.x, vec.y, 0f).Normalized();
					FisterWorld.Launch(item, new Vec3(vec2.x * num3, vec2.y * num3, num3 * 0.18f), 2f, 4f);
				}
			}
			return result;
		}

		private void ResolveDive(NewMovement nm, Event e, Vector3 at)
		{
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_0122: Unknown result type (might be due to invalid IL or missing references)
			float num = e.value * 0.6f;
			bool flag = diveHitAnyone;
			foreach (EnemyIdentifier item in FisterWorld.Targets(e.position, 6f))
			{
				Vec3 vec = FisterWorld.BodyCentre(item);
				if (FMath.Hypot(vec.x - e.position.x, vec.y - e.position.y) <= 2f && Mathf.Abs(vec.z - e.position.z) <= 4f + BodyHalfHeight(item))
				{
					if (item.isBoss || !FisterWorld.CanLaunch(item))
					{
						FisterWorld.StandInImpact(item, new Vec3(0f, 0f, 40f));
					}
					else
					{
						FisterWorld.KillAndLaunch(item, new Vec3(0f, 0f, 12f));
					}
					flag = true;
				}
			}
			flag = ResolveShockwave(e.position, num) || flag;
			Fx.Instance?.DiveLand(at, num);
			FisterAudio.Play("crack", at, 0.8f + 0.4f * num);
			Shake(0.3f + 0.4f * num);
			sim.ResolveDiveImpact(flag);
			if (flag)
			{
				sim.GrantInstantPunchCharge();
			}
		}

		private void ResolveGroundHop(NewMovement nm, Event e)
		{
			foreach (EnemyIdentifier item in FisterWorld.Targets(e.position, 3.5f))
			{
				if (!FisterWorld.IsRagdolled(item))
				{
					float num = FisterWorld.BodyCentre(item).Sub(e.position).Length();
					if (num < 3.5f)
					{
						float z = 12f * (1f - num / 3.5f);
						FisterWorld.Push(item, new Vec3(0f, 0f, z));
					}
				}
			}
		}

		private void ResolveOverchargeExplode(NewMovement nm, Event e)
		{
			//IL_0001: Unknown result type (might be due to invalid IL or missing references)
			//IL_0006: Unknown result type (might be due to invalid IL or missing references)
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0013: 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_0022: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			Vector3 val = FisterPlayer.Feet(nm);
			Fx.Instance?.Explode(val + Vector3.up * 1.1798415f);
			FisterAudio.Play("explode", val, 1f);
			Shake(1f);
			foreach (EnemyIdentifier item in FisterWorld.Targets(e.position, 8f))
			{
				Vec3 vec = FisterWorld.BodyCentre(item).Sub(e.position);
				float num = vec.Length();
				if (!(num >= 8f))
				{
					float num2 = 1f - num / 8f;
					Vec3 vec2 = new Vec3(vec.x, vec.y, 0f).Normalized();
					float num3 = 800f * (0.3f + 0.7f * num2) / 72f;
					FisterWorld.Launch(item, new Vec3(vec2.x * num3, vec2.y * num3, 300f * num2 / 72f), 3f, 0f);
				}
			}
			SelfKill(nm);
		}

		private void ResolveThrow(NewMovement nm, Event e)
		{
			if ((Object)(object)grabbed != (Object)null && !e.flag)
			{
				FisterWorld.SetGrabbed(grabbed, grabbed: false);
				Vec3 vec = e.direction.Mul(e.value * Tuning.Throw.Value);
				FisterWorld.Launch(grabbed, vec, 2.2f, 2f, roomScaled: false, thrown: true);
				ThrowObserved?.Invoke(grabbed, vec);
				grabbed = null;
			}
		}

		private void ResolvePiledriver(NewMovement nm, Event e, Vector3 at)
		{
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_007a: Unknown result type (might be due to invalid IL or missing references)
			if ((Object)(object)grabbed != (Object)null)
			{
				FisterWorld.SetGrabbed(grabbed, grabbed: false);
				FisterWorld.KillAndLaunch(grabbed, new Vec3(0f, 0f, 6f));
			}
			grabbed = null;
			ResolveShockwave(e.position, e.value);
			sim.GrantInstantPunchCharge();
			Fx.Instance?.DiveLand(at, e.value);
			FisterAudio.Play("crack", at, 1.2f);
			Shake(0.3f + 0.4f * e.value);
		}

		internal void ReleaseGrabbed(bool breakSim)
		{
			if ((Object)(object)grabbed != (Object)null)
			{
				FisterWorld.SetGrabbed(grabbed, grabbed: false);
			}
			grabbed = null;
			if (breakSim)
			{
				sim.OnGrabBroken();
			}
		}

		private static float DistanceToSegment(Vec3 pt, Vec3 a, Vec3 b, float halfHeight)
		{
			Vec3 vec = b.Sub(a);
			float num = vec.Dot(vec);
			float s = ((num > 1E-08f) ? FMath.Clamp01(pt.Sub(a).Dot(vec) / num) : 0f);
			Vec3 vec2 = a.Add(vec.Mul(s));
			float num2 = pt.z - vec2.z;
			num2 = ((num2 > 0f) ? Mathf.Max(0f, num2 - halfHeight) : Mathf.Min(0f, num2 + halfHeight));
			return Mathf.Sqrt((pt.x - vec2.x) * (pt.x - vec2.x) + (pt.y - vec2.y) * (pt.y - vec2.y) + num2 * num2);
		}

		internal void TickCombat(NewMovement nm)
		{
			//IL_0d82: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a2b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a30: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a78: Unknown result type (might be due to invalid IL or missing references)
			//IL_0b99: Unknown result type (might be due to invalid IL or missing references)
			//IL_0a97: Unknown result type (might be due to invalid IL or missing references)
			//IL_08f1: Unknown result type (might be due to invalid IL or missing references)
			//IL_0925: Unknown result type (might be due to invalid IL or missing references)
			//IL_0bee: Unknown result type (might be due to invalid IL or missing references)
			//IL_06bb: Unknown result type (might be due to invalid IL or missing references)
			//IL_0773: Unknown result type (might be due to invalid IL or missing references)
			//IL_0799: Unknown result type (might be due to invalid IL or missing references)
			if (sim.IsGrounded())
			{
				landedSinceKick = true;
			}
			if (pendingImpactDamage > 0f && owner.clock - pendingImpactAt >= (double)sim.parryLateWindow)
			{
				float fisterHp = pendingImpactDamage;
				pendingImpactDamage = 0f;
				SelfDamage(nm, fisterHp);
			}
			bool flag = sim.IsGrabbing();
			bool flag2 = sim.IsHoldingGrabbed() || (sim.IsPiledriving() && (Object)(object)grabbed != (Object)null);
			bool flag3 = sim.IsRocketPunchDiving();
			bool flag4 = sim.IsAirKickDashing();
			bool flag5 = sim.IsOverchargeDashing();
			bool flag6 = sim.IsRocketPunchAscending() && sim.RocketAscentTimer() >= 0.05f;
			bool flag7 = sim.Charge() >= 1f && sim.IsAirborneForFullChargePunch();
			if (!flag7)
			{
				sim.SetAirKickLockCandidate(onTarget: false);
			}
			if (!flag && !flag2 && !flag3 && !flag4 && !flag5 && !flag6 && !flag7)
			{
				return;
			}
			Vec3 position = owner.lastEnv.position;
			Vec3 vec = sim.Velocity();
			Vec3 vec2 = FMath.YawForward(Yaw);
			Vec3 vec3 = position.Add(vec.Mul(1f / 60f * owner.TravelScale()));
			float radiusFister = vec.Length() * (1f / 60f) + 8f;
			if (flag2)
			{
				if ((Object)(object)grabbed == (Object)null || (grabbed.dead && !FisterWorld.Victims.ContainsKey(grabbed)))
				{
					ReleaseGrabbed(breakSim: true);
				}
				else
				{
					Vec3 goalSim = new Vec3(position.x + vec2.x * 0.8f, position.y + vec2.y * 0.8f, position.z + 1.5f);
					FisterWorld.Hold(grabbed, goalSim, vec);
				}
			}
			if (flag && (Object)(object)grabbed == (Object)null)
			{
				EnemyIdentifier val = null;
				float num = 1E+09f;
				foreach (EnemyIdentifier item in FisterWorld.Targets(position, 8f))
				{
					Vec3 vec4 = FisterWorld.BodyCentre(item);
					Vec3 vec5 = vec4.Sub(position);
					int num2;
					float num3;
					if (!sim.GrabStartedAirborne())
					{
						num2 = (FisterWorld.IsRagdolled(item) ? 1 : 0);
						if (num2 == 0)
						{
							num3 = 4f;
							goto IL_0293;
						}
					}
					else
					{
						num2 = 1;
					}
					num3 = 4.75f;
					goto IL_0293;
					IL_0293:
					float num4 = num3;
					float a = ((num2 != 0) ? ((float)Math.PI * 23f / 36f) : ((float)Math.PI / 2f)) / 2f;
					float num5 = ((num2 != 0) ? 6f : 3f) + Mathf.Max(0f, BodyHalfHeight(item) - 1.3f);
					float num6 = ((num2 != 0) ? vec5.Length() : FMath.Hypot(vec5.x, vec5.y));
					if (!(num6 > num4) && !(num6 < 0.001f) && !(Mathf.Abs(vec5.z) > num5))
					{
						float num7 = FMath.Hypot(vec5.x, vec5.y);
						if (!(((num7 > 0.001f) ? ((vec5.x * vec2.x + vec5.y * vec2.y) / num7) : 1f) < FMath.Cos(a)) && !(num6 >= num) && FisterWorld.ClearLine(position, vec4))
						{
							val = item;
							num = num6;
						}
					}
				}
				if ((Object)(object)val != (Object)null && FisterWorld.Grab(val))
				{
					sim.OnGrabCaught();
					grabbed = val;
				}
				else if ((Object)(object)val != (Object)null && !grabStandIn.Contains(val))
				{
					grabStandIn.Add(val);
					FisterWorld.StandInImpact(val, vec2.Mul(15f));
				}
			}
			if (flag3)
			{
				foreach (EnemyIdentifier item2 in FisterWorld.Targets(position, radiusFister, sweepBuffer))
				{
					if (DistanceToSegment(FisterWorld.BodyCentre(item2), position, vec3, 0.65f + BodyHalfHeight(item2)) <= 1f)
					{
						if (!FisterWorld.CanLaunch(item2) || item2.isBoss)
						{
							FisterWorld.StandInImpact(item2, vec);
						}
						else
						{
							FisterWorld.KillAndLaunch(item2, new Vec3(vec.x * 0.3f, vec.y * 0.3f, 8f));
						}
						diveHitAnyone = true;
					}
				}
			}
			if (flag4)
			{
				lastAirKickScale = sim.AirKickImpactScale();
				EnemyIdentifier val2 = airKickHomingTarget;
				if ((Object)(object)val2 != (Object)null)
				{
					bool valid = (Object)(object)val2 != (Object)null && ((Component)val2).gameObject.activeInHierarchy && (!val2.dead || FisterWorld.Victims.ContainsKey(val2)) && FisterWorld.ClearLine(position, FisterWorld.BodyCentre(val2));
					sim.SetAirKickHomingTarget(valid, FisterWorld.BodyCentre(val2));
				}
				EnemyIdentifier val3 = null;
				float num8 = 1E+09f;
				foreach (EnemyIdentifier item3 in FisterWorld.Targets(position, radiusFister, sweepBuffer))
				{
					Vec3 vec6 = FisterWorld.BodyCentre(item3);
					if (DistanceToSegment(vec6, position, vec3, 0.65f + Mathf.Max(0f, BodyHalfHeight(item3) - 1.3f)) > 2.65f)
					{
						bool flag8 = false;
						if (FisterWorld.IsHeavy(item3))
						{
							Vec3[] array = new Vec3[3]
							{
								position,
								vec3,
								position.Add(vec3).Mul(0.5f)
							};
							foreach (Vec3 fromSim in array)
							{
								if (DistanceToSegment(FisterWorld.NearestBodyPoint(item3, fromSim), position, vec3, 0.65f) <= 2.65f)
								{
									flag8 = true;
									break;
								}
							}
						}
						if (!flag8)
						{
							continue;
						}
					}
					float num9 = vec6.Sub(position).Length();
					if (num9 < num8)
					{
						val3 = item3;
						num8 = num9;
					}
				}
				if ((Object)(object)val3 == (Object)null)
				{
					for (int j = 0; j < ParryReturn.Live.Count; j++)
					{
						Projectile val4 = ParryReturn.Live[j];
						if (!((Object)(object)val4 == (Object)null) && !val4.friendly && !val4.parried && !val4.unparryable && !val4.undeflectable && !val4.playerBullet && !(DistanceToSegment(Units.PosToSim(((Component)val4).transform.position), position, vec3, 0.65f) > 2.65f))
						{
							Vec3 vec7 = sim.AirKickDirection();
							Vec3 vec8 = sim.Velocity();
							Defense.ParryProjectile(val4);
							sim.StopAirKickDash();
							Vec3 vec9 = new Vec3(vec7.x, vec7.y, 0f).Normalized();
							sim.SetVelocity(new Vec3((0f - vec9.x) * 4f, (0f - vec9.y) * 4f, vec8.z * 0.15f));
							sim.MarkParrySuccessful(0);
							FisterAudio.Play("successful-100-punch", FisterPlayer.Feet(nm), 1f);
							Fx.Instance?.AirKickHit(((Component)val4).transform.position, 1f);
							FisterPlugin.Log.LogInfo((object)("air kick redirected " + ((Object)val4).name));
							return;
						}
					}
				}
				if ((Object)(object)val3 != (Object)null)
				{
					float num10 = sim.AirKickImpactScale();
					if (FisterWorld.IsHardKnocked(val3))
					{
						num10 = Mathf.Max(num10, 1f);
					}
					Vec3 vec10 = sim.AirKickDirection();
					float time = Time.time;
					bool flag9 = airKickChain > 0 && (!landedSinceKick || time - lastKickHitTime <= FisterConfig.AirKickChainWindow.Value);
					airKickChain = ((!flag9) ? 1 : (airKickChain + 1));
					lastKickHitTime = time;
					landedSinceKick = false;
					FisterWorld.AirKickContact(val3, num10, airKickChain);
					float num11 = 28800f * num10 / 72f * FisterConfig.AirKickLaunch.Value;
					float num12 = 40f * Tuning.EnemyLaunch.Value;
					Vec3 velSim = vec10.Normalized().Mul(Mathf.Max(num11, num12 * 1.1f));
					FisterWorld.Launch(val3, velSim, 2f, 4f, roomScaled: false);
					sim.OnAirKickHit();
					FisterAudio.Play("criticalhit", FisterPlayer.Feet(nm), 1f);
					float num13 = FMath.Clamp(num10, 0.5f, 1.5f);
					Fx.Instance?.AirKickHit(FisterWorld.CentreUnity(val3), num13);
					FisterController.Instance.anim.TriggerImpactFrame(0.04f + 0.025f * num10);
					Shake(0.3f * num13);
					airKickHomingTarget = null;
				}
			}
			if (flag5)
			{
				Vec3 vec11 = sim.OverchargeDashDirection();
				float num14 = sim.OverchargeDashLaunchChargeTime();
				float t = Sim.RailingTuningProgress(num14);
				foreach (EnemyIdentifier item4 in FisterWorld.Targets(position, radiusFister, sweepBuffer))
				{
					if (!sim.IsOverchargeDashing() || railingHit.Contains(item4))
					{
						continue;
					}
					Vec3 vec12 = FisterWorld.BodyCentre(item4);
					if (DistanceToSegment(vec12, position, vec3, 0.85f + Mathf.Max(0f, BodyHalfHeight(item4) - 1.3f)) > 2.4f || !FisterWorld.ClearLine(position, vec12))
					{
						continue;
					}
					railingHit.Add(item4);
					Vector3 at = Units.PosToUnity(vec12);
					if (num14 >= 10f && !item4.isBoss)
					{
						FisterWorld.KillAndLaunch(item4, new Vec3(vec11.x * 230f, vec11.y * 230f, 70f));
						FisterAudio.Play("disintegrated", FisterPlayer.Feet(nm), 2f);
						Fx.Instance?.RailingHit(at, 2.35f);
						sim.OnRailingHit(flatten: true);
						sim.GrantInstantPunchCharge();
						continue;
					}
					if (sim.IsOverchargeDashMegaBoosted())
					{
						Vec3 velSim2 = vec11.Mul(400f);
						FisterWorld.Launch(item4, velSim2, 2f, 4f);
					}
					else
					{
						float num15 = FMath.Lerp(75f, 230f, t);
						float z = FMath.Lerp(20f, 70f, t);
						float hardKnockdown = ((num14 > 3f) ? FMath.Lerp(2f, 4.5f, t) : 0f);
						FisterWorld.Damage(item4, FMath.Lerp(40f, 170f, t));
						FisterWorld.Launch(item4, new Vec3(vec11.x * num15, vec11.y * num15, z), hardKnockdown, FMath.Lerp(1f, 2.4f, t) * 2f);
					}
					FisterAudio.Play("criticalhit", FisterPlayer.Feet(nm), 1f);
					float st = (sim.IsOverchargeDashMegaBoosted() ? 2.35f : FMath.Clamp(sim.OverchargeDashSpeed() / 88f, 1f, 2.25f));
					Fx.Instance?.RailingHit(at, st);
					sim.OnRailingHit(flatten: false);
					sim.GrantInstantPunchCharge();
				}
			}
			if (flag6)
			{
				float num16 = sim.RocketAscentCharge();
				foreach (EnemyIdentifier item5 in FisterWorld.Targets(position, radiusFister, sweepBuffer))
				{
					if (!ascentHit.Contains(item5) && !(DistanceToSegment(FisterWorld.BodyCentre(item5), position, vec3, 0.65f + Mathf.Max(0f, BodyHalfHeight(item5) - 1.3f)) > 1.5f))
					{
						ascentHit.Add(item5);
						if (FisterWorld.IsClearlyAirborne(item5) && !item5.isBoss)
						{
							FisterWorld.KillAndLaunch(item5, new Vec3(0f, 0f, 111.111115f));
							continue;
						}
						float num17 = vec.Length();
						float s = 1500f * (0.5f + num16) * (0.5f + 0.5f * num17 / 35f) / 72f;
						Vec3 vec13 = vec.Normalized();
						vec13.z = Mathf.Max(vec13.z, 0.5f);
						vec13 = vec13.Normalized().Mul(s);
						FisterWorld.Damage(item5, 15f + 20f * num16);
						FisterWorld.Launch(item5, vec13, 2f, 3.75f);
						FisterAudio.Play("crack", FisterWorld.CentreUnity(item5), 1f);
					}
				}
			}
			if (!flag7)
			{
				return;
			}
			Vec3 vec14 = FMath.AimDirection(Yaw, owner.pitch);
			Vec3 vec15 = new Vec3(position.x, position.y, position.z + 0.9f);
			EnemyIdentifier val5 = null;
			float num18 = 192f;
			foreach (EnemyIdentifier item6 in FisterWorld.Targets(position, 192f))
			{
				Vec3 vec16 = FisterWorld.BodyCentre(item6);
				float num19 = vec16.Sub(vec15).Dot(vec14);
				if (!(num19 <= 0f) && !(num19 >= num18))
				{
					Vec3 o = vec15.Add(vec14.Mul(num19));
					float num20 = 1.2f + (FisterWorld.IsRagdolled(item6) ? 0.6f : 0f) + Mathf.Max(0f, BodyHalfHeight(item6) - 1.3f);
					if (vec16.Sub(o).Length() <= num20 && FisterWorld.ClearLine(vec15, vec16))
					{
						val5 = item6;
						num18 = num19;
					}
				}
			}
			sim.SetAirKickLockCandidate((Object)(object)val5 != (Object)null && (Object)(object)val5 == (Object)(object)airKickLockTarget);
			airKickLockTarget = val5;
		}

		internal static void SelfDamage(NewMovement nm, float fisterHp)
		{
			int num = Mathf.RoundToInt(fisterHp * 100f / 200f);
			if (num <= 0 || nm.dead)
			{
				return;
			}
			SelfDamageGuard.Active = true;
			try
			{
				nm.GetHurt(num, false, 1f, false, false, 0.35f, false);
			}
			finally
			{
				SelfDamageGuard.Active = false;
			}
		}

		internal static void SelfKill(NewMovement nm)
		{
			if (!FisterConfig.LethalSelfMoves.Value)
			{
				FisterController.Instance.player.sim.EnterRagdoll(3f, hardKnockdown: true);
				return;
			}
			SelfDamageGuard.Active = true;
			try
			{
				nm.GetHurt(1000, false, 1f, false, false, 0.35f, true);
			}
			finally
			{
				SelfDamageGuard.Active = false;
			}
		}
	}
	internal static class SelfDamageGuard
	{
		internal static bool Active;
	}
	internal static class FisterConfig
	{
		internal static ConfigEntry<bool> StartEnabled;

		internal static ConfigEntry<Key> ToggleKey;

		internal static ConfigEntry<float> Volume;

		internal static ConfigEntry<bool> ScreenShake;

		internal static ConfigEntry<bool> LethalSelfMoves;

		internal static ConfigEntry<float> DamageScale;

		internal static ConfigEntry<bool> WhiffKnockdowns;

		internal static ConfigEntry<bool> InstantChargeOnKill;

		internal static ConfigEntry<bool> RagdollOnHit;

		internal static ConfigEntry<float> PunchDamage;

		internal static ConfigEntry<float> JabDamageFraction;

		internal static ConfigEntry<float> HeavyPunchFraction;

		internal static ConfigEntry<float> BossPunchFraction;

		internal static ConfigEntry<float> CriticalPunchMultiplier;

		internal static ConfigEntry<bool> HitStop;

		internal static ConfigEntry<float> SurfaceParryBuffer;

		internal static ConfigEntry<float> SurfaceParryLate;

		internal static ConfigEntry<float> AirKickDamage;

		internal static ConfigEntry<float> AirKickHeavyFraction;

		internal static ConfigEntry<float> AirKickBossFraction;

		internal static ConfigEntry<float> AirKickLaunch;

		internal static ConfigEntry<float> AirKickChainBonus;

		internal static ConfigEntry<float> AirKickChainWindow;

		internal static ConfigEntry<float> BossDamageScale;

		internal static ConfigEntry<float> KillProtectHealth;

		internal static ConfigEntry<int> AirKickChainMax;

		internal static ConfigEntry<bool> LiveRagdollTint;

		internal static ConfigEntry<int> ConfigVersion;

		internal static void Bind(ConfigFile cfg)
		{
			//IL_0060: Unknown result type (might be due to invalid IL or missing references)
			//IL_006a: Expected O, but got Unknown
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_00d8: Expected O, but got Unknown
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0110: Expected O, but got Unknown
			//IL_013e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0148: Expected O, but got Unknown
			//IL_0176: Unknown result type (might be due to invalid IL or missing references)
			//IL_0180: Expected O, but got Unknown
			//IL_01ae: Unknown result type (might be due to invalid IL or missing references)
			//IL_01b8: Expected O, but got Unknown
			//IL_01e6: Unknown result type (might be due to invalid IL or missing references)
			//IL_01f0: Expected O, but got Unknown
			//IL_021e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0228: Expected O, but got Unknown
			//IL_0256: Unknown result type (might be due to invalid IL or missing references)
			//IL_0260: Expected O, but got Unknown
			//IL_028e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0298: Expected O, but got Unknown
			//IL_02c6: Unknown result type (might be due to invalid IL or missing references)
			//IL_02d0: Expected O, but got Unknown
			//IL_02fe: Unknown result type (might be due to invalid IL or missing references)
			//IL_0308: Expected O, but got Unknown
			//IL_0336: Unknown result type (might be due to invalid IL or missing references)
			//IL_0340: Expected O, but got Unknown
			//IL_0363: Unknown result type (might be due to invalid IL or missing references)
			//IL_036d: Expected O, but got Unknown
			//IL_039b: Unknown result type (might be due to invalid IL or missing references)
			//IL_03a5: Expected O, but got Unknown
			//IL_03d3: Unknown result type (might be due to invalid IL or missing references)
			//IL_03dd: Expected O, but got Unknown
			//IL_0492: Unknown result type (might be due to invalid IL or missing references)
			//IL_049c: Expected O, but got Unknown
			//IL_04ca: Unknown result type (might be due to invalid IL or missing references)
			//IL_04d4: Expected O, but got Unknown
			StartEnabled = cfg.Bind<bool>("General", "StartAsJohnFister", true, "Play as John Fister when a level starts. Toggle in game with the toggle key.");
			ToggleKey = cfg.Bind<Key>("General", "ToggleKey", (Key)100, "Switches between John Fister and V1 (restores V1's controls, health, weapons and camera).");
			Volume = cfg.Bind<float>("Audio", "Volume", 1f, new ConfigDescription("John Fister's sounds, on top of the game's SFX volume.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 2f), Array.Empty<object>()));
			ScreenShake = cfg.Bind<bool>("Camera", "ScreenShake", true, "Fister's own hit shake. The game's screen shake setting also scales it.");
			LethalSelfMoves = cfg.Bind<bool>("Combat", "LethalSelfMoves", true, "As in Fister: the 10 s overcharge self-destruct, a mega-boosted railing into a wall and a parried dive or railing kill John. Off: a long hard knockdown instead.");
			DamageScale = cfg.Bind<float>("Combat", "DamageScale", 0.15f, new ConfigDescription("Collision damage: enemy health taken per Fister damage point when a launched body hits a wall, the floor or another enemy (Fister's damage is for a 200 HP brawler; 0.15 maps a full-charge punch into a wall, ~140 Fister damage, to 21).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.01f, 1f), Array.Empty<object>()));
			PunchDamage = cfg.Bind<float>("Combat", "PunchDamage", 3.5f, new ConfigDescription("Direct damage of a full-charge punch on contact (ULTRAKILL health; V1's feedbacker deals 1; a Filth has 0.5, a Stray 1.5, a Schism 5). Added to the collision damage. Lighter punches deal less (see JabDamageFraction).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			JabDamageFraction = cfg.Bind<float>("Combat", "JabDamageFraction", 0.08f, new ConfigDescription("Share of the full punch's contact damage a tap jab deals; charged punches climb from here to 1 with the square of the charge.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			HeavyPunchFraction = cfg.Bind<float>("Combat", "HeavyPunchFraction", 0.1f, new ConfigDescription("Heavy enemies (Swordsmachine, Cerberus, Hideous Mass, Mindflayer, Gutterman... anything too big to launch): a full punch takes at least this share of their full health.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			BossPunchFraction = cfg.Bind<float>("Combat", "BossPunchFraction", 0f, new ConfigDescription("Bosses: a full punch takes at least this share of their full health.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			CriticalPunchMultiplier = cfg.Bind<float>("Combat", "CriticalPunchMultiplier", 3f, new ConfigDescription("Contact damage multiplier of the critical fist (the 7 s charge).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 10f), Array.Empty<object>()));
			AirKickDamage = cfg.Bind<float>("Combat", "AirKickDamage", 7f, new ConfigDescription("Contact damage of an air kick that connects (2x a full punch; a fast kick deals up to 1.5x this; chained kicks add AirKickChainBonus each).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 100f), Array.Empty<object>()));
			AirKickHeavyFraction = cfg.Bind<float>("Combat", "AirKickHeavyFraction", 0.15f, new ConfigDescription("Heavy enemies: least share of their health an air kick takes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			AirKickBossFraction = cfg.Bind<float>("Combat", "AirKickBossFraction", 0f, new ConfigDescription("Bosses: least share of their health an air kick takes.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
			AirKickLaunch = cfg.Bind<float>("Combat", "AirKickLaunch", 0.6f, new ConfigDescription("Air kick launch speed (x Fister's; not reduced by EnemyLaunch, and never slower than a full punch's launch).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 3f), Array.Empty<object>()));
			AirKickChainBonus = cfg.Bind<float>("Combat", "AirKickChainBonus", 1f, new ConfigDescription("Air-kick chain: each consecutive air-kick hit adds this much damage. Kicks chain while John has not landed since the last kick hit, or within AirKickChainWindow seconds of it.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 20f), Array.Empty<object>()));
			AirKickChainWindow = cfg.Bind<float>("Combat", "AirKickChainWindow", 1.5f, new ConfigDescription("Seconds after an air-kick hit within which the next kick hit still counts as a chain (even after landing).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 10f), Array.Empty<object>()));
			AirKickChainMax = cfg.Bind<int>("Combat", "AirKickChainMax", 5, new ConfigDescription("Most chain bonuses that stack (the 6th and later kicks in a chain deal base + 5 x bonus).", (AcceptableValueBase)(object)new AcceptableValueRange<int>(0, 50), Array.Empty<object>()));
			BossDamageScale = cfg.Bind<float>("Combat", "BossDamageScale", 1f, new ConfigDescription("Multiplier on all of John's damage to bosses.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 2f), Array.Empty<object>()));
			KillProtectHealth = cfg.Bind<float>("Combat", "KillProtectHealth", 10f, new ConfigDescription("Enemies with more health than this, and every boss, never take an instant kill (finisher, airborne uppercut, dive, piledriver, throw kill): they take a full punch's damage instead, and no single other hit deals more than two full punches.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1000f), Array.Empty<object>()));
			LiveRagdollTint = cfg.Bind<bool>("Combat", "LiveRagdollTint", true, "Enemies John launches or ragdolls glow orange while they are alive (as Fister tints a hard-knocked-down player); the tint goes when they get up or die.");
			HitStop = cfg.Bind<bool>("Combat", "HitStop", true, "ULTRAKILL's own hit-stop, camera kick and punch impact on every connecting punch.");
			WhiffKnockdowns = cfg.Bind<bool>("Combat", "WhiffKnockdowns", false, "Fister's whiff punishments: an air kick that ends on a wall or the ground, or a dive that hits nobody, knocks John down. Off by default.");
			InstantChargeOnKill = cfg.Bind<bool>("Combat", "InstantChargeOnKill", true, "Any kill credited to John gives the instant full charge (a port rule; in Fister only landed full punches, parries, air-kick and railing hits, connecting dives and throws do).");
			RagdollOnHit = cfg.Bind<bool>("Combat", "RagdollOnHit", true, "Enemies John launches, grabs or throws go limp (their own ragdoll, limbs flopping) and get back up when they settle. Enemies without a jointed rig fly as a rigid body either way.");
			SurfaceParryBuffer = cfg.Bind<float>("Parry timing", "SurfaceParryBuffer", 0.3f, new ConfigDescription("How early (seconds) a parry may be pressed before hitting a wall or the floor while ragdolled or in the air and still rebound. Fister: 0.16 (in flight), 0.25 (ragdolled).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.05f, 1f), Array.Empty<object>()));
			SurfaceParryLate = cfg.Bind<float>("Parry timing", "SurfaceParryLate", 0.1f, new ConfigDescription("How late (seconds) a parry may come after hitting a wall or the floor and still rebound off it (a port addition; Fister: 0).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 0.5f), Array.Empty<object>()));
			Tuning.BindAll(cfg);
			ConfigVersion = cfg.Bind<int>("General", "ConfigVersion", 0, "Settings format version (managed by the mod; do not edit).");
			Migrate(cfg);
		}

		private static void MoveToNewDefault(ConfigEntry<float> e, float oldDefault)
		{
			if (Mathf.Approximately(e.Value, oldDefault))
			{
				e.Value = (float)((ConfigEntryBase)e).DefaultValue;
			}
		}

		private static void Migrate(ConfigFile cfg)
		{
			if (ConfigVersion.Value < 104)
			{
				if (Mathf.Approximately(DamageScale.Value, 0.1f))
				{
					DamageScale.Value = (float)((ConfigEntryBase)DamageScale).DefaultValue;
				}
				if (Mathf.Approximately(Tuning.EnemyLaunch.Value, 0.5f))
				{
					Tuning.EnemyLaunch.Value = (float)((ConfigEntryBase)Tuning.EnemyLaunch).DefaultValue;
				}
				MoveToNewDefault(Tuning.WallParry, 0.6f);
				MoveToNewDefault(Tuning.RailingBounce, 0.4f);
				MoveToNewDefault(Tuning.GroundParry, 0.8f);
				ConfigVersion.Value = 104;
			}
			if (ConfigVersion.Value < 105)
			{
				MoveToNewDefault(Tuning.WallParry, 0.5f);
				MoveToNewDefault(Tuning.GroundParry, 0.6f);
				ConfigVersion.Value = 105;
			}
			if (ConfigVersion.Value < 106)
			{
				DropOrphans(cfg, (ConfigDefinition d) => d.Section == "Parry brake" || (d.Section == "Room scale" && (d.Key == "ParryMaxSpeed" || d.Key == "ParryBrakeTime" || d.Key == "ParryDriftSpeed")));
				ConfigVersion.Value = 106;
			}
			if (ConfigVersion.Value < 110)
			{
				MoveToNewDefault(PunchDamage, 5f);
				MoveToNewDefault(HeavyPunchFraction, 0.3f);
				MoveToNewDefault(BossPunchFraction, 0.1f);
				MoveToNewDefault(AirKickDamage, 7.5f);
				MoveToNewDefault(AirKickHeavyFraction, 0.45f);
				MoveToNewDefault(AirKickBossFraction, 0.15f);
				ConfigVersion.Value = 110;
			}
		}

		private static void DropOrphans(ConfigFile cfg, Func<ConfigDefinition, bool> which)
		{
			try
			{
				Dictionary<ConfigDefinition, string> value = Traverse.Create((object)cfg).Property("OrphanedEntries", (object[])null).GetValue<Dictionary<ConfigDefinition, string>>();
				if (value == null)
				{
					return;
				}
				foreach (ConfigDefinition item in new List<ConfigDefinition>(value.Keys))
				{
					if (which(item))
					{
						value.Remove(item);
					}
				}
				cfg.Save();
			}
			catch (Exception ex)
			{
				FisterPlugin.Log.LogWarning((object)("settings cleanup: " + ex.Message));
			}
		}
	}
	internal sealed class FisterController : MonoBehaviour
	{
		internal static FisterController Instance;

		internal readonly FisterPlayer player = new FisterPlayer();

		internal readonly PoseAnimator anim = new PoseAnimator();

		internal BodyRenderer body;

		internal Pose lastPose;

		internal bool wanted;

		private float animTime;

		private float shake;

		private float airTime;

		private int lastHp = -1;

		private bool grabWasDown;

		private bool itemPress;

		private Vector3 lastHand;

		private string lastGatedScene;

		private string failure;

		private float failureUntil;

		internal NewMovement Player => MonoSingleton<NewMovement>.Instance;

		private void Awake()
		{
			Instance = this;
			wanted = FisterConfig.StartEnabled.Value;
			Fx.Instance = ((Component)this).gameObject.AddComponent<Fx>();
			((Component)this).gameObject.AddComponent<CooldownRing>();
		}

		private void OnEnable()
		{
			SceneManager.sceneLoaded += OnSceneLoaded;
		}

		private void OnDisable()
		{
			SceneManager.sceneLoaded -= OnSceneLoaded;
		}

		private void OnSceneLoaded(Scene scene, LoadSceneMode mode)
		{
			FisterPlugin.Log.LogInfo((object)("scene loaded: " + ((Scene)(ref scene)).name));
			player.Reset();
			anim.Reset();
			FisterInput.Invalidate();
			FisterAudio.StopLoops();
			FisterWorld.ReleaseAll();
			ParryReturn.Clear();
			lastHp = -1;
		}

		private void FixedUpdate()
		{
			FisterCamera.RestoreTransform();
			FisterWorld.FixedTick();
			ParryReturn.FixedTick();
		}

		internal void AddShake(float intensity)
		{
			shake = Mathf.Max(shake, intensity);
		}

		private void Update()
		{
			//IL_0014: Unknown result type (might be due to invalid IL or missing references)
			FisterCamera.RestoreTransform();
			Keyboard current = Keyboard.current;
			if (current != null && ((ButtonControl)current[FisterConfig.ToggleKey.Value]).wasPressedThisFrame)
			{
				wanted = !wanted;
			}
			NewMovement val = Player;
			bool flag = (Object)(object)val != (Object)null && ((Behaviour)val).isActiveAndEnabled && (Object)(object)val.rb != (Object)null;
			bool active = FisterMode.Active;
			bool flag2 = flag && val.levelOver;
			string currentScene = SceneHelper.CurrentScene;
			int num;
			switch (currentScene)
			{
			default:
				num = ((currentScene == "Credits") ? 1 : 0);
				break;
			case "Intro":
			case "Tutorial":
			case "Main Menu":
			case "Bootstrap":
				num = 1;
				break;
			}
			bool flag3 = (byte)num != 0;
			if (flag3 && currentScene != lastGatedScene)
			{
				lastGatedScene = currentScene;
				FisterPlugin.Log.LogInfo((object)("John Fister stays off in " + currentScene + " (V1's own sequence)"));
			}
			flag2 = flag2 || flag3;
			if (flag2 && FisterMode.Active)
			{
				FisterPlugin.Log.LogInfo((object)"level over: handing the end sequence to V1 until the next level");
			}
			try
			{
				FisterMode.Sync(flag ? val : null, wanted && !flag2);
			}
			catch (Exception ex)
			{
				Fail("switching modes failed", ex);
				try
				{
					FisterMode.Sync(flag ? val : null, wants: false);
				}
				catch (Exception ex2)
				{
					FisterPlugin.Log.LogError((object)("restoring V1 failed: " + ex2));
				}
			}
			if (FisterMode.Active && !active)
			{
				Assets.PreloadAll();
				player.Reset();
				anim.Reset();
			}
			if (!FisterMode.Active)
			{
				if (body != null)
				{
					body.Visible = false;
				}
				FisterCamera.HasFollow = false;
				FisterAudio.StopLoops();
			}
			else
			{
				SampleInput(val);
				UpdateFeedback(val);
			}
		}

		private void SampleInput(NewMovement nm)
		{
			//IL_0118: Unknown result type (might be due to invalid IL or missing references)
			//IL_011d: 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_006f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0079: Unknown result type (might be due to invalid IL or missing references)
			//IL_007e: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_0097: Unknown result type (might be due to invalid IL or missing references)
			//IL_009c: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a6: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ab: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ae: Unknown result type (might be due to invalid IL or missing references)
			CameraController cc = nm.cc;
			OptionsManager instance = MonoSingleton<OptionsManager>.Instance;
			FisterInput.State state = (((Object)(object)instance != (Object)null && (instance.paused || instance.frozen)) ? default(FisterInput.State) : FisterInput.Read());
			if (state.grab && !grabWasDown && !state.fist && (Object)(object)cc != (Object)null)
			{
				Vector3 head = ((Component)nm).transform.position + ((Component)nm).transform.up * 1.4f;
				Vector3 aim = Quaternion.Euler(0f - cc.rotationX, cc.rotationY, 0f) * Vector3.forward;
				itemPress = FisterItems.TryUse(head, aim, lastHand);
			}
			grabWasDown = state.grab;
			if (itemPress)
			{
				if (state.grab)
				{
					state.grab = false;
				}
				else
				{
					itemPress = false;
				}
			}
			player.buttons.Update(state.fist, state.parry, state.grab, Time.timeAsDouble);
			player.move = state.move;
			if ((Object)(object)cc != (Object)null)
			{
				player.yaw = Units.SimYaw(cc.rotationY);
				player.pitch = cc.rotationX * ((float)Math.PI / 180f);
			}
		}

		private void UpdateFeedback(NewMovement nm)
		{
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			Sim sim = player.sim;
			if (!sim.IsGrounded())
			{
				airTime += Time.deltaTime;
			}
			else
			{
				if (airTime >= 0.28f)
				{
					FisterAudio.Play("footstep", FisterPlayer.Feet(nm), 0.5f);
				}
				airTime = 0f;
			}
			if (lastHp >= 0 && lastHp - nm.hp >= 1)
			{
				anim.TriggerDamageFlash();
				FisterAudio.Play("damage-taken", FisterPlayer.Feet(nm), 0.8f);
			}
			lastHp = nm.hp;
			FisterSounds.UpdateLoops(sim);
		}

		internal void OnCameraLateUpdate(CameraController cc)
		{
			NewMovement val = Player;
			if (!FisterMode.Active || (Object)(object)val == (Object)null)
			{
				FisterCamera.HasFollow = false;
				return;
			}
			try
			{
				if (body == null)
				{
					body = new BodyRenderer("JohnFisterBody");
				}
				body.Visible = true;
				PlaceBodyAndCamera(cc, val);
			}
			catch (Exception ex)
			{
				Fail("drawing John failed", ex);
				if (body != null)
				{
					body.Visible = false;
				}
				FisterCamera.HasFollow = false;
				FisterCamera.Apply(cc);
			}
		}

		internal void Fail(string what, Exception ex)
		{
			FisterPlugin.Log.LogError((object)("John Fister: " + what + "; switching back to V1 (F7 tries again). " + ex));
			wanted = false;
			failure = "John Fister: " + what + " (" + ex.GetType().Name + ": " + ex.Message + "). Playing as V1; see BepInEx/LogOutput.log.";
			failureUntil = Time.unscaledTime + 12f;
		}

		private void OnGUI()
		{
			//IL_0015: 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)
			if (failure != null && Time.unscaledTime < failureUntil)
			{
				GUI.color = Color.white;
				GUI.Label(new Rect(20f, 20f, (float)Screen.width - 40f, 60f), failure);
			}
		}

		private void PlaceBodyAndCamera(CameraController cc, NewMovement nm)
		{
			//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_0027: Unknown result type (might be due to invalid IL or missing references)
			//IL_002c: Unknown result type (might be due to invalid IL or missing references)
			//IL_0031: Unknown result type (might be due to invalid IL or missing references)
			//IL_00a2: Unknown result type (might be due to invalid IL or missing references)
			//IL_00ce: Unknown result type (might be due to invalid IL or missing references)
			//IL_0146: Unknown result type (might be due to invalid IL or missing references)
			//IL_0147: Unknown result type (might be due to invalid IL or missing references)
			//IL_017f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0192: Unknown result type (might be due to invalid IL or missing references)
			//IL_0178: Unknown result type (might be due to invalid IL or missing references)
			//IL_0197: Unknown result type (might be due to invalid IL or missing references)
			//IL_021d: Unknown result