2025-07-25 22:49:56 +03:00

392 lines
17 KiB
C++

#include "penetration.h"
#include "../../core/variables.h"
#include "../../sdk/datatypes/usercmd.h"
#include "../../core/sdk.h"
#include "../../sdk/entity.h"
#include "../../sdk/interfaces/iengineclient.h"
#include "../../sdk/interfaces/iglobalvars.h"
#include "../../sdk/interfaces/cgameentitysystem.h"
#include "../../sdk/datatypes/qangle.h"
#include "../../sdk/datatypes/vector.h"
#include "../../sdk/entity_handle.h"
#include "../cstrike/sdk/interfaces/ienginecvar.h"
#include <mutex>
#include <array>
#include "../cstrike/sdk/interfaces/itrace.h"
static constexpr std::uint32_t PENMASK = 0x1C300Bu; // mask_shot_hull | contents_hitbox?
namespace F::AUTOWALL {
void F::AUTOWALL::c_auto_wall::pen(data_t& data, const Vector_t local_pos, const Vector_t target_pos, C_BaseEntity* target, C_BaseEntity* local, C_CSPlayerPawn* localpawn, C_CSPlayerPawn* targetpawn,
CCSWeaponBaseVData* wpn_data, float &dmg, bool &valid) {
data.m_local = local;
data.m_target = target;
data.m_local_pawn = localpawn;
data.m_target_pawn = targetpawn;
data.m_wpn_data = wpn_data;
data.m_pos.at(F::AUTOWALL::c_auto_wall::data_t::e_pos::e_local) = local_pos;
data.m_pos.at(F::AUTOWALL::c_auto_wall::data_t::e_pos::e_target) = target_pos;
FireBullet(data, data.m_dmg, data.m_can_hit);
}
void F::AUTOWALL::c_auto_wall::ScaleDamage(data_t& data, const int hitgroup, C_CSPlayerPawn* player)
{
if (!player)
return;
auto WeaponServices = player->GetItemServices();
if (!WeaponServices)
return;
// ida: server.dll; 80 78 42 00 74 08 F3 0F 59 35 ?? ?? ?? ?? 80 BE 04 0D 00 00 00
static CConVar* mp_damage_scale_ct_head = I::Cvar->Find(
FNV1A::HashConst("mp_damage_scale_ct_head")),
* mp_damage_scale_t_head = I::Cvar->Find(
FNV1A::HashConst("mp_damage_scale_t_head")),
* mp_damage_scale_ct_body = I::Cvar->Find(
FNV1A::HashConst("mp_damage_scale_ct_body")),
* mp_damage_scale_t_body = I::Cvar->Find(
FNV1A::HashConst("mp_damage_scale_t_body"));
const auto damage_scale_ct_head = mp_damage_scale_ct_head->value.fl,
damage_scale_t_head = mp_damage_scale_t_head->value.fl,
damage_scale_ct_body = mp_damage_scale_ct_body->value.fl,
damage_scale_t_body = mp_damage_scale_t_body->value.fl;
const bool is_ct = player->GetTeam() == 3, is_t = player->GetTeam() == 2;
float head_damage_scale = is_ct ? damage_scale_ct_head : is_t ? damage_scale_t_head : 1.0f;
const float body_damage_scale = is_ct ? damage_scale_ct_body : is_t ? damage_scale_t_body : 1.0f;
// william: magic values u can see here: ida: server.dll; F3 0F 10 35 ?? ?? ?? ?? 0F 29 7C 24 30 44 0F 29 44 24
// xref: mp_heavybot_damage_reduction_scale
if (WeaponServices->m_bHasHeavyArmor()) {
head_damage_scale *= 0.5f;
}
// todo: mb missed some hitgroups, anyway this is a calculation of all important hitgroups
switch (hitgroup) {
case HitGroup_t::HITGROUP_HEAD:
data.m_dmg *= data.m_wpn_data->m_flHeadshotMultiplier() * head_damage_scale;
break;
case HitGroup_t::HITGROUP_CHEST:
case HitGroup_t::HITGROUP_LEFTARM:
case HitGroup_t::HITGROUP_RIGHTARM:
case HitGroup_t::HITGROUP_NECK:
data.m_dmg *= body_damage_scale;
break;
case HitGroup_t::HITGROUP_STOMACH:
data.m_dmg *= 1.25f * body_damage_scale;
break;
case HitGroup_t::HITGROUP_LEFTLEG:
case HitGroup_t::HITGROUP_RIGHTLEG:
data.m_dmg *= 0.75f * body_damage_scale;
break;
default:
break;
}
if (!player->hasArmour(hitgroup))
return;
float heavy_armor_bonus = 1.0f, armor_bonus = 0.5f, armor_ratio = data.m_wpn_data->m_flArmorRatio() * 0.5f;
if (WeaponServices->m_bHasHeavyArmor()) {
heavy_armor_bonus = 0.25f;
armor_bonus = 0.33f;
armor_ratio *= 0.20f;
}
float damage_to_health = data.m_dmg * armor_ratio;
const float damage_to_armor = (data.m_dmg - damage_to_health) * (heavy_armor_bonus * armor_bonus);
if (damage_to_armor > static_cast<float>(player->GetArmorValue())) {
damage_to_health = data.m_dmg - static_cast<float>(player->GetArmorValue()) / armor_bonus;
}
data.m_dmg = damage_to_health;
}
bool F::AUTOWALL::c_auto_wall::FireBullet(data_t& data, float &dmg, bool &valid)
{
// L_PRINT(LOG_INFO) << "0";
CS_ASSERT(data.m_local != nullptr || data.m_target != nullptr || data.m_wpn_data != nullptr || data.m_local_pawn != nullptr || data.m_target_pawn != nullptr);
if (!data.m_local || !data.m_target || !data.m_wpn_data)
return false;
// L_PRINT(LOG_INFO) << "1";
trace_data_t trace_data = { };
trace_data.m_arr_pointer = &trace_data.m_arr;
void* data_pointer = &trace_data;
//L_PRINT(LOG_INFO) << "created trace_data | trace_data_pointer:" << L::AddFlags(LOG_MODE_INT_SHOWBASE | LOG_MODE_INT_FORMAT_HEX) << reinterpret_cast<uintptr_t>(data_pointer);
//L_PRINT(LOG_INFO) << "created trace_data | pointer:" << L::AddFlags(LOG_MODE_INT_SHOWBASE | LOG_MODE_INT_FORMAT_HEX) << reinterpret_cast<uintptr_t>(trace_data.m_arr_pointer);
const Vector_t direction =
data.m_pos.at(data_t::e_pos::e_target) - data.m_pos.at(data_t::e_pos::e_local),
end_pos = direction * data.m_wpn_data->m_flRange();
trace_filter_t filter = {};
I::Trace->Init(filter, data.m_local_pawn,PENMASK, 3, 7);
void* filter_pointer = &filter;
/* L_PRINT(LOG_INFO) << "created filter_data | filter_pointer:" << L::AddFlags(LOG_MODE_INT_SHOWBASE | LOG_MODE_INT_FORMAT_HEX) << reinterpret_cast<uintptr_t>(filter_pointer);
L_PRINT(LOG_INFO) << "creating trace | filter > layer:" << filter.layer << "mask:" << filter.trace_mask;
L_PRINT(LOG_INFO) << "creating trace | endpos:" << end_pos;
*/
I::Trace->CreateTrace(&trace_data, data.m_pos.at(data_t::e_pos::e_local), end_pos, filter, 4);
struct handle_bullet_data_t {
handle_bullet_data_t(const float dmg_mod, const float pen, const float range_mod, const float range,
const int pen_count, const bool failed) :
m_dmg(dmg_mod),
m_pen(pen),
m_range_mod(range_mod),
m_range(range),
m_pen_count(pen_count),
m_failed(failed) {}
float m_dmg{ }, m_pen{ }, m_range_mod{ }, m_range{ };
int m_pen_count{ };
bool m_failed{ };
}
handle_bullet_data(static_cast<float>(data.m_wpn_data->m_nDamage()), data.m_wpn_data->m_flPenetration(), data.m_wpn_data->m_flRange(),
data.m_wpn_data->m_flRangeModifier(), 4, false);
// L_PRINT(LOG_INFO) << "Initialized handlebulletpen data";
float corrected_dmg = static_cast<float>(data.m_wpn_data->m_nDamage());
float flTraceLength = 0.f;
auto flMaxRange = data.m_wpn_data->m_flRange();
if (trace_data.m_num_update > 0) {
for (int i{ }; i < trace_data.m_num_update; i++) {
auto* value = reinterpret_cast<UpdateValueT* const>(
reinterpret_cast<std::uintptr_t>(trace_data.m_pointer_update_value)
+ i * sizeof(UpdateValueT));
game_trace_t game_trace = { };
I::Trace->InitializeTraceInfo(&game_trace);
I::Trace->get_trace_info(
&trace_data, &game_trace, 0.0f,
reinterpret_cast<void*>(
reinterpret_cast<std::uintptr_t>(trace_data.m_arr.data())
+ sizeof(trace_arr_element_t) * (value->handleIdx & 0x7fffu)));
/*
L_PRINT(LOG_INFO) << "game_trace: entryindex:" << game_trace.HitEntity->GetRefEHandle().GetEntryIndex();
L_PRINT(LOG_INFO) << "game_trace: m_target_pawn entryindex:" << data.m_target_pawn->GetRefEHandle().GetEntryIndex();
L_PRINT(LOG_INFO) << "game_trace: m_target entryindex:" << data.m_target->GetRefEHandle().GetEntryIndex();
*/
flMaxRange -= flTraceLength;
// we didn't hit anything, stop tracing shoot
if (game_trace.Fraction == 1.0f)
{
break;
}
// calculate the damage based on the distance the bullet traveled
flTraceLength += game_trace.Fraction * flMaxRange;
corrected_dmg *= std::powf(data.m_wpn_data->m_flRangeModifier(), flTraceLength / 500.f);
// check is actually can shoot through
if (flTraceLength > 3000.f)
break;
if (game_trace.HitEntity && game_trace.HitEntity->GetRefEHandle().GetEntryIndex() == data.m_target_pawn->GetRefEHandle().GetEntryIndex()) {
ScaleDamage2(game_trace.HitboxData->m_hitgroup, data.m_target_pawn, data.m_wpn_data->m_flArmorRatio(), data.m_wpn_data->m_flHeadshotMultiplier(), &corrected_dmg);
dmg = corrected_dmg;//data.m_dmg > 0.f ? data.m_dmg : handle_bullet_data.m_dmg;
valid = true;
return true;
}
else
valid = false;
if (I::Trace->handle_bullet_penetration(&trace_data, &handle_bullet_data, value, false))
return false;
corrected_dmg = handle_bullet_data.m_dmg;
}
}
return false;
}
void F::AUTOWALL::c_auto_wall::ScaleDamage2(const int iHitGroup, C_CSPlayerPawn* pCSPlayer, const float flWeaponArmorRatio, const float flWeaponHeadShotMultiplier, float* pflDamageToScale)
{
// @ida CCSPlayer::TraceAttack(): server.dll -> "55 8B EC 83 E4 F8 81 EC ? ? ? ? 56 8B 75 08 57 8B F9 C6"
auto WeaponServices = pCSPlayer->GetItemServices();
if (!WeaponServices)
return;
const bool bHeavyArmor = WeaponServices->m_bHasHeavyArmor();
// ida: server.dll; 80 78 42 00 74 08 F3 0F 59 35 ?? ?? ?? ?? 80 BE 04 0D 00 00 00
static CConVar* mp_damage_scale_ct_head = I::Cvar->Find(
FNV1A::HashConst("mp_damage_scale_ct_head")),
* mp_damage_scale_t_head = I::Cvar->Find(
FNV1A::HashConst("mp_damage_scale_t_head")),
* mp_damage_scale_ct_body = I::Cvar->Find(
FNV1A::HashConst("mp_damage_scale_ct_body")),
* mp_damage_scale_t_body = I::Cvar->Find(
FNV1A::HashConst("mp_damage_scale_t_body"));
const auto damage_scale_ct_head = mp_damage_scale_ct_head->value.fl,
damage_scale_t_head = mp_damage_scale_t_head->value.fl,
damage_scale_ct_body = mp_damage_scale_ct_body->value.fl,
damage_scale_t_body = mp_damage_scale_t_body->value.fl;
float flHeadDamageScale = (pCSPlayer->GetTeam() == TEAM_CT ? damage_scale_ct_head : damage_scale_t_head);
const float flBodyDamageScale = (pCSPlayer->GetTeam() == TEAM_CT ? damage_scale_ct_body : damage_scale_t_body);
if (bHeavyArmor) flHeadDamageScale *= 0.5f;
switch (iHitGroup)
{
case HITGROUP_HEAD:
*pflDamageToScale *= flWeaponHeadShotMultiplier * flHeadDamageScale;
break;
case HITGROUP_CHEST:
case HITGROUP_LEFTARM:
case HITGROUP_RIGHTARM:
case HITGROUP_NECK:
*pflDamageToScale *= flBodyDamageScale;
break;
case HITGROUP_STOMACH:
*pflDamageToScale *= 1.25f * flBodyDamageScale;
break;
case HITGROUP_LEFTLEG:
case HITGROUP_RIGHTLEG:
*pflDamageToScale *= 0.75f * flBodyDamageScale;
break;
default:
break;
}
if (pCSPlayer->hasArmour(iHitGroup))
{
// @ida CCSPlayer::OnTakeDamage(): server.dll -> "80 BF ? ? ? ? ? F3 0F 10 5C 24 ? F3 0F 10 35"
const int iArmor = pCSPlayer->GetArmorValue();
float flHeavyArmorBonus = 1.0f, flArmorBonus = 0.5f, flArmorRatio = flWeaponArmorRatio * 0.5f;
if (bHeavyArmor)
{
flHeavyArmorBonus = 0.25f;
flArmorBonus = 0.33f;
flArmorRatio *= 0.20f;
}
float flDamageToHealth = *pflDamageToScale * flArmorRatio;
if (const float flDamageToArmor = (*pflDamageToScale - flDamageToHealth) * (flHeavyArmorBonus * flArmorBonus); flDamageToArmor > static_cast<float>(iArmor))
flDamageToHealth = *pflDamageToScale - static_cast<float>(iArmor) / flArmorBonus;
*pflDamageToScale = flDamageToHealth;
}
}
/* bool F::AUTOWALL::c_auto_wall::FireBullet(data_t& data, float& dmg, bool& valid)
{
CS_ASSERT(data.m_local != nullptr || data.m_target != nullptr || data.m_wpn_data != nullptr || data.m_local_pawn != nullptr || data.m_target_pawn != nullptr);
if (!data.m_local || !data.m_target || !data.m_wpn_data)
return false;
trace_data_t trace_data = { };
trace_data.m_arr_pointer = &trace_data.m_arr;
Vector_t direction =
data.m_pos.at(data_t::e_pos::e_target) - data.m_pos.at(data_t::e_pos::e_local),
end_pos = direction * data.m_wpn_data->m_flRange();
int pen_count = 4;
float flTraceLength = 0.f;
float flMaxRange = data.m_wpn_data->m_flRange();
// set our current damage to what our gun's initial damage reports it will do
data.m_dmg = static_cast<float>(data.m_wpn_data->m_nDamage());
trace_filter_t filter = {};
I::Trace->Init(filter, data.m_local_pawn, MASK_SHOT_HULL | CONTENTS_HITBOX, 3, 7);
I::Trace->CreateTrace(&trace_data, data.m_pos.at(data_t::e_pos::e_local), end_pos, filter, 4);
struct handle_bullet_data_t {
handle_bullet_data_t(const float dmg_mod, const float pen, const float range_mod, const float range,
const int pen_count, const bool failed) :
m_dmg(dmg_mod),
m_pen(pen),
m_range_mod(range_mod),
m_range(range),
m_pen_count(pen_count),
m_failed(failed) {}
float m_dmg{ }, m_pen{ }, m_range_mod{ }, m_range{ };
int m_pen_count{ };
bool m_failed{ };
}
handle_bullet_data(static_cast<float>(data.m_wpn_data->m_nDamage()), data.m_wpn_data->m_flPenetration(), data.m_wpn_data->m_flRange(),
data.m_wpn_data->m_flRangeModifier(), 4, false);
if (trace_data.m_num_update > 0) {
for (int i{ }; i < trace_data.m_num_update; i++) {
auto* value = reinterpret_cast<UpdateValueT* const>(
reinterpret_cast<std::uintptr_t>(trace_data.m_pointer_update_value)
+ i * sizeof(UpdateValueT));
game_trace_t game_trace = { };
I::Trace->InitializeTraceInfo(&game_trace);
I::Trace->get_trace_info(
&trace_data, &game_trace, 0.0f,
reinterpret_cast<void*>(
reinterpret_cast<std::uintptr_t>(trace_data.m_arr.data())
+ sizeof(trace_arr_element_t) * (value->handleIdx & 0x7fffu)));
flMaxRange -= flTraceLength;
game_trace_t trace = {};
ray_t ray = {};
I::Trace->TraceShape(ray, &data.m_pos.at(data_t::e_pos::e_local), &end_pos, filter, trace);
I::Trace->ClipTraceToPlayers(data.m_pos.at(data_t::e_pos::e_local), end_pos, &filter, &trace, 0.F, 60.F, (1.F / (data.m_pos.at(data_t::e_pos::e_local) - end_pos).Length()) * (trace.m_end_pos - data.m_pos.at(data_t::e_pos::e_local)).Length());
if (trace.Fraction == 1.f)
break;
// calculate the damage based on the distance the bullet traveled
flTraceLength += trace.Fraction * flMaxRange;
data.m_dmg *= std::powf(data.m_wpn_data->m_flRangeModifier(), flTraceLength / 500.f);
// check is actually can shoot through
if (flTraceLength > 3000.f)
break;
L_PRINT(LOG_INFO) << "idx:" << trace.HitEntity->GetRefEHandle().GetEntryIndex() << " dmg:" << data.m_dmg;
if (trace.HitEntity->GetRefEHandle().GetEntryIndex() == data.m_target_pawn->GetRefEHandle().GetEntryIndex())
{
ScaleDamage2(game_trace.HitboxData->m_hitgroup, data.m_target_pawn, data.m_wpn_data->m_flArmorRatio(), data.m_wpn_data->m_flHeadshotMultiplier(), &data.m_dmg);
L_PRINT(LOG_INFO) << "dmg:" << data.m_dmg;
return true;
}
// check if the bullet can no longer continue penetrating materials
if (I::Trace->handle_bullet_penetration(&trace_data, &handle_bullet_data, value, false))
return false;
pen_count = handle_bullet_data.m_pen_count;
data.m_dmg = handle_bullet_data.m_dmg;
}
}
return false;
}*/
std::unique_ptr<c_auto_wall> g_auto_wall{};
}