feat: add Task trait for ergonomic CTask base access
Browse filesAdd unsafe trait Task with default methods:
- task() / task_mut() — reference to CTask base
- as_task_ptr() / as_task_ptr_mut() — raw pointers
- ddgame() — direct DDGame pointer access
Implemented for all CTask subclasses (CTaskWorm, CTaskTeam,
CTaskMissile, CTaskArrow, CTaskMine, etc.) via unsafe impls.
Replace all .base.base chains in weapon.rs with trait methods:
- (*worm).base.base.ddgame → (*worm).ddgame()
- &(*worm).base.base as *const _ as *const CTask → (*worm).as_task_ptr()
- SharedDataTable::from_task(task as *const _ as ...) → from_task((*worm).as_task_ptr())
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
crates/openwa-core/src/task/base.rs
CHANGED
|
@@ -141,6 +141,44 @@ pub struct CTask<V: 'static = *const core::ffi::c_void> {
|
|
| 141 |
|
| 142 |
const _: () = assert!(core::mem::size_of::<CTask>() == 0x30);
|
| 143 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 144 |
// ---------------------------------------------------------------------------
|
| 145 |
// Shared-data entity registry
|
| 146 |
// ---------------------------------------------------------------------------
|
|
|
|
| 141 |
|
| 142 |
const _: () = assert!(core::mem::size_of::<CTask>() == 0x30);
|
| 143 |
|
| 144 |
+
/// Trait for all task types in the CTask hierarchy.
|
| 145 |
+
///
|
| 146 |
+
/// Provides safe access to the underlying CTask fields regardless of
|
| 147 |
+
/// inheritance depth. Avoids repetitive `.base.base` chains.
|
| 148 |
+
///
|
| 149 |
+
/// # Safety
|
| 150 |
+
/// Implementors must be `#[repr(C)]` structs where a `CTask` (with any
|
| 151 |
+
/// vtable type parameter) is at offset 0.
|
| 152 |
+
pub unsafe trait Task {
|
| 153 |
+
/// Get a shared reference to the underlying CTask (default vtable type).
|
| 154 |
+
fn task(&self) -> &CTask {
|
| 155 |
+
unsafe { &*(self as *const Self as *const CTask) }
|
| 156 |
+
}
|
| 157 |
+
|
| 158 |
+
/// Get a mutable reference to the underlying CTask.
|
| 159 |
+
fn task_mut(&mut self) -> &mut CTask {
|
| 160 |
+
unsafe { &mut *(self as *mut Self as *mut CTask) }
|
| 161 |
+
}
|
| 162 |
+
|
| 163 |
+
/// Get a raw const pointer to the CTask base.
|
| 164 |
+
fn as_task_ptr(&self) -> *const CTask {
|
| 165 |
+
self as *const Self as *const CTask
|
| 166 |
+
}
|
| 167 |
+
|
| 168 |
+
/// Get a raw mutable pointer to the CTask base.
|
| 169 |
+
fn as_task_ptr_mut(&mut self) -> *mut CTask {
|
| 170 |
+
self as *mut Self as *mut CTask
|
| 171 |
+
}
|
| 172 |
+
|
| 173 |
+
/// Get the DDGame pointer from the CTask base.
|
| 174 |
+
fn ddgame(&self) -> *mut DDGame {
|
| 175 |
+
self.task().ddgame
|
| 176 |
+
}
|
| 177 |
+
}
|
| 178 |
+
|
| 179 |
+
// Blanket impl for any CTask<V>
|
| 180 |
+
unsafe impl<V: 'static> Task for CTask<V> {}
|
| 181 |
+
|
| 182 |
// ---------------------------------------------------------------------------
|
| 183 |
// Shared-data entity registry
|
| 184 |
// ---------------------------------------------------------------------------
|
crates/openwa-core/src/task/mod.rs
CHANGED
|
@@ -14,7 +14,7 @@ pub mod turn_game;
|
|
| 14 |
pub mod worm;
|
| 15 |
|
| 16 |
pub use arrow::{CTaskArrow, CTaskArrowVTable};
|
| 17 |
-
pub use base::{CTask, CTaskBfsIter, SharedDataIter, SharedDataNode, SharedDataTable};
|
| 18 |
pub use cloud::{CTaskCloud, CTaskCloudVTable};
|
| 19 |
pub use filter::{CTaskFilter, CTaskFilterVTable};
|
| 20 |
pub use fire::{CTaskFire, CTaskFireVTable};
|
|
@@ -26,3 +26,18 @@ pub use supply_crate::{CTaskCrate, CTaskCrateVTable};
|
|
| 26 |
pub use team::{CTaskTeam, CTaskTeamVTable};
|
| 27 |
pub use turn_game::{CTaskTurnGame, CTaskTurnGameVTable, TurnGameCtx};
|
| 28 |
pub use worm::{CTaskWorm, CTaskWormVTable};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 14 |
pub mod worm;
|
| 15 |
|
| 16 |
pub use arrow::{CTaskArrow, CTaskArrowVTable};
|
| 17 |
+
pub use base::{CTask, CTaskBfsIter, SharedDataIter, SharedDataNode, SharedDataTable, Task};
|
| 18 |
pub use cloud::{CTaskCloud, CTaskCloudVTable};
|
| 19 |
pub use filter::{CTaskFilter, CTaskFilterVTable};
|
| 20 |
pub use fire::{CTaskFire, CTaskFireVTable};
|
|
|
|
| 26 |
pub use team::{CTaskTeam, CTaskTeamVTable};
|
| 27 |
pub use turn_game::{CTaskTurnGame, CTaskTurnGameVTable, TurnGameCtx};
|
| 28 |
pub use worm::{CTaskWorm, CTaskWormVTable};
|
| 29 |
+
|
| 30 |
+
// Task trait impls — safe access to CTask base regardless of inheritance depth.
|
| 31 |
+
// CTask<V> impl is in base.rs (blanket impl).
|
| 32 |
+
unsafe impl<V: 'static> Task for CGameTask<V> {}
|
| 33 |
+
unsafe impl Task for CTaskTeam {}
|
| 34 |
+
unsafe impl Task for CTaskTurnGame {}
|
| 35 |
+
unsafe impl Task for CTaskFilter {}
|
| 36 |
+
unsafe impl Task for CTaskCloud {}
|
| 37 |
+
unsafe impl Task for CTaskFire {}
|
| 38 |
+
unsafe impl Task for CTaskWorm {}
|
| 39 |
+
unsafe impl Task for CTaskMissile {}
|
| 40 |
+
unsafe impl Task for CTaskArrow {}
|
| 41 |
+
unsafe impl Task for CTaskMine {}
|
| 42 |
+
unsafe impl Task for CTaskOilDrum {}
|
| 43 |
+
unsafe impl Task for CTaskCrate {}
|
crates/openwa-wormkit/src/replacements/weapon.rs
CHANGED
|
@@ -20,6 +20,7 @@ use openwa_core::engine::ddgame::{self, TeamArenaRef};
|
|
| 20 |
use openwa_core::game::weapon::{WeaponEntry, WeaponFireParams};
|
| 21 |
use openwa_core::game::Weapon;
|
| 22 |
use openwa_core::log::log_line;
|
|
|
|
| 23 |
use openwa_core::task::worm::CTaskWorm;
|
| 24 |
|
| 25 |
use crate::hook::{self, usercall_trampoline};
|
|
@@ -406,10 +407,9 @@ unsafe fn fire_weapon_special(
|
|
| 406 |
///
|
| 407 |
/// Returns null if not found.
|
| 408 |
unsafe fn lookup_team_task(worm: *const CTaskWorm) -> *mut openwa_core::task::CTaskTeam {
|
| 409 |
-
use openwa_core::task::SharedDataTable;
|
| 410 |
|
| 411 |
-
let
|
| 412 |
-
let table = SharedDataTable::from_task(task);
|
| 413 |
// CTaskTeam is registered with key (0, 0x14) in SharedData
|
| 414 |
table.lookup(0, 0x14) as *mut openwa_core::task::CTaskTeam
|
| 415 |
}
|
|
@@ -432,7 +432,7 @@ unsafe fn fire_send_team_message(worm: *mut CTaskWorm, msg_type: u32) {
|
|
| 432 |
buf[0..4].copy_from_slice(&team_index.to_ne_bytes());
|
| 433 |
|
| 434 |
(*team).handle_message(
|
| 435 |
-
|
| 436 |
msg_type,
|
| 437 |
4,
|
| 438 |
buf.as_ptr(),
|
|
@@ -453,7 +453,7 @@ unsafe fn fire_select_worm(worm: *mut CTaskWorm) {
|
|
| 453 |
buf[4..8].copy_from_slice(&(*worm).team_index.to_ne_bytes());
|
| 454 |
|
| 455 |
(*team).handle_message(
|
| 456 |
-
|
| 457 |
0x5D,
|
| 458 |
0x408,
|
| 459 |
buf.as_ptr(),
|
|
@@ -467,9 +467,7 @@ unsafe fn fire_select_worm(worm: *mut CTaskWorm) {
|
|
| 467 |
/// In game_version > 0x1C: toggles (set/clear). Otherwise: always sets.
|
| 468 |
unsafe fn fire_skip_go(worm: *const CTaskWorm, entry: *const WeaponEntry) {
|
| 469 |
use openwa_core::engine::ddgame::TeamArenaRef;
|
| 470 |
-
|
| 471 |
-
|
| 472 |
-
let ddgame = (*worm).base.base.ddgame as *mut DDGame;
|
| 473 |
let game_version = (*(*ddgame).game_info).game_version;
|
| 474 |
let team_index = (*worm).team_index as usize;
|
| 475 |
|
|
@@ -496,7 +494,7 @@ unsafe fn fire_surrender(worm: *mut CTaskWorm) {
|
|
| 496 |
|
| 497 |
fire_send_team_message(worm, 0x29);
|
| 498 |
|
| 499 |
-
let ddgame = (*worm).
|
| 500 |
let arena = TeamArenaRef::from_ptr(&raw mut (*ddgame).team_arena);
|
| 501 |
let team_index = (*worm).team_index as usize;
|
| 502 |
let worm_index = (*worm).worm_index as usize;
|
|
@@ -518,9 +516,7 @@ unsafe fn fire_surrender(worm: *mut CTaskWorm) {
|
|
| 518 |
/// - otherwise: skip
|
| 519 |
unsafe fn fire_mail_mine_mole(worm: *mut CTaskWorm) {
|
| 520 |
use openwa_core::engine::ddgame::TeamArenaRef;
|
| 521 |
-
|
| 522 |
-
|
| 523 |
-
let ddgame = (*worm).base.base.ddgame as *mut DDGame;
|
| 524 |
let version = *((ddgame as *const u8).add(0x7E40));
|
| 525 |
let worm_state = (*worm).state();
|
| 526 |
|
|
@@ -713,11 +709,10 @@ unsafe extern "cdecl" fn create_weapon_projectile_impl(
|
|
| 713 |
worm: *mut CTaskWorm, fire_params: *const WeaponFireParams, local_struct: u32,
|
| 714 |
) {
|
| 715 |
use openwa_core::rebase::rb;
|
| 716 |
-
use openwa_core::task::SharedDataTable;
|
| 717 |
use openwa_core::wa_alloc::wa_malloc;
|
| 718 |
|
| 719 |
-
let
|
| 720 |
-
let ddgame = &mut *task.ddgame;
|
| 721 |
|
| 722 |
// Pool capacity check: pool_count + 7 must be <= 700
|
| 723 |
if ddgame.object_pool_count + 7 > 700 {
|
|
@@ -726,7 +721,7 @@ unsafe extern "cdecl" fn create_weapon_projectile_impl(
|
|
| 726 |
}
|
| 727 |
|
| 728 |
// Look up parent CTaskTurnGame via SharedData (key_esi=0, key_edi=0x19)
|
| 729 |
-
let table = SharedDataTable::from_task(
|
| 730 |
let parent = table.lookup(0, 0x19);
|
| 731 |
|
| 732 |
// Allocate CTaskMissile (0x40C bytes)
|
|
@@ -807,7 +802,7 @@ unsafe extern "cdecl" fn projectile_fire_impl(
|
|
| 807 |
let sin_table = rb(va::SIN_TABLE) as *const i32;
|
| 808 |
let cos_table = sin_table.add(256); // cos = sin offset by 256 entries (quarter turn)
|
| 809 |
|
| 810 |
-
let ddgame = &mut *(*worm).
|
| 811 |
|
| 812 |
for _i in 0..shot_count {
|
| 813 |
// Advance game RNG (same LCG as ADVANCE_GAME_RNG at 0x53F320)
|
|
@@ -887,11 +882,10 @@ unsafe extern "cdecl" fn create_arrow_impl(
|
|
| 887 |
worm: *mut CTaskWorm, fire_params: *const WeaponFireParams, local_struct: u32,
|
| 888 |
) {
|
| 889 |
use openwa_core::rebase::rb;
|
| 890 |
-
use openwa_core::task::SharedDataTable;
|
| 891 |
use openwa_core::wa_alloc::wa_malloc;
|
| 892 |
|
| 893 |
-
let
|
| 894 |
-
let ddgame = &mut *task.ddgame;
|
| 895 |
|
| 896 |
// Pool capacity check: pool_count + 2 must be <= 700
|
| 897 |
if ddgame.object_pool_count + 2 > 700 {
|
|
@@ -900,7 +894,7 @@ unsafe extern "cdecl" fn create_arrow_impl(
|
|
| 900 |
}
|
| 901 |
|
| 902 |
// Look up parent CTaskTurnGame via SharedData (key 0, 0x19)
|
| 903 |
-
let table = SharedDataTable::from_task(
|
| 904 |
let parent = table.lookup(0, 0x19);
|
| 905 |
|
| 906 |
// Allocate CTaskArrow (0x168 bytes)
|
|
|
|
| 20 |
use openwa_core::game::weapon::{WeaponEntry, WeaponFireParams};
|
| 21 |
use openwa_core::game::Weapon;
|
| 22 |
use openwa_core::log::log_line;
|
| 23 |
+
use openwa_core::task::Task;
|
| 24 |
use openwa_core::task::worm::CTaskWorm;
|
| 25 |
|
| 26 |
use crate::hook::{self, usercall_trampoline};
|
|
|
|
| 407 |
///
|
| 408 |
/// Returns null if not found.
|
| 409 |
unsafe fn lookup_team_task(worm: *const CTaskWorm) -> *mut openwa_core::task::CTaskTeam {
|
| 410 |
+
use openwa_core::task::{SharedDataTable, Task};
|
| 411 |
|
| 412 |
+
let table = SharedDataTable::from_task((*worm).as_task_ptr());
|
|
|
|
| 413 |
// CTaskTeam is registered with key (0, 0x14) in SharedData
|
| 414 |
table.lookup(0, 0x14) as *mut openwa_core::task::CTaskTeam
|
| 415 |
}
|
|
|
|
| 432 |
buf[0..4].copy_from_slice(&team_index.to_ne_bytes());
|
| 433 |
|
| 434 |
(*team).handle_message(
|
| 435 |
+
(*worm).as_task_ptr_mut(),
|
| 436 |
msg_type,
|
| 437 |
4,
|
| 438 |
buf.as_ptr(),
|
|
|
|
| 453 |
buf[4..8].copy_from_slice(&(*worm).team_index.to_ne_bytes());
|
| 454 |
|
| 455 |
(*team).handle_message(
|
| 456 |
+
(*worm).as_task_ptr_mut(),
|
| 457 |
0x5D,
|
| 458 |
0x408,
|
| 459 |
buf.as_ptr(),
|
|
|
|
| 467 |
/// In game_version > 0x1C: toggles (set/clear). Otherwise: always sets.
|
| 468 |
unsafe fn fire_skip_go(worm: *const CTaskWorm, entry: *const WeaponEntry) {
|
| 469 |
use openwa_core::engine::ddgame::TeamArenaRef;
|
| 470 |
+
let ddgame = (*worm).ddgame();
|
|
|
|
|
|
|
| 471 |
let game_version = (*(*ddgame).game_info).game_version;
|
| 472 |
let team_index = (*worm).team_index as usize;
|
| 473 |
|
|
|
|
| 494 |
|
| 495 |
fire_send_team_message(worm, 0x29);
|
| 496 |
|
| 497 |
+
let ddgame = (*worm).ddgame();
|
| 498 |
let arena = TeamArenaRef::from_ptr(&raw mut (*ddgame).team_arena);
|
| 499 |
let team_index = (*worm).team_index as usize;
|
| 500 |
let worm_index = (*worm).worm_index as usize;
|
|
|
|
| 516 |
/// - otherwise: skip
|
| 517 |
unsafe fn fire_mail_mine_mole(worm: *mut CTaskWorm) {
|
| 518 |
use openwa_core::engine::ddgame::TeamArenaRef;
|
| 519 |
+
let ddgame = (*worm).ddgame();
|
|
|
|
|
|
|
| 520 |
let version = *((ddgame as *const u8).add(0x7E40));
|
| 521 |
let worm_state = (*worm).state();
|
| 522 |
|
|
|
|
| 709 |
worm: *mut CTaskWorm, fire_params: *const WeaponFireParams, local_struct: u32,
|
| 710 |
) {
|
| 711 |
use openwa_core::rebase::rb;
|
| 712 |
+
use openwa_core::task::{SharedDataTable, Task};
|
| 713 |
use openwa_core::wa_alloc::wa_malloc;
|
| 714 |
|
| 715 |
+
let ddgame = &mut *(*worm).ddgame();
|
|
|
|
| 716 |
|
| 717 |
// Pool capacity check: pool_count + 7 must be <= 700
|
| 718 |
if ddgame.object_pool_count + 7 > 700 {
|
|
|
|
| 721 |
}
|
| 722 |
|
| 723 |
// Look up parent CTaskTurnGame via SharedData (key_esi=0, key_edi=0x19)
|
| 724 |
+
let table = SharedDataTable::from_task((*worm).as_task_ptr());
|
| 725 |
let parent = table.lookup(0, 0x19);
|
| 726 |
|
| 727 |
// Allocate CTaskMissile (0x40C bytes)
|
|
|
|
| 802 |
let sin_table = rb(va::SIN_TABLE) as *const i32;
|
| 803 |
let cos_table = sin_table.add(256); // cos = sin offset by 256 entries (quarter turn)
|
| 804 |
|
| 805 |
+
let ddgame = &mut *(*worm).ddgame();
|
| 806 |
|
| 807 |
for _i in 0..shot_count {
|
| 808 |
// Advance game RNG (same LCG as ADVANCE_GAME_RNG at 0x53F320)
|
|
|
|
| 882 |
worm: *mut CTaskWorm, fire_params: *const WeaponFireParams, local_struct: u32,
|
| 883 |
) {
|
| 884 |
use openwa_core::rebase::rb;
|
| 885 |
+
use openwa_core::task::{SharedDataTable, Task};
|
| 886 |
use openwa_core::wa_alloc::wa_malloc;
|
| 887 |
|
| 888 |
+
let ddgame = &mut *(*worm).ddgame();
|
|
|
|
| 889 |
|
| 890 |
// Pool capacity check: pool_count + 2 must be <= 700
|
| 891 |
if ddgame.object_pool_count + 2 > 700 {
|
|
|
|
| 894 |
}
|
| 895 |
|
| 896 |
// Look up parent CTaskTurnGame via SharedData (key 0, 0x19)
|
| 897 |
+
let table = SharedDataTable::from_task((*worm).as_task_ptr());
|
| 898 |
let parent = table.lookup(0, 0x19);
|
| 899 |
|
| 900 |
// Allocate CTaskArrow (0x168 bytes)
|