using System;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using CruiseControl.Configuration;
using CruiseControl.Localization;
using CruiseControl.Runtime;
using CruiseControl.UI;
using Fusion;
using HarmonyLib;
using ModSettingsMenu.Api;
using SunkenlandLocalizationAPI.Api;
using TMPro;
using UnityEngine;
using UnityEngine.UI;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: AssemblyTitle("CruiseControl")]
[assembly: AssemblyDescription("Cruise control mod for Sunkenland by Ice Box Studio")]
[assembly: AssemblyCompany("Ice Box Studio")]
[assembly: AssemblyProduct("CruiseControl")]
[assembly: AssemblyCopyright("Copyright © 2026 Ice Box Studio All rights reserved.")]
[assembly: ComVisible(false)]
[assembly: Guid("c668fef8-d6f1-4e45-a154-1e558cbe9bf4")]
[assembly: AssemblyFileVersion("1.0.1.0")]
[assembly: TargetFramework(".NETFramework,Version=v4.7.2", FrameworkDisplayName = ".NET Framework 4.7.2")]
[assembly: AssemblyVersion("1.0.1.0")]
namespace CruiseControl
{
[BepInPlugin("IceBoxStudio.Sunkenland.CruiseControl", "CruiseControl", "1.0.1")]
[BepInDependency("IceBoxStudio.Sunkenland.ModSettingsMenu", "1.2.0")]
[BepInDependency("IceBoxStudio.Sunkenland.LocalizationAPI", "1.1.0")]
public sealed class CruiseControl : BaseUnityPlugin
{
private Harmony _harmony;
internal static ManualLogSource Log { get; private set; }
private void Awake()
{
//IL_0087: Unknown result type (might be due to invalid IL or missing references)
//IL_0091: Expected O, but got Unknown
Log = ((BaseUnityPlugin)this).Logger;
LocalizationApi.LanguageChanged += OnLanguageChanged;
try
{
Log.LogInfo((object)"=============================================");
Log.LogInfo((object)("CruiseControl " + I18n.Text("plugin.initializing")));
Log.LogInfo((object)(I18n.Text("plugin.author_prefix") + "Ice Box Studio(https://steamcommunity.com/id/ibox666/)"));
ConfigManager.Bind(((BaseUnityPlugin)this).Config);
RegisterSettings();
_harmony = new Harmony("IceBoxStudio.Sunkenland.CruiseControl");
_harmony.PatchAll(Assembly.GetExecutingAssembly());
Log.LogInfo((object)("CruiseControl " + I18n.Text("plugin.initialized")));
Log.LogInfo((object)"=============================================");
}
catch (Exception arg)
{
Log.LogError((object)$"Failed to initialize Cruise Control: {arg}");
}
}
private static void RegisterSettings()
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Unknown result type (might be due to invalid IL or missing references)
//IL_0015: Unknown result type (might be due to invalid IL or missing references)
//IL_003a: 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_005a: 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_0072: Unknown result type (might be due to invalid IL or missing references)
//IL_007d: Unknown result type (might be due to invalid IL or missing references)
//IL_008d: Expected O, but got Unknown
ModSettingsRegistry.Register("IceBoxStudio.Sunkenland.CruiseControl", new ModSettingsModOptions
{
Name = "Cruise Control",
LocalizedName = () => I18n.Text("mod.name"),
Description = I18n.Text("mod.description"),
Author = "Ice Box Studio",
Version = "1.0.1",
NexusModsId = 82,
ThunderstoreTeam = "Ice_Box_Studio_Sunkenland",
ThunderstoreModName = "CruiseControl"
});
}
private static void OnLanguageChanged(string language)
{
RegisterSettings();
}
}
public static class PluginInfo
{
public const string PLUGIN_GUID = "IceBoxStudio.Sunkenland.CruiseControl";
public const string PLUGIN_NAME = "CruiseControl";
public const string PLUGIN_VERSION = "1.0.1";
}
}
namespace CruiseControl.UI
{
internal static class VehicleSpeedDisplay
{
internal static void Append(TextMeshProUGUI speedText)
{
//IL_0036: 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 (!((Object)(object)speedText == (Object)null) && global::CruiseControl.Runtime.CruiseControl.TryGetPrompt(out var active, out var boat, out var _))
{
if (active && (Object)(object)boat != (Object)null && Object.op_Implicit((Object)(object)boat.Rigid))
{
Vector3 velocity = boat.Rigid.velocity;
string text = (((Vector3)(ref velocity)).magnitude * 10f).ToString("f1") + "km/h";
((TMP_Text)speedText).text = ((boat.MoveState < 0) ? ("-" + text) : text);
}
((TMP_Text)speedText).enableWordWrapping = false;
((TMP_Text)speedText).text = ((TMP_Text)speedText).text + Prompt(active, ((TMP_Text)speedText).fontSize);
}
}
internal static void Append(Text speedText)
{
if (!((Object)(object)speedText == (Object)null) && global::CruiseControl.Runtime.CruiseControl.TryGetPrompt(out var active, out var _, out var _))
{
speedText.text += Prompt(active, speedText.fontSize);
}
}
private static string Prompt(bool active, float fontSize)
{
int num = Mathf.Max(12, Mathf.RoundToInt(fontSize * 0.55f));
string text = I18n.Text(active ? "ui.cruise.cancel" : "ui.cruise.enable", ConfigManager.CruiseKeyText);
return "\n<size=" + num + ">" + text.TrimStart(Array.Empty<char>()) + "</size>";
}
}
}
namespace CruiseControl.Runtime
{
internal static class CruiseControl
{
private static Boat _boat;
private static HelicopterController _helicopter;
private static float _targetSpeed;
internal static bool IsActive
{
get
{
if (!((Object)(object)_boat != (Object)null))
{
return (Object)(object)_helicopter != (Object)null;
}
return true;
}
}
internal static void Toggle()
{
//IL_006b: Unknown result type (might be due to invalid IL or missing references)
//IL_0070: 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)
if (Object.op_Implicit((Object)(object)Global.code) && Global.code.OnGUI)
{
return;
}
Boat boat;
HelicopterController helicopter;
if (IsActive)
{
Clear();
}
else if (TryGetCurrentVehicle(out boat, out helicopter) && CanEnable(boat, helicopter))
{
_boat = boat;
_helicopter = helicopter;
Vector3 velocity;
float magnitude;
if (!((Object)(object)boat != (Object)null))
{
velocity = helicopter.Rigid.velocity;
magnitude = ((Vector3)(ref velocity)).magnitude;
}
else
{
velocity = boat.Rigid.velocity;
magnitude = ((Vector3)(ref velocity)).magnitude;
}
_targetSpeed = magnitude;
}
}
internal static void Tick()
{
if (!IsActive)
{
return;
}
if (!TryGetCurrentVehicle(out var boat, out var helicopter) || (Object)(object)boat != (Object)(object)_boat || (Object)(object)helicopter != (Object)(object)_helicopter)
{
Clear();
}
else if ((Object)(object)boat != (Object)null)
{
if (!SupportsCruise(boat) || !HasInputAuthority(boat) || !boat.EngineOn || HasBoatOverrideInput())
{
Clear();
}
}
else if (!HasInputAuthority(helicopter) || !helicopter.EngineOn || HasHelicopterOverrideInput())
{
Clear();
}
}
internal static bool IsActiveFor(Boat boat, HelicopterController helicopter)
{
if (IsActive && (Object)(object)boat == (Object)(object)_boat)
{
return (Object)(object)helicopter == (Object)(object)_helicopter;
}
return false;
}
internal static void UpdateBoatInput(Boat boat, ref BoatNetworkInput input)
{
if (SupportsCruise(boat) && IsActiveFor(boat, null) && HasInputAuthority(boat) && !HasBoatOverrideInput())
{
input.MoveState = 1;
}
}
internal static void MaintainBoat(Boat boat)
{
//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_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_0077: Unknown result type (might be due to invalid IL or missing references)
//IL_007c: 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)
if (!SupportsCruise(boat) || !IsActiveFor(boat, null) || !HasInputAuthority(boat) || HasBoatOverrideInput())
{
return;
}
if (!boat.EngineOn || !Object.op_Implicit((Object)(object)boat.Rigid) || boat.Rigid.isKinematic)
{
Clear();
return;
}
Vector3 boatForward = GetBoatForward(boat);
float num = Vector3.Dot(boat.Rigid.velocity, boatForward);
if (num <= 0.1f)
{
Clear();
return;
}
Rigidbody rigid = boat.Rigid;
rigid.velocity += boatForward * (_targetSpeed - num);
}
internal static void UpdateBoatForce(Boat boat, ref int force)
{
if (SupportsCruise(boat) && IsActiveFor(boat, null) && !HasBoatOverrideInput())
{
force = 1;
}
}
internal static void MaintainHelicopter(HelicopterController helicopter)
{
//IL_0047: Unknown result type (might be due to invalid IL or missing references)
//IL_004c: 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_0061: 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_0076: Unknown result type (might be due to invalid IL or missing references)
//IL_007c: Unknown result type (might be due to invalid IL or missing references)
if (IsActiveFor(null, helicopter) && HasInputAuthority(helicopter) && !HasHelicopterOverrideInput())
{
if (!helicopter.EngineOn || !Object.op_Implicit((Object)(object)helicopter.Rigid) || helicopter.Rigid.isKinematic)
{
Clear();
return;
}
Vector3 velocity = helicopter.Rigid.velocity;
Vector3 val = ((((Vector3)(ref velocity)).sqrMagnitude > 0.01f) ? ((Vector3)(ref velocity)).normalized : ((Component)helicopter).transform.forward);
helicopter.Rigid.velocity = val * _targetSpeed;
}
}
internal static bool TryGetPrompt(out bool active, out Boat boat, out HelicopterController helicopter)
{
active = false;
if (!TryGetCurrentVehicle(out boat, out helicopter))
{
return false;
}
if ((Object)(object)boat != (Object)null && !SupportsCruise(boat))
{
return false;
}
active = IsActiveFor(boat, helicopter);
if (!active)
{
return CanEnable(boat, helicopter);
}
return true;
}
private static bool CanEnable(Boat boat, HelicopterController helicopter)
{
//IL_00ab: 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_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_0063: 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)
Vector3 velocity;
if ((Object)(object)boat != (Object)null)
{
if (SupportsCruise(boat) && HasInputAuthority(boat) && boat.EngineOn && Object.op_Implicit((Object)(object)boat.Rigid) && !boat.Rigid.isKinematic)
{
velocity = boat.Rigid.velocity;
if (!(((Vector3)(ref velocity)).magnitude * 10f < ConfigManager.ActivationSpeedKmh))
{
return Vector3.Dot(boat.Rigid.velocity, GetBoatForward(boat)) > 0.1f;
}
}
return false;
}
if (HasInputAuthority(helicopter) && helicopter.EngineOn && Object.op_Implicit((Object)(object)helicopter.Rigid) && !helicopter.Rigid.isKinematic)
{
velocity = helicopter.Rigid.velocity;
return ((Vector3)(ref velocity)).magnitude * 10f >= ConfigManager.ActivationSpeedKmh;
}
return false;
}
private static bool SupportsCruise(Boat boat)
{
if ((Object)(object)boat != (Object)null && !Object.op_Implicit((Object)(object)boat.M_Sail))
{
return !boat.SeparatWaterObject;
}
return false;
}
private static bool HasInputAuthority(Boat boat)
{
if ((Object)(object)boat != (Object)null && Object.op_Implicit((Object)(object)((SimulationBehaviour)boat).Object))
{
return ((SimulationBehaviour)boat).Object.HasInputAuthority;
}
return false;
}
private static bool HasInputAuthority(HelicopterController helicopter)
{
if ((Object)(object)helicopter != (Object)null && Object.op_Implicit((Object)(object)((SimulationBehaviour)helicopter).Object))
{
return ((SimulationBehaviour)helicopter).Object.HasInputAuthority;
}
return false;
}
private static bool TryGetCurrentVehicle(out Boat boat, out HelicopterController helicopter)
{
boat = null;
helicopter = null;
if (!Object.op_Implicit((Object)(object)Global.code) || !Object.op_Implicit((Object)(object)Global.code.Player))
{
return false;
}
boat = Global.code.Player.drivingObject;
if ((Object)(object)boat != (Object)null)
{
return true;
}
helicopter = Global.code.Player.CurHelicopter;
return (Object)(object)helicopter != (Object)null;
}
private static Vector3 GetBoatForward(Boat boat)
{
//IL_001e: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Unknown result type (might be due to invalid IL or missing references)
//IL_0023: Unknown result type (might be due to invalid IL or missing references)
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: 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)
Transform engineForward = boat.engineForward;
Vector3 val = Vector3.ProjectOnPlane(((Object)(object)engineForward != (Object)null) ? engineForward.forward : ((Component)boat).transform.forward, Vector3.up);
return ((Vector3)(ref val)).normalized;
}
private static bool HasBoatOverrideInput()
{
InputManager instance = InputManager.Instance;
if ((Object)(object)instance != (Object)null)
{
if (!instance.BackHold)
{
if (instance.BoatBoostHold)
{
return !UsesCruiseKey(instance.BoatBoostKeyAction);
}
return false;
}
return true;
}
return false;
}
private static bool UsesCruiseKey(KeyAction keyAction)
{
//IL_0004: Unknown result type (might be due to invalid IL or missing references)
if (keyAction != null)
{
return ConfigManager.UsesKey(keyAction.keyCode);
}
return false;
}
private static bool HasHelicopterOverrideInput()
{
InputManager instance = InputManager.Instance;
if ((!((Object)(object)instance != (Object)null) || (!instance.HelicopterThrottlePositiveHold && !instance.HelicopterThrottleNegativeHold && !instance.HelicopterYawNegativeHold && !instance.HelicopterYawPositiveHold)) && !(Mathf.Abs(Input.GetAxis("Mouse X")) > 0.01f))
{
return Mathf.Abs(Input.GetAxis("Mouse Y")) > 0.01f;
}
return true;
}
private static void Clear()
{
_boat = null;
_helicopter = null;
_targetSpeed = 0f;
}
}
internal static class CruiseInput
{
internal static void Run()
{
if (ConfigManager.IsDown())
{
CruiseControl.Toggle();
}
CruiseControl.Tick();
}
}
}
namespace CruiseControl.Patches
{
[HarmonyPatch(typeof(Boat), "AddForce", new Type[] { typeof(int) })]
internal static class BoatAddForcePatch
{
private static void Prefix(Boat __instance, ref int __0)
{
global::CruiseControl.Runtime.CruiseControl.UpdateBoatForce(__instance, ref __0);
}
}
[HarmonyPatch(typeof(Boat), "FixedUpdate")]
internal static class BoatFixedUpdatePatch
{
private static void Postfix(Boat __instance)
{
global::CruiseControl.Runtime.CruiseControl.MaintainBoat(__instance);
}
}
[HarmonyPatch(typeof(Boat), "PrepareInput")]
internal static class BoatPrepareInputPatch
{
private static void Postfix(Boat __instance, ref BoatNetworkInput __result)
{
global::CruiseControl.Runtime.CruiseControl.UpdateBoatInput(__instance, ref __result);
}
}
[HarmonyPatch(typeof(HelicopterController), "FixedUpdate")]
internal static class HelicopterFixedUpdatePatch
{
private static void Postfix(HelicopterController __instance)
{
global::CruiseControl.Runtime.CruiseControl.MaintainHelicopter(__instance);
}
}
[HarmonyPatch(typeof(HelicopterHUD), "FixedUpdate")]
internal static class HelicopterSpeedUiPatch
{
private static void Postfix(HelicopterHUD __instance)
{
VehicleSpeedDisplay.Append(__instance.speedTxt);
}
}
[HarmonyPatch(typeof(InputManager), "Update")]
internal static class InputManagerUpdatePatch
{
private static void Postfix()
{
CruiseInput.Run();
}
}
[HarmonyPatch(typeof(UICombat), "Update")]
internal static class VehicleSpeedUiPatch
{
private static void Postfix(TextMeshProUGUI ___tmpVehicleSpeed)
{
VehicleSpeedDisplay.Append(___tmpVehicleSpeed);
}
}
}
namespace CruiseControl.Localization
{
internal static class I18n
{
private const string FileName = "CruiseControl.Localization.json";
private static readonly ModLocalizer _localizer = Load();
internal static ModLocalizer Localizer => _localizer;
internal static string Text(string key, params object[] args)
{
return _localizer.GetLocalizedText(key, args);
}
private static ModLocalizer Load()
{
string directoryName = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
ModLocalizer obj = LocalizationApi.For("IceBoxStudio.Sunkenland.CruiseControl");
obj.RegisterJson(Path.Combine(directoryName, "CruiseControl.Localization.json"));
return obj;
}
}
}
namespace CruiseControl.Configuration
{
internal static class ConfigManager
{
private static ConfigEntry<float> _activationSpeedKmh;
private static ConfigEntry<KeyCode> _cruiseKey;
internal static float ActivationSpeedKmh => Math.Max(0.1f, (_activationSpeedKmh == null) ? 5f : _activationSpeedKmh.Value);
internal static KeyCode CruiseKey
{
get
{
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
if (_cruiseKey != null)
{
return _cruiseKey.Value;
}
return (KeyCode)306;
}
}
internal static string CruiseKeyText
{
get
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_000a: Invalid comparison between Unknown and I4
//IL_000c: Unknown result type (might be due to invalid IL or missing references)
//IL_0016: Invalid comparison between Unknown and I4
//IL_0028: Unknown result type (might be due to invalid IL or missing references)
//IL_002d: Unknown result type (might be due to invalid IL or missing references)
if ((int)CruiseKey == 306 || (int)CruiseKey == 305)
{
return I18n.Text("key.ctrl");
}
return ((object)CruiseKey/*cast due to .constrained prefix*/).ToString();
}
}
internal static bool IsDown()
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0007: Unknown result type (might be due to invalid IL or missing references)
if ((int)CruiseKey != 0)
{
return Input.GetKeyDown(CruiseKey);
}
return false;
}
internal static bool UsesKey(KeyCode key)
{
//IL_0000: Unknown result type (might be due to invalid IL or missing references)
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
return CruiseKey == key;
}
internal static void Bind(ConfigFile config)
{
_activationSpeedKmh = config.Bind<float>("Cruise Control", "ActivationSpeedKmh", 5f, I18n.Localizer.Config("config.activation_speed", 10, "Cruise Control", "config.section", 10, (AcceptableValueBase)null));
_cruiseKey = config.Bind<KeyCode>("Cruise Control", "CruiseKey", (KeyCode)306, I18n.Localizer.Config("config.cruise_key", 20, "Cruise Control", "config.section", 10, (AcceptableValueBase)null));
}
}
}