paavohtl Claude Opus 4.6 (1M context) commited on
Commit
fe03f4c
Β·
1 Parent(s): 0706c93

refactor: rename CGameTask._unknown_30 to subclass_data, fix CTaskWorm weapon_ctx

Browse files

Rename the polymorphic 84-byte region at CGameTask+0x30 to
subclass_data with documentation of per-subclass usage.

CTaskWorm+0x36C (weapon_ctx) is NOT a self-pointer β€” runtime logging
confirmed weapon_ctx != worm (differ by ~0x20000). It points to a
separate weapon context object with fire state at +0x30..+0x3C.
Renamed from weapon_self_ptr to weapon_ctx: *mut u8.

Removed incorrect CTaskWorm weapon fire accessors that read from
subclass_data (wrong object). Weapon state must be read from the
weapon_ctx pointer, as done in the working FireWeapon impl.

Verified: headless PASS (byte-identical), headful 133+3 / 0 FAIL.

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

crates/openwa-core/src/task/worm.rs CHANGED
@@ -151,9 +151,11 @@ pub struct CTaskWorm {
151
  pub _unknown_338: [u8; 0x368 - 0x338],
152
  /// 0x368: Animator / controller object (dispatched via vtable for state animations)
153
  pub animator: *mut u8,
154
- /// 0x36C: Self-pointer (points to this CTaskWorm). Used by WeaponRelease
155
- /// to load EAX before calling FireWeapon: `MOV EAX, [EDI+0x36C]`.
156
- pub weapon_self_ptr: *mut CTaskWorm,
 
 
157
  /// 0x370–0x3DB: Unknown (rope anchor, weapon-specific data, etc.)
158
  pub _unknown_370: [u8; 0x3DC - 0x370],
159
  /// 0x3DC: Facing direction. -1 = facing left, +1 = facing right.
@@ -184,49 +186,13 @@ impl CTaskWorm {
184
  unsafe { *((self as *const CTaskWorm as *const u8).add(0x44) as *const u32) }
185
  }
186
 
187
- // ── Weapon fire state accessors (offsets +0x30..+0x3C in subclass_data) ──
188
-
189
- /// Weapon fire type (1=projectile, 2=rope, 3=grenade, 4=special).
190
- /// Offset +0x30 in CGameTask.subclass_data.
191
- pub fn weapon_fire_type(&self) -> i32 {
192
- unsafe { *(self.base.subclass_data.as_ptr().add(0) as *const i32) }
193
- }
194
-
195
- /// Weapon fire subtype for types 3 and 4. Offset +0x34.
196
- pub fn weapon_fire_subtype_34(&self) -> i32 {
197
- unsafe { *(self.base.subclass_data.as_ptr().add(4) as *const i32) }
198
- }
199
-
200
- /// Weapon fire subtype for types 1 and 2. Offset +0x38.
201
- pub fn weapon_fire_subtype_38(&self) -> i32 {
202
- unsafe { *(self.base.subclass_data.as_ptr().add(8) as *const i32) }
203
- }
204
-
205
- /// Weapon fire completion flag. Offset +0x3C.
206
- /// Set to 0 before dispatch, 1 after.
207
- pub fn weapon_fire_complete(&self) -> i32 {
208
- unsafe { *(self.base.subclass_data.as_ptr().add(12) as *const i32) }
209
- }
210
-
211
- /// Mutable pointer to weapon_fire_complete for setting the flag.
212
- pub fn weapon_fire_complete_mut(&mut self) -> &mut i32 {
213
- unsafe { &mut *(self.base.subclass_data.as_mut_ptr().add(12) as *mut i32) }
214
- }
215
-
216
- /// Address of weapon_fire_complete field (passed as params base to fire handlers).
217
- pub fn weapon_params_ptr(&self) -> u32 {
218
- self as *const _ as u32 + 0x3C
219
- }
220
-
221
- /// Address of weapon_fire_subtype_34 field (params for GrenadeMortar).
222
- pub fn weapon_params_34_ptr(&self) -> u32 {
223
- self as *const _ as u32 + 0x34
224
- }
225
-
226
- /// Address of weapon_fire_subtype_38 field (params for type-4 specials).
227
- pub fn weapon_params_38_ptr(&self) -> u32 {
228
- self as *const _ as u32 + 0x38
229
- }
230
 
231
  /// Returns a reference to the vtable.
232
  ///
 
151
  pub _unknown_338: [u8; 0x368 - 0x338],
152
  /// 0x368: Animator / controller object (dispatched via vtable for state animations)
153
  pub animator: *mut u8,
154
+ /// 0x36C: Weapon context pointer. Points to a separate object containing
155
+ /// weapon fire state (type at +0x30, subtypes at +0x34/+0x38, params at +0x3C).
156
+ /// NOT a self-pointer β€” weapon_ctx != worm (confirmed via runtime logging).
157
+ /// Used by WeaponRelease: `MOV EAX, [EDI+0x36C]` before calling FireWeapon.
158
+ pub weapon_ctx: *mut u8,
159
  /// 0x370–0x3DB: Unknown (rope anchor, weapon-specific data, etc.)
160
  pub _unknown_370: [u8; 0x3DC - 0x370],
161
  /// 0x3DC: Facing direction. -1 = facing left, +1 = facing right.
 
186
  unsafe { *((self as *const CTaskWorm as *const u8).add(0x44) as *const u32) }
187
  }
188
 
189
+ // Weapon fire state lives in the WEAPON CONTEXT object (at CTaskWorm+0x36C),
190
+ // NOT in CTaskWorm itself. The weapon_ctx is a separate allocation with:
191
+ // +0x30: weapon_fire_type (1=projectile, 2=rope, 3=grenade, 4=special)
192
+ // +0x34: weapon_fire_subtype_34 (for types 3/4)
193
+ // +0x38: weapon_fire_subtype_38 (for types 1/2)
194
+ // +0x3C: weapon_fire_complete (0 before, 1 after dispatch)
195
+ // Accessed via raw pointers in the FireWeapon hook (weapon.rs).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
196
 
197
  /// Returns a reference to the vtable.
198
  ///
crates/openwa-wormkit/src/replacements/weapon.rs CHANGED
@@ -179,38 +179,28 @@ unsafe extern "C" fn trampoline_fire_weapon() {
179
  }
180
 
181
  /// Rust implementation of FireWeapon dispatch.
182
- /// EAX = worm self-pointer (== stack param), ECX = local_struct from WeaponRelease.
183
- unsafe extern "cdecl" fn fire_weapon_impl(worm_eax: u32, local_struct: u32, worm: u32) {
184
- use openwa_core::rebase::rb;
185
-
186
- // EAX (worm_eax) = *(CTaskWorm+0x36C), stack param (worm) = CTaskWorm*.
187
- // Check if they're actually the same β€” they might not be!
188
- if worm_eax != worm {
189
- let _ = log_line(&format!(
190
- "[Weapon] WARNING: EAX={:#x} != worm={:#x} (delta={})",
191
- worm_eax, worm, worm_eax.wrapping_sub(worm) as i32
192
- ));
193
- }
194
-
195
- // Use worm_eax (original EAX) for field reads β€” this is what the original code uses
196
- let ctx = worm_eax as *const u8;
197
  let weapon_type = *(ctx.add(0x30) as *const i32);
198
  let subtype_34 = *(ctx.add(0x34) as *const i32);
199
  let subtype_38 = *(ctx.add(0x38) as *const i32);
200
- let params = worm_eax.wrapping_add(0x3C);
 
201
 
202
  // Log weapon fire
203
- let weapon_id = *((worm as *const u8).add(0x170) as *const u32);
204
  let weapon_name = Weapon::try_from(weapon_id)
205
- .map(|wp| format!("{:?}", wp))
206
  .unwrap_or_else(|id| format!("Unknown({})", id));
207
  let _ = log_line(&format!(
208
  "[Weapon] FireWeapon: {} (id={}) type={} sub34={} sub38={}",
209
  weapon_name, weapon_id, weapon_type, subtype_34, subtype_38
210
  ));
211
 
212
- // Clear completion flag before dispatch
213
- core::ptr::write_volatile((worm + 0x3C) as *mut i32, 0);
 
 
214
 
215
  match weapon_type {
216
  1 => match subtype_38 {
@@ -226,13 +216,18 @@ unsafe extern "cdecl" fn fire_weapon_impl(worm_eax: u32, local_struct: u32, worm
226
  3 => call_fire_stdcall3(worm, params, local_struct, rb(0x51E240)), // RopeType3
227
  _ => {}
228
  },
229
- 3 => call_fire_stdcall3(worm, worm_eax.wrapping_add(0x34), local_struct, rb(0x51E2C0)),
230
- 4 => fire_weapon_special(subtype_34, worm_eax.wrapping_add(0x38), worm, local_struct),
 
 
 
 
 
 
231
  _ => {}
232
  }
233
 
234
- // Set completion flag after dispatch
235
- core::ptr::write_volatile((worm + 0x3C) as *mut i32, 1);
236
  }
237
 
238
  // ── Sub-function bridges ────────────────────────────────────
 
179
  }
180
 
181
  /// Rust implementation of FireWeapon dispatch.
182
+ unsafe extern "cdecl" fn fire_weapon_impl(weapon_ctx: u32, local_struct: u32, worm: u32) {
183
+ let ctx = weapon_ctx as *const u8;
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  let weapon_type = *(ctx.add(0x30) as *const i32);
185
  let subtype_34 = *(ctx.add(0x34) as *const i32);
186
  let subtype_38 = *(ctx.add(0x38) as *const i32);
187
+ let params = weapon_ctx.wrapping_add(0x3C);
188
+ let worm_ptr = worm as *mut u8;
189
 
190
  // Log weapon fire
191
+ let weapon_id = *(worm_ptr.add(0x170) as *const u32);
192
  let weapon_name = Weapon::try_from(weapon_id)
193
+ .map(|w| format!("{:?}", w))
194
  .unwrap_or_else(|id| format!("Unknown({})", id));
195
  let _ = log_line(&format!(
196
  "[Weapon] FireWeapon: {} (id={}) type={} sub34={} sub38={}",
197
  weapon_name, weapon_id, weapon_type, subtype_34, subtype_38
198
  ));
199
 
200
+ // worm+0x3C = completion flag
201
+ *(worm_ptr.add(0x3C) as *mut i32) = 0;
202
+
203
+ use openwa_core::rebase::rb;
204
 
205
  match weapon_type {
206
  1 => match subtype_38 {
 
216
  3 => call_fire_stdcall3(worm, params, local_struct, rb(0x51E240)), // RopeType3
217
  _ => {}
218
  },
219
+ 3 => {
220
+ let params_34 = weapon_ctx.wrapping_add(0x34);
221
+ call_fire_stdcall3(worm, params_34, local_struct, rb(0x51E2C0)); // GrenadeMortar
222
+ }
223
+ 4 => {
224
+ let params_38 = weapon_ctx.wrapping_add(0x38);
225
+ fire_weapon_special(subtype_34, params_38, worm, local_struct);
226
+ }
227
  _ => {}
228
  }
229
 
230
+ *(worm_ptr.add(0x3C) as *mut i32) = 1;
 
231
  }
232
 
233
  // ── Sub-function bridges ────────────────────────────────────