public static Track GetTrackThatHasEnoughFreeSpace(YardTracksOrganizer yto, List <Track> tracks, float requiredLength) { Debug.Log("[PersistentJobs] getting random track with free space"); List <Track> tracksWithFreeSpace = yto.FilterOutTracksWithoutRequiredFreeSpace(tracks, requiredLength); Debug.Log(string.Format( "[PersistentJobs] {0}/{1} tracks have at least {2}m available", tracksWithFreeSpace.Count, tracks.Count, requiredLength)); if (tracksWithFreeSpace.Count > 0) { return(Utilities.GetRandomFromEnumerable( tracksWithFreeSpace, new System.Random())); } return(null); }
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)); }
public static JobChainControllerWithEmptyHaulGeneration GenerateShuntingUnloadJobWithCarSpawning( StationController destinationStation, bool forceLicenseReqs, System.Random rng) { Debug.Log("[PersistentJobs] unload: generating with car spawning"); YardTracksOrganizer yto = YardTracksOrganizer.Instance; List <CargoGroup> availableCargoGroups = destinationStation.proceduralJobsRuleset.inputCargoGroups; int countTrainCars = rng.Next( destinationStation.proceduralJobsRuleset.minCarsPerJob, destinationStation.proceduralJobsRuleset.maxCarsPerJob); if (forceLicenseReqs) { Debug.Log("[PersistentJobs] unload: forcing license requirements"); if (!LicenseManager.IsJobLicenseAcquired(JobLicenses.Shunting)) { Debug.LogError("[PersistentJobs] unload: Trying to generate a ShuntingUnload job with " + "forceLicenseReqs=true should never happen if player doesn't have Shunting license!"); return(null); } availableCargoGroups = (from cg in availableCargoGroups where LicenseManager.IsLicensedForJob(cg.CargoRequiredLicenses) select cg).ToList(); countTrainCars = Math.Min(countTrainCars, LicenseManager.GetMaxNumberOfCarsPerJobWithAcquiredJobLicenses()); } if (availableCargoGroups.Count == 0) { Debug.LogWarning("[PersistentJobs] unload: no available cargo groups"); return(null); } CargoGroup chosenCargoGroup = Utilities.GetRandomFromEnumerable(availableCargoGroups, rng); // choose cargo & trainCar types Debug.Log("[PersistentJobs] unload: choosing cargo & trainCar types"); List <CargoType> availableCargoTypes = chosenCargoGroup.cargoTypes; List <CargoType> orderedCargoTypes = new List <CargoType>(); List <TrainCarType> orderedTrainCarTypes = new List <TrainCarType>(); for (int i = 0; i < countTrainCars; i++) { CargoType chosenCargoType = Utilities.GetRandomFromEnumerable(availableCargoTypes, rng); List <CargoContainerType> availableContainers = CargoTypes.GetCarContainerTypesThatSupportCargoType(chosenCargoType); CargoContainerType chosenContainerType = Utilities.GetRandomFromEnumerable(availableContainers, rng); List <TrainCarType> availableTrainCarTypes = CargoTypes.GetTrainCarTypesThatAreSpecificContainerType(chosenContainerType); TrainCarType chosenTrainCarType = Utilities.GetRandomFromEnumerable(availableTrainCarTypes, rng); orderedCargoTypes.Add(chosenCargoType); orderedTrainCarTypes.Add(chosenTrainCarType); } float approxTrainLength = yto.GetTotalCarTypesLength(orderedTrainCarTypes) + yto.GetSeparationLengthBetweenCars(countTrainCars); // choose starting track Debug.Log("[PersistentJobs] unload: choosing starting track"); Track startingTrack = Utilities.GetTrackThatHasEnoughFreeSpace(yto, destinationStation.logicStation.yard.TransferInTracks, approxTrainLength); if (startingTrack == null) { Debug.LogWarning("[PersistentJobs] unload: Couldn't find startingTrack with enough free space for train!"); return(null); } // choose random starting station // no need to ensure it has has free space; this is just a back story Debug.Log("[PersistentJobs] unload: choosing origin (inconsequential)"); List <StationController> availableOrigins = new List <StationController>(chosenCargoGroup.stations); StationController startingStation = Utilities.GetRandomFromEnumerable(availableOrigins, rng); // spawn trainCars Debug.Log("[PersistentJobs] unload: spawning trainCars"); RailTrack railTrack = SingletonBehaviour <LogicController> .Instance.LogicToRailTrack[startingTrack]; List <TrainCar> orderedTrainCars = CarSpawner.SpawnCarTypesOnTrack( orderedTrainCarTypes, railTrack, true, 0.0, false, true); if (orderedTrainCars == null) { Debug.LogWarning("[PersistentJobs] unload: Failed to spawn trainCars!"); return(null); } JobChainControllerWithEmptyHaulGeneration jcc = GenerateShuntingUnloadJobWithExistingCars( startingStation, startingTrack, destinationStation, orderedTrainCars, orderedCargoTypes, rng, true); if (jcc == null) { Debug.LogWarning("[PersistentJobs] unload: Couldn't generate job chain. Deleting spawned trainCars!"); SingletonBehaviour <CarSpawner> .Instance.DeleteTrainCars(orderedTrainCars, true); return(null); } return(jcc); }
public PassengerJobGenerator() { TrackOrg = YardTracksOrganizer.Instance; }
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 )); }
public static JobChainControllerWithEmptyHaulGeneration GenerateShuntingLoadJobWithCarSpawning( StationController startingStation, bool forceLicenseReqs, System.Random rng) { Debug.Log("[PersistentJobs] load: generating with car spawning"); YardTracksOrganizer yto = YardTracksOrganizer.Instance; List <CargoGroup> availableCargoGroups = startingStation.proceduralJobsRuleset.outputCargoGroups; int countTrainCars = rng.Next( startingStation.proceduralJobsRuleset.minCarsPerJob, startingStation.proceduralJobsRuleset.maxCarsPerJob); if (forceLicenseReqs) { Debug.Log("[PersistentJobs] load: forcing license requirements"); if (!LicenseManager.IsJobLicenseAcquired(JobLicenses.Shunting)) { Debug.LogError("Trying to generate a ShuntingLoad job with forceLicenseReqs=true should " + "never happen if player doesn't have Shunting license!"); return(null); } availableCargoGroups = (from cg in availableCargoGroups where LicenseManager.IsLicensedForJob(cg.CargoRequiredLicenses) select cg).ToList(); countTrainCars = Math.Min(countTrainCars, LicenseManager.GetMaxNumberOfCarsPerJobWithAcquiredJobLicenses()); } if (availableCargoGroups.Count == 0) { Debug.LogWarning("[PersistentJobs] load: no available cargo groups"); return(null); } CargoGroup chosenCargoGroup = Utilities.GetRandomFromEnumerable(availableCargoGroups, rng); // choose cargo & trainCar types Debug.Log("[PersistentJobs] load: choosing cargo & trainCar types"); List <CargoType> availableCargoTypes = chosenCargoGroup.cargoTypes; List <CargoType> orderedCargoTypes = new List <CargoType>(); List <TrainCarType> orderedTrainCarTypes = new List <TrainCarType>(); for (int i = 0; i < countTrainCars; i++) { CargoType chosenCargoType = Utilities.GetRandomFromEnumerable(availableCargoTypes, rng); List <CargoContainerType> availableContainers = CargoTypes.GetCarContainerTypesThatSupportCargoType(chosenCargoType); CargoContainerType chosenContainerType = Utilities.GetRandomFromEnumerable(availableContainers, rng); List <TrainCarType> availableTrainCarTypes = CargoTypes.GetTrainCarTypesThatAreSpecificContainerType(chosenContainerType); TrainCarType chosenTrainCarType = Utilities.GetRandomFromEnumerable(availableTrainCarTypes, rng); orderedCargoTypes.Add(chosenCargoType); orderedTrainCarTypes.Add(chosenTrainCarType); } // choose starting tracks int maxCountTracks = startingStation.proceduralJobsRuleset.maxShuntingStorageTracks; int countTracks = rng.Next(1, maxCountTracks + 1); // bias toward less than max number of tracks for shorter trains if (orderedTrainCarTypes.Count < 2 * maxCountTracks) { countTracks = rng.Next(0, Mathf.FloorToInt(1.5f * maxCountTracks)) % maxCountTracks + 1; } Debug.Log(string.Format("[PersistentJobs] load: choosing {0} starting tracks", countTracks)); int countCarsPerTrainset = countTrainCars / countTracks; int countTrainsetsWithExtraCar = countTrainCars % countTracks; List <Track> tracks = new List <Track>(); do { tracks.Clear(); for (int i = 0; i < countTracks; i++) { int rangeStart = i * countCarsPerTrainset + Math.Min(i, countTrainsetsWithExtraCar); int rangeCount = i < countTrainsetsWithExtraCar ? countCarsPerTrainset + 1 : countCarsPerTrainset; List <TrainCarType> trainCarTypesPerTrack = orderedTrainCarTypes.GetRange(rangeStart, rangeCount); float approxTrainLengthPerTrack = yto.GetTotalCarTypesLength(trainCarTypesPerTrack) + yto.GetSeparationLengthBetweenCars(trainCarTypesPerTrack.Count); Track track = Utilities.GetTrackThatHasEnoughFreeSpace( yto, startingStation.logicStation.yard.StorageTracks.Except(tracks).ToList(), approxTrainLengthPerTrack / (float)countTracks); if (track == null) { break; } tracks.Add(track); } } while (tracks.Count < countTracks--); if (tracks.Count == 0) { Debug.LogWarning("[PersistentJobs] load: Couldn't find startingTrack with enough free space for train!"); return(null); } // choose random destination station that has at least 1 available track Debug.Log("[PersistentJobs] load: choosing destination"); float approxTrainLength = yto.GetTotalCarTypesLength(orderedTrainCarTypes) + yto.GetSeparationLengthBetweenCars(countTrainCars); List <StationController> availableDestinations = new List <StationController>(chosenCargoGroup.stations); StationController destStation = null; Track destinationTrack = null; while (availableDestinations.Count > 0 && destinationTrack == null) { destStation = Utilities.GetRandomFromEnumerable(availableDestinations, rng); availableDestinations.Remove(destStation); destinationTrack = Utilities.GetTrackThatHasEnoughFreeSpace( yto, yto.FilterOutOccupiedTracks(destStation.logicStation.yard.TransferInTracks), approxTrainLength); } if (destinationTrack == null) { Debug.LogWarning("Couldn't find a station with enough free space for train!"); return(null); } // spawn trainCars & form carsPerStartingTrack Debug.Log("[PersistentJobs] load: spawning trainCars"); List <TrainCar> orderedTrainCars = new List <TrainCar>(); List <CarsPerTrack> carsPerStartingTrack = new List <CarsPerTrack>(); for (int i = 0; i < tracks.Count; i++) { int rangeStart = i * countCarsPerTrainset + Math.Min(i, countTrainsetsWithExtraCar); int rangeCount = i < countTrainsetsWithExtraCar ? countCarsPerTrainset + 1 : countCarsPerTrainset; Debug.Log(string.Format( "[PersistentJobs] load: spawning cars in range [{0}-{1}) from total range [0-{2})", rangeStart, rangeStart + rangeCount, orderedTrainCarTypes.Count)); Track startingTrack = tracks[i]; RailTrack railTrack = SingletonBehaviour <LogicController> .Instance.LogicToRailTrack[startingTrack]; List <TrainCar> spawnedCars = CarSpawner.SpawnCarTypesOnTrack( orderedTrainCarTypes.GetRange(rangeStart, rangeCount), railTrack, true, 0.0, false, true); if (spawnedCars == null) { Debug.LogWarning("[PersistentJobs] load: Failed to spawn some trainCars!"); SingletonBehaviour <CarSpawner> .Instance.DeleteTrainCars(orderedTrainCars, true); return(null); } orderedTrainCars.AddRange(spawnedCars); carsPerStartingTrack.Add( new CarsPerTrack(startingTrack, (from car in spawnedCars select car.logicCar).ToList())); } JobChainControllerWithEmptyHaulGeneration jcc = GenerateShuntingLoadJobWithExistingCars( startingStation, carsPerStartingTrack, destStation, orderedTrainCars, orderedCargoTypes, rng, true); if (jcc == null) { Debug.LogWarning("[PersistentJobs] load: Couldn't generate job chain. Deleting spawned trainCars!"); SingletonBehaviour <CarSpawner> .Instance.DeleteTrainCars(orderedTrainCars, true); return(null); } return(jcc); }