Exemplo n.º 1
0
        /// <summary>
        /// Load settings from XML file.
        /// </summary>
        internal static void LoadSettings()
        {
            try
            {
                // Check to see if configuration file exists.
                if (File.Exists(ConfigFileName))
                {
                    // Read it.
                    using (StreamReader reader = new StreamReader(ConfigFileName))
                    {
                        XmlSerializer xmlSerializer = new XmlSerializer(typeof(XMLConfigurationFile));

                        if (!(xmlSerializer.Deserialize(reader) is XMLConfigurationFile configFile))
                        {
                            Logging.Error("couldn't deserialize configuration file");
                        }
                        else
                        {
                            // Deserialise population calculation packs.
                            foreach (PopPackXML xmlPack in configFile.popPacks)
                            {
                                // Convert to volumetric pack.
                                VolumetricPopPack volPack = new VolumetricPopPack()
                                {
                                    name        = xmlPack.name,
                                    displayName = xmlPack.name,
                                    service     = xmlPack.service,
                                    version     = (int)DataVersion.customOne,
                                    levels      = new LevelData[xmlPack.calculationLevels.Count]
                                };

                                // Iterate through each level in the xml and add to our volumetric pack.
                                foreach (PopLevel calculationLevel in xmlPack.calculationLevels)
                                {
                                    volPack.levels[calculationLevel.level] = new LevelData()
                                    {
                                        //floorHeight = calculationLevel.floorHeight,
                                        emptyArea       = calculationLevel.emptyArea,
                                        emptyPercent    = calculationLevel.emptyPercent,
                                        areaPer         = calculationLevel.areaPer,
                                        multiFloorUnits = calculationLevel.multiLevel
                                    };
                                }

                                // Add new pack to our dictionary.
                                PopData.instance.AddCalculationPack(volPack);
                            }

                            // Deserialise floor calculation packs.
                            foreach (FloorPackXML xmlPack in configFile.floorPacks)
                            {
                                // Convert to floor pack.
                                FloorDataPack floorPack = new FloorDataPack()
                                {
                                    name            = xmlPack.name,
                                    displayName     = xmlPack.name,
                                    version         = (int)DataVersion.customOne,
                                    floorHeight     = xmlPack.floorHeight,
                                    firstFloorMin   = xmlPack.firstMin,
                                    firstFloorExtra = xmlPack.firstExtra,
                                    firstFloorEmpty = xmlPack.firstEmpty
                                };

                                // Add new pack to our dictionary.
                                FloorData.instance.AddCalculationPack(floorPack);
                            }


                            // Deserialise consumption records.
                            DataMapping mapper = new DataMapping();
                            foreach (ConsumptionRecord consumptionRecord in configFile.consumption)
                            {
                                // Get relevant DataStore array for this record.
                                int[][] dataArray = mapper.GetArray(consumptionRecord.service, consumptionRecord.subService);

                                // Iterate through each consumption line and populate relevant DataStore fields.
                                foreach (ConsumptionLine consumptionLine in consumptionRecord.levels)
                                {
                                    int level = (int)consumptionLine.level;
                                    dataArray[level][DataStore.POWER]            = consumptionLine.power;
                                    dataArray[level][DataStore.WATER]            = consumptionLine.water;
                                    dataArray[level][DataStore.SEWAGE]           = consumptionLine.sewage;
                                    dataArray[level][DataStore.GARBAGE]          = consumptionLine.garbage;
                                    dataArray[level][DataStore.GROUND_POLLUTION] = consumptionLine.pollution;
                                    dataArray[level][DataStore.NOISE_POLLUTION]  = consumptionLine.noise;
                                    dataArray[level][DataStore.MAIL]             = consumptionLine.mail;
                                    dataArray[level][DataStore.INCOME]           = consumptionLine.income;
                                }
                            }

                            // Deserialise default pack lists.
                            PopData.instance.DeserializeDefaults(configFile.popDefaults);
                            FloorData.instance.DeserializeDefaults(configFile.floorDefaults);

                            // Deserialise building pack lists.
                            PopData.instance.DeserializeBuildings(configFile.buildings);
                            FloorData.instance.DeserializeBuildings(configFile.buildings);
                            SchoolData.instance.DeserializeBuildings(configFile.buildings);
                            Multipliers.instance.DeserializeBuildings(configFile.buildings);

                            // Deserialise building population overrides.
                            PopData.instance.DeserializeOverrides(configFile.popOverrides);

                            // Deserialize floor overrides.
                            foreach (FloorCalcOverride floorOverride in configFile.floors)
                            {
                                FloorData.instance.SetOverride(floorOverride.prefab, new FloorDataPack
                                {
                                    firstFloorMin = floorOverride.firstHeight,
                                    floorHeight   = floorOverride.floorHeight
                                });
                            }

                            // Deserialise visit modes.
                            RealisticVisitplaceCount.DeserializeVisits(configFile.visitorModes);

                            // Deserialise commercial sales multipliers.
                            GoodsUtils.DeserializeSalesMults(configFile.salesMults);

                            // Deserialise office production multipliers.
                            RealisticOfficeProduction.DeserializeProdMults(configFile.offProdMults);

                            // Deserialize industrial production calculation modes.
                            RealisticIndustrialProduction.DeserializeProds(configFile.indProdModes);
                            RealisticExtractorProduction.DeserializeProds(configFile.extProdModes);

                            // Deserialise commercial inventory caps.
                            GoodsUtils.DeserializeInvCaps(configFile.comIndCaps);
                        }
                    }
                }
        /// <summary>
        /// Perform and display volumetric calculations for the currently selected building.
        /// </summary>
        /// <param name="building">Selected building prefab</param>
        /// <param name="levelData">Population (level) calculation data to apply to calculations</param>
        /// <param name="floorData">Floor calculation data to apply to calculations</param>
        /// <param name="schoolData">School calculation data to apply to calculations</param>
        /// <param name="schoolData">Multiplier to apply to calculations</param>
        internal void CalculateVolumetric(BuildingInfo building, LevelData levelData, FloorDataPack floorData, SchoolDataPack schoolData, float multiplier)
        {
            // Safety first!
            if (building == null)
            {
                return;
            }

            // Reset message label.
            messageLabel.text = string.Empty;

            // Perform calculations.
            // Get floors and allocate area an number of floor labels.
            SortedList <int, float> floors = PopData.instance.VolumetricFloors(building.m_generatedInfo, floorData, out float totalArea);

            floorAreaLabel.text = totalArea.ToString("N0", LocaleManager.cultureInfo);
            numFloorsLabel.text = floors.Count.ToString();

            // Get total units.
            int totalUnits = PopData.instance.VolumetricPopulation(building.m_generatedInfo, levelData, floorData, multiplier, floors, totalArea);

            // Floor labels list.
            List <string> floorLabels = new List <string>();

            // What we call our units for this building.
            string unitName;

            switch (building.GetService())
            {
            case ItemClass.Service.Residential:
                // Residential - households.
                unitName = Translations.Translate("RPR_CAL_UNI_HOU");
                break;

            case ItemClass.Service.Education:
                // Education - students.
                unitName = Translations.Translate("RPR_CAL_UNI_STU");
                break;

            default:
                // Default - workplaces.
                unitName = Translations.Translate("RPR_CAL_UNI_WOR");
                break;
            }

            // See if we're using area calculations for numbers of units, i.e. areaPer is at least one.
            if (levelData.areaPer > 0)
            {
                // Determine area percentage to use for calculations (inverse of empty area percentage).
                float areaPercent = 1 - (levelData.emptyPercent / 100f);

                // Create new floor area labels by iterating through each floor.
                for (int i = 0; i < floors.Count; ++i)
                {
                    // StringBuilder, because we're doing a fair bit of manipulation here.
                    StringBuilder floorString = new StringBuilder("Floor ");

                    // Floor number
                    floorString.Append(i + 1);
                    floorString.Append(" " + Translations.Translate("RPR_CAL_VOL_ARA") + " ");
                    floorString.Append(floors[i].ToString("N0"));

                    // See if we're calculating units per individual floor.
                    if (!levelData.multiFloorUnits)
                    {
                        // Number of units on this floor - always rounded down.
                        int floorUnits = (int)((floors[i] * areaPercent) / levelData.areaPer);
                        // Adjust by multiplier (after rounded calculation above).
                        floorUnits = (int)(floorUnits * multiplier);

                        // Add extra info to label.
                        floorString.Append(" (");
                        floorString.Append(floorUnits.ToString("N0"));
                        floorString.Append(" ");
                        floorString.Append(unitName);
                        floorString.Append(")");
                    }

                    // Add new floor label item with results for this calculation.
                    floorLabels.Add(floorString.ToString());
                }
            }

            // Do we have a current school selection, and are we using school property overrides?
            if (schoolData != null && ModSettings.enableSchoolProperties)
            {
                // Yes - calculate and display school worker breakdown.
                int[] workers = SchoolData.instance.CalcWorkers(schoolData, totalUnits);
                schoolWorkerLabel.Show();
                schoolWorkerLabel.text = workers[0] + " / " + workers[1] + " / " + workers[2] + " / " + workers[3];

                // Calculate construction cost to display.
                int cost = SchoolData.instance.CalcCost(schoolData, totalUnits);
                ColossalFramework.Singleton <EconomyManager> .instance.m_EconomyWrapper.OnGetConstructionCost(ref cost, building.m_class.m_service, building.m_class.m_subService, building.m_class.m_level);

                // Calculate maintenance cost to display.
                int maintenance = SchoolData.instance.CalcMaint(schoolData, totalUnits) * 100;
                ColossalFramework.Singleton <EconomyManager> .instance.m_EconomyWrapper.OnGetMaintenanceCost(ref maintenance, building.m_class.m_service, building.m_class.m_subService, building.m_class.m_level);

                float displayMaint = Mathf.Abs(maintenance * 0.0016f);

                // And display school cost breakdown.
                costLabel.Show();
                costLabel.text = cost.ToString((!(displayMaint >= 10f)) ? Settings.moneyFormat : Settings.moneyFormatNoCents, LocaleManager.cultureInfo) + " / " + displayMaint.ToString((!(displayMaint >= 10f)) ? Settings.moneyFormat : Settings.moneyFormatNoCents, LocaleManager.cultureInfo);

                // Enforce school floors list position.
                ResetFloorListPosition();
            }
            else
            {
                // No - hide school worker breakdown and cost labels.
                schoolWorkerLabel.Hide();
                costLabel.Hide();

                // Enforce default floors list position.
                ResetFloorListPosition();
            }

            // Allocate our new list of labels to the floors list (via an interim fastlist to avoid race conditions if we 'build' manually directly into floorsList).
            FastList <object> fastList = new FastList <object>()
            {
                m_buffer = floorLabels.ToArray(),
                m_size   = floorLabels.Count
            };

            floorsList.rowsData = fastList;

            // Display total unit calculation result.
            switch (building.GetService())
            {
            case ItemClass.Service.Residential:
                // Residential building.
                totalJobsLabel.Hide();
                totalStudentsLabel.Hide();
                totalHomesLabel.Show();
                totalHomesLabel.text = totalUnits.ToString("N0", LocaleManager.cultureInfo);
                break;

            case ItemClass.Service.Education:
                // School building.
                totalHomesLabel.Hide();
                totalJobsLabel.Hide();
                totalStudentsLabel.Show();
                totalStudentsLabel.text = totalUnits.ToString("N0", LocaleManager.cultureInfo);
                break;

            default:
                // Workplace building.
                totalHomesLabel.Hide();
                totalStudentsLabel.Hide();
                totalJobsLabel.Show();
                totalJobsLabel.text = totalUnits.ToString("N0", LocaleManager.cultureInfo);
                break;
            }

            // Display commercial visit count, or hide the label if not commercial.
            if (building.GetAI() is CommercialBuildingAI)
            {
                visitCountLabel.Show();
                visitCountLabel.text = RealisticVisitplaceCount.PreviewVisitCount(building, totalUnits).ToString();
            }
            else
            {
                visitCountLabel.Hide();
            }

            // Display production count, or hide the label if not a production building.
            if (building.GetAI() is PrivateBuildingAI privateAI && (privateAI is OfficeBuildingAI || privateAI is IndustrialBuildingAI || privateAI is IndustrialExtractorAI))
            {
                productionLabel.Show();
                productionLabel.text = privateAI.CalculateProductionCapacity(building.GetClassLevel(), new ColossalFramework.Math.Randomizer(), building.GetWidth(), building.GetLength()).ToString();
            }