Please disclose if any significant portion of your mod was created using AI tools by adding the 'AI Generated' category. Failing to do so may result in the mod being removed from Thunderstore.
Decompiled source of ScoutTwoSeater v1.0.0
ScoutTwoSeater.dll
Decompiled 21 hours agousing 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<PlayerAvatar> PrevFrameGrabbers = new HashSet<PlayerAvatar>(); public bool HasReleasedGrabSinceMount; } private static readonly Dictionary<ItemVehicle, PassengerInfo> Vehicles = new Dictionary<ItemVehicle, PassengerInfo>(); 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<PhysGrabObject>(); 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<PlayerAvatar> hashSet = new HashSet<PlayerAvatar>(); List<PlayerAvatar> list = new List<PlayerAvatar>(); List<PlayerAvatar> list2 = new List<PlayerAvatar>(); 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<PlayerAvatar>.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<PhysGrabObject>(); 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<ItemVehicle, PassengerInfo> 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<ItemVehicle, PassengerInfo> 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<Language> CfgLanguage; internal static ConfigEntry<bool> CfgEnabled; internal static ConfigEntry<PassengerPose> CfgPose; internal static ConfigEntry<bool> CfgForceUnequipOnMount; internal static ConfigEntry<float> CfgMountDebounceSeconds; internal static ConfigEntry<float> CfgAutoDismountDistance; internal static ConfigEntry<bool> CfgVerboseLog; internal static ConfigEntry<bool> CfgHostHotkeyEnabled; internal static ConfigEntry<KeyCode> CfgHostSelfMountKey; internal static ConfigEntry<float> CfgHostSelfMountRange; internal static ConfigEntry<float> CfgDriverSideThresholdZ; internal static ConfigEntry<PullStrength> CfgPullStrength; internal static ConfigEntry<float> CfgBackX; internal static ConfigEntry<float> CfgBackY; internal static ConfigEntry<float> CfgBackZ; internal static ConfigEntry<float> CfgFrontX; internal static ConfigEntry<float> CfgFrontY; internal static ConfigEntry<float> 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<Language>("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<bool>(locStrings.SecGeneral, locStrings.KeyEnabled, true, locStrings.DescEnabled); CfgPose = ((BaseUnityPlugin)this).Config.Bind<PassengerPose>(locStrings.SecGeneral, locStrings.KeyPose, PassengerPose.Back, locStrings.DescPose); CfgForceUnequipOnMount = ((BaseUnityPlugin)this).Config.Bind<bool>(locStrings.SecGeneral, locStrings.KeyForceUnequip, false, locStrings.DescForceUnequip); CfgMountDebounceSeconds = ((BaseUnityPlugin)this).Config.Bind<float>(locStrings.SecGeneral, locStrings.KeyDebounce, 1.5f, new ConfigDescription(locStrings.DescDebounce, (AcceptableValueBase)(object)new AcceptableValueRange<float>(0.1f, 10f), Array.Empty<object>())); CfgAutoDismountDistance = ((BaseUnityPlugin)this).Config.Bind<float>(locStrings.SecGeneral, locStrings.KeyAutoDismount, 8f, new ConfigDescription(locStrings.DescAutoDismount, (AcceptableValueBase)(object)new AcceptableValueRange<float>(2f, 30f), Array.Empty<object>())); CfgVerboseLog = ((BaseUnityPlugin)this).Config.Bind<bool>(locStrings.SecGeneral, locStrings.KeyVerboseLog, false, locStrings.DescVerboseLog); CfgDriverSideThresholdZ = ((BaseUnityPlugin)this).Config.Bind<float>(locStrings.SecGeneral, locStrings.KeyThresholdZ, -0.2f, new ConfigDescription(locStrings.DescThresholdZ, (AcceptableValueBase)(object)new AcceptableValueRange<float>(-3f, 3f), Array.Empty<object>())); CfgPullStrength = ((BaseUnityPlugin)this).Config.Bind<PullStrength>(locStrings.SecGeneral, locStrings.KeyPullStrength, PullStrength.Weak, locStrings.DescPullStrength); CfgHostHotkeyEnabled = ((BaseUnityPlugin)this).Config.Bind<bool>(locStrings.SecAdvanced, locStrings.KeyHotkeyEnabled, false, locStrings.DescHotkeyEnabled); CfgHostSelfMountKey = ((BaseUnityPlugin)this).Config.Bind<KeyCode>(locStrings.SecAdvanced, locStrings.KeyHotkeyKey, (KeyCode)118, locStrings.DescHotkeyKey); CfgHostSelfMountRange = ((BaseUnityPlugin)this).Config.Bind<float>(locStrings.SecAdvanced, locStrings.KeyHotkeyRange, 6f, new ConfigDescription(locStrings.DescHotkeyRange, (AcceptableValueBase)(object)new AcceptableValueRange<float>(1f, 30f), Array.Empty<object>())); CfgBackX = ((BaseUnityPlugin)this).Config.Bind<float>(locStrings.SecBackOffset, locStrings.KeyOffsetX, 0f, locStrings.DescOffsetX); CfgBackY = ((BaseUnityPlugin)this).Config.Bind<float>(locStrings.SecBackOffset, locStrings.KeyOffsetY, 1f, locStrings.DescOffsetY); CfgBackZ = ((BaseUnityPlugin)this).Config.Bind<float>(locStrings.SecBackOffset, locStrings.KeyOffsetZ, -1f, locStrings.DescOffsetZ); CfgFrontX = ((BaseUnityPlugin)this).Config.Bind<float>(locStrings.SecFrontOffset, locStrings.KeyOffsetX, 0f, locStrings.DescOffsetX); CfgFrontY = ((BaseUnityPlugin)this).Config.Bind<float>(locStrings.SecFrontOffset, locStrings.KeyOffsetY, 1f, locStrings.DescOffsetY); CfgFrontZ = ((BaseUnityPlugin)this).Config.Bind<float>(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) { } } }