This commit is contained in:
Oscar
2025-07-29 20:23:06 +03:00
parent 860be9ac4c
commit 5717c7999c
253 changed files with 873847 additions and 0 deletions

View File

@@ -0,0 +1,97 @@
#pragma once
#include <iostream>
// used: MEM_PAD and virtual funcs
#include "../utilities/memory.h"
// used: CUtlBuffer
#include "../sdk/datatypes/utlbuffer.h"
// used: CBaseUserCmdPB
#include "../sdk/datatypes/usercmd.h";
namespace CRC
{
struct CInButtonStateNoVTable
{
public:
std::uint64_t nValue;
std::uint64_t nValueChanged;
std::uint64_t nValueScroll;
};
struct SavedData_t
{
CInButtonStateNoVTable nButtons;
QAngle_t angView;
};
inline SavedData_t savedData;
inline void Save(CBaseUserCmdPB* pBaseCmd)
{
if (pBaseCmd->pViewAngles != nullptr)
savedData.angView = pBaseCmd->pViewAngles->angValue;
savedData.nButtons.nValue = pBaseCmd->pInButtonState->nValue;
savedData.nButtons.nValueChanged = pBaseCmd->pInButtonState->nValueChanged;
savedData.nButtons.nValueScroll = pBaseCmd->pInButtonState->nValueScroll;
}
inline void Apply(CUserCmd* pCmd)
{
CBaseUserCmdPB* pBaseCmd = pCmd->csgoUserCmd.pBaseCmd;
if (pBaseCmd == nullptr)
return;
pCmd->nButtons.nValue = savedData.nButtons.nValue;
pCmd->nButtons.nValueChanged = savedData.nButtons.nValueChanged;
pCmd->nButtons.nValueScroll = savedData.nButtons.nValueScroll;
if (pBaseCmd->pViewAngles != nullptr)
pBaseCmd->pViewAngles->angValue = savedData.angView;
}
inline bool CalculateCRC(CBaseUserCmdPB* pBaseCmd)
{
int nCalcualtedCRCSize = pBaseCmd->CalculateCmdCRCSize();
CUtlBuffer protobufBuffer(0, 0, 0);
protobufBuffer.EnsureCapacity(nCalcualtedCRCSize + 1);
using fnSerializePartialToArray = bool(__fastcall*)(CBaseUserCmdPB*, CUtlBuffer, int);
static const fnSerializePartialToArray oSerializePartialToArray = reinterpret_cast<fnSerializePartialToArray>(MEM::FindPattern(CLIENT_DLL, CS_XOR("48 89 5C 24 18 55 56 57 48 81 EC 90")));
#ifdef CS_PARANOID
CS_ASSERT(oSerializePartialToArray != nullptr);
#endif
if (oSerializePartialToArray(pBaseCmd, protobufBuffer, nCalcualtedCRCSize))
{
std::uintptr_t* pMessage = reinterpret_cast<uintptr_t*>(I::MemAlloc->Alloc(0x18));
pBaseCmd->nCachedBits |= 1;
auto nHasBits = static_cast<uint32_t>(pBaseCmd->nHasBits & 0xFFFFFFFFFFFFFFFC);
if ((pBaseCmd->nHasBits & 1) != 0)
nHasBits = static_cast<uint32_t>(nHasBits);
using fnWriteMessage = void(__fastcall*)(std::uintptr_t*, CUtlBuffer, int);
static const fnWriteMessage oWriteMessage = reinterpret_cast<fnWriteMessage>(MEM::FindPattern(CLIENT_DLL, CS_XOR("48 89 5C 24 10 48 89 6C 24 18 48 89 7C 24 20 41 56 48 83 EC 20 48 BF")));
#ifdef CS_PARANOID
CS_ASSERT(oWriteMessage != nullptr);
#endif
using fnSetMessageData = std::string*(__fastcall*)(void*, std::uintptr_t*, void*);
static const fnSetMessageData oSetMessageData = reinterpret_cast<fnSetMessageData>(MEM::FindPattern(CLIENT_DLL, CS_XOR("48 89 5C 24 20 55 56 57 48 83 EC 30 49")));
#ifdef CS_PARANOID
CS_ASSERT(oSetMessageData != nullptr);
#endif
oWriteMessage(pMessage, protobufBuffer, nCalcualtedCRCSize);
pBaseCmd->strMoveCrc = oSetMessageData(&pBaseCmd->strMoveCrc, pMessage, &nHasBits);
I::MemAlloc->Free(pMessage);
return true;
}
return false;
}
}

View File

@@ -0,0 +1,9 @@
#include "legitbot.h"
// used: movement callback
#include "legitbot/aim.h"
void F::LEGITBOT::OnMove(CUserCmd* pCmd, CBaseUserCmdPB* pBaseCmd, CCSPlayerController* pLocalController, C_CSPlayerPawn* pLocalPawn)
{
AIM::OnMove(pCmd, pBaseCmd, pLocalController, pLocalPawn);
}

View File

@@ -0,0 +1,10 @@
#pragma once
class CUserCmd;
class CBaseUserCmdPB;
class CCSPlayerController;
class C_CSPlayerPawn;
namespace F::LEGITBOT
{
void OnMove(CUserCmd* pCmd, CBaseUserCmdPB* pBaseCmd, CCSPlayerController* pLocalController, C_CSPlayerPawn* pLocalPawn);
}

View File

@@ -0,0 +1,342 @@
#include "aim.h"
// used: sdk entity
#include "../../sdk/entity.h"
#include "../../sdk/interfaces/cgameentitysystem.h"
#include "../../sdk/interfaces/iengineclient.h"
// used: cusercmd
#include "../../sdk/datatypes/usercmd.h"
// used: activation button
#include "../../utilities/inputsystem.h"
// used: cheat variables
#include "../../core/variables.h"
#include "../../sdk/interfaces/cgametracemanager.h"
#include "../../sdk/interfaces/ccsgoinput.h"
void F::LEGITBOT::AIM::OnMove(CUserCmd* pCmd, CBaseUserCmdPB* pBaseCmd, CCSPlayerController* pLocalController, C_CSPlayerPawn* pLocalPawn)
{
// Check if the legitbot is enabled
if (!pLocalController->IsPawnAlive())
return;
if (!C_GET(bool, Vars.bLegitbot))
return;
AimAssist(pBaseCmd, pLocalPawn, pLocalController);
if (!C_GET(bool, Vars.bSilentbot))
return;
SilentAim(pBaseCmd, pLocalPawn, pLocalController);
if (!C_GET(bool, Vars.bTriggerbot))
return;
Triggerbot(pBaseCmd, pLocalPawn, pLocalController);
}
void F::LEGITBOT::AIM::Triggerbot(CBaseUserCmdPB* pCmd, C_CSPlayerPawn* pLocalPawn, CCSPlayerController* pLocalController)
{
// Check if the activation key is down
if (!IPT::IsKeyDown(C_GET(unsigned int, Vars.nTriggerbotActivationKey)))
return;
int iIDEntIndex = pLocalPawn->m_iIDEntIndex();
if (iIDEntIndex == -1)
return;
/*if ((pLocalPawn->m_holdTargetIDTimer().m_timestamp() - pLocalPawn->m_delayTargetIDTimer().m_timestamp() + 0.3) < C_GET(float, Vars.flTriggerbotDelay))
return;*/
C_CSPlayerPawn* pEntity = (C_CSPlayerPawn*)I::GameResourceService->pGameEntitySystem->Get(iIDEntIndex);
if (pEntity == nullptr)
return;
if (pEntity->GetTeam() == 0 || pEntity->GetHealth() <= 0)
return;
if (!pLocalPawn->IsOtherEnemy(pEntity))
return;
pCmd->pInButtonState->nValue |= IN_ATTACK;
}
QAngle_t GetRecoil(CBaseUserCmdPB* pCmd,C_CSPlayerPawn* pLocal)
{
static QAngle_t OldPunch;//get last tick AimPunch angles
if (pLocal->GetShotsFired() >= 1)//only update aimpunch while shooting
{
QAngle_t viewAngles = pCmd->pViewAngles->angValue;
QAngle_t delta = viewAngles - (viewAngles + (OldPunch - (pLocal->GetAimPunchAngle() * 2.f)));//get current AimPunch angles delta
return pLocal->GetAimPunchAngle() * 2.0f;//return correct aimpunch delta
}
else
{
return QAngle_t{ 0, 0 ,0};//return 0 if is not shooting
}
}
QAngle_t GetAngularDifference(CBaseUserCmdPB* pCmd, Vector_t vecTarget, C_CSPlayerPawn* pLocal)
{
// The current position
Vector_t vecCurrent = pLocal->GetEyePosition();
// The new angle
QAngle_t vNewAngle = (vecTarget - vecCurrent).ToAngles();
vNewAngle.Normalize(); // Normalise it so we don't jitter about
// Store our current angles
QAngle_t vCurAngle = pCmd->pViewAngles->angValue;
// Find the difference between the two angles (later useful when adding smoothing)
vNewAngle -= vCurAngle;
return vNewAngle;
}
float GetAngularDistance(CBaseUserCmdPB* pCmd, Vector_t vecTarget, C_CSPlayerPawn* pLocal)
{
return GetAngularDifference(pCmd, vecTarget, pLocal).Length2D();
}
void F::LEGITBOT::AIM::AimAssist(CBaseUserCmdPB* pUserCmd, C_CSPlayerPawn* pLocalPawn, CCSPlayerController* pLocalController)
{
// Check if the activation key is down
if (!IPT::IsKeyDown(C_GET(unsigned int, Vars.nLegitbotActivationKey)) && !C_GET(bool, Vars.bLegitbotAlwaysOn))
return;
// The current best distance
float flDistance = INFINITY;
// The target we have chosen
CCSPlayerController* pTarget = nullptr;
// Cache'd position
Vector_t vecBestPosition = Vector_t();
// Entity loop
const int iHighestIndex = I::GameResourceService->pGameEntitySystem->GetHighestEntityIndex();
auto aimPunch = GetRecoil(pUserCmd, pLocalPawn); //get AimPunch angles
for (int nIndex = 1; nIndex <= iHighestIndex; nIndex++)
{
// Get the entity
C_BaseEntity* pEntity = I::GameResourceService->pGameEntitySystem->Get(nIndex);
if (pEntity == nullptr)
continue;
// Get the class info
SchemaClassInfoData_t* pClassInfo = nullptr;
pEntity->GetSchemaClassInfo(&pClassInfo);
if (pClassInfo == nullptr)
continue;
// Get the hashed name
const FNV1A_t uHashedName = FNV1A::Hash(pClassInfo->szName);
// Make sure they're a player controller
if (uHashedName != FNV1A::HashConst("CCSPlayerController"))
continue;
// Cast to player controller
CCSPlayerController* pPlayer = reinterpret_cast<CCSPlayerController*>(pEntity);
if (pPlayer == nullptr)
continue;
// Check the entity is not us
if (pPlayer == pLocalController)
continue;
// Get the player pawn
C_CSPlayerPawn* pPawn = I::GameResourceService->pGameEntitySystem->Get<C_CSPlayerPawn>(pPlayer->GetPawnHandle());
if (pPawn == nullptr)
continue;
// Make sure they're alive
if (!pPlayer->IsPawnAlive())
continue;
// Check if they're an enemy
if (!pLocalPawn->IsOtherEnemy(pPawn))
continue;
// Check if they're dormant
CGameSceneNode* pCGameSceneNode = pPawn->GetGameSceneNode();
if (pCGameSceneNode == nullptr || pCGameSceneNode->IsDormant())
continue;
// Get the position
// Firstly, get the skeleton
CSkeletonInstance* pSkeleton = pCGameSceneNode->GetSkeletonInstance();
if (pSkeleton == nullptr)
continue;
// Now the bones
Matrix2x4_t* pBoneCache = pSkeleton->pBoneCache;
if (pBoneCache == nullptr)
continue;
const int iBone = 6; // You may wish to change this dynamically but for now let's target the head.
// Get the bone's position
Vector_t vecPos = pBoneCache->GetOrigin(iBone);
// @note: this is a simple example of how to check if the player is visible
// initialize trace, construct filterr and initialize ray
GameTrace_t trace = GameTrace_t();
TraceFilter_t filter = TraceFilter_t(0x1C3003, pLocalPawn, nullptr, 4);
Ray_t ray = Ray_t();
// cast a ray from local player eye positon -> player head bone
// @note: would recommend checking for nullptrs
I::GameTraceManager->TraceShape(&ray, pLocalPawn->GetEyePosition(), pPawn->GetGameSceneNode()->GetSkeletonInstance()->pBoneCache->GetOrigin(6), &filter, &trace);
// check if the hit entity is the one we wanted to check and if the trace end point is visible
if (trace.m_pHitEntity != pPawn || !trace.IsVisible())// if invisible, skip this entity
continue;
// Get the distance/weight of the move
float flCurrentDistance = GetAngularDistance(pUserCmd, vecPos, pLocalPawn);
if (flCurrentDistance > C_GET(float, Vars.flAimRange))// Skip if this move out of aim range
continue;
if (pTarget && flCurrentDistance > flDistance) // Override if this is the first move or if it is a better move
continue;
// Better move found, override.
pTarget = pPlayer;
flDistance = flCurrentDistance;
vecBestPosition = vecPos;
}
// Check if a target was found
if (pTarget == nullptr)
return;
// Point at them
QAngle_t* pViewAngles = &(pUserCmd->pViewAngles->angValue); // Just for readability sake!
// Find the change in angles
QAngle_t vNewAngles = GetAngularDifference(pUserCmd, vecBestPosition, pLocalPawn);
// Get the smoothing
const float flSmoothing = C_GET(float, Vars.flSmoothing);
// Apply smoothing and set angles
pViewAngles->x += (vNewAngles.x / flSmoothing) - (aimPunch.x / ((flSmoothing + 9) * 0.1)) ; // minus AimPunch angle to counteract recoil
pViewAngles->y += (vNewAngles.y / flSmoothing) - (aimPunch.y / ((flSmoothing + 9) * 0.1));
pViewAngles->Normalize();
}
void F::LEGITBOT::AIM::SilentAim(CBaseUserCmdPB* pUserCmd, C_CSPlayerPawn* pLocalPawn, CCSPlayerController* pLocalController)
{
// Check if the activation key is down
if (!IPT::IsKeyDown(C_GET(unsigned int, Vars.nLegitbotActivationKey)) && !C_GET(bool, Vars.bLegitbotAlwaysOn))
return;
// The current best distance
float flDistance = INFINITY;
// The target we have chosen
CCSPlayerController* pTarget = nullptr;
// Cache'd position
Vector_t vecBestPosition = Vector_t();
// Entity loop
const int iHighestIndex = I::GameResourceService->pGameEntitySystem->GetHighestEntityIndex();
auto aimPunch = GetRecoil(pUserCmd, pLocalPawn); //get AimPunch angles
for (int nIndex = 1; nIndex <= iHighestIndex; nIndex++)
{
// Get the entity
C_BaseEntity* pEntity = I::GameResourceService->pGameEntitySystem->Get(nIndex);
if (pEntity == nullptr)
continue;
// Get the class info
SchemaClassInfoData_t* pClassInfo = nullptr;
pEntity->GetSchemaClassInfo(&pClassInfo);
if (pClassInfo == nullptr)
continue;
// Get the hashed name
const FNV1A_t uHashedName = FNV1A::Hash(pClassInfo->szName);
// Make sure they're a player controller
if (uHashedName != FNV1A::HashConst("CCSPlayerController"))
continue;
// Cast to player controller
CCSPlayerController* pPlayer = reinterpret_cast<CCSPlayerController*>(pEntity);
if (pPlayer == nullptr)
continue;
// Check the entity is not us
if (pPlayer == pLocalController)
continue;
// Get the player pawn
C_CSPlayerPawn* pPawn = I::GameResourceService->pGameEntitySystem->Get<C_CSPlayerPawn>(pPlayer->GetPawnHandle());
if (pPawn == nullptr)
continue;
// Make sure they're alive
if (!pPlayer->IsPawnAlive())
continue;
// Check if they're an enemy
if (!pLocalPawn->IsOtherEnemy(pPawn))
continue;
// Check if they're dormant
CGameSceneNode* pCGameSceneNode = pPawn->GetGameSceneNode();
if (pCGameSceneNode == nullptr || pCGameSceneNode->IsDormant())
continue;
// Get the position
// Firstly, get the skeleton
CSkeletonInstance* pSkeleton = pCGameSceneNode->GetSkeletonInstance();
if (pSkeleton == nullptr)
continue;
// Now the bones
Matrix2x4_t* pBoneCache = pSkeleton->pBoneCache;
if (pBoneCache == nullptr)
continue;
const int iBone = 6; // You may wish to change this dynamically but for now let's target the head.
// Get the bone's position
Vector_t vecPos = pBoneCache->GetOrigin(iBone);
// @note: this is a simple example of how to check if the player is visible
// initialize trace, construct filterr and initialize ray
GameTrace_t trace = GameTrace_t();
TraceFilter_t filter = TraceFilter_t(0x1C3003, pLocalPawn, nullptr, 4);
Ray_t ray = Ray_t();
// cast a ray from local player eye positon -> player head bone
// @note: would recommend checking for nullptrs
I::GameTraceManager->TraceShape(&ray, pLocalPawn->GetEyePosition(), pPawn->GetGameSceneNode()->GetSkeletonInstance()->pBoneCache->GetOrigin(6), &filter, &trace);
// check if the hit entity is the one we wanted to check and if the trace end point is visible
if (trace.m_pHitEntity != pPawn || !trace.IsVisible()) // if invisible, skip this entity
continue;
// Get the distance/weight of the move
float flCurrentDistance = GetAngularDistance(pUserCmd, vecPos, pLocalPawn);
if (flCurrentDistance > C_GET(float, Vars.flSilentRange)) // Skip if this move out of aim range
continue;
if (pTarget && flCurrentDistance > flDistance) // Override if this is the first move or if it is a better move
continue;
// Better move found, override.
pTarget = pPlayer;
flDistance = flCurrentDistance;
vecBestPosition = vecPos;
}
// Check if a target was found
if (pTarget == nullptr)
return;
// Point at them
QAngle_t pViewAngles = pUserCmd->pViewAngles->angValue;
// Find the change in angles
QAngle_t vNewAngles = GetAngularDifference(pUserCmd, vecBestPosition, pLocalPawn);
I::Input->GetUserCmd()->SetSubTickAngle({ pViewAngles.x + vNewAngles.x - aimPunch.x, pViewAngles.y + vNewAngles.y - aimPunch.y });
}

View File

@@ -0,0 +1,21 @@
#pragma once
class CUserCmd;
class CBaseUserCmdPB;
class CCSGOInputHistoryEntryPB;
class CCSPlayerController;
class C_CSPlayerPawn;
struct QAngle_t;
namespace F::LEGITBOT::AIM
{
void OnMove(CUserCmd* pCmd, CBaseUserCmdPB* pBaseCmd, CCSPlayerController* pLocalController, C_CSPlayerPawn* pLocalPawn);
void AimAssist(CBaseUserCmdPB* pUserCmd, C_CSPlayerPawn* pLocalPawn, CCSPlayerController* pLocalController);
void RCS(CBaseUserCmdPB* pUserCmd, C_CSPlayerPawn* pLocalPawn, CCSPlayerController* pLocalController);
void SilentAim(CBaseUserCmdPB* pUserCmd, C_CSPlayerPawn* pLocalPawn, CCSPlayerController* pLocalController);
void Triggerbot(CBaseUserCmdPB* pCmd, C_CSPlayerPawn* pLocalPawn, CCSPlayerController* pLocalController);
}

View File

@@ -0,0 +1,22 @@
#pragma once
#include "painter.h"
#include "../../core/variables.h"
#include "../../core/interfaces.h"
#include "../../sdk/interfaces/iswapchaindx11.h"
class AimRangePainter : public Painter
{
public:
virtual void Draw(ImDrawList* pDrawList) override
{
if (C_GET(bool, Vars.bLegitbot))
{
DXGI_SWAP_CHAIN_DESC sd;
I::SwapChain->pDXGISwapChain->GetDesc(&sd);
pDrawList->AddCircle({ sd.BufferDesc.Width / 2.f, sd.BufferDesc.Height / 2.f }, C_GET(float , Vars.flAimRange), IM_COL32(255, 0, 0, 255), 0.3);
}
}
};

View File

@@ -0,0 +1,10 @@
#pragma once
class CUserCmd;
class CBaseUserCmdPB;
class CCSPlayerController;
class C_CSPlayerPawn;
namespace F::LEGITBOT
{
void OnMove(CBaseUserCmdPB* pBaseCmd, CCSPlayerController* pLocalController, C_CSPlayerPawn* pLocalPawn);
}

View File

@@ -0,0 +1,6 @@
#include "painter.h"
#include "../../core/sdk.h"
#include "../../sdk/interfaces/iengineclient.h"
Painter::Painter()
{
}

View File

@@ -0,0 +1,10 @@
#pragma once
#include "../../utilities/draw.h"
class Painter
{
public:
Painter();
virtual void Draw(ImDrawList* pDrawList) = 0;
virtual ~Painter() = default;
};

View File

@@ -0,0 +1,10 @@
#include "misc.h"
// used: movement callback
#include "misc/movement.h"
void F::MISC::OnMove(CUserCmd* pCmd, CBaseUserCmdPB* pBaseCmd, CCSPlayerController* pLocalController, C_CSPlayerPawn* pLocalPawn)
{
// process movement
MOVEMENT::OnMove(pCmd, pBaseCmd, pLocalController, pLocalPawn);
}

View File

@@ -0,0 +1,10 @@
#pragma once
class CUserCmd;
class CBaseUserCmdPB;
class CCSPlayerController;
class C_CSPlayerPawn;
namespace F::MISC
{
void OnMove(CUserCmd* pCmd, CBaseUserCmdPB* pBaseCmd, CCSPlayerController* pLocalController, C_CSPlayerPawn* pLocalPawn);
}

View File

@@ -0,0 +1,187 @@
#include "movement.h"
// used: sdk entity
#include "../../sdk/entity.h"
// used: cusercmd
#include "../../sdk/datatypes/usercmd.h"
// used: convars
#include "../../core/convars.h"
#include "../../sdk/interfaces/ienginecvar.h"
// used: cheat variables
#include "../../core/variables.h"
// movement correction angles
static QAngle_t angCorrectionView = {};
void F::MISC::MOVEMENT::OnMove(CUserCmd* pCmd, CBaseUserCmdPB* pBaseCmd, CCSPlayerController* pLocalController, C_CSPlayerPawn* pLocalPawn)
{
if (!pLocalController->IsPawnAlive())
return;
// check if player is in noclip or on ladder or in water
if (const int32_t nMoveType = pLocalPawn->GetMoveType(); nMoveType == MOVETYPE_NOCLIP || nMoveType == MOVETYPE_LADDER || pLocalPawn->GetWaterLevel() >= WL_WAIST)
return;
BunnyHop(pCmd, pBaseCmd, pLocalPawn);
AutoStrafe(pBaseCmd, pLocalPawn);
// loop through all tick commands
for (int nSubTick = 0; nSubTick < pCmd->csgoUserCmd.inputHistoryField.pRep->nAllocatedSize; nSubTick++)
{
CCSGOInputHistoryEntryPB* pInputEntry = pCmd->GetInputHistoryEntry(nSubTick);
if (pInputEntry == nullptr)
continue;
// save view angles for movement correction
angCorrectionView = pInputEntry->pViewAngles->angValue;
// movement correction & anti-untrusted
ValidateUserCommand(pCmd, pBaseCmd, pInputEntry);
}
}
void F::MISC::MOVEMENT::BunnyHop(CUserCmd* pCmd, CBaseUserCmdPB* pUserCmd, C_CSPlayerPawn* pLocalPawn)
{
if (!C_GET(bool, Vars.bAutoBHop) || CONVAR::sv_autobunnyhopping->value.i1)
return;
// update random seed
//MATH::fnRandomSeed(pUserCmd->nRandomSeed);
//// bypass of possible SMAC/VAC server anticheat detection
//if (static bool bShouldFakeJump = false; bShouldFakeJump)
//{
// pCmd->nButtons.nValue |= IN_JUMP;
// bShouldFakeJump = false;
//}
//// check is player want to jump
//else if (pCmd->nButtons.nValue & IN_JUMP)
//{
// // check is player on the ground
// if (pLocalPawn->GetFlags() & FL_ONGROUND)
// // note to fake jump at the next tick
// bShouldFakeJump = true;
// // check did random jump chance passed
// else if (MATH::fnRandomInt(0, 100) <= C_GET(int, Vars.nAutoBHopChance))
// pCmd->nButtons.nValue &= ~IN_JUMP;
//}
// im lazy so yea :D
if (pLocalPawn->GetFlags() & FL_ONGROUND)
{
pUserCmd->pInButtonState->SetBits(EButtonStatePBBits::BUTTON_STATE_PB_BITS_BUTTONSTATE1);
pUserCmd->pInButtonState->nValue &= ~IN_JUMP;
}
}
void F::MISC::MOVEMENT::AutoStrafe(CBaseUserCmdPB* pUserCmd, C_CSPlayerPawn* pLocalPawn)
{
if (!C_GET(bool, Vars.bAutoStrafe) || pLocalPawn->GetFlags() & FL_ONGROUND)
return;
pUserCmd->SetBits(EBaseCmdBits::BASE_BITS_LEFTMOVE);
pUserCmd->flSideMove = pUserCmd->nMousedX > 0 ? -1.0f : 1.0f; // a bit yanky, but works
}
void F::MISC::MOVEMENT::ValidateUserCommand(CUserCmd* pCmd, CBaseUserCmdPB* pUserCmd, CCSGOInputHistoryEntryPB* pInputEntry)
{
if (pUserCmd == nullptr)
return;
// clamp angle to avoid untrusted angle
if (C_GET(bool, Vars.bAntiUntrusted))
{
pInputEntry->SetBits(EInputHistoryBits::INPUT_HISTORY_BITS_VIEWANGLES);
if (pInputEntry->pViewAngles->angValue.IsValid())
{
pInputEntry->pViewAngles->angValue.Clamp();
pInputEntry->pViewAngles->angValue.z = 0.f;
}
else
{
pInputEntry->pViewAngles->angValue = {};
L_PRINT(LOG_WARNING) << CS_XOR("view angles have a NaN component, the value is reset");
}
}
MovementCorrection(pUserCmd, pInputEntry, angCorrectionView);
// correct movement buttons while player move have different to buttons values
// clear all of the move buttons states
pUserCmd->pInButtonState->SetBits(EButtonStatePBBits::BUTTON_STATE_PB_BITS_BUTTONSTATE1);
pUserCmd->pInButtonState->nValue &= (~IN_FORWARD | ~IN_BACK | ~IN_LEFT | ~IN_RIGHT);
// re-store buttons by active forward/side moves
if (pUserCmd->flForwardMove > 0.0f)
pUserCmd->pInButtonState->nValue |= IN_FORWARD;
else if (pUserCmd->flForwardMove < 0.0f)
pUserCmd->pInButtonState->nValue |= IN_BACK;
if (pUserCmd->flSideMove > 0.0f)
pUserCmd->pInButtonState->nValue |= IN_RIGHT;
else if (pUserCmd->flSideMove < 0.0f)
pUserCmd->pInButtonState->nValue |= IN_LEFT;
if (!pInputEntry->pViewAngles->angValue.IsZero())
{
const float flDeltaX = std::remainderf(pInputEntry->pViewAngles->angValue.x - angCorrectionView.x, 360.f);
const float flDeltaY = std::remainderf(pInputEntry->pViewAngles->angValue.y - angCorrectionView.y, 360.f);
float flPitch = CONVAR::m_pitch->value.fl;
float flYaw = CONVAR::m_yaw->value.fl;
float flSensitivity = CONVAR::sensitivity->value.fl;
if (flSensitivity == 0.0f)
flSensitivity = 1.0f;
pUserCmd->SetBits(EBaseCmdBits::BASE_BITS_MOUSEDX);
pUserCmd->nMousedX = static_cast<short>(flDeltaX / (flSensitivity * flPitch));
pUserCmd->SetBits(EBaseCmdBits::BASE_BITS_MOUSEDY);
pUserCmd->nMousedY = static_cast<short>(-flDeltaY / (flSensitivity * flYaw));
}
}
void F::MISC::MOVEMENT::MovementCorrection(CBaseUserCmdPB* pUserCmd, CCSGOInputHistoryEntryPB* pInputEntry, const QAngle_t& angDesiredViewPoint)
{
if (pUserCmd == nullptr)
return;
Vector_t vecForward = {}, vecRight = {}, vecUp = {};
angDesiredViewPoint.ToDirections(&vecForward, &vecRight, &vecUp);
// we don't attempt on forward/right roll, and on up pitch/yaw
vecForward.z = vecRight.z = vecUp.x = vecUp.y = 0.0f;
vecForward.NormalizeInPlace();
vecRight.NormalizeInPlace();
vecUp.NormalizeInPlace();
Vector_t vecOldForward = {}, vecOldRight = {}, vecOldUp = {};
pInputEntry->pViewAngles->angValue.ToDirections(&vecOldForward, &vecOldRight, &vecOldUp);
// we don't attempt on forward/right roll, and on up pitch/yaw
vecOldForward.z = vecOldRight.z = vecOldUp.x = vecOldUp.y = 0.0f;
vecOldForward.NormalizeInPlace();
vecOldRight.NormalizeInPlace();
vecOldUp.NormalizeInPlace();
const float flPitchForward = vecForward.x * pUserCmd->flForwardMove;
const float flYawForward = vecForward.y * pUserCmd->flForwardMove;
const float flPitchSide = vecRight.x * pUserCmd->flSideMove;
const float flYawSide = vecRight.y * pUserCmd->flSideMove;
const float flRollUp = vecUp.z * pUserCmd->flUpMove;
// solve corrected movement speed
pUserCmd->SetBits(EBaseCmdBits::BASE_BITS_FORWARDMOVE);
pUserCmd->flForwardMove = vecOldForward.x * flPitchSide + vecOldForward.y * flYawSide + vecOldForward.x * flPitchForward + vecOldForward.y * flYawForward + vecOldForward.z * flRollUp;
pUserCmd->SetBits(EBaseCmdBits::BASE_BITS_LEFTMOVE);
pUserCmd->flSideMove = vecOldRight.x * flPitchSide + vecOldRight.y * flYawSide + vecOldRight.x * flPitchForward + vecOldRight.y * flYawForward + vecOldRight.z * flRollUp;
pUserCmd->SetBits(EBaseCmdBits::BASE_BITS_UPMOVE);
pUserCmd->flUpMove = vecOldUp.x * flYawSide + vecOldUp.y * flPitchSide + vecOldUp.x * flYawForward + vecOldUp.y * flPitchForward + vecOldUp.z * flRollUp;
}

View File

@@ -0,0 +1,22 @@
#pragma once
class CUserCmd;
class CBaseUserCmdPB;
class CCSGOInputHistoryEntryPB;
class CCSPlayerController;
class C_CSPlayerPawn;
struct QAngle_t;
namespace F::MISC::MOVEMENT
{
void OnMove(CUserCmd* pCmd, CBaseUserCmdPB* pBaseCmd, CCSPlayerController* pLocalController, C_CSPlayerPawn* pLocalPawn);
void BunnyHop(CUserCmd* pCmd, CBaseUserCmdPB* pUserCmd, C_CSPlayerPawn* pLocalPawn);
void AutoStrafe(CBaseUserCmdPB* pUserCmd, C_CSPlayerPawn* pLocalPawn);
void MovementCorrection(CBaseUserCmdPB* pUserCmd, CCSGOInputHistoryEntryPB* pInputHistory, const QAngle_t& angDesiredViewPoint);
// will call MovementCorrection && validate user's angView to avoid untrusted ban
void ValidateUserCommand(CUserCmd* pCmd, CBaseUserCmdPB* pUserCmd, CCSGOInputHistoryEntryPB* pInputHistory);
}

View File

@@ -0,0 +1,60 @@
#include "visuals.h"
// used: source sdk
#include "../sdk/interfaces/iengineclient.h"
#include "../sdk/entity.h"
// used: overlay
#include "visuals/overlay.h"
#include "visuals/chams.h"
#include "../core/sdk.h"
#include "../core/variables.h"
#include "legitbot/aimRangePainter.hpp"
using namespace F;
bool F::VISUALS::Setup()
{
if (!CHAMS::Initialize())
{
L_PRINT(LOG_ERROR) << CS_XOR("failed to initialize chams");
return false;
}
return true;
}
void F::VISUALS::OnDestroy()
{
CHAMS::Destroy();
}
void VISUALS::OnFrame(const int nStage)
{
if (nStage == FRAME_RENDER_END)
{
// check is render initialized
if (!D::bInitialized)
return;
/*
* game and our gui are based on immediate render mode principe
* this means that we should always reset draw data from previous frame and re-store it again
*/
D::ResetDrawData();
AimRangePainter aimRangePainter;
if (C_GET(bool, Vars.bShowRange))
aimRangePainter.Draw(D::pDrawListActive);
if (CCSPlayerController* pLocal = CCSPlayerController::GetLocalPlayerController(); pLocal != nullptr)
{
OVERLAY::OnFrameStageNotify(pLocal);
}
D::SwapDrawData();
}
}
bool F::VISUALS::OnDrawObject(void* pAnimatableSceneObjectDesc, void* pDx11, CMeshData* arrMeshDraw, int nDataCount, void* pSceneView, void* pSceneLayer, void* pUnk, void* pUnk2)
{
return CHAMS::OnDrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);
}

View File

@@ -0,0 +1,12 @@
#pragma once
class CMeshData;
namespace F::VISUALS
{
bool Setup();
void OnDestroy();
void OnFrame(const int nStage);
bool OnDrawObject(void* pAnimatableSceneObjectDesc, void* pDx11, CMeshData* arrMeshDraw, int nDataCount, void* pSceneView, void* pSceneLayer, void* pUnk, void* pUnk2);
}

View File

@@ -0,0 +1,134 @@
#include "chams.h"
// used: game's interfaces
#include "../../core/interfaces.h"
#include "../../sdk/interfaces/imaterialsystem.h"
#include "../../sdk/interfaces/igameresourceservice.h"
#include "../../sdk/interfaces/cgameentitysystem.h"
#include "../../sdk/interfaces/iresourcesystem.h"
#include "../../core/sdk.h"
#include "../../sdk/entity.h"
// used: original call in hooked function
#include "../../core/hooks.h"
// used: cheat variables
#include "../../core/variables.h"
CStrongHandle<CMaterial2> F::VISUALS::CHAMS::CreateMaterial(const char* szMaterialName, const char szVmatBuffer[])
{
CKeyValues3* pKeyValues3 = CKeyValues3::CreateMaterialResource();
pKeyValues3->LoadFromBuffer(szVmatBuffer);
CStrongHandle<CMaterial2> pCustomMaterial = {};
MEM::fnCreateMaterial(nullptr, &pCustomMaterial, szMaterialName, pKeyValues3, 0, 1);
return pCustomMaterial;
}
struct CustomMaterial_t
{
CStrongHandle<CMaterial2> pMaterial;
CStrongHandle<CMaterial2> pMaterialInvisible;
};
static CustomMaterial_t arrMaterials[VISUAL_MATERIAL_MAX];
bool F::VISUALS::CHAMS::Initialize()
{
// check if we already initialized materials
if (bInitialized)
return bInitialized;
arrMaterials[VISUAL_MATERIAL_PRIMARY_WHITE] = CustomMaterial_t{
.pMaterial = CreateMaterial(CS_XOR("materials/dev/primary_white.vmat"), szVMatBufferWhiteVisible),
.pMaterialInvisible = CreateMaterial(CS_XOR("materials/dev/primary_white.vmat"), szVMatBufferWhiteInvisible)
};
arrMaterials[VISUAL_MATERIAL_ILLUMINATE] = CustomMaterial_t{
.pMaterial = CreateMaterial(CS_XOR("materials/dev/primary_white.vmat"), szVMatBufferIlluminateVisible),
.pMaterialInvisible = CreateMaterial(CS_XOR("materials/dev/primary_white.vmat"), szVMatBufferIlluminateInvisible)
};
bInitialized = true;
for (auto& [pMaterial, pMaterialInvisible] : arrMaterials)
{
if (pMaterial == nullptr || pMaterialInvisible == nullptr)
bInitialized = false;
}
return bInitialized;
}
void F::VISUALS::CHAMS::Destroy()
{
for (auto& [pVisible, pInvisible] : arrMaterials)
{
if (pVisible)
I::ResourceHandleUtils->DeleteResource(pVisible.pBinding);
if (pInvisible)
I::ResourceHandleUtils->DeleteResource(pInvisible.pBinding);
}
}
bool F::VISUALS::CHAMS::OnDrawObject(void* pAnimatableSceneObjectDesc, void* pDx11, CMeshData* arrMeshDraw, int nDataCount, void* pSceneView, void* pSceneLayer, void* pUnk, void* pUnk2)
{
if (!bInitialized)
return false;
if (!C_GET(bool, Vars.bVisualChams))
return false;
if (arrMeshDraw == nullptr)
return false;
if (arrMeshDraw->pSceneAnimatableObject == nullptr)
return false;
CBaseHandle hOwner = arrMeshDraw->pSceneAnimatableObject->hOwner;
auto pEntity = I::GameResourceService->pGameEntitySystem->Get<C_BaseEntity>(hOwner);
if (pEntity == nullptr)
return false;
SchemaClassInfoData_t* pClassInfo;
pEntity->GetSchemaClassInfo(&pClassInfo);
if (pClassInfo == nullptr)
return false;
if (CRT::StringCompare(pClassInfo->szName, CS_XOR("C_CSPlayerPawn")) != 0)
return false;
auto pPlayerPawn = I::GameResourceService->pGameEntitySystem->Get<C_CSPlayerPawn>(hOwner);
if (pPlayerPawn == nullptr)
return false;
if (!pPlayerPawn->IsOtherEnemy(SDK::LocalPawn))
return false;
// alive state
if (pPlayerPawn->GetHealth() <= 0)
return false;
return OverrideMaterial(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);
}
bool F::VISUALS::CHAMS::OverrideMaterial(void* pAnimatableSceneObjectDesc, void* pDx11, CMeshData* arrMeshDraw, int nDataCount, void* pSceneView, void* pSceneLayer, void* pUnk, void* pUnk2)
{
const auto oDrawObject = H::hkDrawObject.GetOriginal();
const CustomMaterial_t customMaterial = arrMaterials[C_GET(int, Vars.nVisualChamMaterial)];
if (C_GET(bool, Vars.bVisualChamsIgnoreZ))
{
arrMeshDraw->pMaterial = customMaterial.pMaterialInvisible;
arrMeshDraw->colValue = C_GET(Color_t, Vars.colVisualChamsIgnoreZ);
oDrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);
}
arrMeshDraw->pMaterial = customMaterial.pMaterial;
arrMeshDraw->colValue = C_GET(Color_t, Vars.colVisualChams);
oDrawObject(pAnimatableSceneObjectDesc, pDx11, arrMeshDraw, nDataCount, pSceneView, pSceneLayer, pUnk, pUnk2);
return true;
}

View File

@@ -0,0 +1,101 @@
#pragma once
// used: stronghandle
#include "../../sdk/datatypes/stronghandle.h"
static constexpr char szVMatBufferWhiteVisible[] =
R"(<!-- kv3 encoding:text:version{e21c7f3c-8a33-41c5-9977-a76d3a32aa0d} format:generic:version{7412167c-06e9-4698-aff2-e63eb59037e7} -->
{
shader = "csgo_unlitgeneric.vfx"
F_PAINT_VERTEX_COLORS = 1
F_TRANSLUCENT = 1
F_BLEND_MODE = 1
g_vColorTint = [1, 1, 1, 1]
TextureAmbientOcclusion = resource:"materials/default/default_mask_tga_fde710a5.vtex"
g_tAmbientOcclusion = resource:"materials/default/default_mask_tga_fde710a5.vtex"
g_tColor = resource:"materials/default/default_mask_tga_fde710a5.vtex"
g_tNormal = resource:"materials/default/default_mask_tga_fde710a5.vtex"
g_tTintMask = resource:"materials/default/default_mask_tga_fde710a5.vtex"
})";
static constexpr char szVMatBufferWhiteInvisible[] =
R"(<!-- kv3 encoding:text:version{e21c7f3c-8a33-41c5-9977-a76d3a32aa0d} format:generic:version{7412167c-06e9-4698-aff2-e63eb59037e7} -->
{
shader = "csgo_unlitgeneric.vfx"
F_PAINT_VERTEX_COLORS = 1
F_TRANSLUCENT = 1
F_BLEND_MODE = 1
F_DISABLE_Z_BUFFERING = 1
g_vColorTint = [1, 1, 1, 1]
TextureAmbientOcclusion = resource:"materials/default/default_mask_tga_fde710a5.vtex"
g_tAmbientOcclusion = resource:"materials/default/default_mask_tga_fde710a5.vtex"
g_tColor = resource:"materials/default/default_mask_tga_fde710a5.vtex"
g_tNormal = resource:"materials/default/default_mask_tga_fde710a5.vtex"
g_tTintMask = resource:"materials/default/default_mask_tga_fde710a5.vtex"
})";
static constexpr char szVMatBufferIlluminateVisible[] =
R"(<!-- kv3 encoding:text:version{e21c7f3c-8a33-41c5-9977-a76d3a32aa0d} format:generic:version{7412167c-06e9-4698-aff2-e63eb59037e7} -->
{
shader = "csgo_complex.vfx"
F_SELF_ILLUM = 1
F_PAINT_VERTEX_COLORS = 1
F_TRANSLUCENT = 1
g_vColorTint = [ 1.000000, 1.000000, 1.000000, 1.000000 ]
g_flSelfIllumScale = [ 3.000000, 3.000000, 3.000000, 3.000000 ]
g_flSelfIllumBrightness = [ 3.000000, 3.000000, 3.000000, 3.000000 ]
g_vSelfIllumTint = [ 10.000000, 10.000000, 10.000000, 10.000000 ]
g_tColor = resource:"materials/default/default_mask_tga_fde710a5.vtex"
g_tNormal = resource:"materials/default/default_mask_tga_fde710a5.vtex"
g_tSelfIllumMask = resource:"materials/default/default_mask_tga_fde710a5.vtex"
TextureAmbientOcclusion = resource:"materials/debug/particleerror.vtex"
g_tAmbientOcclusion = resource:"materials/debug/particleerror.vtex"
})";
static constexpr char szVMatBufferIlluminateInvisible[] =
R"(<!-- kv3 encoding:text:version{e21c7f3c-8a33-41c5-9977-a76d3a32aa0d} format:generic:version{7412167c-06e9-4698-aff2-e63eb59037e7} -->
{
shader = "csgo_complex.vfx"
F_SELF_ILLUM = 1
F_PAINT_VERTEX_COLORS = 1
F_TRANSLUCENT = 1
F_DISABLE_Z_BUFFERING = 1
g_vColorTint = [ 1.000000, 1.000000, 1.000000, 1.000000 ]
g_flSelfIllumScale = [ 3.000000, 3.000000, 3.000000, 3.000000 ]
g_flSelfIllumBrightness = [ 3.000000, 3.000000, 3.000000, 3.000000 ]
g_vSelfIllumTint = [ 10.000000, 10.000000, 10.000000, 10.000000 ]
g_tColor = resource:"materials/default/default_mask_tga_fde710a5.vtex"
g_tNormal = resource:"materials/default/default_mask_tga_fde710a5.vtex"
g_tSelfIllumMask = resource:"materials/default/default_mask_tga_fde710a5.vtex"
TextureAmbientOcclusion = resource:"materials/debug/particleerror.vtex"
g_tAmbientOcclusion = resource:"materials/debug/particleerror.vtex"
})";
class CMaterial2;
class CMeshData;
namespace F::VISUALS::CHAMS
{
bool Initialize();
void Destroy();
bool OnDrawObject(void* pAnimatableSceneObjectDesc, void* pDx11, CMeshData* arrMeshDraw, int nDataCount, void* pSceneView, void* pSceneLayer, void* pUnk, void* pUnk2);
// @note: bDisableZBuffering == true to create invisible material
CStrongHandle<CMaterial2> CreateMaterial(const char* szMaterialName, const char szVmatBuffer[]);
bool OverrideMaterial(void* pAnimatableSceneObjectDesc, void* pDx11, CMeshData* arrMeshDraw, int nDataCount, void* pSceneView, void* pSceneLayer, void* pUnk, void* pUnk2);
inline bool bInitialized = false;
}

View File

@@ -0,0 +1,627 @@
// used: [stl] vector
#include <vector>
// used: [stl] sort
#include <algorithm>
#include "overlay.h"
// used: cheat variables
#include "../../core/variables.h"
// used: entity
#include "../../sdk/entity.h"
#include "../../sdk/interfaces/cgameentitysystem.h"
#include "../../sdk/interfaces/iengineclient.h"
#include "../../sdk/interfaces/cgametracemanager.h"
// used: sdk variables
#include "../../core/sdk.h"
// used: l_print
#include "../../utilities/log.h"
// used: inputsystem
#include "../../utilities/inputsystem.h"
// used: draw system
#include "../../utilities/draw.h"
// used: mainwindowopened
#include "../../core/menu.h"
using namespace F::VISUALS;
#pragma region visual_overlay_components
ImVec2 OVERLAY::CBaseComponent::GetBasePosition(const ImVec4& box) const
{
return { box[this->nSide == SIDE_RIGHT ? SIDE_RIGHT : SIDE_LEFT], box[this->nSide == SIDE_BOTTOM ? SIDE_BOTTOM : SIDE_TOP] };
}
ImVec2 OVERLAY::CBaseDirectionalComponent::GetBasePosition(const ImVec4& box) const
{
ImVec2 vecBasePosition = {};
if (this->nSide == SIDE_TOP || this->nSide == SIDE_BOTTOM)
{
CS_ASSERT(this->nDirection != (this->nSide ^ SIDE_BOTTOM) + 1); // this direction isn't supported for this side
vecBasePosition = { (box[SIDE_LEFT] + box[SIDE_RIGHT]) * 0.5f, box[this->nSide] };
}
else if (this->nSide == SIDE_LEFT || this->nSide == SIDE_RIGHT)
{
CS_ASSERT(this->nDirection != (this->nSide ^ SIDE_RIGHT)); // this direction isn't supported for this side
vecBasePosition = { box[this->nSide], box[this->nDirection == DIR_TOP ? SIDE_BOTTOM : SIDE_TOP] };
}
else
{
L_PRINT(LOG_ERROR) << CS_XOR("CBaseDirectionalComponent::GetBasePosition: invalid side: ") << this->nSide;
CS_ASSERT(false); // this side isn't supported for this component
return vecBasePosition;
}
if (this->nSide != SIDE_RIGHT && this->nDirection != DIR_RIGHT)
vecBasePosition.x -= this->vecSize.x * ((static_cast<std::uint8_t>(this->nDirection) == static_cast<std::uint8_t>(this->nSide) && (this->nSide & 1U) == 1U) ? 0.5f : 1.0f);
if (this->nSide == SIDE_TOP || this->nDirection == DIR_TOP)
vecBasePosition.y -= this->vecSize.y;
return vecBasePosition;
}
OVERLAY::CBarComponent::CBarComponent(const bool bIsMenuItem, const EAlignSide nAlignSide, const ImVec4& vecBox, const float flProgressFactor, const std::size_t uOverlayVarIndex) :
bIsMenuItem(bIsMenuItem), uOverlayVarIndex(uOverlayVarIndex), flProgressFactor(MATH::Clamp(flProgressFactor, 0.f, 1.f))
{
this->nSide = nAlignSide;
const bool bIsHorizontal = ((nAlignSide & 1U) == 1U);
const BarOverlayVar_t& overlayConfig = C_GET(BarOverlayVar_t, uOverlayVarIndex);
this->vecSize = { (bIsHorizontal ? vecBox[SIDE_RIGHT] - vecBox[SIDE_LEFT] : overlayConfig.flThickness), (bIsHorizontal ? overlayConfig.flThickness : vecBox[SIDE_BOTTOM] - vecBox[SIDE_TOP]) };
}
void OVERLAY::CBarComponent::Render(ImDrawList* pDrawList, const ImVec2& vecPosition)
{
BarOverlayVar_t& overlayConfig = C_GET(BarOverlayVar_t, uOverlayVarIndex);
const ImVec2 vecThicknessOffset = { overlayConfig.flThickness, overlayConfig.flThickness };
ImVec2 vecMin = vecPosition, vecMax = vecPosition + this->vecSize;
// background glow
pDrawList->AddShadowRect(vecMin, vecMax, overlayConfig.colBackground.GetU32(), 1.f, ImVec2(0, 0));
// outline
pDrawList->AddRect(vecMin, vecMax, overlayConfig.colOutline.GetU32(), 0.f, ImDrawFlags_None, overlayConfig.flThickness);
// account outline offset
vecMin += vecThicknessOffset;
vecMax -= vecThicknessOffset;
const ImVec2 vecLineSize = vecMax - vecMin;
// modify active side axis by factor
if ((this->nSide & 1U) == 0U)
vecMin.y += vecLineSize.y * (1.0f - this->flProgressFactor);
else
vecMax.x -= vecLineSize.x * (1.0f - this->flProgressFactor);
// bar
if (overlayConfig.bGradient && !overlayConfig.bUseFactorColor)
{
if (this->nSide == SIDE_LEFT || this->nSide == SIDE_RIGHT)
pDrawList->AddRectFilledMultiColor(vecMin, vecMax, overlayConfig.colPrimary.GetU32(), overlayConfig.colPrimary.GetU32(), overlayConfig.colSecondary.GetU32(), overlayConfig.colSecondary.GetU32());
else
pDrawList->AddRectFilledMultiColor(vecMin, vecMax, overlayConfig.colSecondary.GetU32(), overlayConfig.colPrimary.GetU32(), overlayConfig.colPrimary.GetU32(), overlayConfig.colSecondary.GetU32());
}
else
{
const ImU32 u32Color = overlayConfig.bUseFactorColor ? Color_t::FromHSB((flProgressFactor * 120.f) / 360.f, 1.0f, 1.0f).GetU32() : overlayConfig.colPrimary.GetU32();
pDrawList->AddRectFilled(vecMin, vecMax, u32Color, 0.f, ImDrawFlags_None);
}
// only open menu item if menu is opened and overlay is enabled
bIsMenuItem &= (MENU::bMainWindowOpened && overlayConfig.bEnable);
if (bIsMenuItem)
{
// @note: padding 2.f incase the thickness is too small
this->bIsHovered = ImRect(vecPosition - ImVec2(2.f, 2.f), vecPosition + this->vecSize + ImVec2(2.f, 2.f)).Contains(ImGui::GetIO().MousePos);
// if component is hovered + right clicked
if (this->bIsHovered && ImGui::IsMouseClicked(ImGuiMouseButton_Right))
ImGui::OpenPopup(CS_XOR("context##component.bar"));
if (ImGui::BeginPopup(CS_XOR("context##component.bar"), ImGuiWindowFlags_NoResize | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove))
{
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(ImGui::GetStyle().FramePadding.x, -1));
ImGui::Checkbox(CS_XOR("use factor color##component.bar"), &overlayConfig.bUseFactorColor);
if (!overlayConfig.bUseFactorColor)
ImGui::Checkbox(CS_XOR("use gradient##component.bar"), &overlayConfig.bGradient);
ImGui::ColorEdit3(CS_XOR("primary color##component.bar"), &overlayConfig.colPrimary);
if (overlayConfig.bGradient && !overlayConfig.bUseFactorColor)
ImGui::ColorEdit3(CS_XOR("secondary color##component.bar"), &overlayConfig.colSecondary);
ImGui::ColorEdit4(CS_XOR("outline color##component.bar"), &overlayConfig.colOutline);
ImGui::ColorEdit4(CS_XOR("background color##component.bar"), &overlayConfig.colBackground);
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() * 0.75f);
ImGui::SliderFloat(CS_XOR("thickness##component.bar"), &overlayConfig.flThickness, 1.0f, 10.0f, CS_XOR("%.1f"), ImGuiSliderFlags_NoInput);
ImGui::PopStyleVar();
ImGui::EndPopup();
}
}
else
// dont process hovered on menu close...
this->bIsHovered = false;
}
OVERLAY::CTextComponent::CTextComponent(const bool bIsMenuItem, const EAlignSide nAlignSide, const EAlignDirection nAlignDirection, const ImFont* pFont, const char* szText, const std::size_t uOverlayVarIndex) :
bIsMenuItem(bIsMenuItem), pFont(pFont), uOverlayVarIndex(uOverlayVarIndex)
{
// allocate own buffer to safely store a copy of the string
this->szText = new char[CRT::StringLength(szText) + 1U];
CRT::StringCopy(this->szText, szText);
this->nSide = nAlignSide;
this->nDirection = nAlignDirection;
this->vecSize = pFont->CalcTextSizeA(pFont->FontSize, FLT_MAX, 0.0f, szText) + C_GET(TextOverlayVar_t, uOverlayVarIndex).flThickness;
}
OVERLAY::CTextComponent::~CTextComponent()
{
// deallocate buffer of the copied string
delete[] this->szText;
}
void OVERLAY::CTextComponent::Render(ImDrawList* pDrawList, const ImVec2& vecPosition)
{
TextOverlayVar_t& overlayConfig = C_GET(TextOverlayVar_t, this->uOverlayVarIndex);
const ImVec2 vecOutlineOffset = { overlayConfig.flThickness, overlayConfig.flThickness };
// @test: used for spacing debugging
//pDrawList->AddRect(vecPosition, vecPosition + this->vecSize, IM_COL32(255, 255, 255, 255));
// @todo: fix this cringe shit after gui merge
if (overlayConfig.flThickness >= 1.0f)
{
pDrawList->AddText(this->pFont, this->pFont->FontSize, vecPosition, overlayConfig.colOutline.GetU32(), this->szText);
pDrawList->AddText(this->pFont, this->pFont->FontSize, vecPosition + vecOutlineOffset * 2.0f, overlayConfig.colOutline.GetU32(), this->szText);
}
pDrawList->AddText(this->pFont, this->pFont->FontSize, vecPosition + vecOutlineOffset, overlayConfig.colPrimary.GetU32(), this->szText);
// only open menu item if menu is opened and overlay is enabled
bIsMenuItem &= MENU::bMainWindowOpened && overlayConfig.bEnable;
if (bIsMenuItem)
{
this->bIsHovered = ImRect(vecPosition, vecPosition + this->vecSize).Contains(ImGui::GetIO().MousePos);
//pDrawList->AddRect(vecPosition, vecPosition + this->vecSize, IM_COL32(this->bIsHovered ? 0 : 255, this->bIsHovered ? 255 : 0, 0, 255));
// if component is hovered + right clicked
if (this->bIsHovered && ImGui::IsMouseClicked(ImGuiMouseButton_Right))
ImGui::OpenPopup(CS_XOR("context##component.text"));
if (ImGui::BeginPopup(CS_XOR("context##component.text")))
{
ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(ImGui::GetStyle().FramePadding.x, -1));
ImGui::ColorEdit3(CS_XOR("primary color##component.bar"), &overlayConfig.colPrimary);
ImGui::ColorEdit4(CS_XOR("outline color##component.bar"), &overlayConfig.colOutline);
ImGui::SetNextItemWidth(ImGui::GetWindowWidth() * 0.75f);
ImGui::SliderFloat(CS_XOR("outline thickness##component.bar"), &overlayConfig.flThickness, 1.0f, 10.0f, CS_XOR("%.1f"), ImGuiSliderFlags_NoInput);
ImGui::PopStyleVar();
ImGui::EndPopup();
}
}
}
#pragma endregion
#pragma region visual_overlay_context
bool OVERLAY::Context_t::AddBoxComponent(ImDrawList* pDrawList, const ImVec4& vecBox, const int nType, float flThickness, float flRounding, const Color_t& colPrimary, const Color_t& colOutline)
{
flThickness = std::floorf(flThickness);
const ImVec2 vecThicknessOffset = { flThickness, flThickness };
switch (nType)
{
case VISUAL_OVERLAY_BOX_FULL:
{
const ImVec2 vecBoxMin = { vecBox[SIDE_LEFT], vecBox[SIDE_TOP] };
const ImVec2 vecBoxMax = { vecBox[SIDE_RIGHT], vecBox[SIDE_BOTTOM] };
// inner outline
pDrawList->AddRect(vecBoxMin + vecThicknessOffset * 2.0f, vecBoxMax - vecThicknessOffset * 2.0f, colOutline.GetU32(), flRounding, ImDrawFlags_RoundCornersAll, flThickness);
// primary box
pDrawList->AddRect(vecBoxMin + vecThicknessOffset, vecBoxMax - vecThicknessOffset, colPrimary.GetU32(), flRounding, ImDrawFlags_RoundCornersAll, flThickness);
// outer outline
pDrawList->AddRect(vecBoxMin, vecBoxMax, colOutline.GetU32(), flRounding, ImDrawFlags_RoundCornersAll, flThickness);
break;
}
case VISUAL_OVERLAY_BOX_CORNERS:
{
// corner part of the whole line
constexpr float flPartRatio = 0.25f;
const float flCornerWidth = ((vecBox[SIDE_RIGHT] - vecBox[SIDE_LEFT]) * flPartRatio);
const float flCornerHeight = ((vecBox[SIDE_BOTTOM] - vecBox[SIDE_TOP]) * flPartRatio);
const ImVec2 arrCornerPoints[4][3] = {
// top-left
{ ImVec2(vecBox[SIDE_LEFT], vecBox[SIDE_TOP] + flCornerHeight) + vecThicknessOffset, ImVec2(vecBox[SIDE_LEFT], vecBox[SIDE_TOP]) + vecThicknessOffset, ImVec2(vecBox[SIDE_LEFT] + flCornerWidth, vecBox[SIDE_TOP]) + vecThicknessOffset },
// top-right
{ ImVec2(vecBox[SIDE_RIGHT] - flCornerWidth - vecThicknessOffset.x, vecBox[SIDE_TOP] + vecThicknessOffset.y * 2.0f), ImVec2(vecBox[SIDE_RIGHT] - vecThicknessOffset.x, vecBox[SIDE_TOP] + vecThicknessOffset.y * 2.0f), ImVec2(vecBox[SIDE_RIGHT] - vecThicknessOffset.x, vecBox[SIDE_TOP] + flCornerHeight + vecThicknessOffset.y * 2.0f) },
// bottom-left
{ ImVec2(vecBox[SIDE_LEFT] + flCornerWidth + vecThicknessOffset.x, vecBox[SIDE_BOTTOM] - vecThicknessOffset.y * 2.0f), ImVec2(vecBox[SIDE_LEFT] + vecThicknessOffset.x, vecBox[SIDE_BOTTOM] - vecThicknessOffset.y * 2.0f), ImVec2(vecBox[SIDE_LEFT] + vecThicknessOffset.x, vecBox[SIDE_BOTTOM] - flCornerHeight - vecThicknessOffset.y * 2.0f) },
// bottom-right
{ ImVec2(vecBox[SIDE_RIGHT], vecBox[SIDE_BOTTOM] - flCornerHeight) - vecThicknessOffset, ImVec2(vecBox[SIDE_RIGHT], vecBox[SIDE_BOTTOM]) - vecThicknessOffset, ImVec2(vecBox[SIDE_RIGHT] - flCornerWidth, vecBox[SIDE_BOTTOM]) - vecThicknessOffset }
};
for (std::size_t i = 0U; i < CS_ARRAYSIZE(arrCornerPoints); i++)
{
const auto& arrLinePoints = arrCornerPoints[i];
const ImVec2 vecHalfPixelOffset = ((i & 1U) == 1U ? ImVec2(-0.5f, -0.5f) : ImVec2(0.5f, 0.5f));
// @todo: we can even do not clear path and reuse it
pDrawList->PathLineTo(arrLinePoints[0] + vecHalfPixelOffset);
pDrawList->PathLineTo(arrLinePoints[1] + vecHalfPixelOffset);
pDrawList->PathLineTo(arrLinePoints[2] + vecHalfPixelOffset);
pDrawList->PathStroke(colOutline.GetU32(), false, flThickness + 1.0f);
pDrawList->PathLineTo(arrLinePoints[0] + vecHalfPixelOffset);
pDrawList->PathLineTo(arrLinePoints[1] + vecHalfPixelOffset);
pDrawList->PathLineTo(arrLinePoints[2] + vecHalfPixelOffset);
pDrawList->PathStroke(colPrimary.GetU32(), false, flThickness);
}
break;
}
default:
break;
}
// accumulate spacing for next side/directional components
for (float& flSidePadding : this->arrSidePaddings)
flSidePadding += this->flComponentSpacing;
return ImRect(vecBox).Contains(ImGui::GetIO().MousePos);
}
ImVec4 OVERLAY::Context_t::AddFrameComponent(ImDrawList* pDrawList, const ImVec2& vecScreen, const EAlignSide nSide, const Color_t& colBackground, const float flRounding, const ImDrawFlags nRoundingCorners)
{
// calculate frame size by previously added components on active side
const ImVec2 vecFrameSize = this->GetTotalDirectionalSize(nSide);
ImVec2 vecFrameMin = { vecScreen.x - vecFrameSize.x * 0.5f, vecScreen.y - vecFrameSize.y };
ImVec2 vecFrameMax = { vecScreen.x + vecFrameSize.x * 0.5f, vecScreen.y };
pDrawList->AddRectFilled(vecFrameMin - this->flComponentSpacing, vecFrameMax + this->flComponentSpacing, colBackground.GetU32(), flRounding, nRoundingCorners);
// accumulate spacing for next side/directional components
for (float& flSidePadding : this->arrSidePaddings)
flSidePadding += this->flComponentSpacing;
return { vecFrameMin.x, vecFrameMin.y, vecFrameMax.x, vecFrameMax.y };
}
/*
* @todo: currently not well designed, make it more flexible for use cases where we need e.g. previous frame bar factor etc
* also to optimize this, allocate components at stack instead of heap + make all context units static and do not realloc components storage every frame, but reset (like memset idk) it at the end of frame
*/
void OVERLAY::Context_t::AddComponent(CBaseComponent* pComponent)
{
// guarantee that first directional component on each side is in the primary direction
if (pComponent->IsDirectional())
{
CBaseDirectionalComponent* pDirectionalComponent = static_cast<CBaseDirectionalComponent*>(pComponent);
// check if it's not an exception direction and there are no components in the primary direction
if (((pDirectionalComponent->nSide & 1U) == 1U || pDirectionalComponent->nDirection != DIR_TOP) && this->arrSideDirectionPaddings[pDirectionalComponent->nSide][pDirectionalComponent->nSide] == 0.0f)
pDirectionalComponent->nDirection = static_cast<EAlignDirection>(pDirectionalComponent->nSide);
}
float& flSidePadding = this->arrSidePaddings[pComponent->nSide];
if (pComponent->IsDirectional())
{
CBaseDirectionalComponent* pDirectionalComponent = static_cast<CBaseDirectionalComponent*>(pComponent);
float(&arrDirectionPaddings)[DIR_MAX] = this->arrSideDirectionPaddings[pDirectionalComponent->nSide];
// directional components don't change side paddings, but take them into account
pComponent->vecOffset[pDirectionalComponent->nSide & 1U] += ((pDirectionalComponent->nSide < 2U) ? -flSidePadding : flSidePadding);
// check if the component is in the same direction as the side and it's the first component in this direction
if (static_cast<std::uint8_t>(pDirectionalComponent->nDirection) == static_cast<std::uint8_t>(pDirectionalComponent->nSide) && arrDirectionPaddings[pDirectionalComponent->nDirection] == 0.0f)
{
// accumulate paddings for sub-directions
for (std::uint8_t nSubDirection = DIR_LEFT; nSubDirection < DIR_MAX; nSubDirection++)
{
/*
* exclude conflicting sub-directions
*
* SIDE_LEFT[0]: DIR_LEFT[0], DIR_BOTTOM[3] | ~2 & ~1
* SIDE_TOP[1]: DIR_LEFT[0], DIR_TOP[1], DIR_RIGHT[2] | ~3
* SIDE_RIGHT[2]: DIR_RIGHT[2], DIR_BOTTOM[3] | ~0 & ~1
* SIDE_BOTTOM[3]: DIR_LEFT[0], DIR_RIGHT[2], DIR_BOTTOM[3] | ~1
*/
if (nSubDirection == pDirectionalComponent->nSide || nSubDirection == ((pDirectionalComponent->nSide + 2U) & 3U) || (nSubDirection == DIR_TOP && (pDirectionalComponent->nSide & 1U) == 0U))
continue;
arrDirectionPaddings[nSubDirection] += (pDirectionalComponent->vecSize[nSubDirection == DIR_BOTTOM ? SIDE_TOP : SIDE_LEFT] * (((pDirectionalComponent->nSide & 1U) == 1U) ? 0.5f : 1.0f) + this->flComponentSpacing);
}
}
float& flSideDirectionPadding = arrDirectionPaddings[pDirectionalComponent->nDirection];
// append direction padding to offset
pComponent->vecOffset[pDirectionalComponent->nDirection & 1U] += ((pDirectionalComponent->nDirection < 2U) ? -flSideDirectionPadding : flSideDirectionPadding);
// accumulate direction padding for next component
flSideDirectionPadding += pDirectionalComponent->vecSize[pDirectionalComponent->nDirection & 1U];
// accumulate spacing for next directional components
flSideDirectionPadding += this->flComponentSpacing;
}
else
{
// append side padding to offset
pComponent->vecOffset[pComponent->nSide & 1U] += ((pComponent->nSide < 2U) ? -(flSidePadding + pComponent->vecSize[pComponent->nSide]) : flSidePadding);
// accumulate side padding for next component
flSidePadding += pComponent->vecSize[pComponent->nSide & 1U];
// accumulate spacing for next components
flSidePadding += this->flComponentSpacing;
}
this->vecComponents.push_back(pComponent);
}
ImVec2 OVERLAY::Context_t::GetTotalDirectionalSize(const EAlignSide nSide) const
{
ImVec2 vecSideSize = {};
// @todo: we should peek max of bottom + side or top directions at horizontal sides
const float(&arrDirectionPaddings)[DIR_MAX] = this->arrSideDirectionPaddings[nSide];
for (std::uint8_t nSubDirection = DIR_LEFT; nSubDirection < DIR_MAX; nSubDirection++)
vecSideSize[nSubDirection & 1U] += arrDirectionPaddings[nSubDirection];
return vecSideSize;
}
void OVERLAY::Context_t::Render(ImDrawList* pDrawList, const ImVec4& vecBox) const
{
bool bCenteredFirstSideDirectional[SIDE_MAX] = {};
for (CBaseComponent* const pComponent : this->vecComponents)
{
ImVec2 vecPosition = pComponent->GetBasePosition(vecBox);
// check if the component is in the side that supports multi-component centering
if (pComponent->nSide == SIDE_TOP || pComponent->nSide == SIDE_BOTTOM)
{
// check if the component is directional
if (CBaseDirectionalComponent* const pDirectionalComponent = static_cast<CBaseDirectionalComponent*>(pComponent); pDirectionalComponent->IsDirectional())
{
const float(&arrDirectionPaddings)[DIR_MAX] = this->arrSideDirectionPaddings[pComponent->nSide];
// check if the component has horizontal direction
if (static_cast<std::uint8_t>(pDirectionalComponent->nDirection) != static_cast<std::uint8_t>(pDirectionalComponent->nSide))
// add centering offset to the component's offset
pDirectionalComponent->vecOffset.x += (arrDirectionPaddings[DIR_LEFT] - arrDirectionPaddings[DIR_RIGHT]) * 0.5f;
// otherwise check if it's the first component in direction as side
else if (!bCenteredFirstSideDirectional[pDirectionalComponent->nSide])
{
// add centering offset to the component's offset
pDirectionalComponent->vecOffset.x += (arrDirectionPaddings[DIR_LEFT] - arrDirectionPaddings[DIR_RIGHT]) * 0.5f;
bCenteredFirstSideDirectional[pDirectionalComponent->nSide] = true;
}
}
}
// add final component offset to the base position
vecPosition += pComponent->vecOffset;
pComponent->Render(pDrawList, vecPosition);
}
}
#pragma endregion
void OVERLAY::OnFrameStageNotify(CCSPlayerController* pLocalController)
{
// only render when in-game
if (!I::Engine->IsConnected() || !I::Engine->IsInGame())
return;
if (!C_GET(bool, Vars.bVisualOverlay))
return;
enum ESortEntityType : int
{
SORT_ENTITY_NONE = -1,
SORT_ENTITY_PLAYER = 0,
};
struct SortEntityObject_t
{
SortEntityObject_t(C_BaseEntity* pEntity, CBaseHandle hEntity, float flDistance, ESortEntityType nSortType) :
pEntity(pEntity), hEntity(hEntity), flDistance(flDistance), nSortType(nSortType) { }
C_BaseEntity* pEntity;
CBaseHandle hEntity;
float flDistance;
ESortEntityType nSortType;
};
const int nHighestIndex = I::GameResourceService->pGameEntitySystem->GetHighestEntityIndex();
std::vector<SortEntityObject_t> vecSortedEntities = {};
vecSortedEntities.reserve(nHighestIndex);
// @note: 0 is resved for world entity 'CWorld'
for (int nIndex = 1; nIndex <= nHighestIndex; nIndex++)
{
C_BaseEntity* pEntity = I::GameResourceService->pGameEntitySystem->Get(nIndex);
if (pEntity == nullptr)
continue;
SchemaClassInfoData_t* pClassInfo = nullptr;
pEntity->GetSchemaClassInfo(&pClassInfo);
if (pClassInfo == nullptr)
continue;
const FNV1A_t uHashedName = FNV1A::Hash(pClassInfo->szName);
ESortEntityType nEntityType = SORT_ENTITY_NONE;
Vector_t vecOrigin = Vector_t();
if (uHashedName == FNV1A::HashConst("CCSPlayerController"))
{
nEntityType = SORT_ENTITY_PLAYER;
CCSPlayerController* pPlayer = reinterpret_cast<CCSPlayerController*>(pEntity);
if (pPlayer == nullptr)
continue;
vecOrigin = pPlayer->GetPawnOrigin();
}
// only add sortable entities
if (nEntityType != SORT_ENTITY_NONE)
vecSortedEntities.emplace_back(pEntity, pEntity->GetRefEHandle(), SDK::CameraPosition.DistTo(vecOrigin), nEntityType);
}
// sort entities by distance to draw them from the farthest to the nearest
std::ranges::sort(vecSortedEntities.begin(), vecSortedEntities.end(), std::ranges::greater{}, &SortEntityObject_t::flDistance);
for (auto& [pEntity, hEntity, flDistance, nSortType] : vecSortedEntities)
{
// if the handle is invalid, skip this entity
if (!hEntity.IsValid())
continue;
switch (nSortType)
{
case SORT_ENTITY_PLAYER:
{
CCSPlayerController* pPlayer = I::GameResourceService->pGameEntitySystem->Get<CCSPlayerController>(hEntity);
if (pPlayer == nullptr)
break;
if (!pPlayer->IsPawnAlive())
break;
Player(pLocalController, pPlayer, flDistance);
break;
}
default:
break;
}
}
}
bool OVERLAY::GetEntityBoundingBox(C_CSPlayerPawn* pEntity, ImVec4* pVecOut)
{
CCollisionProperty* pCollision = pEntity->GetCollision();
if (pCollision == nullptr)
return false;
CGameSceneNode* pGameSceneNode = pEntity->GetGameSceneNode();
if (pGameSceneNode == nullptr)
return false;
CTransform nodeToWorldTransform = pGameSceneNode->GetNodeToWorld();
const Matrix3x4_t matTransform = nodeToWorldTransform.quatOrientation.ToMatrix(nodeToWorldTransform.vecPosition);
const Vector_t vecMins = pCollision->GetMins();
const Vector_t vecMaxs = pCollision->GetMaxs();
pVecOut->x = pVecOut->y = std::numeric_limits<float>::max();
pVecOut->z = pVecOut->w = -std::numeric_limits<float>::max();
for (int i = 0; i < 8; ++i)
{
const Vector_t vecPoint{
i & 1 ? vecMaxs.x : vecMins.x,
i & 2 ? vecMaxs.y : vecMins.y,
i & 4 ? vecMaxs.z : vecMins.z
};
ImVec2 vecScreen;
if (!D::WorldToScreen(vecPoint.Transform(matTransform), &vecScreen))
return false;
pVecOut->x = MATH::Min(pVecOut->x, vecScreen.x);
pVecOut->y = MATH::Min(pVecOut->y, vecScreen.y);
pVecOut->z = MATH::Max(pVecOut->z, vecScreen.x);
pVecOut->w = MATH::Max(pVecOut->w, vecScreen.y);
}
return true;
}
void OVERLAY::Player(CCSPlayerController* pLocal, CCSPlayerController* pPlayer, const float flDistance)
{
C_CSPlayerPawn* pLocalPawn = I::GameResourceService->pGameEntitySystem->Get<C_CSPlayerPawn>(pLocal->GetPawnHandle());
C_CSPlayerPawn* pPlayerPawn = I::GameResourceService->pGameEntitySystem->Get<C_CSPlayerPawn>(pPlayer->GetPawnHandle());
if (pLocalPawn == nullptr || pPlayerPawn == nullptr)
return;
// @note: this is a simple example of how to check if the player is visible
// initialize trace, construct filterr and initialize ray
//GameTrace_t trace = GameTrace_t();
//TraceFilter_t filter = TraceFilter_t(0x1C3003, pLocalPawn, nullptr, 4);
//Ray_t ray = Ray_t();
// cast a ray from local player eye positon -> player head bone
// @note: would recommend checking for nullptrs
//I::GameTraceManager->TraceShape(&ray, pLocalPawn->GetEyePosition(), pPlayerPawn->GetGameSceneNode()->GetSkeletonInstance()->pBoneCache->GetOrigin(6), &filter, &trace);
// check if the hit entity is the one we wanted to check and if the trace end point is visible
//if (trace.m_pHitEntity != pPlayerPawn || !trace.IsVisible( ))
// return;
bool bIsEnemy = (pLocalPawn->IsOtherEnemy(pPlayerPawn));
// @note: only enemy overlay for now
if (!bIsEnemy)
return;
ImVec4 vecBox = {};
if (!GetEntityBoundingBox(pPlayerPawn, &vecBox))
return;
Context_t context;
if (const auto& frameOverlayConfig = C_GET(FrameOverlayVar_t, Vars.overlayBox); frameOverlayConfig.bEnable)
context.AddBoxComponent(D::pDrawListActive, vecBox, 1, frameOverlayConfig.flThickness, frameOverlayConfig.flRounding, frameOverlayConfig.colPrimary, frameOverlayConfig.colOutline);
if (const auto& nameOverlayConfig = C_GET(TextOverlayVar_t, Vars.overlayName); nameOverlayConfig.bEnable)
{
const char* szPlayerName = pPlayer->GetPlayerName();
context.AddComponent(new CTextComponent(false, SIDE_TOP, DIR_TOP, FONT::pVisual, szPlayerName, Vars.overlayName));
}
if (const auto& healthOverlayConfig = C_GET(BarOverlayVar_t, Vars.overlayHealthBar); healthOverlayConfig.bEnable)
{
// @note: pPlayerPawn->GetMaxHealth() sometime return 0.f
const float flHealthFactor = pPlayerPawn->GetHealth() / 100.f;
context.AddComponent(new CBarComponent(false, SIDE_LEFT, vecBox, flHealthFactor, Vars.overlayHealthBar));
}
if (const auto& armorOverlayConfig = C_GET(BarOverlayVar_t, Vars.overlayArmorBar); armorOverlayConfig.bEnable)
{
const float flArmorFactor = pPlayerPawn->GetArmorValue() / 100.f;
context.AddComponent(new CBarComponent(false, SIDE_BOTTOM, vecBox, flArmorFactor, Vars.overlayArmorBar));
}
// render all the context
context.Render(D::pDrawListActive, vecBox);
}

View File

@@ -0,0 +1,168 @@
#pragma once
#include "../../common.h"
// used: draw system
#include "../../utilities/draw.h"
class CCSPlayerController;
class C_BaseEntity;
class C_CSPlayerPawn;
namespace F::VISUALS::OVERLAY
{
enum EAlignSide : std::uint8_t
{
SIDE_LEFT = 0U,
SIDE_TOP,
SIDE_RIGHT,
SIDE_BOTTOM,
SIDE_MAX
};
enum EAlignDirection : std::uint8_t
{
DIR_LEFT = 0U,
DIR_TOP,
DIR_RIGHT,
DIR_BOTTOM,
DIR_MAX = 4U // @todo: rework stuff based on this cuz one component can have only 3 possible directions at same time. vertical side: left & right + top | bottom, horizontal side: top & bottom + left | right
};
class CBaseComponent
{
public:
[[nodiscard]] virtual ImVec2 GetBasePosition(const ImVec4& box) const;
[[nodiscard]] virtual bool IsDirectional() const
{
return false;
}
virtual void Render(ImDrawList* pDrawList, const ImVec2& vecPosition) = 0;
EAlignSide nSide = SIDE_TOP;
ImVec2 vecOffset = {};
ImVec2 vecSize = {};
};
class CBaseDirectionalComponent : public CBaseComponent
{
public:
[[nodiscard]] ImVec2 GetBasePosition(const ImVec4& box) const final;
[[nodiscard]] bool IsDirectional() const final
{
return true;
}
EAlignDirection nDirection = DIR_TOP;
};
class CBarComponent : public CBaseComponent
{
public:
CBarComponent(const bool bIsMenuItem, const EAlignSide nAlignSide, const ImVec4& vecBox, const float flProgressFactor, const std::size_t uOverlayVarIndex);
void Render(ImDrawList* pDrawList, const ImVec2& vecPosition) final;
private:
bool bIsMenuItem = false;
// bar progress
float flProgressFactor = 0.0f;
// hovered state for context menu
bool bIsHovered = false;
// config variables
std::size_t uOverlayVarIndex = 0ULL;
};
class CTextComponent : public CBaseDirectionalComponent
{
public:
CTextComponent(const bool bIsMenuItem, const EAlignSide nAlignSide, const EAlignDirection nAlignDirection, const ImFont* pFont, const char* szText, const std::size_t uOverlayVarIndex);
~CTextComponent();
void Render(ImDrawList* pDrawList, const ImVec2& vecPosition) final;
private:
bool bIsMenuItem = false;
// font & text for displaying
const ImFont* pFont = nullptr;
char* szText = nullptr;
// hovered state for context menu
bool bIsHovered = false;
// config variables
std::size_t uOverlayVarIndex = 0ULL;
};
/*
* overlay component auto-positioning system
* @note: was designed to support the reordering of components that can be implemented with minimal effort
*
* currently supported next sides and sub-directions:
*
* DIR_TOP
* ^
* |
* DIR_LEFT <-o-> DIR_RIGHT
* DIR_LEFT <-o *---------* o-> DIR_RIGHT
* | | | |
* v | | v
* DIR_BOTTOM | | DIR_BOTTOM
* | |
* DIR_TOP | | DIR_TOP
* ^ | | ^
* | | | |
* o *---------* o
* DIR_LEFT <-o-> DIR_RIGHT
* |
* v
* DIR_BOTTOM
*/
struct Context_t
{
/* @section: special case components */
/// add the box component to overlay
/// @remarks: current implementation expects this to be first component, it's an immediate rendering component
/// @return: if the box component is hovered
bool AddBoxComponent(ImDrawList* pDrawList, const ImVec4& vecBox, const int nType, float flThickness, float flRounding, const Color_t& colPrimary, const Color_t& colOutline);
/// add the frame component to overlay
/// @remarks: current implementation expects this to be added after components that should be inside it, it's an immediate rendering component
/// @returns: size constraints of the added frame
ImVec4 AddFrameComponent(ImDrawList* pDrawList, const ImVec2& vecScreen, const EAlignSide nSide, const Color_t& colBackground, const float flRounding, const ImDrawFlags nRoundingCorners);
/* @section: common components */
/// add new component to overlay
/// @param[in] pComponent pointer to the one of supported component types
void AddComponent(CBaseComponent* pComponent);
/* @section: get */
/// @returns: size of the all directional components currently assigned to @a'nSide'
[[nodiscard]] ImVec2 GetTotalDirectionalSize(const EAlignSide nSide) const;
// calculate final position of components and render them
void Render(ImDrawList* pDrawList, const ImVec4& vecBox) const;
private:
// storage of all components
std::vector<CBaseComponent*> vecComponents = {};
// additional spacing between components
float flComponentSpacing = 1.0f;
// summary padding of all align sides
float arrSidePaddings[SIDE_MAX] = {};
// summary padding for all align directions of all align sides
float arrSideDirectionPaddings[SIDE_MAX][DIR_MAX] = {};
};
/* @section: callbacks */
void OnFrameStageNotify(CCSPlayerController* pLocalController);
/* @section: get */
/// get bounding box of entity
/// @returns: true if entity has collision and all points of box are visible on screen, false otherwise
bool GetEntityBoundingBox(C_CSPlayerPawn* pEntity, ImVec4* pVecOut);
/* @section: main */
// draw box, bars, text infos, etc at player position
void Player(CCSPlayerController* pLocal, CCSPlayerController* pPlayer, const float flDistance);
}