feat: port CTaskCloud::HandleMessage to Rust
Browse filesFirst vtable method hook using the new typed vtable infrastructure.
Replaces CTaskCloud::HandleMessage (vtable slot 2, 0x5484C0) via
vtable_replace! macro.
Rust handles:
- msg 2: position update — Y scroll, X wind drift with landscape
wrapping, wind acceleration convergence toward target (±0x147 step)
- msg 0x54: wind target update from message data
Render path (msg 3) delegates to original WA function (uses usercall
for RQ_DrawSpriteLocal). Base CTask::HandleMessage called for all
non-render messages.
Adds replacements/task/ module directory mirroring the core task
structure.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-wormkit/src/replacements/mod.rs
CHANGED
|
@@ -13,6 +13,7 @@ mod replay;
|
|
| 13 |
mod scheme;
|
| 14 |
mod sound;
|
| 15 |
mod speech;
|
|
|
|
| 16 |
mod team;
|
| 17 |
mod weapon;
|
| 18 |
|
|
@@ -39,5 +40,6 @@ pub fn install_all() -> Result<(), String> {
|
|
| 39 |
ddgame_init::install()?;
|
| 40 |
game_state_hooks::install()?;
|
| 41 |
replay::install()?;
|
|
|
|
| 42 |
Ok(())
|
| 43 |
}
|
|
|
|
| 13 |
mod scheme;
|
| 14 |
mod sound;
|
| 15 |
mod speech;
|
| 16 |
+
mod task;
|
| 17 |
mod team;
|
| 18 |
mod weapon;
|
| 19 |
|
|
|
|
| 40 |
ddgame_init::install()?;
|
| 41 |
game_state_hooks::install()?;
|
| 42 |
replay::install()?;
|
| 43 |
+
task::install()?;
|
| 44 |
Ok(())
|
| 45 |
}
|
crates/openwa-wormkit/src/replacements/task/cloud.rs
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
//! CTaskCloud vtable hooks.
|
| 2 |
+
//!
|
| 3 |
+
//! Replaces CTaskCloud::HandleMessage (vtable slot 2).
|
| 4 |
+
//! Handles cloud position updates (wind parallax), rendering, and wind changes.
|
| 5 |
+
|
| 6 |
+
use core::sync::atomic::{AtomicU32, Ordering};
|
| 7 |
+
|
| 8 |
+
use openwa_core::address::va;
|
| 9 |
+
use openwa_core::fixed::Fixed;
|
| 10 |
+
use openwa_core::log::log_line;
|
| 11 |
+
use openwa_core::task::cloud::CTaskCloud;
|
| 12 |
+
use openwa_core::task::{CTask, Task};
|
| 13 |
+
|
| 14 |
+
/// Original CTaskCloud::HandleMessage, saved for base-class call-through.
|
| 15 |
+
static ORIG_HANDLE_MESSAGE: AtomicU32 = AtomicU32::new(0);
|
| 16 |
+
|
| 17 |
+
/// CTaskCloud::HandleMessage replacement.
|
| 18 |
+
///
|
| 19 |
+
/// Handles three message types:
|
| 20 |
+
/// - msg 2: per-frame position update (parallax scroll with wind drift)
|
| 21 |
+
/// - msg 3: render (draw sprite at computed position) — delegates to original
|
| 22 |
+
/// - msg 0x54: set wind target from message data
|
| 23 |
+
///
|
| 24 |
+
/// Always calls base CTask::HandleMessage at the end (0x562F30).
|
| 25 |
+
unsafe extern "thiscall" fn cloud_handle_message(
|
| 26 |
+
this: *mut CTaskCloud,
|
| 27 |
+
sender: *mut CTask,
|
| 28 |
+
msg_type: u32,
|
| 29 |
+
size: u32,
|
| 30 |
+
data: *const u8,
|
| 31 |
+
) {
|
| 32 |
+
let cloud = &mut *this;
|
| 33 |
+
|
| 34 |
+
match msg_type {
|
| 35 |
+
// Position update
|
| 36 |
+
2 => {
|
| 37 |
+
// Advance Y position
|
| 38 |
+
cloud.pos_y = Fixed(cloud.pos_y.0 + cloud.vel_y.0);
|
| 39 |
+
|
| 40 |
+
// Advance X position: base velocity + wind * 10
|
| 41 |
+
let wind = cloud.wind_accel.0;
|
| 42 |
+
cloud.pos_x = Fixed(cloud.pos_x.0 + cloud.vel_x.0 + wind * 10);
|
| 43 |
+
|
| 44 |
+
// Wrap X at landscape bounds (with 0x800000 = 128.0 padding)
|
| 45 |
+
let ddgame = &*cloud.ddgame();
|
| 46 |
+
let ddgame_raw = ddgame as *const _ as *const u8;
|
| 47 |
+
let level_left = *(ddgame_raw.add(0x779C) as *const i32) - 0x800000;
|
| 48 |
+
let level_right = *(ddgame_raw.add(0x77A0) as *const i32) + 0x800000;
|
| 49 |
+
|
| 50 |
+
if cloud.pos_x.0 < level_left {
|
| 51 |
+
cloud.pos_x = Fixed(level_right);
|
| 52 |
+
} else if cloud.pos_x.0 > level_right {
|
| 53 |
+
cloud.pos_x = Fixed(level_left);
|
| 54 |
+
}
|
| 55 |
+
|
| 56 |
+
// Converge wind_accel toward wind_target (clamp step to ±0x147)
|
| 57 |
+
let target = cloud.wind_target.0;
|
| 58 |
+
let current = cloud.wind_accel.0;
|
| 59 |
+
let diff = target - current;
|
| 60 |
+
if diff.abs() < 0x147 {
|
| 61 |
+
cloud.wind_accel = Fixed(target);
|
| 62 |
+
} else if current < target {
|
| 63 |
+
cloud.wind_accel = Fixed(current + 0x147);
|
| 64 |
+
} else {
|
| 65 |
+
cloud.wind_accel = Fixed(current - 0x147);
|
| 66 |
+
}
|
| 67 |
+
}
|
| 68 |
+
|
| 69 |
+
// Render — delegate to original (uses usercall for RQ_DrawSpriteLocal)
|
| 70 |
+
3 => {
|
| 71 |
+
let orig = ORIG_HANDLE_MESSAGE.load(Ordering::Relaxed);
|
| 72 |
+
let orig_fn: unsafe extern "thiscall" fn(
|
| 73 |
+
*mut CTaskCloud, *mut CTask, u32, u32, *const u8,
|
| 74 |
+
) = core::mem::transmute(orig as usize);
|
| 75 |
+
orig_fn(this, sender, msg_type, size, data);
|
| 76 |
+
return; // original already calls base handler
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
+
// Wind change — set target from message data
|
| 80 |
+
0x54 => {
|
| 81 |
+
if !data.is_null() {
|
| 82 |
+
cloud.wind_target = Fixed(*(data as *const i32));
|
| 83 |
+
}
|
| 84 |
+
}
|
| 85 |
+
|
| 86 |
+
_ => {}
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
// Call base CTask::HandleMessage for all non-render messages
|
| 90 |
+
let base_handler: unsafe extern "thiscall" fn(*mut CTask, *mut CTask, u32, u32, *const u8) =
|
| 91 |
+
core::mem::transmute(openwa_core::rebase::rb(va::CTASK_VT2_HANDLE_MESSAGE) as usize);
|
| 92 |
+
base_handler(cloud.as_task_ptr_mut(), sender, msg_type, size, data);
|
| 93 |
+
}
|
| 94 |
+
|
| 95 |
+
pub fn install() -> Result<(), String> {
|
| 96 |
+
use openwa_core::vtable_replace;
|
| 97 |
+
|
| 98 |
+
vtable_replace!(openwa_core::task::cloud::CTaskCloudVTable, va::CTASK_CLOUD_VTABLE, {
|
| 99 |
+
handle_message [ORIG_HANDLE_MESSAGE] => cloud_handle_message,
|
| 100 |
+
})?;
|
| 101 |
+
|
| 102 |
+
let _ = log_line("[Cloud] HandleMessage hooked via vtable_replace");
|
| 103 |
+
Ok(())
|
| 104 |
+
}
|
crates/openwa-wormkit/src/replacements/task/mod.rs
ADDED
|
@@ -0,0 +1,6 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
mod cloud;
|
| 2 |
+
|
| 3 |
+
pub fn install() -> Result<(), String> {
|
| 4 |
+
cloud::install()?;
|
| 5 |
+
Ok(())
|
| 6 |
+
}
|