Decompiled source of AirFlip v1.0.0

plugins/AirFlip/AirFlip.dll

Decompiled 9 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Reflection.Emit;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using PluginConfig.API;
using PluginConfig.API.Fields;
using UnityEngine;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("AirFlip")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.0.0")]
[assembly: AssemblyInformationalVersion("1.0.0")]
[assembly: AssemblyProduct("Air Flip")]
[assembly: AssemblyTitle("AirFlip")]
[assembly: AssemblyVersion("1.0.0.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
namespace AirFlip
{
	internal static class AirFlipConfig
	{
		private static BoolField enabled;

		private static IntField maxFlips;

		private static IntField staminaPerFlip;

		private static IntField landingTolerance;

		private static IntField failedLandingDamage;

		private static FloatField slowDuration;

		private static BoolField explosions;

		private static BoolField styleBonuses;

		internal static bool Enabled
		{
			get
			{
				if (enabled != null)
				{
					return enabled.value;
				}
				return true;
			}
		}

		internal static int MaxFlips
		{
			get
			{
				if (maxFlips == null)
				{
					return 0;
				}
				return maxFlips.value;
			}
		}

		internal static int StaminaPerFlip
		{
			get
			{
				if (staminaPerFlip == null)
				{
					return 1;
				}
				return staminaPerFlip.value;
			}
		}

		internal static int LandingTolerance
		{
			get
			{
				if (landingTolerance == null)
				{
					return 85;
				}
				return landingTolerance.value;
			}
		}

		internal static int FailedLandingDamage
		{
			get
			{
				if (failedLandingDamage == null)
				{
					return 10;
				}
				return failedLandingDamage.value;
			}
		}

		internal static float SlowDuration
		{
			get
			{
				if (slowDuration == null)
				{
					return 2f;
				}
				return slowDuration.value;
			}
		}

		internal static bool ExplosionsEnabled
		{
			get
			{
				if (explosions != null)
				{
					return explosions.value;
				}
				return true;
			}
		}

		internal static bool StyleBonusesEnabled
		{
			get
			{
				if (styleBonuses != null)
				{
					return styleBonuses.value;
				}
				return true;
			}
		}

		internal static void Initialize()
		{
			//IL_0020: Unknown result type (might be due to invalid IL or missing references)
			//IL_002a: Expected O, but got Unknown
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_003f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0056: Expected O, but got Unknown
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_005b: Expected O, but got Unknown
			//IL_005b: Unknown result type (might be due to invalid IL or missing references)
			//IL_006e: Expected O, but got Unknown
			//IL_0069: Unknown result type (might be due to invalid IL or missing references)
			//IL_0073: Expected O, but got Unknown
			//IL_0088: Expected O, but got Unknown
			//IL_0083: Unknown result type (might be due to invalid IL or missing references)
			//IL_008d: Expected O, but got Unknown
			//IL_009d: 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_00ba: Expected O, but got Unknown
			//IL_00b5: Unknown result type (might be due to invalid IL or missing references)
			//IL_00bf: Expected O, but got Unknown
			//IL_00dd: Expected O, but got Unknown
			//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
			//IL_00e2: Expected O, but got Unknown
			//IL_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_0107: Expected O, but got Unknown
			//IL_0102: Unknown result type (might be due to invalid IL or missing references)
			//IL_010c: Expected O, but got Unknown
			//IL_011c: Expected O, but got Unknown
			//IL_0117: Unknown result type (might be due to invalid IL or missing references)
			//IL_0121: Expected O, but got Unknown
			PluginConfigurator obj = PluginConfigurator.Create("Air Flip", "dev.ultrakill.airflip");
			enabled = new BoolField(obj.rootPanel, "Enable Air Flip", "enabled", true);
			ConfigPanel val = new ConfigPanel(obj.rootPanel, "Gameplay", "gameplay");
			maxFlips = new IntField(val, "Maximum flips per jump (0 = unlimited)", "max_flips", 0, 0, int.MaxValue);
			staminaPerFlip = new IntField(val, "Stamina cost per flip", "stamina_per_flip", 1, 0, 3);
			landingTolerance = new IntField(val, "Landing angle tolerance", "landing_tolerance", 85, 45, 90);
			ConfigPanel val2 = new ConfigPanel(obj.rootPanel, "Failed flip", "failed_flip");
			failedLandingDamage = new IntField(val2, "Damage", "damage", 10, 0, int.MaxValue);
			slowDuration = new FloatField(val2, "Slow duration (seconds)", "slow_duration", 2f, 0f, 5f);
			ConfigPanel val3 = new ConfigPanel(obj.rootPanel, "Rewards", "rewards");
			explosions = new BoolField(val3, "Enable explosions", "explosions", true);
			styleBonuses = new BoolField(val3, "Enable style bonuses", "style_bonuses", true);
		}
	}
	[BepInPlugin("dev.ultrakill.airflip", "Air Flip", "1.0.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class Plugin : BaseUnityPlugin
	{
		internal static ManualLogSource Log;

		private void Awake()
		{
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			Log = ((BaseUnityPlugin)this).Logger;
			AirFlipConfig.Initialize();
			new Harmony("dev.ultrakill.airflip").PatchAll(typeof(Plugin).Assembly);
			((BaseUnityPlugin)this).Logger.LogInfo((object)"Air Flip loaded");
		}
	}
	internal static class FlipState
	{
		internal static bool Airborne;

		internal static bool FlipStarted;

		internal static float FreePitch;

		internal static float StartPitch;

		internal static float PitchNet;

		internal static float MaxPitchNet;

		internal static float MinPitchNet;

		internal static bool Recovering;

		internal static float RecoveryPitch;

		internal static float SlowUntil;

		internal static float PreviousWalkSpeed;

		internal static void Reset()
		{
			Airborne = false;
			FlipStarted = false;
			PitchNet = 0f;
			MaxPitchNet = 0f;
			MinPitchNet = 0f;
		}

		internal static int CompletedTurns()
		{
			double num = Math.Max(0.0, Math.Floor(((double)MaxPitchNet + 60.0) / 360.0));
			double num2 = Math.Max(0.0, Math.Floor((0.0 - (double)MinPitchNet + 60.0) / 360.0));
			double num3 = num + num2;
			int num4 = ((num3 >= 2147483647.0) ? int.MaxValue : ((int)num3));
			int maxFlips = AirFlipConfig.MaxFlips;
			if (maxFlips <= 0)
			{
				return num4;
			}
			return Math.Min(num4, maxFlips);
		}

		internal static void Land(NewMovement player)
		{
			//IL_0093: Unknown result type (might be due to invalid IL or missing references)
			CameraController val = (((Object)(object)player != (Object)null) ? player.cc : null);
			Transform val2 = (((Object)(object)val != (Object)null && (Object)(object)val.cam != (Object)null) ? ((Component)val.cam).transform : null);
			if ((Object)(object)val2 == (Object)null)
			{
				Reset();
				return;
			}
			val.rotationX = Mathf.DeltaAngle(0f, val.rotationX);
			if (!FlipStarted)
			{
				BeginRecoveryIfOutsideLimit(val);
				Reset();
				return;
			}
			bool flag = Mathf.Abs(Mathf.DeltaAngle(StartPitch, val.rotationX)) <= (float)AirFlipConfig.LandingTolerance;
			bool flag2 = val2.up.y <= -0.5f;
			bool num = (Object)(object)player.gc != (Object)null && player.gc.heavyFall;
			int num2 = CompletedTurns();
			if (num2 > 0)
			{
				player.boostCharge = Mathf.Max(0f, player.boostCharge - 100f * (float)AirFlipConfig.StaminaPerFlip * (float)num2);
			}
			bool flag3 = num && flag2;
			bool flag4 = flag && num2 >= 1;
			int num3 = ((flag4 || flag3) ? Mathf.Max(1, num2) : 0);
			if (flag3)
			{
				SpawnFlipBlast(player, 1.5f + 0.12f * (float)(num3 - 1), ScaledBlastDamage(150, 25, num3));
				AwardFlipStyle(player, num3, backSlam: true);
			}
			else if (flag4)
			{
				SpawnFlipBlast(player, 1.1f + 0.12f * (float)(num3 - 1), ScaledBlastDamage(80, 25, num3));
				AwardFlipStyle(player, num3, backSlam: false);
			}
			else if (!flag)
			{
				ApplyNonLethalFlipPenalty(player);
				if (AirFlipConfig.SlowDuration > 0f)
				{
					SlowUntil = Time.time + AirFlipConfig.SlowDuration;
				}
			}
			BeginRecoveryIfOutsideLimit(val);
			Reset();
		}

		private static void BeginRecoveryIfOutsideLimit(CameraController camera)
		{
			if (Mathf.Abs(camera.rotationX) <= 85f)
			{
				Recovering = false;
				return;
			}
			RecoveryPitch = Mathf.Clamp(StartPitch, -85f, 85f);
			Recovering = Mathf.Abs(RecoveryPitch - camera.rotationX) > 1f;
		}

		private static void AwardFlipStyle(NewMovement player, int flips, bool backSlam)
		{
			if (flips < 1 || !AirFlipConfig.StyleBonusesEnabled)
			{
				return;
			}
			string text = (backSlam ? "airflip.poolofblood" : "airflip.flip");
			int num = (int)Math.Min(2147483647L, 100 + (long)(flips - 1) * 75L);
			try
			{
				StyleHUD instance = MonoSingleton<StyleHUD>.Instance;
				if (instance != null)
				{
					instance.AddPoints(num, text, ((Component)player).gameObject, (EnemyIdentifier)null, flips, "", "");
				}
			}
			catch (Exception ex)
			{
				Plugin.Log.LogWarning((object)("Could not award Pool of Blood style: " + ex.Message));
			}
		}

		private static int ScaledBlastDamage(int baseDamage, int perExtraTurn, int flips)
		{
			long val = baseDamage + (long)Math.Max(0, flips - 1) * (long)perExtraTurn;
			return (int)Math.Min(2147483647L, val);
		}

		private static void ApplyNonLethalFlipPenalty(NewMovement player)
		{
			if (!((Object)(object)player == (Object)null) && !player.dead && AirFlipConfig.FailedLandingDamage > 0)
			{
				int num = Mathf.Min(AirFlipConfig.FailedLandingDamage, Mathf.Max(0, player.hp - 1));
				if (num > 0)
				{
					player.GetHurt(num, true, 1f, false, false, 0.35f, true);
				}
				else
				{
					player.antiHp = Mathf.Min(99f, player.antiHp + 3.5f);
				}
			}
		}

		private static void SpawnFlipBlast(NewMovement player, float size, int damage)
		{
			//IL_004c: Unknown result type (might be due to invalid IL or missing references)
			//IL_003a: Unknown result type (might be due to invalid IL or missing references)
			//IL_0051: Unknown result type (might be due to invalid IL or missing references)
			//IL_0058: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Unknown result type (might be due to invalid IL or missing references)
			//IL_00c3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0116: Unknown result type (might be due to invalid IL or missing references)
			//IL_011b: Unknown result type (might be due to invalid IL or missing references)
			//IL_013a: 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)
			if (!AirFlipConfig.ExplosionsEnabled)
			{
				return;
			}
			DefaultReferenceManager instance = MonoSingleton<DefaultReferenceManager>.Instance;
			if (!((Object)(object)instance == (Object)null) && !((Object)(object)instance.explosion == (Object)null))
			{
				Vector3 val = (((Object)(object)player.gc != (Object)null) ? ((Component)player.gc).transform.position : ((Component)player).transform.position);
				GameObject val2 = Object.Instantiate<GameObject>(instance.explosion, val, Quaternion.identity);
				Explosion[] componentsInChildren = val2.GetComponentsInChildren<Explosion>(true);
				foreach (Explosion obj in componentsInChildren)
				{
					obj.maxSize *= size;
					obj.speed *= size;
					obj.damage = Mathf.Max(obj.damage * 2, damage);
					obj.enemyDamageMultiplier = Mathf.Max(obj.enemyDamageMultiplier, 1.5f);
					obj.enemy = false;
					obj.canHit = (AffectedSubjects)2;
				}
				Light[] componentsInChildren2 = val2.GetComponentsInChildren<Light>(true);
				foreach (Light obj2 in componentsInChildren2)
				{
					obj2.range *= size;
				}
				ParticleSystem[] componentsInChildren3 = val2.GetComponentsInChildren<ParticleSystem>(true);
				for (int i = 0; i < componentsInChildren3.Length; i++)
				{
					MainModule main = componentsInChildren3[i].main;
					((MainModule)(ref main)).scalingMode = (ParticleSystemScalingMode)0;
				}
				Transform transform = val2.transform;
				transform.localScale *= size;
				CameraController cc = player.cc;
				if (cc != null)
				{
					cc.CameraShake(size);
				}
			}
		}
	}
	[HarmonyPatch(typeof(CameraController), "LateUpdate")]
	[HarmonyPriority(0)]
	internal static class FreeAirPitch
	{
		private static void Prefix(CameraController __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			NewMovement nm = __instance.nm;
			if (!CanControl(__instance, nm))
			{
				StopAirFlip(__instance);
			}
			else if (!AirFlipConfig.Enabled)
			{
				if (FlipState.Airborne || FlipState.Recovering)
				{
					NormalizeAndClampPitch(__instance);
				}
				StopAirFlip(__instance);
			}
			else if (nm.gc.onGround)
			{
				if (FlipState.Airborne)
				{
					FlipState.Land(nm);
				}
			}
			else if (!FlipState.Airborne)
			{
				FlipState.Airborne = true;
				FlipState.FreePitch = __instance.rotationX;
				FlipState.StartPitch = __instance.rotationX;
				FlipState.PitchNet = 0f;
				FlipState.MaxPitchNet = 0f;
				FlipState.MinPitchNet = 0f;
				FlipState.FlipStarted = false;
			}
		}

		private static void Postfix(CameraController __instance)
		{
			if ((Object)(object)__instance == (Object)null)
			{
				return;
			}
			NewMovement nm = __instance.nm;
			if (!CanControl(__instance, nm))
			{
				StopAirFlip(__instance);
			}
			else if (!AirFlipConfig.Enabled)
			{
				StopAirFlip(__instance);
			}
			else if (nm.gc.onGround)
			{
				if (FlipState.Recovering)
				{
					float rotationX = __instance.rotationX;
					RecoverPitch(__instance);
					if (Mathf.Abs(rotationX - __instance.rotationX) > 0.01f)
					{
						__instance.ApplyRotations(false);
					}
				}
			}
			else if (FlipState.Airborne)
			{
				float num = __instance.rotationX - FlipState.FreePitch;
				FlipState.FreePitch = __instance.rotationX;
				FlipState.PitchNet += num;
				FlipState.MaxPitchNet = Mathf.Max(FlipState.MaxPitchNet, FlipState.PitchNet);
				FlipState.MinPitchNet = Mathf.Min(FlipState.MinPitchNet, FlipState.PitchNet);
				if (FlipState.MaxPitchNet >= 150f || FlipState.MinPitchNet <= -150f)
				{
					FlipState.FlipStarted = true;
				}
			}
		}

		private static void NormalizeAndClampPitch(CameraController camera)
		{
			if (!((Object)(object)camera == (Object)null))
			{
				float rotationX = camera.rotationX;
				camera.rotationX = Mathf.Clamp(Mathf.DeltaAngle(0f, rotationX), -90f, 90f);
				if (Mathf.Abs(rotationX - camera.rotationX) > 0.01f)
				{
					camera.ApplyRotations(false);
				}
			}
		}

		private static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
		{
			List<CodeInstruction> list = new List<CodeInstruction>(instructions);
			MethodInfo methodInfo = AccessTools.Method(typeof(Mathf), "Abs", new Type[1] { typeof(float) }, (Type[])null);
			MethodInfo operand = AccessTools.Method(typeof(FreeAirPitch), "ClampPitchForVanilla", (Type[])null, (Type[])null);
			int num = 0;
			for (int i = 0; i + 1 < list.Count; i++)
			{
				if (CodeInstructionExtensions.Calls(list[i], methodInfo) && list[i + 1].opcode == OpCodes.Ldc_R4 && list[i + 1].operand is float num2 && Mathf.Approximately(num2, 90f))
				{
					list[i].operand = operand;
					num++;
				}
			}
			if (num != 1)
			{
				Plugin.Log.LogWarning((object)("Camera pitch clamp patch found " + num + " targets (expected one)."));
			}
			return list;
		}

		private static float ClampPitchForVanilla(float angle)
		{
			if (!AirFlipConfig.Enabled || !FlipState.Airborne)
			{
				return Mathf.Abs(angle);
			}
			return 0f;
		}

		private static bool CanControl(CameraController camera, NewMovement player)
		{
			if ((Object)(object)camera == (Object)null || (Object)(object)player == (Object)null || (Object)(object)player.gc == (Object)null || player.dead || player.levelOver || !player.activated || !camera.activated)
			{
				return false;
			}
			GameStateManager instance = GameStateManager.Instance;
			if (!((Object)(object)instance == (Object)null))
			{
				return !instance.CameraLocked;
			}
			return true;
		}

		private static void StopAirFlip(CameraController camera)
		{
			FlipState.Recovering = false;
			FlipState.Reset();
		}

		private static void RecoverPitch(CameraController camera)
		{
			if (FlipState.Recovering && !((Object)(object)camera == (Object)null))
			{
				camera.rotationX = Mathf.MoveTowards(camera.rotationX, FlipState.RecoveryPitch, 240f * Time.deltaTime);
				if (Mathf.Abs(FlipState.RecoveryPitch - camera.rotationX) <= 1f)
				{
					camera.rotationX = FlipState.RecoveryPitch;
					FlipState.Recovering = false;
				}
			}
		}
	}
	[HarmonyPatch(typeof(NewMovement), "LandingImpact")]
	internal static class LandingFlip
	{
		private static void Prefix(NewMovement __instance)
		{
			FlipState.Land(__instance);
		}
	}
	[HarmonyPatch(typeof(NewMovement), "Update")]
	internal static class LandingSlow
	{
		private static void Prefix(NewMovement __instance, out float __state)
		{
			__state = __instance.walkSpeed;
			if (FlipState.PreviousWalkSpeed > 0f)
			{
				__instance.walkSpeed = FlipState.PreviousWalkSpeed;
			}
		}

		private static void Postfix(NewMovement __instance, float __state)
		{
			FlipState.PreviousWalkSpeed = __instance.walkSpeed;
			if (Time.time < FlipState.SlowUntil)
			{
				__instance.walkSpeed *= 0.5f;
			}
		}
	}
	[HarmonyPatch(typeof(StyleHUD), "Start")]
	internal static class RegisterFlipStyle
	{
		private static void Postfix(StyleHUD __instance)
		{
			__instance.RegisterStyleItem("airflip.poolofblood", "<color=#B00020>POOL OF BLOOD</color>");
			__instance.RegisterStyleItem("airflip.flip", "<color=#FF6B35>FLIP</color>");
		}
	}
}