summaryrefslogtreecommitdiffstats
path: root/src/esp.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/esp.cpp158
1 files changed, 153 insertions, 5 deletions
diff --git a/src/esp.cpp b/src/esp.cpp
index d32a8bc..594a8fa 100644
--- a/src/esp.cpp
+++ b/src/esp.cpp
@@ -320,7 +320,8 @@ static int npc_damage_class(int idx, const char *model) {
320 if (td == 0 || td == 1) // DAMAGE_NO / DAMAGE_EVENTS_ONLY 320 if (td == 0 || td == 1) // DAMAGE_NO / DAMAGE_EVENTS_ONLY
321 return NPC_DMG_IMMUNE; 321 return NPC_DMG_IMMUNE;
322 if (model_has(model, "gunship") || model_has(model, "combine_strider") || 322 if (model_has(model, "gunship") || model_has(model, "combine_strider") ||
323 model_has(model, "dropship")) 323 model_has(model, "dropship") || model_has(model, "helicopter") ||
324 model_has(model, "chopper"))
324 return NPC_DMG_ARMORED; 325 return NPC_DMG_ARMORED;
325 if (model_has(model, "headcrab") || model_has(model, "antlion") || 326 if (model_has(model, "headcrab") || model_has(model, "antlion") ||
326 model_has(model, "manhack")) 327 model_has(model, "manhack"))
@@ -822,6 +823,12 @@ static bool select_aim_target(const QAngle &view, unsigned bone_mask,
822 *(int *)((uintptr_t)local_ent + g_net.health) <= 0) 823 *(int *)((uintptr_t)local_ent + g_net.health) <= 0)
823 return false; 824 return false;
824 825
826 // With the RPG equipped, armoured units (striders, gunships, dropships,
827 // hunter-choppers) are the ones ordinary shots cannot hurt, so prefer them
828 // over normal NPCs when acquiring.
829 bool rpg = active_weapon_contains(local_ent, "rocket_launcher") ||
830 active_weapon_contains(local_ent, "rpg");
831
825 auto try_target = [&](int idx, bool enforce_fov, Vector &pt, 832 auto try_target = [&](int idx, bool enforce_fov, Vector &pt,
826 float &fov) -> bool { 833 float &fov) -> bool {
827 if (idx <= 0 || idx == local) 834 if (idx <= 0 || idx == local)
@@ -899,6 +906,8 @@ static bool select_aim_target(const QAngle &view, unsigned bone_mask,
899 int idx; 906 int idx;
900 float fov; 907 float fov;
901 Vector approx; 908 Vector approx;
909 bool armored;
910 float key;
902 }; 911 };
903 Candidate cands[16]; 912 Candidate cands[16];
904 int ncands = 0; 913 int ncands = 0;
@@ -928,6 +937,7 @@ static bool select_aim_target(const QAngle &view, unsigned bone_mask,
928 continue; 937 continue;
929 if (!npc_can_be_damaged(idx, model)) 938 if (!npc_can_be_damaged(idx, model))
930 continue; 939 continue;
940 bool armored = npc_damage_class(idx, model) == NPC_DMG_ARMORED;
931 941
932 float maxz = g_net.maxs >= 0 ? *(float *)(e + g_net.maxs + 8) : 72.0f; 942 float maxz = g_net.maxs >= 0 ? *(float *)(e + g_net.maxs + 8) : 72.0f;
933 if (!(maxz > 4.0f && maxz < 512.0f)) 943 if (!(maxz > 4.0f && maxz < 512.0f))
@@ -940,16 +950,17 @@ static bool select_aim_target(const QAngle &view, unsigned bone_mask,
940 float fov = getfov(view, angle_to(g_cam_eye, approx)); 950 float fov = getfov(view, angle_to(g_cam_eye, approx));
941 if (fov > max_fov) 951 if (fov > max_fov)
942 continue; 952 continue;
953 float key = (rpg && armored) ? fov - 1000.0f : fov;
943 954
944 int pos = ncands; 955 int pos = ncands;
945 while (pos > 0 && cands[pos - 1].fov > fov) 956 while (pos > 0 && cands[pos - 1].key > key)
946 pos--; 957 pos--;
947 if (ncands < 16) 958 if (ncands < 16)
948 ncands++; 959 ncands++;
949 for (int j = ncands - 1; j > pos; j--) 960 for (int j = ncands - 1; j > pos; j--)
950 cands[j] = cands[j - 1]; 961 cands[j] = cands[j - 1];
951 if (pos < 16) 962 if (pos < 16)
952 cands[pos] = {idx, fov, approx}; 963 cands[pos] = {idx, fov, approx, armored, key};
953 } 964 }
954 965
955 if (ncands == 0) { 966 if (ncands == 0) {
@@ -1309,6 +1320,26 @@ bool gravity_gun_active(void *local_ent) {
1309 (model && strcasestr(model, "physcannon")); 1320 (model && strcasestr(model, "physcannon"));
1310} 1321}
1311 1322
1323// The citadel supercharges the gravity gun (d3_citadel_03 onward). It can then
1324// punt any NPC, so the melee aimbot should not restrict itself to
1325// physics-vulnerable units.
1326static bool supercharged_gravity_gun() {
1327 if (!g_iface.engine)
1328 return false;
1329 using Fn = const char *(*)(void *);
1330 const char *m = vfunc<Fn>(g_iface.engine, 51)(g_iface.engine);
1331 if (!m)
1332 return false;
1333 bool supe = strstr(m, "d3_citadel_03") || strstr(m, "d3_citadel_04") ||
1334 strstr(m, "d3_breen_01");
1335 static const char *logged_map = nullptr;
1336 if (logged_map != m) {
1337 logged_map = m;
1338 logf("melee: gravity gun map=%s supercharged=%d", m, (int)supe);
1339 }
1340 return supe;
1341}
1342
1312bool melee_aimbot_apply(CUserCmd *cmd, bool gravity) { 1343bool melee_aimbot_apply(CUserCmd *cmd, bool gravity) {
1313 if (!g_iface.entitylist || g_net.origin < 0) 1344 if (!g_iface.entitylist || g_net.origin < 0)
1314 return false; 1345 return false;
@@ -1324,6 +1355,7 @@ bool melee_aimbot_apply(CUserCmd *cmd, bool gravity) {
1324 1355
1325 // The punted object can travel much farther than the crowbar. 1356 // The punted object can travel much farther than the crowbar.
1326 float max_range = gravity ? 1200.0f : MELEE_RANGE; 1357 float max_range = gravity ? 1200.0f : MELEE_RANGE;
1358 bool supercharged = gravity && supercharged_gravity_gun();
1327 1359
1328 using Fn = int (*)(void *); 1360 using Fn = int (*)(void *);
1329 int hi = vfunc<Fn>(g_iface.entitylist, 6)(g_iface.entitylist); 1361 int hi = vfunc<Fn>(g_iface.entitylist, 6)(g_iface.entitylist);
@@ -1363,8 +1395,10 @@ bool melee_aimbot_apply(CUserCmd *cmd, bool gravity) {
1363 continue; 1395 continue;
1364 if (!npc_can_be_damaged(idx, model)) 1396 if (!npc_can_be_damaged(idx, model))
1365 continue; 1397 continue;
1366 // The gravity gun is only the tool for physics-vulnerable units. 1398 // The normal gravity gun is only the tool for physics-vulnerable units;
1367 if (gravity && npc_damage_class(idx, model) != NPC_DMG_VULNERABLE) 1399 // the citadel version behaves like a normal weapon.
1400 if (gravity && !supercharged &&
1401 npc_damage_class(idx, model) != NPC_DMG_VULNERABLE)
1368 continue; 1402 continue;
1369 1403
1370 Vector origin{*(float *)(e + g_net.origin), 1404 Vector origin{*(float *)(e + g_net.origin),
@@ -2747,6 +2781,11 @@ static void items_collect(int local_idx) {
2747 int kind = classify_item(model, cls, label, sizeof(label)); 2781 int kind = classify_item(model, cls, label, sizeof(label));
2748 if (kind < 0) 2782 if (kind < 0)
2749 continue; 2783 continue;
2784 // Health/suit chargers are dispensers that stay useful after a refill, but
2785 // an empty one cannot give anything. m_iJuice is 0 when depleted (until it
2786 // recharges); only hide the tag once the server value is readable.
2787 if (strcasestr(label, "charger") && server_item_juice(idx) == 0)
2788 continue;
2750 2789
2751 Vector origin{*(float *)((uintptr_t)ent + g_net.origin), 2790 Vector origin{*(float *)((uintptr_t)ent + g_net.origin),
2752 *(float *)((uintptr_t)ent + g_net.origin + 4), 2791 *(float *)((uintptr_t)ent + g_net.origin + 4),
@@ -3045,6 +3084,114 @@ static void projs_collect(int local_idx) {
3045 } 3084 }
3046} 3085}
3047 3086
3087// Frag-grenade path preview: throw velocity from weapon_frag.cpp (eye/right
3088// offset, forward*1200 + 0.1 z), gravity 600, and a simple bounce model.
3089// Shows the bounces and the first landing with the blast-radius ring.
3090static void draw_grenade_trajectory(int local_idx) {
3091 if (!g_proj_traj || !g_iface.entitylist)
3092 return;
3093 void *le = entitylist_get(local_idx);
3094 if (!le)
3095 return;
3096 if (!active_weapon_contains(le, "frag") &&
3097 !active_weapon_contains(le, "w_grenade"))
3098 return;
3099
3100 QAngle va = engine_view_angles();
3101 float pitch = va.x * PI_F / 180.0f;
3102 float yaw = va.y * PI_F / 180.0f;
3103 float cp = cosf(pitch), sp = sinf(pitch);
3104 float cy = cosf(yaw), sy = sinf(yaw);
3105 Vector fwd{cp * cy, cp * sy, -sp};
3106 Vector right{sy, -cy, 0.0f};
3107
3108 Vector src{g_cam_eye.x + fwd.x * 18.0f + right.x * 8.0f,
3109 g_cam_eye.y + fwd.y * 18.0f + right.y * 8.0f,
3110 g_cam_eye.z + fwd.z * 18.0f};
3111 Vector vel{};
3112 if (g_net.velocity >= 0) {
3113 const float *pv = (const float *)((uintptr_t)le + g_net.velocity);
3114 vel = Vector{pv[0], pv[1], pv[2]};
3115 }
3116 vel.x += fwd.x * 1200.0f;
3117 vel.y += fwd.y * 1200.0f;
3118 vel.z += fwd.z * 1200.0f + 120.0f; // vForward[2] += 0.1
3119
3120 const float gravity = 600.0f;
3121 const float dt = 0.02f;
3122 const float restitution = 0.35f;
3123 const float friction = 0.35f;
3124
3125 Vector pts[128];
3126 int n = 0;
3127 Vector pos = src;
3128 pts[n++] = pos;
3129 bool landed = false;
3130 for (int step = 0; step < 240 && n < 127; step++) {
3131 Vector nv = vel;
3132 nv.z -= gravity * dt;
3133 Vector next{pos.x + nv.x * dt, pos.y + nv.y * dt, pos.z + nv.z * dt};
3134 Vector hit_end{}, normal{};
3135 if (trace_solid_segment(pos, next, le, hit_end, normal)) {
3136 pts[n++] = hit_end;
3137 float vn = vel.x * normal.x + vel.y * normal.y + vel.z * normal.z;
3138 Vector vt{vel.x - vn * normal.x, vel.y - vn * normal.y,
3139 vel.z - vn * normal.z};
3140 float keep = 1.0f - friction;
3141 vel.x = vt.x * keep - normal.x * vn * restitution;
3142 vel.y = vt.y * keep - normal.y * vn * restitution;
3143 vel.z = vt.z * keep - normal.z * vn * restitution;
3144 float vlen2 = vel.x * vel.x + vel.y * vel.y + vel.z * vel.z;
3145 pos.x = hit_end.x + normal.x * 1.0f;
3146 pos.y = hit_end.y + normal.y * 1.0f;
3147 pos.z = hit_end.z + normal.z * 1.0f;
3148 if (normal.z > 0.6f && (vn > -80.0f || vlen2 < 2500.0f)) {
3149 landed = true;
3150 break;
3151 }
3152 } else {
3153 pos = next;
3154 vel = nv;
3155 pts[n++] = pos;
3156 }
3157 }
3158
3159 surf_color(255, 210, 90, 200);
3160 for (int i = 0; i + 1 < n; i++) {
3161 int x0, y0, x1, y1;
3162 if (project_segment(pts[i], pts[i + 1], x0, y0, x1, y1))
3163 surf_line(x0, y0, x1, y1);
3164 }
3165
3166 Vector land = landed ? pos : pts[n - 1];
3167 float ssx, ssy;
3168 if (world_to_screen(land, ssx, ssy)) {
3169 int x = (int)ssx, y = (int)ssy;
3170 surf_color(255, 210, 90, 230);
3171 surf_rect(0, x - 3, y - 3, x + 3, y + 3);
3172 surf_line(x - 9, y, x - 4, y);
3173 surf_line(x + 4, y, x + 9, y);
3174 surf_line(x, y - 9, x, y - 4);
3175 surf_line(x, y + 4, x, y + 9);
3176 }
3177 if (landed) {
3178 const int SEG = 24;
3179 const float radius = 300.0f; // CBaseGrenade m_DmgRadius
3180 surf_color(255, 120, 40, 120);
3181 for (int k = 0; k < SEG; k++) {
3182 float a0 = (float)k * 2.0f * PI_F / (float)SEG;
3183 float a1 = (float)(k + 1) * 2.0f * PI_F / (float)SEG;
3184 Vector p0{land.x + cosf(a0) * radius, land.y + sinf(a0) * radius,
3185 land.z + 1.0f};
3186 Vector p1{land.x + cosf(a1) * radius, land.y + sinf(a1) * radius,
3187 land.z + 1.0f};
3188 int x0, y0, x1, y1;
3189 if (project_segment(p0, p1, x0, y0, x1, y1))
3190 surf_line(x0, y0, x1, y1);
3191 }
3192 }
3193}
3194
3048void esp_draw_projectiles() { 3195void esp_draw_projectiles() {
3049 if (!g_surface || !g_iface.entitylist || g_net.origin < 0) 3196 if (!g_surface || !g_iface.entitylist || g_net.origin < 0)
3050 return; 3197 return;
@@ -3055,6 +3202,7 @@ void esp_draw_projectiles() {
3055 return; 3202 return;
3056 } 3203 }
3057 projs_collect(engine_get_local_player()); 3204 projs_collect(engine_get_local_player());
3205 draw_grenade_trajectory(engine_get_local_player());
3058 for (int i = 0; i < g_nprojs; i++) { 3206 for (int i = 0; i < g_nprojs; i++) {
3059 const ProjTarget &p = g_projs[i]; 3207 const ProjTarget &p = g_projs[i];
3060 int r = 255, g = 170, b = 60; 3208 int r = 255, g = 170, b = 60;