File size: 2,802 Bytes
f3cbb8a
5c51f8a
f3cbb8a
94ea94e
 
 
 
 
 
 
 
c6d4e73
 
 
 
 
 
 
 
1d3de83
 
c6d4e73
 
 
 
 
 
f3cbb8a
 
 
 
 
 
 
 
 
 
 
045679a
f3cbb8a
 
 
c6d4e73
f3cbb8a
 
 
 
 
 
 
 
 
 
5c51f8a
f3cbb8a
5c51f8a
f3cbb8a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c6d4e73
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
use super::base::CTask;
use crate::{fixed::Fixed, FieldRegistry};

crate::define_addresses! {
    class "CTaskFire" {
        /// CTaskFire vtable - fire/flame entity (0xD8 bytes)
        vtable CTASK_FIRE_VTABLE = 0x0066_9DD8;
        ctor CTASK_FIRE_CTOR = 0x0054_F4C0;
    }
}

/// CTaskFire vtable — 12 slots. Extends CTask base (8 slots) with fire behavior.
///
/// Vtable at Ghidra 0x669DD8.
#[openwa_core::vtable(size = 12, va = 0x0066_9DD8, class = "CTaskFire")]
pub struct CTaskFireVTable {
    /// HandleMessage — processes fire messages.
    /// thiscall + 4 stack params, RET 0x10.
    #[slot(2)]
    pub handle_message:
        fn(this: *mut CTaskFire, sender: *mut CTask, msg_type: u32, size: u32, data: *const u8),
    /// ProcessFrame — per-frame fire update (countdown, spread, damage).
    /// thiscall + 1 stack param (flags), RET 0x4.
    #[slot(7)]
    pub process_frame: fn(this: *mut CTaskFire, flags: u32),
}

/// Fire/flame entity task.
///
/// Extends CTask (not CGameTask) — no physics body.
/// class_type = 0x18. Allocated 0xD8 bytes.
/// Constructor: CTaskFire__Constructor (0x54F4C0).
/// vtable: CTaskFire__vtable (0x00669DD8).
///
/// One CTaskFire is spawned per flame sprite.  The `timer` field starts
/// at 0xFFFF and counts down each frame; when it reaches zero the fire
/// dies.  `lifetime` at +0xB1 is a signed byte: 0xFF (= -1i8) means alive,
/// 0 means the task is being destroyed.
#[derive(FieldRegistry)]
#[repr(C)]
pub struct CTaskFire {
    /// 0x00-0x2F: CTask base
    pub base: CTask<*const CTaskFireVTable>,
    /// 0x30: spread counter (incremented while fire is spreading)
    pub spread_counter: i32,
    /// 0x34: frame countdown; starts at 0xFFFF, decrements each ProcessFrame
    pub timer: i32,
    /// 0x38: random seed / initial offset for sprite variation
    pub rand_offset: u32,
    /// 0x3C: burn rate / intensity (higher = more damage per frame)
    pub burn_rate: u32,
    pub _unknown_40: u32,
    /// 0x44: spawn X position (Fixed 16.16)
    pub spawn_x: Fixed,
    /// 0x48: spawn Y position (Fixed 16.16)
    pub spawn_y: Fixed,
    pub _unknown_4c: [u8; 0x24],
    /// 0x70: absolute tick (frame counter) when this flame was spawned
    pub spawn_time: u32,
    pub _unknown_74: u32,
    /// 0x78-0xA7: per-frame spawn parameter table (12 DWORDs)
    pub spawn_params: [u32; 12],
    /// 0xA8: slot index in the fire-object pool
    pub slot_index: u32,
    pub _unknown_ac: u32,
    pub _flags_b0: u8,
    /// 0xB1: lifetime byte; -1 (0xFF as i8) = alive, 0 = dying/dead
    pub lifetime: i8,
    pub _unknown_b2: [u8; 0x26],
}

const _: () = assert!(core::mem::size_of::<CTaskFire>() == 0xD8);

// Generate typed vtable method wrappers: handle_message(), process_frame().
bind_CTaskFireVTable!(CTaskFire, base.vtable);