diff --git a/src/GameLogic/CastleSiege/CastleSiegeConfiguration.cs b/src/GameLogic/CastleSiege/CastleSiegeConfiguration.cs index 092995c..4a54510 100644 --- a/src/GameLogic/CastleSiege/CastleSiegeConfiguration.cs +++ b/src/GameLogic/CastleSiege/CastleSiegeConfiguration.cs @@ -4,42 +4,82 @@ namespace MUnique.OpenMU.GameLogic.CastleSiege; +using System.ComponentModel; +using System.Linq; +using System.Text.Json.Serialization; + /// -/// Configuration for the Castle Siege cycle timings. -/// Rides on the plugin custom-configuration system (no dedicated database table in P1). +/// Configuration for the Castle Siege cycle. Rides on the plugin custom-configuration system (no dedicated +/// database table). A cycle runs: Ownership -> Registration -> Preparation -> Siege(war) -> Settlement, and +/// auto-starts when the current day/time matches + . +/// The AdminPanel-friendly properties (OpenDays checkboxes, minute/second durations) are proxies over the +/// runtime fields, which are hidden from the editor to keep the form clean. /// public class CastleSiegeConfiguration { - // --- Schedule & period lengths (editable in the AdminPanel) --- - // A cycle runs: Ownership -> Registration -> Preparation -> Siege(war) -> Settlement -> Ownership. - // It auto-starts when the current day/time matches RegistrationOpenDays + RegistrationOpenTimes. - /// - /// Gets or sets the days of week on which registration auto-opens (UTC). Empty = every day - /// (still requires a time in ); with no times set, auto-start is off. + /// Gets or sets the days of week on which registration auto-opens (UTC). None = every day (still needs a + /// time). Editable in the AdminPanel as check boxes; proxies the runtime . /// - public IList RegistrationOpenDays { get; set; } = new List(); + [JsonIgnore] + public CastleSiegeScheduleDays OpenDays + { + get + { + var days = CastleSiegeScheduleDays.None; + foreach (var day in this.RegistrationOpenDays) + { + days |= (CastleSiegeScheduleDays)(1 << (int)day); + } + + return days; + } + + set => this.RegistrationOpenDays = Enum.GetValues() + .Where(day => value.HasFlag((CastleSiegeScheduleDays)(1 << (int)day))) + .ToList(); + } /// /// Gets or sets the times of day (UTC) at which a new cycle opens registration. - /// Empty = no auto-start (admins start cycles manually via chat command). + /// Empty = no auto-start (admins start cycles manually via the chat command). /// public IList RegistrationOpenTimes { get; set; } = new List(); - /// Gets or sets how long the registration period lasts (guilds may register). - public TimeSpan RegistrationDuration { get; set; } = TimeSpan.FromMinutes(5); + /// Gets or sets how long the registration period lasts, in minutes (guilds may register). + [JsonIgnore] + public int RegistrationMinutes + { + get => (int)this.RegistrationDuration.TotalMinutes; + set => this.RegistrationDuration = TimeSpan.FromMinutes(Math.Max(1, value)); + } - /// Gets or sets how long the preparation period lasts (between registration close and the war). - public TimeSpan PreparationDuration { get; set; } = TimeSpan.FromMinutes(2); + /// Gets or sets how long the preparation period lasts, in minutes (between registration and war). + [JsonIgnore] + public int PreparationMinutes + { + get => (int)this.PreparationDuration.TotalMinutes; + set => this.PreparationDuration = TimeSpan.FromMinutes(Math.Max(0, value)); + } - /// Gets or sets how long the war (siege) period lasts. - public TimeSpan SiegeDuration { get; set; } = TimeSpan.FromMinutes(10); + /// Gets or sets how long the war (siege) period lasts, in minutes. + [JsonIgnore] + public int SiegeMinutes + { + get => (int)this.SiegeDuration.TotalMinutes; + set => this.SiegeDuration = TimeSpan.FromMinutes(Math.Max(1, value)); + } /// - /// Gets or sets how long the guild master must hold the Crown (with both switches held and all gates down) - /// to capture the throne. The client shows a 60-second countdown, so 60s matches the on-screen timer. + /// Gets or sets how long the guild master must hold the Crown to capture the throne, in seconds. + /// The client shows a 60-second countdown, so 60 matches the on-screen timer. /// - public TimeSpan CrownHoldDuration { get; set; } = TimeSpan.FromSeconds(60); + [JsonIgnore] + public int CrownHoldSeconds + { + get => (int)this.CrownHoldDuration.TotalSeconds; + set => this.CrownHoldDuration = TimeSpan.FromSeconds(Math.Max(1, value)); + } /// /// Gets or sets the registration fee (in zen) a guild master must pay to register the guild @@ -47,21 +87,46 @@ public class CastleSiegeConfiguration /// public int RegistrationFee { get; set; } = 100000; - // --- Persisted runtime state (P4 persistence) --- - // These ride on the plugin's custom-configuration JSON (already stored in PostgreSQL), so the castle - // owner and the current cycle survive server restarts without a dedicated database table/migration. - // Written by CastleSiegeEventPlugIn whenever the state changes; read back on startup to restore it. + // --- Runtime fields (hidden from the AdminPanel; proxied by the friendly properties above) --- + + /// Gets or sets the days of week registration auto-opens (UTC). Empty = every day. + [Browsable(false)] + public IList RegistrationOpenDays { get; set; } = new List(); + + /// Gets or sets how long the registration period lasts. + [Browsable(false)] + public TimeSpan RegistrationDuration { get; set; } = TimeSpan.FromMinutes(5); + + /// Gets or sets how long the preparation period lasts. + [Browsable(false)] + public TimeSpan PreparationDuration { get; set; } = TimeSpan.FromMinutes(2); + + /// Gets or sets how long the war (siege) period lasts. + [Browsable(false)] + public TimeSpan SiegeDuration { get; set; } = TimeSpan.FromMinutes(10); + + /// Gets or sets how long the guild master must hold the Crown to capture the throne. + [Browsable(false)] + public TimeSpan CrownHoldDuration { get; set; } = TimeSpan.FromSeconds(60); + + // --- Persisted runtime state (hidden from the AdminPanel) --- + // These ride on the plugin's custom-configuration JSON (stored in PostgreSQL), so the castle owner and the + // current cycle survive server restarts. Written by CastleSiegeEventPlugIn; restored on startup. /// Gets or sets the persisted castle owner guild name (null = unowned). + [Browsable(false)] public string? PersistedOwnerGuildName { get; set; } /// Gets or sets the persisted current phase, so the cycle resumes after a restart. + [Browsable(false)] public CastleSiegePhase PersistedPhase { get; set; } = CastleSiegePhase.Ownership; /// Gets or sets when the persisted phase started (UTC), or null if never persisted. + [Browsable(false)] public DateTime? PersistedPhaseStartedUtc { get; set; } /// Gets or sets the persisted registered guild names for the current cycle. + [Browsable(false)] public IList PersistedRegisteredGuilds { get; set; } = new List(); /// @@ -87,3 +152,35 @@ public class CastleSiegeConfiguration return this.RegistrationOpenTimes.Any(p => p.IsBetween(earlier, nowTime)); } } + +/// +/// The days of week on which the Castle Siege registration auto-opens. A [Flags] enum so the AdminPanel +/// renders it as a set of check boxes. +/// +[Flags] +public enum CastleSiegeScheduleDays +{ + /// No day (auto-start day-unrestricted; still needs a time). + None = 0, + + /// Sunday. + Sunday = 1 << 0, + + /// Monday. + Monday = 1 << 1, + + /// Tuesday. + Tuesday = 1 << 2, + + /// Wednesday. + Wednesday = 1 << 3, + + /// Thursday. + Thursday = 1 << 4, + + /// Friday. + Friday = 1 << 5, + + /// Saturday. + Saturday = 1 << 6, +}