Replace raw u32 sprite_flags with typed SpriteOp (packed sprite index +
transform flags) and SpriteFlags (bitflags for the 13 known blit flags).
Follows the same pattern as SoundId/KnownSoundId in the audio system.
- SpriteId enum renamed to KnownSpriteId (non-exhaustive catalog)
- SpriteOp: #[repr(transparent)] newtype with index()/flags() accessors
- SpriteFlags: bitflags! type replacing raw 0x0080_0000-style masks
- Updated through the full pipeline: DrawSpriteCmd, RenderMessage,
vtable signature, dispatcher, and blit_sprite implementation
- Added bitflags 2 as direct dep (already transitive via windows crate)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
refactor(cloud): rename pos_y/vel_y to anim_phase/phase_speed, use RenderMessage
CTaskCloud.pos_y was never a position — it's a per-frame animation phase
counter passed as the palette arg to blit_sprite. Rename to anim_phase
(and vel_y to phase_speed) to match actual semantics. Migrate the cloud
producer from DrawSpriteCmd to RenderMessage::Sprite as the first typed
render queue user.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fix(cloud): use field 0x44 as render Y, not field 0x34
CTaskCloud::HandleMessage RenderScene was sending `pos_y` (field 0x34)
as the sprite Y, which the constructor masks to `(pos_x + render_y) &
0xFFFF` — always 0 — pinning every cloud to the very top of the level.
The original at 0x548527..0x54852f loads `[ESI+0x44]` into EAX (the
usercall Y register for RQ_DrawSpriteLocal) and pushes `[ESI+0x34]` as
the trailing stack arg, which lands in the queue command's `palette`
slot and acts as a per-cloud animation/palette index incremented each
frame by `vel_y`.
Rename the misnamed `y_seed: u32` to `render_y: Fixed`, swap the two
values in the DrawSpriteCmd write, and correct the field docs.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Port the cloud spawning factory (0x552960) that creates a CTaskFilter
and spawns CTaskCloud children with deterministic LCG randomization.
- Add rng.rs with shared wa_lcg() used by advance_rng, advance_effect_rng,
and cloud spawning (eliminates duplicated LCG constants)
- Port create_weather_filter in task/team.rs with naked asm bridge for
CTaskFilter constructor (usercall ECX=0x1B)
- Add Subscribe (slot 8) and SubscribeAll (slot 7) to CTaskFilterVTable
- Add CTASK_TEAM_CREATE_WEATHER_FILTER and CTASK_FILTER_SUBSCRIBE addresses
- Hook via MinHook in wormkit cloud.rs
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>
refactor: extract game logic from wormkit/replacements to openwa-core
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>
feat: add #[derive(FieldRegistry)] to all task entity structs
Apply FieldRegistry to every #[repr(C)] struct under task/:
CTaskMine, CTaskOilDrum, CTaskCrate, CTaskCloud, CTaskFilter,
CTaskFire, CTaskTeam, CTaskTurnGame, TurnGameCtx, BitGrid,
BungeeTrailTask, WeaponAimTask.
All entity types now have auto-generated field maps available in the
global registry, so the debug UI's raw memory view labels every known
field automatically.
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: 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>