kal
This commit is contained in:
@@ -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;
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
Reference in New Issue
Block a user