- Status
- Offline
- Joined
- Mar 3, 2026
- Messages
- 447
- Reaction score
- 7
Digging through some old archives and found my notes from when I was reversing Far Cry Primal. If you're planning to dig into this title, it's actually fairly straightforward because it utilizes the Havok Physics engine. Since bits of Havok's source are floating around the web, you can cross-reference these classes easily.
Engine Overview
The physics implementation handles a lot of the heavy lifting. You'll find that gravity and world simulation are mapped directly through the hkpWorld class. This is your gateway to the simulation islands and entity listeners.
Structure: hkpWorld
This class handles the environmental physics. Note the gravity vector and the simulation island array.
Structure: hkpCharacterProxy
Inherits from hkReferencedObject. This is where the movement data lives—velocity, friction, mass, and the shape phantom.
PlayerEntity & Positioning
The PlayerEntity is a common parameter in position update functions. Use the world pointer to get to the physics state.
These structures should save you some time in IDA or ReClass. The inheritance chain is pretty standard for Havok-based titles, but the offsets for the CharacterProxy inside the PlayerEntity are the real meat here.
Anyone else digging into the entity listeners at 0x1B8?
Engine Overview
The physics implementation handles a lot of the heavy lifting. You'll find that gravity and world simulation are mapped directly through the hkpWorld class. This is your gateway to the simulation islands and entity listeners.
Structure: hkpWorld
This class handles the environmental physics. Note the gravity vector and the simulation island array.
Code:
class hkpWorld
{
public:
char pad_00[0x20];
Vector4 m_gravity; // Game gravity starting at 0x20
char pad_11[0x10];
class hkArray<class hkpSimulationIsland*> activeSimulationIslands; // 0x40
// Entity listeners at 0x1B8 might contain all entities
};
Structure: hkpCharacterProxy
Inherits from hkReferencedObject. This is where the movement data lives—velocity, friction, mass, and the shape phantom.
Code:
class hkpCharacterProxy : public hkReferencedObject
{
public:
char pad_00[0x54];
Vector4 Velocity;
Vector4 oldDisplacment;
class hkpShapePhantom* shapePhantom;
float dynamicFriction;
float staticFriction;
Vector4 up;
float extraUpStaticFriction;
float extraDownStaticFriction;
float characterMass;
};
PlayerEntity & Positioning
The PlayerEntity is a common parameter in position update functions. Use the world pointer to get to the physics state.
Code:
class PlayerEntity
{
public:
char pad_0000[0x10];
class hkpWorld* m_world; // 0x0010
char pad_0018[0x108];
Vector3 Position; // 0x0120
hkpCharacterProxy* GetCharacterProxy()
{
// Access via class + 0xB8, then pointer at 0x8
return *reinterpret_cast<hkpCharacterProxy**>(*(uintptr_t*)((uintptr_t)this + 0xB8) + 0x8);
}
};
If you want to pull entities from the simulation islands, you can use a simple loop like this:
Code:
auto islands = localPlayer->m_world->activeSimulationIslands;
for (int i = 0; i < islands.size(); i++)
{
auto island = islands[i];
if (island)
printf("entities found at: [%llx]\n", island->m_entities);
}
These structures should save you some time in IDA or ReClass. The inheritance chain is pretty standard for Havok-based titles, but the offsets for the CharacterProxy inside the PlayerEntity are the real meat here.
Anyone else digging into the entity listeners at 0x1B8?