Пример #1
0
 static bool Prefix(JobLicenses license, ref string __result)
 {
     if (license == PassLicenses.Passengers1)
     {
         __result = PassengerLicenseUtil.PASS1_LICENSE_NAME;
         return(false);
     }
     return(true);
 }
Пример #2
0
 static bool Prefix(JobLicenses license, ref bool __result)
 {
     if (license == PassLicenses.Passengers1)
     {
         __result = true;
         return(false);
     }
     return(true);
 }
Пример #3
0
 static bool Prefix(JobLicenses license, ref bool __result)
 {
     if (license == PassLicenses.Passengers1)
     {
         __result = LicenseManager.IsJobLicenseAcquired(JobLicenses.Shunting);
         return(false);
     }
     return(true);
 }
Пример #4
0
        private static JobChainControllerWithEmptyHaulGeneration GenerateTransportChainController(
            StationController startingStation,
            Track startingTrack,
            StationController destStation,
            Track destTrack,
            List <TrainCar> orderedTrainCars,
            List <CargoType> orderedCargoTypes,
            List <float> orderedCargoAmounts,
            bool forceCorrectCargoStateOnCars,
            float bonusTimeLimit,
            float initialWage,
            JobLicenses requiredLicenses)
        {
            Debug.Log(string.Format(
                          "[PersistentJobs] transport: attempting to generate ChainJob[{0}]: {1} - {2}",
                          JobType.ShuntingLoad,
                          startingStation.logicStation.ID,
                          destStation.logicStation.ID
                          ));
            GameObject gameObject = new GameObject(string.Format(
                                                       "ChainJob[{0}]: {1} - {2}",
                                                       JobType.Transport,
                                                       startingStation.logicStation.ID,
                                                       destStation.logicStation.ID
                                                       ));

            gameObject.transform.SetParent(startingStation.transform);
            JobChainControllerWithEmptyHaulGeneration jobChainController
                = new JobChainControllerWithEmptyHaulGeneration(gameObject);
            StationsChainData chainData = new StationsChainData(
                startingStation.stationInfo.YardID,
                destStation.stationInfo.YardID
                );

            jobChainController.trainCarsForJobChain = orderedTrainCars;
            List <Car> orderedLogicCars = TrainCar.ExtractLogicCars(orderedTrainCars);
            StaticTransportJobDefinition staticTransportJobDefinition
                = gameObject.AddComponent <StaticTransportJobDefinition>();

            staticTransportJobDefinition.PopulateBaseJobDefinition(
                startingStation.logicStation,
                bonusTimeLimit,
                initialWage,
                chainData,
                requiredLicenses
                );
            staticTransportJobDefinition.startingTrack                = startingTrack;
            staticTransportJobDefinition.destinationTrack             = destTrack;
            staticTransportJobDefinition.trainCarsToTransport         = orderedLogicCars;
            staticTransportJobDefinition.transportedCargoPerCar       = orderedCargoTypes;
            staticTransportJobDefinition.cargoAmountPerCar            = orderedCargoAmounts;
            staticTransportJobDefinition.forceCorrectCargoStateOnCars = forceCorrectCargoStateOnCars;
            jobChainController.AddJobDefinitionToChain(staticTransportJobDefinition);
            return(jobChainController);
        }
Пример #5
0
        static bool Prefix(JobLicenses jobLicense, ref LicenseTemplatePaperData __result)
        {
            if (jobLicense == PassLicenses.Passengers1)
            {
                // override the BookletCreator method
                __result = PassengerLicenseUtil.GetPassengerLicenseTemplate();
                return(false);
            }

            return(true);
        }
Пример #6
0
        static void Postfix()
        {
            int?savedLicenses = SaveGameManager.data.GetInt("Job_Licenses");

            if (savedLicenses.HasValue)
            {
                JobLicenses val = (JobLicenses)savedLicenses.Value;
                if (val.HasFlag(PassLicenses.Passengers1))
                {
                    PassengerJobs.ModEntry.Logger.Log("Acquiring passengers license");
                    LicenseManager.AcquireJobLicense(PassLicenses.Passengers1);
                }
            }
        }
Пример #7
0
        static bool Prefix(JobLicenses license, Vector3 position, Quaternion rotation, Transform parent)
        {
            if (license != PassLicenses.Passengers1)
            {
                return(true);
            }

            // we'll try to copy from the Hazmat 1 license prefab
            GameObject licenseObj = spawnLicenseMethod.Invoke(null,
                                                              new object[] { COPIED_PREFAB_NAME, position, rotation, true, parent }) as GameObject;

            PassengerLicenseUtil.SetLicenseObjectProperties(licenseObj, PassBookletType.Passengers1License);

            PassengerJobs.ModEntry.Logger.Log("Created Passengers 1 license");
            return(false);
        }
Пример #8
0
        static void Postfix(JobLicenses requiredLicenses, Image[] ___requiredLicenseSlots)
        {
            if (requiredLicenses.HasFlag(PassLicenses.Passengers1))
            {
                // get first non-active slot
                Image slot = ___requiredLicenseSlots.FirstOrDefault(img => !img.gameObject.activeSelf);
                if (slot == null)
                {
                    PassengerJobs.ModEntry.Logger.Warning($"Can't fit Passengers 1 license on job overview");
                    return;
                }

                if (PassengerLicenseUtil.Pass1Sprite == null)
                {
                    PassengerJobs.ModEntry.Logger.Warning($"Missing icon for {PassengerLicenseUtil.PASS1_LICENSE_NAME}");
                    return;
                }

                slot.sprite = PassengerLicenseUtil.Pass1Sprite;
                slot.gameObject.SetActive(true);
            }
        }
Пример #9
0
        private static JobChainControllerWithEmptyHaulGeneration GenerateShuntingUnloadChainController(
            StationController startingStation,
            Track startingTrack,
            WarehouseMachine unloadMachine,
            StationController destinationStation,
            List <CarsPerTrack> carsPerDestinationTrack,
            List <TrainCar> orderedTrainCars,
            List <CargoType> orderedCargoTypes,
            List <float> orderedCargoAmounts,
            bool forceCorrectCargoStateOnCars,
            float bonusTimeLimit,
            float initialWage,
            JobLicenses requiredLicenses)
        {
            Debug.Log(string.Format(
                          "[PersistentJobs] unload: attempting to generate ChainJob[{0}]: {1} - {2}",
                          JobType.ShuntingLoad,
                          startingStation.logicStation.ID,
                          destinationStation.logicStation.ID
                          ));
            GameObject gameObject = new GameObject(string.Format(
                                                       "ChainJob[{0}]: {1} - {2}",
                                                       JobType.ShuntingUnload,
                                                       startingStation.logicStation.ID,
                                                       destinationStation.logicStation.ID
                                                       ));

            gameObject.transform.SetParent(destinationStation.transform);
            JobChainControllerWithEmptyHaulGeneration jobChainController
                = new JobChainControllerWithEmptyHaulGeneration(gameObject);
            StationsChainData chainData = new StationsChainData(
                startingStation.stationInfo.YardID,
                destinationStation.stationInfo.YardID);

            jobChainController.trainCarsForJobChain = orderedTrainCars;
            Dictionary <CargoType, List <(TrainCar, float)> > cargoTypeToTrainCarAndAmount
                = new Dictionary <CargoType, List <(TrainCar, float)> >();

            for (int i = 0; i < orderedTrainCars.Count; i++)
            {
                if (!cargoTypeToTrainCarAndAmount.ContainsKey(orderedCargoTypes[i]))
                {
                    cargoTypeToTrainCarAndAmount[orderedCargoTypes[i]] = new List <(TrainCar, float)>();
                }
                cargoTypeToTrainCarAndAmount[orderedCargoTypes[i]].Add((orderedTrainCars[i], orderedCargoAmounts[i]));
            }
            List <CarsPerCargoType> unloadData = cargoTypeToTrainCarAndAmount.Select(
                kvPair => new CarsPerCargoType(
                    kvPair.Key,
                    kvPair.Value.Select(t => t.Item1.logicCar).ToList(),
                    kvPair.Value.Aggregate(0.0f, (sum, t) => sum + t.Item2))).ToList();
            StaticShuntingUnloadJobDefinition staticShuntingUnloadJobDefinition
                = gameObject.AddComponent <StaticShuntingUnloadJobDefinition>();

            staticShuntingUnloadJobDefinition.PopulateBaseJobDefinition(
                destinationStation.logicStation,
                bonusTimeLimit,
                initialWage,
                chainData,
                requiredLicenses);
            staticShuntingUnloadJobDefinition.startingTrack           = startingTrack;
            staticShuntingUnloadJobDefinition.carsPerDestinationTrack = carsPerDestinationTrack;
            staticShuntingUnloadJobDefinition.unloadData    = unloadData;
            staticShuntingUnloadJobDefinition.unloadMachine = unloadMachine;
            staticShuntingUnloadJobDefinition.forceCorrectCargoStateOnCars = forceCorrectCargoStateOnCars;
            jobChainController.AddJobDefinitionToChain(staticShuntingUnloadJobDefinition);
            return(jobChainController);
        }
Пример #10
0
        public static JobChainControllerWithEmptyHaulGeneration GenerateShuntingUnloadJobWithExistingCars(
            StationController startingStation,
            Track startingTrack,
            StationController destinationStation,
            List <TrainCar> trainCars,
            List <CargoType> transportedCargoPerCar,
            System.Random rng,
            bool forceCorrectCargoStateOnCars = false)
        {
            Debug.Log("[PersistentJobs] unload: generating with pre-spawned cars");
            YardTracksOrganizer          yto = YardTracksOrganizer.Instance;
            HashSet <CargoContainerType> carContainerTypes = new HashSet <CargoContainerType>();

            foreach (TrainCar trainCar in trainCars)
            {
                carContainerTypes.Add(CargoTypes.CarTypeToContainerType[trainCar.carType]);
            }
            float approxTrainLength = yto.GetTotalTrainCarsLength(trainCars)
                                      + yto.GetSeparationLengthBetweenCars(trainCars.Count);

            // choose warehouse machine
            Debug.Log("[PersistentJobs] unload: choosing warehouse machine");
            List <WarehouseMachineController> supportedWMCs = destinationStation.warehouseMachineControllers
                                                              .Where(wm => wm.supportedCargoTypes.Intersect(transportedCargoPerCar).Count() > 0)
                                                              .ToList();

            if (supportedWMCs.Count == 0)
            {
                Debug.LogWarning(string.Format(
                                     "[PersistentJobs] unload: Could not create ChainJob[{0}]: {1} - {2}. Found no supported WarehouseMachine!",
                                     JobType.ShuntingLoad,
                                     startingStation.logicStation.ID,
                                     destinationStation.logicStation.ID));
                return(null);
            }
            WarehouseMachine loadMachine = Utilities.GetRandomFromEnumerable(supportedWMCs, rng).warehouseMachine;

            // choose destination tracks
            int maxCountTracks = destinationStation.proceduralJobsRuleset.maxShuntingStorageTracks;
            int countTracks    = rng.Next(1, maxCountTracks + 1);

            // bias toward less than max number of tracks for shorter trains
            if (trainCars.Count < 2 * maxCountTracks)
            {
                countTracks = rng.Next(0, Mathf.FloorToInt(1.5f * maxCountTracks)) % maxCountTracks + 1;
            }
            Debug.Log(string.Format("[PersistentJobs] unload: choosing {0} destination tracks", countTracks));
            List <Track> destinationTracks = new List <Track>();

            do
            {
                destinationTracks.Clear();
                for (int i = 0; i < countTracks; i++)
                {
                    Track track = Utilities.GetTrackThatHasEnoughFreeSpace(
                        yto,
                        destinationStation.logicStation.yard.StorageTracks.Except(destinationTracks).ToList(),
                        approxTrainLength / (float)countTracks);
                    if (track == null)
                    {
                        break;
                    }
                    destinationTracks.Add(track);
                }
            } while (destinationTracks.Count < countTracks--);
            if (destinationTracks.Count == 0)
            {
                Debug.LogWarning(string.Format(
                                     "[PersistentJobs] unload: Could not create ChainJob[{0}]: {1} - {2}. " +
                                     "Found no StorageTrack with enough free space!",
                                     JobType.ShuntingUnload,
                                     startingStation.logicStation.ID,
                                     destinationStation.logicStation.ID));
                return(null);
            }

            // divide trainCars between destination tracks
            int countCarsPerTrainset       = trainCars.Count / destinationTracks.Count;
            int countTrainsetsWithExtraCar = trainCars.Count % destinationTracks.Count;

            Debug.Log(string.Format(
                          "[PersistentJobs] unload: dividing trainCars {0} per track with {1} extra",
                          countCarsPerTrainset,
                          countTrainsetsWithExtraCar));
            List <TrainCar>     orderedTrainCars        = new List <TrainCar>();
            List <CarsPerTrack> carsPerDestinationTrack = new List <CarsPerTrack>();

            for (int i = 0; i < destinationTracks.Count; i++)
            {
                int   rangeStart       = i * countCarsPerTrainset + Math.Min(i, countTrainsetsWithExtraCar);
                int   rangeCount       = i < countTrainsetsWithExtraCar ? countCarsPerTrainset + 1 : countCarsPerTrainset;
                Track destinationTrack = destinationTracks[i];
                carsPerDestinationTrack.Add(
                    new CarsPerTrack(
                        destinationTrack,
                        (from car in trainCars.GetRange(rangeStart, rangeCount) select car.logicCar).ToList()));
            }

            Debug.Log("[PersistentJobs] unload: calculating time/wage/licenses");
            float bonusTimeLimit;
            float initialWage;

            Utilities.CalculateShuntingBonusTimeLimitAndWage(
                JobType.ShuntingLoad,
                destinationTracks.Count,
                (from tc in trainCars select tc.carType).ToList <TrainCarType>(),
                transportedCargoPerCar,
                out bonusTimeLimit,
                out initialWage
                );
            JobLicenses requiredLicenses = LicenseManager.GetRequiredLicensesForJobType(JobType.ShuntingUnload)
                                           | LicenseManager.GetRequiredLicensesForCargoTypes(transportedCargoPerCar)
                                           | LicenseManager.GetRequiredLicenseForNumberOfTransportedCars(trainCars.Count);

            return(GenerateShuntingUnloadChainController(
                       startingStation,
                       startingTrack,
                       loadMachine,
                       destinationStation,
                       carsPerDestinationTrack,
                       trainCars,
                       transportedCargoPerCar,
                       trainCars.Select(
                           tc => tc.logicCar.CurrentCargoTypeInCar == CargoType.None ? 1.0f : tc.logicCar.LoadedCargoAmount).ToList(),
                       forceCorrectCargoStateOnCars,
                       bonusTimeLimit,
                       initialWage,
                       requiredLicenses));
        }
Пример #11
0
 static void Prefix(ref JobLicenses jobLicensesInt)
 {
     // we'll mask out the passenger license and pass on the remaining value
     jobLicensesInt &= ~PassLicenses.Passengers1;
 }
        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;
            }

            // Force cargo state
            foreach (var car in trainCarsToTransport)
            {
                car.DumpCargo();
                car.LoadCargo(car.capacity, CargoType.Passengers);
            }

            // Initialize tasks
            Task transportTask = JobsGenerator.CreateTransportTask(
                trainCarsToTransport, destinationTrack, startingTrack,
                Enumerable.Repeat(CargoType.Passengers, trainCarsToTransport.Count).ToList());

            job = new Job(transportTask, subType, jobTimeLimit, initialWage, chainData, forcedJobId, requiredLicenses);
            jobOriginStation.AddJobToStation(job);
        }
Пример #13
0
        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);
        }
Пример #14
0
        public static JobChainControllerWithEmptyHaulGeneration GenerateTransportJobWithExistingCars(
            StationController startingStation,
            Track startingTrack,
            StationController destStation,
            List <TrainCar> trainCars,
            List <CargoType> transportedCargoPerCar,
            System.Random rng,
            bool forceCorrectCargoStateOnCars = false)
        {
            Debug.Log("[PersistentJobs] transport: generating with pre-spawned cars");
            YardTracksOrganizer          yto = YardTracksOrganizer.Instance;
            HashSet <CargoContainerType> carContainerTypes = new HashSet <CargoContainerType>();

            foreach (TrainCar trainCar in trainCars)
            {
                carContainerTypes.Add(CargoTypes.CarTypeToContainerType[trainCar.carType]);
            }

            Debug.Log("[PersistentJobs] transport: choosing destination track");
            float approxTrainLength = yto.GetTotalTrainCarsLength(trainCars)
                                      + yto.GetSeparationLengthBetweenCars(trainCars.Count);
            Track destinationTrack = Utilities.GetTrackThatHasEnoughFreeSpace(
                yto,
                yto.FilterOutOccupiedTracks(destStation.logicStation.yard.TransferInTracks),
                approxTrainLength);

            if (destinationTrack == null)
            {
                destinationTrack = Utilities.GetTrackThatHasEnoughFreeSpace(
                    yto,
                    destStation.logicStation.yard.TransferInTracks,
                    approxTrainLength);
            }
            if (destinationTrack == null)
            {
                Debug.LogWarning(string.Format(
                                     "[PersistentJobs] transport: Could not create ChainJob[{0}]: {1} - {2}. " +
                                     "Found no TransferInTrack with enough free space!",
                                     JobType.Transport,
                                     startingStation.logicStation.ID,
                                     destStation.logicStation.ID
                                     ));
                return(null);
            }
            List <TrainCarType> transportedCarTypes = (from tc in trainCars select tc.carType)
                                                      .ToList <TrainCarType>();

            Debug.Log("[PersistentJobs] transport: calculating time/wage/licenses");
            float bonusTimeLimit;
            float initialWage;

            Utilities.CalculateTransportBonusTimeLimitAndWage(
                JobType.Transport,
                startingStation,
                destStation,
                transportedCarTypes,
                transportedCargoPerCar,
                out bonusTimeLimit,
                out initialWage
                );
            JobLicenses requiredLicenses = LicenseManager.GetRequiredLicensesForJobType(JobType.Transport)
                                           | LicenseManager.GetRequiredLicensesForCargoTypes(transportedCargoPerCar)
                                           | LicenseManager.GetRequiredLicenseForNumberOfTransportedCars(trainCars.Count);

            return(TransportJobProceduralGenerator.GenerateTransportChainController(
                       startingStation,
                       startingTrack,
                       destStation,
                       destinationTrack,
                       trainCars,
                       transportedCargoPerCar,
                       trainCars.Select(
                           tc => tc.logicCar.CurrentCargoTypeInCar == CargoType.None ? 1.0f : tc.logicCar.LoadedCargoAmount).ToList(),
                       forceCorrectCargoStateOnCars,
                       bonusTimeLimit,
                       initialWage,
                       requiredLicenses
                       ));
        }
        public static JobChainControllerWithEmptyHaulGeneration GenerateShuntingLoadJobWithExistingCars(
            StationController startingStation,
            List <CarsPerTrack> carsPerStartingTrack,
            StationController destStation,
            List <TrainCar> trainCars,
            List <CargoType> transportedCargoPerCar,
            System.Random rng,
            bool forceCorrectCargoStateOnCars = false)
        {
            Debug.Log("[PersistentJobs] load: generating with pre-spawned cars");
            YardTracksOrganizer          yto = YardTracksOrganizer.Instance;
            HashSet <CargoContainerType> carContainerTypes = new HashSet <CargoContainerType>();

            foreach (TrainCar trainCar in trainCars)
            {
                carContainerTypes.Add(CargoTypes.CarTypeToContainerType[trainCar.carType]);
            }
            float approxTrainLength = yto.GetTotalTrainCarsLength(trainCars)
                                      + yto.GetSeparationLengthBetweenCars(trainCars.Count);

            // choose warehosue machine
            Debug.Log("[PersistentJobs] load: choosing warehouse machine");
            List <WarehouseMachineController> supportedWMCs = startingStation.warehouseMachineControllers
                                                              .Where(wm => wm.supportedCargoTypes.Intersect(transportedCargoPerCar).Count() > 0)
                                                              .ToList();

            if (supportedWMCs.Count == 0)
            {
                Debug.LogWarning(string.Format(
                                     "[PersistentJobs] load: Could not create ChainJob[{0}]: {1} - {2}. Found no supported WarehouseMachine!",
                                     JobType.ShuntingLoad,
                                     startingStation.logicStation.ID,
                                     destStation.logicStation.ID
                                     ));
                return(null);
            }
            WarehouseMachine loadMachine = Utilities.GetRandomFromEnumerable(supportedWMCs, rng).warehouseMachine;

            // choose destination track
            Debug.Log("[PersistentJobs] load: choosing destination track");
            Track destinationTrack = Utilities.GetTrackThatHasEnoughFreeSpace(
                yto,
                yto.FilterOutOccupiedTracks(startingStation.logicStation.yard.TransferOutTracks),
                approxTrainLength
                );

            if (destinationTrack == null)
            {
                destinationTrack = Utilities.GetTrackThatHasEnoughFreeSpace(
                    yto,
                    startingStation.logicStation.yard.TransferOutTracks,
                    approxTrainLength
                    );
            }
            if (destinationTrack == null)
            {
                Debug.LogWarning(string.Format(
                                     "[PersistentJobs] load: Could not create ChainJob[{0}]: {1} - {2}. " +
                                     "Found no TransferOutTrack with enough free space!",
                                     JobType.ShuntingLoad,
                                     startingStation.logicStation.ID,
                                     destStation.logicStation.ID
                                     ));
                return(null);
            }

            Debug.Log("[PersistentJobs] load: calculating time/wage/licenses");
            List <TrainCarType> transportedCarTypes = (from tc in trainCars select tc.carType)
                                                      .ToList <TrainCarType>();
            float bonusTimeLimit;
            float initialWage;

            Utilities.CalculateShuntingBonusTimeLimitAndWage(
                JobType.ShuntingLoad,
                carsPerStartingTrack.Count,
                transportedCarTypes,
                transportedCargoPerCar,
                out bonusTimeLimit,
                out initialWage
                );
            JobLicenses requiredLicenses = LicenseManager.GetRequiredLicensesForJobType(JobType.ShuntingLoad)
                                           | LicenseManager.GetRequiredLicensesForCargoTypes(transportedCargoPerCar)
                                           | LicenseManager.GetRequiredLicenseForNumberOfTransportedCars(trainCars.Count);

            return(GenerateShuntingLoadChainController(
                       startingStation,
                       carsPerStartingTrack,
                       loadMachine,
                       destStation,
                       destinationTrack,
                       trainCars,
                       transportedCargoPerCar,
                       Enumerable.Repeat(1.0f, trainCars.Count).ToList(),
                       forceCorrectCargoStateOnCars,
                       bonusTimeLimit,
                       initialWage,
                       requiredLicenses
                       ));
        }