using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Text; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using FishNet.Object; using HarmonyLib; using Microsoft.CodeAnalysis; using UnityEngine; using UnityEngine.Rendering; using UnityEngine.SceneManagement; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")] [assembly: AssemblyCompany("HowToFish.TrueFirstPerson")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("HowToFish.TrueFirstPerson")] [assembly: AssemblyTitle("How to Fish - True First Person")] [assembly: AssemblyVersion("1.0.0.0")] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace HowToFish.TrueFirstPerson { [DefaultExecutionOrder(-10000)] internal sealed class BodyDriver : MonoBehaviour { private readonly BodyReveal _reveal = new BodyReveal(); private readonly HeadHider _head = new HeadHider(); private readonly BodyFollower _follower = new BodyFollower(); private readonly MotionBridge _motion = new MotionBridge(); private readonly JumpPose _jumpPose = new JumpPose(); private readonly DeathHider _death = new DeathHider(); private readonly IkRetimer _ikRetimer = new IkRetimer(); private Player _tracked; private BodyRig _rig; private bool _applied; private bool _lastEnabled; internal static BodyDriver Active { get; private set; } internal static bool LegsPrefixFired { get; private set; } internal BodyRig Rig => _rig; internal bool IsApplied => _applied; internal float? DrivenBoatHeading { get { if (!_applied) { return null; } return BodyFollower.BoatHeading(_rig); } } private void Awake() { Active = this; _lastEnabled = Plugin.Cfg.Enabled.Value; } private void OnDestroy() { Revert(); if ((Object)(object)Active == (Object)(object)this) { Active = null; } } private void LateUpdate() { ModConfig cfg = Plugin.Cfg; if (cfg.Enabled.Value != _lastEnabled) { _lastEnabled = cfg.Enabled.Value; if (!_lastEnabled) { Revert(); } } if (!cfg.Enabled.Value) { return; } Player localPlayer = Player.LocalPlayer; if ((Object)(object)localPlayer != (Object)(object)_tracked) { Revert(); _tracked = localPlayer; } if (!((Object)(object)localPlayer == (Object)null)) { if (!_applied) { TryApply(localPlayer, cfg); return; } _death.Tick(_rig); _jumpPose.Tick(Plugin.Cfg); _reveal.KeepDriversEnabled(_rig); _follower.Tick(cfg, _rig); } } private void TryApply(Player local, ModConfig cfg) { _rig = BodyRig.Capture(local); if (_rig != null && _rig.IsValid && (!cfg.ShowBody.Value || !((Object)(object)_rig.BodyRenderer == (Object)null))) { if (cfg.LogRigOnSpawn.Value) { RigDiagnostics.DumpLocalPlayer(); } _reveal.Apply(_rig, cfg); _follower.Apply(_rig); _head.Apply(_rig, cfg); _jumpPose.Apply(_rig, cfg); _death.Apply(_rig); _ikRetimer.Apply(_rig, cfg); _applied = true; } } internal void Revert() { if (!_applied) { _rig = null; return; } _ikRetimer.Restore(); _death.Restore(); _jumpPose.Restore(); _head.Restore(); _motion.Reset(); _follower.Restore(); _reveal.Restore(); _rig = null; _applied = false; } internal void SyncAfterCamera(PlayerCamera camera) { //IL_003b: 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_004b: Unknown result type (might be due to invalid IL or missing references) if (_applied && !((Object)(object)camera == (Object)null)) { _follower.Tick(Plugin.Cfg, _rig); Transform camTransform = camera.CamTransform; if (!((Object)(object)camTransform == (Object)null)) { camTransform.position += _follower.CameraOffset; } } } internal void SolveIkAfterBody() { if (_applied) { _follower.Tick(Plugin.Cfg, _rig); _ikRetimer.ResolveAll(); } } internal void SyncBeforeIk() { if (_applied) { _follower.Tick(Plugin.Cfg, _rig); } } internal void SyncBody() { LegsPrefixFired = true; if (_applied) { _follower.Tick(Plugin.Cfg, _rig); _motion.Tick(_rig, _follower.CharacterRoot); } } internal void Reapply() { Revert(); _tracked = null; } } internal sealed class BodyFollower { private Transform _characterRoot; private Transform _localPlayer; private Transform _camObject; private Transform _camProxy; private readonly Dictionary _savedColliders = new Dictionary(); private readonly List _neutralisedBodies = new List(); private readonly Dictionary _savedKinematic = new Dictionary(); private Vector3 _origLocalPos; private Quaternion _origLocalRot; private bool _applied; private bool _loggedOnce; private float _bodyYaw; private bool _turning; private int _lastYawFrame = -1; private Vector3 _cameraOffset; private Transform _originalParent; private bool _ridingBoat; private float _boatBlend; public Transform CharacterRoot => _characterRoot; public Vector3 CameraOffset => _cameraOffset; public Vector3 BodyForward { get { //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_000e: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)_characterRoot != (Object)null)) { return Vector3.forward; } return _characterRoot.forward; } } public void Apply(BodyRig rig) { //IL_00d1: 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_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) if (!_applied && rig != null && !((Object)(object)rig.Player == (Object)null)) { _characterRoot = ResolveCharacterRoot(rig.Player); _localPlayer = ResolveLocalPlayer(rig.Player); _camObject = rig.Player.CamObject; if ((Object)(object)_characterRoot == (Object)null || (Object)(object)_localPlayer == (Object)null) { Plugin.Log.LogWarning((object)($"[tfp] follower not engaged - characterRoot={(Object)(object)_characterRoot != (Object)null} " + $"localPlayer={(Object)(object)_localPlayer != (Object)null}")); return; } _camProxy = Refl.Get(rig.Player.Other, "_camProxy"); _origLocalPos = _characterRoot.localPosition; _origLocalRot = _characterRoot.localRotation; NeutralisePhysics(); _applied = true; Plugin.Log.LogInfo((object)("[tfp] follower engaged - " + ((Object)_characterRoot).name + " follows " + ((Object)_localPlayer).name)); } } public void Tick(ModConfig cfg, BodyRig rig) { //IL_00aa: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) if (_applied && !((Object)(object)_characterRoot == (Object)null) && !((Object)(object)_localPlayer == (Object)null)) { Boat val = (cfg.BoatAdjustments.Value ? DrivenBoat(rig) : null); if ((Object)(object)val == (Object)null || !TickBoat(cfg, rig, val)) { TickGround(cfg, rig); } if ((Object)(object)_camProxy != (Object)null && (Object)(object)_camObject != (Object)null) { _camProxy.rotation = _camObject.rotation; } if (!_loggedOnce) { _loggedOnce = true; Plugin.Log.LogInfo((object)($"[tfp] first follow frame - body at {_characterRoot.position}, camera at " + $"{(((Object)(object)_camObject != (Object)null) ? _camObject.position : Vector3.zero)}")); } } } private bool TickBoat(ModConfig cfg, BodyRig rig, Boat boat) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0041: 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_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005b: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_006c: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0081: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_0087: 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) //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_00ba: Unknown result type (might be due to invalid IL or missing references) //IL_00d1: 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_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_00ea: Unknown result type (might be due to invalid IL or missing references) Transform visualBoat = boat.VisualBoat; Transform driverPos = boat.DriverPos; if ((Object)(object)visualBoat == (Object)null || (Object)(object)driverPos == (Object)null) { return false; } if (!_ridingBoat) { BeginBoatRide(visualBoat); } Vector3 val = LocalFacing(boat, visualBoat, driverPos); _characterRoot.localPosition = driverPos.localPosition + Vector3.up * cfg.BodyOffsetY.Value + val * cfg.BoatOffsetForward.Value; _characterRoot.localRotation = Quaternion.LookRotation(val, Vector3.up); _boatBlend = Mathf.MoveTowards(_boatBlend, 1f, 4f * Time.deltaTime); _cameraOffset = visualBoat.rotation * val * (cfg.BoatCameraForward.Value * _boatBlend); Quaternion rotation = _characterRoot.rotation; _bodyYaw = ((Quaternion)(ref rotation)).eulerAngles.y; return true; } private static Vector3 LocalFacing(Boat boat, Transform visual, Transform driver) { //IL_004f: Unknown result type (might be due to invalid IL or missing references) //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_0022: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0049: Unknown result type (might be due to invalid IL or missing references) Transform val = Refl.Get(boat, "_steeringWheel"); if ((Object)(object)val != (Object)null) { Vector3 val2 = visual.InverseTransformPoint(val.position) - driver.localPosition; val2.y = 0f; if (((Vector3)(ref val2)).sqrMagnitude > 0.0004f) { return ((Vector3)(ref val2)).normalized; } } return Vector3.forward; } private void BeginBoatRide(Transform visual) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) _ridingBoat = true; _boatBlend = 0f; _originalParent = _characterRoot.parent; _characterRoot.SetParent(visual, false); Quaternion rotation = _characterRoot.rotation; _bodyYaw = ((Quaternion)(ref rotation)).eulerAngles.y; Plugin.Log.LogInfo((object)("[tfp] boat ride begun - parented to " + ((Object)visual).name)); } private void EndBoatRide() { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) if (_ridingBoat) { if ((Object)(object)_characterRoot != (Object)null) { _characterRoot.SetParent(_originalParent, true); } _ridingBoat = false; _boatBlend = 0f; _cameraOffset = Vector3.zero; _originalParent = null; Plugin.Log.LogInfo((object)"[tfp] boat ride ended"); } } private void TickGround(ModConfig cfg, BodyRig rig) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //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) //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007e: 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_0080: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: 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_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_0048: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0050: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_005c: Unknown result type (might be due to invalid IL or missing references) if (_ridingBoat) { EndBoatRide(); } Vector3 val = _localPlayer.position + new Vector3(0f, cfg.BodyOffsetY.Value, 0f); if (cfg.FollowCameraHorizontally.Value) { Vector3 val2 = CameraAnchor(rig); ((Vector3)(ref val))..ctor(val2.x, val.y, val2.z); } Quaternion val3 = Quaternion.Euler(0f, ResolveYaw(cfg, rig), 0f); val += val3 * new Vector3(0f, 0f, cfg.BodyOffsetForward.Value); _characterRoot.SetPositionAndRotation(val, val3); } public static Transform DrivenWheel(BodyRig rig) { Boat val = DrivenBoat(rig); if (!((Object)(object)val == (Object)null)) { return Refl.Get(val, "_steeringWheel"); } return null; } public static Transform DrivenBoatTransform(BodyRig rig) { Boat val = DrivenBoat(rig); if (!((Object)(object)val == (Object)null)) { return val.VisualBoat; } return null; } public static float? BoatHeading(BodyRig rig) { //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0058: Unknown result type (might be due to invalid IL or missing references) //IL_007f: 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) //IL_008e: 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) Boat val = DrivenBoat(rig); if ((Object)(object)val == (Object)null || (Object)(object)val.VisualBoat == (Object)null || (Object)(object)val.DriverPos == (Object)null) { return null; } Vector3 val2 = val.VisualBoat.rotation * LocalFacing(val, val.VisualBoat, val.DriverPos); val2.y = 0f; if (((Vector3)(ref val2)).sqrMagnitude < 0.0001f) { return null; } Quaternion val3 = Quaternion.LookRotation(((Vector3)(ref val2)).normalized, Vector3.up); return ((Quaternion)(ref val3)).eulerAngles.y; } private static Boat DrivenBoat(BodyRig rig) { PlayerHands val = ((rig != null && (Object)(object)rig.Player != (Object)null) ? rig.Player.Hands : null); if ((Object)(object)val == (Object)null) { return null; } return Refl.InvokeGet(val, "get__drivenBoat"); } private Vector3 CameraAnchor(BodyRig rig) { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_002b: Unknown result type (might be due to invalid IL or missing references) PlayerCamera val = rig?.Camera; if ((Object)(object)val != (Object)null) { Vector3 camPosNoEffects = val.CamPosNoEffects; if (((Vector3)(ref camPosNoEffects)).sqrMagnitude > 0.0001f) { return camPosNoEffects; } } if (!((Object)(object)_camObject != (Object)null)) { return _localPlayer.position; } return _camObject.position; } private float ResolveYaw(ModConfig cfg, BodyRig rig) { if (!TryGetCameraYaw(rig, out var yaw)) { return _bodyYaw; } if (!cfg.BodyTurnsWithCamera.Value) { return yaw; } if (_lastYawFrame == Time.frameCount) { return _bodyYaw; } _lastYawFrame = Time.frameCount; float num = cfg.BodyYawSpeed.Value * Time.deltaTime; if (IsMoving(rig, cfg)) { _turning = false; _bodyYaw = Mathf.MoveTowardsAngle(_bodyYaw, yaw, num); return _bodyYaw; } if (Mathf.Abs(Mathf.DeltaAngle(_bodyYaw, yaw)) > cfg.BodyYawDeadzone.Value) { _turning = true; } if (_turning) { _bodyYaw = Mathf.MoveTowardsAngle(_bodyYaw, yaw, num); if (Mathf.Abs(Mathf.DeltaAngle(_bodyYaw, yaw)) < 1f) { _turning = false; } } return _bodyYaw; } private bool TryGetCameraYaw(BodyRig rig, out float yaw) { //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0060: Unknown result type (might be due to invalid IL or missing references) //IL_0065: Unknown result type (might be due to invalid IL or missing references) //IL_008d: Unknown result type (might be due to invalid IL or missing references) //IL_0092: Unknown result type (might be due to invalid IL or missing references) //IL_0097: 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_009f: Unknown result type (might be due to invalid IL or missing references) if (rig != null && (Object)(object)rig.Camera != (Object)null && Refl.TryGetValue((object)rig.Camera, "_rot", out Vector3 value)) { yaw = value.y; return true; } Transform val = (((Object)(object)_camObject != (Object)null) ? _camObject : _localPlayer); if ((Object)(object)val == (Object)null) { yaw = _bodyYaw; return false; } Vector3 forward = val.forward; forward.y = 0f; if (((Vector3)(ref forward)).sqrMagnitude < 0.0001f) { yaw = _bodyYaw; return false; } Quaternion val2 = Quaternion.LookRotation(((Vector3)(ref forward)).normalized, Vector3.up); yaw = ((Quaternion)(ref val2)).eulerAngles.y; return true; } private static bool IsMoving(BodyRig rig, ModConfig cfg) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) PlayerMovement val = ((rig != null && (Object)(object)rig.Player != (Object)null) ? rig.Player.Movement : null); if ((Object)(object)val == (Object)null) { return false; } Vector3 velocity = val.Velocity; velocity.y = 0f; return ((Vector3)(ref velocity)).sqrMagnitude > cfg.BodyTurnMoveThreshold.Value * cfg.BodyTurnMoveThreshold.Value; } private void NeutralisePhysics() { Collider[] componentsInChildren = ((Component)_characterRoot).GetComponentsInChildren(true); foreach (Collider val in componentsInChildren) { if (!((Object)(object)val == (Object)null) && !_savedColliders.ContainsKey(val)) { _savedColliders[val] = val.enabled; val.enabled = false; } } Rigidbody[] componentsInChildren2 = ((Component)_characterRoot).GetComponentsInChildren(true); foreach (Rigidbody val2 in componentsInChildren2) { if (!((Object)(object)val2 == (Object)null) && !_savedKinematic.ContainsKey(val2)) { _savedKinematic[val2] = val2.isKinematic; val2.isKinematic = true; val2.detectCollisions = false; _neutralisedBodies.Add(val2); } } Plugin.Log.LogInfo((object)($"[tfp] physics stripped from the character - {_savedColliders.Count} collider(s), " + $"{_neutralisedBodies.Count} rigidbody(s)")); } private void RestorePhysics() { foreach (KeyValuePair savedCollider in _savedColliders) { if (!((Object)(object)savedCollider.Key == (Object)null)) { savedCollider.Key.enabled = savedCollider.Value; } } foreach (Rigidbody neutralisedBody in _neutralisedBodies) { if (!((Object)(object)neutralisedBody == (Object)null)) { neutralisedBody.detectCollisions = true; if (_savedKinematic.TryGetValue(neutralisedBody, out var value)) { neutralisedBody.isKinematic = value; } } } _savedColliders.Clear(); _savedKinematic.Clear(); _neutralisedBodies.Clear(); } public void Restore() { //IL_0070: Unknown result type (might be due to invalid IL or missing references) //IL_0075: Unknown result type (might be due to invalid IL or missing references) //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_003b: Unknown result type (might be due to invalid IL or missing references) if (_applied) { EndBoatRide(); RestorePhysics(); if ((Object)(object)_characterRoot != (Object)null) { _characterRoot.localPosition = _origLocalPos; _characterRoot.localRotation = _origLocalRot; } _characterRoot = null; _localPlayer = null; _camObject = null; _camProxy = null; _applied = false; _loggedOnce = false; _cameraOffset = Vector3.zero; } } private static Transform ResolveCharacterRoot(Player player) { List list = Refl.Get>(player, "_otherObjects"); if (list == null) { return null; } foreach (GameObject item in list) { if ((Object)(object)item != (Object)null) { return item.transform; } } return null; } private static Transform ResolveLocalPlayer(Player player) { List list = Refl.Get>(player, "_localObjects"); if (list == null) { return null; } foreach (GameObject item in list) { if (!((Object)(object)item == (Object)null) && (Object)(object)item.GetComponent() != (Object)null) { return item.transform; } } foreach (GameObject item2 in list) { if ((Object)(object)item2 != (Object)null) { return item2.transform; } } return null; } } internal sealed class BodyReveal { private struct State { public bool Enabled; public ShadowCastingMode Shadows; } private readonly Dictionary _saved = new Dictionary(); private readonly Dictionary _savedDrivers = new Dictionary(); private bool _applied; public void Apply(BodyRig rig, ModConfig cfg) { //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0096: Invalid comparison between Unknown and I4 if (_applied || rig == null || !rig.IsValid) { return; } if (cfg.ForceInitializeOther.Value && (Object)(object)rig.Skin != (Object)null) { try { Refl.Invoke(rig.Skin, "InitializeOther"); } catch (Exception ex) { Plugin.Log.LogWarning((object)("[tfp] InitializeOther failed: " + ex.Message)); } } if (cfg.ShowBody.Value) { foreach (SkinnedMeshRenderer thirdPersonRenderer in rig.ThirdPersonRenderers) { Save((Renderer)(object)thirdPersonRenderer); ((Renderer)thirdPersonRenderer).enabled = true; if ((int)((Renderer)thirdPersonRenderer).shadowCastingMode == 3) { ((Renderer)thirdPersonRenderer).shadowCastingMode = (ShadowCastingMode)1; } if (!((Component)thirdPersonRenderer).gameObject.activeSelf) { ((Component)thirdPersonRenderer).gameObject.SetActive(true); } } } if (!cfg.KeepVanillaHands.Value) { foreach (SkinnedMeshRenderer firstPersonHandRenderer in rig.FirstPersonHandRenderers) { if (!rig.IsSharedWithThirdPerson((Renderer)(object)firstPersonHandRenderer)) { Save((Renderer)(object)firstPersonHandRenderer); ((Renderer)firstPersonHandRenderer).enabled = false; } } } EnableRigDrivers(rig); ApplyNearClip(rig, cfg); _applied = true; Plugin.Log.LogInfo((object)("[tfp] body revealed - drivers: " + rig.DescribeDriverState())); } private void EnableRigDrivers(BodyRig rig) { Enable((MonoBehaviour)(object)rig.Body); Enable((MonoBehaviour)(object)rig.Arms); Enable((MonoBehaviour)(object)rig.Legs); } private void Enable(MonoBehaviour mb) { if (!((Object)(object)mb == (Object)null) && !((Behaviour)mb).enabled) { if (!_savedDrivers.ContainsKey(mb)) { _savedDrivers[mb] = ((Behaviour)mb).enabled; } ((Behaviour)mb).enabled = true; } } public void KeepDriversEnabled(BodyRig rig) { if (_applied && rig != null) { EnableRigDrivers(rig); } } private static void ApplyNearClip(BodyRig rig, ModConfig cfg) { if (!(cfg.CameraNearClip.Value <= 0f)) { Camera val = (((Object)(object)rig.Camera != (Object)null) ? rig.Camera.Cam : null); if ((Object)(object)val != (Object)null) { val.nearClipPlane = cfg.CameraNearClip.Value; } } } private void Save(Renderer r) { //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)r == (Object)null) && !_saved.ContainsKey(r)) { _saved[r] = new State { Enabled = r.enabled, Shadows = r.shadowCastingMode }; } } public void Restore() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (!_applied) { return; } foreach (KeyValuePair item in _saved) { if (!((Object)(object)item.Key == (Object)null)) { item.Key.enabled = item.Value.Enabled; item.Key.shadowCastingMode = item.Value.Shadows; } } foreach (KeyValuePair savedDriver in _savedDrivers) { if (!((Object)(object)savedDriver.Key == (Object)null)) { ((Behaviour)savedDriver.Key).enabled = savedDriver.Value; } } _saved.Clear(); _savedDrivers.Clear(); _applied = false; } } internal sealed class BodyRig { public Player Player; public PlayerBody Body; public PlayerArms Arms; public PlayerLegs Legs; public PlayerHands Hands; public PlayerSkin Skin; public PlayerCamera Camera; public Transform Head; public Transform LowerBody; public SkinnedMeshRenderer LocalLeftHand; public SkinnedMeshRenderer LocalRightHand; public SkinnedMeshRenderer BodyRenderer; public SkinnedMeshRenderer LeftHand; public SkinnedMeshRenderer RightHand; public SkinnedMeshRenderer OutfitRenderer; public SkinnedMeshRenderer HatRenderer; public SkinnedMeshRenderer AccessoryRenderer; public IK IkRight; public IK IkLeft; public GameObject NewCharacter; public GameObject OldCharacter; public SkinnedMeshRenderer HeadRenderer => BodyRenderer; public bool IsValid { get { if ((Object)(object)Player != (Object)null) { return (Object)(object)Skin != (Object)null; } return false; } } public IEnumerable ThirdPersonRenderers { get { if ((Object)(object)BodyRenderer != (Object)null) { yield return BodyRenderer; } if ((Object)(object)LeftHand != (Object)null) { yield return LeftHand; } if ((Object)(object)RightHand != (Object)null) { yield return RightHand; } if ((Object)(object)OutfitRenderer != (Object)null) { yield return OutfitRenderer; } if ((Object)(object)HatRenderer != (Object)null) { yield return HatRenderer; } if ((Object)(object)AccessoryRenderer != (Object)null) { yield return AccessoryRenderer; } } } public IEnumerable FirstPersonHandRenderers { get { if ((Object)(object)LocalLeftHand != (Object)null) { yield return LocalLeftHand; } if ((Object)(object)LocalRightHand != (Object)null) { yield return LocalRightHand; } } } public bool IsSharedWithThirdPerson(Renderer r) { if ((Object)(object)r == (Object)null) { return false; } foreach (SkinnedMeshRenderer thirdPersonRenderer in ThirdPersonRenderers) { if ((object)thirdPersonRenderer == r) { return true; } } return false; } public static BodyRig Capture(Player player) { if ((Object)(object)player == (Object)null) { return null; } BodyRig bodyRig = new BodyRig { Player = player }; bodyRig.Body = player.Body; bodyRig.Arms = player.Arms; bodyRig.Legs = player.Legs; bodyRig.Hands = player.Hands; bodyRig.Skin = player.Skin; bodyRig.Camera = player.Camera; if ((Object)(object)bodyRig.Body != (Object)null) { bodyRig.Head = bodyRig.Body.Head; bodyRig.LowerBody = bodyRig.Body.LowerBody; bodyRig.NewCharacter = Refl.Get(bodyRig.Body, "_newCharacter"); bodyRig.OldCharacter = Refl.Get(bodyRig.Body, "_oldCharacter"); } bodyRig.LocalLeftHand = Refl.Get(bodyRig.Skin, "_localLeftHand"); bodyRig.LocalRightHand = Refl.Get(bodyRig.Skin, "_localRightHand"); bodyRig.BodyRenderer = Refl.Get(bodyRig.Skin, "_bodyRenderer"); bodyRig.LeftHand = Refl.Get(bodyRig.Skin, "_leftHand"); bodyRig.RightHand = Refl.Get(bodyRig.Skin, "_rightHand"); bodyRig.OutfitRenderer = Refl.Get(bodyRig.Skin, "_outfitRenderer"); bodyRig.HatRenderer = Refl.Get(bodyRig.Skin, "_hatRenderer"); bodyRig.AccessoryRenderer = Refl.Get(bodyRig.Skin, "_accessoryRenderer"); bodyRig.IkRight = Refl.Get(bodyRig.Arms, "_ikRight"); bodyRig.IkLeft = Refl.Get(bodyRig.Arms, "_ikLeft"); return bodyRig; } public string DescribeDriverState() { return "Body=" + State((MonoBehaviour)(object)Body) + " Arms=" + State((MonoBehaviour)(object)Arms) + " Legs=" + State((MonoBehaviour)(object)Legs) + " Hands=" + State((MonoBehaviour)(object)Hands); static string State(MonoBehaviour mb) { if ((Object)(object)mb == (Object)null) { return "missing"; } if (!((Behaviour)mb).enabled || !((Component)mb).gameObject.activeInHierarchy) { return "disabled"; } return "running"; } } } internal sealed class DeathHider { private GameObject _character; private bool _hidden; private bool _applied; public void Apply(BodyRig rig) { if (!_applied && rig != null) { _character = rig.NewCharacter; _hidden = false; _applied = (Object)(object)_character != (Object)null; } } public void Tick(BodyRig rig) { if (_applied && rig != null && !((Object)(object)rig.Player == (Object)null) && !((Object)(object)_character == (Object)null)) { string source; bool flag = IsDead(rig, out source); if (flag != _hidden) { _hidden = flag; _character.SetActive(!flag); Plugin.Log.LogInfo((object)(flag ? ("[tfp] player died via " + source + " - character hidden so it does not stand next to the ragdoll") : "[tfp] player resurrected - character shown")); } } } public static bool IsDead(BodyRig rig, out string source) { source = "none"; if (rig == null || (Object)(object)rig.Player == (Object)null) { return false; } PlayerDying dying = rig.Player.Dying; if ((Object)(object)dying != (Object)null) { if (dying.IsDead) { source = "PlayerDying.IsDead"; return true; } if ((Object)(object)dying.DeadPlayer != (Object)null) { source = "PlayerDying.DeadPlayer"; return true; } } PlayerVitals vitals = rig.Player.Vitals; bool value = default(bool); if ((Object)(object)vitals != (Object)null && Refl.TryGetValue(vitals, "_localIsDead", out value) && value) { source = "PlayerVitals._localIsDead"; return true; } return false; } public void Restore() { if (_applied) { if ((Object)(object)_character != (Object)null && _hidden) { _character.SetActive(true); } _character = null; _hidden = false; _applied = false; } } } internal sealed class HeadHider { private readonly Dictionary _savedEnabled = new Dictionary(); private readonly Dictionary _savedShadows = new Dictionary(); private Transform _head; private Vector3 _origScale = Vector3.one; private bool _applied; public void Apply(BodyRig rig, ModConfig cfg) { //IL_00bb: Unknown result type (might be due to invalid IL or missing references) //IL_00c0: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) //IL_00db: Unknown result type (might be due to invalid IL or missing references) if (_applied || rig == null) { return; } switch (cfg.HideHead.Value) { case HeadMode.ShadowsOnly: ShadowsOnly((Renderer)(object)rig.HeadRenderer); ShadowsOnly((Renderer)(object)rig.HatRenderer); ShadowsOnly((Renderer)(object)rig.AccessoryRenderer); break; case HeadMode.HideRenderer: Hide((Renderer)(object)rig.HeadRenderer); if (cfg.HideHat.Value) { Hide((Renderer)(object)rig.HatRenderer); } if (cfg.HideAccessory.Value) { Hide((Renderer)(object)rig.AccessoryRenderer); } break; case HeadMode.ShrinkBone: if ((Object)(object)rig.Head != (Object)null) { _head = rig.Head; _origScale = _head.localScale; _head.localScale = Vector3.one * cfg.HeadShrinkScale.Value; } break; } _applied = true; } private void ShadowsOnly(Renderer r) { //IL_0020: Unknown result type (might be due to invalid IL or missing references) if (!((Object)(object)r == (Object)null) && !_savedShadows.ContainsKey(r)) { _savedShadows[r] = r.shadowCastingMode; r.shadowCastingMode = (ShadowCastingMode)3; if (!r.enabled) { _savedEnabled[r] = r.enabled; r.enabled = true; } } } private void Hide(Renderer r) { if (!((Object)(object)r == (Object)null) && !_savedEnabled.ContainsKey(r)) { _savedEnabled[r] = r.enabled; r.enabled = false; } } public void Restore() { //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) if (!_applied) { return; } if ((Object)(object)_head != (Object)null) { _head.localScale = _origScale; } foreach (KeyValuePair savedShadow in _savedShadows) { if (!((Object)(object)savedShadow.Key == (Object)null)) { savedShadow.Key.shadowCastingMode = savedShadow.Value; } } foreach (KeyValuePair item in _savedEnabled) { if (!((Object)(object)item.Key == (Object)null)) { item.Key.enabled = item.Value; } } _savedShadows.Clear(); _savedEnabled.Clear(); _head = null; _applied = false; } } internal sealed class IkRetimer { private static readonly HashSet Owned = new HashSet(); private static bool _reentrant; private readonly List _chains = new List(); private bool _applied; public static bool ShouldSuppress(IK ik) { if (_reentrant) { return false; } if ((Object)(object)ik != (Object)null) { return Owned.Contains(ik); } return false; } public void Apply(BodyRig rig, ModConfig cfg) { if (_applied || rig == null || (Object)(object)rig.Player == (Object)null || !cfg.RetimeIkSolve.Value) { return; } _chains.Clear(); IK[] componentsInChildren = ((Component)rig.Player).GetComponentsInChildren(true); foreach (IK val in componentsInChildren) { if (!((Object)(object)val == (Object)null)) { _chains.Add(val); Owned.Add(val); } } _applied = _chains.Count > 0; if (_applied) { Plugin.Log.LogInfo((object)$"[tfp] retiming {_chains.Count} IK chain(s) to solve after the body pose"); } } public void ResolveAll() { if (!_applied) { return; } _reentrant = true; try { foreach (IK chain in _chains) { if (!((Object)(object)chain == (Object)null) && ((Behaviour)chain).enabled && ((Component)chain).gameObject.activeInHierarchy) { Refl.Invoke(chain, "LateUpdate"); } } } finally { _reentrant = false; } } public void Restore() { if (!_applied) { return; } foreach (IK chain in _chains) { Owned.Remove(chain); } _chains.Clear(); _applied = false; } } internal sealed class JumpPose { private PlayerLegs _legs; private Vector3[] _original; private bool _applied; public void Apply(BodyRig rig, ModConfig cfg) { //IL_00df: Unknown result type (might be due to invalid IL or missing references) //IL_00e4: Unknown result type (might be due to invalid IL or missing references) if (_applied || rig == null || !cfg.JumpPoseOverride.Value) { return; } _legs = rig.Legs; if ((Object)(object)_legs == (Object)null) { return; } Vector3[] array = Refl.Get(_legs, "_jumpFeetPos"); if (array != null && array.Length >= 2) { _original = (Vector3[])array.Clone(); float value = cfg.JumpFootSpread.Value; float value2 = cfg.JumpFootDrop.Value; float value3 = cfg.JumpFootReach.Value; Vector3[] array2 = (Vector3[])(object)new Vector3[array.Length]; for (int i = 0; i < array.Length; i++) { float num = ((i == 0) ? value : (0f - value)); array2[i] = new Vector3(_original[i].x + num, _original[i].y - value2, _original[i].z * value3); } Refl.Set(_legs, "_jumpFeetPos", array2); _applied = true; Plugin.Log.LogInfo((object)("[tfp] jump pose overridden - " + Fmt(_original) + " -> " + Fmt(array2))); } } public void Tick(ModConfig cfg) { if (!_applied || (Object)(object)_legs == (Object)null || !cfg.ForceConsistentJumpPose.Value) { return; } bool[] array = Refl.Get(_legs, "_jumpPosIsLast"); if (array == null) { return; } for (int i = 0; i < array.Length; i++) { if (array[i]) { array[i] = false; } } } public void Restore() { if (_applied) { if ((Object)(object)_legs != (Object)null && _original != null) { Refl.Set(_legs, "_jumpFeetPos", _original); } _legs = null; _original = null; _applied = false; } } private static string Fmt(Vector3[] v) { //IL_000e: Unknown result type (might be due to invalid IL or missing references) //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_001a: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); foreach (Vector3 val in v) { stringBuilder.Append($"({val.x:F2},{val.y:F2},{val.z:F2})"); } return stringBuilder.ToString(); } } internal sealed class ModConfig { public readonly ConfigEntry Enabled; public readonly ConfigEntry ShowBody; public readonly ConfigEntry KeepVanillaHands; public readonly ConfigEntry ForceInitializeOther; public readonly ConfigEntry BodyOffsetY; public readonly ConfigEntry BodyOffsetForward; public readonly ConfigEntry BoatAdjustments; public readonly ConfigEntry BoatOffsetForward; public readonly ConfigEntry BoatCameraForward; public readonly ConfigEntry LockBodyToBoat; public readonly ConfigEntry BoatYawLimit; public readonly ConfigEntry BoatLookDownLimit; public readonly ConfigEntry BoatLookUpLimit; public readonly ConfigEntry RetimeIkSolve; public readonly ConfigEntry FollowCameraHorizontally; public readonly ConfigEntry BodyTurnsWithCamera; public readonly ConfigEntry BodyYawDeadzone; public readonly ConfigEntry BodyYawSpeed; public readonly ConfigEntry BodyTurnMoveThreshold; public readonly ConfigEntry HideHead; public readonly ConfigEntry HeadShrinkScale; public readonly ConfigEntry HideHat; public readonly ConfigEntry HideAccessory; public readonly ConfigEntry JumpPoseOverride; public readonly ConfigEntry JumpFootSpread; public readonly ConfigEntry JumpFootDrop; public readonly ConfigEntry JumpFootReach; public readonly ConfigEntry ForceConsistentJumpPose; public readonly ConfigEntry CameraNearClip; public readonly ConfigEntry MaxLookDownAngle; public readonly ConfigEntry LogRigOnSpawn; public ModConfig(ConfigFile f) { //IL_00a0: Unknown result type (might be due to invalid IL or missing references) //IL_00aa: Expected O, but got Unknown //IL_00d9: Unknown result type (might be due to invalid IL or missing references) //IL_00e3: Expected O, but got Unknown //IL_012e: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Expected O, but got Unknown //IL_0167: Unknown result type (might be due to invalid IL or missing references) //IL_0171: Expected O, but got Unknown //IL_01bc: Unknown result type (might be due to invalid IL or missing references) //IL_01c6: Expected O, but got Unknown //IL_01f5: Unknown result type (might be due to invalid IL or missing references) //IL_01ff: Expected O, but got Unknown //IL_022e: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Expected O, but got Unknown //IL_02bb: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Expected O, but got Unknown //IL_02f4: Unknown result type (might be due to invalid IL or missing references) //IL_02fe: Expected O, but got Unknown //IL_032d: Unknown result type (might be due to invalid IL or missing references) //IL_0337: Expected O, but got Unknown //IL_0382: Unknown result type (might be due to invalid IL or missing references) //IL_038c: Expected O, but got Unknown //IL_040f: Unknown result type (might be due to invalid IL or missing references) //IL_0419: Expected O, but got Unknown //IL_0448: Unknown result type (might be due to invalid IL or missing references) //IL_0452: Expected O, but got Unknown //IL_0481: Unknown result type (might be due to invalid IL or missing references) //IL_048b: Expected O, but got Unknown //IL_04d6: Unknown result type (might be due to invalid IL or missing references) //IL_04e0: Expected O, but got Unknown //IL_050f: Unknown result type (might be due to invalid IL or missing references) //IL_0519: Expected O, but got Unknown Enabled = f.Bind("01 - General", "Enabled", true, "Master switch. When off the game returns to its stock floating-hands view."); ShowBody = f.Bind("01 - General", "ShowBody", true, "Reveal your own character on this client: chest, arms and legs. They share a single skinned mesh, so they appear together. The game already poses all of it procedurally through PlayerBody, PlayerArms and PlayerLegs, so this is a visibility change rather than new animation."); KeepVanillaHands = f.Bind("01 - General", "KeepVanillaHands", false, "Keep the stock first-person hand meshes visible on top of the body. Normally false, since with the body shown they double up with its real hands. Worth turning on once while diagnosing whether the two share a skeleton."); ForceInitializeOther = f.Bind("01 - General", "ForceInitializeOther", true, "Call PlayerSkin.InitializeOther() when revealing. Needed, and on by default: the game only ever runs InitializeLocal() on your own player, which sets up the first-person hands and nothing else. Without this the revealed body wears whatever outfit and colours the prefab happened to ship with, instead of the character you configured in the menu."); BodyOffsetY = f.Bind("01 - General", "BodyOffsetY", 0f, new ConfigDescription("Vertical offset of the character relative to the local player object. Nothing positions the body on a local player - the component that normally does it applies replicated position and must stay off - so the mod copies the player's position across each frame. Adjust this if the body sits too high or sinks into the ground.", (AcceptableValueBase)(object)new AcceptableValueRange(-2f, 2f), Array.Empty())); BodyOffsetForward = f.Bind("01 - General", "BodyOffsetForward", -0.25f, new ConfigDescription("Shifts the character along its own forward axis. 0 by default. A non-zero value pulls the body off the camera pivot, so turning swings it sideways on an arc instead of rotating it in place - which reads as the body sliding around you. Use MaxLookDownAngle to keep the chest out of frame instead; that costs nothing.", (AcceptableValueBase)(object)new AcceptableValueRange(-1f, 1f), Array.Empty())); BoatAdjustments = f.Bind("01 - General", "BoatAdjustments", true, "Master switch for everything boat-specific: parenting the body to the boat, the forward and sideways offsets, and the look limits. Turn off to fall back to the plain ground behaviour everywhere, which is the quickest way to tell whether a problem on a boat comes from this mod's boat handling or from somewhere else."); BoatOffsetForward = f.Bind("01 - General", "BoatOffsetForward", 0f, new ConfigDescription("Replaces BodyOffsetForward while driving a boat. Applies only when actually at the wheel, not merely standing on deck. The wheel is a fixed point in front of the player, so a body pulled back has to reach further for it than the arm can manage, which stretches the arm and makes the IK flicker as it fights its own limit. Nudging the body forward on a boat puts the wheel back inside comfortable reach.", (AcceptableValueBase)(object)new AcceptableValueRange(-1f, 1f), Array.Empty())); BoatCameraForward = f.Bind("01 - General", "BoatCameraForward", 0.3f, new ConfigDescription("Nudges the camera along the boat while driving. 0 by default: the game already parks the player rigidbody on DriverPos and builds the camera from it, so the vanilla view is already correct relative to the wheel. Positive values push past the wheel. Negative pulls back off it. Separate from BodyOffsetForward on purpose - moving the body changes how far the arms must reach, moving the camera does not.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1.5f), Array.Empty())); LockBodyToBoat = f.Bind("01 - General", "LockBodyToBoat", true, "While at the wheel, face the body along the boat instead of along the camera. You are holding a fixed wheel with both hands, so the torso cannot swing as you look around; only the head should turn. This also stops the arms being dragged off the wheel every time you glance sideways."); BoatYawLimit = f.Bind("01 - General", "BoatYawLimit", 80f, new ConfigDescription("How far you may look left or right of the boat heading while driving, in degrees. Anything that follows the look direction is limited with it. Set to 180 to remove the limit.", (AcceptableValueBase)(object)new AcceptableValueRange(20f, 180f), Array.Empty())); BoatLookDownLimit = f.Bind("01 - General", "BoatLookDownLimit", 35f, new ConfigDescription("How far down you may look while driving, in degrees. Tighter than the on-foot limit on purpose: at the wheel the body sits right under the camera, so looking further down just shows your own torso.", (AcceptableValueBase)(object)new AcceptableValueRange(5f, 90f), Array.Empty())); BoatLookUpLimit = f.Bind("01 - General", "BoatLookUpLimit", 70f, new ConfigDescription("How far up you may look while driving, in degrees. Nothing of the body is in the way looking up, so this stays loose.", (AcceptableValueBase)(object)new AcceptableValueRange(5f, 90f), Array.Empty())); RetimeIkSolve = f.Bind("05 - Debug", "RetimeIkSolve", true, "Take over when the IK chains solve, running them right after PlayerBody has posed the torso instead of at Unity's arbitrary point in LateUpdate. The shoulder moves with the torso, so solving before it was posed leaves the arm a frame behind and makes it flicker. Turn off to hand the timing back to the game."); FollowCameraHorizontally = f.Bind("01b - Follow", "FollowCameraHorizontally", true, "Anchor the body to the camera horizontally instead of to the physics capsule. The camera is smoothed on top of the capsule, so anchoring to the capsule lets the body slide sideways while the view stays put, most visible when strafing and turning at the same time."); BodyTurnsWithCamera = f.Bind("01b - Follow", "BodyTurnsWithCamera", true, "Let the body lag behind the camera and catch up, instead of snapping to the look direction every frame. PlayerLegs only takes a new step once you have walked far enough, never when you merely turn, so a body welded to the camera spins on top of feet that stay planted in the world - which is what leaves the legs crossed."); BodyYawDeadzone = f.Bind("01b - Follow", "BodyYawDeadzone", 45f, new ConfigDescription("How far you may look away from where the body faces, in degrees, before the body turns to catch up. Only applies while standing still. 0 keeps the body pointed at the camera at all times, which is what most people expect: turning rotates the body rather than leaving it behind.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 120f), Array.Empty())); BodyYawSpeed = f.Bind("01b - Follow", "BodyYawSpeed", 360f, new ConfigDescription("How fast the body turns toward the look direction, in degrees per second.", (AcceptableValueBase)(object)new AcceptableValueRange(30f, 2000f), Array.Empty())); BodyTurnMoveThreshold = f.Bind("01b - Follow", "BodyTurnMoveThreshold", 0.4f, new ConfigDescription("Speed above which the body is treated as moving and tracks the camera directly, with no deadzone.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 3f), Array.Empty())); HideHead = f.Bind("02 - Head", "HideHead", HeadMode.ShrinkBone, "How to keep your own head out of the camera.\nShrinkBone - scale the head bone to near zero. Standard trick; the mesh still skins as one draw call and hats collapse with it.\nNone - leave the head alone. Expect to see the inside of your own skull."); HeadShrinkScale = f.Bind("02 - Head", "HeadShrinkScale", 0.001f, new ConfigDescription("Head bone scale used by ShrinkBone. Exactly zero can produce degenerate normals in some skinning paths, so a tiny non-zero value is safer.", (AcceptableValueBase)(object)new AcceptableValueRange(0.0001f, 0.2f), Array.Empty())); HideHat = f.Bind("02 - Head", "HideHat", true, "Hide your own hat renderer. Hats skinned to the head bone already collapse with ShrinkBone; this covers hats that have their own root."); HideAccessory = f.Bind("02 - Head", "HideAccessory", false, "Hide your own accessory renderer. Off by default, since many accessories sit on the body rather than the face."); JumpPoseOverride = f.Bind("03b - Jump", "JumpPoseOverride", true, "Retune the airborne foot pose. The game places both feet on the centre line while jumping, one well forward and one back, with the front knee tucked high. That reads fine as a third-person silhouette and looks like crossed legs from inside the body."); JumpFootSpread = f.Bind("03b - Jump", "JumpFootSpread", 0.1f, new ConfigDescription("Sideways separation added to each airborne foot, so the legs stay on their own side instead of meeting in the middle. 0.1 matches the spacing the game already uses when standing.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 0.4f), Array.Empty())); JumpFootDrop = f.Bind("03b - Jump", "JumpFootDrop", 0.1f, new ConfigDescription("Lowers both airborne feet, relaxing the high knee tuck that otherwise swings a leg up into view.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 0.4f), Array.Empty())); JumpFootReach = f.Bind("03b - Jump", "JumpFootReach", 0.6f, new ConfigDescription("Scales how far the airborne feet split forward and back. 1 keeps the game stride, lower values bring them closer together.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 1f), Array.Empty())); ForceConsistentJumpPose = f.Bind("03b - Jump", "ForceConsistentJumpPose", true, "Use the same airborne pose on every jump. PlayerLegs keeps a _jumpPosIsLast flag per foot and sends that foot to its last footstep position instead of the authored jump pose, alternating as the lead foot switches. That is why one jump looks right and the next crosses the legs. Clearing the flags makes both feet always take the authored pose."); CameraNearClip = f.Bind("04 - Camera", "CameraNearClip", 0.2f, new ConfigDescription("Camera near clip plane: the distance below which nothing is drawn. Raising it trims the torso that sits right under the camera; raising it too far cuts a hole through the whole body. Tuned together with BodyOffsetForward. 0 leaves the game value untouched.", (AcceptableValueBase)(object)new AcceptableValueRange(0f, 0.3f), Array.Empty())); MaxLookDownAngle = f.Bind("04 - Camera", "MaxLookDownAngle", 72f, new ConfigDescription("How far down you may look, in degrees. The vanilla limit is 90, straight down into your own collar. Pulling it in a little keeps the camera out of the torso. Set to 90 to disable the clamp entirely.", (AcceptableValueBase)(object)new AcceptableValueRange(30f, 90f), Array.Empty())); LogRigOnSpawn = f.Bind("05 - Debug", "LogRigOnSpawn", false, "Dump the local player's hierarchy, components and renderer states to the BepInEx log on spawn. The same dump is available any time with /tfp dump."); } } internal enum HeadMode { None, ShrinkBone, HideRenderer, ShadowsOnly } internal sealed class MotionBridge { private bool _logged; public void Tick(BodyRig rig, Transform characterRoot) { //IL_003e: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_006a: Unknown result type (might be due to invalid IL or missing references) //IL_0065: 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_0077: 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) //IL_009b: Unknown result type (might be due to invalid IL or missing references) if (rig == null || (Object)(object)rig.Player == (Object)null) { return; } OtherPlayer other = rig.Player.Other; PlayerMovement movement = rig.Player.Movement; if (!((Object)(object)other == (Object)null) && !((Object)(object)movement == (Object)null)) { Vector3 velocity = movement.Velocity; Vector3 val = default(Vector3); ((Vector3)(ref val))..ctor(velocity.x, 0f, velocity.z); Vector3 val2 = (((Object)(object)characterRoot != (Object)null) ? characterRoot.InverseTransformDirection(val) : val); Refl.Set(other, "k__BackingField", velocity); Refl.Set(other, "k__BackingField", val); Refl.Set(other, "k__BackingField", val2); Refl.Set(other, "k__BackingField", ((Vector3)(ref val)).magnitude); Refl.Set(other, "k__BackingField", movement.OnBoat); Refl.Set(other, "_grounded", movement.Grounded); if (Refl.TryGetValue(movement, "_isSwimming", out var value)) { Refl.Set(other, "_isSwimming", value); } if (!_logged) { _logged = true; Plugin.Log.LogInfo((object)$"[tfp] motion bridge engaged - grounded={movement.Grounded} vel={((Vector3)(ref val)).magnitude:F2}"); } } } public void Reset() { _logged = false; } } [BepInPlugin("com.zpaulin.howtofish.truefirstperson", "True First Person", "1.0.0")] [BepInProcess("How to Fish.exe")] public sealed class Plugin : BaseUnityPlugin { private Harmony _harmony; private GameObject _host; internal static Plugin Instance { get; private set; } internal static ManualLogSource Log { get; private set; } internal static ModConfig Cfg { get; private set; } private void Awake() { //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Expected O, but got Unknown //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0056: Unknown result type (might be due to invalid IL or missing references) //IL_0063: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; Cfg = new ModConfig(((BaseUnityPlugin)this).Config); _harmony = new Harmony("com.zpaulin.howtofish.truefirstperson"); _harmony.PatchAll(typeof(Plugin).Assembly); _host = new GameObject("HowToFish.TrueFirstPerson.Driver") { hideFlags = (HideFlags)61 }; Object.DontDestroyOnLoad((Object)(object)_host); _host.AddComponent(); Log.LogInfo((object)"True First Person v1.0.0 loaded"); } private void OnDestroy() { if ((Object)(object)_host != (Object)null) { Object.Destroy((Object)(object)_host); } Harmony harmony = _harmony; if (harmony != null) { harmony.UnpatchSelf(); } } } public static class PluginInfo { public const string Guid = "com.zpaulin.howtofish.truefirstperson"; public const string Name = "True First Person"; public const string Version = "1.0.0"; } internal static class Refl { private static readonly Dictionary Cache = new Dictionary(); private static readonly Dictionary Methods = new Dictionary(); private static readonly HashSet Warned = new HashSet(); private static FieldInfo Field(Type owner, string name) { string text = owner.FullName + "." + name; if (Cache.TryGetValue(text, out var value)) { return value; } value = AccessTools.Field(owner, name); Cache[text] = value; if (value == null && Warned.Add(text)) { Plugin.Log.LogWarning((object)("field not found: " + text + " - the feature relying on it is disabled")); } return value; } public static T Get(object instance, string name) where T : class { if (instance == null) { return null; } FieldInfo fieldInfo = Field(instance.GetType(), name); if (fieldInfo == null) { return null; } try { return fieldInfo.GetValue(instance) as T; } catch (Exception ex) { Plugin.Log.LogWarning((object)("reading " + name + ": " + ex.Message)); return null; } } public static bool TryGetValue(object instance, string name, out T value) where T : struct { value = default(T); if (instance == null) { return false; } FieldInfo fieldInfo = Field(instance.GetType(), name); if (fieldInfo == null) { return false; } try { if (!(fieldInfo.GetValue(instance) is T val)) { return false; } value = val; return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("reading " + name + ": " + ex.Message)); return false; } } public static bool Set(object instance, string name, object value) { if (instance == null) { return false; } FieldInfo fieldInfo = Field(instance.GetType(), name); if (fieldInfo == null) { return false; } try { fieldInfo.SetValue(instance, value); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("writing " + name + ": " + ex.Message)); return false; } } public static T InvokeGet(object instance, string name) where T : class { if (instance == null) { return null; } string text = instance.GetType().FullName + "()" + name; if (!Methods.TryGetValue(text, out var value)) { value = AccessTools.Method(instance.GetType(), name, (Type[])null, (Type[])null); Methods[text] = value; if (value == null && Warned.Add(text)) { Plugin.Log.LogWarning((object)("method not found: " + text)); } } if (value == null) { return null; } try { return value.Invoke(instance, null) as T; } catch (Exception ex) { Plugin.Log.LogWarning((object)("invoking " + name + ": " + ex.Message)); return null; } } public static bool Invoke(object instance, string name) { if (instance == null) { return false; } string text = instance.GetType().FullName + "()" + name; if (!Methods.TryGetValue(text, out var value)) { value = AccessTools.Method(instance.GetType(), name, (Type[])null, (Type[])null); Methods[text] = value; if (value == null && Warned.Add(text)) { Plugin.Log.LogWarning((object)("method not found: " + text)); } } if (value == null) { return false; } try { value.Invoke(instance, null); return true; } catch (Exception ex) { Plugin.Log.LogWarning((object)("invoking " + name + ": " + ex.Message)); return false; } } } internal static class RigDiagnostics { private const int MaxDepth = 12; public static void DumpLocalPlayer() { Player localPlayer = Player.LocalPlayer; if ((Object)(object)localPlayer == (Object)null) { Plugin.Log.LogWarning((object)"[tfp] no local player yet - spawn in first"); return; } BodyRig bodyRig = BodyRig.Capture(localPlayer); StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("================ True First Person : rig dump ================"); stringBuilder.AppendLine("drivers : " + bodyRig.DescribeDriverState()); stringBuilder.AppendLine("head bone : " + Path(bodyRig.Head)); stringBuilder.AppendLine("lower body : " + Path(bodyRig.LowerBody)); stringBuilder.AppendLine("new character : " + Path(((Object)(object)bodyRig.NewCharacter != (Object)null) ? bodyRig.NewCharacter.transform : null)); stringBuilder.AppendLine("old character : " + Path(((Object)(object)bodyRig.OldCharacter != (Object)null) ? bodyRig.OldCharacter.transform : null)); stringBuilder.AppendLine(); stringBuilder.AppendLine("-- renderers --"); Describe(stringBuilder, "local left hand ", bodyRig.LocalLeftHand); Describe(stringBuilder, "local right hand", bodyRig.LocalRightHand); Describe(stringBuilder, "head (_bodyRend)", bodyRig.HeadRenderer); Describe(stringBuilder, "left hand ", bodyRig.LeftHand); Describe(stringBuilder, "right hand ", bodyRig.RightHand); Describe(stringBuilder, "outfit (=body) ", bodyRig.OutfitRenderer); Describe(stringBuilder, "hat ", bodyRig.HatRenderer); Describe(stringBuilder, "accessory ", bodyRig.AccessoryRenderer); stringBuilder.AppendLine(); stringBuilder.AppendLine("-- arm IK chains --"); DescribeIk(stringBuilder, "right", bodyRig.IkRight); DescribeIk(stringBuilder, "left ", bodyRig.IkLeft); stringBuilder.AppendLine(); stringBuilder.AppendLine("-- shared skeleton test --"); stringBuilder.AppendLine(SharedSkeletonVerdict(bodyRig)); stringBuilder.AppendLine(); DumpLegs(stringBuilder, bodyRig); stringBuilder.AppendLine(); DumpObjectLists(stringBuilder, localPlayer); stringBuilder.AppendLine(); stringBuilder.AppendLine("-- hierarchy --"); Walk(stringBuilder, ((Component)localPlayer).transform, 0); stringBuilder.AppendLine("=============================================================="); Plugin.Log.LogInfo((object)stringBuilder.ToString()); } private static string SharedSkeletonVerdict(BodyRig rig) { SkinnedMeshRenderer localRightHand = rig.LocalRightHand; SkinnedMeshRenderer bodyRenderer = rig.BodyRenderer; if ((Object)(object)localRightHand == (Object)null || (Object)(object)bodyRenderer == (Object)null) { return "inconclusive: one of the renderers is missing"; } Transform[] bones = localRightHand.bones; Transform[] bones2 = bodyRenderer.bones; if (bones == null || bones2 == null || bones.Length == 0 || bones2.Length == 0) { return "inconclusive: a renderer reports no bones"; } int num = 0; Transform[] array = bones; foreach (Transform val in array) { Transform[] array2 = bones2; foreach (Transform val2 in array2) { if ((Object)(object)val == (Object)(object)val2) { num++; break; } } } string text = ((num > 0) ? "SHARED skeleton - revealing the body should just work" : "SEPARATE skeletons - the hand rig will need retargeting onto the body arms"); return $"local hand bones {bones.Length}, body bones {bones2.Length}, shared {num}\n{text}"; } private static void DumpLegs(StringBuilder sb, BodyRig rig) { //IL_011d: Unknown result type (might be due to invalid IL or missing references) DumpSpaces(sb, rig); sb.AppendLine(); sb.AppendLine("-- legs --"); PlayerLegs legs = rig.Legs; if ((Object)(object)legs == (Object)null) { sb.AppendLine("PlayerLegs : "); return; } sb.AppendLine($"enabled={((Behaviour)legs).enabled} prefixFired={BodyDriver.LegsPrefixFired}"); Refl.TryGetValue(legs, "_isJumping", out var value); Refl.TryGetValue(legs, "_isSprinting", out var value2); Refl.TryGetValue(legs, "_isLanding", out var value3); Refl.TryGetValue(legs, "_latestFootToStartStepIndex", out var value4); sb.AppendLine($"jumping={value} sprinting={value2} landing={value3} lastFoot={value4}"); sb.AppendLine("_positionSpace : " + Path(Refl.Get(legs, "_positionSpace"))); DumpVectorArray(sb, "_jumpFeetPos", Refl.Get(legs, "_jumpFeetPos")); if (Refl.TryGetValue((object)legs, "_jumpFeetVelOffset", out Vector3 value5)) { sb.AppendLine($"_jumpFeetVelOffset : {value5}"); } if (Refl.TryGetValue(legs, "_spaceBetweenFeet", out var value6)) { sb.AppendLine($"_spaceBetweenFeet : {value6}"); } DumpTransformArray(sb, "_legTargets", Refl.Get(legs, "_legTargets")); DumpTransformArray(sb, "_ikPoles", Refl.Get(legs, "_ikPoles")); DumpTransformArray(sb, "_footModels", Refl.Get(legs, "_footModels")); DumpVectorArray(sb, "_footPoses", Refl.Get(legs, "_footPoses")); DumpVectorArray(sb, "_nextStepPos", Refl.Get(legs, "_nextStepPos")); DumpVectorArray(sb, "_lastStepPos", Refl.Get(legs, "_lastStepPos")); float[] array = Refl.Get(legs, "_curStepPercent"); if (array != null) { StringBuilder stringBuilder = new StringBuilder(); float[] array2 = array; foreach (float num in array2) { stringBuilder.Append($"{num:F2} "); } sb.AppendLine($"_curStepPercent : {stringBuilder}"); } sb.AppendLine("-- IK components under the character --"); if ((Object)(object)rig.Player != (Object)null) { IK[] componentsInChildren = ((Component)rig.Player).GetComponentsInChildren(true); foreach (IK val in componentsInChildren) { Refl.TryGetValue(val, "_chainLength", out var value7); sb.AppendLine($" {Path(((Component)val).transform)} enabled={((Behaviour)val).enabled} chain={value7} " + "target=" + Path(Refl.Get(val, "Target"))); } } } private static void DumpSpaces(StringBuilder sb, BodyRig rig) { //IL_005a: Unknown result type (might be due to invalid IL or missing references) //IL_005f: 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_00ba: 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_018f: 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_0158: Unknown result type (might be due to invalid IL or missing references) sb.AppendLine("-- spaces --"); Player player = rig.Player; if ((Object)(object)player == (Object)null) { sb.AppendLine("player : "); return; } sb.AppendLine("Player.Transform : " + Path(player.Transform) + " pos=" + (((Object)(object)player.Transform != (Object)null) ? ((object)player.Transform.position/*cast due to .constrained prefix*/).ToString() : "-")); sb.AppendLine($"player.gameObject : pos={((Component)player).transform.position}"); sb.AppendLine("CamObject : pos=" + (((Object)(object)player.CamObject != (Object)null) ? ((object)player.CamObject.position/*cast due to .constrained prefix*/).ToString() : "-")); OtherPlayer other = player.Other; if ((Object)(object)other != (Object)null) { Transform val = Refl.Get(other, "_transform"); sb.AppendLine("OtherPlayer._transform : " + Path(val) + " pos=" + (((Object)(object)val != (Object)null) ? ((object)val.position/*cast due to .constrained prefix*/).ToString() : "-")); sb.AppendLine($"OtherPlayer.Grounded={other.Grounded} VelMag={other.VelMag:F2} FlatLocalVel={other.FlatLocalVelocity}"); } PlayerMovement movement = player.Movement; if ((Object)(object)movement != (Object)null) { sb.AppendLine($"PlayerMovement Grounded={movement.Grounded} Velocity={movement.Velocity} OnBoat={movement.OnBoat}"); } } private static void DumpTransformArray(StringBuilder sb, string label, Transform[] items) { //IL_0067: Unknown result type (might be due to invalid IL or missing references) if (items == null) { sb.AppendLine(label + " : "); return; } sb.AppendLine($"{label} : {items.Length} entries"); for (int i = 0; i < items.Length; i++) { sb.AppendLine($" [{i}] {Path(items[i])}" + (((Object)(object)items[i] != (Object)null) ? $" worldPos={items[i].position}" : string.Empty)); } } private static void DumpVectorArray(StringBuilder sb, string label, Vector3[] items) { //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0030: Unknown result type (might be due to invalid IL or missing references) if (items == null) { sb.AppendLine(label + " : "); return; } StringBuilder stringBuilder = new StringBuilder(); foreach (Vector3 val in items) { stringBuilder.Append($"{val} "); } sb.AppendLine($"{label} : {stringBuilder}"); } private static void DumpObjectLists(StringBuilder sb, Player player) { sb.AppendLine("-- local/other split --"); ListGameObjects(sb, "_localObjects", Refl.Get>(player, "_localObjects")); ListGameObjects(sb, "_otherObjects", Refl.Get>(player, "_otherObjects")); ListBehaviours(sb, "_disableWhenLocal", Refl.Get(player, "_disableWhenLocal")); ListBehaviours(sb, "_disableWhenOther", Refl.Get(player, "_disableWhenOther")); } private static void ListGameObjects(StringBuilder sb, string label, List items) { if (items == null) { sb.AppendLine(label + " : "); return; } sb.AppendLine($"{label} : {items.Count} entries"); for (int i = 0; i < items.Count; i++) { GameObject val = items[i]; sb.AppendLine(((Object)(object)val == (Object)null) ? $" [{i}] " : $" [{i}] active={val.activeSelf} {Path(val.transform)}"); } } private static void ListBehaviours(StringBuilder sb, string label, MonoBehaviour[] items) { if (items == null) { sb.AppendLine(label + " : "); return; } sb.AppendLine($"{label} : {items.Length} entries"); for (int i = 0; i < items.Length; i++) { MonoBehaviour val = items[i]; sb.AppendLine(((Object)(object)val == (Object)null) ? $" [{i}] " : $" [{i}] {((object)val).GetType().Name} enabled={((Behaviour)val).enabled}"); } } public static void ScanForBodyAssets() { //IL_005c: Unknown result type (might be due to invalid IL or missing references) //IL_0061: Unknown result type (might be due to invalid IL or missing references) //IL_01d8: Unknown result type (might be due to invalid IL or missing references) //IL_01dd: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_01fb: Unknown result type (might be due to invalid IL or missing references) StringBuilder stringBuilder = new StringBuilder(); stringBuilder.AppendLine("================ True First Person : asset scan ================"); SkinnedMeshRenderer[] array = Resources.FindObjectsOfTypeAll(); stringBuilder.AppendLine($"SkinnedMeshRenderers loaded: {array.Length}"); stringBuilder.AppendLine(); int num = 0; int num2 = 0; SkinnedMeshRenderer[] array2 = array; Scene scene; foreach (SkinnedMeshRenderer val in array2) { if (!((Object)(object)val == (Object)null)) { scene = ((Component)val).gameObject.scene; if (((Scene)(ref scene)).IsValid()) { num2++; } else { num++; } } } stringBuilder.AppendLine($" in a scene: {num2} assets/prefabs: {num}"); stringBuilder.AppendLine(); stringBuilder.AppendLine("-- candidates (name or root suggests a character) --"); int num3 = 0; array2 = array; foreach (SkinnedMeshRenderer val2 in array2) { if ((Object)(object)val2 == (Object)null) { continue; } Transform root = ((Component)val2).transform.root; if (LooksLikeCharacter((((Object)val2).name + "|" + ((Object)root).name + "|" + (((Object)(object)val2.sharedMesh != (Object)null) ? ((Object)val2.sharedMesh).name : string.Empty)).ToLowerInvariant())) { if (num3++ > 80) { stringBuilder.AppendLine(" ... truncated"); break; } string[] obj = new string[8] { string.Format(" {0,-24} mesh={1,-24} ", ((Object)val2).name, ((Object)(object)val2.sharedMesh != (Object)null) ? ((Object)val2.sharedMesh).name : ""), $"bones={((val2.bones != null) ? val2.bones.Length : 0),-3} ", "scene=", null, null, null, null, null }; scene = ((Component)val2).gameObject.scene; object obj2; if (!((Scene)(ref scene)).IsValid()) { obj2 = ""; } else { scene = ((Component)val2).gameObject.scene; obj2 = ((Scene)(ref scene)).name; } obj[3] = (string)obj2; obj[4] = " root="; obj[5] = ((Object)root).name; obj[6] = " path="; obj[7] = Path(((Component)val2).transform); stringBuilder.AppendLine(string.Concat(obj)); } } if (num3 == 0) { stringBuilder.AppendLine(" none matched - widen the filter in LooksLikeCharacter"); } stringBuilder.AppendLine("=============================================================="); Plugin.Log.LogInfo((object)stringBuilder.ToString()); } private static bool LooksLikeCharacter(string lower) { if (!lower.Contains("body") && !lower.Contains("outfit") && !lower.Contains("character") && !lower.Contains("player") && !lower.Contains("hat") && !lower.Contains("accessor") && !lower.Contains("torso") && !lower.Contains("leg") && !lower.Contains("arm") && !lower.Contains("bean") && !lower.Contains("dead")) { return lower.Contains("skin"); } return true; } private static void Describe(StringBuilder sb, string label, SkinnedMeshRenderer r) { //IL_0049: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)r == (Object)null) { sb.AppendLine(label + " : "); } else { sb.AppendLine($"{label} : enabled={((Renderer)r).enabled} activeInHierarchy={((Component)r).gameObject.activeInHierarchy} " + $"shadows={((Renderer)r).shadowCastingMode} layer={LayerMask.LayerToName(((Component)r).gameObject.layer)} " + $"bones={((r.bones != null) ? r.bones.Length : 0)} path={Path(((Component)r).transform)}"); } } private static void DescribeIk(StringBuilder sb, string label, IK ik) { //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_016f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)ik == (Object)null) { sb.AppendLine(label + " : "); return; } Refl.TryGetValue(ik, "_enableStretch", out var value); Refl.TryGetValue(ik, "_chainLength", out var value2); Refl.TryGetValue(ik, "_origCompleteLengthSqr", out var value3); Transform[] array = Refl.Get(ik, "_bones"); Transform val = Refl.Get(ik, "_root"); Refl.TryGetValue(ik, "_snapBackStrength", out var value4); Refl.TryGetValue(ik, "_iterations", out var value5); Refl.TryGetValue((object)ik, "_targetOffset", out Vector3 value6); Refl.TryGetValue(ik, "_stopResolving", out var value7); Transform val2 = Refl.Get(ik, "_pole"); sb.AppendLine($"{label} : enableStretch={value} chainLength={value2} " + string.Format("reach={0:F3}m root={1} target={2}", Mathf.Sqrt(Mathf.Max(0f, value3)), Path(val), Path(Refl.Get(ik, "Target")))); sb.AppendLine($" snapBack={value4:F2} iterations={value5} targetOffset={value6} " + $"stopResolving={value7}"); sb.AppendLine(" pole=" + Path(val2) + (((Object)(object)val2 != (Object)null && (Object)(object)val != (Object)null) ? $" poleInRootSpace={val.InverseTransformPoint(val2.position)}" : string.Empty)); if (array != null) { for (int i = 0; i < array.Length; i++) { sb.AppendLine($" bone[{i}] {Path(array[i])}"); } } } private static void Walk(StringBuilder sb, Transform t, int depth) { if ((Object)(object)t == (Object)null || depth > 12) { return; } string text = new string(' ', depth * 2); Component[] components = ((Component)t).GetComponents(); StringBuilder stringBuilder = new StringBuilder(); Component[] array = components; foreach (Component val in array) { if ((Object)(object)val == (Object)null || val is Transform) { continue; } if (stringBuilder.Length > 0) { stringBuilder.Append(", "); } stringBuilder.Append(((object)val).GetType().Name); Renderer val2 = (Renderer)(object)((val is Renderer) ? val : null); if (val2 != null) { stringBuilder.Append(val2.enabled ? "(on)" : "(off)"); continue; } MonoBehaviour val3 = (MonoBehaviour)(object)((val is MonoBehaviour) ? val : null); if (val3 != null) { stringBuilder.Append(((Behaviour)val3).enabled ? "(on)" : "(off)"); } } sb.AppendLine(text + ((Object)t).name + (((Component)t).gameObject.activeSelf ? "" : " [INACTIVE]") + ((stringBuilder.Length > 0) ? $" <{stringBuilder}>" : string.Empty)); for (int j = 0; j < t.childCount; j++) { Walk(sb, t.GetChild(j), depth + 1); } } private static string Path(Transform t) { if ((Object)(object)t == (Object)null) { return ""; } StringBuilder stringBuilder = new StringBuilder(((Object)t).name); Transform parent = t.parent; int num = 0; while ((Object)(object)parent != (Object)null && num++ < 32) { stringBuilder.Insert(0, ((Object)parent).name + "/"); parent = parent.parent; } return stringBuilder.ToString(); } } } namespace HowToFish.TrueFirstPerson.Patches { [HarmonyPatch(typeof(PlayerCamera), "SetCamPosRot")] internal static class PlayerCameraSetCamPosRotPatch { private static void Prefix(PlayerCamera __instance) { //IL_005f: Unknown result type (might be due to invalid IL or missing references) //IL_00b0: 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_00a3: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) ModConfig cfg = Plugin.Cfg; if (cfg == null || !cfg.Enabled.Value || (Object)(object)BodyDriver.Active == (Object)null || !BodyDriver.Active.IsApplied || !Refl.TryGetValue((object)__instance, "_rot", out Vector3 rot)) { return; } bool flag = ClampBoatYaw(cfg, ref rot); float num; if (BodyDriver.Active.DrivenBoatHeading.HasValue) { num = Mathf.Clamp(rot.x, 0f - cfg.BoatLookUpLimit.Value, cfg.BoatLookDownLimit.Value); } else { float value = cfg.MaxLookDownAngle.Value; if (value >= 90f) { if (flag) { Refl.Set(__instance, "_rot", rot); } return; } num = Mathf.Min(rot.x, value); } if (!Mathf.Approximately(num, rot.x)) { rot.x = num; flag = true; } if (flag) { Refl.Set(__instance, "_rot", rot); } } private static bool ClampBoatYaw(ModConfig cfg, ref Vector3 rot) { if (cfg.BoatYawLimit.Value >= 180f) { return false; } float? num = BodyDriver.Active?.DrivenBoatHeading; if (!num.HasValue) { return false; } float num2 = Mathf.DeltaAngle(num.Value, rot.y); float num3 = Mathf.Clamp(num2, 0f - cfg.BoatYawLimit.Value, cfg.BoatYawLimit.Value); if (Mathf.Approximately(num3, num2)) { return false; } rot.y = num.Value + num3; return true; } private static void Postfix(PlayerCamera __instance) { BodyDriver.Active?.SyncAfterCamera(__instance); } } [HarmonyPatch(typeof(DazedCommands), "IsServerCommand")] internal static class DazedCommandsPatch { private static bool Prefix(string __0, ref bool __result) { if (!ModCommands.TryHandle(__0)) { return true; } __result = true; return false; } } internal static class ModCommands { private const string Root = "tfp"; public static bool TryHandle(string raw) { if (string.IsNullOrEmpty(raw)) { return false; } string text = raw.Trim(); if (text.StartsWith("/")) { text = text.Substring(1); } string[] array = text.Split(new char[1] { ' ' }, StringSplitOptions.RemoveEmptyEntries); if (array.Length == 0) { return false; } if (!array[0].Equals("tfp", StringComparison.OrdinalIgnoreCase)) { return false; } string text2 = ((array.Length > 1) ? array[1].ToLowerInvariant() : "status"); ModConfig cfg = Plugin.Cfg; switch (text2) { case "status": Report(); return true; case "on": case "off": cfg.Enabled.Value = text2 == "on"; BodyDriver.Active?.Reapply(); Say("true first person " + text2); return true; case "dump": RigDiagnostics.DumpLocalPlayer(); Say("rig dumped to the BepInEx log"); return true; case "reload": BodyDriver.Active?.Reapply(); Say("reapplied"); return true; default: Say("commands: status, on, off, dump, reload"); return true; } } private static HeadMode NextHeadMode(HeadMode current) { return current switch { HeadMode.None => HeadMode.ShadowsOnly, HeadMode.ShadowsOnly => HeadMode.HideRenderer, HeadMode.HideRenderer => HeadMode.ShrinkBone, _ => HeadMode.None, }; } private static void Report() { //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_00fc: Unknown result type (might be due to invalid IL or missing references) BodyDriver active = BodyDriver.Active; ModConfig cfg = Plugin.Cfg; string text = ((active?.Rig != null) ? active.Rig.DescribeDriverState() : "no rig captured"); Say($"enabled={cfg.Enabled.Value} applied={active?.IsApplied} body={cfg.ShowBody.Value} " + $"head={cfg.HideHead.Value} jumpPose={cfg.JumpPoseOverride.Value}"); Say("rig drivers: " + text); Player localPlayer = Player.LocalPlayer; if ((Object)(object)localPlayer != (Object)null && (Object)(object)localPlayer.Camera != (Object)null && Refl.TryGetValue((object)localPlayer.Camera, "_rot", out Vector3 value)) { Say($"pitch _rot.x={value.x:F1} (positive should mean looking DOWN) yaw={value.y:F1}"); } if (active?.Rig != null) { string source; bool flag = DeathHider.IsDead(active.Rig, out source); Say($"dead={flag} source={source}"); } } private static void Say(string message) { Plugin.Log.LogMessage((object)("[tfp] " + message)); try { ChatManager.ChatMessage("[tfp] " + message); } catch { } } } [HarmonyPatch(typeof(IK), "LateUpdate")] internal static class IkLateUpdatePatch { private static bool Prefix(IK __instance) { if (IkRetimer.ShouldSuppress(__instance)) { return false; } BodyDriver.Active?.SyncBeforeIk(); return true; } } [HarmonyPatch(typeof(PlayerBody), "LateUpdate")] internal static class PlayerBodyLateUpdatePatch { private static void Postfix() { BodyDriver.Active?.SolveIkAfterBody(); } } [HarmonyPatch(typeof(Player), "InitializePlayer")] internal static class PlayerInitializePatch { private static List _stashedOthers; private static MonoBehaviour[] _stashedDisable; private static void Prefix(Player __instance) { _stashedOthers = null; _stashedDisable = null; ModConfig cfg = Plugin.Cfg; if (cfg != null && cfg.Enabled.Value && cfg.ShowBody.Value && IsLocal(__instance)) { StashOtherObjects(__instance); StashDisableList(__instance); } } private static void Postfix(Player __instance) { RestoreOtherObjects(__instance); RestoreDisableList(__instance); if ((Object)(object)BodyDriver.Active != (Object)null) { BodyDriver.Active.Reapply(); } } private static bool IsLocal(Player player) { try { return ((NetworkBehaviour)player).IsOwner; } catch { return (Object)(object)Player.LocalPlayer == (Object)(object)player; } } private static void StashOtherObjects(Player player) { List list = Refl.Get>(player, "_otherObjects"); if (list != null && list.Count != 0) { _stashedOthers = new List(list); list.Clear(); } } private static void RestoreOtherObjects(Player player) { if (_stashedOthers == null) { return; } Refl.Get>(player, "_otherObjects")?.AddRange(_stashedOthers); int num = 0; foreach (GameObject stashedOther in _stashedOthers) { if (!((Object)(object)stashedOther == (Object)null)) { stashedOther.SetActive(true); num++; } } Plugin.Log.LogInfo((object)((num > 0) ? $"[tfp] kept {num} character object(s) alive that the game would have destroyed" : "[tfp] the character objects were already gone before InitializePlayer ran")); _stashedOthers = null; } private static void StashDisableList(Player player) { MonoBehaviour[] array = Refl.Get(player, "_disableWhenLocal"); if (array == null || array.Length == 0) { return; } _stashedDisable = array; List list = new List(); MonoBehaviour[] array2 = array; foreach (MonoBehaviour val in array2) { if (!((Object)(object)val == (Object)null) && val is OtherPlayer) { list.Add(val); } } Refl.Set(player, "_disableWhenLocal", list.ToArray()); } private static void RestoreDisableList(Player player) { if (_stashedDisable != null) { Refl.Set(player, "_disableWhenLocal", _stashedDisable); _stashedDisable = null; } } } [HarmonyPatch(typeof(PlayerLegs), "Update")] internal static class PlayerLegsUpdatePatch { private static void Prefix() { BodyDriver.Active?.SyncBody(); } } [HarmonyPatch(typeof(PlayerSkin), "InitializeLocal")] internal static class PlayerSkinInitializeLocalPatch { private static void Postfix() { BodyDriver.Active?.Reapply(); } } }