Decompiled source of SprintImprovements v1.1.0

SprintImprovements.dll

Decompiled 9 hours ago
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Bootstrap;
using BepInEx.Configuration;
using BepInEx.Logging;
using EntityStates;
using EntityStates.Bandit2.Weapon;
using EntityStates.Captain.Weapon;
using EntityStates.Drifter;
using EntityStates.DroneTech.Weapon;
using EntityStates.Engi.EngiMissilePainter;
using EntityStates.Huntress.HuntressWeapon;
using EntityStates.Mage.Weapon;
using EntityStates.Railgunner.Scope;
using EntityStates.Toolbot;
using EntityStates.VoidSurvivor.Weapon;
using HG.Reflection;
using HarmonyLib;
using IL.RoR2;
using Microsoft.CodeAnalysis;
using Mono.Cecil;
using Mono.Cecil.Cil;
using MonoMod.Cil;
using MonoMod.RuntimeDetour;
using Rewired;
using RiskOfOptions;
using RiskOfOptions.OptionConfigs;
using RiskOfOptions.Options;
using RoR2;
using RoR2.Skills;
using UnityEngine;
using UnityEngine.Networking;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: OptIn]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("SprintImprovements")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.1.0.0")]
[assembly: AssemblyInformationalVersion("1.1.0")]
[assembly: AssemblyProduct("SprintImprovements")]
[assembly: AssemblyTitle("SprintImprovements")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.1.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 SprintImprovements
{
	public enum SprintKeyMode
	{
		Toggle,
		Hold,
		Always
	}
	internal static class PluginConfig
	{
		private const string Section = "General";

		internal static ConfigEntry<bool> Enabled { get; private set; }

		internal static ConfigEntry<SprintKeyMode> SprintMode { get; private set; }

		internal static ConfigEntry<bool> BlockForcedSprint { get; private set; }

		internal static void Init(ConfigFile config)
		{
			Enabled = config.Bind<bool>("General", "Enabled", true, "Off gives you the normal game's sprint.");
			SprintMode = config.Bind<SprintKeyMode>("General", "SprintMode", SprintKeyMode.Toggle, "How the sprint key works. Toggle: press to sprint, press again to stop; stopping, strafing, skills and stuns only pause sprint. Hold: sprint while you hold the key. Always: sprint whenever you can and hold the key to walk.");
			BlockForcedSprint = config.Bind<bool>("General", "BlockForcedSprint", true, "Skills no longer turn sprint on when you don't want to sprint (e.g. Mercenary's dashes, Commando's Tactical Dive, Huntress's Blink, MUL-T's Transport Mode, Acrid's leaps). When you do want to sprint, they work as in the normal game: in Toggle mode your toggle is on (also while sprint is paused) or you turn it on during the skill; in Hold mode you hold the key; in Always mode you don't. Off: they turn sprint on, and in Toggle mode that turns your toggle on, as in the normal game.");
			Enabled.SettingChanged += delegate
			{
				SprintGuard.OnModeChanged();
			};
			SprintMode.SettingChanged += delegate
			{
				SprintGuard.OnModeChanged();
			};
		}
	}
	internal static class RiskOfOptionsCompat
	{
		[Serializable]
		[CompilerGenerated]
		private sealed class <>c
		{
			public static readonly <>c <>9 = new <>c();

			public static IsDisabledDelegate <>9__4_0;

			public static IsDisabledDelegate <>9__4_1;

			internal bool <Init>b__4_0()
			{
				return !PluginConfig.Enabled.Value;
			}

			internal bool <Init>b__4_1()
			{
				return !PluginConfig.Enabled.Value;
			}
		}

		public const string Guid = "com.rune580.riskofoptions";

		private const string IconResource = "SprintImprovements.icon.png";

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

		[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
		internal static void Init()
		{
			//IL_0019: Unknown result type (might be due to invalid IL or missing references)
			//IL_0023: Expected O, but got Unknown
			//IL_0028: Unknown result type (might be due to invalid IL or missing references)
			//IL_002d: Unknown result type (might be due to invalid IL or missing references)
			//IL_0057: Expected O, but got Unknown
			//IL_0052: Unknown result type (might be due to invalid IL or missing references)
			//IL_005c: Expected O, but got Unknown
			//IL_0061: Unknown result type (might be due to invalid IL or missing references)
			//IL_0066: Unknown result type (might be due to invalid IL or missing references)
			//IL_0042: Unknown result type (might be due to invalid IL or missing references)
			//IL_0047: Unknown result type (might be due to invalid IL or missing references)
			//IL_004d: Expected O, but got Unknown
			//IL_0090: Expected O, but got Unknown
			//IL_008b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0095: Expected O, but got Unknown
			//IL_007b: Unknown result type (might be due to invalid IL or missing references)
			//IL_0080: Unknown result type (might be due to invalid IL or missing references)
			//IL_0086: Expected O, but got Unknown
			ModSettingsManager.SetModDescription("Sprint changes only when you want it to. Skills like Mercenary's dash no longer force it on; stopping, strafing, skills and stuns only pause it; and you can pick toggle, hold or always-on sprint. Only affects your own character.");
			ModSettingsManager.SetModIcon(LoadIcon());
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(PluginConfig.Enabled));
			ConfigEntry<SprintKeyMode> sprintMode = PluginConfig.SprintMode;
			ChoiceConfig val = new ChoiceConfig();
			object obj = <>c.<>9__4_0;
			if (obj == null)
			{
				IsDisabledDelegate val2 = () => !PluginConfig.Enabled.Value;
				<>c.<>9__4_0 = val2;
				obj = (object)val2;
			}
			((BaseOptionConfig)val).checkIfDisabled = (IsDisabledDelegate)obj;
			ModSettingsManager.AddOption((BaseOption)new ChoiceOption((ConfigEntryBase)(object)sprintMode, val));
			ConfigEntry<bool> blockForcedSprint = PluginConfig.BlockForcedSprint;
			CheckBoxConfig val3 = new CheckBoxConfig();
			object obj2 = <>c.<>9__4_1;
			if (obj2 == null)
			{
				IsDisabledDelegate val4 = () => !PluginConfig.Enabled.Value;
				<>c.<>9__4_1 = val4;
				obj2 = (object)val4;
			}
			((BaseOptionConfig)val3).checkIfDisabled = (IsDisabledDelegate)obj2;
			ModSettingsManager.AddOption((BaseOption)new CheckBoxOption(blockForcedSprint, val3));
		}

		private static Sprite LoadIcon()
		{
			//IL_0046: Unknown result type (might be due to invalid IL or missing references)
			//IL_004c: Expected O, but got Unknown
			//IL_0074: 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)
			byte[] array;
			using (Stream stream = typeof(RiskOfOptionsCompat).Assembly.GetManifestResourceStream("SprintImprovements.icon.png"))
			{
				using MemoryStream memoryStream = new MemoryStream();
				stream.CopyTo(memoryStream);
				array = memoryStream.ToArray();
			}
			Texture2D val = new Texture2D(2, 2);
			ImageConversion.LoadImage(val, array);
			((Texture)val).wrapMode = (TextureWrapMode)1;
			return Sprite.Create(val, new Rect(0f, 0f, (float)((Texture)val).width, (float)((Texture)val).height), new Vector2(0.5f, 0.5f));
		}
	}
	internal static class ServerSprintSync
	{
		internal static void OnForcedSprintBlocked(PlayerSprintState state)
		{
			state.blockedSinceLastPoll = true;
		}

		internal static void Update(CharacterBody body, PlayerSprintState state)
		{
			if (state.blockedSinceLastPoll)
			{
				state.blockedSinceLastPoll = false;
				state.serverMaySprint = true;
			}
			else if (state.serverMaySprint)
			{
				state.serverMaySprint = false;
				if (!NetworkServer.active && NetworkClient.active && !body.isSprinting)
				{
					body.CallCmdUpdateSprint(false);
				}
			}
		}
	}
	internal static class SprintGuard
	{
		private const float ResumeDelay = 0.15f;

		private static readonly ConditionalWeakTable<PlayerCharacterMasterController, PlayerSprintState> players = new ConditionalWeakTable<PlayerCharacterMasterController, PlayerSprintState>();

		private static int modeVersion;

		internal static void OnModeChanged()
		{
			modeVersion++;
		}

		internal static bool AllowSprintWrite(CharacterBody body, bool value)
		{
			if (!value || body.isSprinting || !PluginConfig.Enabled.Value || !PluginConfig.BlockForcedSprint.Value || !body.hasEffectiveAuthority || body.isRemoteOp)
			{
				return true;
			}
			CharacterMaster master = body.master;
			PlayerCharacterMasterController val = (Object.op_Implicit((Object)(object)master) ? master.playerCharacterMasterController : null);
			if (!Object.op_Implicit((Object)(object)val) || !players.TryGetValue(val, out var value2) || value2.wantsSprint)
			{
				return true;
			}
			InputBankTest inputBank = body.inputBank;
			if (Object.op_Implicit((Object)(object)inputBank) && inputBank.sprint.down)
			{
				return true;
			}
			ServerSprintSync.OnForcedSprintBlocked(value2);
			return false;
		}

		internal static void OnPoll(PlayerCharacterMasterController pcmc, bool sprinting)
		{
			CharacterBody body = pcmc.body;
			if (!PluginConfig.Enabled.Value || body.isRemoteOp)
			{
				return;
			}
			PlayerSprintState state = GetState(pcmc, body, sprinting);
			ServerSprintSync.Update(body, state);
			float fixedTime = Time.fixedTime;
			bool flag = SprintPause.IsHeldOff(body, state);
			if (flag)
			{
				state.lastHeldOffTime = fixedTime;
			}
			SprintKeyMode value = PluginConfig.SprintMode.Value;
			bool flag2 = false;
			switch (value)
			{
			case SprintKeyMode.Hold:
				flag2 = TakePress(pcmc);
				state.wantsSprint = IsSprintKeyHeld(pcmc);
				break;
			case SprintKeyMode.Always:
				TakePress(pcmc);
				state.wantsSprint = !IsSprintKeyHeld(pcmc);
				break;
			case SprintKeyMode.Toggle:
				if (pcmc.sprintInputPressReceived)
				{
					OnTogglePress(pcmc, body, state, sprinting, flag);
					return;
				}
				break;
			}
			if (state.wantsSprint)
			{
				if (!sprinting && (flag2 || (!flag && fixedTime - state.lastHeldOffTime >= 0.15f && SprintPause.CanStartMoving(body))))
				{
					Press(pcmc);
				}
			}
			else if (sprinting && !flag)
			{
				if (value == SprintKeyMode.Toggle && !PluginConfig.BlockForcedSprint.Value)
				{
					state.wantsSprint = true;
				}
				else
				{
					Press(pcmc);
				}
			}
		}

		private static void OnTogglePress(PlayerCharacterMasterController pcmc, CharacterBody body, PlayerSprintState state, bool sprinting, bool heldOff)
		{
			if (sprinting && (state.wantsSprint || !heldOff))
			{
				state.wantsSprint = false;
			}
			else if (!state.wantsSprint)
			{
				state.wantsSprint = true;
				if (sprinting)
				{
					TakePress(pcmc);
				}
			}
			else if (!SprintPause.CanStartMoving(body) || SprintPause.IsBusy(state.bodyMachine))
			{
				state.wantsSprint = false;
				TakePress(pcmc);
			}
		}

		private static PlayerSprintState GetState(PlayerCharacterMasterController pcmc, CharacterBody body, bool sprinting)
		{
			PlayerSprintState orCreateValue = players.GetOrCreateValue(pcmc);
			if (orCreateValue.modeVersion != modeVersion)
			{
				orCreateValue.modeVersion = modeVersion;
				orCreateValue.wantsSprint = sprinting;
			}
			if ((Object)(object)orCreateValue.body != (Object)(object)body)
			{
				orCreateValue.SetBody(body);
			}
			return orCreateValue;
		}

		private static void Press(PlayerCharacterMasterController pcmc)
		{
			pcmc.sprintInputPressReceived = true;
		}

		private static bool TakePress(PlayerCharacterMasterController pcmc)
		{
			bool sprintInputPressReceived = pcmc.sprintInputPressReceived;
			pcmc.sprintInputPressReceived = false;
			return sprintInputPressReceived;
		}

		private static bool IsSprintKeyHeld(PlayerCharacterMasterController pcmc)
		{
			NetworkUser networkUser = pcmc.networkUser;
			Player val = (Object.op_Implicit((Object)(object)networkUser) ? networkUser.inputPlayer : null);
			if (val != null)
			{
				return val.GetButton(18);
			}
			return false;
		}
	}
	internal static class SprintHooks
	{
		[CompilerGenerated]
		private static class <>O
		{
			public static Manipulator <0>__PlayerCharacterMasterController_PollButtonInput;

			public static Action<Action<CharacterBody, bool>, CharacterBody, bool> <1>__CharacterBody_set_isSprinting;

			public static Action<PlayerCharacterMasterController, bool> <2>__OnPoll;
		}

		private static Hook isSprintingSetterHook;

		internal static void Init()
		{
			//IL_0010: Unknown result type (might be due to invalid IL or missing references)
			//IL_0015: Unknown result type (might be due to invalid IL or missing references)
			//IL_001b: Expected O, but got Unknown
			//IL_004f: Unknown result type (might be due to invalid IL or missing references)
			//IL_0059: Expected O, but got Unknown
			object obj = <>O.<0>__PlayerCharacterMasterController_PollButtonInput;
			if (obj == null)
			{
				Manipulator val = PlayerCharacterMasterController_PollButtonInput;
				<>O.<0>__PlayerCharacterMasterController_PollButtonInput = val;
				obj = (object)val;
			}
			PlayerCharacterMasterController.PollButtonInput += (Manipulator)obj;
			isSprintingSetterHook = new Hook((MethodBase)AccessTools.PropertySetter(typeof(CharacterBody), "isSprinting"), (Delegate)new Action<Action<CharacterBody, bool>, CharacterBody, bool>(CharacterBody_set_isSprinting));
		}

		private static void CharacterBody_set_isSprinting(Action<CharacterBody, bool> orig, CharacterBody self, bool value)
		{
			if (SprintGuard.AllowSprintWrite(self, value))
			{
				orig(self, value);
			}
		}

		private static void PlayerCharacterMasterController_PollButtonInput(ILContext il)
		{
			//IL_0007: Unknown result type (might be due to invalid IL or missing references)
			//IL_000d: Expected O, but got Unknown
			//IL_0075: Unknown result type (might be due to invalid IL or missing references)
			//IL_0081: Unknown result type (might be due to invalid IL or missing references)
			ILCursor val = new ILCursor(il);
			int sprintingLocal = -1;
			if (!val.TryGotoNext((MoveType)2, new Func<Instruction, bool>[2]
			{
				(Instruction x) => ILPatternMatchingExt.MatchCallOrCallvirt(x, (MethodBase)AccessTools.PropertyGetter(typeof(CharacterBody), "isSprinting")),
				(Instruction x) => ILPatternMatchingExt.MatchStloc(x, ref sprintingLocal)
			}))
			{
				SprintImprovementsPlugin.Log.LogError((object)(((MemberReference)il.Method).Name + " IL hook failed: the game changed. SprintImprovements does nothing until it is updated."));
				return;
			}
			val.Emit(OpCodes.Ldarg_0);
			val.Emit(OpCodes.Ldloc, sprintingLocal);
			val.EmitDelegate<Action<PlayerCharacterMasterController, bool>>((Action<PlayerCharacterMasterController, bool>)SprintGuard.OnPoll);
		}
	}
	[BepInPlugin("revor.SprintImprovements", "SprintImprovements", "1.1.0")]
	[BepInDependency(/*Could not decode attribute arguments.*/)]
	public sealed class SprintImprovementsPlugin : BaseUnityPlugin
	{
		public const string PluginGUID = "revor.SprintImprovements";

		public const string PluginName = "SprintImprovements";

		public const string PluginVersion = "1.1.0";

		internal static ManualLogSource Log { get; private set; }

		private void Awake()
		{
			Log = ((BaseUnityPlugin)this).Logger;
			PluginConfig.Init(((BaseUnityPlugin)this).Config);
			SprintHooks.Init();
			if (RiskOfOptionsCompat.IsInstalled)
			{
				RiskOfOptionsCompat.Init();
			}
		}
	}
	internal static class SprintPause
	{
		private const float ResumeMinMove = 0.6f;

		private const float ResumeMaxAimAngle = 50f;

		private static readonly float resumeMinAimMoveDot = Mathf.Cos(0.87266463f);

		internal static bool IsHeldOff(CharacterBody body, PlayerSprintState state)
		{
			//IL_00e0: Unknown result type (might be due to invalid IL or missing references)
			//IL_00f3: Unknown result type (might be due to invalid IL or missing references)
			//IL_0106: Unknown result type (might be due to invalid IL or missing references)
			//IL_0119: Unknown result type (might be due to invalid IL or missing references)
			bool flag = IsBusy(state.bodyMachine);
			EntityStateMachine[] machines = state.machines;
			foreach (EntityStateMachine val in machines)
			{
				EntityState val2 = (Object.op_Implicit((Object)(object)val) ? val.state : null);
				if (val2 != null && !val.IsInMainState() && SprintStates.IsSprintSensitive(((object)val2).GetType()) && !state.heldMachines.Contains(val))
				{
					state.heldMachines.Add(val);
				}
			}
			for (int num = state.heldMachines.Count - 1; num >= 0; num--)
			{
				if (IsBusy(state.heldMachines[num]))
				{
					flag = true;
				}
				else
				{
					state.heldMachines.RemoveAt(num);
				}
			}
			SkillLocator skillLocator = body.skillLocator;
			InputBankTest inputBank = body.inputBank;
			if (!flag)
			{
				if (Object.op_Implicit((Object)(object)skillLocator) && Object.op_Implicit((Object)(object)inputBank))
				{
					if (!IsSkillHeld(skillLocator.primary, inputBank.skill1) && !IsSkillHeld(skillLocator.secondary, inputBank.skill2) && !IsSkillHeld(skillLocator.utility, inputBank.skill3))
					{
						return IsSkillHeld(skillLocator.special, inputBank.skill4);
					}
					return true;
				}
				return false;
			}
			return true;
		}

		internal static bool CanStartMoving(CharacterBody body)
		{
			//IL_0012: Unknown result type (might be due to invalid IL or missing references)
			//IL_0017: Unknown result type (might be due to invalid IL or missing references)
			//IL_0029: Unknown result type (might be due to invalid IL or missing references)
			//IL_0030: Unknown result type (might be due to invalid IL or missing references)
			//IL_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_0056: 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)
			InputBankTest inputBank = body.inputBank;
			if (!Object.op_Implicit((Object)(object)inputBank))
			{
				return false;
			}
			Vector3 moveVector = inputBank.moveVector;
			if (((Vector3)(ref moveVector)).magnitude <= 0.6f)
			{
				return false;
			}
			if ((body.bodyFlags & 0x20) != 0)
			{
				return true;
			}
			Vector3 aimDirection = inputBank.aimDirection;
			aimDirection.y = 0f;
			moveVector.y = 0f;
			return Vector3.Dot(((Vector3)(ref aimDirection)).normalized, ((Vector3)(ref moveVector)).normalized) >= resumeMinAimMoveDot;
		}

		internal static bool IsBusy(EntityStateMachine machine)
		{
			if (Object.op_Implicit((Object)(object)machine))
			{
				if (machine.IsInMainState())
				{
					return machine.HasPendingState();
				}
				return true;
			}
			return false;
		}

		private static bool IsSkillHeld(GenericSkill slot, ButtonState button)
		{
			//IL_002a: 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)
			SkillDef val = (Object.op_Implicit((Object)(object)slot) ? slot.skillDef : null);
			if (Object.op_Implicit((Object)(object)val) && (val.cancelSprintingOnActivation || val.canceledFromSprinting) && button.down && (!val.mustKeyPress || !button.hasPressBeenClaimed))
			{
				return slot.IsReady();
			}
			return false;
		}
	}
	internal sealed class PlayerSprintState
	{
		internal bool wantsSprint;

		internal int modeVersion = -1;

		internal CharacterBody body;

		internal EntityStateMachine bodyMachine;

		internal EntityStateMachine[] machines;

		internal readonly List<EntityStateMachine> heldMachines = new List<EntityStateMachine>();

		internal float lastHeldOffTime = float.NegativeInfinity;

		internal bool blockedSinceLastPoll;

		internal bool serverMaySprint;

		internal void SetBody(CharacterBody newBody)
		{
			body = newBody;
			bodyMachine = EntityStateMachine.FindByCustomName(((Component)newBody).gameObject, "Body");
			machines = ((Component)newBody).GetComponents<EntityStateMachine>();
			heldMachines.Clear();
		}
	}
	internal static class SprintStates
	{
		private static readonly Type[] extraStates = new Type[15]
		{
			typeof(BaseSidearmState),
			typeof(BaseScopeState),
			typeof(FireNailgun),
			typeof(ToolbotDualWieldBase),
			typeof(FireCorruptHandBeam),
			typeof(ShieldFormation),
			typeof(ChargeArrow),
			typeof(Flamethrower),
			typeof(PrepWall),
			typeof(Paint),
			typeof(SetupAirstrike),
			typeof(SetupAirstrikeAlt),
			typeof(SetupSupplyDrop),
			typeof(AimThrowableBase),
			typeof(AimRepossess)
		};

		private static readonly Dictionary<Type, bool> sensitive = new Dictionary<Type, bool>();

		[SystemInitializer(new Type[] { typeof(SkillCatalog) })]
		private static void Init()
		{
			foreach (SkillDef allSkillDef in SkillCatalog.allSkillDefs)
			{
				Type stateType = ((SerializableEntityStateType)(ref allSkillDef.activationState)).stateType;
				if (stateType != null && (allSkillDef.cancelSprintingOnActivation || allSkillDef.canceledFromSprinting || allSkillDef.forceSprintDuringState))
				{
					sensitive[stateType] = true;
				}
			}
		}

		internal static bool IsSprintSensitive(Type stateType)
		{
			if (!sensitive.TryGetValue(stateType, out var value))
			{
				Type[] array = extraStates;
				foreach (Type type in array)
				{
					value |= type.IsAssignableFrom(stateType);
				}
				sensitive[stateType] = value;
			}
			return value;
		}
	}
}