From f889d23977fe88e5d45b3d7ac623fdeff5e2543c Mon Sep 17 00:00:00 2001 From: dymanoid <9433345+dymanoid@users.noreply.github.com> Date: Mon, 30 Jul 2018 21:39:03 +0200 Subject: [PATCH] Create interfaces for various classes to enable testability --- src/RealTime/CustomAI/IRealTimeBuildingAI.cs | 25 +++++++++ src/RealTime/CustomAI/ISpareTimeBehavior.cs | 33 ++++++++++++ src/RealTime/CustomAI/ITravelBehavior.cs | 18 +++++++ src/RealTime/CustomAI/IWorkBehavior.cs | 54 ++++++++++++++++++++ src/RealTime/CustomAI/RealTimeBuildingAI.cs | 10 ++-- src/RealTime/CustomAI/RealTimeHumanAIBase.cs | 6 +-- src/RealTime/CustomAI/RealTimeResidentAI.cs | 24 +++++---- src/RealTime/CustomAI/RealTimeTouristAI.cs | 6 +-- src/RealTime/CustomAI/SpareTimeBehavior.cs | 2 +- src/RealTime/CustomAI/TravelBehavior.cs | 5 +- src/RealTime/CustomAI/WorkBehavior.cs | 10 ++-- src/RealTime/Events/IRealTimeEventManager.cs | 36 +++++++++++++ src/RealTime/Events/RealTimeEventManager.cs | 2 +- src/RealTime/RealTime.csproj | 5 ++ 14 files changed, 206 insertions(+), 30 deletions(-) create mode 100644 src/RealTime/CustomAI/IRealTimeBuildingAI.cs create mode 100644 src/RealTime/CustomAI/ISpareTimeBehavior.cs create mode 100644 src/RealTime/CustomAI/ITravelBehavior.cs create mode 100644 src/RealTime/CustomAI/IWorkBehavior.cs create mode 100644 src/RealTime/Events/IRealTimeEventManager.cs diff --git a/src/RealTime/CustomAI/IRealTimeBuildingAI.cs b/src/RealTime/CustomAI/IRealTimeBuildingAI.cs new file mode 100644 index 0000000..335a7f4 --- /dev/null +++ b/src/RealTime/CustomAI/IRealTimeBuildingAI.cs @@ -0,0 +1,25 @@ +// +// Copyright (c) dymanoid. All rights reserved. +// + +namespace RealTime.CustomAI +{ + /// + /// An interface for the custom logic for the private buildings. + /// + internal interface IRealTimeBuildingAI + { + /// + /// Determines whether the building with the specified is noise restricted + /// (has NIMBY policy that is active on current time). + /// + /// The building ID to check. + /// The ID of a building where the citizen starts their journey. + /// Specify 0 if there is no journey in schedule. + /// + /// true if the building with the specified has NIMBY policy + /// that is active on current time; otherwise, false. + /// + bool IsNoiseRestricted(ushort buildingId, ushort currentBuildingId = 0); + } +} \ No newline at end of file diff --git a/src/RealTime/CustomAI/ISpareTimeBehavior.cs b/src/RealTime/CustomAI/ISpareTimeBehavior.cs new file mode 100644 index 0000000..eea4872 --- /dev/null +++ b/src/RealTime/CustomAI/ISpareTimeBehavior.cs @@ -0,0 +1,33 @@ +// +// Copyright (c) dymanoid. All rights reserved. +// + +namespace RealTime.CustomAI +{ + /// + /// An interface for custom logic for the spare time simulation. + /// + internal interface ISpareTimeBehavior + { + /// + /// 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 (default is ). + /// + /// A percentage value in range of 0..100 that describes the probability whether + /// a citizen with specified age would go relaxing on current time. + uint GetRelaxingChance(Citizen.AgeGroup citizenAge, WorkShift workShift = WorkShift.Unemployed); + + /// + /// Gets the probability whether a citizen with specified age would go shopping on current time. + /// + /// + /// The age of the citizen to check. + /// + /// A percentage value in range of 0..100 that describes the probability whether + /// a citizen with specified age would go shopping on current time. + uint GetShoppingChance(Citizen.AgeGroup citizenAge); + } +} \ No newline at end of file diff --git a/src/RealTime/CustomAI/ITravelBehavior.cs b/src/RealTime/CustomAI/ITravelBehavior.cs new file mode 100644 index 0000000..74f64e8 --- /dev/null +++ b/src/RealTime/CustomAI/ITravelBehavior.cs @@ -0,0 +1,18 @@ +// +// Copyright (c) dymanoid. All rights reserved. +// + +namespace RealTime.CustomAI +{ + /// + /// An interface for citizens traveling behavior. + /// + internal interface ITravelBehavior + { + /// Gets an estimated travel time (in hours) between two specified buildings. + /// The ID of the first building. + /// The ID of the second building. + /// An estimated travel time in hours. + float GetEstimatedTravelTime(ushort building1, ushort building2); + } +} \ No newline at end of file diff --git a/src/RealTime/CustomAI/IWorkBehavior.cs b/src/RealTime/CustomAI/IWorkBehavior.cs new file mode 100644 index 0000000..6150e5a --- /dev/null +++ b/src/RealTime/CustomAI/IWorkBehavior.cs @@ -0,0 +1,54 @@ +// +// Copyright (c) dymanoid. All rights reserved. +// + +namespace RealTime.CustomAI +{ + /// + /// An interface for the citizens work behavior. + /// + internal interface IWorkBehavior + { + /// Notifies this object that a new game day starts. + void BeginNewDay(); + + /// + /// Determines whether a building of specified and + /// currently has working hours. Note that this method always returns true for residential buildings. + /// + /// The building service. + /// The building sub-service. + /// + /// true if a building of specified and + /// currently has working hours; otherwise, false. + /// + bool IsBuildingWorking(ItemClass.Service service, ItemClass.SubService subService); + + /// Updates the citizen's work schedule by determining the time for going to work. + /// The citizen's schedule to update. + /// The ID of the building where the citizen is currently located. + /// The duration (in hours) of a full citizens simulation cycle. + /// true if work was scheduled; otherwise, false. + bool ScheduleGoToWork(ref CitizenSchedule schedule, ushort currentBuilding, float simulationCycle); + + /// Updates the citizen's work schedule by determining the lunch time. + /// The citizen's schedule to update. + /// The citizen's age. + /// true if a lunch time was scheduled; otherwise, false. + bool ScheduleLunch(ref CitizenSchedule schedule, Citizen.AgeGroup citizenAge); + + /// Updates the citizen's work schedule by determining the returning from lunch time. + /// The citizen's schedule to update. + void ScheduleReturnFromLunch(ref CitizenSchedule schedule); + + /// Updates the citizen's work schedule by determining the time for returning from work. + /// The citizen's schedule to update. + /// The age of the citizen. + void ScheduleReturnFromWork(ref CitizenSchedule schedule, Citizen.AgeGroup citizenAge); + + /// Updates the citizen's work shift parameters in the specified citizen's . + /// The citizen's schedule to update the work shift in. + /// The age of the citizen. + void UpdateWorkShift(ref CitizenSchedule schedule, Citizen.AgeGroup citizenAge); + } +} \ No newline at end of file diff --git a/src/RealTime/CustomAI/RealTimeBuildingAI.cs b/src/RealTime/CustomAI/RealTimeBuildingAI.cs index 295127c..490f3fb 100644 --- a/src/RealTime/CustomAI/RealTimeBuildingAI.cs +++ b/src/RealTime/CustomAI/RealTimeBuildingAI.cs @@ -13,7 +13,7 @@ namespace RealTime.CustomAI /// /// A class that incorporates the custom logic for the private buildings. /// - internal sealed class RealTimeBuildingAI + internal sealed class RealTimeBuildingAI : IRealTimeBuildingAI { private const int ConstructionSpeedPaused = 10880; private const int ConstructionSpeedMinimum = 1088; @@ -27,8 +27,8 @@ namespace RealTime.CustomAI private readonly ITimeInfo timeInfo; private readonly IBuildingManagerConnection buildingManager; private readonly IToolManagerConnection toolManager; - private readonly WorkBehavior workBehavior; - private readonly TravelBehavior travelBehavior; + private readonly IWorkBehavior workBehavior; + private readonly ITravelBehavior travelBehavior; private readonly bool[] lightStates; @@ -58,8 +58,8 @@ namespace RealTime.CustomAI ITimeInfo timeInfo, IBuildingManagerConnection buildingManager, IToolManagerConnection toolManager, - WorkBehavior workBehavior, - TravelBehavior travelBehavior) + IWorkBehavior workBehavior, + ITravelBehavior travelBehavior) { this.config = config ?? throw new ArgumentNullException(nameof(config)); this.timeInfo = timeInfo ?? throw new ArgumentNullException(nameof(timeInfo)); diff --git a/src/RealTime/CustomAI/RealTimeHumanAIBase.cs b/src/RealTime/CustomAI/RealTimeHumanAIBase.cs index d7e4d87..563e8ea 100644 --- a/src/RealTime/CustomAI/RealTimeHumanAIBase.cs +++ b/src/RealTime/CustomAI/RealTimeHumanAIBase.cs @@ -28,8 +28,8 @@ namespace RealTime.CustomAI /// /// A configuration to use with this custom logic. /// An object providing the proxies that connect the method calls to the game methods. - /// A reference an instance. - protected RealTimeHumanAIBase(RealTimeConfig config, GameConnections connections, RealTimeEventManager eventManager) + /// A reference to an instance. + protected RealTimeHumanAIBase(RealTimeConfig config, GameConnections connections, IRealTimeEventManager eventManager) { Config = config ?? throw new ArgumentNullException(nameof(config)); EventMgr = eventManager ?? throw new ArgumentNullException(nameof(eventManager)); @@ -68,7 +68,7 @@ namespace RealTime.CustomAI /// /// Gets a reference to the event manager. /// - protected RealTimeEventManager EventMgr { get; } + protected IRealTimeEventManager EventMgr { get; } /// /// Gets a reference to the proxy class that provides access to citizen's methods and fields. diff --git a/src/RealTime/CustomAI/RealTimeResidentAI.cs b/src/RealTime/CustomAI/RealTimeResidentAI.cs index 3f0ff03..f83167e 100644 --- a/src/RealTime/CustomAI/RealTimeResidentAI.cs +++ b/src/RealTime/CustomAI/RealTimeResidentAI.cs @@ -18,11 +18,13 @@ namespace RealTime.CustomAI where TCitizen : struct { private readonly ResidentAIConnection residentAI; - private readonly RealTimeBuildingAI buildingAI; - private readonly WorkBehavior workBehavior; - private readonly SpareTimeBehavior spareTimeBehavior; - private readonly TravelBehavior travelBehavior; + private readonly IRealTimeBuildingAI buildingAI; + private readonly IWorkBehavior workBehavior; + private readonly ISpareTimeBehavior spareTimeBehavior; + private readonly ITravelBehavior travelBehavior; + private readonly CitizenSchedule[] residentSchedules; + private float simulationCycle; /// Initializes a new instance of the class. @@ -30,7 +32,7 @@ namespace RealTime.CustomAI /// A instance containing the mod's configuration. /// A instance that provides the game connection implementation. /// A connection to the game's resident AI. - /// A instance. + /// An instance. /// The custom building AI. /// A behavior that provides simulation info for the citizens work time. /// A behavior that provides simulation info for the citizens spare time. @@ -39,11 +41,11 @@ namespace RealTime.CustomAI RealTimeConfig config, GameConnections connections, ResidentAIConnection residentAI, - RealTimeEventManager eventManager, - RealTimeBuildingAI buildingAI, - WorkBehavior workBehavior, - SpareTimeBehavior spareTimeBehavior, - TravelBehavior travelBehavior) + IRealTimeEventManager eventManager, + IRealTimeBuildingAI buildingAI, + IWorkBehavior workBehavior, + ISpareTimeBehavior spareTimeBehavior, + ITravelBehavior travelBehavior) : base(config, connections, eventManager) { this.residentAI = residentAI ?? throw new ArgumentNullException(nameof(residentAI)); @@ -135,7 +137,7 @@ namespace RealTime.CustomAI /// Performs simulation for starting a new day for all citizens. public void BeginNewDay() { - workBehavior.UpdateLunchTime(); + workBehavior.BeginNewDay(); todayWakeup = TimeInfo.Now.Date.AddHours(Config.WakeUpHour); } diff --git a/src/RealTime/CustomAI/RealTimeTouristAI.cs b/src/RealTime/CustomAI/RealTimeTouristAI.cs index a6e55af..fe3bfbc 100644 --- a/src/RealTime/CustomAI/RealTimeTouristAI.cs +++ b/src/RealTime/CustomAI/RealTimeTouristAI.cs @@ -22,7 +22,7 @@ namespace RealTime.CustomAI where TCitizen : struct { private readonly TouristAIConnection touristAI; - private readonly SpareTimeBehavior spareTimeBehavior; + private readonly ISpareTimeBehavior spareTimeBehavior; /// /// Initializes a new instance of the class. @@ -39,8 +39,8 @@ namespace RealTime.CustomAI RealTimeConfig config, GameConnections connections, TouristAIConnection touristAI, - RealTimeEventManager eventManager, - SpareTimeBehavior spareTimeBehavior) + IRealTimeEventManager eventManager, + ISpareTimeBehavior spareTimeBehavior) : base(config, connections, eventManager) { this.touristAI = touristAI ?? throw new ArgumentNullException(nameof(touristAI)); diff --git a/src/RealTime/CustomAI/SpareTimeBehavior.cs b/src/RealTime/CustomAI/SpareTimeBehavior.cs index 5367f12..af9b3eb 100644 --- a/src/RealTime/CustomAI/SpareTimeBehavior.cs +++ b/src/RealTime/CustomAI/SpareTimeBehavior.cs @@ -13,7 +13,7 @@ namespace RealTime.CustomAI /// /// A class that provides custom logic for the spare time simulation. /// - internal sealed class SpareTimeBehavior + internal sealed class SpareTimeBehavior : ISpareTimeBehavior { private readonly RealTimeConfig config; private readonly ITimeInfo timeInfo; diff --git a/src/RealTime/CustomAI/TravelBehavior.cs b/src/RealTime/CustomAI/TravelBehavior.cs index 4ca776c..779f6e2 100644 --- a/src/RealTime/CustomAI/TravelBehavior.cs +++ b/src/RealTime/CustomAI/TravelBehavior.cs @@ -8,7 +8,10 @@ namespace RealTime.CustomAI using RealTime.Tools; using static Constants; - internal sealed class TravelBehavior + /// + /// A behavior for citizens traveling. + /// + internal sealed class TravelBehavior : ITravelBehavior { private readonly IBuildingManagerConnection buildingManager; diff --git a/src/RealTime/CustomAI/WorkBehavior.cs b/src/RealTime/CustomAI/WorkBehavior.cs index 5779607..ee28e3b 100644 --- a/src/RealTime/CustomAI/WorkBehavior.cs +++ b/src/RealTime/CustomAI/WorkBehavior.cs @@ -14,13 +14,13 @@ namespace RealTime.CustomAI /// /// A class containing methods for managing the citizens' work behavior. /// - internal sealed class WorkBehavior + internal sealed class WorkBehavior : IWorkBehavior { private readonly RealTimeConfig config; private readonly IRandomizer randomizer; private readonly IBuildingManagerConnection buildingManager; private readonly ITimeInfo timeInfo; - private readonly TravelBehavior travelBehavior; + private readonly ITravelBehavior travelBehavior; private DateTime lunchBegin; private DateTime lunchEnd; @@ -37,7 +37,7 @@ namespace RealTime.CustomAI IRandomizer randomizer, IBuildingManagerConnection buildingManager, ITimeInfo timeInfo, - TravelBehavior travelBehavior) + ITravelBehavior travelBehavior) { this.config = config ?? throw new ArgumentNullException(nameof(config)); this.randomizer = randomizer ?? throw new ArgumentNullException(nameof(randomizer)); @@ -90,8 +90,8 @@ namespace RealTime.CustomAI HasExtendedFirstWorkShift(service, subService)); } - /// Updates the lunch time according to current date and configuration. - public void UpdateLunchTime() + /// Notifies this object that a new game day starts. + public void BeginNewDay() { DateTime today = timeInfo.Now.Date; lunchBegin = today.AddHours(config.LunchBegin); diff --git a/src/RealTime/Events/IRealTimeEventManager.cs b/src/RealTime/Events/IRealTimeEventManager.cs new file mode 100644 index 0000000..21506a8 --- /dev/null +++ b/src/RealTime/Events/IRealTimeEventManager.cs @@ -0,0 +1,36 @@ +// +// Copyright (c) dymanoid. All rights reserved. +// + +namespace RealTime.Events +{ + using System; + + /// An interface for the customized city events manager. + internal interface IRealTimeEventManager + { + /// + /// Gets the instance of an ongoing or upcoming city event that takes place in a building + /// with specified ID. + /// + /// The ID of a building to search events for. + /// An instance of the first matching city event, or null if none found. + ICityEvent GetCityEvent(ushort buildingId); + + /// + /// Gets the instance of an upcoming city event whose start time is between the specified values. + /// + /// The earliest city event start time to consider. + /// The latest city event start time to consider. + /// An instance of the first matching city event, or null if none found. + ICityEvent GetUpcomingCityEvent(DateTime earliestStartTime, DateTime latestStartTime); + + /// Gets the state of a city event in the specified building. + /// The building ID to check events in. + /// The latest start time of events to consider. + /// + /// The state of an event that meets the specified criteria, or if none found. + /// + CityEventState GetEventState(ushort buildingId, DateTime latestStart); + } +} \ No newline at end of file diff --git a/src/RealTime/Events/RealTimeEventManager.cs b/src/RealTime/Events/RealTimeEventManager.cs index 525c828..ea80085 100644 --- a/src/RealTime/Events/RealTimeEventManager.cs +++ b/src/RealTime/Events/RealTimeEventManager.cs @@ -16,7 +16,7 @@ namespace RealTime.Events /// The central class for the custom city events logic. /// - internal sealed class RealTimeEventManager : IStorageData + internal sealed class RealTimeEventManager : IStorageData, IRealTimeEventManager { private const int MaximumEventsCount = 5; private const string StorageDataId = "RealTimeEvents"; diff --git a/src/RealTime/RealTime.csproj b/src/RealTime/RealTime.csproj index 27346c6..cb96a92 100644 --- a/src/RealTime/RealTime.csproj +++ b/src/RealTime/RealTime.csproj @@ -60,6 +60,10 @@ + + + + @@ -77,6 +81,7 @@ + -- GitLab