D2ClientSoundChannelStrc: the last-applied position and obstruction (+0x0c..+0x14, +0x1c) are floats, +0x18 an int
SOUND_UpdateChannelParams (0x004dfc20) caches what it last applied and returns early when nothing changed. The cache holds the sound's own floats: MOV bit copies of D2SoundChannelStrc fPosX..fPosZ into +0x0c..+0x14 (004dffd9..004dffe8), FLD/FSTP of fUnitDistance into +0x1c (004dfff1), FUCOMPP of +0x1c against fUnitDistance (004dfe15); SOUND_PlaySoundWithPanning clears +0x1c with FLDZ/FSTP (004e0303). +0x18 is the integer volume (CMP EDI,[EBX+0x18] at 004dfe06; -1 at 004e0306). The Mac twin agrees (UCOMISS/MOVSS on +0x1c). Typed undefined4, the source prints uint32_t: the rebuild truncates positions to integers on store (UB for negatives) and converts back on compare, so the early return never fires and every channel re-sends pan/3D/EAX/volume each update. +0x18 is cosmetic. The dw prefixes now mislead (fLastPosX.. fits the rules); only default names may be proposed, so renames are left to review.
Changes 5, applied together
Struct field
win /D2Client/D2ClientSoundChannelStrc + 0xctypeundefined4→floatStruct field
win /D2Client/D2ClientSoundChannelStrc + 0x10typeundefined4→floatStruct field
win /D2Client/D2ClientSoundChannelStrc + 0x14typeundefined4→floatStruct field
win /D2Client/D2ClientSoundChannelStrc + 0x18typeundefined4→int32_tStruct field
win /D2Client/D2ClientSoundChannelStrc + 0x1ctypeundefined4→float