diff --git a/src/GameLogic/CastleSiege/CastleSiegeGuardsmanTalkPlugIn.cs b/src/GameLogic/CastleSiege/CastleSiegeGuardsmanTalkPlugIn.cs
new file mode 100644
index 0000000..6a00304
--- /dev/null
+++ b/src/GameLogic/CastleSiege/CastleSiegeGuardsmanTalkPlugIn.cs
@@ -0,0 +1,74 @@
+//
+// Licensed under the MIT License. See LICENSE file in the project root for full license information.
+//
+
+namespace MUnique.OpenMU.GameLogic.CastleSiege;
+
+using System.Runtime.InteropServices;
+using MUnique.OpenMU.GameLogic.NPC;
+using MUnique.OpenMU.GameLogic.PlugIns;
+using MUnique.OpenMU.GameLogic.PlugIns.PeriodicTasks;
+using MUnique.OpenMU.GameLogic.Views;
+using MUnique.OpenMU.Interfaces;
+using MUnique.OpenMU.PlugIns;
+
+///
+/// Handles talking to the Castle Siege 'Guardsman' NPC (number 224): registers the talking
+/// player's guild for the current siege while the state machine is in the registration phase.
+/// P2 first increment — guild marks, entrance fee and minimum-guild requirements come later.
+///
+[Guid("CA5710A0-7A1B-4C2D-8E3F-000000000224")]
+[PlugIn]
+[Display(Name = "Castle Siege Guardsman", Description = "Registers a guild for the Castle Siege when talking to the Guardsman (NPC 224).")]
+public class CastleSiegeGuardsmanTalkPlugIn : IPlayerTalkToNpcPlugIn
+{
+ /// Gets the NPC number of the Castle Siege 'Guardsman'.
+ public static short GuardsmanNpcNumber => 224;
+
+ ///
+ public async ValueTask PlayerTalksToNpcAsync(Player player, NonPlayerCharacter npc, NpcTalkEventArgs eventArgs)
+ {
+ if (npc.Definition.Number != GuardsmanNpcNumber)
+ {
+ return;
+ }
+
+ // We handle the dialog ourselves (no client-side window), so suppress the default "not implemented" message.
+ eventArgs.HasBeenHandled = true;
+
+ var context = CastleSiegeEventPlugIn.TryGetContext(player.GameContext);
+ if (context is null)
+ {
+ await ShowAsync(player, "Castle Siege is not active on this server.").ConfigureAwait(false);
+ return;
+ }
+
+ if (context.Phase != CastleSiegePhase.Registration)
+ {
+ await ShowAsync(player, "Castle Siege registration is not open right now.").ConfigureAwait(false);
+ return;
+ }
+
+ if (player.GuildStatus is not { } guildStatus || guildStatus.Position != GuildPosition.GuildMaster)
+ {
+ await ShowAsync(player, "Only a guild master can register for the Castle Siege.").ConfigureAwait(false);
+ return;
+ }
+
+ var guildName = guildStatus.GuildId.ToString();
+ if (player.GameContext is IGameServerContext serverContext)
+ {
+ var guild = await serverContext.GuildServer.GetGuildAsync(guildStatus.GuildId).ConfigureAwait(false);
+ if (guild?.Name is { Length: > 0 } name)
+ {
+ guildName = name;
+ }
+ }
+
+ context.RegisterGuild(guildName);
+ await ShowAsync(player, $"Your guild '{guildName}' is registered for the Castle Siege.").ConfigureAwait(false);
+ }
+
+ private static ValueTask ShowAsync(Player player, string text)
+ => player.InvokeViewPlugInAsync(p => p.ShowMessageAsync(text, MessageType.BlueNormal));
+}