Crusader_Decomp/docs/entity-class-family-split.md
2026-04-05 18:27:09 +02:00

5.6 KiB

Entity Class Family Split

Purpose

This note breaks the large seg001 Entity lane into a conservative class-family model that can later be promoted in Ghidra or emitted as C++ without pretending that every vtable and helper belongs to one monolithic base class.

Current goal is not full inheritance recovery. Current goal is to identify the safest boundaries between:

  • one shared gameplay-entity core layout
  • projectile-specific allocation and movement behavior
  • debris/corpse variants
  • registry/helper surfaces that look adjacent but should not be merged automatically

Core Shared Entity Object

Strongest current common object is the gameplay entity body documented in docs/ne-segment1.md.

Stable shared anchors:

  • 0007:3f2f entity_spawn
  • 0007:40d4 entity_remove
  • 0007:4552 entity_set_position
  • 0007:4591 entity_try_place
  • 0007:5092 entity_deactivate

Stable shared fields from the current note set include:

  • +0x00 vtable pointer
  • +0x02 slot index
  • +0x04 entity type
  • +0x19/+0x1a flags
  • +0x3c sprite handle
  • +0x45/+0x47/+0x49 world position
  • +0x4f/+0x51/+0x53 base position
  • +0x54/+0x56/+0x58 previous position

This is the safest current candidate for a future Entity or ActorBase style root.

Candidate Split

1. Entity base gameplay family

Best current scope:

  • allocation/spawn and placement
  • common position, flags, facing, and sprite ownership
  • generic remove/deactivate behavior
  • registry-facing slot identity

Best current vtable anchor:

  • generic/AI entity vtable 0x29aa

Current caution:

  • this family likely includes several behaviorally different actors, but the verified note set still supports one shared base before the split gets more specific.

2. ShotEntity or projectile-derived family

Strong anchors:

  • 0007:28ce shot_entity_alloc
  • 0007:44a9 shot_entity_free
  • 0007:4659 projectile_init_vector
  • 0007:4b78 projectile_check_hit
  • 0007:4c2e projectile_step_update
  • 0007:4d28 projectile_trace_ray
  • 0007:51ad projectile_update_tick
  • 0007:5a99 projectile_apply_hit

Best current distinct evidence:

  • dedicated vtable 0x297e
  • extra projectile ownership/target fields through +0x6a..+0xbd
  • separate shot sprite handle at +0x3f
  • dedicated cleanup path in shot_entity_free

Current safest interpretation:

  • projectile objects are not just one mode of the generic entity vtable; they deserve at least one derived-family model with their own ctor/free/update surface.

3. DebrisEntity family

Strong anchors:

  • 0007:7490 debris_spawn
  • 0007:75ff entity_die

Best current distinct evidence:

  • dedicated debris/fragment vtable 0x2a57
  • corpse/debris-adjacent vtable pair 0x2a1a and 0x2a33
  • death-spawn path uses separate velocity/facing behavior rather than only the generic entity update lane

Current safest interpretation:

  • debris should stay separate from ShotEntity; both share movement-style fields but not the same lifecycle intent.

4. CorpseEntity or actor-remnant family

Strong current evidence:

  • vtable pair 0x2a1a and 0x2a33
  • adjacency to entity_die and debris-spawn behavior

Current caution:

  • the notes support a corpse/remnant family, but not yet a crisp split between static remains, actor corpse, and debris fragments.
  • keep this as a provisional derived branch until a dedicated caller pass closes object lifetime more tightly.

5. Adjacent but probably separate: dialog/menu object lane

Anchor:

  • 0007:2c92 dialog_spawn

Why it should stay separate:

  • vtable 0x28b5
  • callback registration at 0x39ca
  • behavior is UI/dialog-style rather than ordinary gameplay entity movement or projectile logic

Current safest interpretation:

  • this object is near the entity notes because it lives in the same broad segment lane, but it should not be promoted under the core gameplay Entity family automatically.

Registry And Helper Surfaces That Should Not Be Mis-modeled

Entity registry vtable 0x2969

Current evidence in docs/ne-segment1.md shows 0x2969 stored at 0x39ca + slot*4 as a registry vtable rather than as the entity instance's primary vtable.

That means:

  • do not treat 0x2969 as a normal Entity virtual table
  • keep registry or handle-table behavior separate from per-instance inheritance

Pure helpers that should remain free functions for now

Examples:

  • snap_entity_to_ground
  • spawn_entity_checked
  • map_find_spawn_point
  • actor_find_in_view

These may operate on entities or produce entities, but current evidence still reads better as subsystem helpers than as obvious instance methods.

  1. model the shared Entity base layout first
  2. split ShotEntity next because its ctor/free/update lane is strongest
  3. split debris/corpse branches only after one more caller-side lifetime pass
  4. leave dialog/menu object modeling separate from the entity inheritance tree

Source-Emission Guidance

If this family is emitted to provisional C++ later, safest first skeleton is:

  • one Entity base struct/class with the stable common layout
  • one ShotEntity derived placeholder
  • one DebrisEntity derived placeholder
  • one unresolved CorpseLikeEntity placeholder if needed
  • separate DialogMenuObject class rather than folding it into the gameplay entity tree

Bottom Line

The current evidence strongly supports a shared gameplay entity core, but it does not support flattening generic actor, projectile, debris, corpse, and dialog/menu behavior into one class.

The right near-term move is base first, derived families second, adjacent objects separate.