/// <summary> /// Add a supply chain link between the source and destination buildings. /// The supply chain link overrides all local area, all outside connections, and all district constraints. /// </summary> /// <param name="source"></param> /// <param name="destination"></param> public static void AddSupplyChainConnection(ushort source, ushort destination) { if (!TransferManagerInfo.IsSupplyChainBuilding(source) || !TransferManagerInfo.IsSupplyChainBuilding(destination)) { return; } if (!TransferManagerInfo.IsValidSupplyChainLink(source, destination)) { Logger.Log($"Constraints::AddSupplyChainConnection: Could not add invalid supply chain link: source={source}, destination={destination}"); return; } bool added = false; if (m_supplyDestinations[source] == null) { m_supplyDestinations[source] = new List <int>(); } if (!m_supplyDestinations[source].Contains(destination)) { added = true; m_supplyDestinations[source].Add(destination); } if (added) { var sourceBuildingName = TransferManagerInfo.GetBuildingName(source); var destinationBuildingName = TransferManagerInfo.GetBuildingName(destination); Logger.Log($"Constraints::AddSupplyChainConnection: {sourceBuildingName} ({source}) => {destinationBuildingName} ({destination}) ..."); } }
/// <summary> /// Returns the input type of the building, used to determine which GUI elements are shown in the main panel. /// </summary> /// <param name="building"></param> /// <returns></returns> public static InputType GetBuildingInputType(int building) { if (building == 0) { return(InputType.NONE); } var result = InputType.NONE; // The only building type for which we will not show an outgoing tab is coal and heating power plants. var info = BuildingManager.instance.m_buildings.m_buffer[building].Info; if (TransferManagerInfo.IsDistrictServicesBuilding(building)) { if ((info?.GetService() == ItemClass.Service.Electricity && info?.GetAI() is PowerPlantAI) || (info?.GetService() == ItemClass.Service.Water && info?.GetAI() is HeatingPlantAI) || (info?.GetService() == ItemClass.Service.Monument && info?.gameObject?.name == "ChirpX Launch Control Center")) { } else if (!Settings.enableIndustriesControl && info?.GetService() == ItemClass.Service.PlayerIndustry) { } else { result |= InputType.OUTGOING; } } if (Settings.enableIndustriesControl && TransferManagerInfo.IsSupplyChainBuilding(building)) { result |= InputType.SUPPLY_CHAIN; if (!(info?.GetAI() is ExtractingFacilityAI || info?.GetAI() is FishFarmAI || info?.GetAI() is FishingHarborAI)) { result |= InputType.INCOMING; } } if (TransferManagerInfo.IsCustomVehiclesBuilding(building)) { result |= InputType.VEHICLES; } return(result); }
/// <summary> /// Returns a descriptive text describing problems with this supply chain building ... /// </summary> /// <param name="building"></param> /// <returns></returns> public static string GetSupplyBuildingProblemsText(ushort building) { if (building == 0 || !TransferManagerInfo.IsSupplyChainBuilding(building)) { return(string.Empty); } bool FindSourceBuilding(TransferManager.TransferReason material) { if (material == TransferManager.TransferReason.None) { return(true); } // Assume for now that the outside connection can supply the building with the materials it needs. if (Constraints.InputOutsideConnections(building)) { return(true); } for (ushort buildingIn = 1; buildingIn < BuildingManager.MAX_BUILDING_COUNT; buildingIn++) { if (!TransferManagerInfo.IsSupplyChainBuilding(building)) { continue; } if (GetSupplyBuildingOutputMaterial(buildingIn) == material) { // Check if a supply link exists ... if (Constraints.SupplyDestinations(buildingIn)?.Count > 0 && Constraints.SupplyDestinations(buildingIn).Contains(building)) { return(true); } var requestDistrictPark = TransferManagerInfo.GetDistrictPark(building); var responseDistrictPark = TransferManagerInfo.GetDistrictPark(buildingIn); if (!Constraints.InputAllLocalAreas(building)) { var requestDistrictParksServed = Constraints.InputDistrictParkServiced(building); if (!responseDistrictPark.IsServedBy(requestDistrictParksServed)) { continue; } } if (Constraints.OutputAllLocalAreas(buildingIn)) { return(true); } else { // The call to TransferManagerInfo.GetDistrict applies to offers that are come from buildings, service // vehicles, citizens, AND segments. The latter needs to be considered for road maintenance. var responseDistrictParksServed = Constraints.OutputDistrictParkServiced(buildingIn); if (requestDistrictPark.IsServedBy(responseDistrictParksServed)) { return(true); } } } } return(false); } // Detect if we do have an other building that can supply materials to this building. List <TransferManager.TransferReason> notFound = new List <TransferManager.TransferReason>(); switch (BuildingManager.instance.m_buildings.m_buffer[building].Info?.GetAI()) { case ProcessingFacilityAI processingFacilityAI: if (!FindSourceBuilding(processingFacilityAI.m_inputResource1)) { notFound.Add(processingFacilityAI.m_inputResource1); } if (!FindSourceBuilding(processingFacilityAI.m_inputResource2)) { notFound.Add(processingFacilityAI.m_inputResource2); } if (!FindSourceBuilding(processingFacilityAI.m_inputResource3)) { notFound.Add(processingFacilityAI.m_inputResource3); } if (!FindSourceBuilding(processingFacilityAI.m_inputResource4)) { notFound.Add(processingFacilityAI.m_inputResource4); } break; default: break; } if (notFound.Count > 0) { return(string.Join(",", notFound.Select(x => x.ToString()).ToArray())); } else { return(string.Empty); } }
/// <summary> /// Helper method for displaying information, including district and supply chain constraints, about the /// building with given building id. /// </summary> /// <param name="building"></param> /// <returns></returns> private static string GetBuildingInfoText(ushort building) { var inputType = TransferManagerInfo.GetBuildingInputType(building); var txtItems = new List <string>(); txtItems.Add($"{TransferManagerInfo.GetBuildingName(building)} ({building})"); txtItems.Add(TransferManagerInfo.GetDistrictParkText(building)); // Early return. Rest of info pertains to building types that we deal with in the mod. if (!(TransferManagerInfo.IsDistrictServicesBuilding(building) || TransferManagerInfo.IsCustomVehiclesBuilding(building))) { return(string.Join("\n", txtItems.ToArray())); } txtItems.Add(TransferManagerInfo.GetBuildingInputTypeText(building)); txtItems.Add(TransferManagerInfo.GetServicesText(building)); if (!TransferManagerInfo.IsSupplyChainBuilding(building)) { if (TransferManagerInfo.IsDistrictServicesBuilding(building)) { txtItems.Add(""); txtItems.Add(TransferManagerInfo.GetOutputDistrictsServedText(building)); } if (Settings.enableCustomVehicles && !VehicleManagerMod.BuildingUseDefaultVehicles[building] && VehicleManagerMod.BuildingToVehicles[building] != null && (inputType & InputType.VEHICLES) != InputType.NONE) { txtItems.Add(""); txtItems.Add(TransferManagerInfo.GetCustomVehiclesText(building)); } return(string.Join("\n", txtItems.ToArray())); } if (Settings.enableIndustriesControl) { // From this point forth, we know this is a supply chain building ... txtItems.Add($"Supply Reserve: {Constraints.InternalSupplyBuffer(building)}"); if ((inputType & InputType.INCOMING) != InputType.NONE) { txtItems.Add(""); txtItems.Add(TransferManagerInfo.GetSupplyBuildingSourcesText(building)); } if ((inputType & InputType.OUTGOING) != InputType.NONE) { txtItems.Add(""); txtItems.Add(TransferManagerInfo.GetSupplyBuildingDestinationsText(building)); } if (Settings.enableCustomVehicles && !VehicleManagerMod.BuildingUseDefaultVehicles[building] && VehicleManagerMod.BuildingToVehicles[building] != null && (inputType & InputType.VEHICLES) != InputType.NONE) { txtItems.Add(""); txtItems.Add(TransferManagerInfo.GetCustomVehiclesText(building)); } var problemText = TransferManagerInfo.GetSupplyBuildingProblemsText(building); if (problemText != string.Empty) { txtItems.Add(""); txtItems.Add($"<<WARNING: Cannot find the following materials to procure!>>"); txtItems.Add(problemText); } } return(string.Join("\n", txtItems.ToArray())); }