refactor: rename CGameTask._unknown_30 to subclass_data, fix CTaskWorm weapon_ctx
Browse filesRename 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:
|
| 155 |
-
///
|
| 156 |
-
|
|
|
|
|
|
|
| 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 |
-
//
|
| 188 |
-
|
| 189 |
-
//
|
| 190 |
-
//
|
| 191 |
-
|
| 192 |
-
|
| 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 |
-
|
| 183 |
-
|
| 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 =
|
|
|
|
| 201 |
|
| 202 |
// Log weapon fire
|
| 203 |
-
let weapon_id = *(
|
| 204 |
let weapon_name = Weapon::try_from(weapon_id)
|
| 205 |
-
.map(|
|
| 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 |
-
//
|
| 213 |
-
|
|
|
|
|
|
|
| 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 =>
|
| 230 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 231 |
_ => {}
|
| 232 |
}
|
| 233 |
|
| 234 |
-
|
| 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 ββββββββββββββββββββββββββββββββββββ
|