feat(CS): authentic Crown-hold throne capture (S6 protocol) replacing Sinior-talk
Some checks failed
.NET Core / build (push) Has been cancelled
Some checks failed
.NET Core / build (push) Has been cancelled
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>
This commit is contained in:
@@ -63,6 +63,10 @@ public sealed class CastleSiegeEventPlugIn : IPeriodicTaskPlugIn, ISupportCustom
|
||||
(218, 184, 195),
|
||||
};
|
||||
|
||||
// The Crown (NPC 216) position on Valley of Loren — the guild master holds it here to capture the throne.
|
||||
private static readonly Point CrownPosition = new(176, 212);
|
||||
private const int CrownHoldRange = 4;
|
||||
|
||||
private static readonly ConcurrentDictionary<IGameContext, CastleSiegeContext> Contexts = new();
|
||||
|
||||
private string? _cachedFlagOwner;
|
||||
@@ -350,9 +354,35 @@ public sealed class CastleSiegeEventPlugIn : IPeriodicTaskPlugIn, ISupportCustom
|
||||
context.SetSwitchHolder(switchNumber, holder);
|
||||
}
|
||||
|
||||
var now = DateTime.UtcNow;
|
||||
|
||||
// Crown-hold capture: when a guild holds both switches with every gate down, the crown shield drops;
|
||||
// that guild's master then holds the crown for CrownHoldDuration to take the throne (contestable).
|
||||
var eligible = context.GetShieldEligibleGuild();
|
||||
var masterOnCrown = false;
|
||||
if (eligible is not null)
|
||||
{
|
||||
foreach (var player in map.GetAttackablesInRange(CrownPosition, CrownHoldRange).OfType<Player>())
|
||||
{
|
||||
if (player.GuildStatus?.Position == GuildPosition.GuildMaster
|
||||
&& await GetGuildNameAsync(player).ConfigureAwait(false) == eligible)
|
||||
{
|
||||
masterOnCrown = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var holdDuration = context.Configuration.CrownHoldDuration;
|
||||
var crown = context.TickCrownHold(eligible, masterOnCrown, now, holdDuration);
|
||||
await BroadcastCrownAsync(gameContext, crown).ConfigureAwait(false);
|
||||
if (crown.Event == CrownEvent.Captured && crown.Guild is { } capturedGuild)
|
||||
{
|
||||
await AnnounceAsync(gameContext, $"Guild '{capturedGuild}' has taken the Crown and now holds the throne!").ConfigureAwait(false);
|
||||
}
|
||||
|
||||
// Keep the client's on-map countdown armed and in sync. Resend every 10s so players who just
|
||||
// loaded the battle map pick it up, without visibly resetting the second-counter too often.
|
||||
var now = DateTime.UtcNow;
|
||||
if ((int)(now - context.PhaseStartedUtc).TotalSeconds % 10 == 0)
|
||||
{
|
||||
var remaining = context.GetRemainingSiegeTime(now);
|
||||
@@ -386,6 +416,36 @@ public sealed class CastleSiegeEventPlugIn : IPeriodicTaskPlugIn, ISupportCustom
|
||||
}
|
||||
});
|
||||
|
||||
/// <summary>
|
||||
/// Sends the crown shield state (each tick) and any crown-hold event (start/reset/capture) to every player
|
||||
/// currently on the battle map.
|
||||
/// </summary>
|
||||
private static ValueTask BroadcastCrownAsync(IGameContext gameContext, CrownTickResult crown)
|
||||
=> gameContext.ForEachPlayerAsync(async player =>
|
||||
{
|
||||
if (player.CurrentMap?.Definition.Number != ValleyOfLorenMapNumber)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await player.InvokeViewPlugInAsync<ICastleSiegeStatusViewPlugIn>(p => p.SetCrownShieldAsync(crown.ShieldDown)).ConfigureAwait(false);
|
||||
|
||||
switch (crown.Event)
|
||||
{
|
||||
case CrownEvent.HoldStarted:
|
||||
await player.InvokeViewPlugInAsync<ICastleSiegeStatusViewPlugIn>(p => p.SetCrownRegistAsync(0, 0)).ConfigureAwait(false);
|
||||
break;
|
||||
case CrownEvent.HoldReset:
|
||||
await player.InvokeViewPlugInAsync<ICastleSiegeStatusViewPlugIn>(p => p.SetCrownRegistAsync(2, 0)).ConfigureAwait(false);
|
||||
break;
|
||||
case CrownEvent.Captured when crown.Guild is { } guild:
|
||||
await player.InvokeViewPlugInAsync<ICastleSiegeStatusViewPlugIn>(p => p.AnnounceSealCapturedAsync(guild)).ConfigureAwait(false);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
private async ValueTask BroadcastCastleFlagAsync(IGameContext gameContext, CastleSiegeContext context)
|
||||
{
|
||||
try
|
||||
|
||||
Reference in New Issue
Block a user