/// <summary> /// Harmony Prefix patch to OfficeBuildingAI.CalculateProductionCapacity to implement mod production calculations. /// </summary> /// <param name="__result">Original method result</param> /// <param name="__instance">Original AI instance reference</param> /// <param name="level">Building level</param> /// <returns>False (don't execute base game method after this)</returns> public static bool Prefix(ref int __result, OfficeBuildingAI __instance, ItemClass.Level level) { // Get builidng info. BuildingInfo info = __instance.m_info; ItemClass.SubService subService = info.GetSubService(); // Get cached workplace count and calculate total workplaces. int[] workplaces = PopData.instance.WorkplaceCache(info, (int)level); int totalWorkers = workplaces[0] + workplaces[1] + workplaces[2] + workplaces[3]; // Using legacy settings? if (PopData.instance.ActivePack(info).version == (int)DataVersion.legacy) { // Legacy settings. int[] array = LegacyAIUtils.GetOfficeArray(info, (int)level); // Original method return value. __result = totalWorkers / array[DataStore.PRODUCTION]; } else { // Hew settings - multiply total workers by overall multiplier (from settings) to get result; divisor is 1,000 to match original mod 1/10th when at 100% production. __result = (totalWorkers * (subService == ItemClass.SubService.OfficeHightech ? highTechOfficeProdMult : genericOfficeProdMult)) / 1000; } // Always set at least one. if (__result < 1) { __result = 1; } // Don't execute base method after this. return(false); }
/// <summary> /// Pre-emptive Harmony Prefix patch for IndustrialExtractorAI.GetConsumptionRates, to implement the mod's consumption calculations. /// </summary> /// <param name="__instance">AI instance reference</param> /// <param name="r">Randomizer</param> /// <param name="productionRate">Building production rate</param> /// <param name="electricityConsumption">Building electricity consumption</param> /// <param name="waterConsumption">Building water consumption</param> /// <param name="sewageAccumulation">Building sewage accumulation</param> /// <param name="garbageAccumulation">Building garbage accumulation</param> /// <param name="incomeAccumulation">Building income accumulation</param> /// <param name="mailAccumulation">Building mail accumulation</param> /// <returns>Always false (never execute original method)</returns> public static bool Prefix(IndustrialExtractorAI __instance, Randomizer r, int productionRate, out int electricityConsumption, out int waterConsumption, out int sewageAccumulation, out int garbageAccumulation, out int incomeAccumulation, out int mailAccumulation) { // Get relevant array from datastore. int[] array = LegacyAIUtils.GetExtractorArray(__instance.m_info); // Get consumption rates from array. electricityConsumption = array[DataStore.POWER]; waterConsumption = array[DataStore.WATER]; sewageAccumulation = array[DataStore.SEWAGE]; garbageAccumulation = array[DataStore.GARBAGE]; mailAccumulation = array[DataStore.MAIL]; // Calculate land value. int landValue = LegacyAIUtils.GetLandValueIncomeComponent(r.seed); incomeAccumulation = array[DataStore.INCOME] + landValue; // Apply consumption rates. 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); }
/// <summary> /// Harmony Prefix patch to IndustrialBuildingAI.CalculateProductionCapacity to implement mod production calculations. /// </summary> /// <param name="__result">Original method result</param> /// <param name="__instance">Original AI instance reference</param> /// <param name="level">Building level</param> /// <returns>False (don't execute base game method after this)</returns> public static bool Prefix(ref int __result, IndustrialBuildingAI __instance, ItemClass.Level level, int width, int length) { // Get builidng info. BuildingInfo info = __instance.m_info; ItemClass.SubService subService = info.GetSubService(); // Array index. int arrayIndex = GetIndex(subService); // New or old method? if (prodModes[arrayIndex] == (int)ProdModes.popCalcs) { // New settings, based on population. float multiplier; switch (info.GetClassLevel()) { case ItemClass.Level.Level1: multiplier = 1f; break; case ItemClass.Level.Level2: multiplier = 0.933333f; break; default: multiplier = 0.8f; break; } // Get cached workplace count and calculate total workplaces. int[] workplaces = PopData.instance.WorkplaceCache(info, (int)level); float totalWorkers = workplaces[0] + workplaces[1] + workplaces[2] + workplaces[3]; // Multiply total workers by multipler and overall multiplier (from settings) to get result. __result = (int)((totalWorkers * multiplier * prodMults[arrayIndex]) / 100f); } else { // Legacy calcs. int[] array = LegacyAIUtils.GetIndustryArray(__instance.m_info, (int)level); // Original method return value. __result = Mathf.Max(100, width * length * array[DataStore.PRODUCTION]) / 100; } // Always set at least one. if (__result < 1) { Logging.Error("invalid production result ", __result.ToString(), " for ", __instance.m_info.name, "; setting to 1"); __result = 1; } // Don't execute base method after this. return(false); }
public static bool Prefix(OfficeBuildingAI __instance, ItemClass.Level level, out int groundPollution, out int noisePollution) { int[] array = LegacyAIUtils.GetOfficeArray(__instance.m_info, (int)level); groundPollution = array[DataStore.GROUND_POLLUTION]; noisePollution = array[DataStore.NOISE_POLLUTION]; // Don't execute base method after this. return(false); }
public static bool Prefix(IndustrialExtractorAI __instance, int productionRate, out int groundPollution, out int noisePollution) { int[] array = LegacyAIUtils.GetExtractorArray(__instance.m_info); groundPollution = (productionRate * array[DataStore.GROUND_POLLUTION]) / 100; noisePollution = (productionRate * array[DataStore.NOISE_POLLUTION]) / 100; // Don't execute base method after this. return(false); }
public static bool Prefix(ref int __result, CommercialBuildingAI __instance, ItemClass.Level level, int width, int length) { int[] array = LegacyAIUtils.GetCommercialArray(__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); }
public static bool Prefix(IndustrialBuildingAI __instance, ItemClass.Level level, int productionRate, out int groundPollution, out int noisePollution) { int[] array = LegacyAIUtils.GetIndustryArray(__instance.m_info, (int)level); groundPollution = (productionRate * array[DataStore.GROUND_POLLUTION]) / 100; noisePollution = (productionRate * array[DataStore.NOISE_POLLUTION]) / 100; // Don't execute base method after this. return(false); }
public 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 = LegacyAIUtils.GetCommercialArray(__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); }
/// <summary> /// Legacy visitplace count calculations. /// </summary> /// <param name="prefab">Building prefab</param> /// <param name="level">Building level </param> /// <returns>Calculated visitplace count</returns> internal static int LegacyVisitCount(BuildingInfo prefab, ItemClass.Level level) => UnityEngine.Mathf.Max(200, prefab.GetWidth() * prefab.GetWidth() * LegacyAIUtils.GetCommercialArray(prefab, (int)level)[DataStore.VISIT]) / 100;
/// <summary> /// Called whenever the currently selected building is changed to update the panel display. /// </summary> /// <param name="building">Newly selected building</param> public void SelectionChanged(BuildingInfo building) { // Make sure we have a valid selection before proceeding. if (building?.name == null) { 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)) { Logging.Error("invalid building AI type in building details for building ", building.name); return; } // Residential vs. workplace AI. if (buildingAI is ResidentialBuildingAI) { // Get appropriate calculation array. array = LegacyAIUtils.GetResidentialArray(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 = OverrideUtils.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 = LegacyAIUtils.GetCommercialArray(building, (int)building.GetClassLevel()); break; case ItemClass.Service.Office: array = LegacyAIUtils.GetOfficeArray(building, (int)building.GetClassLevel()); break; case ItemClass.Service.Industrial: if (buildingAI is IndustrialExtractorAI) { array = LegacyAIUtils.GetExtractorArray(building); } else { array = LegacyAIUtils.GetIndustryArray(building, (int)building.GetClassLevel()); } break; default: Logging.Error("invalid building service in building details for building ", building.name); 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 = OverrideUtils.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; // Show visitor count for commercial buildings. if (buildingAI is CommercialBuildingAI commercialAI) { visitCountLabel.Show(); visitCountLabel.text = Translations.Translate("RPR_CAL_VOL_VIS") + " " + commercialAI.CalculateVisitplaceCount(building.GetClassLevel(), new Randomizer(), building.GetWidth(), building.GetLength()); } 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 = Translations.Translate("RPR_CAL_VOL_PRD") + " " + privateAI.CalculateProductionCapacity(building.GetClassLevel(), new ColossalFramework.Math.Randomizer(), building.GetWidth(), building.GetLength()).ToString(); }