Decompiled source of ThirdPersonMode v1.0.1

BepInEx/plugins/ThirdPersonMode.dll

Decompiled 13 hours ago
using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using Pigeon.Movement;
using Unity.Netcode;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;

[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: IgnoresAccessChecksTo("Assembly-CSharp")]
[assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")]
[assembly: AssemblyCompany("Sparroh")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: AssemblyInformationalVersion("1.0.1")]
[assembly: AssemblyProduct("ThirdPersonMode")]
[assembly: AssemblyTitle("ThirdPersonMode")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("1.0.1.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
	[CompilerGenerated]
	[Embedded]
	internal sealed class EmbeddedAttribute : Attribute
	{
	}
}
namespace System.Runtime.CompilerServices
{
	[CompilerGenerated]
	[Embedded]
	[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
	internal sealed class RefSafetyRulesAttribute : Attribute
	{
		public readonly int Version;

		public RefSafetyRulesAttribute(int P_0)
		{
			Version = P_0;
		}
	}
}
public static class ConfigManager
{
	private const float DebounceSeconds = 0.25f;

	private static ConfigFile config;

	private static ManualLogSource logger;

	private static FileSystemWatcher configWatcher;

	private static volatile bool reloadPending;

	private static float lastReloadTime;

	public static ConfigEntry<Key> ToggleKey { get; private set; }

	public static ConfigEntry<float> OrbitDistance { get; private set; }

	public static ConfigEntry<float> ShoulderOffset { get; private set; }

	public static ConfigEntry<float> ShoulderHeightOffset { get; private set; }

	public static ConfigEntry<bool> HideHudInThirdPerson { get; private set; }

	public static ConfigEntry<bool> AdsReturnsToFirstPerson { get; private set; }

	public static ConfigEntry<bool> StartInThirdPerson { get; private set; }

	public static ConfigEntry<bool> ScrollToZoom { get; private set; }

	public static ConfigEntry<float> MinOrbitDistance { get; private set; }

	public static ConfigEntry<float> MaxOrbitDistance { get; private set; }

	public static ConfigEntry<float> CrouchBodyOffset { get; private set; }

	public static void Initialize(ConfigFile configFile, ManualLogSource log)
	{
		//IL_0059: Unknown result type (might be due to invalid IL or missing references)
		//IL_0063: Expected O, but got Unknown
		//IL_0095: Unknown result type (might be due to invalid IL or missing references)
		//IL_009f: Expected O, but got Unknown
		//IL_00d1: Unknown result type (might be due to invalid IL or missing references)
		//IL_00db: Expected O, but got Unknown
		//IL_010d: Unknown result type (might be due to invalid IL or missing references)
		//IL_0117: Expected O, but got Unknown
		//IL_0149: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: Expected O, but got Unknown
		//IL_0201: Unknown result type (might be due to invalid IL or missing references)
		//IL_020b: Expected O, but got Unknown
		config = configFile;
		logger = log;
		ToggleKey = config.Bind<Key>("General", "ToggleKey", (Key)50, "Key used to toggle gameplay third-person mode. Uses Unity Input System key names (e.g. Digit0, V, F5).");
		OrbitDistance = config.Bind<float>("Camera", "OrbitDistance", 3.25f, new ConfigDescription("Default camera distance behind the player.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1.5f, 8f), Array.Empty<object>()));
		MinOrbitDistance = config.Bind<float>("Camera", "MinOrbitDistance", 1.5f, new ConfigDescription("Minimum scroll-zoom distance.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.75f, 5f), Array.Empty<object>()));
		MaxOrbitDistance = config.Bind<float>("Camera", "MaxOrbitDistance", 5.5f, new ConfigDescription("Maximum scroll-zoom distance.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 12f), Array.Empty<object>()));
		ShoulderOffset = config.Bind<float>("Camera", "ShoulderOffset", 0.9f, new ConfigDescription("Over-the-shoulder horizontal offset (positive = right shoulder). Higher values clear the body from the crosshair.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-2f, 2f), Array.Empty<object>()));
		ShoulderHeightOffset = config.Bind<float>("Camera", "ShoulderHeightOffset", 1f, new ConfigDescription("Extra camera height while in third person.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1f, 2f), Array.Empty<object>()));
		ScrollToZoom = config.Bind<bool>("Camera", "ScrollToZoom", true, "Allow mouse wheel to change orbit distance while in third person.");
		HideHudInThirdPerson = config.Bind<bool>("Gameplay", "HideHudInThirdPerson", false, "Hide the main HUD while gameplay third-person is active.");
		AdsReturnsToFirstPerson = config.Bind<bool>("Gameplay", "AdsReturnsToFirstPerson", true, "Temporarily switch back to first person while aiming (ADS), then restore third person when you stop aiming.");
		StartInThirdPerson = config.Bind<bool>("Gameplay", "StartInThirdPerson", false, "Automatically enter third person when the local player spawns.");
		CrouchBodyOffset = config.Bind<float>("Camera", "CrouchBodyOffset", -0.55f, new ConfigDescription("Extra world-space Y applied to the third-person body while crouching (negative lowers the model so feet stay planted).", (AcceptableValueBase)(object)new AcceptableValueRange<float>(-1.5f, 0.5f), Array.Empty<object>()));
		config.SettingChanged += OnSettingChanged;
		try
		{
			SetupFileWatcher();
		}
		catch (Exception ex)
		{
			logger.LogError((object)("Error setting up config file watcher: " + ex.Message));
		}
	}

	public static void Tick()
	{
		if (!reloadPending || Time.unscaledTime - lastReloadTime < 0.25f)
		{
			return;
		}
		reloadPending = false;
		lastReloadTime = Time.unscaledTime;
		try
		{
			config.Reload();
			logger.LogInfo((object)"Config reloaded from disk.");
			NotifyConfigReloaded();
		}
		catch (Exception ex)
		{
			logger.LogError((object)("Error reloading config: " + ex.Message));
		}
	}

	public static void Dispose()
	{
		if (config != null)
		{
			config.SettingChanged -= OnSettingChanged;
		}
		if (configWatcher != null)
		{
			configWatcher.EnableRaisingEvents = false;
			configWatcher.Changed -= OnConfigFileChanged;
			configWatcher.Created -= OnConfigFileChanged;
			configWatcher.Renamed -= OnConfigFileChanged;
			configWatcher.Dispose();
			configWatcher = null;
		}
	}

	private static void SetupFileWatcher()
	{
		configWatcher = new FileSystemWatcher(Paths.ConfigPath, "sparroh.thirdpersonmode.cfg");
		configWatcher.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size | NotifyFilters.LastWrite;
		configWatcher.Changed += OnConfigFileChanged;
		configWatcher.Created += OnConfigFileChanged;
		configWatcher.Renamed += OnConfigFileChanged;
		configWatcher.EnableRaisingEvents = true;
	}

	private static void OnConfigFileChanged(object sender, FileSystemEventArgs e)
	{
		reloadPending = true;
	}

	private static void OnSettingChanged(object sender, EventArgs e)
	{
		NotifyConfigReloaded();
	}

	private static void NotifyConfigReloaded()
	{
		ThirdPersonController.Instance?.OnConfigReloaded();
	}
}
[BepInPlugin("sparroh.thirdpersonmode", "ThirdPersonMode", "1.0.1")]
[MycoMod(/*Could not decode attribute arguments.*/)]
public class ThirdPersonModePlugin : BaseUnityPlugin
{
	public const string PluginGUID = "sparroh.thirdpersonmode";

	public const string PluginName = "ThirdPersonMode";

	public const string PluginVersion = "1.0.1";

	internal static ManualLogSource Log;

	internal static Harmony Harmony;

	private void Awake()
	{
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_002a: Expected O, but got Unknown
		Log = ((BaseUnityPlugin)this).Logger;
		ConfigManager.Initialize(((BaseUnityPlugin)this).Config, Log);
		Harmony = new Harmony("sparroh.thirdpersonmode");
		try
		{
			Harmony.PatchAll(typeof(ThirdPersonPatches));
			Log.LogInfo((object)"ThirdPersonMode v1.0.1 loaded.");
		}
		catch (Exception arg)
		{
			Log.LogError((object)$"Failed to apply Harmony patches: {arg}");
		}
		((Component)this).gameObject.AddComponent<ThirdPersonController>();
	}

	private void Update()
	{
		ConfigManager.Tick();
	}

	private void OnDestroy()
	{
		ConfigManager.Dispose();
		Harmony harmony = Harmony;
		if (harmony != null)
		{
			harmony.UnpatchSelf();
		}
	}
}
internal sealed class ThirdPersonActions
{
	private enum ActionKind
	{
		None,
		Equip,
		Reload,
		Fire,
		Melee,
		Throw,
		Fly
	}

	private static FieldInfo throwableThrowTpField;

	private static FieldInfo wingsuitFlyAnimField;

	private static FieldInfo tpAnimancerField;

	private static bool fieldsResolved;

	private float actionUntil;

	private Gun boundGun;

	private ActionKind currentAction;

	private Key equipKey;

	private Key fireKey;

	private int lastSelectedSlot = int.MinValue;

	private Key reloadKey;

	private bool slotInitialized;

	private Key stowKey;

	private bool suppressStow;

	private bool wasFiring;

	private bool wasFlying;

	private bool wasMeleeActive;

	private bool wasReloading;

	private bool wasThrowableActive;

	public bool BlocksLocomotion
	{
		get
		{
			if (currentAction == ActionKind.Fly)
			{
				return true;
			}
			ActionKind actionKind = currentAction;
			if ((uint)(actionKind - 4) <= 1u)
			{
				return Time.time < actionUntil;
			}
			return false;
		}
	}

	public void Reset()
	{
		boundGun = null;
		equipKey = (reloadKey = (fireKey = (stowKey = null)));
		wasReloading = false;
		wasFiring = false;
		wasMeleeActive = false;
		wasThrowableActive = false;
		wasFlying = false;
		currentAction = ActionKind.None;
		actionUntil = 0f;
		lastSelectedSlot = int.MinValue;
		slotInitialized = false;
		suppressStow = true;
	}

	public void Tick(Player player, PlayerAnimation playerAnim, ThirdPersonAnimator tpAnim, bool tpActive)
	{
		if (!tpActive || (Object)(object)player == (Object)null || (Object)(object)playerAnim == (Object)null || (Object)(object)tpAnim == (Object)null || !((Behaviour)tpAnim).isActiveAndEnabled)
		{
			Reset();
			return;
		}
		ResolveFields();
		EnsureAdditiveLayer(tpAnim);
		Wingsuit val = FindWingsuit(player);
		if ((Object)(object)val != (Object)null && val.IsFlying)
		{
			if (!wasFlying || currentAction != ActionKind.Fly)
			{
				Key wingsuitFlyKey = GetWingsuitFlyKey(val);
				if ((Object)(object)wingsuitFlyKey?.clip != (Object)null)
				{
					PlayBaseLoop(tpAnim, wingsuitFlyKey, ActionKind.Fly);
				}
			}
			wasFlying = true;
			wasMeleeActive = false;
			wasThrowableActive = false;
			return;
		}
		if (wasFlying)
		{
			wasFlying = false;
			currentAction = ActionKind.None;
			actionUntil = 0f;
		}
		int selectedGearSlot = player.SelectedGearSlot;
		if (!slotInitialized)
		{
			lastSelectedSlot = selectedGearSlot;
			slotInitialized = true;
			suppressStow = false;
			IGear selectedGear = player.SelectedGear;
			Gun val2 = (Gun)(object)((selectedGear is Gun) ? selectedGear : null);
			if (val2 != null && val2.Active)
			{
				BindGunSet(val2, player.Character);
				PlayGunKey(tpAnim, equipKey, ActionKind.Equip);
				wasReloading = val2.Reloading;
				wasFiring = val2.IsFiring;
			}
		}
		else if (selectedGearSlot != lastSelectedSlot)
		{
			lastSelectedSlot = selectedGearSlot;
			IGear selectedGear2 = player.SelectedGear;
			Gun val3 = (Gun)(object)((selectedGear2 is Gun) ? selectedGear2 : null);
			Gun val4 = boundGun;
			if ((Object)(object)val4 != (Object)null && ((Object)(object)val3 == (Object)null || (Object)(object)val3 != (Object)(object)val4))
			{
				bool num = (Object)(object)player.Melee != (Object)null && ((Throwable)player.Melee).Active;
				IGear selectedGear3 = player.SelectedGear;
				Throwable val5 = (Throwable)(object)((selectedGear3 is Throwable) ? selectedGear3 : null);
				bool flag = val5 != null && !(val5 is MeleeGear) && val5.Active;
				if (!num && !flag && !suppressStow)
				{
					PlayGunKey(tpAnim, stowKey, ActionKind.None);
				}
				if ((Object)(object)val3 == (Object)null || !val3.Active)
				{
					boundGun = null;
					equipKey = (reloadKey = (fireKey = (stowKey = null)));
				}
			}
			if ((Object)(object)val3 != (Object)null && val3.Active)
			{
				BindGunSet(val3, player.Character);
				PlayGunKey(tpAnim, equipKey, ActionKind.Equip);
				wasReloading = val3.Reloading;
				wasFiring = val3.IsFiring;
			}
		}
		suppressStow = false;
		MeleeGear melee = player.Melee;
		bool flag2 = (Object)(object)melee != (Object)null && ((Throwable)melee).Active;
		if (flag2 && !wasMeleeActive)
		{
			Key meleeTpKey = GetMeleeTpKey(melee, player.Character);
			PlayOneShot(tpAnim, meleeTpKey, ActionKind.Melee);
		}
		wasMeleeActive = flag2;
		IGear selectedGear4 = player.SelectedGear;
		Throwable val6 = (Throwable)(object)((selectedGear4 is Throwable) ? selectedGear4 : null);
		if (val6 != null && !(val6 is MeleeGear))
		{
			bool active = val6.Active;
			if (active && !wasThrowableActive)
			{
				Key throwableTpKey = GetThrowableTpKey(val6);
				PlayOneShot(tpAnim, throwableTpKey, ActionKind.Throw);
			}
			wasThrowableActive = active;
		}
		else
		{
			wasThrowableActive = false;
		}
		bool flag3 = BlocksLocomotion;
		if (flag3)
		{
			ActionKind actionKind = currentAction;
			bool flag4 = (uint)(actionKind - 4) <= 1u;
			flag3 = flag4;
		}
		if (flag3)
		{
			return;
		}
		IGear selectedGear5 = player.SelectedGear;
		Gun val7 = (Gun)(object)((selectedGear5 is Gun) ? selectedGear5 : null);
		if ((Object)(object)val7 == (Object)null || !val7.Active)
		{
			wasReloading = false;
			wasFiring = false;
			return;
		}
		if ((Object)(object)boundGun != (Object)(object)val7)
		{
			BindGunSet(val7, player.Character);
			if (currentAction != ActionKind.Equip || Time.time >= actionUntil)
			{
				PlayGunKey(tpAnim, equipKey, ActionKind.Equip);
			}
			wasReloading = val7.Reloading;
			wasFiring = val7.IsFiring;
			return;
		}
		bool reloading = val7.Reloading;
		if (reloading && !wasReloading)
		{
			PlayGunKey(tpAnim, reloadKey, ActionKind.Reload);
		}
		wasReloading = reloading;
		bool isFiring = val7.IsFiring;
		if (isFiring && !wasFiring && !reloading)
		{
			PlayGunKey(tpAnim, fireKey, ActionKind.Fire);
		}
		else if (isFiring && !reloading && currentAction == ActionKind.Fire && Time.time >= actionUntil)
		{
			PlayGunKey(tpAnim, fireKey, ActionKind.Fire);
		}
		wasFiring = isFiring;
	}

	public void OnGunAttached(Gun gun, Character character, ThirdPersonAnimator tpAnim)
	{
		if (!((Object)(object)gun == (Object)null) && !((Object)(object)tpAnim == (Object)null))
		{
			BindGunSet(gun, character);
			EnsureAdditiveLayer(tpAnim);
			if (slotInitialized)
			{
				PlayGunKey(tpAnim, equipKey, ActionKind.Equip);
				wasReloading = gun.Reloading;
				wasFiring = gun.IsFiring;
			}
		}
	}

	private void BindGunSet(Gun gun, Character character)
	{
		boundGun = gun;
		equipKey = (reloadKey = (fireKey = (stowKey = null)));
		try
		{
			ThirdPersonGearAnimationSet animationSet = ThirdPersonReflection.GetAnimationSet(gun, character);
			if (!((Object)(object)animationSet == (Object)null))
			{
				equipKey = CloneKey(animationSet.equip);
				reloadKey = CloneKey(animationSet.reload);
				fireKey = CloneKey(animationSet.fire);
				stowKey = CloneKey(((PlayerAnimationSet)animationSet).stow);
			}
		}
		catch (Exception ex)
		{
			ThirdPersonModePlugin.Log.LogWarning((object)("BindGunSet failed: " + ex.Message));
		}
	}

	private void PlayGunKey(ThirdPersonAnimator tpAnim, Key key, ActionKind kind)
	{
		if ((Object)(object)key?.clip == (Object)null || (Object)(object)tpAnim == (Object)null)
		{
			return;
		}
		try
		{
			EnsureAdditiveLayer(tpAnim);
			tpAnim.PlayAdditive(key);
			currentAction = kind;
			float num = key.clip.length / Mathf.Max(key.speed, 0.01f);
			actionUntil = Time.time + Mathf.Clamp(num * 0.9f, 0.05f, 2.5f);
		}
		catch (Exception ex)
		{
			ThirdPersonModePlugin.Log.LogWarning((object)$"PlayGunKey({kind}) failed: {ex.Message}");
		}
	}

	private void PlayOneShot(ThirdPersonAnimator tpAnim, Key key, ActionKind kind)
	{
		if ((Object)(object)key?.clip == (Object)null || (Object)(object)tpAnim == (Object)null)
		{
			return;
		}
		try
		{
			tpAnim.SetState(key, -1f, 0f);
			currentAction = kind;
			float num = key.clip.length / Mathf.Max(key.speed, 0.01f);
			actionUntil = Time.time + Mathf.Clamp(num * 0.95f, 0.1f, 3f);
		}
		catch (Exception ex)
		{
			ThirdPersonModePlugin.Log.LogWarning((object)$"PlayOneShot({kind}) failed: {ex.Message}");
		}
	}

	private void PlayBaseLoop(ThirdPersonAnimator tpAnim, Key key, ActionKind kind)
	{
		if ((Object)(object)key?.clip == (Object)null || (Object)(object)tpAnim == (Object)null)
		{
			return;
		}
		try
		{
			tpAnim.SetState(key, -1f, 0f);
			currentAction = kind;
			actionUntil = float.MaxValue;
		}
		catch (Exception ex)
		{
			ThirdPersonModePlugin.Log.LogWarning((object)$"PlayBaseLoop({kind}) failed: {ex.Message}");
		}
	}

	private static Wingsuit FindWingsuit(Player player)
	{
		try
		{
			IGear selectedGear = player.SelectedGear;
			Wingsuit val = (Wingsuit)(object)((selectedGear is Wingsuit) ? selectedGear : null);
			if (val != null)
			{
				return val;
			}
			IGear[] gear = player.Gear;
			if (gear == null)
			{
				return null;
			}
			foreach (IGear obj in gear)
			{
				Wingsuit val2 = (Wingsuit)(object)((obj is Wingsuit) ? obj : null);
				if (val2 != null)
				{
					return val2;
				}
			}
		}
		catch
		{
		}
		return null;
	}

	private static Key GetWingsuitFlyKey(Wingsuit wingsuit)
	{
		ResolveFields();
		try
		{
			if (wingsuitFlyAnimField != null)
			{
				object? value = wingsuitFlyAnimField.GetValue(wingsuit);
				Key val = (Key)((value is Key) ? value : null);
				if ((Object)(object)val?.clip != (Object)null)
				{
					return CloneKey(val);
				}
			}
		}
		catch
		{
		}
		return null;
	}

	private static Key GetMeleeTpKey(MeleeGear melee, Character character)
	{
		//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_006f: Unknown result type (might be due to invalid IL or missing references)
		//IL_007b: Expected O, but got Unknown
		ResolveFields();
		try
		{
			if (throwableThrowTpField != null)
			{
				object? value = throwableThrowTpField.GetValue(melee);
				Key val = (Key)((value is Key) ? value : null);
				if ((Object)(object)val?.clip != (Object)null)
				{
					return CloneKey(val);
				}
			}
		}
		catch
		{
		}
		if ((Object)(object)character?.MeleeAnimationThirdPerson != (Object)null)
		{
			return new Key(character.MeleeAnimationThirdPerson)
			{
				fadeDuration = 0.1f,
				speed = 1f
			};
		}
		return null;
	}

	private static Key GetThrowableTpKey(Throwable throwable)
	{
		ResolveFields();
		try
		{
			if (throwableThrowTpField != null)
			{
				object? value = throwableThrowTpField.GetValue(throwable);
				Key val = (Key)((value is Key) ? value : null);
				if ((Object)(object)val?.clip != (Object)null)
				{
					return CloneKey(val);
				}
			}
		}
		catch
		{
		}
		return null;
	}

	private static Key CloneKey(Key source)
	{
		//IL_0014: Unknown result type (might be due to invalid IL or missing references)
		//IL_001a: Expected O, but got Unknown
		if (source == null || (Object)(object)source.clip == (Object)null)
		{
			return null;
		}
		return new Key(source);
	}

	private static void EnsureAdditiveLayer(ThirdPersonAnimator tpAnim)
	{
		try
		{
			if (tpAnimancerField == null)
			{
				tpAnimancerField = AccessTools.Field(typeof(ThirdPersonAnimator), "animator");
			}
			object obj = tpAnimancerField?.GetValue(tpAnim);
			if (obj == null)
			{
				return;
			}
			object obj2 = AccessTools.Property(obj.GetType(), "Layers")?.GetValue(obj);
			if (obj2 == null)
			{
				return;
			}
			PropertyInfo propertyInfo = AccessTools.Property(obj2.GetType(), "Count");
			if (((propertyInfo != null) ? ((int)propertyInfo.GetValue(obj2)) : 0) <= 1)
			{
				return;
			}
			PropertyInfo propertyInfo2 = AccessTools.Property(obj2.GetType(), "Item");
			object obj3 = ((propertyInfo2 != null) ? propertyInfo2.GetValue(obj2, new object[1] { 1 }) : null);
			if (obj3 != null)
			{
				PropertyInfo propertyInfo3 = AccessTools.Property(obj3.GetType(), "Weight");
				if (!(propertyInfo3 == null) && (float)propertyInfo3.GetValue(obj3) < 0.99f)
				{
					propertyInfo3.SetValue(obj3, 1f);
				}
			}
		}
		catch
		{
		}
	}

	private static void ResolveFields()
	{
		if (!fieldsResolved)
		{
			fieldsResolved = true;
			throwableThrowTpField = AccessTools.Field(typeof(Throwable), "throwAnimationThirdPerson");
			wingsuitFlyAnimField = AccessTools.Field(typeof(Wingsuit), "thirdPersonFlyAnimation");
			tpAnimancerField = AccessTools.Field(typeof(ThirdPersonAnimator), "animator");
		}
	}
}
internal static class ThirdPersonCamera
{
	private static FieldInfo thirdPersonModeField;

	private static FieldInfo orbitDistanceField;

	private static FieldInfo enableThirdPersonTimeField;

	private static FieldInfo minOrbitDistanceField;

	private static FieldInfo maxOrbitDistanceField;

	private static FieldInfo wasThirdPersonEnabledWithHudField;

	private static FieldInfo isAimingField;

	private static PropertyInfo allowFirstPersonRenderingProp;

	private static bool allowFirstPersonRenderingResolved;

	private static bool fieldsResolved;

	public static float AppliedOrbitDistance { get; set; } = 3.25f;

	public static void OnConfigReloaded()
	{
		AppliedOrbitDistance = Mathf.Clamp(ConfigManager.OrbitDistance.Value, ConfigManager.MinOrbitDistance.Value, ConfigManager.MaxOrbitDistance.Value);
		if (ThirdPersonController.IsGameplayThirdPersonActive)
		{
			ApplyOrbitSettings(PlayerLook.Instance);
		}
	}

	public static void ResetOrbitDistance()
	{
		AppliedOrbitDistance = ConfigManager.OrbitDistance.Value;
	}

	public static void ApplyOrbitSettings(PlayerLook look)
	{
		if ((Object)(object)look == (Object)null)
		{
			return;
		}
		ResolveFields();
		if (orbitDistanceField != null)
		{
			orbitDistanceField.SetValue(look, AppliedOrbitDistance);
		}
		if (minOrbitDistanceField != null)
		{
			minOrbitDistanceField.SetValue(look, ConfigManager.MinOrbitDistance.Value);
		}
		if (maxOrbitDistanceField != null)
		{
			maxOrbitDistanceField.SetValue(look, ConfigManager.MaxOrbitDistance.Value);
		}
		if (ConfigManager.ScrollToZoom.Value && orbitDistanceField != null)
		{
			try
			{
				AppliedOrbitDistance = (float)orbitDistanceField.GetValue(look);
				return;
			}
			catch
			{
				return;
			}
		}
		if (orbitDistanceField != null)
		{
			orbitDistanceField.SetValue(look, AppliedOrbitDistance);
		}
	}

	public static void ApplyShoulderOffset(PlayerLook look)
	{
		//IL_003a: Unknown result type (might be due to invalid IL or missing references)
		//IL_0040: Unknown result type (might be due to invalid IL or missing references)
		//IL_0046: Unknown result type (might be due to invalid IL or missing references)
		//IL_004c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0052: Unknown result type (might be due to invalid IL or missing references)
		//IL_0057: 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)
		float value = ConfigManager.ShoulderOffset.Value;
		float value2 = ConfigManager.ShoulderHeightOffset.Value;
		if (!Mathf.Approximately(value, 0f) || !Mathf.Approximately(value2, 0f))
		{
			Transform transform = ((Component)look).transform;
			transform.position += transform.right * value + transform.up * value2;
		}
	}

	public static void MarkThirdPersonEnabled(PlayerLook look)
	{
		ResolveFields();
		if (enableThirdPersonTimeField != null)
		{
			enableThirdPersonTimeField.SetValue(look, Time.time);
		}
		if (wasThirdPersonEnabledWithHudField != null)
		{
			wasThirdPersonEnabledWithHudField.SetValue(look, ConfigManager.HideHudInThirdPerson.Value);
		}
	}

	public static bool IsPlayerAiming(PlayerLook look)
	{
		//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)
		ResolveFields();
		if (isAimingField != null)
		{
			try
			{
				if ((bool)isAimingField.GetValue(look))
				{
					return true;
				}
			}
			catch
			{
			}
		}
		try
		{
			PlayerActions player = PlayerInput.Controls.Player;
			return ((PlayerActions)(ref player)).Aim.IsPressed();
		}
		catch
		{
			return false;
		}
	}

	public static bool GetThirdPersonMode(PlayerLook look)
	{
		if ((Object)(object)look == (Object)null)
		{
			return false;
		}
		ResolveFields();
		if (thirdPersonModeField == null)
		{
			thirdPersonModeField = AccessTools.Field(typeof(PlayerLook), "thirdPersonMode");
		}
		try
		{
			return thirdPersonModeField != null && (bool)thirdPersonModeField.GetValue(look);
		}
		catch
		{
			return look.IsInThirdPerson;
		}
	}

	public static void SetThirdPersonMode(PlayerLook look, bool value)
	{
		ResolveFields();
		if (thirdPersonModeField == null)
		{
			thirdPersonModeField = AccessTools.Field(typeof(PlayerLook), "thirdPersonMode");
		}
		thirdPersonModeField?.SetValue(look, value);
	}

	public static void SetAllowFirstPersonRendering(bool allow)
	{
		if (!allowFirstPersonRenderingResolved)
		{
			allowFirstPersonRenderingResolved = true;
			try
			{
				Type type = AccessTools.TypeByName("UnityEngine.Rendering.Universal.UniversalRenderer") ?? AccessTools.TypeByName("UniversalRenderer");
				if (type != null)
				{
					allowFirstPersonRenderingProp = AccessTools.Property(type, "AllowFirstPersonRendering");
				}
				if (allowFirstPersonRenderingProp == null)
				{
					ThirdPersonModePlugin.Log.LogWarning((object)"UniversalRenderer.AllowFirstPersonRendering not found; FP arms may still render in TP.");
				}
			}
			catch (Exception ex)
			{
				ThirdPersonModePlugin.Log.LogWarning((object)("Failed resolving AllowFirstPersonRendering: " + ex.Message));
			}
		}
		try
		{
			allowFirstPersonRenderingProp?.SetValue(null, allow);
		}
		catch (Exception ex2)
		{
			ThirdPersonModePlugin.Log.LogWarning((object)("Could not set AllowFirstPersonRendering: " + ex2.Message));
		}
	}

	private static void ResolveFields()
	{
		if (!fieldsResolved)
		{
			Type? typeFromHandle = typeof(PlayerLook);
			thirdPersonModeField = AccessTools.Field(typeFromHandle, "thirdPersonMode");
			orbitDistanceField = AccessTools.Field(typeFromHandle, "orbitDistance");
			enableThirdPersonTimeField = AccessTools.Field(typeFromHandle, "enableThirdPersonTime");
			minOrbitDistanceField = AccessTools.Field(typeFromHandle, "minOrbitDistance");
			maxOrbitDistanceField = AccessTools.Field(typeFromHandle, "maxOrbitDistance");
			wasThirdPersonEnabledWithHudField = AccessTools.Field(typeFromHandle, "wasThirdPersonEnabledWithHUD");
			isAimingField = AccessTools.Field(typeFromHandle, "isAiming");
			fieldsResolved = thirdPersonModeField != null && orbitDistanceField != null;
			if (!fieldsResolved)
			{
				ThirdPersonModePlugin.Log.LogError((object)"Failed to resolve PlayerLook third-person fields. TP mode will not work.");
			}
		}
	}
}
public sealed class ThirdPersonController : MonoBehaviour
{
	internal static bool AllowVanillaDisable;

	private readonly ThirdPersonActions actions = new ThirdPersonActions();

	private readonly ThirdPersonGearAttachment gearAttachment = new ThirdPersonGearAttachment();

	private readonly ThirdPersonLocomotion locomotion = new ThirdPersonLocomotion();

	private bool adsForcedFirstPerson;

	private bool startInTpPending;

	public static ThirdPersonController Instance { get; private set; }

	public static bool WantGameplayThirdPerson { get; private set; }

	public static bool IsGameplayThirdPersonActive { get; private set; }

	private void Awake()
	{
		Instance = this;
		Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject);
		gearAttachment.Actions = actions;
		locomotion.ShouldBlockLocomotion = () => actions.BlocksLocomotion;
		startInTpPending = ConfigManager.StartInThirdPerson.Value;
		ThirdPersonCamera.ResetOrbitDistance();
	}

	private void Update()
	{
		if (!TryGetLocalContext(out var player, out var look))
		{
			if (IsGameplayThirdPersonActive || WantGameplayThirdPerson)
			{
				ForceExit();
			}
			return;
		}
		if (!player.IsAlive || look.IsSpectating)
		{
			if (WantGameplayThirdPerson || IsGameplayThirdPersonActive)
			{
				ExitGameplayThirdPerson(look, player, clearWant: false);
			}
			return;
		}
		if (startInTpPending)
		{
			startInTpPending = false;
			EnterGameplayThirdPerson(look, player);
		}
		if (WasTogglePressed())
		{
			if (WantGameplayThirdPerson)
			{
				WantGameplayThirdPerson = false;
				ExitGameplayThirdPerson(look, player, clearWant: true);
			}
			else
			{
				EnterGameplayThirdPerson(look, player);
			}
		}
		if (!WantGameplayThirdPerson)
		{
			return;
		}
		if (ConfigManager.AdsReturnsToFirstPerson.Value && ThirdPersonCamera.IsPlayerAiming(look))
		{
			if (!adsForcedFirstPerson)
			{
				adsForcedFirstPerson = true;
				SuspendForAds(look, player);
			}
			return;
		}
		if (adsForcedFirstPerson)
		{
			adsForcedFirstPerson = false;
			ResumeFromAds(look, player);
		}
		if (!IsGameplayThirdPersonActive || !ThirdPersonCamera.GetThirdPersonMode(look))
		{
			if (ThirdPersonCamera.GetThirdPersonMode(look) && !IsGameplayThirdPersonActive)
			{
				return;
			}
			ApplyGameplayThirdPerson(look, player);
		}
		else
		{
			ThirdPersonCamera.ApplyOrbitSettings(look);
		}
		TickLocomotion(player);
	}

	private void LateUpdate()
	{
		if (IsGameplayThirdPersonActive && !adsForcedFirstPerson && TryGetLocalContext(out var player, out var look))
		{
			ThirdPersonCamera.ApplyShoulderOffset(look);
			TickLocomotion(player);
		}
	}

	private void OnDestroy()
	{
		if ((Object)(object)Instance == (Object)(object)this)
		{
			ForceExit();
			Instance = null;
		}
	}

	internal void OnConfigReloaded()
	{
		ThirdPersonCamera.OnConfigReloaded();
	}

	private void TickLocomotion(Player player)
	{
		if (!IsGameplayThirdPersonActive || adsForcedFirstPerson || (Object)(object)player == (Object)null)
		{
			gearAttachment.Tick(player, (player != null) ? player.Animator : null, tpActive: false);
			actions.Tick(player, (player != null) ? player.Animator : null, null, tpActive: false);
			return;
		}
		PlayerAnimation animator = player.Animator;
		ThirdPersonAnimator tpAnim = (((Object)(object)animator != (Object)null) ? animator.SelfThirdPersonAnimator : null);
		actions.Tick(player, animator, tpAnim, tpActive: true);
		locomotion.Tick(player, animator, tpAnim);
		gearAttachment.Tick(player, animator, tpActive: true);
	}

	private static bool TryGetLocalContext(out Player player, out PlayerLook look)
	{
		player = Player.LocalPlayer;
		look = PlayerLook.Instance;
		if ((Object)(object)player != (Object)null && (Object)(object)look != (Object)null)
		{
			return ((NetworkBehaviour)player).IsLocalPlayer;
		}
		return false;
	}

	private static bool WasTogglePressed()
	{
		//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_0017: Unknown result type (might be due to invalid IL or missing references)
		Keyboard current = Keyboard.current;
		if (current == null)
		{
			return false;
		}
		Key value = ConfigManager.ToggleKey.Value;
		try
		{
			return ((ButtonControl)current[value]).wasPressedThisFrame;
		}
		catch
		{
			return false;
		}
	}

	private void EnterGameplayThirdPerson(PlayerLook look, Player player)
	{
		WantGameplayThirdPerson = true;
		adsForcedFirstPerson = false;
		ThirdPersonCamera.ResetOrbitDistance();
		locomotion.Reset();
		actions.Reset();
		gearAttachment.Reset();
		ApplyGameplayThirdPerson(look, player);
		TickLocomotion(player);
		ThirdPersonModePlugin.Log.LogInfo((object)"Gameplay third-person enabled.");
	}

	private void ExitGameplayThirdPerson(PlayerLook look, Player player, bool clearWant)
	{
		if (clearWant)
		{
			WantGameplayThirdPerson = false;
		}
		adsForcedFirstPerson = false;
		gearAttachment.Reset();
		actions.Reset();
		locomotion.Reset();
		RestoreFirstPerson(look, player);
		ThirdPersonModePlugin.Log.LogInfo((object)"Gameplay third-person disabled.");
	}

	private void ForceExit()
	{
		WantGameplayThirdPerson = false;
		adsForcedFirstPerson = false;
		IsGameplayThirdPersonActive = false;
		gearAttachment.Reset();
		actions.Reset();
		locomotion.Reset();
		PlayerLook instance = PlayerLook.Instance;
		Player localPlayer = Player.LocalPlayer;
		if ((Object)(object)instance != (Object)null && (Object)(object)localPlayer != (Object)null)
		{
			RestoreFirstPerson(instance, localPlayer);
		}
	}

	private void SuspendForAds(PlayerLook look, Player player)
	{
		gearAttachment.Reset();
		actions.Reset();
		locomotion.Reset();
		RestoreFirstPerson(look, player, keepWantFlag: true);
	}

	private void ResumeFromAds(PlayerLook look, Player player)
	{
		if (WantGameplayThirdPerson && player.IsAlive && !look.IsSpectating)
		{
			locomotion.Reset();
			actions.Reset();
			gearAttachment.Reset();
			ApplyGameplayThirdPerson(look, player);
			TickLocomotion(player);
		}
	}

	private void ApplyGameplayThirdPerson(PlayerLook look, Player player)
	{
		bool thirdPersonMode = ThirdPersonCamera.GetThirdPersonMode(look);
		ThirdPersonCamera.SetThirdPersonMode(look, value: true);
		ThirdPersonCamera.ApplyOrbitSettings(look);
		ThirdPersonCamera.MarkThirdPersonEnabled(look);
		ThirdPersonCamera.SetAllowFirstPersonRendering(allow: false);
		try
		{
			PlayerAnimation animator = player.Animator;
			if (animator != null)
			{
				animator.EnableThirdPerson();
			}
		}
		catch (Exception ex)
		{
			ThirdPersonModePlugin.Log.LogWarning((object)("EnableThirdPerson body failed: " + ex.Message));
		}
		if ((Object)(object)look.ArmsModelParent != (Object)null)
		{
			look.ArmsModelParent.SetActive(false);
		}
		if (ConfigManager.HideHudInThirdPerson.Value && !thirdPersonMode)
		{
			try
			{
				look.DisableMainHUD = true;
			}
			catch
			{
			}
		}
		IsGameplayThirdPersonActive = true;
	}

	private void RestoreFirstPerson(PlayerLook look, Player player, bool keepWantFlag = false)
	{
		bool isGameplayThirdPersonActive = IsGameplayThirdPersonActive;
		IsGameplayThirdPersonActive = false;
		if (ThirdPersonCamera.GetThirdPersonMode(look))
		{
			ThirdPersonCamera.SetThirdPersonMode(look, value: false);
		}
		ThirdPersonCamera.SetAllowFirstPersonRendering(allow: true);
		try
		{
			PlayerAnimation animator = player.Animator;
			if (animator != null)
			{
				animator.DisableThirdPerson();
			}
		}
		catch
		{
		}
		if ((Object)(object)look.ArmsModelParent != (Object)null)
		{
			look.ArmsModelParent.SetActive(true);
		}
		if (isGameplayThirdPersonActive && ConfigManager.HideHudInThirdPerson.Value)
		{
			try
			{
				look.DisableMainHUD = false;
			}
			catch
			{
			}
		}
		if (!keepWantFlag)
		{
			WantGameplayThirdPerson = false;
		}
	}
}
internal sealed class ThirdPersonGearAttachment
{
	private Gun attachedGun;

	private bool attachedIsThrowable;

	private Transform fpWeaponRoot;

	private Transform gunModel;

	private bool isAttached;

	private Transform mag1Model;

	private bool mag1WasActive;

	private Transform magModel;

	private bool magWasActive;

	private Vector3 savedLocalPos;

	private Quaternion savedLocalRot;

	private Vector3 savedLocalScale;

	public ThirdPersonActions Actions { get; set; }

	public void Reset()
	{
		if (isAttached)
		{
			try
			{
				DetachInternal(restoreFp: true);
				return;
			}
			catch (Exception ex)
			{
				ThirdPersonModePlugin.Log.LogWarning((object)("Gear attachment Reset detach failed: " + ex.Message));
				ClearState();
				return;
			}
		}
		ClearState();
	}

	public void Tick(Player player, PlayerAnimation playerAnim, bool tpActive)
	{
		if (!tpActive || (Object)(object)player == (Object)null || (Object)(object)playerAnim == (Object)null)
		{
			if (isAttached)
			{
				DetachInternal(restoreFp: true);
			}
			return;
		}
		IGear selectedGear = player.SelectedGear;
		Gun val = (Gun)(object)((selectedGear is Gun) ? selectedGear : null);
		Throwable val2 = null;
		if ((Object)(object)val == (Object)null || !val.Active)
		{
			IGear selectedGear2 = player.SelectedGear;
			Throwable val3 = (Throwable)(object)((selectedGear2 is Throwable) ? selectedGear2 : null);
			if (val3 != null && val3.Active)
			{
				val2 = val3;
			}
			else if ((Object)(object)player.Melee != (Object)null && ((Throwable)player.Melee).Active)
			{
				val2 = (Throwable)(object)player.Melee;
			}
		}
		if (((Object)(object)val == (Object)null || !val.Active) && (Object)(object)val2 == (Object)null)
		{
			if (isAttached)
			{
				DetachInternal(restoreFp: true);
			}
		}
		else if ((Object)(object)val != (Object)null && val.Active)
		{
			if (isAttached && ((Object)(object)attachedGun != (Object)(object)val || attachedIsThrowable))
			{
				DetachInternal(restoreFp: true);
			}
			if (!isAttached)
			{
				TryAttachGun(player, playerAnim, val);
			}
			else if ((Object)(object)gunModel != (Object)null && !((Component)gunModel).gameObject.activeSelf)
			{
				((Component)gunModel).gameObject.SetActive(true);
			}
		}
		else if (isAttached && attachedIsThrowable)
		{
			if ((Object)(object)gunModel != (Object)null && !((Component)gunModel).gameObject.activeSelf)
			{
				((Component)gunModel).gameObject.SetActive(true);
			}
		}
		else
		{
			if (isAttached)
			{
				DetachInternal(restoreFp: true);
			}
			TryAttachThrowable(player, playerAnim, val2);
		}
	}

	private void TryAttachGun(Player player, PlayerAnimation playerAnim, Gun gun)
	{
		//IL_0152: Unknown result type (might be due to invalid IL or missing references)
		//IL_0153: Unknown result type (might be due to invalid IL or missing references)
		//IL_018f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0186: 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_007f: Unknown result type (might be due to invalid IL or missing references)
		//IL_008b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0090: Unknown result type (might be due to invalid IL or missing references)
		//IL_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_0194: Unknown result type (might be due to invalid IL or missing references)
		//IL_019c: Unknown result type (might be due to invalid IL or missing references)
		//IL_01a0: Unknown result type (might be due to invalid IL or missing references)
		Transform val = ThirdPersonReflection.GetGunModel(gun);
		if ((Object)(object)val == (Object)null || !ThirdPersonReflection.IsGunHeld(gun))
		{
			return;
		}
		Transform tpWeaponRoot = GetTpWeaponRoot(playerAnim);
		if ((Object)(object)tpWeaponRoot == (Object)null)
		{
			ThirdPersonModePlugin.Log.LogWarning((object)"TP WeaponRootThirdPerson not found; cannot attach gun.");
			return;
		}
		fpWeaponRoot = playerAnim.WeaponRoot;
		gunModel = val;
		magModel = ThirdPersonReflection.GetMagModel(gun);
		mag1Model = ThirdPersonReflection.GetMag1Model(gun);
		attachedGun = gun;
		attachedIsThrowable = false;
		savedLocalPos = gunModel.localPosition;
		savedLocalRot = gunModel.localRotation;
		savedLocalScale = gunModel.localScale;
		if ((Object)(object)magModel != (Object)null)
		{
			magWasActive = ((Component)magModel).gameObject.activeSelf;
			((Component)magModel).gameObject.SetActive(false);
		}
		if ((Object)(object)mag1Model != (Object)null)
		{
			mag1WasActive = ((Component)mag1Model).gameObject.activeSelf;
			((Component)mag1Model).gameObject.SetActive(false);
		}
		ThirdPersonReflection.GetThirdPersonPose(gun, player.Character, out var pos, out var rot, out var scaleMul);
		try
		{
			try
			{
				IGear.SetGunRenderingLayerRecursive(gunModel, 1u);
			}
			catch
			{
				SetLayerRecursive(gunModel, 0);
			}
			gunModel.SetParent(tpWeaponRoot, false);
			gunModel.SetLocalPositionAndRotation(pos, rot);
			Transform val2 = null;
			try
			{
				IUpgradable prefab = gun.Prefab;
				Gun val3 = (Gun)(object)((prefab is Gun) ? prefab : null);
				if (val3 != null)
				{
					val2 = ThirdPersonReflection.GetGunModel(val3);
				}
			}
			catch
			{
			}
			Vector3 val4 = (((Object)(object)val2 != (Object)null) ? val2.localScale : savedLocalScale);
			gunModel.localScale = val4 * scaleMul;
			((Component)gunModel).gameObject.SetActive(true);
			isAttached = true;
			ThirdPersonAnimator selfThirdPersonAnimator = playerAnim.SelfThirdPersonAnimator;
			Actions?.OnGunAttached(gun, player.Character, selfThirdPersonAnimator);
		}
		catch (Exception ex)
		{
			ThirdPersonModePlugin.Log.LogWarning((object)("Failed to attach gun to TP root: " + ex.Message));
			ClearState();
		}
	}

	private void TryAttachThrowable(Player player, PlayerAnimation playerAnim, Throwable throwable)
	{
		//IL_00be: 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_00d4: Unknown result type (might be due to invalid IL or missing references)
		//IL_0062: Unknown result type (might be due to invalid IL or missing references)
		//IL_0067: Unknown result type (might be due to invalid IL or missing references)
		//IL_0073: Unknown result type (might be due to invalid IL or missing references)
		//IL_0078: Unknown result type (might be due to invalid IL or missing references)
		//IL_0084: Unknown result type (might be due to invalid IL or missing references)
		//IL_0089: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)throwable == (Object)null)
		{
			return;
		}
		Transform throwableModel = ThirdPersonReflection.GetThrowableModel(throwable);
		if ((Object)(object)throwableModel == (Object)null)
		{
			return;
		}
		Transform tpWeaponRoot = GetTpWeaponRoot(playerAnim);
		if ((Object)(object)tpWeaponRoot == (Object)null)
		{
			return;
		}
		fpWeaponRoot = playerAnim.WeaponRoot;
		gunModel = throwableModel;
		magModel = null;
		mag1Model = null;
		attachedGun = null;
		attachedIsThrowable = true;
		savedLocalPos = gunModel.localPosition;
		savedLocalRot = gunModel.localRotation;
		savedLocalScale = gunModel.localScale;
		try
		{
			try
			{
				IGear.SetGunRenderingLayerRecursive(gunModel, 1u);
			}
			catch
			{
				SetLayerRecursive(gunModel, 0);
			}
			gunModel.SetParent(tpWeaponRoot, false);
			gunModel.SetLocalPositionAndRotation(Vector3.zero, Quaternion.identity);
			gunModel.localScale = savedLocalScale;
			((Component)gunModel).gameObject.SetActive(true);
			isAttached = true;
		}
		catch (Exception ex)
		{
			ThirdPersonModePlugin.Log.LogWarning((object)("Failed to attach throwable to TP root: " + ex.Message));
			ClearState();
		}
	}

	private void DetachInternal(bool restoreFp)
	{
		//IL_004b: 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_0062: Unknown result type (might be due to invalid IL or missing references)
		if (!isAttached || (Object)(object)gunModel == (Object)null)
		{
			ClearState();
			return;
		}
		try
		{
			if (restoreFp && (Object)(object)fpWeaponRoot != (Object)null)
			{
				gunModel.SetParent(fpWeaponRoot, false);
				gunModel.SetLocalPositionAndRotation(savedLocalPos, savedLocalRot);
				gunModel.localScale = savedLocalScale;
				try
				{
					IGear.SetGunRenderingLayerRecursive(gunModel, 2u);
				}
				catch
				{
				}
				if ((Object)(object)attachedGun != (Object)null && attachedGun.Active)
				{
					((Component)gunModel).gameObject.SetActive(true);
				}
			}
			if ((Object)(object)magModel != (Object)null)
			{
				((Component)magModel).gameObject.SetActive(magWasActive && (Object)(object)attachedGun != (Object)null && attachedGun.Active);
			}
			if ((Object)(object)mag1Model != (Object)null)
			{
				((Component)mag1Model).gameObject.SetActive(mag1WasActive && (Object)(object)attachedGun != (Object)null && attachedGun.Active);
			}
		}
		catch (Exception ex)
		{
			ThirdPersonModePlugin.Log.LogWarning((object)("Failed to restore FP gun parenting: " + ex.Message));
		}
		ClearState();
	}

	private void ClearState()
	{
		attachedGun = null;
		gunModel = null;
		magModel = null;
		mag1Model = null;
		fpWeaponRoot = null;
		isAttached = false;
		attachedIsThrowable = false;
	}

	private static Transform GetTpWeaponRoot(PlayerAnimation playerAnim)
	{
		try
		{
			ThirdPersonAnimator selfThirdPersonAnimator = playerAnim.SelfThirdPersonAnimator;
			if ((Object)(object)selfThirdPersonAnimator == (Object)null)
			{
				return null;
			}
			ThirdPersonRig rig = selfThirdPersonAnimator.Rig;
			if ((Object)(object)rig != (Object)null && (Object)(object)rig.WeaponRootThirdPerson != (Object)null)
			{
				return rig.WeaponRootThirdPerson;
			}
		}
		catch
		{
		}
		try
		{
			if ((Object)(object)playerAnim.WeaponRootThirdPerson != (Object)null)
			{
				return playerAnim.WeaponRootThirdPerson;
			}
		}
		catch
		{
		}
		return null;
	}

	private static void SetLayerRecursive(Transform t, int layer)
	{
		if (!((Object)(object)t == (Object)null))
		{
			((Component)t).gameObject.layer = layer;
			for (int i = 0; i < t.childCount; i++)
			{
				SetLayerRecursive(t.GetChild(i), layer);
			}
		}
	}
}
internal sealed class ThirdPersonLocomotion
{
	private enum LocoState
	{
		None,
		Idle,
		Walk,
		Run,
		Jump,
		AirJump,
		JumpLoop,
		Land,
		Clamber,
		Slide
	}

	private Key airJumpKey;

	private Character boundCharacter;

	private Key clamberKey;

	private LocoState current;

	private Key idleKey;

	private Key jumpKey;

	private Key jumpLoopKey;

	private Key landKey;

	private float landUntil;

	private Key runKey;

	private Key slideKey;

	private bool usingCrouchSet;

	private Key walkKey;

	private bool wasGrounded = true;

	public Func<bool> ShouldBlockLocomotion { get; set; }

	public void Reset()
	{
		current = LocoState.None;
		boundCharacter = null;
		usingCrouchSet = false;
		idleKey = (runKey = (walkKey = (jumpKey = (airJumpKey = (jumpLoopKey = (landKey = (clamberKey = (slideKey = null))))))));
		landUntil = 0f;
		wasGrounded = true;
	}

	public void Tick(Player player, PlayerAnimation playerAnim, ThirdPersonAnimator tpAnim)
	{
		//IL_008c: Unknown result type (might be due to invalid IL or missing references)
		//IL_0091: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ac: Unknown result type (might be due to invalid IL or missing references)
		//IL_00b4: Unknown result type (might be due to invalid IL or missing references)
		if ((Object)(object)player == (Object)null || (Object)(object)playerAnim == (Object)null || (Object)(object)tpAnim == (Object)null || !((Behaviour)tpAnim).isActiveAndEnabled)
		{
			return;
		}
		Character character = player.Character;
		if ((Object)(object)character == (Object)null)
		{
			return;
		}
		bool flag = player.Crouching && !player.Sliding;
		if ((Object)(object)boundCharacter != (Object)(object)character || usingCrouchSet != flag)
		{
			BindAnimationSet(character, flag);
		}
		if (idleKey == null || (Object)(object)idleKey.clip == (Object)null)
		{
			return;
		}
		try
		{
			Vector3 localPosition = ((Component)player).transform.localPosition;
			if (flag)
			{
				localPosition.y += ConfigManager.CrouchBodyOffset.Value;
			}
			tpAnim.SetTransform(localPosition, ((Component)player).transform.localRotation);
		}
		catch
		{
		}
		if (ShouldBlockLocomotion != null && ShouldBlockLocomotion())
		{
			return;
		}
		LocoState locoState = ResolveDesiredState(player, playerAnim);
		if ((locoState == current && IsLoopingState(locoState) && !AnimatorDriftedFrom(tpAnim, GetKey(locoState), idleKey)) || (current == LocoState.Land && Time.time < landUntil && locoState != LocoState.Jump && locoState != LocoState.Slide && locoState != LocoState.Clamber))
		{
			return;
		}
		Key key = GetKey(locoState);
		if (key == null || (Object)(object)key.clip == (Object)null)
		{
			key = idleKey;
			locoState = LocoState.Idle;
		}
		try
		{
			tpAnim.SetState(key, -1f, 0f);
			current = locoState;
			if (locoState == LocoState.Land)
			{
				float num = (((Object)(object)key.clip != (Object)null) ? (key.clip.length / Mathf.Max(key.speed, 0.01f)) : 0.25f);
				landUntil = Time.time + Mathf.Clamp(num * 0.85f, 0.1f, 0.6f);
			}
		}
		catch (Exception ex)
		{
			ThirdPersonModePlugin.Log.LogWarning((object)$"TP loco SetState failed ({locoState}): {ex.Message}");
		}
	}

	private void BindAnimationSet(Character character, bool crouch)
	{
		//IL_0109: Unknown result type (might be due to invalid IL or missing references)
		//IL_010e: Unknown result type (might be due to invalid IL or missing references)
		//IL_011f: Unknown result type (might be due to invalid IL or missing references)
		//IL_0130: Unknown result type (might be due to invalid IL or missing references)
		//IL_0137: Unknown result type (might be due to invalid IL or missing references)
		//IL_0172: Expected O, but got Unknown
		boundCharacter = character;
		usingCrouchSet = crouch;
		PlayerAnimationSet val = (crouch ? (character.ThirdPersonCrouchAnimationSet ?? character.ThirdPersonAnimationSet) : character.ThirdPersonAnimationSet);
		if ((Object)(object)val == (Object)null)
		{
			ThirdPersonModePlugin.Log.LogWarning((object)("No ThirdPersonAnimationSet on character " + ((Object)character).name));
			idleKey = null;
			return;
		}
		idleKey = CloneKey(val.idle);
		runKey = CloneKey(val.run);
		jumpKey = CloneKey(val.jump);
		airJumpKey = CloneKey(val.airJump);
		jumpLoopKey = CloneKey(val.jumpLoop);
		landKey = CloneKey(val.land);
		clamberKey = CloneKey(val.clamber);
		slideKey = CloneKey(val.slide);
		walkKey = null;
		if (runKey != null && (Object)(object)runKey.walkingClip != (Object)null)
		{
			walkKey = new Key(runKey)
			{
				clip = runKey.walkingClip,
				walkingClip = runKey.walkingClip,
				isWalkingKey = true,
				speed = runKey.speed * ((runKey.walkSpeedMultiplier > 0f) ? runKey.walkSpeedMultiplier : 1f)
			};
		}
		current = LocoState.None;
		ThirdPersonModePlugin.Log.LogInfo((object)string.Format("Bound TP loco set for {0} (crouch={1}, idle={2})", ((Object)character).name, crouch, ((Object)(object)idleKey?.clip != (Object)null) ? ((Object)idleKey.clip).name : "null"));
	}

	private static Key CloneKey(Key source)
	{
		//IL_0006: Unknown result type (might be due to invalid IL or missing references)
		//IL_000b: Unknown result type (might be due to invalid IL or missing references)
		//IL_0012: Unknown result type (might be due to invalid IL or missing references)
		//IL_0019: Unknown result type (might be due to invalid IL or missing references)
		//IL_0020: Unknown result type (might be due to invalid IL or missing references)
		//IL_0028: Expected O, but got Unknown
		if (source == null)
		{
			return null;
		}
		return new Key(source)
		{
			autoTransitionTo = null,
			autoTransitionToKey = null,
			canBeInterruptedBy = null,
			canBeInterruptedByRunState = false
		};
	}

	private static bool AnimatorDriftedFrom(ThirdPersonAnimator tpAnim, Key key, Key fallbackIdle)
	{
		Key val = key ?? fallbackIdle;
		if (val == null || (Object)(object)val.clip == (Object)null)
		{
			return false;
		}
		try
		{
			Key currentStateKey = tpAnim.CurrentStateKey;
			if (currentStateKey == null)
			{
				return true;
			}
			if (currentStateKey == val)
			{
				return false;
			}
			if ((Object)(object)currentStateKey.clip != (Object)null && (Object)(object)val.clip != (Object)null && currentStateKey.clip == val.clip)
			{
				return false;
			}
			return true;
		}
		catch
		{
			return false;
		}
	}

	private LocoState ResolveDesiredState(Player player, PlayerAnimation playerAnim)
	{
		bool flag = player.Grounded || playerAnim.Grounded > 0;
		if (flag && !wasGrounded)
		{
			wasGrounded = true;
			if ((Object)(object)landKey?.clip != (Object)null && !player.Sliding)
			{
				return LocoState.Land;
			}
		}
		else if (!flag)
		{
			wasGrounded = false;
		}
		if (player.Sliding || playerAnim.Sliding)
		{
			return LocoState.Slide;
		}
		if (playerAnim.Clambering)
		{
			return LocoState.Clamber;
		}
		if (!flag)
		{
			if (player.AirJumpCount > 0 && (Object)(object)airJumpKey?.clip != (Object)null && Time.time - player.LastJumpTime < 0.35f)
			{
				return LocoState.AirJump;
			}
			if (Time.time - player.LastJumpTime < 0.4f && (Object)(object)jumpKey?.clip != (Object)null)
			{
				return LocoState.Jump;
			}
			if (!((Object)(object)jumpLoopKey?.clip != (Object)null))
			{
				return LocoState.Jump;
			}
			return LocoState.JumpLoop;
		}
		if (player.IsRunning || playerAnim.Running > 0 || player.Speed > 0.35f)
		{
			if (!player.IsSprinting && playerAnim.Running < 2 && (Object)(object)walkKey?.clip != (Object)null)
			{
				return LocoState.Walk;
			}
			return LocoState.Run;
		}
		return LocoState.Idle;
	}

	private Key GetKey(LocoState state)
	{
		return (Key)(state switch
		{
			LocoState.Walk => walkKey ?? runKey, 
			LocoState.Run => runKey, 
			LocoState.Jump => jumpKey, 
			LocoState.AirJump => airJumpKey ?? jumpKey, 
			LocoState.JumpLoop => jumpLoopKey ?? jumpKey, 
			LocoState.Land => landKey, 
			LocoState.Clamber => clamberKey, 
			LocoState.Slide => slideKey, 
			_ => idleKey, 
		});
	}

	private static bool IsLoopingState(LocoState state)
	{
		switch (state)
		{
		case LocoState.Idle:
		case LocoState.Walk:
		case LocoState.Run:
		case LocoState.JumpLoop:
		case LocoState.Slide:
			return true;
		default:
			return false;
		}
	}
}
[HarmonyPatch]
internal static class ThirdPersonPatches
{
	[HarmonyPrefix]
	[HarmonyPatch(typeof(PlayerLook), "DisableThirdPerson")]
	private static bool DisableThirdPerson_Prefix(PlayerLook __instance)
	{
		if (ThirdPersonController.AllowVanillaDisable)
		{
			return true;
		}
		if ((ThirdPersonController.IsGameplayThirdPersonActive || ThirdPersonController.WantGameplayThirdPerson) && ThirdPersonCamera.GetThirdPersonMode(__instance))
		{
			return false;
		}
		return true;
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(PlayerLook), "EnableThirdPerson")]
	private static void EnableThirdPerson_Postfix(PlayerLook __instance, bool enableThirdPerson, bool disableHUD)
	{
		if (!ThirdPersonController.WantGameplayThirdPerson)
		{
			return;
		}
		Player localPlayer = Player.LocalPlayer;
		if ((Object)(object)localPlayer == (Object)null)
		{
			return;
		}
		try
		{
			if (localPlayer.MovementControlLocks > 0)
			{
				int movementControlLocks = localPlayer.MovementControlLocks;
				localPlayer.MovementControlLocks = movementControlLocks - 1;
			}
			if (localPlayer.GearLocks > 0)
			{
				int movementControlLocks = localPlayer.GearLocks;
				localPlayer.GearLocks = movementControlLocks - 1;
			}
			if (localPlayer.InteractionLocks > 0)
			{
				int movementControlLocks = localPlayer.InteractionLocks;
				localPlayer.InteractionLocks = movementControlLocks - 1;
			}
			localPlayer.LockFiring(false);
		}
		catch
		{
		}
		ThirdPersonController.Instance?.OnConfigReloaded();
	}

	[HarmonyPostfix]
	[HarmonyPatch(typeof(PlayerLook), "LateUpdate")]
	private static void PlayerLook_LateUpdate_Postfix(PlayerLook __instance)
	{
		if (ThirdPersonController.IsGameplayThirdPersonActive)
		{
			Traverse val = Traverse.Create((object)__instance);
			val.Field("minOrbitDistance").SetValue((object)ConfigManager.MinOrbitDistance.Value);
			val.Field("maxOrbitDistance").SetValue((object)ConfigManager.MaxOrbitDistance.Value);
			if (!ConfigManager.ScrollToZoom.Value)
			{
				val.Field("orbitDistance").SetValue((object)ConfigManager.OrbitDistance.Value);
			}
		}
	}
}
internal static class ThirdPersonReflection
{
	private static FieldInfo gunModelField;

	private static FieldInfo magModelField;

	private static FieldInfo mag1ModelField;

	private static FieldInfo isGunHeldField;

	private static FieldInfo thirdPersonDataField;

	private static FieldInfo thirdPersonScaleField;

	private static FieldInfo throwableModelField;

	private static bool fieldsResolved;

	public static void Resolve()
	{
		if (!fieldsResolved)
		{
			fieldsResolved = true;
			Type? typeFromHandle = typeof(Gun);
			gunModelField = AccessTools.Field(typeFromHandle, "gunModel");
			magModelField = AccessTools.Field(typeFromHandle, "magModel");
			mag1ModelField = AccessTools.Field(typeFromHandle, "mag1Model");
			isGunHeldField = AccessTools.Field(typeFromHandle, "isGunHeld");
			thirdPersonDataField = AccessTools.Field(typeFromHandle, "thirdPersonData");
			thirdPersonScaleField = AccessTools.Field(typeFromHandle, "thirdPersonScaleMultiplier");
			throwableModelField = AccessTools.Field(typeof(Throwable), "gunModel");
			if (gunModelField == null)
			{
				ThirdPersonModePlugin.Log.LogWarning((object)"Gun.gunModel field not found — TP gear attachment disabled.");
			}
		}
	}

	public static Transform GetGunModel(Gun gun)
	{
		Resolve();
		try
		{
			object? obj = gunModelField?.GetValue(gun);
			return (Transform)((obj is Transform) ? obj : null);
		}
		catch
		{
			return null;
		}
	}

	public static Transform GetMagModel(Gun gun)
	{
		Resolve();
		try
		{
			object? obj = magModelField?.GetValue(gun);
			return (Transform)((obj is Transform) ? obj : null);
		}
		catch
		{
			return null;
		}
	}

	public static Transform GetMag1Model(Gun gun)
	{
		Resolve();
		try
		{
			object? obj = mag1ModelField?.GetValue(gun);
			return (Transform)((obj is Transform) ? obj : null);
		}
		catch
		{
			return null;
		}
	}

	public static bool IsGunHeld(Gun gun)
	{
		Resolve();
		try
		{
			if (isGunHeldField != null)
			{
				return (bool)isGunHeldField.GetValue(gun);
			}
		}
		catch
		{
		}
		return true;
	}

	public static Transform GetThrowableModel(Throwable throwable)
	{
		Resolve();
		try
		{
			object? obj = throwableModelField?.GetValue(throwable);
			return (Transform)((obj is Transform) ? obj : null);
		}
		catch
		{
			return null;
		}
	}

	public static void GetThirdPersonPose(Gun gun, Character character, out Vector3 pos, out Quaternion rot, out float scaleMul)
	{
		//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_0011: 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_00e7: Unknown result type (might be due to invalid IL or missing references)
		//IL_00ff: Unknown result type (might be due to invalid IL or missing references)
		//IL_0104: Unknown result type (might be due to invalid IL or missing references)
		//IL_013e: Unknown result type (might be due to invalid IL or missing references)
		//IL_0143: Unknown result type (might be due to invalid IL or missing references)
		pos = Vector3.zero;
		rot = Quaternion.identity;
		scaleMul = 1f;
		Resolve();
		try
		{
			if (thirdPersonScaleField != null)
			{
				scaleMul = (float)thirdPersonScaleField.GetValue(gun);
			}
		}
		catch
		{
			scaleMul = 1f;
		}
		try
		{
			if (thirdPersonDataField == null || (Object)(object)character == (Object)null || !(thirdPersonDataField.GetValue(gun) is Array { Length: not 0 } array))
			{
				return;
			}
			int num = character.Index;
			if (num < 0 || num >= array.Length)
			{
				num = 0;
			}
			object value = array.GetValue(num);
			if (value == null)
			{
				return;
			}
			Type type = value.GetType();
			FieldInfo fieldInfo = AccessTools.Field(type, "position");
			FieldInfo fieldInfo2 = AccessTools.Field(type, "rotation");
			if (fieldInfo != null)
			{
				pos = (Vector3)fieldInfo.GetValue(value);
			}
			if (fieldInfo2 != null)
			{
				rot = (Quaternion)fieldInfo2.GetValue(value);
				if (rot.x == 0f && rot.y == 0f && rot.z == 0f && rot.w == 0f)
				{
					rot = Quaternion.identity;
				}
			}
		}
		catch (Exception ex)
		{
			ThirdPersonModePlugin.Log.LogWarning((object)("Could not read gun thirdPersonData: " + ex.Message));
		}
	}

	public static object GetThirdPersonDataEntry(Gun gun, Character character)
	{
		Resolve();
		try
		{
			if (!(thirdPersonDataField?.GetValue(gun) is Array { Length: not 0 } array))
			{
				return null;
			}
			int num = (((Object)(object)character != (Object)null) ? character.Index : 0);
			if (num < 0 || num >= array.Length)
			{
				num = 0;
			}
			return array.GetValue(num);
		}
		catch
		{
			return null;
		}
	}

	public static ThirdPersonGearAnimationSet GetAnimationSet(Gun gun, Character character)
	{
		try
		{
			object thirdPersonDataEntry = GetThirdPersonDataEntry(gun, character);
			if (thirdPersonDataEntry == null)
			{
				return null;
			}
			object? obj = AccessTools.Field(thirdPersonDataEntry.GetType(), "animationSet")?.GetValue(thirdPersonDataEntry);
			return (ThirdPersonGearAnimationSet)((obj is ThirdPersonGearAnimationSet) ? obj : null);
		}
		catch
		{
			return null;
		}
	}
}
namespace ThirdPersonMode
{
	public static class MyPluginInfo
	{
		public const string PLUGIN_GUID = "ThirdPersonMode";

		public const string PLUGIN_NAME = "ThirdPersonMode";

		public const string PLUGIN_VERSION = "1.0.1";
	}
}
namespace System.Runtime.CompilerServices
{
	[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
	internal sealed class IgnoresAccessChecksToAttribute : Attribute
	{
		public IgnoresAccessChecksToAttribute(string assemblyName)
		{
		}
	}
}