From 9904891162a6749d39110110c0812a64c241a7ea Mon Sep 17 00:00:00 2001 From: salmonik Date: Tue, 27 Oct 2026 21:22:47 +0100 Subject: Remove stucklaunch; add valuable-item ESP - Drop the stucklaunch feature, its settings, diagnostics and m_hGroundEntity resolution. - Add a Visuals feature that scans the client entity list for valuable pickups: health/medkits/chargers, ammo boxes/crates/grenades, batteries, HEV suit modules/chargers, supply crates and generic items. - Model-first classification with class fallbacks; category toggles, distance labels and a max-distance slider. - Draw colored marker/label tags through walls, independent of the hostile NPC ESP. - Persist [items] active/bind/health/ammo/armor/crates/distance/max_dist; Reset restores defaults; GUI card and CONTEXT updated. --- src/esp.cpp | 318 +++++++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 253 insertions(+), 65 deletions(-) (limited to 'src/esp.cpp') diff --git a/src/esp.cpp b/src/esp.cpp index ca7bae0..d10a2cf 100644 --- a/src/esp.cpp +++ b/src/esp.cpp @@ -1132,71 +1132,6 @@ static void spread_compute() { g_spread_ready = true; } -static bool icontains(const char *hay, const char *needle) { - if (!hay || !needle || !*needle) - return false; - size_t n = strlen(needle); - for (const char *p = hay; *p; p++) - if (strncasecmp(p, needle, n) == 0) - return true; - return false; -} - -bool entity_is_physics_prop(int idx) { - if (idx <= 0 || idx > 4096) - return false; - void *ent = entitylist_get(idx); - if (!ent) - return false; - int mt = -1; - // The reliable signal: physics objects use MOVETYPE_VPHYSICS. The client - // class is CPhysicsProp / CPhysBox, not the server's prop_physics name. - if (g_net.movetype >= 0) { - mt = *(int *)((uintptr_t)ent + g_net.movetype); - if (mt == MOVETYPE_VPHYSICS) - return true; - } - void *net = entity_networkable(idx); - const char *cls = net ? entity_class_name(net, nullptr) : nullptr; - const char *model = entity_model_name(ent); - bool is_prop = icontains(cls, "physicsprop") || icontains(cls, "physbox") || - icontains(cls, "prop_physics") || - icontains(model, "props_junk") || - icontains(model, "props_physics") || - icontains(model, "props_c17") || - icontains(model, "props_interiors") || - icontains(model, "props_lab"); - if (!is_prop) { - static int miss_logs = 0; - if (miss_logs < 6) { - miss_logs++; - logf("stucklaunch prop? idx=%d movetype=%d cls=%s model=%s", idx, mt, - cls ? cls : "?", model ? model : "?"); - } - } - return is_prop; -} - -bool active_weapon_is_gravity_gun(void *local_ent) { - if (!local_ent || g_net.activeweapon < 0) - return false; - uint32_t h = *(uint32_t *)((uintptr_t)local_ent + g_net.activeweapon); - if (h == 0xFFFFFFFFu) - return false; - int idx = (int)(h & 0xFFFF); - if (idx <= 0 || idx > 4096) - return false; - void *wep = entitylist_get(idx); - void *net = entity_networkable(idx); - const char *cls = net ? entity_class_name(net, nullptr) : nullptr; - const char *model = wep ? entity_model_name(wep) : nullptr; - if (cls && strstr(cls, "physcannon")) - return true; - if (model && (strstr(model, "physcannon") || strstr(model, "w_physics"))) - return true; - return false; -} - static bool active_weapon_is_shotgun(void *local_ent) { if (!local_ent || g_net.activeweapon < 0) return false; @@ -1402,3 +1337,256 @@ void esp_draw() { draw_speed_graph(); } + + +// --------------------------------------------------------------------------- +// Valuable item tags +// --------------------------------------------------------------------------- + +enum ItemKind { IK_HEALTH, IK_AMMO, IK_ARMOR, IK_CRATE, IK_OTHER }; + +struct ItemTarget { + int idx; + int kind; + Vector origin; + char label[48]; +}; + +static ItemTarget g_item_targets[192]; +static int g_nitems = 0; +static bool g_items_logged = false; + +static bool item_icontains(const char *hay, const char *needle) { + if (!hay || !needle || !*needle) + return false; + size_t n = strlen(needle); + for (const char *p = hay; *p; p++) + if (strncasecmp(p, needle, n) == 0) + return true; + return false; +} + +static int classify_item(const char *model, const char *cls, char *label, + size_t outn) { + if (!model && !cls) + return -1; + + if (item_icontains(model, "healthkit")) { + snprintf(label, outn, "Medkit"); + return IK_HEALTH; + } + if (item_icontains(model, "healthvial")) { + snprintf(label, outn, "Health vial"); + return IK_HEALTH; + } + if (item_icontains(model, "health_charger")) { + snprintf(label, outn, "Health charger"); + return IK_HEALTH; + } + if (item_icontains(model, "battery")) { + snprintf(label, outn, "Battery"); + return IK_ARMOR; + } + if (item_icontains(model, "hevsuit")) { + snprintf(label, outn, "HEV suit"); + return IK_ARMOR; + } + if (item_icontains(model, "w_longjump")) { + snprintf(label, outn, "Long jump"); + return IK_ARMOR; + } + if (item_icontains(model, "suit_charger")) { + snprintf(label, outn, "Suit charger"); + return IK_ARMOR; + } + if (item_icontains(model, "item_item_crate") || + item_icontains(model, "item_beacon_crate")) { + snprintf(label, outn, "Supply crate"); + return IK_CRATE; + } + if (item_icontains(model, "ammocrate")) { + snprintf(label, outn, "Ammo crate"); + return IK_AMMO; + } + if (item_icontains(model, "w_missile_closed")) { + snprintf(label, outn, "RPG ammo"); + return IK_AMMO; + } + if (item_icontains(model, "models/items/")) { + if (item_icontains(model, "crossbowrounds")) + snprintf(label, outn, "Crossbow bolts"); + else if (item_icontains(model, "ar2_grenade")) + snprintf(label, outn, "AR2 grenade"); + else if (item_icontains(model, "357ammo")) + snprintf(label, outn, "357 ammo"); + else if (item_icontains(model, "boxbuckshot")) + snprintf(label, outn, "Buckshot"); + else if (item_icontains(model, "boxsniperrounds")) + snprintf(label, outn, "Sniper ammo"); + else + snprintf(label, outn, "Ammo"); + return IK_AMMO; + } + + if (item_icontains(cls, "healthkit") || item_icontains(cls, "healthvial") || + item_icontains(cls, "wallhealth")) { + snprintf(label, outn, "Health"); + return IK_HEALTH; + } + if (item_icontains(cls, "battery") || item_icontains(cls, "itemsuit") || + item_icontains(cls, "recharge")) { + snprintf(label, outn, "Armor"); + return IK_ARMOR; + } + if (item_icontains(cls, "itemcrate") || + item_icontains(cls, "dynamicresupply")) { + snprintf(label, outn, "Supply crate"); + return IK_CRATE; + } + if (item_icontains(cls, "item_ammo") || item_icontains(cls, "itemammo")) { + snprintf(label, outn, "Ammo"); + return IK_AMMO; + } + if (item_icontains(cls, "item") || item_icontains(cls, "pickup")) { + snprintf(label, outn, "Item"); + return IK_OTHER; + } + return -1; +} + +static bool item_kind_enabled(int kind) { + switch (kind) { + case IK_HEALTH: return g_item_health; + case IK_AMMO: return g_item_ammo; + case IK_ARMOR: return g_item_armor; + case IK_CRATE: return g_item_crates; + default: return true; + } +} + +static void items_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_nitems = 0; + if (!g_iface.entitylist || g_net.origin < 0 || g_net.modelindex < 0) + return; + + using Fn = int (*)(void *); + int hi = vfunc(g_iface.entitylist, 6)(g_iface.entitylist); + if (hi < 0 || hi > 4096) + hi = 4096; + + static int dbg_lines = 0; + for (int idx = 1; idx <= hi && g_nitems < 192; 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); + char label[48]; + int kind = classify_item(model, cls, label, sizeof(label)); + if (kind < 0) + continue; + + Vector origin{*(float *)((uintptr_t)ent + g_net.origin), + *(float *)((uintptr_t)ent + g_net.origin + 4), + *(float *)((uintptr_t)ent + g_net.origin + 8)}; + float dx = origin.x - g_cam_eye.x; + float dy = origin.y - g_cam_eye.y; + float dz = origin.z - g_cam_eye.z; + float dist = sqrtf(dx * dx + dy * dy + dz * dz); + if (g_item_max_dist > 0 && dist > (float)g_item_max_dist) + continue; + + ItemTarget &t = g_item_targets[g_nitems++]; + t.idx = idx; + t.kind = kind; + t.origin = origin; + snprintf(t.label, sizeof(t.label), "%s", label); + if (dbg_lines < 8) { + dbg_lines++; + logf("item: idx=%d kind=%d cls=%s model=%s", idx, kind, + cls ? cls : "?", model ? model : "?"); + } + } + + if (!g_items_logged) { + g_items_logged = true; + logf("items: tagging %d valuable entities", g_nitems); + } +} + +void esp_draw_items() { + if (!g_surface || !g_iface.entitylist || g_net.origin < 0) + return; + if (!g_item_health && !g_item_ammo && !g_item_armor && !g_item_crates) + return; + int local = engine_get_local_player(); + if (local <= 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; + } + items_collect(local); + + for (int i = 0; i < g_nitems; i++) { + const ItemTarget &t = g_item_targets[i]; + if (!item_kind_enabled(t.kind)) + continue; + float sx, sy; + if (!world_to_screen(t.origin, sx, sy)) + continue; + if (sx < -40 || sx > (float)g_scr_w + 40 || sy < -20 || + sy > (float)g_scr_h + 20) + continue; + + int r = 230, g = 230, b = 230; + switch (t.kind) { + case IK_HEALTH: r = 90; g = 255; b = 130; break; + case IK_AMMO: r = 255; g = 210; b = 80; break; + case IK_ARMOR: r = 90; g = 180; b = 255; break; + case IK_CRATE: r = 220; g = 150; b = 255; break; + default: break; + } + 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_line(x - 8, y, x - 5, y); + surf_line(x + 5, y, x + 8, y); + surf_line(x, y - 8, x, y - 5); + surf_line(x, y + 5, x, y + 8); + + float dx = t.origin.x - g_cam_eye.x; + float dy = t.origin.y - g_cam_eye.y; + float dz = t.origin.z - g_cam_eye.z; + int dist = (int)sqrtf(dx * dx + dy * dy + dz * dz); + char text[96]; + if (g_item_distance) + snprintf(text, sizeof(text), "%s %du", t.label, dist); + else + snprintf(text, sizeof(text), "%s", t.label); + int tw = (int)strlen(text) * 9; + int tx = x + 10; + if (tx + tw > g_scr_w - 4) + tx = x - 10 - tw; + if (tx < 4) + tx = 4; + int ty = y - 7; + if (ty < 0) + ty = 0; + if (ty + 16 > g_scr_h) + ty = g_scr_h - 16; + draw_text_col(tx, ty, text, r, g, b); + } +} -- cgit v1.2.3