refactor: type FireWeapon params as *const WeaponEntry and *mut CTaskWorm
Browse filesReplace raw u32 parameters with typed pointers:
- entry: *const WeaponEntry (EAX = active weapon table entry)
- worm: *mut CTaskWorm (stack param = worm task)
Read fire_type/subtypes via WeaponEntry fields instead of raw offsets.
Read selected_weapon via CTaskWorm field.
Clarify that completion flag (worm+0x3C) writes to CGameTask.subclass_data,
while params pointer (entry+0x3C) points into WeaponEntry.fire_complete β
same relative offset on different objects.
Verified: headless PASS, headful 133+3 / 0 FAIL.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-wormkit/src/replacements/weapon.rs
CHANGED
|
@@ -11,8 +11,10 @@ use core::sync::atomic::{AtomicU32, Ordering};
|
|
| 11 |
|
| 12 |
use openwa_core::address::va;
|
| 13 |
use openwa_core::engine::ddgame::{self, TeamArenaRef};
|
|
|
|
| 14 |
use openwa_core::game::Weapon;
|
| 15 |
use openwa_core::log::log_line;
|
|
|
|
| 16 |
|
| 17 |
use crate::hook::{self, usercall_trampoline};
|
| 18 |
|
|
@@ -179,19 +181,26 @@ unsafe extern "C" fn trampoline_fire_weapon() {
|
|
| 179 |
}
|
| 180 |
|
| 181 |
/// Rust implementation of FireWeapon dispatch.
|
| 182 |
-
|
| 183 |
-
|
| 184 |
-
|
| 185 |
-
|
| 186 |
-
|
| 187 |
-
|
| 188 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 189 |
|
| 190 |
-
|
| 191 |
-
|
|
|
|
|
|
|
|
|
|
| 192 |
|
| 193 |
// Log weapon fire
|
| 194 |
-
let weapon_id =
|
| 195 |
let weapon_name = Weapon::try_from(weapon_id)
|
| 196 |
.map(|w| format!("{:?}", w))
|
| 197 |
.unwrap_or_else(|id| format!("Unknown({})", id));
|
|
@@ -200,37 +209,35 @@ unsafe extern "cdecl" fn fire_weapon_impl(weapon_ctx: u32, local_struct: u32, wo
|
|
| 200 |
weapon_name, weapon_id, weapon_type, subtype_34, subtype_38
|
| 201 |
));
|
| 202 |
|
| 203 |
-
// worm+0x3C
|
| 204 |
-
*(
|
| 205 |
-
|
| 206 |
-
use openwa_core::rebase::rb;
|
| 207 |
|
| 208 |
match weapon_type {
|
| 209 |
1 => match subtype_38 {
|
| 210 |
-
1 => call_fire_stdcall1(
|
| 211 |
-
2 => call_fire_stdcall3(
|
| 212 |
-
3 => call_fire_thiscall2(
|
| 213 |
-
4 => call_fire_stdcall2(
|
| 214 |
_ => {}
|
| 215 |
},
|
| 216 |
2 => match subtype_38 {
|
| 217 |
-
1 => call_fire_stdcall3(
|
| 218 |
-
2 => call_fire_thiscall2(
|
| 219 |
-
3 => call_fire_stdcall3(
|
| 220 |
_ => {}
|
| 221 |
},
|
| 222 |
3 => {
|
| 223 |
-
let params_34 =
|
| 224 |
-
call_fire_stdcall3(
|
| 225 |
}
|
| 226 |
4 => {
|
| 227 |
-
let params_38 =
|
| 228 |
-
fire_weapon_special(subtype_34, params_38,
|
| 229 |
}
|
| 230 |
_ => {}
|
| 231 |
}
|
| 232 |
|
| 233 |
-
*(
|
| 234 |
}
|
| 235 |
|
| 236 |
// ββ Sub-function bridges ββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 11 |
|
| 12 |
use openwa_core::address::va;
|
| 13 |
use openwa_core::engine::ddgame::{self, TeamArenaRef};
|
| 14 |
+
use openwa_core::game::weapon::WeaponEntry;
|
| 15 |
use openwa_core::game::Weapon;
|
| 16 |
use openwa_core::log::log_line;
|
| 17 |
+
use openwa_core::task::worm::CTaskWorm;
|
| 18 |
|
| 19 |
use crate::hook::{self, usercall_trampoline};
|
| 20 |
|
|
|
|
| 181 |
}
|
| 182 |
|
| 183 |
/// Rust implementation of FireWeapon dispatch.
|
| 184 |
+
///
|
| 185 |
+
/// `entry`: EAX = active WeaponEntry pointer (from CTaskWorm+0x36C).
|
| 186 |
+
/// `local_struct`: ECX = stack-local buffer from WeaponRelease.
|
| 187 |
+
/// `worm`: stack param = CTaskWorm pointer (ESI in original).
|
| 188 |
+
///
|
| 189 |
+
/// Completion flag at worm+0x3C (CGameTask.subclass_data[12]).
|
| 190 |
+
/// Params pointer at entry+0x3C (WeaponEntry.fire_complete) β different object, same offset.
|
| 191 |
+
unsafe extern "cdecl" fn fire_weapon_impl(
|
| 192 |
+
entry: *const WeaponEntry, local_struct: u32, worm: *mut CTaskWorm,
|
| 193 |
+
) {
|
| 194 |
+
use openwa_core::rebase::rb;
|
| 195 |
|
| 196 |
+
let weapon_type = (*entry).fire_type;
|
| 197 |
+
let subtype_34 = (*entry).fire_subtype_34;
|
| 198 |
+
let subtype_38 = (*entry).fire_subtype_38;
|
| 199 |
+
let params = &raw const (*entry).fire_complete as u32;
|
| 200 |
+
let worm_u32 = worm as u32;
|
| 201 |
|
| 202 |
// Log weapon fire
|
| 203 |
+
let weapon_id = (*worm).selected_weapon;
|
| 204 |
let weapon_name = Weapon::try_from(weapon_id)
|
| 205 |
.map(|w| format!("{:?}", w))
|
| 206 |
.unwrap_or_else(|id| format!("Unknown({})", id));
|
|
|
|
| 209 |
weapon_name, weapon_id, weapon_type, subtype_34, subtype_38
|
| 210 |
));
|
| 211 |
|
| 212 |
+
// Completion flag at CGameTask.subclass_data[12] (worm+0x3C), NOT WeaponEntry
|
| 213 |
+
*(worm as *mut u8).add(0x3C).cast::<i32>() = 0;
|
|
|
|
|
|
|
| 214 |
|
| 215 |
match weapon_type {
|
| 216 |
1 => match subtype_38 {
|
| 217 |
+
1 => call_fire_stdcall1(worm_u32, params, rb(0x51EC80)), // PlacedExplosive
|
| 218 |
+
2 => call_fire_stdcall3(worm_u32, params, local_struct, rb(0x51DFB0)), // Projectile
|
| 219 |
+
3 => call_fire_thiscall2(worm_u32, params, local_struct, rb(0x51E0F0)), // CreateWeaponProjectile
|
| 220 |
+
4 => call_fire_stdcall2(worm_u32, params, local_struct, rb(0x51ED90)), // Shotgun
|
| 221 |
_ => {}
|
| 222 |
},
|
| 223 |
2 => match subtype_38 {
|
| 224 |
+
1 => call_fire_stdcall3(worm_u32, params, local_struct, rb(0x51E1C0)), // RopeType1
|
| 225 |
+
2 => call_fire_thiscall2(worm_u32, params, local_struct, rb(0x51E0F0)), // CreateWeaponProjectile
|
| 226 |
+
3 => call_fire_stdcall3(worm_u32, params, local_struct, rb(0x51E240)), // RopeType3
|
| 227 |
_ => {}
|
| 228 |
},
|
| 229 |
3 => {
|
| 230 |
+
let params_34 = &raw const (*entry).fire_subtype_34 as u32;
|
| 231 |
+
call_fire_stdcall3(worm_u32, params_34, local_struct, rb(0x51E2C0)); // GrenadeMortar
|
| 232 |
}
|
| 233 |
4 => {
|
| 234 |
+
let params_38 = &raw const (*entry).fire_subtype_38 as u32;
|
| 235 |
+
fire_weapon_special(subtype_34, params_38, worm_u32, local_struct);
|
| 236 |
}
|
| 237 |
_ => {}
|
| 238 |
}
|
| 239 |
|
| 240 |
+
*(worm as *mut u8).add(0x3C).cast::<i32>() = 1;
|
| 241 |
}
|
| 242 |
|
| 243 |
// ββ Sub-function bridges ββββββββββββββββββββββββββββββββββββ
|