Exemple #1
0
        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);
        }
Exemple #2
0
        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);
        }
Exemple #3
0
        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 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);
        }
        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 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);
        }
Exemple #7
0
        private bool DoScheduledShopping(ref CitizenSchedule schedule, TAI instance, uint citizenId, ref TCitizen citizen)
        {
            // Shopping was already scheduled last time, but the citizen is still at school/work or in shelter.
            // This can occur when the game's transfer manager can't find any activity for the citizen.
            // In that case, move back home.
            if ((schedule.CurrentState == ResidentState.AtSchoolOrWork || schedule.CurrentState == ResidentState.InShelter) &&
                schedule.LastScheduledState == ResidentState.Shopping)
            {
                Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} wanted go shopping but is still at work or in shelter. No shopping activity found. Now going home.");
                return(false);
            }

            ushort currentBuilding = CitizenProxy.GetCurrentBuilding(ref citizen);

            if (schedule.Hint == ScheduleHint.LocalShoppingOnly)
            {
                schedule.Schedule(ResidentState.Unknown);

                ushort shop = MoveToCommercialBuilding(instance, citizenId, ref citizen, LocalSearchDistance);
                if (shop == 0)
                {
                    Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} wanted go shopping, but didn't find a local shop");
                    return(false);
                }

                if (TimeInfo.IsNightTime)
                {
                    schedule.Hint = ScheduleHint.NoShoppingAnyMore;
                }

                Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} goes shopping at a local shop {shop}");
                return(true);
            }

            uint          moreShoppingChance = spareTimeBehavior.GetShoppingChance(CitizenProxy.GetAge(ref citizen));
            ResidentState nextState          = schedule.Hint != ScheduleHint.NoShoppingAnyMore && Random.ShouldOccur(moreShoppingChance)
                ? ResidentState.Shopping
                : ResidentState.Unknown;

            schedule.Schedule(nextState);

            if (schedule.CurrentState != ResidentState.Shopping || Random.ShouldOccur(FindAnotherShopOrEntertainmentChance))
            {
                Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} in state {schedule.CurrentState} wanna go shopping and schedules {nextState}, heading to a random shop, hint = {schedule.Hint}");
                residentAI.FindVisitPlace(instance, citizenId, currentBuilding, residentAI.GetShoppingReason(instance));
            }
#if DEBUG
            else
            {
                Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} continues shopping in the same building.");
            }
#endif

            return(true);
        }
Exemple #8
0
        private bool ProcessCitizenMoving(ref CitizenSchedule schedule, TAI instance, uint citizenId, ref TCitizen citizen)
        {
            ushort instanceId = CitizenProxy.GetInstance(ref citizen);
            ushort vehicleId  = CitizenProxy.GetVehicle(ref citizen);

            if (vehicleId == 0 && instanceId == 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, false);
                    schedule.Schedule(ResidentState.Unknown);
                }

                return(true);
            }

            if (vehicleId == 0 && CitizenMgr.IsAreaEvacuating(instanceId) && !CitizenProxy.HasFlags(ref citizen, Citizen.Flags.Evacuating))
            {
                Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} was on the way, but the area evacuates. Finding an evacuation place.");
                schedule.Schedule(ResidentState.Unknown);
                TransferMgr.AddOutgoingOfferFromCurrentPosition(citizenId, residentAI.GetEvacuationReason(instance, 0));
                return(true);
            }

            ushort targetBuilding = CitizenMgr.GetTargetBuilding(instanceId);

            if (targetBuilding == CitizenProxy.GetWorkBuilding(ref citizen))
            {
                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 DoScheduledRelaxing(ref CitizenSchedule schedule, TAI instance, uint citizenId, ref TCitizen citizen)
        {
            ushort buildingId = CitizenProxy.GetCurrentBuilding(ref citizen);

            switch (schedule.Hint)
            {
            case ScheduleHint.RelaxAtLeisureBuilding:
                schedule.Schedule(ResidentState.Unknown, default);

                ushort leisure = MoveToLeisureBuilding(instance, citizenId, ref citizen, buildingId);
                if (leisure == 0)
                {
                    Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} wanted relax but didn't found a leisure building");
                }
                else
                {
                    Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} heading to a leisure building {leisure}");
                }

                return;

            case ScheduleHint.AttendingEvent:
                DateTime   returnTime = default;
                ICityEvent cityEvent  = EventMgr.GetCityEvent(schedule.EventBuilding);
                if (cityEvent == null)
                {
                    Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} wanted attend an event at '{schedule.EventBuilding}', but there was no event there");
                }
                else if (StartMovingToVisitBuilding(instance, citizenId, ref citizen, schedule.EventBuilding))
                {
                    returnTime = cityEvent.EndTime;
                    Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} wanna attend an event at '{schedule.EventBuilding}', will return at {returnTime}");
                }

                schedule.Schedule(ResidentState.Unknown, returnTime);
                schedule.EventBuilding = 0;
                return;
            }

            uint          relaxChance = spareTimeBehavior.GetGoOutChance(CitizenProxy.GetAge(ref citizen));
            ResidentState nextState   = Random.ShouldOccur(relaxChance)
                    ? ResidentState.Unknown
                    : ResidentState.Relaxing;

            schedule.Schedule(nextState, default);

            if (schedule.CurrentState != ResidentState.Relaxing)
            {
                Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} in state {schedule.CurrentState} wanna relax and then schedules {nextState}, heading to an entertainment building.");
                residentAI.FindVisitPlace(instance, citizenId, buildingId, residentAI.GetEntertainmentReason(instance));
            }
        }
Exemple #10
0
        /// <summary>Updates the citizen's work schedule by determining the time for going to work.</summary>
        /// <param name="schedule">The citizen's schedule to update.</param>
        /// <param name="currentBuilding">The ID of the building where the citizen is currently located.</param>
        /// <param name="simulationCycle">The duration (in hours) of a full citizens simulation cycle.</param>
        /// <returns><c>true</c> if work was scheduled; otherwise, <c>false</c>.</returns>
        public bool ScheduleGoToWork(ref CitizenSchedule schedule, ushort currentBuilding, float simulationCycle)
        {
            if (schedule.CurrentState == ResidentState.AtSchoolOrWork)
            {
                return(false);
            }

            DateTime now = timeInfo.Now;

            if (config.IsWeekendEnabled && now.IsWeekend() && !schedule.WorksOnWeekends)
            {
                return(false);
            }

            float travelTime = GetTravelTimeToWork(ref schedule, currentBuilding);

            DateTime workEndTime   = now.FutureHour(schedule.WorkShiftEndHour);
            DateTime departureTime = now.FutureHour(schedule.WorkShiftStartHour - travelTime - simulationCycle);

            if (departureTime > workEndTime && now.AddHours(travelTime + simulationCycle) < workEndTime)
            {
                departureTime = now;
            }

            schedule.Schedule(ResidentState.AtSchoolOrWork, departureTime);
            return(true);
        }
Exemple #11
0
 /// <summary>Updates the citizen's work schedule by determining the returning from lunch time.</summary>
 /// <param name="schedule">The citizen's schedule to update.</param>
 public void ScheduleReturnFromLunch(ref CitizenSchedule schedule)
 {
     if (schedule.WorkStatus == WorkStatus.Working)
     {
         schedule.Schedule(ResidentState.AtSchoolOrWork, lunchEnd);
     }
 }
Exemple #12
0
        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);
        }
Exemple #13
0
        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 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);
        }
Exemple #15
0
        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 DoScheduledEvacuation(ref CitizenSchedule schedule, TAI instance, uint citizenId, ref TCitizen citizen)
        {
            ushort building = CitizenProxy.GetCurrentBuilding(ref citizen);

            if (building == 0)
            {
                schedule.Schedule(ResidentState.AtHome);
                return;
            }

            schedule.Schedule(ResidentState.InShelter);
            if (schedule.CurrentState != ResidentState.InShelter)
            {
                Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} is trying to find an evacuation place");
                residentAI.FindEvacuationPlace(instance, citizenId, building, residentAI.GetEvacuationReason(instance, building));
            }
        }
        private bool ProcessCitizenVisit(ref CitizenSchedule schedule, ref TCitizen citizen)
        {
            if (schedule.Hint == ScheduleHint.OnTour)
            {
                Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(0, ref citizen)} quits a tour (see next line for citizen ID)");
                schedule.Schedule(ResidentState.Unknown, default);
                return(true);
            }

            return(RescheduleVisit(ref schedule, ref citizen, CitizenProxy.GetVisitBuilding(ref citizen)));
        }
Exemple #18
0
        /// <summary>Updates the citizen's work schedule by determining the time for returning from work.</summary>
        /// <param name="schedule">The citizen's schedule to update.</param>
        /// <param name="citizenAge">The age of the citizen.</param>
        public void ScheduleReturnFromWork(ref CitizenSchedule schedule, Citizen.AgeGroup citizenAge)
        {
            if (schedule.WorkStatus != WorkStatus.Working)
            {
                return;
            }

            float departureHour = schedule.WorkShiftEndHour + GetOvertime(citizenAge);

            schedule.Schedule(ResidentState.Unknown, timeInfo.Now.FutureHour(departureHour));
        }
Exemple #19
0
        private bool ProcessCitizenVisit(ref CitizenSchedule schedule, uint citizenId, ref TCitizen citizen)
        {
            if (schedule.Hint == ScheduleHint.OnTour)
            {
                Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} quits a tour");
                schedule.Schedule(ResidentState.Unknown);
                return(true);
            }

            return(RescheduleVisit(ref schedule, citizenId, ref citizen, CitizenProxy.GetVisitBuilding(ref citizen)));
        }
Exemple #20
0
        private bool RescheduleVisit(ref CitizenSchedule schedule, uint citizenId, ref TCitizen citizen, ushort currentBuilding)
        {
            switch (schedule.ScheduledState)
            {
            case ResidentState.Shopping:
            case ResidentState.Relaxing:
            case ResidentState.Visiting:
                break;

            default:
                return(false);
            }

            if (schedule.CurrentState != ResidentState.Shopping && WeatherInfo.IsBadWeather)
            {
                Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} quits a visit because of bad weather");
                schedule.Schedule(ResidentState.AtHome);
                return(true);
            }

            if (buildingAI.IsNoiseRestricted(currentBuilding, currentBuilding))
            {
                Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} quits a visit because of NIMBY policy");
                schedule.Schedule(ResidentState.Unknown);
                return(true);
            }

            Citizen.AgeGroup age        = CitizenProxy.GetAge(ref citizen);
            uint             stayChance = schedule.CurrentState == ResidentState.Shopping
                ? spareTimeBehavior.GetShoppingChance(age)
                : spareTimeBehavior.GetRelaxingChance(age, schedule.WorkShift, schedule.WorkStatus == WorkStatus.OnVacation);

            if (!Random.ShouldOccur(stayChance))
            {
                Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} quits a visit because of time");
                schedule.Schedule(ResidentState.AtHome);
                return(true);
            }

            return(false);
        }
        private bool ProcessCitizenInShelter(ref CitizenSchedule schedule, ref TCitizen citizen)
        {
            ushort shelter = CitizenProxy.GetVisitBuilding(ref citizen);

            if (BuildingMgr.BuildingHasFlags(shelter, Building.Flags.Downgrading))
            {
                schedule.Schedule(ResidentState.Unknown, default);
                return(true);
            }

            return(false);
        }
        private bool ProcessCitizenInShelter(ref CitizenSchedule schedule, ref TCitizen citizen)
        {
            ushort shelter = CitizenProxy.GetVisitBuilding(ref citizen);

            if (BuildingMgr.BuildingHasFlags(shelter, Building.Flags.Downgrading) && schedule.ScheduledState == ResidentState.InShelter)
            {
                CitizenProxy.RemoveFlags(ref citizen, Citizen.Flags.Evacuating);
                schedule.Schedule(ResidentState.Unknown);
                return(true);
            }

            return(false);
        }
Exemple #23
0
        /// <summary>Updates the citizen's work schedule by determining the lunch time.</summary>
        /// <param name="schedule">The citizen's schedule to update.</param>
        /// <param name="citizenAge">The citizen's age.</param>
        /// <returns><c>true</c> if a lunch time was scheduled; otherwise, <c>false</c>.</returns>
        public bool ScheduleLunch(ref CitizenSchedule schedule, Citizen.AgeGroup citizenAge)
        {
            if (timeInfo.Now < lunchBegin &&
                schedule.WorkStatus == WorkStatus.Working &&
                schedule.WorkShift == WorkShift.First &&
                WillGoToLunch(citizenAge))
            {
                schedule.Schedule(ResidentState.Shopping, lunchBegin);
                return(true);
            }

            return(false);
        }
        private void ExecuteCitizenSchedule(ref CitizenSchedule schedule, TAI instance, uint citizenId, ref TCitizen citizen, bool noReschedule)
        {
            if (ProcessCurrentState(ref schedule, citizenId, ref citizen) &&
                schedule.ScheduledState == ResidentState.Unknown && !noReschedule)
            {
                Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} will re-schedule now");

                // If the state processing changed the schedule, we need to update it
                UpdateCitizenSchedule(ref schedule, citizenId, ref citizen);
            }

            if (TimeInfo.Now < schedule.ScheduledStateTime)
            {
                return;
            }

            if (schedule.CurrentState == ResidentState.AtHome && IsCitizenVirtual(instance, ref citizen, ShouldRealizeCitizen))
            {
                Log.Debug($" *** Citizen {citizenId} is virtual this time");
                schedule.Schedule(ResidentState.Unknown, default);
                return;
            }

            switch (schedule.ScheduledState)
            {
            case ResidentState.AtHome:
                DoScheduledHome(ref schedule, instance, citizenId, ref citizen);
                break;

            case ResidentState.AtSchoolOrWork:
                DoScheduledWork(ref schedule, instance, citizenId, ref citizen);
                break;

            case ResidentState.Shopping when schedule.WorkStatus == WorkStatus.Working:
                DoScheduledLunch(ref schedule, instance, citizenId, ref citizen);
                break;

            case ResidentState.Shopping:
                DoScheduledShopping(ref schedule, instance, citizenId, ref citizen);
                break;

            case ResidentState.Relaxing:
                DoScheduledRelaxing(ref schedule, instance, citizenId, ref citizen);
                break;

            case ResidentState.InShelter:
                DoScheduledEvacuation(ref schedule, instance, citizenId, ref citizen);
                break;
            }
        }
        private void DoScheduledEvacuation(ref CitizenSchedule schedule, TAI instance, uint citizenId, ref TCitizen citizen)
        {
            schedule.Schedule(ResidentState.Unknown);
            ushort building = CitizenProxy.GetCurrentBuilding(ref citizen);

            if (building == 0)
            {
                Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} is trying to find a shelter from current position");
                TransferMgr.AddOutgoingOfferFromCurrentPosition(citizenId, residentAI.GetEvacuationReason(instance, 0));
            }
            else
            {
                Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} is trying to find a shelter from {building}");
                residentAI.FindEvacuationPlace(instance, citizenId, building, residentAI.GetEvacuationReason(instance, building));
            }
        }
        private void DoScheduledWork(ref CitizenSchedule schedule, TAI instance, uint citizenId, ref TCitizen citizen)
        {
            ushort currentBuilding = CitizenProxy.GetCurrentBuilding(ref citizen);

            schedule.WorkStatus = WorkStatus.Working;

            if (currentBuilding == schedule.WorkBuilding && schedule.CurrentState != ResidentState.AtSchoolOrWork)
            {
                CitizenProxy.SetVisitPlace(ref citizen, citizenId, 0);
                CitizenProxy.SetLocation(ref citizen, Citizen.Location.Work);
                return;
            }

#if DEBUG
            string citizenDesc = GetCitizenDesc(citizenId, ref citizen);
#else
            const string citizenDesc = null;
#endif

            if (residentAI.StartMoving(instance, citizenId, ref citizen, currentBuilding, schedule.WorkBuilding))
            {
                if (schedule.CurrentState != ResidentState.AtHome)
                {
                    // The start moving method will register a departure from any building to work,
                    // but we are only interested in the 'home->work' route.
                    schedule.DepartureTime = default;
                }

                Citizen.AgeGroup citizenAge = CitizenProxy.GetAge(ref citizen);
                if (workBehavior.ScheduleLunch(ref schedule, citizenAge))
                {
                    Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{citizenDesc} 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(LogCategory.Movement, TimeInfo.Now, $"{citizenDesc} is going from {currentBuilding} to school/work {schedule.WorkBuilding} and will leave work at {schedule.ScheduledStateTime}");
                }
            }
            else
            {
                Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} wanted to go to work from {currentBuilding} but can't, will try once again next time");
                schedule.Schedule(ResidentState.Unknown);
            }
        }
        private bool ScheduleWork(ref CitizenSchedule schedule, ref TCitizen citizen)
        {
            ushort currentBuilding = CitizenProxy.GetCurrentBuilding(ref citizen);

            if (!workBehavior.ScheduleGoToWork(ref schedule, currentBuilding, simulationCycle))
            {
                return(false);
            }

            Log.Debug($"  - Schedule work at {schedule.ScheduledStateTime}");

            float timeLeft = (float)(schedule.ScheduledStateTime - TimeInfo.Now).TotalHours;

            if (timeLeft <= PrepareToWorkHours)
            {
                // Just sit at home if the work time will come soon
                Log.Debug($"  - Work time in {timeLeft} hours, preparing for departure");
                return(true);
            }

            if (timeLeft <= MaxTravelTime)
            {
                if (schedule.CurrentState != ResidentState.AtHome)
                {
                    Log.Debug($"  - Work time in {timeLeft} hours, returning home");
                    schedule.Schedule(ResidentState.AtHome, default);
                    return(true);
                }

                // If we have some time, try to shop locally.
                if (ScheduleShopping(ref schedule, ref citizen, true))
                {
                    Log.Debug($"  - Work time in {timeLeft} hours, trying local shop");
                }
                else
                {
                    Log.Debug($"  - Work time in {timeLeft} hours, doing nothing");
                }

                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);
                return;
            }

            if (residentAI.StartMoving(instance, citizenId, ref citizen, currentBuilding, schedule.WorkBuilding))
            {
                if (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}");
                }
            }
            else
            {
                Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} wanted to go to work from {currentBuilding} but can't, will try once again next time");
                schedule.Schedule(ResidentState.Unknown, default);
            }
        }
Exemple #29
0
        private bool DoScheduledRelaxing(ref CitizenSchedule schedule, TAI instance, uint citizenId, ref TCitizen citizen)
        {
            // Relaxing was already scheduled last time, but the citizen is still at school/work or in shelter.
            // This can occur when the game's transfer manager can't find any activity for the citizen.
            // In that case, move back home.
            if ((schedule.CurrentState == ResidentState.AtSchoolOrWork || schedule.CurrentState == ResidentState.InShelter) &&
                schedule.LastScheduledState == ResidentState.Relaxing)
            {
                Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} wanted relax but is still at work or in shelter. No relaxing activity found. Now going home.");
                return(false);
            }

            ushort buildingId = CitizenProxy.GetCurrentBuilding(ref citizen);

            switch (schedule.Hint)
            {
            case ScheduleHint.RelaxAtLeisureBuilding:
                schedule.Schedule(ResidentState.Unknown);

                ushort leisure = MoveToLeisureBuilding(instance, citizenId, ref citizen, buildingId);
                if (leisure == 0)
                {
                    Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} wanted relax but didn't find a leisure building");
                    return(false);
                }

                Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} heading to a leisure building {leisure}");
                return(true);

            case ScheduleHint.AttendingEvent:
                ushort eventBuilding = schedule.EventBuilding;
                schedule.EventBuilding = 0;

                ICityEvent cityEvent = EventMgr.GetCityEvent(eventBuilding);
                if (cityEvent == null)
                {
                    Log.Debug(LogCategory.Events, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} wanted attend an event at '{eventBuilding}', but there was no event there");
                }
                else if (StartMovingToVisitBuilding(instance, citizenId, ref citizen, eventBuilding))
                {
                    schedule.Schedule(ResidentState.Unknown, cityEvent.EndTime);
                    Log.Debug(LogCategory.Events, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} wanna attend an event at '{eventBuilding}', will return at {cityEvent.EndTime}");
                    return(true);
                }

                schedule.Schedule(ResidentState.Unknown);
                return(false);

            case ScheduleHint.RelaxNearbyOnly:
                Vector3 currentPosition = CitizenMgr.GetCitizenPosition(CitizenProxy.GetInstance(ref citizen));
                ushort  parkBuildingId  = BuildingMgr.FindActiveBuilding(currentPosition, LocalSearchDistance, ItemClass.Service.Beautification);
                if (StartMovingToVisitBuilding(instance, citizenId, ref citizen, parkBuildingId))
                {
                    Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} heading to a nearby entertainment building {parkBuildingId}");
                    schedule.Schedule(ResidentState.Unknown);
                    return(true);
                }

                schedule.Schedule(ResidentState.Unknown);
                DoScheduledHome(ref schedule, instance, citizenId, ref citizen);
                return(true);
            }

            uint relaxChance = spareTimeBehavior.GetRelaxingChance(
                CitizenProxy.GetAge(ref citizen),
                schedule.WorkShift,
                schedule.WorkStatus == WorkStatus.OnVacation);

            relaxChance = AdjustRelaxChance(relaxChance, ref citizen);

            ResidentState nextState = Random.ShouldOccur(relaxChance)
                    ? ResidentState.Relaxing
                    : ResidentState.Unknown;

            schedule.Schedule(nextState);
            if (schedule.CurrentState != ResidentState.Relaxing || Random.ShouldOccur(FindAnotherShopOrEntertainmentChance))
            {
                Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} in state {schedule.CurrentState} wanna relax and then schedules {nextState}, heading to an entertainment building.");
                residentAI.FindVisitPlace(instance, citizenId, buildingId, residentAI.GetEntertainmentReason(instance));
            }
#if DEBUG
            else
            {
                Log.Debug(LogCategory.Movement, TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} continues relaxing in the same entertainment building.");
            }
#endif

            return(true);
        }
Exemple #30
0
        private bool ProcessCitizenMoving(ref CitizenSchedule schedule, 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)
                {
                    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, false);
                        schedule.Schedule(ResidentState.Unknown);
                    }
                }

                return(true);
            }

            if (vehicleId == 0 && !CitizenProxy.HasFlags(ref citizen, Citizen.Flags.Evacuating) && 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.Schedule(ResidentState.Unknown);
                schedule.DepartureTime = default;
                TransferMgr.AddOutgoingOfferFromCurrentPosition(citizenId, residentAI.GetEvacuationReason(instance, 0));
                return(true);
            }

            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;
                    schedule.CurrentState = ResidentState.InTransition;
                    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);
                schedule.CurrentState = ResidentState.InTransition;
                return(false);
            }

            return(true);
        }