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; public const uint GetAndWieldItemOpcode = 0x001Au; public const uint DropItemOpcode = 0x001Bu; public const uint NoLongerViewingContentsOpcode = 0x0195u; /// /// 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. ShortCutData = Index(u32), ObjectId(u32), /// SpellId(u16), Layer(u16) — CONFIRMED across ACE/Chorizite/holtburger (action-bar deep-dive /// §131-145). For an ITEM: objectGuid = item guid, spellId = layer = 0. public static byte[] BuildAddShortcut( uint seq, uint index, uint objectGuid, ushort spellId, ushort layer) { 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), index); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(16), objectGuid); BinaryPrimitives.WriteUInt16LittleEndian(body.AsSpan(20), spellId); BinaryPrimitives.WriteUInt16LittleEndian(body.AsSpan(22), layer); 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; } /// Drop an item on the ground. ACE GameActionDropItem.Handle reads 1 u32. public static byte[] BuildDropItem(uint seq, uint itemGuid) { byte[] body = new byte[16]; BinaryPrimitives.WriteUInt32LittleEndian(body, GameActionEnvelope); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), seq); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), DropItemOpcode); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(12), itemGuid); return body; } /// Equip an item from inventory onto the doll. holtburger actions.rs /// GetAndWieldItemActionData = (itemGuid, equipMask). public static byte[] BuildGetAndWieldItem(uint seq, uint itemGuid, uint equipMask) { byte[] body = new byte[20]; BinaryPrimitives.WriteUInt32LittleEndian(body, GameActionEnvelope); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), seq); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), GetAndWieldItemOpcode); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(12), itemGuid); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(16), equipMask); return body; } /// Close a side-pack / ground-container view. holtburger actions.rs = (containerGuid). public static byte[] BuildNoLongerViewingContents(uint seq, uint containerGuid) { byte[] body = new byte[16]; BinaryPrimitives.WriteUInt32LittleEndian(body, GameActionEnvelope); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(4), seq); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(8), NoLongerViewingContentsOpcode); BinaryPrimitives.WriteUInt32LittleEndian(body.AsSpan(12), containerGuid); return body; } }