feat(hs): disable wings in-event + periodic HUD-state packet/broadcast

- HeykelSavasiContext.IsItemAllowedToEquip disallows wings/capes during
  Playing only (so entry is never rejected for wearing wings); auto-unequips
  any equipped wing/cape into a free inventory slot in OnGameStartAsync.
- New S2C packet HeykelSavasiHudState (C1, code FB, length 11) carrying
  phase, team, counts, statue progress and remaining seconds, plus its view
  plugin/interface, broadcast once per second across registration/prep/
  battle and one-shot on join/statue-break/game-end.
This commit is contained in:
Acentech Dev
2026-07-21 03:16:29 +03:00
parent 5750ab6511
commit 7aae9dff0c
9 changed files with 697 additions and 0 deletions

View File

@@ -29122,3 +29122,136 @@ public readonly ref struct HeykelSavasiOpenTeamPanelRef
/// <returns>The packet as byte span.</returns>
public static implicit operator Span<byte>(HeykelSavasiOpenTeamPanelRef packet) => packet._data;
}
/// <summary>
/// Is sent by the server when: Periodically (about once per second) while the Heykel Savasi (Statue War) event is open for registration, in its pre-battle countdown, or being played.
/// Causes reaction on client side: The client updates its Heykel Savasi HUD panel (team counts, statue progress, and remaining time).
/// </summary>
public readonly ref struct HeykelSavasiHudStateRef
{
private readonly Span<byte> _data;
/// <summary>
/// Initializes a new instance of the <see cref="HeykelSavasiHudStateRef"/> struct.
/// </summary>
/// <param name="data">The underlying data.</param>
public HeykelSavasiHudStateRef(Span<byte> data)
: this(data, true)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="HeykelSavasiHudStateRef"/> struct.
/// </summary>
/// <param name="data">The underlying data.</param>
/// <param name="initialize">If set to <c>true</c>, the header data is automatically initialized and written to the underlying span.</param>
private HeykelSavasiHudStateRef(Span<byte> data, bool initialize)
{
this._data = data;
if (initialize)
{
var header = this.Header;
header.Type = HeaderType;
header.Code = Code;
header.Length = (byte)Math.Min(data.Length, Length);
}
}
/// <summary>
/// Gets the header type of this data packet.
/// </summary>
public static byte HeaderType => 0xC1;
/// <summary>
/// Gets the operation code of this data packet.
/// </summary>
public static byte Code => 0xFB;
/// <summary>
/// Gets the initial length of this data packet. When the size is dynamic, this value may be bigger than actually needed.
/// </summary>
public static int Length => 11;
/// <summary>
/// Gets the header of this packet.
/// </summary>
public C1HeaderRef Header => new (this._data);
/// <summary>
/// Gets or sets 0 = registration open, 1 = preparation countdown, 2 = battle, 3 = ended.
/// </summary>
public byte Phase
{
get => this._data[3];
set => this._data[3] = value;
}
/// <summary>
/// Gets or sets 0 = none, 1 = red, 2 = blue.
/// </summary>
public byte MyTeam
{
get => this._data[4];
set => this._data[4] = value;
}
/// <summary>
/// Gets or sets the red count.
/// </summary>
public byte RedCount
{
get => this._data[5];
set => this._data[5] = value;
}
/// <summary>
/// Gets or sets the blue count.
/// </summary>
public byte BlueCount
{
get => this._data[6];
set => this._data[6] = value;
}
/// <summary>
/// Gets or sets the number (0-7) of statues the red team has destroyed of the blue team's line.
/// </summary>
public byte RedProgress
{
get => this._data[7];
set => this._data[7] = value;
}
/// <summary>
/// Gets or sets the number (0-7) of statues the blue team has destroyed of the red team's line.
/// </summary>
public byte BlueProgress
{
get => this._data[8];
set => this._data[8] = value;
}
/// <summary>
/// Gets or sets the number of seconds left in the current phase.
/// </summary>
public ushort RemainingSeconds
{
get => ReadUInt16BigEndian(this._data[9..]);
set => WriteUInt16BigEndian(this._data[9..], value);
}
/// <summary>
/// Performs an implicit conversion from a Span of bytes to a <see cref="HeykelSavasiHudState"/>.
/// </summary>
/// <param name="packet">The packet as span.</param>
/// <returns>The packet as struct.</returns>
public static implicit operator HeykelSavasiHudStateRef(Span<byte> packet) => new (packet, false);
/// <summary>
/// Performs an implicit conversion from <see cref="HeykelSavasiHudState"/> to a Span of bytes.
/// </summary>
/// <param name="packet">The packet as struct.</param>
/// <returns>The packet as byte span.</returns>
public static implicit operator Span<byte>(HeykelSavasiHudStateRef packet) => packet._data;
}