paavohtl Claude Opus 4.6 (1M context) commited on
Commit
893074e
Β·
1 Parent(s): 8e9f44c

refactor: unify BitGrid to display::bitgrid, clean up naming

Browse files

Move BitGrid from task::bit_grid to display::bitgrid as its canonical
home. Delete the old module. Rename fields to match known semantics:
_unused_04 β†’ external_buffer, _unused_1c/20 β†’ clip_left/clip_top,
width_dup/height_dup β†’ clip_right/clip_bottom.

Convert bit_grid_init to BitGrid::init with *mut BitGrid parameter.
Replace magic vtable address with va::BIT_GRID_VTABLE constant.
Remove redundant double memset. Remove dead TaskStateMachine class.

Rename DDGame::display_gfx β†’ display_bitgrid (it's a BitGrid, not
DisplayGfx). Rename Sprite::display_gfx β†’ bitgrid_vtable. Clean up
tsm variable names and stale re-exports throughout.

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

crates/openwa-core/src/address.rs CHANGED
@@ -182,10 +182,8 @@ pub mod va {
182
  fn/Usercall INPUT_CTRL_INIT = 0x0058_C0D0;
183
  }
184
 
185
- class "TaskStateMachine" {
186
- /// TaskStateMachine vtable
187
- vtable TASK_STATE_MACHINE_VTABLE = 0x0066_4118;
188
- }
189
 
190
  class "OpenGLCPU" {
191
  /// OpenGLCPU vtable (0x48-byte object)
@@ -230,7 +228,7 @@ pub mod va {
230
  class "BitGrid" {
231
  /// BitGrid base vtable
232
  vtable BIT_GRID_VTABLE = 0x0066_40EC;
233
- /// BitGrid variant vtable (TaskStateMachine-class)
234
  vtable BIT_GRID_VARIANT_VTABLE = 0x0066_4118;
235
  /// BitGrid init
236
  fn BIT_GRID_INIT = 0x004F_6370;
 
182
  fn/Usercall INPUT_CTRL_INIT = 0x0058_C0D0;
183
  }
184
 
185
+ // TaskStateMachine was our old name for BitGrid before we understood it.
186
+ // Vtable 0x664118 is now BIT_GRID_VARIANT_VTABLE in the BitGrid class block.
 
 
187
 
188
  class "OpenGLCPU" {
189
  /// OpenGLCPU vtable (0x48-byte object)
 
228
  class "BitGrid" {
229
  /// BitGrid base vtable
230
  vtable BIT_GRID_VTABLE = 0x0066_40EC;
231
+ /// BitGrid variant vtable (used for DDGame+0x380 collision grid)
232
  vtable BIT_GRID_VARIANT_VTABLE = 0x0066_4118;
233
  /// BitGrid init
234
  fn BIT_GRID_INIT = 0x004F_6370;
crates/openwa-core/src/display/bitgrid.rs CHANGED
@@ -1,29 +1,108 @@
1
- //! BitGrid display layer vtable β€” pixel drawing primitives for rendering layers.
2
  //!
3
- //! The BitGrid struct lives in `crate::task::bit_grid`. This module adds the
4
- //! display-layer vtable (0x664144, 8 slots) used by the three rendering layers
5
- //! in DisplayGfx (+0x3D9C, +0x3DA0, +0x3DA4).
 
 
 
6
  //!
7
- //! When used as display layers, the BitGrid is allocated at 0x4C bytes (vs the
8
- //! base 0x2C). The first 0x2C bytes share the same layout; the extra 0x20 bytes
9
- //! hold display-layer-specific state.
10
  //!
11
- //! ## Field reinterpretation
12
- //!
13
- //! Some fields have different semantics in display-layer context:
14
  //! - `cells_per_unit` (+0x0C) β†’ bit depth (8 for 8bpp)
15
- //! - `width/height` (+0x14/+0x18) β†’ unused in display context
16
- //! - `_unused_1c/20` (+0x1C/+0x20) β†’ clip_left / clip_top
17
  //! - `width_dup/height_dup` (+0x24/+0x28) β†’ clip_right / clip_bottom
18
 
19
- pub use crate::task::bit_grid::BitGrid;
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
 
21
  /// BitGrid display-layer vtable (0x664144, 8 slots).
22
  ///
23
  /// Pixel buffer operations for 8-bit paletted rendering layers.
24
  /// All pixel addressing uses: `data[y * row_stride + x]`
25
  ///
26
- /// The DDDisplay creates 3 layer objects in DDDisplay__Init:
27
  /// - Layer 0 at DisplayGfx+0x3D9C
28
  /// - Layer 1 at DisplayGfx+0x3DA0
29
  /// - Layer 2 at DisplayGfx+0x3DA4 (uses BitGrid__Init for extended setup)
@@ -42,8 +121,6 @@ pub struct BitGridDisplayVtable {
42
  #[slot(3)]
43
  pub destructor: fn(this: *mut BitGrid, flags: u8) -> *mut BitGrid,
44
  /// get pixel (clipped) β€” returns 0 if (x, y) is outside clip rect (0x4F9140, RET 0x8)
45
- ///
46
- /// Clip rect: `_unused_1c`..`width_dup` (x), `_unused_20`..`height_dup` (y)
47
  #[slot(4)]
48
  pub get_pixel_clipped: fn(this: *mut BitGrid, x: i32, y: i32) -> u8,
49
  /// put pixel (clipped) β€” no-op if (x, y) is outside clip rect (0x4F9180, RET 0xC)
 
1
+ //! BitGrid β€” 2D pixel/bit buffer used for rendering layers and spatial queries.
2
  //!
3
+ //! Two vtables:
4
+ //! - **Base** (0x6640EC): Bitfield grid for spatial queries (collision, visibility).
5
+ //! Initialized by `BitGrid::init` (0x4F6370). Size: 0x2C bytes.
6
+ //! - **Display layer** (0x664144, 8 slots): Pixel buffer with drawing primitives.
7
+ //! Used as rendering layers in DisplayGfx (+0x3D9C/+0x3DA0/+0x3DA4).
8
+ //! Allocated at 0x4C bytes; first 0x2C bytes share the base layout.
9
  //!
10
+ //! ## Field reinterpretation (display-layer context)
 
 
11
  //!
12
+ //! Some fields have different semantics when used as a display layer:
 
 
13
  //! - `cells_per_unit` (+0x0C) β†’ bit depth (8 for 8bpp)
14
+ //! - `width/height` (+0x14/+0x18) β†’ unused
15
+ //! - `_unused_1c/_unused_20` (+0x1C/+0x20) β†’ clip_left / clip_top
16
  //! - `width_dup/height_dup` (+0x24/+0x28) β†’ clip_right / clip_bottom
17
 
18
+ use crate::address::va;
19
+ use crate::rebase::rb;
20
+ use crate::wa_alloc::wa_malloc;
21
+ use crate::FieldRegistry;
22
+
23
+ /// BitGrid β€” 2D pixel/bit buffer.
24
+ ///
25
+ /// Base vtable at 0x6640EC (bitfield spatial grid).
26
+ /// Display-layer vtable at 0x664144 (pixel drawing).
27
+ /// Size: 0x2C bytes (base), 0x4C bytes (display layer).
28
+ #[derive(FieldRegistry)]
29
+ #[repr(C)]
30
+ pub struct BitGrid {
31
+ pub vtable: u32,
32
+ /// 0x04: External buffer flag. 0 = BitGrid owns data (frees on destroy),
33
+ /// nonzero = external ownership.
34
+ pub external_buffer: u32,
35
+ /// 0x08: Pixel/bit data pointer
36
+ pub data: *mut u8,
37
+ /// 0x0C: Cells per unit (spatial) / bit depth (display, typically 8)
38
+ pub cells_per_unit: u32,
39
+ /// 0x10: Row stride in bytes
40
+ pub row_stride: u32,
41
+ /// 0x14: Width in pixels/cells
42
+ pub width: u32,
43
+ /// 0x18: Height in pixels/cells
44
+ pub height: u32,
45
+ /// 0x1C: Clip left / x minimum (display), init 0 (spatial)
46
+ pub clip_left: u32,
47
+ /// 0x20: Clip top / y minimum (display), init 0 (spatial)
48
+ pub clip_top: u32,
49
+ /// 0x24: Clip right / x maximum (display), init = width (spatial)
50
+ pub clip_right: u32,
51
+ /// 0x28: Clip bottom / y maximum (display), init = height (spatial)
52
+ pub clip_bottom: u32,
53
+ }
54
+
55
+ const _: () = assert!(core::mem::size_of::<BitGrid>() == 0x2C);
56
+
57
+ impl BitGrid {
58
+ /// Pure Rust implementation of BitGrid__Init (0x4F6370).
59
+ ///
60
+ /// Allocates a bit-per-cell grid buffer. `cells_per_unit` is typically 1.
61
+ /// `width` and `height` are pixel dimensions. The buffer is a row-major
62
+ /// bitfield with rows aligned to 4 bytes.
63
+ ///
64
+ /// # Safety
65
+ /// `this` must point to a zero-filled allocation of at least 0x2C bytes.
66
+ pub unsafe fn init(this: *mut BitGrid, cells_per_unit: u32, width: u32, height: u32) {
67
+ let bits = cells_per_unit.wrapping_mul(width).wrapping_add(7) as i32;
68
+ let row_stride = ((bits >> 3) + 3) & !3;
69
+ let total_size = row_stride as u32 * height;
70
+
71
+ let alloc_size = ((total_size + 3) & !3) + 0x20;
72
+ let buffer = wa_malloc(alloc_size);
73
+
74
+ if buffer.is_null() {
75
+ return;
76
+ }
77
+ if total_size as usize > alloc_size as usize {
78
+ return;
79
+ }
80
+
81
+ // Note: the original calls memset twice on the same buffer (possibly a
82
+ // debug/paranoia pattern from MSVC 2005). We only do it once since the
83
+ // second write is a no-op that modern compilers would eliminate anyway.
84
+ core::ptr::write_bytes(buffer, 0, total_size as usize);
85
+
86
+ (*this).vtable = rb(va::BIT_GRID_VTABLE);
87
+ (*this).external_buffer = 0;
88
+ (*this).data = buffer;
89
+ (*this).cells_per_unit = cells_per_unit;
90
+ (*this).row_stride = row_stride as u32;
91
+ (*this).width = width;
92
+ (*this).height = height;
93
+ (*this).clip_left = 0;
94
+ (*this).clip_top = 0;
95
+ (*this).clip_right = width;
96
+ (*this).clip_bottom = height;
97
+ }
98
+ }
99
 
100
  /// BitGrid display-layer vtable (0x664144, 8 slots).
101
  ///
102
  /// Pixel buffer operations for 8-bit paletted rendering layers.
103
  /// All pixel addressing uses: `data[y * row_stride + x]`
104
  ///
105
+ /// DDDisplay creates 3 layer objects in DDDisplay__Init:
106
  /// - Layer 0 at DisplayGfx+0x3D9C
107
  /// - Layer 1 at DisplayGfx+0x3DA0
108
  /// - Layer 2 at DisplayGfx+0x3DA4 (uses BitGrid__Init for extended setup)
 
121
  #[slot(3)]
122
  pub destructor: fn(this: *mut BitGrid, flags: u8) -> *mut BitGrid,
123
  /// get pixel (clipped) β€” returns 0 if (x, y) is outside clip rect (0x4F9140, RET 0x8)
 
 
124
  #[slot(4)]
125
  pub get_pixel_clipped: fn(this: *mut BitGrid, x: i32, y: i32) -> u8,
126
  /// put pixel (clipped) β€” no-op if (x, y) is outside clip rect (0x4F9180, RET 0xC)
crates/openwa-core/src/display/gfx.rs CHANGED
@@ -1,5 +1,5 @@
1
  use super::base::DisplayBase;
2
- use crate::task::bit_grid::BitGrid;
3
  use core::ffi::c_void;
4
 
5
  /// DisplayGfx β€” full display/graphics subsystem (derived from DisplayBase).
 
1
  use super::base::DisplayBase;
2
+ use super::bitgrid::BitGrid;
3
  use core::ffi::c_void;
4
 
5
  /// DisplayGfx β€” full display/graphics subsystem (derived from DisplayBase).
crates/openwa-core/src/display/gradient.rs CHANGED
@@ -6,10 +6,10 @@
6
  //! may have subtle color differences due to the tangled decompiler output.
7
 
8
  use crate::address::va;
 
9
  use crate::engine::ddgame::DDGame;
10
  use crate::rebase::rb;
11
  use crate::render::gfx_dir::call_gfx_find_and_load;
12
- use crate::task::bit_grid::BitGrid;
13
  use crate::wa_alloc::{wa_free, wa_malloc};
14
 
15
  /// Palette context for gradient color mapping.
 
6
  //! may have subtle color differences due to the tangled decompiler output.
7
 
8
  use crate::address::va;
9
+ use crate::display::bitgrid::BitGrid;
10
  use crate::engine::ddgame::DDGame;
11
  use crate::rebase::rb;
12
  use crate::render::gfx_dir::call_gfx_find_and_load;
 
13
  use crate::wa_alloc::{wa_free, wa_malloc};
14
 
15
  /// Palette context for gradient color mapping.
crates/openwa-core/src/engine/ddgame.rs CHANGED
@@ -4,7 +4,11 @@ use crate::audio::music::Music;
4
  use crate::audio::speech::SpeechSlotTable;
5
  use crate::display::dd_display::DDDisplay;
6
  use crate::display::palette::Palette;
 
7
  use crate::engine::game_info::GameInfo;
 
 
 
8
  use crate::fixed::Fixed;
9
  use crate::game::weapon::WeaponTable;
10
  use crate::input::keyboard::DDKeyboard;
@@ -13,22 +17,6 @@ use crate::render::queue::RenderQueue;
13
  use crate::render::turn_order::TurnOrderWidget;
14
  use crate::FieldRegistry;
15
 
16
- // Re-export constructor items so existing `engine::ddgame::*` imports keep working.
17
- pub use super::ddgame_constructor::{
18
- bit_grid_init, create_ddgame, ddgame_init_fields, ddgame_init_render_indices,
19
- display_layer_color_init, gfx_dir_find_entry, gfx_resource_create, init_constructor_addrs,
20
- ON_DDGAME_ALLOC,
21
- };
22
- // Re-export public GfxHandler functions so existing `engine::ddgame::*` imports keep working.
23
- pub use crate::render::gfx_dir::gfx_dir_load_dir;
24
-
25
- // Re-export all team_arena types so existing `engine::ddgame::*` imports keep working.
26
- pub use crate::engine::team_arena::{
27
- worm, CoordEntry, CoordList, CoordListEntry, RenderEntry, SoundQueueEntry, TeamArenaRef,
28
- TeamArenaState, TeamBlock, TeamHeader, TeamIndexMap, TeamSlot0, TeamWeaponSlots, WeaponSlots,
29
- WormEntry, GAME_PHASE_NORMAL_MIN, GAME_PHASE_SUDDEN_DEATH,
30
- };
31
-
32
  /// DDGame β€” the main game engine object.
33
  ///
34
  /// This is a massive ~39KB struct (0x98D8 bytes) that owns all major subsystems:
@@ -81,15 +69,16 @@ pub struct DDGame {
81
  pub arrow_sprites: [*mut u8; 32],
82
  /// 0x0B8-0x134: Arrow GfxDir pointers (32 entries, conditional)
83
  pub arrow_gfxdirs: [*mut u8; 32],
84
- /// 0x138: DisplayGfx object pointer (vtable 0x664144)
85
- pub display_gfx: *mut u8,
86
- /// 0x13C-0x37F: Sprite/image object cache (145 pointer slots).
87
- /// All populated entries have vtable 0x664144 (same class as `display_gfx`).
 
88
  /// Not initialized in DDGame__Constructor β€” filled during gameplay with
89
  /// weapon sprites, effect images, cursor graphics, etc.
90
  pub sprite_cache: [*mut u8; 145],
91
  /// 0x380: BitGrid pointer (vtable 0x664118, 0x2C bytes)
92
- pub bit_grid: *mut u8,
93
  /// 0x384-0x467: Additional sprite/image object slots.
94
  /// Same vtable 0x664144 as sprite_cache. ~20 entries populated at runtime.
95
  pub sprite_cache_2: [*mut u8; 57],
@@ -590,7 +579,7 @@ impl DDGame {
590
  // DDGame constructor code (create_ddgame, init helpers, usercall bridges)
591
  // has been moved to ddgame_constructor.rs.
592
 
593
- // BitGrid__Init moved to crate::task::bit_grid
594
  // Re-exported via ddgame_constructor.rs
595
  /// Well-known byte offsets into DDGame, for use with raw pointer access.
596
  ///
 
4
  use crate::audio::speech::SpeechSlotTable;
5
  use crate::display::dd_display::DDDisplay;
6
  use crate::display::palette::Palette;
7
+ use crate::display::BitGrid;
8
  use crate::engine::game_info::GameInfo;
9
+ use crate::engine::{
10
+ CoordEntry, CoordList, RenderEntry, SoundQueueEntry, TeamArenaState, TeamIndexMap,
11
+ };
12
  use crate::fixed::Fixed;
13
  use crate::game::weapon::WeaponTable;
14
  use crate::input::keyboard::DDKeyboard;
 
17
  use crate::render::turn_order::TurnOrderWidget;
18
  use crate::FieldRegistry;
19
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
20
  /// DDGame β€” the main game engine object.
21
  ///
22
  /// This is a massive ~39KB struct (0x98D8 bytes) that owns all major subsystems:
 
69
  pub arrow_sprites: [*mut u8; 32],
70
  /// 0x0B8-0x134: Arrow GfxDir pointers (32 entries, conditional)
71
  pub arrow_gfxdirs: [*mut u8; 32],
72
+ /// 0x138: Primary display BitGrid (vtable 0x664144, 8bpp pixel buffer).
73
+ /// Allocated as 0x4C bytes, initialized with BitGrid::init(8, 0x100, 0x1E0).
74
+ pub display_bitgrid: *mut BitGrid,
75
+ /// 0x13C-0x37F: Sprite/image BitGrid cache (145 pointer slots).
76
+ /// All populated entries have vtable 0x664144 (same class as `display_bitgrid`).
77
  /// Not initialized in DDGame__Constructor β€” filled during gameplay with
78
  /// weapon sprites, effect images, cursor graphics, etc.
79
  pub sprite_cache: [*mut u8; 145],
80
  /// 0x380: BitGrid pointer (vtable 0x664118, 0x2C bytes)
81
+ pub bit_grid: *mut BitGrid,
82
  /// 0x384-0x467: Additional sprite/image object slots.
83
  /// Same vtable 0x664144 as sprite_cache. ~20 entries populated at runtime.
84
  pub sprite_cache_2: [*mut u8; 57],
 
579
  // DDGame constructor code (create_ddgame, init helpers, usercall bridges)
580
  // has been moved to ddgame_constructor.rs.
581
 
582
+ // BitGrid__Init lives in crate::display::bitgrid
583
  // Re-exported via ddgame_constructor.rs
584
  /// Well-known byte offsets into DDGame, for use with raw pointer access.
585
  ///
crates/openwa-core/src/engine/ddgame_constructor.rs CHANGED
@@ -12,6 +12,7 @@ use crate::address::va;
12
  use crate::audio::active_sound::ActiveSoundTable;
13
  use crate::audio::dssound::DSSound;
14
  use crate::audio::music::Music;
 
15
  use crate::display::dd_display::DDDisplay;
16
  use crate::display::gradient::compute_complex_gradient;
17
  use crate::display::palette::Palette;
@@ -26,12 +27,8 @@ use crate::render::gfx_dir::{
26
  };
27
  pub use crate::render::gfx_dir::{gfx_dir_find_entry, gfx_resource_create};
28
  use crate::render::landscape::PCLandscape;
29
- use crate::task::bit_grid::BitGrid;
30
  use crate::wa_alloc::{wa_malloc, wa_malloc_zeroed};
31
 
32
- // Re-export bit_grid_init so existing `engine::ddgame::bit_grid_init` paths keep working.
33
- pub use crate::task::bit_grid::bit_grid_init;
34
-
35
  // ============================================================
36
  // Pure Rust implementations of DDGame sub-functions
37
  // ============================================================
@@ -708,16 +705,16 @@ unsafe fn init_graphics_and_resources(
708
  }
709
  };
710
 
711
- // ── BitGrid at DDGame+0x380 (alloc 0x4C, memset 0x2C) ──
712
- // Pure Rust: allocate object, call bit_grid_init, override vtable.
713
  {
714
- let bit_grid = wa_malloc(0x4C);
715
- core::ptr::write_bytes(bit_grid, 0, 0x2C);
 
716
  if !bit_grid.is_null() {
717
  let width = (*ddgame).level_width;
718
  let height = (*ddgame).level_height;
719
- bit_grid_init(bit_grid, 1, width, height);
720
- *(bit_grid as *mut u32) = rb(va::BIT_GRID_VARIANT_VTABLE);
721
  }
722
  (*ddgame).bit_grid = bit_grid;
723
  }
@@ -803,9 +800,9 @@ unsafe fn init_graphics_and_resources(
803
  // right/bottom = dim - margin
804
  // Result: this[5]=this[6]=10 for sprites >20px (the 10px inset).
805
  if !sprite.is_null() {
806
- let tsm = &*(sprite as *const BitGrid);
807
- let sprite_w = tsm.width as i32;
808
- let sprite_h = tsm.height as i32;
809
  let margin_w = (sprite_w / 2 - 10).max(0);
810
  let margin_h = (sprite_h / 2 - 10).max(0);
811
 
@@ -846,15 +843,16 @@ unsafe fn init_graphics_and_resources(
846
  release(gfx_resource, 1);
847
  }
848
 
849
- // ── DisplayGfx at DDGame+0x138 ──
850
  {
851
- let tsm = wa_malloc(0x4C);
852
- core::ptr::write_bytes(tsm, 0, 0x2C);
853
- if !tsm.is_null() {
854
- bit_grid_init(tsm, 8, 0x100, 0x1E0);
855
- *(tsm as *mut u32) = rb(va::BIT_GRID_DISPLAY_VTABLE);
 
856
  }
857
- (*ddgame).display_gfx = tsm;
858
  }
859
 
860
  // ── CoordList at DDGame+0x50C (capacity 600, 0x12C0 buffer) ──
 
12
  use crate::audio::active_sound::ActiveSoundTable;
13
  use crate::audio::dssound::DSSound;
14
  use crate::audio::music::Music;
15
+ use crate::display::bitgrid::BitGrid;
16
  use crate::display::dd_display::DDDisplay;
17
  use crate::display::gradient::compute_complex_gradient;
18
  use crate::display::palette::Palette;
 
27
  };
28
  pub use crate::render::gfx_dir::{gfx_dir_find_entry, gfx_resource_create};
29
  use crate::render::landscape::PCLandscape;
 
30
  use crate::wa_alloc::{wa_malloc, wa_malloc_zeroed};
31
 
 
 
 
32
  // ============================================================
33
  // Pure Rust implementations of DDGame sub-functions
34
  // ============================================================
 
705
  }
706
  };
707
 
708
+ // ── BitGrid at DDGame+0x380 ──
 
709
  {
710
+ use crate::wa_alloc::wa_malloc_struct_zeroed;
711
+
712
+ let bit_grid = wa_malloc_struct_zeroed::<BitGrid>();
713
  if !bit_grid.is_null() {
714
  let width = (*ddgame).level_width;
715
  let height = (*ddgame).level_height;
716
+ BitGrid::init(bit_grid as *mut BitGrid, 1, width, height);
717
+ (*bit_grid).vtable = rb(va::BIT_GRID_VARIANT_VTABLE);
718
  }
719
  (*ddgame).bit_grid = bit_grid;
720
  }
 
800
  // right/bottom = dim - margin
801
  // Result: this[5]=this[6]=10 for sprites >20px (the 10px inset).
802
  if !sprite.is_null() {
803
+ let grid = &*(sprite as *const BitGrid);
804
+ let sprite_w = grid.width as i32;
805
+ let sprite_h = grid.height as i32;
806
  let margin_w = (sprite_w / 2 - 10).max(0);
807
  let margin_h = (sprite_h / 2 - 10).max(0);
808
 
 
843
  release(gfx_resource, 1);
844
  }
845
 
846
+ // ── Display BitGrid at DDGame+0x138 ──
847
  {
848
+ use crate::wa_alloc::wa_malloc_struct_zeroed;
849
+
850
+ let grid = wa_malloc_struct_zeroed::<BitGrid>();
851
+ if !grid.is_null() {
852
+ BitGrid::init(grid, 8, 0x100, 0x1E0);
853
+ *(grid as *mut u32) = rb(va::BIT_GRID_DISPLAY_VTABLE);
854
  }
855
+ (*ddgame).display_bitgrid = grid as *mut BitGrid;
856
  }
857
 
858
  // ── CoordList at DDGame+0x50C (capacity 600, 0x12C0 buffer) ──
crates/openwa-core/src/engine/team_arena.rs CHANGED
@@ -143,6 +143,7 @@ pub struct WormEntry {
143
  /// 0x00: Worm state machine state.
144
  /// Values: 0x65=idle, 0x67=active/selected, 0x68=active variant.
145
  /// Special states {0x80..0x85, 0x89} = dying/drowning/special animation.
 
146
  pub state: u32,
147
  /// 0x04: Unknown counter. Incremented by Freeze (+10).
148
  pub effect_counter_04_Maybe: i32,
@@ -310,6 +311,7 @@ pub struct TeamArenaState {
310
  /// 0x2C10: Unknown padding
311
  pub _pad_2c10: [u8; 0x18],
312
  /// 0x2C28: Game phase counter (>=484 = sudden death, >=-2 = normal game)
 
313
  pub game_phase: i32,
314
 
315
  // === Alliance tracking (set by CountTeamsByAlliance + SetActiveWorm_Maybe) ===
@@ -332,6 +334,7 @@ pub struct TeamArenaState {
332
  const _: () = assert!(core::mem::size_of::<TeamArenaState>() == 0x2C48);
333
 
334
  /// Worm state constants and helpers.
 
335
  pub mod worm {
336
  // --- Known state values (runtime-validated via validator dumps) ---
337
 
 
143
  /// 0x00: Worm state machine state.
144
  /// Values: 0x65=idle, 0x67=active/selected, 0x68=active variant.
145
  /// Special states {0x80..0x85, 0x89} = dying/drowning/special animation.
146
+ /// TODO: Update this to use WormState enum
147
  pub state: u32,
148
  /// 0x04: Unknown counter. Incremented by Freeze (+10).
149
  pub effect_counter_04_Maybe: i32,
 
311
  /// 0x2C10: Unknown padding
312
  pub _pad_2c10: [u8; 0x18],
313
  /// 0x2C28: Game phase counter (>=484 = sudden death, >=-2 = normal game)
314
+ /// TODO: Convert this to GamePhase enum
315
  pub game_phase: i32,
316
 
317
  // === Alliance tracking (set by CountTeamsByAlliance + SetActiveWorm_Maybe) ===
 
334
  const _: () = assert!(core::mem::size_of::<TeamArenaState>() == 0x2C48);
335
 
336
  /// Worm state constants and helpers.
337
+ /// TODO: Replace this module with the WormState enum
338
  pub mod worm {
339
  // --- Known state values (runtime-validated via validator dumps) ---
340
 
crates/openwa-core/src/engine/team_ops.rs CHANGED
@@ -13,7 +13,9 @@
13
  //! - CheckAnyWormState0x8b (0x522970)
14
  //! - SetActiveWorm_Maybe (0x522500)
15
 
16
- use super::ddgame::{self, TeamArenaRef};
 
 
17
 
18
  /// Count teams by alliance membership β€” port of 0x522030.
19
  ///
@@ -67,7 +69,7 @@ pub unsafe fn is_worm_in_special_state(
67
  worm_index: u32,
68
  arena: TeamArenaRef,
69
  ) -> u32 {
70
- if ddgame::worm::is_special_state(
71
  arena
72
  .team_worm(team_index as usize, worm_index as usize)
73
  .state,
 
13
  //! - CheckAnyWormState0x8b (0x522970)
14
  //! - SetActiveWorm_Maybe (0x522500)
15
 
16
+ use crate::engine::team_arena::worm;
17
+
18
+ use super::team_arena::TeamArenaRef;
19
 
20
  /// Count teams by alliance membership β€” port of 0x522030.
21
  ///
 
69
  worm_index: u32,
70
  arena: TeamArenaRef,
71
  ) -> u32 {
72
+ if worm::is_special_state(
73
  arena
74
  .team_worm(team_index as usize, worm_index as usize)
75
  .state,
crates/openwa-core/src/game/weapon_fire.rs CHANGED
@@ -11,7 +11,7 @@
11
 
12
  use crate::address::va;
13
  use crate::audio::{KnownSoundId, SoundId};
14
- use crate::engine::ddgame::{self, TeamArenaRef};
15
  use crate::fixed::Fixed;
16
  use crate::game::weapon::{WeaponEntry, WeaponFireParams, WeaponSpawnData};
17
  use crate::game::Weapon;
@@ -93,15 +93,13 @@ pub unsafe fn get_ammo(team_index: u32, arena: TeamArenaRef, weapon_id: u32) ->
93
  }
94
  // In sudden death (phase >= 484), delayed weapons return 0
95
  // unless it's Teleport (weapon 0x28)
96
- if state.game_phase >= ddgame::GAME_PHASE_SUDDEN_DEATH
97
- && weapon_id != Weapon::Teleport as u32
98
- {
99
  return 0;
100
  }
101
  }
102
 
103
  // SelectWorm (0x3B) requires >1 alive worm on the team
104
- if state.game_phase >= ddgame::GAME_PHASE_NORMAL_MIN
105
  && weapon_id == Weapon::SelectWorm as u32
106
  && count_alive_worms(team_index, arena) == 0
107
  {
@@ -465,7 +463,6 @@ unsafe fn fire_select_worm(worm: *mut CTaskWorm) {
465
  /// Bit position comes from weapon entry's fire_params.
466
  /// In game_version > 0x1C: toggles (set/clear). Otherwise: always sets.
467
  unsafe fn fire_skip_go(worm: *const CTaskWorm, entry: *const WeaponEntry) {
468
- use crate::engine::ddgame::TeamArenaRef;
469
  let ddgame = CTask::ddgame_raw(worm as *const CTask);
470
  let game_version = (*(*ddgame).game_info).game_version;
471
  let team_index = (*worm).team_index as usize;
@@ -489,8 +486,6 @@ unsafe fn fire_skip_go(worm: *const CTaskWorm, entry: *const WeaponEntry) {
489
  /// Sends message 0x29 (TaskMessage::Freeze) to CTaskTurnGame, then increments
490
  /// WormEntry.turn_action_counter_Maybe by 14 (0x0E).
491
  unsafe fn fire_freeze(worm: *mut CTaskWorm) {
492
- use crate::engine::ddgame::TeamArenaRef;
493
-
494
  fire_send_team_message(worm, 0x29);
495
 
496
  let ddgame = CTask::ddgame_raw(worm as *const CTask);
@@ -514,7 +509,6 @@ unsafe fn fire_freeze(worm: *mut CTaskWorm) {
514
  /// - version >= 5 && worm state == 0x78 && version < 8: call vtable
515
  /// - otherwise: skip
516
  unsafe fn fire_mail_mine_mole(worm: *mut CTaskWorm) {
517
- use crate::engine::ddgame::TeamArenaRef;
518
  let ddgame = CTask::ddgame_raw(worm as *const CTask);
519
  let version = (*ddgame).version_flag_4;
520
  let worm_state = (*worm).state();
@@ -989,7 +983,7 @@ unsafe fn fire_girder(worm: *mut CTaskWorm) {
989
  girder_visual(landscape, girder_x >> 16, girder_y >> 16, sprite1, sprite2);
990
 
991
  // Increment WormEntry counters
992
- let arena = crate::engine::ddgame::TeamArenaRef::from_ptr(&raw mut (*ddgame).team_arena);
993
  let team_index = (*worm).team_index as usize;
994
  let worm_index = (*worm).worm_index as usize;
995
  let entry = arena.team_worm_mut(team_index, worm_index);
 
11
 
12
  use crate::address::va;
13
  use crate::audio::{KnownSoundId, SoundId};
14
+ use crate::engine::{TeamArenaRef, GAME_PHASE_NORMAL_MIN, GAME_PHASE_SUDDEN_DEATH};
15
  use crate::fixed::Fixed;
16
  use crate::game::weapon::{WeaponEntry, WeaponFireParams, WeaponSpawnData};
17
  use crate::game::Weapon;
 
93
  }
94
  // In sudden death (phase >= 484), delayed weapons return 0
95
  // unless it's Teleport (weapon 0x28)
96
+ if state.game_phase >= GAME_PHASE_SUDDEN_DEATH && weapon_id != Weapon::Teleport as u32 {
 
 
97
  return 0;
98
  }
99
  }
100
 
101
  // SelectWorm (0x3B) requires >1 alive worm on the team
102
+ if state.game_phase >= GAME_PHASE_NORMAL_MIN
103
  && weapon_id == Weapon::SelectWorm as u32
104
  && count_alive_worms(team_index, arena) == 0
105
  {
 
463
  /// Bit position comes from weapon entry's fire_params.
464
  /// In game_version > 0x1C: toggles (set/clear). Otherwise: always sets.
465
  unsafe fn fire_skip_go(worm: *const CTaskWorm, entry: *const WeaponEntry) {
 
466
  let ddgame = CTask::ddgame_raw(worm as *const CTask);
467
  let game_version = (*(*ddgame).game_info).game_version;
468
  let team_index = (*worm).team_index as usize;
 
486
  /// Sends message 0x29 (TaskMessage::Freeze) to CTaskTurnGame, then increments
487
  /// WormEntry.turn_action_counter_Maybe by 14 (0x0E).
488
  unsafe fn fire_freeze(worm: *mut CTaskWorm) {
 
 
489
  fire_send_team_message(worm, 0x29);
490
 
491
  let ddgame = CTask::ddgame_raw(worm as *const CTask);
 
509
  /// - version >= 5 && worm state == 0x78 && version < 8: call vtable
510
  /// - otherwise: skip
511
  unsafe fn fire_mail_mine_mole(worm: *mut CTaskWorm) {
 
512
  let ddgame = CTask::ddgame_raw(worm as *const CTask);
513
  let version = (*ddgame).version_flag_4;
514
  let worm_state = (*worm).state();
 
983
  girder_visual(landscape, girder_x >> 16, girder_y >> 16, sprite1, sprite2);
984
 
985
  // Increment WormEntry counters
986
+ let arena = TeamArenaRef::from_ptr(&raw mut (*ddgame).team_arena);
987
  let team_index = (*worm).team_index as usize;
988
  let worm_index = (*worm).worm_index as usize;
989
  let entry = arena.team_worm_mut(team_index, worm_index);
crates/openwa-core/src/render/sprite.rs CHANGED
@@ -795,8 +795,9 @@ pub struct Sprite {
795
  pub secondary_frame_count: u16,
796
  /// 0x32: Padding
797
  pub _pad_32: u16,
798
- /// 0x34: DisplayGfx bitmap handle (vtable 0x664144 set by ConstructSprite)
799
- pub display_gfx: *mut u8,
 
800
  /// 0x38-0x5F: Unknown fields
801
  pub _unknown_38: [u8; 0x28],
802
  /// 0x60: Pointer to raw frame header data
 
795
  pub secondary_frame_count: u16,
796
  /// 0x32: Padding
797
  pub _pad_32: u16,
798
+ /// 0x34: Embedded BitGrid vtable pointer (0x664144, set by ConstructSprite).
799
+ /// Part of a BitGrid sub-object within the Sprite.
800
+ pub bitgrid_vtable: *mut u8,
801
  /// 0x38-0x5F: Unknown fields
802
  pub _unknown_38: [u8; 0x28],
803
  /// 0x60: Pointer to raw frame header data
crates/openwa-core/src/task/bit_grid.rs DELETED
@@ -1,67 +0,0 @@
1
- //! BitGrid initialization β€” bitfield grid buffer for spatial queries.
2
-
3
- use crate::rebase::rb;
4
- use crate::wa_alloc::wa_malloc;
5
- use crate::FieldRegistry;
6
-
7
- /// Bitfield grid buffer used for spatial collision/visibility queries.
8
- /// Vtable at 0x6640EC (shared with DisplayGfx base).
9
- /// Size: 0x2C bytes (11 u32 fields).
10
- #[derive(FieldRegistry)]
11
- #[repr(C)]
12
- pub struct BitGrid {
13
- pub vtable: u32,
14
- pub _unused_04: u32,
15
- pub data: *mut u8,
16
- pub cells_per_unit: u32,
17
- pub row_stride: u32,
18
- pub width: u32,
19
- pub height: u32,
20
- pub _unused_1c: u32,
21
- pub _unused_20: u32,
22
- pub width_dup: u32,
23
- pub height_dup: u32,
24
- }
25
-
26
- const _: () = assert!(core::mem::size_of::<BitGrid>() == 0x2C);
27
-
28
- /// Pure Rust implementation of BitGrid__Init (0x4F6370).
29
- ///
30
- /// Allocates a bit-per-cell grid buffer. `cells_per_unit` is typically 1.
31
- /// `width` and `height` are pixel dimensions. The buffer is a row-major
32
- /// bitfield with rows aligned to 4 bytes.
33
- ///
34
- /// # Safety
35
- /// `object` must point to a zero-filled allocation of at least 0x2C bytes.
36
- pub unsafe fn bit_grid_init(object: *mut u8, cells_per_unit: u32, width: u32, height: u32) {
37
- let bits = cells_per_unit.wrapping_mul(width).wrapping_add(7) as i32;
38
- let row_stride = ((bits >> 3) + 3) & !3;
39
- let total_size = row_stride as u32 * height;
40
-
41
- let alloc_size = ((total_size + 3) & !3) + 0x20;
42
- let buffer = wa_malloc(alloc_size);
43
-
44
- if buffer.is_null() {
45
- return;
46
- }
47
- if total_size as usize > alloc_size as usize {
48
- return;
49
- }
50
-
51
- // Memset twice (matches original β€” likely redundant but exact match)
52
- core::ptr::write_bytes(buffer, 0, total_size as usize);
53
- core::ptr::write_bytes(buffer, 0, total_size as usize);
54
-
55
- let tsm = &mut *(object as *mut BitGrid);
56
- tsm.vtable = rb(0x6640EC);
57
- tsm._unused_04 = 0;
58
- tsm.data = buffer;
59
- tsm.cells_per_unit = cells_per_unit;
60
- tsm.row_stride = row_stride as u32;
61
- tsm.width = width;
62
- tsm.height = height;
63
- tsm._unused_1c = 0;
64
- tsm._unused_20 = 0;
65
- tsm.width_dup = width;
66
- tsm.height_dup = height;
67
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
crates/openwa-core/src/task/mod.rs CHANGED
@@ -1,6 +1,5 @@
1
  pub mod arrow;
2
  pub mod base;
3
- pub mod bit_grid;
4
  pub mod cloud;
5
  pub mod filter;
6
  pub mod fire;
 
1
  pub mod arrow;
2
  pub mod base;
 
3
  pub mod cloud;
4
  pub mod filter;
5
  pub mod fire;
crates/openwa-core/src/wa_alloc.rs CHANGED
@@ -13,6 +13,25 @@ pub unsafe fn wa_malloc(size: u32) -> *mut u8 {
13
  f(size)
14
  }
15
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  /// Allocate `size` bytes from WA's CRT heap and zero-initialize them.
17
  ///
18
  /// Equivalent to `wa_malloc(size)` followed by `write_bytes(ptr, 0, size)`.
 
13
  f(size)
14
  }
15
 
16
+ /// Allocate space for a `T` on WA's heap and return a pointer to it.
17
+ ///
18
+ /// Equivalent to `wa_malloc(size_of::<T>())` with a cast. The allocation is uninitialized.
19
+ ///
20
+ /// # Safety
21
+ /// Must only be called from within the WA.exe process (game or injected DLL).
22
+ pub unsafe fn wa_malloc_struct<T>() -> *mut T {
23
+ wa_malloc(core::mem::size_of::<T>() as u32) as *mut T
24
+ }
25
+
26
+ /// Allocate space for a `T` on WA's heap, zero-initialize it, and return a pointer to it.
27
+ /// Equivalent to `wa_malloc_zeroed(size_of::<T>())` with a cast. The allocation is zero-initialized.
28
+ ///
29
+ /// # Safety
30
+ /// Must only be called from within the WA.exe process (game or injected DLL).
31
+ pub unsafe fn wa_malloc_struct_zeroed<T>() -> *mut T {
32
+ wa_malloc_zeroed(core::mem::size_of::<T>() as u32) as *mut T
33
+ }
34
+
35
  /// Allocate `size` bytes from WA's CRT heap and zero-initialize them.
36
  ///
37
  /// Equivalent to `wa_malloc(size)` followed by `write_bytes(ptr, 0, size)`.
crates/openwa-dll/src/replacements/ddgame_init.rs CHANGED
@@ -14,15 +14,15 @@
14
  use crate::hook;
15
  use core::ffi::c_char;
16
  use openwa_core::address::va;
17
- use openwa_core::engine::ddgame::{
18
- bit_grid_init, ddgame_init_fields, ddgame_init_render_indices, display_layer_color_init,
19
- gfx_dir_find_entry, gfx_dir_load_dir, gfx_resource_create, DDGame,
20
- };
21
  use openwa_core::engine::game_state_init::{
22
  check_weapon_avail, init_alliance_data, init_landscape_flags, init_team_scoring,
23
  init_turn_state, is_super_weapon, ring_buffer_init, sprite_gfx_table_init,
24
  };
25
- use openwa_core::engine::DDGameWrapper;
 
 
 
26
 
27
  // ─── DDGame__InitFields (0x526120) ──────────────────────────────────────────
28
 
@@ -54,13 +54,18 @@ extern "cdecl" fn impl_init_render_indices(base: u32) -> u32 {
54
 
55
  // ─── BitGrid__Init (0x4F6370) ──────────────────────────────────────
56
 
57
- extern "cdecl" fn impl_tsm_init(object: u32, param1: u32, height: u32, width: u32) -> u32 {
58
- unsafe { bit_grid_init(object as *mut u8, param1, width, height) }
59
- object // Original: MOV EAX, ESI; RET 0x4
 
 
 
 
 
60
  }
61
 
62
  #[unsafe(naked)]
63
- unsafe extern "C" fn tsm_init_trampoline() {
64
  core::arch::naked_asm!(
65
  "push edx",
66
  "push [esp+8]", // width (stack param)
@@ -71,7 +76,7 @@ unsafe extern "C" fn tsm_init_trampoline() {
71
  "add esp, 16",
72
  "pop edx",
73
  "ret 0x4",
74
- impl_fn = sym impl_tsm_init,
75
  );
76
  }
77
 
@@ -180,7 +185,7 @@ pub fn install() -> Result<(), String> {
180
  hook::install(
181
  "BitGrid__Init",
182
  va::BIT_GRID_INIT,
183
- tsm_init_trampoline as *const (),
184
  )?;
185
 
186
  hook::install(
 
14
  use crate::hook;
15
  use core::ffi::c_char;
16
  use openwa_core::address::va;
17
+ use openwa_core::display::BitGrid;
 
 
 
18
  use openwa_core::engine::game_state_init::{
19
  check_weapon_avail, init_alliance_data, init_landscape_flags, init_team_scoring,
20
  init_turn_state, is_super_weapon, ring_buffer_init, sprite_gfx_table_init,
21
  };
22
+ use openwa_core::engine::{
23
+ ddgame_init_fields, ddgame_init_render_indices, display_layer_color_init, DDGame, DDGameWrapper,
24
+ };
25
+ use openwa_core::render::gfx_dir::{gfx_dir_find_entry, gfx_dir_load_dir, gfx_resource_create};
26
 
27
  // ─── DDGame__InitFields (0x526120) ──────────────────────────────────────────
28
 
 
54
 
55
  // ─── BitGrid__Init (0x4F6370) ──────────────────────────────────────
56
 
57
+ extern "cdecl" fn impl_bitgrid_init(
58
+ bit_grid: *mut BitGrid,
59
+ cells_per_unit: u32,
60
+ height: u32,
61
+ width: u32,
62
+ ) -> *mut BitGrid {
63
+ unsafe { BitGrid::init(bit_grid, cells_per_unit, width, height) }
64
+ bit_grid // Original: MOV EAX, ESI; RET 0x4
65
  }
66
 
67
  #[unsafe(naked)]
68
+ unsafe extern "C" fn bitgrid_init_trampoline() {
69
  core::arch::naked_asm!(
70
  "push edx",
71
  "push [esp+8]", // width (stack param)
 
76
  "add esp, 16",
77
  "pop edx",
78
  "ret 0x4",
79
+ impl_fn = sym impl_bitgrid_init,
80
  );
81
  }
82
 
 
185
  hook::install(
186
  "BitGrid__Init",
187
  va::BIT_GRID_INIT,
188
+ bitgrid_init_trampoline as *const (),
189
  )?;
190
 
191
  hook::install(
crates/openwa-dll/src/replacements/game_session.rs CHANGED
@@ -17,7 +17,8 @@ use crate::log_line;
17
  use openwa_core::address::va;
18
  use openwa_core::audio::DSSound;
19
  use openwa_core::display::{DDDisplay, Palette};
20
- use openwa_core::engine::ddgame::{create_ddgame, init_constructor_addrs};
 
21
  use openwa_core::engine::{DDGameWrapper, GameInfo, GameSession};
22
  use openwa_core::rebase::rb;
23
 
 
17
  use openwa_core::address::va;
18
  use openwa_core::audio::DSSound;
19
  use openwa_core::display::{DDDisplay, Palette};
20
+ use openwa_core::engine::create_ddgame;
21
+ use openwa_core::engine::init_constructor_addrs;
22
  use openwa_core::engine::{DDGameWrapper, GameInfo, GameSession};
23
  use openwa_core::rebase::rb;
24
 
crates/openwa-dll/src/replacements/replay_test.rs CHANGED
@@ -13,9 +13,10 @@
13
  use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU32, Ordering};
14
 
15
  use crate::log_line;
16
- use openwa_core::engine::ddgame::{offsets, TeamArenaRef};
17
  use openwa_core::engine::game_session;
18
  use openwa_core::engine::DDGame;
 
19
 
20
  // ---------------------------------------------------------------------------
21
  // Fast-forward
 
13
  use std::sync::atomic::{AtomicBool, AtomicI32, AtomicU32, Ordering};
14
 
15
  use crate::log_line;
16
+ use openwa_core::engine::ddgame::offsets;
17
  use openwa_core::engine::game_session;
18
  use openwa_core::engine::DDGame;
19
+ use openwa_core::engine::TeamArenaRef;
20
 
21
  // ---------------------------------------------------------------------------
22
  // Fast-forward
crates/openwa-dll/src/replacements/sprite.rs CHANGED
@@ -24,9 +24,9 @@ unsafe extern "cdecl" fn construct_sprite_impl(sprite: *mut Sprite, context: *mu
24
 
25
  (*sprite).vtable = rb(va::SPRITE_VTABLE) as *mut u8;
26
  (*sprite).context_ptr = context;
27
- (*sprite).display_gfx = rb(va::BIT_GRID_DISPLAY_VTABLE) as *mut u8;
28
 
29
- // DisplayGfx sub-object fields (within _unknown_38)
30
  let p = sprite as *mut u8;
31
  *(p.add(0x38) as *mut u32) = 1;
32
  *(p.add(0x40) as *mut u32) = 8;
 
24
 
25
  (*sprite).vtable = rb(va::SPRITE_VTABLE) as *mut u8;
26
  (*sprite).context_ptr = context;
27
+ (*sprite).bitgrid_vtable = rb(va::BIT_GRID_DISPLAY_VTABLE) as *mut u8;
28
 
29
+ // BitGrid sub-object fields (within _unknown_38)
30
  let p = sprite as *mut u8;
31
  *(p.add(0x38) as *mut u32) = 1;
32
  *(p.add(0x40) as *mut u32) = 8;
crates/openwa-dll/src/replacements/team.rs CHANGED
@@ -3,9 +3,8 @@
3
  //! Thin hook shim β€” all game logic lives in `openwa_core::engine::team_ops`.
4
  //! This file contains only usercall trampolines and hook installation.
5
 
6
- use openwa_core::address::va;
7
- use openwa_core::engine::ddgame::TeamArenaRef;
8
  use openwa_core::engine::team_ops;
 
9
 
10
  use crate::hook::{self, usercall_trampoline};
11
 
 
3
  //! Thin hook shim β€” all game logic lives in `openwa_core::engine::team_ops`.
4
  //! This file contains only usercall trampolines and hook installation.
5
 
 
 
6
  use openwa_core::engine::team_ops;
7
+ use openwa_core::{address::va, engine::TeamArenaRef};
8
 
9
  use crate::hook::{self, usercall_trampoline};
10
 
crates/openwa-dll/src/replacements/weapon.rs CHANGED
@@ -12,12 +12,11 @@
12
 
13
  use core::sync::atomic::{AtomicU32, Ordering};
14
 
15
- use openwa_core::address::va;
16
- use openwa_core::engine::ddgame::TeamArenaRef;
17
  use openwa_core::game::weapon::WeaponFireParams;
18
  use openwa_core::game::weapon_fire;
19
  use openwa_core::log::log_line;
20
  use openwa_core::task::worm::CTaskWorm;
 
21
 
22
  use crate::hook::{self, usercall_trampoline};
23
 
 
12
 
13
  use core::sync::atomic::{AtomicU32, Ordering};
14
 
 
 
15
  use openwa_core::game::weapon::WeaponFireParams;
16
  use openwa_core::game::weapon_fire;
17
  use openwa_core::log::log_line;
18
  use openwa_core::task::worm::CTaskWorm;
19
+ use openwa_core::{address::va, engine::TeamArenaRef};
20
 
21
  use crate::hook::{self, usercall_trampoline};
22
 
crates/openwa-dll/src/snapshot.rs CHANGED
@@ -19,6 +19,8 @@ use openwa_core::task::{CTask, CTaskBfsIter, CTaskMissile, CTaskWorm};
19
  /// Must be called from the DLL while the game is paused (frame breakpoint).
20
  #[cfg(target_arch = "x86")]
21
  pub unsafe fn capture() -> String {
 
 
22
  let mut out = String::with_capacity(128 * 1024);
23
 
24
  let session_ptr = rb(va::G_GAME_SESSION) as *const *mut GameSession;
@@ -60,7 +62,7 @@ pub unsafe fn capture() -> String {
60
  // ── Team blocks + worm entries ──
61
  let team_count = (*ddgame).team_arena.team_count as usize;
62
  let _ = writeln!(out, "[Teams] count={}", team_count);
63
- let arena = openwa_core::engine::ddgame::TeamArenaRef::from_ptr(&raw mut (*ddgame).team_arena);
64
  let blocks = arena.blocks();
65
  for t in 0..team_count {
66
  let header = arena.team_header(t);
 
19
  /// Must be called from the DLL while the game is paused (frame breakpoint).
20
  #[cfg(target_arch = "x86")]
21
  pub unsafe fn capture() -> String {
22
+ use openwa_core::engine::TeamArenaRef;
23
+
24
  let mut out = String::with_capacity(128 * 1024);
25
 
26
  let session_ptr = rb(va::G_GAME_SESSION) as *const *mut GameSession;
 
62
  // ── Team blocks + worm entries ──
63
  let team_count = (*ddgame).team_arena.team_count as usize;
64
  let _ = writeln!(out, "[Teams] count={}", team_count);
65
+ let arena = TeamArenaRef::from_ptr(&raw mut (*ddgame).team_arena);
66
  let blocks = arena.blocks();
67
  for t in 0..team_count {
68
  let header = arena.team_header(t);