Some mods target the Mono version of the game, which is available by opting into the Steam beta branch "alternate"
Decompiled source of HireableDeliveryDriver v1.0.0
Mods/HireableDeliveryDriver.dll
Decompiled 5 days agousing System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Reflection; using System.Resources; using System.Runtime.CompilerServices; using System.Runtime.Versioning; using System.Security; using System.Security.Permissions; using FishNet; using FishNet.Connection; using HireableDeliveryDriver; using MelonLoader; using Microsoft.CodeAnalysis; using ScheduleOne.Delivery; using ScheduleOne.DevUtilities; using ScheduleOne.GameTime; using ScheduleOne.ItemFramework; using ScheduleOne.Map; using ScheduleOne.Money; using ScheduleOne.Property; using ScheduleOne.Storage; using ScheduleOne.UI.Shop; using ScheduleOne.Vehicles; using UnityEngine; [assembly: CompilationRelaxations(8)] [assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)] [assembly: Debuggable(DebuggableAttribute.DebuggingModes.Default | DebuggableAttribute.DebuggingModes.DisableOptimizations | DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints | DebuggableAttribute.DebuggingModes.EnableEditAndContinue)] [assembly: MelonInfo(typeof(DeliveryDriverMod), "Hireable Delivery Driver", "1.0.0", "You", null)] [assembly: TargetFramework(".NETStandard,Version=v2.1", FrameworkDisplayName = ".NET Standard 2.1")] [assembly: AssemblyCompany("HireableDeliveryDriver")] [assembly: AssemblyConfiguration("Debug")] [assembly: AssemblyFileVersion("1.0.0.0")] [assembly: AssemblyInformationalVersion("1.0.0+5b8bfea65b08fe9810ef21cd116fad8f5be354cf")] [assembly: AssemblyProduct("HireableDeliveryDriver")] [assembly: AssemblyTitle("HireableDeliveryDriver")] [assembly: NeutralResourcesLanguage("en-US")] [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.Module, AllowMultiple = false, Inherited = false)] internal sealed class RefSafetyRulesAttribute : Attribute { public readonly int Version; public RefSafetyRulesAttribute(int P_0) { Version = P_0; } } } namespace HireableDeliveryDriver { public sealed class DeliveryDriverMod : MelonMod { private const KeyCode OPEN_UI_KEY = 290; private const float CHECK_INTERVAL_SECONDS = 2f; public const float SIGNING_FEE = 500f; public const float DAILY_WAGE = 100f; private const int DEFAULT_FILL_THRESHOLD = 80; private const int TRAVEL_TIME_MINUTES = 5; private const int EMPTY_DELAY_MINUTES = 2; private DeliveryDriverManager _driverManager; private float _nextCheckTime; private bool _uiOpen; private Rect _windowRect = new Rect(100f, 100f, 600f, 500f); private Vector2 _scrollPosition; private CursorLockMode _previousCursorLockState; private bool _previousCursorVisible; private int _selectedSourcePropertyIndex; private int _selectedSourceDockIndex; private int _selectedDestPropertyIndex; private int _selectedDestDockIndex; private int _fillThreshold = 80; private List<Property> _cachedProperties; private string _deliveryVehicleCode; private bool _vehicleCodeSearched; public override void OnInitializeMelon() { _driverManager = new DeliveryDriverManager(); _nextCheckTime = 0f; MelonLogger.Msg("[DeliveryDriver] Loaded. Press F9 to open delivery route management."); } public override void OnUpdate() { if (Input.GetKeyDown((KeyCode)290)) { ToggleUI(); } if (Time.unscaledTime >= _nextCheckTime) { _nextCheckTime = Time.unscaledTime + 2f; ProcessDeliveryRoutes(); } } private void ToggleUI() { //IL_004b: 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_0021: Unknown result type (might be due to invalid IL or missing references) _uiOpen = !_uiOpen; if (_uiOpen) { _previousCursorLockState = Cursor.lockState; _previousCursorVisible = Cursor.visible; Cursor.lockState = (CursorLockMode)0; Cursor.visible = true; RefreshPropertyCache(); } else { Cursor.lockState = _previousCursorLockState; Cursor.visible = _previousCursorVisible; } } public override void OnGUI() { //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_0068: Expected O, but got Unknown //IL_0063: Unknown result type (might be due to invalid IL or missing references) //IL_0068: Unknown result type (might be due to invalid IL or missing references) if (_uiOpen) { GUI.skin.window.fontSize = 14; GUI.skin.label.fontSize = 13; GUI.skin.button.fontSize = 13; _windowRect = GUI.Window(98765, _windowRect, new WindowFunction(DrawWindow), "Delivery Driver Management (F9 to close)"); } } private void DrawWindow(int windowId) { //IL_01c4: Unknown result type (might be due to invalid IL or missing references) //IL_01dc: Unknown result type (might be due to invalid IL or missing references) //IL_01e1: Unknown result type (might be due to invalid IL or missing references) //IL_0379: Unknown result type (might be due to invalid IL or missing references) GUILayout.BeginVertical(Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label($"Active Drivers: {_driverManager.Drivers.Count}", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) }); GUILayout.Label($"Daily Cost: ${_driverManager.GetTotalDailyWage():F0}", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) }); float num = 0f; if (NetworkSingleton<MoneyManager>.InstanceExists) { num = NetworkSingleton<MoneyManager>.Instance.cashBalance; } GUILayout.Label($"Your Cash: ${num:F0}", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) }); GUILayout.EndHorizontal(); GUILayout.Space(10f); GUILayout.Label("--- Hire New Driver ---", Array.Empty<GUILayoutOption>()); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label($"Signing Fee: ${500f:F0} | Daily Wage: ${100f:F0}", Array.Empty<GUILayoutOption>()); bool enabled = num >= 500f; GUI.enabled = enabled; if (GUILayout.Button("Hire Driver", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }) && NetworkSingleton<MoneyManager>.InstanceExists) { NetworkSingleton<MoneyManager>.Instance.ChangeCashBalance(-500f, true, false); _driverManager.HireDriver(); MelonLogger.Msg("[DeliveryDriver] Hired new driver!"); } GUI.enabled = true; GUILayout.EndHorizontal(); GUILayout.Space(15f); DrawNewRouteSection(); GUILayout.Space(15f); GUILayout.Label("--- Active Delivery Routes ---", Array.Empty<GUILayoutOption>()); _scrollPosition = GUILayout.BeginScrollView(_scrollPosition, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Height(200f) }); if (_driverManager.Routes.Count == 0) { GUILayout.Label("No active routes. Create one above.", Array.Empty<GUILayoutOption>()); } else { for (int i = 0; i < _driverManager.Routes.Count; i++) { DrawRouteEntry(_driverManager.Routes[i], i); } } GUILayout.EndScrollView(); GUILayout.Space(10f); GUILayout.Label($"--- Idle Drivers ({_driverManager.GetIdleDriverCount()}) ---", Array.Empty<GUILayoutOption>()); foreach (DeliveryDriver item in _driverManager.Drivers.Where((DeliveryDriver d) => d.AssignedRoute == null)) { GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label(" " + item.Name + " - Idle", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(200f) }); if (GUILayout.Button("Fire", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { _driverManager.FireDriver(item); } GUILayout.EndHorizontal(); } GUILayout.EndVertical(); GUI.DragWindow(new Rect(0f, 0f, 10000f, 20f)); } private void DrawNewRouteSection() { GUILayout.Label("--- Create New Route ---", Array.Empty<GUILayoutOption>()); if (_cachedProperties == null || _cachedProperties.Count == 0) { GUILayout.Label("No properties available. Own some properties first!", Array.Empty<GUILayoutOption>()); if (GUILayout.Button("Refresh", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(80f) })) { RefreshPropertyCache(); } return; } string[] array = _cachedProperties.Select((Property p) => p.PropertyName).ToArray(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("Source Property:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }); _selectedSourcePropertyIndex = Mathf.Clamp(_selectedSourcePropertyIndex, 0, array.Length - 1); if (GUILayout.Button("< " + array[_selectedSourcePropertyIndex] + " >", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(200f) })) { _selectedSourcePropertyIndex = (_selectedSourcePropertyIndex + 1) % array.Length; _selectedSourceDockIndex = 0; } Property val = _cachedProperties[_selectedSourcePropertyIndex]; int loadingDockCount = val.LoadingDockCount; GUILayout.Label("Dock:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) }); if (loadingDockCount > 0) { _selectedSourceDockIndex = Mathf.Clamp(_selectedSourceDockIndex, 0, loadingDockCount - 1); if (GUILayout.Button($"< {_selectedSourceDockIndex + 1} >", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { _selectedSourceDockIndex = (_selectedSourceDockIndex + 1) % loadingDockCount; } } else { GUILayout.Label("None", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("Dest Property:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }); _selectedDestPropertyIndex = Mathf.Clamp(_selectedDestPropertyIndex, 0, array.Length - 1); if (GUILayout.Button("< " + array[_selectedDestPropertyIndex] + " >", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(200f) })) { _selectedDestPropertyIndex = (_selectedDestPropertyIndex + 1) % array.Length; _selectedDestDockIndex = 0; } Property val2 = _cachedProperties[_selectedDestPropertyIndex]; int loadingDockCount2 = val2.LoadingDockCount; GUILayout.Label("Dock:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) }); if (loadingDockCount2 > 0) { _selectedDestDockIndex = Mathf.Clamp(_selectedDestDockIndex, 0, loadingDockCount2 - 1); if (GUILayout.Button($"< {_selectedDestDockIndex + 1} >", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) })) { _selectedDestDockIndex = (_selectedDestDockIndex + 1) % loadingDockCount2; } } else { GUILayout.Label("None", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(60f) }); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label("Fill Threshold:", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(120f) }); _fillThreshold = Mathf.RoundToInt(GUILayout.HorizontalSlider((float)_fillThreshold, 10f, 100f, (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(150f) })); GUILayout.Label($"{_fillThreshold}%", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) }); GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); bool flag2 = (GUI.enabled = loadingDockCount > 0 && loadingDockCount2 > 0 && (_selectedSourcePropertyIndex != _selectedDestPropertyIndex || _selectedSourceDockIndex != _selectedDestDockIndex) && _driverManager.GetIdleDriverCount() > 0); if (GUILayout.Button("Create Route & Assign Driver", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(200f) })) { CreateRoute(val, val2); } GUI.enabled = true; if (!flag2) { if (_driverManager.GetIdleDriverCount() == 0) { GUILayout.Label("(Need idle driver)", Array.Empty<GUILayoutOption>()); } else if (loadingDockCount == 0 || loadingDockCount2 == 0) { GUILayout.Label("(Properties need loading docks)", Array.Empty<GUILayoutOption>()); } else { GUILayout.Label("(Source and dest must differ)", Array.Empty<GUILayoutOption>()); } } GUILayout.EndHorizontal(); } private void CreateRoute(Property sourceProperty, Property destProperty) { DeliveryRoute deliveryRoute = new DeliveryRoute { SourcePropertyCode = sourceProperty.PropertyCode, SourceDockIndex = _selectedSourceDockIndex, DestinationPropertyCode = destProperty.PropertyCode, DestinationDockIndex = _selectedDestDockIndex, FillThresholdPercent = _fillThreshold }; DeliveryDriver deliveryDriver = _driverManager.Drivers.FirstOrDefault((DeliveryDriver d) => d.AssignedRoute == null); if (deliveryDriver != null) { deliveryRoute.AssignedDriverId = deliveryDriver.Id; deliveryDriver.AssignedRoute = deliveryRoute; _driverManager.Routes.Add(deliveryRoute); SpawnVanAtSource(deliveryRoute); MelonLogger.Msg($"[DeliveryDriver] Created route: {sourceProperty.PropertyName} Dock {_selectedSourceDockIndex + 1} -> {destProperty.PropertyName} Dock {_selectedDestDockIndex + 1}"); } } private void DrawRouteEntry(DeliveryRoute route, int index) { PropertyManager instance = Singleton<PropertyManager>.Instance; Property val = ((instance != null) ? instance.GetProperty(route.SourcePropertyCode) : null); PropertyManager instance2 = Singleton<PropertyManager>.Instance; Property val2 = ((instance2 != null) ? instance2.GetProperty(route.DestinationPropertyCode) : null); string text = ((val != null) ? val.PropertyName : null) ?? route.SourcePropertyCode; string text2 = ((val2 != null) ? val2.PropertyName : null) ?? route.DestinationPropertyCode; DeliveryDriver deliveryDriver = _driverManager.Drivers.FirstOrDefault((DeliveryDriver d) => d.Id == route.AssignedDriverId); string text3 = deliveryDriver?.Name ?? "Unassigned"; GUILayout.BeginHorizontal(Array.Empty<GUILayoutOption>()); GUILayout.Label($"{index + 1}. {text} #{route.SourceDockIndex + 1} -> {text2} #{route.DestinationDockIndex + 1}", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(300f) }); GUILayout.Label("[" + text3 + "]", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) }); GUILayout.Label($"{route.FillThresholdPercent}%", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(40f) }); GUILayout.Label(route.Status.ToString(), (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(100f) }); if (GUILayout.Button("X", (GUILayoutOption[])(object)new GUILayoutOption[1] { GUILayout.Width(25f) })) { RemoveRoute(route, deliveryDriver, index); } GUILayout.EndHorizontal(); } private void RemoveRoute(DeliveryRoute route, DeliveryDriver driver, int index) { if ((Object)(object)route.ActiveVehicle != (Object)null) { DeactivateVan(route); try { Object.Destroy((Object)(object)((Component)route.ActiveVehicle).gameObject); } catch (Exception ex) { MelonLogger.Warning("[DeliveryDriver] Error destroying vehicle: " + ex.Message); } route.ActiveVehicle = null; } if (driver != null) { driver.AssignedRoute = null; } _driverManager.Routes.RemoveAt(index); MelonLogger.Msg($"[DeliveryDriver] Removed route {index + 1}"); } private void RefreshPropertyCache() { _cachedProperties = new List<Property>(); if (Property.OwnedProperties != null) { foreach (Property ownedProperty in Property.OwnedProperties) { if ((Object)(object)ownedProperty != (Object)null && ownedProperty.LoadingDockCount > 0) { _cachedProperties.Add(ownedProperty); } } } MelonLogger.Msg($"[DeliveryDriver] Found {_cachedProperties.Count} properties with loading docks"); } private string GetDeliveryVehicleCode() { if (!string.IsNullOrEmpty(_deliveryVehicleCode)) { return _deliveryVehicleCode; } if (_vehicleCodeSearched) { return null; } _vehicleCodeSearched = true; if (ShopInterface.AllShops != null) { foreach (ShopInterface allShop in ShopInterface.AllShops) { object obj; if (allShop == null) { obj = null; } else { DeliveryVehicle deliveryVehicle = allShop.DeliveryVehicle; obj = ((deliveryVehicle != null) ? deliveryVehicle.Vehicle : null); } if ((Object)obj != (Object)null) { _deliveryVehicleCode = allShop.DeliveryVehicle.Vehicle.VehicleCode; MelonLogger.Msg("[DeliveryDriver] Found delivery vehicle code from " + allShop.ShopName + ": " + _deliveryVehicleCode); return _deliveryVehicleCode; } } } if (NetworkSingleton<VehicleManager>.InstanceExists) { VehicleManager instance = NetworkSingleton<VehicleManager>.Instance; foreach (LandVehicle vehiclePrefab in instance.VehiclePrefabs) { if ((Object)(object)vehiclePrefab != (Object)null) { string text = vehiclePrefab.VehicleCode.ToLower(); if (text.Contains("van") || text.Contains("cargo") || text.Contains("delivery")) { _deliveryVehicleCode = vehiclePrefab.VehicleCode; MelonLogger.Msg("[DeliveryDriver] Found van vehicle code from prefabs: " + _deliveryVehicleCode); return _deliveryVehicleCode; } } } if (instance.VehiclePrefabs.Count > 0 && (Object)(object)instance.VehiclePrefabs[0] != (Object)null) { _deliveryVehicleCode = instance.VehiclePrefabs[0].VehicleCode; MelonLogger.Warning("[DeliveryDriver] Using fallback vehicle code: " + _deliveryVehicleCode); return _deliveryVehicleCode; } } MelonLogger.Error("[DeliveryDriver] Could not find any vehicle code!"); return null; } private void SpawnVanAtSource(DeliveryRoute route) { //IL_030b: Unknown result type (might be due to invalid IL or missing references) //IL_01a6: Unknown result type (might be due to invalid IL or missing references) //IL_01ab: Unknown result type (might be due to invalid IL or missing references) //IL_01b4: Unknown result type (might be due to invalid IL or missing references) //IL_01b9: Unknown result type (might be due to invalid IL or missing references) //IL_01c0: Unknown result type (might be due to invalid IL or missing references) //IL_01c9: Unknown result type (might be due to invalid IL or missing references) //IL_0213: Unknown result type (might be due to invalid IL or missing references) //IL_0215: 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_029f: Unknown result type (might be due to invalid IL or missing references) //IL_02ab: Unknown result type (might be due to invalid IL or missing references) //IL_02b2: Unknown result type (might be due to invalid IL or missing references) //IL_02b5: Unknown result type (might be due to invalid IL or missing references) //IL_02ba: Unknown result type (might be due to invalid IL or missing references) //IL_02c5: Expected O, but got Unknown MelonLogger.Msg("[DeliveryDriver] Attempting to spawn van for route..."); string deliveryVehicleCode = GetDeliveryVehicleCode(); if (string.IsNullOrEmpty(deliveryVehicleCode)) { MelonLogger.Error("[DeliveryDriver] Cannot spawn van - no vehicle code available!"); route.Status = RouteStatus.Error; return; } PropertyManager instance = Singleton<PropertyManager>.Instance; Property val = ((instance != null) ? instance.GetProperty(route.SourcePropertyCode) : null); if ((Object)(object)val == (Object)null) { MelonLogger.Error("[DeliveryDriver] Cannot find source property: " + route.SourcePropertyCode); route.Status = RouteStatus.Error; return; } if (route.SourceDockIndex >= val.LoadingDockCount) { MelonLogger.Error($"[DeliveryDriver] Invalid source dock index: {route.SourceDockIndex}"); route.Status = RouteStatus.Error; return; } LoadingDock val2 = val.LoadingDocks[route.SourceDockIndex]; if ((Object)(object)val2 == (Object)null) { MelonLogger.Error("[DeliveryDriver] Source dock is null!"); route.Status = RouteStatus.Error; return; } if (val2.IsInUse) { MelonLogger.Warning($"[DeliveryDriver] Source dock {route.SourceDockIndex + 1} is in use, waiting..."); route.Status = RouteStatus.WaitingForDock; return; } ParkingLot parking = val2.Parking; if ((Object)(object)parking == (Object)null) { MelonLogger.Error("[DeliveryDriver] Source dock has no parking lot!"); route.Status = RouteStatus.Error; return; } if (parking.ParkingSpots == null || parking.ParkingSpots.Count == 0) { MelonLogger.Error("[DeliveryDriver] Parking lot has no spots!"); route.Status = RouteStatus.Error; return; } ParkingSpot val3 = parking.ParkingSpots[0]; Vector3 position = ((Component)val3).transform.position; Quaternion rotation = ((Component)val3).transform.rotation; MelonLogger.Msg($"[DeliveryDriver] Spawn position: {position}, Rotation: {((Quaternion)(ref rotation)).eulerAngles}"); if (!NetworkSingleton<VehicleManager>.InstanceExists) { MelonLogger.Error("[DeliveryDriver] VehicleManager not available!"); route.Status = RouteStatus.Error; return; } VehicleManager instance2 = NetworkSingleton<VehicleManager>.Instance; LandVehicle val4 = null; try { val4 = instance2.SpawnAndReturnVehicle(deliveryVehicleCode, position, rotation, false); } catch (Exception ex) { MelonLogger.Error("[DeliveryDriver] Exception spawning vehicle: " + ex.Message); route.Status = RouteStatus.Error; return; } if ((Object)(object)val4 == (Object)null) { MelonLogger.Error("[DeliveryDriver] SpawnAndReturnVehicle returned null for code: " + deliveryVehicleCode); route.Status = RouteStatus.Error; return; } MelonLogger.Msg("[DeliveryDriver] Successfully spawned vehicle: " + val4.VehicleName); val2.SetStaticOccupant(val4); try { val4.Park((NetworkConnection)null, new ParkData { lotGUID = parking.GUID, spotIndex = 0, alignment = val3.Alignment }, false); } catch (Exception ex2) { MelonLogger.Warning("[DeliveryDriver] Error parking vehicle: " + ex2.Message); } val4.SetVisible(true); if ((Object)(object)val4.Storage != (Object)null) { val4.Storage.AccessSettings = (EAccessSettings)2; MelonLogger.Msg($"[DeliveryDriver] Vehicle storage has {val4.Storage.ItemSlots.Count} slots"); } else { MelonLogger.Warning("[DeliveryDriver] Vehicle has no storage!"); } StorageDoorAnimation componentInChildren = ((Component)val4).GetComponentInChildren<StorageDoorAnimation>(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.OverrideState(true); MelonLogger.Msg("[DeliveryDriver] Opened vehicle storage door"); } route.ActiveVehicle = val4; route.CurrentDock = val2; route.Status = RouteStatus.LoadingAtSource; ConfigureDockForLoading(val2, val4); MelonLogger.Msg($"[DeliveryDriver] Van ready at {val.PropertyName} Dock {route.SourceDockIndex + 1}"); } private void ConfigureDockForLoading(LoadingDock dock, LandVehicle vehicle) { if (!((Object)(object)dock == (Object)null) && !((Object)(object)vehicle?.Storage == (Object)null)) { dock.InputSlots.Clear(); dock.InputSlots.AddRange(vehicle.Storage.ItemSlots); dock.IsAcceptingItems = true; MelonLogger.Msg($"[DeliveryDriver] Configured dock for loading with {vehicle.Storage.ItemSlots.Count} input slots"); } } private void ConfigureDockForUnloading(LoadingDock dock, LandVehicle vehicle) { if (!((Object)(object)dock == (Object)null) && !((Object)(object)vehicle?.Storage == (Object)null)) { dock.OutputSlots.Clear(); dock.OutputSlots.AddRange(vehicle.Storage.ItemSlots); MelonLogger.Msg($"[DeliveryDriver] Configured dock for unloading with {vehicle.Storage.ItemSlots.Count} output slots"); } } private void ClearDockSlots(LoadingDock dock) { if (!((Object)(object)dock == (Object)null)) { dock.InputSlots.Clear(); dock.OutputSlots.Clear(); dock.IsAcceptingItems = false; } } private void EnsureDockSlotsConfigured(LoadingDock dock, LandVehicle vehicle, bool isSourceDock) { if ((Object)(object)dock == (Object)null || (Object)(object)vehicle?.Storage == (Object)null) { return; } List<ItemSlot> itemSlots = vehicle.Storage.ItemSlots; if (isSourceDock) { if (dock.InputSlots.Count != itemSlots.Count || (dock.InputSlots.Count > 0 && dock.InputSlots[0] != itemSlots[0])) { dock.InputSlots.Clear(); dock.InputSlots.AddRange(itemSlots); dock.IsAcceptingItems = true; } } else if (dock.OutputSlots.Count != itemSlots.Count || (dock.OutputSlots.Count > 0 && dock.OutputSlots[0] != itemSlots[0])) { dock.OutputSlots.Clear(); dock.OutputSlots.AddRange(itemSlots); } } private void ActivateVanAtDock(DeliveryRoute route, LoadingDock dock, bool isSourceDock) { //IL_008e: Unknown result type (might be due to invalid IL or missing references) //IL_0093: Unknown result type (might be due to invalid IL or missing references) //IL_009f: Unknown result type (might be due to invalid IL or missing references) //IL_00a6: Unknown result type (might be due to invalid IL or missing references) //IL_00a8: Unknown result type (might be due to invalid IL or missing references) //IL_00ad: Unknown result type (might be due to invalid IL or missing references) //IL_00b8: Expected O, but got Unknown //IL_00db: Unknown result type (might be due to invalid IL or missing references) if ((Object)(object)route.ActiveVehicle == (Object)null || (Object)(object)dock == (Object)null) { MelonLogger.Error("[DeliveryDriver] Cannot activate van - vehicle or dock is null"); return; } LandVehicle activeVehicle = route.ActiveVehicle; ParkingLot parking = dock.Parking; if ((Object)(object)parking == (Object)null || parking.ParkingSpots == null || parking.ParkingSpots.Count == 0) { MelonLogger.Error("[DeliveryDriver] Dock has no valid parking"); return; } ParkingSpot val = parking.ParkingSpots[0]; dock.SetStaticOccupant(activeVehicle); activeVehicle.Park((NetworkConnection)null, new ParkData { lotGUID = parking.GUID, spotIndex = 0, alignment = val.Alignment }, false); activeVehicle.SetVisible(true); if ((Object)(object)activeVehicle.Storage != (Object)null) { activeVehicle.Storage.AccessSettings = (EAccessSettings)2; } StorageDoorAnimation componentInChildren = ((Component)activeVehicle).GetComponentInChildren<StorageDoorAnimation>(); if ((Object)(object)componentInChildren != (Object)null) { componentInChildren.OverrideState(true); } route.CurrentDock = dock; if (isSourceDock) { ConfigureDockForLoading(dock, activeVehicle); } else { ConfigureDockForUnloading(dock, activeVehicle); } } private void DeactivateVan(DeliveryRoute route) { //IL_003e: 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) if ((Object)(object)route.ActiveVehicle == (Object)null) { return; } LandVehicle activeVehicle = route.ActiveVehicle; try { activeVehicle.ExitPark(false); activeVehicle.SetVisible(false); activeVehicle.SetTransform(new Vector3(0f, -100f, 0f), Quaternion.identity); } catch (Exception ex) { MelonLogger.Warning("[DeliveryDriver] Error deactivating vehicle: " + ex.Message); } if ((Object)(object)route.CurrentDock != (Object)null) { ClearDockSlots(route.CurrentDock); route.CurrentDock.SetStaticOccupant((LandVehicle)null); if ((Object)(object)route.CurrentDock.VehicleDetector != (Object)null) { route.CurrentDock.VehicleDetector.Clear(); } route.CurrentDock = null; } } private void ProcessDeliveryRoutes() { if (!InstanceFinder.IsServer) { return; } if (_driverManager.ShouldPayDailyWages()) { PayDailyWages(); } foreach (DeliveryRoute item in _driverManager.Routes.ToList()) { if (item.Status != RouteStatus.Error) { ProcessRoute(item); } } } private void PayDailyWages() { if (NetworkSingleton<MoneyManager>.InstanceExists) { float totalDailyWage = _driverManager.GetTotalDailyWage(); float cashBalance = NetworkSingleton<MoneyManager>.Instance.cashBalance; if (cashBalance >= totalDailyWage) { NetworkSingleton<MoneyManager>.Instance.ChangeCashBalance(0f - totalDailyWage, true, false); _driverManager.MarkWagesPaid(); MelonLogger.Msg($"[DeliveryDriver] Paid daily wages: ${totalDailyWage:F0}"); } else { MelonLogger.Warning($"[DeliveryDriver] Cannot pay driver wages! Need ${totalDailyWage:F0}, have ${cashBalance:F0}"); } } } private void ProcessRoute(DeliveryRoute route) { switch (route.Status) { case RouteStatus.WaitingForDock: SpawnVanAtSource(route); break; case RouteStatus.LoadingAtSource: ProcessLoadingAtSource(route); break; case RouteStatus.InTransit: ProcessInTransit(route); break; case RouteStatus.UnloadingAtDest: ProcessUnloadingAtDest(route); break; case RouteStatus.Returning: ProcessReturning(route); break; } } private void ProcessLoadingAtSource(DeliveryRoute route) { if ((Object)(object)route.ActiveVehicle == (Object)null) { MelonLogger.Warning("[DeliveryDriver] Vehicle lost during loading, respawning..."); route.Status = RouteStatus.WaitingForDock; return; } StorageEntity storage = route.ActiveVehicle.Storage; if ((Object)(object)storage == (Object)null) { MelonLogger.Warning("[DeliveryDriver] Vehicle has no storage!"); return; } if ((Object)(object)route.CurrentDock != (Object)null) { EnsureDockSlotsConfigured(route.CurrentDock, route.ActiveVehicle, isSourceDock: true); } int count = storage.ItemSlots.Count; int num = storage.ItemSlots.Count((ItemSlot s) => s.ItemInstance != null && s.Quantity > 0); if (count != 0) { float num2 = (float)num / (float)count * 100f; int fillThresholdPercent = route.FillThresholdPercent; bool flag = num2 >= (float)fillThresholdPercent; bool flag2 = num == count; if (flag || flag2) { MelonLogger.Msg($"[DeliveryDriver] Van departing from {route.SourcePropertyCode} with {num}/{count} slots filled ({num2:F0}%)"); DeactivateVan(route); route.TransitStartTime = GetCurrentGameMinutes(); route.Status = RouteStatus.InTransit; } } } private void ProcessInTransit(DeliveryRoute route) { int elapsedMinutes = GetElapsedMinutes(route.TransitStartTime); if (elapsedMinutes < 5) { return; } PropertyManager instance = Singleton<PropertyManager>.Instance; Property val = ((instance != null) ? instance.GetProperty(route.DestinationPropertyCode) : null); if ((Object)(object)val == (Object)null || route.DestinationDockIndex >= val.LoadingDockCount) { MelonLogger.Error("[DeliveryDriver] Invalid destination!"); return; } LoadingDock val2 = val.LoadingDocks[route.DestinationDockIndex]; if (!val2.IsInUse) { ActivateVanAtDock(route, val2, isSourceDock: false); route.Status = RouteStatus.UnloadingAtDest; route.EmptyCheckStartTime = -1; MelonLogger.Msg($"[DeliveryDriver] Van arrived at {val.PropertyName} Dock {route.DestinationDockIndex + 1}"); } } private void ProcessUnloadingAtDest(DeliveryRoute route) { if ((Object)(object)route.ActiveVehicle == (Object)null) { MelonLogger.Warning("[DeliveryDriver] Vehicle lost during unloading!"); route.Status = RouteStatus.WaitingForDock; return; } StorageEntity storage = route.ActiveVehicle.Storage; if ((Object)(object)storage == (Object)null) { return; } if ((Object)(object)route.CurrentDock != (Object)null) { EnsureDockSlotsConfigured(route.CurrentDock, route.ActiveVehicle, isSourceDock: false); } if (storage.ItemSlots.Count((ItemSlot s) => s.ItemInstance != null && s.Quantity > 0) == 0) { if (route.EmptyCheckStartTime < 0) { route.EmptyCheckStartTime = GetCurrentGameMinutes(); return; } int elapsedMinutes = GetElapsedMinutes(route.EmptyCheckStartTime); if (elapsedMinutes >= 2) { MelonLogger.Msg("[DeliveryDriver] Van empty at destination, returning to source"); DeactivateVan(route); route.TransitStartTime = GetCurrentGameMinutes(); route.Status = RouteStatus.Returning; } } else { route.EmptyCheckStartTime = -1; } } private void ProcessReturning(DeliveryRoute route) { int elapsedMinutes = GetElapsedMinutes(route.TransitStartTime); if (elapsedMinutes < 5) { return; } PropertyManager instance = Singleton<PropertyManager>.Instance; Property val = ((instance != null) ? instance.GetProperty(route.SourcePropertyCode) : null); if ((Object)(object)val == (Object)null || route.SourceDockIndex >= val.LoadingDockCount) { MelonLogger.Error("[DeliveryDriver] Invalid source on return!"); return; } LoadingDock val2 = val.LoadingDocks[route.SourceDockIndex]; if (!val2.IsInUse) { ActivateVanAtDock(route, val2, isSourceDock: true); route.Status = RouteStatus.LoadingAtSource; MelonLogger.Msg($"[DeliveryDriver] Van returned to {val.PropertyName} Dock {route.SourceDockIndex + 1}"); } } private int GetCurrentGameMinutes() { if (NetworkSingleton<TimeManager>.InstanceExists) { TimeManager instance = NetworkSingleton<TimeManager>.Instance; int num = instance.CurrentTime / 100; int num2 = instance.CurrentTime % 100; return instance.ElapsedDays * 24 * 60 + num * 60 + num2; } return 0; } private int GetElapsedMinutes(int startTime) { return GetCurrentGameMinutes() - startTime; } } public class DeliveryDriver { private static readonly string[] FirstNames = new string[15] { "Mike", "Dave", "Joe", "Tom", "Bill", "Sam", "Jack", "Pete", "Nick", "Steve", "Maria", "Lisa", "Kate", "Emma", "Anna" }; private static readonly string[] LastNames = new string[10] { "Smith", "Jones", "Brown", "Wilson", "Taylor", "Clark", "Hall", "Lee", "King", "Wright" }; public string Id { get; set; } public string Name { get; set; } public DeliveryRoute AssignedRoute { get; set; } public bool IsPaidToday { get; set; } public static DeliveryDriver CreateRandom() { Random random = new Random(); return new DeliveryDriver { Id = Guid.NewGuid().ToString(), Name = FirstNames[random.Next(FirstNames.Length)] + " " + LastNames[random.Next(LastNames.Length)], IsPaidToday = true }; } } public class DeliveryRoute { public string SourcePropertyCode { get; set; } public int SourceDockIndex { get; set; } public string DestinationPropertyCode { get; set; } public int DestinationDockIndex { get; set; } public int FillThresholdPercent { get; set; } = 80; public string AssignedDriverId { get; set; } public RouteStatus Status { get; set; } = RouteStatus.WaitingForDock; public int TransitStartTime { get; set; } public int EmptyCheckStartTime { get; set; } = -1; public LandVehicle ActiveVehicle { get; set; } public LoadingDock CurrentDock { get; set; } } public enum RouteStatus { WaitingForDock, LoadingAtSource, InTransit, UnloadingAtDest, Returning, Error } public class DeliveryDriverManager { private int _lastPayDay = -1; public List<DeliveryDriver> Drivers { get; } = new List<DeliveryDriver>(); public List<DeliveryRoute> Routes { get; } = new List<DeliveryRoute>(); public void HireDriver() { DeliveryDriver item = DeliveryDriver.CreateRandom(); Drivers.Add(item); } public void FireDriver(DeliveryDriver driver) { if (driver.AssignedRoute != null) { Routes.Remove(driver.AssignedRoute); } Drivers.Remove(driver); } public int GetIdleDriverCount() { return Drivers.Count((DeliveryDriver d) => d.AssignedRoute == null); } public float GetTotalDailyWage() { return (float)Drivers.Count * 100f; } public bool ShouldPayDailyWages() { if (!NetworkSingleton<TimeManager>.InstanceExists) { return false; } TimeManager instance = NetworkSingleton<TimeManager>.Instance; int elapsedDays = instance.ElapsedDays; if (elapsedDays != _lastPayDay && instance.CurrentTime >= 600) { return true; } return false; } public void MarkWagesPaid() { if (NetworkSingleton<TimeManager>.InstanceExists) { _lastPayDay = NetworkSingleton<TimeManager>.Instance.ElapsedDays; } foreach (DeliveryDriver driver in Drivers) { driver.IsPaidToday = true; } } } }