static bool Prefix(ref int __result, ushort instanceID, ref CitizenInstance citizenData, Citizen.AgeGroup ageGroup) { // Cache as best we can. The order of calls is car, bike, taxi AIUtils.citizenCache = citizenData.m_citizen; // Not needed, but just in case Citizen citizen = Singleton <CitizenManager> .instance.m_citizens.m_buffer[(int)((UIntPtr)citizenData.m_citizen)]; ushort homeBuilding = citizen.m_homeBuilding; ItemClass.SubService subService = ItemClass.SubService.ResidentialLow; if (homeBuilding != 0) { DistrictManager instance = Singleton <DistrictManager> .instance; Building building = Singleton <BuildingManager> .instance.m_buildings.m_buffer[(int)homeBuilding]; District district = instance.m_districts.m_buffer[instance.GetDistrict(building.m_position)]; DistrictPolicies.CityPlanning cityPlanningPolicies = district.m_cityPlanningPolicies; AIUtils.livesInBike = (cityPlanningPolicies & DistrictPolicies.CityPlanning.EncourageBiking) != DistrictPolicies.CityPlanning.None; subService = Singleton <BuildingManager> .instance.m_buildings.m_buffer[homeBuilding].Info.GetSubService(); } // Set the cache AIUtils.cacheArray = AIUtils.GetArray(citizen.WealthLevel, subService, ageGroup); // Original method return value. __result = AIUtils.cacheArray[DataStore.CAR]; if (Debugging.UseTransportLog) { Debugging.WriteToLog(Debugging.TransportLogName, citizen.WealthLevel + "-wealth " + ageGroup + " has " + __result + "% chance of driving."); } // Don't execute base method after this. return(false); }
static bool Prefix(ref int __result, ushort instanceID, ref CitizenInstance citizenData, Citizen.AgeGroup ageGroup) { // Original method return value. // Array cache has already been set when GetCarProbability was called. __result = AIUtils.cacheArray[DataStore.TAXI]; if (Debugging.UseTransportLog) { Debugging.WriteToLog(Debugging.TransportLogName, "The same " + ageGroup + " has " + __result + "% chance of using a taxi."); } // Don't execute base method after this. return(false); }
/// <summary> /// Method called by OutsideConnectionAI.StartConnectionTransferImpl Transpiler insertion. /// Randomises (within parameters) age and education levels of immigrants. /// All variables are local variables within OutsideConnectionAI.StartConnectionTransferImpl. /// </summary> /// <param name="i">Loop counter (for loop containing method call)</param> /// <param name="education">Education level input (StartConnectionTransferImpl local variable)</param> /// <param name="ageArray">Array of acceptible ages (from mod DataStore); placed on stack in advance via Transpiler insertion</param> /// <param name="childrenAgeMax">Maximum child immigrant age; placed on stack in advance via Transpiler insertion </param> /// <param name="childrenAgeMin">Minimum child immigrant age; placed on stack in advance via Transpiler insertion</param> /// <param name="minAdultAge">Minimum adult immigrant age; placed on stack in advance via Transpiler insertion</param> /// <param name="resultEducation">Resultant education level for immigrant after mod calculations (StartConnectionTransferImpl local variable 'education2')</param> /// <param name="resultAge">Resultant age level for immigrant after mod calculations (StartConnectionTransferImpl local variable 'age')</param> public static void RandomizeImmigrants(int i, Citizen.Education education, int[] ageArray, ref int childrenAgeMax, ref int childrenAgeMin, ref int minAdultAge, out Citizen.Education resultEducation, out int resultAge) { // Minimum and maximum ages. int min = ageArray[0]; int max = ageArray[1]; // We start inside an i loop. // i is is the family member number for this incoming family. 0 is primary adult, 1 is secondary adults, and after that are children. if (i == 1) { // Age of second adult - shouldn't be too far from the first. Just because. min = Math.Max(minAdultAge - 20, DataStore.incomingAdultAge[0]); max = Math.Min(minAdultAge + 20, DataStore.incomingAdultAge[1]); } else if (i >= 2) { // Children. min = childrenAgeMin; max = childrenAgeMax; } // Calculate actual age randomly between minumum and maxiumum. resultAge = Singleton <SimulationManager> .instance.m_randomizer.Int32(min, max); // Adust age brackets for subsequent family members. if (i == 0) { minAdultAge = resultAge; } else if (i == 1) { // Restrict to adult age. Young adult is 18 according to National Institutes of Health... even if the young adult section in a library isn't that range. minAdultAge = Math.Min(resultAge, minAdultAge); // Children should be between 80 and 180 younger than the youngest adult. childrenAgeMax = Math.Max(minAdultAge - 80, 0); // Allow people 10 ticks from 'adulthood' to have kids childrenAgeMin = Math.Max(minAdultAge - 178, 0); // Accounting gestation, which isn't simulated yet (2 ticks) } // Set default eductation output to what the game has already determined. resultEducation = education; // Apply education level randomisation if that option is selected. if (ModSettings.randomImmigrantEd) { if (i < 2) { // Adults. // 24% different education levels int eduModifier = Singleton <SimulationManager> .instance.m_randomizer.Int32(-12, 12) / 10; resultEducation += eduModifier; if (resultEducation < Citizen.Education.Uneducated) { resultEducation = Citizen.Education.Uneducated; } else if (resultEducation > Citizen.Education.ThreeSchools) { resultEducation = Citizen.Education.ThreeSchools; } } else { // Children. switch (Citizen.GetAgeGroup(resultAge)) { case Citizen.AgeGroup.Child: resultEducation = Citizen.Education.Uneducated; break; case Citizen.AgeGroup.Teen: resultEducation = Citizen.Education.OneSchool; break; default: // Make it that 80% graduate from high school resultEducation = (Singleton <SimulationManager> .instance.m_randomizer.Int32(0, 100) < 80) ? Citizen.Education.TwoSchools : Citizen.Education.OneSchool; break; } } } // Write to immigration log if that option is selected. if (Debugging.UseImmigrationLog) { Debugging.WriteToLog(Debugging.ImmigrationLogName, "Family member " + i + " immigrating with age " + resultAge + " (" + (int)(resultAge / 3.5) + " years old) and education level " + education + "."); } }
private static bool Prefix(ref bool __result, ref ResidentAI __instance, uint citizenID, ref Citizen data) { if ((citizenID % DataStore.lifeSpanMultiplier) == Threading.counter) { int num = data.Age + 1; // Threading.sb.Append(citizenID + ": " + num + "\n"); //Debugging.writeDebugToFile(citizenID + ": " + num + " " + Threading.counter); if (num <= 45) { if (num == 15 || num == 45) { FinishSchoolOrWorkRev(__instance, citizenID, ref data); } } else if (num == 90 || num >= ModSettings.retirementAge) { FinishSchoolOrWorkRev(__instance, citizenID, ref data); } else if ((data.m_flags & Citizen.Flags.Student) != Citizen.Flags.None && (num % 15 == 0)) // Workspeed multiplier? { FinishSchoolOrWorkRev(__instance, citizenID, ref data); } if ((data.m_flags & Citizen.Flags.Original) != Citizen.Flags.None) { CitizenManager instance = Singleton <CitizenManager> .instance; if (instance.m_tempOldestOriginalResident < num) { instance.m_tempOldestOriginalResident = num; } if (num == 240) { Singleton <StatisticsManager> .instance.Acquire <StatisticInt32>(StatisticType.FullLifespans).Add(1); } } data.Age = num; // Checking for death and sickness chances. // Citizens who are currently moving or currently in a vehicle aren't affected. if (data.CurrentLocation != Citizen.Location.Moving && data.m_vehicle == 0) { bool died = false; if (ModSettings.VanillaCalcs) { // Using vanilla lifecycle calculations. int num2 = 240; int num3 = 255; int num4 = Mathf.Max(0, 145 - (100 - data.m_health) * 3); if (num4 != 0) { num2 += num4 / 3; num3 += num4; } if (num >= num2) { bool flag = Singleton <SimulationManager> .instance.m_randomizer.Int32(2000u) < 3; died = (Singleton <SimulationManager> .instance.m_randomizer.Int32(num2 * 100, num3 * 100) / 100 <= num || flag); } } else { // Using custom lifecycle calculations. // Game defines years as being age divided by 3.5. Hence, 35 age increments per decade. // Legacy mod behaviour worked on 25 increments per decade. // If older than the maximum index - lucky them, but keep going using that final index. int index = Math.Min((int)(num * ModSettings.decadeFactor), 10); // Calculate 90% - 110%; using 100,000 as 100% (for precision). int modifier = 100000 + ((150 * data.m_health) + (50 * data.m_wellbeing) - 10000); // Death chance is simply if a random number between 0 and the modifier calculated above is less than the survival probability calculation for that decade of life. // Also set maximum age of 400 (~114 years) to be consistent with the base game. died = (Singleton <SimulationManager> .instance.m_randomizer.Int32(0, modifier) < DataStore.survivalProbCalc[index]) || num > 400; // Check for sickness chance if they haven't died. if (!died && Singleton <SimulationManager> .instance.m_randomizer.Int32(0, modifier) < DataStore.sicknessProbCalc[index]) { // Make people sick, if they're unlucky. data.Sick = true; if (Debugging.UseSicknessLog) { Debugging.WriteToLog(Debugging.SicknessLogName, "Citizen became sick with chance factor " + DataStore.sicknessProbCalc[index] + "."); } } } if (died) { if (Debugging.UseDeathLog) { Debugging.WriteToLog(Debugging.DeathLogName, "Citizen died at age: " + data.Age + " (" + (int)(data.Age / 3.5) + " years old)."); } // Reverse redirect to access private method Die(). DieRev(__instance, citizenID, ref data); // Chance for 'vanishing corpse' (no need for deathcare). if (!AIUtils.KeepCorpse()) { Singleton <CitizenManager> .instance.ReleaseCitizen(citizenID); return(true); } } } } // Original method return value. __result = false; // Don't execute base method after this. return(false); }