Модератор раздела «Создание читов CS2»
-
Автор темы
- #1
Обратите внимание, пользователь заблокирован на форуме. Не рекомендуется проводить сделки.
В этой теме вы узнаете:
- Как работает движок трассировки.
- Что такое фильтры трассировки.
- Как работает система пенетрации.
- Как сделать правильный autowall.
- И многое другое.
Структура игровой трассировки (GameTrace) #1
Давайте взглянем на исходник игры и найдем соответствующий класс нужной нами игровой трассировки. (
Как мы видим, класс состоит из переменных: fractionleftsolid, surface, hitgroup, physicsbone, worldSurfaceIndex, hitbox, m_pEnt и др., а так же из функций:
Данные, которые хранит игровая трассировка #1.1
Принцип работы функции
Функция отслеживает только мировые столкновения и возвращает true, если объект это мировая сущность.
Примечание от Valve: Если функция возвращает true, то вы не сможете использовать функцию GetHitBoxIndex.
Принцип работы функции
Функция отслеживает все столкновения, кроме мировых. Возвращает true, если мы(трассировка) наткнулись на что-то и это не является мировым объектом.
Для проверки столкновения с миром, мы используем уже известную нами функцию
Принцип работы функции
Функция возвращает индекс объекта, на который мы(трассировка) наткнулись. В случае неудачи возвращается -1.
Принцип работы функции
Функция возвращает true, если мы(трассировка) наткнулись на какой-либо объект при условии: если мы в него попали && недействительная плоскость && начальная точка находилась в сплошной области (см. #1.1).
Фильтр трассировки #2
Если кратно, то это создано для того, чтобы фильтровать/игнорировать определенные выбранные нами объекты.
Мы можем выбрать что хотим трассировать: всё на карте, только мир, только не мировые объекты и т.д.
Соответствующее классы для определенного фильтра (
Структура лучей (Ray_t) #3
Данная структура помогает в создании собственного луча.
Ниже приведена таблица с данными и их объяснениями, которые хранит и использует структура Ray_t (
Функции, которые инициализируют нужные данные (
Структура движка трассировки (EngineTrace) #4
Это очень интересная вещь, но в основном всех кодеров читов интересует только эти функции:
- GetPointContents: Возвращает маску содержимого и объект в определенной позиции карты (принимает 3 параметра: позицию, маску содержимого и объект).
- ClipRayToEntity: Пускает луч до определенного объекта на карте и возвращает данные трассировки (принимает 4 аргумента: данные о луче(см. #3), маску содержимого, объект и данные о трассировке).
- TraceRay: Просто принимает луч трассировки (принимает 4 аргумента: данные о луче, маску содержимого, объект и данные о трассировке).
Я не буду заострять внимание на всех функциях, так как их много, но если вам интересно более глубже изучить движок трассировки, то вот ссылка:
Система пенетрации (или как сделать autowall) #5
Изучив всю нужную информацию о движке трассировки и сопутствующих к нему структур и классов, мы можем сделать функцию, которая в простонародии называется autowall или же система пенетрации.
Приступим к самому главному — изучению работы простелов в CS:GO. Существует интересная функция
Но нас не интересуют импакты, нам нужно сделать хорошую систему прострелов. Поэтому, если более внимательней изучить код, то можно найти интересные переменные:
Ниже приведена таблица, которая объясняет значение этих данных:
Прочитав, я думаю понятно, что это весьма интересные и нужные данные для системы прострела. Посмотрим, какие они имеют значения, где еще рассчитываются и где применяются, исключив ненужный код импактов и другого дерьма.
Как мы можем видеть, новое расстояние рассчитывается на базе разницы пройденого и максимального, а урон исходя из нового пройденного пулей расстояния.
А так же видим проверку на достижения дистанции пробития пулей, после этого пробитий не должно быть. И если наш модификатор мощности пробития очень низкий, просто останавливаем пулю. Установка nPenetrationCount в ноль предотвращает попадание пули в объект на максимальном расстоянии.
Видим, что переменные передаются в качестве аргументов функции HandleBulletPenetration.
Данная функция рассчитывает при каком условии пуля больше не может пробивать определенные объекты(материалы), т.е справляется с проникновением пуль в объекты. Возвращает значение true, если пуля больше не может простреливать какой-либо объект.
Вот один из примеров, когда пуля не сможет прострелить объект (при условии, если мы на максимальном расстоянии и материал это решетка или стекло):
Далее, если немного опуститься вглубь функции, можно увидеть расчет модификации мощности пуль, после того, как они пробивают стену, модификатор мощности пробития, урон пули на текущей дистанции, толщину стены, которую может пробить пуля и центр дистанции.
Хорошо, начинку мы изучили, но потеряли одну проверку связанную с функцией
Что же она такого интересного делает? Данная функция находит точную конечную позицию проникновения пули.
Если кратко, то она работает пока дистанция меньше или равна максимальной, после вычисляет конечную позицию на основе дистанции, далее получает содержимое точки и работает с системой трассировки и его движком, пускает луч к объекту, проверяет разные данные по типу startsolid и surface флаги для проверки попадания через стену в хитбокс игрока.
Но самое интересное во всех этих вычислениях — функция
Вроде бы все, не забываем о расчете урона для каждой хитгрупы противника. Для расчета урона в CS:GO сделана функция
Имея на руках всю нужную информацию, можем сделать полноценный, готовый autowall.
- Как работает движок трассировки.
- Что такое фильтры трассировки.
- Как работает система пенетрации.
- Как сделать правильный autowall.
- И многое другое.
Структура игровой трассировки (GameTrace) #1
Давайте взглянем на исходник игры и найдем соответствующий класс нужной нами игровой трассировки. (
Пожалуйста, авторизуйтесь для просмотра ссылки.
)Как мы видим, класс состоит из переменных: fractionleftsolid, surface, hitgroup, physicsbone, worldSurfaceIndex, hitbox, m_pEnt и др., а так же из функций:
Пожалуйста, авторизуйтесь для просмотра ссылки.
,
Пожалуйста, авторизуйтесь для просмотра ссылки.
,
Пожалуйста, авторизуйтесь для просмотра ссылки.
,
Пожалуйста, авторизуйтесь для просмотра ссылки.
и
Пожалуйста, авторизуйтесь для просмотра ссылки.
Ниже мы разберем каждую переменную и функцию.Данные, которые хранит игровая трассировка #1.1
surface | поверхность удара |
hitgroup | определенная часть тела |
physicsbone | физическая кость |
worldSurfaceIndex | индекс структуры msurface2_t |
m_pEnt | объект столкновения |
hitbox | если мы(трассировка) наткнулись на что-то и это не является мировым объектом, то возвращаемым значением будет индекс хитбокса, иначе статическим индексом пропа. |
startpos | начальная позиция |
endpos | конечная позиция |
plane | поверхности при ударе |
fraction | время завершения (равен 1.0, если мы не во что не попали) |
contents | содержимое на другой стороне поверхности удара |
dispFlags | флаги смещения для маркировки поверхностей |
allsolid | возвращает true, если плоскость невалидна |
startsolid | возвращает true, если начальная точка находилась в сплошной области |
Принцип работы функции
Пожалуйста, авторизуйтесь для просмотра ссылки.
#1.2Функция отслеживает только мировые столкновения и возвращает true, если объект это мировая сущность.
C++:
bool CGameTrace::DidHitWorld() const
{
return m_pEnt == GetWorldEntity();
}
Принцип работы функции
Пожалуйста, авторизуйтесь для просмотра ссылки.
#1.3Функция отслеживает все столкновения, кроме мировых. Возвращает true, если мы(трассировка) наткнулись на что-то и это не является мировым объектом.
Для проверки столкновения с миром, мы используем уже известную нами функцию
Пожалуйста, авторизуйтесь для просмотра ссылки.
(#1.2)
C++:
bool CGameTrace::DidHitNonWorldEntity() const
{
return m_pEnt != NULL && !DidHitWorld();
}
Пожалуйста, авторизуйтесь для просмотра ссылки.
#1.4Функция возвращает индекс объекта, на который мы(трассировка) наткнулись. В случае неудачи возвращается -1.
C++:
int CGameTrace::GetEntityIndex() const
{
if ( m_pEnt )
return m_pEnt->entindex();
else
return -1;
}
Пожалуйста, авторизуйтесь для просмотра ссылки.
#1.5Функция возвращает true, если мы(трассировка) наткнулись на какой-либо объект при условии: если мы в него попали && недействительная плоскость && начальная точка находилась в сплошной области (см. #1.1).
C++:
bool CGameTrace::DidHit() const
{
return fraction < 1 || allsolid || startsolid;
}
Если кратно, то это создано для того, чтобы фильтровать/игнорировать определенные выбранные нами объекты.
Мы можем выбрать что хотим трассировать: всё на карте, только мир, только не мировые объекты и т.д.
C++:
enum TraceType_t
{
TRACE_EVERYTHING = 0,
TRACE_WORLD_ONLY,
TRACE_ENTITIES_ONLY,
TRACE_EVERYTHING_FILTER_PROPS, // будет передавать IHandleEntity для реквизита через фильтр, в отличие от всех других фильтров.
};
Пожалуйста, авторизуйтесь для просмотра ссылки.
):
C++:
class CTraceFilterEntitiesOnly : public ITraceFilter
{
public:
virtual TraceType_t GetTraceType() const
{
return TRACE_ENTITIES_ONLY;
}
};
class CTraceFilterWorldOnly : public ITraceFilter
{
public:
bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
{
return false;
}
virtual TraceType_t GetTraceType() const
{
return TRACE_WORLD_ONLY;
}
};
class CTraceFilterWorldAndPropsOnly : public ITraceFilter
{
public:
bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
{
return false;
}
virtual TraceType_t GetTraceType() const
{
return TRACE_EVERYTHING;
}
};
class CTraceFilterHitAll : public CTraceFilter
{
public:
virtual bool ShouldHitEntity( IHandleEntity *pServerEntity, int contentsMask )
{
return true;
}
};
Данная структура помогает в создании собственного луча.
Ниже приведена таблица с данными и их объяснениями, которые хранит и использует структура Ray_t (
Пожалуйста, авторизуйтесь для просмотра ссылки.
).m_Start | начальная точка |
m_Delta | направление и длина луча |
m_StartOffset | фактическое начало луча (чтобы получить это, добавьте m_Start) |
m_Extents | описывает выровненный по оси прямоугольник, выдавленный вдоль луча |
m_IsRay | возвращает true, если m_Extents равен нулю |
m_IsSwept | возвращает true, если направление и длина луча не равны нулю |
Функции, которые инициализируют нужные данные (
Пожалуйста, авторизуйтесь для просмотра ссылки.
):
C++:
void Init( Vector const& start, Vector const& end )
{
Assert( &end );
VectorSubtract( end, start, m_Delta );
m_IsSwept = (m_Delta.LengthSqr() != 0);
VectorClear( m_Extents );
m_pWorldAxisTransform = NULL;
m_IsRay = true;
// Offset m_Start to be in the center of the box...
VectorClear( m_StartOffset );
VectorCopy( start, m_Start );
}
void Init( Vector const& start, Vector const& end, Vector const& mins, Vector const& maxs )
{
Assert( &end );
VectorSubtract( end, start, m_Delta );
m_pWorldAxisTransform = NULL;
m_IsSwept = (m_Delta.LengthSqr() != 0);
VectorSubtract( maxs, mins, m_Extents );
m_Extents *= 0.5f;
m_IsRay = (m_Extents.LengthSqr() < 1e-6);
Assert(m_Extents.x >=0 && m_Extents.y >= 0 && m_Extents.z >= 0);
// Offset m_Start to be in the center of the box...
VectorAdd( mins, maxs, m_StartOffset );
m_StartOffset *= 0.5f;
VectorAdd( start, m_StartOffset, m_Start );
m_StartOffset *= -1.0f;
}
Это очень интересная вещь, но в основном всех кодеров читов интересует только эти функции:
- GetPointContents: Возвращает маску содержимого и объект в определенной позиции карты (принимает 3 параметра: позицию, маску содержимого и объект).
- ClipRayToEntity: Пускает луч до определенного объекта на карте и возвращает данные трассировки (принимает 4 аргумента: данные о луче(см. #3), маску содержимого, объект и данные о трассировке).
- TraceRay: Просто принимает луч трассировки (принимает 4 аргумента: данные о луче, маску содержимого, объект и данные о трассировке).
Я не буду заострять внимание на всех функциях, так как их много, но если вам интересно более глубже изучить движок трассировки, то вот ссылка:
Пожалуйста, авторизуйтесь для просмотра ссылки.
Система пенетрации (или как сделать autowall) #5
Изучив всю нужную информацию о движке трассировки и сопутствующих к нему структур и классов, мы можем сделать функцию, которая в простонародии называется autowall или же система пенетрации.
Приступим к самому главному — изучению работы простелов в CS:GO. Существует интересная функция
Пожалуйста, авторизуйтесь для просмотра ссылки.
, она проводит различные вычисления и работу с выстреленной пулей, в пример возьмем импакты: внутри функции сохраняются данные x,y,z из конечной позиции в структуре трассировкии передаются в событие(эвент) "bullet_impact".Но нас не интересуют импакты, нам нужно сделать хорошую систему прострелов. Поэтому, если более внимательней изучить код, то можно найти интересные переменные:
Пожалуйста, авторизуйтесь для просмотра ссылки.
и другие, о которых я напишу позже.Ниже приведена таблица, которая объясняет значение этих данных:
fCurrentDamage | урон пули на текущей траектории |
flCurrentDistance | расстояние, которое прошла пуля |
flPenetrationPower | толщина стены, которую может пробить эта пуля |
flPenetrationDistance | расстояние, на котором пуля способна пробить стену |
flDamageModifier | модификация мощности пуль по умолчанию после того, как они пробивают стену |
flPenetrationModifier | модификатор мощности пробития |
Прочитав, я думаю понятно, что это весьма интересные и нужные данные для системы прострела. Посмотрим, какие они имеют значения, где еще рассчитываются и где применяются, исключив ненужный код импактов и другого дерьма.
Как мы можем видеть, новое расстояние рассчитывается на базе разницы пройденого и максимального, а урон исходя из нового пройденного пулей расстояния.
А так же видим проверку на достижения дистанции пробития пулей, после этого пробитий не должно быть. И если наш модификатор мощности пробития очень низкий, просто останавливаем пулю. Установка nPenetrationCount в ноль предотвращает попадание пули в объект на максимальном расстоянии.
Видим, что переменные передаются в качестве аргументов функции HandleBulletPenetration.
Данная функция рассчитывает при каком условии пуля больше не может пробивать определенные объекты(материалы), т.е справляется с проникновением пуль в объекты. Возвращает значение true, если пуля больше не может простреливать какой-либо объект.
Вот один из примеров, когда пуля не сможет прострелить объект (при условии, если мы на максимальном расстоянии и материал это решетка или стекло):
Далее, если немного опуститься вглубь функции, можно увидеть расчет модификации мощности пуль, после того, как они пробивают стену, модификатор мощности пробития, урон пули на текущей дистанции, толщину стены, которую может пробить пуля и центр дистанции.
Хорошо, начинку мы изучили, но потеряли одну проверку связанную с функцией
Пожалуйста, авторизуйтесь для просмотра ссылки.
.Что же она такого интересного делает? Данная функция находит точную конечную позицию проникновения пули.
Если кратко, то она работает пока дистанция меньше или равна максимальной, после вычисляет конечную позицию на основе дистанции, далее получает содержимое точки и работает с системой трассировки и его движком, пускает луч к объекту, проверяет разные данные по типу startsolid и surface флаги для проверки попадания через стену в хитбокс игрока.
Но самое интересное во всех этих вычислениях — функция
Пожалуйста, авторизуйтесь для просмотра ссылки.
. Она возвращает true, если интересующий нас объект является ломаемым. Нам нужно сделать ребилд данной функции: сделать бекап take_damage`a, установить значение damage_yes(2) для take_damage`a, вызвать оригинал и ресторить take_damage.Вроде бы все, не забываем о расчете урона для каждой хитгрупы противника. Для расчета урона в CS:GO сделана функция
Пожалуйста, авторизуйтесь для просмотра ссылки.
. Украдем оттуда немного расчетов и переделаем их под себя.Имея на руках всю нужную информацию, можем сделать полноценный, готовый autowall.
C++:
if ( !weapon )
return false;
const auto weapon_data = weapon->get_weapon_info( );
if ( !weapon_data )
return false;
c_game_trace enter_trace;
const uint32_t filter_[ 4 ] = { *patterns[ trace_filter ].add( 0x3D ).as<uint32_t*>( ),
reinterpret_cast< uint32_t >( ctx::local( ) ), 0, 0 };
current_damage = weapon_data->m_damage; // damage of the bullet at it's current trajectory
auto eye_position = pos;
constexpr auto penetration_distance = 3000.0f; // distance at which the bullet is capable of penetrating a wall
auto penetration_power = 35.0f; // thickness of a wall that this bullet can penetrate
hits = 4;
static const auto var = i::convar->find_convar( "sv_penetration_type" );
if ( const int possible_hits = var->get_int( ); possible_hits == 1 ) {
penetration_power = weapon_data->m_penetration;
}
const float max_range = std::min( weapon_data->m_range, ( eye_position - point ).length( false ) );
const auto end = eye_position + direction * max_range;
while ( current_damage >= 1.0f ) {
i::engine_trace->trace_ray( ray_t( eye_position, end ), mask_shot_hull | contents_hitbox,
( c_trace_filter* )filter_, &enter_trace );
if ( e )
clip_trace_to_player( eye_position, end + direction * 40.0f, mask_shot_hull | contents_hitbox, &enter_trace, static_cast< c_csplayer* >( e ) );
const auto enter_surface_data = i::phys_surface_props->get_surface_data( enter_trace.surface.surface_props );
const auto enter_surf_penetration_modifier = enter_surface_data->game.penetration_modifier;
// calculate the damage based on the distance the bullet travelled.
const float distance_traced = ( enter_trace.end - eye_position ).length( false );
current_damage *= std::pow( weapon_data->m_range_modifier, distance_traced / 500.f );
// we didn't hit anything, stop tracing shoot
if ( enter_trace.fraction == 1.0f ) {
break;
}
// check if we reach penetration distance, no more penetrations after that
// or if our modifyer is super low, just stop the bullet
if ( distance_traced > penetration_distance && weapon_data->m_penetration > 0.0f || enter_surf_penetration_modifier < 0.1f ) {
break;
}
//if ( *( v123 + 13196 ) == v83 ) {
// v115 = 4352;
// if ( *( hit_ent + 39649 ) )
// current_damage = 1.0;
//}
const auto can_do_damage = enter_trace.hitgroup != hitgroup_gear && enter_trace.hitgroup != hitgroup_generic;
const auto is_player = enter_trace.entity->is_player( );
const auto is_enemy = static_cast< c_csplayer* >( enter_trace.entity )->team( ) != ctx::local( )->team( );
if ( can_do_damage && is_player && is_enemy ) {
scale_damage( static_cast< c_csplayer* >( enter_trace.entity ), enter_trace.hitgroup, weapon_data, current_damage );
hitbox = enter_trace.hitbox;
return true;
}
if ( !hits )
break;
static const auto damage_reduction_bullets = i::convar->find_convar( "ff_damage_reduction_bullets" );
static const auto damage_bullet_penetration = i::convar->find_convar( "ff_damage_bullet_penetration" );
if ( !handle_bullet_penetration( weapon_data, enter_trace, eye_position, direction, hits, current_damage, penetration_power, damage_reduction_bullets->get_float( ), damage_bullet_penetration->get_float( ) ) )
break;
}
return false;
C++:
if ( !e->is_player( ) )
return;
auto is_armored = [&]( )->bool {
switch ( group ) {
case hitgroup_head:
return e->has_helmet( );
case hitgroup_generic:
case hitgroup_chest:
case hitgroup_stomach:
case hitgroup_leftarm:
case hitgroup_rightarm:
return true;
default:
return false;
}
};
// @xref: https://github.com/perilouswithadollarsign/cstrike15_src/blob/master/game/server/player.cpp#L1194
static auto mp_damage_scale_ct_head = i::convar->find_convar( "mp_damage_scale_ct_head" );
static auto mp_damage_scale_t_head = i::convar->find_convar( "mp_damage_scale_t_head" );
static auto mp_damage_scale_ct_body = i::convar->find_convar( "mp_damage_scale_ct_body" );
static auto mp_damage_scale_t_body = i::convar->find_convar( "mp_damage_scale_t_body" );
float head_damage_scale = e->team( ) == 3 ? mp_damage_scale_ct_head->get_float( ) : e->team( ) == 2 ? mp_damage_scale_t_head->get_float( ) : 1.0f;
const float body_damage_scale = e->team( ) == 3 ? mp_damage_scale_ct_body->get_float( ) : e->team( ) == 2 ? mp_damage_scale_t_body->get_float( ) : 1.0f;
if ( e->has_heavy_armor( ) )
head_damage_scale *= 0.5f;
switch ( group ) {
case hitgroup_head:
{
current_damage *= weapon_data->m_headshot_multiplier * head_damage_scale;
} break;
case hitgroup_chest:
case hitgroup_leftarm:
case hitgroup_rightarm:
case 8: // neck
{
current_damage *= body_damage_scale;
} break;
case hitgroup_stomach:
{
current_damage *= 1.25f * body_damage_scale;
} break;
case hitgroup_leftleg:
case hitgroup_rightleg:
{
current_damage *= 0.75f * body_damage_scale;
} break;
default: break;
}
const auto armor_value = static_cast< float >( e->armor( ) );
if ( armor_value <= 0.0f || !is_armored( ) )
return;
// @ida: module: server.dll; sig: 80 BF ? ? ? ? ? F3 0F 10 5C 24 ? F3 0F 10 35
float heavy_armor_bonus = 1.0f,
armor_bonus = 0.5f,
armor_ratio = weapon_data->m_armor_ratio * 0.5f;
if ( e->has_heavy_armor( ) ) {
heavy_armor_bonus = 0.25f;
armor_bonus = 0.33f;
armor_ratio *= 0.20f;
}
float damage_to_health = current_damage * armor_ratio;
const auto damage_to_armor = ( current_damage - damage_to_health ) * ( heavy_armor_bonus * armor_bonus );
// does this use more armor than we have?
if ( damage_to_armor > armor_value )
damage_to_health = current_damage - armor_value / armor_bonus;
current_damage = damage_to_health;
C++:
// @ida: https://imgur.com/x3Qe12r
// module: server.dll; sig: F3 0F 5C CE F3 0F 11 5D ?
auto end = vector3d( );
auto v22 = vector3d( );
auto current_distance = 0.0f;
auto first_contents = 0;
while ( current_distance <= 90.0f ) {
current_distance += 4.0f;
end = start_position + direction * current_distance;
if ( !first_contents )
first_contents = i::engine_trace->get_point_contents( start, mask_shot_hull | contents_hitbox, nullptr );
const auto point_contents = i::engine_trace->get_point_contents( start, mask_shot_hull | contents_hitbox, nullptr );
if ( point_contents & mask_shot_hull && ( !( point_contents & contents_hitbox ) || point_contents == first_contents ) )
continue;
v22 = end - direction * 4.0f;
i::engine_trace->trace_ray( ray_t( end, v22 ), mask_shot_hull | contents_hitbox, nullptr, &exit_trace );
if ( static const auto var = i::convar->find_convar( "sv_clip_penetration_traces_to_players" );
var->get_int( ) ) {
clip_trace_to_player( end, v22, 0x4600400B, &exit_trace, ctx::local( ) );
}
if ( exit_trace.start_solid && exit_trace.surface.flags & surf_hitbox ) {
const uint32_t filter[ 4 ] = { *patterns[ trace_filter ].add( 0x3D ).as<uint32_t*>( ),
reinterpret_cast< uint32_t >( exit_trace.entity ), 0, 0 };
i::engine_trace->trace_ray( ray_t( end, start_position ), mask_shot_hull | contents_hitbox,
( c_trace_filter* )filter, &exit_trace );
if ( exit_trace.hit( ) && !exit_trace.start_solid ) {
return true;
}
continue;
}
// client.dll; 0F 85 D3 00 00 00 8A 5B 42
if ( !exit_trace.hit( ) && exit_trace.start_solid ) {
const auto ent = static_cast< c_csplayer* >( enter_trace.entity );
if ( ent && ent != ctx::local( ) && ent->is_enemy( ) ) {
if ( is_breakable_entity( ent ) ) {
exit_trace = enter_trace;
exit_trace.end = start_position + direction;
return true;
}
}
continue;
}
// server.dll;
if ( exit_trace.hit( ) && !exit_trace.start_solid ) {
if ( is_breakable_entity( static_cast< c_csplayer* >( enter_trace.entity ) ) && is_breakable_entity( static_cast< c_csplayer* >( exit_trace.entity ) ) ) {
end = exit_trace.end;
return true;
}
if ( enter_trace.surface.flags & surf_nodraw || !( exit_trace.surface.flags & surf_nodraw ) && exit_trace.plane.normal.dot( direction ) <= 1.0f ) {
end -= direction * exit_trace.fraction * 4.0f;
return true;
}
}
}
//if ( !enter_trace.did_hit_non_world_entity() || !is_breakable_entity( enter_trace.entity ) ) {
// direction = a6;
// direction = a7;
// direction = a8;
// return true;
//}
return false;
C++:
c_game_trace exit_trace;
const auto enter_surface_data = i::phys_surface_props->get_surface_data( enter_trace.surface.surface_props );
const auto enter_material = enter_surface_data->game.material;
const bool is_solid_surf = enter_trace.contents >> 3 & contents_solid;
const bool is_light_surf = enter_trace.surface.flags >> 7 & surf_light;
if ( !possible_hits_remaining && enter_material != 89 && enter_material != 71 )
return false;
if ( !weapon_data->m_penetration
|| !trace_to_exit( enter_trace, exit_trace, enter_trace.end, direction )
&& !( i::engine_trace->get_point_contents( enter_trace.end, 0x600400B, nullptr ) & 0x600400B ) )
return false;
static const auto var = i::convar->find_convar( "sv_penetration_type" );
const int possible_hits = var->get_int( );
const auto exit_surface_data = i::phys_surface_props->get_surface_data( exit_trace.surface.surface_props );
const auto exit_material = exit_surface_data->game.material;
float combined_penetration_modifier{};
float final_damage_modifier;
if ( possible_hits != 1 ) {
if ( is_solid_surf || is_light_surf ) {
combined_penetration_modifier = 1.0f;
final_damage_modifier = 0.99f;
}
else {
final_damage_modifier = enter_surface_data->game.damage_modifier;
combined_penetration_modifier = fminf( enter_surface_data->game.penetration_modifier,
exit_surface_data->game.penetration_modifier );
if ( enter_surface_data->game.damage_modifier > enter_surface_data->game.jump_factor )
final_damage_modifier = enter_surface_data->game.jump_factor;
}
if ( enter_material == exit_material && ( exit_material == 87 || exit_material == 77 ) )
combined_penetration_modifier *= 2.0f;
if ( ( exit_trace.end - enter_trace.end ).length_sqr( ) > combined_penetration_modifier * penetration_power )
return false;
current_damage *= final_damage_modifier;
eye_position = exit_trace.end;
--possible_hits_remaining;
return true;
}
final_damage_modifier = 0.16f;
if ( !is_solid_surf && !is_light_surf ) {
if ( enter_material == 89 ) {
final_damage_modifier = 0.05f;
combined_penetration_modifier = 3.0f;
}
if ( enter_material != 71 ) {
// CLIENT.DLL
const auto hit_ent = static_cast< c_csplayer* >( enter_trace.entity );
if ( hit_ent ) {
const auto func = memory::vfunc<bool( __thiscall* )( c_csplayer* )>( hit_ent, 158 )( hit_ent );
if ( func && hit_ent->unk_awol_thing( ) ) // 0x9AE1 {
combined_penetration_modifier = exit_surface_data->game.penetration_modifier;
final_damage_modifier = 0.16f;
}
}
if ( enter_material == 70 && ( ff_damage_reduction_bullets == 0.0f && hit_ent ) ) {
const auto func = memory::vfunc<bool( __thiscall* )( c_csplayer* )>( hit_ent, 158 )( hit_ent );
if ( func && !hit_ent->is_enemy( ) ) {
// If friendly fire is off, this will scale the penetration power and damage a bullet does when penetrating another friendly player
if ( ff_damage_bullet_penetration == 0.0f )
return false;
combined_penetration_modifier = ff_damage_bullet_penetration;
final_damage_modifier = 0.16f;
}
}
combined_penetration_modifier = ( enter_surface_data->game.penetration_modifier + exit_surface_data->game.penetration_modifier ) / 2.0f;
final_damage_modifier = 0.16f;
// SERVER.DLL
/*if ( !hit_ent || !hit_ent->unk_awol_thing( ) ) {
if ( enter_material != 70
|| ff_damage_reduction_bullets
|| hit_ent->team( ) == ctx::local( )->team( ) ) {
combined_penetration_modifier = ( enter_surface_data->game.penetration_modifier + exit_surface_data->game.penetration_modifier ) / 2.0f;
final_damage_modifier = 0.16f;
}
}
// i think we must remove this check
if ( ff_damage_bullet_penetration == 0.0f )
return false;
combined_penetration_modifier = ff_damage_bullet_penetration;
final_damage_modifier = 0.16f;*/
}
}
if ( enter_material != 89 && enter_material != 71 ) {
combined_penetration_modifier = 1.0f;
if ( enter_material == exit_material ) {
if ( exit_material == 87 || exit_material == 85 ) {
combined_penetration_modifier = 3.0f;
}
else if ( exit_material == 76 ) {
combined_penetration_modifier = 2.0f;
}
}
}
const auto thickness = ( exit_trace.end - enter_trace.end ).length_sqr( );
const auto modifier = fmax( 1.0f / combined_penetration_modifier, 0.0f );
const auto lost_damage = fmax(
modifier * thickness / 24.0f + current_damage * final_damage_modifier + fmax(
3.75f / penetration_power, 0.0f ) * 3.0f * modifier, 0.0f );
if ( lost_damage > current_damage )
return false;
if ( lost_damage > 0.0f )
current_damage -= lost_damage;
if ( current_damage < 1.0f )
return false;
eye_position = exit_trace.end;
--possible_hits_remaining;
return true;