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)
{
}
}
}