From 77bd6ba7054b19332dc357e866f158321ec889fb Mon Sep 17 00:00:00 2001 From: salmonik Date: Tue, 27 Oct 2026 21:37:41 +0100 Subject: Item ESP: tag world weapons, name ammo types, restrict crates - Add a Weapons category and classify world weapon models (crowbar, pistol, revolver, SMG, AR2, shotgun, crossbow, RPG, grenade, gravity gun, stunstick, bugbait, sniper). Skip the active weapon and anything with a valid m_hOwner/m_hOwnerEntity handle so held weapons do not tag. - Label ammo by model: pistol, SMG, AR2, .357, crossbow, shotgun, sniper, flares, AR2 grenade, RPG rocket and specific ammo-crate variants. - Only item_item_crate and item_beacon_crate are supply crates; generic breakable crates are no longer tagged. Crate chunks stay excluded. - Add item diagnostics: category state, prop-like entities with class/model/ kind/distance/origin, and every item crate seen. --- src/esp.cpp | 241 +++++++++++++++++++++++++++++++++++++++++++++++++------- src/gui.cpp | 3 +- src/hl2h.h | 3 + src/menu.cpp | 10 ++- src/netvars.cpp | 7 +- 5 files changed, 232 insertions(+), 32 deletions(-) (limited to 'src') diff --git a/src/esp.cpp b/src/esp.cpp index 0d64c78..afdedac 100644 --- a/src/esp.cpp +++ b/src/esp.cpp @@ -1343,7 +1343,14 @@ void esp_draw() { // Valuable item tags // --------------------------------------------------------------------------- -enum ItemKind { IK_HEALTH, IK_AMMO, IK_ARMOR, IK_CRATE, IK_OTHER }; +enum ItemKind { + IK_HEALTH, + IK_AMMO, + IK_ARMOR, + IK_WEAPON, + IK_CRATE, + IK_OTHER +}; struct ItemTarget { int idx; @@ -1366,6 +1373,78 @@ static bool item_icontains(const char *hay, const char *needle) { return false; } +static bool item_handle_valid(uint32_t h) { + return h != 0 && h != 0xFFFFFFFFu; +} + +// Weapons held by the player or an NPC are not world pickups. +static bool item_weapon_is_owned(void *ent) { + if (!ent) + return false; + if (g_net.owner >= 0) { + uint32_t h = *(uint32_t *)((uintptr_t)ent + g_net.owner); + if (item_handle_valid(h)) + return true; + } + if (g_net.weapon_owner >= 0) { + uint32_t h = *(uint32_t *)((uintptr_t)ent + g_net.weapon_owner); + if (item_handle_valid(h)) + return true; + } + return false; +} + +static int local_active_weapon_idx(int local_idx) { + if (local_idx <= 0 || g_net.activeweapon < 0) + return -1; + void *local = entitylist_get(local_idx); + if (!local) + return -1; + uint32_t h = *(uint32_t *)((uintptr_t)local + g_net.activeweapon); + if (!item_handle_valid(h)) + return -1; + return (int)(h & 0xFFFF); +} + +static int classify_weapon(const char *model, const char *cls, char *label, + size_t outn) { + if (!item_icontains(model, "models/weapons/w_") && + !item_icontains(cls, "weapon")) + return 0; + if (item_icontains(model, "w_crowbar")) + snprintf(label, outn, "Crowbar"); + else if (item_icontains(model, "w_pistol")) + snprintf(label, outn, "Pistol"); + else if (item_icontains(model, "w_357")) + snprintf(label, outn, "Revolver"); + else if (item_icontains(model, "w_smg1") || + item_icontains(model, "w_smg2")) + snprintf(label, outn, "SMG"); + else if (item_icontains(model, "w_ar2")) + snprintf(label, outn, "AR2"); + else if (item_icontains(model, "w_shotgun")) + snprintf(label, outn, "Shotgun"); + else if (item_icontains(model, "w_crossbow")) + snprintf(label, outn, "Crossbow"); + else if (item_icontains(model, "w_rpg")) + snprintf(label, outn, "RPG"); + else if (item_icontains(model, "w_frag") || + item_icontains(model, "w_grenade")) + snprintf(label, outn, "Grenade"); + else if (item_icontains(model, "w_physcannon") || + item_icontains(model, "w_physics")) + snprintf(label, outn, "Gravity gun"); + else if (item_icontains(model, "w_stunstick")) + snprintf(label, outn, "Stunstick"); + else if (item_icontains(model, "w_bugbait")) + snprintf(label, outn, "Bugbait"); + else if (item_icontains(model, "w_sniper")) + snprintf(label, outn, "Sniper"); + else + snprintf(label, outn, "Weapon"); + return 1; +} + static int classify_item(const char *model, const char *cls, char *label, size_t outn) { if (!model && !cls) @@ -1403,40 +1482,100 @@ static int classify_item(const char *model, const char *cls, char *label, snprintf(label, outn, "Suit charger"); return IK_ARMOR; } + + // Only the two item-crate models are supply crates. Other crates are just + // breakable map props and deliberately not tagged. 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_pistol")) { + snprintf(label, outn, "Pistol ammo crate"); + return IK_AMMO; + } + if (item_icontains(model, "ammocrate_smg1")) { + snprintf(label, outn, "SMG ammo crate"); + return IK_AMMO; + } + if (item_icontains(model, "ammocrate_ar2")) { + snprintf(label, outn, "AR2 ammo crate"); + return IK_AMMO; + } + if (item_icontains(model, "ammocrate_rockets")) { + snprintf(label, outn, "RPG ammo crate"); + return IK_AMMO; + } + if (item_icontains(model, "ammocrate_buckshot")) { + snprintf(label, outn, "Shotgun ammo crate"); + return IK_AMMO; + } + if (item_icontains(model, "ammocrate_grenade")) { + snprintf(label, outn, "Grenade crate"); + return IK_AMMO; + } + if (item_icontains(model, "ammocrate_smg2")) { + snprintf(label, outn, "SMG grenade crate"); + return IK_AMMO; + } if (item_icontains(model, "ammocrate")) { snprintf(label, outn, "Ammo crate"); return IK_AMMO; } - // Generic breakable crates (wood/plastic/metal) are supply caches too. - if (item_icontains(model, "crate")) { - snprintf(label, outn, "Crate"); - return IK_CRATE; - } + if (item_icontains(model, "w_missile_closed")) { - snprintf(label, outn, "RPG ammo"); + snprintf(label, outn, "RPG rocket"); + return IK_AMMO; + } + if (item_icontains(model, "boxsrounds")) { + snprintf(label, outn, "Pistol ammo"); + return IK_AMMO; + } + if (item_icontains(model, "boxmrounds")) { + snprintf(label, outn, "SMG ammo"); + return IK_AMMO; + } + if (item_icontains(model, "boxlrounds") || + item_icontains(model, "combine_rifle_cartridge") || + item_icontains(model, "combine_rifle_ammo")) { + snprintf(label, outn, "AR2 ammo"); + return IK_AMMO; + } + if (item_icontains(model, "357ammo") || + item_icontains(model, "357ammobox")) { + snprintf(label, outn, ".357 ammo"); + return IK_AMMO; + } + if (item_icontains(model, "crossbowrounds")) { + snprintf(label, outn, "Crossbow bolts"); + return IK_AMMO; + } + if (item_icontains(model, "boxbuckshot")) { + snprintf(label, outn, "Shotgun shells"); + return IK_AMMO; + } + if (item_icontains(model, "boxsniperrounds")) { + snprintf(label, outn, "Sniper ammo"); + return IK_AMMO; + } + if (item_icontains(model, "boxflares") || + item_icontains(model, "models/items/flare")) { + snprintf(label, outn, "Flares"); + return IK_AMMO; + } + if (item_icontains(model, "ar2_grenade")) { + snprintf(label, outn, "AR2 grenade"); 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"); + snprintf(label, outn, "Ammo"); return IK_AMMO; } + if (classify_weapon(model, cls, label, outn) == 1) + return IK_WEAPON; + if (item_icontains(cls, "healthkit") || item_icontains(cls, "healthvial") || item_icontains(cls, "wallhealth")) { snprintf(label, outn, "Health"); @@ -1447,8 +1586,7 @@ static int classify_item(const char *model, const char *cls, char *label, snprintf(label, outn, "Armor"); return IK_ARMOR; } - if (item_icontains(cls, "itemcrate") || - item_icontains(cls, "dynamicresupply")) { + if (item_icontains(cls, "itemcrate")) { snprintf(label, outn, "Supply crate"); return IK_CRATE; } @@ -1456,9 +1594,9 @@ static int classify_item(const char *model, const char *cls, char *label, snprintf(label, outn, "Ammo"); return IK_AMMO; } - if (item_icontains(cls, "crate")) { - snprintf(label, outn, "Crate"); - return IK_CRATE; + if (item_icontains(cls, "weapon")) { + snprintf(label, outn, "Weapon"); + return IK_WEAPON; } if (item_icontains(cls, "item") || item_icontains(cls, "pickup")) { snprintf(label, outn, "Item"); @@ -1472,6 +1610,7 @@ static bool item_kind_enabled(int kind) { case IK_HEALTH: return g_item_health; case IK_AMMO: return g_item_ammo; case IK_ARMOR: return g_item_armor; + case IK_WEAPON: return g_item_weapons; case IK_CRATE: return g_item_crates; default: return true; } @@ -1491,8 +1630,25 @@ static void items_collect(int local_idx) { int hi = vfunc(g_iface.entitylist, 6)(g_iface.entitylist); if (hi < 0 || hi > 4096) hi = 4096; - + int active_idx = local_active_weapon_idx(local_idx); + static bool cats_logged = false; + if (!cats_logged) { + cats_logged = true; + logf("items: categories health=%d ammo=%d armor=%d weapons=%d " + "crates=%d distance=%d max=%d", + (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); + } static int dbg_lines = 0; + static int prop_logs = 0; + static int crate_logs = 0; + static bool owner_warned = false; + if (!owner_warned && g_net.owner < 0 && g_net.weapon_owner < 0) { + owner_warned = true; + logf("items: weapon owner netvar unavailable; held weapons may tag"); + } + for (int idx = 1; idx <= hi && g_nitems < 192; idx++) { if (idx == local_idx) continue; @@ -1506,7 +1662,14 @@ static void items_collect(int local_idx) { const char *model = entity_model_name(ent); char label[48]; int kind = classify_item(model, cls, label, sizeof(label)); - if (kind < 0) + + bool propish = item_icontains(cls, "crate") || + item_icontains(model, "crate") || + item_icontains(cls, "physicsprop") || + item_icontains(cls, "breakableprop") || + item_icontains(cls, "func_breakable") || + item_icontains(cls, "physbox"); + if (kind < 0 && !propish) continue; Vector origin{*(float *)((uintptr_t)ent + g_net.origin), @@ -1516,9 +1679,31 @@ static void items_collect(int local_idx) { 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 (propish && prop_logs < 24) { + prop_logs++; + logf("item prop: idx=%d cls=%s model=%s kind=%d dist=%.0f " + "origin=(%.0f %.0f %.0f)", + idx, cls ? cls : "?", model ? model : "?", kind, dist, origin.x, + origin.y, origin.z); + } + if (kind < 0) + continue; + + if (kind == IK_WEAPON) { + if (idx == active_idx || item_weapon_is_owned(ent)) + continue; + } if (g_item_max_dist > 0 && dist > (float)g_item_max_dist) continue; + if (kind == IK_CRATE && crate_logs < 8) { + crate_logs++; + logf("item crate: idx=%d cls=%s model=%s origin=(%.0f %.0f %.0f)", idx, + cls ? cls : "?", model ? model : "?", origin.x, origin.y, + origin.z); + } + ItemTarget &t = g_item_targets[g_nitems++]; t.idx = idx; t.kind = kind; @@ -1540,7 +1725,8 @@ static void items_collect(int local_idx) { 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) + if (!g_item_health && !g_item_ammo && !g_item_armor && !g_item_weapons && + !g_item_crates) return; int local = engine_get_local_player(); if (local <= 0) @@ -1569,6 +1755,7 @@ void esp_draw_items() { 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_WEAPON: r = 120; g = 230; b = 255; break; case IK_CRATE: r = 220; g = 150; b = 255; break; default: break; } diff --git a/src/gui.cpp b/src/gui.cpp index eb9a964..29fa60b 100644 --- a/src/gui.cpp +++ b/src/gui.cpp @@ -350,7 +350,7 @@ static void build_cards() { add_toggle(esp, esp_names[i], esp_descs[i], &g_esp_opt[i]); int items = add_card(2, &g_item_esp, "Valuable items", - "Tag medkits, ammo, armor and supply crates.", true); + "Tag medkits, ammo, armor, weapons and supply crates.", true); add_toggle(items, "Medkits / health", "Tag health kits, health vials and health chargers.", &g_item_health); @@ -358,6 +358,7 @@ static void build_cards() { &g_item_ammo); add_toggle(items, "Armor / suit", "Tag batteries, HEV suits and chargers.", &g_item_armor); + add_toggle(items, "Weapons", "Tag world weapon pickups.", &g_item_weapons); add_toggle(items, "Supply crates", "Tag item and beacon crates.", &g_item_crates); add_toggle(items, "Show distance", "Append the distance in units.", diff --git a/src/hl2h.h b/src/hl2h.h index 8aeef7a..cc11e0d 100644 --- a/src/hl2h.h +++ b/src/hl2h.h @@ -92,6 +92,8 @@ struct NetVars { int modelindex = -1; int absvelocity = -1; // client C_BaseEntity::m_vecAbsVelocity (prediction) int activeweapon = -1; // client m_hActiveWeapon handle + int owner = -1; // client m_hOwnerEntity handle + int weapon_owner = -1; // client m_hOwner handle (C_BaseCombatWeapon) // 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. @@ -212,6 +214,7 @@ extern bool g_spread_no_spread; extern bool g_item_health; extern bool g_item_ammo; extern bool g_item_armor; +extern bool g_item_weapons; extern bool g_item_crates; extern bool g_item_distance; extern int g_item_max_dist; diff --git a/src/menu.cpp b/src/menu.cpp index af8d10d..69c3b45 100644 --- a/src/menu.cpp +++ b/src/menu.cpp @@ -48,6 +48,7 @@ FeatureState g_item_esp{true, 0}; // valuable item tags, always-on by default 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; int g_item_max_dist = 2500; @@ -184,10 +185,10 @@ void config_save() { fprintf(f, "[silentaim]\nactive=%d\nbind=%d\nfov=%d\n", g_silentaim.active, g_silentaim.bind, g_silent_fov); fprintf(f, "[items]\nactive=%d\nbind=%d\nhealth=%d\nammo=%d\n" - "armor=%d\ncrates=%d\ndistance=%d\nmax_dist=%d\n", + "armor=%d\nweapons=%d\ncrates=%d\ndistance=%d\nmax_dist=%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_crates, - (int)g_item_distance, g_item_max_dist); + (int)g_item_ammo, (int)g_item_armor, (int)g_item_weapons, + (int)g_item_crates, (int)g_item_distance, g_item_max_dist); fprintf(f, "[esp]\nactive=%d\nbind=%d\nneutral=%d\nhealthbar=%d\n" "healthnum=%d\nname=%d\nskeleton=%d\nbox=%d\nspeedgraph=%d\n", g_esp.active, g_esp.bind, g_esp_opt[ESP_OPT_SHOW_NEUTRAL], @@ -271,6 +272,8 @@ static bool config_load() { g_item_ammo = atoi(val) != 0; else if (strcmp(key, "armor") == 0) g_item_armor = atoi(val) != 0; + else if (strcmp(key, "weapons") == 0) + g_item_weapons = atoi(val) != 0; else if (strcmp(key, "crates") == 0) g_item_crates = atoi(val) != 0; else if (strcmp(key, "distance") == 0) @@ -372,6 +375,7 @@ void config_reset_defaults() { 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_max_dist = 2500; diff --git a/src/netvars.cpp b/src/netvars.cpp index 3b2d3b8..4980acc 100644 --- a/src/netvars.cpp +++ b/src/netvars.cpp @@ -92,6 +92,10 @@ static void collect_netvars(uintptr_t table, uintptr_t base_off, int depth) { } else if (strcmp(pname2, "m_hActiveWeapon") == 0 && g_net.activeweapon < 0) { g_net.activeweapon = (int)(base_off + (uint32_t)poff); + } else if (strcmp(pname2, "m_hOwnerEntity") == 0 && g_net.owner < 0) { + g_net.owner = (int)(base_off + (uint32_t)poff); + } else if (strcmp(pname2, "m_hOwner") == 0 && g_net.weapon_owner < 0) { + g_net.weapon_owner = (int)(base_off + (uint32_t)poff); } 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) { @@ -272,7 +276,8 @@ void resolve_netvars() { logf("m_vecAbsVelocity unresolved; jumpbug assist disabled"); 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", + "modelindex=%d absvelocity=%d activeweapon=%d owner=%d " + "weapon_owner=%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, -- cgit v1.2.3