-
Автор темы
- #1
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
Код:
#include "autowall.h"
bool autowall::is_breakable_entity(IClientEntity* e)
{
if (!e || !e->EntIndex())
return false;
static auto is_breakable = util::FindSignature(crypt_str("client.dll"), crypt_str("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)e + take_damage);
auto client_class = e->GetClientClass();
auto network_name = client_class->m_pNetworkName;
if (!strcmp(network_name, "CBreakableSurface"))
*(uint8_t*)((uintptr_t)e + take_damage) = DAMAGE_YES;
else if (!strcmp(network_name, "CBaseDoor") || !strcmp(network_name, "CDynamicProp"))
*(uint8_t*)((uintptr_t)e + take_damage) = DAMAGE_NO;
using Fn = bool(__thiscall*)(IClientEntity*);
auto result = ((Fn)is_breakable)(e);
*(uint8_t*)((uintptr_t)e + take_damage) = backup;
return result;
}
void autowall::scale_damage(const int hit_group, player_t* entity, const float weapon_armor_ratio, const float weapon_headshot_multiplier, float& damage) {
const auto heavy_armor = entity->m_bHasHeavyArmor();
static auto mp_damage_scale_ct_head = m_cvar()->FindVar(crypt_str("mp_damage_scale_ct_head"));
static auto mp_damage_scale_t_head = m_cvar()->FindVar(crypt_str("mp_damage_scale_t_head"));
static auto mp_damage_scale_ct_body = m_cvar()->FindVar(crypt_str("mp_damage_scale_ct_body"));
static auto mp_damage_scale_t_body = m_cvar()->FindVar(crypt_str("mp_damage_scale_t_body"));
auto head_damage_scale = entity->m_iTeamNum() == 3 ? mp_damage_scale_ct_head->GetFloat() : mp_damage_scale_t_head->GetFloat();
auto body_damage_scale = entity->m_iTeamNum() == 3 ? mp_damage_scale_ct_body->GetFloat() : mp_damage_scale_t_body->GetFloat();
if (heavy_armor)
head_damage_scale *= 0.5f;
switch (hit_group) {
case HITGROUP_HEAD:
damage *= weapon_headshot_multiplier * head_damage_scale;
break;
case HITGROUP_CHEST:
case HITGROUP_LEFTARM:
case HITGROUP_RIGHTARM:
damage *= body_damage_scale;
break;
case HITGROUP_STOMACH:
damage *= 1.25f * body_damage_scale;
break;
case HITGROUP_LEFTLEG:
case HITGROUP_RIGHTLEG:
damage *= 0.75f * body_damage_scale;
break;
default:
break;
}
auto is_armored = [&hit_group]() -> bool {
switch (hit_group) {
case HITGROUP_HEAD:
case HITGROUP_GENERIC:
case HITGROUP_CHEST:
case HITGROUP_STOMACH:
case HITGROUP_LEFTARM:
case HITGROUP_RIGHTARM:
case HITGROUP_LEFTLEG:
case HITGROUP_RIGHTLEG:
case HITGROUP_GEAR:
return true;
default:
return false;
}
};
if (entity->m_ArmorValue() > 0 && is_armored()) {
float heavy_armor_bonus = 1.f, armor_bonus = 0.5f, armor_ratio = weapon_armor_ratio * 0.5f;
if (heavy_armor) {
heavy_armor_bonus = 0.25f;
armor_bonus = 0.33f;
armor_ratio *= 0.20f;
}
float damage_to_health = damage * armor_ratio;
if (const auto damage_to_armor = (damage - damage_to_health) * (heavy_armor_bonus * armor_bonus); damage_to_armor > static_cast<float>(entity->m_ArmorValue()))
damage_to_health = damage - static_cast<float>(entity->m_ArmorValue()) / armor_bonus;
}
}
void autowall::clip_trace_to_player(const Vector& start, const Vector& end, const unsigned int mask, CTraceFilter* filter, CGameTrace* trace, const float min_range) {
// @credits: https://github.com/perilouswithadollarsign/cstrike15_src/blob/master/game/shared/util_shared.cpp#L757
CGameTrace* game_trace = {};
if (game_trace == nullptr)
return;
auto smallest_fraction = game_trace->fraction;
const Ray_t ray(start, end);
for (int i = 1; i < m_globals()->m_maxclients; i++) {
auto entity = static_cast<player_t*>(m_entitylist()->GetClientEntity(i));
if (!entity || !entity->is_alive() || entity->IsDormant())
continue;
if (filter != nullptr && filter->ShouldHitEntity(entity, mask))
continue;
const auto collideable = entity->GetCollideable();
if (!collideable)
continue;
// get bounding box.
const auto mins = collideable->OBBMins();
const auto maxs = collideable->OBBMaxs();
// calculate world space center.
const auto center = (maxs + mins) * 0.5f;
const auto position = center + entity->m_vecOrigin();
const auto to = position - start;
auto direction = end - start;
const auto length = direction.Normalize();
const auto range_along = direction.Dot(to);
auto range = 0.f;
// calculate distance to ray.
if (range_along < 0.0f)
// off start point.
range = -to.Length();
else if (range_along > length)
// off end point.
range = -(position - end).Length();
else
// within ray bounds.
range = (position - (direction * range_along + start)).Length();
constexpr auto max_range = 60.f;
if (range < min_range || range > max_range)
continue;
m_trace()->ClipRayToEntity(ray, mask | CONTENTS_HITBOX, entity, trace);
if (trace->fraction < smallest_fraction) {
// we shortened the ray - save off the trace.
trace = game_trace;
smallest_fraction = trace->fraction;
}
}
}
bool autowall::trace_to_exit(CGameTrace& enter_trace, CGameTrace& exit_trace, const Vector& position, const Vector& direction, player_t* clip_player) {
auto distance = 0.f;
auto start_contents = 0;
while (distance <= 90.f) {
// add extra distance to our ray.
distance += 4.f;
// multiply the direction vector to the distance so we go outwards, add our position to it.
auto start = position + direction * distance;
if (!start_contents)
start_contents = m_trace()->GetPointContents(start, MASK_SHOT_HULL | CONTENTS_HITBOX, nullptr);
const auto current_contets = m_trace()->GetPointContents(start, MASK_SHOT_HULL | CONTENTS_HITBOX, nullptr);
if (!(current_contets & MASK_SHOT_HULL) || (current_contets & CONTENTS_HITBOX && current_contets != start_contents)) {
// setup our end position by deducting the direction by the extra added distance.
const auto end = start - (direction * 4.f);
// trace ray to world.
Ray_t ray_world(start, end);
m_trace()->TraceRay(ray_world, MASK_SHOT_HULL | CONTENTS_HITBOX, nullptr, &exit_trace);
if (static auto sv_clip_penetration_traces_to_players = m_cvar()->FindVar(crypt_str("sv_clip_penetration_traces_to_players")); sv_clip_penetration_traces_to_players != nullptr && sv_clip_penetration_traces_to_players->GetBool()) {
CTraceFilter filter;
filter.pSkip = clip_player;
clip_trace_to_player(end, start, MASK_SHOT_HULL | CONTENTS_HITBOX, &filter, &exit_trace, -60.f);
}
// check if a hitbox is in-front of our enemy and if they are behind of a solid wall.
if (exit_trace.startsolid && exit_trace.surface.flags & SURF_HITBOX) {
// trace ray to entity.
Ray_t ray(start, position);
CTraceFilter filter;
filter.pSkip = exit_trace.hit_entity;
m_trace()->TraceRay(ray, MASK_SHOT_HULL, &filter, &exit_trace);
if (exit_trace.DidHit() && !exit_trace.startsolid) {
start = exit_trace.endpos;
return true;
}
continue;
}
if (exit_trace.DidHit() && !exit_trace.startsolid) {
if (is_breakable_entity(exit_trace.hit_entity) && is_breakable_entity(exit_trace.hit_entity))
return true;
if (enter_trace.surface.flags & SURF_NODRAW || (!(exit_trace.surface.flags & SURF_NODRAW) && exit_trace.plane.normal.Dot(direction) <= 1.f)) {
const auto multiplier = exit_trace.fraction * 4.f;
start -= direction * multiplier;
return true;
}
continue;
}
if (!exit_trace.DidHit() || exit_trace.startsolid) {
if (enter_trace.hit_entity != nullptr && enter_trace.hit_entity->EntIndex() != 0 && is_breakable_entity(exit_trace.hit_entity)) {
// did hit breakable non world entity.
exit_trace = enter_trace;
exit_trace.endpos = start + direction;
return true;
}
continue;
}
}
}
return false;
}
bool autowall::handle_bullet_penetration(player_t* entity, const weapon_info_t* weapon_data, const surfacedata_t* enter_surface_data, fire_bullet_t& data) {
static auto ff_damage_reduction_bullets = m_cvar()->FindVar(crypt_str("ff_damage_reduction_bullets"));
static auto ff_damage_bullet_penetration = m_cvar()->FindVar(crypt_str("ff_damage_bullet_penetration"));
const auto reduction_damage = ff_damage_reduction_bullets->GetFloat();
const auto penetratate_damage = ff_damage_bullet_penetration->GetFloat();
const auto enter_material = enter_surface_data->game.material;
if (data.penetrate_count == 0 && enter_material != CHAR_TEX_GRATE && enter_material != CHAR_TEX_GLASS && !(data.enter_trace.surface.flags & SURF_NODRAW))
return false;
if (weapon_data->flPenetration <= 0.f || data.penetrate_count <= 0)
return false;
CGameTrace exit_trace = {};
if (!trace_to_exit(data.enter_trace, exit_trace, data.enter_trace.endpos, data.direction, entity) && !(m_trace()->GetPointContents(data.enter_trace.endpos, MASK_SHOT_HULL, nullptr) & MASK_SHOT_HULL))
return false;
const auto exit_surface_data = m_physsurface()->GetSurfaceData(exit_trace.surface.surfaceProps);
const auto exit_material = exit_surface_data->game.material;
const auto enter_penetration_modifier = enter_surface_data->game.flPenetrationModifier;
const auto exit_penetration_modifier = exit_surface_data->game.flPenetrationModifier;
auto damage_lost_modifier = 0.16f;
auto penetration_modifier = 0.f;
if (enter_material == CHAR_TEX_GRATE || enter_material == CHAR_TEX_GLASS) {
damage_lost_modifier = 0.05f;
penetration_modifier = 3.f;
}
else if (((data.enter_trace.contents >> 3) & CONTENTS_SOLID) || ((data.enter_trace.surface.flags >> 7) & SURF_LIGHT)) {
damage_lost_modifier = 0.16f;
penetration_modifier = 1.f;
}
else if (enter_material == CHAR_TEX_FLESH && reduction_damage == 0.f && data.enter_trace.hit_entity != nullptr && ((player_t*)data.enter_trace.hit_entity)->is_player() && (entity->m_iTeamNum() == ((player_t*)data.enter_trace.hit_entity)->m_iTeamNum())) {
if (penetratate_damage == 0.f)
return false;
// shot through teammates.
damage_lost_modifier = penetratate_damage;
penetration_modifier = penetratate_damage;
}
else {
damage_lost_modifier = 0.16f;
penetration_modifier = (enter_penetration_modifier + exit_penetration_modifier) * 0.5f;
}
if (enter_material == exit_material) {
if (exit_material == CHAR_TEX_CARDBOARD || exit_material == CHAR_TEX_WOOD)
penetration_modifier = 3.f;
else if (exit_material == CHAR_TEX_PLASTIC)
penetration_modifier = 2.f;
}
const auto trace_distance = (exit_trace.endpos - data.enter_trace.endpos).LengthSqr();
// penetration modifier.
const auto modifier = (penetration_modifier > 0.f ? 1.f / penetration_modifier : 0.f);
// this calculates how much damage we've lost depending on thickness of the wall, our penetration, damage, and the modifiers set earlier.
const auto lost_damage = (data.current_damage * damage_lost_modifier + (weapon_data->flPenetration > 0.f ? 3.75f / weapon_data->flPenetration : 0.f) * (modifier * 3.f)) + ((modifier * trace_distance) / 24.f);
// did we loose too much damage?
if (lost_damage > data.current_damage)
return false;
// we can't use any of the damage that we've lost.
if (lost_damage > 0.f)
data.current_damage -= lost_damage;
// do we still have enough damage to deal?
if (data.current_damage < 1.f)
return false;
data.position = exit_trace.endpos;
--data.penetrate_count;
return true;
}
bool autowall::fire_bullet(weapon_t* pWeapon, Vector& direction, bool& visible, float& currentDamage, int& hitbox, player_t* e, float length, const Vector& pos)
{
if (!pWeapon)
return false;
auto weaponData = pWeapon->get_csweapon_info();
if (!weaponData)
return false;
CGameTrace enterTrace;
CTraceFilter filter;
fire_bullet_t data = { };
filter.pSkip = g_ctx.local();
currentDamage = weaponData->iDamage;
auto eyePosition = pos;
auto currentDistance = 0.0f;
auto maxRange = weaponData->flRange;
auto penetrationDistance = 3000.0f;
auto penetrationPower = weaponData->flPenetration;
auto possibleHitsRemaining = 4;
while (currentDamage >= 1.0f)
{
maxRange -= currentDistance;
auto end = eyePosition + direction * maxRange;
CTraceFilter filter;
filter.pSkip = g_ctx.local();
util::trace_line(eyePosition, end, MASK_SHOT_HULL | CONTENTS_HITBOX, &filter, &enterTrace);
// check for player hitboxes extending outside their collision bounds.
clip_trace_to_player( eyePosition, end + direction * 40.0f, MASK_SHOT_HULL | CONTENTS_HITBOX, &filter, &enterTrace);
auto enterSurfaceData = m_physsurface()->GetSurfaceData(enterTrace.surface.surfaceProps);
auto enterSurfPenetrationModifier = enterSurfaceData->game.flPenetrationModifier;
auto enterMaterial = enterSurfaceData->game.material;
if (enterTrace.fraction == 1.0f) //-V550
break;
currentDistance += enterTrace.fraction * maxRange;
currentDamage *= pow(weaponData->flRangeModifier, currentDistance / 500.0f);
if (currentDistance > penetrationDistance && weaponData->flPenetration || enterSurfPenetrationModifier < 0.1f) //-V1051
break;
auto canDoDamage = enterTrace.hitgroup != HITGROUP_GEAR && enterTrace.hitgroup != HITGROUP_GENERIC;
auto isPlayer = ((player_t*)enterTrace.hit_entity)->is_player();
auto isEnemy = ((player_t*)enterTrace.hit_entity)->m_iTeamNum() != g_ctx.local()->m_iTeamNum();
if (canDoDamage && isPlayer && isEnemy)
{
scale_damage(enterTrace.hitgroup, ((player_t*)enterTrace.hit_entity), weaponData->flArmorRatio, weaponData->headshotmultyplrier, currentDamage);
hitbox = enterTrace.hitbox;
return true;
}
if (!possibleHitsRemaining)
break;
static auto damageReductionBullets = m_cvar()->FindVar(crypt_str("ff_damage_reduction_bullets"));
static auto damageBulletPenetration = m_cvar()->FindVar(crypt_str("ff_damage_bullet_penetration"));
// calling handlebulletpenetration here reduces our penetration Counter, and if it returns true, we can't shot through it.
if (!handle_bullet_penetration(e, weaponData, enterSurfaceData, data))
break;
visible = false;
}
return false;
}
autowall::returninfo_t autowall::wall_penetration(const Vector& eye_pos, Vector& point, player_t* e)
{
g_ctx.globals.autowalling = true;
auto tmp = point - eye_pos;
auto angles = ZERO;
math::vector_angles(tmp, angles);
auto direction = ZERO;
math::angle_vectors(angles, direction);
direction.NormalizeInPlace();
auto visible = true;
auto damage = -1.0f;
auto hitbox = -1;
auto weapon = g_ctx.local()->m_hActiveWeapon().Get();
if (fire_bullet(weapon, direction, visible, damage, hitbox, e, 0.0f, eye_pos))
{
g_ctx.globals.autowalling = false;
return returninfo_t(visible, (int)damage, hitbox); //-V2003
}
else
{
g_ctx.globals.autowalling = false;
return returninfo_t(false, -1, -1);
}
}
Код:
#include "..\..\includes.hpp"
class weapon_info_t;
class weapon_t;
struct fire_bullet_data
{
Vector src;
trace_t enter_trace;
Vector direction;
CTraceFilter filter;
float trace_length;
float trace_length_remaining;
float current_damage;
int penetrate_count;
};
struct fire_bullet_t {
Vector position = {};
Vector direction = {};
CGameTrace enter_trace = {};
float current_damage = 0.f;
int penetrate_count = 0;
};
class autowall : public singleton <autowall>
{
public:
struct returninfo_t
{
bool valid = false;
bool visible = false;
int damage = -1;
int hitbox = -1;
returninfo_t()
{
valid = false;
visible = false;
damage = -1;
hitbox = -1;
}
returninfo_t(bool visible, int damage, int hitbox)
{
valid = true;
this->visible = visible;
this->damage = damage;
this->hitbox = hitbox;
}
};
struct FireBulletData
{
Vector src;
trace_t enter_trace;
Vector direction;
CTraceFilter filter;
weapon_info_t* wpn_data;
float trace_length;
float trace_length_remaining;
float length_to_end;
float current_damage;
int penetrate_count;
};
bool is_breakable_entity(IClientEntity* e);
void scale_damage(const int hit_group, player_t* entity, const float weapon_armor_ratio, const float weapon_headshot_multiplier, float& damage);
void clip_trace_to_player(const Vector& start, const Vector& end, const unsigned int mask, CTraceFilter* filter, CGameTrace* trace, const float min_range = 0.f);
bool trace_to_exit(CGameTrace& enter_trace, CGameTrace& exit_trace, const Vector& position, const Vector& direction, player_t* clip_player);
bool handle_bullet_penetration(player_t* entity, const weapon_info_t* weapon_data, const surfacedata_t* enter_surface_data, fire_bullet_t& data);
returninfo_t wall_penetration(const Vector& eye_pos, Vector& point, player_t* e);
bool fire_bullet(weapon_t* pWeapon, Vector& direction, bool& visible, float& currentDamage, int& hitbox, player_t* e = nullptr, float length = 0.f, const Vector& pos = { 0.f,0.f,0.f });
};