CFramework/CFramework/Cheat/SDK/CPlayer/CPlayer.cpp

238 lines
8.2 KiB
C++

#include "CPlayer.h"
std::vector<int> PlayerBoneList{ BONE_PELVIS, BONE_LEFT_THIGH, BONE_LEFT_FOOT, BONE_RIGHT_THIGH, BONE_RIGHT_FOOT, BONE_LEFT_FOREARM, BONE_LEFT_PALM, BONE_RIGHT_FOREARM, BONE_RIGHT_PALM };
namespace
{
uintptr_t ReadBifacialOriginal(uintptr_t playerBones, uintptr_t fieldOffset)
{
const uintptr_t bifacialTransform = m.Read<uintptr_t>(playerBones + fieldOffset);
return bifacialTransform
? m.Read<uintptr_t>(bifacialTransform + offset::BifacialTransform::Original)
: 0;
}
uintptr_t ReadTransformArrayElement(uintptr_t array, int index)
{
if (!array || index < 0 || index > 1)
return 0;
return m.Read<uintptr_t>(array + offset::PlayerBones::TransformArrayData + static_cast<uintptr_t>(index) * sizeof(uintptr_t));
}
Vector3 ReadTransformPositionWithFallback(uintptr_t transform)
{
if (!transform)
return {};
Vector3 position = GetTransformPosition(transform);
if (!Vec3_Empty(position))
return position;
// Keep compatibility with the previous Transform wrapper layout.
uintptr_t wrappedTransform = m.Read<uintptr_t>(transform + 0x10);
if (!wrappedTransform || wrappedTransform == transform)
return {};
return GetTransformPosition(wrappedTransform);
}
}
bool CPlayer::Update()
{
m_vecOrigin = GetBonePosition(BONE_ORIGIN);
return !Vec3_Empty(m_vecOrigin);
}
void CPlayer::UpdateStatic()
{
m_pHealthController = 0;
m_pProfile = 0;
m_pInfo = 0;
m_pSetting = 0;
m_pPlayerBody = 0;
m_pPlayerBones = 0;
m_pWeaponAnimation = 0;
m_iSpawnType = 0;
m_pBoneArray = {};
m_vecBoneList.clear();
if (!m_address)
return;
// EFT.Player fields from dump.txt.
m_pHealthController = m.Read<uintptr_t>(m_address + offset::Player::pHealthController);
m_pWeaponAnimation = m.Read<uintptr_t>(m_address + offset::Player::pWeaponAnimation);
m_pPlayerBody = m.Read<uintptr_t>(m_address + offset::Player::pPlayerBody);
if (m_pPlayerBody)
m_pPlayerBones = m.Read<uintptr_t>(m_pPlayerBody + offset::PlayerBody::PlayerBones);
m_pProfile = m.Read<uintptr_t>(m_address + offset::Player::pProfile);
if (m_pProfile)
{
m_pInfo = m.Read<uintptr_t>(m_pProfile + offset::Profile::pInfoClass);
if (m_pInfo)
{
m_pSetting = m.Read<uintptr_t>(m_pInfo + offset::InfoClass::profileInfoSettingsClass);
if (m_pSetting)
m_iSpawnType = m.Read<int>(m_pSetting + 0x10); // ProfileInfoSettingsClass.Role
}
}
// PlayerBody +0x30 = SkeletonRootJoint; Skeleton +0x30 = values List;
// List +0x10 = Transform[] data pointer, as described by dump.txt.
const uintptr_t boneMatrix = m.ReadChain(m_address, { offset::Player::pPlayerBody, offset::PlayerBody::SkeletonRootJoint, 0x30, 0x10 });
if (boneMatrix)
m_pBoneArray = m.Read<CBoneArray>(boneMatrix + 0x20);
}
void CPlayer::UpdateHealth()
{
m_fHealth = 0.f;
m_fHealthMax = 0.f;
if (!m_address)
return;
const uintptr_t healthController = m_pHealthController != 0
? m_pHealthController
: m.Read<uintptr_t>(m_address + offset::Player::pHealthController);
if (!healthController)
return;
// GClass3009<T>.Dictionary_0_1 contains BodyPartState<T> entries.
const uintptr_t bodyParts = m.Read<uintptr_t>(healthController + offset::HealthController::BodyParts);
if (!bodyParts)
return;
const uintptr_t entries = m.Read<uintptr_t>(bodyParts + offset::HealthController::DictionaryEntries);
const int entryCount = m.Read<int>(bodyParts + offset::HealthController::DictionaryCount);
if (!entries || entryCount <= 0 || entryCount > 64)
return;
for (int i = 0; i < entryCount; ++i)
{
const uintptr_t entry = entries + offset::HealthController::ArrayData
+ static_cast<uintptr_t>(i) * offset::HealthController::EntryStride;
const uintptr_t bodyPartState = m.Read<uintptr_t>(entry + offset::HealthController::EntryValue);
if (!bodyPartState)
continue;
const uintptr_t healthValue = m.Read<uintptr_t>(bodyPartState + offset::HealthController::BodyPartHealth);
if (!healthValue)
continue;
m_fHealth += m.Read<float>(healthValue + offset::HealthController::HealthValue + offset::HealthController::Current);
m_fHealthMax += m.Read<float>(healthValue + offset::HealthController::HealthValue + offset::HealthController::Maximum);
}
}
void CPlayer::UpdateBone()
{
// Refresh the direct PlayerBody -> PlayerBones link before reading the selected aim bone.
// The list thread is intentionally slow, while animation/weapon objects can be replaced sooner.
if (m_address)
{
uintptr_t playerBody = 0;
uintptr_t playerBones = 0;
if (m.TryRead(m_address + offset::Player::pPlayerBody, playerBody) && playerBody &&
m.TryRead(playerBody + offset::PlayerBody::PlayerBones, playerBones) && playerBones)
{
m_pPlayerBody = playerBody;
m_pPlayerBones = playerBones;
}
}
m_vecHeadOrigin = GetBonePosition(BONE_HEAD);
m_vecNeckOrigin = GetBonePosition(BONE_NECK);
m_vecBoneList.clear();
if (!g.g_ESP_Skeleton)
return;
m_vecBoneList.reserve(PlayerBoneList.size());
for (const auto id : PlayerBoneList)
{
const Vector3 bone = GetBonePosition(id);
if (Vec3_Empty(bone))
{
m_vecBoneList.clear();
return;
}
m_vecBoneList.push_back(bone);
}
}
bool CPlayer::IsAiming()
{
return m_pWeaponAnimation != 0 && m.Read<bool>(m_pWeaponAnimation + offset::WeaponAnimations::isAiming);
}
Vector3 CPlayer::GetBonePosition(const int BoneId)
{
constexpr int kBoneCount = static_cast<int>(sizeof(m_pBoneArray.address) / sizeof(m_pBoneArray.address[0]));
if (BoneId < 0 || BoneId >= kBoneCount)
return {};
uintptr_t transform = 0;
if (m_pPlayerBones)
{
switch (BoneId)
{
case BONE_ORIGIN:
transform = m.Read<uintptr_t>(m_pPlayerBones + offset::PlayerBones::RootJoint);
break;
case BONE_NECK:
transform = m.Read<uintptr_t>(m_pPlayerBones + offset::PlayerBones::Neck);
break;
case BONE_HEAD:
transform = ReadBifacialOriginal(m_pPlayerBones, offset::PlayerBones::Head);
break;
case BONE_PELVIS:
transform = ReadBifacialOriginal(m_pPlayerBones, offset::PlayerBones::Pelvis);
break;
case BONE_LEFT_THIGH:
transform = ReadBifacialOriginal(m_pPlayerBones, offset::PlayerBones::LeftThigh);
break;
case BONE_RIGHT_THIGH:
transform = ReadBifacialOriginal(m_pPlayerBones, offset::PlayerBones::RightThigh);
break;
case BONE_LEFT_FOREARM:
transform = ReadTransformArrayElement(m.Read<uintptr_t>(m_pPlayerBones + offset::PlayerBones::Forearms), 0);
break;
case BONE_RIGHT_FOREARM:
transform = ReadTransformArrayElement(m.Read<uintptr_t>(m_pPlayerBones + offset::PlayerBones::Forearms), 1);
break;
case BONE_LEFT_PALM:
transform = m.Read<uintptr_t>(m_pPlayerBones + offset::PlayerBones::LeftPalm);
break;
case BONE_RIGHT_PALM:
transform = m.Read<uintptr_t>(m_pPlayerBones + offset::PlayerBones::RightPalm);
break;
case BONE_SPINE_3:
transform = ReadBifacialOriginal(m_pPlayerBones, offset::PlayerBones::Ribcage);
break;
default:
break;
}
}
// Feet and any unmapped legacy IDs fall back to the current Skeleton.values array.
// The array elements are Transform references; do not dereference +0x10 here.
if (!transform)
transform = m_pBoneArray.address[BoneId];
const Vector3 position = ReadTransformPositionWithFallback(transform);
if (!Vec3_Empty(position))
return position;
const uintptr_t legacyTransform = m_pBoneArray.address[BoneId];
if (!legacyTransform || legacyTransform == transform)
return {};
return ReadTransformPositionWithFallback(legacyTransform);
}