using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using System.Text; using FishNet.Object; using HarmonyLib; using MelonLoader; using MelonLoader.Preferences; using Microsoft.CodeAnalysis; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using S1FuelMod; using S1FuelMod.Integrations; using S1FuelMod.Networking; using S1FuelMod.Systems; using S1FuelMod.Systems.FuelTypes; using S1FuelMod.UI; using S1FuelMod.Utils; using ScheduleOne.DevUtilities; using ScheduleOne.Equipping; using ScheduleOne.GameTime; using ScheduleOne.Interaction; using ScheduleOne.ItemFramework; using ScheduleOne.Levelling; using ScheduleOne.Money; using ScheduleOne.NPCs.CharacterClasses; using ScheduleOne.Networking; using ScheduleOne.Persistence.Datas; using ScheduleOne.Persistence.Loaders; using ScheduleOne.PlayerScripts; using ScheduleOne.UI; using ScheduleOne.Vehicles; using ScheduleOne.Vehicles.Modification; using ScheduleOne.Vehicles.Sound; using Steamworks; using TMPro; using UnityEngine; using UnityEngine.Events; using UnityEngine.Rendering; using UnityEngine.SceneManagement; using UnityEngine.UI; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(Core), "S1FuelMod", "1.3.3", "Bars & SirTidez", null)] [assembly: MelonGame("TVGS", "Schedule I")] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("S1FuelMod-Mono")] [assembly: AssemblyConfiguration("Release Mono")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+fc0346bd6d68bc20217c3bde97184ab8105e0792")] [assembly: AssemblyProduct("S1FuelMod-Mono")] [assembly: AssemblyTitle("S1FuelMod-Mono")] [assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)] [assembly: AssemblyVersion("1.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.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)] internal sealed class NullableAttribute : Attribute { public readonly byte[] NullableFlags; public NullableAttribute(byte P_0) { NullableFlags = new byte[1] { P_0 }; } public NullableAttribute(byte[] P_0) { NullableFlags = P_0; } } [CompilerGenerated] [Microsoft.CodeAnalysis.Embedded] [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)] internal sealed class NullableContextAttribute : Attribute { public readonly byte Flag; public NullableContextAttribute(byte P_0) { Flag = P_0; } } [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 S1FuelMod { public class Core : MelonMod { private MelonPreferences_Category? _preferencesCategory; private MelonPreferences_Category? _capacityCategory; private MelonPreferences_Entry? _enableFuelSystem; private MelonPreferences_Entry? _fuelConsumptionMultiplier; private MelonPreferences_Entry? _defaultFuelCapacity; private MelonPreferences_Entry? _shitboxFuelCapacity; private MelonPreferences_Entry? _veeperFuelCapacity; private MelonPreferences_Entry? _bruiserFuelCapacity; private MelonPreferences_Entry? _dinklerFuelCapacity; private MelonPreferences_Entry? _hounddogFuelCapacity; private MelonPreferences_Entry? _cheetahFuelCapacity; private MelonPreferences_Entry? _hotboxFuelCapacity; private MelonPreferences_Entry? _bugattiTourbillonFuelCapacity; private MelonPreferences_Entry? _canofsoupcarFuelCapacity; private MelonPreferences_Entry? _cyberTruckFuelCapacity; private MelonPreferences_Entry? _demonFuelCapacity; private MelonPreferences_Entry? _driftcarFuelCapacity; private MelonPreferences_Entry? _gtrR34FuelCapacity; private MelonPreferences_Entry? _gtrR35FuelCapacity; private MelonPreferences_Entry? _lamborghiniVenenoFuelCapacity; private MelonPreferences_Entry? _rollsRoyceGhostFuelCapacity; private MelonPreferences_Entry? _supercarFuelCapacity; private MelonPreferences_Entry? _koenigseggCc850FuelCapacity; private MelonPreferences_Entry? _showFuelGauge; private MelonPreferences_Entry? _enableDynamicPricing; private MelonPreferences_Entry? _enablePricingOnTier; private MelonPreferences_Entry? _enableDebugLogging; private MelonPreferences_Entry? _baseFuelPricePerLiter; private MelonPreferences_Entry? _enableCurfewFuelTax; private MelonPreferences_Entry? _swapGaugeDirection; private MelonPreferences_Entry? _useNewGaugeUI; private MelonPreferences_Entry? _maxFuelPerCanUse; private FuelTypeManager? _fuelTypeManager; private FuelSystemManager? _fuelSystemManager; private FuelUIManager? _fuelUIManager; private FuelStationManager? _fuelStationManager; private FuelSignManager? _fuelSignManager; public static Core Instance { get; private set; } public bool EnableFuelSystem => _enableFuelSystem?.Value ?? true; public float FuelConsumptionMultiplier => _fuelConsumptionMultiplier?.Value ?? 1f; public float DefaultFuelCapacity => _defaultFuelCapacity?.Value ?? 40f; public float ShitboxFuelCapacity => _shitboxFuelCapacity?.Value ?? 30f; public float VeeperFuelCapacity => _veeperFuelCapacity?.Value ?? 40f; public float BruiserFuelCapacity => _bruiserFuelCapacity?.Value ?? 40f; public float DinklerFuelCapacity => _dinklerFuelCapacity?.Value ?? 55f; public float HounddogFuelCapacity => _hounddogFuelCapacity?.Value ?? 35f; public float CheetahFuelCapacity => _cheetahFuelCapacity?.Value ?? 35f; public float HotboxFuelCapacity => _hotboxFuelCapacity?.Value ?? 50f; public float BugattiTourbillonFuelCapacity => _bugattiTourbillonFuelCapacity?.Value ?? 40f; public float CanofsoupcarFuelCapacity => _canofsoupcarFuelCapacity?.Value ?? 30f; public float CyberTruckFuelCapacity => _cyberTruckFuelCapacity?.Value ?? 50f; public float DemonFuelCapacity => _demonFuelCapacity?.Value ?? 40f; public float DriftcarFuelCapacity => _driftcarFuelCapacity?.Value ?? 25f; public float GtrR34FuelCapacity => _gtrR34FuelCapacity?.Value ?? 30f; public float GtrR35FuelCapacity => _gtrR35FuelCapacity?.Value ?? 35f; public float LamborghiniVenenoFuelCapacity => _lamborghiniVenenoFuelCapacity?.Value ?? 35f; public float RollsRoyceGhostFuelCapacity => _rollsRoyceGhostFuelCapacity?.Value ?? 45f; public float SupercarFuelCapacity => _supercarFuelCapacity?.Value ?? 30f; public float KoenigseggCc850FuelCapacity => _koenigseggCc850FuelCapacity?.Value ?? 45f; public bool ShowFuelGauge => _showFuelGauge?.Value ?? true; public bool EnableDynamicPricing => _enableDynamicPricing?.Value ?? true; public bool EnablePricingOnTier => _enablePricingOnTier?.Value ?? true; public bool EnableDebugLogging => _enableDebugLogging?.Value ?? false; public float BaseFuelPricePerLiter => _baseFuelPricePerLiter?.Value ?? 4f; public bool EnableCurfewFuelTax => _enableCurfewFuelTax?.Value ?? false; public bool SwapGaugeDirection => _swapGaugeDirection?.Value ?? false; public bool UseNewGaugeUI => _useNewGaugeUI?.Value ?? true; public float MaxFuelPerCanUse => _maxFuelPerCanUse?.Value ?? 5f; public override void OnInitializeMelon() { Instance = this; ModLogger.LogInitialization(); try { InitializePreferences(); HarmonyPatches.SetModInstance(this); ModLogger.Info("S1FuelMod initialized successfully"); ModLogger.Info($"Fuel System Enabled: {EnableFuelSystem}"); ModLogger.Info($"Show Fuel Gauge: {ShowFuelGauge}"); ModLogger.Info("Gauge Direction: " + (SwapGaugeDirection ? "Left to Right" : "Right to Left")); ModLogger.Info(string.Format("Use New Gauge UI: {0} ({1})", UseNewGaugeUI, UseNewGaugeUI ? "Circular" : "Slider")); ModLogger.Info("Debug logging can be toggled in the mod preferences"); } catch (Exception exception) { ModLogger.Error("Failed to initialize S1FuelMod", exception); } } public override void OnSceneWasInitialized(int buildIndex, string sceneName) { try { ModLogger.Debug($"Scene initialized: {sceneName} (index: {buildIndex})"); if (sceneName.Contains("Main")) { ModLogger.Debug("Main game scene detected, initializing fuel systems..."); InitializeSystems(); } else { ModLogger.Debug("Scene '" + sceneName + "' is not a main game scene, skipping system initialization"); } } catch (Exception exception) { ModLogger.Error("Error during scene initialization", exception); } } public override void OnUpdate() { try { _fuelSystemManager?.Update(); _fuelUIManager?.Update(); _fuelStationManager?.Update(); } catch (Exception exception) { ModLogger.Error("Error during update", exception); } } private void InitializePreferences() { try { _preferencesCategory = MelonPreferences.CreateCategory("S1FuelMod"); _capacityCategory = MelonPreferences.CreateCategory("S1FuelMod_Capacity", "Fuel Tank Capacity"); _enableFuelSystem = _preferencesCategory.CreateEntry("EnableFuelSystem", true, "Enable Fuel System", "If enabled, vehicles will consume fuel and require refueling", false, false, (ValueValidator)null, (string)null); _fuelConsumptionMultiplier = _preferencesCategory.CreateEntry("FuelConsumptionMultiplier", 1f, "Fuel Consumption Multiplier", "Multiplier for fuel consumption rate (1.0 = normal, 0.5 = half consumption, 2.0 = double consumption)", false, false, (ValueValidator)(object)new ValueRange(0.1f, 5f), (string)null); _baseFuelPricePerLiter = _preferencesCategory.CreateEntry("BaseFuelPricePerLiter", 4f, "Fuel Price Per Liter ($)", "Base price per liter of fuel in dollars", false, false, (ValueValidator)(object)new ValueRange(0.1f, 1000f), (string)null); _defaultFuelCapacity = _preferencesCategory.CreateEntry("DefaultFuelCapacity", 40f, "Default Fuel Capacity (L)", "Default fuel tank capacity for vehicles in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _shitboxFuelCapacity = _capacityCategory.CreateEntry("ShitboxFuelCapacity", 30f, "Shitbox Fuel Capacity (L)", "Fuel capacity for the Shitbox vehicle in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _veeperFuelCapacity = _capacityCategory.CreateEntry("VeeperFuelCapacity", 40f, "Veeper Fuel Capacity (L)", "Fuel capacity for the Veeper vehicle in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _bruiserFuelCapacity = _capacityCategory.CreateEntry("BruiserFuelCapacity", 40f, "Bruiser Fuel Capacity (L)", "Fuel capacity for the Bruiser vehicle in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _dinklerFuelCapacity = _capacityCategory.CreateEntry("DinklerFuelCapacity", 55f, "Dinkler Fuel Capacity (L)", "Fuel capacity for the Dinkler vehicle in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _hounddogFuelCapacity = _capacityCategory.CreateEntry("HounddogFuelCapacity", 35f, "Hounddog Fuel Capacity (L)", "Fuel capacity for the Hounddog vehicle in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _cheetahFuelCapacity = _capacityCategory.CreateEntry("CheetahFuelCapacity", 35f, "Cheetah Fuel Capacity (L)", "Fuel capacity for the Cheetah vehicle in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _hotboxFuelCapacity = _capacityCategory.CreateEntry("HotboxFuelCapacity", 50f, "Hotbox Fuel Capacity (L)", "Fuel capacity for the Hotbox vehicle in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _bugattiTourbillonFuelCapacity = _capacityCategory.CreateEntry("Bugatti_TourbillonFuelCapacity", 40f, "Bugatti Tourbillon Fuel Capacity (L)", "Fuel capacity for the Bugatti Tourbillon vehicle in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _canofsoupcarFuelCapacity = _capacityCategory.CreateEntry("canofsoupcarFuelCapacity", 30f, "canofsoupcar Fuel Capacity (L)", "Fuel capacity for the canofsoupcar vehicle in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _cyberTruckFuelCapacity = _capacityCategory.CreateEntry("Cyber_TruckFuelCapacity", 50f, "Cyber Truck Fuel Capacity (L)", "Fuel capacity for the Cyber Truck vehicle in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _demonFuelCapacity = _capacityCategory.CreateEntry("DemonFuelCapacity", 40f, "Demon Fuel Capacity (L)", "Fuel capacity for the Demon vehicle in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _driftcarFuelCapacity = _capacityCategory.CreateEntry("driftcarFuelCapacity", 25f, "driftcar Fuel Capacity (L)", "Fuel capacity for the driftcar vehicle in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _gtrR34FuelCapacity = _capacityCategory.CreateEntry("GTR_R34FuelCapacity", 30f, "GTR R34 Fuel Capacity (L)", "Fuel capacity for the GTR R34 vehicle in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _gtrR35FuelCapacity = _capacityCategory.CreateEntry("GTR_R35FuelCapacity", 35f, "GTR R35 Fuel Capacity (L)", "Fuel capacity for the GTR R35 vehicle in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _lamborghiniVenenoFuelCapacity = _capacityCategory.CreateEntry("Lamborghini_VenenoFuelCapacity", 35f, "Lamborghini Veneno Fuel Capacity (L)", "Fuel capacity for the Lamborghini Veneno vehicle in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _rollsRoyceGhostFuelCapacity = _capacityCategory.CreateEntry("Rolls_Royce_GhostFuelCapacity", 45f, "Rolls Royce Ghost Fuel Capacity (L)", "Fuel capacity for the Rolls Royce Ghost vehicle in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _supercarFuelCapacity = _capacityCategory.CreateEntry("supercarFuelCapacity", 30f, "Supercar Fuel Capacity (L)", "Fuel capacity for the Supercar vehicle in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _koenigseggCc850FuelCapacity = _capacityCategory.CreateEntry("Koenigsegg_CC850FuelCapacity", 45f, "Koenigsegg CC850 Fuel Capacity (L)", "Fuel capacity for the Koenigsegg CC850 vehicle in liters", false, false, (ValueValidator)(object)new ValueRange(10f, 200f), (string)null); _showFuelGauge = _preferencesCategory.CreateEntry("ShowFuelGauge", true, "Show Fuel Gauge", "If enabled, shows fuel gauge UI when driving vehicles", false, false, (ValueValidator)null, (string)null); _enableDynamicPricing = _preferencesCategory.CreateEntry("EnableDynamicPricing", true, "Enable Dynamic Pricing", "If enabled, fuel prices will vary based on which day it is", false, false, (ValueValidator)null, (string)null); _enablePricingOnTier = _preferencesCategory.CreateEntry("EnablePricingOnTier", true, "Enable Pricing on Tier", "If enabled, fuel prices will be inflated based on the player's current tier", false, false, (ValueValidator)null, (string)null); _enableCurfewFuelTax = _preferencesCategory.CreateEntry("EnableCurfewFuelTax", true, "Enable Curfew Fuel Tax", "If enabled, fuel price is doubled during curfew hours", false, false, (ValueValidator)null, (string)null); _enableDebugLogging = _preferencesCategory.CreateEntry("EnableDebugLogging", false, "Enable Debug Logging", "If enabled, shows detailed debug information in console", false, false, (ValueValidator)null, (string)null); _swapGaugeDirection = _preferencesCategory.CreateEntry("SwapGaugeDirection", false, "Swap Gauge Direction", "If enabled, fuel gauge fills from left to right instead of right to left", false, false, (ValueValidator)null, (string)null); _useNewGaugeUI = _preferencesCategory.CreateEntry("UseNewGaugeUI", true, "Use New Gauge UI", "If enabled, uses the new circular fuel gauge instead of the old slider-based gauge. Change requires vehicle re-entry to take effect.", false, false, (ValueValidator)null, (string)null); _maxFuelPerCanUse = _preferencesCategory.CreateEntry("MaxFuelPerCanUse", 5f, "Max Fuel Per Can Use (L)", "Maximum amount of fuel (in liters) that can be added per gasoline can use. The can will be consumed after this amount is added.", false, false, (ValueValidator)(object)new ValueRange(0.1f, 50f), (string)null); ModLogger.Debug("MelonPreferences initialized successfully"); } catch (Exception exception) { ModLogger.Error("Failed to initialize MelonPreferences", exception); } } private void InitializeSystems() { //IL_0044: Unknown result type (might be due to invalid IL or missing references) //IL_004a: Expected O, but got Unknown //IL_00da: Unknown result type (might be due to invalid IL or missing references) //IL_00e1: Expected O, but got Unknown try { if (!EnableFuelSystem) { ModLogger.Debug("Fuel system disabled via preferences"); return; } if ((Object)(object)FuelTypeManager.Instance != (Object)null) { _fuelTypeManager = FuelTypeManager.Instance; } else { GameObject val = new GameObject("S1FuelTypeManager"); _fuelTypeManager = val.AddComponent(); } ModLogger.Debug("Fuel type manager initialized"); _fuelSystemManager = new FuelSystemManager(); ModLogger.Debug("Fuel system manager initialized"); HarmonyPatches.SetModInstance(this); ModLogger.Debug("Harmony patches updated with fuel systems"); _fuelUIManager = new FuelUIManager(); ModLogger.Debug("Fuel UI manager initialized"); _fuelStationManager = new FuelStationManager(); ModLogger.Debug("Fuel station manager initialized"); if ((Object)(object)FuelSignManager.Instance != (Object)null) { _fuelSignManager = FuelSignManager.Instance; } else { GameObject val2 = new GameObject("S1FuelSignManager"); _fuelSignManager = val2.AddComponent(); } ModLogger.Debug("Fuel sign manager initialized"); ModLogger.Debug("All fuel systems initialized successfully"); } catch (Exception exception) { ModLogger.Error("Failed to initialize fuel systems", exception); } } public void ToggleDebugLogging() { try { if (_enableDebugLogging != null) { _enableDebugLogging.Value = !_enableDebugLogging.Value; ModLogger.Info("Debug logging " + (_enableDebugLogging.Value ? "enabled" : "disabled")); if (_enableDebugLogging.Value) { ModLogger.Info("Debug logging is now ON - you should see [FUEL] and [UI] messages"); } else { ModLogger.Info("Debug logging is now OFF"); } } } catch (Exception exception) { ModLogger.Error("Error toggling debug logging", exception); } } public override void OnApplicationQuit() { try { ModLogger.Info("S1FuelMod shutting down..."); if ((Object)(object)_fuelTypeManager != (Object)null) { Object.Destroy((Object)(object)((Component)_fuelTypeManager).gameObject); _fuelTypeManager = null; } _fuelSystemManager?.Dispose(); _fuelUIManager?.Dispose(); _fuelStationManager?.Dispose(); Instance = null; } catch (Exception exception) { ModLogger.Error("Error during mod shutdown", exception); } } public void SavePreferences() { try { MelonPreferences_Category? preferencesCategory = _preferencesCategory; if (preferencesCategory != null) { preferencesCategory.SaveToFile(true); } ModLogger.Debug("Preferences saved successfully"); } catch (Exception exception) { ModLogger.Error("Failed to save preferences", exception); } } public FuelSystemManager? GetFuelSystemManager() { return _fuelSystemManager; } public FuelUIManager? GetFuelUIManager() { return _fuelUIManager; } public FuelStationManager? GetFuelStationManager() { return _fuelStationManager; } public FuelSignManager? GetFuelSignManager() { return _fuelSignManager; } public void RefreshFuelGauges() { try { _fuelUIManager?.RefreshAllGauges(); ModLogger.Debug("Fuel gauges refreshed to apply preference changes"); } catch (Exception exception) { ModLogger.Error("Error refreshing fuel gauges", exception); } } public void OnNewGaugeUIPreferenceChanged() { try { ModLogger.Info($"New Gauge UI preference changed to: {UseNewGaugeUI}"); RefreshFuelGauges(); } catch (Exception exception) { ModLogger.Error("Error handling new gauge UI preference change", exception); } } public void OnFuelPricingPreferenceChanged() { try { ModLogger.Info($"Fuel pricing preferences changed - Dynamic: {EnableDynamicPricing}, Tier: {EnablePricingOnTier}, Curfew: {EnableCurfewFuelTax}"); UpdateFuelSigns(); } catch (Exception exception) { ModLogger.Error("Error handling fuel pricing preference change", exception); } } public void UpdateFuelSigns() { try { if ((Object)(object)_fuelSignManager != (Object)null) { _fuelSignManager.UpdateAllFuelSigns(); ModLogger.Debug("Fuel signs updated with current prices"); } } catch (Exception exception) { ModLogger.Error("Error updating fuel signs", exception); } } } } namespace S1FuelMod.Utils { public static class Constants { public static class Defaults { public const bool ENABLE_FUEL_SYSTEM = true; public const float FUEL_CONSUMPTION_MULTIPLIER = 1f; public const float DEFAULT_FUEL_CAPACITY = 40f; public const float SHITBOX_FUEL_CAPACITY = 30f; public const float VEEPER_FUEL_CAPACITY = 40f; public const float BRUISER_FUEL_CAPACITY = 40f; public const float DINKLER_FUEL_CAPACITY = 55f; public const float HOUNDDOG_FUEL_CAPACITY = 35f; public const float CHEETAH_FUEL_CAPACITY = 35f; public const float HOTBOX_FUEL_CAPACITY = 50f; public const float BUGATTI_TOURBILLON_FUEL_CAPACITY = 40f; public const float CANOFSOUPCAR_FUEL_CAPACITY = 30f; public const float CYBER_TRUCK_FUEL_CAPACITY = 50f; public const float DEMON_FUEL_CAPACITY = 40f; public const float DRIFTCAR_FUEL_CAPACITY = 25f; public const float GTR_R34_FUEL_CAPACITY = 30f; public const float GTR_R35_FUEL_CAPACITY = 35f; public const float LAMBORGHINI_VENENO_FUEL_CAPACITY = 35f; public const float ROLLS_ROYCE_GHOST_FUEL_CAPACITY = 45f; public const float SUPERCAR_FUEL_CAPACITY = 30f; public const float KOENIGSEGG_CC850_FUEL_CAPACITY = 45f; public const bool SHOW_FUEL_GAUGE = true; public const bool ENABLE_DEBUG_LOGGING = false; public const float MAX_FUEL_PER_CAN_USE = 5f; } public static class Constraints { public const float MIN_CONSUMPTION_MULTIPLIER = 0.1f; public const float MAX_CONSUMPTION_MULTIPLIER = 5f; public const float MIN_FUEL_CAPACITY = 10f; public const float MAX_FUEL_CAPACITY = 200f; public const float MIN_FUEL_PRICE_PER_LITER = 0.1f; public const float MAX_FUEL_PRICE_PER_LITER = 1000f; public const float MIN_MAX_FUEL_PER_CAN_USE = 0.1f; public const float MAX_MAX_FUEL_PER_CAN_USE = 50f; } public static class Fuel { public const float BASE_CONSUMPTION_RATE = 360f; public const float IDLE_CONSUMPTION_RATE = 30f; public const float LOW_FUEL_WARNING_THRESHOLD = 20f; public const float CRITICAL_FUEL_WARNING_THRESHOLD = 5f; public const float REFUEL_RATE = 4f; public const float FUEL_PRICE_PER_LITER = 4f; public const float ENGINE_CUTOFF_FUEL_LEVEL = 0f; public const float ENGINE_SPUTTER_FUEL_LEVEL = 4f; } public static class UI { public static class Colors { public static readonly Color FUEL_NORMAL = new Color(0.2f, 0.8f, 0.2f, 0.8f); public static readonly Color FUEL_LOW = new Color(1f, 0.8f, 0f, 0.8f); public static readonly Color FUEL_CRITICAL = new Color(1f, 0.2f, 0.2f, 0.8f); public static readonly Color GAUGE_BACKGROUND = new Color(0.1f, 0.1f, 0.1f, 0.6f); public static readonly Color GAUGE_BORDER = new Color(0.8f, 0.8f, 0.8f, 0.8f); } public const float GAUGE_UPDATE_INTERVAL = 0.1f; public const float GAUGE_WIDTH = 200f; public const float GAUGE_HEIGHT = 20f; } public static class Game { public const string GAME_STUDIO = "TVGS"; public const string GAME_NAME = "Schedule I"; public const string VEHICLE_LAYER = "Vehicle"; public const string UI_LAYER = "UI"; public const string MAIN_SCENE = "Main"; public const string MENU_SCENE = "Menu"; } public static class Network { public const string FUEL_UPDATE_MESSAGE_TYPE = "fuel_update"; public const string FUEL_SYNC_MESSAGE_TYPE = "fuel_sync"; public const float SYNC_INTERVAL = 1f; } public static class SaveSystem { public const string FUEL_DATA_KEY_PREFIX = "fuel_"; public const string FUEL_LEVEL_KEY = "fuel_level"; public const string MAX_CAPACITY_KEY = "max_capacity"; public const string CONSUMPTION_RATE_KEY = "consumption_rate"; } public const string MOD_NAME = "S1FuelMod"; public const string MOD_VERSION = "1.3.3"; public const string MOD_AUTHORS = "Bars & SirTidez"; public const string MOD_DESCRIPTION = "Adds a comprehensive fuel system to LandVehicles in Schedule I"; public const string PREFERENCES_CATEGORY = "S1FuelMod"; public const string PREFERENCES_FILE_PATH = "UserData/S1FuelMod.cfg"; } public enum FuelTypeId { Regular, MidGrade, Premium, Diesel } public static class ModLogger { public static void Info(string message) { MelonLogger.Msg(message); } public static void Warning(string message) { MelonLogger.Warning(message); } public static void Error(string message) { MelonLogger.Error(message); } public static void Error(string message, Exception exception) { MelonLogger.Error(message + ": " + exception.Message); MelonLogger.Error("Stack trace: " + exception.StackTrace); } public static void Debug(string message) { Core instance = Core.Instance; if (instance != null && instance.EnableDebugLogging) { MelonLogger.Msg("[DEBUG] " + message); } } public static void FuelDebug(string message) { Core instance = Core.Instance; if (instance != null && instance.EnableDebugLogging) { MelonLogger.Msg("[FUEL] " + message); } } public static void UIDebug(string message) { Core instance = Core.Instance; if (instance != null && instance.EnableDebugLogging) { MelonLogger.Msg("[UI] " + message); } } public static void LogInitialization() { Info("Initializing S1FuelMod v1.3.3 by Bars & SirTidez"); Info("Adds a comprehensive fuel system to LandVehicles in Schedule I"); } public static void LogVehicleFuel(string vehicleCode, string vehicleGUID, float currentFuel, float maxCapacity) { Core instance = Core.Instance; if (instance != null && instance.EnableDebugLogging) { MelonLogger.Msg($"[FUEL] {vehicleCode} ({vehicleGUID.Substring(0, 8)}...): {currentFuel:F1}L / {maxCapacity:F1}L ({currentFuel / maxCapacity * 100f:F1}%)"); } } public static void LogFuelConsumption(string vehicleGUID, float consumed, float remaining) { Core instance = Core.Instance; if (instance != null && instance.EnableDebugLogging) { MelonLogger.Msg($"[FUEL] Vehicle {vehicleGUID.Substring(0, 8)}... consumed {consumed:F3}L, remaining: {remaining:F1}L"); } } public static void LogFuelWarning(string vehicleGUID, float fuelLevel, string warningType) { MelonLogger.Warning($"[FUEL] Vehicle {vehicleGUID.Substring(0, 8)}... {warningType} fuel warning: {fuelLevel:F1}L"); } } internal static class ReflectionUtils { internal static MethodInfo? GetMethod(Type? type, string methodName, BindingFlags bindingFlags) { while (type != null && type != typeof(object)) { MethodInfo method = type.GetMethod(methodName, bindingFlags); if (method != null) { return method; } type = type.BaseType; } return null; } internal static bool TrySetFieldOrProperty(object target, string memberName, object value) { if (target == null) { return false; } Type type = target.GetType(); FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { try { if (value == null || field.FieldType.IsInstanceOfType(value)) { field.SetValue(target, value); return true; } } catch { } } PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite) { try { if (value == null || property.PropertyType.IsInstanceOfType(value)) { property.SetValue(target, value); return true; } } catch { } } return false; } internal static object TryGetFieldOrProperty(object target, string memberName) { if (target == null) { return null; } Type type = target.GetType(); FieldInfo field = type.GetField(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { try { return field.GetValue(target); } catch { } } PropertyInfo property = type.GetProperty(memberName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanRead) { try { return property.GetValue(target); } catch { } } return null; } internal static object TryGetStaticFieldOrProperty(Type type, string memberName) { if (type == null) { return null; } FieldInfo field = type.GetField(memberName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { try { return field.GetValue(null); } catch { } } PropertyInfo property = type.GetProperty(memberName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanRead) { try { return property.GetValue(null); } catch { } } return null; } internal static bool TrySetStaticFieldOrProperty(Type type, string memberName, object value) { if (type == null) { return false; } FieldInfo field = type.GetField(memberName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (field != null) { try { if (value == null || field.FieldType.IsInstanceOfType(value)) { field.SetValue(null, value); return true; } } catch { } } PropertyInfo property = type.GetProperty(memberName, BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic); if (property != null && property.CanWrite) { try { if (value == null || property.PropertyType.IsInstanceOfType(value)) { property.SetValue(null, value); return true; } } catch { } } return false; } } public static class VehicleTypeExtensions { public static VehicleClass GetVehicleClass(this VehicleType vehicleType) { switch (vehicleType) { case VehicleType.Hounddog: case VehicleType.Cheetah: case VehicleType.BugattiTourbillon: case VehicleType.GTR_R34: case VehicleType.GTR_R35: case VehicleType.LamborghiniVeneno: case VehicleType.Supercar: case VehicleType.KoenigseggCC850: return VehicleClass.Sports; case VehicleType.Bruiser: case VehicleType.Dinkler: case VehicleType.CyberTruck: return VehicleClass.Truck; case VehicleType.RollsRoyceGhost: return VehicleClass.Luxury; default: return VehicleClass.Economy; } } } public enum VehicleClass { Economy, Sports, Truck, Luxury } } namespace S1FuelMod.UI { public class FuelGauge : IDisposable { private readonly VehicleFuelSystem _fuelSystem; private GameObject? _gaugeContainer; private RectTransform? _gaugeContainerRect; private RectTransform? _gaugeNeedleRect; private Image? _gaugeBackground; private TextMeshProUGUI? _gaugeText; private Canvas? _parentCanvas; private CanvasGroup? _gaugeTextGroup; private bool _isVisible; private float _lastUpdateTime; public bool IsVisible => _isVisible && (Object)(object)_gaugeContainer != (Object)null && _gaugeContainer.activeInHierarchy; public FuelGauge(VehicleFuelSystem fuelSystem) { _fuelSystem = fuelSystem ?? throw new ArgumentNullException("fuelSystem"); CreateGauge(); } private void CreateGauge() { //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_003e: Expected O, but got Unknown //IL_007c: Unknown result type (might be due to invalid IL or missing references) //IL_0097: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) //IL_00de: Unknown result type (might be due to invalid IL or missing references) _parentCanvas = FindUICanvas(); if ((Object)(object)_parentCanvas == (Object)null) { ModLogger.Error("FuelGaugeUI: No suitable UI canvas found. Cannot create gauge."); return; } _gaugeContainer = new GameObject("FuelGauge"); _gaugeContainer.transform.SetParent(((Component)_parentCanvas).transform, false); _gaugeContainerRect = _gaugeContainer.AddComponent(); _gaugeContainerRect.anchorMin = new Vector2(0.42f, 0.1f); _gaugeContainerRect.anchorMax = new Vector2(0.42f, 0.1f); _gaugeContainerRect.pivot = new Vector2(0f, 1f); _gaugeContainerRect.sizeDelta = new Vector2(200f, 75f); _gaugeContainerRect.anchoredPosition = Vector2.zero; SetupEventListeners(); CreateGaugeBackground(); CreateFillElements(); CreateGaugeNeedle(); _gaugeContainer.SetActive(true); } private void SetupEventListeners() { try { _fuelSystem.OnFuelLevelChanged.AddListener((UnityAction)OnFuelLevelChanged); _fuelSystem.OnFuelPercentageChanged.AddListener((UnityAction)OnFuelPercentageChanged); _fuelSystem.OnLowFuelWarning.AddListener((UnityAction)OnLowFuelWarning); _fuelSystem.OnCriticalFuelWarning.AddListener((UnityAction)OnCriticalFuelWarning); _fuelSystem.OnFuelEmpty.AddListener((UnityAction)OnFuelEmpty); } catch (Exception exception) { ModLogger.Error("Error setting up event listeners", exception); } } private void CreateGaugeBackground() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0045: Unknown result type (might be due to invalid IL or missing references) //IL_0051: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) try { GameObject val = new GameObject("FuelGaugeBackground"); val.transform.SetParent(_gaugeContainer.transform, false); RectTransform val2 = val.AddComponent(); val2.anchorMin = Vector2.zero; val2.anchorMax = Vector2.one; val2.offsetMin = Vector2.zero; val2.offsetMax = Vector2.zero; _gaugeBackground = val.AddComponent(); ((Graphic)_gaugeBackground).color = new Color(0.8f, 0.8f, 0.8f, 0.5f); _gaugeBackground.type = (Type)0; Outline val3 = val.AddComponent(); ((Shadow)val3).effectColor = new Color(0f, 0f, 0f, 0.8f); ((Shadow)val3).effectDistance = new Vector2(1f, 1f); } catch (Exception exception) { ModLogger.Error("Error creating gauge background", exception); } } private void CreateFillElements() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_003d: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_0069: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_00b2: Unknown result type (might be due to invalid IL or missing references) //IL_00d3: Unknown result type (might be due to invalid IL or missing references) //IL_00ea: Unknown result type (might be due to invalid IL or missing references) //IL_00fa: Unknown result type (might be due to invalid IL or missing references) //IL_0101: Expected O, but got Unknown //IL_0138: Unknown result type (might be due to invalid IL or missing references) //IL_014f: 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_017d: Unknown result type (might be due to invalid IL or missing references) //IL_01b6: Unknown result type (might be due to invalid IL or missing references) //IL_01d9: Unknown result type (might be due to invalid IL or missing references) //IL_01f0: Unknown result type (might be due to invalid IL or missing references) //IL_0206: Unknown result type (might be due to invalid IL or missing references) //IL_0221: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_0254: Unknown result type (might be due to invalid IL or missing references) //IL_027f: Unknown result type (might be due to invalid IL or missing references) //IL_0286: Expected O, but got Unknown //IL_02d9: Unknown result type (might be due to invalid IL or missing references) //IL_02e0: Expected O, but got Unknown //IL_031b: Unknown result type (might be due to invalid IL or missing references) //IL_0332: Unknown result type (might be due to invalid IL or missing references) //IL_0349: Unknown result type (might be due to invalid IL or missing references) //IL_0356: Unknown result type (might be due to invalid IL or missing references) //IL_036d: Unknown result type (might be due to invalid IL or missing references) //IL_03ba: Unknown result type (might be due to invalid IL or missing references) //IL_03e1: Unknown result type (might be due to invalid IL or missing references) //IL_03f8: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("Empty"); val.transform.SetParent(_gaugeContainer.transform, false); CanvasGroup val2 = val.AddComponent(); RectTransform val3 = val.AddComponent(); val3.anchorMin = new Vector2(0.13f, 0.75f); val3.anchorMax = new Vector2(0.23f, 0.85f); val3.anchoredPosition = new Vector2(-5f, -2f); val3.sizeDelta = new Vector2(40f, 20f); TextMeshProUGUI val4 = val.AddComponent(); ((TMP_Text)val4).alignment = (TextAlignmentOptions)513; ((TMP_Text)val4).fontSize = 18f; ((TMP_Text)val4).fontStyle = (FontStyles)1; ((Graphic)val4).color = Color.red; ((TMP_Text)val4).text = "E"; Shadow val5 = val.AddComponent(); val5.effectColor = Color.black; val5.effectDistance = new Vector2(1f, -1f); GameObject val6 = new GameObject("Full"); val6.transform.SetParent(_gaugeContainer.transform, false); CanvasGroup val7 = val6.AddComponent(); RectTransform val8 = val6.AddComponent(); val8.anchorMin = new Vector2(0.77f, 0.75f); val8.anchorMax = new Vector2(0.87f, 0.85f); val8.anchoredPosition = new Vector2(5f, 0f); val8.sizeDelta = new Vector2(40f, 20f); TextMeshProUGUI val9 = val6.AddComponent(); ((TMP_Text)val9).alignment = (TextAlignmentOptions)516; ((TMP_Text)val9).fontSize = 18f; ((TMP_Text)val9).fontStyle = (FontStyles)1; ((Graphic)val9).color = Color.white; ((TMP_Text)val9).text = "F"; Shadow val10 = val6.AddComponent(); val10.effectColor = Color.black; val10.effectDistance = new Vector2(1f, -1f); val3.pivot = new Vector2(0.5f, 0.5f); ((Transform)val3).localRotation = Quaternion.Euler(0f, 0f, 40f); val8.pivot = new Vector2(0.5f, 0.5f); ((Transform)val8).localRotation = Quaternion.Euler(0f, 0f, -43f); List list = new List(); int num = 0; while (list.Count < 5) { GameObject val11 = new GameObject($"Fuel Gauge Tick {num + 1}"); val11.transform.SetParent(_gaugeContainer.transform, false); RectTransform val12 = val11.AddComponent(); CreateTick(num, val11); list.Add(val11); num++; } GameObject val13 = new GameObject("Fuel Readout"); val13.transform.SetParent(_gaugeContainer.transform, false); _gaugeTextGroup = val13.AddComponent(); RectTransform val14 = val13.AddComponent(); val14.anchorMin = new Vector2(0f, 0.1f); val14.anchorMax = new Vector2(0.3f, 0.25f); val14.pivot = new Vector2(0f, 1f); val14.anchoredPosition = Vector2.zero; val14.sizeDelta = new Vector2(50f, 40f); _gaugeText = val13.AddComponent(); ((TMP_Text)_gaugeText).alignment = (TextAlignmentOptions)513; ((TMP_Text)_gaugeText).fontSize = 12f; ((TMP_Text)_gaugeText).fontStyle = (FontStyles)1; ((Graphic)_gaugeText).color = Color.white; ((TMP_Text)_gaugeText).text = "50.0L (100%)"; Shadow val15 = val13.AddComponent(); val15.effectColor = Color.black; val15.effectDistance = new Vector2(1f, -1f); } private void CreateGaugeNeedle() { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_000c: Expected O, but got Unknown //IL_0040: 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_0076: Unknown result type (might be due to invalid IL or missing references) //IL_0091: Unknown result type (might be due to invalid IL or missing references) //IL_00ac: Unknown result type (might be due to invalid IL or missing references) //IL_00bf: Unknown result type (might be due to invalid IL or missing references) GameObject val = new GameObject("FuelGaugeNeedle"); val.transform.SetParent(_gaugeContainer.transform, false); _gaugeNeedleRect = val.AddComponent(); _gaugeNeedleRect.anchorMin = new Vector2(0.5f, 0.01f); _gaugeNeedleRect.anchorMax = new Vector2(0.5f, 0.01f); _gaugeNeedleRect.sizeDelta = new Vector2(1f, 70f); _gaugeNeedleRect.pivot = new Vector2(0.5f, 0f); _gaugeNeedleRect.anchoredPosition = new Vector2(0f, 0f); Image val2 = val.AddComponent(); ((Graphic)val2).color = Color.red; val2.type = (Type)0; } private void CreateTick(int index, GameObject tickObject) { //IL_0036: 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_0062: Unknown result type (might be due to invalid IL or missing references) //IL_0078: Unknown result type (might be due to invalid IL or missing references) //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_00a9: Unknown result type (might be due to invalid IL or missing references) //IL_00c4: Unknown result type (might be due to invalid IL or missing references) //IL_00da: 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_0106: 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_0137: 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_0168: Unknown result type (might be due to invalid IL or missing references) //IL_017e: Unknown result type (might be due to invalid IL or missing references) //IL_0194: 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_01c5: Unknown result type (might be due to invalid IL or missing references) //IL_01e0: Unknown result type (might be due to invalid IL or missing references) //IL_01f6: Unknown result type (might be due to invalid IL or missing references) //IL_020c: Unknown result type (might be due to invalid IL or missing references) //IL_0222: Unknown result type (might be due to invalid IL or missing references) //IL_0238: Unknown result type (might be due to invalid IL or missing references) //IL_0253: Unknown result type (might be due to invalid IL or missing references) //IL_026e: Unknown result type (might be due to invalid IL or missing references) //IL_0284: Unknown result type (might be due to invalid IL or missing references) //IL_029a: Unknown result type (might be due to invalid IL or missing references) //IL_02b0: Unknown result type (might be due to invalid IL or missing references) //IL_02c6: Unknown result type (might be due to invalid IL or missing references) //IL_02e1: Unknown result type (might be due to invalid IL or missing references) //IL_033c: Unknown result type (might be due to invalid IL or missing references) RectTransform component = tickObject.GetComponent(); switch (index) { case 0: component.anchorMin = new Vector2(0.5f, 0.75f); component.anchorMax = new Vector2(0.5f, 0.75f); component.anchoredPosition = new Vector2(-77f, -25f); component.sizeDelta = new Vector2(3f, 15f); component.pivot = new Vector2(0.5f, 0.5f); ((Transform)component).localRotation = Quaternion.Euler(0f, 0f, 55f); break; case 1: component.anchorMin = new Vector2(0.5f, 0.75f); component.anchorMax = new Vector2(0.5f, 0.75f); component.anchoredPosition = new Vector2(-40f, -5f); component.sizeDelta = new Vector2(3f, 10f); component.pivot = new Vector2(0.5f, 0.5f); ((Transform)component).localRotation = Quaternion.Euler(0f, 0f, 30f); break; case 2: component.anchorMin = new Vector2(0.5f, 0.75f); component.anchorMax = new Vector2(0.5f, 0.75f); component.anchoredPosition = new Vector2(0f, 0f); component.sizeDelta = new Vector2(3f, 15f); component.pivot = new Vector2(0.5f, 0.5f); ((Transform)component).localRotation = Quaternion.Euler(0f, 0f, 0f); break; case 3: component.anchorMin = new Vector2(0.5f, 0.75f); component.anchorMax = new Vector2(0.5f, 0.75f); component.anchoredPosition = new Vector2(40f, -5f); component.sizeDelta = new Vector2(3f, 10f); component.pivot = new Vector2(0.5f, 0.5f); ((Transform)component).localRotation = Quaternion.Euler(0f, 0f, -30f); break; case 4: component.anchorMin = new Vector2(0.5f, 0.75f); component.anchorMax = new Vector2(0.5f, 0.75f); component.anchoredPosition = new Vector2(77f, -25f); component.sizeDelta = new Vector2(3f, 15f); component.pivot = new Vector2(0.5f, 0.5f); ((Transform)component).localRotation = Quaternion.Euler(0f, 0f, -55f); break; default: ModLogger.Error($"FuelGaugeUI: Invalid tick index {index}. Must be between 0 and 4."); return; } Image val = tickObject.AddComponent(); val.type = (Type)0; ((Graphic)val).color = new Color(0.15f, 0.15f, 0.15f, (index == 0 || index == 2 || index == 4) ? 1f : 0.75f); tickObject.SetActive(true); } private Canvas? FindUICanvas() { //IL_0002: 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) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) try { Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; ModLogger.UIDebug("FuelGaugeUI: Current scene: " + name); if (name.Contains("Menu")) { ModLogger.UIDebug("FuelGaugeUI: Skipping UI creation in scene: " + name); return null; } if (Singleton.InstanceExists) { Canvas canvas = Singleton.Instance.canvas; if ((Object)(object)canvas != (Object)null) { ModLogger.UIDebug("FuelGaugeUI: Found HUD Singleton canvas in scene: " + name); return canvas; } } GameObject val = GameObject.Find("HUD"); if ((Object)(object)val != (Object)null) { Canvas component = val.GetComponent(); if ((Object)(object)component != (Object)null) { ModLogger.UIDebug("FuelGaugeUI: Found HUD GameObject canvas in scene: " + name); return component; } } Canvas[] array = Object.FindObjectsOfType(); Canvas[] array2 = array; foreach (Canvas val2 in array2) { if ((int)val2.renderMode == 0 && !((Object)val2).name.Contains("Menu")) { ModLogger.UIDebug("FuelGaugeUI: Found suitable overlay canvas: " + ((Object)val2).name + " in scene: " + name); return val2; } } ModLogger.Warning("FuelGaugeUI: No suitable UI canvas found in scene: " + name); return null; } catch (Exception exception) { ModLogger.Error("Error finding UI canvas", exception); return null; } } public void Update() { try { if (!_isVisible && !((Object)(object)_gaugeContainer == (Object)null) && !(Time.time - _lastUpdateTime < 0.1f)) { UpdateDisplay(); _lastUpdateTime = Time.time; } } catch (Exception exception) { ModLogger.Error("Error in updating the fuel gauge", exception); } } private void UpdateDisplay() { try { if ((Object)(object)_fuelSystem == (Object)null) { ModLogger.UIDebug("FuelGauge: UpdateDisplay called but fuel system is null"); } float currentFuelLevel = _fuelSystem.CurrentFuelLevel; float maxFuelCapacity = _fuelSystem.MaxFuelCapacity; float fuelPercentage = _fuelSystem.FuelPercentage; if ((Object)(object)_gaugeNeedleRect != (Object)null) { RotateNeedle(); } if ((Object)(object)_gaugeText != (Object)null) { string text = $"{currentFuelLevel:F1}L ({fuelPercentage:F1}%)"; ((TMP_Text)_gaugeText).text = text; } } catch (Exception exception) { ModLogger.Error("Error updating gauge display", exception); } } public void RotateNeedle() { //IL_003f: Unknown result type (might be due to invalid IL or missing references) float currentFuelLevel = _fuelSystem.CurrentFuelLevel; float maxFuelCapacity = _fuelSystem.MaxFuelCapacity; float num = currentFuelLevel / maxFuelCapacity; float num2 = Mathf.Lerp(70f, -70f, num); ((Transform)_gaugeNeedleRect).localRotation = Quaternion.Euler(0f, 0f, num2); } private void OnFuelLevelChanged(float fuelLevel) { if (_isVisible) { UpdateDisplay(); } } private void OnFuelPercentageChanged(float percentage) { if (_isVisible) { UpdateDisplay(); } } public void Show() { try { if ((Object)(object)_gaugeContainer != (Object)null) { _gaugeContainer.SetActive(true); _isVisible = true; UpdateDisplay(); ModLogger.UIDebug("FuelGauge: Shown for vehicle " + _fuelSystem.VehicleGUID.Substring(0, 8) + "..."); } } catch (Exception exception) { ModLogger.Error("Error showing fuel gauge", exception); } } public void Hide() { try { if ((Object)(object)_gaugeContainer != (Object)null) { _gaugeContainer.SetActive(false); _isVisible = false; ModLogger.UIDebug("FuelGauge: Hidden for vehicle " + _fuelSystem.VehicleGUID.Substring(0, 8) + "..."); } } catch (Exception exception) { ModLogger.Error("Error hiding fuel gauge", exception); } } public void Dispose() { try { if ((Object)(object)_fuelSystem != (Object)null) { _fuelSystem.OnFuelLevelChanged.RemoveListener((UnityAction)OnFuelLevelChanged); _fuelSystem.OnFuelPercentageChanged.RemoveListener((UnityAction)OnFuelPercentageChanged); _fuelSystem.OnLowFuelWarning.RemoveListener((UnityAction)OnLowFuelWarning); _fuelSystem.OnCriticalFuelWarning.RemoveListener((UnityAction)OnCriticalFuelWarning); _fuelSystem.OnFuelEmpty.RemoveListener((UnityAction)OnFuelEmpty); } if ((Object)(object)_gaugeContainer != (Object)null) { Object.Destroy((Object)(object)_gaugeContainer); _gaugeContainer = null; } _gaugeNeedleRect = null; _gaugeText = null; _isVisible = false; } catch (Exception exception) { ModLogger.Error("Error disposing FuelGauge", exception); } } private void OnLowFuelWarning(bool isActive) { ModLogger.UIDebug("FuelGauge: Low fuel warning " + (isActive ? "activated" : "deactivated")); } private void OnCriticalFuelWarning(bool isActive) { ModLogger.UIDebug("FuelGauge: Critical fuel warning " + (isActive ? "activated" : "deactivated")); } private void OnFuelEmpty(bool isEmpty) { ModLogger.UIDebug("FuelGauge: Fuel empty state " + (isEmpty ? "activated" : "deactivated")); } } public class FuelGaugeUI : IDisposable { private readonly VehicleFuelSystem _fuelSystem; private GameObject? _gaugeContainer; private RectTransform? _gaugeRect; private Image? _gaugeBackground; private Slider? _gaugeSlider; private TextMeshProUGUI? _fuelText; private Image? _warningIcon; private Image? _gaugeSliderImage; private bool _isVisible = false; private float _lastUpdateTime = 0f; private Canvas? _parentCanvas; private CanvasGroup? _fuelTextParentGroup; public bool IsVisible => _isVisible && (Object)(object)_gaugeContainer != (Object)null && _gaugeContainer.activeInHierarchy; public FuelGaugeUI(VehicleFuelSystem fuelSystem) { _fuelSystem = fuelSystem ?? throw new ArgumentNullException("fuelSystem"); try { CreateGaugeUI(); SetupEventListeners(); UpdateDisplay(); ModLogger.UIDebug("FuelGaugeUI: Created for vehicle " + _fuelSystem.VehicleGUID.Substring(0, 8) + "..."); } catch (Exception exception) { ModLogger.Error("Error creating FuelGaugeUI", exception); } } private void CreateGaugeUI() { //IL_0035: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Expected O, but got Unknown //IL_007d: 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_00b3: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Unknown result type (might be due to invalid IL or missing references) //IL_00df: Unknown result type (might be due to invalid IL or missing references) try { _parentCanvas = FindUICanvas(); if ((Object)(object)_parentCanvas == (Object)null) { ModLogger.Error("FuelGaugeUI: Could not find UI canvas"); return; } _gaugeContainer = new GameObject("FuelGauge"); _gaugeContainer.transform.SetParent(((Component)_parentCanvas).transform, false); _gaugeRect = _gaugeContainer.AddComponent(); _gaugeRect.anchorMin = new Vector2(0.02f, 0.95f); _gaugeRect.anchorMax = new Vector2(0.02f, 0.95f); _gaugeRect.pivot = new Vector2(0f, 1f); _gaugeRect.sizeDelta = new Vector2(200f, 45f); _gaugeRect.anchoredPosition = Vector2.zero; CreateGaugeBackground(); CreateGaugeFill(); CreateFuelText(); CreateWarningIcon(); _gaugeContainer.SetActive(false); ModLogger.UIDebug("FuelGaugeUI: UI elements created successfully"); } catch (Exception exception) { ModLogger.Error("Error creating gauge UI elements", exception); } } private Canvas? FindUICanvas() { //IL_0002: 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) //IL_00f2: Unknown result type (might be due to invalid IL or missing references) try { Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; ModLogger.UIDebug("FuelGaugeUI: Current scene: " + name); if (name.Contains("Menu")) { ModLogger.UIDebug("FuelGaugeUI: Skipping UI creation in scene: " + name); return null; } if (Singleton.InstanceExists) { Canvas canvas = Singleton.Instance.canvas; if ((Object)(object)canvas != (Object)null) { ModLogger.UIDebug("FuelGaugeUI: Found HUD Singleton canvas in scene: " + name); return canvas; } } GameObject val = GameObject.Find("HUD"); if ((Object)(object)val != (Object)null) { Canvas component = val.GetComponent(); if ((Object)(object)component != (Object)null) { ModLogger.UIDebug("FuelGaugeUI: Found HUD GameObject canvas in scene: " + name); return component; } } Canvas[] array = Object.FindObjectsOfType(); Canvas[] array2 = array; foreach (Canvas val2 in array2) { if ((int)val2.renderMode == 0 && !((Object)val2).name.Contains("Menu")) { ModLogger.UIDebug("FuelGaugeUI: Found suitable overlay canvas: " + ((Object)val2).name + " in scene: " + name); return val2; } } ModLogger.Warning("FuelGaugeUI: No suitable UI canvas found in scene: " + name); return null; } catch (Exception exception) { ModLogger.Error("Error finding UI canvas", exception); return null; } } private void CreateGaugeBackground() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0043: Unknown result type (might be due to invalid IL or missing references) //IL_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_0078: 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_00ae: Unknown result type (might be due to invalid IL or missing references) try { GameObject val = new GameObject("Background"); val.transform.SetParent(_gaugeContainer.transform, false); RectTransform val2 = val.AddComponent(); val2.anchorMin = Vector2.zero; val2.anchorMax = new Vector2(1f, 0.8f); val2.offsetMin = Vector2.zero; val2.offsetMax = Vector2.zero; _gaugeBackground = val.AddComponent(); ((Graphic)_gaugeBackground).color = Constants.UI.Colors.GAUGE_BACKGROUND; _gaugeBackground.type = (Type)0; Outline val3 = val.AddComponent(); ((Shadow)val3).effectColor = Constants.UI.Colors.GAUGE_BORDER; ((Shadow)val3).effectDistance = new Vector2(1f, 1f); } catch (Exception exception) { ModLogger.Error("Error creating gauge background", exception); } } private void CreateGaugeFill() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown //IL_0037: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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_00c8: Unknown result type (might be due to invalid IL or missing references) //IL_00ce: Expected O, but got Unknown //IL_00fb: Unknown result type (might be due to invalid IL or missing references) //IL_0108: Unknown result type (might be due to invalid IL or missing references) //IL_0115: Unknown result type (might be due to invalid IL or missing references) //IL_0122: Unknown result type (might be due to invalid IL or missing references) //IL_012f: Unknown result type (might be due to invalid IL or missing references) try { GameObject val = new GameObject("Fill"); val.transform.SetParent(_gaugeContainer.transform, false); RectTransform val2 = val.AddComponent(); val2.anchorMin = new Vector2(0.05f, 0.1f); val2.anchorMax = new Vector2(0.95f, 0.7f); val2.offsetMin = Vector2.zero; val2.offsetMax = Vector2.zero; _gaugeSlider = val.AddComponent(); bool flag = Core.Instance?.SwapGaugeDirection ?? false; _gaugeSlider.direction = (Direction)((!flag) ? 1 : 0); _gaugeSlider.minValue = 0f; _gaugeSlider.maxValue = 100f; GameObject val3 = new GameObject("SliderFill"); val3.transform.SetParent(val.transform, false); RectTransform val4 = val3.AddComponent(); _gaugeSliderImage = val3.AddComponent(); ((Graphic)_gaugeSliderImage).color = Constants.UI.Colors.FUEL_NORMAL; val4.anchorMin = Vector2.zero; val4.anchorMax = Vector2.one; val4.offsetMin = Vector2.zero; val4.offsetMax = Vector2.zero; _gaugeSlider.fillRect = val4; } catch (Exception exception) { ModLogger.Error("Error creating gauge fill", exception); } } private void CreateFuelText() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown //IL_0043: 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_006f: Unknown result type (might be due to invalid IL or missing references) //IL_0085: Unknown result type (might be due to invalid IL or missing references) //IL_009b: Unknown result type (might be due to invalid IL or missing references) //IL_00e7: Unknown result type (might be due to invalid IL or missing references) //IL_010b: 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) try { GameObject val = new GameObject("FuelText"); val.transform.SetParent(_gaugeContainer.transform, false); _fuelTextParentGroup = val.AddComponent(); RectTransform val2 = val.AddComponent(); val2.anchorMin = new Vector2(0f, 0.8f); val2.anchorMax = new Vector2(1f, 1f); val2.pivot = new Vector2(0f, 1f); val2.anchoredPosition = new Vector2(0f, -50f); val2.sizeDelta = new Vector2(400f, 20f); _fuelText = val.AddComponent(); ((TMP_Text)_fuelText).alignment = (TextAlignmentOptions)513; ((TMP_Text)_fuelText).fontSize = 12f; ((TMP_Text)_fuelText).fontStyle = (FontStyles)1; ((Graphic)_fuelText).color = Color.white; ((TMP_Text)_fuelText).text = "50.0L (100%)"; Shadow val3 = val.AddComponent(); val3.effectColor = Color.black; val3.effectDistance = new Vector2(1f, -1f); } catch (Exception exception) { ModLogger.Error("Error creating fuel text", exception); } } private void CreateWarningIcon() { //IL_0007: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Expected O, but got Unknown //IL_007f: Unknown result type (might be due to invalid IL or missing references) //IL_0095: 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_0065: Unknown result type (might be due to invalid IL or missing references) //IL_00a2: Unknown result type (might be due to invalid IL or missing references) //IL_00ae: Unknown result type (might be due to invalid IL or missing references) //IL_00cb: Unknown result type (might be due to invalid IL or missing references) try { GameObject val = new GameObject("WarningIcon"); val.transform.SetParent(_gaugeContainer.transform, false); RectTransform val2 = val.AddComponent(); Core instance = Core.Instance; if (instance != null && instance.SwapGaugeDirection) { val2.anchorMin = new Vector2(0.05f, 0.15f); val2.anchorMax = new Vector2(0.15f, 0.65f); } else { val2.anchorMin = new Vector2(0.85f, 0.15f); val2.anchorMax = new Vector2(0.95f, 0.65f); } val2.offsetMin = Vector2.zero; val2.offsetMax = Vector2.zero; _warningIcon = val.AddComponent(); ((Graphic)_warningIcon).color = Constants.UI.Colors.FUEL_CRITICAL; _warningIcon.type = (Type)0; val.SetActive(false); } catch (Exception exception) { ModLogger.Error("Error creating warning icon", exception); } } private void SetupEventListeners() { try { if ((Object)(object)_fuelSystem != (Object)null) { ModLogger.UIDebug("FuelGaugeUI: Setting up event listeners for vehicle " + _fuelSystem.VehicleGUID.Substring(0, 8) + "..."); _fuelSystem.OnFuelLevelChanged.AddListener((UnityAction)OnFuelLevelChanged); _fuelSystem.OnFuelPercentageChanged.AddListener((UnityAction)OnFuelPercentageChanged); _fuelSystem.OnLowFuelWarning.AddListener((UnityAction)OnLowFuelWarning); _fuelSystem.OnCriticalFuelWarning.AddListener((UnityAction)OnCriticalFuelWarning); _fuelSystem.OnFuelEmpty.AddListener((UnityAction)OnFuelEmpty); ModLogger.UIDebug("FuelGaugeUI: Event listeners set up successfully for vehicle " + _fuelSystem.VehicleGUID.Substring(0, 8) + "..."); } else { ModLogger.Warning("FuelGaugeUI: Cannot setup event listeners - fuel system is null"); } } catch (Exception exception) { ModLogger.Error("Error setting up event listeners", exception); } } public void Update() { try { if (_isVisible && !((Object)(object)_gaugeContainer == (Object)null) && !(Time.time - _lastUpdateTime < 0.1f)) { UpdateDisplay(); _lastUpdateTime = Time.time; } } catch (Exception exception) { ModLogger.Error("Error updating fuel gauge display", exception); } } private void UpdateDisplay() { try { if ((Object)(object)_fuelSystem == (Object)null) { ModLogger.UIDebug("FuelGaugeUI: UpdateDisplay called but fuel system is null"); return; } float currentFuelLevel = _fuelSystem.CurrentFuelLevel; float maxFuelCapacity = _fuelSystem.MaxFuelCapacity; float fuelPercentage = _fuelSystem.FuelPercentage; if ((Object)(object)_gaugeSlider != (Object)null && (Object)(object)_gaugeSliderImage != (Object)null) { _gaugeSlider.value = fuelPercentage; } ModLogger.UIDebug($"FuelGaugeUI: Percentage shown: {fuelPercentage}"); if ((Object)(object)_fuelText != (Object)null) { string text = $"{currentFuelLevel:F1}L ({fuelPercentage:F1}%)"; ((TMP_Text)_fuelText).text = text; } UpdateGaugeColors(); UpdateWarningIcon(); } catch (Exception exception) { ModLogger.Error("Error updating gauge display", exception); } } private void UpdateGaugeColors() { //IL_0017: Unknown result type (might be due to invalid IL or missing references) //IL_001c: Unknown result type (might be due to invalid IL or missing references) //IL_002d: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0038: Unknown result type (might be due to invalid IL or missing references) //IL_005e: 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_004e: Unknown result type (might be due to invalid IL or missing references) //IL_0053: Unknown result type (might be due to invalid IL or missing references) //IL_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_0071: 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_0082: 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) try { if (!((Object)(object)_fuelText == (Object)null)) { Color color = Color.white; Color val; if (_fuelSystem.IsCriticalFuel) { val = Constants.UI.Colors.FUEL_CRITICAL; color = Constants.UI.Colors.FUEL_CRITICAL; } else if (_fuelSystem.IsLowFuel) { val = Constants.UI.Colors.FUEL_LOW; color = Constants.UI.Colors.FUEL_LOW; } else { val = Constants.UI.Colors.FUEL_NORMAL; } ((Graphic)_gaugeSliderImage).color = Color.Lerp(((Graphic)_gaugeSliderImage).color, val, Time.deltaTime * 5f); ((Graphic)_fuelText).color = color; } } catch (Exception exception) { ModLogger.Error("Error updating gauge colors", exception); } } public void UpdateGaugeDirection() { //IL_00b6: Unknown result type (might be due to invalid IL or missing references) //IL_00cc: Unknown result type (might be due to invalid IL or missing references) //IL_0086: Unknown result type (might be due to invalid IL or missing references) //IL_009c: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)_gaugeSlider == (Object)null || (Object)(object)_warningIcon == (Object)null || (Object)(object)_gaugeRect == (Object)null) { return; } bool flag = Core.Instance?.SwapGaugeDirection ?? false; _gaugeSlider.direction = (Direction)((!flag) ? 1 : 0); RectTransform component = ((Component)_warningIcon).GetComponent(); if ((Object)(object)component != (Object)null) { if (flag) { component.anchorMin = new Vector2(0.05f, 0.15f); component.anchorMax = new Vector2(0.15f, 0.65f); } else { component.anchorMin = new Vector2(0.85f, 0.15f); component.anchorMax = new Vector2(0.95f, 0.65f); } } ModLogger.UIDebug("FuelGaugeUI: Gauge updated - Direction: " + (flag ? "LeftToRight" : "RightToLeft")); } catch (Exception exception) { ModLogger.Error("Error updating gauge direction", exception); } } private void UpdateWarningIcon() { //IL_008f: Unknown result type (might be due to invalid IL or missing references) //IL_0094: Unknown result type (might be due to invalid IL or missing references) //IL_00a5: Unknown result type (might be due to invalid IL or missing references) //IL_0145: Unknown result type (might be due to invalid IL or missing references) //IL_014a: 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_00f9: Unknown result type (might be due to invalid IL or missing references) //IL_00fe: 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_010b: 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_0133: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)_warningIcon == (Object)null) { return; } bool flag = _fuelSystem.IsLowFuel || _fuelSystem.IsCriticalFuel || _fuelSystem.IsOutOfFuel; ((Component)_warningIcon).gameObject.SetActive(flag); if (flag) { if (_fuelSystem.IsLowFuel) { float a = 0.5f + 0.5f * Mathf.Sin(Time.time * 2f); Color color = ((Graphic)_gaugeSliderImage).color; color.a = a; ((Graphic)_gaugeSliderImage).color = color; } else if (_fuelSystem.IsCriticalFuel || _fuelSystem.IsOutOfFuel) { float a2 = 0.5f + 0.5f * Mathf.Sin(Time.time * 3f); Color color2 = ((Graphic)_warningIcon).color; Color color3 = ((Graphic)_gaugeSliderImage).color; color2.a = a2; color3.a = a2; ((Graphic)_warningIcon).color = color2; ((Graphic)_gaugeSliderImage).color = color3; } else { Color color4 = ((Graphic)_warningIcon).color; color4.a = 1f; ((Graphic)_warningIcon).color = color4; } } } catch (Exception exception) { ModLogger.Error("Error updating warning icon", exception); } } public void Show() { //IL_003a: Unknown result type (might be due to invalid IL or missing references) //IL_003f: Unknown result type (might be due to invalid IL or missing references) //IL_0052: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)_gaugeContainer != (Object)null) { _gaugeContainer.SetActive(true); if ((Object)(object)_gaugeSliderImage != (Object)null) { Color color = ((Graphic)_gaugeSliderImage).color; color.a = 1f; ((Graphic)_gaugeSliderImage).color = color; } _isVisible = true; UpdateGaugeDirection(); UpdateDisplay(); ModLogger.UIDebug("FuelGaugeUI: Shown for vehicle " + _fuelSystem.VehicleGUID.Substring(0, 8) + "..."); } } catch (Exception exception) { ModLogger.Error("Error showing fuel gauge", exception); } } public void Hide() { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_002f: Unknown result type (might be due to invalid IL or missing references) //IL_0042: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)_gaugeContainer != (Object)null) { if ((Object)(object)_gaugeSliderImage != (Object)null) { Color color = ((Graphic)_gaugeSliderImage).color; color.a = 0f; ((Graphic)_gaugeSliderImage).color = color; } _gaugeContainer.SetActive(false); _isVisible = false; ModLogger.UIDebug("FuelGaugeUI: Hidden for vehicle " + _fuelSystem.VehicleGUID.Substring(0, 8) + "..."); } } catch (Exception exception) { ModLogger.Error("Error hiding fuel gauge", exception); } } private void OnFuelLevelChanged(float fuelLevel) { if (_isVisible) { UpdateDisplay(); } } private void OnFuelPercentageChanged(float percentage) { if (_isVisible) { UpdateDisplay(); } } private void OnLowFuelWarning(bool isActive) { ModLogger.UIDebug("FuelGaugeUI: Low fuel warning " + (isActive ? "activated" : "deactivated")); } private void OnCriticalFuelWarning(bool isActive) { ModLogger.UIDebug("FuelGaugeUI: Critical fuel warning " + (isActive ? "activated" : "deactivated")); } private void OnFuelEmpty(bool isEmpty) { ModLogger.UIDebug("FuelGaugeUI: Fuel empty state " + (isEmpty ? "activated" : "deactivated")); } public void Dispose() { try { if ((Object)(object)_fuelSystem != (Object)null) { _fuelSystem.OnFuelLevelChanged.RemoveListener((UnityAction)OnFuelLevelChanged); _fuelSystem.OnFuelPercentageChanged.RemoveListener((UnityAction)OnFuelPercentageChanged); _fuelSystem.OnLowFuelWarning.RemoveListener((UnityAction)OnLowFuelWarning); _fuelSystem.OnCriticalFuelWarning.RemoveListener((UnityAction)OnCriticalFuelWarning); _fuelSystem.OnFuelEmpty.RemoveListener((UnityAction)OnFuelEmpty); } if ((Object)(object)_gaugeContainer != (Object)null) { Object.Destroy((Object)(object)_gaugeContainer); _gaugeContainer = null; } _gaugeRect = null; _gaugeBackground = null; _gaugeSliderImage = null; _fuelText = null; _warningIcon = null; _isVisible = false; ModLogger.UIDebug("FuelGaugeUI: Disposed for vehicle " + _fuelSystem.VehicleGUID.Substring(0, 8) + "..."); } catch (Exception exception) { ModLogger.Error("Error disposing FuelGaugeUI", exception); } } } public class FuelUIManager : IDisposable { private readonly Dictionary _activeFuelGauges = new Dictionary(); private readonly Dictionary _activeNewGauges = new Dictionary(); private Player? _localPlayer; private LandVehicle? _currentVehicle; private string _currentVehicleGUID = string.Empty; private FuelGaugeUI? _currentGauge; private FuelGauge? _currentNewGauge; public FuelUIManager() { ModLogger.UIDebug("FuelUIManager: Initializing..."); SceneManager.sceneLoaded += OnSceneLoaded; SceneManager.sceneUnloaded += OnSceneUnloaded; FindLocalPlayer(); ModLogger.UIDebug("FuelUIManager: Initialized"); } public void Update() { try { Core instance = Core.Instance; if ((instance == null || instance.ShowFuelGauge) && IsInGameScene()) { if ((Object)(object)_localPlayer == (Object)null) { FindLocalPlayer(); return; } CheckVehicleChange(); _currentGauge?.Update(); _currentNewGauge?.Update(); } } catch (Exception exception) { ModLogger.Error("Error in FuelUIManager.Update", exception); } } private void OnSceneLoaded(Scene scene, LoadSceneMode mode) { try { ModLogger.UIDebug("FuelUIManager: Scene loaded: " + ((Scene)(ref scene)).name); _localPlayer = null; _currentVehicle = null; _currentVehicleGUID = string.Empty; if (_currentGauge != null) { _currentGauge.Hide(); _currentGauge = null; } if (_currentNewGauge != null) { _currentNewGauge.Hide(); _currentNewGauge = null; } } catch (Exception exception) { ModLogger.Error("Error handling scene loaded event", exception); } } private void OnSceneUnloaded(Scene scene) { try { ModLogger.UIDebug("FuelUIManager: Scene unloaded: " + ((Scene)(ref scene)).name); List list = new List(); foreach (KeyValuePair activeFuelGauge in _activeFuelGauges) { if (activeFuelGauge.Value != null && !activeFuelGauge.Value.IsVisible) { list.Add(activeFuelGauge.Key); } } foreach (string item in list) { RemoveFuelGaugeForVehicle(item); } list.Clear(); foreach (KeyValuePair activeNewGauge in _activeNewGauges) { if (activeNewGauge.Value != null && !activeNewGauge.Value.IsVisible) { list.Add(activeNewGauge.Key); } } foreach (string item2 in list) { RemoveNewFuelGaugeForVehicle(item2); } } catch (Exception exception) { ModLogger.Error("Error handling scene unloaded event", exception); } } private bool IsInGameScene() { //IL_0002: 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) try { Scene activeScene = SceneManager.GetActiveScene(); string name = ((Scene)(ref activeScene)).name; if (name.Contains("Menu")) { return false; } return true; } catch (Exception exception) { ModLogger.Error("Error checking game scene", exception); return false; } } private void FindLocalPlayer() { try { if ((Object)(object)Player.Local != (Object)null) { _localPlayer = Player.Local; ModLogger.UIDebug("FuelUIManager: Found local player"); } else { ModLogger.UIDebug("FuelUIManager: Local player not found"); } } catch (Exception exception) { ModLogger.Error("Error finding local player", exception); } } private void CheckVehicleChange() { try { if ((Object)(object)_localPlayer == (Object)null) { return; } LandVehicle val = null; string text = string.Empty; if ((Object)(object)_localPlayer.CurrentVehicle != (Object)null) { NetworkObject currentVehicle = _localPlayer.CurrentVehicle; val = ((Component)currentVehicle).GetComponent(); if ((Object)(object)val != (Object)null) { text = val.GUID.ToString(); } } if (!(text != _currentVehicleGUID)) { return; } if (_currentGauge != null) { _currentGauge.Hide(); _currentGauge = null; } if (_currentNewGauge != null) { _currentNewGauge.Hide(); _currentNewGauge = null; } _currentVehicle = val; _currentVehicleGUID = text; if ((Object)(object)_currentVehicle != (Object)null) { Core instance = Core.Instance; if (instance != null && instance.UseNewGaugeUI) { ShowNewFuelGaugeForVehicle(_currentVehicle); } else { ShowFuelGaugeForVehicle(_currentVehicle); } } LandVehicle? currentVehicle2 = _currentVehicle; ModLogger.UIDebug("FuelUIManager: Vehicle changed to " + (((currentVehicle2 != null) ? currentVehicle2.VehicleName : null) ?? "none")); } catch (Exception exception) { ModLogger.Error("Error checking vehicle change", exception); } } public void ShowFuelGaugeForVehicle(LandVehicle vehicle) { try { if ((Object)(object)vehicle == (Object)null) { return; } string text = vehicle.GUID.ToString(); if (!_activeFuelGauges.ContainsKey(text)) { VehicleFuelSystem vehicleFuelSystem = Core.Instance?.GetFuelSystemManager()?.GetFuelSystem(vehicle.GUID.ToString()); if ((Object)(object)vehicleFuelSystem == (Object)null) { ModLogger.Warning("FuelUIManager: No fuel system found for vehicle " + text.Substring(0, 8) + "..."); return; } FuelGaugeUI value = new FuelGaugeUI(vehicleFuelSystem); _activeFuelGauges[text] = value; ModLogger.UIDebug("FuelUIManager: Created fuel gauge for vehicle " + text.Substring(0, 8) + "..."); } _currentGauge = _activeFuelGauges[text]; _currentGauge.Show(); ModLogger.UIDebug("FuelUIManager: Showing fuel gauge for " + vehicle.VehicleName); } catch (Exception exception) { ModLogger.Error("Error showing fuel gauge for vehicle", exception); } } public void HideFuelGaugeForVehicle(string vehicleGUID) { try { if (_activeFuelGauges.TryGetValue(vehicleGUID, out FuelGaugeUI value)) { value.Hide(); ModLogger.UIDebug("FuelUIManager: Hidden fuel gauge for vehicle " + vehicleGUID.Substring(0, 8) + "..."); } } catch (Exception exception) { ModLogger.Error("Error hiding fuel gauge for vehicle", exception); } } public void RemoveFuelGaugeForVehicle(string vehicleGUID) { try { if (_activeFuelGauges.TryGetValue(vehicleGUID, out FuelGaugeUI value)) { value.Dispose(); _activeFuelGauges.Remove(vehicleGUID); if (_currentGauge == value) { _currentGauge = null; } ModLogger.UIDebug("FuelUIManager: Removed fuel gauge for vehicle " + vehicleGUID.Substring(0, 8) + "..."); } } catch (Exception exception) { ModLogger.Error("Error removing fuel gauge for vehicle", exception); } } public void ShowNewFuelGaugeForVehicle(LandVehicle vehicle) { try { if ((Object)(object)vehicle == (Object)null) { return; } string text = vehicle.GUID.ToString(); if (!_activeNewGauges.ContainsKey(text)) { VehicleFuelSystem vehicleFuelSystem = Core.Instance?.GetFuelSystemManager()?.GetFuelSystem(vehicle.GUID.ToString()); if ((Object)(object)vehicleFuelSystem == (Object)null) { ModLogger.Warning("FuelUIManager: No fuel system found for vehicle " + text.Substring(0, 8) + "..."); return; } FuelGauge value = new FuelGauge(vehicleFuelSystem); _activeNewGauges[text] = value; ModLogger.UIDebug("FuelUIManager: Created new fuel gauge for vehicle " + text.Substring(0, 8) + "..."); } _currentNewGauge = _activeNewGauges[text]; _currentNewGauge.Show(); ModLogger.UIDebug("FuelUIManager: Showing new fuel gauge for " + vehicle.VehicleName); } catch (Exception exception) { ModLogger.Error("Error showing new fuel gauge for vehicle", exception); } } public void HideNewFuelGaugeForVehicle(string vehicleGUID) { try { if (_activeNewGauges.TryGetValue(vehicleGUID, out FuelGauge value)) { value.Hide(); ModLogger.UIDebug("FuelUIManager: Hidden new fuel gauge for vehicle " + vehicleGUID.Substring(0, 8) + "..."); } } catch (Exception exception) { ModLogger.Error("Error hiding new fuel gauge for vehicle", exception); } } public void RemoveNewFuelGaugeForVehicle(string vehicleGUID) { try { if (_activeNewGauges.TryGetValue(vehicleGUID, out FuelGauge value)) { value.Dispose(); _activeNewGauges.Remove(vehicleGUID); if (_currentNewGauge == value) { _currentNewGauge = null; } ModLogger.UIDebug("FuelUIManager: Removed new fuel gauge for vehicle " + vehicleGUID.Substring(0, 8) + "..."); } } catch (Exception exception) { ModLogger.Error("Error removing new fuel gauge for vehicle", exception); } } public void HideAllGauges() { try { foreach (FuelGaugeUI value in _activeFuelGauges.Values) { value?.Hide(); } foreach (FuelGauge value2 in _activeNewGauges.Values) { value2?.Hide(); } _currentGauge = null; _currentNewGauge = null; ModLogger.UIDebug("FuelUIManager: Hidden all fuel gauges"); } catch (Exception exception) { ModLogger.Error("Error hiding all fuel gauges", exception); } } public void ShowAllGauges() { try { foreach (FuelGaugeUI value in _activeFuelGauges.Values) { value?.Show(); } foreach (FuelGauge value2 in _activeNewGauges.Values) { value2?.Show(); } ModLogger.UIDebug("FuelUIManager: Shown all fuel gauges (debug mode)"); } catch (Exception exception) { ModLogger.Error("Error showing all fuel gauges", exception); } } public FuelUIStats GetStatistics() { FuelUIStats fuelUIStats = new FuelUIStats(); try { fuelUIStats.TotalGauges = _activeFuelGauges.Count + _activeNewGauges.Count; fuelUIStats.HasCurrentGauge = _currentGauge != null || _currentNewGauge != null; LandVehicle? currentVehicle = _currentVehicle; fuelUIStats.CurrentVehicleName = ((currentVehicle != null) ? currentVehicle.VehicleName : null) ?? "None"; Player? localPlayer = _localPlayer; fuelUIStats.LocalPlayerInVehicle = (Object)(object)((localPlayer != null) ? localPlayer.CurrentVehicle : null) != (Object)null; foreach (FuelGaugeUI value in _activeFuelGauges.Values) { if (value != null) { if (value.IsVisible) { fuelUIStats.VisibleGauges++; } else { fuelUIStats.HiddenGauges++; } } else { fuelUIStats.InvalidGauges++; } } foreach (FuelGauge value2 in _activeNewGauges.Values) { if (value2 != null) { if (value2.IsVisible) { fuelUIStats.VisibleGauges++; } else { fuelUIStats.HiddenGauges++; } } else { fuelUIStats.InvalidGauges++; } } } catch (Exception exception) { ModLogger.Error("Error calculating fuel UI statistics", exception); } return fuelUIStats; } public void RefreshAllGauges() { try { ModLogger.UIDebug("FuelUIManager: Refreshing all gauges..."); if (_currentGauge != null) { _currentGauge.Hide(); _currentGauge = null; } if (_currentNewGauge != null) { _currentNewGauge.Hide(); _currentNewGauge = null; } if ((Object)(object)_currentVehicle != (Object)null) { Core instance = Core.Instance; if (instance != null && instance.UseNewGaugeUI) { ShowNewFuelGaugeForVehicle(_currentVehicle); } else { ShowFuelGaugeForVehicle(_currentVehicle); } } ModLogger.UIDebug("FuelUIManager: All gauges refreshed"); } catch (Exception exception) { ModLogger.Error("Error refreshing fuel gauges", exception); } } public void Dispose() { try { ModLogger.UIDebug("FuelUIManager: Disposing..."); SceneManager.sceneLoaded -= OnSceneLoaded; SceneManager.sceneUnloaded -= OnSceneUnloaded; foreach (FuelGaugeUI value in _activeFuelGauges.Values) { value?.Dispose(); } foreach (FuelGauge value2 in _activeNewGauges.Values) { value2?.Dispose(); } _activeFuelGauges.Clear(); _activeNewGauges.Clear(); _currentGauge = null; _currentNewGauge = null; _currentVehicle = null; _localPlayer = null; ModLogger.Info("FuelUIManager: Disposed"); } catch (Exception exception) { ModLogger.Error("Error disposing FuelUIManager", exception); } } } public class FuelUIStats { public int TotalGauges { get; set; } public int VisibleGauges { get; set; } public int HiddenGauges { get; set; } public int InvalidGauges { get; set; } public bool HasCurrentGauge { get; set; } public string CurrentVehicleName { get; set; } = string.Empty; public bool LocalPlayerInVehicle { get; set; } } } namespace S1FuelMod.Systems { public class Equippable_GasolineCan : Equippable { private enum MessageType { Info, Warning, Error, Success } public float RefuelRate = 3f; public float LitersPerCan = 10f; public FuelTypeId FuelTypeForCan = FuelTypeId.Regular; private bool isRefueling; private LandVehicle targetVehicle; private VehicleFuelSystem targetFuelSystem; private float pendingCanConsumption; private float totalFuelAdded; private float refuelStartTime; public override void Equip(ItemInstance item) { //IL_000f: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_006b: Unknown result type (might be due to invalid IL or missing references) //IL_0071: Invalid comparison between Unknown and I4 ((Equippable)this).Equip(item); ((Component)this).transform.localPosition = Vector3.zero; ((Component)this).transform.localEulerAngles = Vector3.zero; ((Component)this).transform.localScale = Vector3.one; LayerUtility.SetLayerRecursively(((Component)this).gameObject, LayerMask.NameToLayer("Viewmodel")); MeshRenderer[] componentsInChildren = ((Component)this).gameObject.GetComponentsInChildren(); MeshRenderer[] array = componentsInChildren; foreach (MeshRenderer val in array) { if ((int)((Renderer)val).shadowCastingMode == 3) { ((Renderer)val).enabled = false; } else { ((Renderer)val).shadowCastingMode = (ShadowCastingMode)0; } } } public void Update() { if (isRefueling) { ContinueRefueling(); } } public void BeginRefuelInteraction(LandVehicle vehicle, VehicleFuelSystem fuelSystem) { if (!((Object)(object)vehicle == (Object)null) && !((Object)(object)fuelSystem == (Object)null) && (!isRefueling || !((Object)(object)targetVehicle == (Object)(object)vehicle) || !((Object)(object)targetFuelSystem == (Object)(object)fuelSystem))) { FuelTypeId currentFuelType = fuelSystem.CurrentFuelType; FuelTypeForCan = currentFuelType; float num = fuelSystem.MaxFuelCapacity - fuelSystem.CurrentFuelLevel; if (num <= 0.1f) { ShowMessage("Vehicle tank is already full!", MessageType.Warning); return; } isRefueling = true; pendingCanConsumption = 0f; totalFuelAdded = 0f; refuelStartTime = Time.time; targetVehicle = vehicle; targetFuelSystem = fuelSystem; ShowFuelGaugeForVehicle(vehicle); string fuelTypeDisplayName = GetFuelTypeDisplayName(FuelTypeForCan); string text = BuildFuelCompatibilityTag(fuelSystem, FuelTypeForCan); ModLogger.Debug("Started refueling " + vehicle.VehicleName + " with gasoline can"); } } public void EndRefuelInteraction() { StopRefuel(); } private void ContinueRefueling() { if ((Object)(object)targetFuelSystem == (Object)null || (Object)(object)targetVehicle == (Object)null) { StopRefuel(); return; } HotbarSlot equippedSlot = PlayerSingleton.Instance.equippedSlot; if (equippedSlot == null || ((ItemSlot)equippedSlot).ItemInstance == null) { StopRefuel(); return; } int quantity = ((ItemSlot)equippedSlot).Quantity; if (quantity <= 0) { StopRefuel(); ((ItemSlot)equippedSlot).ClearStoredInstance(false); return; } float deltaTime = Time.deltaTime; float num = Mathf.Max(0f, RefuelRate * deltaTime); float num2 = Mathf.Max(0f, targetFuelSystem.MaxFuelCapacity - targetFuelSystem.CurrentFuelLevel); if (num2 <= 0.001f) { StopRefuel(); return; } float num3 = Mathf.Min(num, num2); if (!targetFuelSystem.ChangeFuelType(FuelTypeForCan, num3)) { StopRefuel(); return; } float num4 = (float)quantity * LitersPerCan - pendingCanConsumption; if (num4 <= 0.001f) { ConsumeUnitsIfNeeded(equippedSlot); StopRefuel(); return; } float num5 = Core.Instance?.MaxFuelPerCanUse ?? 5f; float num6 = num5 - totalFuelAdded; if (num6 <= 0.001f) { StopRefuel(); return; } float amount = Mathf.Min(new float[3] { num3, num4, num6 }); float num7 = targetFuelSystem.AddFuel(amount); if (num7 > 0f) { pendingCanConsumption += num7; totalFuelAdded += num7; ConsumeUnitsIfNeeded(equippedSlot); if (totalFuelAdded >= num5 - 0.001f) { StopRefuel(); } } } private void ConsumeUnitsIfNeeded(HotbarSlot slot) { while (pendingCanConsumption >= LitersPerCan && slot != null && ((ItemSlot)slot).ItemInstance != null) { ((ItemSlot)slot).ChangeQuantity(-1, false); pendingCanConsumption -= LitersPerCan; if (((ItemSlot)slot).Quantity <= 0) { ((ItemSlot)slot).ClearStoredInstance(false); StopRefuel(); break; } } } private void ConsumeOnFinish(HotbarSlot slot) { while (pendingCanConsumption > 0f && slot != null && ((ItemSlot)slot).ItemInstance != null) { ((ItemSlot)slot).ChangeQuantity(-1, false); pendingCanConsumption -= LitersPerCan; if (((ItemSlot)slot).Quantity <= 0) { ((ItemSlot)slot).ClearStoredInstance(false); break; } } } private void StopRefuel() { if (!isRefueling) { return; } float num = totalFuelAdded; LandVehicle val = targetVehicle; FuelTypeId fuelTypeForCan = FuelTypeForCan; if ((Object)(object)targetVehicle != (Object)null) { HideFuelGaugeForVehicle(targetVehicle.GUID.ToString()); } HotbarSlot equippedSlot = PlayerSingleton.Instance.equippedSlot; if (num > 0.01f) { string fuelTypeDisplayName = GetFuelTypeDisplayName(fuelTypeForCan); float num2 = Core.Instance?.MaxFuelPerCanUse ?? 5f; if (num >= num2 - 0.001f) { ShowMessage($"Refueled {num:F1}L of {fuelTypeDisplayName} (limit reached)", MessageType.Success); ConsumeItemOnLimit(equippedSlot); } else { ShowMessage($"Refueled {num:F1}L of {fuelTypeDisplayName} with gasoline can", MessageType.Success); ConsumeOnFinish(equippedSlot); } ModLogger.Debug($"Completed gasoline can refueling: {num:F1}L of {fuelTypeDisplayName}"); } else if ((Object)(object)val != (Object)null) { ShowMessage("No fuel added", MessageType.Warning); } isRefueling = false; targetVehicle = null; targetFuelSystem = null; pendingCanConsumption = 0f; totalFuelAdded = 0f; } private void ConsumeItemOnLimit(HotbarSlot slot) { if (slot != null && ((ItemSlot)slot).ItemInstance != null) { ((ItemSlot)slot).ChangeQuantity(-1, false); if (((ItemSlot)slot).Quantity <= 0) { ((ItemSlot)slot).ClearStoredInstance(false); } } } private void ShowFuelGaugeForVehicle(LandVehicle vehicle) { try { FuelUIManager fuelUIManager = Core.Instance?.GetFuelUIManager(); if (fuelUIManager != null) { Core instance = Core.Instance; if (instance != null && instance.UseNewGaugeUI) { fuelUIManager.ShowNewFuelGaugeForVehicle(vehicle); } else { fuelUIManager.ShowFuelGaugeForVehicle(vehicle); } } } catch (Exception ex) { ModLogger.Debug("Error showing fuel gauge UI: " + ex.Message); } } private void HideFuelGaugeForVehicle(string vehicleGUID) { try { FuelUIManager fuelUIManager = Core.Instance?.GetFuelUIManager(); if (fuelUIManager != null) { Core instance = Core.Instance; if (instance != null && instance.UseNewGaugeUI) { fuelUIManager.HideNewFuelGaugeForVehicle(vehicleGUID); } else { fuelUIManager.HideFuelGaugeForVehicle(vehicleGUID); } } } catch (Exception ex) { ModLogger.Debug("Error hiding fuel gauge UI: " + ex.Message); } } private string GetFuelTypeDisplayName(FuelTypeId fuelTypeId) { if ((Object)(object)FuelTypeManager.Instance != (Object)null) { string fuelDisplayName = FuelTypeManager.Instance.GetFuelDisplayName(fuelTypeId); if (!string.IsNullOrEmpty(fuelDisplayName)) { return fuelDisplayName; } } return fuelTypeId.ToString(); } private string BuildFuelCompatibilityTag(VehicleFuelSystem fuelSystem, FuelTypeId fuelType) { if ((Object)(object)fuelSystem == (Object)null) { return string.Empty; } try { FuelTypeId recommendedFuelType = fuelSystem.GetRecommendedFuelType(); bool flag = fuelSystem.IsFuelCompatible(fuelType); if (fuelType == recommendedFuelType && flag) { return " (Recommended)"; } if (flag) { return " (Compatible)"; } return " (Penalty)"; } catch (Exception ex) { ModLogger.Debug("Error building fuel compatibility tag: " + ex.Message); return string.Empty; } } private void ShowMessage(string message, MessageType type) { try { switch (type) { case MessageType.Error: ModLogger.Warning("Gasoline Can Error: " + message); break; case MessageType.Warning: ModLogger.Warning("Gasoline Can Warning: " + message); break; case MessageType.Success: ModLogger.Info("Gasoline Can Success: " + message); break; default: ModLogger.Info("Gasoline Can: " + message); break; } } catch (Exception exception) { ModLogger.Error("Error showing gasoline can message", exception); } } } public class FuelSign : MonoBehaviour { private readonly Dictionary _signDisplays = new Dictionary(); private bool _hasInitialized = false; private void Start() { try { InitializeFuelSign(); ModLogger.Debug("FuelSign initialized for " + ((Object)((Component)this).gameObject).name); } catch (Exception exception) { ModLogger.Error("Error initializing FuelSign", exception); } } private void InitializeFuelSign() { try { _signDisplays.Clear(); ModLogger.Debug("FuelSign: Initializing sign '" + ((Object)((Component)this).gameObject).name + "', searching for child GameObjects..."); ModLogger.Debug($"FuelSign: Sign has {((Component)this).transform.childCount} children"); for (int i = 0; i < ((Component)this).transform.childCount; i++) { Transform child = ((Component)this).transform.GetChild(i); ModLogger.Debug($"FuelSign: Child {i}: '{((Object)child).name}'"); } Transform val = ((Component)this).transform.Find("GameObject"); if ((Object)(object)val != (Object)null) { FuelSignDisplay fuelSignDisplay = CreateFuelSignDisplay(val, 0); if (fuelSignDisplay != null) { _signDisplays[0] = fuelSignDisplay; ModLogger.Debug("FuelSign: Found fuel display for unnumbered GameObject (position 0)"); } } for (int j = 1; j < 8; j++) { Transform val2 = ((Component)this).transform.Find($"GameObject ({j})"); if ((Object)(object)val2 != (Object)null) { FuelSignDisplay fuelSignDisplay2 = CreateFuelSignDisplay(val2, j); if (fuelSignDisplay2 != null) { _signDisplays[j] = fuelSignDisplay2; ModLogger.Debug($"FuelSign: Found fuel display for position {j}"); } } } if (_signDisplays.Count > 0) { ModLogger.Debug($"FuelSign: Initialized with {_signDisplays.Count} fuel displays"); UpdateFuelPrices(); } else { ModLogger.Warning("FuelSign: No fuel displays found for " + ((Object)((Component)this).gameObject).name); ModLogger.Warning("FuelSign: Expected to find children named 'GameObject' with numbers (1) through (7)"); } _hasInitialized = true; } catch (Exception exception) { ModLogger.Error("Error initializing fuel sign displays", exception); } } private FuelSignDisplay? CreateFuelSignDisplay(Transform fuelTypeObject, int position) { try { Transform val = fuelTypeObject.Find("Name"); Transform val2 = fuelTypeObject.Find("Price"); if ((Object)(object)val == (Object)null || (Object)(object)val2 == (Object)null) { ModLogger.Debug($"FuelSign: Missing Name or Price child for position {position}"); return null; } TMP_Text component = ((Component)val).GetComponent(); TMP_Text component2 = ((Component)val2).GetComponent(); if ((Object)(object)component == (Object)null || (Object)(object)component2 == (Object)null) { ModLogger.Debug($"FuelSign: Missing TMP_Text components for position {position}"); return null; } return new FuelSignDisplay { Position = position, NameText = component, PriceText = component2, FuelTypeId = GetFuelTypeForPosition(position) }; } catch (Exception exception) { ModLogger.Error($"Error creating fuel sign display for position {position}", exception); return null; } } private FuelTypeId GetFuelTypeForPosition(int position) { switch (position) { case 0: case 4: return FuelTypeId.Regular; case 1: case 5: return FuelTypeId.MidGrade; case 2: case 6: return FuelTypeId.Premium; case 3: case 7: return FuelTypeId.Diesel; default: return FuelTypeId.Regular; } } public void UpdateFuelPrices() { if (!_hasInitialized) { ModLogger.Debug("FuelSign: Not initialized yet, skipping price update"); return; } try { foreach (KeyValuePair signDisplay in _signDisplays) { FuelSignDisplay value = signDisplay.Value; UpdateFuelDisplay(value); } ModLogger.Debug($"FuelSign: Updated {_signDisplays.Count} fuel displays for {((Object)((Component)this).gameObject).name}"); } catch (Exception exception) { ModLogger.Error("Error updating fuel prices", exception); } } private void UpdateFuelDisplay(FuelSignDisplay display) { try { if ((Object)(object)display.NameText == (Object)null || (Object)(object)display.PriceText == (Object)null) { ModLogger.Debug($"FuelSign: Display components are null for position {display.Position}"); return; } string fuelDisplayName = GetFuelDisplayName(display.FuelTypeId); float fuelPrice = GetFuelPrice(display.FuelTypeId); display.NameText.text = fuelDisplayName; display.PriceText.text = $"${fuelPrice:F2}"; ModLogger.Debug($"FuelSign: Updated position {display.Position} - {fuelDisplayName}: ${fuelPrice:F2}"); } catch (Exception exception) { ModLogger.Error($"Error updating fuel display for position {display.Position}", exception); } } private string GetFuelDisplayName(FuelTypeId fuelTypeId) { try { if ((Object)(object)FuelTypeManager.Instance != (Object)null) { string fuelDisplayName = FuelTypeManager.Instance.GetFuelDisplayName(fuelTypeId); if (!string.IsNullOrEmpty(fuelDisplayName)) { return fuelDisplayName; } } return fuelTypeId.ToString(); } catch (Exception exception) { ModLogger.Error($"Error getting fuel display name for {fuelTypeId}", exception); return fuelTypeId.ToString(); } } private float GetFuelPrice(FuelTypeId fuelTypeId) { try { float num = Core.Instance?.BaseFuelPricePerLiter ?? 4f; Core instance = Core.Instance; if (instance != null && instance.EnableDynamicPricing) { num = CalculateDynamicPrice(num); } if ((Object)(object)FuelTypeManager.Instance != (Object)null) { FuelType fuelType = FuelTypeManager.Instance.GetFuelType(fuelTypeId); if (fuelType != null) { num *= Mathf.Max(0.01f, fuelType.PriceMultiplier); } } return num; } catch (Exception exception) { ModLogger.Error($"Error getting fuel price for {fuelTypeId}", exception); return 4f; } } private float CalculateDynamicPrice(float basePrice) { try { float num = 0f; if (NetworkSingleton.InstanceExists) { int hashCode = ("Petrol" + NetworkSingleton.Instance.DayIndex).GetHashCode(); num = Mathf.Lerp(0f, 0.2f, Mathf.InverseLerp(-2.1474836E+09f, 2.1474836E+09f, (float)hashCode)); } float tierMultiplier = GetTierMultiplier(); float price = (Core.Instance.EnablePricingOnTier ? (basePrice + basePrice * num * tierMultiplier) : (basePrice + basePrice * num)); return ApplyCurfewTax(price); } catch (Exception exception) { ModLogger.Error("Error calculating dynamic price", exception); return basePrice; } } private float GetTierMultiplier() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected I4, but got Unknown try { if (!NetworkSingleton.InstanceExists) { return 1f; } ERank rank = NetworkSingleton.Instance.Rank; ERank val = rank; return (val - 1) switch { 0 => 1.05f, 1 => 1.1f, 2 => 1.15f, 3 => 1.2f, 4 => 1.25f, 5 => 1.3f, 6 => 1.4f, 7 => 1.5f, 8 => 1.6f, 9 => 1.8f, _ => 1f, }; } catch (Exception exception) { ModLogger.Error("Error retrieving tier multiplier", exception); return 1f; } } private float ApplyCurfewTax(float price) { Core instance = Core.Instance; if (instance == null || !instance.EnableCurfewFuelTax) { return price; } try { if (NetworkSingleton.Instance.IsCurrentTimeWithinRange(2100, 500)) { return price * 2f; } } catch (Exception exception) { ModLogger.Error("Error applying curfew tax", exception); } return price; } public FuelSignStats GetStatistics() { return new FuelSignStats { SignName = ((Object)((Component)this).gameObject).name, TotalDisplays = _signDisplays.Count, ActiveDisplays = _signDisplays.Count }; } private void OnDestroy() { try { _signDisplays.Clear(); ModLogger.Debug("FuelSign destroyed for " + ((Object)((Component)this).gameObject).name); } catch (Exception exception) { ModLogger.Error("Error destroying FuelSign", exception); } } } public class FuelSignDisplay { public int Position { get; set; } public TMP_Text? NameText { get; set; } public TMP_Text? PriceText { get; set; } public FuelTypeId FuelTypeId { get; set; } } public class FuelSignStats { public string SignName { get; set; } = string.Empty; public int TotalDisplays { get; set; } public int ActiveDisplays { get; set; } } public class FuelSignManager : MonoBehaviour { private static FuelSignManager? _instance; private readonly List _activeFuelSigns = new List(); private readonly string[] FUEL_SIGN_PATHS = new string[3] { "Map/Hyland Point/Region_Downtown/Gas Station/gas station/Sign", "Map/Hyland Point/Region_Downtown/Gas Station/gas station/Sign (1)", "Map/Hyland Point/Region_Westville/Slums Gas Station/gas station/Sign" }; private bool _hasInitialized = false; private float _lastSignCheckTime = 0f; private const float SIGN_CHECK_INTERVAL = 5f; private const int EXPECTED_FUEL_SIGNS = 3; private bool _shouldStopChecking = false; private int _initializationAttempts = 0; private const int MAX_INITIALIZATION_ATTEMPTS = 6; private const float INITIAL_DELAY = 3f; private float _startTime = 0f; private bool _delayedInitStarted = false; private readonly FuelTypeId[] FRONT_SIDE_FUEL_TYPES = new FuelTypeId[4] { FuelTypeId.Regular, FuelTypeId.MidGrade, FuelTypeId.Premium, FuelTypeId.Diesel }; private readonly FuelTypeId[] BACK_SIDE_FUEL_TYPES = new FuelTypeId[4] { FuelTypeId.Regular, FuelTypeId.MidGrade, FuelTypeId.Premium, FuelTypeId.Diesel }; public static FuelSignManager? Instance => _instance; private void Awake() { try { if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } _instance = this; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); ModLogger.Debug("FuelSignManager initialized successfully"); } catch (Exception exception) { ModLogger.Error("Error in FuelSignManager.Awake", exception); } } private void Start() { _startTime = Time.time; ModLogger.Debug($"FuelSignManager.Start() called, will attempt initialization after {3f}s delay"); } private void Update() { try { if (!_delayedInitStarted && Time.time - _startTime >= 3f) { _delayedInitStarted = true; _lastSignCheckTime = Time.time; ModLogger.Debug("Initial delay complete, starting fuel sign initialization"); InitializeFuelSigns(); } if (_delayedInitStarted) { CleanupDestroyedSigns(); if (!_shouldStopChecking && Time.time - _lastSignCheckTime > 5f) { CheckForNewFuelSigns(); _lastSignCheckTime = Time.time; } } } catch (Exception exception) { ModLogger.Error("Error in FuelSignManager.Update", exception); } } private void InitializeFuelSigns() { try { _initializationAttempts++; int num = 0; int num2 = 0; string[] fUEL_SIGN_PATHS = FUEL_SIGN_PATHS; foreach (string text in fUEL_SIGN_PATHS) { GameObject val = FindGameObjectByPath(text); if ((Object)(object)val != (Object)null) { num++; ModLogger.Debug("FuelSignManager: Found sign at path: " + text); if (SetupFuelSign(val)) { num2++; } } else { ModLogger.Debug("FuelSignManager: Could not find sign at path: " + text); } } if (num > 0) { ModLogger.Debug($"FuelSignManager: Attempt {_initializationAttempts} - Found {num} fuel sign objects, setup {num2} new fuel signs"); } else { ModLogger.Debug($"FuelSignManager: Attempt {_initializationAttempts} - No fuel sign objects found in scene"); } if (_activeFuelSigns.Count >= 3 && !_shouldStopChecking) { _shouldStopChecking = true; ModLogger.Debug($"FuelSignManager: Found expected number of fuel signs ({3}), stopping periodic checks for performance"); } else if (_initializationAttempts >= 6 && !_shouldStopChecking) { _shouldStopChecking = true; ModLogger.Debug($"FuelSignManager: Reached maximum initialization attempts ({6}), stopping periodic checks. Found {_activeFuelSigns.Count}/{3} signs."); } _hasInitialized = true; } catch (Exception exception) { ModLogger.Error("Error initializing fuel signs", exception); } } private void CheckForNewFuelSigns() { try { _initializationAttempts++; int num = 0; string[] fUEL_SIGN_PATHS = FUEL_SIGN_PATHS; foreach (string path in fUEL_SIGN_PATHS) { GameObject val = FindGameObjectByPath(path); if (!((Object)(object)val != (Object)null)) { continue; } FuelSign component = val.GetComponent(); if ((Object)(object)component == (Object)null) { if (SetupFuelSign(val)) { num++; } } else if (!_activeFuelSigns.Contains(component)) { _activeFuelSigns.Add(component); ModLogger.Debug("FuelSignManager: Added existing fuel sign to tracking: " + ((Object)val).name); } } if (num > 0) { ModLogger.Debug($"FuelSignManager: Attempt {_initializationAttempts} - Found and setup {num} new fuel signs"); } if (_activeFuelSigns.Count >= 3 && !_shouldStopChecking) { _shouldStopChecking = true; ModLogger.Debug($"FuelSignManager: Found expected number of fuel signs ({3}), stopping periodic checks for performance"); } else if (_initializationAttempts >= 6 && !_shouldStopChecking) { _shouldStopChecking = true; ModLogger.Debug($"FuelSignManager: Reached maximum initialization attempts ({6}), stopping periodic checks. Found {_activeFuelSigns.Count}/{3} signs."); } } catch (Exception exception) { ModLogger.Error("Error checking for new fuel signs", exception); } } private bool SetupFuelSign(GameObject signObject) { //IL_0088: Unknown result type (might be due to invalid IL or missing references) try { FuelSign component = signObject.GetComponent(); if ((Object)(object)component != (Object)null) { if (!_activeFuelSigns.Contains(component)) { _activeFuelSigns.Add(component); ModLogger.Debug("FuelSignManager: Added existing fuel sign to tracking: " + ((Object)signObject).name); } return true; } FuelSign fuelSign = signObject.AddComponent(); if ((Object)(object)fuelSign != (Object)null) { _activeFuelSigns.Add(fuelSign); ModLogger.Debug($"FuelSignManager: Setup fuel sign on {((Object)signObject).name} at position {signObject.transform.position}"); return true; } ModLogger.Warning("FuelSignManager: Failed to add FuelSign component to " + ((Object)signObject).name); return false; } catch (Exception exception) { ModLogger.Error("Error setting up fuel sign for " + ((Object)signObject).name, exception); return false; } } private GameObject? FindGameObjectByPath(string path) { //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) try { string[] array = path.Split('/'); if (array.Length == 0) { return null; } Transform val = null; string[] array2 = array; foreach (string text in array2) { if ((Object)(object)val == (Object)null) { Scene activeScene = SceneManager.GetActiveScene(); GameObject[] rootGameObjects = ((Scene)(ref activeScene)).GetRootGameObjects(); GameObject[] array3 = rootGameObjects; foreach (GameObject val2 in array3) { if (((Object)val2).name == text) { val = val2.transform; break; } } } else { Transform val3 = val.Find(text); if ((Object)(object)val3 != (Object)null) { val = val3; } else { for (int k = 0; k < val.childCount; k++) { Transform child = val.GetChild(k); if (((Object)child).name == text) { val = child; break; } } } } if ((Object)(object)val == (Object)null) { return null; } } return (val != null) ? ((Component)val).gameObject : null; } catch (Exception exception) { ModLogger.Error("Error finding GameObject by path '" + path + "'", exception); return null; } } private void CleanupDestroyedSigns() { try { for (int num = _activeFuelSigns.Count - 1; num >= 0; num--) { if ((Object)(object)_activeFuelSigns[num] == (Object)null || (Object)(object)((Component)_activeFuelSigns[num]).gameObject == (Object)null) { _activeFuelSigns.RemoveAt(num); } } } catch (Exception exception) { ModLogger.Error("Error cleaning up destroyed fuel signs", exception); } } public void UpdateAllFuelSigns() { try { int num = 0; foreach (FuelSign activeFuelSign in _activeFuelSigns) { if ((Object)(object)activeFuelSign != (Object)null && (Object)(object)((Component)activeFuelSign).gameObject != (Object)null) { activeFuelSign.UpdateFuelPrices(); num++; } } if (num > 0) { ModLogger.Debug($"FuelSignManager: Updated {num} fuel signs with current prices"); } } catch (Exception exception) { ModLogger.Error("Error updating fuel signs", exception); } } public void ForceUpdateAllSigns() { try { ModLogger.Debug("FuelSignManager: Forcing immediate update of all fuel signs"); UpdateAllFuelSigns(); } catch (Exception exception) { ModLogger.Error("Error forcing fuel sign updates", exception); } } public FuelTypeId GetFuelTypeForPosition(int position) { if (position >= 0 && position < 4) { return FRONT_SIDE_FUEL_TYPES[position]; } if (position >= 4 && position < 8) { return BACK_SIDE_FUEL_TYPES[position - 4]; } return FuelTypeId.Regular; } public IReadOnlyList GetActiveFuelSigns() { return _activeFuelSigns.AsReadOnly(); } public void ForceScan() { ModLogger.Debug("FuelSignManager: Forcing fuel sign scan..."); _initializationAttempts = 0; _shouldStopChecking = false; InitializeFuelSigns(); } private void OnDestroy() { if ((Object)(object)_instance == (Object)(object)this) { _instance = null; } } } public class FuelStation : InteractableObject { private enum MessageType { Info, Warning, Error, Success } private float refuelRate = 40f; private float pricePerLiter = 1f; private float maxInteractionDistance = 4f; private float vehicleDetectionRadius = 6f; private static readonly FuelTypeId[] EmptyFuelTypes = new FuelTypeId[0]; private readonly Dictionary _fuelTypePrices = new Dictionary(); private FuelTypeId _selectedFuelType = FuelTypeId.Regular; private FuelTypeId[] _compatibleFuelTypes = EmptyFuelTypes; private AudioSource refuelAudioSource; private AudioClip refuelStartSound; private AudioClip refuelLoopSound; private AudioClip refuelEndSound; private bool _isRefueling = false; private LandVehicle _targetVehicle; private VehicleFuelSystem _targetFuelSystem; private float _refuelStartTime; private float _totalFuelAdded; private float _totalCost; private LandVehicle _cachedVehicle; private FuelTypeId[] _cachedCompatibleFuelTypes = EmptyFuelTypes; private FuelTypeId _cachedSelectedFuelType = FuelTypeId.Regular; private float _cachedPricePerLiter = 1f; private float _lastPriceUpdateTime = 0f; private const float PRICE_CACHE_DURATION = 1f; private MoneyManager _moneyManager; private FuelSystemManager _fuelSystemManager; private void Start() { //IL_0014: Unknown result type (might be due to invalid IL or missing references) try { InitializeFuelStation(); ModLogger.Debug($"FuelStation initialized at {((Component)this).transform.position}"); } catch (Exception exception) { ModLogger.Error("Error initializing FuelStation", exception); } } private void InitializeFuelStation() { ((InteractableObject)this).SetMessage("Refuel Vehicle - Hold to refuel"); ((InteractableObject)this).SetInteractionType((EInteractionType)0); base.MaxInteractionRange = maxInteractionDistance; base.RequiresUniqueClick = false; if (NetworkSingleton.InstanceExists) { _moneyManager = NetworkSingleton.Instance; } if (Core.Instance?.GetFuelSystemManager() != null) { _fuelSystemManager = Core.Instance.GetFuelSystemManager(); } if ((Object)(object)refuelAudioSource == (Object)null) { refuelAudioSource = ((Component)this).GetComponent(); if ((Object)(object)refuelAudioSource == (Object)null) { refuelAudioSource = ((Component)this).gameObject.AddComponent(); } } if ((Object)(object)refuelAudioSource != (Object)null) { refuelAudioSource.playOnAwake = false; refuelAudioSource.loop = false; refuelAudioSource.volume = 0.7f; refuelAudioSource.spatialBlend = 1f; refuelAudioSource.maxDistance = 20f; } refuelRate = 4f; pricePerLiter = 4f; SetFuelPrice(); } public override void Hovered() { //IL_01ba: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Invalid comparison between Unknown and I4 try { LandVehicle nearestOwnedVehicle = GetNearestOwnedVehicle(); if ((Object)(object)nearestOwnedVehicle != (Object)null && (Object)(object)((Component)nearestOwnedVehicle).gameObject != (Object)null) { try { VehicleFuelSystem vehicleFuelSystem = _fuelSystemManager?.GetFuelSystem(nearestOwnedVehicle.GUID.ToString()); if ((Object)(object)vehicleFuelSystem != (Object)null) { if ((Object)(object)_cachedVehicle != (Object)(object)nearestOwnedVehicle) { PrepareFuelSelectionCached(vehicleFuelSystem, nearestOwnedVehicle); } float num = vehicleFuelSystem.MaxFuelCapacity - vehicleFuelSystem.CurrentFuelLevel; float cachedPricePerLiter = _cachedPricePerLiter; float num2 = num * cachedPricePerLiter; string fuelTypeDisplayName = GetFuelTypeDisplayName(_cachedSelectedFuelType); if (num > 0.1f) { string text = BuildFuelCompatibilityTag(vehicleFuelSystem, _cachedSelectedFuelType); ((InteractableObject)this).SetMessage($"Refuel {nearestOwnedVehicle.VehicleName} [{fuelTypeDisplayName}{text}] - {MoneyManager.FormatAmount(num2, false, false)} | ${cachedPricePerLiter:F2}/L"); ((InteractableObject)this).SetInteractableState((EInteractableState)0); } else { ((InteractableObject)this).SetMessage(nearestOwnedVehicle.VehicleName + " - Tank Full"); ((InteractableObject)this).SetInteractableState((EInteractableState)1); } } else { ((InteractableObject)this).SetMessage("Vehicle has no fuel system"); ((InteractableObject)this).SetInteractableState((EInteractableState)1); } } catch (Exception ex) { ModLogger.Debug("Error accessing vehicle properties in Hovered: " + ex.Message); ((InteractableObject)this).SetMessage("Vehicle not accessible"); ((InteractableObject)this).SetInteractableState((EInteractableState)1); } } else { ((InteractableObject)this).SetMessage("No owned vehicle nearby"); ((InteractableObject)this).SetInteractableState((EInteractableState)1); } if (base.onHovered != null) { base.onHovered.Invoke(); } if ((int)base.interactionState != 2) { ((InteractableObject)this).ShowMessage(); } } catch (Exception exception) { ModLogger.Error("Error in FuelStation.Hovered", exception); ((InteractableObject)this).SetMessage("Error"); ((InteractableObject)this).SetInteractableState((EInteractableState)1); } } public override void StartInteract() { //IL_0230: Unknown result type (might be due to invalid IL or missing references) //IL_0236: Invalid comparison between Unknown and I4 if (_isRefueling) { return; } try { _targetVehicle = GetNearestOwnedVehicle(); if ((Object)(object)_targetVehicle == (Object)null || (Object)(object)((Component)_targetVehicle).gameObject == (Object)null) { ShowMessage("No owned vehicle nearby!", MessageType.Error); return; } try { FuelUIManager fuelUIManager = Core.Instance?.GetFuelUIManager(); if (fuelUIManager != null) { Core instance = Core.Instance; if (instance != null && instance.UseNewGaugeUI) { fuelUIManager.ShowNewFuelGaugeForVehicle(_targetVehicle); } else { fuelUIManager.ShowFuelGaugeForVehicle(_targetVehicle); } } } catch (Exception ex) { ModLogger.Debug("Error showing fuel gauge UI: " + ex.Message); } try { _targetFuelSystem = _fuelSystemManager?.GetFuelSystem(_targetVehicle.GUID.ToString()); } catch (Exception ex2) { ModLogger.Debug("Error getting fuel system: " + ex2.Message); _targetFuelSystem = null; } if ((Object)(object)_targetFuelSystem == (Object)null) { ShowMessage("Vehicle has no fuel system!", MessageType.Error); return; } if ((Object)(object)_cachedVehicle != (Object)(object)_targetVehicle) { PrepareFuelSelectionCached(_targetFuelSystem, _targetVehicle); } _selectedFuelType = _cachedSelectedFuelType; pricePerLiter = _cachedPricePerLiter; _compatibleFuelTypes = _cachedCompatibleFuelTypes; float num = _targetFuelSystem.MaxFuelCapacity - _targetFuelSystem.CurrentFuelLevel; if (num <= 0.1f) { ShowMessage("Vehicle tank is already full!", MessageType.Warning); return; } if ((Object)(object)_moneyManager != (Object)null && _moneyManager.onlineBalance < pricePerLiter) { ShowMessage("Insufficient funds! Need " + MoneyManager.FormatAmount(pricePerLiter, false, false) + " minimum", MessageType.Error); return; } if ((int)base.interactionState != 1) { if (base.onInteractStart != null) { base.onInteractStart.Invoke(); } Singleton.Instance.LerpDisplayScale(0.9f); } StartRefueling(); } catch (Exception exception) { ModLogger.Error("Error starting fuel station interaction", exception); ShowMessage("Error starting refuel process", MessageType.Error); } } public override void EndInteract() { try { try { FuelUIManager fuelUIManager = Core.Instance?.GetFuelUIManager(); if (fuelUIManager != null && (Object)(object)_targetVehicle != (Object)null) { Core instance = Core.Instance; if (instance != null && instance.UseNewGaugeUI) { fuelUIManager.HideNewFuelGaugeForVehicle(_targetVehicle.GUID.ToString()); } else { fuelUIManager.HideFuelGaugeForVehicle(_targetVehicle.GUID.ToString()); } } } catch (Exception ex) { ModLogger.Debug("Error hiding fuel gauge UI: " + ex.Message); } if (_isRefueling) { StopRefueling(); } if (base.onInteractEnd != null) { base.onInteractEnd.Invoke(); } Singleton.Instance.LerpDisplayScale(1f); } catch (Exception exception) { ModLogger.Error("Error in FuelStation.EndInteract", exception); } } private void StartRefueling() { _isRefueling = true; _refuelStartTime = Time.time; _totalFuelAdded = 0f; _totalCost = 0f; PlayRefuelSound(refuelStartSound); string fuelTypeDisplayName = GetFuelTypeDisplayName(_selectedFuelType); string text = BuildFuelCompatibilityTag(_targetFuelSystem, _selectedFuelType); ModLogger.Debug("Started refueling " + _targetVehicle.VehicleName + " with " + fuelTypeDisplayName + " at fuel station"); ShowMessage("Refueling " + _targetVehicle.VehicleName + " with " + fuelTypeDisplayName + text + "...", MessageType.Info); } private void StopRefueling() { if (_isRefueling) { _isRefueling = false; PlayRefuelSound(refuelEndSound); if (_totalFuelAdded > 0.01f) { string fuelTypeDisplayName = GetFuelTypeDisplayName(_selectedFuelType); ProcessPayment(); ShowMessage($"Refueled {_totalFuelAdded:F1}L of {fuelTypeDisplayName} for {MoneyManager.FormatAmount(_totalCost, false, false)}", MessageType.Success); ModLogger.Debug($"Completed refueling: {_totalFuelAdded:F1}L of {fuelTypeDisplayName} for {MoneyManager.FormatAmount(_totalCost, false, false)}"); } else { ShowMessage("No fuel added", MessageType.Warning); } _targetVehicle = null; _targetFuelSystem = null; _totalFuelAdded = 0f; _totalCost = 0f; } } private void Update() { if (_isRefueling && (Object)(object)_targetFuelSystem != (Object)null) { UpdateRefueling(); } } private void UpdateRefueling() { //IL_01e5: Unknown result type (might be due to invalid IL or missing references) //IL_01f5: Unknown result type (might be due to invalid IL or missing references) float deltaTime = Time.deltaTime; float num = refuelRate * deltaTime; if ((Object)(object)_targetFuelSystem == (Object)null) { return; } float num2 = Mathf.Max(0f, _targetFuelSystem.MaxFuelCapacity - _targetFuelSystem.CurrentFuelLevel); float num3 = Mathf.Min(num, num2); float selectedFuelPrice = GetSelectedFuelPrice(); float num4 = num3 * selectedFuelPrice; if ((Object)(object)_targetVehicle == (Object)null || (Object)(object)_targetFuelSystem == (Object)null) { return; } if (num3 <= 0.0001f) { StopRefueling(); ShowMessage("Vehicle tank is now full!", MessageType.Success); return; } if ((Object)(object)_moneyManager != (Object)null && _moneyManager.onlineBalance < num4) { StopRefueling(); ShowMessage("Insufficient funds to continue refueling!", MessageType.Error); return; } if (!_targetFuelSystem.ChangeFuelType(_selectedFuelType, num3)) { StopRefueling(); ShowMessage("Selected fuel type is incompatible with this vehicle!", MessageType.Error); return; } float num5 = _targetFuelSystem.AddFuel(num3); if (num5 > 0f) { _totalFuelAdded += num5; _totalCost += num5 * selectedFuelPrice; if ((Object)(object)refuelLoopSound != (Object)null && !refuelAudioSource.isPlaying) { PlayRefuelSound(refuelLoopSound); } } else { StopRefueling(); ShowMessage("Vehicle tank is now full!", MessageType.Success); } if (!((Object)(object)_targetVehicle == (Object)null) && !((Object)(object)((Component)_targetVehicle).transform == (Object)null) && Vector3.Distance(((Component)this).transform.position, ((Component)_targetVehicle).transform.position) > vehicleDetectionRadius) { StopRefueling(); ShowMessage("Vehicle moved too far away!", MessageType.Warning); } } private void PrepareFuelSelection(VehicleFuelSystem fuelSystem) { if ((Object)(object)fuelSystem == (Object)null) { _selectedFuelType = FuelTypeId.Regular; pricePerLiter = 4f; return; } SetFuelPrice(); _compatibleFuelTypes = GetCompatibleFuelTypesForVehicle(fuelSystem); FuelTypeId fuelTypeId = fuelSystem.GetRecommendedFuelType(); if (!IsFuelTypeCompatible(fuelTypeId)) { fuelTypeId = GetFallbackFuelType(); } if (!IsFuelTypeCompatible(_selectedFuelType)) { _selectedFuelType = fuelTypeId; } if (!IsFuelTypeCompatible(_selectedFuelType)) { _selectedFuelType = FuelTypeId.Regular; } pricePerLiter = GetSelectedFuelPrice(); } private void PrepareFuelSelectionCached(VehicleFuelSystem fuelSystem, LandVehicle vehicle) { if ((Object)(object)fuelSystem == (Object)null) { _cachedSelectedFuelType = FuelTypeId.Regular; _cachedPricePerLiter = 4f; _cachedVehicle = vehicle; return; } _cachedVehicle = vehicle; _cachedCompatibleFuelTypes = GetCompatibleFuelTypesForVehicle(fuelSystem); FuelTypeId fuelTypeId = fuelSystem.GetRecommendedFuelType(); if (!IsFuelTypeCompatibleCached(fuelTypeId)) { fuelTypeId = GetFallbackFuelTypeCached(); } if (!IsFuelTypeCompatibleCached(_cachedSelectedFuelType)) { _cachedSelectedFuelType = fuelTypeId; } if (!IsFuelTypeCompatibleCached(_cachedSelectedFuelType)) { _cachedSelectedFuelType = FuelTypeId.Regular; } if (Time.time - _lastPriceUpdateTime > 1f) { UpdatePriceCache(); } _cachedPricePerLiter = GetSelectedFuelPriceCached(); } private FuelTypeId[] GetCompatibleFuelTypesForVehicle(VehicleFuelSystem fuelSystem) { if ((Object)(object)fuelSystem == (Object)null || (Object)(object)FuelTypeManager.Instance == (Object)null) { return EmptyFuelTypes; } List compatibleFuelTypes = FuelTypeManager.Instance.GetCompatibleFuelTypes(fuelSystem.VehicleType); if (compatibleFuelTypes == null || compatibleFuelTypes.Count == 0) { return EmptyFuelTypes; } return compatibleFuelTypes.ToArray(); } private bool IsFuelTypeCompatible(FuelTypeId fuelType) { if (_compatibleFuelTypes == null) { return false; } for (int i = 0; i < _compatibleFuelTypes.Length; i++) { if (_compatibleFuelTypes[i] == fuelType) { return true; } } return false; } private FuelTypeId GetFallbackFuelType() { if (_compatibleFuelTypes != null && _compatibleFuelTypes.Length != 0) { return _compatibleFuelTypes[0]; } return FuelTypeId.Regular; } private bool IsFuelTypeCompatibleCached(FuelTypeId fuelType) { if (_cachedCompatibleFuelTypes == null) { return false; } for (int i = 0; i < _cachedCompatibleFuelTypes.Length; i++) { if (_cachedCompatibleFuelTypes[i] == fuelType) { return true; } } return false; } private FuelTypeId GetFallbackFuelTypeCached() { if (_cachedCompatibleFuelTypes != null && _cachedCompatibleFuelTypes.Length != 0) { return _cachedCompatibleFuelTypes[0]; } return FuelTypeId.Regular; } private float GetSelectedFuelPriceCached() { return GetFuelPriceForType(_cachedSelectedFuelType); } private void UpdatePriceCache() { SetFuelPrice(); _lastPriceUpdateTime = Time.time; } private void ClearFuelSelectionCache() { _cachedVehicle = null; _cachedCompatibleFuelTypes = EmptyFuelTypes; _cachedSelectedFuelType = FuelTypeId.Regular; _cachedPricePerLiter = 4f; _lastPriceUpdateTime = 0f; } private float GetSelectedFuelPrice() { return GetFuelPriceForType(_selectedFuelType); } private float GetFuelPriceForType(FuelTypeId fuelTypeId) { if (_fuelTypePrices.TryGetValue(fuelTypeId, out var value)) { return value; } return Core.Instance?.BaseFuelPricePerLiter ?? 4f; } private string GetFuelTypeDisplayName(FuelTypeId fuelTypeId) { if ((Object)(object)FuelTypeManager.Instance != (Object)null) { string fuelDisplayName = FuelTypeManager.Instance.GetFuelDisplayName(fuelTypeId); if (!string.IsNullOrEmpty(fuelDisplayName)) { return fuelDisplayName; } } return fuelTypeId.ToString(); } private string BuildFuelCompatibilityTag(VehicleFuelSystem fuelSystem, FuelTypeId fuelType) { if ((Object)(object)fuelSystem == (Object)null) { return string.Empty; } try { FuelTypeId recommendedFuelType = fuelSystem.GetRecommendedFuelType(); bool flag = fuelSystem.IsFuelCompatible(fuelType); if (fuelType == recommendedFuelType && flag) { return " (Recommended)"; } if (flag) { return " (Compatible)"; } return " (Penalty)"; } catch (Exception ex) { ModLogger.Debug("Error building fuel compatibility tag: " + ex.Message); return string.Empty; } } private void ProcessPayment() { if ((Object)(object)_moneyManager != (Object)null && _totalCost > 0f) { string text = "Fuel Station"; string text2 = $"Refueled {_targetVehicle.VehicleName} with {_totalFuelAdded:F1}L"; _moneyManager.CreateOnlineTransaction(text, 0f - _totalCost, 1f, text2); ModLogger.Debug($"Processed fuel payment: {MoneyManager.FormatAmount(_totalCost, false, false)} for {_totalFuelAdded:F1}L"); } } private LandVehicle GetNearestOwnedVehicle() { //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_015d: Unknown result type (might be due to invalid IL or missing references) //IL_0169: Unknown result type (might be due to invalid IL or missing references) try { Collider[] array = null; try { array = Physics.OverlapSphere(((Component)this).transform.position, vehicleDetectionRadius); } catch (Exception exception) { ModLogger.Error("Error calling Physics.OverlapSphere", exception); return null; } if (array == null) { return null; } List list = new List(); Collider[] array2 = array; foreach (Collider val in array2) { if ((Object)(object)val == (Object)null || (Object)(object)((Component)val).gameObject == (Object)null) { continue; } LandVehicle val2 = null; try { val2 = ((Component)val).GetComponentInParent(); } catch (Exception ex) { ModLogger.Debug("Error getting LandVehicle component: " + ex.Message); continue; } if (!((Object)(object)val2 != (Object)null) || !((Object)(object)((Component)val2).gameObject != (Object)null)) { continue; } try { if (val2.IsPlayerOwned && !list.Contains(val2)) { list.Add(val2); } } catch (Exception ex2) { ModLogger.Debug("Skipping vehicle due to property access error: " + ex2.Message); } } if (list.Count > 0) { try { LandVehicle val3 = list.OrderBy((LandVehicle v) => Vector3.Distance(((Component)this).transform.position, ((Component)v).transform.position)).First(); if (Vector3.Distance(((Component)this).transform.position, ((Component)val3).transform.position) <= maxInteractionDistance) { return val3; } } catch (Exception exception2) { ModLogger.Error("Error processing vehicle list", exception2); } } return null; } catch (Exception exception3) { ModLogger.Error("Error finding nearest owned vehicle", exception3); return null; } } private void PlayRefuelSound(AudioClip clip) { if ((Object)(object)refuelAudioSource != (Object)null && (Object)(object)clip != (Object)null) { refuelAudioSource.clip = clip; refuelAudioSource.Play(); } } private void ShowMessage(string message, MessageType type) { try { ModLogger.Debug("FuelStation: " + message); switch (type) { case MessageType.Error: ModLogger.Warning("FuelStation Error: " + message); break; case MessageType.Warning: ModLogger.Warning("FuelStation Warning: " + message); break; case MessageType.Success: ModLogger.Info("FuelStation Success: " + message); break; default: ModLogger.Info("FuelStation: " + message); break; } } catch (Exception exception) { ModLogger.Error("Error showing fuel station message", exception); } } private void SetFuelPrice() { float num = Core.Instance?.BaseFuelPricePerLiter ?? 4f; Core instance = Core.Instance; if (instance != null && instance.EnableDynamicPricing) { float num2 = 0f; try { if (NetworkSingleton.InstanceExists) { int hashCode = ("Petrol" + NetworkSingleton.Instance.DayIndex).GetHashCode(); num2 = Mathf.Lerp(0f, 0.2f, Mathf.InverseLerp(-2.1474836E+09f, 2.1474836E+09f, (float)hashCode)); } } catch (Exception ex) { ModLogger.Debug("Error calculating dynamic price modifier: " + ex.Message); } float tierMultiplier = GetTierMultiplier(); float price = (Core.Instance.EnablePricingOnTier ? (num + num * num2 * tierMultiplier) : (num + num * num2)); num = ApplyCurfewTax(price); } UpdateFuelTypePriceCache(num); pricePerLiter = GetSelectedFuelPrice(); _lastPriceUpdateTime = 0f; } private float GetTierMultiplier() { //IL_001f: Unknown result type (might be due to invalid IL or missing references) //IL_0024: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0026: Unknown result type (might be due to invalid IL or missing references) //IL_0027: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0057: Expected I4, but got Unknown try { if (!NetworkSingleton.InstanceExists) { return 1f; } ERank rank = NetworkSingleton.Instance.Rank; ERank val = rank; return (val - 1) switch { 0 => 1.05f, 1 => 1.1f, 2 => 1.15f, 3 => 1.2f, 4 => 1.25f, 5 => 1.3f, 6 => 1.4f, 7 => 1.5f, 8 => 1.6f, 9 => 1.8f, _ => 1f, }; } catch (Exception ex) { ModLogger.Debug("Error retrieving tier multiplier: " + ex.Message); return 1f; } } private float ApplyCurfewTax(float price) { Core instance = Core.Instance; if (instance == null || !instance.EnableCurfewFuelTax) { return price; } try { if (NetworkSingleton.Instance.IsCurrentTimeWithinRange(2100, 500)) { return price * 2f; } } catch (Exception ex) { ModLogger.Debug("Error applying curfew tax: " + ex.Message); } return price; } private void UpdateFuelTypePriceCache(float basePrice) { _fuelTypePrices.Clear(); if ((Object)(object)FuelTypeManager.Instance == (Object)null) { _fuelTypePrices[FuelTypeId.Regular] = basePrice; _fuelTypePrices[FuelTypeId.Premium] = basePrice; _fuelTypePrices[FuelTypeId.Diesel] = basePrice; return; } Array values = Enum.GetValues(typeof(FuelTypeId)); for (int i = 0; i < values.Length; i++) { FuelTypeId fuelTypeId = (FuelTypeId)values.GetValue(i); float value = basePrice; FuelType fuelType = FuelTypeManager.Instance.GetFuelType(fuelTypeId); if (fuelType != null) { value = basePrice * Mathf.Max(0.01f, fuelType.PriceMultiplier); } _fuelTypePrices[fuelTypeId] = value; } UpdateFuelSigns(); } private void UpdateFuelSigns() { try { if ((Object)(object)FuelSignManager.Instance != (Object)null) { FuelSignManager.Instance.ForceUpdateAllSigns(); } } catch (Exception ex) { ModLogger.Debug("Error updating fuel signs: " + ex.Message); } } private void OnDrawGizmosSelected() { //IL_0001: Unknown result type (might be due to invalid IL or missing references) //IL_0012: 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_0034: 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_0072: Unknown result type (might be due to invalid IL or missing references) //IL_0082: Unknown result type (might be due to invalid IL or missing references) Gizmos.color = Color.green; Gizmos.DrawWireSphere(((Component)this).transform.position, vehicleDetectionRadius); Gizmos.color = Color.blue; Gizmos.DrawWireSphere(((Component)this).transform.position, maxInteractionDistance); if (_isRefueling && (Object)(object)_targetVehicle != (Object)null) { Gizmos.color = Color.yellow; Gizmos.DrawLine(((Component)this).transform.position, ((Component)_targetVehicle).transform.position); } } private void OnDestroy() { try { if (_isRefueling) { StopRefueling(); } ModLogger.Debug("FuelStation destroyed"); } catch (Exception exception) { ModLogger.Error("Error destroying FuelStation", exception); } } } public class FuelStationManager : IDisposable { private readonly List _activeFuelStations = new List(); private readonly string FUEL_STATION_OBJECT_NAME = "Bowser (EMC Merge)"; private readonly string FUEL_STATION_OBJECT_NAME_ALT = "Bowser (EMC Merge)"; private bool _hasInitialized = false; private float _lastStationCheckTime = 0f; private const float STATION_CHECK_INTERVAL = 15f; private const int EXPECTED_FUEL_STATIONS = 4; private bool _shouldStopChecking = false; public FuelStationManager() { ModLogger.Debug("FuelStationManager: Initializing..."); try { ScanForFuelStations(); _hasInitialized = true; ModLogger.Debug($"FuelStationManager: Initialized with {_activeFuelStations.Count} fuel stations"); } catch (Exception exception) { ModLogger.Error("Error initializing FuelStationManager", exception); } } public void Update() { try { CleanupDestroyedStations(); if (!_shouldStopChecking && Time.time - _lastStationCheckTime > 15f) { CheckForNewFuelStations(); _lastStationCheckTime = Time.time; } } catch (Exception exception) { ModLogger.Error("Error in FuelStationManager.Update", exception); } } private void ScanForFuelStations() { try { int num = 0; int num2 = 0; GameObject[] collection = FindGameObjectsWithName(FUEL_STATION_OBJECT_NAME); GameObject[] collection2 = FindGameObjectsWithName(FUEL_STATION_OBJECT_NAME_ALT); List list = new List(); list.AddRange(collection); list.AddRange(collection2); foreach (GameObject item in list) { if ((Object)(object)item != (Object)null) { num++; if (SetupFuelStation(item)) { num2++; } } } if (num > 0) { ModLogger.Debug($"FuelStationManager: Found {num} Bowser objects, setup {num2} new fuel stations"); } else { ModLogger.Debug("FuelStationManager: No Bowser objects found in scene"); } if (_activeFuelStations.Count >= 4 && !_shouldStopChecking) { _shouldStopChecking = true; ModLogger.Debug($"FuelStationManager: Found expected number of fuel stations ({4}), stopping periodic checks for performance"); } } catch (Exception exception) { ModLogger.Error("Error scanning for fuel stations", exception); } } private void CheckForNewFuelStations() { try { int num = 0; GameObject[] collection = FindGameObjectsWithName(FUEL_STATION_OBJECT_NAME); GameObject[] collection2 = FindGameObjectsWithName(FUEL_STATION_OBJECT_NAME_ALT); List list = new List(); list.AddRange(collection); list.AddRange(collection2); foreach (GameObject item in list) { if (!((Object)(object)item != (Object)null)) { continue; } Transform parent = item.transform.parent; GameObject val = ((parent != null) ? ((Component)parent).gameObject : null); if (!((Object)(object)val != (Object)null)) { continue; } FuelStation component = val.GetComponent(); if ((Object)(object)component == (Object)null) { if (SetupFuelStation(item)) { num++; } } else if (!_activeFuelStations.Contains(component)) { _activeFuelStations.Add(component); ModLogger.Debug("FuelStationManager: Added existing fuel station to tracking: " + ((Object)val).name); } } if (num > 0) { ModLogger.Debug($"FuelStationManager: Found and setup {num} new fuel stations"); } if (_activeFuelStations.Count >= 4 && !_shouldStopChecking) { _shouldStopChecking = true; ModLogger.Debug($"FuelStationManager: Found expected number of fuel stations ({4}), stopping periodic checks for performance"); } } catch (Exception exception) { ModLogger.Error("Error checking for new fuel stations", exception); } } private bool SetupFuelStation(GameObject bowserObject) { //IL_00fe: Unknown result type (might be due to invalid IL or missing references) try { Transform parent = bowserObject.transform.parent; GameObject val = ((parent != null) ? ((Component)parent).gameObject : null); if ((Object)(object)val == (Object)null) { ModLogger.Warning("FuelStationManager: Bowser object " + ((Object)bowserObject).name + " has no parent - skipping"); return false; } FuelStation component = val.GetComponent(); if ((Object)(object)component != (Object)null) { if (!_activeFuelStations.Contains(component)) { _activeFuelStations.Add(component); ModLogger.Debug("FuelStationManager: Added existing fuel station to tracking: " + ((Object)val).name + " (parent of " + ((Object)bowserObject).name + ")"); } return true; } FuelStation fuelStation = val.AddComponent(); if ((Object)(object)fuelStation != (Object)null) { _activeFuelStations.Add(fuelStation); ModLogger.Debug($"FuelStationManager: Setup fuel station on {((Object)val).name} (parent of {((Object)bowserObject).name}) at position {val.transform.position}"); ConfigureFuelStation(fuelStation, val, bowserObject); return true; } ModLogger.Warning("FuelStationManager: Failed to add FuelStation component to " + ((Object)val).name); return false; } catch (Exception exception) { ModLogger.Error("Error setting up fuel station for bowser " + ((Object)bowserObject).name, exception); return false; } } private void ConfigureFuelStation(FuelStation fuelStation, GameObject parentObject, GameObject bowserObject) { try { AudioSource componentInChildren = parentObject.GetComponentInChildren(); if ((Object)(object)componentInChildren != (Object)null) { ModLogger.Debug("FuelStationManager: Found existing audio source on fuel station " + ((Object)parentObject).name); } ModLogger.Debug("FuelStationManager: Configured fuel station " + ((Object)parentObject).name + " with bowser child " + ((Object)bowserObject).name); } catch (Exception exception) { ModLogger.Error("Error configuring fuel station for " + ((Object)parentObject).name, exception); } } private GameObject[] FindGameObjectsWithName(string name) { List list = new List(); GameObject[] array = Object.FindObjectsOfType(); GameObject[] array2 = array; foreach (GameObject val in array2) { if (((Object)val).name == name) { list.Add(val); } } return list.ToArray(); } private void CleanupDestroyedStations() { try { for (int num = _activeFuelStations.Count - 1; num >= 0; num--) { if ((Object)(object)_activeFuelStations[num] == (Object)null || (Object)(object)((Component)_activeFuelStations[num]).gameObject == (Object)null) { _activeFuelStations.RemoveAt(num); } } } catch (Exception exception) { ModLogger.Error("Error cleaning up destroyed fuel stations", exception); } } public IReadOnlyList GetActiveFuelStations() { return _activeFuelStations.AsReadOnly(); } public FuelStationStats GetStatistics() { FuelStationStats fuelStationStats = new FuelStationStats { TotalStations = _activeFuelStations.Count, ActiveStations = 0, InactiveStations = 0 }; try { foreach (FuelStation activeFuelStation in _activeFuelStations) { if ((Object)(object)activeFuelStation != (Object)null && (Object)(object)((Component)activeFuelStation).gameObject != (Object)null && ((Behaviour)activeFuelStation).enabled) { fuelStationStats.ActiveStations++; } else { fuelStationStats.InactiveStations++; } } } catch (Exception exception) { ModLogger.Error("Error calculating fuel station statistics", exception); } return fuelStationStats; } public void ForceScan() { ModLogger.Debug("FuelStationManager: Forcing fuel station scan..."); _shouldStopChecking = false; ScanForFuelStations(); } public void Dispose() { try { ModLogger.Debug("FuelStationManager: Disposing..."); _activeFuelStations.Clear(); ModLogger.Debug("FuelStationManager: Disposed"); } catch (Exception exception) { ModLogger.Error("Error disposing FuelStationManager", exception); } } } public class FuelStationStats { public int TotalStations { get; set; } public int ActiveStations { get; set; } public int InactiveStations { get; set; } } public class FuelSystemManager : IDisposable { private readonly Dictionary _vehicleFuelSystems = new Dictionary(); private bool _debugInfoEnabled = false; private float _lastDebugLogTime = 0f; private const float DEBUG_LOG_INTERVAL = 5f; private readonly FuelNetworkManager _network = new FuelNetworkManager(); public FuelSystemManager() { ModLogger.Debug("FuelSystemManager: Initializing..."); InitializeExistingVehicles(); ModLogger.Debug($"FuelSystemManager: Initialized with {_vehicleFuelSystems.Count} vehicles"); _network.Initialize(); foreach (VehicleFuelSystem value in _vehicleFuelSystems.Values) { _network.RegisterFuelSystem(value); } ModLogger.Debug($"FuelSystemManager: Registered {_vehicleFuelSystems.Count} fuel systems with network manager"); } public void Update() { try { if (_debugInfoEnabled && Time.time - _lastDebugLogTime > 5f) { LogDebugInfo(); _lastDebugLogTime = Time.time; } CheckForNewVehicles(); _network.Update(); } catch (Exception exception) { ModLogger.Error("Error in FuelSystemManager.Update", exception); } } private void InitializeExistingVehicles() { try { LandVehicle[] array = Object.FindObjectsOfType(); LandVehicle[] array2 = array; foreach (LandVehicle val in array2) { if ((Object)(object)val != (Object)null) { AddFuelSystemToVehicle(val); } } ModLogger.Debug($"FuelSystemManager: Found and initialized {array.Length} existing vehicles"); } catch (Exception exception) { ModLogger.Error("Error initializing existing vehicles", exception); } } private void CheckForNewVehicles() { try { if (!NetworkSingleton.InstanceExists) { return; } VehicleManager instance = NetworkSingleton.Instance; foreach (LandVehicle allVehicle in instance.AllVehicles) { if ((Object)(object)allVehicle != (Object)null && !_vehicleFuelSystems.ContainsKey(allVehicle.GUID.ToString())) { VehicleFuelSystem vehicleFuelSystem = AddFuelSystemToVehicle(allVehicle); if ((Object)(object)vehicleFuelSystem != (Object)null) { _network.RegisterFuelSystem(vehicleFuelSystem); } } } } catch (Exception exception) { ModLogger.Error("Error checking for new vehicles", exception); } } private GameObject? FindFuelDoorChild(LandVehicle vehicle) { try { if ((Object)(object)vehicle == (Object)null || (Object)(object)((Component)vehicle).transform == (Object)null) { return null; } Transform val = ((Component)vehicle).transform.Find("FuelDoor"); if ((Object)(object)val != (Object)null) { return ((Component)val).gameObject; } return null; } catch (Exception ex) { ModLogger.Debug("Error finding FuelDoor child: " + ex.Message); return null; } } public VehicleFuelSystem? AddFuelSystemToVehicle(LandVehicle vehicle) { try { if ((Object)(object)vehicle == (Object)null) { ModLogger.Warning("FuelSystemManager: Attempted to add fuel system to null vehicle"); return null; } string text = vehicle.GUID.ToString(); if (_vehicleFuelSystems.ContainsKey(text)) { ModLogger.Debug("FuelSystemManager: Vehicle " + text.Substring(0, 8) + "... already has fuel system"); return _vehicleFuelSystems[text]; } VehicleFuelSystem component = ((Component)vehicle).GetComponent(); if ((Object)(object)component != (Object)null) { _vehicleFuelSystems[text] = component; ModLogger.Debug("FuelSystemManager: Found existing fuel system for vehicle " + text.Substring(0, 8) + "..."); return component; } VehicleFuelSystem vehicleFuelSystem = ((Component)vehicle).gameObject.AddComponent(); _vehicleFuelSystems[text] = vehicleFuelSystem; _network.RegisterFuelSystem(vehicleFuelSystem); GameObject val = FindFuelDoorChild(vehicle); if ((Object)(object)val != (Object)null) { Collider component2 = val.GetComponent(); BoxCollider val2 = (BoxCollider)(object)((component2 is BoxCollider) ? component2 : null); BoxCollider displayLocationCollider = ((val2 != null) ? val2 : ((!((Object)(object)component2 == (Object)null)) ? val.AddComponent() : val.AddComponent())); VehicleRefuelInteractable vehicleRefuelInteractable = val.AddComponent(); vehicleRefuelInteractable.SetDisplayLocationCollider((Collider)(object)displayLocationCollider); ModLogger.Debug("FuelSystemManager: Added fuel system to " + vehicle.VehicleName + " (" + text.Substring(0, 8) + "...) with FuelDoor interactable"); } else { ((Component)vehicle).gameObject.AddComponent(); ModLogger.Debug("FuelSystemManager: Added fuel system to " + vehicle.VehicleName + " (" + text.Substring(0, 8) + "...)"); } return vehicleFuelSystem; } catch (Exception exception) { ModLogger.Error("Error adding fuel system to vehicle", exception); return null; } } public void RemoveFuelSystem(string vehicleGUID) { try { if (_vehicleFuelSystems.ContainsKey(vehicleGUID)) { VehicleFuelSystem vehicleFuelSystem = _vehicleFuelSystems[vehicleGUID]; if ((Object)(object)vehicleFuelSystem != (Object)null) { _network.UnregisterFuelSystem(vehicleFuelSystem); } _vehicleFuelSystems.Remove(vehicleGUID); ModLogger.Debug("FuelSystemManager: Removed fuel system for vehicle " + vehicleGUID.Substring(0, 8) + "..."); } } catch (Exception exception) { ModLogger.Error("Error removing fuel system", exception); } } public VehicleFuelSystem? GetFuelSystem(string vehicleGUID) { VehicleFuelSystem value; return _vehicleFuelSystems.TryGetValue(vehicleGUID, out value) ? value : null; } public VehicleFuelSystem? GetFuelSystem(LandVehicle vehicle) { if ((Object)(object)vehicle == (Object)null) { return null; } return GetFuelSystem(vehicle.GUID.ToString()); } public IReadOnlyCollection GetAllFuelSystems() { return _vehicleFuelSystems.Values; } public void RefillAllVehicles() { try { int num = 0; foreach (VehicleFuelSystem value in _vehicleFuelSystems.Values) { if ((Object)(object)value != (Object)null && (Object)(object)((Component)value).gameObject != (Object)null) { value.SetFuelLevel(value.MaxFuelCapacity); num++; } } ModLogger.Debug($"FuelSystemManager: Refilled {num} vehicles"); } catch (Exception exception) { ModLogger.Error("Error refilling all vehicles", exception); } } public void DrainAllVehicles(float amount) { try { int num = 0; foreach (VehicleFuelSystem value in _vehicleFuelSystems.Values) { if ((Object)(object)value != (Object)null && (Object)(object)((Component)value).gameObject != (Object)null) { value.ConsumeFuel(amount); num++; } } ModLogger.Debug($"FuelSystemManager: Drained {amount}L from {num} vehicles"); } catch (Exception exception) { ModLogger.Error("Error draining all vehicles", exception); } } public void ToggleDebugInfo() { _debugInfoEnabled = !_debugInfoEnabled; ModLogger.Info("FuelSystemManager: Debug info " + (_debugInfoEnabled ? "enabled" : "disabled")); if (_debugInfoEnabled) { LogDebugInfo(); } } private void LogDebugInfo() { try { ModLogger.Info($"=== Fuel System Debug Info ({_vehicleFuelSystems.Count} vehicles) ==="); foreach (KeyValuePair vehicleFuelSystem in _vehicleFuelSystems) { VehicleFuelSystem value = vehicleFuelSystem.Value; if ((Object)(object)value != (Object)null && (Object)(object)((Component)value).gameObject != (Object)null) { LandVehicle component = ((Component)value).GetComponent(); string text = ((component != null) ? component.VehicleName : null) ?? "Unknown"; string text2 = ((component != null) ? component.VehicleCode : null) ?? "unknown"; ModLogger.Info($" {text} ({text2}): {value.CurrentFuelLevel:F1}L / {value.MaxFuelCapacity:F1}L " + $"({value.FuelPercentage:F1}%) - Engine: {value.IsEngineRunning} - " + "Warnings: " + (value.IsLowFuel ? "LOW" : "") + " " + (value.IsCriticalFuel ? "CRITICAL" : "") + " " + (value.IsOutOfFuel ? "EMPTY" : "")); } else { ModLogger.Warning(" Invalid fuel system found for vehicle " + vehicleFuelSystem.Key); } } ModLogger.Info("=== End Fuel System Debug Info ==="); } catch (Exception exception) { ModLogger.Error("Error logging debug info", exception); } } public FuelSystemStats GetStatistics() { FuelSystemStats fuelSystemStats = new FuelSystemStats(); try { fuelSystemStats.TotalVehicles = _vehicleFuelSystems.Count; foreach (VehicleFuelSystem value in _vehicleFuelSystems.Values) { if ((Object)(object)value != (Object)null && (Object)(object)((Component)value).gameObject != (Object)null) { fuelSystemStats.ActiveVehicles++; if (value.IsEngineRunning) { fuelSystemStats.RunningVehicles++; } if (value.IsOutOfFuel) { fuelSystemStats.EmptyVehicles++; } else if (value.IsCriticalFuel) { fuelSystemStats.CriticalFuelVehicles++; } else if (value.IsLowFuel) { fuelSystemStats.LowFuelVehicles++; } fuelSystemStats.TotalFuelCapacity += value.MaxFuelCapacity; fuelSystemStats.TotalCurrentFuel += value.CurrentFuelLevel; } else { fuelSystemStats.InactiveVehicles++; } } fuelSystemStats.AverageFuelLevel = ((fuelSystemStats.ActiveVehicles > 0) ? (fuelSystemStats.TotalCurrentFuel / (float)fuelSystemStats.ActiveVehicles) : 0f); fuelSystemStats.OverallFuelPercentage = ((fuelSystemStats.TotalFuelCapacity > 0f) ? (fuelSystemStats.TotalCurrentFuel / fuelSystemStats.TotalFuelCapacity * 100f) : 0f); } catch (Exception exception) { ModLogger.Error("Error calculating fuel system statistics", exception); } return fuelSystemStats; } public void Dispose() { try { ModLogger.Debug("FuelSystemManager: Disposing..."); _vehicleFuelSystems.Clear(); _network.Dispose(); ModLogger.Debug("FuelSystemManager: Disposed"); } catch (Exception exception) { ModLogger.Error("Error disposing FuelSystemManager", exception); } } } public class FuelSystemStats { public int TotalVehicles { get; set; } public int ActiveVehicles { get; set; } public int InactiveVehicles { get; set; } public int RunningVehicles { get; set; } public int EmptyVehicles { get; set; } public int CriticalFuelVehicles { get; set; } public int LowFuelVehicles { get; set; } public float TotalFuelCapacity { get; set; } public float TotalCurrentFuel { get; set; } public float AverageFuelLevel { get; set; } public float OverallFuelPercentage { get; set; } } [Serializable] public class FuelVehicleData : VehicleData { public float CurrentFuelLevel; public float MaxFuelCapacity; public float FuelConsumptionRate; public int FuelDataVersion = 1; public FuelVehicleData(Guid guid, string code, Vector3 pos, Quaternion rot, EVehicleColor col, ItemSet vehicleContents, List spraySurfaces, FuelData fuelData) : base(guid, code, pos, rot, col, vehicleContents, spraySurfaces) { //IL_000a: Unknown result type (might be due to invalid IL or missing references) //IL_000b: Unknown result type (might be due to invalid IL or missing references) //IL_000d: Unknown result type (might be due to invalid IL or missing references) if (fuelData != null) { CurrentFuelLevel = fuelData.CurrentFuelLevel; MaxFuelCapacity = fuelData.MaxFuelCapacity; FuelConsumptionRate = fuelData.FuelConsumptionRate; } else { CurrentFuelLevel = 50f; MaxFuelCapacity = 50f; FuelConsumptionRate = 6f; } } public FuelData GetFuelData() { return new FuelData { CurrentFuelLevel = CurrentFuelLevel, MaxFuelCapacity = MaxFuelCapacity, FuelConsumptionRate = FuelConsumptionRate }; } public void GetFuelDataValues(out float currentLevel, out float maxCapacity, out float consumptionRate) { currentLevel = CurrentFuelLevel; maxCapacity = MaxFuelCapacity; consumptionRate = FuelConsumptionRate; } public static bool HasFuelData(VehicleData vehicleData) { return vehicleData is FuelVehicleData; } public static FuelData? TryGetFuelData(VehicleData vehicleData) { if (vehicleData is FuelVehicleData fuelVehicleData) { return fuelVehicleData.GetFuelData(); } return null; } public static bool TryGetFuelDataValues(VehicleData vehicleData, out float currentLevel, out float maxCapacity, out float consumptionRate) { if (vehicleData is FuelVehicleData fuelVehicleData) { fuelVehicleData.GetFuelDataValues(out currentLevel, out maxCapacity, out consumptionRate); return true; } currentLevel = 0f; maxCapacity = 0f; consumptionRate = 0f; return false; } public static FuelVehicleData FromVehicleData(VehicleData vehicleData, FuelData fuelData) { //IL_0023: Unknown result type (might be due to invalid IL or missing references) //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) List spraySurfaces = vehicleData.SpraySurfaces ?? new List(); return new FuelVehicleData(new Guid(vehicleData.GUID), vehicleData.VehicleCode, vehicleData.Position, vehicleData.Rotation, Enum.Parse(vehicleData.Color), vehicleData.VehicleContents, spraySurfaces, fuelData); } } public class VehicleFuelSystem : MonoBehaviour { private float currentFuelLevel = 50f; private float maxFuelCapacity = 50f; private float globalMaxFuelCapacity = 50f; private float baseFuelConsumptionRate = 6f; private float idleConsumptionRate = 0.5f; private float lowFuelThreshold = 30f; private float criticalFuelThreshold = 5f; private LandVehicle? _landVehicle; private string _vehicleGUID = string.Empty; private bool _isEngineRunning = false; private bool _isInVehicle = false; private bool _lowFuelWarningShown = false; private bool _criticalFuelWarningShown = false; private float _lastConsumptionTime = 0f; private VehicleType _vehicleType = VehicleType.Other; private FuelTypeId _currentFuelType = FuelTypeId.Regular; private float _fuelQuality = 1f; public UnityEvent OnFuelLevelChanged = new UnityEvent(); public UnityEvent OnFuelPercentageChanged = new UnityEvent(); public UnityEvent OnLowFuelWarning = new UnityEvent(); public UnityEvent OnCriticalFuelWarning = new UnityEvent(); public UnityEvent OnFuelEmpty = new UnityEvent(); public float CurrentFuelLevel => currentFuelLevel; public float MaxFuelCapacity => maxFuelCapacity; public float GlobalMaxFuelCapacity => globalMaxFuelCapacity; public float FuelPercentage => (maxFuelCapacity > 0f) ? (currentFuelLevel / maxFuelCapacity * 100f) : 0f; public bool IsOutOfFuel => currentFuelLevel <= 0f; public bool IsLowFuel => FuelPercentage <= lowFuelThreshold; public bool IsCriticalFuel => FuelPercentage <= criticalFuelThreshold; public bool IsEngineRunning => _isEngineRunning; public bool IsInVehicle => _isInVehicle; public string VehicleGUID => _vehicleGUID; public string NetworkID { get { LandVehicle? landVehicle = _landVehicle; object obj; if (landVehicle == null) { obj = null; } else { NetworkObject networkObject = ((NetworkBehaviour)landVehicle).NetworkObject; obj = ((networkObject != null) ? networkObject.ObjectId.ToString() : null); } if (obj == null) { obj = _vehicleGUID; } return (string)obj; } } public FuelTypeId CurrentFuelType => _currentFuelType; public float FuelQuality => _fuelQuality; public VehicleType VehicleType => _vehicleType; public FuelTypeId GetRecommendedFuelType() { return FuelTypeManager.Instance?.GetRecommendedFuelType(_vehicleType) ?? FuelTypeId.Regular; } public bool IsFuelCompatible(FuelTypeId fuelTypeId) { return FuelTypeManager.Instance?.IsFuelCompatible(fuelTypeId, _vehicleType) ?? true; } public string GetCurrentFuelDisplayName() { return FuelTypeManager.Instance?.GetFuelDisplayName(_currentFuelType) ?? "Regular"; } private void Awake() { try { _landVehicle = ((Component)this).GetComponent(); if ((Object)(object)_landVehicle == (Object)null) { ModLogger.Error("VehicleFuelSystem: LandVehicle component not found on " + ((Object)((Component)this).gameObject).name); ((Behaviour)this).enabled = false; return; } _vehicleGUID = _landVehicle.GUID.ToString(); if (Core.Instance != null) { SetVehicleType(); InitializeFuelTypeState(); baseFuelConsumptionRate = SetBaseFuelConsumption(); globalMaxFuelCapacity = Core.Instance.DefaultFuelCapacity; maxFuelCapacity = SetMaxFuelCapacity(); currentFuelLevel = maxFuelCapacity; idleConsumptionRate = 30f * Core.Instance.FuelConsumptionMultiplier; } ModLogger.FuelDebug("VehicleFuelSystem initialized for " + _landVehicle.VehicleName + " (" + _vehicleGUID.Substring(0, 8) + "...)"); ModLogger.LogVehicleFuel(_landVehicle.VehicleCode, _vehicleGUID, currentFuelLevel, maxFuelCapacity); } catch (Exception exception) { ModLogger.Error("Error in VehicleFuelSystem.Awake", exception); } } private void Start() { try { if ((Object)(object)_landVehicle != (Object)null) { if (_landVehicle.onVehicleStart != null) { LandVehicle? landVehicle = _landVehicle; landVehicle.onVehicleStart = (Action)Delegate.Combine(landVehicle.onVehicleStart, new Action(OnVehicleStarted)); } if (_landVehicle.onVehicleStop != null) { LandVehicle? landVehicle2 = _landVehicle; landVehicle2.onVehicleStop = (Action)Delegate.Combine(landVehicle2.onVehicleStop, new Action(OnVehicleStopped)); } } _lastConsumptionTime = Time.time; TriggerFuelLevelChanged(); } catch (Exception exception) { ModLogger.Error("Error in VehicleFuelSystem.Start", exception); } } private void Update() { try { if (!Object.op_Implicit((Object)(object)_landVehicle) || _landVehicle.LocalPlayerIsDriver) { UpdateEngineState(); if (_isEngineRunning) { UpdateFuelConsumption(); } CheckFuelWarnings(); if (IsOutOfFuel && _isEngineRunning) { HandleOutOfFuel(); } } } catch (Exception exception) { ModLogger.Error("Error in VehicleFuelSystem.Update", exception); } } public float SetMaxFuelCapacity() { VehicleType vehicleType = _vehicleType; if (1 == 0) { } float num = vehicleType switch { VehicleType.Shitbox => Core.Instance.ShitboxFuelCapacity, VehicleType.Veeper => Core.Instance.VeeperFuelCapacity, VehicleType.Bruiser => Core.Instance.BruiserFuelCapacity, VehicleType.Dinkler => Core.Instance.DinklerFuelCapacity, VehicleType.Hounddog => Core.Instance.HounddogFuelCapacity, VehicleType.Cheetah => Core.Instance.CheetahFuelCapacity, VehicleType.Hotbox => Core.Instance.HotboxFuelCapacity, VehicleType.BugattiTourbillon => Core.Instance.BugattiTourbillonFuelCapacity, VehicleType.CanOfSoupCar => Core.Instance.CanofsoupcarFuelCapacity, VehicleType.CyberTruck => Core.Instance.CyberTruckFuelCapacity, VehicleType.Demon => Core.Instance.DemonFuelCapacity, VehicleType.Driftcar => Core.Instance.DriftcarFuelCapacity, VehicleType.GTR_R34 => Core.Instance.GtrR34FuelCapacity, VehicleType.GTR_R35 => Core.Instance.GtrR35FuelCapacity, VehicleType.LamborghiniVeneno => Core.Instance.LamborghiniVenenoFuelCapacity, VehicleType.RollsRoyceGhost => Core.Instance.RollsRoyceGhostFuelCapacity, VehicleType.Supercar => Core.Instance.SupercarFuelCapacity, VehicleType.KoenigseggCC850 => Core.Instance.KoenigseggCc850FuelCapacity, VehicleType.Other => Core.Instance.DefaultFuelCapacity, _ => Core.Instance.DefaultFuelCapacity, }; if (1 == 0) { } return maxFuelCapacity = num; } private float SetBaseFuelConsumption() { VehicleType vehicleType = _vehicleType; if (1 == 0) { } float num = vehicleType switch { VehicleType.Shitbox => 288f, VehicleType.Veeper => 360f, VehicleType.Bruiser => 414f, VehicleType.Dinkler => 432.00003f, VehicleType.Hounddog => 377.99997f, VehicleType.Cheetah => 377.99997f, VehicleType.Hotbox => 360f, VehicleType.Other => 360f, _ => 360f, }; if (1 == 0) { } float num2 = num; return num2 * Core.Instance.FuelConsumptionMultiplier; } private void UpdateEngineState() { if (!Object.op_Implicit((Object)(object)_landVehicle)) { return; } bool isOccupied = _landVehicle.IsOccupied; if (isOccupied != _isEngineRunning) { _isEngineRunning = isOccupied; if (_isEngineRunning) { _lastConsumptionTime = Time.time; ModLogger.FuelDebug("Vehicle " + _vehicleGUID.Substring(0, 8) + "... engine started, consumption timer reset"); } ModLogger.FuelDebug($"Vehicle {_vehicleGUID.Substring(0, 8)}... engine state changed: {_isEngineRunning} " + $"(occupied: {_landVehicle.IsOccupied}, throttle: {_landVehicle.currentThrottle:F2}, speed: {_landVehicle.Speed_Kmh:F1})"); } } private void UpdateFuelConsumption() { if (!Object.op_Implicit((Object)(object)_landVehicle) || currentFuelLevel <= 0f || !_isEngineRunning) { return; } float deltaTime = Time.deltaTime; float currentThrottle = _landVehicle.currentThrottle; float num = CalculateBaseConsumption(currentThrottle); if (num <= 0f) { _lastConsumptionTime = Time.time; return; } float num2 = CalculateAdvancedSpeedMultiplier(_landVehicle.Speed_Kmh); float currentFuelEfficiencyModifier = GetCurrentFuelEfficiencyModifier(); float num3 = num * num2 * currentFuelEfficiencyModifier; if (num3 <= 0f) { _lastConsumptionTime = Time.time; return; } float num4 = num3 / 3600f * deltaTime; if (num4 > 0.0005f && _landVehicle.IsOccupied) { ConsumeFuel(num4); } _lastConsumptionTime = Time.time; } private float CalculateBaseConsumption(float throttleInput) { float num = Mathf.Clamp01(Mathf.Abs(throttleInput)); if (num > 0.01f) { return Mathf.Lerp(idleConsumptionRate, baseFuelConsumptionRate, num); } if (Object.op_Implicit((Object)(object)_landVehicle) && _landVehicle.IsOccupied) { return idleConsumptionRate; } return 0f; } private float CalculateAdvancedSpeedMultiplier(float speedKmh) { if (speedKmh <= 20f) { return 0.8f; } if (speedKmh <= 40f) { return 1f; } if (speedKmh <= 60f) { return 1f + (speedKmh - 40f) * 0.025f; } if (speedKmh <= 80f) { return 1.5f + (speedKmh - 60f) * 0.05f; } float num = speedKmh - 80f; return 2.5f + num * num * 0.002f; } private float GetCurrentFuelEfficiencyModifier() { if (!Object.op_Implicit((Object)(object)_landVehicle)) { return 1f; } float num = 1f; if (Object.op_Implicit((Object)(object)FuelTypeManager.Instance)) { num = FuelTypeManager.Instance.GetFuelEfficiency(_currentFuelType, _vehicleType, _landVehicle.Speed_Kmh, _landVehicle.currentThrottle); } float num2 = Mathf.Lerp(1f, 1.25f, 1f - Mathf.Clamp01(_fuelQuality)); return num * num2; } private void CheckFuelWarnings() { float fuelPercentage = FuelPercentage; if (fuelPercentage <= criticalFuelThreshold && !_criticalFuelWarningShown) { _criticalFuelWarningShown = true; _lowFuelWarningShown = true; OnCriticalFuelWarning.Invoke(true); ModLogger.LogFuelWarning(_vehicleGUID, currentFuelLevel, "CRITICAL"); } else if (fuelPercentage > criticalFuelThreshold && _criticalFuelWarningShown) { _criticalFuelWarningShown = false; OnCriticalFuelWarning.Invoke(false); } if (fuelPercentage <= lowFuelThreshold && !_lowFuelWarningShown && !_criticalFuelWarningShown) { _lowFuelWarningShown = true; OnLowFuelWarning.Invoke(true); ModLogger.LogFuelWarning(_vehicleGUID, currentFuelLevel, "LOW"); } else if (fuelPercentage > lowFuelThreshold && _lowFuelWarningShown && !_criticalFuelWarningShown) { _lowFuelWarningShown = false; OnLowFuelWarning.Invoke(false); } } private void HandleOutOfFuel() { if (Object.op_Implicit((Object)(object)_landVehicle)) { OnFuelEmpty.Invoke(true); } } public void ConsumeFuel(float amount) { if (!(amount <= 0f)) { float num = currentFuelLevel; currentFuelLevel = Math.Max(0f, currentFuelLevel - amount); if (Math.Abs(num - currentFuelLevel) > 0.001f) { ModLogger.LogFuelConsumption(_vehicleGUID, amount, currentFuelLevel); ModLogger.FuelDebug($"Vehicle {_vehicleGUID.Substring(0, 8)}... fuel consumed: {amount:F3}L, " + $"new level: {currentFuelLevel:F2}L ({FuelPercentage:F1}%)"); TriggerFuelLevelChanged(); } } } public float AddFuel(float amount) { if (amount <= 0f) { return 0f; } float num = currentFuelLevel; currentFuelLevel = Math.Min(maxFuelCapacity, currentFuelLevel + amount); float num2 = currentFuelLevel - num; if (num2 > 0f) { ModLogger.FuelDebug($"Vehicle {_vehicleGUID.Substring(0, 8)}... refueled: +{num2:F1}L"); TriggerFuelLevelChanged(); if (FuelPercentage > lowFuelThreshold) { _lowFuelWarningShown = false; _criticalFuelWarningShown = false; OnLowFuelWarning.Invoke(false); OnCriticalFuelWarning.Invoke(false); OnFuelEmpty.Invoke(false); } } return num2; } public float FullTank() { float num = currentFuelLevel; currentFuelLevel = maxFuelCapacity; if (Math.Abs(num - currentFuelLevel) > 0.001f) { ModLogger.FuelDebug($"Vehicle {_vehicleGUID.Substring(0, 8)}... tank filled to {currentFuelLevel:F1}L"); TriggerFuelLevelChanged(); } return currentFuelLevel; } public void SetFuelLevel(float level) { float num = currentFuelLevel; currentFuelLevel = Math.Clamp(level, 0f, maxFuelCapacity); if (Math.Abs(num - currentFuelLevel) > 0.001f) { ModLogger.FuelDebug($"Vehicle {_vehicleGUID.Substring(0, 8)}... fuel level set to {currentFuelLevel:F1}L"); TriggerFuelLevelChanged(); } } public void SetMaxCapacity(float capacity) { maxFuelCapacity = Math.Max(1f, capacity); currentFuelLevel = Math.Min(currentFuelLevel, maxFuelCapacity); TriggerFuelLevelChanged(); } private void TriggerFuelLevelChanged() { OnFuelLevelChanged.Invoke(currentFuelLevel); OnFuelPercentageChanged.Invoke(FuelPercentage); } private void OnVehicleStarted() { ModLogger.FuelDebug("Vehicle " + _vehicleGUID.Substring(0, 8) + "... started"); } private void OnVehicleStopped() { _isEngineRunning = false; ModLogger.FuelDebug("Vehicle " + _vehicleGUID.Substring(0, 8) + "... stopped"); } public FuelData GetFuelData() { return new FuelData { CurrentFuelLevel = currentFuelLevel, MaxFuelCapacity = maxFuelCapacity, FuelConsumptionRate = baseFuelConsumptionRate }; } public void LoadFuelData(FuelData data) { maxFuelCapacity = data.MaxFuelCapacity; currentFuelLevel = data.CurrentFuelLevel; baseFuelConsumptionRate = data.FuelConsumptionRate; InitializeFuelTypeState(); TriggerFuelLevelChanged(); ModLogger.FuelDebug("Vehicle " + _vehicleGUID.Substring(0, 8) + "... fuel data loaded"); } public void GetFuelDataValues(out float currentLevel) { currentLevel = currentFuelLevel; } public void LoadFuelDataValues(float currentLevel) { currentFuelLevel = currentLevel; SetBaseFuelConsumption(); maxFuelCapacity = SetMaxFuelCapacity(); InitializeFuelTypeState(); TriggerFuelLevelChanged(); ModLogger.FuelDebug("Vehicle " + _vehicleGUID.Substring(0, 8) + "... fuel data loaded"); } private void SetVehicleType() { switch (ReflectionUtils.TryGetFieldOrProperty(_landVehicle, "vehicleName")?.ToString()) { case "Shitbox": _vehicleType = VehicleType.Shitbox; break; case "Veeper": _vehicleType = VehicleType.Veeper; break; case "Bruiser": _vehicleType = VehicleType.Bruiser; break; case "Dinkler": _vehicleType = VehicleType.Dinkler; break; case "Hounddog": _vehicleType = VehicleType.Hounddog; break; case "Cheetah": _vehicleType = VehicleType.Cheetah; break; case "Hotbox": _vehicleType = VehicleType.Hotbox; break; case "Bugatti_Tourbillon": _vehicleType = VehicleType.BugattiTourbillon; break; case "canofsoupcar": _vehicleType = VehicleType.CanOfSoupCar; break; case "Cyber_Truck": _vehicleType = VehicleType.CyberTruck; break; case "Demon": _vehicleType = VehicleType.Demon; break; case "driftcar": _vehicleType = VehicleType.Driftcar; break; case "GTR_R34": _vehicleType = VehicleType.GTR_R34; break; case "GTR_R35": _vehicleType = VehicleType.GTR_R35; break; case "Lamborghini_Veneno": _vehicleType = VehicleType.LamborghiniVeneno; break; case "Rolls_Royce_Ghost": _vehicleType = VehicleType.RollsRoyceGhost; break; case "supercar": _vehicleType = VehicleType.Supercar; break; case "Koenigsegg_CC850": _vehicleType = VehicleType.KoenigseggCC850; break; default: _vehicleType = VehicleType.Other; break; } } private void InitializeFuelTypeState() { _fuelQuality = 1f; if ((Object)(object)FuelTypeManager.Instance != (Object)null) { _currentFuelType = FuelTypeManager.Instance.GetRecommendedFuelType(_vehicleType); } else { _currentFuelType = FuelTypeId.Regular; } } public bool ChangeFuelType(FuelTypeId newFuelType, float refuelAmount) { try { if (!IsFuelCompatible(newFuelType)) { ModLogger.Warning($"Fuel type {newFuelType} is not compatible with {_vehicleType}"); return false; } if (_currentFuelType != newFuelType && CurrentFuelLevel > 0.1f) { CalculateFuelMixingEffect(newFuelType, refuelAmount); } else { _currentFuelType = newFuelType; _fuelQuality = 1f; } return true; } catch (Exception exception) { ModLogger.Error("Error changing fuel type", exception); return false; } } private void CalculateFuelMixingEffect(FuelTypeId newFuelType, float refuelAmount) { try { float num = Mathf.Max(0.01f, CurrentFuelLevel + Mathf.Max(0f, refuelAmount)); float num2 = Mathf.Clamp01(CurrentFuelLevel / num); float num3 = Mathf.Clamp01(Mathf.Max(0f, refuelAmount) / num); if (GetFuelCompatibilityScore(_currentFuelType, newFuelType) < 0.8f) { _fuelQuality = Mathf.Max(0.7f, _fuelQuality * 0.9f); } else { _fuelQuality = Mathf.Clamp01(Mathf.Lerp(_fuelQuality, 1f, num3)); } if (num3 > 0.6f || num2 < 0.2f) { _currentFuelType = newFuelType; } } catch (Exception exception) { ModLogger.Error("Error calculating fuel mixing effect", exception); } } private float GetFuelCompatibilityScore(FuelTypeId fuel1, FuelTypeId fuel2) { if (fuel1 == fuel2) { return 1f; } if (fuel1 == FuelTypeId.Regular || fuel2 == FuelTypeId.Regular) { return 0.8f; } return 0.6f; } private void OnDestroy() { try { if ((Object)(object)_landVehicle != (Object)null) { if (_landVehicle.onVehicleStart != null) { LandVehicle? landVehicle = _landVehicle; landVehicle.onVehicleStart = (Action)Delegate.Remove(landVehicle.onVehicleStart, new Action(OnVehicleStarted)); } if (_landVehicle.onVehicleStop != null) { LandVehicle? landVehicle2 = _landVehicle; landVehicle2.onVehicleStop = (Action)Delegate.Remove(landVehicle2.onVehicleStop, new Action(OnVehicleStopped)); } } ModLogger.FuelDebug("VehicleFuelSystem destroyed for vehicle " + _vehicleGUID.Substring(0, 8) + "..."); } catch (Exception exception) { ModLogger.Error("Error in VehicleFuelSystem.OnDestroy", exception); } } } [Serializable] public class FuelData { public float CurrentFuelLevel; public float MaxFuelCapacity; public float FuelConsumptionRate; } public enum VehicleType { Shitbox, Veeper, Bruiser, Dinkler, Hounddog, Cheetah, Hotbox, BugattiTourbillon, CanOfSoupCar, CyberTruck, Demon, Driftcar, GTR_R34, GTR_R35, LamborghiniVeneno, RollsRoyceGhost, Supercar, KoenigseggCC850, Other } public class VehicleRefuelInteractable : InteractableObject { private LandVehicle _vehicle; private VehicleFuelSystem _fuelSystem; private Equippable_GasolineCan _activeGasCan; public void SetDisplayLocationCollider(Collider collider) { try { base.displayLocationCollider = collider; } catch (Exception exception) { ModLogger.Error("Error setting display location collider", exception); } } private void Awake() { try { _vehicle = ((Component)this).GetComponent(); _fuelSystem = ((Component)this).GetComponent(); if ((Object)(object)_vehicle == (Object)null) { _vehicle = ((Component)this).GetComponentInParent(); } if ((Object)(object)_fuelSystem == (Object)null) { _fuelSystem = ((Component)this).GetComponentInParent(); } if ((Object)(object)_vehicle == (Object)null || (Object)(object)_fuelSystem == (Object)null) { ModLogger.Warning("VehicleRefuelInteractable: Missing required components"); ((Behaviour)this).enabled = false; return; } base.MaxInteractionRange = 1f; base.RequiresUniqueClick = false; base.Priority = 10; ModLogger.Debug("VehicleRefuelInteractable initialized for " + _vehicle.VehicleName); } catch (Exception exception) { ModLogger.Error("Error in VehicleRefuelInteractable.Awake", exception); } } public override void Hovered() { //IL_0151: Unknown result type (might be due to invalid IL or missing references) //IL_0157: Invalid comparison between Unknown and I4 try { PlayerInventory instance = PlayerSingleton.Instance; Equippable val = ((instance == null) ? null : instance.equippedSlot?.Equippable); if ((Object)(object)val == (Object)null) { return; } _activeGasCan = ((Component)val).GetComponent(); if (!((Object)(object)_activeGasCan == (Object)null) && _vehicle.IsPlayerOwned) { float num = _fuelSystem.MaxFuelCapacity - _fuelSystem.CurrentFuelLevel; if (num > 0.1f) { string fuelTypeDisplayName = GetFuelTypeDisplayName(_fuelSystem.CurrentFuelType); string text = BuildFuelCompatibilityTag(_fuelSystem, _fuelSystem.CurrentFuelType); ((InteractableObject)this).SetMessage("Refuel " + _vehicle.VehicleName + " [" + fuelTypeDisplayName + text + "] (Hold)"); ((InteractableObject)this).SetInteractableState((EInteractableState)0); ((InteractableObject)this).SetInteractionType((EInteractionType)0); } else { ((InteractableObject)this).SetMessage(_vehicle.VehicleName + " - Tank Full"); ((InteractableObject)this).SetInteractableState((EInteractableState)1); } UnityEvent onHovered = base.onHovered; if (onHovered != null) { onHovered.Invoke(); } if ((int)((InteractableObject)this)._interactionState != 2) { ((InteractableObject)this).ShowMessage(); } } } catch (Exception ex) { ModLogger.Debug("Error in VehicleRefuelInteractable.Hovered: " + ex.Message); } } public override void StartInteract() { //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Invalid comparison between Unknown and I4 try { if ((int)((InteractableObject)this)._interactionState != 1) { UnityEvent onInteractStart = base.onInteractStart; if (onInteractStart != null) { onInteractStart.Invoke(); } Singleton.Instance.LerpDisplayScale(0.9f); } if ((Object)(object)_activeGasCan != (Object)null && (Object)(object)_fuelSystem != (Object)null) { _activeGasCan.BeginRefuelInteraction(_vehicle, _fuelSystem); } } catch (Exception ex) { ModLogger.Debug("Error in VehicleRefuelInteractable.StartInteract: " + ex.Message); } } public override void EndInteract() { try { UnityEvent onInteractEnd = base.onInteractEnd; if (onInteractEnd != null) { onInteractEnd.Invoke(); } Singleton.Instance.LerpDisplayScale(1f); if ((Object)(object)_activeGasCan != (Object)null) { _activeGasCan.EndRefuelInteraction(); } } catch (Exception ex) { ModLogger.Debug("Error in VehicleRefuelInteractable.EndInteract: " + ex.Message); } } private string GetFuelTypeDisplayName(FuelTypeId fuelTypeId) { if ((Object)(object)FuelTypeManager.Instance != (Object)null) { string fuelDisplayName = FuelTypeManager.Instance.GetFuelDisplayName(fuelTypeId); if (!string.IsNullOrEmpty(fuelDisplayName)) { return fuelDisplayName; } } return fuelTypeId.ToString(); } private string BuildFuelCompatibilityTag(VehicleFuelSystem fuelSystem, FuelTypeId fuelType) { if ((Object)(object)fuelSystem == (Object)null) { return string.Empty; } try { FuelTypeId recommendedFuelType = fuelSystem.GetRecommendedFuelType(); bool flag = fuelSystem.IsFuelCompatible(fuelType); if (fuelType == recommendedFuelType && flag) { return " (Recommended)"; } if (flag) { return " (Compatible)"; } return " (Penalty)"; } catch (Exception ex) { ModLogger.Debug("Error building fuel compatibility tag: " + ex.Message); return string.Empty; } } } } namespace S1FuelMod.Systems.FuelTypes { public sealed class DieselFuel : FuelType { private static readonly Color DieselFuelColor = new Color(0.6f, 0.4f, 0.1f, 1f); private static readonly HashSet CompatibleVehicleTypes = new HashSet { VehicleType.Dinkler, VehicleType.Bruiser, VehicleType.CyberTruck }; public override FuelTypeId Id => FuelTypeId.Diesel; public override string DisplayName => "Diesel"; public override string Description => "High-efficiency fuel for heavy vehicles."; public override float PriceMultiplier => 1.15f; public override float ConsumptionEfficiency => 0.75f; public override float TorqueModifier => 1.25f; public override float AccelerationModifier => 0.9f; public override float TopSpeedModifier => 0.95f; public override Color UIColor => DieselFuelColor; public override (float minOptimalSpeed, float maxOptimalSpeed) GetOptimalSpeedRange() { return (50f, 75f); } protected override HashSet GetCompatibleVehicleTypes() { return CompatibleVehicleTypes; } } public abstract class FuelType { public abstract FuelTypeId Id { get; } public abstract string DisplayName { get; } public abstract string Description { get; } public abstract float PriceMultiplier { get; } public abstract float ConsumptionEfficiency { get; } public abstract float TorqueModifier { get; } public abstract float AccelerationModifier { get; } public abstract float TopSpeedModifier { get; } public abstract Color UIColor { get; } public virtual bool IsCompatibleWith(VehicleType vehicleType) { try { return GetCompatibleVehicleTypes()?.Contains(vehicleType) ?? false; } catch (Exception exception) { ModLogger.Error("FuelType compatibility check failed for " + DisplayName, exception); return false; } } public virtual float CalculateConsumptionModifier(float speedKmh, float throttleInput, VehicleType vehicleType) { try { float num = CalculateSpeedEfficiencyCurve(speedKmh); float num2 = CalculateThrottleModifier(throttleInput); float vehicleTypeModifier = GetVehicleTypeModifier(vehicleType); return num * num2 * vehicleTypeModifier; } catch (Exception exception) { ModLogger.Error("FuelType consumption modifier failed for " + DisplayName, exception); return 1f; } } public virtual (float minOptimalSpeed, float maxOptimalSpeed) GetOptimalSpeedRange() { return (45f, 75f); } public virtual float GetIncompatibilityPenalty(VehicleType vehicleType) { return IsCompatibleWith(vehicleType) ? 1f : 0.7f; } protected abstract HashSet GetCompatibleVehicleTypes(); protected virtual float CalculateSpeedEfficiencyCurve(float speedKmh) { var (num, num2) = GetOptimalSpeedRange(); if (speedKmh >= num && speedKmh <= num2) { return 1f; } float num3 = ((speedKmh < num) ? (num - speedKmh) : (speedKmh - num2)); float num4 = 1f - num3 * 0.01f; return Mathf.Max(0.7f, num4); } protected virtual float CalculateThrottleModifier(float throttleInput) { float num = Mathf.Clamp01(Mathf.Abs(throttleInput)); return 1f + num * num * 0.5f; } protected virtual float GetVehicleTypeModifier(VehicleType vehicleType) { return IsCompatibleWith(vehicleType) ? 1f : 1.3f; } } public class FuelTypeManager : MonoBehaviour { private static FuelTypeManager? _instance; private readonly Dictionary _fuelTypes = new Dictionary(); private readonly Dictionary _recommendedFuelTypes = new Dictionary(); public static FuelTypeManager? Instance => _instance; private void Awake() { try { if ((Object)(object)_instance != (Object)null && (Object)(object)_instance != (Object)(object)this) { Object.Destroy((Object)(object)((Component)this).gameObject); return; } _instance = this; Object.DontDestroyOnLoad((Object)(object)((Component)this).gameObject); InitializeFuelTypes(); ModLogger.Debug("FuelTypeManager initialized successfully"); } catch (Exception exception) { ModLogger.Error("Error in FuelTypeManager.Awake", exception); } } private void InitializeFuelTypes() { _fuelTypes.Clear(); RegisterFuelType(new RegularFuel()); RegisterFuelType(new MidGradeFuel()); RegisterFuelType(new PremiumFuel()); RegisterFuelType(new DieselFuel()); BuildRecommendationMatrix(); } private void RegisterFuelType(FuelType fuelType) { if (fuelType == null) { ModLogger.Warning("Attempted to register a null fuel type"); } else { _fuelTypes[fuelType.Id] = fuelType; } } private void BuildRecommendationMatrix() { _recommendedFuelTypes.Clear(); SetRecommendation(VehicleType.Shitbox, FuelTypeId.Regular); SetRecommendation(VehicleType.Veeper, FuelTypeId.Regular); SetRecommendation(VehicleType.Hotbox, FuelTypeId.Regular); SetRecommendation(VehicleType.CanOfSoupCar, FuelTypeId.Regular); SetRecommendation(VehicleType.Other, FuelTypeId.Regular); SetRecommendation(VehicleType.Hounddog, FuelTypeId.MidGrade); SetRecommendation(VehicleType.Cheetah, FuelTypeId.MidGrade); SetRecommendation(VehicleType.Supercar, FuelTypeId.MidGrade); SetRecommendation(VehicleType.Demon, FuelTypeId.MidGrade); SetRecommendation(VehicleType.Driftcar, FuelTypeId.MidGrade); SetRecommendation(VehicleType.BugattiTourbillon, FuelTypeId.Premium); SetRecommendation(VehicleType.GTR_R34, FuelTypeId.Premium); SetRecommendation(VehicleType.GTR_R35, FuelTypeId.Premium); SetRecommendation(VehicleType.LamborghiniVeneno, FuelTypeId.Premium); SetRecommendation(VehicleType.RollsRoyceGhost, FuelTypeId.Premium); SetRecommendation(VehicleType.KoenigseggCC850, FuelTypeId.Premium); SetRecommendation(VehicleType.Bruiser, FuelTypeId.Diesel); SetRecommendation(VehicleType.Dinkler, FuelTypeId.Diesel); SetRecommendation(VehicleType.CyberTruck, FuelTypeId.Diesel); } private void SetRecommendation(VehicleType vehicleType, FuelTypeId fuelTypeId) { _recommendedFuelTypes[vehicleType] = fuelTypeId; } public FuelTypeId GetRecommendedFuelType(VehicleType vehicleType) { FuelTypeId value; return _recommendedFuelTypes.TryGetValue(vehicleType, out value) ? value : FuelTypeId.Regular; } public bool IsFuelCompatible(FuelTypeId fuelTypeId, VehicleType vehicleType) { FuelType value; return _fuelTypes.TryGetValue(fuelTypeId, out value) && value.IsCompatibleWith(vehicleType); } public FuelType? GetFuelType(FuelTypeId fuelTypeId) { FuelType value; return _fuelTypes.TryGetValue(fuelTypeId, out value) ? value : null; } public string GetFuelDisplayName(FuelTypeId fuelTypeId) { FuelType value; return _fuelTypes.TryGetValue(fuelTypeId, out value) ? value.DisplayName : "Unknown"; } public float GetFuelPrice(FuelTypeId fuelTypeId) { if (!_fuelTypes.TryGetValue(fuelTypeId, out FuelType value)) { return 4f; } return 4f * value.PriceMultiplier; } public float GetFuelTorqueModifier(FuelTypeId fuelTypeId) { FuelType value; return _fuelTypes.TryGetValue(fuelTypeId, out value) ? value.TorqueModifier : 1f; } public float GetFuelAccelerationModifier(FuelTypeId fuelTypeId) { FuelType value; return _fuelTypes.TryGetValue(fuelTypeId, out value) ? value.AccelerationModifier : 1f; } public float GetFuelTopSpeedModifier(FuelTypeId fuelTypeId) { FuelType value; return _fuelTypes.TryGetValue(fuelTypeId, out value) ? value.TopSpeedModifier : 1f; } public Color GetFuelUIColor(FuelTypeId fuelTypeId) { //IL_0019: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_001e: Unknown result type (might be due to invalid IL or missing references) //IL_0021: Unknown result type (might be due to invalid IL or missing references) FuelType value; return _fuelTypes.TryGetValue(fuelTypeId, out value) ? value.UIColor : Color.white; } public float GetFuelEfficiency(FuelTypeId fuelTypeId, VehicleType vehicleType, float speedKmh, float throttleInput) { if (!_fuelTypes.TryGetValue(fuelTypeId, out FuelType value)) { return 1f; } return value.CalculateConsumptionModifier(speedKmh, throttleInput, vehicleType); } public FuelTypeId[] GetCompatibleFuelTypesArray(VehicleType vehicleType) { List list = new List(); foreach (KeyValuePair fuelType in _fuelTypes) { if (fuelType.Value.IsCompatibleWith(vehicleType)) { list.Add(fuelType.Key); } } return list.ToArray(); } private void OnDestroy() { if ((Object)(object)_instance == (Object)(object)this) { _instance = null; } } public List GetCompatibleFuelTypes(VehicleType vehicleType) { List list = new List(); foreach (KeyValuePair fuelType in _fuelTypes) { if (fuelType.Value.IsCompatibleWith(vehicleType)) { list.Add(fuelType.Key); } } return list; } } public sealed class MidGradeFuel : FuelType { private static readonly Color MidGradeFuelColor = new Color(0.4f, 0.6f, 0.2f, 1f); private static readonly HashSet CompatibleVehicleTypes = new HashSet { VehicleType.Shitbox, VehicleType.Veeper, VehicleType.Hotbox, VehicleType.CanOfSoupCar, VehicleType.Other, VehicleType.Hounddog, VehicleType.Cheetah, VehicleType.Supercar, VehicleType.BugattiTourbillon, VehicleType.GTR_R34, VehicleType.GTR_R35, VehicleType.LamborghiniVeneno, VehicleType.RollsRoyceGhost, VehicleType.KoenigseggCC850, VehicleType.Demon, VehicleType.Driftcar }; public override FuelTypeId Id => FuelTypeId.MidGrade; public override string DisplayName => "Mid-Grade"; public override string Description => "Mid-grade gasoline offering balanced performance and efficiency."; public override float PriceMultiplier => 1.15f; public override float ConsumptionEfficiency => 0.9f; public override float TorqueModifier => 1.02f; public override float AccelerationModifier => 1.04f; public override float TopSpeedModifier => 1.02f; public override Color UIColor => MidGradeFuelColor; public override (float minOptimalSpeed, float maxOptimalSpeed) GetOptimalSpeedRange() { return (50f, 70f); } protected override HashSet GetCompatibleVehicleTypes() { return CompatibleVehicleTypes; } } public sealed class PremiumFuel : FuelType { private static readonly Color PremiumFuelColor = new Color(0.8f, 0.2f, 0.8f, 1f); private static readonly HashSet CompatibleVehicleTypes = new HashSet { VehicleType.Cheetah, VehicleType.Hounddog, VehicleType.Supercar, VehicleType.BugattiTourbillon, VehicleType.GTR_R34, VehicleType.GTR_R35, VehicleType.LamborghiniVeneno, VehicleType.RollsRoyceGhost, VehicleType.KoenigseggCC850 }; public override FuelTypeId Id => FuelTypeId.Premium; public override string DisplayName => "Premium"; public override string Description => "High-octane fuel for performance vehicles."; public override float PriceMultiplier => 1.4f; public override float ConsumptionEfficiency => 0.85f; public override float TorqueModifier => 1.12f; public override float AccelerationModifier => 1.18f; public override float TopSpeedModifier => 1.08f; public override Color UIColor => PremiumFuelColor; public override (float minOptimalSpeed, float maxOptimalSpeed) GetOptimalSpeedRange() { return (65f, 90f); } protected override HashSet GetCompatibleVehicleTypes() { return CompatibleVehicleTypes; } } public sealed class RegularFuel : FuelType { private static readonly Color RegularFuelColor = new Color(0.2f, 0.8f, 0.2f, 1f); private static readonly HashSet CompatibleVehicleTypes = new HashSet { VehicleType.Shitbox, VehicleType.Veeper, VehicleType.Hotbox, VehicleType.CanOfSoupCar, VehicleType.Other }; public override FuelTypeId Id => FuelTypeId.Regular; public override string DisplayName => "Regular"; public override string Description => "Standard gasoline for everyday driving."; public override float PriceMultiplier => 1f; public override float ConsumptionEfficiency => 1f; public override float TorqueModifier => 1f; public override float AccelerationModifier => 1f; public override float TopSpeedModifier => 1f; public override Color UIColor => RegularFuelColor; public override (float minOptimalSpeed, float maxOptimalSpeed) GetOptimalSpeedRange() { return (40f, 60f); } protected override HashSet GetCompatibleVehicleTypes() { return CompatibleVehicleTypes; } } } namespace S1FuelMod.Networking { internal class FuelNetworkManager : IDisposable { private const int P2P_CHANNEL = 98; private bool _initialized; private bool _disposed; private Callback? _sessionRequestCb; private Callback? _sessionFailCb; private readonly Dictionary _trackedFuelSystems = new Dictionary(); private float _lastHeartbeatTime = 0f; private const float HEARTBEAT_INTERVAL = 3f; private readonly Dictionary _lastSentLevel = new Dictionary(); private readonly Dictionary _lastSentTime = new Dictionary(); private readonly Dictionary _lastNetLevel = new Dictionary(); private readonly Dictionary _lastNetTime = new Dictionary(); private bool _snapshotRequested; private readonly Dictionary _pendingSnapshotData = new Dictionary(); internal void Initialize() { if (_initialized) { return; } try { if (!SteamAPI.IsSteamRunning()) { ModLogger.Warning("FuelNetwork: Steam not running, deferring init"); return; } if (!SteamManager.Initialized) { ModLogger.Warning("FuelNetwork: SteamManager not initialized, deferring init"); return; } _sessionRequestCb = Callback.Create((DispatchDelegate)OnSessionRequest); _sessionFailCb = Callback.Create((DispatchDelegate)OnSessionConnectFail); _initialized = true; ModLogger.Debug("FuelNetwork: Initialized P2P callbacks"); } catch (Exception exception) { ModLogger.Error("FuelNetwork: Initialize failed", exception); } } public void Dispose() { if (!_disposed) { try { _sessionRequestCb?.Dispose(); _sessionFailCb?.Dispose(); _trackedFuelSystems.Clear(); _pendingSnapshotData.Clear(); } catch (Exception exception) { ModLogger.Error("FuelNetwork: Dispose error", exception); } _disposed = true; } } internal void Update() { if (!_initialized) { Initialize(); return; } ProcessIncomingPackets(); ProcessHeartbeat(); TryRequestSnapshotOnce(); } private void TryRequestSnapshotOnce() { //IL_0054: Unknown result type (might be due to invalid IL or missing references) if (_snapshotRequested) { return; } Lobby instance = Singleton.Instance; if ((Object)(object)instance == (Object)null || !instance.IsInLobby) { _snapshotRequested = false; return; } if (instance.IsHost) { _snapshotRequested = true; return; } try { FuelSnapshotRequestMessage message = new FuelSnapshotRequestMessage(); SendTo(GetHostId(), message); _snapshotRequested = true; ModLogger.FuelDebug("FuelNetwork: Snapshot request sent to host"); } catch (Exception exception) { ModLogger.Error("FuelNetwork: Failed to request snapshot", exception); } } internal void RegisterFuelSystem(VehicleFuelSystem fuelSystem) { if ((Object)(object)fuelSystem == (Object)null) { return; } string networkID = fuelSystem.NetworkID; if (!string.IsNullOrEmpty(networkID) && !_trackedFuelSystems.ContainsKey(networkID)) { _trackedFuelSystems[networkID] = fuelSystem; if (_pendingSnapshotData.TryGetValue(networkID, out PendingFuelData value)) { fuelSystem.SetMaxCapacity(Mathf.Max(1f, value.MaxCapacity)); fuelSystem.SetFuelLevel(Mathf.Clamp(value.FuelLevel, 0f, fuelSystem.MaxFuelCapacity)); _lastNetLevel[networkID] = fuelSystem.CurrentFuelLevel; _lastNetTime[networkID] = value.ReceivedTime; _lastSentLevel[networkID] = fuelSystem.CurrentFuelLevel; _lastSentTime[networkID] = Time.time; _pendingSnapshotData.Remove(networkID); ModLogger.Debug($"FuelNetwork: Applied pending snapshot data to NetworkID:{networkID} - Level: {value.FuelLevel:F2}L, MaxCapacity: {value.MaxCapacity:F2}L"); } ModLogger.Info("FuelNetwork: Registered fuel system for vehicle NetworkID:" + networkID + " (GUID:" + fuelSystem.VehicleGUID?.Substring(0, 8) + "...)"); } } internal void UnregisterFuelSystem(VehicleFuelSystem fuelSystem) { if (!((Object)(object)fuelSystem == (Object)null)) { string networkID = fuelSystem.NetworkID; if (!string.IsNullOrEmpty(networkID)) { _trackedFuelSystems.Remove(networkID); _lastSentLevel.Remove(networkID); _lastSentTime.Remove(networkID); _lastNetLevel.Remove(networkID); _lastNetTime.Remove(networkID); _pendingSnapshotData.Remove(networkID); } } } private void ProcessHeartbeat() { //IL_0189: Unknown result type (might be due to invalid IL or missing references) float time = Time.time; if (time - _lastHeartbeatTime < 3f) { return; } _lastHeartbeatTime = time; Lobby instance = Singleton.Instance; if ((Object)(object)instance == (Object)null || !instance.IsInLobby || instance.PlayerCount <= 1) { return; } bool flag = IsHost(); int num = 0; foreach (KeyValuePair trackedFuelSystem in _trackedFuelSystems) { string key = trackedFuelSystem.Key; VehicleFuelSystem value = trackedFuelSystem.Value; if ((Object)(object)value == (Object)null) { continue; } float currentFuelLevel = value.CurrentFuelLevel; bool flag2 = false; if (!_lastSentLevel.ContainsKey(key)) { if (_lastNetTime.ContainsKey(key) && time - _lastNetTime[key] < 5f) { flag2 = false; _lastSentLevel[key] = currentFuelLevel; _lastSentTime[key] = time; ModLogger.Debug("FuelNetwork: Suppressed initial update for NetworkID:" + key + " due to recent network data"); } else { flag2 = true; } } else { float num2 = _lastSentLevel[key]; float num3 = Mathf.Abs(currentFuelLevel - num2); if (num3 > 0.1f) { flag2 = true; } } if (flag2) { if (flag) { BroadcastFuelUpdate(value); } else { SendTo(GetHostId(), new FuelUpdateMessage { VehicleGuid = key, FuelLevel = currentFuelLevel, MaxCapacity = value.MaxFuelCapacity }); } _lastSentLevel[key] = currentFuelLevel; _lastSentTime[key] = time; num++; } } if (num > 0) { ModLogger.Debug($"FuelNetwork: Heartbeat sent {num} fuel updates (IsHost: {flag})"); } } private void BroadcastFuelUpdate(VehicleFuelSystem fs) { try { Lobby instance = Singleton.Instance; if (!((Object)(object)instance == (Object)null) && instance.IsInLobby) { FuelUpdateMessage message = new FuelUpdateMessage { VehicleGuid = fs.NetworkID, FuelLevel = fs.CurrentFuelLevel, MaxCapacity = fs.MaxFuelCapacity }; Broadcast(message); } } catch (Exception exception) { ModLogger.Error("FuelNetwork: BroadcastFuelUpdate failed", exception); } } private void ProcessIncomingPackets() { //IL_00b9: Unknown result type (might be due to invalid IL or missing references) //IL_0105: Unknown result type (might be due to invalid IL or missing references) try { int[] array = new int[4] { 98, 0, 1, 2 }; int[] array2 = array; uint num2 = default(uint); uint num3 = default(uint); CSteamID val = default(CSteamID); uint num4 = default(uint); foreach (int num in array2) { while (SteamNetworking.IsP2PPacketAvailable(ref num2, num)) { ModLogger.Debug($"FuelNetwork: Found P2P packet - size: {num2}, channel: {num}"); if (num2 == 0 || num2 > 32768) { byte[] array3 = new byte[num2]; SteamNetworking.ReadP2PPacket(array3, num2, ref num3, ref val, num); ModLogger.Warning($"FuelNetwork: Discarded oversized packet: {num2} bytes"); continue; } byte[] array4 = new byte[num2]; if (!SteamNetworking.ReadP2PPacket(array4, num2, ref num4, ref val, num)) { continue; } ModLogger.Debug($"FuelNetwork: Read P2P packet - {num4} bytes from {val}, channel {num}"); if (num4 != 0) { if (num4 < num2) { byte[] array5 = new byte[num4]; Array.Copy(array4, array5, num4); array4 = array5; } HandlePacket(val, array4); } } } } catch (Exception exception) { ModLogger.Error("FuelNetwork: ProcessIncomingPackets error", exception); } } private void HandlePacket(CSteamID sender, byte[] data) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_01bb: Unknown result type (might be due to invalid IL or missing references) //IL_0112: Unknown result type (might be due to invalid IL or missing references) //IL_00f1: Unknown result type (might be due to invalid IL or missing references) //IL_00bc: Unknown result type (might be due to invalid IL or missing references) //IL_0143: Unknown result type (might be due to invalid IL or missing references) //IL_016a: Unknown result type (might be due to invalid IL or missing references) //IL_0191: Unknown result type (might be due to invalid IL or missing references) ModLogger.Debug($"FuelNetwork: HandlePacket from {sender.m_SteamID} - {data.Length} bytes"); try { if (!MiniMessageSerializer.IsValidMessage(data)) { string text = $"Invalid message: length={data.Length}"; if (data.Length >= 4) { byte[] array = new byte[4]; Array.Copy(data, 0, array, 0, 4); string @string = Encoding.UTF8.GetString(array); text = text + ", header='" + @string + "' (expected='SNLM')"; if (data.Length >= 5) { text += $", typeLen={data[4]}"; } } ModLogger.Warning($"FuelNetwork: Invalid message format from {sender.m_SteamID} - {text}"); return; } string messageType = MiniMessageSerializer.GetMessageType(data); if (string.IsNullOrEmpty(messageType)) { ModLogger.Warning($"FuelNetwork: Empty message type from {sender.m_SteamID}"); return; } ModLogger.Debug($"FuelNetwork: Processing {messageType} from {sender.m_SteamID}"); switch (messageType) { case "FUEL_UPDATE": { FuelUpdateMessage msg2 = MiniMessageSerializer.CreateMessage(data); OnFuelUpdateReceived(sender, msg2); break; } case "FUEL_SNAPSHOT": { FuelSnapshotMessage msg = MiniMessageSerializer.CreateMessage(data); OnFuelSnapshotReceived(sender, msg); break; } case "FUEL_SNAPSHOT_REQ": { FuelSnapshotRequestMessage _ = MiniMessageSerializer.CreateMessage(data); OnFuelSnapshotRequestReceived(sender, _); break; } default: ModLogger.Warning("FuelNetwork: Unknown message type: " + messageType); break; } } catch (Exception exception) { ModLogger.Error($"FuelNetwork: HandlePacket error from {sender.m_SteamID}", exception); } } private void OnFuelUpdateReceived(CSteamID sender, FuelUpdateMessage msg) { //IL_0014: Unknown result type (might be due to invalid IL or missing references) //IL_016e: Unknown result type (might be due to invalid IL or missing references) bool flag = IsHost(); ModLogger.Debug($"FuelNetwork: Received fuel update from {sender.m_SteamID} - Vehicle NetworkID: {msg.VehicleGuid}, Level: {msg.FuelLevel:F2}L, IsHost: {flag}"); try { if (_trackedFuelSystems.TryGetValue(msg.VehicleGuid, out VehicleFuelSystem value) && (Object)(object)value != (Object)null) { ModLogger.Debug($"FuelNetwork: Applying fuel update to vehicle NetworkID:{msg.VehicleGuid} - {msg.FuelLevel:F2}L"); value.SetMaxCapacity(Mathf.Max(1f, msg.MaxCapacity)); value.SetFuelLevel(Mathf.Clamp(msg.FuelLevel, 0f, value.MaxFuelCapacity)); _lastNetLevel[msg.VehicleGuid] = value.CurrentFuelLevel; _lastNetTime[msg.VehicleGuid] = Time.time; _lastSentLevel[msg.VehicleGuid] = value.CurrentFuelLevel; } else if (_trackedFuelSystems.Count > 0) { ModLogger.Warning($"FuelNetwork: Vehicle NetworkID:{msg.VehicleGuid} not found in tracked systems (have {_trackedFuelSystems.Count} tracked)"); } if (flag) { ModLogger.Debug("FuelNetwork: Host rebroadcasting fuel update for NetworkID:" + msg.VehicleGuid); BroadcastToOthers(msg, sender); } } catch (Exception exception) { ModLogger.Error("FuelNetwork: OnFuelUpdateReceived error", exception); } } private void OnFuelSnapshotRequestReceived(CSteamID sender, FuelSnapshotRequestMessage _) { //IL_00b5: Unknown result type (might be due to invalid IL or missing references) //IL_00cd: Unknown result type (might be due to invalid IL or missing references) if (!IsHost()) { return; } try { List list = new List(); foreach (KeyValuePair trackedFuelSystem in _trackedFuelSystems) { VehicleFuelSystem value = trackedFuelSystem.Value; if (!((Object)(object)value == (Object)null)) { list.Add(new FuelUpdateMessage.Item { VehicleGuid = value.NetworkID, FuelLevel = value.CurrentFuelLevel, MaxCapacity = value.MaxFuelCapacity }); } } FuelSnapshotMessage message = new FuelSnapshotMessage { Items = list.ToArray() }; SendTo(sender, message); ModLogger.Debug($"FuelNetwork: Sent snapshot with {list.Count} items to {sender.m_SteamID}"); } catch (Exception exception) { ModLogger.Error("FuelNetwork: OnFuelSnapshotRequestReceived error", exception); } } private void OnFuelSnapshotReceived(CSteamID sender, FuelSnapshotMessage msg) { try { if (msg.Items == null) { return; } FuelUpdateMessage.Item[] items = msg.Items; for (int i = 0; i < items.Length; i++) { FuelUpdateMessage.Item item = items[i]; if (string.IsNullOrEmpty(item.VehicleGuid)) { continue; } if (_trackedFuelSystems.TryGetValue(item.VehicleGuid, out VehicleFuelSystem value) && (Object)(object)value != (Object)null) { value.SetMaxCapacity(Mathf.Max(1f, item.MaxCapacity)); value.SetFuelLevel(Mathf.Clamp(item.FuelLevel, 0f, value.MaxFuelCapacity)); _lastNetLevel[item.VehicleGuid] = value.CurrentFuelLevel; _lastNetTime[item.VehicleGuid] = Time.time; continue; } _pendingSnapshotData[item.VehicleGuid] = new PendingFuelData { FuelLevel = item.FuelLevel, MaxCapacity = item.MaxCapacity, ReceivedTime = Time.time }; if (_trackedFuelSystems.Count > 0) { ModLogger.Debug($"FuelNetwork: Stored pending snapshot data for NetworkID:{item.VehicleGuid} (have {_trackedFuelSystems.Count} tracked, {_pendingSnapshotData.Count} pending)"); } } ModLogger.Debug($"FuelNetwork: Applied snapshot with {msg.Items.Length} items"); } catch (Exception exception) { ModLogger.Error("FuelNetwork: OnFuelSnapshotReceived error", exception); } } private void Broadcast(MiniP2PMessage message) { //IL_002d: 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_006a: 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_0071: Unknown result type (might be due to invalid IL or missing references) //IL_0074: 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_00a3: Unknown result type (might be due to invalid IL or missing references) Lobby instance = Singleton.Instance; if ((Object)(object)instance == (Object)null || !instance.IsInLobby) { return; } byte[] array = MiniMessageSerializer.SerializeMessage(message); int numLobbyMembers = SteamMatchmaking.GetNumLobbyMembers(instance.LobbySteamID); ModLogger.Debug($"FuelNetwork: Broadcasting {message.MessageType} to {numLobbyMembers} members ({array.Length} bytes)"); for (int i = 0; i < numLobbyMembers; i++) { CSteamID lobbyMemberByIndex = SteamMatchmaking.GetLobbyMemberByIndex(instance.LobbySteamID, i); if (!(lobbyMemberByIndex == instance.LocalPlayerID)) { ModLogger.Debug($"FuelNetwork: Sending to member {lobbyMemberByIndex.m_SteamID}"); SafeSendPacket(lobbyMemberByIndex, array); } } } private void BroadcastToOthers(MiniP2PMessage message, CSteamID excludePlayer) { //IL_002a: Unknown result type (might be due to invalid IL or missing references) //IL_003c: Unknown result type (might be due to invalid IL or missing references) //IL_0043: 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_004a: Unknown result type (might be due to invalid IL or missing references) //IL_004d: Unknown result type (might be due to invalid IL or missing references) //IL_0059: 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_006d: Unknown result type (might be due to invalid IL or missing references) Lobby instance = Singleton.Instance; if ((Object)(object)instance == (Object)null || !instance.IsInLobby) { return; } byte[] data = MiniMessageSerializer.SerializeMessage(message); int numLobbyMembers = SteamMatchmaking.GetNumLobbyMembers(instance.LobbySteamID); for (int i = 0; i < numLobbyMembers; i++) { CSteamID lobbyMemberByIndex = SteamMatchmaking.GetLobbyMemberByIndex(instance.LobbySteamID, i); if (!(lobbyMemberByIndex == instance.LocalPlayerID) && !(lobbyMemberByIndex == excludePlayer)) { SafeSendPacket(lobbyMemberByIndex, data); } } } private void SendTo(CSteamID target, MiniP2PMessage message) { //IL_0013: Unknown result type (might be due to invalid IL or missing references) //IL_0032: Unknown result type (might be due to invalid IL or missing references) byte[] array = MiniMessageSerializer.SerializeMessage(message); ModLogger.Debug($"FuelNetwork: Sending {message.MessageType} to {target.m_SteamID} ({array.Length} bytes)"); SafeSendPacket(target, array); } private void SafeSendPacket(CSteamID target, byte[] data) { //IL_0042: Unknown result type (might be due to invalid IL or missing references) //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_001b: Unknown result type (might be due to invalid IL or missing references) try { bool flag = SteamNetworking.SendP2PPacket(target, data, (uint)data.Length, (EP2PSend)2, 98); ModLogger.Debug($"FuelNetwork: SendP2PPacket result: {flag} to {target.m_SteamID} on channel {98}"); } catch (Exception exception) { ModLogger.Error($"FuelNetwork: Failed sending packet to {target.m_SteamID}", exception); } } private void OnSessionRequest(P2PSessionRequest_t e) { //IL_0002: Unknown result type (might be due to invalid IL or missing references) //IL_0003: Unknown result type (might be due to invalid IL or missing references) //IL_0008: Unknown result type (might be due to invalid IL or missing references) //IL_0009: Unknown result type (might be due to invalid IL or missing references) try { CSteamID steamIDRemote = e.m_steamIDRemote; SteamNetworking.AcceptP2PSessionWithUser(steamIDRemote); } catch (Exception exception) { ModLogger.Error("FuelNetwork: OnSessionRequest error", exception); } } private void OnSessionConnectFail(P2PSessionConnectFail_t e) { //IL_0006: Unknown result type (might be due to invalid IL or missing references) //IL_0011: Unknown result type (might be due to invalid IL or missing references) //IL_0012: Unknown result type (might be due to invalid IL or missing references) ModLogger.Warning($"FuelNetwork: P2P connect fail {e.m_eP2PSessionError} with {e.m_steamIDRemote.m_SteamID}"); } private static bool IsHost() { Lobby instance = Singleton.Instance; return (Object)(object)instance != (Object)null && instance.IsInLobby && instance.IsHost; } private static CSteamID GetHostId() { //IL_0029: Unknown result type (might be due to invalid IL or missing references) //IL_002e: Unknown result type (might be due to invalid IL or missing references) //IL_0033: Unknown result type (might be due to invalid IL or missing references) //IL_0020: Unknown result type (might be due to invalid IL or missing references) //IL_0025: Unknown result type (might be due to invalid IL or missing references) //IL_0036: Unknown result type (might be due to invalid IL or missing references) Lobby instance = Singleton.Instance; if ((Object)(object)instance == (Object)null || !instance.IsInLobby) { return CSteamID.Nil; } return SteamMatchmaking.GetLobbyOwner(instance.LobbySteamID); } } internal class PendingFuelData { public float FuelLevel { get; set; } public float MaxCapacity { get; set; } public float ReceivedTime { get; set; } } internal static class MiniMessageSerializer { public const string HEADER = "SNLM"; public static byte[] SerializeMessage(MiniP2PMessage message) { try { byte[] bytes = Encoding.UTF8.GetBytes(message.SerializeJson()); byte[] bytes2 = Encoding.UTF8.GetBytes(message.MessageType); byte[] bytes3 = Encoding.UTF8.GetBytes("SNLM"); if (bytes2.Length > 255) { throw new Exception($"MiniMessageSerializer: Message type too long: {bytes2.Length}"); } int num = bytes3.Length + 1 + bytes2.Length + bytes.Length; byte[] array = new byte[num]; int num2 = 0; Array.Copy(bytes3, 0, array, num2, bytes3.Length); num2 += bytes3.Length; array[num2++] = (byte)bytes2.Length; Array.Copy(bytes2, 0, array, num2, bytes2.Length); num2 += bytes2.Length; Array.Copy(bytes, 0, array, num2, bytes.Length); return array; } catch (Exception innerException) { throw new Exception("MiniMessageSerializer: SerializeMessage failed for " + message.MessageType, innerException); } } public static bool IsValidMessage(byte[] data) { if (data == null || data.Length < 6) { return false; } byte[] bytes = Encoding.UTF8.GetBytes("SNLM"); for (int i = 0; i < bytes.Length; i++) { if (data[i] != bytes[i]) { return false; } } int num = data[bytes.Length]; return bytes.Length + 1 + num <= data.Length; } public static string? GetMessageType(byte[] data) { if (!IsValidMessage(data)) { return null; } try { byte[] bytes = Encoding.UTF8.GetBytes("SNLM"); int num = bytes.Length; int num2 = data[num++]; if (num + num2 > data.Length) { return null; } return Encoding.UTF8.GetString(data, num, num2); } catch (Exception) { return null; } } public static T CreateMessage(byte[] data) where T : MiniP2PMessage, new() { if (!IsValidMessage(data)) { throw new Exception("MiniMessageSerializer: Invalid message format in CreateMessage"); } byte[] bytes = Encoding.UTF8.GetBytes("SNLM"); int num = bytes.Length; int num2 = data[num++]; num += num2; int num3 = data.Length - num; if (num3 < 0) { throw new Exception("MiniMessageSerializer: Invalid payload length"); } string @string; try { @string = Encoding.UTF8.GetString(data, num, num3); } catch (Exception innerException) { throw new Exception("MiniMessageSerializer: UTF8 decoding failed for payload", innerException); } T val = new T(); string messageType = GetMessageType(data); if (val.MessageType != messageType) { throw new Exception("MiniMessageSerializer: type mismatch expected " + val.MessageType + ", got " + messageType); } val.DeserializeJson(@string); return val; } } internal abstract class MiniP2PMessage { public abstract string MessageType { get; } public abstract string SerializeJson(); public abstract void DeserializeJson(string json); protected static string Escape(string s) { return (s ?? string.Empty).Replace("\\", "\\\\").Replace("\"", "\\\"").Replace("\n", "\\n"); } protected static string Extract(string json, string key) { try { string text = "\"" + key + "\":"; int num = json.IndexOf(text, StringComparison.Ordinal); if (num < 0) { return string.Empty; } for (num += text.Length; num < json.Length && char.IsWhiteSpace(json[num]); num++) { } if (num >= json.Length) { return string.Empty; } if (json[num] == '"') { num++; int i = num; bool flag = false; for (; i < json.Length; i++) { char c = json[i]; if (c == '\\' && !flag) { flag = true; continue; } if (c == '"' && !flag) { break; } flag = false; } if (i >= json.Length) { return string.Empty; } string text2 = json.Substring(num, i - num); return text2.Replace("\\\"", "\"").Replace("\\\\", "\\").Replace("\\n", "\n"); } int j; for (j = num; j < json.Length && json[j] != ',' && json[j] != '}' && !char.IsWhiteSpace(json[j]); j++) { } return json.Substring(num, j - num); } catch { } return string.Empty; } } internal sealed class FuelUpdateMessage : MiniP2PMessage { internal struct Item { public string VehicleGuid; public float FuelLevel; public float MaxCapacity; } public const string TYPE = "FUEL_UPDATE"; public string VehicleGuid = string.Empty; public float FuelLevel; public float MaxCapacity; public override string MessageType => "FUEL_UPDATE"; public override string SerializeJson() { return "{\"VehicleGuid\":\"" + MiniP2PMessage.Escape(VehicleGuid) + "\",\"FuelLevel\":" + FuelLevel.ToString(CultureInfo.InvariantCulture) + ",\"MaxCapacity\":" + MaxCapacity.ToString(CultureInfo.InvariantCulture) + "}"; } public override void DeserializeJson(string json) { VehicleGuid = MiniP2PMessage.Extract(json, "VehicleGuid"); float.TryParse(MiniP2PMessage.Extract(json, "FuelLevel"), NumberStyles.Float, CultureInfo.InvariantCulture, out FuelLevel); float.TryParse(MiniP2PMessage.Extract(json, "MaxCapacity"), NumberStyles.Float, CultureInfo.InvariantCulture, out MaxCapacity); } } internal sealed class FuelSnapshotMessage : MiniP2PMessage { public const string TYPE = "FUEL_SNAPSHOT"; public FuelUpdateMessage.Item[] Items = Array.Empty(); public override string MessageType => "FUEL_SNAPSHOT"; public override string SerializeJson() { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.Append("{\"Items\":["); for (int i = 0; i < Items.Length; i++) { FuelUpdateMessage.Item item = Items[i]; stringBuilder.Append("{"); stringBuilder.Append("\"VehicleGuid\":\"").Append(MiniP2PMessage.Escape(item.VehicleGuid)).Append("\","); stringBuilder.Append("\"FuelLevel\":").Append(item.FuelLevel.ToString(CultureInfo.InvariantCulture)).Append(","); stringBuilder.Append("\"MaxCapacity\":").Append(item.MaxCapacity.ToString(CultureInfo.InvariantCulture)); stringBuilder.Append("}"); if (i < Items.Length - 1) { stringBuilder.Append(","); } } stringBuilder.Append("]}"); return stringBuilder.ToString(); } public override void DeserializeJson(string json) { int num = json.IndexOf("[", StringComparison.Ordinal); int num2 = json.LastIndexOf("]", StringComparison.Ordinal); if (num < 0 || num2 <= num) { Items = Array.Empty(); return; } string s = json.Substring(num + 1, num2 - num - 1); string[] array = SplitTopLevelObjects(s); List list = new List(array.Length); string[] array2 = array; foreach (string text in array2) { if (!string.IsNullOrWhiteSpace(text)) { FuelUpdateMessage.Item item = default(FuelUpdateMessage.Item); item.VehicleGuid = MiniP2PMessage.Extract(text, "VehicleGuid"); FuelUpdateMessage.Item item2 = item; float.TryParse(MiniP2PMessage.Extract(text, "FuelLevel"), NumberStyles.Float, CultureInfo.InvariantCulture, out item2.FuelLevel); float.TryParse(MiniP2PMessage.Extract(text, "MaxCapacity"), NumberStyles.Float, CultureInfo.InvariantCulture, out item2.MaxCapacity); list.Add(item2); } } Items = list.ToArray(); } private static string[] SplitTopLevelObjects(string s) { List list = new List(); int num = 0; int num2 = -1; for (int i = 0; i < s.Length; i++) { switch (s[i]) { case '{': if (num == 0) { num2 = i; } num++; break; case '}': num--; if (num == 0 && num2 >= 0) { list.Add(s.Substring(num2, i - num2 + 1)); num2 = -1; } break; } } return list.ToArray(); } } internal sealed class FuelSnapshotRequestMessage : MiniP2PMessage { public const string TYPE = "FUEL_SNAPSHOT_REQ"; public override string MessageType => "FUEL_SNAPSHOT_REQ"; public override string SerializeJson() { return "{}"; } public override void DeserializeJson(string json) { } } } namespace S1FuelMod.Integrations { [HarmonyPatch] public static class HarmonyPatches { private static Core? _modInstance; public static void SetModInstance(Core modInstance) { _modInstance = modInstance; } [HarmonyPatch(typeof(LandVehicle), "ApplyThrottle")] [HarmonyPrefix] public static bool LandVehicle_ApplyThrottle_Prefix(LandVehicle __instance) { try { Core? modInstance = _modInstance; if (modInstance == null || !modInstance.EnableFuelSystem) { return true; } VehicleFuelSystem vehicleFuelSystem = ((Component)__instance).GetComponent(); if ((Object)(object)vehicleFuelSystem == (Object)null) { vehicleFuelSystem = _modInstance.GetFuelSystemManager()?.AddFuelSystemToVehicle(__instance); if ((Object)(object)vehicleFuelSystem == (Object)null) { return true; } } if (vehicleFuelSystem.IsOutOfFuel) { ModLogger.FuelDebug("Vehicle " + vehicleFuelSystem.VehicleGUID.Substring(0, 8) + "... engine disabled - out of fuel"); ReflectionUtils.TrySetFieldOrProperty(__instance, "currentThrottle", 0f); ApplyCoastingBehavior(__instance); return false; } return true; } catch (Exception exception) { ModLogger.Error("Error in LandVehicle.ApplyThrottle prefix", exception); return true; } } private static void ApplyCoastingBehavior(LandVehicle vehicle) { //IL_002c: Unknown result type (might be due to invalid IL or missing references) //IL_0031: Unknown result type (might be due to invalid IL or missing references) //IL_0034: Unknown result type (might be due to invalid IL or missing references) //IL_0039: Unknown result type (might be due to invalid IL or missing references) //IL_0055: 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_0061: Unknown result type (might be due to invalid IL or missing references) try { if ((Object)(object)vehicle.Rb != (Object)null && !vehicle.Rb.isKinematic) { Vector3 velocity = vehicle.Rb.velocity; Vector3 val = -((Vector3)(ref velocity)).normalized * Mathf.Min(((Vector3)(ref velocity)).magnitude * 2f, 50f); vehicle.Rb.AddForce(val, (ForceMode)0); } foreach (Wheel wheel in vehicle.wheels) { if ((Object)(object)wheel?.wheelCollider != (Object)null) { wheel.wheelCollider.motorTorque = 0f; } } } catch (Exception exception) { ModLogger.Error("Error applying coasting behavior", exception); } } [HarmonyPatch(typeof(VehicleManager), "SpawnAndReturnVehicle")] [HarmonyPostfix] public static void VehicleManager_SpawnAndReturnVehicle_Postfix(LandVehicle __result) { try { Core? modInstance = _modInstance; if (modInstance != null && modInstance.EnableFuelSystem && !((Object)(object)__result == (Object)null)) { _modInstance.GetFuelSystemManager()?.AddFuelSystemToVehicle(__result); ModLogger.FuelDebug("Added fuel system to newly spawned vehicle: " + __result.VehicleName); } } catch (Exception exception) { ModLogger.Error("Error in VehicleManager.SpawnAndReturnVehicle postfix", exception); } } [HarmonyPatch(typeof(Player), "EnterVehicle")] [HarmonyPostfix] public static void Player_EnterVehicle_Postfix(Player __instance, LandVehicle vehicle) { try { Core? modInstance = _modInstance; if (modInstance != null && modInstance.ShowFuelGauge && !((Object)(object)vehicle == (Object)null) && !((Object)(object)__instance != (Object)(object)Player.Local)) { ModLogger.UIDebug("Player entered vehicle: " + vehicle.VehicleName); } } catch (Exception exception) { ModLogger.Error("Error in Player.EnterVehicle postfix", exception); } } [HarmonyPatch(typeof(Player), "ExitVehicle")] [HarmonyPostfix] public static void Player_ExitVehicle_Postfix(Player __instance, Transform exitPoint) { try { Core? modInstance = _modInstance; if (modInstance != null && modInstance.ShowFuelGauge && !((Object)(object)__instance != (Object)(object)Player.Local)) { ModLogger.UIDebug("Player exited vehicle"); } } catch (Exception exception) { ModLogger.Error("Error in Player.ExitVehicle postfix", exception); } } [HarmonyPatch(typeof(VehicleManager), "GetSaveString")] [HarmonyPostfix] public static void VehicleManager_GetSaveString_Postfix(VehicleManager __instance, ref string __result) { //IL_00fc: Unknown result type (might be due to invalid IL or missing references) //IL_0102: Invalid comparison between Unknown and I4 //IL_0079: Unknown result type (might be due to invalid IL or missing references) //IL_007f: Invalid comparison between Unknown and I4 try { Core? modInstance = _modInstance; if (modInstance == null || !modInstance.EnableFuelSystem || (Object)(object)__instance == (Object)null || string.IsNullOrEmpty(__result)) { return; } JObject val = JObject.Parse(__result); JToken val2 = default(JToken); if (!val.TryGetValue("Vehicles", ref val2)) { return; } JArray val3 = null; bool flag = false; try { JArray val4 = (JArray)(object)((val2 is JArray) ? val2 : null); if (val4 != null) { val3 = val4; } else if ((int)val2.Type == 2) { val3 = (JArray)(object)((val2 is JArray) ? val2 : null); } } catch (Exception ex) { ModLogger.Error("GetSaveString: Error parsing vehicles array: " + ex.Message); return; } if (val3 == null) { return; } bool flag2 = false; JToken val8 = default(JToken); for (int i = 0; i < ((JContainer)val3).Count; i++) { JToken val5 = val3[i]; JObject val6 = null; bool flag3 = false; try { JObject val7 = (JObject)(object)((val5 is JObject) ? val5 : null); if (val7 != null) { val6 = val7; } else if ((int)val5.Type == 1) { val6 = (JObject)(object)((val5 is JObject) ? val5 : null); } if (val6 == null) { continue; } } catch (Exception ex2) { ModLogger.Error($"GetSaveString: Error parsing vehicle {i}: {ex2.Message}"); continue; } string text = (val6.TryGetValue("GUID", ref val8) ? ((string)val8) : null); if (string.IsNullOrEmpty(text)) { continue; } LandVehicle val9 = FindVehicleByGuid(text); if (!((Object)(object)val9 == (Object)null)) { VehicleFuelSystem component = ((Component)val9).GetComponent(); float num; if ((Object)(object)component != (Object)null) { FuelData fuelData = component.GetFuelData(); num = fuelData.CurrentFuelLevel; } else { num = _modInstance.DefaultFuelCapacity * 0.75f; } val6["DataType"] = JToken.op_Implicit("FuelVehicleData"); val6["CurrentFuelLevel"] = JToken.op_Implicit(num); val6["FuelDataVersion"] = JToken.op_Implicit(1); flag2 = true; ModLogger.FuelDebug($"GetSaveString: Injected fuel data for vehicle {text.Substring(0, 8)}... - {num:F1}L"); } } if (flag2) { __result = ((JToken)val).ToString((Formatting)1, Array.Empty()); } } catch (Exception exception) { ModLogger.Error("Error in VehicleManager.GetSaveString postfix", exception); } } private static LandVehicle? FindVehicleByGuid(string guid) { try { if (string.IsNullOrEmpty(guid)) { return null; } if (NetworkSingleton.InstanceExists) { VehicleManager instance = NetworkSingleton.Instance; foreach (LandVehicle allVehicle in instance.AllVehicles) { if ((Object)(object)allVehicle != (Object)null && allVehicle.GUID.ToString() == guid) { return allVehicle; } } } LandVehicle[] array = Object.FindObjectsOfType(); LandVehicle[] array2 = array; foreach (LandVehicle val in array2) { if ((Object)(object)val != (Object)null && val.GUID.ToString() == guid) { return val; } } return null; } catch (Exception exception) { ModLogger.Error("Error finding vehicle by GUID " + guid, exception); return null; } } [HarmonyPatch(typeof(LandVehicle), "Load")] [HarmonyPostfix] public static void LandVehicle_Load_Postfix(LandVehicle __instance, VehicleData data, string containerPath) { try { Core? modInstance = _modInstance; if (modInstance == null || !modInstance.EnableFuelSystem || (Object)(object)__instance == (Object)null) { return; } VehicleFuelSystem vehicleFuelSystem = _modInstance.GetFuelSystemManager()?.AddFuelSystemToVehicle(__instance); if ((Object)(object)vehicleFuelSystem == (Object)null) { return; } ModLogger.FuelDebug("LandVehicle_Load_Postfix: Loading fuel data for vehicle " + __instance.GUID.ToString().Substring(0, 8) + "..."); FuelData fuelData = FuelVehicleData.TryGetFuelData(data); if (fuelData != null) { vehicleFuelSystem.LoadFuelData(fuelData); ModLogger.FuelDebug($"Applied saved fuel data: {fuelData.CurrentFuelLevel:F1}L/{fuelData.MaxFuelCapacity:F1}L"); return; } ModLogger.FuelDebug("LandVehicle_Load_Postfix: No fuel data found in VehicleData for " + __instance.GUID.ToString().Substring(0, 8) + "..."); if (vehicleFuelSystem.CurrentFuelLevel == vehicleFuelSystem.MaxFuelCapacity) { ModLogger.FuelDebug("LandVehicle_Load_Postfix: Vehicle at max fuel, will apply random level after VehiclesLoader processes"); } } catch (Exception exception) { ModLogger.Error("Error in LandVehicle.Load postfix", exception); } } [HarmonyPatch(typeof(VehiclesLoader), "Load")] [HarmonyPostfix] public static void VehiclesLoader_Load_Postfix(string mainPath) { //IL_037c: Unknown result type (might be due to invalid IL or missing references) //IL_0382: Invalid comparison between Unknown and I4 //IL_03af: Unknown result type (might be due to invalid IL or missing references) //IL_0239: Unknown result type (might be due to invalid IL or missing references) //IL_023f: Invalid comparison between Unknown and I4 //IL_0265: Unknown result type (might be due to invalid IL or missing references) try { Core? modInstance = _modInstance; if (modInstance == null || !modInstance.EnableFuelSystem) { return; } ModLogger.FuelDebug("VehiclesLoader_Load_Postfix: Processing " + mainPath); ModLogger.FuelDebug("VehiclesLoader_Load_Postfix: Running on Mono"); string text = (File.Exists(mainPath) ? mainPath : (mainPath + ".json")); ModLogger.FuelDebug($"VehiclesLoader_Load_Postfix: Checking mainPath exists: {File.Exists(mainPath)}"); ModLogger.FuelDebug($"VehiclesLoader_Load_Postfix: Checking jsonPath exists: {File.Exists(text)}"); ModLogger.FuelDebug("VehiclesLoader_Load_Postfix: Final jsonPath: " + text); if (!File.Exists(text)) { ModLogger.FuelDebug("VehiclesLoader_Load_Postfix: JSON file not found: " + text); if (!Directory.Exists(mainPath)) { return; } string[] files = Directory.GetFiles(mainPath, "*.json"); ModLogger.FuelDebug("VehiclesLoader_Load_Postfix: Directory exists, JSON files found: " + string.Join(", ", files)); string[] array = files; foreach (string text2 in array) { try { string text3 = File.ReadAllText(text2); if (text3.Contains("\"Vehicles\"") && text3.Contains("FuelVehicleData")) { ModLogger.FuelDebug("VehiclesLoader_Load_Postfix: Found potential fuel data file: " + text2); text = text2; break; } } catch (Exception ex) { ModLogger.FuelDebug("VehiclesLoader_Load_Postfix: Error reading file " + text2 + ": " + ex.Message); } } if (!File.Exists(text)) { ModLogger.FuelDebug("VehiclesLoader_Load_Postfix: No suitable JSON file found in directory"); return; } } string text4 = File.ReadAllText(text); ModLogger.FuelDebug("VehiclesLoader_Load_Postfix: File contents (first 500 chars): " + text4.Substring(0, Math.Min(500, text4.Length))); JObject val = JObject.Parse(text4); JToken val2 = default(JToken); if (!val.TryGetValue("Vehicles", ref val2)) { ModLogger.FuelDebug("VehiclesLoader_Load_Postfix: No Vehicles property found in JSON"); return; } JArray val3 = null; try { JArray val4 = (JArray)(object)((val2 is JArray) ? val2 : null); if (val4 != null) { val3 = val4; ModLogger.FuelDebug("VehiclesLoader_Load_Postfix: Got vehicles as direct JArray"); } else { if ((int)val2.Type != 2) { ModLogger.FuelDebug($"VehiclesLoader_Load_Postfix: Vehicles token is not an array. Type: {val2.Type}, Value: {val2}"); return; } val3 = (JArray)(object)((val2 is JArray) ? val2 : null); ModLogger.FuelDebug("VehiclesLoader_Load_Postfix: Converted vehicles token to JArray"); } } catch (Exception ex2) { ModLogger.Error("VehiclesLoader_Load_Postfix: Error parsing vehicles array: " + ex2.Message); return; } if (val3 == null) { ModLogger.FuelDebug("VehiclesLoader_Load_Postfix: Failed to get vehicles array"); return; } ModLogger.FuelDebug($"VehiclesLoader_Load_Postfix: Found {((JContainer)val3).Count} vehicles in JSON"); List list = new List(); if (NetworkSingleton.InstanceExists) { foreach (LandVehicle allVehicle in NetworkSingleton.Instance.AllVehicles) { if ((Object)(object)allVehicle != (Object)null) { list.Add(allVehicle); } } } JToken val8 = default(JToken); JToken val9 = default(JToken); for (int j = 0; j < ((JContainer)val3).Count; j++) { JToken val5 = val3[j]; JObject val6 = null; try { JObject val7 = (JObject)(object)((val5 is JObject) ? val5 : null); if (val7 != null) { val6 = val7; } else if ((int)val5.Type == 1) { val6 = (JObject)(object)((val5 is JObject) ? val5 : null); } if (val6 == null) { ModLogger.FuelDebug($"VehiclesLoader_Load_Postfix: Vehicle {j} is not a JObject. Type: {val5.Type}"); continue; } } catch (Exception ex3) { ModLogger.Error($"VehiclesLoader_Load_Postfix: Error parsing vehicle {j}: {ex3.Message}"); continue; } string guid = (val6.TryGetValue("GUID", ref val8) ? (((string)val8) ?? string.Empty) : string.Empty); if (string.IsNullOrEmpty(guid)) { ModLogger.FuelDebug($"VehiclesLoader_Load_Postfix: Vehicle {j} has no GUID"); continue; } ModLogger.FuelDebug("VehiclesLoader_Load_Postfix: Processing vehicle " + guid.Substring(0, 8) + "..."); if (!val6.TryGetValue("CurrentFuelLevel", ref val9)) { ModLogger.FuelDebug("VehiclesLoader_Load_Postfix: Vehicle " + guid.Substring(0, 8) + "... has no CurrentFuelLevel field"); continue; } float num = (float)val9; ModLogger.FuelDebug($"VehiclesLoader_Load_Postfix: Vehicle {guid.Substring(0, 8)}... found fuel data: {num:F1}L"); LandVehicle val10 = FindVehicleByGuid(guid); if ((Object)(object)val10 == (Object)null) { ModLogger.FuelDebug("VehiclesLoader_Load_Postfix: Vehicle " + guid.Substring(0, 8) + "... not found in scene"); continue; } VehicleFuelSystem vehicleFuelSystem = _modInstance.GetFuelSystemManager()?.AddFuelSystemToVehicle(val10); if ((Object)(object)vehicleFuelSystem == (Object)null) { ModLogger.FuelDebug("VehiclesLoader_Load_Postfix: Vehicle " + guid.Substring(0, 8) + "... could not get fuel system"); continue; } vehicleFuelSystem.SetMaxFuelCapacity(); vehicleFuelSystem.SetFuelLevel(num); ModLogger.FuelDebug($"VehiclesLoader_Load_Postfix: Applied saved fuel to {guid.Substring(0, 8)}... {num:F1}"); list.RemoveAll((LandVehicle v) => v.GUID.ToString() == guid); } foreach (LandVehicle item in list) { VehicleFuelSystem vehicleFuelSystem2 = _modInstance.GetFuelSystemManager()?.GetFuelSystem(item); if ((Object)(object)vehicleFuelSystem2 != (Object)null && vehicleFuelSystem2.CurrentFuelLevel == vehicleFuelSystem2.MaxFuelCapacity) { vehicleFuelSystem2.SetFuelLevel(vehicleFuelSystem2.MaxFuelCapacity); ModLogger.FuelDebug($"VehiclesLoader_Load_Postfix: No saved fuel data for {item.GUID.ToString().Substring(0, 8)}... - set max fuel level: {vehicleFuelSystem2.MaxFuelCapacity:F1}L"); } } } catch (Exception exception) { ModLogger.Error("Error in VehiclesLoader.Load postfix", exception); } } [HarmonyPatch(typeof(LandVehicle), "Update")] [HarmonyPostfix] public static void LandVehicle_Update_Postfix(LandVehicle __instance) { try { Core? modInstance = _modInstance; if (modInstance != null && modInstance.EnableFuelSystem && !((Object)(object)__instance == (Object)null)) { VehicleFuelSystem component = ((Component)__instance).GetComponent(); if (!((Object)(object)component == (Object)null)) { ApplyFuelPerformanceEffects(__instance, component); } } } catch (Exception exception) { ModLogger.Error("Error in LandVehicle.Update postfix", exception); } } [HarmonyPatch(typeof(Marco), "RecoverVehicle")] [HarmonyPostfix] public static void Marco_RecoverVehicle_Postfix() { try { Core? modInstance = _modInstance; if (modInstance != null && modInstance.EnableFuelSystem) { _modInstance.GetFuelSystemManager()?.GetFuelSystem(Player.Local.LastDrivenVehicle).FullTank(); ModLogger.FuelDebug(((Object)Player.Local.LastDrivenVehicle).name + " refueled due to tow"); } } catch (Exception exception) { ModLogger.Error("Error in Marco.RecoverVehicle postfix", exception); } } private static void ApplyFuelPerformanceEffects(LandVehicle vehicle, VehicleFuelSystem fuelSystem) { try { if (fuelSystem.CurrentFuelLevel <= 4f && fuelSystem.CurrentFuelLevel > 0f && Random.Range(0f, 1f) < 0.1f) { ReflectionUtils.TrySetFieldOrProperty(vehicle, "currentThrottle", vehicle.currentThrottle * 0.5f); } } catch (Exception exception) { ModLogger.Error("Error applying fuel performance effects", exception); } } [HarmonyPatch(typeof(Equippable), "Equip")] [HarmonyPostfix] public static void Equippable_Equip_Postfix(Equippable __instance, ItemInstance item) { try { ModLogger.Debug("Equippable_Equip_Postfix: Item ID = " + ((item != null) ? item.ID : null) + ", Instance = " + ((__instance != null) ? ((Object)__instance).name : null)); Core? modInstance = _modInstance; if (modInstance == null || !modInstance.EnableFuelSystem || ((item == null) ? null : item.ID?.ToLower()) != "gasoline") { ModLogger.Debug("Not gasoline, returning"); return; } ModLogger.Debug("Gasoline item detected, checking for existing component..."); if ((Object)(object)((Component)__instance).GetComponent() != (Object)null) { ModLogger.Debug("Equippable_GasolineCan component already exists, skipping"); return; } Equippable_GasolineCan equippable_GasolineCan = ((Component)__instance).gameObject.AddComponent(); ModLogger.Debug("Successfully added Equippable_GasolineCan component to gasoline item"); } catch (Exception exception) { ModLogger.Error("Error adding gasoline can functionality to equippable", exception); } } [HarmonyPatch(typeof(HotbarSlot), "Equip")] [HarmonyPostfix] public static void HotbarSlot_Equip_Postfix(HotbarSlot __instance) { try { Core? modInstance = _modInstance; if (modInstance == null || !modInstance.EnableFuelSystem) { return; } ItemInstance val = ((__instance != null) ? ((ItemSlot)__instance).ItemInstance : null); Equippable val2 = __instance?.Equippable; if (val != null && !((Object)(object)val2 == (Object)null)) { ModLogger.Debug("HotbarSlot_Equip_Postfix: Item ID = " + val.ID + ", Equippable = " + ((Object)val2).name); if (!(val.ID?.ToLower() != "gasoline") && !((Object)(object)((Component)val2).GetComponent() != (Object)null)) { ((Component)val2).gameObject.AddComponent(); ModLogger.Debug("HotbarSlot_Equip_Postfix: Added Equippable_GasolineCan to equipped gasoline item"); } } } catch (Exception exception) { ModLogger.Error("Error in HotbarSlot.Equip postfix for gasoline can", exception); } } [HarmonyPatch(typeof(HotbarSlot), "SetStoredItem")] [HarmonyPostfix] public static void HotbarSlot_SetStoredItem_Postfix(HotbarSlot __instance) { try { Core? modInstance = _modInstance; if (modInstance != null && modInstance.EnableFuelSystem) { ItemInstance val = ((__instance != null) ? ((ItemSlot)__instance).ItemInstance : null); Equippable val2 = __instance?.Equippable; if (val != null && !((Object)(object)val2 == (Object)null) && !(val.ID?.ToLower() != "gasoline") && !((Object)(object)((Component)val2).GetComponent() != (Object)null)) { ((Component)val2).gameObject.AddComponent(); ModLogger.Debug("HotbarSlot_SetStoredItem_Postfix: Added Equippable_GasolineCan to equipped gasoline item"); } } } catch (Exception exception) { ModLogger.Error("Error in HotbarSlot.SetStoredItem postfix for gasoline can", exception); } } [HarmonyPatch(typeof(VehicleSound), "UpdateIdle")] [HarmonyPrefix] public static void VehicleSound_UpdateIdle_Prefix(VehicleSound __instance, ref bool engineRunning) { try { Core? modInstance = _modInstance; if (modInstance == null || !modInstance.EnableFuelSystem || (Object)(object)__instance == (Object)null) { return; } LandVehicle vehicle = __instance.Vehicle; if (!((Object)(object)vehicle == (Object)null)) { VehicleFuelSystem component = ((Component)vehicle).GetComponent(); if ((Object)(object)component != (Object)null && component.IsOutOfFuel) { engineRunning = false; } } } catch (Exception exception) { ModLogger.Error("Error in VehicleSound.UpdateIdle prefix", exception); } } [HarmonyPatch(typeof(VehicleSound), "UpdateEngineLoop")] [HarmonyPrefix] public static void VehicleSound_UpdateEngineLoop_Prefix(VehicleSound __instance, ref bool engineRunning, ref float normalizedspeed) { try { Core? modInstance = _modInstance; if (modInstance == null || !modInstance.EnableFuelSystem || (Object)(object)__instance == (Object)null) { return; } LandVehicle vehicle = __instance.Vehicle; if (!((Object)(object)vehicle == (Object)null)) { VehicleFuelSystem component = ((Component)vehicle).GetComponent(); if ((Object)(object)component != (Object)null && component.IsOutOfFuel) { engineRunning = false; normalizedspeed = 0f; } } } catch (Exception exception) { ModLogger.Error("Error in VehicleSound.UpdateEngineLoop prefix", exception); } } [HarmonyPatch(typeof(VehicleSound), "EngineStart")] [HarmonyPrefix] public static bool VehicleSound_EngineStart_Prefix(VehicleSound __instance) { try { Core? modInstance = _modInstance; if (modInstance == null || !modInstance.EnableFuelSystem || (Object)(object)__instance == (Object)null) { return true; } LandVehicle vehicle = __instance.Vehicle; if ((Object)(object)vehicle == (Object)null) { return true; } VehicleFuelSystem component = ((Component)vehicle).GetComponent(); if ((Object)(object)component != (Object)null && component.IsOutOfFuel) { if ((Object)(object)__instance.EngineStartSource != (Object)null) { __instance.EngineStartSource.volumeMultiplier = 0f; } try { MethodInfo method = ReflectionUtils.GetMethod(((object)__instance).GetType(), "StartUpdateVolume", BindingFlags.Instance | BindingFlags.NonPublic); if (method != null) { method.Invoke(__instance, null); } } catch (Exception exception) { ModLogger.Error("Error invoking StartUpdateVolume", exception); } ModLogger.FuelDebug("VehicleSound: Skipped engine start sound - out of fuel"); return false; } return true; } catch (Exception exception2) { ModLogger.Error("Error in VehicleSound.EngineStart prefix", exception2); return true; } } } }