Пример #1
0
        public static bool CanUseTaxis(Vector3 startPosition, Vector3 endPosition)
        {
            var startDistrictPark = DistrictPark.FromPosition(startPosition);
            var endDistrictPark   = DistrictPark.FromPosition(endPosition);

            // Now see whether any taxi buildings serve this position.
            for (int i = 0; i < m_taxiBuildings.Count; i++)
            {
                var buildingId = m_taxiBuildings[i];
                if (BuildingManager.instance.m_buildings.m_buffer[buildingId].Info?.GetSubService() == ItemClass.SubService.PublicTransportTaxi)
                {
                    var districtParkServed = Constraints.OutputDistrictParkServiced((ushort)buildingId);
                    if (startDistrictPark.IsServedBy(districtParkServed) && endDistrictPark.IsServedBy(districtParkServed))
                    {
                        return(true);
                    }
                }
            }

            return(false);
        }
Пример #2
0
        /// <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);
            }
        }
Пример #3
0
        /// <summary>
        /// Returns a descriptive text indicating the supply chain source buildings that the given building
        /// will receive shipments from.
        /// </summary>
        /// <param name="building"></param>
        /// <returns></returns>
        public static string GetSupplyBuildingSourcesText(ushort building)
        {
            if (building == 0)
            {
                return(string.Empty);
            }

            var txtItems = new List <string>();

            txtItems.Add($"<<Supply Chain Shipments From>>");

            if (Constraints.InputAllLocalAreas(building))
            {
                txtItems.Add($"All local areas");
            }

            if (Constraints.InputOutsideConnections(building))
            {
                txtItems.Add($"All outside connections");
            }

            if (Constraints.InputAllLocalAreas(building))
            {
                return(string.Join("\n", txtItems.ToArray()));
            }

            // Next, list input building names
            var supply = Constraints.SupplySources(building);

            if (supply?.Count > 0)
            {
                var buildingNames = supply
                                    .Select(b => $"{GetBuildingName(b)} ({b})")
                                    .OrderBy(s => s);

                foreach (var buildingName in buildingNames)
                {
                    txtItems.Add(buildingName);
                }
            }

            // Then add district names
            var districts = Constraints.InputDistrictParkServiced(building);

            if (districts?.Count > 0)
            {
                var districtParkNames = districts
                                        .Select(dp => dp.Name)
                                        .OrderBy(s => s);

                foreach (var districtParkName in districtParkNames)
                {
                    txtItems.Add($"{districtParkName} (DISTRICT)");
                }
            }

            if (txtItems.Count == 1)
            {
                return($"<<WARNING: No supply chain shipments accepted!>>");
            }
            else
            {
                return(string.Join("\n", txtItems.ToArray()));
            }
        }
Пример #4
0
        /// <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()));
        }
            /// <summary>
            /// Principles:
            ///   1) max concurrent orders per building per type is capped per MAX_TTL day period
            ///   2)
            /// </summary>
            /// <param name="material"></param>
            /// <param name="requestBuilding"></param>
            /// <param name="responseBuilding"></param>
            /// <returns></returns>
            public bool IsRestricted(TransferManager.TransferReason material, ushort requestBuilding, ushort responseBuilding)
            {
                if (!Events.TryGetValue(requestBuilding, out var list))
                {
                    return(false);
                }

                var isRequestBuildingOutside  = TransferManagerInfo.IsOutsideBuilding(requestBuilding);
                var isResponseBuildingOutside = TransferManagerInfo.IsOutsideBuilding(responseBuilding);

                if (!Settings.enableDummyCargoTraffic.value && isRequestBuildingOutside && isResponseBuildingOutside)
                {
                    return(true);
                }

                var concurrentOrderCount = list.Count;

                var concurrentOrderCountToOutsideConnection = 0;
                var concurrentOrderCountToResponseBuilding  = 0;

                for (int i = 0; i < list.Count; i++)
                {
                    if (TransferManagerInfo.IsOutsideBuilding(list[i].ResponseBuilding))
                    {
                        concurrentOrderCountToOutsideConnection++;
                    }

                    if (list[i].ResponseBuilding == responseBuilding)
                    {
                        concurrentOrderCountToResponseBuilding++;
                    }
                }

                var vehicleCount1 = TransferManagerInfo.GetCargoVehicleCount(requestBuilding, material);
                var vehicleCount2 = TransferManagerInfo.GetCargoVehicleCount(responseBuilding, material);

                var maxConcurrentOrderCount = Math.Ceiling(Constraints.GlobalOutsideConnectionIntensity() / 10.0); // 500 setting = 50 count

                if (isRequestBuildingOutside && TransferManagerInfo.IsOutsideRoadConnection(requestBuilding))
                {
                    maxConcurrentOrderCount *= 4;
                }
                else
                {
                    maxConcurrentOrderCount *= 2;
                }

                var maxConcurrentOrderCountToResponseBuilding  = Math.Ceiling(maxConcurrentOrderCount / 2.0);
                var maxConcurrentOrderCountToOutsideConnection = Math.Ceiling(0.25 * maxConcurrentOrderCount * Constraints.GlobalOutsideToOutsidePerc() / 100.0);

                var maxVehicleCount = Math.Ceiling(maxConcurrentOrderCount / 4.0);

                // Logger.LogVerbose($"TransferHistory::IsRestricted: request={requestBuilding}, response={responseBuilding}, concurrentOrderCount = {concurrentOrderCount}, maxConcurrentOrderCount={maxConcurrentOrderCount}, concurrentOrderCountToOutsideConnection={concurrentOrderCountToOutsideConnection}, maxConcurrentOrderCountToOutsideConnection={maxConcurrentOrderCountToOutsideConnection}, vehicleCount1={vehicleCount1}, vehicleCount2={vehicleCount2}, maxVehicleCount={maxVehicleCount}");

                bool isRestrictedConcurrent                    = concurrentOrderCount >= maxConcurrentOrderCount;
                bool isRestrictedConcurrentToBuilding          = concurrentOrderCountToResponseBuilding >= maxConcurrentOrderCountToResponseBuilding;
                bool isRestrictedConcurrentToOutsideConnection = isRequestBuildingOutside && isResponseBuildingOutside && concurrentOrderCountToOutsideConnection >= maxConcurrentOrderCountToOutsideConnection;
                bool isVehicleConstrained = vehicleCount1 >= maxVehicleCount || vehicleCount2 >= maxVehicleCount;

                return(isRestrictedConcurrent || isRestrictedConcurrentToBuilding || isRestrictedConcurrentToOutsideConnection || isVehicleConstrained);
            }
        protected override void OnClick(UIMouseEventParameter p)
        {
            UIView.playSoundDelegate(this.clickSound, 1f);

            var tool = ToolsModifierControl.toolController.gameObject.GetComponent <EnhancedDistrictServicesTool>();

            if (p.buttons.IsFlagSet(UIMouseButton.Left) && tool != null)
            {
                if (Settings.modVersion.value != EnhancedDistrictServicesMod.version)
                {
                    Settings.modVersion.value         = EnhancedDistrictServicesMod.version;
                    Settings.showWelcomeMessage.value = true;
                }

                if (Settings.showWelcomeMessage)
                {
                    Settings.showWelcomeMessage.value = false;

                    Utils.DisplayMessage(
                        str1: "Enhanced District Services",
                        str2: $"Please check the EDS mod page for latest updates.  Please note that the Global Outside Connection Intensity value is now recommended to be at least 500!  Please change this setting in your games to allow more outside traffic.",
                        str3: "IconMessage");
                }
                else if (Settings.showWarnLowOutsideCapMessage && Constraints.GlobalOutsideConnectionIntensity() <= 100)
                {
                    Utils.DisplayMessage(
                        str1: "Enhanced District Services",
                        str2: $"Please note your city has a global outside connection intensity that is too low ({Constraints.GlobalOutsideConnectionIntensity()}).  Please readjust value to around 500 or above.  To permanently disable this warning, goto settings page to disable this warning message.",
                        str3: "IconMessage");
                }

                if (ToolsModifierControl.toolController.CurrentTool == tool)
                {
                    ToolsModifierControl.toolController.CurrentTool = ToolsModifierControl.GetTool <DefaultTool>();
                }
                else
                {
                    ToolsModifierControl.infoViewsPanel.CloseAllPanels();
                    ToolsModifierControl.mainToolbar.CloseEverything();

                    ToolsModifierControl.toolController.CurrentTool = tool;
                }
            }
        }
        /// <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);
        }