Example #1
0
        static bool Prefix(IdGenerator __instance, JobType jobType, StationsChainData jobStationsInfo, ref string __result)
        {
            if ((jobType != PassJobType.Express) &&
                (jobType != PassJobType.Commuter))
            {
                return(true);
            }

            string yardId = null;

            if (jobStationsInfo != null)
            {
                yardId = jobStationsInfo.chainOriginYardId;
            }

            string typeStr = (jobType == PassJobType.Express) ? EXPRESS_TYPE : COMMUTE_TYPE;
            string idStr   = FindUnusedID(typeStr, yardId);

            if (idStr != null)
            {
                __instance.RegisterJobId(idStr);
                __result = idStr;
            }
            else
            {
                PassengerJobs.ModEntry.Logger.Warning($"Couldn't find free jobId for job type: {typeStr}! Using 0 for jobId number!");
                __result = (yardId != null) ? $"{yardId}-{typeStr}-{0:D2}" : $"{typeStr}-{0:D2}";
            }

            return(false);
        }
Example #2
0
        private bool ParseRoutes()
        {
            if (string.IsNullOrWhiteSpace(RouteString) || "*".Equals(RouteString))
            {
                AllowAnyRoute = true;
                return(true);
            }

            string[] routePairs = RouteString.Split(',');
            Routes = new StationsChainData[routePairs.Length];

            for (int i = 0; i < routePairs.Length; i++)
            {
                string[] stations = routePairs[i].ToUpper().Split('-');

                if ((stations.Length == 2) &&
                    LogicController.Instance.YardIdToStationController.TryGetValue(stations[0], out _) &&
                    LogicController.Instance.YardIdToStationController.TryGetValue(stations[1], out _))
                {
                    Routes[i] = new StationsChainData(stations[0], stations[1]);
                }
                else
                {
                    return(false);
                }
            }

            return(true);
        }
Example #3
0
        private static StaticPassengerJobDefinition PopulateTransportJobAndSpawn(
            JobChainController chainController, Station startStation,
            Track startTrack, Track destTrack, List <TrainCarType> carTypes,
            StationsChainData chainData, float timeLimit, float initialPay, bool unifyConsist = false)
        {
            // Spawn the cars
            RailTrack startRT     = SingletonBehaviour <LogicController> .Instance.LogicToRailTrack[startTrack];
            var       spawnedCars = CarSpawner.SpawnCarTypesOnTrack(carTypes, startRT, true, 0, false, true);

            if (spawnedCars == null)
            {
                return(null);
            }

            chainController.trainCarsForJobChain = spawnedCars;
            var logicCars = TrainCar.ExtractLogicCars(spawnedCars);

            if (logicCars == null)
            {
                PassengerJobs.ModEntry.Logger.Error("Couldn't extract logic cars, deleting spawned cars");
                SingletonBehaviour <CarSpawner> .Instance.DeleteTrainCars(spawnedCars, true);

                return(null);
            }

            if (unifyConsist && SkinManager_Patch.Enabled)
            {
                SkinManager_Patch.UnifyConsist(spawnedCars);
            }

            return(PopulateTransportJobExistingCars(chainController, startStation, startTrack, destTrack, logicCars, chainData, timeLimit, initialPay));
        }
Example #4
0
        private static StaticPassengerJobDefinition CreateSavedPassengerJob(GameObject jobChainGO, PassengerJobDefinitionData jobData)
        {
            // associated station
            if (!(GetStationWithId(jobData.stationId) is Station logicStation))
            {
                PrintError($"Couldn't find corresponding Station with ID: {jobData.stationId}! Skipping load of this job chain!");
                return(null);
            }

            // bonus time limit, base payment
            if (jobData.timeLimitForJob < 0f || jobData.initialWage < 0f ||
                string.IsNullOrEmpty(jobData.originStationId) || string.IsNullOrEmpty(jobData.destinationStationId))
            {
                PrintError("Invalid data! Skipping load of this job chain!");
                return(null);
            }

            // license requirements
            if (!LicenseManager.IsValidForParsingToJobLicense(jobData.requiredLicenses))
            {
                PrintError("Undefined job licenses requirement! Skipping load of this job chain!");
                return(null);
            }

            // starting track
            if (!(GetYardTrackWithId(jobData.startingTrack) is Track startTrack))
            {
                PrintError($"Couldn't find corresponding start Track with ID: {jobData.startingTrack}! Skipping load of this job chain!");
                return(null);
            }

            // destination track
            if (!(GetYardTrackWithId(jobData.destinationTrack) is Track destTrack))
            {
                PrintError($"Couldn't find corresponding destination Track with ID: {jobData.destinationTrack}! Skipping load of this job chain!");
                return(null);
            }

            // consist
            if (!(GetCarsFromCarGuids(jobData.trainCarGuids) is List <Car> consist))
            {
                PrintError("Couldn't find all carsToTransport with transportCarGuids! Skipping load of this job chain!");
                return(null);
            }

            StaticPassengerJobDefinition jobDefinition = jobChainGO.AddComponent <StaticPassengerJobDefinition>();
            var chainData = new StationsChainData(jobData.originStationId, jobData.destinationStationId);

            jobDefinition.PopulateBaseJobDefinition(logicStation, jobData.timeLimitForJob, jobData.initialWage, chainData, (JobLicenses)jobData.requiredLicenses);

            jobDefinition.subType              = (JobType)jobData.subType;
            jobDefinition.startingTrack        = startTrack;
            jobDefinition.destinationTrack     = destTrack;
            jobDefinition.trainCarsToTransport = consist;

            return(jobDefinition);
        }
Example #5
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);
        }
Example #6
0
        private static StaticPassengerJobDefinition PopulateTransportJobExistingCars(
            JobChainController chainController, Station startStation,
            Track startTrack, Track destTrack, List <Car> logicCars,
            StationsChainData chainData, float timeLimit, float initialPay)
        {
            // populate the actual job
            StaticPassengerJobDefinition jobDefinition = chainController.jobChainGO.AddComponent <StaticPassengerJobDefinition>();

            jobDefinition.PopulateBaseJobDefinition(startStation, timeLimit, initialPay, chainData, PassLicenses.Passengers1);

            jobDefinition.startingTrack        = startTrack;
            jobDefinition.trainCarsToTransport = logicCars;
            jobDefinition.destinationTrack     = destTrack;

            return(jobDefinition);
        }
Example #7
0
        static bool Prefix(JobType jobType, StationsChainData jobStationsInfo, ref string __result,
                           System.Random ___idRng, HashSet <string> ___existingJobIds)
        {
            if ((jobType != PassJobType.Express) &&
                (jobType != PassJobType.Commuter))
            {
                return(true);
            }

            string yardId = null;

            if (jobStationsInfo != null)
            {
                yardId = jobStationsInfo.chainOriginYardId;
            }

            string typeStr = (jobType == PassJobType.Express) ? EXPRESS_TYPE : COMMUTE_TYPE;

            int idNum = ___idRng.Next(0, 100);

            for (int attemptNum = 0; attemptNum < 99; attemptNum++)
            {
                string idStr = (yardId != null) ? $"{yardId}-{typeStr}-{idNum:D2}" : $"{typeStr}-{idNum:D2}";

                if (!___existingJobIds.Contains(idStr))
                {
                    IdGenerator.RegisterJobId(idStr);
                    __result = idStr;
                    return(false);
                }

                idNum = (idNum >= 99) ? 0 : (idNum + 1);
            }

            PassengerJobs.ModEntry.Logger.Warning($"Couldn't find free jobId for job type: {typeStr}! Using 0 for jobId number!");
            __result = (yardId != null) ? $"{yardId}-{typeStr}-{0:D2}" : $"{typeStr}-{0:D2}";

            return(false);
        }
Example #8
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);
        }
Example #9
0
        private static StaticPassengerJobDefinition CreateSavedPassengerJob(GameObject jobChainGO, PassengerJobDefinitionData jobData)
        {
            // associated station
            if (!(GetStationWithId(jobData.stationId) is Station logicStation))
            {
                PrintError($"Couldn't find corresponding Station with ID: {jobData.stationId}! Skipping load of this job chain!");
                return(null);
            }

            // bonus time limit, base payment
            if (jobData.timeLimitForJob < 0f || jobData.initialWage < 0f ||
                string.IsNullOrEmpty(jobData.originStationId) || string.IsNullOrEmpty(jobData.destinationStationId))
            {
                PrintError("Invalid data! Skipping load of this job chain!");
                return(null);
            }

            // license requirements
            if (!LicenseManager.IsValidForParsingToJobLicense(jobData.requiredLicenses))
            {
                PrintError("Undefined job licenses requirement! Skipping load of this job chain!");
                return(null);
            }

            // starting track
            if (!(GetYardTrackWithId(jobData.startingTrack) is Track startTrack))
            {
                PrintError($"Couldn't find corresponding start Track with ID: {jobData.startingTrack}! Skipping load of this job chain!");
                return(null);
            }

            // destination track
            if (!(GetYardTrackWithId(jobData.destinationTrack) is Track destTrack))
            {
                PrintError($"Couldn't find corresponding destination Track with ID: {jobData.destinationTrack}! Skipping load of this job chain!");
                return(null);
            }

            // consist
            if (!(GetCarsFromCarGuids(jobData.trainCarGuids) is List <Car> consist))
            {
                PrintError("Couldn't find all carsToTransport with transportCarGuids! Skipping load of this job chain!");
                return(null);
            }

            // loading platform
            WarehouseMachine loadMachine = null;

            if (!string.IsNullOrEmpty(jobData.loadingTrackId))
            {
                if (PlatformManager.GetPlatformByTrackId(jobData.loadingTrackId) is PlatformDefinition pd)
                {
                    loadMachine = pd.Controller.LogicMachine;
                }
            }

            // unloading platform
            WarehouseMachine unloadMachine = null;

            if (!string.IsNullOrEmpty(jobData.unloadingTrackId))
            {
                if (PlatformManager.GetPlatformByTrackId(jobData.unloadingTrackId) is PlatformDefinition pd)
                {
                    unloadMachine = pd.Controller.LogicMachine;
                }
            }

            // Named train
            SpecialTrain special = null;

            if (!string.IsNullOrWhiteSpace(jobData.specialName))
            {
                special = SpecialConsistManager.TrainDefinitions.Find(train => string.Equals(train.Name, jobData.specialName));
            }

            StaticPassengerJobDefinition jobDefinition = jobChainGO.AddComponent <StaticPassengerJobDefinition>();
            var chainData = new StationsChainData(jobData.originStationId, jobData.destinationStationId);

            jobDefinition.PopulateBaseJobDefinition(logicStation, jobData.timeLimitForJob, jobData.initialWage, chainData, (JobLicenses)jobData.requiredLicenses);

            jobDefinition.subType              = (JobType)jobData.subType;
            jobDefinition.startingTrack        = startTrack;
            jobDefinition.destinationTrack     = destTrack;
            jobDefinition.trainCarsToTransport = consist;
            jobDefinition.loadMachine          = loadMachine;
            jobDefinition.unloadMachine        = unloadMachine;
            jobDefinition.specialDefinition    = special;

            return(jobDefinition);
        }
Example #10
0
        private static JobChainController CreateSavedJobChain(PassengerChainSaveData passChainData)
        {
            // Figure out chain type
            PassengerChainSaveData.PassChainType chainType = passChainData.ChainType;

            if (InitializeCorrespondingJobBooklet == null)
            {
                PrintError("Failed to connect to JobSaveManager methods");
                return(null);
            }

            List <TrainCar> trainCarsFromCarGuids = GetTrainCarsFromCarGuids(passChainData.trainCarGuids);

            if (trainCarsFromCarGuids == null)
            {
                PrintError("Couldn't find trainCarsForJobChain with trainCarGuids from chainSaveData! Skipping load of this job chain!");
                return(null);
            }

            var jobChainGO = new GameObject();
            JobChainController chainController;

            if (chainType == PassengerChainSaveData.PassChainType.Transport)
            {
                // PASSENGER TRANSPORT (EXPRESS) CHAIN
                StaticJobDefinition jobDefinition;
                StationsChainData   chainData = null;
                chainController = new PassengerTransportChainController(jobChainGO)
                {
                    trainCarsForJobChain = trainCarsFromCarGuids
                };

                foreach (JobDefinitionDataBase jobData in passChainData.jobChainData)
                {
                    if (jobData is PassengerJobDefinitionData pjData)
                    {
                        jobDefinition = CreateSavedPassengerJob(jobChainGO, pjData);
                        jobDefinition?.ForceJobId(passChainData.firstJobId);
                        chainData = new StationsChainData(jobData.originStationId, jobData.destinationStationId);
                    }
                    else
                    {
                        PrintError("Express pax chain contains invalid job type");
                        return(null);
                    }

                    if (jobDefinition == null)
                    {
                        PrintError("Failed to generate job definition from save data");
                        UnityEngine.Object.Destroy(jobChainGO);
                        return(null);
                    }

                    chainController.AddJobDefinitionToChain(jobDefinition);
                }

                jobChainGO.name = $"[LOADED] ChainJob[Passenger]: {chainData.chainOriginYardId} - {chainData.chainDestinationYardId}";
            }
            else
            {
                // COMMUTER JOB CHAIN
                StaticJobDefinition jobDefinition;
                StationsChainData   chainData = null;
                bool first = true;

                chainController = new CommuterChainController(jobChainGO)
                {
                    trainCarsForJobChain = trainCarsFromCarGuids
                };

                foreach (JobDefinitionDataBase jobDataBase in passChainData.jobChainData)
                {
                    if (jobDataBase is PassengerJobDefinitionData jobData)
                    {
                        jobDefinition = CreateSavedPassengerJob(jobChainGO, jobData);

                        if (first)
                        {
                            chainData = new StationsChainData(jobData.originStationId, jobData.destinationStationId);
                            jobDefinition?.ForceJobId(passChainData.firstJobId);
                            first = false;
                        }
                    }
                    else
                    {
                        PrintError("Commuter chain contains invalid job type");
                        return(null);
                    }

                    if (jobDefinition == null)
                    {
                        PrintError("Failed to generate job definition from save data");
                        UnityEngine.Object.Destroy(jobChainGO);
                        return(null);
                    }

                    chainController.AddJobDefinitionToChain(jobDefinition);
                }

                jobChainGO.name = $"[LOADED] ChainJob[Commuter]: {chainData.chainOriginYardId} - {chainData.chainDestinationYardId}";
            }

            return(chainController);
        }
Example #11
0
        public PassengerTransportChainController GenerateNewTransportJob(TrainCarsPerLogicTrack consistInfo = null)
        {
            int nTotalCars;
            List <TrainCarType> jobCarTypes;
            float trainLength;
            Track startPlatform;

            if (consistInfo == null)
            {
                // generate a consist
                nTotalCars = Rand.Next(MIN_CARS_EXPRESS, MAX_CARS_EXPRESS + 1);

                if (PassengerJobs.Settings.UniformConsists)
                {
                    TrainCarType carType = PassCarTypes.ChooseOne(Rand);
                    jobCarTypes = Enumerable.Repeat(carType, nTotalCars).ToList();
                }
                else
                {
                    jobCarTypes = PassCarTypes.ChooseMany(Rand, nTotalCars);
                }

                trainLength = TrackOrg.GetTotalCarTypesLength(jobCarTypes) + TrackOrg.GetSeparationLengthBetweenCars(nTotalCars);

                var pool = TrackOrg.FilterOutReservedTracks(TrackOrg.FilterOutOccupiedTracks(PlatformTracks));
                if (!(TrackOrg.GetTrackThatHasEnoughFreeSpace(pool, trainLength) is Track startTrack))
                {
                    PassengerJobs.ModEntry.Logger.Log($"Couldn't find storage track with enough free space for new job at {Controller.stationInfo.YardID}");
                    return(null);
                }

                startPlatform = startTrack;
            }
            else
            {
                // Use existing consist
                nTotalCars    = consistInfo.cars.Count;
                jobCarTypes   = consistInfo.cars.Select(car => car.carType).ToList();
                trainLength   = TrackOrg.GetTotalCarTypesLength(jobCarTypes) + TrackOrg.GetSeparationLengthBetweenCars(nTotalCars);
                startPlatform = consistInfo.track;
            }

            if (startPlatform == null)
            {
                PassengerJobs.ModEntry.Logger.Log($"No available platform for new job at {Controller.stationInfo.Name}");
                return(null);
            }

            // Choose a route
            var               destPool     = PassDestinations.Values.ToList();
            Track             destPlatform = null;
            StationController destStation  = null;

            // this prevents generating jobs like "ChainJob[Passenger]: FF - FF (FF-PE-47)"
            destPool.Remove(Controller);

            while ((destPlatform == 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];
                destPlatform = TrackOrg.GetTrackThatHasEnoughFreeSpace(destGenerator.PlatformTracks, trainLength);

                // remove this station from the pool
                destPool.Remove(destStation);
            }

            if (destPlatform == null)
            {
                PassengerJobs.ModEntry.Logger.Log($"No available destination platform for new job at {Controller.stationInfo.Name}");
                return(null);
            }

            // 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);

            // 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, startPlatform, destPlatform,
                    jobCarTypes, chainData, bonusLimit, transportPayment, true);
            }
            else
            {
                chainController.trainCarsForJobChain = consistInfo.cars;

                jobDefinition = PopulateTransportJobExistingCars(
                    chainController, Controller.logicStation, startPlatform, destPlatform,
                    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;

            chainController.AddJobDefinitionToChain(jobDefinition);

            // Finalize job
            chainController.FinalizeSetupAndGenerateFirstJob();
            PassengerJobs.ModEntry.Logger.Log($"Generated new passenger haul job: {chainJobObject.name} ({chainController.currentJobInChain.ID})");

            return(chainController);
        }
Example #12
0
        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);
        }