diff --git a/src/DataModel/Configuration/CastleSiegeConfiguration.cs b/src/DataModel/Configuration/CastleSiegeConfiguration.cs new file mode 100644 index 000000000..595d67355 --- /dev/null +++ b/src/DataModel/Configuration/CastleSiegeConfiguration.cs @@ -0,0 +1,152 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.DataModel.Configuration; + +using MUnique.OpenMU.Annotations; +using MUnique.OpenMU.DataModel.Configuration.Items; + +/// +/// Main configuration for the castle siege event. +/// +[Cloneable] +public partial class CastleSiegeConfiguration +{ + /// + /// Gets or sets a value indicating whether the castle siege feature is enabled. + /// + public bool Enabled { get; set; } + + /// + /// Gets or sets the number of seconds a guild must hold the crown to capture the castle. + /// + public int CrownHoldTimeSeconds { get; set; } = 30; + + /// + /// Gets or sets the minimum combined level of a guild master required to register for the siege. + /// + public int RegisterMinLevel { get; set; } = 200; + + /// + /// Gets or sets the minimum number of guild members required to register for the siege. + /// + public int RegisterMinMembers { get; set; } = 20; + + /// + /// Gets or sets the minimum number of seconds a participant must be present in the battle to be eligible for a reward. + /// + public int ParticipantRewardMinSeconds { get; set; } + + /// + /// Gets or sets the maximum number of attacking alliance slots. + /// + public int MaxAttackingGuilds { get; set; } = 3; + + /// + /// Gets or sets the guild score awarded to the guild that wins the siege. + /// + public int GuildScoreCastleSiege { get; set; } + + /// + /// Gets or sets the guild score awarded to alliance member guilds of the winning side. + /// + public int GuildScoreCastleSiegeMembers { get; set; } + + /// + /// Gets or sets the Zen cost for the castle owner to re-purchase a destroyed gate. + /// + public int GateBuyPrice { get; set; } + + /// + /// Gets or sets the Zen cost for the castle owner to re-purchase a destroyed statue. + /// + public int StatueBuyPrice { get; set; } + + /// + /// Gets or sets the map definition for the Valley of Loren (map 30), where the siege takes place. + /// + public virtual GameMapDefinition? CastleSiegeMapDefinition { get; set; } + + /// + /// Gets or sets the map definition for the Land of Trials (map 31), the castle-owner's exclusive zone. + /// + public virtual GameMapDefinition? LandOfTrialsMapDefinition { get; set; } + + /// + /// Gets or sets the item definition for the participation reward item. + /// + public virtual ItemDefinition? RewardItemDefinition { get; set; } + + /// + /// Gets or sets the schedule entries that define when each siege state begins. + /// + [MemberOfAggregate] + public virtual ICollection StateSchedule { get; protected set; } = null!; + + /// + /// Gets or sets the definitions for all castle siege NPCs (gates, statues, etc.). + /// + [MemberOfAggregate] + public virtual ICollection NpcDefinitions { get; protected set; } = null!; + + /// + /// Gets or sets the upgrade levels for gate defense. + /// + [MemberOfAggregate] + public virtual ICollection GateDefenseUpgrades { get; protected set; } = null!; + + /// + /// Gets or sets the upgrade levels for gate maximum HP. + /// + [MemberOfAggregate] + public virtual ICollection GateLifeUpgrades { get; protected set; } = null!; + + /// + /// Gets or sets the upgrade levels for statue defense. + /// + [MemberOfAggregate] + public virtual ICollection StatueDefenseUpgrades { get; protected set; } = null!; + + /// + /// Gets or sets the upgrade levels for statue maximum HP. + /// + [MemberOfAggregate] + public virtual ICollection StatueLifeUpgrades { get; protected set; } = null!; + + /// + /// Gets or sets the upgrade levels for statue HP regeneration. + /// + [MemberOfAggregate] + public virtual ICollection StatueRegenUpgrades { get; protected set; } = null!; + + /// + /// Gets or sets the zones on the siege map where attacking siege machines may be placed. + /// + [MemberOfAggregate] + public virtual ICollection AttackMachineZones { get; protected set; } = null!; + + /// + /// Gets or sets the zones on the siege map where defensive siege machines may be placed. + /// + [MemberOfAggregate] + public virtual ICollection DefenseMachineZones { get; protected set; } = null!; + + /// + /// Gets or sets the zone where defending players respawn during the siege. + /// + [MemberOfAggregate] + public virtual CastleSiegeZoneDefinition? DefenseRespawnArea { get; set; } + + /// + /// Gets or sets the zone where attacking players respawn during the siege. + /// + [MemberOfAggregate] + public virtual CastleSiegeZoneDefinition? AttackRespawnArea { get; set; } + + /// + public override string ToString() + { + return "Castle Siege Configuration"; + } +} diff --git a/src/DataModel/Configuration/CastleSiegeJoinSide.cs b/src/DataModel/Configuration/CastleSiegeJoinSide.cs new file mode 100644 index 000000000..bae4d83a9 --- /dev/null +++ b/src/DataModel/Configuration/CastleSiegeJoinSide.cs @@ -0,0 +1,36 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.DataModel.Configuration; + +/// +/// Defines the side (defending or attacking) a guild or NPC belongs to in the castle siege. +/// +public enum CastleSiegeJoinSide : byte +{ + /// + /// No side assigned. + /// + None = 0, + + /// + /// The defending guild side. + /// + Defense = 1, + + /// + /// The first attacking alliance slot. + /// + Attack1 = 2, + + /// + /// The second attacking alliance slot. + /// + Attack2 = 3, + + /// + /// The third attacking alliance slot. + /// + Attack3 = 4, +} diff --git a/src/DataModel/Configuration/CastleSiegeNpcDefinition.cs b/src/DataModel/Configuration/CastleSiegeNpcDefinition.cs new file mode 100644 index 000000000..b3d3fc000 --- /dev/null +++ b/src/DataModel/Configuration/CastleSiegeNpcDefinition.cs @@ -0,0 +1,55 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.DataModel.Configuration; + +using MUnique.OpenMU.Annotations; + +/// +/// Defines a castle siege NPC instance, including its spawn location, side, and persistence settings. +/// +[Cloneable] +public partial class CastleSiegeNpcDefinition +{ + /// + /// Gets or sets the monster definition template for this NPC. + /// + public virtual MonsterDefinition? MonsterDefinition { get; set; } + + /// + /// Gets or sets the unique instance identifier within its NPC type. + /// + public byte InstanceId { get; set; } + + /// + /// Gets or sets a value indicating whether this NPC's state is persisted to the database between sieges. + /// + public bool IsPersistedToDatabase { get; set; } + + /// + /// Gets or sets the default join side this NPC belongs to. + /// + public CastleSiegeJoinSide DefaultSide { get; set; } + + /// + /// Gets or sets the X coordinate of the NPC's spawn position. + /// + public short SpawnX { get; set; } + + /// + /// Gets or sets the Y coordinate of the NPC's spawn position. + /// + public short SpawnY { get; set; } + + /// + /// Gets or sets the facing direction of the NPC at spawn. + /// + public Direction Direction { get; set; } + + /// + public override string ToString() + { + return $"{this.MonsterDefinition} #{this.InstanceId} at ({this.SpawnX},{this.SpawnY})"; + } +} diff --git a/src/DataModel/Configuration/CastleSiegeState.cs b/src/DataModel/Configuration/CastleSiegeState.cs new file mode 100644 index 000000000..82b5a2426 --- /dev/null +++ b/src/DataModel/Configuration/CastleSiegeState.cs @@ -0,0 +1,61 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.DataModel.Configuration; + +/// +/// The state of the castle siege event cycle. +/// +public enum CastleSiegeState : byte +{ + /// + /// Idle state before guild registration opens. + /// + Idle1 = 0, + + /// + /// Guilds may register for the siege. + /// + RegisterGuild = 1, + + /// + /// Idle state after guild registration. + /// + Idle2 = 2, + + /// + /// Guilds may register emblems (Marks of Lord) to determine the attacking guilds. + /// + RegisterMark = 3, + + /// + /// Idle state after mark registration. + /// + Idle3 = 4, + + /// + /// Players are notified that the siege is about to start. + /// + Notify = 5, + + /// + /// The siege map is prepared and entry is allowed. + /// + Ready = 6, + + /// + /// The siege battle is in progress. + /// + Start = 7, + + /// + /// The siege battle has ended and results are being processed. + /// + End = 8, + + /// + /// The full siege cycle has completed. + /// + EndCycle = 9, +} diff --git a/src/DataModel/Configuration/CastleSiegeStateScheduleEntry.cs b/src/DataModel/Configuration/CastleSiegeStateScheduleEntry.cs new file mode 100644 index 000000000..3065f40c8 --- /dev/null +++ b/src/DataModel/Configuration/CastleSiegeStateScheduleEntry.cs @@ -0,0 +1,40 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.DataModel.Configuration; + +using MUnique.OpenMU.Annotations; + +/// +/// Defines a scheduled transition to a specific at a given day and time. +/// +[Cloneable] +public partial class CastleSiegeStateScheduleEntry +{ + /// + /// Gets or sets the siege state that becomes active at the scheduled time. + /// + public CastleSiegeState State { get; set; } + + /// + /// Gets or sets the day of the week on which this state transition occurs. + /// + public DayOfWeek DayOfWeek { get; set; } + + /// + /// Gets or sets the hour (0–23) at which this state transition occurs. + /// + public byte Hour { get; set; } + + /// + /// Gets or sets the minute (0–59) at which this state transition occurs. + /// + public byte Minute { get; set; } + + /// + public override string ToString() + { + return $"{this.State} on {this.DayOfWeek} at {this.Hour:D2}:{this.Minute:D2}"; + } +} diff --git a/src/DataModel/Configuration/CastleSiegeUpgradeDefinition.cs b/src/DataModel/Configuration/CastleSiegeUpgradeDefinition.cs new file mode 100644 index 000000000..dd372c1fa --- /dev/null +++ b/src/DataModel/Configuration/CastleSiegeUpgradeDefinition.cs @@ -0,0 +1,40 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.DataModel.Configuration; + +using MUnique.OpenMU.Annotations; + +/// +/// Defines one level of an upgrade that the castle owner can apply to a gate or statue NPC. +/// +[Cloneable] +public partial class CastleSiegeUpgradeDefinition +{ + /// + /// Gets or sets the upgrade level (0–3), where 0 represents the base/unupgraded state. + /// + public byte Level { get; set; } + + /// + /// Gets or sets the number of Jewels of Guardian required to perform this upgrade. + /// + public int RequiredJewelOfGuardianCount { get; set; } + + /// + /// Gets or sets the amount of Zen required to perform this upgrade. + /// + public int RequiredZen { get; set; } + + /// + /// Gets or sets the resulting stat value granted by this upgrade level (defense or max HP). + /// + public int Value { get; set; } + + /// + public override string ToString() + { + return $"Level {this.Level}: Value={this.Value}, Jewels={this.RequiredJewelOfGuardianCount}, Zen={this.RequiredZen}"; + } +} diff --git a/src/DataModel/Configuration/CastleSiegeUpgradeType.cs b/src/DataModel/Configuration/CastleSiegeUpgradeType.cs new file mode 100644 index 000000000..e5a6bed3f --- /dev/null +++ b/src/DataModel/Configuration/CastleSiegeUpgradeType.cs @@ -0,0 +1,26 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.DataModel.Configuration; + +/// +/// The type of upgrade applied to a castle siege NPC (gate or statue). +/// +public enum CastleSiegeUpgradeType : byte +{ + /// + /// Increases the defense stat of the NPC. + /// + Defense = 1, + + /// + /// Increases the HP regeneration rate of the NPC. + /// + Regen = 2, + + /// + /// Increases the maximum HP of the NPC. + /// + Life = 3, +} diff --git a/src/DataModel/Configuration/CastleSiegeZoneDefinition.cs b/src/DataModel/Configuration/CastleSiegeZoneDefinition.cs new file mode 100644 index 000000000..602042bcc --- /dev/null +++ b/src/DataModel/Configuration/CastleSiegeZoneDefinition.cs @@ -0,0 +1,40 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.DataModel.Configuration; + +using MUnique.OpenMU.Annotations; + +/// +/// Defines a rectangular zone on the castle siege map, used for spawn areas and machine zones. +/// +[Cloneable] +public partial class CastleSiegeZoneDefinition +{ + /// + /// Gets or sets the top-left X coordinate of the zone. + /// + public byte X1 { get; set; } + + /// + /// Gets or sets the top-left Y coordinate of the zone. + /// + public byte Y1 { get; set; } + + /// + /// Gets or sets the bottom-right X coordinate of the zone. + /// + public byte X2 { get; set; } + + /// + /// Gets or sets the bottom-right Y coordinate of the zone. + /// + public byte Y2 { get; set; } + + /// + public override string ToString() + { + return $"{this.X1} / {this.Y1} to {this.X2} / {this.Y2}"; + } +} diff --git a/src/DataModel/Configuration/GameConfiguration.cs b/src/DataModel/Configuration/GameConfiguration.cs index 564e1813f..9f6941a16 100644 --- a/src/DataModel/Configuration/GameConfiguration.cs +++ b/src/DataModel/Configuration/GameConfiguration.cs @@ -300,6 +300,12 @@ public partial class GameConfiguration [MemberOfAggregate] public virtual ICollection MiniGameDefinitions { get; protected set; } = null!; + /// + /// Gets or sets the castle siege configuration. + /// + [MemberOfAggregate] + public virtual CastleSiegeConfiguration? CastleSiegeConfiguration { get; set; } + /// public override string ToString() { diff --git a/src/DataModel/Configuration/MonsterDefinition.cs b/src/DataModel/Configuration/MonsterDefinition.cs index fca37dd56..54ade48fd 100644 --- a/src/DataModel/Configuration/MonsterDefinition.cs +++ b/src/DataModel/Configuration/MonsterDefinition.cs @@ -165,6 +165,16 @@ public enum NpcWindow /// The dialog for the legacy quest system. /// LegacyQuest, + + /// + /// The castle siege gate NPC interaction window. + /// + CastleSiegeGateNpc, + + /// + /// The castle siege lever NPC interaction window. + /// + CastleSiegeLeverNpc, } /// diff --git a/src/DataModel/Entities/CastleSiegeData.cs b/src/DataModel/Entities/CastleSiegeData.cs new file mode 100644 index 000000000..5067fcd98 --- /dev/null +++ b/src/DataModel/Entities/CastleSiegeData.cs @@ -0,0 +1,67 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.DataModel.Entities; + +/// +/// Persistent state of the castle siege, stored as a single row across siege cycles. +/// +[AggregateRoot] +public class CastleSiegeData +{ + /// + /// Gets or sets the unique identifier of this record. + /// + public Guid Id { get; set; } + + /// + /// Gets or sets the persistent identifier of the guild that currently owns the castle. + /// when no guild owns the castle. + /// + public Guid? OwnerGuildId { get; set; } + + /// + /// Gets or sets a value indicating whether any guild currently occupies the castle. + /// + public bool IsOccupied { get; set; } + + /// + /// Gets or sets the Chaos Machine tax rate applied by the castle owner (0–3). + /// + public byte TaxChaos { get; set; } + + /// + /// Gets or sets the personal store tax rate applied by the castle owner (0–3). + /// + public byte TaxStore { get; set; } + + /// + /// Gets or sets the entry fee (in Zen) for the castle owner's hunt zone (0–300000). + /// + public int TaxHunt { get; set; } + + /// + /// Gets or sets a value indicating whether the hunt zone (Land of Trials) is currently open to the public. + /// + public bool IsHuntZoneEnabled { get; set; } + + /// + /// Gets or sets the accumulated tribute money collected from the hunt zone and taxes. + /// + public long TributeMoney { get; set; } + + /// + /// Gets or sets the persisted states of all castle NPCs. + /// + [MemberOfAggregate] + public virtual ICollection NpcStates { get; protected set; } = null!; + + /// + public override string ToString() + { + return this.IsOccupied + ? $"Castle owned by guild {this.OwnerGuildId}" + : "Castle unoccupied"; + } +} diff --git a/src/DataModel/Entities/CastleSiegeGuildRegistration.cs b/src/DataModel/Entities/CastleSiegeGuildRegistration.cs new file mode 100644 index 000000000..39119ae81 --- /dev/null +++ b/src/DataModel/Entities/CastleSiegeGuildRegistration.cs @@ -0,0 +1,44 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.DataModel.Entities; + +/// +/// Stores a guild's registration data for the current castle siege cycle, +/// including the number of emblems submitted to determine attacking guilds. +/// +[AggregateRoot] +public class CastleSiegeGuildRegistration +{ + /// + /// Gets or sets the unique identifier of this registration record. + /// + public Guid Id { get; set; } + + /// + /// Gets or sets the persistent identifier of the registered guild. + /// + public Guid GuildId { get; set; } + + /// + /// Gets or sets the guild name, denormalized for convenience to avoid extra lookups during siege processing. + /// + public string GuildName { get; set; } = string.Empty; + + /// + /// Gets or sets the number of Emblems of Lord registered by this guild. + /// + public int Marks { get; set; } + + /// + /// Gets or sets the insertion order of this registration, used for tie-breaking when guilds have equal marks. + /// + public int RegistrationOrder { get; set; } + + /// + public override string ToString() + { + return $"{this.GuildName} (Marks={this.Marks}, Order={this.RegistrationOrder})"; + } +} diff --git a/src/DataModel/Entities/CastleSiegeNpcState.cs b/src/DataModel/Entities/CastleSiegeNpcState.cs new file mode 100644 index 000000000..509a0d8b7 --- /dev/null +++ b/src/DataModel/Entities/CastleSiegeNpcState.cs @@ -0,0 +1,52 @@ +// +// Licensed under the MIT License. See LICENSE file in the project root for full license information. +// + +namespace MUnique.OpenMU.DataModel.Entities; + +/// +/// Persistent state of a single castle siege NPC between siege cycles. +/// +public class CastleSiegeNpcState +{ + /// + /// Gets or sets the unique identifier of this NPC state. + /// + public Guid Id { get; set; } + + /// + /// Gets or sets the monster definition number that identifies the NPC template. + /// + public short MonsterNumber { get; set; } + + /// + /// Gets or sets the instance identifier matching . + /// + public byte InstanceId { get; set; } + + /// + /// Gets or sets the current defense upgrade level (0–3). + /// + public byte DefenseLevel { get; set; } + + /// + /// Gets or sets the current HP regeneration upgrade level (0–3). + /// + public byte RegenLevel { get; set; } + + /// + /// Gets or sets the current maximum HP upgrade level (0–3). + /// + public byte LifeLevel { get; set; } + + /// + /// Gets or sets the current HP of the NPC. A value of 0 means the NPC is destroyed. + /// + public int CurrentHp { get; set; } + + /// + public override string ToString() + { + return $"NPC {this.MonsterNumber} #{this.InstanceId} (HP={this.CurrentHp})"; + } +}