using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Core.Logging.Interpolation;
using BepInEx.Logging;
using BepInEx.Unity.IL2CPP;
using Fusion;
using HarmonyLib;
using HarmonyLib.Tools;
using Il2CppInterop.Runtime.InteropTypes.Arrays;
using Microsoft.CodeAnalysis;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.Controls;
using UnityEngine.InputSystem.LowLevel;
using UnityEngine.Rendering;
using UnityEngine.SceneManagement;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace AskaFirstPerson
{
[HarmonyPatch("Cinemachine.CinemachineBrain", "LateUpdate")]
public static class CinemachineBrainPatch
{
[HarmonyPrefix]
public static bool Prefix()
{
return !FirstPersonBehaviour.IsFirstPerson;
}
}
public class FirstPersonBehaviour : MonoBehaviour
{
public static bool IsFirstPerson;
internal static string SuppressedToggleButtonName;
internal static bool ChordModifierIsHeld;
internal static bool InSelfRead;
private Camera _cam;
private Transform _headBone;
private Transform _playerRoot;
private Transform _characterRoot;
private Transform _skeletonRoot;
private Transform _leftHand;
private Transform _rightHand;
private Transform _spine;
private Transform _spine1;
private Gamepad _gamepad;
private float _pitch;
private float _yaw;
private float _savedFOV;
private float _savedNearClip;
private Vector3 _smoothVelocity;
private Vector3 _stableBasePos;
private readonly List<Renderer> _shadowOnlyRenderers = new List<Renderer>();
private float _rendererScanTimer;
private const float RendererScanInterval = 0.5f;
private static readonly string[] LowerBodyPrefixes = new string[6] { "ragnar_base_forearm", "ragnar_base_hand", "ragnar_base_pants", "ragnar_base_boots", "ragnar_base_belt", "ragnar_base_glove" };
private void Update()
{
//IL_004d: 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_0096: Expected O, but got Unknown
//IL_0131: Unknown result type (might be due to invalid IL or missing references)
//IL_0136: Unknown result type (might be due to invalid IL or missing references)
//IL_0147: Unknown result type (might be due to invalid IL or missing references)
//IL_0169: Unknown result type (might be due to invalid IL or missing references)
if (!IsInGameplay())
{
if (IsFirstPerson)
{
IsFirstPerson = false;
DisableFirstPerson();
ClearCachedReferences();
FirstPersonPlugin.Log.LogInfo((object)"Left gameplay — first-person auto-disabled");
}
return;
}
try
{
_gamepad = Gamepad.current;
}
catch
{
_gamepad = null;
}
if (Input.GetKeyDown(FirstPersonPlugin.CfgToggleKey.Value) || TryReadGamepadToggle())
{
IsFirstPerson = !IsFirstPerson;
if (IsFirstPerson)
{
EnableFirstPerson();
}
else
{
DisableFirstPerson();
}
ManualLogSource log = FirstPersonPlugin.Log;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(19, 1, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("First-person mode: ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(IsFirstPerson ? "ON" : "OFF");
}
log.LogInfo(val);
}
if (!IsFirstPerson || (Object)(object)_cam == (Object)null || IsGamePaused())
{
return;
}
float value = FirstPersonPlugin.CfgSensitivity.Value;
_yaw += Input.GetAxis("Mouse X") * value;
_pitch -= Input.GetAxis("Mouse Y") * value;
if (_gamepad != null)
{
try
{
Vector2 val2 = ((InputControl<Vector2>)(object)_gamepad.rightStick).ReadValue();
float num = value * 3f;
_yaw += val2.x * num * Time.deltaTime * 60f;
_pitch -= val2.y * num * Time.deltaTime * 60f;
}
catch
{
}
}
_pitch = Mathf.Clamp(_pitch, -85f, 85f);
}
private bool TryReadGamepadToggle()
{
//IL_00a4: Unknown result type (might be due to invalid IL or missing references)
//IL_00aa: Unknown result type (might be due to invalid IL or missing references)
if (_gamepad == null)
{
ResetSuppressionState();
return false;
}
try
{
InSelfRead = true;
try
{
string value = FirstPersonPlugin.CfgGamepadToggleButton.Value;
ButtonControl gamepadButton = GetGamepadButton(_gamepad, value);
string value2 = FirstPersonPlugin.CfgGamepadModifierButton.Value;
bool flag = string.Equals(value2, "None", StringComparison.OrdinalIgnoreCase);
bool flag2 = false;
if (!flag)
{
ButtonControl gamepadButton2 = GetGamepadButton(_gamepad, value2);
flag2 = gamepadButton2 != null && gamepadButton2.isPressed;
}
bool chordModifierIsHeld = ChordModifierIsHeld;
SuppressedToggleButtonName = ((flag || gamepadButton == null) ? null : ((InputControl)gamepadButton).name);
ChordModifierIsHeld = flag2;
if (chordModifierIsHeld != flag2)
{
InputSuppressionPatch.LoggedMagnitudeThisHold = false;
}
if (flag2 && gamepadButton != null)
{
try
{
InputState.Change<float>((InputControl)(object)gamepadButton, 0f, (InputUpdateType)0, default(InputEventPtr));
}
catch
{
}
}
return gamepadButton != null && gamepadButton.wasPressedThisFrame && (flag || flag2);
}
finally
{
InSelfRead = false;
}
}
catch
{
return false;
}
}
private static void ResetSuppressionState()
{
SuppressedToggleButtonName = null;
ChordModifierIsHeld = false;
InputSuppressionPatch.LoggedMagnitudeThisHold = false;
}
private void LateUpdate()
{
//IL_0044: Unknown result type (might be due to invalid IL or missing references)
//IL_0055: Unknown result type (might be due to invalid IL or missing references)
//IL_0066: Unknown result type (might be due to invalid IL or missing references)
//IL_00bf: Unknown result type (might be due to invalid IL or missing references)
//IL_00cf: Unknown result type (might be due to invalid IL or missing references)
//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
//IL_00d6: Unknown result type (might be due to invalid IL or missing references)
//IL_00e5: Unknown result type (might be due to invalid IL or missing references)
//IL_00ea: Unknown result type (might be due to invalid IL or missing references)
//IL_00ef: Unknown result type (might be due to invalid IL or missing references)
//IL_00fb: Unknown result type (might be due to invalid IL or missing references)
//IL_0100: Unknown result type (might be due to invalid IL or missing references)
//IL_0105: Unknown result type (might be due to invalid IL or missing references)
//IL_011d: Unknown result type (might be due to invalid IL or missing references)
//IL_0122: Unknown result type (might be due to invalid IL or missing references)
//IL_013a: Unknown result type (might be due to invalid IL or missing references)
//IL_0160: Unknown result type (might be due to invalid IL or missing references)
//IL_0192: Unknown result type (might be due to invalid IL or missing references)
//IL_01ca: Unknown result type (might be due to invalid IL or missing references)
//IL_01db: Unknown result type (might be due to invalid IL or missing references)
//IL_01e0: Unknown result type (might be due to invalid IL or missing references)
//IL_0204: Unknown result type (might be due to invalid IL or missing references)
//IL_0215: Unknown result type (might be due to invalid IL or missing references)
//IL_021a: Unknown result type (might be due to invalid IL or missing references)
if (!IsFirstPerson)
{
return;
}
if ((Object)(object)_cam == (Object)null || (Object)(object)_headBone == (Object)null)
{
TryFindReferences();
}
else
{
if (IsGamePaused())
{
return;
}
float value = FirstPersonPlugin.CfgMotionDampening.Value;
float x = _headBone.position.x;
float z = _headBone.position.z;
float y = _headBone.position.y;
float num = Mathf.Lerp(30f, 6f, value);
_stableBasePos.y = Mathf.Lerp(_stableBasePos.y, y, Time.deltaTime * num);
float num2 = Mathf.Lerp(y, _stableBasePos.y, value);
Vector3 val = new Vector3(x, num2, z);
Vector3 forward = ((Component)_cam).transform.forward;
Vector3 val2 = val + Vector3.up * FirstPersonPlugin.CfgVerticalOffset.Value + forward * FirstPersonPlugin.CfgForwardOffset.Value;
((Component)_cam).transform.position = Vector3.SmoothDamp(((Component)_cam).transform.position, val2, ref _smoothVelocity, 1f / FirstPersonPlugin.CfgSmoothSpeed.Value);
((Component)_cam).transform.rotation = Quaternion.Euler(_pitch, _yaw, 0f);
if ((Object)(object)_spine != (Object)null && (Object)(object)_playerRoot != (Object)null)
{
float num3 = Mathf.Clamp(Mathf.DeltaAngle(_playerRoot.eulerAngles.y, _yaw), -80f, 80f) * 0.5f;
_spine.localRotation *= Quaternion.Euler(0f, num3, 0f);
if ((Object)(object)_spine1 != (Object)null)
{
_spine1.localRotation *= Quaternion.Euler(0f, num3, 0f);
}
}
_rendererScanTimer += Time.deltaTime;
if (_rendererScanTimer >= 0.5f)
{
_rendererScanTimer = 0f;
HidePlayerRenderers(rebuildCache: false);
}
}
}
private void EnableFirstPerson()
{
//IL_0081: Unknown result type (might be due to invalid IL or missing references)
//IL_0086: Unknown result type (might be due to invalid IL or missing references)
//IL_0088: Unknown result type (might be due to invalid IL or missing references)
//IL_0094: Unknown result type (might be due to invalid IL or missing references)
//IL_00a9: Unknown result type (might be due to invalid IL or missing references)
//IL_00a1: Unknown result type (might be due to invalid IL or missing references)
//IL_00e1: Unknown result type (might be due to invalid IL or missing references)
//IL_00d4: Unknown result type (might be due to invalid IL or missing references)
//IL_00e6: Unknown result type (might be due to invalid IL or missing references)
//IL_0113: Unknown result type (might be due to invalid IL or missing references)
//IL_0124: Unknown result type (might be due to invalid IL or missing references)
//IL_0139: Unknown result type (might be due to invalid IL or missing references)
//IL_013f: Expected O, but got Unknown
//IL_01af: Unknown result type (might be due to invalid IL or missing references)
TryFindReferences();
if ((Object)(object)_cam == (Object)null)
{
FirstPersonPlugin.Log.LogWarning((object)"Cannot enable first-person — main camera not found. Ensure you are in-game (not a loading screen).");
IsFirstPerson = false;
return;
}
_savedFOV = _cam.fieldOfView;
_savedNearClip = _cam.nearClipPlane;
_cam.fieldOfView = FirstPersonPlugin.CfgFOV.Value;
_cam.nearClipPlane = FirstPersonPlugin.CfgNearClip.Value;
Vector3 eulerAngles = ((Component)_cam).transform.eulerAngles;
_yaw = eulerAngles.y;
_pitch = ((eulerAngles.x > 180f) ? (eulerAngles.x - 360f) : eulerAngles.x);
_stableBasePos = (((Object)(object)_headBone != (Object)null) ? _headBone.position : ((Component)_cam).transform.position);
if ((Object)(object)_headBone != (Object)null && (Object)(object)_playerRoot != (Object)null)
{
float y = _playerRoot.position.y;
float y2 = _headBone.position.y;
ManualLogSource log = FirstPersonPlugin.Log;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(73, 5, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Diagnostics] PlayerRoot Y=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(y, "F2");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", HeadBone Y=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(y2, "F2");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", ");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("HeadHeight=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(y2 - y, "F2");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", Camera Y=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<float>(((Component)_cam).transform.position.y, "F2");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(", ");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Player=");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(((Object)_playerRoot).name);
}
log.LogInfo(val);
}
HidePlayerRenderers(rebuildCache: true);
}
private void DisableFirstPerson()
{
if ((Object)(object)_cam != (Object)null)
{
_cam.fieldOfView = _savedFOV;
_cam.nearClipPlane = _savedNearClip;
}
ShowPlayerModel();
}
private void TryFindReferences()
{
//IL_0104: Unknown result type (might be due to invalid IL or missing references)
//IL_010b: Expected O, but got Unknown
//IL_017c: Unknown result type (might be due to invalid IL or missing references)
//IL_0183: Expected O, but got Unknown
//IL_00a3: Unknown result type (might be due to invalid IL or missing references)
//IL_00aa: Expected O, but got Unknown
if ((Object)(object)_cam == (Object)null)
{
_cam = Camera.main;
}
bool flag = default(bool);
if ((Object)(object)_playerRoot == (Object)null)
{
GameObject val = FindLocalPlayer();
if ((Object)(object)val == (Object)null)
{
return;
}
_playerRoot = val.transform;
Transform val2 = _playerRoot.Find("GeometryParent");
BepInExInfoLogInterpolatedStringHandler val4;
if ((Object)(object)val2 != (Object)null)
{
for (int i = 0; i < val2.childCount; i++)
{
Transform child = val2.GetChild(i);
Transform val3 = child.Find("master");
if ((Object)(object)val3 != (Object)null)
{
_characterRoot = child;
_skeletonRoot = val3;
ManualLogSource log = FirstPersonPlugin.Log;
val4 = new BepInExInfoLogInterpolatedStringHandler(37, 2, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("Character geometry root: ");
((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(((Object)child).name);
((BepInExLogInterpolatedStringHandler)val4).AppendLiteral(", skeleton: ");
((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(((Object)val3).name);
}
log.LogInfo(val4);
break;
}
}
}
ManualLogSource log2 = FirstPersonPlugin.Log;
val4 = new BepInExInfoLogInterpolatedStringHandler(14, 1, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("Local player: ");
((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(((Object)val).name);
}
log2.LogInfo(val4);
}
if (!((Object)(object)_skeletonRoot != (Object)null))
{
return;
}
if ((Object)(object)_headBone == (Object)null)
{
_headBone = _skeletonRoot.Find("Bip001/Bip001 Pelvis/Bip001 Spine/Bip001 Spine1/Bip001 Neck/Bip001 Head");
if ((Object)(object)_headBone != (Object)null)
{
ManualLogSource log3 = FirstPersonPlugin.Log;
BepInExInfoLogInterpolatedStringHandler val4 = new BepInExInfoLogInterpolatedStringHandler(20, 1, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val4).AppendLiteral("Head bone resolved: ");
((BepInExLogInterpolatedStringHandler)val4).AppendFormatted<string>(((Object)_headBone).name);
}
log3.LogInfo(val4);
}
else
{
FirstPersonPlugin.Log.LogWarning((object)"Could not find head bone via explicit path. The skeleton hierarchy may have changed.");
}
}
if ((Object)(object)_leftHand == (Object)null)
{
_leftHand = _skeletonRoot.Find("Bip001/Bip001 Pelvis/Bip001 Spine/Bip001 Spine1/Bip001 L Clavicle/Bip001 L UpperArm/Bip001 L Forearm/Bip001 L Hand");
}
if ((Object)(object)_rightHand == (Object)null)
{
_rightHand = _skeletonRoot.Find("Bip001/Bip001 Pelvis/Bip001 Spine/Bip001 Spine1/Bip001 R Clavicle/Bip001 R UpperArm/Bip001 R Forearm/Bip001 R Hand");
}
if ((Object)(object)_spine == (Object)null)
{
_spine = _skeletonRoot.Find("Bip001/Bip001 Pelvis/Bip001 Spine");
}
if ((Object)(object)_spine1 == (Object)null)
{
_spine1 = _skeletonRoot.Find("Bip001/Bip001 Pelvis/Bip001 Spine/Bip001 Spine1");
}
}
private static bool IsLowerBody(string name)
{
string[] lowerBodyPrefixes = LowerBodyPrefixes;
foreach (string value in lowerBodyPrefixes)
{
if (name.StartsWith(value, StringComparison.OrdinalIgnoreCase))
{
return true;
}
}
return false;
}
private static bool IsChildOf(Transform t, Transform ancestor)
{
if ((Object)(object)ancestor == (Object)null)
{
return false;
}
Transform parent = t.parent;
while ((Object)(object)parent != (Object)null)
{
if ((Object)(object)parent == (Object)(object)ancestor)
{
return true;
}
parent = parent.parent;
}
return false;
}
private void HidePlayerRenderers(bool rebuildCache)
{
//IL_005a: Unknown result type (might be due to invalid IL or missing references)
//IL_0060: Invalid comparison between Unknown and I4
if ((Object)(object)_playerRoot == (Object)null)
{
return;
}
if (rebuildCache)
{
_shadowOnlyRenderers.Clear();
}
bool value = FirstPersonPlugin.CfgShowBody.Value;
foreach (Renderer componentsInChild in ((Component)_playerRoot).GetComponentsInChildren<Renderer>())
{
if (!((Object)(object)componentsInChild == (Object)null) && componentsInChild.enabled && (int)componentsInChild.shadowCastingMode != 3 && !IsChildOf(((Component)componentsInChild).transform, _leftHand) && !IsChildOf(((Component)componentsInChild).transform, _rightHand) && (!value || !((Object)(object)_characterRoot != (Object)null) || !((Object)(object)((Component)componentsInChild).transform.parent == (Object)(object)_characterRoot) || !IsLowerBody(((Object)((Component)componentsInChild).gameObject).name)))
{
componentsInChild.shadowCastingMode = (ShadowCastingMode)3;
_shadowOnlyRenderers.Add(componentsInChild);
}
}
if (rebuildCache)
{
FirstPersonPlugin.Log.LogInfo((object)($"Set {_shadowOnlyRenderers.Count} renderers to shadow-only " + "(body + equipment + accessories; hands kept visible)"));
}
}
private void ShowPlayerModel()
{
foreach (Renderer shadowOnlyRenderer in _shadowOnlyRenderers)
{
if ((Object)(object)shadowOnlyRenderer != (Object)null)
{
shadowOnlyRenderer.shadowCastingMode = (ShadowCastingMode)1;
}
}
_shadowOnlyRenderers.Clear();
}
private GameObject FindLocalPlayer()
{
//IL_00c8: Unknown result type (might be due to invalid IL or missing references)
//IL_00d8: Unknown result type (might be due to invalid IL or missing references)
Il2CppReferenceArray<GameObject> val = GameObject.FindGameObjectsWithTag("Player");
if (val == null || ((Il2CppArrayBase<GameObject>)(object)val).Length == 0)
{
return null;
}
if (((Il2CppArrayBase<GameObject>)(object)val).Length == 1)
{
return ((Il2CppArrayBase<GameObject>)(object)val)[0];
}
try
{
foreach (GameObject item in (Il2CppArrayBase<GameObject>)(object)val)
{
NetworkObject component = item.GetComponent<NetworkObject>();
if ((Object)(object)component != (Object)null && component.HasInputAuthority)
{
return item;
}
}
}
catch
{
}
if ((Object)(object)_cam == (Object)null)
{
_cam = Camera.main;
}
if ((Object)(object)_cam == (Object)null)
{
return ((Il2CppArrayBase<GameObject>)(object)val)[0];
}
GameObject result = null;
float num = float.MaxValue;
foreach (GameObject item2 in (Il2CppArrayBase<GameObject>)(object)val)
{
float num2 = Vector3.Distance(item2.transform.position, ((Component)_cam).transform.position);
if (num2 < num)
{
num = num2;
result = item2;
}
}
return result;
}
private static bool IsInGameplay()
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
Scene activeScene = SceneManager.GetActiveScene();
return ((Scene)(ref activeScene)).name == "StreamingWorld";
}
private void ClearCachedReferences()
{
_cam = null;
_headBone = null;
_playerRoot = null;
_characterRoot = null;
_skeletonRoot = null;
_leftHand = null;
_rightHand = null;
_spine = null;
_spine1 = null;
ResetSuppressionState();
}
private static bool IsGamePaused()
{
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_001b: Invalid comparison between Unknown and I4
if (Time.timeScale == 0f)
{
return true;
}
if (Cursor.visible || (int)Cursor.lockState != 1)
{
return true;
}
return false;
}
private static ButtonControl GetGamepadButton(Gamepad pad, string name)
{
switch (name)
{
case "North":
case "ButtonNorth":
return pad.buttonNorth;
case "South":
case "ButtonSouth":
return pad.buttonSouth;
case "East":
case "ButtonEast":
return pad.buttonEast;
case "West":
case "ButtonWest":
return pad.buttonWest;
case "LeftBumper":
case "LeftShoulder":
return pad.leftShoulder;
case "RightBumper":
case "RightShoulder":
return pad.rightShoulder;
case "LeftTrigger":
return pad.leftTrigger;
case "RightTrigger":
return pad.rightTrigger;
case "LeftStickButton":
return pad.leftStickButton;
case "RightStickButton":
return pad.rightStickButton;
case "Start":
case "StartButton":
return pad.startButton;
case "SelectButton":
case "Select":
return pad.selectButton;
case "DpadUp":
return pad.dpad.up;
case "DpadDown":
return pad.dpad.down;
case "DpadLeft":
return pad.dpad.left;
case "DpadRight":
return pad.dpad.right;
default:
return null;
}
}
}
[BepInPlugin("com.community.askafirstperson", "Aska First Person Camera", "1.0.2")]
public class FirstPersonPlugin : BasePlugin
{
public const string PluginGuid = "com.community.askafirstperson";
public const string PluginName = "Aska First Person Camera";
public const string PluginVersion = "1.0.2";
internal static ManualLogSource Log;
public static ConfigEntry<float> CfgFOV;
public static ConfigEntry<float> CfgSensitivity;
public static ConfigEntry<float> CfgNearClip;
public static ConfigEntry<float> CfgVerticalOffset;
public static ConfigEntry<float> CfgForwardOffset;
public static ConfigEntry<float> CfgSmoothSpeed;
public static ConfigEntry<float> CfgMotionDampening;
public static ConfigEntry<bool> CfgShowBody;
public static ConfigEntry<KeyCode> CfgToggleKey;
public static ConfigEntry<string> CfgGamepadToggleButton;
public static ConfigEntry<string> CfgGamepadModifierButton;
public override void Load()
{
//IL_0039: Unknown result type (might be due to invalid IL or missing references)
//IL_0043: Expected O, but got Unknown
//IL_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_0080: Expected O, but got Unknown
//IL_011f: Unknown result type (might be due to invalid IL or missing references)
//IL_0129: Expected O, but got Unknown
//IL_015c: Unknown result type (might be due to invalid IL or missing references)
//IL_0166: Expected O, but got Unknown
//IL_01fc: Unknown result type (might be due to invalid IL or missing references)
//IL_0202: Expected O, but got Unknown
//IL_0208: Unknown result type (might be due to invalid IL or missing references)
//IL_020d: Unknown result type (might be due to invalid IL or missing references)
//IL_020e: Unknown result type (might be due to invalid IL or missing references)
//IL_0211: Unknown result type (might be due to invalid IL or missing references)
//IL_027a: Unknown result type (might be due to invalid IL or missing references)
//IL_028b: Unknown result type (might be due to invalid IL or missing references)
//IL_0292: Expected O, but got Unknown
//IL_0310: Unknown result type (might be due to invalid IL or missing references)
//IL_0317: Expected O, but got Unknown
//IL_0352: Unknown result type (might be due to invalid IL or missing references)
Log = ((BasePlugin)this).Log;
CfgFOV = ((BasePlugin)this).Config.Bind<float>("Camera", "FOV", 80f, new ConfigDescription("First-person field of view", (AcceptableValueBase)(object)new AcceptableValueRange<float>(60f, 120f), Array.Empty<object>()));
CfgSensitivity = ((BasePlugin)this).Config.Bind<float>("Camera", "MouseSensitivity", 2f, new ConfigDescription("Mouse look sensitivity", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>()));
CfgNearClip = ((BasePlugin)this).Config.Bind<float>("Camera", "NearClipPlane", 0.05f, "Near clip plane distance — lower values prevent seeing inside geometry");
CfgVerticalOffset = ((BasePlugin)this).Config.Bind<float>("Camera", "VerticalOffset", 0.1f, "Vertical offset above the head bone (metres)");
CfgForwardOffset = ((BasePlugin)this).Config.Bind<float>("Camera", "ForwardOffset", 0.12f, "Forward offset from the head bone (metres)");
CfgSmoothSpeed = ((BasePlugin)this).Config.Bind<float>("Camera", "SmoothSpeed", 15f, new ConfigDescription("Position smoothing factor — higher = less smoothing", (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 100f), Array.Empty<object>()));
CfgMotionDampening = ((BasePlugin)this).Config.Bind<float>("Camera", "MotionDampening", 0.4f, new ConfigDescription("Reduces vertical camera shake from combat, rolls, and head bob. Only affects up/down movement — lateral tracking stays responsive. 0 = no dampening (raw head tracking), 1 = maximum dampening. Recommended 0.3-0.5.", (AcceptableValueBase)(object)new AcceptableValueRange<float>(0f, 1f), Array.Empty<object>()));
CfgShowBody = ((BasePlugin)this).Config.Bind<bool>("Visibility", "ShowLowerBody", false, "Show lower body (forearms, hands, legs) in first-person. Off = cleaner look (shadow-only body, weapons visible). On = see arms/legs but may have visual artifacts (mesh seams, backface culling).");
CfgToggleKey = ((BasePlugin)this).Config.Bind<KeyCode>("Controls", "ToggleKey", (KeyCode)286, "Key to toggle between first-person and third-person camera");
CfgGamepadToggleButton = ((BasePlugin)this).Config.Bind<string>("Controls", "GamepadToggleButton", "RightStickButton", "Gamepad button to toggle first-person mode. Valid: North, South, East, West, LeftShoulder, RightShoulder, LeftTrigger, RightTrigger, LeftStickButton, RightStickButton, Start, Select, DpadUp, DpadDown, DpadLeft, DpadRight");
CfgGamepadModifierButton = ((BasePlugin)this).Config.Bind<string>("Controls", "GamepadModifierButton", "LeftShoulder", "Modifier button that must be held while pressing the toggle button. Set to \"None\" to disable the modifier (bare button press). Default: LeftShoulder (LB + R3 chord avoids conflicting with game actions). Valid: None, North, South, East, West, LeftShoulder, RightShoulder, LeftTrigger, RightTrigger, LeftStickButton, RightStickButton, Start, Select, DpadUp, DpadDown, DpadLeft, DpadRight");
Harmony harmony = new Harmony("com.community.askafirstperson");
List<string> list = new List<string>();
LogChannel channelFilter = Logger.ChannelFilter;
Logger.ChannelFilter = (LogChannel)(channelFilter & -9);
try
{
if (TryPatchClass(harmony, typeof(CinemachineBrainPatch)))
{
list.Add("Cinemachine");
}
(string, Type)[] patchClasses = InputSuppressionPatch.PatchClasses;
for (int i = 0; i < patchClasses.Length; i++)
{
var (item, type) = patchClasses[i];
if (TryPatchClass(harmony, type))
{
list.Add(item);
}
}
}
finally
{
Logger.ChannelFilter = channelFilter;
}
ManualLogSource log = Log;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(26, 1, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Harmony patches active: [");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(string.Join(", ", list));
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("]");
}
log.LogInfo(val);
((BasePlugin)this).AddComponent<FirstPersonBehaviour>();
string value = CfgGamepadModifierButton.Value;
string value2 = CfgGamepadToggleButton.Value;
string text = (string.Equals(value, "None", StringComparison.OrdinalIgnoreCase) ? value2 : (value + "+" + value2));
ManualLogSource log2 = Log;
val = new BepInExInfoLogInterpolatedStringHandler(44, 4, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("Aska First Person Camera");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" v");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>("1.0.2");
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(" loaded — press [");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<KeyCode>(CfgToggleKey.Value);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] or gamepad [");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(text);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] to toggle");
}
log2.LogInfo(val);
}
private static bool TryPatchClass(Harmony harmony, Type type)
{
//IL_001c: Unknown result type (might be due to invalid IL or missing references)
//IL_0022: Expected O, but got Unknown
try
{
harmony.CreateClassProcessor(type).Patch();
return true;
}
catch (Exception ex)
{
ManualLogSource log = Log;
bool flag = default(bool);
BepInExWarningLogInterpolatedStringHandler val = new BepInExWarningLogInterpolatedStringHandler(25, 2, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("Skipping Harmony patch ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(type.Name);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral(": ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(ex.Message);
}
log.LogWarning(val);
return false;
}
}
}
internal static class InputSuppressionPatch
{
[HarmonyPatch("UnityEngine.InputSystem.InputControl", "EvaluateMagnitude")]
[HarmonyPatch(new Type[] { })]
public static class EvaluateMagnitudePatch
{
[HarmonyPostfix]
public static void Postfix(InputControl __instance, ref float __result)
{
if (!(__result <= 0f) && ShouldSuppressControl(__instance))
{
__result = 0f;
LogOnce(ref LoggedMagnitudeThisHold, "Magnitude", "InputControl.EvaluateMagnitude(" + __instance.name + ") -> 0");
}
}
}
internal static bool LoggedMagnitudeThisHold;
internal static readonly (string Layer, Type PatchClass)[] PatchClasses = new(string, Type)[1] { ("Magnitude", typeof(EvaluateMagnitudePatch)) };
private static bool ShouldSuppressControl(InputControl instance)
{
if (instance == null)
{
return false;
}
if (FirstPersonBehaviour.InSelfRead)
{
return false;
}
if (!FirstPersonBehaviour.ChordModifierIsHeld)
{
return false;
}
string suppressedToggleButtonName = FirstPersonBehaviour.SuppressedToggleButtonName;
if (string.IsNullOrEmpty(suppressedToggleButtonName))
{
return false;
}
try
{
return string.Equals(instance.name, suppressedToggleButtonName, StringComparison.Ordinal);
}
catch
{
return false;
}
}
private static void LogOnce(ref bool latch, string layerTag, string detail)
{
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0018: Expected O, but got Unknown
if (latch)
{
return;
}
latch = true;
try
{
ManualLogSource log = FirstPersonPlugin.Log;
bool flag = default(bool);
BepInExInfoLogInterpolatedStringHandler val = new BepInExInfoLogInterpolatedStringHandler(12, 2, ref flag);
if (flag)
{
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("[Suppress.");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(layerTag);
((BepInExLogInterpolatedStringHandler)val).AppendLiteral("] ");
((BepInExLogInterpolatedStringHandler)val).AppendFormatted<string>(detail);
}
log.LogInfo(val);
}
catch
{
}
}
}
}