using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using BepInEx; using BepInEx.Configuration; using BepInEx.Logging; using HarmonyLib; using Microsoft.CodeAnalysis; using Photon.Pun; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("0.0.0.0")] [module: UnverifiableCode] [module: RefSafetyRules(11)] namespace Microsoft.CodeAnalysis { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] internal sealed class EmbeddedAttribute : Attribute { } } namespace System.Runtime.CompilerServices { [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace REPOJP.ScoutTwoSeater { [HarmonyPatch] internal static class ScoutTwoSeaterPatches { private sealed class PassengerInfo { public PlayerAvatar Player; public GameObject Anchor; public float MountTime; public HashSet PrevFrameGrabbers = new HashSet(); public bool HasReleasedGrabSinceMount; } private static readonly Dictionary Vehicles = new Dictionary(); private static bool IsHostAuthority() { return SemiFunc.IsMasterClientOrSingleplayer(); } [HarmonyPostfix] [HarmonyPatch(typeof(ItemVehicle), "Start")] private static void Start_Postfix(ItemVehicle __instance) { //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Expected O, but got Unknown //IL_0050: 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) if (!IsHostAuthority() || !ScoutTwoSeaterPlugin.CfgEnabled.Value || (Object)(object)__instance == (Object)null || Vehicles.ContainsKey(__instance)) { return; } try { GameObject val = new GameObject("ScoutTwoSeater_Anchor"); val.transform.SetParent(((Component)__instance).transform, false); val.transform.localPosition = ScoutTwoSeaterPlugin.GetPoseLocalPosition(); val.transform.localRotation = Quaternion.identity; Vehicles[__instance] = new PassengerInfo { Player = null, Anchor = val, MountTime = 0f }; if (ScoutTwoSeaterPlugin.CfgVerboseLog.Value) { ScoutTwoSeaterPlugin.Log.LogInfo((object)$"[ScoutTwoSeater] Registered vehicle {((Object)__instance).GetInstanceID()}"); } } catch (Exception ex) { ScoutTwoSeaterPlugin.Log.LogError((object)("[ScoutTwoSeater] Start postfix failed: " + ex)); } } [HarmonyPostfix] [HarmonyPatch(typeof(ItemVehicle), "Update")] private static void Update_Postfix(ItemVehicle __instance) { //IL_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004f: 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_0073: Unknown result type (might be due to invalid IL or missing references) if (!IsHostAuthority() || !ScoutTwoSeaterPlugin.CfgEnabled.Value || (Object)(object)__instance == (Object)null || !Vehicles.TryGetValue(__instance, out var value)) { return; } if ((Object)(object)value.Anchor == (Object)null) { Vehicles.Remove(__instance); return; } Vector3 poseLocalPosition = ScoutTwoSeaterPlugin.GetPoseLocalPosition(); if (value.Anchor.transform.localPosition != poseLocalPosition) { value.Anchor.transform.localPosition = poseLocalPosition; } if ((Object)(object)value.Player == (Object)null) { TryMountNew(__instance, value); } else { MaintainPassenger(__instance, value); } } [HarmonyPostfix] [HarmonyPatch(typeof(ItemVehicle), "FixedUpdate")] private static void FixedUpdate_Postfix(ItemVehicle __instance) { if (IsHostAuthority() && ScoutTwoSeaterPlugin.CfgEnabled.Value && !((Object)(object)__instance == (Object)null) && Vehicles.TryGetValue(__instance, out var value) && !((Object)(object)value.Player == (Object)null) && !((Object)(object)value.Anchor == (Object)null) && !((Object)(object)value.Player.tumble == (Object)null) && !((Object)(object)value.Player.tumble.physGrabObject == (Object)null) && !((Object)(object)__instance.rb == (Object)null)) { ApplyPullForce(__instance, value.Player, value.Anchor.transform); } } private static void ApplyPullForce(ItemVehicle vehicle, PlayerAvatar player, Transform anchor) { //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Unknown result type (might be due to invalid IL or missing references) //IL_0106: Unknown result type (might be due to invalid IL or missing references) //IL_0109: Unknown result type (might be due to invalid IL or missing references) //IL_0111: Unknown result type (might be due to invalid IL or missing references) //IL_0117: Unknown result type (might be due to invalid IL or missing references) //IL_011c: Unknown result type (might be due to invalid IL or missing references) //IL_0121: Unknown result type (might be due to invalid IL or missing references) //IL_0125: Unknown result type (might be due to invalid IL or missing references) //IL_012a: Unknown result type (might be due to invalid IL or missing references) //IL_0131: Unknown result type (might be due to invalid IL or missing references) //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_013d: 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_0159: Unknown result type (might be due to invalid IL or missing references) //IL_015e: Unknown result type (might be due to invalid IL or missing references) //IL_0166: Unknown result type (might be due to invalid IL or missing references) //IL_016b: Unknown result type (might be due to invalid IL or missing references) //IL_0170: 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) PlayerTumble tumble = player.tumble; PhysGrabObject physGrabObject = tumble.physGrabObject; Rigidbody rb = tumble.rb; Rigidbody rb2 = vehicle.rb; if (!tumble.isTumbling) { tumble.TumbleRequest(true, false); } tumble.TumbleOverrideTime(0.5f); physGrabObject.OverrideMaterial(SemiFunc.PhysicMaterialSlippery(), 0.1f); physGrabObject.OverrideZeroGravity(0.2f); if ((Object)(object)physGrabObject.impactDetector != (Object)null) { physGrabObject.impactDetector.ImpactDisable(0.2f); } tumble.OverrideEnemyHurt(0.1f); if ((Object)(object)player.physGrabber != (Object)null && player.isLocal) { player.physGrabber.OverrideDisableSpecialGrabPowers(1f); tumble.OverrideTumbleUIDisable(0.2f); } PullStrength value = ScoutTwoSeaterPlugin.CfgPullStrength.Value; float num; float num2; float num3; if (value != 0 && value == PullStrength.Strong) { num = 1f; num2 = 0.5f; num3 = 1f; } else { num = 0.05f; num2 = 0.08f; num3 = 0.5f; } physGrabObject.OverrideMass(num, 0.1f); Vector3 pointVelocity = rb2.GetPointVelocity(rb.position); rb.velocity = pointVelocity; Vector3 val = anchor.position - rb.position; rb.velocity += val / Time.fixedDeltaTime * num2; rb.angularVelocity = rb2.angularVelocity; Quaternion rotation = ((Component)player).transform.rotation; Vector3 val2 = SemiFunc.PhysFollowRotation(((Component)tumble).transform, rotation, rb, num3); rb.AddTorque(val2, (ForceMode)1); } private static void TryMountNew(ItemVehicle vehicle, PassengerInfo info) { //IL_0212: Unknown result type (might be due to invalid IL or missing references) //IL_0218: Invalid comparison between Unknown and I4 //IL_0114: Unknown result type (might be due to invalid IL or missing references) //IL_0119: Unknown result type (might be due to invalid IL or missing references) //IL_0121: 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) PhysGrabObject component = ((Component)vehicle).GetComponent(); if ((Object)(object)component == (Object)null || component.playerGrabbing == null) { info.PrevFrameGrabbers.Clear(); return; } PlayerAvatar val = null; if (vehicle.seats != null && vehicle.seats.Length != 0 && vehicle.seats[0] != null) { val = vehicle.seats[0].seatedPlayer; } float value = ScoutTwoSeaterPlugin.CfgDriverSideThresholdZ.Value; HashSet hashSet = new HashSet(); List list = new List(); List list2 = new List(); for (int i = 0; i < component.playerGrabbing.Count; i++) { PhysGrabber val2 = component.playerGrabbing[i]; if ((Object)(object)val2 == (Object)null) { continue; } PlayerAvatar playerAvatar = val2.playerAvatar; if ((Object)(object)playerAvatar == (Object)null || (Object)(object)playerAvatar == (Object)(object)val || playerAvatar.isDisabled || (Object)(object)playerAvatar.tumble == (Object)null) { continue; } hashSet.Add(playerAvatar); if (info.PrevFrameGrabbers.Contains(playerAvatar)) { continue; } bool flag = false; float num = 0f; try { if ((Object)(object)val2.physGrabPoint != (Object)null) { Vector3 position = val2.physGrabPoint.position; num = ((Component)vehicle).transform.InverseTransformPoint(position).z; flag = num > value; if (ScoutTwoSeaterPlugin.CfgVerboseLog.Value) { ScoutTwoSeaterPlugin.Log.LogInfo((object)string.Format("[ScoutTwoSeater] {0} NEW GRAB localZ={1:F2} threshold={2:F2} → {3}", playerAvatar.playerName, num, value, flag ? "FRONT(car-head)" : "REAR(car-tail)")); } } else { if (ScoutTwoSeaterPlugin.CfgVerboseLog.Value) { ScoutTwoSeaterPlugin.Log.LogWarning((object)("[ScoutTwoSeater] " + playerAvatar.playerName + " NEW GRAB but physGrabPoint is null - treating as FRONT")); } flag = true; } } catch (Exception ex) { ScoutTwoSeaterPlugin.Log.LogWarning((object)("[ScoutTwoSeater] Grab point check error: " + ex.Message)); flag = true; } if (flag) { list.Add(playerAvatar); } else { list2.Add(playerAvatar); } } if ((int)vehicle.currentState != 2 && !vehicle.isFullyDestroyed) { using List.Enumerator enumerator = list2.GetEnumerator(); if (enumerator.MoveNext()) { PlayerAvatar current = enumerator.Current; Mount(vehicle, info, current); info.PrevFrameGrabbers = hashSet; return; } } info.PrevFrameGrabbers = hashSet; } private static void Mount(ItemVehicle vehicle, PassengerInfo info, PlayerAvatar player) { info.Player = player; info.MountTime = Time.time; info.HasReleasedGrabSinceMount = false; if ((Object)(object)player.tumble != (Object)null) { player.tumble.TumbleRequest(true, false); player.tumble.TumbleOverrideTime(2f); } if ((Object)(object)player.physGrabber != (Object)null) { try { player.physGrabber.OverrideGrabRelease(-1, 0.5f); } catch { } } if (ScoutTwoSeaterPlugin.CfgForceUnequipOnMount.Value && (Object)(object)player.physGrabber != (Object)null && (Object)(object)player.physGrabber.grabbedPhysGrabObject != (Object)null) { try { player.physGrabber.OverrideGrabRelease(-1, 0.5f); } catch { } } if (ScoutTwoSeaterPlugin.CfgVerboseLog.Value) { ScoutTwoSeaterPlugin.Log.LogInfo((object)$"[ScoutTwoSeater] MOUNT: {player.playerName} -> vehicle {((Object)vehicle).GetInstanceID()}"); } } private static void MaintainPassenger(ItemVehicle vehicle, PassengerInfo info) { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003a: Invalid comparison between Unknown and I4 //IL_010e: Unknown result type (might be due to invalid IL or missing references) //IL_011e: Unknown result type (might be due to invalid IL or missing references) PlayerAvatar player = info.Player; if ((Object)(object)player == (Object)null || player.isDisabled || (Object)(object)player.tumble == (Object)null) { Dismount(vehicle, info, "passenger disabled"); return; } if ((int)vehicle.currentState == 2 || vehicle.isFullyDestroyed) { Dismount(vehicle, info, "vehicle tumbled or destroyed"); return; } if (vehicle.seats != null && vehicle.seats.Length != 0 && vehicle.seats[0] != null && (Object)(object)vehicle.seats[0].seatedPlayer == (Object)(object)player) { try { if ((Object)(object)vehicle.photonView != (Object)null) { vehicle.photonView.RPC("ClearSeatRPC", (RpcTarget)0, new object[1] { 0 }); } if (ScoutTwoSeaterPlugin.CfgVerboseLog.Value) { ScoutTwoSeaterPlugin.Log.LogInfo((object)("[ScoutTwoSeater] Rescued " + player.playerName + " from driver seat")); } } catch { } } if ((Object)(object)info.Anchor != (Object)null && (Object)(object)player.tumble.rb != (Object)null) { float num = Vector3.Distance(player.tumble.rb.position, info.Anchor.transform.position); if (num > ScoutTwoSeaterPlugin.CfgAutoDismountDistance.Value) { Dismount(vehicle, info, $"distance {num:F1}m"); return; } } bool flag = false; PhysGrabObject component = ((Component)vehicle).GetComponent(); if ((Object)(object)component != (Object)null && component.playerGrabbing != null) { for (int i = 0; i < component.playerGrabbing.Count; i++) { PhysGrabber val = component.playerGrabbing[i]; if ((Object)(object)val != (Object)null && (Object)(object)val.playerAvatar == (Object)(object)player) { flag = true; break; } } } if (!flag && Time.time - info.MountTime >= 0.3f) { info.HasReleasedGrabSinceMount = true; } if (!(info.HasReleasedGrabSinceMount && flag) || !(Time.time - info.MountTime >= ScoutTwoSeaterPlugin.CfgMountDebounceSeconds.Value)) { return; } if ((Object)(object)player.physGrabber != (Object)null) { try { player.physGrabber.OverrideGrabRelease(-1, 0.5f); } catch { } } Dismount(vehicle, info, "re-grab after release"); } private static void Dismount(ItemVehicle vehicle, PassengerInfo info, string reason) { PlayerAvatar player = info.Player; info.Player = null; if ((Object)(object)player != (Object)null && (Object)(object)player.tumble != (Object)null && player.tumble.isTumbling) { try { player.tumble.TumbleRequest(false, false); } catch { } } if (ScoutTwoSeaterPlugin.CfgVerboseLog.Value && (Object)(object)player != (Object)null) { ScoutTwoSeaterPlugin.Log.LogInfo((object)("[ScoutTwoSeater] DISMOUNT: " + player.playerName + " (" + reason + ")")); } } internal static void HostSelfMountToggle() { //IL_00fe: Unknown result type (might be due to invalid IL or missing references) //IL_0104: Invalid comparison between Unknown and I4 //IL_011a: Unknown result type (might be due to invalid IL or missing references) //IL_012b: Unknown result type (might be due to invalid IL or missing references) if (!IsHostAuthority() || !ScoutTwoSeaterPlugin.CfgEnabled.Value) { return; } PlayerAvatar val = SemiFunc.PlayerGetLocal(); if ((Object)(object)val == (Object)null) { ScoutTwoSeaterPlugin.Log.LogInfo((object)"[ScoutTwoSeater] Hotkey: no local player"); return; } foreach (KeyValuePair vehicle in Vehicles) { if (vehicle.Value != null && (Object)(object)vehicle.Value.Player == (Object)(object)val) { Dismount(vehicle.Key, vehicle.Value, "hotkey toggle (dismount)"); return; } } ItemVehicle val2 = null; float num = float.MaxValue; float value = ScoutTwoSeaterPlugin.CfgHostSelfMountRange.Value; foreach (KeyValuePair vehicle2 in Vehicles) { if (!((Object)(object)vehicle2.Key == (Object)null) && vehicle2.Value != null && !((Object)(object)vehicle2.Value.Player != (Object)null) && (int)vehicle2.Key.currentState != 2 && !vehicle2.Key.isFullyDestroyed) { float num2 = Vector3.Distance(((Component)val).transform.position, ((Component)vehicle2.Key).transform.position); if (num2 < num && num2 <= value) { num = num2; val2 = vehicle2.Key; } } } if ((Object)(object)val2 == (Object)null) { ScoutTwoSeaterPlugin.Log.LogInfo((object)$"[ScoutTwoSeater] Hotkey: no Scout within {value}m"); } else if (val2.seats != null && val2.seats.Length != 0 && val2.seats[0] != null && (Object)(object)val2.seats[0].seatedPlayer == (Object)(object)val) { ScoutTwoSeaterPlugin.Log.LogInfo((object)"[ScoutTwoSeater] Hotkey: host is driver, cannot self-mount"); } else { Mount(val2, Vehicles[val2], val); } } [HarmonyPostfix] [HarmonyPatch(typeof(ItemVehicle), "OnDestroy")] private static void OnDestroy_Postfix(ItemVehicle __instance) { if (!((Object)(object)__instance == (Object)null) && Vehicles.TryGetValue(__instance, out var value)) { if ((Object)(object)value.Anchor != (Object)null) { Object.Destroy((Object)(object)value.Anchor); } Vehicles.Remove(__instance); } } } public enum PassengerPose { Back, Front } public enum PullStrength { Weak, Strong } public enum Language { EN, CN } [BepInPlugin("REPOJP.ScoutTwoSeater", "ScoutTwoSeater", "1.0.0")] public sealed class ScoutTwoSeaterPlugin : BaseUnityPlugin { public const string PluginGuid = "REPOJP.ScoutTwoSeater"; public const string PluginName = "ScoutTwoSeater"; public const string PluginVersion = "1.0.0"; internal static ManualLogSource Log; internal static ScoutTwoSeaterPlugin Instance; private Harmony harmony; internal static ConfigEntry CfgLanguage; internal static ConfigEntry CfgEnabled; internal static ConfigEntry CfgPose; internal static ConfigEntry CfgForceUnequipOnMount; internal static ConfigEntry CfgMountDebounceSeconds; internal static ConfigEntry CfgAutoDismountDistance; internal static ConfigEntry CfgVerboseLog; internal static ConfigEntry CfgHostHotkeyEnabled; internal static ConfigEntry CfgHostSelfMountKey; internal static ConfigEntry CfgHostSelfMountRange; internal static ConfigEntry CfgDriverSideThresholdZ; internal static ConfigEntry CfgPullStrength; internal static ConfigEntry CfgBackX; internal static ConfigEntry CfgBackY; internal static ConfigEntry CfgBackZ; internal static ConfigEntry CfgFrontX; internal static ConfigEntry CfgFrontY; internal static ConfigEntry CfgFrontZ; private float cfgReloadAccum; private const float CfgReloadInterval = 2f; private void Awake() { //IL_0041: Unknown result type (might be due to invalid IL or missing references) //IL_004b: Expected O, but got Unknown Instance = this; Log = ((BaseUnityPlugin)this).Logger; try { ((Component)this).transform.parent = null; ((Object)((Component)this).gameObject).hideFlags = (HideFlags)61; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); InitializeConfig(); harmony = new Harmony("REPOJP.ScoutTwoSeater"); harmony.PatchAll(); Log.LogInfo((object)"[ScoutTwoSeater] Loaded v1.0.0"); } catch (Exception ex) { Log.LogError((object)("[ScoutTwoSeater] Awake failed: " + ex)); } } private void Update() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) if (CfgEnabled == null || !CfgEnabled.Value) { return; } try { if (CfgHostHotkeyEnabled != null && CfgHostHotkeyEnabled.Value && CfgHostSelfMountKey != null && Input.GetKeyDown(CfgHostSelfMountKey.Value)) { ScoutTwoSeaterPatches.HostSelfMountToggle(); } cfgReloadAccum += Time.deltaTime; if (cfgReloadAccum >= 2f) { cfgReloadAccum = 0f; try { ((BaseUnityPlugin)this).Config.Reload(); return; } catch { return; } } } catch (Exception ex) { Log.LogError((object)("[ScoutTwoSeater] Update error: " + ex)); } } private void OnDestroy() { try { if (harmony != null) { harmony.UnpatchSelf(); } } catch (Exception ex) { Log.LogError((object)("[ScoutTwoSeater] OnDestroy failed: " + ex)); } } private void InitializeConfig() { //IL_00ca: Unknown result type (might be due to invalid IL or missing references) //IL_00d4: Expected O, but got Unknown //IL_010a: Unknown result type (might be due to invalid IL or missing references) //IL_0114: Expected O, but got Unknown //IL_016d: Unknown result type (might be due to invalid IL or missing references) //IL_0177: Expected O, but got Unknown //IL_0217: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Expected O, but got Unknown CfgLanguage = ((BaseUnityPlugin)this).Config.Bind("00_Setup", "Language", Language.EN, "Change interface language. Restart the game after switching.\n切換介面語言。切換後需要重新啟動遊戲才會生效。\nEN = English / CN = 繁體中文"); LocStrings locStrings = LocStrings.Build(CfgLanguage.Value); CfgEnabled = ((BaseUnityPlugin)this).Config.Bind(locStrings.SecGeneral, locStrings.KeyEnabled, true, locStrings.DescEnabled); CfgPose = ((BaseUnityPlugin)this).Config.Bind(locStrings.SecGeneral, locStrings.KeyPose, PassengerPose.Back, locStrings.DescPose); CfgForceUnequipOnMount = ((BaseUnityPlugin)this).Config.Bind(locStrings.SecGeneral, locStrings.KeyForceUnequip, false, locStrings.DescForceUnequip); CfgMountDebounceSeconds = ((BaseUnityPlugin)this).Config.Bind(locStrings.SecGeneral, locStrings.KeyDebounce, 1.5f, new ConfigDescription(locStrings.DescDebounce, (AcceptableValueBase)(object)new AcceptableValueRange(0.1f, 10f), Array.Empty())); CfgAutoDismountDistance = ((BaseUnityPlugin)this).Config.Bind(locStrings.SecGeneral, locStrings.KeyAutoDismount, 8f, new ConfigDescription(locStrings.DescAutoDismount, (AcceptableValueBase)(object)new AcceptableValueRange(2f, 30f), Array.Empty())); CfgVerboseLog = ((BaseUnityPlugin)this).Config.Bind(locStrings.SecGeneral, locStrings.KeyVerboseLog, false, locStrings.DescVerboseLog); CfgDriverSideThresholdZ = ((BaseUnityPlugin)this).Config.Bind(locStrings.SecGeneral, locStrings.KeyThresholdZ, -0.2f, new ConfigDescription(locStrings.DescThresholdZ, (AcceptableValueBase)(object)new AcceptableValueRange(-3f, 3f), Array.Empty())); CfgPullStrength = ((BaseUnityPlugin)this).Config.Bind(locStrings.SecGeneral, locStrings.KeyPullStrength, PullStrength.Weak, locStrings.DescPullStrength); CfgHostHotkeyEnabled = ((BaseUnityPlugin)this).Config.Bind(locStrings.SecAdvanced, locStrings.KeyHotkeyEnabled, false, locStrings.DescHotkeyEnabled); CfgHostSelfMountKey = ((BaseUnityPlugin)this).Config.Bind(locStrings.SecAdvanced, locStrings.KeyHotkeyKey, (KeyCode)118, locStrings.DescHotkeyKey); CfgHostSelfMountRange = ((BaseUnityPlugin)this).Config.Bind(locStrings.SecAdvanced, locStrings.KeyHotkeyRange, 6f, new ConfigDescription(locStrings.DescHotkeyRange, (AcceptableValueBase)(object)new AcceptableValueRange(1f, 30f), Array.Empty())); CfgBackX = ((BaseUnityPlugin)this).Config.Bind(locStrings.SecBackOffset, locStrings.KeyOffsetX, 0f, locStrings.DescOffsetX); CfgBackY = ((BaseUnityPlugin)this).Config.Bind(locStrings.SecBackOffset, locStrings.KeyOffsetY, 1f, locStrings.DescOffsetY); CfgBackZ = ((BaseUnityPlugin)this).Config.Bind(locStrings.SecBackOffset, locStrings.KeyOffsetZ, -1f, locStrings.DescOffsetZ); CfgFrontX = ((BaseUnityPlugin)this).Config.Bind(locStrings.SecFrontOffset, locStrings.KeyOffsetX, 0f, locStrings.DescOffsetX); CfgFrontY = ((BaseUnityPlugin)this).Config.Bind(locStrings.SecFrontOffset, locStrings.KeyOffsetY, 1f, locStrings.DescOffsetY); CfgFrontZ = ((BaseUnityPlugin)this).Config.Bind(locStrings.SecFrontOffset, locStrings.KeyOffsetZ, 1f, locStrings.DescOffsetZ); } internal static Vector3 GetPoseLocalPosition() { //IL_0054: 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) PassengerPose value = CfgPose.Value; if (value != 0 && value == PassengerPose.Front) { return new Vector3(CfgFrontX.Value, CfgFrontY.Value, CfgFrontZ.Value); } return new Vector3(CfgBackX.Value, CfgBackY.Value, CfgBackZ.Value); } } internal sealed class LocStrings { public string SecGeneral; public string SecAdvanced; public string SecBackOffset; public string SecFrontOffset; public string KeyEnabled; public string KeyPose; public string KeyForceUnequip; public string KeyDebounce; public string KeyAutoDismount; public string KeyVerboseLog; public string KeyThresholdZ; public string KeyPullStrength; public string KeyHotkeyEnabled; public string KeyHotkeyKey; public string KeyHotkeyRange; public string KeyOffsetX; public string KeyOffsetY; public string KeyOffsetZ; public string DescEnabled; public string DescPose; public string DescForceUnequip; public string DescDebounce; public string DescAutoDismount; public string DescVerboseLog; public string DescThresholdZ; public string DescPullStrength; public string DescHotkeyEnabled; public string DescHotkeyKey; public string DescHotkeyRange; public string DescOffsetX; public string DescOffsetY; public string DescOffsetZ; public static LocStrings Build(Language lang) { if (lang == Language.EN) { return new LocStrings { SecGeneral = "General", SecAdvanced = "Advanced", SecBackOffset = "BackOffset", SecFrontOffset = "FrontOffset", KeyEnabled = "Enabled", KeyPose = "PassengerSeat", KeyForceUnequip = "ForceUnequipOnMount", KeyDebounce = "MountDebounceSeconds", KeyAutoDismount = "AutoDismountDistance", KeyVerboseLog = "VerboseLog", KeyThresholdZ = "DriverSideThresholdZ", KeyPullStrength = "PullStrength", KeyHotkeyEnabled = "EnableHostHotkey", KeyHotkeyKey = "HostSelfMountKey", KeyHotkeyRange = "HostSelfMountRange", KeyOffsetX = "OffsetX", KeyOffsetY = "OffsetY", KeyOffsetZ = "OffsetZ", DescEnabled = "Enable the two-seater Scout. When false, Scout behaves like vanilla.", DescPose = "Where the passenger sits. Back = rear of vehicle. Front = front of vehicle.", DescForceUnequip = "If true, forces the passenger to unequip their held item when they board.", DescDebounce = "Delay (seconds) after mounting before the passenger can press grab again to dismount.", DescAutoDismount = "If the passenger's ragdoll drifts more than this distance (meters) from the seat, auto-dismount.", DescVerboseLog = "Log mount/dismount details to BepInEx console. Use for debugging.", DescThresholdZ = "Threshold for front/back detection (Scout local Z).\nlocalZ > value = car front (vanilla behavior) / localZ <= value = car rear (passenger).\nDefault -0.2 puts the boundary slightly forward of center.\nIf axis is reversed, try a positive value.", DescPullStrength = "Weak: Smooth, vehicle does not get pushed. Loose grip.\nStrong: Passenger snaps to seat fast, but vehicle gets pushed around.", DescHotkeyEnabled = "Disabled by default. Enable to allow host to force themselves into the passenger seat via a hotkey - useful for solo testing.", DescHotkeyKey = "The key the host presses to toggle being a passenger on the nearest Scout. Requires 'EnableHostHotkey' to be true.", DescHotkeyRange = "Max distance (meters) the host needs to be within a Scout for the hotkey to take effect.", DescOffsetX = "Local X offset relative to Scout (left/right).", DescOffsetY = "Local Y offset (up/down). Positive = up.", DescOffsetZ = "Local Z offset (front/back). Negative usually = rear, positive = front." }; } return new LocStrings { SecGeneral = "一般", SecAdvanced = "進階", SecBackOffset = "車尾偏移", SecFrontOffset = "車頭偏移", KeyEnabled = "啟用雙人座", KeyPose = "乘客位置", KeyForceUnequip = "上車時強制收起手上道具", KeyDebounce = "上下車按鍵間隔(秒)", KeyAutoDismount = "自動下車距離", KeyVerboseLog = "詳細日誌", KeyThresholdZ = "車前後分界線Z", KeyPullStrength = "吸力強度", KeyHotkeyEnabled = "啟用主機熱鍵", KeyHotkeyKey = "主機自我乘客切換鍵", KeyHotkeyRange = "主機自我乘客觸發距離", KeyOffsetX = "X(左右)", KeyOffsetY = "Y(上下)", KeyOffsetZ = "Z(前後)", DescEnabled = "啟用雙人座 Scout。關閉後 Scout 行為與原版一致。", DescPose = "乘客的位置。Back = 車尾後方 / Front = 車頭前方。", DescForceUnequip = "若開啟,乘客上車當下會把手上拿的道具收回物品欄。", DescDebounce = "乘客上車後,要等這麼久才能再次抓 Scout 觸發下車。避免一按就上車又馬上下車。", DescAutoDismount = "乘客的 ragdoll 飄離 Scout 超過這個距離(米)就自動下車。避免乘客被甩飛但仍卡在 tumble 狀態。", DescVerboseLog = "開啟後會在 BepInEx 主控台輸出乘客上下車的詳細過程,用於除錯。", DescThresholdZ = "玩家抓住 Scout 哪個部位 (用 Scout 本體 local Z 座標判斷):\nlocalZ > 此值 = 車前部 (走原版邏輯) / localZ <= 此值 = 車後部 (變乘客)\n預設 -0.2 = 偏前一點分界 (車中心線稍微往前),這樣大部分後段都是乘客區。\n若軸向反了 (抓車前變乘客、抓車後沒反應) 改成正值試試。", DescPullStrength = "Weak (弱):乘客平順上車,車子不會被推開,但拉力較鬆。\nStrong (強):乘客快速貼緊座位,但車子會被乘客推動產生位移。", DescHotkeyEnabled = "預設關閉。開啟後主機可以用熱鍵 (預設 V) 強制把自己變乘客 - 給單人測試用。", DescHotkeyKey = "主機按下此鍵 -> 把附近最近的 Scout 當成乘客車,自己上車/下車。需要先開啟「啟用主機熱鍵」。", DescHotkeyRange = "主機按熱鍵時,要在這距離內找到 Scout 才會生效 (米)。", DescOffsetX = "Scout 本體 local 座標 X (左右)。", DescOffsetY = "Scout 本體 local 座標 Y (上下,正=上方)。", DescOffsetZ = "Scout 本體 local 座標 Z (前後,負通常=後方車尾、正=前方車頭)。" }; } } } namespace System.Runtime.CompilerServices { [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] internal sealed class IgnoresAccessChecksToAttribute : Attribute { internal IgnoresAccessChecksToAttribute(string assemblyName) { } } }