feat: add CTaskFire struct and debug inspector support
Browse files- address.rs: CTASK_FIRE_VTABLE = 0x00669DD8
- task.rs: CTaskFire (0xD8 bytes, extends CTask) with spawn_x/y, timer,
burn_rate, spread_counter, lifetime, slot_index fields
- app.rs: add CTaskFire to KNOWN_VTABLES; add inspector section showing
key fields (position, timer, burn rate, lifetime, slot)
Vtable labeled in Ghidra: CTaskFire__Init, __Free, __HandleMessage,
__ProcessFrame all renamed.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
crates/openwa-core/src/address.rs
CHANGED
|
@@ -73,6 +73,8 @@ pub mod va {
|
|
| 73 |
pub const CTASK_CLOUD_VTABLE: u32 = 0x0066_9D38;
|
| 74 |
/// CTaskSeaBubble vtable - water bubble particle (constructor 0x554FE0)
|
| 75 |
pub const CTASK_SEA_BUBBLE_VTABLE: u32 = 0x0066_9E88;
|
|
|
|
|
|
|
| 76 |
/// Sprite vtable (0x70-byte objects, 8 entries)
|
| 77 |
pub const SPRITE_VTABLE: u32 = 0x0066_418C;
|
| 78 |
|
|
|
|
| 73 |
pub const CTASK_CLOUD_VTABLE: u32 = 0x0066_9D38;
|
| 74 |
/// CTaskSeaBubble vtable - water bubble particle (constructor 0x554FE0)
|
| 75 |
pub const CTASK_SEA_BUBBLE_VTABLE: u32 = 0x0066_9E88;
|
| 76 |
+
/// CTaskFire vtable - fire/flame entity (constructor 0x54F4C0, 0xD8 bytes)
|
| 77 |
+
pub const CTASK_FIRE_VTABLE: u32 = 0x0066_9DD8;
|
| 78 |
/// Sprite vtable (0x70-byte objects, 8 entries)
|
| 79 |
pub const SPRITE_VTABLE: u32 = 0x0066_418C;
|
| 80 |
|
crates/openwa-core/src/task.rs
CHANGED
|
@@ -566,6 +566,51 @@ impl CTaskOilDrum {
|
|
| 566 |
}
|
| 567 |
}
|
| 568 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 569 |
// ============================================================
|
| 570 |
// Derived task overlays — for accessing task-specific fields
|
| 571 |
// beyond or within CGameTask that differ per task type.
|
|
|
|
| 566 |
}
|
| 567 |
}
|
| 568 |
|
| 569 |
+
/// Fire/flame entity task.
|
| 570 |
+
///
|
| 571 |
+
/// Extends CTask (not CGameTask) — no physics body.
|
| 572 |
+
/// class_type = 0x18. Allocated 0xD8 bytes.
|
| 573 |
+
/// Constructor: CTaskFire__Constructor (0x54F4C0).
|
| 574 |
+
/// vtable: CTaskFire__vtable (0x00669DD8).
|
| 575 |
+
///
|
| 576 |
+
/// One CTaskFire is spawned per flame sprite. The `timer` field starts
|
| 577 |
+
/// at 0xFFFF and counts down each frame; when it reaches zero the fire
|
| 578 |
+
/// dies. `lifetime` at +0xB1 is a signed byte: 0xFF (= -1i8) means alive,
|
| 579 |
+
/// 0 means the task is being destroyed.
|
| 580 |
+
#[repr(C)]
|
| 581 |
+
pub struct CTaskFire {
|
| 582 |
+
/// 0x00-0x2F: CTask base
|
| 583 |
+
pub base: CTask,
|
| 584 |
+
/// 0x30: spread counter (incremented while fire is spreading)
|
| 585 |
+
pub spread_counter: i32,
|
| 586 |
+
/// 0x34: frame countdown; starts at 0xFFFF, decrements each ProcessFrame
|
| 587 |
+
pub timer: i32,
|
| 588 |
+
/// 0x38: random seed / initial offset for sprite variation
|
| 589 |
+
pub rand_offset: u32,
|
| 590 |
+
/// 0x3C: burn rate / intensity (higher = more damage per frame)
|
| 591 |
+
pub burn_rate: u32,
|
| 592 |
+
pub _unknown_40: u32,
|
| 593 |
+
/// 0x44: spawn X position (Fixed 16.16)
|
| 594 |
+
pub spawn_x: crate::fixed::Fixed,
|
| 595 |
+
/// 0x48: spawn Y position (Fixed 16.16)
|
| 596 |
+
pub spawn_y: crate::fixed::Fixed,
|
| 597 |
+
pub _unknown_4c: [u8; 0x24],
|
| 598 |
+
/// 0x70: absolute tick (frame counter) when this flame was spawned
|
| 599 |
+
pub spawn_time: u32,
|
| 600 |
+
pub _unknown_74: u32,
|
| 601 |
+
/// 0x78-0xA7: per-frame spawn parameter table (12 DWORDs)
|
| 602 |
+
pub spawn_params: [u32; 12],
|
| 603 |
+
/// 0xA8: slot index in the fire-object pool
|
| 604 |
+
pub slot_index: u32,
|
| 605 |
+
pub _unknown_ac: u32,
|
| 606 |
+
pub _flags_b0: u8,
|
| 607 |
+
/// 0xB1: lifetime byte; -1 (0xFF as i8) = alive, 0 = dying/dead
|
| 608 |
+
pub lifetime: i8,
|
| 609 |
+
pub _unknown_b2: [u8; 0x26],
|
| 610 |
+
}
|
| 611 |
+
|
| 612 |
+
const _: () = assert!(core::mem::size_of::<CTaskFire>() == 0xD8);
|
| 613 |
+
|
| 614 |
// ============================================================
|
| 615 |
// Derived task overlays — for accessing task-specific fields
|
| 616 |
// beyond or within CGameTask that differ per task type.
|
crates/openwa-debugui/src/app.rs
CHANGED
|
@@ -3,7 +3,7 @@ use openwa_core::address::va;
|
|
| 3 |
use openwa_core::ddgame::DDGame;
|
| 4 |
use openwa_core::ddgame_wrapper::DDGameWrapper;
|
| 5 |
use openwa_core::rebase::rb;
|
| 6 |
-
use openwa_core::task::{CTask, CTaskCloud, CTaskMine, CTaskOilDrum, CTaskWorm};
|
| 7 |
|
| 8 |
use crate::log;
|
| 9 |
|
|
@@ -25,6 +25,7 @@ const KNOWN_VTABLES: &[(u32, &str)] = &[
|
|
| 25 |
(va::CTASK_OILDRUM_VTABLE, "CTaskOilDrum"),
|
| 26 |
(va::CTASK_CLOUD_VTABLE, "CTaskCloud"),
|
| 27 |
(va::CTASK_SEA_BUBBLE_VTABLE, "CTaskSeaBubble"),
|
|
|
|
| 28 |
];
|
| 29 |
|
| 30 |
/// Vtables of entities that are created/destroyed every frame (particles,
|
|
@@ -459,6 +460,24 @@ impl DebugApp {
|
|
| 459 |
});
|
| 460 |
}
|
| 461 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 462 |
// --- CTaskWorm-specific fields ---
|
| 463 |
if name == "CTaskWorm" || (*task).class_type == openwa_core::class_type::ClassType::Worm {
|
| 464 |
let worm = addr as *const CTaskWorm;
|
|
|
|
| 3 |
use openwa_core::ddgame::DDGame;
|
| 4 |
use openwa_core::ddgame_wrapper::DDGameWrapper;
|
| 5 |
use openwa_core::rebase::rb;
|
| 6 |
+
use openwa_core::task::{CTask, CTaskCloud, CTaskFire, CTaskMine, CTaskOilDrum, CTaskWorm};
|
| 7 |
|
| 8 |
use crate::log;
|
| 9 |
|
|
|
|
| 25 |
(va::CTASK_OILDRUM_VTABLE, "CTaskOilDrum"),
|
| 26 |
(va::CTASK_CLOUD_VTABLE, "CTaskCloud"),
|
| 27 |
(va::CTASK_SEA_BUBBLE_VTABLE, "CTaskSeaBubble"),
|
| 28 |
+
(va::CTASK_FIRE_VTABLE, "CTaskFire"),
|
| 29 |
];
|
| 30 |
|
| 31 |
/// Vtables of entities that are created/destroyed every frame (particles,
|
|
|
|
| 460 |
});
|
| 461 |
}
|
| 462 |
|
| 463 |
+
// --- CTaskFire-specific fields ---
|
| 464 |
+
if name == "CTaskFire" {
|
| 465 |
+
let fire = addr as *const CTaskFire;
|
| 466 |
+
egui::CollapsingHeader::new("CTaskFire")
|
| 467 |
+
.default_open(true)
|
| 468 |
+
.show(ui, |ui| {
|
| 469 |
+
egui::Grid::new("fire_grid").striped(true).show(ui, |ui| {
|
| 470 |
+
ui.label("spawn_x"); ui.label(format!("{:.1}", (*fire).spawn_x.to_f32())); ui.end_row();
|
| 471 |
+
ui.label("spawn_y"); ui.label(format!("{:.1}", (*fire).spawn_y.to_f32())); ui.end_row();
|
| 472 |
+
ui.label("timer"); ui.label(format!("{}", (*fire).timer)); ui.end_row();
|
| 473 |
+
ui.label("burn_rate"); ui.label(format!("{}", (*fire).burn_rate)); ui.end_row();
|
| 474 |
+
ui.label("spread_ctr"); ui.label(format!("{}", (*fire).spread_counter)); ui.end_row();
|
| 475 |
+
ui.label("lifetime"); ui.label(format!("{}", (*fire).lifetime)); ui.end_row();
|
| 476 |
+
ui.label("slot_index"); ui.label(format!("{}", (*fire).slot_index)); ui.end_row();
|
| 477 |
+
});
|
| 478 |
+
});
|
| 479 |
+
}
|
| 480 |
+
|
| 481 |
// --- CTaskWorm-specific fields ---
|
| 482 |
if name == "CTaskWorm" || (*task).class_type == openwa_core::class_type::ClassType::Worm {
|
| 483 |
let worm = addr as *const CTaskWorm;
|