diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-04 15:55:08 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-04 15:55:08 +0100 |
| commit | 1267387f58d41d1421e3c2122a6930de0b279d12 (patch) | |
| tree | 1d4c5cbfdecb9aa85f16ff4f14bbc189241bf87a /src | |
| parent | Add usable item filter, projectile ESP and gravity-gun melee (diff) | |
| download | hl2h-1267387f58d41d1421e3c2122a6930de0b279d12.tar.gz hl2h-1267387f58d41d1421e3c2122a6930de0b279d12.zip | |
Items: fallen owned weapons count as ammo; fuse diagnostics
- World weapons of a type already in the local player's inventory are retagged
as ammo ("SMG ammo", ...). Inventory scan only counts weapons owned by the
local player index, so NPC weapons do not trigger it.
- Projectile fuse: log det/cur/base/raw server value, and fall back to a
3-second first-sighting countdown when m_flDetonateTime or engine time is
unavailable.
Diffstat (limited to '')
| -rw-r--r-- | src/esp.cpp | 111 | ||||
| -rw-r--r-- | src/server.cpp | 9 |
2 files changed, 113 insertions, 7 deletions
diff --git a/src/esp.cpp b/src/esp.cpp index c956d1b..e273bf6 100644 --- a/src/esp.cpp +++ b/src/esp.cpp | |||
| @@ -2049,6 +2049,21 @@ static bool item_weapon_is_owned(void *ent) { | |||
| 2049 | return false; | 2049 | return false; |
| 2050 | } | 2050 | } |
| 2051 | 2051 | ||
| 2052 | // Entity index of the weapon's owner, or -1. Used to distinguish the local | ||
| 2053 | // player's inventory from NPC weapons. | ||
| 2054 | static int item_weapon_owner_idx(void *ent) { | ||
| 2055 | if (!ent) | ||
| 2056 | return -1; | ||
| 2057 | uint32_t h = 0; | ||
| 2058 | if (g_net.owner >= 0) | ||
| 2059 | h = *(uint32_t *)((uintptr_t)ent + g_net.owner); | ||
| 2060 | if (!item_handle_valid(h) && g_net.weapon_owner >= 0) | ||
| 2061 | h = *(uint32_t *)((uintptr_t)ent + g_net.weapon_owner); | ||
| 2062 | if (!item_handle_valid(h)) | ||
| 2063 | return -1; | ||
| 2064 | return (int)(h & 0x7FF); | ||
| 2065 | } | ||
| 2066 | |||
| 2052 | static int local_active_weapon_idx(int local_idx) { | 2067 | static int local_active_weapon_idx(int local_idx) { |
| 2053 | if (local_idx <= 0 || g_net.activeweapon < 0) | 2068 | if (local_idx <= 0 || g_net.activeweapon < 0) |
| 2054 | return -1; | 2069 | return -1; |
| @@ -2104,6 +2119,44 @@ static int classify_weapon(const char *model, const char *cls, char *label, | |||
| 2104 | return 1; | 2119 | return 1; |
| 2105 | } | 2120 | } |
| 2106 | 2121 | ||
| 2122 | // Weapon identity for the "owned weapon -> fallen weapon is ammo" rule. | ||
| 2123 | enum { | ||
| 2124 | WT_NONE = 0, | ||
| 2125 | WT_PISTOL, | ||
| 2126 | WT_357, | ||
| 2127 | WT_SMG, | ||
| 2128 | WT_AR2, | ||
| 2129 | WT_SHOTGUN, | ||
| 2130 | WT_CROSSBOW, | ||
| 2131 | WT_RPG, | ||
| 2132 | WT_GRENADE, | ||
| 2133 | WT_SNIPER, | ||
| 2134 | WT_COUNT | ||
| 2135 | }; | ||
| 2136 | |||
| 2137 | static int weapon_type_id(const char *model, const char *cls) { | ||
| 2138 | if (item_icontains(model, "pistol") || item_icontains(cls, "pistol")) | ||
| 2139 | return WT_PISTOL; | ||
| 2140 | if (item_icontains(model, "357") || item_icontains(cls, "357")) | ||
| 2141 | return WT_357; | ||
| 2142 | if (item_icontains(model, "smg") || item_icontains(cls, "smg")) | ||
| 2143 | return WT_SMG; | ||
| 2144 | if (item_icontains(model, "ar2") || item_icontains(model, "irifle") || | ||
| 2145 | item_icontains(cls, "ar2") || item_icontains(cls, "irifle")) | ||
| 2146 | return WT_AR2; | ||
| 2147 | if (item_icontains(model, "shotgun") || item_icontains(cls, "shotgun")) | ||
| 2148 | return WT_SHOTGUN; | ||
| 2149 | if (item_icontains(model, "crossbow") || item_icontains(cls, "crossbow")) | ||
| 2150 | return WT_CROSSBOW; | ||
| 2151 | if (item_icontains(model, "rocket") || item_icontains(cls, "rpg")) | ||
| 2152 | return WT_RPG; | ||
| 2153 | if (item_icontains(model, "grenade") || item_icontains(cls, "frag")) | ||
| 2154 | return WT_GRENADE; | ||
| 2155 | if (item_icontains(model, "sniper") || item_icontains(cls, "sniper")) | ||
| 2156 | return WT_SNIPER; | ||
| 2157 | return WT_NONE; | ||
| 2158 | } | ||
| 2159 | |||
| 2107 | static int classify_item(const char *model, const char *cls, char *label, | 2160 | static int classify_item(const char *model, const char *cls, char *label, |
| 2108 | size_t outn) { | 2161 | size_t outn) { |
| 2109 | if (!model && !cls) | 2162 | if (!model && !cls) |
| @@ -2291,6 +2344,23 @@ static void items_collect(int local_idx) { | |||
| 2291 | if (hi < 0 || hi > 4096) | 2344 | if (hi < 0 || hi > 4096) |
| 2292 | hi = 4096; | 2345 | hi = 4096; |
| 2293 | int active_idx = local_active_weapon_idx(local_idx); | 2346 | int active_idx = local_active_weapon_idx(local_idx); |
| 2347 | |||
| 2348 | // Which weapon types the local player already carries. A fallen weapon of | ||
| 2349 | // the same type is then just an ammo pickup. | ||
| 2350 | bool owned_weapon[WT_COUNT] = {}; | ||
| 2351 | for (int idx = 1; idx <= hi; idx++) { | ||
| 2352 | if (idx == local_idx) | ||
| 2353 | continue; | ||
| 2354 | void *ent = entitylist_get(idx); | ||
| 2355 | if (!ent || item_weapon_owner_idx(ent) != local_idx) | ||
| 2356 | continue; | ||
| 2357 | void *net = entity_networkable(idx); | ||
| 2358 | const char *cls = net ? entity_class_name(net, nullptr) : nullptr; | ||
| 2359 | const char *model = entity_model_name(ent); | ||
| 2360 | int wt = weapon_type_id(model, cls); | ||
| 2361 | if (wt > 0) | ||
| 2362 | owned_weapon[wt] = true; | ||
| 2363 | } | ||
| 2294 | static bool cats_logged = false; | 2364 | static bool cats_logged = false; |
| 2295 | if (!cats_logged) { | 2365 | if (!cats_logged) { |
| 2296 | cats_logged = true; | 2366 | cats_logged = true; |
| @@ -2347,6 +2417,13 @@ static void items_collect(int local_idx) { | |||
| 2347 | if (kind == IK_WEAPON) { | 2417 | if (kind == IK_WEAPON) { |
| 2348 | if (idx == active_idx || item_weapon_is_owned(ent)) | 2418 | if (idx == active_idx || item_weapon_is_owned(ent)) |
| 2349 | continue; | 2419 | continue; |
| 2420 | int wt = weapon_type_id(model, cls); | ||
| 2421 | if (wt > 0 && owned_weapon[wt]) { | ||
| 2422 | char base_label[160]; | ||
| 2423 | snprintf(base_label, sizeof(base_label), "%s", label); | ||
| 2424 | snprintf(label, sizeof(label), "%.100s ammo", base_label); | ||
| 2425 | kind = IK_AMMO; | ||
| 2426 | } | ||
| 2350 | } | 2427 | } |
| 2351 | // Distance applies to every tag; 0 means unlimited. | 2428 | // Distance applies to every tag; 0 means unlimited. |
| 2352 | if (g_item_max_dist > 0 && dist > (float)g_item_max_dist) | 2429 | if (g_item_max_dist > 0 && dist > (float)g_item_max_dist) |
| @@ -2511,6 +2588,23 @@ static const char *proj_name(int kind) { | |||
| 2511 | } | 2588 | } |
| 2512 | } | 2589 | } |
| 2513 | 2590 | ||
| 2591 | // Fallback fuse clock when the server detonate time cannot be read. The stock | ||
| 2592 | // frag fuse is 3 s from the first sighting. | ||
| 2593 | static double proj_first_seen(int idx, double now) { | ||
| 2594 | static int idxs[32]; | ||
| 2595 | static double first[32]; | ||
| 2596 | static int n = 0; | ||
| 2597 | for (int i = 0; i < n; i++) | ||
| 2598 | if (idxs[i] == idx) | ||
| 2599 | return first[i]; | ||
| 2600 | if (n < 32) { | ||
| 2601 | idxs[n] = idx; | ||
| 2602 | first[n] = now; | ||
| 2603 | n++; | ||
| 2604 | } | ||
| 2605 | return now; | ||
| 2606 | } | ||
| 2607 | |||
| 2514 | static void projs_collect(int local_idx) { | 2608 | static void projs_collect(int local_idx) { |
| 2515 | static double last_scan = 0.0; | 2609 | static double last_scan = 0.0; |
| 2516 | double now = esp_now(); | 2610 | double now = esp_now(); |
| @@ -2578,19 +2672,24 @@ static void projs_collect(int local_idx) { | |||
| 2578 | p.radius = kind == PK_RPG ? 200.0f : kind == PK_BALL ? 150.0f : 300.0f; | 2672 | p.radius = kind == PK_RPG ? 200.0f : kind == PK_BALL ? 150.0f : 300.0f; |
| 2579 | // m_flDetonateTime is server-only (not in the send table). | 2673 | // m_flDetonateTime is server-only (not in the send table). |
| 2580 | p.fuse = -1.0f; | 2674 | p.fuse = -1.0f; |
| 2675 | float det = -1.0f; | ||
| 2581 | if (kind != PK_RPG) { | 2676 | if (kind != PK_RPG) { |
| 2582 | float det = server_grenade_detonate(idx); | 2677 | det = server_grenade_detonate(idx); |
| 2583 | if (det > 0.0f && curtime > 0.0f) | 2678 | if (det > 0.0f && curtime > 0.0f) |
| 2584 | p.fuse = det - curtime; | 2679 | p.fuse = det - curtime; |
| 2585 | if (!(p.fuse > -2.0f && p.fuse < 30.0f)) | 2680 | if (!(p.fuse > -2.0f && p.fuse < 30.0f)) { |
| 2586 | p.fuse = -1.0f; | 2681 | p.fuse = 3.0f - (float)(now - proj_first_seen(idx, now)); |
| 2682 | if (!(p.fuse > -2.0f && p.fuse < 30.0f)) | ||
| 2683 | p.fuse = -1.0f; | ||
| 2684 | } | ||
| 2587 | } | 2685 | } |
| 2588 | static int logs = 0; | 2686 | static int logs = 0; |
| 2589 | if (logs < 8) { | 2687 | if (logs < 8) { |
| 2590 | logs++; | 2688 | logs++; |
| 2591 | logf("projectile: idx=%d cls=%s model=%s kind=%s fuse=%.2f " | 2689 | logf("projectile: idx=%d cls=%s model=%s kind=%s det=%.2f cur=%.2f " |
| 2592 | "radius=%.0f", | 2690 | "fuse=%.2f radius=%.0f", |
| 2593 | idx, cls ? cls : "?", model, proj_name(kind), p.fuse, p.radius); | 2691 | idx, cls ? cls : "?", model, proj_name(kind), det, curtime, p.fuse, |
| 2692 | p.radius); | ||
| 2594 | } | 2693 | } |
| 2595 | } | 2694 | } |
| 2596 | } | 2695 | } |
diff --git a/src/server.cpp b/src/server.cpp index 40aa850..1313f00 100644 --- a/src/server.cpp +++ b/src/server.cpp | |||
| @@ -455,7 +455,14 @@ float server_grenade_detonate(int idx) { | |||
| 455 | server_entity_health(idx, nullptr, nullptr, &base); | 455 | server_entity_health(idx, nullptr, nullptr, &base); |
| 456 | if (!base) | 456 | if (!base) |
| 457 | return -1.0f; | 457 | return -1.0f; |
| 458 | return *(float *)((uintptr_t)base + g_srv_detonate); | 458 | float v = *(float *)((uintptr_t)base + g_srv_detonate); |
| 459 | static int logs = 0; | ||
| 460 | if (logs < 8) { | ||
| 461 | logs++; | ||
| 462 | logf("grenade fuse: idx=%d base=%p off=%d value=%.2f", idx, base, | ||
| 463 | g_srv_detonate, v); | ||
| 464 | } | ||
| 465 | return v; | ||
| 459 | } | 466 | } |
| 460 | 467 | ||
| 461 | float server_grenade_radius(int idx) { | 468 | float server_grenade_radius(int idx) { |
