paavohtl Claude Opus 4.6 (1M context) commited on
Commit
1d08533
·
1 Parent(s): 881aadd

refactor: fully type CTaskCloud handler — no raw offsets

Browse files

Replace all raw pointer arithmetic in CTaskCloud::HandleMessage with
typed DDGame fields. Render path now uses RenderQueue::alloc directly
instead of delegating to original WA function.

New/renamed DDGame fields:
- render_phase (+0x764C): rendering phase, clouds render when == 5
- parallax_scale (+0x8150): renamed from crosshair_scale, also used
for cloud parallax X offset computation
- level_bound_min_x/max_x: changed from i32 to Fixed type

Cloud handler improvements:
- Uses TaskMessage enum (FrameFinish, RenderScene, SetWind)
- Uses Fixed arithmetic with from_int(128) for padding
- Calls RenderQueue::alloc::<DrawSpriteCmd> directly for rendering
- No ORIG_HANDLE_MESSAGE needed — fully self-contained

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

crates/openwa-core/src/engine/ddgame.rs CHANGED
@@ -1,5 +1,6 @@
1
  use crate::FieldRegistry;
2
  use crate::audio::active_sound::ActiveSoundTable;
 
3
  use crate::audio::dssound::DSSound;
4
  use crate::audio::music::Music;
5
  use crate::audio::speech::SpeechSlotTable;
@@ -254,8 +255,11 @@ pub struct DDGame {
254
  /// 0x73B0: Render entry table (14 entries × 0x14 bytes).
255
  /// First u32 of each entry zeroed by InitRenderIndices.
256
  pub render_entries: [RenderEntry; 14],
257
- /// 0x74C8-0x764F: Unknown
258
- pub _unknown_74c8: [u8; 0x7650 - 0x74C8],
 
 
 
259
 
260
  /// 0x7650: Team index permutation maps (3 × 0x64 bytes).
261
  /// Used for team-to-slot mapping (render order, turn order, display order).
@@ -268,10 +272,10 @@ pub struct DDGame {
268
  /// 0x7784-0x779B: Unknown
269
  pub _unknown_7784: [u8; 0x779C - 0x7784],
270
 
271
- /// 0x779C: Level bound min X (Fixed-point, negative = off-screen left).
272
- pub level_bound_min_x: i32,
273
- /// 0x77A0: Level bound max X (Fixed-point).
274
- pub level_bound_max_x: i32,
275
  /// 0x77A4: Level bound min Y (Fixed-point, same as min_x typically).
276
  pub level_bound_min_y: i32,
277
  /// 0x77A8: Level bound max Y (Fixed-point).
@@ -342,8 +346,10 @@ pub struct DDGame {
342
 
343
  /// 0x8144-0x814F: Unknown
344
  pub _unknown_8144: [u8; 0x8150 - 0x8144],
345
- /// 0x8150: Scale factor used by DrawCrosshairLine (multiplied by 0x140000).
346
- pub crosshair_scale: i32,
 
 
347
 
348
  /// 0x8154-0x818B: Unknown
349
  pub _unknown_8154: [u8; 0x818C - 0x8154],
@@ -533,7 +539,7 @@ pub mod offsets {
533
  pub const TURN_TIME_LIMIT: usize = 0x7EA8;
534
  pub const SOUND_AVAILABLE: usize = 0x7EF8;
535
  /// Scale factor used by DrawCrosshairLine (multiplied by 0x140000).
536
- pub const CROSSHAIR_SCALE: usize = 0x8150;
537
  /// Turn status text (null-terminated ASCII, shown during gameplay).
538
  pub const TURN_STATUS_TEXT: usize = 0x818C;
539
  /// Checkpoint active flag.
 
1
  use crate::FieldRegistry;
2
  use crate::audio::active_sound::ActiveSoundTable;
3
+ use crate::fixed::Fixed;
4
  use crate::audio::dssound::DSSound;
5
  use crate::audio::music::Music;
6
  use crate::audio::speech::SpeechSlotTable;
 
255
  /// 0x73B0: Render entry table (14 entries × 0x14 bytes).
256
  /// First u32 of each entry zeroed by InitRenderIndices.
257
  pub render_entries: [RenderEntry; 14],
258
+ /// 0x74C8-0x764B: Unknown
259
+ pub _unknown_74c8: [u8; 0x764C - 0x74C8],
260
+ /// 0x764C: Rendering phase. Checked by CTaskCloud::HandleMessage:
261
+ /// clouds only render when this == 5 (in-game rendering active).
262
+ pub render_phase: i32,
263
 
264
  /// 0x7650: Team index permutation maps (3 × 0x64 bytes).
265
  /// Used for team-to-slot mapping (render order, turn order, display order).
 
272
  /// 0x7784-0x779B: Unknown
273
  pub _unknown_7784: [u8; 0x779C - 0x7784],
274
 
275
+ /// 0x779C: Level bound min X (Fixed16.16, negative = off-screen left).
276
+ pub level_bound_min_x: Fixed,
277
+ /// 0x77A0: Level bound max X (Fixed16.16).
278
+ pub level_bound_max_x: Fixed,
279
  /// 0x77A4: Level bound min Y (Fixed-point, same as min_x typically).
280
  pub level_bound_min_y: i32,
281
  /// 0x77A8: Level bound max Y (Fixed-point).
 
346
 
347
  /// 0x8144-0x814F: Unknown
348
  pub _unknown_8144: [u8; 0x8150 - 0x8144],
349
+ /// 0x8150: Parallax/camera scale factor (Fixed-point multiplier).
350
+ /// Used by DrawCrosshairLine (multiplied by 0x140000) and
351
+ /// CTaskCloud render (parallax X offset = wind_speed * this >> 16).
352
+ pub parallax_scale: i32,
353
 
354
  /// 0x8154-0x818B: Unknown
355
  pub _unknown_8154: [u8; 0x818C - 0x8154],
 
539
  pub const TURN_TIME_LIMIT: usize = 0x7EA8;
540
  pub const SOUND_AVAILABLE: usize = 0x7EF8;
541
  /// Scale factor used by DrawCrosshairLine (multiplied by 0x140000).
542
+ pub const PARALLAX_SCALE: usize = 0x8150;
543
  /// Turn status text (null-terminated ASCII, shown during gameplay).
544
  pub const TURN_STATUS_TEXT: usize = 0x818C;
545
  /// Checkpoint active flag.
crates/openwa-wormkit/src/replacements/render.rs CHANGED
@@ -507,8 +507,8 @@ unsafe extern "cdecl" fn draw_crosshair_line_impl(task_ptr: u32) {
507
  let sin_interp = trig_lookup(sin_table, angle);
508
  let cos_interp = trig_lookup(cos_table, angle);
509
 
510
- // Scale = fixed_mul(DDGame.crosshair_scale, 0x140000) + task.aim_range_offset
511
- let scale = fixed_mul(ddgame.crosshair_scale, 0x14_0000) + task.aim_range_offset;
512
 
513
  // Endpoint = start + direction * scale
514
  let mut endpoint_x = fixed_mul(sin_interp, scale).wrapping_add(start_x);
 
507
  let sin_interp = trig_lookup(sin_table, angle);
508
  let cos_interp = trig_lookup(cos_table, angle);
509
 
510
+ // Scale = fixed_mul(DDGame.parallax_scale, 0x140000) + task.aim_range_offset
511
+ let scale = fixed_mul(ddgame.parallax_scale, 0x14_0000) + task.aim_range_offset;
512
 
513
  // Endpoint = start + direction * scale
514
  let mut endpoint_x = fixed_mul(sin_interp, scale).wrapping_add(start_x);
crates/openwa-wormkit/src/replacements/task/cloud.rs CHANGED
@@ -3,18 +3,14 @@
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::game::TaskMessage;
11
  use openwa_core::log::log_line;
 
12
  use openwa_core::task::cloud::CTaskCloud;
13
  use openwa_core::task::{CTask, Task};
14
 
15
- /// Original CTaskCloud::HandleMessage, saved for render call-through.
16
- static ORIG_HANDLE_MESSAGE: AtomicU32 = AtomicU32::new(0);
17
-
18
  /// CTaskCloud::HandleMessage replacement.
19
  ///
20
  /// Handles three message types:
@@ -42,16 +38,16 @@ unsafe extern "thiscall" fn cloud_handle_message(
42
  let wind = cloud.wind_accel.0;
43
  cloud.pos_x = Fixed(cloud.pos_x.0 + cloud.vel_x.0 + wind * 10);
44
 
45
- // Wrap X at landscape bounds (with 0x800000 = 128.0 padding)
46
  let ddgame = &*cloud.ddgame();
47
- let ddgame_raw = ddgame as *const _ as *const u8;
48
- let level_left = *(ddgame_raw.add(0x779C) as *const i32) - 0x800000;
49
- let level_right = *(ddgame_raw.add(0x77A0) as *const i32) + 0x800000;
50
-
51
- if cloud.pos_x.0 < level_left {
52
- cloud.pos_x = Fixed(level_right);
53
- } else if cloud.pos_x.0 > level_right {
54
- cloud.pos_x = Fixed(level_left);
55
  }
56
 
57
  // Converge wind_accel toward wind_target (clamp step to ±0x147)
@@ -68,13 +64,27 @@ unsafe extern "thiscall" fn cloud_handle_message(
68
  }
69
 
70
  Ok(TaskMessage::RenderScene) => {
71
- // Delegate to original (uses usercall for RQ_DrawSpriteLocal)
72
- let orig = ORIG_HANDLE_MESSAGE.load(Ordering::Relaxed);
73
- let orig_fn: unsafe extern "thiscall" fn(
74
- *mut CTaskCloud, *mut CTask, u32, u32, *const u8,
75
- ) = core::mem::transmute(orig as usize);
76
- orig_fn(this, sender, msg_type, size, data);
77
- return; // original already calls base handler
 
 
 
 
 
 
 
 
 
 
 
 
 
 
78
  }
79
 
80
  Ok(TaskMessage::SetWind) => {
@@ -96,7 +106,7 @@ 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");
 
3
  //! Replaces CTaskCloud::HandleMessage (vtable slot 2).
4
  //! Handles cloud position updates (wind parallax), rendering, and wind changes.
5
 
 
 
6
  use openwa_core::address::va;
7
  use openwa_core::fixed::Fixed;
8
  use openwa_core::game::TaskMessage;
9
  use openwa_core::log::log_line;
10
+ use openwa_core::render::queue::{command_type, DrawSpriteCmd};
11
  use openwa_core::task::cloud::CTaskCloud;
12
  use openwa_core::task::{CTask, Task};
13
 
 
 
 
14
  /// CTaskCloud::HandleMessage replacement.
15
  ///
16
  /// Handles three message types:
 
38
  let wind = cloud.wind_accel.0;
39
  cloud.pos_x = Fixed(cloud.pos_x.0 + cloud.vel_x.0 + wind * 10);
40
 
41
+ // Wrap X at landscape bounds (with 128.0 Fixed padding)
42
  let ddgame = &*cloud.ddgame();
43
+ let padding = Fixed::from_int(128);
44
+ let level_left = ddgame.level_bound_min_x - padding;
45
+ let level_right = ddgame.level_bound_max_x + padding;
46
+
47
+ if cloud.pos_x < level_left {
48
+ cloud.pos_x = level_right;
49
+ } else if cloud.pos_x > level_right {
50
+ cloud.pos_x = level_left;
51
  }
52
 
53
  // Converge wind_accel toward wind_target (clamp step to ±0x147)
 
64
  }
65
 
66
  Ok(TaskMessage::RenderScene) => {
67
+ let ddgame = &mut *cloud.ddgame();
68
+
69
+ // Only render when rendering phase == 5 (in-game rendering active)
70
+ if ddgame.render_phase == 5 {
71
+ // Compute parallax X offset: (vel_x + wind * 10) * parallax_scale
72
+ let scroll_speed = cloud.vel_x.0 + cloud.wind_accel.0 * 10;
73
+ let parallax_x = ((scroll_speed as i64 * ddgame.parallax_scale as i64) >> 16) as i32;
74
+ let x = parallax_x + cloud.pos_x.0;
75
+
76
+ let rq = &mut *ddgame.render_queue;
77
+ if let Some(entry) = rq.alloc::<DrawSpriteCmd>() {
78
+ *entry = DrawSpriteCmd {
79
+ command_type: command_type::DRAW_SPRITE_LOCAL,
80
+ layer: cloud.layer_depth.0 as u32,
81
+ x_pos: x as u32 & 0xFFFF0000,
82
+ y_pos: cloud.pos_y.0 as u32 & 0xFFFF0000,
83
+ sprite_id: cloud.sprite_id,
84
+ frame: 0,
85
+ };
86
+ }
87
+ }
88
  }
89
 
90
  Ok(TaskMessage::SetWind) => {
 
106
  use openwa_core::vtable_replace;
107
 
108
  vtable_replace!(openwa_core::task::cloud::CTaskCloudVTable, va::CTASK_CLOUD_VTABLE, {
109
+ handle_message => cloud_handle_message,
110
  })?;
111
 
112
  let _ = log_line("[Cloud] HandleMessage hooked via vtable_replace");