diff --git a/src/RealTime/CustomAI/Constants.cs b/src/RealTime/CustomAI/Constants.cs
index 01df13a6..50e7395b 100644
--- a/src/RealTime/CustomAI/Constants.cs
+++ b/src/RealTime/CustomAI/Constants.cs
@@ -18,11 +18,14 @@ internal static class Constants
public const float FullSearchDistance = BuildingManager.BUILDINGGRID_RESOLUTION * BuildingManager.BUILDINGGRID_CELL_SIZE / 2f;
/// A chance in percent for a citizen to stay home until next scheduled action.
- public const uint StayHomeAllDayChance = 15;
+ public const uint StayHomeAllDayChance = 2;
/// A chance in percent for a citizen to go shopping in the night.
public const uint NightShoppingChance = 20u;
+ /// A chance in percent for a citizen to go shopping just for fun.
+ public const uint FunShoppingChance = 35u;
+
/// A chance in percent for a tourist to find a hotel for sleepover.
public const uint FindHotelChance = 80;
diff --git a/src/RealTime/CustomAI/RealTimeBuildingAI.cs b/src/RealTime/CustomAI/RealTimeBuildingAI.cs
index 40d6ded3..87f4896d 100644
--- a/src/RealTime/CustomAI/RealTimeBuildingAI.cs
+++ b/src/RealTime/CustomAI/RealTimeBuildingAI.cs
@@ -94,9 +94,8 @@ public int GetConstructionTime()
/// Performs the custom processing of the outgoing problem timer.
///
/// The ID of the building to process.
- /// The old value of the outgoing problem timer.
- /// The new value of the outgoing problem timer.
- public void ProcessOutgoingProblems(ushort buildingId, byte oldValue, byte newValue)
+ /// The previous value of the outgoing problem timer.
+ public void ProcessBuildingProblems(ushort buildingId, byte outgoingProblemTimer)
{
// We have only few customers at night - that's an intended behavior.
// To avoid commercial buildings from collapsing due to lack of customers,
@@ -104,7 +103,7 @@ public void ProcessOutgoingProblems(ushort buildingId, byte oldValue, byte newVa
// In the daytime, the timer is running slower.
if (timeInfo.IsNightTime || timeInfo.Now.Minute % ProblemTimersInterval != 0 || freezeProblemTimers)
{
- buildingManager.SetOutgoingProblemTimer(buildingId, oldValue);
+ buildingManager.SetOutgoingProblemTimer(buildingId, outgoingProblemTimer);
}
}
@@ -113,8 +112,7 @@ public void ProcessOutgoingProblems(ushort buildingId, byte oldValue, byte newVa
///
/// The ID of the building to process.
/// The old value of the worker problem timer.
- /// The new value of the worker problem timer.
- public void ProcessWorkerProblems(ushort buildingId, byte oldValue, byte newValue)
+ public void ProcessWorkerProblems(ushort buildingId, byte oldValue)
{
// We force the problem timer to pause at night time.
// In the daytime, the timer is running slower.
@@ -176,9 +174,7 @@ public void ProcessFrame(uint frameIndex)
///
public bool ShouldSwitchBuildingLightsOff(ushort buildingId)
{
- return config.SwitchOffLightsAtNight
- ? !lightStates[buildingId]
- : false;
+ return config.SwitchOffLightsAtNight && !lightStates[buildingId];
}
private void UpdateLightState(uint frameIndex)
diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.Common.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Common.cs
index 5290d75d..d234704d 100644
--- a/src/RealTime/CustomAI/RealTimeResidentAI.Common.cs
+++ b/src/RealTime/CustomAI/RealTimeResidentAI.Common.cs
@@ -239,7 +239,7 @@ when BuildingMgr.GetBuildingSubService(currentBuilding) == ItemClass.SubService.
return ScheduleAction.Ignore;
}
- private void UpdateCitizenSchedule(ref CitizenSchedule schedule, uint citizenId, ref TCitizen citizen)
+ private bool UpdateCitizenSchedule(ref CitizenSchedule schedule, uint citizenId, ref TCitizen citizen)
{
// If the game changed the work building, we have to update the work shifts first
ushort workBuilding = CitizenProxy.GetWorkBuilding(ref citizen);
@@ -264,7 +264,7 @@ private void UpdateCitizenSchedule(ref CitizenSchedule schedule, uint citizenId,
if (schedule.ScheduledState != ResidentState.Unknown)
{
- return;
+ return false;
}
Log.Debug(TimeInfo.Now, $"Scheduling for {GetCitizenDesc(citizenId, ref citizen)}...");
@@ -279,7 +279,7 @@ private void UpdateCitizenSchedule(ref CitizenSchedule schedule, uint citizenId,
{
if (ScheduleWork(ref schedule, ref citizen))
{
- return;
+ return true;
}
if (schedule.ScheduledStateTime > nextActivityTime)
@@ -291,23 +291,39 @@ private void UpdateCitizenSchedule(ref CitizenSchedule schedule, uint citizenId,
if (ScheduleShopping(ref schedule, ref citizen, false))
{
Log.Debug($" - Schedule shopping");
- return;
+ return true;
}
if (ScheduleRelaxing(ref schedule, citizenId, ref citizen))
{
Log.Debug($" - Schedule relaxing");
- return;
+ return true;
}
if (schedule.CurrentState == ResidentState.AtHome)
{
if (Random.ShouldOccur(StayHomeAllDayChance))
{
- nextActivityTime = todayWakeup.FutureHour(Config.WakeupHour);
+ if (nextActivityTime < TimeInfo.Now)
+ {
+ nextActivityTime = todayWakeup.FutureHour(Config.WakeupHour);
+ }
+ }
+ else
+ {
+ nextActivityTime = default;
}
- Log.Debug($" - Schedule sleeping at home until {nextActivityTime}");
+#if DEBUG
+ if (nextActivityTime <= TimeInfo.Now)
+ {
+ Log.Debug($" - Schedule idle until next scheduling run");
+ }
+ else
+ {
+ Log.Debug($" - Schedule idle until {nextActivityTime}");
+ }
+#endif
schedule.Schedule(ResidentState.Unknown, nextActivityTime);
}
else
@@ -315,13 +331,16 @@ private void UpdateCitizenSchedule(ref CitizenSchedule schedule, uint citizenId,
Log.Debug($" - Schedule moving home");
schedule.Schedule(ResidentState.AtHome, default);
}
+
+ return true;
}
- private void ExecuteCitizenSchedule(ref CitizenSchedule schedule, TAI instance, uint citizenId, ref TCitizen citizen)
+ private void ExecuteCitizenSchedule(ref CitizenSchedule schedule, TAI instance, uint citizenId, ref TCitizen citizen, bool noReschedule)
{
- if (ProcessCurrentState(ref schedule, ref citizen) && schedule.ScheduledState == ResidentState.Unknown)
+ if (ProcessCurrentState(ref schedule, citizenId, ref citizen)
+ && schedule.ScheduledState == ResidentState.Unknown && !noReschedule)
{
- Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} will be re-scheduled now");
+ 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);
@@ -367,21 +386,21 @@ private void ExecuteCitizenSchedule(ref CitizenSchedule schedule, TAI instance,
}
}
- private bool ProcessCurrentState(ref CitizenSchedule schedule, ref TCitizen citizen)
+ private bool ProcessCurrentState(ref CitizenSchedule schedule, uint citizenId, ref TCitizen citizen)
{
switch (schedule.CurrentState)
{
case ResidentState.AtHome:
- return RescheduleAtHome(ref schedule, ref citizen);
+ return RescheduleAtHome(ref schedule, citizenId, ref citizen);
case ResidentState.Shopping:
- return ProcessCitizenShopping(ref schedule, ref citizen);
+ return ProcessCitizenShopping(ref schedule, citizenId, ref citizen);
case ResidentState.Relaxing:
- return ProcessCitizenRelaxing(ref schedule, ref citizen);
+ return ProcessCitizenRelaxing(ref schedule, citizenId, ref citizen);
case ResidentState.Visiting:
- return ProcessCitizenVisit(ref schedule, ref citizen);
+ return ProcessCitizenVisit(ref schedule, citizenId, ref citizen);
case ResidentState.InShelter:
return ProcessCitizenInShelter(ref schedule, ref citizen);
diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs
index 869cdb28..71eff2fb 100644
--- a/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs
+++ b/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs
@@ -21,13 +21,20 @@ private void DoScheduledHome(ref CitizenSchedule schedule, TAI instance, uint ci
ushort currentBuilding = CitizenProxy.GetCurrentBuilding(ref citizen);
CitizenProxy.RemoveFlags(ref citizen, Citizen.Flags.Evacuating);
- CitizenProxy.SetVisitPlace(ref citizen, citizenId, 0);
- residentAI.StartMoving(instance, citizenId, ref citizen, currentBuilding, homeBuilding);
- schedule.Schedule(ResidentState.Unknown, default);
- Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} is going from {currentBuilding} back home");
+
+ if (residentAI.StartMoving(instance, citizenId, ref citizen, currentBuilding, homeBuilding))
+ {
+ CitizenProxy.SetVisitPlace(ref citizen, citizenId, 0);
+ schedule.Schedule(ResidentState.Unknown, default);
+ Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} is going from {currentBuilding} back home");
+ }
+ else
+ {
+ Log.Debug(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, ref TCitizen citizen)
+ private bool RescheduleAtHome(ref CitizenSchedule schedule, uint citizenId, ref TCitizen citizen)
{
if (schedule.CurrentState != ResidentState.AtHome || TimeInfo.Now < schedule.ScheduledStateTime)
{
@@ -39,24 +46,24 @@ private bool RescheduleAtHome(ref CitizenSchedule schedule, ref TCitizen citizen
return false;
}
- if (IsBadWeather())
+ if (schedule.ScheduledState != ResidentState.Shopping && IsBadWeather())
{
- Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(0, ref citizen)} re-schedules an activity because of bad weather (see next line for citizen ID)");
+ Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} re-schedules an activity because of bad weather");
schedule.Schedule(ResidentState.Unknown, default);
return true;
}
- uint goOutChance = spareTimeBehavior.GetGoOutChance(
- CitizenProxy.GetAge(ref citizen),
- schedule.WorkShift,
- schedule.ScheduledState == ResidentState.Shopping);
+ var age = CitizenProxy.GetAge(ref citizen);
+ uint goOutChance = schedule.ScheduledState == ResidentState.Shopping
+ ? spareTimeBehavior.GetShoppingChance(age)
+ : spareTimeBehavior.GetRelaxingChance(age, schedule.WorkShift);
- if (Random.ShouldOccur(goOutChance))
+ if (goOutChance > 0)
{
return false;
}
- Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(0, ref citizen)} re-schedules an activity because of time (see next line for citizen ID)");
+ Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} re-schedules an activity because of time");
schedule.Schedule(ResidentState.Unknown, default);
return true;
}
diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.Moving.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Moving.cs
index cdfe4e5e..8722855d 100644
--- a/src/RealTime/CustomAI/RealTimeResidentAI.Moving.cs
+++ b/src/RealTime/CustomAI/RealTimeResidentAI.Moving.cs
@@ -70,6 +70,12 @@ private ushort MoveToCommercialBuilding(TAI instance, uint citizenId, ref TCitiz
}
ushort foundBuilding = BuildingMgr.FindActiveBuilding(currentBuilding, distance, ItemClass.Service.Commercial);
+ if (foundBuilding == 0)
+ {
+ Log.Debug($"Citizen {citizenId} didn't find any visitable commercial buildings nearby");
+ return 0;
+ }
+
if (IsBuildingNoiseRestricted(foundBuilding, currentBuilding))
{
Log.Debug($"Citizen {citizenId} won't go to the commercial building {foundBuilding}, it has a NIMBY policy");
@@ -120,14 +126,12 @@ private bool StartMovingToVisitBuilding(TAI instance, uint citizenId, ref TCitiz
if (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;
}
diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.SchoolWork.cs b/src/RealTime/CustomAI/RealTimeResidentAI.SchoolWork.cs
index 3f1f962d..3abd5ef7 100644
--- a/src/RealTime/CustomAI/RealTimeResidentAI.SchoolWork.cs
+++ b/src/RealTime/CustomAI/RealTimeResidentAI.SchoolWork.cs
@@ -23,7 +23,7 @@ private bool ScheduleWork(ref CitizenSchedule schedule, ref TCitizen citizen)
if (timeLeft <= PrepareToWorkHours)
{
// Just sit at home if the work time will come soon
- Log.Debug($" - Worktime in {timeLeft} hours, preparing for departure");
+ Log.Debug($" - Work time in {timeLeft} hours, preparing for departure");
return true;
}
@@ -31,7 +31,7 @@ private bool ScheduleWork(ref CitizenSchedule schedule, ref TCitizen citizen)
{
if (schedule.CurrentState != ResidentState.AtHome)
{
- Log.Debug($" - Worktime in {timeLeft} hours, returning home");
+ Log.Debug($" - Work time in {timeLeft} hours, returning home");
schedule.Schedule(ResidentState.AtHome, default);
return true;
}
@@ -39,11 +39,11 @@ private bool ScheduleWork(ref CitizenSchedule schedule, ref TCitizen citizen)
// If we have some time, try to shop locally.
if (ScheduleShopping(ref schedule, ref citizen, true))
{
- Log.Debug($" - Worktime in {timeLeft} hours, trying local shop");
+ Log.Debug($" - Work time in {timeLeft} hours, trying local shop");
}
else
{
- Log.Debug($" - Worktime in {timeLeft} hours, doing nothing");
+ Log.Debug($" - Work time in {timeLeft} hours, doing nothing");
}
return true;
@@ -62,22 +62,31 @@ private void DoScheduledWork(ref CitizenSchedule schedule, TAI instance, uint ci
{
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;
+ return;
}
- Citizen.AgeGroup citizenAge = CitizenProxy.GetAge(ref citizen);
- if (workBehavior.ScheduleLunch(ref schedule, citizenAge))
+ if (residentAI.StartMoving(instance, citizenId, ref citizen, currentBuilding, schedule.WorkBuilding))
{
- 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}");
+ 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
{
- 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}");
+ 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);
}
}
diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs
index db78579a..3b993ac0 100644
--- a/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs
+++ b/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs
@@ -14,7 +14,7 @@ internal sealed partial class RealTimeResidentAI
private bool ScheduleRelaxing(ref CitizenSchedule schedule, uint citizenId, ref TCitizen citizen)
{
Citizen.AgeGroup citizenAge = CitizenProxy.GetAge(ref citizen);
- if (!Random.ShouldOccur(spareTimeBehavior.GetGoOutChance(citizenAge, schedule.WorkShift, false)) || IsBadWeather())
+ if (!Random.ShouldOccur(spareTimeBehavior.GetRelaxingChance(citizenAge, schedule.WorkShift)) || IsBadWeather())
{
return false;
}
@@ -49,7 +49,7 @@ private void DoScheduledRelaxing(ref CitizenSchedule schedule, TAI instance, uin
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");
+ Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} wanted relax but didn't find a leisure building");
}
else
{
@@ -76,10 +76,10 @@ private void DoScheduledRelaxing(ref CitizenSchedule schedule, TAI instance, uin
return;
}
- uint relaxChance = spareTimeBehavior.GetGoOutChance(CitizenProxy.GetAge(ref citizen), schedule.WorkShift, false);
+ uint relaxChance = spareTimeBehavior.GetRelaxingChance(CitizenProxy.GetAge(ref citizen), schedule.WorkShift);
ResidentState nextState = Random.ShouldOccur(relaxChance)
- ? ResidentState.Unknown
- : ResidentState.Relaxing;
+ ? ResidentState.Relaxing
+ : ResidentState.Unknown;
schedule.Schedule(nextState, default);
@@ -90,7 +90,7 @@ private void DoScheduledRelaxing(ref CitizenSchedule schedule, TAI instance, uin
}
}
- private bool ProcessCitizenRelaxing(ref CitizenSchedule schedule, ref TCitizen citizen)
+ private bool ProcessCitizenRelaxing(ref CitizenSchedule schedule, uint citizenId, ref TCitizen citizen)
{
ushort currentBuilding = CitizenProxy.GetVisitBuilding(ref citizen);
if (CitizenProxy.HasFlags(ref citizen, Citizen.Flags.NeedGoods)
@@ -100,17 +100,28 @@ private bool ProcessCitizenRelaxing(ref CitizenSchedule schedule, ref TCitizen c
BuildingMgr.ModifyMaterialBuffer(currentBuilding, TransferManager.TransferReason.Shopping, -ShoppingGoodsAmount);
}
- return RescheduleVisit(ref schedule, ref citizen, currentBuilding);
+ return RescheduleVisit(ref schedule, citizenId, ref citizen, currentBuilding);
}
private bool ScheduleShopping(ref CitizenSchedule schedule, ref TCitizen citizen, bool localOnly)
{
- if (!CitizenProxy.HasFlags(ref citizen, Citizen.Flags.NeedGoods) || IsBadWeather())
+ // If the citizen doesn't need any good, he/she still can go shopping just for fun
+ if (!CitizenProxy.HasFlags(ref citizen, Citizen.Flags.NeedGoods))
{
- return false;
+ if (schedule.Hint == ScheduleHint.NoShoppingAnyMore || !Random.ShouldOccur(FunShoppingChance))
+ {
+ schedule.Hint = ScheduleHint.None;
+ return false;
+ }
+
+ schedule.Hint = ScheduleHint.NoShoppingAnyMore;
+ }
+ else
+ {
+ schedule.Hint = ScheduleHint.None;
}
- if (!Random.ShouldOccur(spareTimeBehavior.GetGoOutChance(CitizenProxy.GetAge(ref citizen), schedule.WorkShift, true)))
+ if (!Random.ShouldOccur(spareTimeBehavior.GetShoppingChance(CitizenProxy.GetAge(ref citizen))))
{
return false;
}
@@ -119,10 +130,6 @@ private bool ScheduleShopping(ref CitizenSchedule schedule, ref TCitizen citizen
{
schedule.Hint = ScheduleHint.LocalShoppingOnly;
}
- else
- {
- schedule.Hint = ScheduleHint.None;
- }
schedule.Schedule(ResidentState.Shopping, default);
return true;
@@ -143,27 +150,32 @@ private void DoScheduledShopping(ref CitizenSchedule schedule, TAI instance, uin
}
else
{
+ if (TimeInfo.IsNightTime)
+ {
+ schedule.Hint = ScheduleHint.NoShoppingAnyMore;
+ }
+
Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} goes shopping at a local shop {shop}");
}
}
else
{
- uint moreShoppingChance = spareTimeBehavior.GetGoOutChance(CitizenProxy.GetAge(ref citizen), schedule.WorkShift, true);
- ResidentState nextState = Random.ShouldOccur(moreShoppingChance)
- ? ResidentState.Unknown
- : ResidentState.Shopping;
+ uint moreShoppingChance = spareTimeBehavior.GetShoppingChance(CitizenProxy.GetAge(ref citizen));
+ ResidentState nextState = schedule.Hint != ScheduleHint.NoShoppingAnyMore && Random.ShouldOccur(moreShoppingChance)
+ ? ResidentState.Shopping
+ : ResidentState.Unknown;
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");
+ Log.Debug(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));
}
}
}
- private bool ProcessCitizenShopping(ref CitizenSchedule schedule, ref TCitizen citizen)
+ private bool ProcessCitizenShopping(ref CitizenSchedule schedule, uint citizenId, ref TCitizen citizen)
{
ushort currentBuilding = CitizenProxy.GetVisitBuilding(ref citizen);
if (CitizenProxy.HasFlags(ref citizen, Citizen.Flags.NeedGoods) && currentBuilding != 0)
@@ -172,19 +184,19 @@ private bool ProcessCitizenShopping(ref CitizenSchedule schedule, ref TCitizen c
CitizenProxy.RemoveFlags(ref citizen, Citizen.Flags.NeedGoods);
}
- return RescheduleVisit(ref schedule, ref citizen, currentBuilding);
+ return RescheduleVisit(ref schedule, citizenId, ref citizen, currentBuilding);
}
- private bool ProcessCitizenVisit(ref CitizenSchedule schedule, ref TCitizen citizen)
+ private bool ProcessCitizenVisit(ref CitizenSchedule schedule, uint citizenId, 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)");
+ Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} quits a tour");
schedule.Schedule(ResidentState.Unknown, default);
return true;
}
- return RescheduleVisit(ref schedule, ref citizen, CitizenProxy.GetVisitBuilding(ref citizen));
+ return RescheduleVisit(ref schedule, citizenId, ref citizen, CitizenProxy.GetVisitBuilding(ref citizen));
}
private bool IsBuildingNoiseRestricted(ushort targetBuilding, ushort currentBuilding)
@@ -215,7 +227,7 @@ private bool IsBuildingNoiseRestricted(ushort targetBuilding, ushort currentBuil
return false;
}
- private bool RescheduleVisit(ref CitizenSchedule schedule, ref TCitizen citizen, ushort currentBuilding)
+ private bool RescheduleVisit(ref CitizenSchedule schedule, uint citizenId, ref TCitizen citizen, ushort currentBuilding)
{
switch (schedule.ScheduledState)
{
@@ -228,28 +240,28 @@ private bool RescheduleVisit(ref CitizenSchedule schedule, ref TCitizen citizen,
return false;
}
- if (IsBadWeather())
+ if (schedule.CurrentState != ResidentState.Shopping && IsBadWeather())
{
- Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(0, ref citizen)} quits a visit because of bad weather (see next line for citizen ID)");
+ Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} quits a visit because of bad weather");
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)");
+ Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} quits a visit because of NIMBY policy");
schedule.Schedule(ResidentState.Unknown, default);
return true;
}
- uint stayChance = spareTimeBehavior.GetGoOutChance(
- CitizenProxy.GetAge(ref citizen),
- schedule.WorkShift,
- schedule.CurrentState == ResidentState.Shopping);
+ Citizen.AgeGroup age = CitizenProxy.GetAge(ref citizen);
+ uint stayChance = schedule.CurrentState == ResidentState.Shopping
+ ? spareTimeBehavior.GetShoppingChance(age)
+ : spareTimeBehavior.GetRelaxingChance(age, schedule.WorkShift);
if (!Random.ShouldOccur(stayChance))
{
- Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(0, ref citizen)} quits a visit because of time (see next line for citizen ID)");
+ Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} quits a visit because of time");
schedule.Schedule(ResidentState.AtHome, default);
return true;
}
diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.cs b/src/RealTime/CustomAI/RealTimeResidentAI.cs
index 6358e2f8..0fdf6b95 100644
--- a/src/RealTime/CustomAI/RealTimeResidentAI.cs
+++ b/src/RealTime/CustomAI/RealTimeResidentAI.cs
@@ -101,8 +101,8 @@ public void UpdateLocation(TAI instance, uint citizenId, ref TCitizen citizen)
return;
}
- UpdateCitizenSchedule(ref schedule, citizenId, ref citizen);
- ExecuteCitizenSchedule(ref schedule, instance, citizenId, ref citizen);
+ bool updated = UpdateCitizenSchedule(ref schedule, citizenId, ref citizen);
+ ExecuteCitizenSchedule(ref schedule, instance, citizenId, ref citizen, updated);
}
/// Notifies that a citizen has arrived their destination.
diff --git a/src/RealTime/CustomAI/RealTimeTouristAI.cs b/src/RealTime/CustomAI/RealTimeTouristAI.cs
index e3cdbb6a..d6f70055 100644
--- a/src/RealTime/CustomAI/RealTimeTouristAI.cs
+++ b/src/RealTime/CustomAI/RealTimeTouristAI.cs
@@ -198,10 +198,10 @@ private void FindRandomVisitPlace(TAI instance, uint citizenId, ref TCitizen cit
return;
}
- uint goOutChance = spareTimeBehavior.GetGoOutChance(
- CitizenProxy.GetAge(ref citizen),
- WorkShift.Unemployed,
- CitizenProxy.HasFlags(ref citizen, Citizen.Flags.NeedGoods));
+ Citizen.AgeGroup age = CitizenProxy.GetAge(ref citizen);
+ uint goOutChance = CitizenProxy.HasFlags(ref citizen, Citizen.Flags.NeedGoods)
+ ? spareTimeBehavior.GetShoppingChance(age)
+ : spareTimeBehavior.GetRelaxingChance(age, WorkShift.Unemployed);
if (!Random.ShouldOccur(goOutChance) || IsBadWeather())
{
@@ -252,9 +252,10 @@ private void FindHotel(TAI instance, uint citizenId, ref TCitizen citizen)
private void StartMovingToVisitBuilding(TAI instance, uint citizenId, ref TCitizen citizen, ushort currentBuilding, ushort visitBuilding)
{
- CitizenProxy.SetVisitPlace(ref citizen, citizenId, visitBuilding);
- CitizenProxy.SetVisitBuilding(ref citizen, visitBuilding);
- touristAI.StartMoving(instance, citizenId, ref citizen, currentBuilding, visitBuilding);
+ if (touristAI.StartMoving(instance, citizenId, ref citizen, currentBuilding, visitBuilding))
+ {
+ CitizenProxy.SetVisitPlace(ref citizen, citizenId, visitBuilding);
+ }
}
}
}
diff --git a/src/RealTime/CustomAI/ScheduleHint.cs b/src/RealTime/CustomAI/ScheduleHint.cs
index 15aea66b..0f3c8755 100644
--- a/src/RealTime/CustomAI/ScheduleHint.cs
+++ b/src/RealTime/CustomAI/ScheduleHint.cs
@@ -13,6 +13,9 @@ internal enum ScheduleHint : byte
/// The citizen can shop only locally.
LocalShoppingOnly,
+ /// The citizen will not go shopping one more time right away.
+ NoShoppingAnyMore,
+
/// The citizen should find a leisure building.
RelaxAtLeisureBuilding,
diff --git a/src/RealTime/CustomAI/SpareTimeBehavior.cs b/src/RealTime/CustomAI/SpareTimeBehavior.cs
index f00209a9..eaab42ee 100644
--- a/src/RealTime/CustomAI/SpareTimeBehavior.cs
+++ b/src/RealTime/CustomAI/SpareTimeBehavior.cs
@@ -49,7 +49,7 @@ public void SetSimulationCyclePeriod(float cyclePeriod)
}
/// Calculates the chances for the citizens to go out based on the current game time.
- public void RefreshGoOutChances()
+ public void RefreshChances()
{
uint weekdayModifier;
if (config.IsWeekendEnabled)
@@ -73,22 +73,29 @@ public void RefreshGoOutChances()
}
///
- /// Gets the probability whether a citizen with specified age would go out on current time.
+ /// Gets the probability whether a citizen with specified age would go shopping on current time.
///
///
/// The age of the citizen to check.
- /// The citizen's assigned work shift (or ).
- /// true when the citizen needs to buy something; otherwise, false.
///
/// A percentage value in range of 0..100 that describes the probability whether
- /// a citizen with specified age would go out on current time.
- public uint GetGoOutChance(Citizen.AgeGroup citizenAge, WorkShift workShift, bool needsShopping)
+ /// a citizen with specified age would go shopping on current time.
+ public uint GetShoppingChance(Citizen.AgeGroup citizenAge)
{
- if (needsShopping)
- {
- return shoppingChances[(int)citizenAge];
- }
+ return shoppingChances[(int)citizenAge];
+ }
+ ///
+ /// Gets the probability whether a citizen with specified age would go relaxing on current time.
+ ///
+ ///
+ /// The age of the citizen to check.
+ /// The citizen's assigned work shift (or ).
+ ///
+ /// A percentage value in range of 0..100 that describes the probability whether
+ /// a citizen with specified age would go relaxing on current time.
+ public uint GetRelaxingChance(Citizen.AgeGroup citizenAge, WorkShift workShift)
+ {
int age = (int)citizenAge;
switch (citizenAge)
{
@@ -246,11 +253,11 @@ private void CalculateShoppingChance(float currentHour)
uint roundedChance = (uint)Math.Round(chance);
- shoppingChances[(int)Citizen.AgeGroup.Child] = isNight ? 0u : (uint)Math.Round(chance * 0.6f);
+ shoppingChances[(int)Citizen.AgeGroup.Child] = isNight ? 0u : roundedChance;
shoppingChances[(int)Citizen.AgeGroup.Teen] = isNight ? 0u : roundedChance;
shoppingChances[(int)Citizen.AgeGroup.Young] = roundedChance;
shoppingChances[(int)Citizen.AgeGroup.Adult] = roundedChance;
- shoppingChances[(int)Citizen.AgeGroup.Senior] = isNight ? 0u : (uint)Math.Round(chance * 0.8f);
+ shoppingChances[(int)Citizen.AgeGroup.Senior] = isNight ? (uint)Math.Round(chance * 0.1f) : roundedChance;
#if DEBUG
if (oldChance != roundedChance)
diff --git a/src/RealTime/GameConnection/BuildingManagerConnection.cs b/src/RealTime/GameConnection/BuildingManagerConnection.cs
index 9af5276d..fc2c76bf 100644
--- a/src/RealTime/GameConnection/BuildingManagerConnection.cs
+++ b/src/RealTime/GameConnection/BuildingManagerConnection.cs
@@ -14,6 +14,9 @@ namespace RealTime.GameConnection
///
internal sealed class BuildingManagerConnection : IBuildingManagerConnection
{
+ private const int MaxBuildingGridIndex = BuildingManager.BUILDINGGRID_RESOLUTION - 1;
+ private const int BuildingGridMiddle = BuildingManager.BUILDINGGRID_RESOLUTION / 2;
+
/// Gets the service type of the building with specified ID.
/// The ID of the building to get the service type of.
///
@@ -24,7 +27,7 @@ public ItemClass.Service GetBuildingService(ushort buildingId)
{
return buildingId == 0
? ItemClass.Service.None
- : BuildingManager.instance.m_buildings.m_buffer[buildingId].Info.m_class.m_service;
+ : BuildingManager.instance.m_buildings.m_buffer[buildingId].Info?.m_class?.m_service ?? ItemClass.Service.None;
}
/// Gets the sub-service type of the building with specified ID.
@@ -37,7 +40,7 @@ public ItemClass.SubService GetBuildingSubService(ushort buildingId)
{
return buildingId == 0
? ItemClass.SubService.None
- : BuildingManager.instance.m_buildings.m_buffer[buildingId].Info.m_class.m_subService;
+ : BuildingManager.instance.m_buildings.m_buffer[buildingId].Info?.m_class?.m_subService ?? ItemClass.SubService.None;
}
/// Gets the service and sub-service types of the building with specified ID.
@@ -130,10 +133,10 @@ public void ModifyMaterialBuffer(ushort buildingId, TransferManager.TransferReas
}
ref Building building = ref BuildingManager.instance.m_buildings.m_buffer[buildingId];
- building.Info.m_buildingAI.ModifyMaterialBuffer(buildingId, ref building, reason, ref delta);
+ building.Info?.m_buildingAI.ModifyMaterialBuffer(buildingId, ref building, reason, ref delta);
}
- /// Finds an active building that matches the specified criteria.
+ /// Finds an active building that matches the specified criteria and can accept visitors.
/// The building ID that represents the search area center point.
/// The maximum distance for search, the search area radius.
/// The building service type to find.
@@ -155,13 +158,47 @@ public ushort FindActiveBuilding(
Building.Flags restrictedFlags = Building.Flags.Deleted | Building.Flags.Evacuating | Building.Flags.Flooded | Building.Flags.Collapsed
| Building.Flags.BurnedDown | Building.Flags.RoadAccessFailed;
- return BuildingManager.instance.FindBuilding(
- currentPosition,
- maxDistance,
- service,
- subService,
- Building.Flags.Created | Building.Flags.Completed | Building.Flags.Active,
- restrictedFlags);
+ Building.Flags requiredFlags = Building.Flags.Created | Building.Flags.Completed | Building.Flags.Active;
+ Building.Flags combinedFlags = requiredFlags | restrictedFlags;
+
+ int gridXFrom = Mathf.Max((int)(((currentPosition.x - maxDistance) / BuildingManager.BUILDINGGRID_CELL_SIZE) + BuildingGridMiddle), 0);
+ int gridZFrom = Mathf.Max((int)(((currentPosition.z - maxDistance) / BuildingManager.BUILDINGGRID_CELL_SIZE) + BuildingGridMiddle), 0);
+ int gridXTo = Mathf.Min((int)(((currentPosition.x + maxDistance) / BuildingManager.BUILDINGGRID_CELL_SIZE) + BuildingGridMiddle), MaxBuildingGridIndex);
+ int gridZTo = Mathf.Min((int)(((currentPosition.z + maxDistance) / BuildingManager.BUILDINGGRID_CELL_SIZE) + BuildingGridMiddle), MaxBuildingGridIndex);
+
+ float sqrMaxDistance = maxDistance * maxDistance;
+ for (int z = gridZFrom; z <= gridZTo; ++z)
+ {
+ for (int x = gridXFrom; x <= gridXTo; ++x)
+ {
+ ushort buildingId = BuildingManager.instance.m_buildingGrid[(z * BuildingManager.BUILDINGGRID_RESOLUTION) + x];
+ uint counter = 0;
+ while (buildingId != 0)
+ {
+ ref Building building = ref BuildingManager.instance.m_buildings.m_buffer[buildingId];
+ if (building.Info != null
+ && building.Info.m_class != null
+ && (building.Info.m_class.m_service == service)
+ && (subService == ItemClass.SubService.None || building.Info.m_class.m_subService == subService)
+ && (building.m_flags & combinedFlags) == requiredFlags)
+ {
+ float sqrDistance = Vector3.SqrMagnitude(currentPosition - building.m_position);
+ if (sqrDistance < sqrMaxDistance && BuildingCanBeVisited(buildingId))
+ {
+ return buildingId;
+ }
+ }
+
+ buildingId = building.m_nextGridBuilding;
+ if (++counter >= BuildingManager.MAX_BUILDING_COUNT)
+ {
+ break;
+ }
+ }
+ }
+ }
+
+ return 0;
}
/// Gets the ID of an event that takes place in the building with specified ID.
@@ -253,7 +290,7 @@ public string GetBuildingClassName(ushort buildingId)
{
return buildingId == 0
? string.Empty
- : BuildingManager.instance.m_buildings.m_buffer[buildingId].Info.name;
+ : BuildingManager.instance.m_buildings.m_buffer[buildingId].Info?.name ?? string.Empty;
}
/// Gets the localized name of a building with specified ID.
@@ -304,5 +341,33 @@ public void UpdateBuildingColors(ushort buildingId)
BuildingManager.instance.UpdateBuildingColors(buildingId);
}
}
+
+ private static bool BuildingCanBeVisited(ushort buildingId)
+ {
+ uint currentUnitId = BuildingManager.instance.m_buildings.m_buffer[buildingId].m_citizenUnits;
+
+ uint counter = 0;
+ while (currentUnitId != 0)
+ {
+ ref CitizenUnit currentUnit = ref CitizenManager.instance.m_units.m_buffer[currentUnitId];
+ if ((currentUnit.m_flags & CitizenUnit.Flags.Visit) != 0
+ && (currentUnit.m_citizen0 == 0
+ || currentUnit.m_citizen1 == 0
+ || currentUnit.m_citizen2 == 0
+ || currentUnit.m_citizen3 == 0
+ || currentUnit.m_citizen4 == 0))
+ {
+ return true;
+ }
+
+ currentUnitId = currentUnit.m_nextUnit;
+ if (++counter >= CitizenManager.MAX_UNIT_COUNT)
+ {
+ break;
+ }
+ }
+
+ return false;
+ }
}
}
diff --git a/src/RealTime/GameConnection/CitizenConnection.cs b/src/RealTime/GameConnection/CitizenConnection.cs
index 608c1b53..c21b25f1 100644
--- a/src/RealTime/GameConnection/CitizenConnection.cs
+++ b/src/RealTime/GameConnection/CitizenConnection.cs
@@ -224,16 +224,6 @@ public void SetLocation(ref Citizen citizen, Citizen.Location location)
citizen.CurrentLocation = location;
}
- ///
- /// Sets the ID of the building that is currently visited by the specified citizen.
- ///
- /// The citizen to set the visited building ID for.
- /// The ID of the currently visited building.
- public void SetVisitBuilding(ref Citizen citizen, ushort visitBuilding)
- {
- citizen.m_visitBuilding = visitBuilding;
- }
-
///
/// Sets the ID of the building the specified citizen is currently visiting.
///
diff --git a/src/RealTime/GameConnection/IBuildingManagerConnection.cs b/src/RealTime/GameConnection/IBuildingManagerConnection.cs
index 0de218ed..0b80cc81 100644
--- a/src/RealTime/GameConnection/IBuildingManagerConnection.cs
+++ b/src/RealTime/GameConnection/IBuildingManagerConnection.cs
@@ -66,7 +66,7 @@ internal interface IBuildingManagerConnection
/// The amount to modify the buffer by.
void ModifyMaterialBuffer(ushort buildingId, TransferManager.TransferReason reason, int delta);
- /// Finds an active building that matches the specified criteria.
+ /// Finds an active building that matches the specified criteria and can accept visitors.
///
/// The building ID that represents the search area center point.
///
diff --git a/src/RealTime/GameConnection/ICitizenConnection.cs b/src/RealTime/GameConnection/ICitizenConnection.cs
index 8538e80e..b068e22d 100644
--- a/src/RealTime/GameConnection/ICitizenConnection.cs
+++ b/src/RealTime/GameConnection/ICitizenConnection.cs
@@ -26,13 +26,6 @@ internal interface ICitizenConnection
/// The ID of the building currently visited by the citizen, or 0 if none found.
ushort GetVisitBuilding(ref T citizen);
- ///
- /// Sets the ID of the building that is currently visited by the specified citizen.
- ///
- /// The citizen to set the visited building ID for.
- /// The ID of the currently visited building.
- void SetVisitBuilding(ref T citizen, ushort visitBuilding);
-
/// Gets the instance ID of the specified citizen.
/// The citizen to get the instance ID of.
/// The ID of the citizen's instance, or 0 if none found.
diff --git a/src/RealTime/GameConnection/Patches/BuildingAIPatches.cs b/src/RealTime/GameConnection/Patches/BuildingAIPatches.cs
index 09fb9ab1..c080271c 100644
--- a/src/RealTime/GameConnection/Patches/BuildingAIPatches.cs
+++ b/src/RealTime/GameConnection/Patches/BuildingAIPatches.cs
@@ -48,6 +48,13 @@ protected override MethodInfo GetMethod()
private static bool Prefix(ref Building buildingData, ref byte __state)
{
__state = buildingData.m_outgoingProblemTimer;
+ if (buildingData.m_customBuffer2 > 0)
+ {
+ // Simulate some goods become spoiled; additionally, this will cause the buildings to never reach the 'stock full' state.
+ // In that state, no visits are possible anymore, so the building gets stuck
+ --buildingData.m_customBuffer2;
+ }
+
return true;
}
@@ -55,7 +62,7 @@ private static void Postfix(ushort buildingID, ref Building buildingData, byte _
{
if (__state != buildingData.m_outgoingProblemTimer)
{
- RealTimeAI?.ProcessOutgoingProblems(buildingID, __state, buildingData.m_outgoingProblemTimer);
+ RealTimeAI?.ProcessBuildingProblems(buildingID, __state);
}
}
#pragma warning restore SA1313 // Parameter names must begin with lower-case letter
@@ -86,7 +93,7 @@ private static void Postfix(ushort buildingID, ref Building buildingData, byte _
{
if (__state != buildingData.m_workerProblemTimer)
{
- RealTimeAI?.ProcessWorkerProblems(buildingID, __state, buildingData.m_workerProblemTimer);
+ RealTimeAI?.ProcessWorkerProblems(buildingID, __state);
}
}
#pragma warning restore SA1313 // Parameter names must begin with lower-case letter
diff --git a/src/RealTime/Localization/Translations/de.xml b/src/RealTime/Localization/Translations/de.xml
index 819ca15b..134d888b 100644
--- a/src/RealTime/Localization/Translations/de.xml
+++ b/src/RealTime/Localization/Translations/de.xml
@@ -1,4 +1,4 @@
-
+
diff --git a/src/RealTime/Localization/Translations/en.xml b/src/RealTime/Localization/Translations/en.xml
index 76bb20ff..19e9081e 100644
--- a/src/RealTime/Localization/Translations/en.xml
+++ b/src/RealTime/Localization/Translations/en.xml
@@ -1,4 +1,4 @@
-
+
diff --git a/src/RealTime/Localization/Translations/es.xml b/src/RealTime/Localization/Translations/es.xml
index 4a10c751..ef7059bf 100644
--- a/src/RealTime/Localization/Translations/es.xml
+++ b/src/RealTime/Localization/Translations/es.xml
@@ -1,4 +1,4 @@
-
+
diff --git a/src/RealTime/Localization/Translations/fr.xml b/src/RealTime/Localization/Translations/fr.xml
index 38a7732d..7b8ec65f 100644
--- a/src/RealTime/Localization/Translations/fr.xml
+++ b/src/RealTime/Localization/Translations/fr.xml
@@ -1,4 +1,4 @@
-
+
diff --git a/src/RealTime/Localization/Translations/ko.xml b/src/RealTime/Localization/Translations/ko.xml
index 2de086e1..5aa00faf 100644
--- a/src/RealTime/Localization/Translations/ko.xml
+++ b/src/RealTime/Localization/Translations/ko.xml
@@ -1,4 +1,4 @@
-
+
diff --git a/src/RealTime/Localization/Translations/pl.xml b/src/RealTime/Localization/Translations/pl.xml
index 18c37f04..dd79c59e 100644
--- a/src/RealTime/Localization/Translations/pl.xml
+++ b/src/RealTime/Localization/Translations/pl.xml
@@ -1,4 +1,4 @@
-
+
diff --git a/src/RealTime/Localization/Translations/pt.xml b/src/RealTime/Localization/Translations/pt.xml
index 65259669..89a0b125 100644
--- a/src/RealTime/Localization/Translations/pt.xml
+++ b/src/RealTime/Localization/Translations/pt.xml
@@ -1,4 +1,4 @@
-
+
diff --git a/src/RealTime/Localization/Translations/ru.xml b/src/RealTime/Localization/Translations/ru.xml
index 7c300256..c08b8141 100644
--- a/src/RealTime/Localization/Translations/ru.xml
+++ b/src/RealTime/Localization/Translations/ru.xml
@@ -1,4 +1,4 @@
-
+
diff --git a/src/RealTime/Localization/Translations/zh.xml b/src/RealTime/Localization/Translations/zh.xml
index f2ac101b..09e67f9c 100644
--- a/src/RealTime/Localization/Translations/zh.xml
+++ b/src/RealTime/Localization/Translations/zh.xml
@@ -1,4 +1,4 @@
-
+
diff --git a/src/RealTime/Simulation/CitizenProcessor.cs b/src/RealTime/Simulation/CitizenProcessor.cs
index 977eb401..86b5e842 100644
--- a/src/RealTime/Simulation/CitizenProcessor.cs
+++ b/src/RealTime/Simulation/CitizenProcessor.cs
@@ -51,7 +51,7 @@ public void UpdateFrameDuration()
/// Processes the simulation tick.
public void ProcessTick()
{
- spareTimeBehavior.RefreshGoOutChances();
+ spareTimeBehavior.RefreshChances();
}
/// Processes the simulation frame.
diff --git a/src/RealTime/UI/CustomTimeBar.cs b/src/RealTime/UI/CustomTimeBar.cs
index 56409950..587fe26b 100644
--- a/src/RealTime/UI/CustomTimeBar.cs
+++ b/src/RealTime/UI/CustomTimeBar.cs
@@ -231,11 +231,11 @@ private UIDateTimeWrapper SetUIDateTimeWrapper(UIDateTimeWrapper wrapper, bool c
private void DisplayCityEvent(ICityEvent cityEvent, DateTime todayStart, DateTime todayEnd)
{
float startPercent = cityEvent.StartTime <= todayStart
- ? startPercent = 0
+ ? 0
: (float)cityEvent.StartTime.TimeOfDay.TotalHours / 24f;
float endPercent = cityEvent.EndTime >= todayEnd
- ? endPercent = 1f
+ ? 1f
: (float)cityEvent.EndTime.TimeOfDay.TotalHours / 24f;
float startPosition = progressSprite.width * startPercent;