using System.Buffers.Binary; namespace AcDream.Core.Net.Messages; /// /// Outbound interaction GameActions. Each carries a single uint32 /// target guid (plus a second guid for UseWithTarget) inside the /// standard 0xF7B1 GameAction envelope. /// /// /// Wire layout (r08 §3 rows 0x0035/0x0036): /// /// 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) /// /// /// /// /// Server reply is GameEventType.UseDone (0x01C7) carrying a /// WeenieError; 0 = success. /// /// 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; /// /// Use an object: click a door, loot a corpse, talk to an NPC, /// activate a lifestone, step onto a portal. /// 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; } /// /// Use source item on target: e.g. key on locked door, key on /// chest, scroll on yourself, salvage tool on an item. /// 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; } /// /// 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). /// 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; } /// /// Pick up a ground item or move an item between containers. The /// server places the item in at /// the given slot (pass 0 to let the /// server choose). For F-key ground-pickup, pass the player's own /// server guid as . /// /// /// Wire layout (ACE GameActionPutItemInContainer.Handle): /// /// 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 /// /// /// 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; } }