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:
@@ -23,6 +23,8 @@ public class CastleSiegeContext
|
||||
private string? _occupier;
|
||||
private int _defensesRemaining;
|
||||
private bool _dirty;
|
||||
private string? _crownHoldGuild;
|
||||
private DateTime? _crownHoldStartUtc;
|
||||
|
||||
/// <summary>Initializes a new instance of the <see cref="CastleSiegeContext"/> class.</summary>
|
||||
/// <param name="configuration">The cycle timing configuration.</param>
|
||||
@@ -188,6 +190,69 @@ public class CastleSiegeContext
|
||||
return remaining > TimeSpan.Zero ? remaining : TimeSpan.Zero;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the guild that currently holds BOTH crown switches while all castle defenses are down (so the
|
||||
/// crown's shield is dropped for them), or null. Only meaningful during the siege.
|
||||
/// </summary>
|
||||
public string? GetShieldEligibleGuild()
|
||||
{
|
||||
if (this.Phase != CastleSiegePhase.Siege || this._defensesRemaining > 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var holder = this._switchHolders[217];
|
||||
return holder is not null && holder == this._switchHolders[218] ? holder : null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Advances the crown-hold capture. <paramref name="eligibleGuild"/> is the guild with both switches held
|
||||
/// and no defenses left (shield down); <paramref name="masterHolding"/> is whether that guild's master is
|
||||
/// standing on the crown. Captures the throne for the guild once it has held for <paramref name="holdDuration"/>.
|
||||
/// Losing a switch or the master leaving the crown resets the hold (contestable until the siege ends).
|
||||
/// </summary>
|
||||
/// <param name="eligibleGuild">The guild with both switches and no defenses, or null.</param>
|
||||
/// <param name="masterHolding">Whether that guild's master is on the crown.</param>
|
||||
/// <param name="now">The current UTC time.</param>
|
||||
/// <param name="holdDuration">How long the master must hold to capture.</param>
|
||||
public CrownTickResult TickCrownHold(string? eligibleGuild, bool masterHolding, DateTime now, TimeSpan holdDuration)
|
||||
{
|
||||
if (this.Phase != CastleSiegePhase.Siege)
|
||||
{
|
||||
this._crownHoldGuild = null;
|
||||
this._crownHoldStartUtc = null;
|
||||
return new CrownTickResult(false, CrownEvent.None, null);
|
||||
}
|
||||
|
||||
var shieldDown = eligibleGuild is not null;
|
||||
var wasHolding = this._crownHoldGuild is not null;
|
||||
|
||||
if (eligibleGuild is null || !masterHolding)
|
||||
{
|
||||
this._crownHoldGuild = null;
|
||||
this._crownHoldStartUtc = null;
|
||||
return new CrownTickResult(shieldDown, wasHolding ? CrownEvent.HoldReset : CrownEvent.None, null);
|
||||
}
|
||||
|
||||
if (this._crownHoldGuild != eligibleGuild || this._crownHoldStartUtc is null)
|
||||
{
|
||||
this._crownHoldGuild = eligibleGuild;
|
||||
this._crownHoldStartUtc = now;
|
||||
return new CrownTickResult(shieldDown, CrownEvent.HoldStarted, eligibleGuild);
|
||||
}
|
||||
|
||||
if (now - this._crownHoldStartUtc.Value >= holdDuration)
|
||||
{
|
||||
this._occupier = eligibleGuild;
|
||||
this._crownHoldGuild = null;
|
||||
this._crownHoldStartUtc = null;
|
||||
this._dirty = true;
|
||||
return new CrownTickResult(shieldDown, CrownEvent.Captured, eligibleGuild);
|
||||
}
|
||||
|
||||
return new CrownTickResult(shieldDown, CrownEvent.None, eligibleGuild);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the persistable state changed since the last call, resetting the flag.
|
||||
/// Called each tick by the plugin to decide whether to write state to the database.
|
||||
@@ -296,6 +361,8 @@ public class CastleSiegeContext
|
||||
this._switchHolders[218] = null;
|
||||
this._defensesRemaining = 0;
|
||||
this._occupier = null;
|
||||
this._crownHoldGuild = null;
|
||||
this._crownHoldStartUtc = null;
|
||||
}
|
||||
|
||||
private ValueTask TransitionAsync(CastleSiegePhase phase, DateTime now)
|
||||
@@ -307,3 +374,25 @@ public class CastleSiegeContext
|
||||
return ValueTask.CompletedTask;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>The event produced by a single <see cref="CastleSiegeContext.TickCrownHold"/> call.</summary>
|
||||
public enum CrownEvent
|
||||
{
|
||||
/// <summary>Nothing changed this tick.</summary>
|
||||
None,
|
||||
|
||||
/// <summary>A guild master just started holding the crown (start the client countdown).</summary>
|
||||
HoldStarted,
|
||||
|
||||
/// <summary>The crown was held long enough — the guild captured the throne.</summary>
|
||||
Captured,
|
||||
|
||||
/// <summary>An in-progress hold was interrupted (switch lost or master left the crown).</summary>
|
||||
HoldReset,
|
||||
}
|
||||
|
||||
/// <summary>The result of a crown-hold tick: the shield state and any event that occurred.</summary>
|
||||
/// <param name="ShieldDown">Whether the crown shield is currently down (both switches held, defenses cleared).</param>
|
||||
/// <param name="Event">The event that occurred this tick.</param>
|
||||
/// <param name="Guild">The guild the event refers to, if any.</param>
|
||||
public readonly record struct CrownTickResult(bool ShieldDown, CrownEvent Event, string? Guild);
|
||||
|
||||
Reference in New Issue
Block a user