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 +++++++++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 214 insertions(+), 27 deletions(-) (limited to 'src/esp.cpp') 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; } -- cgit v1.2.3