diff --git a/src/RealTime/CustomAI/RealTimeBuildingAI.cs b/src/RealTime/CustomAI/RealTimeBuildingAI.cs
index 3fe1b5880b147c1a0338c1032261ace13a528cde..ae6e88713c28ae7258ee536d11359a1f92669bc5 100644
--- a/src/RealTime/CustomAI/RealTimeBuildingAI.cs
+++ b/src/RealTime/CustomAI/RealTimeBuildingAI.cs
@@ -319,6 +319,32 @@ namespace RealTime.CustomAI
return true;
}
+ ///
+ /// Determines whether the building with the specified ID is a shopping target.
+ ///
+ /// The building ID to check.
+ ///
+ /// true if the building is a shopping target; otherwise, false.
+ ///
+ public bool IsShoppingTarget(ushort buildingId)
+ {
+ if (buildingId == 0)
+ {
+ return true;
+ }
+
+ // A building still can post outgoing offers while inactive.
+ // This is to prevent those offers from being dispatched.
+ if (!buildingManager.BuildingHasFlags(buildingId, Building.Flags.Active))
+ {
+ return false;
+ }
+
+ var buildingService = buildingManager.GetBuildingService(buildingId);
+ return buildingService != ItemClass.Service.VarsitySports
+ && buildingManager.IsRealUniqueBuilding(buildingId);
+ }
+
/// Determines whether a building with specified ID is currently active.
/// The ID of the building to check.
///
diff --git a/src/RealTime/GameConnection/BuildingManagerConnection.cs b/src/RealTime/GameConnection/BuildingManagerConnection.cs
index b9cd34ac0240286cacfb0ef647df681c67c3ca16..491042c173f0e92c6c9222a0e616015d2921e95c 100644
--- a/src/RealTime/GameConnection/BuildingManagerConnection.cs
+++ b/src/RealTime/GameConnection/BuildingManagerConnection.cs
@@ -422,6 +422,32 @@ namespace RealTime.GameConnection
public bool IsAreaEvacuating(ushort buildingId)
=> buildingId != 0 && DisasterManager.instance.IsEvacuating(BuildingManager.instance.m_buildings.m_buffer[buildingId].m_position);
+ ///
+ /// Determines whether the building with specified ID is a real unique building (not a stadium, not a concert area).
+ ///
+ /// The building ID to check.
+ ///
+ /// true if the building with the specified ID is a real unique building; otherwise, false.
+ ///
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("General", "RCS1130", Justification = "The EventType enum has no [Flags] attribute but has values of power of 2")]
+ public bool IsRealUniqueBuilding(ushort buildingId)
+ {
+ if (buildingId == 0)
+ {
+ return false;
+ }
+
+ var buildingInfo = BuildingManager.instance.m_buildings.m_buffer[buildingId].Info;
+ if (buildingInfo?.m_class?.m_service != ItemClass.Service.Monument)
+ {
+ return false;
+ }
+
+ var monumentAI = buildingInfo.m_buildingAI as MonumentAI;
+ return monumentAI != null
+ && (monumentAI.m_supportEvents & (EventManager.EventType.Football | EventManager.EventType.Concert)) == 0;
+ }
+
private static bool BuildingCanBeVisited(ushort buildingId)
{
uint currentUnitId = BuildingManager.instance.m_buildings.m_buffer[buildingId].m_citizenUnits;
diff --git a/src/RealTime/GameConnection/IBuildingManagerConnection.cs b/src/RealTime/GameConnection/IBuildingManagerConnection.cs
index 81e73a665a9180769fa47d3631d8846febff6b2e..baa11dcac4f84a5a5f4697cfc779b9f9ef837e24 100644
--- a/src/RealTime/GameConnection/IBuildingManagerConnection.cs
+++ b/src/RealTime/GameConnection/IBuildingManagerConnection.cs
@@ -184,8 +184,17 @@ namespace RealTime.GameConnection
///
/// The building ID to check.
///
- /// true if the area around the building with specified ID is currently being evacuated.; otherwise, false.
+ /// true if the area around the building with specified ID is currently being evacuated; otherwise, false.
///
bool IsAreaEvacuating(ushort buildingId);
+
+ ///
+ /// Determines whether the building with specified ID is a real unique building (not a stadium, not a concert area).
+ ///
+ /// The building ID to check.
+ ///
+ /// true if the building with the specified ID is a real unique building; otherwise, false.
+ ///
+ bool IsRealUniqueBuilding(ushort buildingId);
}
}
\ No newline at end of file
diff --git a/src/RealTime/GameConnection/Patches/TransferManagerPatch.cs b/src/RealTime/GameConnection/Patches/TransferManagerPatch.cs
index 8e91d9c0eebcc3c9ddc4929029bc2535474ae9bb..4fb03a902a0d78e411c0c5cdf9c82d876bd272d4 100644
--- a/src/RealTime/GameConnection/Patches/TransferManagerPatch.cs
+++ b/src/RealTime/GameConnection/Patches/TransferManagerPatch.cs
@@ -46,6 +46,16 @@ namespace RealTime.GameConnection.Patches
case TransferManager.TransferReason.TouristD:
return RealTimeAI.IsEntertainmentTarget(offer.Building);
+ case TransferManager.TransferReason.Shopping:
+ case TransferManager.TransferReason.ShoppingB:
+ case TransferManager.TransferReason.ShoppingC:
+ case TransferManager.TransferReason.ShoppingD:
+ case TransferManager.TransferReason.ShoppingE:
+ case TransferManager.TransferReason.ShoppingF:
+ case TransferManager.TransferReason.ShoppingG:
+ case TransferManager.TransferReason.ShoppingH:
+ return RealTimeAI.IsShoppingTarget(offer.Building);
+
case TransferManager.TransferReason.ParkMaintenance:
return RealTimeAI.IsBuildingActive(offer.Building);