paavohtl Claude Opus 4.6 (1M context) commited on
Commit
7b3018b
·
1 Parent(s): af1e67a

feat: port CTaskCloud::HandleMessage to Rust

Browse files

First 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
+ }