using System.Buffers.Binary;
namespace AcDream.Core.Net.Messages;
///
/// Retail Sound game message (0xF750): the server tells the client
/// to play one SoundType slot from an object's SoundTable.
///
///
/// Wire layout, 16 bytes, S→C — three oracles agree:
/// u32 opcode | u32 guid | u32 SoundType | f32 volume. Retail dispatch is
/// CM_Physics::DispatchSB_SoundEvent @ 0x006AC760 (which reads
/// buf+4, buf+8, buf+0xC); ACE writes exactly those fields
/// in GameMessageSound.cs with a declared length of 16; holtburger's
/// PlaySoundData parses the same triple.
///
///
///
/// This message carries every server-driven cue: melee hits and wounds, wield
/// and unwield, pickup and drop, lockpicking, lifestone bind, spell resist,
/// trap triggers, item mana depletion. It was unhandled by acdream until
/// Campaign A slice A3, which is why all of those were silent.
///
///
public readonly record struct SoundEvent(uint Guid, uint SoundType, float Volume)
{
public const uint Opcode = 0xF750u;
public const int WireSize = 16;
public static SoundEvent? TryParse(ReadOnlySpan body)
{
if (body.Length < WireSize
|| BinaryPrimitives.ReadUInt32LittleEndian(body) != Opcode)
return null;
return new SoundEvent(
BinaryPrimitives.ReadUInt32LittleEndian(body[4..]),
BinaryPrimitives.ReadUInt32LittleEndian(body[8..]),
BinaryPrimitives.ReadSingleLittleEndian(body[12..]));
}
}