namespace HSchool.Simulation; /// Tunables of the authoritative simulation. Bound from the Simulation config section. public sealed class SimulationOptions { public const string SectionName = "Simulation"; private DateTime _defaultStartDate = new(2012, 3, 31, 6, 0, 0, DateTimeKind.Utc); /// Fixed simulation steps per second. public int TickRate { get; set; } = 20; /// How many schools one player may own at the same time. public int MaxSchools { get; set; } = 2; /// How many school workers the process may run at once. public int MaxSchoolsTotal { get; set; } = 16; /// Base speed of the game clock: real seconds are multiplied by this many game minutes. public double GameMinutesPerRealSecond { get; set; } = 1d; /// Prefilled start of a new school; the client shows it in the creation form. public DateTime DefaultStartDate { get => _defaultStartDate; // Configuration binding yields Kind=Unspecified, which serializes without a "Z" and makes // the browser read the date in its own time zone. The game calendar is always UTC. set => _defaultStartDate = DateTime.SpecifyKind(value, DateTimeKind.Utc); } /// /// Directory for per-school save files. Relative paths are resolved against the content root. /// public string SavesDirectory { get; set; } = "saves"; /// /// Directory of mod packs (core and optional add-ons). Relative paths are resolved /// against the content root. /// public string ModsDirectory { get; set; } = "mods"; /// /// How often a running school writes its clock to disk. Create and shutdown write /// immediately; the tick itself never does. /// public int SaveIntervalSeconds { get; set; } = 30; /// /// Shortest gap between two saves caused by pause or speed. A client can send those as fast /// as the socket allows, and each one used to be a file write on the school's own thread. /// public int MinSaveIntervalMilliseconds { get; set; } = 1000; /// /// Monthly payroll the player may commit. Hire and subject assignment that would exceed it /// are rejected immediately; money itself does not move. /// public float MonthlyPayrollCap { get; set; } = 100_000f; /// /// How many people may change destination in one tick. Overflow waits for the next tick /// instead of being dropped — a queue, not a cutoff. /// public int MaxDecisionsPerTick { get; set; } = 64; /// How many scene text fragments a portrait may take. Silent defs do not count. public int PortraitSceneFragmentLimit { get; set; } = 6; /// How many scene LoRAs a portrait may add beyond model / preset / shot layers. public int PortraitSceneLoraLimit { get; set; } = 2; /// How many scene embeddings a portrait may add beyond model / preset / shot layers. public int PortraitSceneEmbeddingLimit { get; set; } = 2; /// /// Working days from Monday. Five is Mon–Fri; six adds Saturday; seven is every day. /// public int SchoolWeekDays { get; set; } = 5; /// Length of one fixed step. public double FixedDeltaTime => 1d / TickRate; public TimeSpan TickInterval => TimeSpan.FromSeconds(1d / TickRate); public TimeSpan SaveInterval => TimeSpan.FromSeconds(SaveIntervalSeconds); public TimeSpan MinSaveInterval => TimeSpan.FromMilliseconds(MinSaveIntervalMilliseconds); }