// // Licensed under the MIT License. See LICENSE file in the project root for full license information. // namespace MUnique.OpenMU.GameLogic.CastleSiege; using System.ComponentModel; using System.Linq; using System.Text.Json.Serialization; /// /// 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 { /// /// 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 . /// [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 the chat command). /// public IList RegistrationOpenTimes { get; set; } = new List(); /// 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, 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, 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 to capture the throne, in seconds. /// The client shows a 60-second countdown, so 60 matches the on-screen timer. /// [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 /// for the siege. 0 disables the fee. /// public int RegistrationFee { get; set; } = 100000; /// /// Gets or sets the game server (SW) id on which the Castle Siege runs. With multiple game servers each /// has its own map instances, so the siege must happen on ONE designated server; the others skip it (they /// only honor the shared castle owner for the rewards). Players must be on this server to take part. /// public byte CastleSiegeServerId { get; set; } // --- 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(); /// /// Returns true if (UTC) matches a scheduled registration-open day and falls /// within a 5-second window of a configured open time. When is empty, /// the day is not restricted (every day). When no times are configured, auto-start is disabled. /// /// The current UTC time. public bool IsRegistrationOpenTime(DateTime now) { if (this.RegistrationOpenTimes.Count == 0) { return false; } if (this.RegistrationOpenDays.Count > 0 && !this.RegistrationOpenDays.Contains(now.DayOfWeek)) { return false; } var nowTime = TimeOnly.FromDateTime(now); var earlier = nowTime.Add(TimeSpan.FromSeconds(-5)); 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, }