diff options
| author | salmonik <salmonik@tuta.io> | 2026-10-27 14:52:00 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-10-27 14:52:24 +0100 |
| commit | 293eda2bdce9d732fad168b5b6838bd89ce59fe8 (patch) | |
| tree | 9f345d4a19d1957ccfd7764012221d62eb2df328 /src | |
| parent | Menu: latch the Insert key and expand the root on open (diff) | |
| download | hl2h-293eda2bdce9d732fad168b5b6838bd89ce59fe8.tar.gz hl2h-293eda2bdce9d732fad168b5b6838bd89ce59fe8.zip | |
Melee killbot: 360-degree crowbar silent aim
- New feature (default on): while the local player's active weapon is the
crowbar, pick the nearest visible hostile NPC within the 75-unit crowbar
range, sorted by distance to its collision AABB, and attack it through the
normal silent-aim path (usercmd view + IN_ATTACK, movement preserved,
rendered view restored at FRAME_RENDER_START).
- Prefer the nearest visible hitbox center; fall back to the AABB center /
closest point when bones are unavailable. Reuses the existing friendly /
neutral and life-state filters and the world-only vischeck.
- Detect the active weapon from the client m_hActiveWeapon handle, with the
authoritative server m_hActiveWeapon handle as a fallback (client entity
index from the low 16 bits).
- While the crowbar is active and this feature is on, skip the gun aimbot,
triggerbot and silent aim so they do not fight over the usercmd view.
- Menu: Melee killbot -> Active; config [melee] active=...
Diffstat (limited to '')
| -rw-r--r-- | src/esp.cpp | 258 | ||||
| -rw-r--r-- | src/hl2h.cpp | 42 | ||||
| -rw-r--r-- | src/hl2h.h | 7 | ||||
| -rw-r--r-- | src/menu.cpp | 25 | ||||
| -rw-r--r-- | src/netvars.cpp | 7 | ||||
| -rw-r--r-- | src/server.cpp | 22 |
6 files changed, 328 insertions, 33 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 | |||
| 798 | static constexpr float MELEE_RANGE = 75.0f; | ||
| 799 | |||
| 800 | static 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 | |||
| 805 | bool 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 | |||
| 858 | bool 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 | |||
| 787 | bool triggerbot_apply(CUserCmd *cmd) { | 1045 | bool 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; |
diff --git a/src/hl2h.cpp b/src/hl2h.cpp index 4ba30bc..ee2e990 100644 --- a/src/hl2h.cpp +++ b/src/hl2h.cpp | |||
| @@ -500,34 +500,36 @@ static void do_move(int seq, float frametime) { | |||
| 500 | if (jumpbug_apply(cmd, localIdx, ent, flags, vel, frametime)) | 500 | if (jumpbug_apply(cmd, localIdx, ent, flags, vel, frametime)) |
| 501 | note_modified(cmd, flags, vel); | 501 | note_modified(cmd, flags, vel); |
| 502 | 502 | ||
| 503 | if (!feature_active(FEAT_AIMBOT) || !feature_has_bind(FEAT_AIMBOT)) | 503 | bool melee = feature_active(FEAT_MELEE) && melee_weapon_active(ent); |
| 504 | |||
| 505 | if (!feature_active(FEAT_AIMBOT) || !feature_has_bind(FEAT_AIMBOT) || melee) | ||
| 504 | aimbot_reset_lock(); | 506 | aimbot_reset_lock(); |
| 505 | if (!feature_active(FEAT_SILENTAIM) || !feature_has_bind(FEAT_SILENTAIM)) | 507 | if (!feature_active(FEAT_SILENTAIM) || !feature_has_bind(FEAT_SILENTAIM) || |
| 508 | melee) | ||
| 506 | silentaim_reset_lock(); | 509 | silentaim_reset_lock(); |
| 507 | 510 | ||
| 508 | if (feature_active(FEAT_AIMBOT)) { | 511 | if (melee) { |
| 509 | static bool aim_feature_logged = false; | 512 | if (melee_aimbot_apply(cmd)) |
| 510 | if (!aim_feature_logged) { | ||
| 511 | aim_feature_logged = true; | ||
| 512 | logf("aimbot: feature active (bind held)"); | ||
| 513 | } | ||
| 514 | if (aimbot_apply(cmd)) { | ||
| 515 | const float *vel = (const float *)(e + g_net.velocity); | ||
| 516 | note_modified(cmd, flags, vel); | 513 | note_modified(cmd, flags, vel); |
| 514 | } else { | ||
| 515 | if (feature_active(FEAT_AIMBOT)) { | ||
| 516 | static bool aim_feature_logged = false; | ||
| 517 | if (!aim_feature_logged) { | ||
| 518 | aim_feature_logged = true; | ||
| 519 | logf("aimbot: feature active (bind held)"); | ||
| 520 | } | ||
| 521 | if (aimbot_apply(cmd)) | ||
| 522 | note_modified(cmd, flags, vel); | ||
| 517 | } | 523 | } |
| 518 | } | ||
| 519 | 524 | ||
| 520 | if (feature_active(FEAT_TRIGGERBOT)) { | 525 | if (feature_active(FEAT_TRIGGERBOT)) { |
| 521 | if (triggerbot_apply(cmd)) { | 526 | if (triggerbot_apply(cmd)) |
| 522 | const float *vel = (const float *)(e + g_net.velocity); | 527 | note_modified(cmd, flags, vel); |
| 523 | note_modified(cmd, flags, vel); | ||
| 524 | } | 528 | } |
| 525 | } | ||
| 526 | 529 | ||
| 527 | if (feature_active(FEAT_SILENTAIM)) { | 530 | if (feature_active(FEAT_SILENTAIM)) { |
| 528 | if (silent_aim_apply(cmd)) { | 531 | if (silent_aim_apply(cmd)) |
| 529 | const float *vel = (const float *)(e + g_net.velocity); | 532 | note_modified(cmd, flags, vel); |
| 530 | note_modified(cmd, flags, vel); | ||
| 531 | } | 533 | } |
| 532 | } | 534 | } |
| 533 | } | 535 | } |
| @@ -82,6 +82,7 @@ struct NetVars { | |||
| 82 | int lifestate = -1; | 82 | int lifestate = -1; |
| 83 | int modelindex = -1; | 83 | int modelindex = -1; |
| 84 | int absvelocity = -1; // client C_BaseEntity::m_vecAbsVelocity (prediction) | 84 | int absvelocity = -1; // client C_BaseEntity::m_vecAbsVelocity (prediction) |
| 85 | int activeweapon = -1; // client m_hActiveWeapon handle | ||
| 85 | // ponytail: mins/maxs may be absent from some HL2 netvar tables; esp.cpp falls back to h=72 | 86 | // ponytail: mins/maxs may be absent from some HL2 netvar tables; esp.cpp falls back to h=72 |
| 86 | bool ok() const { return flags >= 0 && velocity >= 0 && movetype >= 0; } | 87 | bool ok() const { return flags >= 0 && velocity >= 0 && movetype >= 0; } |
| 87 | // ponytail: m_iMaxHealth isn't a netvar in HL2 SP; esp.cpp falls back to 100. | 88 | // ponytail: m_iMaxHealth isn't a netvar in HL2 SP; esp.cpp falls back to 100. |
| @@ -147,6 +148,8 @@ float getfov(const QAngle &my_view, const QAngle &aim_ang); | |||
| 147 | bool aimbot_apply(CUserCmd *cmd); | 148 | bool aimbot_apply(CUserCmd *cmd); |
| 148 | bool triggerbot_apply(CUserCmd *cmd); | 149 | bool triggerbot_apply(CUserCmd *cmd); |
| 149 | bool silent_aim_apply(CUserCmd *cmd); | 150 | bool silent_aim_apply(CUserCmd *cmd); |
| 151 | bool melee_weapon_active(void *local_ent); | ||
| 152 | bool melee_aimbot_apply(CUserCmd *cmd); | ||
| 150 | enum FeatureId { | 153 | enum FeatureId { |
| 151 | FEAT_BHOP = 0, | 154 | FEAT_BHOP = 0, |
| 152 | FEAT_ABH = 1, | 155 | FEAT_ABH = 1, |
| @@ -154,7 +157,8 @@ enum FeatureId { | |||
| 154 | FEAT_AIMBOT = 3, | 157 | FEAT_AIMBOT = 3, |
| 155 | FEAT_TRIGGERBOT = 4, | 158 | FEAT_TRIGGERBOT = 4, |
| 156 | FEAT_SILENTAIM = 5, | 159 | FEAT_SILENTAIM = 5, |
| 157 | FEAT_JUMPBUG = 6 | 160 | FEAT_JUMPBUG = 6, |
| 161 | FEAT_MELEE = 7 | ||
| 158 | }; | 162 | }; |
| 159 | enum EspOption { | 163 | enum EspOption { |
| 160 | ESP_OPT_SHOW_NEUTRAL = 0, | 164 | ESP_OPT_SHOW_NEUTRAL = 0, |
| @@ -179,6 +183,7 @@ void resolve_netvars(); | |||
| 179 | bool resolve_server_netvars(); | 183 | bool resolve_server_netvars(); |
| 180 | int server_entity_health(int idx, int *maxhealth, int *lifestate, void **base_out); | 184 | int server_entity_health(int idx, int *maxhealth, int *lifestate, void **base_out); |
| 181 | bool server_set_velocity_z(int idx, float vz); | 185 | bool server_set_velocity_z(int idx, float vz); |
| 186 | int server_active_weapon(int idx); | ||
| 182 | int server_model_index(void *base); | 187 | int server_model_index(void *base); |
| 183 | void find_modules(); | 188 | void find_modules(); |
| 184 | void init_conmsg(); | 189 | void init_conmsg(); |
diff --git a/src/menu.cpp b/src/menu.cpp index 8df67fe..f590d69 100644 --- a/src/menu.cpp +++ b/src/menu.cpp | |||
| @@ -64,6 +64,7 @@ static bool g_bhop_autostrafe = true; | |||
| 64 | static FeatureState g_abh{true, 0}; | 64 | static FeatureState g_abh{true, 0}; |
| 65 | static FeatureState g_jumpbug{true, 0}; | 65 | static FeatureState g_jumpbug{true, 0}; |
| 66 | static bool g_jumpbug_assist = false; | 66 | static bool g_jumpbug_assist = false; |
| 67 | static FeatureState g_melee{true, 0}; | ||
| 67 | static FeatureState g_esp{true, 0}; | 68 | static FeatureState g_esp{true, 0}; |
| 68 | static FeatureState g_aimbot{true, 604}; // default Mouse5, rebindable in menu | 69 | static FeatureState g_aimbot{true, 604}; // default Mouse5, rebindable in menu |
| 69 | static FeatureState g_triggerbot{true, 603}; // default Mouse4 | 70 | static FeatureState g_triggerbot{true, 603}; // default Mouse4 |
| @@ -101,6 +102,8 @@ static MenuNode g_abh_active{NODE_TOGGLE, "Active", false, 0, {}, &g_abh.active, | |||
| 101 | static MenuNode g_jumpbug_feat{NODE_FEATURE, "Jumpbug", false, 1, {}, nullptr, nullptr, nullptr}; | 102 | static MenuNode g_jumpbug_feat{NODE_FEATURE, "Jumpbug", false, 1, {}, nullptr, nullptr, nullptr}; |
| 102 | static MenuNode g_jumpbug_active{NODE_TOGGLE, "Active", false, 0, {}, &g_jumpbug.active, nullptr, nullptr}; | 103 | static MenuNode g_jumpbug_active{NODE_TOGGLE, "Active", false, 0, {}, &g_jumpbug.active, nullptr, nullptr}; |
| 103 | static MenuNode g_jumpbug_assist_node{NODE_TOGGLE, "Assist timing", false, 0, {}, &g_jumpbug_assist, nullptr, nullptr}; | 104 | static MenuNode g_jumpbug_assist_node{NODE_TOGGLE, "Assist timing", false, 0, {}, &g_jumpbug_assist, nullptr, nullptr}; |
| 105 | static MenuNode g_melee_feat{NODE_FEATURE, "Melee killbot", false, 1, {}, nullptr, nullptr, nullptr}; | ||
| 106 | static MenuNode g_melee_active{NODE_TOGGLE, "Active", false, 0, {}, &g_melee.active, nullptr, nullptr}; | ||
| 104 | static MenuNode g_esp_feat{NODE_FEATURE, "ESP", false, 1, {}, nullptr, nullptr, nullptr}; | 107 | static MenuNode g_esp_feat{NODE_FEATURE, "ESP", false, 1, {}, nullptr, nullptr, nullptr}; |
| 105 | static MenuNode g_esp_active{NODE_TOGGLE, "Active", false, 0, {}, &g_esp.active, nullptr, nullptr}; | 108 | static MenuNode g_esp_active{NODE_TOGGLE, "Active", false, 0, {}, &g_esp.active, nullptr, nullptr}; |
| 106 | static MenuNode g_esp_opt_nodes[ESP_OPT_COUNT] = { | 109 | static MenuNode g_esp_opt_nodes[ESP_OPT_COUNT] = { |
| @@ -156,6 +159,8 @@ static void build_tree() { | |||
| 156 | g_jumpbug_feat.children[0] = &g_jumpbug_active; | 159 | g_jumpbug_feat.children[0] = &g_jumpbug_active; |
| 157 | g_jumpbug_feat.children[1] = &g_jumpbug_assist_node; | 160 | g_jumpbug_feat.children[1] = &g_jumpbug_assist_node; |
| 158 | g_jumpbug_feat.child_count = 2; | 161 | g_jumpbug_feat.child_count = 2; |
| 162 | g_melee_feat.children[0] = &g_melee_active; | ||
| 163 | g_melee_feat.child_count = 1; | ||
| 159 | g_esp_feat.children[0] = &g_esp_active; | 164 | g_esp_feat.children[0] = &g_esp_active; |
| 160 | g_aimbot_feat.children[0] = &g_aimbot_active; | 165 | g_aimbot_feat.children[0] = &g_aimbot_active; |
| 161 | g_aimbot_feat.children[1] = &g_aimbot_bind; | 166 | g_aimbot_feat.children[1] = &g_aimbot_bind; |
| @@ -181,11 +186,12 @@ static void build_tree() { | |||
| 181 | g_root.children[0] = &g_bhop_feat; | 186 | g_root.children[0] = &g_bhop_feat; |
| 182 | g_root.children[1] = &g_abh_feat; | 187 | g_root.children[1] = &g_abh_feat; |
| 183 | g_root.children[2] = &g_jumpbug_feat; | 188 | g_root.children[2] = &g_jumpbug_feat; |
| 184 | g_root.children[3] = &g_esp_feat; | 189 | g_root.children[3] = &g_melee_feat; |
| 185 | g_root.children[4] = &g_aimbot_feat; | 190 | g_root.children[4] = &g_esp_feat; |
| 186 | g_root.children[5] = &g_triggerbot_feat; | 191 | g_root.children[5] = &g_aimbot_feat; |
| 187 | g_root.children[6] = &g_silentaim_feat; | 192 | g_root.children[6] = &g_triggerbot_feat; |
| 188 | g_root.child_count = 7; | 193 | g_root.children[7] = &g_silentaim_feat; |
| 194 | g_root.child_count = 8; | ||
| 189 | } | 195 | } |
| 190 | 196 | ||
| 191 | static int g_depth[48]; // depth of each visible row (for indentation) | 197 | static int g_depth[48]; // depth of each visible row (for indentation) |
| @@ -309,6 +315,7 @@ static void config_save() { | |||
| 309 | fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind); | 315 | fprintf(f, "[abh]\nactive=%d\nbind=%d\n", g_abh.active, g_abh.bind); |
| 310 | fprintf(f, "[jumpbug]\nactive=%d\nassist=%d\n", g_jumpbug.active, | 316 | fprintf(f, "[jumpbug]\nactive=%d\nassist=%d\n", g_jumpbug.active, |
| 311 | (int)g_jumpbug_assist); | 317 | (int)g_jumpbug_assist); |
| 318 | fprintf(f, "[melee]\nactive=%d\n", g_melee.active); | ||
| 312 | fprintf(f, "[aimbot]\nactive=%d\nbind=%d\nsmooth=%d\nvischeck=%d\n" | 319 | fprintf(f, "[aimbot]\nactive=%d\nbind=%d\nsmooth=%d\nvischeck=%d\n" |
| 313 | "fov=%d\nfov_circle=%d\n" | 320 | "fov=%d\nfov_circle=%d\n" |
| 314 | "bone_head=%d\nbone_torso=%d\nbone_larm=%d\nbone_rarm=%d\n" | 321 | "bone_head=%d\nbone_torso=%d\nbone_larm=%d\nbone_rarm=%d\n" |
| @@ -349,6 +356,8 @@ static void config_load() { | |||
| 349 | cur = &g_abh; | 356 | cur = &g_abh; |
| 350 | else if (strstr(line, "[jumpbug]")) | 357 | else if (strstr(line, "[jumpbug]")) |
| 351 | cur = &g_jumpbug; | 358 | cur = &g_jumpbug; |
| 359 | else if (strstr(line, "[melee]")) | ||
| 360 | cur = &g_melee; | ||
| 352 | else if (strstr(line, "[esp]")) | 361 | else if (strstr(line, "[esp]")) |
| 353 | cur = &g_esp; | 362 | cur = &g_esp; |
| 354 | else if (strstr(line, "[aimbot]")) | 363 | else if (strstr(line, "[aimbot]")) |
| @@ -537,10 +546,10 @@ static bool key_pressed(int sc) { | |||
| 537 | // feature gate queried from hl2h.cpp do_move(): feature must be Active AND | 546 | // feature gate queried from hl2h.cpp do_move(): feature must be Active AND |
| 538 | // its bind key held (always-on when unbound). | 547 | // its bind key held (always-on when unbound). |
| 539 | static FeatureState *feature_state(int feature) { | 548 | static FeatureState *feature_state(int feature) { |
| 540 | static FeatureState *feats[7] = {&g_bhop, &g_abh, &g_esp, | 549 | static FeatureState *feats[8] = {&g_bhop, &g_abh, &g_esp, |
| 541 | &g_aimbot, &g_triggerbot, &g_silentaim, | 550 | &g_aimbot, &g_triggerbot, &g_silentaim, |
| 542 | &g_jumpbug}; | 551 | &g_jumpbug, &g_melee}; |
| 543 | if (feature < 0 || feature > 6) | 552 | if (feature < 0 || feature > 7) |
| 544 | return nullptr; | 553 | return nullptr; |
| 545 | return feats[feature]; | 554 | return feats[feature]; |
| 546 | } | 555 | } |
diff --git a/src/netvars.cpp b/src/netvars.cpp index 98ebe31..3b2d3b8 100644 --- a/src/netvars.cpp +++ b/src/netvars.cpp | |||
| @@ -89,6 +89,9 @@ static void collect_netvars(uintptr_t table, uintptr_t base_off, int depth) { | |||
| 89 | g_net.health = (int)(base_off + (uint32_t)poff); | 89 | g_net.health = (int)(base_off + (uint32_t)poff); |
| 90 | } else if (strcmp(pname2, "m_iMaxHealth") == 0 && g_net.maxhealth < 0) { | 90 | } else if (strcmp(pname2, "m_iMaxHealth") == 0 && g_net.maxhealth < 0) { |
| 91 | g_net.maxhealth = (int)(base_off + (uint32_t)poff); | 91 | g_net.maxhealth = (int)(base_off + (uint32_t)poff); |
| 92 | } else if (strcmp(pname2, "m_hActiveWeapon") == 0 && | ||
| 93 | g_net.activeweapon < 0) { | ||
| 94 | g_net.activeweapon = (int)(base_off + (uint32_t)poff); | ||
| 92 | } else if (strcmp(pname2, "m_iTeamNum") == 0 && g_net.teamnum < 0) { | 95 | } else if (strcmp(pname2, "m_iTeamNum") == 0 && g_net.teamnum < 0) { |
| 93 | g_net.teamnum = (int)(base_off + (uint32_t)poff); | 96 | g_net.teamnum = (int)(base_off + (uint32_t)poff); |
| 94 | } else if (strcmp(pname2, "m_vecOrigin") == 0 && g_net.origin < 0) { | 97 | } else if (strcmp(pname2, "m_vecOrigin") == 0 && g_net.origin < 0) { |
| @@ -269,9 +272,9 @@ void resolve_netvars() { | |||
| 269 | logf("m_vecAbsVelocity unresolved; jumpbug assist disabled"); | 272 | logf("m_vecAbsVelocity unresolved; jumpbug assist disabled"); |
| 270 | logf("netvars: flags=%d velocity=%d movetype=%d health=%d maxhealth=%d " | 273 | logf("netvars: flags=%d velocity=%d movetype=%d health=%d maxhealth=%d " |
| 271 | "teamnum=%d origin=%d viewoffset=%d mins=%d maxs=%d lifeste=%d " | 274 | "teamnum=%d origin=%d viewoffset=%d mins=%d maxs=%d lifeste=%d " |
| 272 | "modelindex=%d absvelocity=%d", | 275 | "modelindex=%d absvelocity=%d activeweapon=%d", |
| 273 | g_net.flags, g_net.velocity, g_net.movetype, g_net.health, | 276 | g_net.flags, g_net.velocity, g_net.movetype, g_net.health, |
| 274 | g_net.maxhealth, g_net.teamnum, g_net.origin, g_net.viewoffset, | 277 | g_net.maxhealth, g_net.teamnum, g_net.origin, g_net.viewoffset, |
| 275 | g_net.mins, g_net.maxs, g_net.lifestate, g_net.modelindex, | 278 | g_net.mins, g_net.maxs, g_net.lifestate, g_net.modelindex, |
| 276 | g_net.absvelocity); | 279 | g_net.absvelocity, g_net.activeweapon); |
| 277 | } | 280 | } |
diff --git a/src/server.cpp b/src/server.cpp index 3766906..896ce3e 100644 --- a/src/server.cpp +++ b/src/server.cpp | |||
| @@ -33,6 +33,7 @@ int g_srv_lifestate = -1; | |||
| 33 | int g_srv_modelindex = -1; | 33 | int g_srv_modelindex = -1; |
| 34 | int g_srv_absvelocity = -1; | 34 | int g_srv_absvelocity = -1; |
| 35 | int g_srv_velocity = -1; | 35 | int g_srv_velocity = -1; |
| 36 | int g_srv_activeweapon = -1; | ||
| 36 | static bool g_srv_ready = false; | 37 | static bool g_srv_ready = false; |
| 37 | static bool g_srv_checked = false; | 38 | static bool g_srv_checked = false; |
| 38 | 39 | ||
| @@ -109,12 +110,14 @@ bool resolve_server_netvars() { | |||
| 109 | g_srv_modelindex = datamap_offset("m_nModelIndex", 7); // FIELD_SHORT | 110 | g_srv_modelindex = datamap_offset("m_nModelIndex", 7); // FIELD_SHORT |
| 110 | g_srv_absvelocity = datamap_offset("m_vecAbsVelocity", 3); // FIELD_VECTOR | 111 | g_srv_absvelocity = datamap_offset("m_vecAbsVelocity", 3); // FIELD_VECTOR |
| 111 | g_srv_velocity = datamap_offset("m_vecVelocity", 3); // FIELD_VECTOR | 112 | g_srv_velocity = datamap_offset("m_vecVelocity", 3); // FIELD_VECTOR |
| 113 | g_srv_activeweapon = datamap_offset("m_hActiveWeapon", 13); // FIELD_EHANDLE | ||
| 112 | g_srv_ready = g_srv_health > 0; | 114 | g_srv_ready = g_srv_health > 0; |
| 113 | if (g_srv_ready) { | 115 | if (g_srv_ready) { |
| 114 | logf("server datamap (%s): health=%d maxhealth=%d lifeste=%d " | 116 | logf("server datamap (%s): health=%d maxhealth=%d lifeste=%d " |
| 115 | "modelindex=%d absvel=%d vel=%d", | 117 | "modelindex=%d absvel=%d vel=%d activeweapon=%d", |
| 116 | g_server.path.c_str(), g_srv_health, g_srv_maxhealth, g_srv_lifestate, | 118 | g_server.path.c_str(), g_srv_health, g_srv_maxhealth, g_srv_lifestate, |
| 117 | g_srv_modelindex, g_srv_absvelocity, g_srv_velocity); | 119 | g_srv_modelindex, g_srv_absvelocity, g_srv_velocity, |
| 120 | g_srv_activeweapon); | ||
| 118 | fail_logged = false; | 121 | fail_logged = false; |
| 119 | } else if (!fail_logged) { | 122 | } else if (!fail_logged) { |
| 120 | fail_logged = true; | 123 | fail_logged = true; |
| @@ -194,3 +197,18 @@ bool server_set_velocity_z(int idx, float vz) { | |||
| 194 | *(float *)((uintptr_t)base + g_srv_absvelocity + 8) = vz; | 197 | *(float *)((uintptr_t)base + g_srv_absvelocity + 8) = vz; |
| 195 | return true; | 198 | return true; |
| 196 | } | 199 | } |
| 200 | |||
| 201 | // Entity index of the player's active weapon from the authoritative server | ||
| 202 | // entity. Used as a fallback if the client m_hActiveWeapon netvar is missing. | ||
| 203 | int server_active_weapon(int idx) { | ||
| 204 | if (!g_srv_ready || !g_iface.engineserver || g_srv_activeweapon <= 0) | ||
| 205 | return -1; | ||
| 206 | void *base = nullptr; | ||
| 207 | if (server_entity_health(idx, nullptr, nullptr, &base) < 0 || !base) | ||
| 208 | return -1; | ||
| 209 | uint32_t h = *(uint32_t *)((uintptr_t)base + g_srv_activeweapon); | ||
| 210 | if (h == 0xFFFFFFFFu) | ||
| 211 | return -1; | ||
| 212 | int wi = (int)(h & 0xFFFF); | ||
| 213 | return (wi > 0 && wi <= 4096) ? wi : -1; | ||
| 214 | } | ||
