CFramework/CFramework/Cheat/Visual.cpp

400 lines
17 KiB
C++

#include "Framework.h"
#include "../Framework/Input/Input.h"
#include <cstdio>
bool InScreen(const Vector2& vIn)
{
return !(vIn.x < g.GameRect.left || vIn.x > g.GameRect.right ||
vIn.y < g.GameRect.top || vIn.y > g.GameRect.bottom);
}
namespace
{
const char* GetRefreshStatusName(GameRefreshStatus status)
{
switch (status)
{
case GameRefreshStatus::Waiting: return "WAIT";
case GameRefreshStatus::GomUnavailable: return "GOM";
case GameRefreshStatus::NodeListUnavailable: return "NODES";
case GameRefreshStatus::GameWorldUnavailable: return "GW";
case GameRefreshStatus::LocalWorldUnavailable: return "LGW";
case GameRefreshStatus::Ready: return "READY";
default: return "UNKNOWN";
}
}
ImColor GetItemDisplayColor(const CItem& item,
const ImColor& questColor, const ImColor& commonColor,
const ImColor& rareColor, const ImColor& superRareColor,
const ImColor& corpseColor)
{
if (item.m_bIsCorpse)
return corpseColor;
if (item.m_bIsQuestItem)
return questColor;
// JsonType.ELootRarity exposes member order only in the dump. These values
// assume the C# enum uses its default sequential numbering: Common=1,
// Rare=2, Superrare=3. Unknown values intentionally fall back to common.
switch (item.m_iRarity)
{
case 2: return rareColor;
case 3: return superRareColor;
case 1:
default: return commonColor;
}
}
bool HasRuntimeDisplayName(const std::string& value, const char* fallback,
const char* legacyFallback)
{
return !value.empty() && value != fallback && value != legacyFallback;
}
std::string GetChineseItemDisplayName(const CItem& item)
{
if (item.m_bIsCorpse)
return "尸体";
if (HasRuntimeDisplayName(item.m_CName, "物品", "Item"))
return item.m_CName;
if (item.m_bIsQuestItem)
return "任务物品";
switch (item.m_iRarity)
{
case 2: return "稀有物品";
case 3: return "超稀有物品";
default: return "物品";
}
}
std::string GetChineseContainerDisplayName(const CContainer& container)
{
return HasRuntimeDisplayName(container.m_CName, "容器", "Container") ?
container.m_CName : "容器";
}
std::string GetChineseExfilDisplayName(const CExfil& exfil)
{
return HasRuntimeDisplayName(exfil.m_pExfilName, "撤离点", "Exfil") ?
exfil.m_pExfilName : "撤离点";
}
ImColor GetExfilDisplayColor(const ExfilDisplayCategory category,
const ImColor& closedColor, const ImColor& openColor,
const ImColor& pendingColor, const ImColor& unknownColor)
{
switch (category)
{
case ExfilDisplayCategory::Closed: return closedColor;
case ExfilDisplayCategory::Open: return openColor;
case ExfilDisplayCategory::Pending: return pendingColor;
default: return unknownColor;
}
}
}
void CFramework::RenderInfo()
{
if (!g.g_DebugMode)
return;
const auto snapshot = GetDynamicSnapshot();
const bool snapshotFresh = snapshot && IsSnapshotFresh(snapshot->capturedAt);
std::string infoText{ "Tarkov PvE | " + std::to_string((int)ImGui::GetIO().Framerate) + "FPS" };
StringEx(ImVec2(8.f, 8.f), ImColor(1.f, 1.f, 1.f, 1.f), ImGui::GetFontSize(), infoText.c_str());
const bool initialized = snapshot && snapshot->initialized;
const bool inRaid = snapshot && snapshot->inRaid;
const uintptr_t localAddress = snapshot ? snapshot->local.m_address : 0;
const std::size_t entityCount = snapshot ? snapshot->players.size() : 0;
const uint64_t ageMs = snapshot ? GetSnapshotAgeMs(snapshot->capturedAt) : kInvalidSnapshotAgeMs;
char ageText[32]{};
if (ageMs == kInvalidSnapshotAgeMs)
snprintf(ageText, sizeof(ageText), "N/A");
else
snprintf(ageText, sizeof(ageText), "%llums", (unsigned long long)ageMs);
char diag[320]{};
snprintf(diag, sizeof(diag),
"St:%s Init:%s Raid:%s Cam:%s Fresh:%s Age:%s Fail:%llu Try:%llu Gen:%llu",
snapshot ? GetRefreshStatusName(snapshot->refreshStatus) : "NONE",
initialized ? "OK" : "FAIL",
inRaid ? "Y" : "N",
(snapshot && snapshot->cameraValid) ? "OK" : "FAIL",
snapshotFresh ? "Y" : "N",
ageText,
(unsigned long long)(snapshot ? snapshot->consecutiveRefreshFailures : 0),
(unsigned long long)(snapshot ? snapshot->refreshAttempts : 0),
(unsigned long long)(snapshot ? snapshot->generation : 0));
StringEx(ImVec2(8.f, 24.f), ImColor(1.f, 1.f, 0.f, 1.f), ImGui::GetFontSize(), diag);
char pointers[320]{};
snprintf(pointers, sizeof(pointers),
"B:%llX GOM:%llX N:%llX GW:%llX LGW:%llX L:%llX E:%zu",
(unsigned long long)(snapshot ? snapshot->moduleBase : 0),
(unsigned long long)(snapshot ? snapshot->gameObjectManager : 0),
(unsigned long long)(snapshot ? snapshot->activeNodeList : 0),
(unsigned long long)(snapshot ? snapshot->gameWorld : 0),
(unsigned long long)(snapshot ? snapshot->localGameWorld : 0),
(unsigned long long)localAddress,
entityCount);
StringEx(ImVec2(8.f, 40.f), ImColor(0.3f, 0.9f, 1.f, 1.f), ImGui::GetFontSize(), pointers);
char aim[192]{};
snprintf(aim, sizeof(aim),
"Aim:%s M:%s F:%s Tgt:%s d=%.0fpx",
g.g_Aim ? "ON" : "OFF",
g.g_AimMethod == 0 ? "MEM" : "MOU",
g.g_AimForce ? "FORCE" : "KEY",
m_diagBestAddr ? "LOCK" : "NONE",
m_diagBestDist);
StringEx(ImVec2(8.f, 58.f), ImColor(1.f, 0.f, 0.f, 1.f), ImGui::GetFontSize(), aim);
if (g.g_Crosshair && snapshotFresh && snapshot->local.IsValid() && !snapshot->local.IsAiming())
{
switch (g.g_CrosshairType)
{
case 0:
{
ImVec2 center = ImVec2(g.GameRect.right / 2.f, g.GameRect.bottom / 2.f);
ImGui::GetBackgroundDrawList()->AddLine(ImVec2(center.x - g.g_CrosshairSize, center.y), ImVec2(center.x + g.g_CrosshairSize + 1.f, center.y), CrosshairColor, 1.f);
ImGui::GetBackgroundDrawList()->AddLine(ImVec2(center.x, center.y - g.g_CrosshairSize), ImVec2(center.x, center.y + g.g_CrosshairSize + 1.f), CrosshairColor, 1.f);
break;
}
case 1:
ImGui::GetBackgroundDrawList()->AddCircle(ImVec2((float)g.GameRect.right / 2.f, (float)g.GameRect.bottom / 2.f), g.g_CrosshairSize, CrosshairColor, 0);
break;
default:
break;
}
}
}
void CFramework::RenderESP()
{
const auto snapshot = GetDynamicSnapshot();
if (!snapshot || !IsSnapshotFresh(snapshot->capturedAt) ||
!snapshot->inRaid || !snapshot->cameraValid || !snapshot->local.IsValid())
return;
const CPlayer& localPlayer = snapshot->local;
const Matrix& viewMatrix = snapshot->viewMatrix;
const Vector2 gameSize((float)g.GameRect.right, (float)g.GameRect.bottom);
// Rendering consumes an immutable snapshot: no player/camera RPM is performed in this loop.
for (const auto& entity : snapshot->players)
{
const CPlayer& player = entity;
if (!player.IsValid() || Vec3_Empty(player.m_vecOrigin))
continue;
const float distance = GetDistance(localPlayer.m_vecOrigin, player.m_vecOrigin);
Vector2 screen{}, head{}, neck{};
if (!WorldToScreen(viewMatrix, gameSize, player.m_vecOrigin, screen) || !InScreen(screen))
continue;
if (!WorldToScreen(viewMatrix, gameSize, player.m_vecHeadOrigin, head) ||
!WorldToScreen(viewMatrix, gameSize, player.m_vecNeckOrigin, neck))
continue;
const float headToNeck = neck.y - head.y;
const float height = screen.y - head.y + (headToNeck * 2.5f);
const float width = height / 3.5f;
if (height <= 0.f || width <= 0.f)
continue;
std::string name = "InValid";
ImColor color = ImColor(1.f, 1.f, 1.f, 1.f);
if (player.m_bSpawnTypeValid)
{
GetESPInfo(player.m_iSpawnType, name, color);
}
else if (player.IsObserved())
{
if (player.m_iSide == SIDE_SCAV)
{
name = "Observed SCAV";
color = Col_ESP_Scav;
}
else
{
name = "Observed PMC";
color = Col_ESP_PMC;
}
}
if (player.m_bSpawnTypeValid && player.m_iSpawnType == BTR_VEHICLE)
{
ImGui::GetBackgroundDrawList()->AddCircleFilled(ImVec2(screen.x, screen.y - 2.f), 2.f, ImColor(1.f, 1.f, 1.f, 1.f), 0);
const std::string text = "BTR-82 [" + std::to_string((int)distance) + "m]";
String(ImVec2(screen.x - (ImGui::CalcTextSize(text.c_str()).x / 2.f), screen.y), ImColor(1.f, 1.f, 1.f, 1.f), text.c_str());
continue;
}
if (g.g_ESP_Line)
DrawLine(ImVec2(g.GameRect.right / 2.f, g.GameRect.bottom), ImVec2(screen.x, screen.y), color, 1.f);
if (g.g_ESP_Box)
{
static float boxScale = 1.75f;
if (g.g_ESP_BoxFilled)
ImGui::GetBackgroundDrawList()->AddRectFilled(ImVec2(screen.x - width, screen.y - height), ImVec2(screen.x + width, screen.y), Col_ESP_Filled, 1.f);
switch (g.g_ESP_BoxStyle)
{
case 0:
DrawLine(ImVec2(screen.x - width, screen.y - height), ImVec2(screen.x + width, screen.y - height), color, 1.f);
DrawLine(ImVec2(screen.x - width, screen.y), ImVec2(screen.x - width, screen.y - height), color, 1.f);
DrawLine(ImVec2(screen.x + width, screen.y), ImVec2(screen.x + width, screen.y - height), color, 1.f);
DrawLine(ImVec2(screen.x - width, screen.y), ImVec2(screen.x + width, screen.y), color, 1.f);
break;
case 1:
DrawLine(ImVec2(screen.x - width, screen.y - height), ImVec2(screen.x - width + (width / boxScale), screen.y - height), color, 1.f);
DrawLine(ImVec2(screen.x + width, screen.y - height), ImVec2(screen.x + width - (width / boxScale), screen.y - height), color, 1.f);
DrawLine(ImVec2(screen.x - width, screen.y - height), ImVec2(screen.x - width, screen.y - height + (width / boxScale)), color, 1.f);
DrawLine(ImVec2(screen.x + width, screen.y - height), ImVec2(screen.x + width, screen.y - height + (width / boxScale)), color, 1.f);
DrawLine(ImVec2(screen.x - width, screen.y), ImVec2(screen.x - width, screen.y - (width / boxScale)), color, 1.f);
DrawLine(ImVec2(screen.x + width, screen.y), ImVec2(screen.x + width, screen.y - (width / boxScale)), color, 1.f);
DrawLine(ImVec2(screen.x - width, screen.y), ImVec2(screen.x - width + (width / boxScale), screen.y), color, 1.f);
DrawLine(ImVec2(screen.x + width, screen.y), ImVec2(screen.x + width - (width / boxScale), screen.y), color, 1.f);
break;
default:
break;
}
}
if (g.g_ESP_Skeleton && distance < 250.f && player.m_vecBoneList.size() >= 9)
{
ImGui::GetBackgroundDrawList()->AddCircle(ImVec2(head.x, head.y - (headToNeck / 2.f)), headToNeck * 1.55f, color, 0);
const Vector3 bones[][2]{
{ player.m_vecNeckOrigin, player.m_vecBoneList[CPelvis] },
{ player.m_vecNeckOrigin, player.m_vecBoneList[CLeftForearm] },
{ player.m_vecBoneList[CLeftForearm], player.m_vecBoneList[CLeftPalm] },
{ player.m_vecNeckOrigin, player.m_vecBoneList[CRightForearm] },
{ player.m_vecBoneList[CRightForearm], player.m_vecBoneList[CRightPalm] },
{ player.m_vecBoneList[CPelvis], player.m_vecBoneList[CLeftThigh] },
{ player.m_vecBoneList[CLeftThigh], player.m_vecBoneList[CLeftFoot] },
{ player.m_vecBoneList[CPelvis], player.m_vecBoneList[CRightThigh] },
{ player.m_vecBoneList[CRightThigh], player.m_vecBoneList[CRightFoot] }
};
for (const auto& pair : bones)
{
Vector2 a{}, b{};
if (!WorldToScreen(viewMatrix, gameSize, pair[0], a) || !WorldToScreen(viewMatrix, gameSize, pair[1], b))
continue;
DrawLine(ImVec2(a.x, a.y), ImVec2(b.x, b.y), color, 1.f);
}
}
if (g.g_ESP_Distance)
{
const std::string distanceText = std::to_string((int)distance) + "m";
String(ImVec2(screen.x - (ImGui::CalcTextSize(distanceText.c_str()).x / 2.f), screen.y), ImColor(1.f, 1.f, 1.f, 1.f), distanceText.c_str());
}
if (g.g_ESP_HealthBar && player.m_bHealthValid)
HealthBar(screen.x - width - 4.f, screen.y, 2.f, -height, (int)player.m_fHealth, (int)player.m_fHealthMax);
if (g.g_ESP_Name && (!player.m_bSpawnTypeValid ||
(player.m_iSpawnType != SIDE_SCAV && player.m_iSpawnType != SNIPER_SCAV && player.m_iSpawnType != NORMAL_SCAV)))
{
if (name == "InValid" && player.m_bSpawnTypeValid)
name += "[" + std::to_string(player.m_iSpawnType) + "]";
String(ImVec2(screen.x - (ImGui::CalcTextSize(name.c_str()).x / 2.f), screen.y - height - 14.f), ImColor(1.f, 1.f, 1.f, 1.f), name.c_str());
}
}
if (g.g_ESP_Grenade)
{
for (const auto& grenade : snapshot->grenades)
{
const float distance = GetDistance(localPlayer.m_vecOrigin, grenade.position);
if (distance > 100.f)
continue;
if (distance < 10.f)
String(ImVec2(g.GameRect.right / 2.f - (ImGui::CalcTextSize("[ WARNING ] Grenade!!").x / 2.f), g.GameRect.bottom / 2.f), ImColor(1.f, 0.f, 0.f, 1.f), "[ WARNING ] Grenade!!");
Vector2 screen{};
if (!WorldToScreen(viewMatrix, gameSize, grenade.position, screen))
continue;
const std::string text = "Grenade [" + std::to_string((int)distance) + "m]";
ImGui::GetBackgroundDrawList()->AddCircleFilled(ImVec2(screen.x, screen.y), 2.f, ImColor(1.f, 0.f, 0.f, 1.f), 0);
String(ImVec2(screen.x - (ImGui::CalcTextSize(text.c_str()).x / 2.f), screen.y - 13.f), ImColor(1.f, 0.f, 0.f, 1.f), text.c_str());
}
}
const auto staticSnapshot = GetStaticSnapshot();
if (!staticSnapshot)
return;
if (g.g_ESP_Item)
{
for (const auto& item : staticSnapshot->items)
{
if (item.m_CName.empty())
continue;
if (!g.g_ESP_Corpse && item.m_bIsCorpse)
continue;
const float distance = GetDistance(localPlayer.m_vecOrigin, item.m_vecOrigin);
if (distance > g.g_ESP_MaxItemDistance)
continue;
Vector2 screen{};
if (!WorldToScreen(viewMatrix, gameSize, item.m_vecOrigin, screen) || !InScreen(screen))
continue;
std::string text = GetChineseItemDisplayName(item);
text += " [" + std::to_string((int)distance) + "m]";
const ImColor itemColor = GetItemDisplayColor(item, Col_ESP_ItemQuest, Col_ESP_ItemCommon,
Col_ESP_ItemRare, Col_ESP_ItemSuperRare, Col_ESP_Corpse);
ImGui::GetBackgroundDrawList()->AddCircleFilled(ImVec2(screen.x, screen.y - 2.f), 2.f, itemColor, 0);
String(ImVec2(screen.x - (ImGui::CalcTextSize(text.c_str()).x / 2.f), screen.y), itemColor, text.c_str());
}
}
if (g.g_ESP_Container)
{
for (const auto& container : staticSnapshot->containers)
{
const float distance = GetDistance(localPlayer.m_vecOrigin, container.m_vecOrigin);
if (distance > g.g_ESP_MaxContainerDistance)
continue;
Vector2 screen{};
if (!WorldToScreen(viewMatrix, gameSize, container.m_vecOrigin, screen) || !InScreen(screen))
continue;
const std::string text = GetChineseContainerDisplayName(container) + " [" + std::to_string((int)distance) + "m]";
ImGui::GetBackgroundDrawList()->AddCircleFilled(ImVec2(screen.x, screen.y - 2.f), 2.f, Col_ESP_Container, 0);
String(ImVec2(screen.x - (ImGui::CalcTextSize(text.c_str()).x / 2.f), screen.y), Col_ESP_Container, text.c_str());
}
}
if (g.g_ESP_Exfil)
{
for (const auto& exfil : staticSnapshot->exfils)
{
Vector2 screen{};
if (!WorldToScreen(viewMatrix, gameSize, exfil.m_pVecLocation, screen) || !InScreen(screen))
continue;
const ExfilDisplayCategory category = ClassifyExfilStatus(exfil.m_pExfilStatus);
const ImColor exfilColor = GetExfilDisplayColor(category, Col_ESP_ExfilClose,
Col_ESP_ExfilOpen, Col_ESP_ExfilPending, Col_ESP_ExfilUnknown);
const float distance = GetDistance(localPlayer.m_vecOrigin, exfil.m_pVecLocation);
const std::string text = GetChineseExfilDisplayName(exfil) + " [" + GetExfilStatusText(exfil.m_pExfilStatus) + "] [" +
std::to_string((int)distance) + "m]";
ImGui::GetBackgroundDrawList()->AddCircleFilled(ImVec2(screen.x, screen.y - 2.f), 2.f, exfilColor, 0);
String(ImVec2(screen.x - (ImGui::CalcTextSize(text.c_str()).x / 2.f), screen.y), exfilColor, text.c_str());
}
}
}