public PassengerTransportChainController GenerateNewTransportJob(TrainCarsPerLogicTrack consistInfo = null, SpecialTrain prevSpecial = null) { int nTotalCars; List <TrainCarType> jobCarTypes = null; float trainLength; Track startSiding; // Establish the starting consist and its storage location if (consistInfo == null) { // generate a consist nTotalCars = Rand.Next(MIN_CARS_EXPRESS, MAX_CARS_EXPRESS + 1); float singleCarLength = TrackOrg.GetCarTypeLength(TrainCarType.PassengerRed); trainLength = (singleCarLength * nTotalCars) + TrackOrg.GetSeparationLengthBetweenCars(nTotalCars); // pick start storage track var emptyTracks = TrackOrg.FilterOutOccupiedTracks(StorageTracks); startSiding = TrackOrg.GetTrackWithEnoughFreeSpace(emptyTracks, trainLength); if (startSiding == null) { startSiding = TrackOrg.GetTrackWithEnoughFreeSpace(StorageTracks, trainLength); } } else { // Use existing consist nTotalCars = consistInfo.cars.Count; jobCarTypes = consistInfo.cars.Select(car => car.carType).ToList(); trainLength = TrackOrg.GetTotalCarTypesLength(jobCarTypes) + TrackOrg.GetSeparationLengthBetweenCars(nTotalCars); startSiding = consistInfo.track; } if (startSiding == null) { //PassengerJobs.ModEntry.Logger.Log($"No available starting siding for express job at {Controller.stationInfo.Name}"); return(null); } // Try to find a loading platform to use PlatformDefinition loadingPlatform = PlatformManager.PickPlatform(Controller.stationInfo.YardID); // Choose a route var destPool = PassDestinations.Values.ToList(); Track destSiding = null; StationController destStation = null; // this prevents generating jobs like "ChainJob[Passenger]: FF - FF (FF-PE-47)" destPool.Remove(Controller); while ((destSiding == null) && (destPool.Count > 0)) { // search the possible destinations 1 by 1 until we find an opening (or we don't) destStation = destPool.ChooseOne(Rand); // pick ending platform PassengerJobGenerator destGenerator = LinkedGenerators[destStation]; destSiding = TrackOrg.GetTrackWithEnoughFreeSpace(destGenerator.StorageTracks, trainLength); // remove this station from the pool destPool.Remove(destStation); } if (destSiding == null) { //PassengerJobs.ModEntry.Logger.Log($"No available destination siding for new job at {Controller.stationInfo.Name}"); return(null); } // we found a route :D // if we're creating a new consist, check if it can be a special train // let's try 2/3 chance of special train, 1/3 normal gen SpecialTrain specialInfo = null; List <string> skinList = null; if (consistInfo == null) { // default 67% chance, or as configured (if there is a special available) double choice = Rand.NextDouble(); if ((choice <= PassengerJobs.Settings.NamedTrainProbability) && (SpecialConsistManager.GetTrainForRoute(Controller.stationInfo.YardID, destStation.stationInfo.YardID) is SpecialTrain special)) { specialInfo = special; IEnumerable <SpecialTrainSkin> consistSkins = special.Skins.ChooseMany(Rand, nTotalCars); jobCarTypes = consistSkins.Select(s => s.CarType).ToList(); skinList = consistSkins.Select(s => s.Name).ToList(); } else { // normal consist generation if (PassengerJobs.Settings.UniformConsists) { TrainCarType carType = PassCarTypes.ChooseOne(Rand); jobCarTypes = Enumerable.Repeat(carType, nTotalCars).ToList(); } else { jobCarTypes = PassCarTypes.ChooseMany(Rand, nTotalCars); } } } else { // extant consist, use existing special (if it exists) specialInfo = prevSpecial; } // Try to find an unloading platform PlatformDefinition unloadingPlatform = PlatformManager.PickPlatform(destStation.stationInfo.YardID); // create job chain controller var chainJobObject = new GameObject($"ChainJob[Passenger]: {Controller.logicStation.ID} - {destStation.logicStation.ID}"); chainJobObject.transform.SetParent(Controller.transform); var chainController = new PassengerTransportChainController(chainJobObject); StaticPassengerJobDefinition jobDefinition; //-------------------------------------------------------------------------------------------------------------------------------- // Create transport leg job var chainData = new StationsChainData(Controller.stationInfo.YardID, destStation.stationInfo.YardID); PaymentCalculationData transportPaymentData = GetJobPaymentData(jobCarTypes); // calculate haul payment float haulDistance = JobPaymentCalculator.GetDistanceBetweenStations(Controller, destStation); float bonusLimit = JobPaymentCalculator.CalculateHaulBonusTimeLimit(haulDistance, false); float transportPayment = JobPaymentCalculator.CalculateJobPayment(JobType.Transport, haulDistance, transportPaymentData); // calculate additional payment for shunting const float shuntDistance = 500f; PaymentCalculationData emptyPaymentData = GetJobPaymentData(jobCarTypes, true); float platformBonusTime = JobPaymentCalculator.CalculateShuntingBonusTimeLimit(1) * 0.7f + PlatformController.START_XFER_DELAY; if (loadingPlatform?.Initialized == true) { float loadPayment = JobPaymentCalculator.CalculateJobPayment(JobType.ShuntingLoad, shuntDistance, emptyPaymentData); transportPayment += loadPayment; bonusLimit += platformBonusTime; } if (unloadingPlatform?.Initialized == true) { float unloadPayment = JobPaymentCalculator.CalculateJobPayment(JobType.ShuntingUnload, shuntDistance, emptyPaymentData); transportPayment += unloadPayment; bonusLimit += platformBonusTime; } // scale job payment depending on settings float wageScale = PassengerJobs.Settings.UseCustomWages ? BASE_WAGE_SCALE : 1; transportPayment = Mathf.Round(transportPayment * wageScale); if (consistInfo == null) { jobDefinition = PopulateTransportJobAndSpawn( chainController, Controller.logicStation, startSiding, destSiding, jobCarTypes, chainData, bonusLimit, transportPayment, true, skinList); } else { chainController.trainCarsForJobChain = consistInfo.cars; jobDefinition = PopulateTransportJobExistingCars( chainController, Controller.logicStation, startSiding, destSiding, consistInfo.LogicCars, chainData, bonusLimit, transportPayment); } if (jobDefinition == null) { PassengerJobs.ModEntry.Logger.Warning($"Failed to generate transport job definition for {chainController.jobChainGO.name}"); chainController.DestroyChain(); return(null); } jobDefinition.subType = PassJobType.Express; jobDefinition.specialDefinition = specialInfo; // Setup any warehouse tasks if (loadingPlatform?.Initialized == true) { jobDefinition.loadMachine = loadingPlatform.Controller.LogicMachine; } if (unloadingPlatform?.Initialized == true) { jobDefinition.unloadMachine = unloadingPlatform.Controller.LogicMachine; } chainController.AddJobDefinitionToChain(jobDefinition); // Finalize job chainController.FinalizeSetupAndGenerateFirstJob(); PassengerJobs.ModEntry.Logger.Log($"Generated new passenger haul job: {chainJobObject.name} ({chainController.currentJobInChain.ID})"); return(chainController); }
protected override void GenerateJob(Station jobOriginStation, float jobTimeLimit = 0, float initialWage = 0, string forcedJobId = null, JobLicenses requiredLicenses = JobLicenses.Basic) { if ((trainCarsToTransport == null) || (trainCarsToTransport.Count == 0) || (startingTrack == null) || (destinationTrack == null)) { trainCarsToTransport = null; startingTrack = null; destinationTrack = null; return; } // Get total cargo capacity float totalCapacity = 0; foreach (var car in trainCarsToTransport) { //car.DumpCargo(); totalCapacity += car.capacity; } Track departTrack = startingTrack; Track arriveTrack = destinationTrack; var taskList = new List <Task>(); // Check for loading task PlatformController loadPlatform = null; if (loadMachine != null) { departTrack = loadMachine.WarehouseTrack; Task stageCarsTask = JobsGenerator.CreateTransportTask(trainCarsToTransport, departTrack, startingTrack); taskList.Add(stageCarsTask); Task loadTask = new WarehouseTask(trainCarsToTransport, WarehouseTaskType.Loading, loadMachine, CargoType.Passengers, totalCapacity); taskList.Add(loadTask); // check to register for unloading display if ((PlatformManager.GetPlatformByTrackId(loadMachine.WarehouseTrack.ID.FullID) is PlatformDefinition pdef) && pdef.Initialized) { loadPlatform = pdef.Controller; } } else { foreach (var car in trainCarsToTransport) { car.LoadCargo(car.capacity, CargoType.Passengers); } } if (unloadMachine != null) { arriveTrack = unloadMachine.WarehouseTrack; } // actual move between stations Task transportTask = JobsGenerator.CreateTransportTask( trainCarsToTransport, arriveTrack, departTrack, Enumerable.Repeat(CargoType.Passengers, trainCarsToTransport.Count).ToList()); taskList.Add(transportTask); // check for unloading task PlatformController unloadPlatform = null; if (unloadMachine != null) { Task unloadTask = new WarehouseTask(trainCarsToTransport, WarehouseTaskType.Unloading, unloadMachine, CargoType.Passengers, totalCapacity); taskList.Add(unloadTask); Task storeCarsTask = JobsGenerator.CreateTransportTask(trainCarsToTransport, destinationTrack, arriveTrack); taskList.Add(storeCarsTask); // check to register for unloading display if ((PlatformManager.GetPlatformByTrackId(unloadMachine.WarehouseTrack.ID.FullID) is PlatformDefinition pdef) && pdef.Initialized) { unloadPlatform = pdef.Controller; } } Task superTask = new SequentialTasks(taskList); // check if we should generate a special job ID if (string.IsNullOrEmpty(forcedJobId) && (specialDefinition != null)) { forcedJobId = IG_GenerateJobId_Patch.GetNamedExpressId(specialDefinition); } job = new Job(superTask, subType, jobTimeLimit, initialWage, chainData, forcedJobId, requiredLicenses); // set up platform displays if (loadPlatform != null) { loadPlatform.AddOutgoingJobToDisplay(job); } if (unloadPlatform != null) { job.JobTaken += unloadPlatform.AddIncomingJobToDisplay; } // track the job if it's a special, for booklet info etc if (specialDefinition != null) { SpecialConsistManager.JobToSpecialMap.Add(job.ID, specialDefinition); } jobOriginStation.AddJobToStation(job); }