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,
+}