Move BitGrid from task::bit_grid to display::bitgrid as its canonical
home. Delete the old module. Rename fields to match known semantics:
_unused_04 β external_buffer, _unused_1c/20 β clip_left/clip_top,
width_dup/height_dup β clip_right/clip_bottom.
Convert bit_grid_init to BitGrid::init with *mut BitGrid parameter.
Replace magic vtable address with va::BIT_GRID_VTABLE constant.
Remove redundant double memset. Remove dead TaskStateMachine class.
Rename DDGame::display_gfx β display_bitgrid (it's a BitGrid, not
DisplayGfx). Rename Sprite::display_gfx β bitgrid_vtable. Clean up
tsm variable names and stale re-exports throughout.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor: fix CTaskCloud vtable (12β7 slots), add constructor and addressesβ
The vtable was incorrectly sized at 12 slots β verified as 7 by gap to
CTaskCPU vtable (0x669D54 - 0x669D38 = 0x1C). CTaskCloud does not
override ProcessFrame; all logic runs in HandleMessage via FrameFinish.
- Fix vtable size 12β7, remove phantom process_frame slot 7
- Add write_replay_state (slot 0, 0x548430) to vtable definition
- Add ReadReplayState (0x548370) and WriteReplayState address constants
- Rename _unknown_44 β y_seed (EDI from constructor, seeds initial pos_y)
- Document _unknown_54 as unused padding (never touched by any code path)
- Add CloudType enum and CTaskCloud::init() Rust constructor
- Export new addresses and CloudType
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The bind_! macro generated &mut self methods for vtable calls, giving
LLVM noalias guarantees that WA bridge calls violated. This caused
codegen-sensitive desyncs where unrelated code changes would shift
optimization decisions and expose the UB.
Fix: auto-generate _raw associated functions (taking raw pointers) for
every bind_! vtable method. Migrate all call sites in weapon dispatch,
weapon release, sound, cloud, and filter handlers to use _raw variants
and CTask::ddgame_raw() instead of the Task trait methods.
Also adds TeamMessage enum with NapalmStrike variant as the first typed
message, and CTaskTeam::on_napalm_strike handler (not yet wired up due
to a separate binary layout sensitivity issue under investigation).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: add WormState enum β typed worm state machine valuesβ
Add repr(u8) WormState enum with all known states from CTaskWorm::HandleMessage
(0x510B40) decompilation and weapon fire dispatch analysis:
- Idle/Active states (0x64-0x68)
- Weapon fire states (0x6C-0x75: Blowtorch, Bat, Kamikaze, etc.)
- Aiming/charging states (0x77-0x7E)
- Dying/dead states (0x7F-0x8B)
Update CTaskWormVTable::set_state to take WormState parameter.
Replace all hex literals in weapon.rs with enum variants.
Source: Ghidra decompilation of HandleMessage switch cases +
SetState call sites across weapon fire dispatch.
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>
feat: add Vtable marker trait for CTask<V> type safetyβ
Add unsafe trait Vtable as a bound on CTask<V> and CGameTask<V>,
preventing arbitrary types from being used as vtable parameters.
- *const c_void implements Vtable (the default/untyped case)
- #[vtable] proc macro auto-generates `impl Vtable for *const MyVTable`
- CTask<V: Vtable> and CGameTask<V: Vtable> enforce the bound
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: add Task trait for ergonomic CTask base accessβ
Add unsafe trait Task with default methods:
- task() / task_mut() β reference to CTask base
- as_task_ptr() / as_task_ptr_mut() β raw pointers
- ddgame() β direct DDGame pointer access
Implemented for all CTask subclasses (CTaskWorm, CTaskTeam,
CTaskMissile, CTaskArrow, CTaskMine, etc.) via unsafe impls.
Replace all .base.base chains in weapon.rs with trait methods:
- (*worm).base.base.ddgame β (*worm).ddgame()
- &(*worm).base.base as *const _ as *const CTask β (*worm).as_task_ptr()
- SharedDataTable::from_task(task as *const _ as ...) β from_task((*worm).as_task_ptr())
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Define CTaskTeamVTable (12 slots, vtable at 0x669EE4) via #[vtable] macro
with typed signatures for handle_message (slot 2), write_replay_state,
free, get_entity_data, and process_frame.
Add CTaskTeam::vtable() accessor and CTaskTeam::handle_message() method
that replaces raw vtable pointer arithmetic in weapon fire handlers.
Extract lookup_team_task() helper for SharedData CTaskTeam lookup.
Note: bind_CTaskTeamVTable! not used because CTask.vtable is *mut u8
(FieldRegistry derive doesn't support generic structs, so CTask<V>
would require manual impls across the entire task hierarchy).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
feat: port weapon firing pipeline β CreateWeaponProjectile, ProjectileFire, CreateArrowβ
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β¦
refactor: distribute task class addresses to their home modulesβ
Move all CTask* class blocks from address.rs into their respective
task/ module files. address.rs re-exports via pub use so all existing
va::* call sites are unchanged.
Moved to task/base.rs: CTask, CTaskLand, CTaskDirt, CTaskSpriteAnim,
CTaskCPU, CTaskSeaBubble, + 10 ctor-only entities
Moved to task/game_task.rs: CGameTask
Moved to task/worm.rs: CTaskWorm
Moved to task/turn_game.rs: CTaskTurnGame
Moved to task/missile.rs: CTaskMissile
Moved to task/mine_oil_drum.rs: CTaskMine, CTaskOilDrum
Moved to task/supply_crate.rs: CTaskCrate
Moved to task/cloud.rs: CTaskCloud
Moved to task/filter.rs: CTaskFilter
Moved to task/fire.rs: CTaskFire
Moved to task/team.rs: CTaskTeam
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor: rename CGameTask._unknown_30 to subclass_data, add CTaskWorm weapon accessorsβ
Rename the polymorphic 84-byte region at CGameTask+0x30 to
subclass_data with documentation of per-subclass usage (worm weapon
state, filter subscriptions, missile params, team vtable, etc.).
Add CTaskWorm weapon fire accessor methods:
- weapon_fire_type(), weapon_fire_subtype_34/38()
- weapon_fire_complete() / weapon_fire_complete_mut()
- weapon_params_ptr(), weapon_params_34_ptr(), weapon_params_38_ptr()
- weapon_self_ptr field at +0x36C
Make task::worm and task::game_task modules public.
Update FireWeapon impl to use worm_eax (first cdecl param = EAX) for
field reads β must match original register source for determinism.
Verified: headless PASS (byte-identical), headful 133+3 / 0 FAIL.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor: name CTaskWorm weapon_self_ptr field, make game_task pubβ
Add weapon_self_ptr (*mut CTaskWorm) at CTaskWorm+0x36C β the
self-pointer read by WeaponRelease (MOV EAX, [EDI+0x36C]) before
calling FireWeapon. Make game_task module public for typed access.
Note: weapon fire state fields at CGameTask+0x30..+0x3C are NOT added
to CGameTask because the _unknown_30 region is polymorphic β each
subclass (worm, missile, filter, team) uses it differently.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ddgame.rs: 1965 β 1924 lines (-41)
New: task/state_machine.rs (50 lines)
Re-exported via pub use for backward compatibility.
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>
feat: add CTaskCrate struct, raw field debug UI, new task fieldsβ
- CTaskMissile (0x41C bytes): fully decompiled constructor layout with
spawn_params (11 DWORDs), weapon_data (94 DWORDs), render_data (42 DWORDs).
render_data is a shifted copy of weapon_data (weapon_data[N+3] for single
shots, weapon_data[N+52] for cluster sub-pellets) β likely for frame
interpolation support in the Steam version.
- MissileType enum: Standard=2, Homing=3, Sheep=4, Cluster=5 (confirmed via
constructor switch analysis).
- CTaskBfsIter: general-purpose BFS tree walker over CTask children using
VecDeque. Replaces inline fixed-size queue arrays in validation code.
- dump_missile_tasks(): uses CTaskBfsIter, routes raw DWORD dump to
OpenWA_validation.log (not OpenWA.log).
- Validation log now cleared unconditionally on DLL load (not just when
OPENWA_VALIDATE=1).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
refactor: split task.rs into per-type modules under task/β
Each task struct now lives in its own file. Tightly coupled types
are kept together (TurnGameCtx+CTaskTurnGame, CTaskMine+CTaskOilDrum,
SoundEmitter+CGameTask, CTaskWorm+CTaskWormVTable). mod.rs re-exports
all public types so external import paths are unchanged.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>