diff --git a/src/GameLogic/CastleSiege/CastleSiegeContext.cs b/src/GameLogic/CastleSiege/CastleSiegeContext.cs index 61ff735..d9d985d 100644 --- a/src/GameLogic/CastleSiege/CastleSiegeContext.cs +++ b/src/GameLogic/CastleSiege/CastleSiegeContext.cs @@ -25,6 +25,7 @@ public class CastleSiegeContext private bool _dirty; private string? _crownHoldGuild; private DateTime? _crownHoldStartUtc; + private bool _lastShieldDown; /// Initializes a new instance of the class. /// The cycle timing configuration. @@ -217,28 +218,31 @@ public class CastleSiegeContext /// How long the master must hold to capture. public CrownTickResult TickCrownHold(string? eligibleGuild, bool masterHolding, DateTime now, TimeSpan holdDuration) { + var shieldDown = this.Phase == CastleSiegePhase.Siege && eligibleGuild is not null; + var shieldChanged = shieldDown != this._lastShieldDown; + this._lastShieldDown = shieldDown; + if (this.Phase != CastleSiegePhase.Siege) { this._crownHoldGuild = null; this._crownHoldStartUtc = null; - return new CrownTickResult(false, CrownEvent.None, null); + return new CrownTickResult(false, shieldChanged, 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); + return new CrownTickResult(shieldDown, shieldChanged, 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); + return new CrownTickResult(shieldDown, shieldChanged, CrownEvent.HoldStarted, eligibleGuild); } if (now - this._crownHoldStartUtc.Value >= holdDuration) @@ -247,10 +251,10 @@ public class CastleSiegeContext this._crownHoldGuild = null; this._crownHoldStartUtc = null; this._dirty = true; - return new CrownTickResult(shieldDown, CrownEvent.Captured, eligibleGuild); + return new CrownTickResult(shieldDown, shieldChanged, CrownEvent.Captured, eligibleGuild); } - return new CrownTickResult(shieldDown, CrownEvent.None, eligibleGuild); + return new CrownTickResult(shieldDown, shieldChanged, CrownEvent.None, eligibleGuild); } /// @@ -363,6 +367,7 @@ public class CastleSiegeContext this._occupier = null; this._crownHoldGuild = null; this._crownHoldStartUtc = null; + this._lastShieldDown = false; } private ValueTask TransitionAsync(CastleSiegePhase phase, DateTime now) @@ -393,6 +398,7 @@ public enum CrownEvent /// The result of a crown-hold tick: the shield state and any event that occurred. /// Whether the crown shield is currently down (both switches held, defenses cleared). +/// Whether the shield state changed this tick (only then should the client be told). /// The event that occurred this tick. /// The guild the event refers to, if any. -public readonly record struct CrownTickResult(bool ShieldDown, CrownEvent Event, string? Guild); +public readonly record struct CrownTickResult(bool ShieldDown, bool ShieldChanged, CrownEvent Event, string? Guild); diff --git a/src/GameLogic/PlugIns/PeriodicTasks/CastleSiegeEventPlugIn.cs b/src/GameLogic/PlugIns/PeriodicTasks/CastleSiegeEventPlugIn.cs index 5aeafb4..9e15915 100644 --- a/src/GameLogic/PlugIns/PeriodicTasks/CastleSiegeEventPlugIn.cs +++ b/src/GameLogic/PlugIns/PeriodicTasks/CastleSiegeEventPlugIn.cs @@ -428,7 +428,12 @@ public sealed class CastleSiegeEventPlugIn : IPeriodicTaskPlugIn, ISupportCustom return; } - await player.InvokeViewPlugInAsync(p => p.SetCrownShieldAsync(crown.ShieldDown)).ConfigureAwait(false); + // Only tell the client when the shield state actually flips — the 0xB2/0x16 packet pops a modal + // message box, so sending it every tick would block player input. + if (crown.ShieldChanged) + { + await player.InvokeViewPlugInAsync(p => p.SetCrownShieldAsync(crown.ShieldDown)).ConfigureAwait(false); + } switch (crown.Event) {