Replace raw i32 with Fixed in all line_draw.rs function signatures and
internals. Add Fixed helper methods: floor(), pixel_center(),
round_to_int(), div_raw(), mul_raw(), HALF. Eliminates all magic
0x10000/0x8000/0xFFFF literals in favor of Fixed::ONE/HALF/floor().
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: port streaming sound chain to Rust, unify SoundId/SoundSlot
Fully port play_worm_sound/play_worm_sound_2 — the streaming sound
playback chain no longer calls into WA.exe via naked asm bridges.
New Rust implementations (sound_ops.rs):
- distance_3d_attenuation: stereo panning with i64 fixed-point math
- compute_distance_params: reads listener pos + attenuation from DDGame
- record_active_sound: ring buffer insert into ActiveSoundTable
- dispatch_local_sound: 3D audio + play_sound_pooled + record
- load_and_play_streaming / load_and_play_streaming_positional
Removed: naked asm bridges (call_load_and_play_streaming,
call_load_and_play_streaming_positional, LOAD_AND_PLAY_STREAMING_ADDR).
Unify SoundId and SoundSlot into a single SoundId type — they were
the same packed u32 format (low 16 = slot index, bit 16 = loop,
bit 17 = raw volume). Flag accessors moved to SoundId.
Other changes:
- Add Fixed Div/Div<i32> operators
- Name DDGame+0x77B8 (level_width_sound), GameInfo+0xF38C
(sound_attenuation), DDGame listener_pos() accessor
Co-Authored-By…
Rename ChannelDescriptor fields:
- flags_a -> pool_idx (buffer pool index, -1 = not pooled)
- flags_b -> priority (sound priority for channel eviction)
- channel_freq, channel_volume -> Fixed type (were already Fixed 16.16)
- flags_and_slot -> slot_and_flags
Use Fixed for all volume/frequency/pan parameters:
- core_play_sound, play_sound, play_sound_pooled
- set_master_volume, set_channel_volume, set_pan
- DSSoundVtable fn signatures updated to match
Fixed is #[repr(transparent)] wrapping i32 — same ABI, no behavioral change.
Updated all stale comments referencing old field names.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor: add weapon fire type enums, use Fixed for aim/scale params
Add FireType, FireMethod, and SpecialFireSubtype enums to weapon.rs
with TryFrom<i32>. All weapon dispatch matches in weapon.rs and
weapon_release.rs now use typed enum variants instead of raw integers.
Rename fire_subtype_34 → special_subtype, fire_subtype_38 → fire_method
in WeaponEntry for clarity.
Use Fixed type for aim direction params and landscape_scale field.
Add Mul<i32> for Fixed (integer scaling without shift). Remove
fixed_mul_shrd — superseded by Fixed's Mul impl. Spawn offset
calculation now reads as: aim_dir_x * scale * 0x18.
Rename param_1/param_2 → spawn_x/spawn_y, param_3/param_4 →
aim_dir_x/aim_dir_y based on caller analysis (FUN_0051cb60 computes
adjusted worm position + aim direction from angle).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: add CTaskVtable, broadcast_message, and retype children_data
Add base CTaskVtable (8 slots, 0x669F8C) via #[vtable] macro with
write_replay_state, free, handle_message, and process_frame.
Add Task::broadcast_message() — pure Rust port of CTask::HandleMessage
(0x562F30) that iterates the sparse children array and dispatches via
typed CTaskVtable. Currently unused (callers use original WA function
pending investigation of a subtle iteration bug).
Retype CTask.children_data from *mut u8 to *mut *mut CTask for type
safety.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: remove extra loading progress ticks, cargo fmt
Reduce from 4 to 2 DDGameWrapper__LoadingProgressTick calls, matching
the original constructor. Tick counter +0x04D8 now matches exactly
(0x334). All DDGameWrapper fields match the original.
Remaining headless checksum mismatch is likely due to headless display
handling differences (our headless display is non-null vs original's
NULL display parameter).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor: merge openwa-types and openwa-lib into unified openwa-core crate
The split between openwa-types (pure types/addresses) and openwa-lib
(rebase/wa_call/typed wrappers) was arbitrary and added friction when
deciding where new code belongs. Merged both into openwa-core.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>