- Статус
- Оффлайн
- Регистрация
- 13 Фев 2026
- Сообщения
- 458
- Реакции
- 10
Народ, кто плотно ковыряет серию Far Cry (начиная с тройки и выше), в курсе, что там под капотом вовсю юзается Havok Engine. Если вам нужно вытащить энтити для ESP или реализовать телепорт, проще всего это сделать через RTTI самого Havok и его структурированный лайаут.
Основная фишка в том, что энтити в Havok имеют четкую иерархию. Нам интересен hkpWorld, который хранит массив активных hkpSimulationIsland. В этих «островах» и лежат реальные объекты: игроки, NPC, животные.
Техническое мясо по структурам:
Логика обновления энтити-листа:
Проходимся по всем островам симуляции, чекаем наличие капсулы и закидываем в свой массив. Это гораздо стабильнее, чем пытаться парсить игровые массивы через Pawn, если вы только начали реверсить бинарник.
Полезные сигнатуры (актуально для FC3, на других частях нужно подправлять):
Если хотите копнуть глубже в сторону оригинальных игровых классов, ищите функции LUA в IDA. Там полно инфы по Pawn-ам. И не забывайте про старые PDB от Xbox версии — структуры там практически идентичные PC билдам.
Кто уже пробовал этот метод в FC6, насколько сильно там перепахали hkpWorld?
Основная фишка в том, что энтити в Havok имеют четкую иерархию. Нам интересен hkpWorld, который хранит массив активных hkpSimulationIsland. В этих «островах» и лежат реальные объекты: игроки, NPC, животные.
Техническое мясо по структурам:
- Все полезные для нас объекты (игроки, боты) имеют hkpCapsuleShape по офсету 0x10. Это идеальный фильтр, чтобы отсеять всякий статический мусор типа деревьев.
- У энтити есть поинтер на объект перемещения по офсету 0x18. Сами координаты (Vector3) лежат внутри этого объекта по офсету 0x30.
- По офсету 0xE0 часто висит вектор позиции, доступный для записи (удобно для телепорта).
Код:
namespace HAVOK
{
class hkpEntity
{
public:
Vector3 GetPosition()
{
auto pos_class = *(uintptr_t*)((uintptr_t)this + 0x18);
if (!pos_class) return { 0, 0, 0 };
return *(Vector3*)(pos_class + 0x30);
}
bool HasCapsuleShape()
{
static void* hkpCapsuleShapeVFT = reinterpret_cast<void*>(OFFSETS::GameBase + OFFSETS::hkpCapsuleVFT);
auto shape = *(uintptr_t**)((uintptr_t)this + 0x10);
return (shape && *shape == hkpCapsuleShapeVFT);
}
};
class hkpWorld
{
public:
char pad_10[0x10];
Vector3 gravity;
char pad_1c[0x4];
uintptr_t* fixedSimulationIsland;
uintptr_t* fixedRigidBody;
Array<class hkpSimulationIsland*> activeSimulationIslands;
static hkpWorld* GetWorld()
{
static uintptr_t StartingAddr = (uintptr_t)(OFFSETS::GameBase + 0x01EE9114);
return (hkpWorld*)(ResolvePTR(StartingAddr, { 0x3C, 0x11C, 0x48, 0x18, 0xC8, 0x4, 0x0 }));
}
};
}
Логика обновления энтити-листа:
Проходимся по всем островам симуляции, чекаем наличие капсулы и закидываем в свой массив. Это гораздо стабильнее, чем пытаться парсить игровые массивы через Pawn, если вы только начали реверсить бинарник.
Код:
void UpdateHavokEntities()
{
HavokEntities.clear();
auto world = HAVOK::hkpWorld::GetWorld();
if (!world) return;
for (int i = 0; i < world->activeSimulationIslands.Num(); i++)
{
auto island = world->activeSimulationIslands[i];
if (!island) continue;
for (int j = 0; j < island->Entities.Num(); j++)
{
auto entity = island->Entities[j];
if (!entity || !entity->HasCapsuleShape()) continue;
HavokEntities.push_back(entity);
}
}
}
Полезные сигнатуры (актуально для FC3, на других частях нужно подправлять):
- Локальный игрок в Havok (ESI после брейкпоинта на позиции):
Код:0F 29 86 ? ? ? ? 8B 40 ? D9 40 ? 8B 11 DC 0D - GetAllPawns (для получения игровых объектов):
Код:B8 ? ? ? ? C3 55 8B EC 8B 41 ? 8B 88 ? ? ? ? 8B 45
Если хотите копнуть глубже в сторону оригинальных игровых классов, ищите функции LUA в IDA. Там полно инфы по Pawn-ам. И не забывайте про старые PDB от Xbox версии — структуры там практически идентичные PC билдам.
Кто уже пробовал этот метод в FC6, насколько сильно там перепахали hkpWorld?