using System.Buffers.Binary; namespace AcDream.Core.Net.Messages; /// /// Outbound inventory-manipulation GameActions: stack merge/split, give, /// drop, shortcuts. All ride in the 0xF7B1 GameAction envelope. /// /// /// References: r08 ยง3 rows 0x0054-0x0056 / 0x019B-0x019D / 0x00CD. /// /// public static class InventoryActions { public const uint GameActionEnvelope = 0xF7B1u; public const uint StackableMergeOpcode = 0x0054u; public const uint StackableSplitToContainerOpcode = 0x0055u; public const uint StackableSplitTo3DOpcode = 0x0056u; public const uint StackableSplitToWieldOpcode = 0x019Bu; public const uint GiveObjectRequestOpcode = 0x00CDu; public const uint AddShortcutOpcode = 0x019Cu; public const uint RemoveShortcutOpcode = 0x019Du; public const uint TeleToPoiOpcode = 0x00B1u; /// /// Merge stack A into stack B of the same item type. Server validates /// compatibility; if the merge fails it rolls back via /// InventoryServerSaveFailed (0x00A0). /// public static byte[] BuildStackableMerge(uint seq, uint mergeFromGuid, uint mergeToGuid, uint amount) { byte[] body = new byte[24]; BinaryPrimitives.WriteUInt32LittleEndian(body, GameActionEnvelope); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), seq); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), StackableMergeOpcode); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(12), mergeFromGuid); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(16), mergeToGuid); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(20), amount); return body; } /// Split N items off a stack into a container at placement. public static byte[] BuildStackableSplitToContainer( uint seq, uint stackGuid, uint containerGuid, uint placement, uint amount) { byte[] body = new byte[28]; BinaryPrimitives.WriteUInt32LittleEndian(body, GameActionEnvelope); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), seq); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), StackableSplitToContainerOpcode); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(12), stackGuid); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(16), containerGuid); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(20), placement); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(24), amount); return body; } /// Split N items off a stack and drop them on the ground. public static byte[] BuildStackableSplitTo3D(uint seq, uint stackGuid, uint amount) { byte[] body = new byte[20]; BinaryPrimitives.WriteUInt32LittleEndian(body, GameActionEnvelope); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), seq); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), StackableSplitTo3DOpcode); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(12), stackGuid); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(16), amount); return body; } /// Split N items off a stack into an equip slot. public static byte[] BuildStackableSplitToWield( uint seq, uint stackGuid, uint equipLocation, uint amount) { byte[] body = new byte[24]; BinaryPrimitives.WriteUInt32LittleEndian(body, GameActionEnvelope); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), seq); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), StackableSplitToWieldOpcode); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(12), stackGuid); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(16), equipLocation); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(20), amount); return body; } /// Give an item (or a stack of N) to a target creature/NPC. public static byte[] BuildGiveObjectRequest( uint seq, uint targetGuid, uint itemGuid, uint amount) { byte[] body = new byte[24]; BinaryPrimitives.WriteUInt32LittleEndian(body, GameActionEnvelope); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), seq); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), GiveObjectRequestOpcode); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(12), targetGuid); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(16), itemGuid); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(20), amount); return body; } /// Pin an item / spell to a quickbar slot. public static byte[] BuildAddShortcut( uint seq, uint slotIndex, uint objectType, uint targetId) { byte[] body = new byte[24]; BinaryPrimitives.WriteUInt32LittleEndian(body, GameActionEnvelope); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), seq); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), AddShortcutOpcode); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(12), slotIndex); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(16), objectType); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(20), targetId); return body; } /// Unpin a quickbar slot. public static byte[] BuildRemoveShortcut(uint seq, uint slotIndex) { byte[] body = new byte[16]; BinaryPrimitives.WriteUInt32LittleEndian(body, GameActionEnvelope); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), seq); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), RemoveShortcutOpcode); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(12), slotIndex); return body; } /// Teleport to a Point of Interest (quest-driven recall). public static byte[] BuildTeleToPoi(uint seq, uint poiId) { byte[] body = new byte[16]; BinaryPrimitives.WriteUInt32LittleEndian(body, GameActionEnvelope); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), seq); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), TeleToPoiOpcode); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(12), poiId); return body; } }