feat(ui): port retail selected-item mana

Parse and route the complete 0x0264 guid/fraction/valid response, send exact 0x0263 item-mana queries, and reproduce retail meter visibility plus guid-zero cancellation for mana and health selection changes. Keep the behavior in Core state and the retained selected-object controller with wire/state/UI conformance coverage.

Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-11 11:16:21 +02:00
parent df460f94c3
commit fa1da02783
21 changed files with 402 additions and 38 deletions

View file

@ -450,15 +450,20 @@ public static class GameEvents
return BinaryPrimitives.ReadUInt32LittleEndian(payload);
}
/// <summary>0x0264 QueryItemManaResponse: (itemGuid, manaPercent).</summary>
public readonly record struct QueryItemManaResponse(uint ItemGuid, float ManaPercent);
/// <summary>
/// 0x0264 QueryItemManaResponse: (itemGuid, manaPercent, valid).
/// Retail anchor: <c>CM_Item::DispatchUI_QueryItemManaResponse @ 0x006A84D0</c>
/// reads the trailing 32-bit validity flag at message offset 0x0C.
/// </summary>
public readonly record struct QueryItemManaResponse(uint ItemGuid, float ManaPercent, bool Valid);
public static QueryItemManaResponse? ParseQueryItemManaResponse(ReadOnlySpan<byte> payload)
{
if (payload.Length < 8) return null;
if (payload.Length < 12) return null;
return new QueryItemManaResponse(
BinaryPrimitives.ReadUInt32LittleEndian(payload),
BinaryPrimitives.ReadSingleLittleEndian(payload.Slice(4)));
BinaryPrimitives.ReadSingleLittleEndian(payload.Slice(4)),
BinaryPrimitives.ReadUInt32LittleEndian(payload.Slice(8)) != 0);
}
/// <summary>0x0274 CharacterConfirmationRequest — server-driven modal confirm.</summary>

View file

@ -29,6 +29,7 @@ public static class SocialActions
// Queries
public const uint QueryHealthOpcode = 0x01BFu; // u32 targetGuid
public const uint QueryItemManaOpcode = 0x0263u; // u32 itemGuid; zero cancels
public const uint PingRequestOpcode = 0x01E9u; // u32 clientId
// Fellowship
@ -56,6 +57,20 @@ public static class SocialActions
return body;
}
/// <summary>
/// Query an owned item's mana fraction, or cancel the active query with item guid zero.
/// Retail anchor: <c>CM_Item::Event_QueryItemMana @ 0x006A8610</c>.
/// </summary>
public static byte[] BuildQueryItemMana(uint seq, uint itemGuid)
{
byte[] body = new byte[16];
BinaryPrimitives.WriteUInt32LittleEndian(body, GameActionEnvelope);
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), seq);
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), QueryItemManaOpcode);
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(12), itemGuid);
return body;
}
/// <summary>Keepalive ping — server echoes with PingResponse (0x01EA).</summary>
public static byte[] BuildPingRequest(uint seq, uint clientId)
{