Nodes (PM)
Nodes are how the world data is organised and structured. Please see the general node overview first. This page describes node data structures for Pirate's Moon only. Refer also to MechWarrior 3 nodes.
Node base/shared structure
Only analysed in the mechlib.
This is the structure used by all nodes, and is 208 bytes in size. Please note that while this is the same size as NodeMw
, the layout is different! Refer to the base game for any other types.
#![allow(unused)] fn main() { struct NodePm { name: [u8; 36], flags: NodeFlags, unk040: u32, // always 0 unk044: u32, zone_id: u32, // always 255 (mechlib only?) node_type: NodeType, data_ptr: u32, mesh_index: i32, environment_data: u32, // always 0 action_priority: u32, // always 1 action_callback: u32, // always 0 area_partition_x: i32, // -1, or >= 0, <= 64 area_partition_y: i32, // -1, or >= 0, <= 64 parent_count: u16, // always 0 or 1 children_count: u16, parent_array_ptr: u32, children_array_ptr: u32, unk096: u32, // always 0 unk100: u32, // always 0 unk104: u32, // always 0 unk108: u32, // always 0 unk112: u32, // 0, 1, 2 (mechlib?) unk116: Box3d, unk140: Box3d, unk164: Box3d, unk188: u32, // always 0 unk192: u32, // always 0 unk196: u32, // always 0x000000A0 (mechlib?) unk200: u32, // always 0 unk204: u32, // always 0 } }
Preliminary analysis of nodes in the mechlib indicates this data structure is largely the same as the base game. The biggest change is around offset 84:
- parent_count: u32, // always 0 or 1
- parent_array_ptr: u32,
- children_count: u32,
- children_array_ptr: u32,
+ parent_count: u16, // always 0 or 1
+ children_count: u16,
+ parent_array_ptr: u32,
+ children_array_ptr: u32,
+ unk096: u32, // always 0
I.e. the parent_count
and children_count
have been changed from u32 values to u16 values. This has shifted the parent and child array pointers, and has introduced an extra field, unk096
(u32), which is always zero (0).
Additionally, the field unk112
(u32) is now variable, but always 0, 1, or 2.
Camera nodes base structure
Not analysed yet.
Display nodes base structure
Not analysed yet.
Empty nodes base structure
Not analysed yet.
Light nodes base structure
Not analysed yet.
LOD nodes base structure
Preliminary analysis of nodes in the mechlib indicates LOD nodes always have the node flags:
BASE
UNK08
UNK10
ALTITUDE_SURFACE
INTERSECT_SURFACE
UNK25
The field unk044
will always be one (1).
The zone ID will be the default zone ID (255), but this is probably down to the mechlib. Assuming the same behaviour as the base game, the zone ID will be either the default zone ID (255), or a value greater than or equal to one (1) and less than or equal to 80 (this upper bound is arbitrarily chosen based on usual zone IDs).
LOD nodes always have data associated with them, so the data pointer will always be non-zero/non-null.
Although LOD nodes cannot have a mesh, the mesh index does depend on whether the node is in a GameZ file or a mechlib archive. For a GameZ file, the mesh index is an index, so it is always negative one (-1). For a mechlib archive, the mesh index is actually a pointer value, since the data is already stored hierarchically. So it is always zero (0). See Object3d nodes for mode information.
There will be one parent, and therefore the parent array pointer is non-zero/non-null. There will be at last one child, and therefore the child array pointer is non-zero/non-null.
The fields unk116
and unk140
will always be zeros (0.0). The field unk164
will be unequal to (0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
.
The field unk112
will always be 2. The field unk196
will always be 160.
Object3d nodes base structure
The field unk044
will be either 1 or 45697.
The zone ID will be the default zone ID (255), but this is probably down to the mechlib. Assuming the same behaviour as the base game, the zone ID will be either the default zone ID (255), or a value greater than or equal to one (1) and less than or equal to 80 (this upper bound is arbitrarily chosen based on usual zone IDs).
Object3d nodes always have data associated with them, so the data pointer will always be non-zero/non-null.
The mesh index depends on the HasMesh flag, and whether the node is in a GameZ file or a mechlib archive. For a GameZ file, the mesh index is an index. So if the flag is set, then the index is greater than or equal to zero (0). If the flag is unset, then the index is always negative one (-1). For a mechlib archive, the mesh index is actually a pointer value, since the data is already stored hierarchically. So if the flag is set, this is non-zero/non-null. If the flag is unset, this is zero/null. Note that for the non-null case, if you are loading the value as a signed integer (i32), the memory on 32-bit machines was limited. In practice, it won't be greater than 2147483647 bytes, so you can also check if the value is greater than zero.
In short:
IsMechlib && !HasMesh
=>mesh_index == 0
(null ptr)IsMechlib && HasMesh
=>mesh_index != 0
(non-null ptr)IsGameZ && !HasMesh
=>mesh_index == -1
(invalid index)IsGameZ && HasMesh
=>mesh_index > -1
(valid index)
The field unk196
will always be 160.
Other fields have not been analysed in detail, since they are liable to change outside the Mechlib.
Window nodes base structure
Not analysed yet.
World nodes base structure
Not analysed yet.
Node type data structures
All nodes except empty nodes have extra, type-specific data associated with them.
Camera data
Not analysed yet.
Display data
Not analysed yet.
Empty data
Not analysed yet.
Light data
Not analysed yet.
LOD data
Only analysed in the mechlib.
#![allow(unused)] fn main() { struct LodPm { level: u32, // always 0 or 1 range_near_sq: f32, range_far: f32, range_far_sq: f32, zero16: [u8; 44], // always 0 unk60: f32, // always == 0.0 unk64: f32, // always >= 0.0 unk68: f32, // always == unk64 * unk64 unk72: f32, // always >= 0.0 unk76: f32, // always == unk72 * unk72 unk80: u32, // always 1 unk84: u32, // always 0 unk88: u32, // always 0 } }
The size of the LOD structure is 92 bytes.
The level field (u32) is always zero (0) or one (1). Usually, this would make it a Boolean, but I think it corresponds to the level of detail setting, so e.g. low and high (hence the name). The near range value (f32) is always greater than or equal to zero (0.0) and less than or equal to 1000.0 squared, so it's assumed this is the near range squared. The far range value is stored as the base value (f32), which is always greater than zero (0.0), and why I suspect this is the far range, and as a squared value (f32). These are guesses at best.
The unk60
field (f32) is always zero (0.0). The unk64
field (f32) is greater than or equal to zero (0.0), while the unk68
field (f32) is this value squared. . The unk72
field (f32) is greater than or equal to zero (0.0), while the unk76
field (f32) is this value squared. The unk80
field (u32) is always one (1). The unk84
field (u32) and unk88
field (u32) are both always zero (0).
Object3d data
Only analysed in the mechlib.
This seems to be the same as the base game.
Window data
Not analysed yet.
World data
Not analysed yet.