-
Автор темы
- #1
Hello! Can anyone tell me why fraction = 1.f always? I think ClipTraceToPlayers is broken but I can't understand why...
C++:
#include "Autowall.h"
#include "..\..\Utils\Utils.h"
#include "..\..\SDK\IVEngineClient.h"
#include "..\..\SDK\PlayerInfo.h"
#include "..\..\SDK\ISurfaceData.h"
#include "..\..\SDK\Hitboxes.h"
#include "..\..\SDK\bspflags.h"
#include "..\..\SDK\ICvar.h"
#include "..\..\Utils\Math.h"
#include "..\..\SDK\ClientClass.h"
#include <algorithm>
// esoterik ftw
bool Autowall::breakable_entity(C_BaseEntity* pEnt)
{
if (!pEnt || !pEnt->EntIndex())
return false;
static auto is_breakable = Utils::FindSignature("client.dll", "55 8B EC 51 56 8B F1 85 F6 74 68");
auto take_damage = *(uintptr_t*)((uintptr_t)is_breakable + 0x26);
auto backup = *(uint8_t*)((uintptr_t)pEnt + take_damage);
auto client_class = pEnt->GetClientClass();
auto network_name = client_class->pNetworkName;
if (!strcmp(network_name, "CBreakableSurface"))
*(uint8_t*)((uintptr_t)pEnt + take_damage) = 2;
else if (!strcmp(network_name, "CBaseDoor") || !strcmp(network_name, "CDynamicProp"))
*(uint8_t*)((uintptr_t)pEnt + take_damage) = 0;
using Fn = bool(__thiscall*)(C_BaseEntity*);
autoresult = ((Fn)is_breakable)(pEnt);
*(uint8_t*)((uintptr_t)pEnt + take_damage) = backup;
return result;
}
float Autowall::GetDamage(const Vector& vecPoint, FireBulletData* pDataOut)
{
const Vector vecPosition = g::pLocal->GetEyePosition();
// setup data
FireBulletData data = { };
data.vecPosition = vecPosition;
data.vecDirection = (vecPoint - vecPosition).Normalized();
C_BaseCombatWeapon* pWeapon = g::pLocal->GetActiveWeapon();
if (pWeapon == nullptr || !SimulateFireBullet(pWeapon, data))
return -1.0f;
if (pDataOut != nullptr)
*pDataOut = data;
return data.flCurrentDamage;
}
bool IsArmored(int hitgroup, C_BaseEntity* player)
{
if (player->ArmorValue() > 0)
{
switch (hitgroup)
{
case HITGROUP_GENERIC:
case HITGROUP_CHEST:
case HITGROUP_STOMACH:
case HITGROUP_LEFTARM:
case HITGROUP_RIGHTARM:
case HITGROUP_NECK:
return true;
break;
case HITGROUP_HEAD:
if (player->HasHelmet())
return true;
[[fallthrough]];
case HITGROUP_LEFTLEG:
case HITGROUP_RIGHTLEG:
if (player->GetHeavyArmor())
return true;
break;
default:
break;
}
}
return false;
}
void Autowall::ScaleDamage(const int iHitGroup, C_BaseEntity* pEntity, const float flWeaponArmorRatio, const float flWeaponHeadShotMultiplier, float& flDamage)
{
// @ida traceattack: server.dll @ 55 8B EC 83 E4 F8 81 EC C0 00 00 00 56 8B 75 08 57 8B F9 C6 44 24 13 01
const bool bHeavyArmor = pEntity->GetHeavyArmor();
static ConVar* mp_damage_scale_ct_head = g_pCvar->FindVar("mp_damage_scale_ct_head");
static ConVar* mp_damage_scale_t_head = g_pCvar->FindVar("mp_damage_scale_t_head");
static ConVar* mp_damage_scale_ct_body = g_pCvar->FindVar("mp_damage_scale_ct_body");
static ConVar* mp_damage_scale_t_body = g_pCvar->FindVar("mp_damage_scale_t_body");
float flHeadDamageScale = pEntity->GetTeam() == 3 ? mp_damage_scale_ct_head->GetFloat() : pEntity->GetTeam() == 2 ? mp_damage_scale_t_head->GetFloat() : 1.0f;
const float flBodyDamageScale = pEntity->GetTeam() == 3 ? mp_damage_scale_ct_body->GetFloat() : pEntity->GetTeam() == 2 ? mp_damage_scale_t_body->GetFloat() : 1.0f;
if (bHeavyArmor)
flHeadDamageScale *= 0.5f;
switch(iHitGroup)
{
case HITGROUP_HEAD:
flDamage *= flWeaponHeadShotMultiplier * flHeadDamageScale;
break;
case HITGROUP_CHEST:
case HITGROUP_LEFTARM:
case HITGROUP_RIGHTARM:
case HITGROUP_NECK:
flDamage *= flBodyDamageScale;
break;
case HITGROUP_STOMACH:
flDamage *= 1.25f * flBodyDamageScale;
break;
case HITGROUP_LEFTLEG:
case HITGROUP_RIGHTLEG:
flDamage *= 0.75f * flBodyDamageScale;
break;
default:
break;
}
if (IsArmored(iHitGroup, pEntity))
{
// @ida ontakedamage: server.dll @ 80 BF ? ? ? ? ? F3 0F 10 5C 24 ? F3 0F 10 35
const int iArmor = pEntity->ArmorValue();
float flHeavyArmorBonus = 1.0f, flArmorBonus = 0.5f, flArmorRatio = flWeaponArmorRatio * 0.5f;
if (bHeavyArmor)
{
flHeavyArmorBonus = 0.25f;
flArmorBonus = 0.33f;
flArmorRatio *= 0.20f;
}
float flDamageToHealth = flDamage * flArmorRatio;
if (const float flDamageToArmor = (flDamage - flDamageToHealth) * (flHeavyArmorBonus * flArmorBonus); flDamageToArmor > static_cast<float>(iArmor))
flDamageToHealth = flDamage - static_cast<float>(iArmor) / flArmorBonus;
flDamage = flDamageToHealth;
}
}
// @credits: https://github.com/perilouswithadollarsign/cstrike15_src/blob/master/game/shared/util_shared.cpp#L757
void Autowall::ClipTraceToPlayers(const Vector& vecAbsStart, const Vector& vecAbsEnd, const unsigned int fMask, ITraceFilter* pFilter, C_Trace* pTrace)
{
// @ida util_cliptracetoplayers: client.dll @ E8 ? ? ? ? 0F 28 84 24 68 02 00 00
float frac = pTrace->flFraction;
C_Trace trace = { };
C_Ray ray(vecAbsStart, vecAbsEnd);
for (int i = 1; i < g_pGlobalVars->maxClients; i++)
{
C_BaseEntity* pEntity = g_pEntityList->GetClientEntity(i);
if (pEntity == nullptr || !pEntity->IsAlive() || pEntity->IsDormant())
continue;
if (pFilter != nullptr && !pFilter->ShouldHitEntity(pEntity, fMask))
continue;
// get bounding box
const Vector vecMin = pEntity->GetCollideable()->OBBMins();
const Vector vecMax = pEntity->GetCollideable()->OBBMaxs();
// calculate world space center
const Vector vecCenter = (vecMax + vecMin) * 0.5f;
const Vector vecPosition = vecCenter + pEntity->GetOrigin();
const Vector vecTo = vecPosition - vecAbsStart;
Vector vecDirection = vecAbsEnd - vecAbsStart;
const float flLength = vecDirection.NormalizeInPlace2();
const float flRangeAlong = vecDirection.Dot(vecTo);
float flRange = 0.0f;
// calculate distance to ray
if (flRangeAlong < 0.0f)
// off start point
flRange = -vecTo.Length();
else if (flRangeAlong > vecDirection.Length())
// off end point
flRange = -(vecPosition - vecAbsEnd).Length();
else
// within ray bounds
flRange = (vecPosition - (vecDirection * flRangeAlong + vecAbsStart)).Length();
if (flRange <= 60.f) {
g_pTrace->ClipRayToEntity(ray, fMask | contents_hitbox, pEntity, &trace);
//Utils::Log("Sending fraction = " + std::to_string(trace->flFraction));
if (trace.flFraction < frac) {
*p trace = trace; // we shortened the ray - save off the trace
frac = trace.flFraction;
}
}
}
}
bool Autowall::TraceToExit(C_Trace& enterTrace, C_Trace& exitTrace, const Vector& vecPosition, const Vector& vecDirection)
{
// @ida tracetoexit: client.dll @ 55 8B EC 83 EC 4C F3
// server.dll @ 55 8B EC 83 EC 4C F3 0F 10 75
float flDistance = 0.0f;
int iStartContents = 0;
while (flDistance <= 90.0f)
{
// add extra distance to our ray
flDistance += 4.0f;
// multiply the direction vector to the distance so we go outwards, add our position to it
Vector vecStart = vecPosition + vecDirection * flDistance;
if (!iStartContents)
iStartContents = g_pTrace->GetPointContents(vecStart, mask_shot_hull | contents_hitbox, nullptr);
const int iCurrentContents = g_pTrace->GetPointContents(vecStart, mask_shot_hull | contents_hitbox, nullptr);
if (!(iCurrentContents & mask_shot_hull) || (iCurrentContents & contents_hitbox && iCurrentContents != iStartContents))
{
// setup our end position by deducting the direction by the extra added distance
const Vector vecEnd = vecStart - (vecDirection * 4.0f);
// trace ray to world
C_Ray rayWorld(vecStart, vecEnd);
g_pTrace->TraceRay(rayWorld, mask_shot_hull | contents_hitbox, nullptr, &exitTrace);
if (static ConVar* sv_clip_penetration_traces_to_players = g_pCvar->FindVar("sv_clip_penetration_traces_to_players"); sv_clip_penetration_traces_to_players != nullptr && sv_clip_penetration_traces_to_players->GetBool())
{
C_TraceFilter filter(g::pLocal);
ClipTraceToPlayers(vecEnd, vecStart, mask_shot_hull | contents_hitbox, &filter, &exitTrace);
}
// check if a hitbox is in-front of our enemy and if they are behind of a solid wall
if (exitTrace.startSolid && exitTrace.surface.flags & surf_hitbox)
{
// trace ray to entity
C_Ray ray(vecStart, vecPosition);
C_TraceFilter filter(exitTrace.m_pEnt);
g_pTrace->TraceRay(ray, mask_shot_hull, &filter, &exitTrace);
if (exitTrace.DidHit() && !exitTrace.startSolid)
{
vecStart = exitTrace.end;
return true;
}
continue;
}
if (exitTrace.DidHit() && !exitTrace.startSolid)
{
if (Autowall::breakable_entity(enterTrace.m_pEnt) && Autowall::breakable_entity(exitTrace.m_pEnt))
return true;
if (enterTrace.surface.flags & surf_nodraw || (!(exitTrace.surface.flags & surf_nodraw) && exitTrace.plane.normal.Dot(vecDirection) <= 1.0f))
{
const float flMultiplier = exitTrace.flFraction * 4.0f;
vecStart -= vecDirection * flMultiplier;
return true;
}
continue;
}
if (!exitTrace.DidHit() || exitTrace.startSolid)
{
if (enterTrace.m_pEnt != nullptr && enterTrace.m_pEnt->EntIndex() != 0 && Autowall::breakable_entity(enterTrace.m_pEnt))
{
// did hit breakable non world entity
exitTrace = enterTrace;
exitTrace.end = vecStart + vecDirection;
return true;
}
continue;
}
}
}
return false;
}
void TraceLine(Vector& vecAbsStart, Vector& vecAbsEnd, unsigned int mask, C_BaseEntity* ignore, C_Trace* ptr)
{
C_TraceFilter filter(ignore);
g_pTrace->TraceRay(C_Ray(vecAbsStart, vecAbsEnd), mask, &filter, ptr);
}
bool Autowall::HandleBulletPenetration(const WeaponInfo_t* pWeaponData, surfacedata_t* pEnterSurfaceData, FireBulletData& data)
{
// @ida handlebulletpenetration: client.dll @ E8 ? ? ? ? 83 C4 40 84 C0
static ConVar* ff_damage_reduction_bullets = g_pCvar->FindVar("ff_damage_reduction_bullets");
static ConVar* ff_damage_bullet_penetration = g_pCvar->FindVar("ff_damage_bullet_penetration");
const float flReductionDamage = ff_damage_reduction_bullets->GetFloat();
const float flPenetrateDamage = ff_damage_bullet_penetration->GetFloat();
const std::uint16_t hEnterMaterial = pEnterSurfaceData->game.material;
if (data.iPenetrateCount == 0 && hEnterMaterial != CHAR_TEX_GRATE && hEnterMaterial != CHAR_TEX_GLASS && !(data.enterTrace.surface.flags & surf_nodraw))
return false;
if (pWeaponData->penetration <= 0.0f || data.iPenetrateCount <= 0)
return false;
C_Trace exitTrace = { };
if (!TraceToExit(data.enterTrace, exitTrace, data.enterTrace.end, data.vecDirection) && !(g_pTrace->GetPointContents(data.enterTrace.end, mask_shot_hull, nullptr) & mask_shot_hull))
return false;
const surfacedata_t* pExitSurfaceData = g_pSurfaceData->GetSurfaceData(exitTrace.surface.surfaceProps);
const std::uint16_t hExitMaterial = pExitSurfaceData->game.material;
const float flEnterPenetrationModifier = pEnterSurfaceData->game.flPenetrationModifier;
const float flExitPenetrationModifier = pExitSurfaceData->game.flPenetrationModifier;
float flDamageLostModifier = 0.16f;
float flPenetrationModifier = 0.0f;
if (hEnterMaterial == CHAR_TEX_GRATE || hEnterMaterial == CHAR_TEX_GLASS)
{
flDamageLostModifier = 0.05f;
flPenetrationModifier = 3.0f;
}
else if (((data.enterTrace.contents >> 3) & contents_solid) || ((data.enterTrace.surface.flags >> 7) & surf_light))
{
flDamageLostModifier = 0.16f;
flPenetrationModifier = 1.0f;
}
else if (hEnterMaterial == CHAR_TEX_FLESH && flReductionDamage == 0.0f && data.enterTrace.m_pEnt != nullptr && data.enterTrace.m_pEnt->IsPlayer() && (g::pLocal->GetTeam() == data.enterTrace. m_pEnt->GetTeam()))
{
if (flPenetrateDamage == 0.0f)
return false;
// shoot through teammates
flDamageLostModifier = flPenetrateDamage;
flPenetrationModifier = flPenetrateDamage;
}
else
{
flDamageLostModifier = 0.16f;
flPenetrationModifier = (flEnterPenetrationModifier + flExitPenetrationModifier) * 0.5f;
}
if (hEnterMaterial == hExitMaterial)
{
if (hExitMaterial == CHAR_TEX_CARDBOARD || hExitMaterial == CHAR_TEX_WOOD)
flPenetrationModifier = 3.0f;
else if (hExitMaterial == CHAR_TEX_PLASTIC)
flPenetrationModifier = 2.0f;
}
const float flTraceDistance = (exitTrace.end - data.enterTrace.end).LengthSqr();
// penetration modifier
const float flModifier = (flPenetrationModifier > 0.0f ? 1.0f / flPenetrationModifier : 0.0f);
// this calculates how much damage we've lost depending on thickness of the wall, our penetration, damage, and the modifiers set earlier
const float flLostDamage = (data.flCurrentDamage * flDamageLostModifier + (pWeaponData->penetration > 0.0f ? 3.75f / pWeaponData->penetration : 0.0f) * (flModifier * 3.0f)) + ((flModifier * flTraceDistance) / 24.0f) ;
// did we loose too much damage?
if (flLostDamage > data.flCurrentDamage)
return false;
// we can't use any of the damage that we've lost
if (flLostDamage > 0.0f)
data.flCurrentDamage -= flLostDamage;
// do we still have enough damage to deal?
if (data.flCurrentDamage < 1.0f)
return false;
data.vecPosition = exitTrace.end;
--data.iPenetrateCount;
return true;
}
bool Autowall::SimulateFireBullet(C_BaseCombatWeapon* pWeapon, FireBulletData& data)
{
// @ida firebullet: client.dll @ 55 8B EC 83 E4 F0 81 EC ? ? ? ? F3 0F 7E
WeaponInfo_t* pWeaponData = pWeapon->GetCSWpnData();
if (pWeaponData == nullptr)
return false;
float flMaxRange = pWeaponData->range;
// the total number of any surfaces bullet can penetrate in a single flight is capped at 4
data.iPenetrateCount = 4;
// set our current damage to what our gun's initial damage reports it will do
data.flCurrentDamage = static_cast<float>(pWeaponData->damage);
float flTraceLength = 0.0f;
C_TraceFilter filter(g::pLocal);
while ((data.iPenetrateCount > 0) && (data.flCurrentDamage >= 1.0f))
{
// max bullet range
flMaxRange -= flTraceLength;
// end position of bullet
Vector vecEnd = data.vecPosition + data.vecDirection * flMaxRange;
TraceLine(data.vecPosition, vecEnd, mask_shot_hull | contents_hitbox, g::pLocal, &data.enterTrace);
// check for player hitboxes extending outside their collision bounds
ClipTraceToPlayers(data.vecPosition, vecEnd + data.vecDirection * 40.0f, mask_shot_hull | contents_hitbox, &filter, &data.enterTrace);
surfacedata_t* pEnterSurfaceData = g_pSurfaceData->GetSurfaceData(data.enterTrace.surface.surfaceProps);
const float flEnterPenetrationModifier = pEnterSurfaceData->game.flPenetrationModifier;
Utils::Log("fraction = " + std::to_string(data.enterTrace.flFraction));
// we didn't hit anything, stop tracing shoot
if (data.enterTrace.flFraction == 1.0f)
break;
// calculate the damage based on the distance the bullet traveled
flTraceLength += data.enterTrace.flFraction * flMaxRange;
data.flCurrentDamage *= std::powf(pWeaponData->range_modifier, flTraceLength / 500.f);
// check is actually can shoot through
if (flTraceLength > 3000.f || flEnterPenetrationModifier < 0.1f)
break;
Utils::Log("Got passed 2nd breakpoint");
// check is can do damage
if (data.enterTrace.hitGroup != HITGROUP_GENERIC && data.enterTrace.hitGroup != HITGROUP_GEAR && g::pLocal->IsEnemy(data.enterTrace.m_pEnt))
{
// we got target - scale damage
ScaleDamage(data.enterTrace.hitGroup, data.enterTrace.m_pEnt, pWeaponData->armor_ratio, pWeaponData->flHeadShotMultiplier, data.flCurrentDamage);
Utils::Log("Simulate returned true");
return true;
}
// calling handlebulletpenetration here reduces our penetration ñounter, and if it returns true, we can't shoot through it
if (!HandleBulletPenetration(pWeaponData, pEnterSurfaceData, data)) {
Utils::Log("HandleBulletPen returned false");
break;
}
Utils::Log("HandleBulletPen returned true ");
}
Utils::Log("Simulate returned false");
return false;
}
bool VectortoVectorVisible(Vector src, Vector point)
{
C_Trace Trace;
TraceLine(src, point, mask_solid, g::pLocal, &Trace);
if (Trace.flFraction == 1.0f)
{
return true;
}
return false;
}
bool Autowall::CanHitFloatingPoint(const Vector& point, const Vector& source) // source = eyepos
{
if (!g::pLocal)
return false;
FireBulletData data = {};
data.vecPosition = source;
Vector angles = g_Math.CalcAngle(data.vecPosition, point);
g_Math.AngleVectors(angles, &data.vecDirection);
VectorNormalize(data.vecDirection);
C_BaseCombatWeapon* pWeapon = (C_BaseCombatWeapon*)g::pLocal->GetActiveWeapon();
if (!pWeapon)
return false;
data.iPenetrateCount = 1;
data.iTraceLength = 0.0f;
WeaponInfo_t* weaponData = pWeapon->GetCSWpnData();
if (!weaponData)
return false;
data.flCurrentDamage = (float)weaponData->damage;
data.iTraceLengthRemaining = weaponData->range - data.iTraceLength;
Vector end = data.vecPosition + data.vecDirection * data.iTraceLengthRemaining;
TraceLine(data.vecPosition, end, mask_shot | contents_hitbox, g::pLocal, &data.enterTrace);
surfacedata_t* pEnterSurfaceData = g_pSurfaceData->GetSurfaceData(data.enterTrace.surface.surfaceProps);
if (VectortoVectorVisible(data.vecPosition, point))
{
return true;
}
if (HandleBulletPenetration(weaponData, pEnterSurfaceData, data))
{
return true;
}
return false;
}