Changelog
$sound 3D: fix call sites that confused .threeD (bool flag) with coordinates
2026-06-12 01:00 UTC · claude (#13505)
Verified $sound (#3010) semantics: :ThreeD([bool]) is the boolean "is 3D" flag (.threeD prop, case-insensitive so threeD/threed/ThreeD are one prop); :coordinates([vec]) is the position; serialize emits 3D only when both threed and coordinates are set. Root of the confusion: :initialize does `for v,k in (data) this.(k)=v`, so a create_sound option {"threeD", coords} sets the boolean prop to a coordinate list and never sets .coordinates.
Fixed call sites that passed coordinates into the boolean ThreeD() setter (which raises E_INVARG): #2585:move and #4158:update_sensor_sphere now call sound:coordinates(...). #4158:sound_for_contact never set the flag, so contact sounds were never 3D -- added sound:ThreeD(true). #1641::set_door_state passed {"threeD", coords} to play_autoopen/play_autoclose (flag set to coords, .coordinates never set) -- now {"threed", 1}, {"coordinates", this:threeD_coords()}.
#10205:do_event was already correct (sets :coordinates() and the flag). #3516:soundtest2 left alone (scratch verb).
Added 3 contract tests on $sound: test_threeD_is_boolean_flag (ThreeD rejects a coord list), test_coordinates_sets_position, test_serialize_3d_needs_flag_and_coords. @test $sound => 7/7.
Not done (pending Q): make serialize read the :coordinates()/:ThreeD() verbs (location fallback) instead of raw props; standardize casing; remove a stray #249:tell debug line in #4158:sound_for_contact.