Root cause: the launcher's hooks-ready event had a 10s timeout. Under
high concurrency (j=16), DLL loading + hook installation exceeds 10s
due to I/O contention, causing the launcher to resume WA.exe's main
thread before all hooks are installed. The FireWeapon trap then fires
because WeaponRelease (last hook in the chain) was never installed.
Fix: increase hooks-ready timeout to 120s (matching test runner timeout).
Also:
- Redirect all temp files to per-PID .openwa_tmp/{pid}/ directory
instead of individual per-PID suffixed filenames. Now covers
writetest.txt, mono.tmp, custom.dat, current.thm, playback.thm
in addition to existing land.dat, landgen.svg
- Clean up temp directory on DLL detach and in test runner
- Remove 200ms test stagger (no longer needed with proper isolation)
- Add .gitignore for replay output logs (only *_expected.log tracked)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
core_play_sound stored flags_and_slot (sound ID) in desc.flags_b instead
of priority (flags). This made all channels appear high-priority (IDs like
55, 121) so new sounds with real priority (1-8) could never evict them.
Channels accumulated until all 8 were permanently occupied, blocking all
subsequent pooled sounds (explosions, sizzle, etc.).
Also fixed stop_channel to unconditionally return pool entries to free list
even when the descriptor was already evicted by allocate_channel. WA does
this; without it, evicted pool entries leak and pool_free_count drops to 0.
Also fixed stop_channel used-list removal to use swap-remove (matching WA)
instead of shift-down.
Added sound_priority replay test.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix: correct SharedData entity type and rename all SpecialFireSubtype/WormState
Port FireWeapon__Freeze (0x51E920) — plays freeze sound at worm position,
creates landscape freeze overlay via PCLandscape vtable[5], and increments
WormEntry counters (turn_action +3, effect_counter +10).
Sound positioning uses the DDGame sound queue entry directly after
PlaySoundGlobal queues the sound effect.
Add freeze_nuke_armageddon replay test (2× Freeze, plus Nuke/Armageddon).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: port weapon fire sub-functions to Rust — Drill, Prod, vtable calls
Port the core weapon firing functions from WA.exe to Rust:
- CreateWeaponProjectile (0x51E0F0): full replacement — allocates CTaskMissile
(0x40C bytes), SharedData lookup for parent CTaskTurnGame, pool capacity
check, calls original constructor. All projectile weapons flow through this.
- ProjectileFire (0x51DFB0): full replacement — advances game RNG, computes
random spread angle via Fixed16.16 trig table interpolation, applies 2D
rotation matrix to template velocity, calls ProjectileFire_Single per shot.
Used by Uzi, Handgun, Minigun.
- CreateArrow (0x51ED90): full replacement — allocates CTaskArrow (0x168 bytes),
same pool/SharedData pattern as CWP. Used by Shotgun and Longbow (which fire
physically simulated arrow entities, NOT hitscan).
Also adds:
- WeaponSpawnData struct (11 DWORDs) replacing raw [u32; 11] in CTaskMissile
- CTaskArrow struct (0x168 bytes) with vtable at 0x664198
- CTaskMissile size corrected from 0x41C to 0x40C (matching actual allocation)
- Pas…
fix: pad team names in replay log to match original WA formatting
The original WA pads quoted team names to the longest name width so
that [CPU X.XX] brackets align across all teams. Our replay loader
was missing this padding, causing log comparison failures with 6-team
replays where team names have different lengths.
Also adds bots2 replay test (6-team CPU torture test with custom scheme).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: port Surrender and Mail/Mine/Mole weapon handlers to pure Rust
Port two FireWeapon sub-handlers from WA bridge calls to pure Rust:
- Surrender (0x51E600): SharedData lookup + HandleMessage(0x29) + WormEntry counter
- Mail/Mine/Mole (0x51E670): version-conditional vtable call + HandleMessage(0x28) + counter
Fix replay loader V2 scheme bug: our code was clearing super weapon data
(memset 0x4C) for both V1 and V2 schemes when header < 0, but the original
WA only clears for V1. V2 schemes already contain valid super weapon data.
Other changes:
- Name WormEntry fields: effect_counter_04_Maybe (+0x04), turn_action_counter_Maybe (+0x08)
- Add TeamArenaRef::team_worm_mut() for mutable worm entry access
- Add mail_mine_mole replay test (Mail Strike, Mine Strike, Mole Squadron, Surrender)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
test: add longbow_water and longbow_alt_theme replay tests
Two new replay files for regression testing:
- longbow_water: longbow fired at water (no terrain collision)
- longbow_alt_theme: longbow on a different theme (non-zero map_seed)
Expected logs generated from unmodded WA.exe 3.8.1 (Steam).
These replays exposed hook-level bugs (FireWeapon, ReplayLoader)
that the original longbow replay didn't trigger.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Documents the debug server, CLI commands, frame breakpoints,
snapshot system, and key env vars (OPENWA_DEBUG_SERVER,
OPENWA_BREAK_FRAME, OPENWA_USE_ORIG_CTOR, OPENWA_WATCH_FRAME).
Also adds openwa-debug-cli and openwa-debug-proto to crate list.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
debug: add OPENWA_WATCH_FRAME for runtime hardware watchpoints
Arms DR0 on DDGame+0x50 at the specified frame via the existing VEH
watchpoint infrastructure. Set OPENWA_WATCH_FRAME=0 to arm from the
first frame.
Key finding: DDGame+0x50 is a sprite pointer at frame 1350, NOT a
small integer. The earlier raw-dump comparison was flawed (cross-
process pointer values compared without ASLR normalization). The real
desync is in sub-object content, not in DDGame field values.
Also adds shotgun replay (PASSES with both constructors, confirming
the desync is longbow-specific, not arrow-handler-specific).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Recorded from unmodded WA 3.8.1. Exercises Longbow (subtype_38=4)
and Dynamite. Expected log from vanilla game serves as ground truth.
Note: currently shows checksum mismatches at frames 1350/2186/2249/2775
due to a pre-existing bug in one of the other hooks (proven by
disabling FireWeapon hook entirely — mismatches persist). Needs
bisection to identify the offending hook.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: add headless mode with launcher↔DLL event synchronization
Add OPENWA_HEADLESS=1 mode that hooks user32!MessageBoxA/W via MinHook
to auto-dismiss all message boxes, enabling fully unattended /getlog
replay processing.
Add named event synchronization (OpenWA_HooksReady) between the launcher
and DLL: launcher creates the event before injection, DLL signals it
after install_all() completes, launcher waits before ResumeThread. This
eliminates the race between hook installation and main thread startup.
Add bots_expected.log as golden reference for headless replay log diff
testing.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: reduce auto-capture wait to 5s, add skill and replay files