feat: add CTaskTeam struct (per-team entity, 0x460 bytes)
Browse files- CTaskTeam (0x460 bytes, vtable 0x669EE4, class_type=10): per-team entity
with team_index (+0x38), _item_slots[100] (+0x88), worm_count (+0x218)
- Size confirmed from malloc(0x460) in FUN_005528c0 (the factory function)
- Secondary interface vtable at +0x30 (Ghidra 0x669F00)
- Registered in SharedData with type code 0x15 (team_index as key)
- Rename CTaskTurnGame._ctaskteam → _game_ctx with detailed comment
explaining it's the intermediate game-context base (class_type=5, vtable
0x669C44), NOT the per-team CTaskTeam — different class, different size
- Add CTaskTeam offset_of! checks in validation (team_index, worm_count)
- Add CTaskTeam inspector section in debug UI (team_index, worm_count)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
@@ -566,6 +566,43 @@ impl CTaskOilDrum {
|
|
| 566 |
}
|
| 567 |
}
|
| 568 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 569 |
/// Fire/flame entity task.
|
| 570 |
///
|
| 571 |
/// Extends CTask (not CGameTask) — no physics body.
|
|
@@ -634,9 +671,22 @@ const _: () = assert!(core::mem::size_of::<CTaskFire>() == 0xD8);
|
|
| 634 |
pub struct CTaskTurnGame {
|
| 635 |
/// 0x00-0x2F: CTask base (vtable, parent, children, shared_data, ddgame, …)
|
| 636 |
pub base: CTask,
|
| 637 |
-
/// 0x30-0xDB:
|
| 638 |
-
///
|
| 639 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 640 |
|
| 641 |
// ---- CTaskTurnGame-specific fields (0xDC onwards) ----
|
| 642 |
|
|
|
|
| 566 |
}
|
| 567 |
}
|
| 568 |
|
| 569 |
+
/// Per-team state-tracker task — one instance per team, child of CTaskTurnGame.
|
| 570 |
+
///
|
| 571 |
+
/// Tracks per-team data: which team number it represents, how many worms were
|
| 572 |
+
/// spawned, and a weapon/item slot table. Registered in the SharedData hash
|
| 573 |
+
/// table with type code 0x15 (21).
|
| 574 |
+
///
|
| 575 |
+
/// Inheritance: extends CTask directly (CTask base at +0x00).
|
| 576 |
+
/// class_type = 10. Allocation: 0x460 bytes via `operator new` (0x5C0AB8).
|
| 577 |
+
/// Constructor: `CTaskTeam__Constructor` (0x555BB0).
|
| 578 |
+
/// Primary vtable: `CTaskTeam__vtable2` (0x00669EE4).
|
| 579 |
+
/// Secondary interface vtable: 0x00669F00 (at object +0x30).
|
| 580 |
+
///
|
| 581 |
+
/// Key constructor logic (0x555BB0):
|
| 582 |
+
/// - `team_index` at +0x38: 1-based team number (passed as param_3)
|
| 583 |
+
/// - `_item_slots[0..99]` at +0x88: up to 100 weapon/item IDs loaded from scheme
|
| 584 |
+
/// - `worm_count` at +0x218: number of CTaskWorm children constructed (1-indexed)
|
| 585 |
+
/// - SharedData node (0x30 bytes) registered with key = (team_index, type=0x15)
|
| 586 |
+
#[repr(C)]
|
| 587 |
+
pub struct CTaskTeam {
|
| 588 |
+
/// 0x00–0x2F: CTask base (vtable, parent, children, shared_data, ddgame, …)
|
| 589 |
+
pub base: CTask,
|
| 590 |
+
/// 0x30: Secondary interface vtable pointer (Ghidra 0x00669F00)
|
| 591 |
+
pub _secondary_vtable: u32,
|
| 592 |
+
pub _unknown_34: u32,
|
| 593 |
+
/// 0x38: Team number, **1-based**. Matches TeamArenaState slot index.
|
| 594 |
+
pub team_index: u32,
|
| 595 |
+
pub _unknown_3c: [u8; 0x4C],
|
| 596 |
+
/// 0x88–0x217: Up to 100 weapon/item slot entries written from scheme data when
|
| 597 |
+
/// specific scheme flags are set. `worm_count` tracks how many entries are valid.
|
| 598 |
+
pub _item_slots: [u32; 100],
|
| 599 |
+
/// 0x218: Number of CTaskWorm children constructed for this team.
|
| 600 |
+
pub worm_count: u32,
|
| 601 |
+
pub _unknown_21c: [u8; 0x244],
|
| 602 |
+
}
|
| 603 |
+
|
| 604 |
+
const _: () = assert!(core::mem::size_of::<CTaskTeam>() == 0x460);
|
| 605 |
+
|
| 606 |
/// Fire/flame entity task.
|
| 607 |
///
|
| 608 |
/// Extends CTask (not CGameTask) — no physics body.
|
|
|
|
| 671 |
pub struct CTaskTurnGame {
|
| 672 |
/// 0x00-0x2F: CTask base (vtable, parent, children, shared_data, ddgame, …)
|
| 673 |
pub base: CTask,
|
| 674 |
+
/// 0x30-0xDB: Intermediate game-context base region (opaque).
|
| 675 |
+
///
|
| 676 |
+
/// Initialised by `CTaskTeam__Constructor_Maybe` (0x550EB0) which creates a
|
| 677 |
+
/// class_type-5 object with primary vtable 0x669E34 and secondary interface vtable
|
| 678 |
+
/// 0x669C44 at +0x30. CTaskTurnGame then overrides the primary vtable and
|
| 679 |
+
/// class_type.
|
| 680 |
+
///
|
| 681 |
+
/// Known non-zero fields inside this region (all offsets relative to object base):
|
| 682 |
+
/// +0x30: secondary interface vtable ptr (0x669C44 Ghidra)
|
| 683 |
+
/// +0x40 / +0x44: landscape height as Fixed16.16 (e.g. Fixed(696.0) for 696px map)
|
| 684 |
+
/// +0x48 / +0x58 / +0x68: Fixed(-1) — boundary sentinels
|
| 685 |
+
/// +0x70 / +0x74: Fixed(1.0) — x/y scale factors
|
| 686 |
+
/// +0x7C: team count (copy of DDGame+0x44C)
|
| 687 |
+
/// +0xD0: small integer (≈ total worm count)
|
| 688 |
+
/// +0xD4 / +0xD8: heap pointers (role unknown)
|
| 689 |
+
pub _game_ctx: [u8; 0xAC],
|
| 690 |
|
| 691 |
// ---- CTaskTurnGame-specific fields (0xDC onwards) ----
|
| 692 |
|
|
@@ -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, CTaskFire, CTaskMine, CTaskOilDrum, CTaskTurnGame, CTaskWorm};
|
| 7 |
|
| 8 |
use crate::log;
|
| 9 |
|
|
@@ -484,6 +484,19 @@ impl DebugApp {
|
|
| 484 |
});
|
| 485 |
}
|
| 486 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 487 |
// --- CTaskFire-specific fields ---
|
| 488 |
if name == "CTaskFire" {
|
| 489 |
let fire = addr as *const CTaskFire;
|
|
|
|
| 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, CTaskTeam, CTaskTurnGame, CTaskWorm};
|
| 7 |
|
| 8 |
use crate::log;
|
| 9 |
|
|
|
|
| 484 |
});
|
| 485 |
}
|
| 486 |
|
| 487 |
+
// --- CTaskTeam-specific fields ---
|
| 488 |
+
if name == "CTaskTeam" {
|
| 489 |
+
let team = addr as *const CTaskTeam;
|
| 490 |
+
egui::CollapsingHeader::new("CTaskTeam")
|
| 491 |
+
.default_open(true)
|
| 492 |
+
.show(ui, |ui| {
|
| 493 |
+
egui::Grid::new("team_grid").striped(true).show(ui, |ui| {
|
| 494 |
+
ui.label("team_index"); ui.label(format!("{}", (*team).team_index)); ui.end_row();
|
| 495 |
+
ui.label("worm_count"); ui.label(format!("{}", (*team).worm_count)); ui.end_row();
|
| 496 |
+
});
|
| 497 |
+
});
|
| 498 |
+
}
|
| 499 |
+
|
| 500 |
// --- CTaskFire-specific fields ---
|
| 501 |
if name == "CTaskFire" {
|
| 502 |
let fire = addr as *const CTaskFire;
|
|
@@ -11,7 +11,7 @@ use std::sync::atomic::{AtomicU32, Ordering};
|
|
| 11 |
|
| 12 |
use openwa_core::rebase::rb;
|
| 13 |
use openwa_core::address::va;
|
| 14 |
-
use openwa_core::task::{CTask, CGameTask, CTaskTurnGame, SharedDataTable};
|
| 15 |
use openwa_core::ddgame::DDGame;
|
| 16 |
use openwa_core::ddgame_wrapper::DDGameWrapper;
|
| 17 |
|
|
@@ -297,6 +297,11 @@ fn validate_struct_offsets(result: &mut ValidationResult) {
|
|
| 297 |
check_offset!(result, DDGameWrapper, landscape, 0x4CC);
|
| 298 |
check_offset!(result, DDGameWrapper, display, 0x4D0);
|
| 299 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 300 |
let _ = log_validation("");
|
| 301 |
let _ = log_validation(" CTaskTurnGame:");
|
| 302 |
check_offset!(result, CTaskTurnGame, worm_active, 0x108);
|
|
|
|
| 11 |
|
| 12 |
use openwa_core::rebase::rb;
|
| 13 |
use openwa_core::address::va;
|
| 14 |
+
use openwa_core::task::{CTask, CGameTask, CTaskTeam, CTaskTurnGame, SharedDataTable};
|
| 15 |
use openwa_core::ddgame::DDGame;
|
| 16 |
use openwa_core::ddgame_wrapper::DDGameWrapper;
|
| 17 |
|
|
|
|
| 297 |
check_offset!(result, DDGameWrapper, landscape, 0x4CC);
|
| 298 |
check_offset!(result, DDGameWrapper, display, 0x4D0);
|
| 299 |
|
| 300 |
+
let _ = log_validation("");
|
| 301 |
+
let _ = log_validation(" CTaskTeam:");
|
| 302 |
+
check_offset!(result, CTaskTeam, team_index, 0x38);
|
| 303 |
+
check_offset!(result, CTaskTeam, worm_count, 0x218);
|
| 304 |
+
|
| 305 |
let _ = log_validation("");
|
| 306 |
let _ = log_validation(" CTaskTurnGame:");
|
| 307 |
check_offset!(result, CTaskTurnGame, worm_active, 0x108);
|