/// <summary> /// Helper method for dumping the contents of an offer, for debugging purposes. /// </summary> /// <param name="offer"></param> /// <param name="material"></param> /// <returns></returns> public static string ToString(ref TransferManager.TransferOffer offer, TransferManager.TransferReason material) { var outsideOfferText = TransferManagerInfo.IsOutsideOffer(ref offer) ? "(O)" : ""; if (offer.NetSegment != 0) { return($"Id=S{offer.NetSegment}, (Amt,Mat,Pri,Exc,Act)=({offer.Amount},{material},{offer.Priority},{offer.Exclude},{offer.Active})"); } if (offer.Vehicle != 0) { var homeBuilding = VehicleManager.instance.m_vehicles.m_buffer[offer.Vehicle].m_sourceBuilding; return($"Id=V{offer.Vehicle}, Home=B{homeBuilding}{outsideOfferText}, (Amt,Mat,Pri,Exc,Act)=({offer.Amount},{material},{offer.Priority},{offer.Exclude},{offer.Active})"); } if (offer.Citizen != 0) { var homeBuilding = CitizenManager.instance.m_citizens.m_buffer[offer.Citizen].m_homeBuilding; return($"Id=C{offer.Citizen}, Home=B{homeBuilding}{outsideOfferText}, (Amt,Mat,Pri,Exc,Act)=({offer.Amount},{material},{offer.Priority},{offer.Exclude},{offer.Active})"); } if (offer.Building != 0) { return($"Id=B{offer.Building}{outsideOfferText}, (Amt,Mat,Pri,Exc,Act)=({offer.Amount},{material},{offer.Priority},{offer.Exclude},{offer.Active})"); } return($"Id=0, (Amt,Mat,Pri,Exc,Act)=({offer.Amount},{material},{offer.Priority},{offer.Exclude},{offer.Active})"); }
/// <summary> /// Sets the priority of outside connection offers to 0, while ensuring that local offers have priority 1 /// or greater. /// </summary> /// <remarks> /// We are a modifying the priority of offers as a way of prioritizing the local supply chain, and only /// resorting to outside connections if materials cannot be found locally. /// </remarks> /// <param name="material"></param> /// <param name="offer"></param> public static void ModifyOffer(TransferManager.TransferReason material, ref TransferManager.TransferOffer offer) { var isOutsideOffer = TransferManagerInfo.IsOutsideOffer(ref offer); if (isOutsideOffer) { offer.Priority = 0; } else { offer.Priority = Mathf.Clamp(offer.Priority + 1, 1, 7); } if (offer.Vehicle != 0) { offer.Priority = 7; } }
/// <summary> /// Returns true if we can potentially match the two given offers. /// </summary> /// <returns></returns> private static bool IsValidLowPriorityOffer( TransferManager.TransferReason material, ref TransferManager.TransferOffer requestOffer, int requestPriority, ref TransferManager.TransferOffer responseOffer, int responsePriority) { var requestBuilding = TransferManagerInfo.GetHomeBuilding(ref requestOffer); var responseBuilding = TransferManagerInfo.GetHomeBuilding(ref responseOffer); if (responseBuilding == 0) { Logger.LogMaterial( $"TransferManager::IsValidLowPriorityOffer: {Utils.ToString(ref responseOffer, material)}, not a district services building", material); return(false); } // Special logic if both buildings are warehouses. Used to prevent goods from being shuffled back and forth between warehouses. if (BuildingManager.instance.m_buildings.m_buffer[requestBuilding].Info.GetAI() is WarehouseAI && BuildingManager.instance.m_buildings.m_buffer[responseBuilding].Info.GetAI() is WarehouseAI) { return(false); } // Special logic for recycling centers, since they can produce recycled goods but the district policies // should not apply to these materials. if (responseBuilding != 0 && BuildingManager.instance.m_buildings.m_buffer[responseBuilding].Info.GetAI() is LandfillSiteAI) { if (TransferManagerInfo.IsOutsideOffer(ref requestOffer)) { Logger.LogMaterial( $"TransferManager::IsValidLowPriorityOffer: {Utils.ToString(ref responseOffer, material)}, allow recycling centers", material); return(true); } // Only allow if there are no restrictions on the request, OR if recycling center resides in an allowed district. var requestDistrictParksServed = Constraints.InputDistrictParkServiced(requestBuilding); var responseDistrictPark = TransferManagerInfo.GetDistrictPark(responseBuilding); if (Constraints.InputAllLocalAreas(requestBuilding) || responseDistrictPark.IsServedBy(requestDistrictParksServed)) { Logger.LogMaterial( $"TransferManager::IsValidLowPriorityOffer: {Utils.ToString(ref responseOffer, material)}, allow recycling centers", material); return(true); } else { return(false); } } // See if the request is from an outside connection ... if (TransferManagerInfo.IsOutsideOffer(ref requestOffer)) { if (TransferManagerInfo.IsOutsideOffer(ref responseOffer)) { // Prevent matching roads that are too close together ... var distanceSquared = Vector3.SqrMagnitude(responseOffer.Position - requestOffer.Position); return(distanceSquared > 100000); } else if (TransferManagerInfo.GetSupplyBuildingAmount(responseBuilding) > Constraints.InternalSupplyBuffer(responseBuilding)) { Logger.LogMaterial( $"TransferManager::IsValidLowPriorityOffer: {Utils.ToString(ref responseOffer, material)}, internal supply buffer, supply amount={TransferManagerInfo.GetSupplyBuildingAmount(responseBuilding)}, supply buffer={Constraints.InternalSupplyBuffer(responseBuilding)}", material); return(true); } else if (Constraints.OutputOutsideConnections(responseBuilding)) { Logger.LogMaterial( $"TransferManager::IsValidLowPriorityOffer: {Utils.ToString(ref responseOffer, material)}, matched inside to outside offer", material); return(true); } else { Logger.LogMaterial( $"TransferManager::IsValidLowPriorityOffer: {Utils.ToString(ref responseOffer, material)}, disallowed outside offer", material); return(false); } } // Here, we are guaranteed that the request is a local offer. if (TransferManagerInfo.IsOutsideBuilding(responseBuilding)) { // Don't be so aggressive in trying to serve low priority orders with outside connections. if (requestPriority > 1 && Constraints.InputOutsideConnections(requestBuilding)) { Logger.LogMaterial( $"TransferManager::IsValidLowPriorityOffer: {Utils.ToString(ref responseOffer, material)}, matched outside to inside offer", material); return(true); } } else if (TransferManagerInfo.GetSupplyBuildingAmount(responseBuilding) > Constraints.InternalSupplyBuffer(responseBuilding)) { // Only allow if the request building allows all incoming shipments if (Constraints.InputAllLocalAreas(requestBuilding)) { Logger.LogMaterial( $"TransferManager::IsValidLowPriorityOffer: {Utils.ToString(ref responseOffer, material)}, internal supply buffer, supply amount={TransferManagerInfo.GetSupplyBuildingAmount(responseBuilding)}, supply buffer={Constraints.InternalSupplyBuffer(responseBuilding)}", material); return(true); } } Logger.LogMaterial( $"TransferManager::IsValidLowPriorityOffer: {Utils.ToString(ref responseOffer, material)}, not valid", material); return(false); }
/// <summary> /// Returns true if we can potentially match the two given offers. /// </summary> /// <returns></returns> private static bool IsValidSupplyChainOffer( TransferManager.TransferReason material, ref TransferManager.TransferOffer requestOffer, int requestPriority, ref TransferManager.TransferOffer responseOffer, int responsePriority) { var requestBuilding = TransferManagerInfo.GetHomeBuilding(ref requestOffer); var responseBuilding = TransferManagerInfo.GetHomeBuilding(ref responseOffer); if (responseBuilding == 0) { Logger.LogMaterial( $"TransferManager::IsValidSupplyChainOffer: {Utils.ToString(ref responseOffer, material)}, not a district services building", material); return(false); } // First check if a supply link exists. var responseSupplyDestinations = Constraints.SupplyDestinations(responseBuilding); if (responseSupplyDestinations?.Count > 0) { for (int i = 0; i < responseSupplyDestinations.Count; i++) { if (responseSupplyDestinations[i] == (int)requestBuilding) { Logger.LogMaterial( $"TransferManager::IsValidSupplyChainOffer: {Utils.ToString(ref responseOffer, material)}, supply link allowed", material); return(true); } } } // Special logic if both buildings are warehouses. Used to prevent goods from being shuffled back and forth between warehouses. if (BuildingManager.instance.m_buildings.m_buffer[requestBuilding].Info.GetAI() is WarehouseAI && BuildingManager.instance.m_buildings.m_buffer[responseBuilding].Info.GetAI() is WarehouseAI) { return(false); } // Now match on all local areas and district restrictions, both on request an response buildings! var requestDistrictPark = TransferManagerInfo.GetDistrictPark(material, ref requestOffer); var responseDistrictPark = TransferManagerInfo.GetDistrictPark(material, ref responseOffer); // If the request constrains the districts that it can accept orders from ... if (!Constraints.InputAllLocalAreas(requestBuilding)) { var requestDistrictParksServed = Constraints.InputDistrictParkServiced(requestBuilding); if (!responseDistrictPark.IsServedBy(requestDistrictParksServed)) { Logger.LogMaterial( $"TransferManager::IsValidSupplyChainOffer: {Utils.ToString(ref responseOffer, material)}, request is constrained to accept offers from certain districts only!", material); return(false); } } if (!Constraints.InputOutsideConnections(requestBuilding) && TransferManagerInfo.IsOutsideOffer(ref responseOffer)) { Logger.LogMaterial( $"TransferManager::IsValidSupplyChainOffer: {Utils.ToString(ref responseOffer, material)}, request is constrained not to accept outside offers!", material); return(false); } if (Constraints.OutputAllLocalAreas(responseBuilding)) { Logger.LogMaterial( $"TransferManager::IsValidSupplyChainOffer: {Utils.ToString(ref responseOffer, material)}, serves all local areas", material); 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(responseBuilding); if (requestDistrictPark.IsServedBy(responseDistrictParksServed)) { Logger.LogMaterial( $"TransferManager::IsValidSupplyChainOffer: {Utils.ToString(ref responseOffer, material)}, serves district {requestDistrictPark.Name}", material); return(true); } } Logger.LogMaterial( $"TransferManager::IsValidSupplyChainOffer: {Utils.ToString(ref responseOffer, material)}, not valid", material); return(false); }