paavohtl Claude Opus 4.6 (1M context) commited on
Commit
586482f
·
1 Parent(s): 9f3cb75

refactor: add WeaponEntry/WeaponTable structs, use Weapon enum in match

Browse files

Add WeaponEntry (0x1D0 bytes) and WeaponTable (header + 71 entries)
structs from wkJellyWorm RE data. DDGame.weapon_table is now typed
*mut WeaponTable, enabling array indexing instead of pointer math.

Use Weapon enum names in check_weapon_avail match arms instead of hex
literals. Fix hex-to-decimal conversion errors (0x37=55=NuclearTest,
not 0x37=37=NinjaRope). Rename fields to match actual weapons:
- weapon_36_disabled → donkey_disabled
- weapon_42_mode → invisibility_mode
- weapon_avail_threshold → double_turn_time_threshold
- weapon_index_offset → aquasheep_is_supersheep
- weapon_restriction_active → supersheep_restricted

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

crates/openwa-core/src/engine/ddgame.rs CHANGED
@@ -9,6 +9,7 @@ use crate::display::palette::Palette;
9
  use crate::engine::ddgame_wrapper::DDGameWrapper;
10
  use crate::engine::game_info::GameInfo;
11
  use crate::engine::net_bridge::NetBridge;
 
12
  use crate::input::keyboard::DDKeyboard;
13
  use crate::rebase::rb;
14
  // Re-export public GfxHandler functions so existing `engine::ddgame::*` imports keep working.
@@ -96,8 +97,8 @@ pub struct DDGame {
96
  pub arrow_collision_regions: [*mut u8; 32],
97
  /// 0x50C: Coordinate list — dynamic array of packed (x,y) terrain coords.
98
  pub coord_list: *mut CoordList,
99
- /// 0x510: Weapon table pointer
100
- pub weapon_table: *mut u8,
101
  /// 0x514: Unknown pointer (populated at runtime)
102
  pub _unknown_514: *mut u8,
103
  /// 0x518: Unknown pointer (populated at runtime)
@@ -281,9 +282,9 @@ pub struct DDGame {
281
 
282
  /// 0x7D84-0x7E24: Unknown
283
  pub _unknown_7d84: [u8; 0x7E25 - 0x7D84],
284
- /// 0x7E25: Weapon restriction active flag (byte).
285
- /// When nonzero, specific weapons (0x19 area) are disabled.
286
- pub weapon_restriction_active: u8,
287
  /// 0x7E26-0x7E2D: Unknown
288
  pub _unknown_7e26: [u8; 0x7E2E - 0x7E26],
289
  /// 0x7E2E: Version flag byte 1 (set by InitVersionFlags).
 
9
  use crate::engine::ddgame_wrapper::DDGameWrapper;
10
  use crate::engine::game_info::GameInfo;
11
  use crate::engine::net_bridge::NetBridge;
12
+ use crate::game::weapon::WeaponTable;
13
  use crate::input::keyboard::DDKeyboard;
14
  use crate::rebase::rb;
15
  // Re-export public GfxHandler functions so existing `engine::ddgame::*` imports keep working.
 
97
  pub arrow_collision_regions: [*mut u8; 32],
98
  /// 0x50C: Coordinate list — dynamic array of packed (x,y) terrain coords.
99
  pub coord_list: *mut CoordList,
100
+ /// 0x510: Weapon table pointer (0x10 header + 71 × WeaponEntry).
101
+ pub weapon_table: *mut WeaponTable,
102
  /// 0x514: Unknown pointer (populated at runtime)
103
  pub _unknown_514: *mut u8,
104
  /// 0x518: Unknown pointer (populated at runtime)
 
282
 
283
  /// 0x7D84-0x7E24: Unknown
284
  pub _unknown_7d84: [u8; 0x7E25 - 0x7D84],
285
+ /// 0x7E25: SuperSheep/AquaSheep restriction active flag (byte).
286
+ /// When nonzero, the SuperSheep/AquaSheep weapon check is applied.
287
+ pub supersheep_restricted: u8,
288
  /// 0x7E26-0x7E2D: Unknown
289
  pub _unknown_7e26: [u8; 0x7E2E - 0x7E26],
290
  /// 0x7E2E: Version flag byte 1 (set by InitVersionFlags).
crates/openwa-core/src/engine/game_info.rs CHANGED
@@ -29,9 +29,9 @@ pub struct GameInfo {
29
 
30
  /// 0xD77C-0xD931: Unknown
31
  pub _unknown_d77c: [u8; 0xD932 - 0xD77C],
32
- /// 0xD932: Weapon availability threshold (u16). For weapon 0x45:
33
- /// if game_version > 0xD1 and this > 0x7FFF, weapon is disabled.
34
- pub weapon_avail_threshold: u16,
35
  /// 0xD934-0xD93B: Unknown
36
  pub _unknown_d934: [u8; 0xD93C - 0xD934],
37
  /// 0xD93C: Super weapon allowed flag. If 0, super weapons are
@@ -49,12 +49,12 @@ pub struct GameInfo {
49
 
50
  /// 0xD947-0xD94B: Unknown
51
  pub _unknown_d947: [u8; 0xD94C - 0xD947],
52
- /// 0xD94C: Weapon 0x36 (Indian Nuclear Test) disable flag.
53
- pub weapon_36_disabled: u8,
54
  /// 0xD94D-0xD955: Unknown
55
  pub _unknown_d94d: [u8; 0xD956 - 0xD94D],
56
- /// 0xD956: Weapon index offset flag. Affects weapon 0x19 availability check.
57
- pub weapon_index_offset: u8,
58
  /// 0xD957-0xD958: Unknown
59
  pub _unknown_d957: [u8; 0xD959 - 0xD957],
60
  /// 0xD959: Version-gated weapon restriction. If nonzero and game_version > 0x29,
@@ -97,9 +97,9 @@ pub struct GameInfo {
97
 
98
  /// 0xDAFA-0xDB07: Unknown
99
  pub _unknown_dafa: [u8; 0xDB08 - 0xDAFA],
100
- /// 0xDB08: Weapon 0x42 mode flag (u32). Controls team-count vs network_ecx
101
- /// check for weapon availability.
102
- pub weapon_42_mode: u32,
103
  /// 0xDB0C-0xDB1B: Unknown
104
  pub _unknown_db0c: [u8; 0xDB1C - 0xDB0C],
105
  /// 0xDB1C: Replay map sub-type (first DWORD of first payload).
 
29
 
30
  /// 0xD77C-0xD931: Unknown
31
  pub _unknown_d77c: [u8; 0xD932 - 0xD77C],
32
+ /// 0xD932: DoubleTurnTime availability threshold (u16).
33
+ /// If game_version > 0xD1 and this > 0x7FFF, DoubleTurnTime is disabled.
34
+ pub double_turn_time_threshold: u16,
35
  /// 0xD934-0xD93B: Unknown
36
  pub _unknown_d934: [u8; 0xD93C - 0xD934],
37
  /// 0xD93C: Super weapon allowed flag. If 0, super weapons are
 
49
 
50
  /// 0xD947-0xD94B: Unknown
51
  pub _unknown_d947: [u8; 0xD94C - 0xD947],
52
+ /// 0xD94C: Donkey (weapon 0x36) disable flag.
53
+ pub donkey_disabled: u8,
54
  /// 0xD94D-0xD955: Unknown
55
  pub _unknown_d94d: [u8; 0xD956 - 0xD94D],
56
+ /// 0xD956: When set, the AquaSheep slot is treated as SuperSheep instead.
57
+ pub aquasheep_is_supersheep: u8,
58
  /// 0xD957-0xD958: Unknown
59
  pub _unknown_d957: [u8; 0xD959 - 0xD957],
60
  /// 0xD959: Version-gated weapon restriction. If nonzero and game_version > 0x29,
 
97
 
98
  /// 0xDAFA-0xDB07: Unknown
99
  pub _unknown_dafa: [u8; 0xDB08 - 0xDAFA],
100
+ /// 0xDB08: Invisibility (weapon 0x42) mode flag (u32). Controls team-count
101
+ /// vs network_ecx check for availability.
102
+ pub invisibility_mode: u32,
103
  /// 0xDB0C-0xDB1B: Unknown
104
  pub _unknown_db0c: [u8; 0xDB1C - 0xDB0C],
105
  /// 0xDB1C: Replay map sub-type (first DWORD of first payload).
crates/openwa-core/src/engine/game_state_init.rs CHANGED
@@ -4,7 +4,6 @@
4
  //! InitGameState itself is Rust or the original WA code.
5
 
6
  use crate::engine::ddgame::DDGame;
7
- use crate::log::log_line;
8
  use crate::wa_alloc::wa_malloc;
9
 
10
  /// Pure Rust implementation of SpriteGfxTable__Init (0x541620).
@@ -350,20 +349,25 @@ pub unsafe fn check_weapon_avail(ddgame: *mut DDGame, weapon_index: u32) -> i32
350
  let game_version = (*gi).game_version;
351
  let num_teams = (*gi).num_teams;
352
 
 
 
353
  // Step 1: Special per-weapon disabling rules
354
  match weapon_index {
355
- 10 | 0x37 | 0x38 => {
 
 
 
356
  if (*gi).net_config_2 != 0 && (*gi).net_weapon_exception == 0 {
357
  return 0;
358
  }
359
  }
360
- 0x36 => {
361
- if (*gi).weapon_36_disabled != 0 {
362
  return 0;
363
  }
364
  }
365
- 0x42 => {
366
- if (*gi).weapon_42_mode == 0 {
367
  if (*ddgame).network_ecx == 0 {
368
  return 0;
369
  }
@@ -371,9 +375,9 @@ pub unsafe fn check_weapon_avail(ddgame: *mut DDGame, weapon_index: u32) -> i32
371
  return 0;
372
  }
373
  }
374
- 0x45 => {
375
  if game_version > 0xD1 {
376
- if (*gi).weapon_avail_threshold > 0x7FFF {
377
  return 0;
378
  }
379
  }
@@ -381,11 +385,11 @@ pub unsafe fn check_weapon_avail(ddgame: *mut DDGame, weapon_index: u32) -> i32
381
  _ => {}
382
  }
383
 
384
- // Step 2: Branch on weapon table entry + level_width_raw
385
- let weapon_table = (*ddgame).weapon_table as *const u8;
386
- let weapon_entry = *(weapon_table.add(0x10 + (weapon_index as usize) * 0x1D0) as *const i32);
387
 
388
- if (*ddgame).level_width_raw == 0 || weapon_entry != 0 {
389
  // Main path: check super weapon flag
390
  let super_result = is_super_weapon(weapon_index, (*ddgame).version_flag_3);
391
  if super_result != 0 && (*gi).super_weapon_allowed == 0 {
@@ -393,13 +397,14 @@ pub unsafe fn check_weapon_avail(ddgame: *mut DDGame, weapon_index: u32) -> i32
393
  return (game_version < 0x2A) as i32 - 1;
394
  }
395
 
396
- if (*ddgame).weapon_restriction_active == 0 {
397
  return 1;
398
  }
399
 
400
- // Check if weapon_index == (0x19 - (weapon_index_offset != 0))
401
- let offset = ((*gi).weapon_index_offset != 0) as u32;
402
- if weapon_index != 0x19 - offset {
 
403
  return 1;
404
  }
405
 
 
4
  //! InitGameState itself is Rust or the original WA code.
5
 
6
  use crate::engine::ddgame::DDGame;
 
7
  use crate::wa_alloc::wa_malloc;
8
 
9
  /// Pure Rust implementation of SpriteGfxTable__Init (0x541620).
 
349
  let game_version = (*gi).game_version;
350
  let num_teams = (*gi).num_teams;
351
 
352
+ use crate::game::weapon::Weapon;
353
+
354
  // Step 1: Special per-weapon disabling rules
355
  match weapon_index {
356
+ w if w == Weapon::Earthquake as u32
357
+ || w == Weapon::NuclearTest as u32
358
+ || w == Weapon::Armageddon as u32 =>
359
+ {
360
  if (*gi).net_config_2 != 0 && (*gi).net_weapon_exception == 0 {
361
  return 0;
362
  }
363
  }
364
+ w if w == Weapon::Donkey as u32 => {
365
+ if (*gi).donkey_disabled != 0 {
366
  return 0;
367
  }
368
  }
369
+ w if w == Weapon::Invisibility as u32 => {
370
+ if (*gi).invisibility_mode == 0 {
371
  if (*ddgame).network_ecx == 0 {
372
  return 0;
373
  }
 
375
  return 0;
376
  }
377
  }
378
+ w if w == Weapon::DoubleTurnTime as u32 => {
379
  if game_version > 0xD1 {
380
+ if (*gi).double_turn_time_threshold > 0x7FFF {
381
  return 0;
382
  }
383
  }
 
385
  _ => {}
386
  }
387
 
388
+ // Step 2: Branch on weapon name pointer (non-null = weapon defined).
389
+ let weapon_table = (*ddgame).weapon_table;
390
+ let name_ptr = (*weapon_table).entries[weapon_index as usize].name1;
391
 
392
+ if (*ddgame).level_width_raw == 0 || !name_ptr.is_null() {
393
  // Main path: check super weapon flag
394
  let super_result = is_super_weapon(weapon_index, (*ddgame).version_flag_3);
395
  if super_result != 0 && (*gi).super_weapon_allowed == 0 {
 
397
  return (game_version < 0x2A) as i32 - 1;
398
  }
399
 
400
+ if (*ddgame).supersheep_restricted == 0 {
401
  return 1;
402
  }
403
 
404
+ // AquaSheep (25) or SuperSheep (24) depending on weapon_index_offset
405
+ let restricted_id = Weapon::AquaSheep as u32
406
+ - ((*gi).aquasheep_is_supersheep != 0) as u32;
407
+ if weapon_index != restricted_id {
408
  return 1;
409
  }
410
 
crates/openwa-core/src/game/mod.rs CHANGED
@@ -14,4 +14,4 @@ pub use scheme::{
14
  SCHEME_PAYLOAD_V2, SCHEME_PAYLOAD_V3, SCHEME_WEAPON_ORDER, WEAPONS_TOTAL_COUNT,
15
  WEAPONS_V1_COUNT, WEAPONS_V1_OFFSET, WEAPONS_V2_COUNT, WEAPONS_V2_OFFSET, WEAPON_ENTRY_SIZE,
16
  };
17
- pub use weapon::Weapon;
 
14
  SCHEME_PAYLOAD_V2, SCHEME_PAYLOAD_V3, SCHEME_WEAPON_ORDER, WEAPONS_TOTAL_COUNT,
15
  WEAPONS_V1_COUNT, WEAPONS_V1_OFFSET, WEAPONS_V2_COUNT, WEAPONS_V2_OFFSET, WEAPON_ENTRY_SIZE,
16
  };
17
+ pub use weapon::{Weapon, WeaponEntry, WeaponTable};
crates/openwa-core/src/game/weapon.rs CHANGED
@@ -1,3 +1,5 @@
 
 
1
  /// Weapon types. Contiguous range 0-70.
2
  ///
3
  /// Source: wkJellyWorm Constants.h
@@ -92,3 +94,45 @@ impl TryFrom<u32> for Weapon {
92
  }
93
  }
94
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ use core::ffi::c_char;
2
+
3
  /// Weapon types. Contiguous range 0-70.
4
  ///
5
  /// Source: wkJellyWorm Constants.h
 
94
  }
95
  }
96
  }
97
+
98
+ // ============================================================
99
+ // WeaponEntry — per-weapon data in the weapon table (0x1D0 bytes)
100
+ // ============================================================
101
+
102
+ /// Per-weapon data entry in the weapon table (0x1D0 = 464 bytes).
103
+ ///
104
+ /// 71 standard entries (indices 0..70), matching the `Weapon` enum.
105
+ /// Source: wkJellyWorm/src/CustomWeapons.h (WeaponStruct).
106
+ ///
107
+ /// Only 3 fields are confirmed; the rest is opaque. wkJellyWorm copies
108
+ /// entire entries via memcpy when creating custom weapons.
109
+ #[repr(C)]
110
+ pub struct WeaponEntry {
111
+ /// +0x00: Pointer to primary weapon name string.
112
+ /// Non-null means the weapon is defined/available.
113
+ pub name1: *const c_char,
114
+ /// +0x04: Pointer to secondary weapon name string.
115
+ pub name2: *const c_char,
116
+ /// +0x08: Weapon panel row index (0..12).
117
+ pub panel_row: i32,
118
+ /// +0x0C: Unknown.
119
+ pub _unknown_0c: i32,
120
+ /// +0x10-0x1CF: Unknown fields (113 × i32).
121
+ pub _unknown_10: [u8; 0x1D0 - 0x10],
122
+ }
123
+ const _: () = assert!(core::mem::size_of::<WeaponEntry>() == 0x1D0);
124
+
125
+ /// Weapon table header (0x10 = 16 bytes before the first entry).
126
+ ///
127
+ /// The table is allocated by `InitWeaponTable` (0x53CAB0) and stored
128
+ /// at DDGame+0x510. Layout: 16-byte header + 71 × WeaponEntry.
129
+ #[repr(C)]
130
+ pub struct WeaponTable {
131
+ /// +0x00-0x0F: Header (purpose unknown).
132
+ pub _header: [u8; 0x10],
133
+ /// +0x10: Weapon entries array (71 standard weapons).
134
+ pub entries: [WeaponEntry; 71],
135
+ }
136
+ const _: () = assert!(
137
+ core::mem::size_of::<WeaponTable>() == 0x10 + 71 * 0x1D0
138
+ );