private void DoScheduledHome(ref CitizenSchedule schedule, TAI instance, uint citizenId, ref TCitizen citizen) { ushort homeBuilding = CitizenProxy.GetHomeBuilding(ref citizen); if (homeBuilding == 0) { Log.Debug(LogCategory.State, $"WARNING: {GetCitizenDesc(citizenId, ref citizen)} is in corrupt state: want to go home with no home building. Releasing the poor citizen."); CitizenMgr.ReleaseCitizen(citizenId); schedule = default; return; } ushort currentBuilding = CitizenProxy.GetCurrentBuilding(ref citizen); CitizenProxy.RemoveFlags(ref citizen, Citizen.Flags.Evacuating); if (residentAI.StartMoving(instance, citizenId, ref citizen, currentBuilding, homeBuilding)) { CitizenProxy.SetVisitPlace(ref citizen, citizenId, 0); schedule.Schedule(ResidentState.Unknown); Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} is going from {currentBuilding} back home"); } else { Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} wanted to go home from {currentBuilding} but can't, waiting for the next opportunity"); } }
private bool RescheduleAtHome(ref CitizenSchedule schedule, uint citizenId, ref TCitizen citizen) { if (schedule.CurrentState != ResidentState.AtHome || TimeInfo.Now < schedule.ScheduledStateTime) { return(false); } if (schedule.ScheduledState != ResidentState.Relaxing && schedule.ScheduledState != ResidentState.Shopping) { return(false); } if (schedule.ScheduledState != ResidentState.Shopping && WeatherInfo.IsBadWeather) { Log.Debug(LogCategory.Schedule, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} re-schedules an activity because of bad weather"); schedule.Schedule(ResidentState.Unknown); return(true); } Citizen.AgeGroup age = CitizenProxy.GetAge(ref citizen); uint goingOutChance = schedule.ScheduledState == ResidentState.Shopping ? spareTimeBehavior.GetShoppingChance(age) : spareTimeBehavior.GetRelaxingChance(age, schedule.WorkShift); if (goingOutChance > 0) { return(false); } Log.Debug(LogCategory.Schedule, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} re-schedules an activity because of time"); schedule.Schedule(ResidentState.Unknown); return(true); }
private bool RescheduleVisit(ref CitizenSchedule schedule, ref TCitizen citizen, ushort currentBuilding) { if (schedule.ScheduledState != ResidentState.Relaxing && schedule.ScheduledState != ResidentState.Shopping && schedule.ScheduledState != ResidentState.Visiting) { return(false); } if (IsBadWeather()) { Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(0, ref citizen)} quits a visit because of bad weather (see next line for citizen ID)"); schedule.Schedule(ResidentState.AtHome, default); return(true); } if (IsBuildingNoiseRestricted(currentBuilding, currentBuilding)) { Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(0, ref citizen)} quits a visit because of NIMBY policy (see next line for citizen ID)"); schedule.Schedule(ResidentState.Unknown, default); return(true); } uint stayChance = spareTimeBehavior.GetGoOutChance(CitizenProxy.GetAge(ref citizen)); if (!Random.ShouldOccur(stayChance)) { Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(0, ref citizen)} quits a visit because of time (see next line for citizen ID)"); schedule.Schedule(ResidentState.AtHome, default); return(true); } return(false); }
/// <summary>Determines whether the specified citizen must be processed as a virtual citizen.</summary> /// <typeparam name="TAI">The type of the citizen's AI.</typeparam> /// <param name="humanAI">The citizen AI reference.</param> /// <param name="citizen">The citizen to check.</param> /// <param name="realizeCitizen">A callback to determine whether a virtual citizen should be realized.</param> /// <returns><c>true</c> if the citizen must be processed as a virtual citizen; otherwise, <c>false</c>.</returns> protected bool IsCitizenVirtual <TAI>(TAI humanAI, ref TCitizen citizen, Func <TAI, bool> realizeCitizen) { uint virtualChance; switch (Config.VirtualCitizens) { case VirtualCitizensLevel.None: return(false); case VirtualCitizensLevel.Few: virtualChance = FewVirtualCitizensChance; break; case VirtualCitizensLevel.Vanilla: return(CitizenProxy.GetInstance(ref citizen) == 0 && !realizeCitizen(humanAI)); case VirtualCitizensLevel.Many: virtualChance = ManyVirtualCitizensChance; break; default: return(false); } return(!Random.ShouldOccur(virtualChance)); }
private bool StartMovingToVisitBuilding(TAI instance, uint citizenId, ref TCitizen citizen, ushort visitBuilding, bool isVirtual) { if (visitBuilding == 0) { return(false); } ushort currentBuilding = CitizenProxy.GetCurrentBuilding(ref citizen); if (isVirtual || currentBuilding == visitBuilding) { CitizenProxy.SetVisitPlace(ref citizen, citizenId, visitBuilding); CitizenProxy.SetVisitBuilding(ref citizen, visitBuilding); CitizenProxy.SetLocation(ref citizen, Citizen.Location.Visit); return(true); } else if (residentAI.StartMoving(instance, citizenId, ref citizen, currentBuilding, visitBuilding)) { CitizenProxy.SetVisitPlace(ref citizen, citizenId, visitBuilding); CitizenProxy.SetVisitBuilding(ref citizen, visitBuilding); return(true); } return(false); }
private void DoScheduledShopping(ref CitizenSchedule schedule, TAI instance, uint citizenId, ref TCitizen citizen) { ushort currentBuilding = CitizenProxy.GetCurrentBuilding(ref citizen); if (schedule.Hint == ScheduleHint.LocalShoppingOnly) { schedule.Schedule(ResidentState.Unknown, default); ushort shop = MoveToCommercialBuilding(instance, citizenId, ref citizen, LocalSearchDistance); if (shop == 0) { Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} wanted go shopping, but didn't find a local shop"); } else { Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} goes shopping at a local shop {shop}"); } } else { uint moreShoppingChance = spareTimeBehavior.GetGoOutChance(CitizenProxy.GetAge(ref citizen)); ResidentState nextState = Random.ShouldOccur(moreShoppingChance) ? ResidentState.Unknown : ResidentState.Shopping; schedule.Schedule(nextState, default); if (schedule.CurrentState != ResidentState.Shopping) { Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} in state {schedule.CurrentState} wanna go shopping and schedules {nextState}, heading to a random shop"); residentAI.FindVisitPlace(instance, citizenId, currentBuilding, residentAI.GetShoppingReason(instance)); } } }
private void StartMovingToVisitBuilding(TAI instance, uint citizenId, ref TCitizen citizen, ushort currentBuilding, ushort visitBuilding) { if (touristAI.StartMoving(instance, citizenId, ref citizen, currentBuilding, visitBuilding)) { CitizenProxy.SetVisitPlace(ref citizen, citizenId, visitBuilding); } }
private bool CitizenReturnsHomeFromVisit(TAI instance, uint citizenId, ref TCitizen citizen) { ushort homeBuilding = CitizenProxy.GetHomeBuilding(ref citizen); if (homeBuilding == 0 || CitizenProxy.GetVehicle(ref citizen) != 0) { return(false); } ushort visitBuilding = CitizenProxy.GetVisitBuilding(ref citizen); switch (EventMgr.GetEventState(visitBuilding, TimeInfo.Now.AddHours(MaxHoursOnTheWay))) { case CityEventState.Upcoming: case CityEventState.Ongoing: return(false); case CityEventState.Finished: Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} returning from an event at {visitBuilding} back home to {homeBuilding}"); ReturnFromVisit(instance, citizenId, ref citizen, homeBuilding); return(true); } if (IsChance(ReturnFromVisitChance)) { Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} returning from visit back home"); ReturnFromVisit(instance, citizenId, ref citizen, homeBuilding); return(true); } return(false); }
private ushort MoveToCommercialBuilding(TAI instance, uint citizenId, ref TCitizen citizen, float distance, bool isVirtual) { ushort buildingId = CitizenProxy.GetCurrentBuilding(ref citizen); if (buildingId == 0) { return(0); } ushort foundBuilding = BuildingMgr.FindActiveBuilding(buildingId, distance, ItemClass.Service.Commercial); if (IsBuildingNoiseRestricted(foundBuilding)) { Log.Debug($"Citizen {citizenId} won't go to the commercial building {foundBuilding}, it has a NIMBY policy"); return(0); } if (StartMovingToVisitBuilding(instance, citizenId, ref citizen, foundBuilding, isVirtual)) { ushort homeBuilding = CitizenProxy.GetHomeBuilding(ref citizen); uint homeUnit = BuildingMgr.GetCitizenUnit(homeBuilding); uint citizenUnit = CitizenProxy.GetContainingUnit(ref citizen, citizenId, homeUnit, CitizenUnit.Flags.Home); if (citizenUnit != 0) { CitizenMgr.ModifyUnitGoods(citizenUnit, ShoppingGoodsAmount); } } return(foundBuilding); }
/// <summary>Determines whether the specified citizen must be processed as a virtual citizen.</summary> /// <typeparam name="TAI">The type of the citizen's AI.</typeparam> /// <param name="humanAI">The citizen AI reference.</param> /// <param name="citizen">The citizen to check.</param> /// <param name="realizeCitizen">A callback to determine whether a virtual citizen should be realized.</param> /// <returns><c>true</c> if the citizen must be processed as a virtual citizen; otherwise, <c>false</c>.</returns> protected bool IsCitizenVirtual <TAI>(TAI humanAI, ref TCitizen citizen, Func <TAI, bool> realizeCitizen) { if (CitizenProxy.GetInstance(ref citizen) != 0) { return(false); } uint virtualChance; switch (Config.VirtualCitizens) { case VirtualCitizensLevel.None: return(false); case VirtualCitizensLevel.Few: virtualChance = FewVirtualCitizensChance; break; case VirtualCitizensLevel.Vanilla: return(!realizeCitizen(humanAI)); case VirtualCitizensLevel.Many: virtualChance = ManyVirtualCitizensChance; break; default: return(false); } return(CitizenMgr.GetInstancesCount() * 100 / CitizenInstancesMaxCount < virtualChance ? !realizeCitizen(humanAI) : Random.ShouldOccur(virtualChance)); }
private void ReturnFromVisit( TAI instance, uint citizenId, ref TCitizen citizen, ushort targetBuilding, Citizen.Location targetLocation, bool isVirtual) { if (targetBuilding == 0 || targetLocation == Citizen.Location.Visit || CitizenProxy.GetVehicle(ref citizen) != 0) { return; } ushort currentBuilding = CitizenProxy.GetCurrentBuilding(ref citizen); CitizenProxy.RemoveFlags(ref citizen, Citizen.Flags.Evacuating); CitizenProxy.SetVisitPlace(ref citizen, citizenId, 0); if (isVirtual || targetBuilding == currentBuilding) { CitizenProxy.SetLocation(ref citizen, targetLocation); } else { residentAI.StartMoving(instance, citizenId, ref citizen, currentBuilding, targetBuilding); } }
private bool CitizenReturnsHomeFromVisit(TAI instance, uint citizenId, ref TCitizen citizen, bool isVirtual) { ushort homeBuilding = CitizenProxy.GetHomeBuilding(ref citizen); if (homeBuilding == 0 || CitizenProxy.GetVehicle(ref citizen) != 0) { return(false); } ushort visitBuilding = CitizenProxy.GetVisitBuilding(ref citizen); switch (EventMgr.GetEventState(visitBuilding, TimeInfo.Now.AddHours(MaxHoursOnTheWay))) { case CityEventState.Upcoming: case CityEventState.Ongoing: return(false); case CityEventState.Finished: Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen, isVirtual)} returning from an event at {visitBuilding} back home to {homeBuilding}"); ReturnFromVisit(instance, citizenId, ref citizen, homeBuilding, Citizen.Location.Home, isVirtual); return(true); } ItemClass.SubService visitedSubService = BuildingMgr.GetBuildingSubService(visitBuilding); if (Random.ShouldOccur(ReturnFromVisitChance) || (visitedSubService == ItemClass.SubService.CommercialLeisure && TimeInfo.IsNightTime && BuildingMgr.IsBuildingNoiseRestricted(visitBuilding))) { Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen, isVirtual)} returning from visit back home"); ReturnFromVisit(instance, citizenId, ref citizen, homeBuilding, Citizen.Location.Home, isVirtual); return(true); } return(false); }
private void DoScheduledWork(ref CitizenSchedule schedule, TAI instance, uint citizenId, ref TCitizen citizen) { ushort currentBuilding = CitizenProxy.GetCurrentBuilding(ref citizen); schedule.WorkStatus = WorkStatus.Working; schedule.DepartureToWorkTime = default; if (currentBuilding == schedule.WorkBuilding && schedule.CurrentState != ResidentState.AtSchoolOrWork) { CitizenProxy.SetVisitPlace(ref citizen, citizenId, 0); CitizenProxy.SetLocation(ref citizen, Citizen.Location.Work); } else if (residentAI.StartMoving(instance, citizenId, ref citizen, currentBuilding, schedule.WorkBuilding) && schedule.CurrentState == ResidentState.AtHome) { schedule.DepartureToWorkTime = TimeInfo.Now; } Citizen.AgeGroup citizenAge = CitizenProxy.GetAge(ref citizen); if (workBehavior.ScheduleLunch(ref schedule, citizenAge)) { Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} is going from {currentBuilding} to school/work {schedule.WorkBuilding} and will go to lunch at {schedule.ScheduledStateTime}"); } else { workBehavior.ScheduleReturnFromWork(ref schedule, citizenAge); Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} is going from {currentBuilding} to school/work {schedule.WorkBuilding} and will leave work at {schedule.ScheduledStateTime}"); } }
private void ProcessMoving(TAI instance, uint citizenId, ref TCitizen citizen) { ushort instanceId = CitizenProxy.GetInstance(ref citizen); ushort vehicleId = CitizenProxy.GetVehicle(ref citizen); if (instanceId == 0) { if (vehicleId == 0) { CitizenMgr.ReleaseCitizen(citizenId); } return; } if (vehicleId == 0 && CitizenMgr.IsAreaEvacuating(instanceId) && !CitizenProxy.HasFlags(ref citizen, Citizen.Flags.Evacuating)) { Log.Debug(TimeInfo.Now, $"Tourist {GetCitizenDesc(citizenId, ref citizen)} was on the way, but the area evacuates. Leaving the city."); touristAI.FindVisitPlace(instance, citizenId, CitizenProxy.GetCurrentBuilding(ref citizen), touristAI.GetLeavingReason(instance, citizenId, ref citizen)); return; } if (CitizenMgr.InstanceHasFlags(instanceId, CitizenInstance.Flags.TargetIsNode | CitizenInstance.Flags.OnTour, true)) { FindRandomVisitPlace(instance, citizenId, ref citizen, TouristDoNothingProbability, 0); } }
private bool ScheduleShopping(ref CitizenSchedule schedule, ref TCitizen citizen, bool localOnly) { if (!CitizenProxy.HasFlags(ref citizen, Citizen.Flags.NeedGoods) || IsBadWeather()) { return(false); } if (!Random.ShouldOccur(spareTimeBehavior.GetGoOutChance(CitizenProxy.GetAge(ref citizen))) || !Random.ShouldOccur(GoShoppingChance)) { return(false); } if (TimeInfo.IsNightTime || localOnly || Random.ShouldOccur(Config.LocalBuildingSearchQuota)) { schedule.Hint = ScheduleHint.LocalShoppingOnly; } else { schedule.Hint = ScheduleHint.None; } schedule.Schedule(ResidentState.Shopping, default); return(true); }
private bool CitzenReturnsFromShelter(TAI instance, uint citizenId, ref TCitizen citizen) { ushort visitBuilding = CitizenProxy.GetVisitBuilding(ref citizen); if (BuildingMgr.GetBuildingService(visitBuilding) != ItemClass.Service.Disaster) { return(true); } if (!BuildingMgr.BuildingHasFlags(visitBuilding, Building.Flags.Downgrading)) { return(false); } ushort homeBuilding = CitizenProxy.GetHomeBuilding(ref citizen); if (homeBuilding == 0) { Log.Debug($"WARNING: {GetCitizenDesc(citizenId, ref citizen)} was in a shelter but seems to be homeless. Releasing the citizen."); CitizenMgr.ReleaseCitizen(citizenId); return(true); } Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} returning from evacuation place back home"); ReturnFromVisit(instance, citizenId, ref citizen, homeBuilding); return(true); }
private bool ScheduleRelaxing(ref CitizenSchedule schedule, uint citizenId, ref TCitizen citizen) { Citizen.AgeGroup citizenAge = CitizenProxy.GetAge(ref citizen); if (!Random.ShouldOccur(spareTimeBehavior.GetGoOutChance(citizenAge)) || IsBadWeather()) { return(false); } ICityEvent cityEvent = GetUpcomingEventToAttend(citizenId, ref citizen); if (cityEvent != null) { ushort currentBuilding = CitizenProxy.GetCurrentBuilding(ref citizen); DateTime departureTime = cityEvent.StartTime.AddHours(-GetEstimatedTravelTime(currentBuilding, cityEvent.BuildingId)); schedule.Schedule(ResidentState.Relaxing, departureTime); schedule.EventBuilding = cityEvent.BuildingId; schedule.Hint = ScheduleHint.AttendingEvent; return(true); } schedule.Schedule(ResidentState.Relaxing, default); schedule.Hint = TimeInfo.IsNightTime ? ScheduleHint.RelaxAtLeisureBuilding : ScheduleHint.None; return(true); }
private bool RescheduleAtHome(ref CitizenSchedule schedule, ref TCitizen citizen) { if (schedule.CurrentState != ResidentState.AtHome || TimeInfo.Now < schedule.ScheduledStateTime) { return(false); } if (schedule.ScheduledState != ResidentState.Relaxing && schedule.ScheduledState != ResidentState.Shopping) { return(false); } if (IsBadWeather()) { Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(0, ref citizen)} re-schedules an activity because of bad weather (see next line for citizen ID)"); schedule.Schedule(ResidentState.Unknown, default); return(true); } uint goOutChance = spareTimeBehavior.GetGoOutChance( CitizenProxy.GetAge(ref citizen), schedule.WorkShift, schedule.ScheduledState == ResidentState.Shopping); if (Random.ShouldOccur(goOutChance)) { return(false); } Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(0, ref citizen)} re-schedules an activity because of time (see next line for citizen ID)"); schedule.Schedule(ResidentState.Unknown, default); return(true); }
private bool CitizenGoesRelaxing(TAI instance, uint citizenId, ref TCitizen citizen) { Citizen.AgeGroup citizenAge = CitizenProxy.GetAge(ref citizen); if (!IsChance(GetGoOutChance(citizenAge))) { return(false); } ushort buildingId = CitizenProxy.GetCurrentBuilding(ref citizen); if (buildingId == 0) { return(false); } if (TimeInfo.IsNightTime) { ushort leisure = MoveToLeisure(instance, citizenId, ref citizen, buildingId); Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} wanna relax at night, trying leisure area '{leisure}'"); return(leisure != 0); } if (CitizenProxy.GetWorkBuilding(ref citizen) != 0 && IsWorkDayMorning(citizenAge)) { return(false); } Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} wanna relax, heading to an entertainment place"); residentAI.FindVisitPlace(instance, citizenId, buildingId, residentAI.GetEntertainmentReason(instance)); return(true); }
/// <summary> /// The entry method of the custom AI. /// </summary> /// /// <param name="instance">A reference to an object instance of the original AI.</param> /// <param name="citizenId">The ID of the citizen to process.</param> /// <param name="citizen">A <typeparamref name="TCitizen"/> reference to process.</param> public void UpdateLocation(TAI instance, uint citizenId, ref TCitizen citizen) { if (!EnsureCitizenCanBeProcessed(citizenId, ref citizen)) { return; } if (CitizenProxy.IsDead(ref citizen) || CitizenProxy.IsSick(ref citizen)) { CitizenMgr.ReleaseCitizen(citizenId); return; } switch (CitizenProxy.GetLocation(ref citizen)) { case Citizen.Location.Home: case Citizen.Location.Work: CitizenMgr.ReleaseCitizen(citizenId); break; case Citizen.Location.Visit: ProcessVisit(instance, citizenId, ref citizen); break; case Citizen.Location.Moving: ProcessMoving(instance, citizenId, ref citizen); break; } }
private void DoScheduledHome(ref CitizenSchedule schedule, TAI instance, uint citizenId, ref TCitizen citizen) { ushort homeBuilding = CitizenProxy.GetHomeBuilding(ref citizen); if (homeBuilding == 0) { Log.Debug(LogCategory.State, $"{GetCitizenDesc(citizenId, ref citizen)} is currently homeless. Cannot move home, waiting for the next opportunity"); return; } ushort currentBuilding = CitizenProxy.GetCurrentBuilding(ref citizen); CitizenProxy.RemoveFlags(ref citizen, Citizen.Flags.Evacuating); #if DEBUG string logEntry = $"{GetCitizenDesc(citizenId, ref citizen)} is going from {currentBuilding} back home"; #else const string logEntry = null; #endif if (residentAI.StartMoving(instance, citizenId, ref citizen, currentBuilding, homeBuilding)) { CitizenProxy.SetVisitPlace(ref citizen, citizenId, 0); schedule.Schedule(ResidentState.Unknown); Log.Debug(LogCategory.Movement, TimeInfo.Now, logEntry); } else { Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} wanted to go home from {currentBuilding} but can't, waiting for the next opportunity"); } }
private void FindRandomVisitPlace(TAI instance, uint citizenId, ref TCitizen citizen, int doNothingProbability, ushort currentBuilding) { int targetType = touristAI.GetRandomTargetType(instance, doNothingProbability); if (targetType == 1) { Log.Debug(TimeInfo.Now, $"Tourist {GetCitizenDesc(citizenId, ref citizen)} decides to leave the city"); touristAI.FindVisitPlace(instance, citizenId, currentBuilding, touristAI.GetLeavingReason(instance, citizenId, ref citizen)); return; } uint goOutChance = spareTimeBehavior.GetGoOutChance( CitizenProxy.GetAge(ref citizen), WorkShift.Unemployed, CitizenProxy.HasFlags(ref citizen, Citizen.Flags.NeedGoods)); if (!Random.ShouldOccur(goOutChance) || IsBadWeather()) { FindHotel(instance, citizenId, ref citizen); return; } switch (targetType) { case 2: touristAI.FindVisitPlace(instance, citizenId, currentBuilding, touristAI.GetShoppingReason(instance)); Log.Debug(TimeInfo.Now, $"Tourist {GetCitizenDesc(citizenId, ref citizen)} stays in the city, goes shopping"); break; case 3: Log.Debug(TimeInfo.Now, $"Tourist {GetCitizenDesc(citizenId, ref citizen)} stays in the city, goes relaxing"); touristAI.FindVisitPlace(instance, citizenId, currentBuilding, touristAI.GetEntertainmentReason(instance)); break; } }
private void FindHotel(TAI instance, uint citizenId, ref TCitizen citizen) { ushort currentBuilding = CitizenProxy.GetCurrentBuilding(ref citizen); if (!Random.ShouldOccur(FindHotelChance)) { Log.Debug(TimeInfo.Now, $"Tourist {GetCitizenDesc(citizenId, ref citizen)} didn't want to stay in a hotel, leaving the city"); touristAI.FindVisitPlace(instance, citizenId, currentBuilding, touristAI.GetLeavingReason(instance, citizenId, ref citizen)); return; } ushort hotel = BuildingMgr.FindActiveBuilding( currentBuilding, FullSearchDistance, ItemClass.Service.Commercial, ItemClass.SubService.CommercialTourist); if (hotel == 0) { Log.Debug(TimeInfo.Now, $"Tourist {GetCitizenDesc(citizenId, ref citizen)} didn't find a hotel, leaving the city"); touristAI.FindVisitPlace(instance, citizenId, currentBuilding, touristAI.GetLeavingReason(instance, citizenId, ref citizen)); return; } StartMovingToVisitBuilding(instance, citizenId, ref citizen, currentBuilding, hotel); Log.Debug(TimeInfo.Now, $"Tourist {GetCitizenDesc(citizenId, ref citizen)} stays in a hotel {hotel}"); }
private bool ScheduleRelaxing(ref CitizenSchedule schedule, uint citizenId, ref TCitizen citizen) { Citizen.AgeGroup citizenAge = CitizenProxy.GetAge(ref citizen); uint relaxChance = spareTimeBehavior.GetRelaxingChance(citizenAge, schedule.WorkShift, schedule.WorkStatus == WorkStatus.OnVacation); relaxChance = AdjustRelaxChance(relaxChance, ref citizen); if (!Random.ShouldOccur(relaxChance) || WeatherInfo.IsBadWeather) { return(false); } ICityEvent cityEvent = GetUpcomingEventToAttend(citizenId, ref citizen); if (cityEvent != null) { ushort currentBuilding = CitizenProxy.GetCurrentBuilding(ref citizen); DateTime departureTime = cityEvent.StartTime.AddHours(-travelBehavior.GetEstimatedTravelTime(currentBuilding, cityEvent.BuildingId)); schedule.Schedule(ResidentState.Relaxing, departureTime); schedule.EventBuilding = cityEvent.BuildingId; schedule.Hint = ScheduleHint.AttendingEvent; return(true); } schedule.Schedule(ResidentState.Relaxing); schedule.Hint = TimeInfo.IsNightTime && Random.ShouldOccur(NightLeisureChance) ? ScheduleHint.RelaxAtLeisureBuilding : ScheduleHint.None; return(true); }
private bool ScheduleShopping(ref CitizenSchedule schedule, ref TCitizen citizen, bool localOnly) { // If the citizen doesn't need any goods, he/she still can go shopping just for fun if (!CitizenProxy.HasFlags(ref citizen, Citizen.Flags.NeedGoods)) { if (schedule.Hint == ScheduleHint.NoShoppingAnyMore || WeatherInfo.IsBadWeather || !Random.ShouldOccur(Config.ShoppingForFunQuota)) { schedule.Hint = ScheduleHint.None; return(false); } schedule.Hint = ScheduleHint.NoShoppingAnyMore; } else { schedule.Hint = ScheduleHint.None; } if (!Random.ShouldOccur(spareTimeBehavior.GetShoppingChance(CitizenProxy.GetAge(ref citizen)))) { return(false); } if (TimeInfo.IsNightTime || localOnly || Random.ShouldOccur(Config.LocalBuildingSearchQuota)) { schedule.Hint = ScheduleHint.LocalShoppingOnly; } schedule.Schedule(ResidentState.Shopping); return(true); }
private bool StartMovingToVisitBuilding(TAI instance, uint citizenId, ref TCitizen citizen, ushort visitBuilding) { if (visitBuilding == 0) { return(false); } ushort currentBuilding = CitizenProxy.GetCurrentBuilding(ref citizen); if (currentBuilding == visitBuilding) { CitizenProxy.SetVisitPlace(ref citizen, citizenId, visitBuilding); CitizenProxy.SetLocation(ref citizen, Citizen.Location.Visit); return(true); } CitizenProxy.SetVisitPlace(ref citizen, citizenId, visitBuilding); if (CitizenProxy.GetVisitBuilding(ref citizen) == 0) { // Building is full and doesn't accept visitors anymore return(false); } if (!residentAI.StartMoving(instance, citizenId, ref citizen, currentBuilding, visitBuilding)) { CitizenProxy.SetVisitPlace(ref citizen, citizenId, 0); return(false); } return(true); }
private bool ProcessCitizenSick(TAI instance, uint citizenId, ref TCitizen citizen) { Citizen.Location currentLocation = CitizenProxy.GetLocation(ref citizen); if (currentLocation == Citizen.Location.Moving) { return(false); } ushort currentBuilding = CitizenProxy.GetCurrentBuilding(ref citizen); if (currentLocation != Citizen.Location.Home && currentBuilding == 0) { Log.Debug(LogCategory.State, $"Teleporting {GetCitizenDesc(citizenId, ref citizen)} back home because they are sick but no building is specified"); CitizenProxy.SetLocation(ref citizen, Citizen.Location.Home); return(true); } if (currentLocation != Citizen.Location.Home && CitizenProxy.GetVehicle(ref citizen) != 0) { return(true); } if (currentLocation == Citizen.Location.Visit) { ItemClass.Service service = BuildingMgr.GetBuildingService(CitizenProxy.GetVisitBuilding(ref citizen)); if (service == ItemClass.Service.HealthCare || service == ItemClass.Service.Disaster) { return(true); } } Log.Debug(LogCategory.State, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} is sick, trying to get to a hospital"); residentAI.FindHospital(instance, citizenId, currentBuilding, TransferManager.TransferReason.Sick); return(true); }
private void ProcessCitizenAtHome(TAI instance, uint citizenId, ref TCitizen citizen) { if (CitizenProxy.GetHomeBuilding(ref citizen) == 0) { Log.Debug($"WARNING: {GetCitizenDesc(citizenId, ref citizen)} is in corrupt state: at home with no home building. Releasing the poor citizen."); CitizenMgr.ReleaseCitizen(citizenId); return; } ushort vehicle = CitizenProxy.GetVehicle(ref citizen); if (vehicle != 0) { Log.Debug(TimeInfo.Now, $"WARNING: {GetCitizenDesc(citizenId, ref citizen)} is at home but vehicle = {vehicle}"); return; } if (CitizenGoesWorking(instance, citizenId, ref citizen)) { return; } if (IsBusyAtHomeInTheMorning(CitizenProxy.GetAge(ref citizen)) || !residentAI.DoRandomMove(instance)) { return; } if (CitizenGoesShopping(instance, citizenId, ref citizen) || CitizenGoesToEvent(instance, citizenId, ref citizen)) { return; } CitizenGoesRelaxing(instance, citizenId, ref citizen); }
private void ProcessVisit(TAI instance, uint citizenId, ref TCitizen citizen) { ushort visitBuilding = CitizenProxy.GetVisitBuilding(ref citizen); if (visitBuilding == 0) { CitizenMgr.ReleaseCitizen(citizenId); return; } if (BuildingMgr.BuildingHasFlags(visitBuilding, Building.Flags.Evacuating)) { touristAI.FindEvacuationPlace(instance, citizenId, visitBuilding, touristAI.GetEvacuationReason(instance, visitBuilding)); return; } switch (BuildingMgr.GetBuildingService(visitBuilding)) { case ItemClass.Service.Disaster: if (BuildingMgr.BuildingHasFlags(visitBuilding, Building.Flags.Downgrading)) { CitizenProxy.RemoveFlags(ref citizen, Citizen.Flags.Evacuating); FindRandomVisitPlace(instance, citizenId, ref citizen, 0, visitBuilding); } return; // Tourist is sleeping in a hotel case ItemClass.Service.Commercial when BuildingMgr.GetBuildingSubService(visitBuilding) == ItemClass.SubService.CommercialTourist && !Random.ShouldOccur(GetHotelLeaveChance()): return; } ICityEvent currentEvent = EventMgr.GetCityEvent(visitBuilding); if (currentEvent != null && currentEvent.StartTime < TimeInfo.Now) { if (Random.ShouldOccur(TouristShoppingChance)) { BuildingMgr.ModifyMaterialBuffer(visitBuilding, TransferManager.TransferReason.Shopping, -ShoppingGoodsAmount); } return; } if (Random.ShouldOccur(TouristEventChance) && !WeatherInfo.IsBadWeather) { ICityEvent cityEvent = GetUpcomingEventToAttend(citizenId, ref citizen); if (cityEvent != null && StartMovingToVisitBuilding(instance, citizenId, ref citizen, CitizenProxy.GetCurrentBuilding(ref citizen), cityEvent.BuildingId)) { Log.Debug(LogCategory.Events, TimeInfo.Now, $"Tourist {GetCitizenDesc(citizenId, ref citizen)} attending an event at {cityEvent.BuildingId}"); return; } } FindRandomVisitPlace(instance, citizenId, ref citizen, 0, visitBuilding); }
/// <summary> /// The main method of the custom AI. /// </summary> /// /// <param name="instance">A reference to an object instance of the original AI.</param> /// <param name="citizenId">The ID of the citizen to process.</param> /// <param name="citizen">A <see cref="Citizen"/> reference to process.</param> public void UpdateLocation(TAI instance, uint citizenId, ref TCitizen citizen) { if (!EnsureCitizenValid(citizenId, ref citizen)) { return; } if (CitizenProxy.IsDead(ref citizen)) { ProcessCitizenDead(instance, citizenId, ref citizen); return; } if ((CitizenProxy.IsSick(ref citizen) && ProcessCitizenSick(instance, citizenId, ref citizen)) || (CitizenProxy.IsArrested(ref citizen) && ProcessCitizenArrested(ref citizen))) { return; } ResidentState residentState = GetResidentState(ref citizen); switch (residentState) { case ResidentState.LeftCity: CitizenMgr.ReleaseCitizen(citizenId); break; case ResidentState.MovingHome: ProcessCitizenMoving(instance, citizenId, ref citizen, false); break; case ResidentState.AtHome: ProcessCitizenAtHome(instance, citizenId, ref citizen); break; case ResidentState.MovingToTarget: ProcessCitizenMoving(instance, citizenId, ref citizen, true); break; case ResidentState.AtSchoolOrWork: ProcessCitizenAtSchoolOrWork(instance, citizenId, ref citizen); break; case ResidentState.AtLunch: case ResidentState.Shopping: case ResidentState.AtLeisureArea: case ResidentState.Visiting: ProcessCitizenVisit(instance, residentState, citizenId, ref citizen); break; case ResidentState.Evacuating: ProcessCitizenEvacuation(instance, citizenId, ref citizen); break; case ResidentState.InShelter: CitzenReturnsFromShelter(instance, citizenId, ref citizen); return; } }