#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 #include #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(player->GetArmorValue())) { damage_to_health = data.m_dmg - static_cast(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(data_pointer); //L_PRINT(LOG_INFO) << "created trace_data | pointer:" << L::AddFlags(LOG_MODE_INT_SHOWBASE | LOG_MODE_INT_FORMAT_HEX) << reinterpret_cast(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(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(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(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( reinterpret_cast(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( reinterpret_cast(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(iArmor)) flDamageToHealth = *pflDamageToScale - static_cast(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(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(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( reinterpret_cast(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( reinterpret_cast(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 g_auto_wall{}; }