Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of FroggosFPMotorcycle v1.0.0
Mods/Froggos-FP-Motorcycle.dll
Decompiled a day agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using HarmonyLib; using Il2CppFishNet.Object; using Il2CppInterop.Runtime.InteropTypes; using Il2CppInterop.Runtime.InteropTypes.Arrays; using Il2CppScheduleOne; using Il2CppScheduleOne.AvatarFramework; using Il2CppScheduleOne.AvatarFramework.Animation; using Il2CppScheduleOne.DevUtilities; using Il2CppScheduleOne.PlayerScripts; using Il2CppScheduleOne.Vehicles; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using MotoFirstPerson; using UnityEngine; using UnityEngine.InputSystem; using UnityEngine.InputSystem.Controls; using UnityEngine.Rendering; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: MelonInfo(typeof(MfpMod), "MotoFirstPerson", "1.0.0", "froggonater", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: MelonOptionalDependencies(new string[] { "Chassis", "ScheduleIBikePack" })] [assembly: TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName = ".NET 6.0")] [assembly: AssemblyCompany("MotoFirstPerson")] [assembly: AssemblyConfiguration("Release")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0")] [assembly: AssemblyProduct("MotoFirstPerson")] [assembly: AssemblyTitle("MotoFirstPerson")] [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 MotoFirstPerson { internal static class ChassisBridge { private static bool _resolved; private static PropertyInfo _firstPersonActive; private static MethodInfo _profileFor; public static bool Installed { get; private set; } public static bool FirstPersonActive { get { if (!Installed || _firstPersonActive == null) { return false; } try { return (bool)_firstPersonActive.GetValue(null); } catch { return false; } } } public static void Resolve() { if (_resolved) { return; } _resolved = true; try { Type type = null; Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach (Assembly assembly in assemblies) { if (!(assembly.GetName().Name != "Chassis")) { type = assembly.GetType("Chassis.Api.ChassisApi", throwOnError: false); if (type != null) { break; } } } if (!(type == null)) { _firstPersonActive = type.GetProperty("FirstPersonActive", BindingFlags.Static | BindingFlags.Public); _profileFor = type.GetMethod("For", BindingFlags.Static | BindingFlags.Public, null, new Type[1] { typeof(LandVehicle) }, null); Installed = true; MfpMod.Log.Msg($"Chassis found (FirstPersonActive {((_firstPersonActive != null) ? "ok" : "missing")}, For() {((_profileFor != null) ? "ok" : "missing")}). " + "Chassis keeps first person for its cars; this mod covers bikes and cars without a Chassis profile."); } } catch (Exception ex) { MfpMod.Log.Warning("Looking for Chassis failed, acting as if it is not installed: " + ex.Message); Installed = false; } } public static bool HasProfile(LandVehicle vehicle) { if (!Installed || _profileFor == null || (Object)(object)vehicle == (Object)null) { return false; } try { return _profileFor.Invoke(null, new object[1] { vehicle }) != null; } catch { return false; } } } internal enum RideKind { None, Bike, Car } internal static class FirstPersonCamera { private const float MaxYaw = 130f; private const float MinPitch = -70f; private const float MaxPitch = 65f; private const float MouseScale = 0.6f; private static readonly HashSet<string> BikeCodes = new HashSet<string>(StringComparer.OrdinalIgnoreCase) { "s1_offroad_motorcycle", "s1_street_motorcycle", "s1_cruiser_motorcycle", "s1_offroad_bicycle", "s1_street_bicycle" }; private static RideKind _kind; private static LandVehicle _vehicle; private static LandVehicle _lastVehicle; private static bool _wantFirstPerson; private static float _yaw; private static float _pitch; private static float _savedNearClip = -1f; private static AvatarEffects _effects; private static bool _disableHeadBefore; private static IntPtr _localAnimPtr; private static readonly List<KeyValuePair<Renderer, ShadowCastingMode>> _hidden = new List<KeyValuePair<Renderer, ShadowCastingMode>>(); private static bool _announced; private static bool _headMissingLogged; private static bool _placeFailed; public static bool Active { get; private set; } public static bool IsBikeCode(string code) { if (string.IsNullOrEmpty(code)) { return false; } if (!BikeCodes.Contains(code) && !code.EndsWith("_motorcycle", StringComparison.OrdinalIgnoreCase)) { return code.EndsWith("_bicycle", StringComparison.OrdinalIgnoreCase); } return true; } private static LandVehicle CurrentVehicle(Player local) { if ((Object)(object)local == (Object)null || !local.IsInVehicle) { return null; } NetworkObject currentVehicle = local.CurrentVehicle; if ((Object)(object)currentVehicle == (Object)null) { return null; } return ((Component)currentVehicle).GetComponent<LandVehicle>(); } private static RideKind Classify(LandVehicle lv) { if ((Object)(object)lv == (Object)null) { return RideKind.None; } if (ChassisBridge.HasProfile(lv)) { return RideKind.None; } if (IsBikeCode(lv.vehicleCode)) { return RideKind.Bike; } if (!MfpMod.Cfg.CarsWithoutChassis.Value) { return RideKind.None; } return RideKind.Car; } public static void Tick() { //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_0173: Unknown result type (might be due to invalid IL or missing references) //IL_0179: Invalid comparison between Unknown and I4 //IL_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_01a5: Unknown result type (might be due to invalid IL or missing references) //IL_01aa: Unknown result type (might be due to invalid IL or missing references) //IL_01b1: Unknown result type (might be due to invalid IL or missing references) //IL_01d4: 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) LandVehicle val = CurrentVehicle(Player.Local); if ((Object)(object)val != (Object)(object)_lastVehicle) { if (Active) { Set(on: false, "changed vehicle"); } _lastVehicle = val; if ((Object)(object)val != (Object)null && MfpMod.Cfg.RememberView.Value && _wantFirstPerson && Classify(val) != RideKind.None) { Set(on: true, "remembered view"); } } if (Active && ((Object)(object)val == (Object)null || (Object)(object)val != (Object)(object)_vehicle)) { Set(on: false, "not riding"); } else if (Active && ChassisBridge.FirstPersonActive) { Set(on: false, "Chassis first person took over"); } else { if ((Object)(object)val == (Object)null) { return; } if (!GameInput.IsTyping) { Keyboard current = Keyboard.current; if (current != null && ((ButtonControl)current[MfpMod.Cfg.Toggle]).wasPressedThisFrame) { if (Active) { Set(on: false, $"{MfpMod.Cfg.Toggle} pressed"); } else if (Classify(val) != RideKind.None) { Set(on: true, $"{MfpMod.Cfg.Toggle} pressed"); } _wantFirstPerson = Active; } } if (Active && !GameInput.IsTyping && (int)Cursor.lockState == 1) { float num = 0.6f * MfpMod.Cfg.LookSensitivity.Value; if (Singleton<Settings>.InstanceExists) { num *= Singleton<Settings>.Instance.LookSensitivity; } Vector2 mouseDelta = GameInput.MouseDelta; _yaw = Mathf.Clamp(_yaw + mouseDelta.x * num, -130f, 130f); _pitch = Mathf.Clamp(_pitch - mouseDelta.y * num, -70f, 65f); } } } public static void Reset() { if (Active) { Set(on: false, "world unloaded"); } _lastVehicle = null; } public static void Set(bool on, string why) { if (on == Active) { return; } Player local = Player.Local; if (on) { LandVehicle val = CurrentVehicle(local); RideKind rideKind = Classify(val); if (rideKind == RideKind.None) { return; } _vehicle = val; _kind = rideKind; } Active = on; _yaw = 0f; _pitch = 0f; _announced = false; _placeFailed = false; Camera val2 = (PlayerSingleton<PlayerCamera>.InstanceExists ? PlayerSingleton<PlayerCamera>.Instance.Camera : null); if ((Object)(object)val2 != (Object)null) { if (on) { _savedNearClip = val2.nearClipPlane; val2.nearClipPlane = Mathf.Min(val2.nearClipPlane, MfpMod.Cfg.NearClip.Value); } else if (_savedNearClip > 0f) { val2.nearClipPlane = _savedNearClip; } } if (on) { Avatar val3 = (((Object)(object)local != (Object)null) ? local.Avatar : null); _localAnimPtr = (((Object)(object)val3 != (Object)null && (Object)(object)val3.Animation != (Object)null) ? ((Il2CppObjectBase)val3.Animation).Pointer : IntPtr.Zero); if (_kind == RideKind.Bike) { HideHead(val3); } } else { ShowHead(); _vehicle = null; _kind = RideKind.None; _localAnimPtr = IntPtr.Zero; } string value = ((on && (Object)(object)_vehicle != (Object)null) ? _vehicle.vehicleCode : ""); MfpMod.Log.Msg($"First person {(on ? "on" : "off")} ({why}){(on ? $" on {value} as {_kind}" : "")}."); } private static void HideHead(Avatar avatar) { //IL_0054: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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_0087: Invalid comparison between Unknown and I4 //IL_008a: Unknown result type (might be due to invalid IL or missing references) //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_00a7: 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_00c3: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)avatar == (Object)null) { return; } _effects = avatar.Effects; Transform val = null; if ((Object)(object)_effects != (Object)null) { _disableHeadBefore = _effects.DisableHead; _effects.DisableHead = true; val = _effects.HeadBone; } if ((Object)(object)val == (Object)null) { return; } Vector3 position = val.position; foreach (Renderer componentsInChild in ((Component)avatar).GetComponentsInChildren<Renderer>()) { if ((Object)(object)componentsInChild == (Object)null || !componentsInChild.enabled || (int)componentsInChild.shadowCastingMode == 3) { continue; } Bounds bounds = componentsInChild.bounds; if (!(Vector3.Distance(((Bounds)(ref bounds)).center, position) > 0.3f)) { Vector3 extents = ((Bounds)(ref bounds)).extents; if (!(((Vector3)(ref extents)).magnitude > 0.4f)) { _hidden.Add(new KeyValuePair<Renderer, ShadowCastingMode>(componentsInChild, componentsInChild.shadowCastingMode)); componentsInChild.shadowCastingMode = (ShadowCastingMode)3; } } } if (MfpMod.Cfg.VerboseLog.Value) { MfpMod.Log.Msg($"Head hidden: DisableHead set, {_hidden.Count} head part(s) shadow-only."); } } private static void ShowHead() { //IL_004f: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)_effects != (Object)null) { _effects.DisableHead = _disableHeadBefore; } _effects = null; foreach (KeyValuePair<Renderer, ShadowCastingMode> item in _hidden) { if ((Object)(object)item.Key != (Object)null) { item.Key.shadowCastingMode = item.Value; } } _hidden.Clear(); } public static void Place(string from) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0047: Unknown result type (might be due to invalid IL or missing references) //IL_0048: 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) //IL_004b: 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_0071: 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_0086: Unknown result type (might be due to invalid IL or missing references) //IL_008b: Unknown result type (might be due to invalid IL or missing references) //IL_0090: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_0098: Unknown result type (might be due to invalid IL or missing references) //IL_0152: Unknown result type (might be due to invalid IL or missing references) //IL_0149: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Unknown result type (might be due to invalid IL or missing references) //IL_00bd: 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_00e6: Unknown result type (might be due to invalid IL or missing references) //IL_00eb: Unknown result type (might be due to invalid IL or missing references) //IL_00f0: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Unknown result type (might be due to invalid IL or missing references) //IL_017b: Unknown result type (might be due to invalid IL or missing references) //IL_0180: Unknown result type (might be due to invalid IL or missing references) //IL_0185: Unknown result type (might be due to invalid IL or missing references) //IL_018a: Unknown result type (might be due to invalid IL or missing references) //IL_0123: Unknown result type (might be due to invalid IL or missing references) //IL_0128: Unknown result type (might be due to invalid IL or missing references) //IL_012d: Unknown result type (might be due to invalid IL or missing references) //IL_018c: Unknown result type (might be due to invalid IL or missing references) //IL_019c: Unknown result type (might be due to invalid IL or missing references) //IL_01a1: Unknown result type (might be due to invalid IL or missing references) //IL_01b5: Unknown result type (might be due to invalid IL or missing references) //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01bf: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) //IL_01d5: Unknown result type (might be due to invalid IL or missing references) //IL_020a: 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_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Unknown result type (might be due to invalid IL or missing references) //IL_021f: Unknown result type (might be due to invalid IL or missing references) //IL_0224: Unknown result type (might be due to invalid IL or missing references) //IL_0231: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Unknown result type (might be due to invalid IL or missing references) //IL_023a: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Unknown result type (might be due to invalid IL or missing references) //IL_0244: Unknown result type (might be due to invalid IL or missing references) //IL_02a3: Unknown result type (might be due to invalid IL or missing references) //IL_02be: Unknown result type (might be due to invalid IL or missing references) //IL_02c0: Unknown result type (might be due to invalid IL or missing references) //IL_02ea: Unknown result type (might be due to invalid IL or missing references) //IL_02ef: Unknown result type (might be due to invalid IL or missing references) //IL_02f3: Unknown result type (might be due to invalid IL or missing references) if (!Active || (Object)(object)_vehicle == (Object)null) { return; } try { if (!PlayerSingleton<PlayerCamera>.InstanceExists) { return; } Player local = Player.Local; if ((Object)(object)local == (Object)null) { return; } Transform transform = ((Component)_vehicle).transform; Quaternion rotation = transform.rotation; Quaternion val = rotation; Vector3 forward = transform.forward; float num = Mathf.Clamp01(MfpMod.Cfg.FollowLean.Value); if (num < 0.999f && Mathf.Abs(forward.y) < 0.95f) { val = Quaternion.Slerp(Quaternion.LookRotation(forward, Vector3.up), rotation, num); } Vector3 val3; if (_kind == RideKind.Bike) { Transform val2 = HeadOf(local); if ((Object)(object)val2 != (Object)null) { val3 = val2.position + rotation * new Vector3(0f, MfpMod.Cfg.BikeEyeUp.Value, MfpMod.Cfg.BikeEyeForward.Value); } else { if (!_headMissingLogged) { _headMissingLogged = true; MfpMod.Log.Warning("Rider head bone not found; using a fixed eye point above the bike instead."); } val3 = transform.TransformPoint(new Vector3(0f, 1.45f, -0.15f)); } } else { Transform val4 = SeatOf(local, _vehicle); val3 = (((Object)(object)val4 != (Object)null) ? val4.position : transform.position) + rotation * new Vector3(0f, MfpMod.Cfg.CarEyeUp.Value, MfpMod.Cfg.CarEyeForward.Value); } Quaternion val5 = val * Quaternion.Euler(0f, _yaw, 0f) * Quaternion.Euler(_pitch, 0f, 0f); PlayerCamera instance = PlayerSingleton<PlayerCamera>.Instance; Transform transform2 = ((Component)instance).transform; transform2.SetPositionAndRotation(val3, val5); Transform val6 = (((Object)(object)instance.Camera != (Object)null) ? ((Component)instance.Camera).transform : transform2); if ((Object)(object)val6 != (Object)(object)transform2) { transform2.rotation = val5 * Quaternion.Inverse(val6.rotation) * transform2.rotation; transform2.position += val3 - val6.position; } if (!_announced) { _announced = true; if (MfpMod.Cfg.VerboseLog.Value) { Instance log = MfpMod.Log; DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(77, 5); defaultInterpolatedStringHandler.AppendLiteral("Camera placed from "); defaultInterpolatedStringHandler.AppendFormatted(from); defaultInterpolatedStringHandler.AppendLiteral(": eye "); defaultInterpolatedStringHandler.AppendFormatted<Vector3>(val3, "F2"); defaultInterpolatedStringHandler.AppendLiteral(" (vehicle-local "); defaultInterpolatedStringHandler.AppendFormatted<Vector3>(transform.InverseTransformPoint(val3), "F2"); defaultInterpolatedStringHandler.AppendLiteral("), "); defaultInterpolatedStringHandler.AppendLiteral("vehicle rot "); Quaternion rotation2 = transform.rotation; defaultInterpolatedStringHandler.AppendFormatted<Vector3>(((Quaternion)(ref rotation2)).eulerAngles, "F0"); defaultInterpolatedStringHandler.AppendLiteral(", lens "); defaultInterpolatedStringHandler.AppendFormatted(((Object)(object)val6 == (Object)(object)transform2) ? "is" : "under"); defaultInterpolatedStringHandler.AppendLiteral(" PlayerCamera."); log.Msg(defaultInterpolatedStringHandler.ToStringAndClear()); } } _placeFailed = false; } catch (Exception ex) { if (!_placeFailed) { MfpMod.Log.Error("Placing the first person camera failed, back to the normal camera: " + ex); } _placeFailed = true; Set(on: false, "camera error"); } } private static Transform HeadOf(Player local) { Avatar avatar = local.Avatar; if ((Object)(object)avatar == (Object)null) { return null; } AvatarEffects effects = avatar.Effects; if (!((Object)(object)effects != (Object)null)) { return null; } return effects.HeadBone; } private static Transform SeatOf(Player local, LandVehicle lv) { VehicleSeat localPlayerSeat = lv.localPlayerSeat; if ((Object)(object)localPlayerSeat != (Object)null) { return ((Component)localPlayerSeat).transform; } Il2CppReferenceArray<VehicleSeat> seats = lv.Seats; if (seats == null) { return null; } foreach (VehicleSeat item in (Il2CppArrayBase<VehicleSeat>)(object)seats) { if ((Object)(object)item != (Object)null && (Object)(object)item.Occupant != (Object)null && ((Il2CppObjectBase)item.Occupant).Pointer == ((Il2CppObjectBase)local).Pointer) { return ((Component)item).transform; } } return null; } public static bool IsLocalAnimation(AvatarAnimation anim) { if (Active && _kind == RideKind.Bike && (Object)(object)anim != (Object)null && _localAnimPtr != IntPtr.Zero) { return ((Il2CppObjectBase)anim).Pointer == _localAnimPtr; } return false; } } [HarmonyPatch(typeof(VehicleCamera), "LateUpdate")] internal static class VehicleCameraLatePatch { [HarmonyPriority(0)] private static void Postfix() { if (FirstPersonCamera.Active) { FirstPersonCamera.Place("vehicle camera"); } } } [HarmonyPatch(typeof(AvatarAnimation), "LateUpdate")] internal static class AvatarAnimationLatePatch { [HarmonyPriority(0)] private static void Postfix(AvatarAnimation __instance) { if (FirstPersonCamera.IsLocalAnimation(__instance)) { FirstPersonCamera.Place("rider pose"); } } } [HarmonyPatch(typeof(Player), "ExitVehicle")] internal static class ExitVehiclePatch { private static void Prefix(Player __instance) { try { if ((Object)(object)__instance != (Object)null && __instance.IsLocalPlayer && FirstPersonCamera.Active) { FirstPersonCamera.Set(on: false, "exited vehicle"); } } catch (Exception ex) { MfpMod.Log.Error("Leaving first person on exit failed: " + ex.Message); } } } internal class MfpConfig { public readonly MelonPreferences_Category Category; public readonly MelonPreferences_Entry<string> ToggleKey; public readonly MelonPreferences_Entry<bool> RememberView; public readonly MelonPreferences_Entry<bool> CarsWithoutChassis; public readonly MelonPreferences_Entry<float> FollowLean; public readonly MelonPreferences_Entry<float> LookSensitivity; public readonly MelonPreferences_Entry<float> BikeEyeUp; public readonly MelonPreferences_Entry<float> BikeEyeForward; public readonly MelonPreferences_Entry<float> CarEyeUp; public readonly MelonPreferences_Entry<float> CarEyeForward; public readonly MelonPreferences_Entry<float> NearClip; public readonly MelonPreferences_Entry<bool> VerboseLog; public Key Toggle { get; private set; } = (Key)36; public MfpConfig() { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_01cf: Unknown result type (might be due to invalid IL or missing references) //IL_01d3: Unknown result type (might be due to invalid IL or missing references) Category = MelonPreferences.CreateCategory("MotoFirstPerson", "MotoFirstPerson"); ToggleKey = Category.CreateEntry<string>("ToggleKey", "V", (string)null, "Key that switches first person on/off while riding (UnityEngine.InputSystem.Key name, e.g. V, F6, C). V matches Chassis.", false, false, (ValueValidator)null, (string)null); RememberView = Category.CreateEntry<bool>("RememberView", true, (string)null, "Start in first person when you get on a bike if you left the last one in first person.", false, false, (ValueValidator)null, (string)null); CarsWithoutChassis = Category.CreateEntry<bool>("CarsWithoutChassis", true, (string)null, "Also give cars a first person view when Chassis is not installed (or has no profile for that car). With Chassis installed, its own view is used for its cars.", false, false, (ValueValidator)null, (string)null); FollowLean = Category.CreateEntry<float>("FollowLean", 1f, (string)null, "How much the view tilts with the bike in corners: 0 = horizon stays level, 1 = lean fully with the bike.", false, false, (ValueValidator)null, (string)null); LookSensitivity = Category.CreateEntry<float>("LookSensitivity", 1f, (string)null, "Mouse look speed in first person (multiplied by the game's own look sensitivity).", false, false, (ValueValidator)null, (string)null); BikeEyeUp = Category.CreateEntry<float>("BikeEyeUp", 0.08f, (string)null, "Bikes: eye height above the rider's head bone (meters).", false, false, (ValueValidator)null, (string)null); BikeEyeForward = Category.CreateEntry<float>("BikeEyeForward", 0.1f, (string)null, "Bikes: eye distance in front of the rider's head bone (meters).", false, false, (ValueValidator)null, (string)null); CarEyeUp = Category.CreateEntry<float>("CarEyeUp", 0.7f, (string)null, "Cars without Chassis: eye height above the seat point (meters).", false, false, (ValueValidator)null, (string)null); CarEyeForward = Category.CreateEntry<float>("CarEyeForward", 0f, (string)null, "Cars without Chassis: eye offset forward from the seat point (meters).", false, false, (ValueValidator)null, (string)null); NearClip = Category.CreateEntry<float>("NearClip", 0.03f, (string)null, "Camera near clip plane in first person, so the handlebars/dashboard right in front of you are not cut off.", false, false, (ValueValidator)null, (string)null); VerboseLog = Category.CreateEntry<bool>("VerboseLog", false, (string)null, "Log extra detail (camera placement) for troubleshooting.", false, false, (ValueValidator)null, (string)null); if (Enum.TryParse<Key>(ToggleKey.Value?.Trim(), ignoreCase: true, out Key result) && (int)result != 0) { Toggle = result; } else { MfpMod.Log.Warning("ToggleKey '" + ToggleKey.Value + "' is not a valid key name, using V."); } } } public class MfpMod : MelonMod { internal static Instance Log; internal static MfpConfig Cfg; private static bool _tickFailed; public override void OnInitializeMelon() { //IL_0037: Unknown result type (might be due to invalid IL or missing references) Log = ((MelonBase)this).LoggerInstance; Cfg = new MfpConfig(); Log.Msg($"MotoFirstPerson loaded. Press [{Cfg.Toggle}] on a motorcycle for first person."); } public override void OnLateInitializeMelon() { ChassisBridge.Resolve(); if (!ChassisBridge.Installed) { Log.Msg(Cfg.CarsWithoutChassis.Value ? "Chassis not installed: this mod also gives cars a first person view." : "Chassis not installed and CarsWithoutChassis is off: first person on bikes only."); } } public override void OnUpdate() { try { FirstPersonCamera.Tick(); _tickFailed = false; } catch (Exception ex) { if (!_tickFailed) { Log.Error("Update failed (logged once until it works again): " + ex); } _tickFailed = true; } } public override void OnSceneWasUnloaded(int buildIndex, string sceneName) { FirstPersonCamera.Reset(); } } }