static bool Prefix(CommercialBuildingAI __instance, ItemClass.Level level, Randomizer r, int productionRate, out int electricityConsumption, out int waterConsumption, out int sewageAccumulation, out int garbageAccumulation, out int incomeAccumulation, out int mailAccumulation)
        {
            ItemClass item = __instance.m_info.m_class;

            int[] array = CommercialBuildingAIMod.GetArray(__instance.m_info, (int)level);

            electricityConsumption = array[DataStore.POWER];
            waterConsumption       = array[DataStore.WATER];
            sewageAccumulation     = array[DataStore.SEWAGE];
            garbageAccumulation    = array[DataStore.GARBAGE];
            mailAccumulation       = array[DataStore.MAIL];

            int landVal = AI_Utils.GetLandValueIncomeComponent(r.seed);

            incomeAccumulation = array[DataStore.INCOME] + landVal;

            electricityConsumption = Mathf.Max(100, productionRate * electricityConsumption) / 100;
            waterConsumption       = Mathf.Max(100, productionRate * waterConsumption) / 100;
            sewageAccumulation     = Mathf.Max(100, productionRate * sewageAccumulation) / 100;
            garbageAccumulation    = Mathf.Max(100, productionRate * garbageAccumulation) / 100;
            incomeAccumulation     = productionRate * incomeAccumulation;
            mailAccumulation       = Mathf.Max(100, productionRate * mailAccumulation) / 100;

            // Don't execute base method after this.
            return(false);
        }
        static bool Prefix(ref int __result, CommercialBuildingAI __instance, ItemClass.Level level, Randomizer r, int width, int length)
        {
            PrefabEmployStruct visitors;


            // All commercial places will need visitors. CalcWorkplaces is normally called first, redirected above to include a calculation of worker visits (CalculateprefabWorkerVisit).
            // However, there is a problem with some converted assets that don't come through the "front door" (i.e. Ploppable RICO - see below).

            // Try to retrieve previously calculated value.
            if (!DataStore.prefabWorkerVisit.TryGetValue(__instance.m_info.gameObject.GetHashCode(), out visitors))
            {
                // If we didn't get a value, most likely it was because the prefab wasn't properly initialised.
                // This can happen with Ploppable RICO when the underlying asset class isn't 'Default' (for example, where Ploppable RICO assets are originally Parks, Plazas or Monuments).
                // When that happens, the above line returns zero, which sets the building to 'Not Operating Properly'.
                // So, if the call returns false, we force a recalculation of workplace visits to make sure.

                // If it's still zero after this, then we'll just return a "legitimate" zero.
                visitors.visitors = 0;

                int[] array = CommercialBuildingAIMod.GetArray(__instance.m_info, (int)level);
                AI_Utils.CalculateprefabWorkerVisit(width, length, ref __instance.m_info, 4, ref array, out visitors);
                DataStore.prefabWorkerVisit.Add(__instance.m_info.gameObject.GetHashCode(), visitors);

                // Log this to help identify specific issues.  Should only occur once per prefab.
                Debugging.Message("CalculateprefabWorkerVisit redux: " + __instance.m_info.name);
            }

            // Original method return value.
            __result = visitors.visitors;

            // Don't execute base method after this.
            return(false);
        }
        static bool Prefix(ref int __result, CommercialBuildingAI __instance, ItemClass.Level level, Randomizer r, int width, int length)
        {
            ItemClass @class = __instance.m_info.m_class;

            int[] array = CommercialBuildingAIMod.GetArray(__instance.m_info, (int)level);

            // Original method return value.
            __result = Mathf.Max(100, width * length * array[DataStore.PRODUCTION]) / 100;

            // Don't execute base method after this.
            return(false);
        }
        static bool Prefix(CommercialBuildingAI __instance, ItemClass.Level level, int productionRate, DistrictPolicies.CityPlanning cityPlanningPolicies, out int groundPollution, out int noisePollution)
        {
            ItemClass item = __instance.m_info.m_class;

            int[] array = CommercialBuildingAIMod.GetArray(__instance.m_info, (int)level);

            groundPollution = array[DataStore.GROUND_POLLUTION];
            noisePollution  = (productionRate * array[DataStore.NOISE_POLLUTION]) / 100;
            if (item.m_subService == ItemClass.SubService.CommercialLeisure)
            {
                if ((cityPlanningPolicies & DistrictPolicies.CityPlanning.NoLoudNoises) != DistrictPolicies.CityPlanning.None)
                {
                    noisePollution /= 2;
                }
            }

            // Don't execute base method after this.
            return(false);
        }
Exemplo n.º 5
0
        /// <summary>
        /// Sets the customised number of workers for a given prefab.
        /// If a record doesn't already exist, a new one will be created.
        /// </summary>
        /// <param name="prefab">The prefab (BuildingInfo) to set</param>
        /// <param name="workers">The updated worker count</param>
        public static void SetWorker(BuildingInfo prefab, int workers)
        {
            // Update or add entry to configuration file cache.
            if (DataStore.workerCache.ContainsKey(prefab.name))
            {
                // Prefab already has a record; update.
                DataStore.workerCache[prefab.name] = workers;
            }
            else
            {
                // Prefab doesn't already have a record; create.
                DataStore.workerCache.Add(prefab.name, workers);
            }

            // Save the updated configuration file.
            XMLUtilsWG.WriteToXML();

            // Get current building hash (for updating prefab dictionary).
            var prefabHash = prefab.gameObject.GetHashCode();

            // Calculate employment breakdown.
            int[] array = CommercialBuildingAIMod.GetArray(prefab, (int)prefab.GetClassLevel());
            PrefabEmployStruct output = new PrefabEmployStruct();

            AI_Utils.CalculateprefabWorkerVisit(prefab.GetWidth(), prefab.GetLength(), ref prefab, 4, ref array, out output);

            // Update entry in 'live' settings.
            if (DataStore.prefabWorkerVisit.ContainsKey(prefabHash))
            {
                // Prefab already has a record; update.
                DataStore.prefabWorkerVisit[prefabHash] = output;
            }
            else
            {
                // Prefab doesn't already have a record; create.
                DataStore.prefabWorkerVisit.Add(prefabHash, output);
            }
        }
        static bool Prefix(CommercialBuildingAI __instance, ItemClass.Level level, Randomizer r, int width, int length, out int level0, out int level1, out int level2, out int level3)
        {
            ulong        seed = r.seed;
            BuildingInfo item = __instance.m_info;

            PrefabEmployStruct output;

            // If not seen prefab, calculate
            if (!DataStore.prefabWorkerVisit.TryGetValue(item.gameObject.GetHashCode(), out output))
            {
                int[] array = CommercialBuildingAIMod.GetArray(__instance.m_info, (int)level);
                AI_Utils.CalculateprefabWorkerVisit(width, length, ref item, 4, ref array, out output);
                DataStore.prefabWorkerVisit.Add(item.gameObject.GetHashCode(), output);
            }

            level0 = output.level0;
            level1 = output.level1;
            level2 = output.level2;
            level3 = output.level3;

            // Don't execute base method after this.
            return(false);
        }
        /// <summary>
        /// Called whenever the currently selected building is changed to update the panel display.
        /// </summary>
        /// <param name="building"></param>
        public void SelectionChanged(BuildingInfo building)
        {
            if ((building == null) || (building.name == null))
            {
                // If no valid building selected, then hide the calculations panel.
                detailsPanel.height    = 0;
                detailsPanel.isVisible = false;
                return;
            }

            // Variables to compare actual counts vs. mod count, to see if there's another mod overriding counts.
            int appliedCount;
            int modCount;

            // Building model size, not plot size.
            Vector3 buildingSize = building.m_size;
            int     floorCount;

            // Array used for calculations depending on building service/subservice (via DataStore).
            int[] array;
            // Default minimum number of homes or jobs is one; different service types will override this.
            int minHomesJobs = 1;
            int customHomeJobs;

            // Check for valid building AI.
            if (!(building.GetAI() is PrivateBuildingAI buildingAI))
            {
                Debugging.Message("invalid building AI type in building details");
                return;
            }

            // Residential vs. workplace AI.
            if (buildingAI is ResidentialBuildingAI)
            {
                // Get appropriate calculation array.
                array = ResidentialBuildingAIMod.GetArray(building, (int)building.GetClassLevel());

                // Set calculated homes label.
                homesJobsCalcLabel.text = Translations.Translate("RPR_CAL_HOM_CALC");

                // Set customised homes label and get value (if any).
                homesJobsCustomLabel.text = Translations.Translate("RPR_CAL_HOM_CUST");
                customHomeJobs            = ExternalCalls.GetResidential(building);

                // Applied homes is what's actually being returned by the CaclulateHomeCount call to this building AI.
                // It differs from calculated homes if there's an override value for that building with this mod, or if another mod is overriding.
                appliedCount = buildingAI.CalculateHomeCount(building.GetClassLevel(), new Randomizer(0), building.GetWidth(), building.GetLength());
                homesJobsActualLabel.text = Translations.Translate("RPR_CAL_HOM_APPL") + appliedCount;
            }
            else
            {
                // Workplace AI.
                // Default minimum number of jobs is 4.
                minHomesJobs = 4;

                // Find the correct array for the relevant building AI.
                switch (building.GetService())
                {
                case ItemClass.Service.Commercial:
                    array = CommercialBuildingAIMod.GetArray(building, (int)building.GetClassLevel());
                    break;

                case ItemClass.Service.Office:
                    array = OfficeBuildingAIMod.GetArray(building, (int)building.GetClassLevel());
                    break;

                case ItemClass.Service.Industrial:
                    if (buildingAI is IndustrialExtractorAI)
                    {
                        array = IndustrialExtractorAIMod.GetArray(building, (int)building.GetClassLevel());
                    }
                    else
                    {
                        array = IndustrialBuildingAIMod.GetArray(building, (int)building.GetClassLevel());
                    }
                    break;

                default:
                    Debugging.Message("invalid building service in building details");
                    return;
                }

                // Set calculated jobs label.
                homesJobsCalcLabel.text = Translations.Translate("RPR_CAL_JOB_CALC") + " ";

                // Set customised jobs label and get value (if any).
                homesJobsCustomLabel.text = Translations.Translate("RPR_CAL_JOB_CUST") + " ";
                customHomeJobs            = ExternalCalls.GetWorker(building);

                // Applied jobs is what's actually being returned by the CalculateWorkplaceCount call to this building AI.
                // It differs from calculated jobs if there's an override value for that building with this mod, or if another mod is overriding.
                int[] jobs = new int[4];
                buildingAI.CalculateWorkplaceCount(building.GetClassLevel(), new Randomizer(0), building.GetWidth(), building.GetLength(), out jobs[0], out jobs[1], out jobs[2], out jobs[3]);
                appliedCount = jobs[0] + jobs[1] + jobs[2] + jobs[3];
                homesJobsActualLabel.text = Translations.Translate("RPR_CAL_JOB_APPL") + " " + appliedCount;
            }

            // Reproduce CalcBase calculations to get building area.
            int calcWidth  = building.GetWidth();
            int calcLength = building.GetLength();

            floorCount = Mathf.Max(1, Mathf.FloorToInt(buildingSize.y / array[DataStore.LEVEL_HEIGHT]));

            // If CALC_METHOD is zero, then calculations are based on building model size, not plot size.
            if (array[DataStore.CALC_METHOD] == 0)
            {
                // If asset has small x dimension, then use plot width in squares x 6m (75% of standard width) instead.
                if (buildingSize.x <= 1)
                {
                    calcWidth *= 6;
                }
                else
                {
                    calcWidth = (int)buildingSize.x;
                }

                // If asset has small z dimension, then use plot length in squares x 6m (75% of standard length) instead.
                if (buildingSize.z <= 1)
                {
                    calcLength *= 6;
                }
                else
                {
                    calcLength = (int)buildingSize.z;
                }
            }
            else
            {
                // If CALC_METHOD is nonzero, then caluclations are based on plot size, not building size.
                // Plot size is 8 metres per square.
                calcWidth  *= 8;
                calcLength *= 8;
            }

            // Display calculated (and retrieved) details.
            detailLabels[(int)Details.width].text       = Translations.Translate("RPR_CAL_BLD_X") + " " + calcWidth;
            detailLabels[(int)Details.length].text      = Translations.Translate("RPR_CAL_BLD_Z") + " " + calcLength;
            detailLabels[(int)Details.height].text      = Translations.Translate("RPR_CAL_BLD_Y") + " " + (int)buildingSize.y;
            detailLabels[(int)Details.personArea].text  = Translations.Translate("RPR_CAL_BLD_M2") + " " + array[DataStore.PEOPLE];
            detailLabels[(int)Details.floorHeight].text = Translations.Translate("RPR_CAL_FLR_Y") + " " + array[DataStore.LEVEL_HEIGHT];
            detailLabels[(int)Details.floors].text      = Translations.Translate("RPR_CAL_FLR") + " " + floorCount;

            // Area calculation - will need this later.
            int calculatedArea = calcWidth * calcLength;

            detailLabels[(int)Details.area].text = Translations.Translate("RPR_CAL_M2") + " " + calculatedArea;

            // Show or hide extra floor modifier as appropriate (hide for zero or less, otherwise show).
            if (array[DataStore.DENSIFICATION] > 0)
            {
                detailLabels[(int)Details.extraFloors].text      = Translations.Translate("RPR_CAL_FLR_M") + " " + array[DataStore.DENSIFICATION];
                detailLabels[(int)Details.extraFloors].isVisible = true;
            }
            else
            {
                detailLabels[(int)Details.extraFloors].isVisible = false;
            }

            // Set minimum residences for high density.
            if ((building.GetSubService() == ItemClass.SubService.ResidentialHigh) || (building.GetSubService() == ItemClass.SubService.ResidentialHighEco))
            {
                // Minimum of 2, or 90% number of floors, whichever is greater. This helps the 1x1 high density.
                minHomesJobs = Mathf.Max(2, Mathf.CeilToInt(0.9f * floorCount));
            }

            // Perform actual household or workplace calculation.
            modCount = Mathf.Max(minHomesJobs, (calculatedArea * (floorCount + Mathf.Max(0, array[DataStore.DENSIFICATION]))) / array[DataStore.PEOPLE]);
            homesJobsCalcLabel.text += modCount;

            // Set customised homes/jobs label (leave blank if no custom setting retrieved).
            if (customHomeJobs > 0)
            {
                homesJobsCustomLabel.text += customHomeJobs.ToString();

                // Update modCount to reflect the custom figures.
                modCount = customHomeJobs;
            }

            // Check to see if Ploppable RICO Revisited is controlling this building's population.
            if (ModUtils.CheckRICO(building))
            {
                messageLabel.text = Translations.Translate("RPR_CAL_RICO");
                messageLabel.Show();
            }
            else
            {
                // Hide message text by default.
                messageLabel.Hide();
            }

            // We've got a valid building and results, so show panel.
            detailsPanel.height    = 270;
            detailsPanel.isVisible = true;
        }