Files
AdamuSw/src/GameLogic/CastleSiege/CastleSiegeThroneCaptureTalkPlugIn.cs
Acentech Dev 3dd4881406
Some checks failed
.NET Core / build (push) Has been cancelled
feat(CS): authentic Crown-hold throne capture (S6 protocol) replacing Sinior-talk
Break all gates + hold both switches (defenses down) -> the Crown shield drops
(C1 B2 16=0). The guild master then stands on the Crown (176,212) and holds for
CrownHoldDuration (default 60s, client shows a 60s countdown via C1 B2 15) to
capture; capture broadcasts C1 B2 18 + golden text and sets the occupier. Losing a
switch or leaving the crown resets the hold (contestable until the siege timer ends).
Sinior (223) is now informational guidance. +2 tests (14 total).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 15:04:10 +03:00

101 lines
3.9 KiB
C#

// <copyright file="CastleSiegeThroneCaptureTalkPlugIn.cs" company="MUnique">
// Licensed under the MIT License. See LICENSE file in the project root for full license information.
// </copyright>
namespace MUnique.OpenMU.GameLogic.CastleSiege;
using System.Linq;
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;
/// <summary>
/// Handles registering on the throne at the Sinior (223) / Crown (216) NPCs on Valley of Loren.
/// A registered guild takes the throne only when it has destroyed all castle defenses (gates + statues)
/// AND is holding both Crown Switches (a member standing on each). The guild on the throne when the siege
/// ends becomes the castle owner. Capturing broadcasts a server-wide golden message.
/// </summary>
[Guid("CA5710A0-7A1B-4C2D-8E3F-000000000223")]
[PlugIn]
[Display(Name = "Castle Siege Throne (Sinior/Crown)", Description = "Registers a guild on the throne (Sinior 223 / Crown 216) once defenses are down and both switches held.")]
public class CastleSiegeThroneCaptureTalkPlugIn : IPlayerTalkToNpcPlugIn
{
private static readonly short[] ThroneNpcNumbers = { 223, 216 };
/// <inheritdoc />
public async ValueTask PlayerTalksToNpcAsync(Player player, NonPlayerCharacter npc, NpcTalkEventArgs eventArgs)
{
if (!ThroneNpcNumbers.Contains(npc.Definition.Number))
{
return;
}
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 (player.GuildStatus is not { } guildStatus)
{
await ShowAsync(player, "Only members of a registered guild can take the throne.").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;
}
}
if (!context.RegisteredGuilds.Contains(guildName))
{
await ShowAsync(player, "Your guild is not registered for this Castle Siege.").ConfigureAwait(false);
return;
}
// The throne is taken by holding the Crown, not by talking here — give guidance based on the state.
await ShowAsync(player, DescribeThroneStep(context, guildName)).ConfigureAwait(false);
}
private static string DescribeThroneStep(CastleSiegeContext context, string guildName)
{
if (context.Phase != CastleSiegePhase.Siege)
{
return "The siege is not running yet.";
}
if (context.DefensesRemaining > 0)
{
return $"Destroy all castle gates first ({context.DefensesRemaining} remaining), then hold both Crown Switches.";
}
var eligible = context.GetShieldEligibleGuild();
if (eligible is null)
{
return "All gates are down! Hold BOTH Crown Switches with your guild — the Crown's shield will drop.";
}
if (eligible == guildName)
{
return "Your guild holds both switches and the shield is down — send your GUILD MASTER to hold the Crown to take the throne!";
}
return $"Guild '{eligible}' is holding both switches. Take a switch back to raise their shield.";
}
private static ValueTask ShowAsync(Player player, string text)
=> player.InvokeViewPlugInAsync<IShowMessagePlugIn>(p => p.ShowMessageAsync(text, MessageType.BlueNormal));
}