refactor: add WeaponEntry/WeaponTable structs, use Weapon enum in match
Browse filesAdd 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
|
| 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:
|
| 285 |
-
/// When nonzero,
|
| 286 |
-
pub
|
| 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:
|
| 33 |
-
///
|
| 34 |
-
pub
|
| 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:
|
| 53 |
-
pub
|
| 54 |
/// 0xD94D-0xD955: Unknown
|
| 55 |
pub _unknown_d94d: [u8; 0xD956 - 0xD94D],
|
| 56 |
-
/// 0xD956:
|
| 57 |
-
pub
|
| 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:
|
| 101 |
-
/// check for
|
| 102 |
-
pub
|
| 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 |
-
|
|
|
|
|
|
|
|
|
|
| 356 |
if (*gi).net_config_2 != 0 && (*gi).net_weapon_exception == 0 {
|
| 357 |
return 0;
|
| 358 |
}
|
| 359 |
}
|
| 360 |
-
|
| 361 |
-
if (*gi).
|
| 362 |
return 0;
|
| 363 |
}
|
| 364 |
}
|
| 365 |
-
|
| 366 |
-
if (*gi).
|
| 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 |
-
|
| 375 |
if game_version > 0xD1 {
|
| 376 |
-
if (*gi).
|
| 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
|
| 385 |
-
let weapon_table = (*ddgame).weapon_table
|
| 386 |
-
let
|
| 387 |
|
| 388 |
-
if (*ddgame).level_width_raw == 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).
|
| 397 |
return 1;
|
| 398 |
}
|
| 399 |
|
| 400 |
-
//
|
| 401 |
-
let
|
| 402 |
-
|
|
|
|
| 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 |
+
);
|