feat: add CTaskCrate struct, raw field debug UI, new task fields
Browse filesAdd CTaskCrate (0x4B0 bytes, vtable 0x664298) with slot_id, timer,
scheme_data block, and sequence_ref. 17 vtable methods labelled in
Ghidra.
Add raw DWORD inspector to egui debug UI for CTaskMine, CTaskOilDrum,
CTaskCrate, CTaskWorm, and CTaskMissile — shows every field with
offset, hex/decimal value, and name. All sections collapsed by default.
New fields discovered via runtime observation:
- CGameTask.angle (+0x8C): rotation angle, fixed-point 16.16
- CTaskMine.fuse_timer: corrected from +0x11C to +0x118
- CTaskCrate+0x24C: health crate healing amount
- CTaskWorm: selected_weapon (+0x170), facing_direction copies
(+0x1A8/+0x1AC/+0x334), aim_angle (+0x24C), show_cursor (+0x268),
stationary_frames (+0x164), movement_streak (+0x1EC),
facing_direction (+0x3DC), input keys (+0x3E4..+0x3F0)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- crates/openwa-core/src/address.rs +2 -0
- crates/openwa-core/src/task/game_task.rs +2 -2
- crates/openwa-core/src/task/mine_oil_drum.rs +5 -3
- crates/openwa-core/src/task/mod.rs +2 -0
- crates/openwa-core/src/task/supply_crate.rs +49 -0
- crates/openwa-core/src/task/worm.rs +54 -10
- crates/openwa-debugui/src/app.rs +217 -49
- crates/openwa-wormkit/src/replacements/mod.rs +2 -1
- crates/openwa-wormkit/src/validation/mod.rs +1 -0
|
@@ -80,6 +80,8 @@ pub mod va {
|
|
| 80 |
pub const CTASK_MINE_VTABLE: u32 = 0x0066_43E8;
|
| 81 |
/// CTaskOilDrum vtable - oil drum entity (constructor 0x504AF0)
|
| 82 |
pub const CTASK_OILDRUM_VTABLE: u32 = 0x0066_4338;
|
|
|
|
|
|
|
| 83 |
/// CTaskCloud vtable - cloud/airstrike entity (constructor 0x5482E0)
|
| 84 |
pub const CTASK_CLOUD_VTABLE: u32 = 0x0066_9D38;
|
| 85 |
/// CTaskSeaBubble vtable - water bubble particle (constructor 0x554FE0)
|
|
|
|
| 80 |
pub const CTASK_MINE_VTABLE: u32 = 0x0066_43E8;
|
| 81 |
/// CTaskOilDrum vtable - oil drum entity (constructor 0x504AF0)
|
| 82 |
pub const CTASK_OILDRUM_VTABLE: u32 = 0x0066_4338;
|
| 83 |
+
/// CTaskCrate vtable - weapon/health/utility crate (constructor 0x502490)
|
| 84 |
+
pub const CTASK_CRATE_VTABLE: u32 = 0x0066_4298;
|
| 85 |
/// CTaskCloud vtable - cloud/airstrike entity (constructor 0x5482E0)
|
| 86 |
pub const CTASK_CLOUD_VTABLE: u32 = 0x0066_9D38;
|
| 87 |
/// CTaskSeaBubble vtable - water bubble particle (constructor 0x554FE0)
|
|
@@ -19,8 +19,8 @@ pub struct CGameTask {
|
|
| 19 |
pub pos_x: Fixed,
|
| 20 |
/// 0x88: Y position in fixed-point
|
| 21 |
pub pos_y: Fixed,
|
| 22 |
-
/// 0x8C
|
| 23 |
-
pub
|
| 24 |
/// 0x90: X velocity in fixed-point
|
| 25 |
pub speed_x: Fixed,
|
| 26 |
/// 0x94: Y velocity in fixed-point
|
|
|
|
| 19 |
pub pos_x: Fixed,
|
| 20 |
/// 0x88: Y position in fixed-point
|
| 21 |
pub pos_y: Fixed,
|
| 22 |
+
/// 0x8C: Rotation angle (fixed-point 16.16).
|
| 23 |
+
pub angle: Fixed,
|
| 24 |
/// 0x90: X velocity in fixed-point
|
| 25 |
pub speed_x: Fixed,
|
| 26 |
/// 0x94: Y velocity in fixed-point
|
|
@@ -18,13 +18,15 @@ pub struct CTaskMine {
|
|
| 18 |
pub _unknown_fc: [u8; 0x14],
|
| 19 |
/// 0x110: Object pool slot index (assigned from DDGame+0x3600 pool)
|
| 20 |
pub slot_id: u32,
|
| 21 |
-
/// 0x114
|
| 22 |
-
pub _unknown_114:
|
| 23 |
-
///
|
| 24 |
/// Negative = just placed / disarmed.
|
| 25 |
/// 0 = armed (will trigger on contact).
|
| 26 |
/// Positive = countdown ticks remaining.
|
| 27 |
pub fuse_timer: i32,
|
|
|
|
|
|
|
| 28 |
/// 0x120–0x123: Unknown (init data param_3[0])
|
| 29 |
pub _unknown_120: u32,
|
| 30 |
/// 0x124: Owner team index (param_3[6]; -1 = no owner)
|
|
|
|
| 18 |
pub _unknown_fc: [u8; 0x14],
|
| 19 |
/// 0x110: Object pool slot index (assigned from DDGame+0x3600 pool)
|
| 20 |
pub slot_id: u32,
|
| 21 |
+
/// 0x114: Unknown
|
| 22 |
+
pub _unknown_114: u32,
|
| 23 |
+
/// 0x118: Fuse timer (signed i32).
|
| 24 |
/// Negative = just placed / disarmed.
|
| 25 |
/// 0 = armed (will trigger on contact).
|
| 26 |
/// Positive = countdown ticks remaining.
|
| 27 |
pub fuse_timer: i32,
|
| 28 |
+
/// 0x11C: Unknown
|
| 29 |
+
pub _unknown_11c: u32,
|
| 30 |
/// 0x120–0x123: Unknown (init data param_3[0])
|
| 31 |
pub _unknown_120: u32,
|
| 32 |
/// 0x124: Owner team index (param_3[6]; -1 = no owner)
|
|
@@ -1,5 +1,6 @@
|
|
| 1 |
mod base;
|
| 2 |
mod cloud;
|
|
|
|
| 3 |
mod filter;
|
| 4 |
mod fire;
|
| 5 |
mod game_task;
|
|
@@ -12,6 +13,7 @@ mod worm;
|
|
| 12 |
|
| 13 |
pub use base::{CTask, CTaskBfsIter, SharedDataIter, SharedDataNode, SharedDataTable};
|
| 14 |
pub use cloud::CTaskCloud;
|
|
|
|
| 15 |
pub use filter::CTaskFilter;
|
| 16 |
pub use fire::CTaskFire;
|
| 17 |
pub use game_task::{CGameTask, SoundEmitter, SoundEmitterVTable};
|
|
|
|
| 1 |
mod base;
|
| 2 |
mod cloud;
|
| 3 |
+
mod supply_crate;
|
| 4 |
mod filter;
|
| 5 |
mod fire;
|
| 6 |
mod game_task;
|
|
|
|
| 13 |
|
| 14 |
pub use base::{CTask, CTaskBfsIter, SharedDataIter, SharedDataNode, SharedDataTable};
|
| 15 |
pub use cloud::CTaskCloud;
|
| 16 |
+
pub use supply_crate::CTaskCrate;
|
| 17 |
pub use filter::CTaskFilter;
|
| 18 |
pub use fire::CTaskFire;
|
| 19 |
pub use game_task::{CGameTask, SoundEmitter, SoundEmitterVTable};
|
|
@@ -0,0 +1,49 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
use super::game_task::CGameTask;
|
| 2 |
+
|
| 3 |
+
/// Weapon/health/utility crate entity task.
|
| 4 |
+
///
|
| 5 |
+
/// Extends CGameTask (0xFC bytes). Crates fall from the sky with a parachute,
|
| 6 |
+
/// land on terrain, and are collected by worms on contact. Can contain weapons,
|
| 7 |
+
/// health, or utility items depending on crate type.
|
| 8 |
+
///
|
| 9 |
+
/// Constructor: 0x502490 (stdcall, 4 params: this, parent, scheme_data, spawn_mode).
|
| 10 |
+
/// Vtable: 0x664298. Class type byte: 0x0F.
|
| 11 |
+
///
|
| 12 |
+
/// The constructor copies 0xE5 DWORDs (0x394 bytes) of scheme/crate data from
|
| 13 |
+
/// param_3 into offset 0x110, making the crate carry its full configuration.
|
| 14 |
+
///
|
| 15 |
+
/// Source: Ghidra decompilation of 0x502490, wkJellyWorm CTaskCrate.h
|
| 16 |
+
#[repr(C)]
|
| 17 |
+
pub struct CTaskCrate {
|
| 18 |
+
/// 0x00–0xFB: CGameTask base (pos at 0x84/0x88, speed at 0x90/0x94)
|
| 19 |
+
pub base: CGameTask,
|
| 20 |
+
/// 0xFC: Unknown (zeroed by constructor)
|
| 21 |
+
pub _unknown_fc: u32,
|
| 22 |
+
/// 0x100: Object pool slot index (assigned from DDGame+0x3600 pool)
|
| 23 |
+
pub slot_id: u32,
|
| 24 |
+
/// 0x104: Unknown (zeroed by constructor)
|
| 25 |
+
pub _unknown_104: u32,
|
| 26 |
+
/// 0x108: Unknown (zeroed by constructor, also cleared for spawn_mode=1)
|
| 27 |
+
pub _unknown_108: u32,
|
| 28 |
+
/// 0x10C: Timer/counter (zeroed by constructor; set to scheme_data[0x52]*1000
|
| 29 |
+
/// for crate_type == 3)
|
| 30 |
+
pub timer: u32,
|
| 31 |
+
/// 0x110–0x4A3: Scheme/crate data (0xE5 DWORDs = 0x394 bytes, copied from
|
| 32 |
+
/// constructor param_3). Contains weapon properties, crate type, quantities, etc.
|
| 33 |
+
///
|
| 34 |
+
/// Key indices (DWORD offsets from 0x110):
|
| 35 |
+
/// [0x05] (0x124): crate_type — discriminator (3=timed?, 5=airstrike?)
|
| 36 |
+
/// [0x4F] (0x24C): health crate healing amount
|
| 37 |
+
/// [0x45] (0x224): scheme weapon data sub-field
|
| 38 |
+
/// [0x55] (0x264): nonzero triggers additional init
|
| 39 |
+
/// [0x85] (0x324): additional scheme params
|
| 40 |
+
pub scheme_data: [u32; 0xE5],
|
| 41 |
+
/// 0x4A4: Unknown (zeroed by constructor)
|
| 42 |
+
pub _unknown_4a4: u32,
|
| 43 |
+
/// 0x4A8: Sequence/reference index (-1 = none; set conditionally from DDGame+0x51C)
|
| 44 |
+
pub sequence_ref: i32,
|
| 45 |
+
/// 0x4AC: Unknown (zeroed by constructor as param_1[299])
|
| 46 |
+
pub _unknown_4ac: u32,
|
| 47 |
+
}
|
| 48 |
+
|
| 49 |
+
const _: () = assert!(core::mem::size_of::<CTaskCrate>() == 0x4B0);
|
|
@@ -104,12 +104,38 @@ pub struct CTaskWorm {
|
|
| 104 |
pub _unknown_154: u32,
|
| 105 |
/// 0x158: Worm pool slot index in DDGame (assigned from pool at construction)
|
| 106 |
pub slot_id: u32,
|
| 107 |
-
/// 0x15C–
|
| 108 |
-
pub _unknown_15c: [u8;
|
| 109 |
-
///
|
| 110 |
-
pub
|
| 111 |
-
///
|
| 112 |
-
pub
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
/// 0x284–0x28B: Aiming data (slots 5–6 in GetEntityData query 0x7D4)
|
| 114 |
pub _unknown_284: [u32; 2],
|
| 115 |
/// 0x28C–0x2EF: Unknown
|
|
@@ -118,12 +144,30 @@ pub struct CTaskWorm {
|
|
| 118 |
pub worm_name: [u8; 0x11],
|
| 119 |
/// 0x301: Country / team name from scheme, null-terminated (max 17 chars)
|
| 120 |
pub country_name: [u8; 0x11],
|
| 121 |
-
/// 0x312–
|
| 122 |
-
pub _unknown_312: [u8;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
/// 0x368: Animator / controller object (dispatched via vtable for state animations)
|
| 124 |
pub animator: *mut u8,
|
| 125 |
-
/// 0x36C–
|
| 126 |
-
pub _unknown_36c: [u8;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 127 |
}
|
| 128 |
|
| 129 |
const _: () = assert!(core::mem::size_of::<CTaskWorm>() == 0x3FC);
|
|
|
|
| 104 |
pub _unknown_154: u32,
|
| 105 |
/// 0x158: Worm pool slot index in DDGame (assigned from pool at construction)
|
| 106 |
pub slot_id: u32,
|
| 107 |
+
/// 0x15C–0x163: Unknown
|
| 108 |
+
pub _unknown_15c: [u8; 0x164 - 0x15C],
|
| 109 |
+
/// 0x164: Frames the worm has stayed stationary (no movement). Resets on movement.
|
| 110 |
+
pub stationary_frames: u32,
|
| 111 |
+
/// 0x168–0x16F: Unknown
|
| 112 |
+
pub _unknown_168: [u8; 0x170 - 0x168],
|
| 113 |
+
/// 0x170: Currently selected weapon ID.
|
| 114 |
+
pub selected_weapon: u32,
|
| 115 |
+
/// 0x174–0x1A7: Unknown
|
| 116 |
+
pub _unknown_174: [u8; 0x1A8 - 0x174],
|
| 117 |
+
/// 0x1A8: Facing direction copy. -1 = left, +1 = right (same as +0x3DC).
|
| 118 |
+
pub facing_direction_2: i32,
|
| 119 |
+
/// 0x1AC: Inverted facing direction. +1 = left, -1 = right.
|
| 120 |
+
pub facing_direction_inv: i32,
|
| 121 |
+
/// 0x1B0–0x1EB: Unknown
|
| 122 |
+
pub _unknown_1b0: [u8; 0x1EC - 0x1B0],
|
| 123 |
+
/// 0x1EC: Movement streak counter. Increases ~once per second while moving
|
| 124 |
+
/// in one direction. Resets to 0 when movement resumes after a stop.
|
| 125 |
+
/// Set to -1 when the worm is blocked (e.g. hits a wall).
|
| 126 |
+
pub movement_streak: i32,
|
| 127 |
+
/// 0x1F0–0x247: Unknown
|
| 128 |
+
pub _unknown_1f0: [u8; 0x248 - 0x1F0],
|
| 129 |
+
/// 0x248: Unknown
|
| 130 |
+
pub _unknown_248: u32,
|
| 131 |
+
/// 0x24C: Aim angle (fixed-point 16.16, range 0..0x10000 = 0..360 degrees).
|
| 132 |
+
pub aim_angle: u32,
|
| 133 |
+
/// 0x250–0x267: Unknown
|
| 134 |
+
pub _unknown_250: [u8; 0x268 - 0x250],
|
| 135 |
+
/// 0x268: Show aiming cursor flag (nonzero = cursor visible).
|
| 136 |
+
pub show_cursor: u32,
|
| 137 |
+
/// 0x26C–0x283: Unknown
|
| 138 |
+
pub _unknown_26c: [u8; 0x284 - 0x26C],
|
| 139 |
/// 0x284–0x28B: Aiming data (slots 5–6 in GetEntityData query 0x7D4)
|
| 140 |
pub _unknown_284: [u32; 2],
|
| 141 |
/// 0x28C–0x2EF: Unknown
|
|
|
|
| 144 |
pub worm_name: [u8; 0x11],
|
| 145 |
/// 0x301: Country / team name from scheme, null-terminated (max 17 chars)
|
| 146 |
pub country_name: [u8; 0x11],
|
| 147 |
+
/// 0x312–0x333: Unknown (rope string, state history, etc.)
|
| 148 |
+
pub _unknown_312: [u8; 0x334 - 0x312],
|
| 149 |
+
/// 0x334: Facing direction copy. -1 = left, +1 = right (same as +0x3DC).
|
| 150 |
+
pub facing_direction_3: i32,
|
| 151 |
+
/// 0x338–0x367: Unknown
|
| 152 |
+
pub _unknown_338: [u8; 0x368 - 0x338],
|
| 153 |
/// 0x368: Animator / controller object (dispatched via vtable for state animations)
|
| 154 |
pub animator: *mut u8,
|
| 155 |
+
/// 0x36C–0x3DB: Unknown (rope anchor, weapon-specific data, etc.)
|
| 156 |
+
pub _unknown_36c: [u8; 0x3DC - 0x36C],
|
| 157 |
+
/// 0x3DC: Facing direction. -1 = facing left, +1 = facing right.
|
| 158 |
+
pub facing_direction: i32,
|
| 159 |
+
/// 0x3E0–0x3E3: Unknown
|
| 160 |
+
pub _unknown_3e0: u32,
|
| 161 |
+
/// 0x3E4: Input: aim up key held (nonzero = adjusting aim upward).
|
| 162 |
+
pub input_aim_up: u32,
|
| 163 |
+
/// 0x3E8: Input: aim down key held (nonzero = adjusting aim downward).
|
| 164 |
+
pub input_aim_down: u32,
|
| 165 |
+
/// 0x3EC: Input: move left key held (nonzero = worm walking left).
|
| 166 |
+
pub input_move_left: u32,
|
| 167 |
+
/// 0x3F0: Input: move right key held (nonzero = worm walking right).
|
| 168 |
+
pub input_move_right: u32,
|
| 169 |
+
/// 0x3F4–0x3FB: Unknown
|
| 170 |
+
pub _unknown_3f4: [u8; 0x3FC - 0x3F4],
|
| 171 |
}
|
| 172 |
|
| 173 |
const _: () = assert!(core::mem::size_of::<CTaskWorm>() == 0x3FC);
|
|
@@ -2,7 +2,7 @@ use eframe::egui;
|
|
| 2 |
use openwa_core::address::va;
|
| 3 |
use openwa_core::engine::{DDGame, DDGameWrapper};
|
| 4 |
use openwa_core::rebase::rb;
|
| 5 |
-
use openwa_core::task::{CTask, CTaskCloud, CTaskFire,
|
| 6 |
|
| 7 |
use crate::log;
|
| 8 |
|
|
@@ -22,6 +22,7 @@ const KNOWN_VTABLES: &[(u32, &str)] = &[
|
|
| 22 |
(va::CTASK_MISSILE_VTABLE, "CTaskMissile"),
|
| 23 |
(va::CTASK_MINE_VTABLE, "CTaskMine"),
|
| 24 |
(va::CTASK_OILDRUM_VTABLE, "CTaskOilDrum"),
|
|
|
|
| 25 |
(va::CTASK_CLOUD_VTABLE, "CTaskCloud"),
|
| 26 |
(va::CTASK_SEA_BUBBLE_VTABLE, "CTaskSeaBubble"),
|
| 27 |
(va::CTASK_FIRE_VTABLE, "CTaskFire"),
|
|
@@ -279,6 +280,113 @@ impl DebugApp {
|
|
| 279 |
}
|
| 280 |
}
|
| 281 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 282 |
// ---------------------------------------------------------------------------
|
| 283 |
// Panel: Struct Inspector
|
| 284 |
// ---------------------------------------------------------------------------
|
|
@@ -403,41 +511,48 @@ impl DebugApp {
|
|
| 403 |
|
| 404 |
// --- CTaskMine-specific fields ---
|
| 405 |
if name == "CTaskMine" {
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
|
| 412 |
-
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
ui.label("slot_id"); ui.label(format!("{}", (*mine).slot_id)); ui.end_row();
|
| 420 |
-
});
|
| 421 |
-
});
|
| 422 |
}
|
| 423 |
|
| 424 |
// --- CTaskOilDrum-specific fields ---
|
| 425 |
if name == "CTaskOilDrum" {
|
| 426 |
-
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
|
| 430 |
-
|
| 431 |
-
|
| 432 |
-
|
| 433 |
-
|
| 434 |
-
|
| 435 |
-
|
| 436 |
-
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 441 |
}
|
| 442 |
|
| 443 |
// --- CTaskCloud-specific fields ---
|
|
@@ -539,24 +654,77 @@ impl DebugApp {
|
|
| 539 |
|
| 540 |
// --- CTaskWorm-specific fields ---
|
| 541 |
if name == "CTaskWorm" || (*task).class_type == openwa_core::game::ClassType::Worm {
|
|
|
|
| 542 |
let worm = addr as *const CTaskWorm;
|
| 543 |
-
|
| 544 |
-
|
| 545 |
-
|
| 546 |
-
|
| 547 |
-
|
| 548 |
-
|
| 549 |
-
|
| 550 |
-
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
|
| 554 |
-
|
| 555 |
-
|
| 556 |
-
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 560 |
}
|
| 561 |
|
| 562 |
}});
|
|
|
|
| 2 |
use openwa_core::address::va;
|
| 3 |
use openwa_core::engine::{DDGame, DDGameWrapper};
|
| 4 |
use openwa_core::rebase::rb;
|
| 5 |
+
use openwa_core::task::{CTask, CTaskCloud, CTaskFire, CTaskTeam, CTaskTurnGame, CTaskWorm, TurnGameCtx};
|
| 6 |
|
| 7 |
use crate::log;
|
| 8 |
|
|
|
|
| 22 |
(va::CTASK_MISSILE_VTABLE, "CTaskMissile"),
|
| 23 |
(va::CTASK_MINE_VTABLE, "CTaskMine"),
|
| 24 |
(va::CTASK_OILDRUM_VTABLE, "CTaskOilDrum"),
|
| 25 |
+
(va::CTASK_CRATE_VTABLE, "CTaskCrate"),
|
| 26 |
(va::CTASK_CLOUD_VTABLE, "CTaskCloud"),
|
| 27 |
(va::CTASK_SEA_BUBBLE_VTABLE, "CTaskSeaBubble"),
|
| 28 |
(va::CTASK_FIRE_VTABLE, "CTaskFire"),
|
|
|
|
| 280 |
}
|
| 281 |
}
|
| 282 |
|
| 283 |
+
// ---------------------------------------------------------------------------
|
| 284 |
+
// Raw field viewer for CGameTask-derived types
|
| 285 |
+
// ---------------------------------------------------------------------------
|
| 286 |
+
|
| 287 |
+
/// CGameTask field labels for the base class region (0x00..0xFC).
|
| 288 |
+
const CGAMETASK_FIELD_LABELS: &[(usize, &str)] = &[
|
| 289 |
+
// CTask base (0x00..0x30)
|
| 290 |
+
(0x00, "vtable"),
|
| 291 |
+
(0x04, "parent"),
|
| 292 |
+
(0x08, "children_data"),
|
| 293 |
+
(0x0C, "children_watermark"),
|
| 294 |
+
(0x10, "children_capacity"),
|
| 295 |
+
(0x14, "class_type"),
|
| 296 |
+
(0x18, "state_machine"),
|
| 297 |
+
(0x1C, "shared_data"),
|
| 298 |
+
(0x20, "owns_shared_data"),
|
| 299 |
+
(0x24, "msg_handler"),
|
| 300 |
+
(0x28, "creation_flags"),
|
| 301 |
+
(0x2C, "ddgame"),
|
| 302 |
+
// CGameTask fields (0x30..0xFC)
|
| 303 |
+
(0x84, "pos_x"),
|
| 304 |
+
(0x88, "pos_y"),
|
| 305 |
+
(0x8C, "angle"),
|
| 306 |
+
(0x90, "speed_x"),
|
| 307 |
+
(0x94, "speed_y"),
|
| 308 |
+
// SoundEmitter (0xE8..0xFC)
|
| 309 |
+
(0xE8, "emitter_vtable"),
|
| 310 |
+
(0xEC, "emitter_unk04"),
|
| 311 |
+
(0xF0, "emitter_unk08"),
|
| 312 |
+
(0xF4, "local_snd_count"),
|
| 313 |
+
(0xF8, "emitter_owner"),
|
| 314 |
+
];
|
| 315 |
+
|
| 316 |
+
/// Display all DWORDs of a CGameTask-derived entity with labelled fields.
|
| 317 |
+
/// `extra_labels` provides field names for offsets beyond the CGameTask base (0xFC+).
|
| 318 |
+
unsafe fn show_game_task_raw_fields(
|
| 319 |
+
ui: &mut egui::Ui,
|
| 320 |
+
addr: u32,
|
| 321 |
+
type_name: &str,
|
| 322 |
+
total_size: usize,
|
| 323 |
+
extra_labels: &[(usize, &str)],
|
| 324 |
+
) {
|
| 325 |
+
let base = addr as *const u32;
|
| 326 |
+
|
| 327 |
+
let label_for = |off: usize| -> Option<&str> {
|
| 328 |
+
CGAMETASK_FIELD_LABELS.iter()
|
| 329 |
+
.find(|&&(o, _)| o == off)
|
| 330 |
+
.map(|&(_, n)| n)
|
| 331 |
+
.or_else(|| extra_labels.iter()
|
| 332 |
+
.find(|&&(o, _)| o == off)
|
| 333 |
+
.map(|&(_, n)| n))
|
| 334 |
+
};
|
| 335 |
+
|
| 336 |
+
// Sections: CTask base, CGameTask unknowns, pos/speed, more unknowns, emitter,
|
| 337 |
+
// then type-specific in 0x80-byte chunks to keep each section manageable.
|
| 338 |
+
let mut sections: Vec<(usize, usize, String)> = vec![
|
| 339 |
+
(0x000, 0x030, "CTask base".into()),
|
| 340 |
+
(0x030, 0x084, "CGameTask +0x30".into()),
|
| 341 |
+
(0x084, 0x098, "pos / speed / angle".into()),
|
| 342 |
+
(0x098, 0x0E8, "CGameTask +0x98".into()),
|
| 343 |
+
(0x0E8, 0x0FC, "SoundEmitter".into()),
|
| 344 |
+
];
|
| 345 |
+
// Split type-specific region into chunks of 0x80 bytes
|
| 346 |
+
let mut chunk_start = 0x0FC;
|
| 347 |
+
while chunk_start < total_size {
|
| 348 |
+
let chunk_end = (chunk_start + 0x80).min(total_size);
|
| 349 |
+
sections.push((chunk_start, chunk_end,
|
| 350 |
+
format!("{} +0x{:03X}..0x{:03X}", type_name, chunk_start, chunk_end)));
|
| 351 |
+
chunk_start = chunk_end;
|
| 352 |
+
}
|
| 353 |
+
|
| 354 |
+
for (start, end, section_name) in §ions {
|
| 355 |
+
let (start, end) = (*start, *end);
|
| 356 |
+
if start >= total_size { break; }
|
| 357 |
+
let end = end.min(total_size);
|
| 358 |
+
let header = format!("{} (0x{:03X}..0x{:03X})", section_name, start, end);
|
| 359 |
+
let default_open = false;
|
| 360 |
+
egui::CollapsingHeader::new(header)
|
| 361 |
+
.id_source(format!("{}_{}_{:03X}", type_name, addr, start))
|
| 362 |
+
.default_open(default_open)
|
| 363 |
+
.show(ui, |ui| {
|
| 364 |
+
egui::Grid::new(format!("raw_{}_{}_{:03X}", type_name, addr, start))
|
| 365 |
+
.striped(true)
|
| 366 |
+
.num_columns(3)
|
| 367 |
+
.show(ui, |ui| {
|
| 368 |
+
ui.strong("Offset");
|
| 369 |
+
ui.strong("Value");
|
| 370 |
+
ui.strong("Field");
|
| 371 |
+
ui.end_row();
|
| 372 |
+
|
| 373 |
+
let dwords = (end - start) / 4;
|
| 374 |
+
for i in 0..dwords {
|
| 375 |
+
let off = start + i * 4;
|
| 376 |
+
let val = *base.add(off / 4);
|
| 377 |
+
let field_name = label_for(off).unwrap_or("");
|
| 378 |
+
|
| 379 |
+
ui.label(format!("+0x{:03X}", off));
|
| 380 |
+
// Show as hex + signed decimal
|
| 381 |
+
ui.label(format!("{:#010X} ({})", val, val as i32));
|
| 382 |
+
ui.label(field_name);
|
| 383 |
+
ui.end_row();
|
| 384 |
+
}
|
| 385 |
+
});
|
| 386 |
+
});
|
| 387 |
+
}
|
| 388 |
+
}
|
| 389 |
+
|
| 390 |
// ---------------------------------------------------------------------------
|
| 391 |
// Panel: Struct Inspector
|
| 392 |
// ---------------------------------------------------------------------------
|
|
|
|
| 511 |
|
| 512 |
// --- CTaskMine-specific fields ---
|
| 513 |
if name == "CTaskMine" {
|
| 514 |
+
show_game_task_raw_fields(ui, addr, "CTaskMine", 0x128, &[
|
| 515 |
+
(0x0FC, "unknown_fc[0]"),
|
| 516 |
+
(0x100, "unknown_fc[4]"),
|
| 517 |
+
(0x104, "unknown_fc[8]"),
|
| 518 |
+
(0x108, "unknown_fc[C]"),
|
| 519 |
+
(0x10C, "unknown_fc[10]"),
|
| 520 |
+
(0x110, "slot_id"),
|
| 521 |
+
(0x114, "unknown_114"),
|
| 522 |
+
(0x118, "fuse_timer"),
|
| 523 |
+
(0x11C, "unknown_11c"),
|
| 524 |
+
(0x120, "unknown_120"),
|
| 525 |
+
(0x124, "owner_team"),
|
| 526 |
+
]);
|
|
|
|
|
|
|
|
|
|
| 527 |
}
|
| 528 |
|
| 529 |
// --- CTaskOilDrum-specific fields ---
|
| 530 |
if name == "CTaskOilDrum" {
|
| 531 |
+
show_game_task_raw_fields(ui, addr, "CTaskOilDrum", 0x110, &[
|
| 532 |
+
(0x0FC, "triggered"),
|
| 533 |
+
(0x100, "slot_id"),
|
| 534 |
+
(0x104, "unknown_104"),
|
| 535 |
+
(0x108, "health"),
|
| 536 |
+
(0x10C, "roll_counter"),
|
| 537 |
+
]);
|
| 538 |
+
}
|
| 539 |
+
|
| 540 |
+
// --- CTaskCrate-specific fields ---
|
| 541 |
+
if name == "CTaskCrate" {
|
| 542 |
+
show_game_task_raw_fields(ui, addr, "CTaskCrate", 0x4B0, &[
|
| 543 |
+
(0x0FC, "unknown_fc"),
|
| 544 |
+
(0x100, "slot_id"),
|
| 545 |
+
(0x104, "unknown_104"),
|
| 546 |
+
(0x108, "unknown_108"),
|
| 547 |
+
(0x10C, "timer"),
|
| 548 |
+
// scheme_data starts at 0x110 (0xE5 DWORDs)
|
| 549 |
+
(0x124, "crate_type"),
|
| 550 |
+
(0x24C, "heal_amount"),
|
| 551 |
+
(0x264, "extra_init_flag"),
|
| 552 |
+
(0x4A4, "unknown_4a4"),
|
| 553 |
+
(0x4A8, "sequence_ref"),
|
| 554 |
+
(0x4AC, "unknown_4ac"),
|
| 555 |
+
]);
|
| 556 |
}
|
| 557 |
|
| 558 |
// --- CTaskCloud-specific fields ---
|
|
|
|
| 654 |
|
| 655 |
// --- CTaskWorm-specific fields ---
|
| 656 |
if name == "CTaskWorm" || (*task).class_type == openwa_core::game::ClassType::Worm {
|
| 657 |
+
// Summary header with key info
|
| 658 |
let worm = addr as *const CTaskWorm;
|
| 659 |
+
let name_bytes = &(*worm).worm_name;
|
| 660 |
+
let nul = name_bytes.iter().position(|&b| b == 0).unwrap_or(name_bytes.len());
|
| 661 |
+
let worm_name = std::str::from_utf8(&name_bytes[..nul]).unwrap_or("?");
|
| 662 |
+
ui.label(format!("Worm: \"{}\" state={:#04X} team={} idx={}",
|
| 663 |
+
worm_name, (*worm).state(), (*worm).team_index, (*worm).worm_index));
|
| 664 |
+
ui.separator();
|
| 665 |
+
|
| 666 |
+
show_game_task_raw_fields(ui, addr, "CTaskWorm", 0x3FC, &[
|
| 667 |
+
(0x0FC, "team_index"),
|
| 668 |
+
(0x100, "worm_index"),
|
| 669 |
+
(0x104, "unknown_104"),
|
| 670 |
+
(0x110, "spawn_params[0]"),
|
| 671 |
+
(0x114, "spawn_params[1]"),
|
| 672 |
+
(0x118, "spawn_params[2]"),
|
| 673 |
+
(0x11C, "spawn_params[3]"),
|
| 674 |
+
(0x120, "spawn_params[4]"),
|
| 675 |
+
(0x124, "spawn_params[5]"),
|
| 676 |
+
(0x128, "spawn_params[6]"),
|
| 677 |
+
(0x12C, "spawn_params[7]"),
|
| 678 |
+
(0x130, "spawn_params[8]"),
|
| 679 |
+
(0x134, "spawn_params[9]"),
|
| 680 |
+
(0x158, "slot_id"),
|
| 681 |
+
(0x164, "stationary_frames"),
|
| 682 |
+
(0x170, "selected_weapon"),
|
| 683 |
+
(0x1A8, "facing_dir_2"),
|
| 684 |
+
(0x1AC, "facing_dir_inv"),
|
| 685 |
+
(0x1EC, "movement_streak"),
|
| 686 |
+
(0x24C, "aim_angle"),
|
| 687 |
+
(0x268, "show_cursor"),
|
| 688 |
+
(0x2F0, "worm_name[0..4]"),
|
| 689 |
+
(0x2F4, "worm_name[4..8]"),
|
| 690 |
+
(0x2F8, "worm_name[8..12]"),
|
| 691 |
+
(0x2FC, "worm_name[12..16]"),
|
| 692 |
+
(0x334, "facing_dir_3"),
|
| 693 |
+
(0x368, "animator"),
|
| 694 |
+
(0x3DC, "facing_direction"),
|
| 695 |
+
(0x3E4, "input_aim_up"),
|
| 696 |
+
(0x3E8, "input_aim_down"),
|
| 697 |
+
(0x3EC, "input_move_left"),
|
| 698 |
+
(0x3F0, "input_move_right"),
|
| 699 |
+
]);
|
| 700 |
+
}
|
| 701 |
+
|
| 702 |
+
// --- CTaskMissile-specific fields ---
|
| 703 |
+
if name == "CTaskMissile" {
|
| 704 |
+
use openwa_core::task::CTaskMissile;
|
| 705 |
+
let m = &*(addr as *const CTaskMissile);
|
| 706 |
+
ui.label(format!("Missile: type={:?} slot={} homing={} dir={}",
|
| 707 |
+
m.missile_type(), m.slot_id, m.homing_enabled, m.direction));
|
| 708 |
+
ui.separator();
|
| 709 |
+
|
| 710 |
+
show_game_task_raw_fields(ui, addr, "CTaskMissile", 0x41C, &[
|
| 711 |
+
(0x128, "launch_seed"),
|
| 712 |
+
(0x12C, "slot_id"),
|
| 713 |
+
(0x130, "spawn: owner_id"),
|
| 714 |
+
(0x134, "spawn: [1]"),
|
| 715 |
+
(0x138, "spawn: spawn_x"),
|
| 716 |
+
(0x13C, "spawn: spawn_y"),
|
| 717 |
+
(0x140, "spawn: speed_x"),
|
| 718 |
+
(0x144, "spawn: speed_y"),
|
| 719 |
+
(0x148, "spawn: cursor_x"),
|
| 720 |
+
(0x14C, "spawn: cursor_y"),
|
| 721 |
+
(0x150, "spawn: pellet_idx"),
|
| 722 |
+
(0x154, "spawn: fb_timer"),
|
| 723 |
+
(0x158, "spawn: fb_param"),
|
| 724 |
+
(0x3A0, "launch_speed"),
|
| 725 |
+
(0x3A8, "homing_enabled"),
|
| 726 |
+
(0x3C8, "direction"),
|
| 727 |
+
]);
|
| 728 |
}
|
| 729 |
|
| 730 |
}});
|
|
@@ -18,7 +18,8 @@ pub fn install_all() -> Result<(), String> {
|
|
| 18 |
game_session::install()?;
|
| 19 |
hardware_init::install()?;
|
| 20 |
frontend::install()?;
|
| 21 |
-
|
|
|
|
| 22 |
config::install()?;
|
| 23 |
weapon::install()?;
|
| 24 |
team::install()?;
|
|
|
|
| 18 |
game_session::install()?;
|
| 19 |
hardware_init::install()?;
|
| 20 |
frontend::install()?;
|
| 21 |
+
// Temporarily disabled: loading custom scheme causes game to play super fast
|
| 22 |
+
// scheme::install()?;
|
| 23 |
config::install()?;
|
| 24 |
weapon::install()?;
|
| 25 |
team::install()?;
|
|
@@ -801,6 +801,7 @@ fn dump_entity_census() {
|
|
| 801 |
(va::CTASK_DIRT_VTABLE, "CTaskDirt"),
|
| 802 |
(va::CTASK_SPRITE_ANIM_VTABLE, "CTaskSpriteAnim"),
|
| 803 |
(va::CTASK_CPU_VTABLE, "CTaskCPU"),
|
|
|
|
| 804 |
(va::CTASK_VTABLE, "CTask"),
|
| 805 |
(va::CGAMETASK_VTABLE, "CGameTask"),
|
| 806 |
];
|
|
|
|
| 801 |
(va::CTASK_DIRT_VTABLE, "CTaskDirt"),
|
| 802 |
(va::CTASK_SPRITE_ANIM_VTABLE, "CTaskSpriteAnim"),
|
| 803 |
(va::CTASK_CPU_VTABLE, "CTaskCPU"),
|
| 804 |
+
(va::CTASK_CRATE_VTABLE, "CTaskCrate"),
|
| 805 |
(va::CTASK_VTABLE, "CTask"),
|
| 806 |
(va::CGAMETASK_VTABLE, "CGameTask"),
|
| 807 |
];
|