Exemplo n.º 1
0
        /// <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);
        }