paavohtl Claude Opus 4.6 (1M context) commited on
Commit
9d2738d
Β·
1 Parent(s): 654bf33

refactor: use Weapon enum for weapon IDs and classification

Browse files

Change CTaskWorm.selected_weapon from u32 to Weapon enum. Update
is_super_weapon, is_weapon_category_a, is_weapon_category_b to take
Weapon and match on named variants instead of hex magic numbers.

Replace raw 0x22/0x24 check with Weapon::Girder/GirderPack match.
Use SpecialFireSubtype variants in type-4 sound dispatch.
Idiomatic range check: Weapon::JetPack..=Weapon::CrateShower.

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

crates/openwa-core/src/game/weapon.rs CHANGED
@@ -5,7 +5,7 @@ use crate::fixed::Fixed;
5
  /// Weapon types. Contiguous range 0-70.
6
  ///
7
  /// Source: wkJellyWorm Constants.h
8
- #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
9
  #[repr(u32)]
10
  pub enum Weapon {
11
  None = 0,
 
5
  /// Weapon types. Contiguous range 0-70.
6
  ///
7
  /// Source: wkJellyWorm Constants.h
8
+ #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd)]
9
  #[repr(u32)]
10
  pub enum Weapon {
11
  None = 0,
crates/openwa-core/src/task/worm.rs CHANGED
@@ -1,6 +1,7 @@
1
  use super::base::CTask;
2
  use super::game_task::CGameTask;
3
  use crate::fixed::Fixed;
 
4
  use crate::game::weapon::WeaponEntry;
5
  use crate::FieldRegistry;
6
 
@@ -278,7 +279,7 @@ pub struct CTaskWorm {
278
  /// 0x168–0x16F: Unknown
279
  pub _unknown_168: [u8; 0x170 - 0x168],
280
  /// 0x170: Currently selected weapon ID.
281
- pub selected_weapon: u32,
282
  /// 0x174–0x177: Unknown
283
  pub _unknown_174: [u8; 4],
284
  /// 0x178: Display health (animated toward target). Used for health bar interpolation.
@@ -467,7 +468,7 @@ impl crate::snapshot::Snapshot for CTaskWorm {
467
  write_indent(w, i)?; writeln!(w, "team_index = {}", self.team_index)?;
468
  write_indent(w, i)?; writeln!(w, "worm_index = {}", self.worm_index)?;
469
  write_indent(w, i)?; writeln!(w, "slot_id = {}", self.slot_id)?;
470
- write_indent(w, i)?; writeln!(w, "selected_weapon = {}", self.selected_weapon)?;
471
  write_indent(w, i)?; writeln!(w, "facing = {}", self.facing_direction)?;
472
  write_indent(w, i)?; writeln!(w, "aim_angle = 0x{:08X}", self.aim_angle)?;
473
 
 
1
  use super::base::CTask;
2
  use super::game_task::CGameTask;
3
  use crate::fixed::Fixed;
4
+ use crate::game::Weapon;
5
  use crate::game::weapon::WeaponEntry;
6
  use crate::FieldRegistry;
7
 
 
279
  /// 0x168–0x16F: Unknown
280
  pub _unknown_168: [u8; 0x170 - 0x168],
281
  /// 0x170: Currently selected weapon ID.
282
+ pub selected_weapon: Weapon,
283
  /// 0x174–0x177: Unknown
284
  pub _unknown_174: [u8; 4],
285
  /// 0x178: Display health (animated toward target). Used for health bar interpolation.
 
468
  write_indent(w, i)?; writeln!(w, "team_index = {}", self.team_index)?;
469
  write_indent(w, i)?; writeln!(w, "worm_index = {}", self.worm_index)?;
470
  write_indent(w, i)?; writeln!(w, "slot_id = {}", self.slot_id)?;
471
+ write_indent(w, i)?; writeln!(w, "selected_weapon = {:?}", self.selected_weapon)?;
472
  write_indent(w, i)?; writeln!(w, "facing = {}", self.facing_direction)?;
473
  write_indent(w, i)?; writeln!(w, "aim_angle = 0x{:08X}", self.aim_angle)?;
474
 
crates/openwa-wormkit/src/replacements/weapon.rs CHANGED
@@ -205,18 +205,15 @@ pub(crate) unsafe extern "cdecl" fn fire_weapon_impl(
205
  let fire_method = (*entry).fire_method;
206
  let fire_params = &raw const (*entry).fire_params;
207
  // Log weapon fire
208
- let weapon_id = (*worm).selected_weapon;
209
- let weapon_name = Weapon::try_from(weapon_id)
210
- .map(|wp| format!("{:?}", wp))
211
- .unwrap_or_else(|id| format!("Unknown({})", id));
212
  let _ = log_line(&format!(
213
- "[Weapon] FireWeapon: {} (id={}) type={} sub34={} sub38={}",
214
- weapon_name, weapon_id, fire_type, (*entry).special_subtype, fire_method
215
  ));
216
 
217
  CTaskWorm::set_fire_complete_raw(worm, 0);
218
 
219
- use openwa_core::game::weapon::{FireType, FireMethod, SpecialFireSubtype};
220
  match FireType::try_from(fire_type) {
221
  Ok(FireType::Projectile) => match FireMethod::try_from(fire_method) {
222
  Ok(FireMethod::PlacedExplosive) => call_fire_placed_explosive(worm, fire_params, local_struct, rb(0x51EC80)),
@@ -791,7 +788,7 @@ unsafe fn fire_low_gravity(worm: *mut CTaskWorm) {
791
  // buf[0x04] = 100 (0x64), buf[0x08] = 166 (0xA6), buf[0x0C] = weapon_id, buf[0x10] = team_index
792
  buf[0x04..0x08].copy_from_slice(&100i32.to_ne_bytes());
793
  buf[0x08..0x0C].copy_from_slice(&166i32.to_ne_bytes());
794
- buf[0x0C..0x10].copy_from_slice(&(*worm).selected_weapon.to_ne_bytes());
795
  buf[0x10..0x14].copy_from_slice(&(*worm).team_index.to_ne_bytes());
796
 
797
  (*team).handle_message(
 
205
  let fire_method = (*entry).fire_method;
206
  let fire_params = &raw const (*entry).fire_params;
207
  // Log weapon fire
208
+ let weapon = (*worm).selected_weapon;
 
 
 
209
  let _ = log_line(&format!(
210
+ "[Weapon] FireWeapon: {:?} (id={}) type={} sub34={} sub38={}",
211
+ weapon, weapon as u32, fire_type, (*entry).special_subtype, fire_method
212
  ));
213
 
214
  CTaskWorm::set_fire_complete_raw(worm, 0);
215
 
216
+ use openwa_core::game::weapon::{FireType, FireMethod};
217
  match FireType::try_from(fire_type) {
218
  Ok(FireType::Projectile) => match FireMethod::try_from(fire_method) {
219
  Ok(FireMethod::PlacedExplosive) => call_fire_placed_explosive(worm, fire_params, local_struct, rb(0x51EC80)),
 
788
  // buf[0x04] = 100 (0x64), buf[0x08] = 166 (0xA6), buf[0x0C] = weapon_id, buf[0x10] = team_index
789
  buf[0x04..0x08].copy_from_slice(&100i32.to_ne_bytes());
790
  buf[0x08..0x0C].copy_from_slice(&166i32.to_ne_bytes());
791
+ buf[0x0C..0x10].copy_from_slice(&((*worm).selected_weapon as u32).to_ne_bytes());
792
  buf[0x10..0x14].copy_from_slice(&(*worm).team_index.to_ne_bytes());
793
 
794
  (*team).handle_message(
crates/openwa-wormkit/src/replacements/weapon_release.rs CHANGED
@@ -8,6 +8,7 @@
8
  use openwa_core::address::va;
9
  use openwa_core::audio::SoundId;
10
  use openwa_core::fixed::Fixed;
 
11
  use openwa_core::log::log_line;
12
  use openwa_core::rebase::rb;
13
  use openwa_core::task::worm::{CTaskWorm, WormState};
@@ -46,28 +47,58 @@ const _: () = assert!(core::mem::size_of::<WeaponReleaseContext>() == 0x2C);
46
  // ── Weapon category classifiers (pure Rust) ─────────────────
47
 
48
  /// IsSuperWeapon (0x565960): returns true for "super weapon" IDs.
49
- /// param_1 is the byte at DDGame+0x7E3F; for weapon 0x3B (SelectWorm) it returns param_1.
50
- fn is_super_weapon(weapon_id: u32, ddgame_7e3f: u8) -> bool {
51
- match weapon_id {
52
- 10 | 0x13 | 0x1D | 0x1E | 0x1F | 0x24 | 0x29 | 0x2A | 0x2D | 0x2E | 0x31 | 0x32
53
- | 0x33 | 0x36 | 0x37 | 0x38 | 0x3C | 0x3D => true,
54
- 0x3B => ddgame_7e3f != 0,
55
- _ => false,
56
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  }
58
 
59
- /// FUN_005658C0: weapon category A β€” checks if weapon is in a specific set.
60
- fn is_weapon_category_a(weapon_id: u32) -> bool {
 
61
  matches!(
62
- weapon_id,
63
- 4 | 5 | 0x17 | 0x18 | 0x19 | 0x1A | 0x1F | 0x2D | 0x2E | 0x30 | 0x32 | 0x34 | 0x35
64
- | 0x36
 
 
 
 
 
 
 
 
 
 
 
 
65
  )
66
  }
67
 
68
- /// FUN_00565920: weapon category B β€” checks if weapon is in a specific set.
69
- fn is_weapon_category_b(weapon_id: u32) -> bool {
70
- matches!(weapon_id, 0x1C | 0x2C | 0x2F | 0x32)
 
71
  }
72
 
73
  // ── Main implementation ─────────────────────────────────────
@@ -198,9 +229,9 @@ unsafe extern "cdecl" fn weapon_release_impl(
198
  ctx.network_delay = (client_idx + 1) * 1000;
199
  }
200
 
201
- // ── 6. Weapon 0x22/0x24 special ─────────────────────────
202
- let weapon_id = w.selected_weapon;
203
- if (weapon_id == 0x22 || weapon_id == 0x24) && w.weapon_param_3 == 0 {
204
  (*worm).shot_data_1 = w.shot_data_2;
205
  }
206
 
@@ -217,7 +248,7 @@ unsafe extern "cdecl" fn weapon_release_impl(
217
  0x18,
218
  if w._unknown_2cc != 0 { 1 } else { 0 },
219
  );
220
- write_u32(&mut msg_buf, 0x1C, weapon_id);
221
 
222
  let team = weapon::lookup_team_task(worm);
223
  if !team.is_null() {
@@ -234,20 +265,20 @@ unsafe extern "cdecl" fn weapon_release_impl(
234
  let team_id = (*worm).team_index;
235
  let worm_id = (*worm).worm_index;
236
 
237
- if is_super_weapon(weapon_id, g.version_flag_3) {
238
  *g.weapon_stat_counter(team_id, worm_id, 0x40D8) += 1;
239
  }
240
 
241
- // Range 0x3E..=0x46
242
- if weapon_id.wrapping_sub(0x3E) <= 8 {
243
  *g.weapon_stat_counter(team_id, worm_id, 0x40D4) += 1;
244
  }
245
 
246
- if is_weapon_category_a(weapon_id) {
247
  *g.weapon_stat_counter(team_id, worm_id, 0x40D0) += 1;
248
  }
249
 
250
- if is_weapon_category_b(weapon_id) {
251
  *g.weapon_stat_counter(team_id, worm_id, 0x40CC) += 1;
252
  }
253
 
@@ -314,29 +345,30 @@ unsafe extern "cdecl" fn weapon_release_impl(
314
  }
315
  // Type 3 (Strike): no sound
316
  Ok(FireType::Special) => {
317
- match (*entry).special_subtype {
318
- 2 => {
 
319
  sound::play_sound_local(
320
  task, SoundId::BaseballBatRelease, 3, Fixed::ONE, Fixed::ONE,
321
  );
322
  }
323
- 3 => {
324
  if let Ok(sid) = SoundId::try_from((*entry).fire_method as u32) {
325
  sound::play_sound_local(task, sid, 3, Fixed::ONE, Fixed::ONE);
326
  }
327
  }
328
- 4 => {
329
- // Sound ID at entry+0x40 = fire_params.spread (polymorphic use)
330
  if let Ok(sid) = SoundId::try_from((*entry).fire_params.spread as u32) {
331
  sound::play_sound_local(task, sid, 3, Fixed::ONE, Fixed::ONE);
332
  }
333
  }
334
- 10 => {
335
  if w._unknown_208 == 0 {
336
  sound::play_sound_local(task, SoundId::Teleport, 3, Fixed::ONE, Fixed::ONE);
337
  }
338
  }
339
- 0xB => {
340
  if w.sound_handle == 0 {
341
  call_play_worm_sound(worm, 0x10035, 0x10000, play_worm_sound_addr);
342
  }
@@ -377,8 +409,8 @@ unsafe extern "cdecl" fn weapon_release_impl(
377
  weapon::fire_weapon_impl(entry, ctx_ptr, worm);
378
 
379
  let _ = log_line(&format!(
380
- "[WeaponRelease] worm=0x{:08X} weapon={} type={} sub34={} sub38={}",
381
- worm as u32, weapon_id, fire_type, special_subtype, fire_method,
382
  ));
383
  }
384
 
 
8
  use openwa_core::address::va;
9
  use openwa_core::audio::SoundId;
10
  use openwa_core::fixed::Fixed;
11
+ use openwa_core::game::Weapon;
12
  use openwa_core::log::log_line;
13
  use openwa_core::rebase::rb;
14
  use openwa_core::task::worm::{CTaskWorm, WormState};
 
47
  // ── Weapon category classifiers (pure Rust) ─────────────────
48
 
49
  /// IsSuperWeapon (0x565960): returns true for "super weapon" IDs.
50
+ /// For SelectWorm, returns the DDGame+0x7E3F version flag (mode-dependent).
51
+ fn is_super_weapon(weapon: Weapon, ddgame_7e3f: u8) -> bool {
52
+ use Weapon::*;
53
+ matches!(
54
+ weapon,
55
+ Earthquake
56
+ | SuicideBomber
57
+ | MailStrike
58
+ | MineStrike
59
+ | MoleSquadron
60
+ | GirderPack
61
+ | ScalesOfJustice
62
+ | SuperBanana
63
+ | SalvationArmy
64
+ | MbBomb
65
+ | MingVase
66
+ | SheepStrike
67
+ | CarpetBomb
68
+ | Donkey
69
+ | NuclearTest
70
+ | Armageddon
71
+ | Freeze
72
+ | MagicBullet
73
+ ) || (weapon == Weapon::SelectWorm && ddgame_7e3f != 0)
74
  }
75
 
76
+ /// FUN_005658C0: weapon category A β€” homing/animal/special projectile weapons.
77
+ fn is_weapon_category_a(weapon: Weapon) -> bool {
78
+ use Weapon::*;
79
  matches!(
80
+ weapon,
81
+ HomingPigeon
82
+ | SheepLauncher
83
+ | Sheep
84
+ | SuperSheep
85
+ | AquaSheep
86
+ | MoleBomb
87
+ | MoleSquadron
88
+ | SalvationArmy
89
+ | MbBomb
90
+ | Skunk
91
+ | SheepStrike
92
+ | MadCow
93
+ | OldWoman
94
+ | Donkey
95
  )
96
  }
97
 
98
+ /// FUN_00565920: weapon category B β€” fire/napalm weapons.
99
+ fn is_weapon_category_b(weapon: Weapon) -> bool {
100
+ use Weapon::*;
101
+ matches!(weapon, NapalmStrike | FlameThrower | PetrolBomb | SheepStrike)
102
  }
103
 
104
  // ── Main implementation ─────────────────────────────────────
 
229
  ctx.network_delay = (client_idx + 1) * 1000;
230
  }
231
 
232
+ // ── 6. Girder/GirderPack special ────────────────────────
233
+ let weapon = w.selected_weapon;
234
+ if matches!(weapon, Weapon::Girder | Weapon::GirderPack) && w.weapon_param_3 == 0 {
235
  (*worm).shot_data_1 = w.shot_data_2;
236
  }
237
 
 
248
  0x18,
249
  if w._unknown_2cc != 0 { 1 } else { 0 },
250
  );
251
+ write_u32(&mut msg_buf, 0x1C, weapon as u32);
252
 
253
  let team = weapon::lookup_team_task(worm);
254
  if !team.is_null() {
 
265
  let team_id = (*worm).team_index;
266
  let worm_id = (*worm).worm_index;
267
 
268
+ if is_super_weapon(weapon, g.version_flag_3) {
269
  *g.weapon_stat_counter(team_id, worm_id, 0x40D8) += 1;
270
  }
271
 
272
+ // Powerup/utility weapons (JetPack..=CrateShower)
273
+ if (Weapon::JetPack..=Weapon::CrateShower).contains(&weapon) {
274
  *g.weapon_stat_counter(team_id, worm_id, 0x40D4) += 1;
275
  }
276
 
277
+ if is_weapon_category_a(weapon) {
278
  *g.weapon_stat_counter(team_id, worm_id, 0x40D0) += 1;
279
  }
280
 
281
+ if is_weapon_category_b(weapon) {
282
  *g.weapon_stat_counter(team_id, worm_id, 0x40CC) += 1;
283
  }
284
 
 
345
  }
346
  // Type 3 (Strike): no sound
347
  Ok(FireType::Special) => {
348
+ use openwa_core::game::weapon::SpecialFireSubtype as S;
349
+ match S::try_from((*entry).special_subtype) {
350
+ Ok(S::PneumaticDrill) => {
351
  sound::play_sound_local(
352
  task, SoundId::BaseballBatRelease, 3, Fixed::ONE, Fixed::ONE,
353
  );
354
  }
355
+ Ok(S::Girder) => {
356
  if let Ok(sid) = SoundId::try_from((*entry).fire_method as u32) {
357
  sound::play_sound_local(task, sid, 3, Fixed::ONE, Fixed::ONE);
358
  }
359
  }
360
+ Ok(S::BaseballBat) => {
361
+ // Sound ID from fire_params.spread (polymorphic use of field)
362
  if let Ok(sid) = SoundId::try_from((*entry).fire_params.spread as u32) {
363
  sound::play_sound_local(task, sid, 3, Fixed::ONE, Fixed::ONE);
364
  }
365
  }
366
+ Ok(S::AirStrike) => {
367
  if w._unknown_208 == 0 {
368
  sound::play_sound_local(task, SoundId::Teleport, 3, Fixed::ONE, Fixed::ONE);
369
  }
370
  }
371
+ Ok(S::ScalesOfJustice) => {
372
  if w.sound_handle == 0 {
373
  call_play_worm_sound(worm, 0x10035, 0x10000, play_worm_sound_addr);
374
  }
 
409
  weapon::fire_weapon_impl(entry, ctx_ptr, worm);
410
 
411
  let _ = log_line(&format!(
412
+ "[WeaponRelease] worm=0x{:08X} weapon={:?} type={} sub34={} sub38={}",
413
+ worm as u32, weapon, fire_type, special_subtype, fire_method,
414
  ));
415
  }
416