// // 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; using MUnique.OpenMU.DataModel.Configuration; /// /// AdaMu operational settings for the Castle Siege cycle. Rides on the plugin custom-configuration system, so /// it is editable in the AdminPanel and needs no dedicated database table. /// /// This is deliberately separate from , which is /// the upstream, database-backed configuration holding the NPC/zone/upgrade definitions and the crown hold /// time. Keeping AdaMu's operational knobs out of that entity means upstream schema changes apply cleanly and /// no hand-editing of the generated persistence code is needed. /// /// /// What lives where: /// /// Castle owner and guild registrations: database (CastleSiegeData, CastleSiegeGuildRegistration). /// NPC/zone/upgrade definitions and crown hold time: database (GameConfiguration.CastleSiegeConfiguration). /// Cycle durations, registration fee, designated server and the current state: here. /// /// /// A cycle runs Idle1 -> RegisterGuild -> Ready -> Start -> End -> EndCycle -> Idle1, and auto-starts when the /// current day/time matches + . /// The AdminPanel-friendly properties (OpenDays checkboxes, minute durations) are proxies over the runtime /// fields, which are hidden from the editor to keep the form clean. /// public class CastleSiegeSettings { /// /// 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 or the AdminPanel). /// 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 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 many seconds a player has to operate a Crown Switch before it counts for their guild. /// The player has to stay in the switch's area for that long, and keeps it until they leave. /// public int SwitchPushSeconds { get; set; } = 15; // --- Persisted cycle bookkeeping (hidden from the AdminPanel) --- // Only the CURRENT state and when it started ride on the plugin's custom-configuration JSON. The castle // owner and the guild registrations live in real database tables, so they are not duplicated here. /// Gets or sets the persisted current state, so the cycle resumes after a restart. [Browsable(false)] public CastleSiegeState PersistedState { get; set; } = CastleSiegeState.Idle1; /// Gets or sets when the persisted state started (UTC), or null if never persisted. [Browsable(false)] public DateTime? PersistedStateStartedUtc { get; set; } /// /// 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, }