private ScheduleAction UpdateCitizenState(ref TCitizen citizen, ref CitizenSchedule schedule) { if (schedule.CurrentState == ResidentState.Ignored) { return(ScheduleAction.Ignore); } if (CitizenProxy.HasFlags(ref citizen, Citizen.Flags.DummyTraffic)) { schedule.CurrentState = ResidentState.Ignored; return(ScheduleAction.Ignore); } Citizen.Location location = CitizenProxy.GetLocation(ref citizen); if (location == Citizen.Location.Moving) { if (CitizenMgr.InstanceHasFlags( CitizenProxy.GetInstance(ref citizen), CitizenInstance.Flags.OnTour | CitizenInstance.Flags.TargetIsNode, true)) { // Guided tours are treated as visits schedule.Hint = ScheduleHint.OnTour; } schedule.CurrentState = ResidentState.InTransition; return(ScheduleAction.ProcessTransition); } ushort currentBuilding = CitizenProxy.GetCurrentBuilding(ref citizen); if (currentBuilding == 0) { schedule.CurrentState = ResidentState.Unknown; return(ScheduleAction.ProcessState); } if (BuildingMgr.BuildingHasFlags(currentBuilding, Building.Flags.Evacuating)) { schedule.CurrentState = ResidentState.Evacuation; return(ScheduleAction.ProcessState); } ItemClass.Service buildingService = BuildingMgr.GetBuildingService(currentBuilding); switch (location) { case Citizen.Location.Home: schedule.CurrentState = ResidentState.AtHome; return(ScheduleAction.ProcessState); case Citizen.Location.Work: if (CitizenProxy.GetVisitBuilding(ref citizen) == currentBuilding && schedule.WorkStatus != WorkStatus.Working) { // A citizen may visit their own work building (e.g. shopping), // but the game sets the location to 'work' even if the citizen visits the building. goto case Citizen.Location.Visit; } switch (buildingService) { case ItemClass.Service.Electricity: case ItemClass.Service.Water: case ItemClass.Service.HealthCare: case ItemClass.Service.PoliceDepartment: case ItemClass.Service.FireDepartment: case ItemClass.Service.Disaster: if (BuildingMgr.IsAreaEvacuating(currentBuilding)) { schedule.CurrentState = ResidentState.InShelter; return(ScheduleAction.ProcessState); } break; } schedule.CurrentState = ResidentState.AtSchoolOrWork; return(ScheduleAction.ProcessState); case Citizen.Location.Visit: switch (buildingService) { case ItemClass.Service.Beautification: case ItemClass.Service.Monument: case ItemClass.Service.Tourism: case ItemClass.Service.Commercial when BuildingMgr.GetBuildingSubService(currentBuilding) == ItemClass.SubService.CommercialLeisure && schedule.WorkStatus != WorkStatus.Working: schedule.CurrentState = ResidentState.Relaxing; return(ScheduleAction.ProcessState); case ItemClass.Service.Commercial: schedule.CurrentState = ResidentState.Shopping; return(ScheduleAction.ProcessState); case ItemClass.Service.Disaster: schedule.CurrentState = ResidentState.InShelter; return(ScheduleAction.ProcessState); } schedule.CurrentState = ResidentState.Visiting; return(ScheduleAction.ProcessState); } return(ScheduleAction.Ignore); }
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)) { FindRandomVisitPlace(instance, citizenId, ref citizen, 0, visitBuilding); } return; // Tourist is sleeping in a hotel case ItemClass.Service.Commercial when TimeInfo.IsNightTime && BuildingMgr.GetBuildingSubService(visitBuilding) == ItemClass.SubService.CommercialTourist: return; } if (Random.ShouldOccur(TouristEventChance) && !IsBadWeather()) { ICityEvent cityEvent = GetUpcomingEventToAttend(citizenId, ref citizen); if (cityEvent != null) { StartMovingToVisitBuilding(instance, citizenId, ref citizen, CitizenProxy.GetCurrentBuilding(ref citizen), cityEvent.BuildingId); Log.Debug(TimeInfo.Now, $"Tourist {GetCitizenDesc(citizenId, ref citizen)} attending an event at {cityEvent.BuildingId}"); return; } } int doNothingChance; switch (EventMgr.GetEventState(visitBuilding, DateTime.MaxValue)) { case CityEventState.Ongoing: if (Random.ShouldOccur(TouristShoppingChance)) { BuildingMgr.ModifyMaterialBuffer(visitBuilding, TransferManager.TransferReason.Shopping, -ShoppingGoodsAmount); } return; case CityEventState.Finished: doNothingChance = 0; break; default: doNothingChance = TouristDoNothingProbability; break; } FindRandomVisitPlace(instance, citizenId, ref citizen, doNothingChance, visitBuilding); }
private bool ProcessCitizenMoving(ref CitizenSchedule schedule, uint citizenId, ref TCitizen citizen) { ushort instanceId = CitizenProxy.GetInstance(ref citizen); ushort vehicleId = CitizenProxy.GetVehicle(ref citizen); if (instanceId == 0) { if (vehicleId == 0) { if (CitizenProxy.GetVisitBuilding(ref citizen) != 0) { CitizenProxy.SetVisitPlace(ref citizen, citizenId, 0); } if (CitizenProxy.HasFlags(ref citizen, Citizen.Flags.MovingIn)) { CitizenMgr.ReleaseCitizen(citizenId); schedule = default; } else { CitizenProxy.SetLocation(ref citizen, Citizen.Location.Home); CitizenProxy.SetArrested(ref citizen, isArrested: false); schedule.Schedule(ResidentState.Unknown); } } return(true); } bool isEvacuating = CitizenProxy.HasFlags(ref citizen, Citizen.Flags.Evacuating); if (vehicleId == 0 && !isEvacuating && CitizenMgr.IsAreaEvacuating(instanceId)) { Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} was on the way, but the area evacuates. Finding an evacuation place."); schedule.CurrentState = ResidentState.Evacuation; return(false); } if (isEvacuating) { return(true); } if (schedule.Hint == ScheduleHint.OnTour) { Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} quits a tour"); schedule.Schedule(ResidentState.Unknown); schedule.Hint = ScheduleHint.None; return(false); } ushort targetBuilding = CitizenMgr.GetTargetBuilding(instanceId); bool headingToWork = targetBuilding == CitizenProxy.GetWorkBuilding(ref citizen); if (vehicleId != 0 && schedule.DepartureTime != default) { float maxTravelTime = headingToWork ? abandonCarRideToWorkDurationThreshold : abandonCarRideDurationThreshold; if ((TimeInfo.Now - schedule.DepartureTime).TotalHours > maxTravelTime) { buildingAI.RegisterReachingTrouble(targetBuilding); if (targetBuilding == CitizenProxy.GetHomeBuilding(ref citizen)) { return(true); } Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} cancels the trip because of traffic jam"); schedule.Schedule(ResidentState.Relaxing); schedule.Hint = ScheduleHint.RelaxNearbyOnly; return(false); } } if (headingToWork) { return(true); } ItemClass.Service targetService = BuildingMgr.GetBuildingService(targetBuilding); if (targetService == ItemClass.Service.Beautification && WeatherInfo.IsBadWeather) { Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} cancels the trip to a park due to bad weather"); schedule.Schedule(ResidentState.AtHome); return(false); } return(true); }
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; } bool isEvacuating = CitizenProxy.HasFlags(ref citizen, Citizen.Flags.Evacuating); if (vehicleId == 0 && !isEvacuating && CitizenMgr.IsAreaEvacuating(instanceId)) { Log.Debug(LogCategory.Movement, TimeInfo.Now, $"Tourist {GetCitizenDesc(citizenId, ref citizen)} was on the way, but the area evacuates. Searching for a shelter."); TransferMgr.AddOutgoingOfferFromCurrentPosition(citizenId, touristAI.GetEvacuationReason(instance, 0)); return; } if (isEvacuating) { return; } if (CitizenMgr.InstanceHasFlags(instanceId, CitizenInstance.Flags.TargetIsNode | CitizenInstance.Flags.OnTour, all: true)) { Log.Debug(LogCategory.Movement, TimeInfo.Now, $"Tourist {GetCitizenDesc(citizenId, ref citizen)} exits the guided tour."); FindRandomVisitPlace(instance, citizenId, ref citizen, TouristDoNothingProbability, 0); return; } ushort targetBuildingId = CitizenProxy.GetVisitBuilding(ref citizen); TouristTarget target; if (CitizenMgr.InstanceHasFlags(instanceId, CitizenInstance.Flags.TargetIsNode)) { if (CitizenMgr.GetTargetNode(instanceId) != 0) { target = TouristTarget.Relaxing; } else { return; } } else { if (targetBuildingId == 0) { targetBuildingId = CitizenMgr.GetTargetBuilding(instanceId); } BuildingMgr.GetBuildingService(targetBuildingId, out var targetService, out var targetSubService); switch (targetService) { // Heading to a hotel, no need to change anything case ItemClass.Service.Commercial when targetSubService == ItemClass.SubService.CommercialTourist: return; case ItemClass.Service.Commercial when targetSubService == ItemClass.SubService.CommercialLeisure: target = TouristTarget.Party; break; case ItemClass.Service.Tourism: case ItemClass.Service.Beautification: case ItemClass.Service.Monument: target = TouristTarget.Relaxing; break; case ItemClass.Service.Commercial: target = TouristTarget.Shopping; break; default: return; } } if (GetTouristGoingOutChance(ref citizen, target) > 0) { return; } ushort hotel = FindHotel(targetBuildingId); if (hotel != 0) { Log.Debug(LogCategory.Movement, TimeInfo.Now, $"Tourist {GetCitizenDesc(citizenId, ref citizen)} changes the target and moves to a hotel {hotel} because of time or weather"); StartMovingToVisitBuilding(instance, citizenId, ref citizen, 0, hotel); } else { Log.Debug(LogCategory.Movement, TimeInfo.Now, $"Tourist {GetCitizenDesc(citizenId, ref citizen)} leaves the city because of time or weather"); touristAI.FindVisitPlace(instance, citizenId, 0, touristAI.GetLeavingReason(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)) { FindRandomVisitPlace(instance, citizenId, ref citizen, 0, visitBuilding); } return; // Tourist is sleeping in a hotel case ItemClass.Service.Commercial when TimeInfo.IsNightTime && BuildingMgr.GetBuildingSubService(visitBuilding) == ItemClass.SubService.CommercialTourist: return; } if (IsChance(TouristEventChance) && AttendUpcomingEvent(citizenId, ref citizen, out ushort eventBuilding)) { StartMovingToVisitBuilding(instance, citizenId, ref citizen, CitizenProxy.GetCurrentBuilding(ref citizen), eventBuilding); touristAI.AddTouristVisit(instance, citizenId, eventBuilding); Log.Debug(TimeInfo.Now, $"Tourist {GetCitizenDesc(citizenId, ref citizen)} attending an event at {eventBuilding}"); return; } bool doShopping; switch (EventMgr.GetEventState(visitBuilding, DateTime.MaxValue)) { case CityEventState.Ongoing: doShopping = IsChance(TouristShoppingChance); break; case CityEventState.Finished: doShopping = !FindRandomVisitPlace(instance, citizenId, ref citizen, 0, visitBuilding); break; default: doShopping = false; break; } if (doShopping || !FindRandomVisitPlace(instance, citizenId, ref citizen, TouristDoNothingProbability, visitBuilding)) { BuildingMgr.ModifyMaterialBuffer(visitBuilding, TransferManager.TransferReason.Shopping, -ShoppingGoodsAmount); touristAI.AddTouristVisit(instance, citizenId, visitBuilding); } }
private ResidentState GetResidentState(ref TCitizen citizen) { ushort currentBuilding = CitizenProxy.GetCurrentBuilding(ref citizen); ItemClass.Service buildingService = BuildingMgr.GetBuildingService(currentBuilding); if (BuildingMgr.BuildingHasFlags(currentBuilding, Building.Flags.Evacuating) && buildingService != ItemClass.Service.Disaster) { return(ResidentState.Evacuating); } switch (CitizenProxy.GetLocation(ref citizen)) { case Citizen.Location.Home: if (CitizenProxy.HasFlags(ref citizen, Citizen.Flags.MovingIn)) { return(ResidentState.LeftCity); } if (currentBuilding != 0) { return(ResidentState.AtHome); } return(ResidentState.Unknown); case Citizen.Location.Work: if (buildingService == ItemClass.Service.Disaster && CitizenProxy.HasFlags(ref citizen, Citizen.Flags.Evacuating)) { return(ResidentState.InShelter); } return(currentBuilding != 0 ? ResidentState.AtSchoolOrWork : ResidentState.Unknown); case Citizen.Location.Visit: if (currentBuilding == 0) { return(ResidentState.Unknown); } switch (buildingService) { case ItemClass.Service.Commercial: if (CitizenProxy.GetWorkBuilding(ref citizen) != 0 && IsWorkDay && TimeInfo.CurrentHour > Config.LunchBegin && TimeInfo.CurrentHour < GetSpareTimeBeginHour(CitizenProxy.GetAge(ref citizen))) { return(ResidentState.AtLunch); } if (BuildingMgr.GetBuildingSubService(currentBuilding) == ItemClass.SubService.CommercialLeisure) { return(ResidentState.AtLeisureArea); } return(ResidentState.Shopping); case ItemClass.Service.Beautification: return(ResidentState.AtLeisureArea); case ItemClass.Service.Disaster: return(ResidentState.InShelter); } return(ResidentState.Visiting); case Citizen.Location.Moving: ushort homeBuilding = CitizenProxy.GetHomeBuilding(ref citizen); return(homeBuilding != 0 && CitizenMgr.GetTargetBuilding(CitizenProxy.GetInstance(ref citizen)) == homeBuilding ? ResidentState.MovingHome : ResidentState.MovingToTarget); default: return(ResidentState.Unknown); } }