TDD: failing test first (CS0117 on BuildPickUp + PutItemInContainerOpcode), then implementation. Wire layout matches ACE GameActionPutItemInContainer: 0xF7B1 envelope + seq + 0x0019 opcode + itemGuid + containerGuid + placement (24 bytes). For F-key ground-pickup, caller passes player's server guid as containerGuid; Task 2 (GameWindow wiring) will handle that dispatch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
109 lines
4.5 KiB
C#
109 lines
4.5 KiB
C#
using System.Buffers.Binary;
|
|
|
|
namespace AcDream.Core.Net.Messages;
|
|
|
|
/// <summary>
|
|
/// Outbound interaction GameActions. Each carries a single uint32
|
|
/// target guid (plus a second guid for <c>UseWithTarget</c>) inside the
|
|
/// standard <c>0xF7B1</c> GameAction envelope.
|
|
///
|
|
/// <para>
|
|
/// Wire layout (r08 §3 rows 0x0035/0x0036):
|
|
/// <code>
|
|
/// u32 0xF7B1
|
|
/// u32 gameActionSequence
|
|
/// u32 subOpcode
|
|
/// u32 targetGuid // e.g. door, NPC, lifestone, corpse, item
|
|
/// u32 sourceGuid // only for UseWithTarget (the item you're using)
|
|
/// </code>
|
|
/// </para>
|
|
///
|
|
/// <para>
|
|
/// Server reply is <c>GameEventType.UseDone</c> (0x01C7) carrying a
|
|
/// <c>WeenieError</c>; 0 = success.
|
|
/// </para>
|
|
/// </summary>
|
|
public static class InteractRequests
|
|
{
|
|
public const uint GameActionEnvelope = 0xF7B1u;
|
|
public const uint UseOpcode = 0x0036u;
|
|
public const uint UseWithTargetOpcode = 0x0035u;
|
|
public const uint TeleToLifestoneOpcode = 0x0063u;
|
|
public const uint PutItemInContainerOpcode = 0x0019u;
|
|
|
|
/// <summary>
|
|
/// Use an object: click a door, loot a corpse, talk to an NPC,
|
|
/// activate a lifestone, step onto a portal.
|
|
/// </summary>
|
|
public static byte[] BuildUse(uint gameActionSequence, uint targetGuid)
|
|
{
|
|
byte[] body = new byte[16];
|
|
BinaryPrimitives.WriteUInt32LittleEndian(body, GameActionEnvelope);
|
|
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), gameActionSequence);
|
|
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), UseOpcode);
|
|
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(12), targetGuid);
|
|
return body;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Use source item on target: e.g. key on locked door, key on
|
|
/// chest, scroll on yourself, salvage tool on an item.
|
|
/// </summary>
|
|
public static byte[] BuildUseWithTarget(
|
|
uint gameActionSequence, uint sourceGuid, uint targetGuid)
|
|
{
|
|
byte[] body = new byte[20];
|
|
BinaryPrimitives.WriteUInt32LittleEndian(body, GameActionEnvelope);
|
|
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), gameActionSequence);
|
|
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), UseWithTargetOpcode);
|
|
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(12), sourceGuid);
|
|
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(16), targetGuid);
|
|
return body;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Teleport to your lifestone. No target guid — just tells the
|
|
/// server "recall me." Fails if you haven't tied to a lifestone
|
|
/// (server responds with a WeenieError).
|
|
/// </summary>
|
|
public static byte[] BuildTeleToLifestone(uint gameActionSequence)
|
|
{
|
|
byte[] body = new byte[12];
|
|
BinaryPrimitives.WriteUInt32LittleEndian(body, GameActionEnvelope);
|
|
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), gameActionSequence);
|
|
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), TeleToLifestoneOpcode);
|
|
return body;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Pick up a ground item or move an item between containers. The
|
|
/// server places the item in <paramref name="containerGuid"/> at
|
|
/// the given <paramref name="placement"/> slot (pass 0 to let the
|
|
/// server choose). For F-key ground-pickup, pass the player's own
|
|
/// server guid as <paramref name="containerGuid"/>.
|
|
///
|
|
/// <para>
|
|
/// Wire layout (ACE GameActionPutItemInContainer.Handle):
|
|
/// <code>
|
|
/// u32 0xF7B1
|
|
/// u32 gameActionSequence
|
|
/// u32 0x0019 // PutItemInContainer
|
|
/// u32 itemGuid // server guid of the item
|
|
/// u32 containerGuid // destination container (player or bag)
|
|
/// i32 placement // 0 = server picks slot
|
|
/// </code>
|
|
/// </para>
|
|
/// </summary>
|
|
public static byte[] BuildPickUp(
|
|
uint gameActionSequence, uint itemGuid, uint containerGuid, int placement)
|
|
{
|
|
byte[] body = new byte[24];
|
|
BinaryPrimitives.WriteUInt32LittleEndian(body, GameActionEnvelope);
|
|
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), gameActionSequence);
|
|
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), PutItemInContainerOpcode);
|
|
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(12), itemGuid);
|
|
BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(16), containerGuid);
|
|
BinaryPrimitives.WriteInt32LittleEndian (body.AsSpan(20), placement);
|
|
return body;
|
|
}
|
|
}
|