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));
                }
            }
        }
Exemple #2
0
 /// <summary>Schedules next actions for the citizen with no action time (ASAP).</summary>
 /// <param name="nextState">The next scheduled citizen's state.</param>
 public void Schedule(ResidentState nextState)
 {
     // Note: not calling the overload to avoid additional method call - this method will be called frequently
     LastScheduledState = ScheduledState;
     ScheduledState     = nextState;
     ScheduledStateTime = default;
 }
        /// <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;
            }
        }
Exemple #4
0
        private void ProcessCitizenVisit(TAI instance, ResidentState citizenState, uint citizenId, ref TCitizen citizen, bool isVirtual)
        {
            ushort currentBuilding = CitizenProxy.GetVisitBuilding(ref citizen);

            if (currentBuilding == 0)
            {
                Log.Debug($"WARNING: {GetCitizenDesc(citizenId, ref citizen, isVirtual)} is in corrupt state: visiting with no visit building. Teleporting home.");
                CitizenProxy.SetLocation(ref citizen, Citizen.Location.Home);
                return;
            }

            switch (citizenState)
            {
            case ResidentState.AtLunch:
                CitizenReturnsFromLunch(instance, citizenId, ref citizen, isVirtual);

                return;

            case ResidentState.AtLeisureArea:
                if (CitizenProxy.HasFlags(ref citizen, Citizen.Flags.NeedGoods) &&
                    BuildingMgr.GetBuildingSubService(currentBuilding) == ItemClass.SubService.CommercialLeisure)
                {
                    // No Citizen.Flags.NeedGoods flag reset here, because we only bought 'beer' or 'champagne' in a leisure building.
                    BuildingMgr.ModifyMaterialBuffer(CitizenProxy.GetVisitBuilding(ref citizen), TransferManager.TransferReason.Shopping, -ShoppingGoodsAmount);
                }

                goto case ResidentState.Visiting;

            case ResidentState.Visiting:
                if (!CitizenGoesWorking(instance, citizenId, ref citizen, isVirtual))
                {
                    CitizenReturnsHomeFromVisit(instance, citizenId, ref citizen, isVirtual);
                }

                return;

            case ResidentState.Shopping:
                if (CitizenProxy.HasFlags(ref citizen, Citizen.Flags.NeedGoods))
                {
                    BuildingMgr.ModifyMaterialBuffer(CitizenProxy.GetVisitBuilding(ref citizen), TransferManager.TransferReason.Shopping, -ShoppingGoodsAmount);
                    CitizenProxy.RemoveFlags(ref citizen, Citizen.Flags.NeedGoods);
                }

                if (CitizenGoesWorking(instance, citizenId, ref citizen, isVirtual) ||
                    CitizenGoesToEvent(instance, citizenId, ref citizen, isVirtual))
                {
                    return;
                }

                if (Random.ShouldOccur(ReturnFromShoppingChance) || IsWorkDayMorning(CitizenProxy.GetAge(ref citizen)))
                {
                    Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen, isVirtual)} returning from shopping at {currentBuilding} back home");
                    ReturnFromVisit(instance, citizenId, ref citizen, CitizenProxy.GetHomeBuilding(ref citizen), Citizen.Location.Home, isVirtual);
                }

                return;
            }
        }
Exemple #5
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);
        }
        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 #7
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 #8
0
 /// <summary>Schedules next actions for the citizen with a specified action time.</summary>
 /// <param name="nextState">The next scheduled citizen's state.</param>
 /// <param name="nextStateTime">The time when the scheduled state must change.</param>
 public void Schedule(ResidentState nextState, DateTime nextStateTime)
 {
     LastScheduledState = ScheduledState;
     ScheduledState     = nextState;
     ScheduledStateTime = nextStateTime;
 }
Exemple #9
0
        /// <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))
            {
                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);
            bool          isVirtual;

            switch (residentState)
            {
            case ResidentState.MovingHome:
                ProcessCitizenMoving(instance, citizenId, ref citizen, false);
                break;

            case ResidentState.AtHome:
                isVirtual = IsCitizenVirtual(instance, ref citizen, ShouldRealizeCitizen);
                ProcessCitizenAtHome(instance, citizenId, ref citizen, isVirtual);
                break;

            case ResidentState.MovingToTarget:
                ProcessCitizenMoving(instance, citizenId, ref citizen, true);
                break;

            case ResidentState.AtSchoolOrWork:
                isVirtual = IsCitizenVirtual(instance, ref citizen, ShouldRealizeCitizen);
                ProcessCitizenAtSchoolOrWork(instance, citizenId, ref citizen, isVirtual);
                break;

            case ResidentState.AtLunch:
            case ResidentState.Shopping:
            case ResidentState.AtLeisureArea:
            case ResidentState.Visiting:
                isVirtual = IsCitizenVirtual(instance, ref citizen, ShouldRealizeCitizen);
                ProcessCitizenVisit(instance, residentState, citizenId, ref citizen, isVirtual);
                break;

            case ResidentState.OnTour:
                ProcessCitizenOnTour(instance, citizenId, ref citizen);
                break;

            case ResidentState.Evacuating:
                ProcessCitizenEvacuation(instance, citizenId, ref citizen);
                break;

            case ResidentState.InShelter:
                isVirtual = IsCitizenVirtual(instance, ref citizen, ShouldRealizeCitizen);
                CitizenReturnsFromShelter(instance, citizenId, ref citizen, isVirtual);
                break;

            case ResidentState.Unknown:
                Log.Debug(TimeInfo.Now, $"WARNING: {GetCitizenDesc(citizenId, ref citizen, null)} is in an UNKNOWN state! Teleporting back home");
                if (CitizenProxy.GetHomeBuilding(ref citizen) != 0)
                {
                    CitizenProxy.SetLocation(ref citizen, Citizen.Location.Home);
                }

                break;
            }
        }