From 714262bcf759a238daf906de16e7b204712a104f Mon Sep 17 00:00:00 2001 From: salmonik Date: Wed, 4 Nov 2026 14:28:21 +0100 Subject: Add usable item filter, projectile ESP and gravity-gun melee - Items: "Only if usable" hides health tags at full health and armor at full suit. Health ceiling is the server player's m_iMaxHealth; armor uses the server m_ArmorValue against HL2's 100 cap. Fails open when unreadable. - Projectiles: new Visuals feature/card for thrown grenades, RPG rockets, helicopter bombs and AR2 balls. Fuse from server m_flDetonateTime vs engine time; radius from m_DmgRadius with per-type fallback; optional 24-segment radius ring and FRAG timer label. - Melee: the killbot now handles the gravity gun as well; only VULNERABLE targets, 1200-unit range, primary attack to launch the held object, and no aim-punch compensation because the launch uses EyeVectors. - Server/netvars: m_ArmorValue, m_flDetonateTime, m_DmgRadius. --- src/esp.cpp | 283 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/gui.cpp | 15 ++- src/hl2h.cpp | 11 ++- src/hl2h.h | 15 ++- src/menu.cpp | 39 +++++++- src/netvars.cpp | 10 +- src/server.cpp | 49 +++++++++- 7 files changed, 394 insertions(+), 28 deletions(-) (limited to 'src') diff --git a/src/esp.cpp b/src/esp.cpp index 7baf2c8..c956d1b 100644 --- a/src/esp.cpp +++ b/src/esp.cpp @@ -898,7 +898,8 @@ static bool active_weapon_contains(void *local_ent, const char *needle) { void *net = entity_networkable(idx); const char *cls = net ? entity_class_name(net, nullptr) : nullptr; const char *model = wep ? entity_model_name(wep) : nullptr; - return (cls && strstr(cls, needle)) || (model && strstr(model, needle)); + return (cls && strcasestr(cls, needle)) || + (model && strcasestr(model, needle)); } // Crossbow bolts and RPG rockets are not hitscan. Lead moving targets by the @@ -1175,7 +1176,35 @@ bool melee_weapon_active(void *local_ent) { return crowbar; } -bool melee_aimbot_apply(CUserCmd *cmd) { +bool gravity_gun_active(void *local_ent) { + if (!local_ent) + return false; + if (active_weapon_contains(local_ent, "physcannon")) + return true; + // Same server fallback as the crowbar: map transitions can leave the + // client activeweapon handle stale. + int local = engine_get_local_player(); + if (local <= 0 || !g_iface.engineserver) + return false; + resolve_server_netvars(); + int si = server_active_weapon(local); + if (si <= 0) + return false; + void *wep = entitylist_get(si); + void *net = entity_networkable(si); + const char *cls = net ? entity_class_name(net, nullptr) : nullptr; + const char *model = wep ? entity_model_name(wep) : nullptr; + if (!model) { + void *sbase = nullptr; + server_entity_health(si, nullptr, nullptr, &sbase); + if (sbase) + model = model_name_from_index(server_model_index(sbase)); + } + return (cls && strcasestr(cls, "physcannon")) || + (model && strcasestr(model, "physcannon")); +} + +bool melee_aimbot_apply(CUserCmd *cmd, bool gravity) { if (!g_iface.entitylist || g_net.origin < 0) return false; int local = engine_get_local_player(); @@ -1188,6 +1217,9 @@ bool melee_aimbot_apply(CUserCmd *cmd) { *(int *)((uintptr_t)local_ent + g_net.health) <= 0) return false; + // The punted object can travel much farther than the crowbar. + float max_range = gravity ? 1200.0f : MELEE_RANGE; + using Fn = int (*)(void *); int hi = vfunc(g_iface.entitylist, 6)(g_iface.entitylist); if (hi < 0 || hi > 4096) @@ -1226,6 +1258,9 @@ bool melee_aimbot_apply(CUserCmd *cmd) { continue; if (!npc_can_be_damaged(idx, model)) continue; + // The gravity gun is only the tool for physics-vulnerable units. + if (gravity && npc_damage_class(idx, model) != NPC_DMG_VULNERABLE) + continue; Vector origin{*(float *)(e + g_net.origin), *(float *)(e + g_net.origin + 4), @@ -1265,7 +1300,7 @@ bool melee_aimbot_apply(CUserCmd *cmd) { if (closest.z < lo.z) closest.z = lo.z; if (closest.z > hiV.z) closest.z = hiV.z; float d = vec_dist(g_cam_eye, closest); - if (d > MELEE_RANGE) + if (d > max_range) continue; Vector center{(lo.x + hiV.x) * 0.5f, (lo.y + hiV.y) * 0.5f, @@ -1298,7 +1333,7 @@ bool melee_aimbot_apply(CUserCmd *cmd) { float bestd = 1e9f; for (int i = 0; i < np; i++) { float pd = vec_dist(g_cam_eye, pts[i]); - if (pd > MELEE_RANGE || pd >= bestd) + if (pd > max_range || pd >= bestd) continue; if (!is_visible(g_cam_eye, pts[i], local_ent, tent)) continue; @@ -1311,7 +1346,7 @@ bool melee_aimbot_apply(CUserCmd *cmd) { if (!have_aim) { aim = c.center; float ad = vec_dist(g_cam_eye, aim); - if (ad > MELEE_RANGE) { + if (ad > max_range) { // The body is in range but its center is not: aim just inside the // closest point on the AABB. aim = c.closest; @@ -1319,7 +1354,7 @@ bool melee_aimbot_apply(CUserCmd *cmd) { aim.y += (c.center.y - c.closest.y) * 0.25f; aim.z += (c.center.z - c.closest.z) * 0.25f; ad = vec_dist(g_cam_eye, aim); - if (ad > MELEE_RANGE) + if (ad > max_range) aim = c.closest; } if (!is_visible(g_cam_eye, aim, local_ent, tent)) @@ -1328,8 +1363,10 @@ bool melee_aimbot_apply(CUserCmd *cmd) { QAngle orig{g_cam_view.x, g_cam_view.y, g_cam_view.z}; QAngle desired = angle_to(g_cam_eye, aim); + // The crowbar uses GetAutoaimVector (adds punch); the gravity gun's + // launch uses EyeVectors (no punch), so only compensate for melee. float punch_x = 0.0f, punch_y = 0.0f; - if (punch_angles(punch_x, punch_y)) { + if (!gravity && punch_angles(punch_x, punch_y)) { desired.x -= punch_x; desired.y -= punch_y; } @@ -1350,13 +1387,15 @@ bool melee_aimbot_apply(CUserCmd *cmd) { using SetViewFn = void (*)(void *, QAngle *); vfunc(g_iface.engine, 20)(g_iface.engine, &restore); } + // Gravity gun primary attack launches the held object (EyeVectors); + // secondary would drop it instead. cmd->buttons |= IN_ATTACK; static int logs = 0; if (logs < 10) { logs++; - logd("melee: swing idx=%d dist=%.1f aim=(%.1f %.1f %.1f)", c.idx, - c.dist, aim.x, aim.y, aim.z); + logd("melee: %s idx=%d dist=%.1f aim=(%.1f %.1f %.1f)", + gravity ? "punt" : "swing", c.idx, c.dist, aim.x, aim.y, aim.z); } return true; } @@ -1365,7 +1404,7 @@ bool melee_aimbot_apply(CUserCmd *cmd) { double now = esp_now(); if (now - last_none >= 2.0) { last_none = now; - logd("melee: no visible target within %.0f", MELEE_RANGE); + logd("melee: no visible target within %.0f", max_range); } return false; } @@ -2331,6 +2370,30 @@ static void items_collect(int local_idx) { } } +// Authoritative local health/armor for the "only if usable" item filter. +// The health ceiling comes from the server player's m_iMaxHealth, not a +// hardcoded 100; when it cannot be read the filter fails open. +static void local_vitals(int &hp, int &maxhp, int &armor) { + hp = maxhp = armor = -1; + int local = engine_get_local_player(); + if (local <= 0) + return; + void *le = entitylist_get(local); + if (le && g_net.health >= 0) + hp = *(int *)((uintptr_t)le + g_net.health); + if (g_iface.engineserver) { + int smax = -1; + int shp = server_entity_health(local, &smax, nullptr, nullptr); + if (shp >= 0) + hp = shp; + if (smax > 0) + maxhp = smax; + armor = server_player_armor(local); + } + if (maxhp < 0 && le && g_net.maxhealth >= 0) + maxhp = *(int *)((uintptr_t)le + g_net.maxhealth); +} + void esp_draw_items() { if (!g_surface || !g_iface.entitylist || g_net.origin < 0) return; @@ -2348,10 +2411,21 @@ void esp_draw_items() { } items_collect(local); + int vit_hp = -1, vit_max = -1, vit_armor = -1; + if (g_item_usable) + local_vitals(vit_hp, vit_max, vit_armor); + for (int i = 0; i < g_nitems; i++) { const ItemTarget &t = g_item_targets[i]; if (!item_kind_enabled(t.kind)) continue; + if (g_item_usable) { + if (t.kind == IK_HEALTH && vit_hp >= 0 && vit_max > 0 && + vit_hp >= vit_max) + continue; + if (t.kind == IK_ARMOR && vit_armor >= 0 && vit_armor >= 100) + continue; + } float sx, sy; if (!world_to_screen(t.origin, sx, sy)) continue; @@ -2402,3 +2476,192 @@ void esp_draw_items() { draw_text_col(tx, ty, text, r, g, b); } } + +// --------------------------------------------------------------------------- +// projectile ESP: grenades, rockets, helicopter bombs, AR2 balls +// --------------------------------------------------------------------------- + +enum ProjKind { PK_FRAG, PK_RPG, PK_BOMB, PK_BALL }; + +struct ProjTarget { + int idx; + int kind; + Vector origin; + float fuse; // seconds; < 0 = impact / no fuse + float radius; +}; + +static ProjTarget g_projs[32]; +static int g_nprojs = 0; + +static float engine_curtime() { + if (!g_iface.engine) + return 0.0f; + using TimeFn = float (*)(void *); + return vfunc(g_iface.engine, 14)(g_iface.engine); +} + +static const char *proj_name(int kind) { + switch (kind) { + case PK_FRAG: return "FRAG"; + case PK_RPG: return "RPG"; + case PK_BOMB: return "BOMB"; + case PK_BALL: return "AR2"; + default: return "PROJ"; + } +} + +static void projs_collect(int local_idx) { + static double last_scan = 0.0; + double now = esp_now(); + if (now - last_scan < 0.10) + return; + last_scan = now; + g_nprojs = 0; + if (!g_iface.entitylist || g_net.origin < 0) + return; + resolve_server_netvars(); + using Fn = int (*)(void *); + int hi = vfunc(g_iface.entitylist, 6)(g_iface.entitylist); + if (hi < 0 || hi > 4096) + hi = 4096; + float curtime = engine_curtime(); + for (int idx = 1; idx <= hi && g_nprojs < 32; idx++) { + if (idx == local_idx) + continue; + void *ent = entitylist_get(idx); + if (!ent) + continue; + void *net = entity_networkable(idx); + if (!net) + continue; + const char *cls = entity_class_name(net, nullptr); + const char *model = entity_model_name(ent); + if (!model) + continue; + bool owned = item_weapon_is_owned(ent); + bool frag_cls = cls && strcasestr(cls, "grenade") && + !strcasestr(cls, "weapon"); + bool missile_cls = cls && strcasestr(cls, "missile") && + !strcasestr(cls, "weapon"); + int kind; + if (strcasestr(model, "w_grenade") && + (frag_cls || (!cls && owned))) { + kind = PK_FRAG; + } else if ((strcasestr(model, "w_missile") || + strcasestr(model, "missile_launch") || + strcasestr(model, "missile_closed")) && + (missile_cls || (!cls && owned))) { + kind = PK_RPG; + } else if (strcasestr(model, "helicopter_bomb")) { + kind = PK_BOMB; + } else if (strcasestr(model, "combineball")) { + kind = PK_BALL; + } else { + continue; + } + uintptr_t e = (uintptr_t)ent; + ProjTarget &p = g_projs[g_nprojs++]; + p.idx = idx; + p.kind = kind; + p.origin = Vector{*(float *)(e + g_net.origin), + *(float *)(e + g_net.origin + 4), + *(float *)(e + g_net.origin + 8)}; + // The client m_DmgRadius is networked; fall back to the server field and + // then to the stock radius for this projectile type. + p.radius = -1.0f; + if (g_net.dmg_radius >= 0) + p.radius = *(float *)(e + g_net.dmg_radius); + if (!(p.radius > 1.0f && p.radius < 2048.0f)) + p.radius = server_grenade_radius(idx); + if (!(p.radius > 1.0f && p.radius < 2048.0f)) + p.radius = kind == PK_RPG ? 200.0f : kind == PK_BALL ? 150.0f : 300.0f; + // m_flDetonateTime is server-only (not in the send table). + p.fuse = -1.0f; + if (kind != PK_RPG) { + float det = server_grenade_detonate(idx); + if (det > 0.0f && curtime > 0.0f) + p.fuse = det - curtime; + if (!(p.fuse > -2.0f && p.fuse < 30.0f)) + p.fuse = -1.0f; + } + static int logs = 0; + if (logs < 8) { + logs++; + logf("projectile: idx=%d cls=%s model=%s kind=%s fuse=%.2f " + "radius=%.0f", + idx, cls ? cls : "?", model, proj_name(kind), p.fuse, p.radius); + } + } +} + +void esp_draw_projectiles() { + if (!g_surface || !g_iface.entitylist || g_net.origin < 0) + return; + if (!g_scr_w) { + using Fn = void (*)(void *, int &, int &); + vfunc(g_surface, SURF_GetScreenSize)(g_surface, g_scr_w, g_scr_h); + if (!g_scr_w) + return; + } + projs_collect(engine_get_local_player()); + for (int i = 0; i < g_nprojs; i++) { + const ProjTarget &p = g_projs[i]; + float sx, sy; + if (!world_to_screen(p.origin, sx, sy)) + continue; + if (sx < -40 || sx > (float)g_scr_w + 40 || sy < -40 || + sy > (float)g_scr_h + 40) + continue; + int r = 255, g = 170, b = 60; + if (p.kind == PK_RPG) { + r = 255; g = 120; b = 40; + } else if (p.kind == PK_BOMB) { + r = 255; g = 60; b = 60; + } else if (p.kind == PK_BALL) { + r = 120; g = 220; b = 255; + } else if (p.fuse >= 0.0f && p.fuse < 0.8f) { + r = 255; g = 60; b = 60; + } + int x = (int)sx, y = (int)sy; + surf_color(r, g, b, 255); + surf_rect(0, x - 4, y - 4, x + 4, y + 4); + surf_rect(1, x - 1, y - 1, x + 2, y + 2); + char text[64]; + if (g_proj_fuse && p.fuse >= 0.0f) + snprintf(text, sizeof(text), "%s %.1fs", proj_name(p.kind), p.fuse); + else + snprintf(text, sizeof(text), "%s", proj_name(p.kind)); + int tx = x + 9; + if (tx > g_scr_w - 80) + tx = x - 9 - (int)strlen(text) * 6; + if (tx < 4) + tx = 4; + int ty = y - 14; + if (ty < 4) + ty = 4; + draw_text_small_col(tx + 1, ty + 1, text, 0, 0, 0); + draw_text_small_col(tx, ty, text, r, g, b); + if (g_proj_radius && p.radius > 1.0f) { + const int SEG = 24; + float lx = 0.0f, ly = 0.0f; + bool have = false; + surf_color(r, g, b, 150); + for (int k = 0; k <= SEG; k++) { + float a = (float)k * 2.0f * PI_F / (float)SEG; + Vector w{p.origin.x + cosf(a) * p.radius, + p.origin.y + sinf(a) * p.radius, p.origin.z}; + float rx, ry; + if (!world_to_screen(w, rx, ry)) { + have = false; + continue; + } + if (have) + surf_line((int)lx, (int)ly, (int)rx, (int)ry); + lx = rx; + ly = ry; + have = true; + } + } + } +} diff --git a/src/gui.cpp b/src/gui.cpp index 59c4364..2f832a2 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -364,7 +364,9 @@ static void build_cards() { add_card(1, &g_triggerbot, "Triggerbot", "Fire when the crosshair is on a hostile.", true); - add_card(1, &g_melee, "Melee killbot", "360-degree crowbar silent aim.", + add_card(1, &g_melee, "Melee killbot", + "360-degree crowbar swing, or a gravity-gun launch at VULN " + "targets while holding an object.", true); int esp = add_card(2, &g_esp, "ESP", "Draw hostile NPC information.", true); @@ -405,11 +407,22 @@ static void build_cards() { &g_item_crates); add_toggle(items, "Show distance", "Append the distance in units.", &g_item_distance); + add_toggle(items, "Only if usable", + "Hide health kits at full health and armor at full suit.", + &g_item_usable); add_slider(items, "Max distance", "0 = unlimited; otherwise items farther away are hidden.", 0, 10000, &g_item_max_dist); g_ctrls[g_nctrl - 1].zero_unlimited = true; + int proj = add_card(2, &g_projectiles, "Projectiles", + "Tag grenades, rockets, bombs and AR2 balls.", true); + add_toggle(proj, "Fuse timer", + "Show the remaining time until a grenade or ball detonates.", + &g_proj_fuse); + add_toggle(proj, "Blast radius", + "Draw the projectile's damage radius on the ground.", &g_proj_radius); + int view = add_card(2, nullptr, "Camera / display", "Camera FOV, viewmodel FOV and HDR brightness.", true); add_slider(view, "Camera FOV", diff --git a/src/hl2h.cpp b/src/hl2h.cpp index ca7ee85..703a582 100644 --- a/src/hl2h.cpp +++ b/src/hl2h.cpp @@ -909,15 +909,18 @@ static void do_move(int seq, float frametime) { note_modified(cmd, flags, vel); bool melee = feature_active(FEAT_MELEE) && melee_weapon_active(ent); + bool gravity = feature_active(FEAT_MELEE) && gravity_gun_active(ent); + bool melee_mode = melee || gravity; - if (!feature_active(FEAT_AIMBOT) || !feature_has_bind(FEAT_AIMBOT) || melee) + if (!feature_active(FEAT_AIMBOT) || !feature_has_bind(FEAT_AIMBOT) || + melee_mode) aimbot_reset_lock(); if (!feature_active(FEAT_SILENTAIM) || !feature_has_bind(FEAT_SILENTAIM) || - melee) + melee_mode) silentaim_reset_lock(); - if (melee) { - if (melee_aimbot_apply(cmd)) + if (melee_mode) { + if (melee_aimbot_apply(cmd, gravity && !melee)) note_modified(cmd, flags, vel); } else { if (feature_active(FEAT_AIMBOT)) { diff --git a/src/hl2h.h b/src/hl2h.h index f1e72be..0c8ffb5 100644 --- a/src/hl2h.h +++ b/src/hl2h.h @@ -99,6 +99,7 @@ struct NetVars { int clr_render = -1; // client m_clrRender (entity render colour) int punch = -1; // client m_vecPunchAngle (aim punch) int default_fov = -1; // client m_iDefaultFOV (camera fov) + int dmg_radius = -1; // client C_BaseGrenade::m_DmgRadius // ponytail: mins/maxs may be absent from some HL2 netvar tables; esp.cpp falls back to h=72 bool ok() const { return flags >= 0 && velocity >= 0 && movetype >= 0; } // ponytail: m_iMaxHealth isn't a netvar in HL2 SP; esp.cpp falls back to 100. @@ -165,7 +166,8 @@ bool aimbot_apply(CUserCmd *cmd); bool triggerbot_apply(CUserCmd *cmd); bool silent_aim_apply(CUserCmd *cmd); bool melee_weapon_active(void *local_ent); -bool melee_aimbot_apply(CUserCmd *cmd); +bool gravity_gun_active(void *local_ent); +bool melee_aimbot_apply(CUserCmd *cmd, bool gravity); bool spread_control_apply(CUserCmd *cmd, void *local_ent); int spread_best_seed(int pellets); extern int g_spread_pellets; @@ -184,7 +186,8 @@ enum FeatureId { FEAT_WORLD_TINT = 11, FEAT_FOG = 12, FEAT_SKYBOX = 13, - FEAT_COUNT = 14 + FEAT_PROJECTILES = 14, + FEAT_COUNT = 15 }; enum EspOption { ESP_OPT_SHOW_NEUTRAL = 0, @@ -221,7 +224,8 @@ struct FeatureState { extern FeatureState g_bhop, g_abh, g_jumpbug, g_melee, g_spread, g_esp, g_aimbot, g_triggerbot, g_silentaim, g_item_esp, g_fullbright, - g_world_tint, g_fog, g_skybox; + g_world_tint, g_fog, g_skybox, g_projectiles; +extern bool g_proj_fuse, g_proj_radius; extern bool g_tint_world_on, g_tint_sky_on, g_tint_prop_on; extern int g_tint_world_rgb[3], g_tint_sky_rgb[3], g_tint_prop_rgb[3]; extern bool g_fog_skybox, g_fog_disable; @@ -236,6 +240,7 @@ extern bool g_item_armor; extern bool g_item_weapons; extern bool g_item_crates; extern bool g_item_distance; +extern bool g_item_usable; extern int g_item_max_dist; extern bool g_aim_bones[AIM_BONE_COUNT]; extern int g_aim_smooth; @@ -289,6 +294,9 @@ int server_npc_relation(int idx, int local_idx); bool server_npc_has_follow_target(int idx); int server_npc_state(int idx); int server_npc_takedamage(int idx); +int server_player_armor(int idx); +float server_grenade_detonate(int idx); +float server_grenade_radius(int idx); int server_entity_count(); void *server_entity_base(int idx); const char *server_entity_classname(int idx); @@ -322,6 +330,7 @@ void draw_text_col(int x, int y, const char *s, int r, int g, int b); void draw_text_small_col(int x, int y, const char *s, int r, int g, int b); void esp_draw(); void esp_draw_items(); +void esp_draw_projectiles(); void prop_tint_update(); void studio_props_init(); bool world_to_screen(const Vector &w, float &sx, float &sy); diff --git a/src/menu.cpp b/src/menu.cpp index 020d2c5..521bf43 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -60,12 +60,16 @@ int g_fog_rgb[3] = {160, 170, 190}; int g_fog_start = 200; int g_fog_end = 3000; FeatureState g_skybox{false, 0}; // skybox.png image override +FeatureState g_projectiles{true, 0}; // grenade / rocket ESP +bool g_proj_fuse = true; +bool g_proj_radius = true; bool g_item_health = true; bool g_item_ammo = true; bool g_item_armor = true; bool g_item_weapons = true; bool g_item_crates = true; bool g_item_distance = true; +bool g_item_usable = true; int g_item_max_dist = 5000; // Aimbot bone checklist. Head defaults on (v1 behaviour). @@ -227,11 +231,16 @@ void config_save() { g_fog_start, g_fog_end, (int)g_fog_skybox, (int)g_fog_disable); fprintf(f, "[skybox]\nactive=%d\nbind=%d\n", g_skybox.active, g_skybox.bind); + fprintf(f, "[projectiles]\nactive=%d\nbind=%d\nfuse=%d\nradius=%d\n", + g_projectiles.active, g_projectiles.bind, (int)g_proj_fuse, + (int)g_proj_radius); fprintf(f, "[items]\nactive=%d\nbind=%d\nhealth=%d\nammo=%d\n" - "armor=%d\nweapons=%d\ncrates=%d\ndistance=%d\nmax_dist=%d\n", + "armor=%d\nweapons=%d\ncrates=%d\ndistance=%d\nmax_dist=%d\n" + "usable=%d\n", g_item_esp.active, g_item_esp.bind, (int)g_item_health, (int)g_item_ammo, (int)g_item_armor, (int)g_item_weapons, - (int)g_item_crates, (int)g_item_distance, g_item_max_dist); + (int)g_item_crates, (int)g_item_distance, g_item_max_dist, + (int)g_item_usable); fprintf(f, "[esp]\nactive=%d\nbind=%d\nneutral=%d\nhealthbar=%d\n" "healthnum=%d\nname=%d\nskeleton=%d\nbox=%d\nspeedgraph=%d\n" "dmg=%d\nalert=%d\nweapon=%d\narrows=%d\n" @@ -274,7 +283,8 @@ static bool config_load() { FeatureState *cur = nullptr; bool in_esp = false, in_bhop = false, in_aim = false, in_jumpbug = false, in_spread = false, in_items = false, - in_tint = false, in_fog = false, in_view = false, in_movement = false; + in_tint = false, in_fog = false, in_view = false, in_movement = false, + in_proj = false; while (fgets(line, sizeof(line), f)) { line[strcspn(line, "\r\n")] = 0; if (line[0] == '[') { @@ -300,6 +310,8 @@ static bool config_load() { cur = &g_fog; else if (strstr(line, "[skybox]")) cur = &g_skybox; + else if (strstr(line, "[projectiles]")) + cur = &g_projectiles; else if (strstr(line, "[aimbot]")) cur = &g_aimbot; else if (strstr(line, "[triggerbot]")) @@ -322,6 +334,7 @@ static bool config_load() { in_fog = (cur == &g_fog); in_view = strstr(line, "[view]") != nullptr; in_movement = strstr(line, "[movement]") != nullptr; + in_proj = (cur == &g_projectiles); } else if (cur || in_view || in_movement) { char *eq = strchr(line, '='); if (!eq) @@ -353,6 +366,8 @@ static bool config_load() { g_item_crates = atoi(val) != 0; else if (strcmp(key, "distance") == 0) g_item_distance = atoi(val) != 0; + else if (strcmp(key, "usable") == 0) + g_item_usable = atoi(val) != 0; else if (strcmp(key, "max_dist") == 0) { int v = atoi(val); if (v < 0) v = 0; // 0 = unlimited @@ -360,6 +375,12 @@ static bool config_load() { g_item_max_dist = v; } } + if (in_proj) { + if (strcmp(key, "fuse") == 0) + g_proj_fuse = atoi(val) != 0; + else if (strcmp(key, "radius") == 0) + g_proj_radius = atoi(val) != 0; + } if (in_aim) { if (strcmp(key, "smooth") == 0) { int v = atoi(val); @@ -581,12 +602,16 @@ void config_reset_defaults() { g_fog_start = 200; g_fog_end = 3000; g_skybox = {false, 0}; + g_projectiles = {true, 0}; + g_proj_fuse = true; + g_proj_radius = true; g_item_health = true; g_item_ammo = true; g_item_armor = true; g_item_weapons = true; g_item_crates = true; g_item_distance = true; + g_item_usable = true; g_item_max_dist = 5000; g_aimbot = {true, 604}; g_triggerbot = {true, 603}; @@ -713,7 +738,7 @@ static FeatureState *feature_state(int feature) { static FeatureState *feats[FEAT_COUNT] = { &g_bhop, &g_abh, &g_esp, &g_aimbot, &g_triggerbot, &g_silentaim, &g_jumpbug, &g_melee, &g_spread, &g_item_esp, - &g_fullbright, &g_world_tint, &g_fog, &g_skybox}; + &g_fullbright, &g_world_tint, &g_fog, &g_skybox, &g_projectiles}; if (feature < 0 || feature >= FEAT_COUNT) return nullptr; return feats[feature]; @@ -940,9 +965,11 @@ void outer_draw() { static bool draw_test = getenv("HL2H_DRAW_TEST") != nullptr; bool esp_on = feature_active(FEAT_ESP); bool items_on = feature_active(FEAT_ITEM_ESP); + bool projs_on = feature_active(FEAT_PROJECTILES); bool toasts = gui_toasts_active(); if (!g_surface || - (!gui_is_open() && !draw_test && !esp_on && !items_on && !toasts)) + (!gui_is_open() && !draw_test && !esp_on && !items_on && !projs_on && + !toasts)) return; // Level/menu -> game transitions can invalidate the VGUI font handle. @@ -1007,6 +1034,8 @@ void outer_draw() { esp_draw(); if (items_on && overlay_font) esp_draw_items(); + if (projs_on && overlay_font) + esp_draw_projectiles(); if (draw_test && overlay_font) { surf_draw_set_color(255, 0, 0, 255); surf_draw_filled_rect(20, 20, 180, 60); diff --git a/src/netvars.cpp b/src/netvars.cpp index 33fd6a3..ef564cd 100644 --- a/src/netvars.cpp +++ b/src/netvars.cpp @@ -107,6 +107,11 @@ static void collect_netvars(uintptr_t table, uintptr_t base_off, int depth) { int off = (int)(base_off + (uint32_t)poff); if (off >= 0x20 && off <= 0x2000) g_net.default_fov = off; + } else if (strcmp(pname2, "m_DmgRadius") == 0 && + g_net.dmg_radius < 0) { + int off = (int)(base_off + (uint32_t)poff); + if (off >= 0x20 && off <= 0x2000) + g_net.dmg_radius = off; } else if (strcmp(pname2, "m_iTeamNum") == 0 && g_net.teamnum < 0) { g_net.teamnum = (int)(base_off + (uint32_t)poff); } else if (strcmp(pname2, "m_vecOrigin") == 0 && g_net.origin < 0) { @@ -288,11 +293,12 @@ void resolve_netvars() { logf("netvars: flags=%d velocity=%d movetype=%d health=%d maxhealth=%d " "teamnum=%d origin=%d viewoffset=%d mins=%d maxs=%d lifeste=%d " "modelindex=%d absvelocity=%d activeweapon=%d owner=%d " - "weapon_owner=%d clr_render=%d punch=%d default_fov=%d", + "weapon_owner=%d clr_render=%d punch=%d default_fov=%d " + "dmg_radius=%d", g_net.flags, g_net.velocity, g_net.movetype, g_net.health, g_net.maxhealth, g_net.teamnum, g_net.origin, g_net.viewoffset, g_net.mins, g_net.maxs, g_net.lifestate, g_net.modelindex, g_net.absvelocity, g_net.activeweapon, g_net.owner, g_net.weapon_owner, g_net.clr_render, g_net.punch, - g_net.default_fov); + g_net.default_fov, g_net.dmg_radius); } diff --git a/src/server.cpp b/src/server.cpp index 39bb162..40aa850 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -31,7 +31,9 @@ enum { FIELD_CHARACTER = 8, FIELD_EMBEDDED = 10, FIELD_CUSTOM = 11, - FIELD_EHANDLE = 13 + FIELD_EHANDLE = 13, + FIELD_TIME = 16, + FIELD_FLOAT = 1 }; // SF_FLOOR_TURRET_CITIZEN (npc_turret_floor.h). Citizen-built turrets @@ -53,6 +55,9 @@ static int g_srv_hacked = -1; static int g_srv_npcstate = -1; static int g_srv_takedamage = -1; static int g_srv_mapname = -1; +static int g_srv_armor = -1; +static int g_srv_detonate = -1; +static int g_srv_grenade_radius = -1; static bool g_srv_ready = false; static bool g_srv_checked = false; @@ -150,17 +155,22 @@ bool resolve_server_netvars() { g_srv_takedamage = datamap_offset("m_takedamage", FIELD_INTEGER); // CChangeLevel::m_szMapName is a char[cchMapNameMost] on trigger_changelevel. g_srv_mapname = datamap_offset("m_szMapName", FIELD_CHARACTER, 0x40, 0x4000); + // CBasePlayer::m_ArmorValue (int). + g_srv_armor = datamap_offset("m_ArmorValue", FIELD_INTEGER); + // CBaseGrenade fuse / blast radius (m_flDetonateTime is not networked). + g_srv_detonate = datamap_offset("m_flDetonateTime", FIELD_TIME); + g_srv_grenade_radius = datamap_offset("m_DmgRadius", FIELD_FLOAT); g_srv_ready = g_srv_health > 0; if (g_srv_ready) { logf("server datamap (%s): health=%d maxhealth=%d lifeste=%d " "modelindex=%d absvel=%d vel=%d activeweapon=%d relationship=%d " "followtarget=%d spawnflags=%d hacked=%d npcstate=%d " - "takedamage=%d mapname=%d", + "takedamage=%d mapname=%d armor=%d detonate=%d dmgradius=%d", g_server.path.c_str(), g_srv_health, g_srv_maxhealth, g_srv_lifestate, g_srv_modelindex, g_srv_absvelocity, g_srv_velocity, g_srv_activeweapon, g_srv_relationship, g_srv_followtarget, g_srv_spawnflags, g_srv_hacked, g_srv_npcstate, g_srv_takedamage, - g_srv_mapname); + g_srv_mapname, g_srv_armor, g_srv_detonate, g_srv_grenade_radius); fail_logged = false; } else if (!fail_logged) { fail_logged = true; @@ -425,6 +435,39 @@ bool server_set_velocity_z(int idx, float vz) { return true; } +// Local player's HEV suit armor from the authoritative server entity. +int server_player_armor(int idx) { + if (!g_srv_ready || g_srv_armor < 0) + return -1; + void *base = nullptr; + server_entity_health(idx, nullptr, nullptr, &base); + if (!base) + return -1; + return *(int *)((uintptr_t)base + g_srv_armor); +} + +// CBaseGrenade fuse time and blast radius from the server entity. Both are +// non-networked (m_flDetonateTime is commented out of the send table). +float server_grenade_detonate(int idx) { + if (!g_srv_ready || g_srv_detonate < 0) + return -1.0f; + void *base = nullptr; + server_entity_health(idx, nullptr, nullptr, &base); + if (!base) + return -1.0f; + return *(float *)((uintptr_t)base + g_srv_detonate); +} + +float server_grenade_radius(int idx) { + if (!g_srv_ready || g_srv_grenade_radius < 0) + return -1.0f; + void *base = nullptr; + server_entity_health(idx, nullptr, nullptr, &base); + if (!base) + return -1.0f; + return *(float *)((uintptr_t)base + g_srv_grenade_radius); +} + // Entity index of the player's active weapon from the authoritative server // entity. Used as a fallback if the client m_hActiveWeapon netvar is missing. int server_active_weapon(int idx) { -- cgit v1.2.3