summaryrefslogtreecommitdiffstats
path: root/src/esp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/esp.cpp')
-rw-r--r--src/esp.cpp258
1 files changed, 258 insertions, 0 deletions
diff --git a/src/esp.cpp b/src/esp.cpp
index 1e00a92..47df57d 100644
--- a/src/esp.cpp
+++ b/src/esp.cpp
@@ -784,6 +784,264 @@ bool silent_aim_apply(CUserCmd *cmd) {
784 return true; 784 return true;
785} 785}
786 786
787// ---------------------------------------------------------------------------
788// melee killbot
789// ---------------------------------------------------------------------------
790//
791// The crowbar's CBaseHLBludgeonWeapon::Swing traces 75 units from
792// Weapon_ShootPosition along the view forward (CROWBAR_RANGE in
793// weapon_crowbar.h). It first tries a line, then a +/-16 hull backed off by
794// ~28 units, and ignores targets more than ~45 degrees off the aim direction.
795// We therefore only need to point the usercmd at the target for one command;
796// the rendered view is restored exactly like silent aim.
797
798static constexpr float MELEE_RANGE = 75.0f;
799
800static float vec_dist(const Vector &a, const Vector &b) {
801 float dx = a.x - b.x, dy = a.y - b.y, dz = a.z - b.z;
802 return sqrtf(dx * dx + dy * dy + dz * dz);
803}
804
805bool melee_weapon_active(void *local_ent) {
806 if (!local_ent)
807 return false;
808 int local = engine_get_local_player();
809
810 auto check = [&](int idx, const char **cls_out,
811 const char **model_out) -> bool {
812 if (idx <= 0 || idx > 4096)
813 return false;
814 void *wep = entitylist_get(idx);
815 void *net = entity_networkable(idx);
816 const char *cls = net ? entity_class_name(net, nullptr) : nullptr;
817 const char *model = wep ? entity_model_name(wep) : nullptr;
818 *cls_out = cls;
819 *model_out = model;
820 return (cls && strstr(cls, "Crowbar")) ||
821 (model && strstr(model, "crowbar"));
822 };
823
824 int idx = -1;
825 if (g_net.activeweapon >= 0) {
826 uint32_t h = *(uint32_t *)((uintptr_t)local_ent + g_net.activeweapon);
827 if (h != 0xFFFFFFFFu)
828 idx = (int)(h & 0xFFFF);
829 }
830 const char *cls = nullptr, *model = nullptr;
831 bool crowbar = check(idx, &cls, &model);
832
833 if (!crowbar) {
834 // Fallback: the authoritative server player's m_hActiveWeapon handle.
835 // Also covers a client netvar offset that resolves but does not point at
836 // the weapon the server is actually using.
837 if (local > 0) {
838 resolve_server_netvars();
839 int si = server_active_weapon(local);
840 if (si > 0 && si != idx) {
841 idx = si;
842 crowbar = check(idx, &cls, &model);
843 }
844 }
845 }
846
847 static int last_idx = -2;
848 static bool last_crowbar = false;
849 if (idx != last_idx || crowbar != last_crowbar) {
850 last_idx = idx;
851 last_crowbar = crowbar;
852 logf("melee: active idx=%d cls=%s model=%s crowbar=%d", idx,
853 cls ? cls : "?", model ? model : "?", (int)crowbar);
854 }
855 return crowbar;
856}
857
858bool melee_aimbot_apply(CUserCmd *cmd) {
859 if (!g_iface.entitylist || g_net.origin < 0)
860 return false;
861 int local = engine_get_local_player();
862 if (local <= 0)
863 return false;
864 void *local_ent = entitylist_get(local);
865 if (!local_ent)
866 return false;
867 if (g_net.health >= 0 &&
868 *(int *)((uintptr_t)local_ent + g_net.health) <= 0)
869 return false;
870
871 using Fn = int (*)(void *);
872 int hi = vfunc<Fn>(g_iface.entitylist, 6)(g_iface.entitylist);
873 if (hi < 0 || hi > 4096)
874 hi = 4096;
875
876 struct Cand {
877 int idx;
878 float dist;
879 Vector closest;
880 Vector center;
881 };
882 Cand cands[16];
883 int n = 0;
884
885 for (int idx = 1; idx <= hi; idx++) {
886 if (idx == local)
887 continue;
888 void *ent = entitylist_get(idx);
889 if (!ent)
890 continue;
891 void *net = entity_networkable(idx);
892 if (!net)
893 continue;
894 const char *cls = entity_class_name(net, nullptr);
895 if (!cls || !is_npc_class(cls))
896 continue;
897 uintptr_t e = (uintptr_t)ent;
898 int life = g_net.lifestate >= 0
899 ? (int)*(unsigned char *)(e + g_net.lifestate)
900 : -1;
901 if (life != LIFE_ALIVE)
902 continue;
903 const char *model = entity_model_name(ent);
904 if (model_is_friendly(model) || model_is_neutral(model))
905 continue;
906
907 Vector origin{*(float *)(e + g_net.origin),
908 *(float *)(e + g_net.origin + 4),
909 *(float *)(e + g_net.origin + 8)};
910 float minx = -16.0f, miny = -16.0f, minz = 0.0f;
911 float maxx = 16.0f, maxy = 16.0f, maxz = 72.0f;
912 if (g_net.mins >= 0) {
913 minx = *(float *)(e + g_net.mins);
914 miny = *(float *)(e + g_net.mins + 4);
915 minz = *(float *)(e + g_net.mins + 8);
916 }
917 if (g_net.maxs >= 0) {
918 maxx = *(float *)(e + g_net.maxs);
919 maxy = *(float *)(e + g_net.maxs + 4);
920 maxz = *(float *)(e + g_net.maxs + 8);
921 }
922 if (!(maxz > minz + 4.0f && maxz < 512.0f)) {
923 minz = 0.0f;
924 maxz = 72.0f;
925 }
926 if (!(maxx > minx + 1.0f && maxx < 256.0f)) {
927 minx = -16.0f;
928 maxx = 16.0f;
929 }
930 if (!(maxy > miny + 1.0f && maxy < 256.0f)) {
931 miny = -16.0f;
932 maxy = 16.0f;
933 }
934
935 Vector lo{origin.x + minx, origin.y + miny, origin.z + minz};
936 Vector hiV{origin.x + maxx, origin.y + maxy, origin.z + maxz};
937 Vector closest{g_cam_eye.x, g_cam_eye.y, g_cam_eye.z};
938 if (closest.x < lo.x) closest.x = lo.x;
939 if (closest.x > hiV.x) closest.x = hiV.x;
940 if (closest.y < lo.y) closest.y = lo.y;
941 if (closest.y > hiV.y) closest.y = hiV.y;
942 if (closest.z < lo.z) closest.z = lo.z;
943 if (closest.z > hiV.z) closest.z = hiV.z;
944 float d = vec_dist(g_cam_eye, closest);
945 if (d > MELEE_RANGE)
946 continue;
947
948 Vector center{(lo.x + hiV.x) * 0.5f, (lo.y + hiV.y) * 0.5f,
949 (lo.z + hiV.z) * 0.5f};
950
951 int pos = n;
952 while (pos > 0 && cands[pos - 1].dist > d)
953 pos--;
954 if (n < 16)
955 n++;
956 else if (pos >= 16)
957 continue;
958 for (int j = n - 1; j > pos; j--)
959 cands[j] = cands[j - 1];
960 cands[pos] = {idx, d, closest, center};
961 }
962
963 for (int ci = 0; ci < n; ci++) {
964 const Cand &c = cands[ci];
965 Vector aim{};
966 bool have_aim = false;
967
968 // Prefer the nearest visible hitbox center. This handles small NPCs
969 // (headcrabs, antlions) whose AABB center can be less useful than the
970 // actual body hitboxes.
971 void *tent = entitylist_get(c.idx);
972 if (tent && bones_update(c.idx, tent)) {
973 Vector pts[32];
974 int np = aim_points(c.idx, 0xFE, pts, 32);
975 float bestd = 1e9f;
976 for (int i = 0; i < np; i++) {
977 float pd = vec_dist(g_cam_eye, pts[i]);
978 if (pd > MELEE_RANGE || pd >= bestd)
979 continue;
980 if (!is_visible(g_cam_eye, pts[i]))
981 continue;
982 bestd = pd;
983 aim = pts[i];
984 have_aim = true;
985 }
986 }
987
988 if (!have_aim) {
989 aim = c.center;
990 float ad = vec_dist(g_cam_eye, aim);
991 if (ad > MELEE_RANGE) {
992 // The body is in range but its center is not: aim just inside the
993 // closest point on the AABB.
994 aim = c.closest;
995 aim.x += (c.center.x - c.closest.x) * 0.25f;
996 aim.y += (c.center.y - c.closest.y) * 0.25f;
997 aim.z += (c.center.z - c.closest.z) * 0.25f;
998 ad = vec_dist(g_cam_eye, aim);
999 if (ad > MELEE_RANGE)
1000 aim = c.closest;
1001 }
1002 if (!is_visible(g_cam_eye, aim))
1003 continue;
1004 }
1005
1006 QAngle orig{g_cam_view.x, g_cam_view.y, g_cam_view.z};
1007 QAngle desired = angle_to(g_cam_eye, aim);
1008 float np = desired.x;
1009 if (np > 89.0f) np = 89.0f;
1010 if (np < -89.0f) np = -89.0f;
1011 float ny = desired.y;
1012 while (ny > 180.0f) ny -= 360.0f;
1013 while (ny < -180.0f) ny += 360.0f;
1014
1015 preserve_movement(cmd, orig.y, ny);
1016 cmd->viewangles[0] = np;
1017 cmd->viewangles[1] = ny;
1018 cmd->viewangles[2] = orig.z;
1019 set_silent_restore(orig);
1020 if (g_iface.engine) {
1021 QAngle restore{orig.x, orig.y, orig.z};
1022 using SetViewFn = void (*)(void *, QAngle *);
1023 vfunc<SetViewFn>(g_iface.engine, 20)(g_iface.engine, &restore);
1024 }
1025 cmd->buttons |= IN_ATTACK;
1026
1027 static int logs = 0;
1028 if (logs < 10) {
1029 logs++;
1030 logf("melee: swing idx=%d dist=%.1f aim=(%.1f %.1f %.1f)", c.idx,
1031 c.dist, aim.x, aim.y, aim.z);
1032 }
1033 return true;
1034 }
1035
1036 static double last_none = 0.0;
1037 double now = esp_now();
1038 if (now - last_none >= 2.0) {
1039 last_none = now;
1040 logf("melee: no visible target within %.0f", MELEE_RANGE);
1041 }
1042 return false;
1043}
1044
787bool triggerbot_apply(CUserCmd *cmd) { 1045bool triggerbot_apply(CUserCmd *cmd) {
788 if (!g_iface.enginetrace || !g_iface.entitylist) 1046 if (!g_iface.enginetrace || !g_iface.entitylist)
789 return false; 1047 return false;