fix: restore esp transform fallback

This commit is contained in:
kedou 2026-08-07 21:25:51 +08:00
parent 52454e23ea
commit fcb1f599a4
9 changed files with 145 additions and 18 deletions

View File

@ -258,7 +258,7 @@ Vector3 CFramework::GetAimBone(CPlayer* pEntity)
switch (g.g_AimBone) switch (g.g_AimBone)
{ {
case 1: return pEntity->m_vecNeckOrigin; case 1: return pEntity->m_vecNeckOrigin;
case 2: return pEntity->m_vecOrigin; case 2: return pEntity->GetBonePosition(BONE_SPINE_3); // Ribcage
case 0: case 0:
default: default:
return pEntity->m_vecHeadOrigin; return pEntity->m_vecHeadOrigin;

View File

@ -2,6 +2,42 @@
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 }; 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() bool CPlayer::Update()
{ {
m_vecOrigin = GetBonePosition(BONE_ORIGIN); m_vecOrigin = GetBonePosition(BONE_ORIGIN);
@ -15,6 +51,8 @@ void CPlayer::UpdateStatic()
m_pProfile = 0; m_pProfile = 0;
m_pInfo = 0; m_pInfo = 0;
m_pSetting = 0; m_pSetting = 0;
m_pPlayerBody = 0;
m_pPlayerBones = 0;
m_pWeaponAnimation = 0; m_pWeaponAnimation = 0;
m_iSpawnType = 0; m_iSpawnType = 0;
m_pBoneArray = {}; m_pBoneArray = {};
@ -27,6 +65,10 @@ void CPlayer::UpdateStatic()
m_pHealthController = m.Read<uintptr_t>(m_address + offset::Player::pHealthController); m_pHealthController = m.Read<uintptr_t>(m_address + offset::Player::pHealthController);
m_pWeaponAnimation = m.Read<uintptr_t>(m_address + offset::Player::pWeaponAnimation); 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); m_pProfile = m.Read<uintptr_t>(m_address + offset::Player::pProfile);
if (m_pProfile) if (m_pProfile)
{ {
@ -41,7 +83,7 @@ void CPlayer::UpdateStatic()
// PlayerBody +0x30 = SkeletonRootJoint; Skeleton +0x30 = values List; // PlayerBody +0x30 = SkeletonRootJoint; Skeleton +0x30 = values List;
// List +0x10 = Transform[] data pointer, as described by dump.txt. // List +0x10 = Transform[] data pointer, as described by dump.txt.
const uintptr_t boneMatrix = m.ReadChain(m_address, { offset::Player::pPlayerBody, 0x30, 0x30, 0x10 }); const uintptr_t boneMatrix = m.ReadChain(m_address, { offset::Player::pPlayerBody, offset::PlayerBody::SkeletonRootJoint, 0x30, 0x10 });
if (boneMatrix) if (boneMatrix)
m_pBoneArray = m.Read<CBoneArray>(boneMatrix + 0x20); m_pBoneArray = m.Read<CBoneArray>(boneMatrix + 0x20);
} }
@ -121,13 +163,61 @@ Vector3 CPlayer::GetBonePosition(const int BoneId)
if (BoneId < 0 || BoneId >= kBoneCount) if (BoneId < 0 || BoneId >= kBoneCount)
return {}; return {};
const uintptr_t bone = m_pBoneArray.address[BoneId]; uintptr_t transform = 0;
if (!bone) if (m_pPlayerBones)
return {}; {
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;
}
}
uintptr_t transform = m.Read<uintptr_t>(bone + 0x10); // 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) 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 {};
return GetTransformPosition(transform); return ReadTransformPositionWithFallback(legacyTransform);
} }

View File

@ -13,6 +13,8 @@ public:
uintptr_t m_pProfile{ 0 }; uintptr_t m_pProfile{ 0 };
uintptr_t m_pInfo{ 0 }; uintptr_t m_pInfo{ 0 };
uintptr_t m_pSetting{ 0 }; uintptr_t m_pSetting{ 0 };
uintptr_t m_pPlayerBody{ 0 };
uintptr_t m_pPlayerBones{ 0 };
uintptr_t m_pHealthController{ 0 }; uintptr_t m_pHealthController{ 0 };
uintptr_t m_pWeaponAnimation{ 0 }; uintptr_t m_pWeaponAnimation{ 0 };

View File

@ -25,6 +25,35 @@ namespace offset
constexpr auto pHealthController = 0x660; // _healthController constexpr auto pHealthController = 0x660; // _healthController
} }
// EFT.PlayerBody / EFT.PlayerBones (dump.txt)
namespace PlayerBody
{
constexpr auto PlayerBones = 0x28;
constexpr auto SkeletonRootJoint = 0x30;
}
namespace PlayerBones
{
constexpr auto Neck = 0x48;
constexpr auto Forearms = 0x70;
constexpr auto LeftPalm = 0x78;
constexpr auto RightPalm = 0x80;
constexpr auto RootJoint = 0x90;
constexpr auto Ribcage = 0x110;
constexpr auto Head = 0x118;
constexpr auto Body = 0x140;
constexpr auto Pelvis = 0x150;
constexpr auto LeftThigh = 0x158;
constexpr auto RightThigh = 0x160;
constexpr auto Spine3 = 0x168;
constexpr auto TransformArrayData = 0x20;
}
namespace BifacialTransform
{
constexpr auto Original = 0x10;
}
// EFT.MovementContext (dump.txt: EFT.MovementContext) // EFT.MovementContext (dump.txt: EFT.MovementContext)
namespace MovementContext namespace MovementContext
{ {

View File

@ -39,7 +39,7 @@ struct Globals
float g_AimSens = 1.0f; // Mouse 模式灵敏度倍率 float g_AimSens = 1.0f; // Mouse 模式灵敏度倍率
float g_AimMaxSpeed = 80.f; // Mouse 模式单帧最大移动 [px] float g_AimMaxSpeed = 80.f; // Mouse 模式单帧最大移动 [px]
float g_AimMaxDist = 500.f; // 最大锁定距离 [m] float g_AimMaxDist = 500.f; // 最大锁定距离 [m]
int g_AimBone = 0; // 0=Head 1=Neck 2=Body int g_AimBone = 0; // 0=Head 1=Neck 2=Body (PlayerBones direct transforms)
bool g_AimTargetPMC = true; bool g_AimTargetPMC = true;
bool g_AimTargetScav = true; bool g_AimTargetScav = true;
bool g_AimTargetBoss = true; bool g_AimTargetBoss = true;

View File

@ -24,7 +24,7 @@ bool Overlay::CreateOverlay()
// Create Overlay // Create Overlay
wc = { sizeof(WNDCLASSEXA), 0, WndProc, 0, 0, NULL, NULL, NULL, NULL, Title, Class, NULL}; wc = { sizeof(WNDCLASSEXA), 0, WndProc, 0, 0, NULL, NULL, NULL, NULL, Title, Class, NULL};
RegisterClassExA(&wc); RegisterClassExA(&wc);
Hwnd = CreateWindowExA(WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW | WS_EX_TOPMOST, wc.lpszClassName, wc.lpszMenuName, WS_POPUP | WS_VISIBLE, g.GamePoint.x, g.GamePoint.y, g.GameRect.right, g.GameRect.bottom, NULL, NULL, wc.hInstance, NULL); Hwnd = CreateWindowExA(WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_NOACTIVATE, wc.lpszClassName, wc.lpszMenuName, WS_POPUP | WS_VISIBLE, g.GamePoint.x, g.GamePoint.y, g.GameRect.right, g.GameRect.bottom, NULL, NULL, wc.hInstance, NULL);
SetLayeredWindowAttributes(Hwnd, RGB(0, 0, 0), 255, LWA_ALPHA); SetLayeredWindowAttributes(Hwnd, RGB(0, 0, 0), 255, LWA_ALPHA);
MARGINS margin = { -1 }; MARGINS margin = { -1 };
DwmExtendFrameIntoClientArea(Hwnd, &margin); DwmExtendFrameIntoClientArea(Hwnd, &margin);
@ -117,6 +117,12 @@ extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg
LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{ {
// ESP overlay must never activate or consume the game's mouse input.
if (!g.ShowMenu && msg == WM_NCHITTEST)
return HTTRANSPARENT;
if (!g.ShowMenu && msg == WM_MOUSEACTIVATE)
return MA_NOACTIVATE;
if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam)) if (ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam))
return true; return true;

View File

@ -25,7 +25,7 @@ private:
// Window LONG // Window LONG
LONG MenuStyle = WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TOPMOST; LONG MenuStyle = WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TOPMOST;
LONG ESPStyle = WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TOPMOST; LONG ESPStyle = WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TOPMOST | WS_EX_NOACTIVATE;
bool CreateOverlay(); bool CreateOverlay();
HWND GetTargetWindow(const std::string processName); // 実行ファイル名からウィンドウハンドルを取得する HWND GetTargetWindow(const std::string processName); // 実行ファイル名からウィンドウハンドルを取得する

View File

@ -12,6 +12,7 @@ bool Overlay::InitOverlay(const std::string targetName, int InitMode)
} }
GetClassNameA(TargetHwnd, TargetClass, sizeof(TargetClass)); GetClassNameA(TargetHwnd, TargetClass, sizeof(TargetClass));
g.GameHwnd = TargetHwnd;
return CreateOverlay(); return CreateOverlay();
} }
@ -68,9 +69,9 @@ void Overlay::OverlayManager()
LONG TmpLong = GetWindowLong(Hwnd, GWL_EXSTYLE); LONG TmpLong = GetWindowLong(Hwnd, GWL_EXSTYLE);
if (g.ShowMenu && MenuStyle != TmpLong) if (g.ShowMenu && MenuStyle != TmpLong)
SetWindowLong(Hwnd, GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TOPMOST); SetWindowLong(Hwnd, GWL_EXSTYLE, MenuStyle);
else if (!g.ShowMenu && ESPStyle != TmpLong) else if (!g.ShowMenu && ESPStyle != TmpLong)
SetWindowLong(Hwnd, GWL_EXSTYLE, WS_EX_TRANSPARENT | WS_EX_LAYERED | WS_EX_TOOLWINDOW | WS_EX_TOPMOST); SetWindowLong(Hwnd, GWL_EXSTYLE, ESPStyle);
// MenuToggle // MenuToggle
if (GetKeyState(g.g_MenuKey) && !g.ShowMenu) { if (GetKeyState(g.g_MenuKey) && !g.ShowMenu) {
@ -97,7 +98,7 @@ void Overlay::OverlayManager()
{ {
g.GameRect = TmpRect; g.GameRect = TmpRect;
g.GamePoint = TmpPoint; g.GamePoint = TmpPoint;
SetWindowPos(Hwnd, nullptr, TmpPoint.x, TmpPoint.y, g.GameRect.right, g.GameRect.bottom, SWP_NOREDRAW); SetWindowPos(Hwnd, nullptr, TmpPoint.x, TmpPoint.y, g.GameRect.right, g.GameRect.bottom, SWP_NOREDRAW | SWP_NOACTIVATE);
} }
} }

View File

@ -39,8 +39,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
{ {
auto overlay = std::make_unique<Overlay>(); auto overlay = std::make_unique<Overlay>();
// Fix DPI Scale // 使用每监视器 DPI,保证 overlay、GameRect 和 W2S 使用同一套物理像素坐标。
SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_SYSTEM_AWARE); SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
// ウィンドウをベースにして初期化を行う // ウィンドウをベースにして初期化を行う
if (!m.AttachProcess("EscapeFromTarkov", InitMode::WINDOW_TITLE)) // 詳細は Memory/Memory.h を参照 if (!m.AttachProcess("EscapeFromTarkov", InitMode::WINDOW_TITLE)) // 詳細は Memory/Memory.h を参照
@ -50,8 +50,8 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
if (!overlay->InitOverlay("EscapeFromTarkov", InitMode::WINDOW_TITLE)) // MemoryInitModeと同様 if (!overlay->InitOverlay("EscapeFromTarkov", InitMode::WINDOW_TITLE)) // MemoryInitModeと同様
return 2; return 2;
// 低级输入钩子 (GetAsyncKeyState 检测不到鼠标键的环境用) // 不安装全局 WH_MOUSE_LL 钩子,避免改变游戏原始鼠标输入链路。
InstallInputHook(); // 鼠标按键使用 GetAsyncKeyState,F9 可作为触发键兜底。
// ImGui io setting // ImGui io setting
ImGuiIO& io = ImGui::GetIO(); (void)io; ImGuiIO& io = ImGui::GetIO(); (void)io;
@ -92,7 +92,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine
overlay->OverlayLoop(); overlay->OverlayLoop();
overlay->DestroyOverlay(); overlay->DestroyOverlay();
RemoveInputHook();
m.DetachProcess(); m.DetachProcess();
g.process_active = false; g.process_active = false;