From 8f88212cb31f5e430019fb83fed0d14bd94f4021 Mon Sep 17 00:00:00 2001
From: dymanoid <9433345+dymanoid@users.noreply.github.com>
Date: Tue, 24 Jul 2018 12:41:58 +0200
Subject: [PATCH 01/14] Prevent exceptions when querying buildings that have no
building info
---
src/RealTime/GameConnection/BuildingManagerConnection.cs | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/RealTime/GameConnection/BuildingManagerConnection.cs b/src/RealTime/GameConnection/BuildingManagerConnection.cs
index 9af5276d..bf6964ea 100644
--- a/src/RealTime/GameConnection/BuildingManagerConnection.cs
+++ b/src/RealTime/GameConnection/BuildingManagerConnection.cs
@@ -24,7 +24,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 +37,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,7 +130,7 @@ 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.
@@ -253,7 +253,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.
From 46f8cabdb4f838d47d667bb4a7844a7d4217528d Mon Sep 17 00:00:00 2001
From: dymanoid <9433345+dymanoid@users.noreply.github.com>
Date: Thu, 26 Jul 2018 20:09:33 +0200
Subject: [PATCH 02/14] Fix #9 by removing UTF-8 BOM from the localization XML
files
---
src/RealTime/Localization/Translations/de.xml | 2 +-
src/RealTime/Localization/Translations/en.xml | 2 +-
src/RealTime/Localization/Translations/es.xml | 2 +-
src/RealTime/Localization/Translations/fr.xml | 2 +-
src/RealTime/Localization/Translations/ko.xml | 2 +-
src/RealTime/Localization/Translations/pl.xml | 2 +-
src/RealTime/Localization/Translations/pt.xml | 2 +-
src/RealTime/Localization/Translations/ru.xml | 2 +-
src/RealTime/Localization/Translations/zh.xml | 2 +-
9 files changed, 9 insertions(+), 9 deletions(-)
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 @@
-
+
From 97845a9e8482f0809b76bc13651d66c0d7a0ee7a Mon Sep 17 00:00:00 2001
From: dymanoid <9433345+dymanoid@users.noreply.github.com>
Date: Thu, 26 Jul 2018 21:24:43 +0200
Subject: [PATCH 03/14] Consider only buildings with free visit slots when
searching for shops
---
.../CustomAI/RealTimeResidentAI.Moving.cs | 8 +-
.../CustomAI/RealTimeResidentAI.Visit.cs | 2 +-
src/RealTime/CustomAI/RealTimeTouristAI.cs | 7 +-
.../BuildingManagerConnection.cs | 81 +++++++++++++++++--
.../IBuildingManagerConnection.cs | 2 +-
.../GameConnection/ICitizenConnection.cs | 7 --
6 files changed, 85 insertions(+), 22 deletions(-)
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.Visit.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs
index db78579a..7bebb427 100644
--- a/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs
+++ b/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs
@@ -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
{
diff --git a/src/RealTime/CustomAI/RealTimeTouristAI.cs b/src/RealTime/CustomAI/RealTimeTouristAI.cs
index e3cdbb6a..3506e358 100644
--- a/src/RealTime/CustomAI/RealTimeTouristAI.cs
+++ b/src/RealTime/CustomAI/RealTimeTouristAI.cs
@@ -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/GameConnection/BuildingManagerConnection.cs b/src/RealTime/GameConnection/BuildingManagerConnection.cs
index bf6964ea..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.
///
@@ -133,7 +136,7 @@ public void ModifyMaterialBuffer(ushort buildingId, TransferManager.TransferReas
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.
@@ -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/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.
From 8fedeb7d5d6f5c1e0cf767559999d622248d4d56 Mon Sep 17 00:00:00 2001
From: dymanoid <9433345+dymanoid@users.noreply.github.com>
Date: Fri, 27 Jul 2018 22:07:14 +0200
Subject: [PATCH 04/14] Fix issue caused too many citizens to stay at home for
too long
---
src/RealTime/CustomAI/Constants.cs | 2 +-
.../CustomAI/RealTimeResidentAI.Common.cs | 20 +++++++++++++++++--
2 files changed, 19 insertions(+), 3 deletions(-)
diff --git a/src/RealTime/CustomAI/Constants.cs b/src/RealTime/CustomAI/Constants.cs
index 01df13a6..6b4a76ff 100644
--- a/src/RealTime/CustomAI/Constants.cs
+++ b/src/RealTime/CustomAI/Constants.cs
@@ -18,7 +18,7 @@ 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;
diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.Common.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Common.cs
index 5290d75d..b27d16c7 100644
--- a/src/RealTime/CustomAI/RealTimeResidentAI.Common.cs
+++ b/src/RealTime/CustomAI/RealTimeResidentAI.Common.cs
@@ -304,10 +304,26 @@ private void UpdateCitizenSchedule(ref CitizenSchedule schedule, uint citizenId,
{
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
From a23fc42b581185fe19274dc6cffe537f85bb9d48 Mon Sep 17 00:00:00 2001
From: dymanoid <9433345+dymanoid@users.noreply.github.com>
Date: Fri, 27 Jul 2018 22:08:55 +0200
Subject: [PATCH 05/14] Separate shopping and relaxing chances queries
---
.../CustomAI/RealTimeResidentAI.Home.cs | 8 +++---
.../CustomAI/RealTimeResidentAI.Visit.cs | 16 ++++++------
src/RealTime/CustomAI/RealTimeTouristAI.cs | 8 +++---
src/RealTime/CustomAI/SpareTimeBehavior.cs | 25 ++++++++++++-------
4 files changed, 32 insertions(+), 25 deletions(-)
diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs
index 869cdb28..b60d1cb8 100644
--- a/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs
+++ b/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs
@@ -46,10 +46,10 @@ private bool RescheduleAtHome(ref CitizenSchedule schedule, ref TCitizen citizen
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))
{
diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs
index 7bebb427..67d2ec18 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;
}
@@ -76,7 +76,7 @@ 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;
@@ -110,7 +110,7 @@ private bool ScheduleShopping(ref CitizenSchedule schedule, ref TCitizen citizen
return false;
}
- if (!Random.ShouldOccur(spareTimeBehavior.GetGoOutChance(CitizenProxy.GetAge(ref citizen), schedule.WorkShift, true)))
+ if (!Random.ShouldOccur(spareTimeBehavior.GetShoppingChance(CitizenProxy.GetAge(ref citizen))))
{
return false;
}
@@ -148,7 +148,7 @@ private void DoScheduledShopping(ref CitizenSchedule schedule, TAI instance, uin
}
else
{
- uint moreShoppingChance = spareTimeBehavior.GetGoOutChance(CitizenProxy.GetAge(ref citizen), schedule.WorkShift, true);
+ uint moreShoppingChance = spareTimeBehavior.GetShoppingChance(CitizenProxy.GetAge(ref citizen));
ResidentState nextState = Random.ShouldOccur(moreShoppingChance)
? ResidentState.Unknown
: ResidentState.Shopping;
@@ -242,10 +242,10 @@ private bool RescheduleVisit(ref CitizenSchedule schedule, ref TCitizen citizen,
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))
{
diff --git a/src/RealTime/CustomAI/RealTimeTouristAI.cs b/src/RealTime/CustomAI/RealTimeTouristAI.cs
index 3506e358..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())
{
diff --git a/src/RealTime/CustomAI/SpareTimeBehavior.cs b/src/RealTime/CustomAI/SpareTimeBehavior.cs
index f00209a9..80dd46a9 100644
--- a/src/RealTime/CustomAI/SpareTimeBehavior.cs
+++ b/src/RealTime/CustomAI/SpareTimeBehavior.cs
@@ -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)
{
From 3e9ce1165eb954d0b91a540ce4f2ec1100f419a6 Mon Sep 17 00:00:00 2001
From: dymanoid <9433345+dymanoid@users.noreply.github.com>
Date: Fri, 27 Jul 2018 22:11:38 +0200
Subject: [PATCH 06/14] Fix wrong scheduling when citizen didn't move to the
scheduled building
---
.../CustomAI/RealTimeResidentAI.Home.cs | 15 ++++++--
.../CustomAI/RealTimeResidentAI.SchoolWork.cs | 37 ++++++++++++-------
2 files changed, 34 insertions(+), 18 deletions(-)
diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs
index b60d1cb8..bbb75269 100644
--- a/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs
+++ b/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs
@@ -21,10 +21,17 @@ 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)
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);
}
}
From ceaabcdfa686892de895d8e98260422fabe4c704 Mon Sep 17 00:00:00 2001
From: dymanoid <9433345+dymanoid@users.noreply.github.com>
Date: Fri, 27 Jul 2018 23:10:42 +0200
Subject: [PATCH 07/14] Ignore weather when it's time to go shopping
- don't force the problems "no customers" / "building full on stock"
---
src/RealTime/CustomAI/RealTimeResidentAI.Home.cs | 2 +-
src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs | 4 ++--
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs
index bbb75269..907dc9be 100644
--- a/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs
+++ b/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs
@@ -46,7 +46,7 @@ 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)");
schedule.Schedule(ResidentState.Unknown, default);
diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs
index 67d2ec18..4db2c4e7 100644
--- a/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs
+++ b/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs
@@ -105,7 +105,7 @@ private bool ProcessCitizenRelaxing(ref CitizenSchedule schedule, ref TCitizen c
private bool ScheduleShopping(ref CitizenSchedule schedule, ref TCitizen citizen, bool localOnly)
{
- if (!CitizenProxy.HasFlags(ref citizen, Citizen.Flags.NeedGoods) || IsBadWeather())
+ if (!CitizenProxy.HasFlags(ref citizen, Citizen.Flags.NeedGoods))
{
return false;
}
@@ -228,7 +228,7 @@ 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)");
schedule.Schedule(ResidentState.AtHome, default);
From 5417cec4a68682b959e0f4dc1089b6d6b01fbb4b Mon Sep 17 00:00:00 2001
From: dymanoid <9433345+dymanoid@users.noreply.github.com>
Date: Fri, 27 Jul 2018 23:13:08 +0200
Subject: [PATCH 08/14] Clean up code issues
---
src/RealTime/CustomAI/RealTimeBuildingAI.cs | 4 +---
src/RealTime/UI/CustomTimeBar.cs | 4 ++--
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/src/RealTime/CustomAI/RealTimeBuildingAI.cs b/src/RealTime/CustomAI/RealTimeBuildingAI.cs
index 40d6ded3..c2bf0c80 100644
--- a/src/RealTime/CustomAI/RealTimeBuildingAI.cs
+++ b/src/RealTime/CustomAI/RealTimeBuildingAI.cs
@@ -176,9 +176,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/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;
From 8c7da25e5d0b0d235bd43f84bb59b0334e998094 Mon Sep 17 00:00:00 2001
From: dymanoid <9433345+dymanoid@users.noreply.github.com>
Date: Sat, 28 Jul 2018 01:16:48 +0200
Subject: [PATCH 09/14] Enable just for fun shopping
---
src/RealTime/CustomAI/Constants.cs | 3 ++
.../CustomAI/RealTimeResidentAI.Visit.cs | 34 +++++++++++++------
src/RealTime/CustomAI/ScheduleHint.cs | 3 ++
src/RealTime/CustomAI/SpareTimeBehavior.cs | 6 ++--
src/RealTime/Simulation/CitizenProcessor.cs | 2 +-
5 files changed, 33 insertions(+), 15 deletions(-)
diff --git a/src/RealTime/CustomAI/Constants.cs b/src/RealTime/CustomAI/Constants.cs
index 6b4a76ff..e0800789 100644
--- a/src/RealTime/CustomAI/Constants.cs
+++ b/src/RealTime/CustomAI/Constants.cs
@@ -23,6 +23,9 @@ internal static class Constants
/// 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 = 50u;
+
/// A chance in percent for a tourist to find a hotel for sleepover.
public const uint FindHotelChance = 80;
diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs
index 4db2c4e7..7795eea2 100644
--- a/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs
+++ b/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs
@@ -78,8 +78,8 @@ private void DoScheduledRelaxing(ref CitizenSchedule schedule, TAI instance, uin
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);
@@ -105,9 +105,20 @@ private bool ProcessCitizenRelaxing(ref CitizenSchedule schedule, ref TCitizen c
private bool ScheduleShopping(ref CitizenSchedule schedule, ref TCitizen citizen, bool localOnly)
{
+ // 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.GetShoppingChance(CitizenProxy.GetAge(ref citizen))))
@@ -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,21 +150,26 @@ 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.GetShoppingChance(CitizenProxy.GetAge(ref citizen));
- ResidentState nextState = Random.ShouldOccur(moreShoppingChance)
- ? ResidentState.Unknown
- : ResidentState.Shopping;
+ 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));
}
}
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 80dd46a9..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)
@@ -253,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/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.
From aacca1a3a280894c4db4d2bccb99bdfff1ea5585 Mon Sep 17 00:00:00 2001
From: dymanoid <9433345+dymanoid@users.noreply.github.com>
Date: Sat, 28 Jul 2018 02:44:31 +0200
Subject: [PATCH 10/14] Fix issue caused the transfer offers for shopping to
hang up
- the root cause were buildings becoming inactive
- inactive buildings continued to push outgoing offers
- but they refused to accept customers, so stayed inactive forever
---
src/RealTime/CustomAI/RealTimeBuildingAI.cs | 10 ++++------
.../GameConnection/Patches/BuildingAIPatches.cs | 11 +++++++++--
2 files changed, 13 insertions(+), 8 deletions(-)
diff --git a/src/RealTime/CustomAI/RealTimeBuildingAI.cs b/src/RealTime/CustomAI/RealTimeBuildingAI.cs
index c2bf0c80..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.
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
From 01b4ecb39b6cd2012806aa56db32f130515e2649 Mon Sep 17 00:00:00 2001
From: dymanoid <9433345+dymanoid@users.noreply.github.com>
Date: Sat, 28 Jul 2018 03:19:53 +0200
Subject: [PATCH 11/14] Reduce the shopping for fun chance
---
src/RealTime/CustomAI/Constants.cs | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/RealTime/CustomAI/Constants.cs b/src/RealTime/CustomAI/Constants.cs
index e0800789..50e7395b 100644
--- a/src/RealTime/CustomAI/Constants.cs
+++ b/src/RealTime/CustomAI/Constants.cs
@@ -24,7 +24,7 @@ internal static class Constants
public const uint NightShoppingChance = 20u;
/// A chance in percent for a citizen to go shopping just for fun.
- public const uint FunShoppingChance = 50u;
+ public const uint FunShoppingChance = 35u;
/// A chance in percent for a tourist to find a hotel for sleepover.
public const uint FindHotelChance = 80;
From 52557cbb6478b7bfdd946aaa5b0690f6265d5ad5 Mon Sep 17 00:00:00 2001
From: dymanoid <9433345+dymanoid@users.noreply.github.com>
Date: Sat, 28 Jul 2018 03:21:25 +0200
Subject: [PATCH 12/14] Improve debug logging
---
.../CustomAI/RealTimeResidentAI.Common.cs | 12 +++++-----
.../CustomAI/RealTimeResidentAI.Home.cs | 6 ++---
.../CustomAI/RealTimeResidentAI.Visit.cs | 22 +++++++++----------
3 files changed, 20 insertions(+), 20 deletions(-)
diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.Common.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Common.cs
index b27d16c7..d41bd811 100644
--- a/src/RealTime/CustomAI/RealTimeResidentAI.Common.cs
+++ b/src/RealTime/CustomAI/RealTimeResidentAI.Common.cs
@@ -337,7 +337,7 @@ private void ExecuteCitizenSchedule(ref CitizenSchedule schedule, TAI instance,
{
if (ProcessCurrentState(ref schedule, ref citizen) && schedule.ScheduledState == ResidentState.Unknown)
{
- 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);
@@ -383,21 +383,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 907dc9be..39bc654d 100644
--- a/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs
+++ b/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs
@@ -34,7 +34,7 @@ private void DoScheduledHome(ref CitizenSchedule schedule, TAI instance, uint ci
}
}
- 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)
{
@@ -48,7 +48,7 @@ private bool RescheduleAtHome(ref CitizenSchedule schedule, ref TCitizen citizen
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;
}
@@ -63,7 +63,7 @@ private bool RescheduleAtHome(ref CitizenSchedule schedule, ref TCitizen citizen
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.Visit.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs
index 7795eea2..3b993ac0 100644
--- a/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs
+++ b/src/RealTime/CustomAI/RealTimeResidentAI.Visit.cs
@@ -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,7 +100,7 @@ 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)
@@ -175,7 +175,7 @@ private void DoScheduledShopping(ref CitizenSchedule schedule, TAI instance, uin
}
}
- 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)
@@ -184,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)
@@ -227,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)
{
@@ -242,14 +242,14 @@ private bool RescheduleVisit(ref CitizenSchedule schedule, ref TCitizen citizen,
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;
}
@@ -261,7 +261,7 @@ private bool RescheduleVisit(ref CitizenSchedule schedule, ref TCitizen 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)");
+ Log.Debug(TimeInfo.Now, $"{GetCitizenDesc(citizenId, ref citizen)} quits a visit because of time");
schedule.Schedule(ResidentState.AtHome, default);
return true;
}
From 828d5b5855ea47792cd586d9f07a15bc455bfcae Mon Sep 17 00:00:00 2001
From: dymanoid <9433345+dymanoid@users.noreply.github.com>
Date: Sat, 28 Jul 2018 03:22:02 +0200
Subject: [PATCH 13/14] Improve performance by avoiding unnecessary
re-scheduling
---
.../CustomAI/RealTimeResidentAI.Common.cs | 17 ++++++++++-------
.../CustomAI/RealTimeResidentAI.Home.cs | 2 +-
src/RealTime/CustomAI/RealTimeResidentAI.cs | 4 ++--
3 files changed, 13 insertions(+), 10 deletions(-)
diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.Common.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Common.cs
index d41bd811..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,13 +291,13 @@ 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)
@@ -331,11 +331,14 @@ 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 re-schedule now");
diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs b/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs
index 39bc654d..71eff2fb 100644
--- a/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs
+++ b/src/RealTime/CustomAI/RealTimeResidentAI.Home.cs
@@ -58,7 +58,7 @@ private bool RescheduleAtHome(ref CitizenSchedule schedule, uint citizenId, ref
? spareTimeBehavior.GetShoppingChance(age)
: spareTimeBehavior.GetRelaxingChance(age, schedule.WorkShift);
- if (Random.ShouldOccur(goOutChance))
+ if (goOutChance > 0)
{
return false;
}
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.
From 8d68ebdbb5840b0bb05d85d4bcafe5feb14cc0a1 Mon Sep 17 00:00:00 2001
From: dymanoid <9433345+dymanoid@users.noreply.github.com>
Date: Sat, 28 Jul 2018 03:26:07 +0200
Subject: [PATCH 14/14] Delete unused method
---
src/RealTime/GameConnection/CitizenConnection.cs | 10 ----------
1 file changed, 10 deletions(-)
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.
///