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