summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--CONTEXT.md10
-rw-r--r--src/esp.cpp111
-rw-r--r--src/server.cpp9
3 files changed, 122 insertions, 8 deletions
diff --git a/CONTEXT.md b/CONTEXT.md
index 96a4660..d8fdb97 100644
--- a/CONTEXT.md
+++ b/CONTEXT.md
@@ -594,6 +594,11 @@ vocabulary / decisions.
594 is the server player's `m_iMaxHealth` (not a hardcoded 100); armor uses the 594 is the server player's `m_iMaxHealth` (not a hardcoded 100); armor uses the
595 HL2 `MAX_NORMAL_BATTERY` cap of 100, read from the server 595 HL2 `MAX_NORMAL_BATTERY` cap of 100, read from the server
596 `m_ArmorValue`. If the values cannot be read the filter fails open. 596 `m_ArmorValue`. If the values cannot be read the filter fails open.
597- A world weapon whose type is already in the local player's inventory is
598 reclassified as ammo (`SMG ammo`, `AR2 ammo`, ...). Inventory types come
599 from client weapon entities whose owner handle resolves to the local player
600 index; NPC-held weapons are ignored. Weapons the player does not own stay
601 under the Weapons category.
597- Scans the client entity list every 100 ms and tags health, ammo, armor/suit, 602- Scans the client entity list every 100 ms and tags health, ammo, armor/suit,
598 world weapon pickups, the two supply-crate models and generic items. 603 world weapon pickups, the two supply-crate models and generic items.
599- Each tag is a colored marker with a name and optional distance, filtered by 604- Each tag is a colored marker with a name and optional distance, filtered by
@@ -621,7 +626,10 @@ vocabulary / decisions.
621 `CGrenadeFrag`-style class names or by an owner handle. 626 `CGrenadeFrag`-style class names or by an owner handle.
622- Fuse: `CBaseGrenade::m_flDetonateTime` is not networked, so it is read from 627- Fuse: `CBaseGrenade::m_flDetonateTime` is not networked, so it is read from
623 the server entity datamap (`FIELD_TIME`) and compared with 628 the server entity datamap (`FIELD_TIME`) and compared with
624 `IVEngineClient::Time()` (slot 14). RPG rockets have no fuse. 629 `IVEngineClient::Time()` (slot 14). RPG rockets have no fuse. If the server
630 value or the engine time is unavailable, a 3-second countdown from the first
631 sighting is used so the label still ticks. The first 8 projectiles log
632 `det=`, `cur=`, the server base and the raw fuse value for diagnosis.
625- Blast radius: networked client `m_DmgRadius` when available, server 633- Blast radius: networked client `m_DmgRadius` when available, server
626 `m_DmgRadius` (`FIELD_FLOAT`) as fallback, then 300/200/150/300 for 634 `m_DmgRadius` (`FIELD_FLOAT`) as fallback, then 300/200/150/300 for
627 frag/RPG/AR2/bomb. 635 frag/RPG/AR2/bomb.
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.
2054static 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
2052static int local_active_weapon_idx(int local_idx) { 2067static 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.
2123enum {
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
2137static 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
2107static int classify_item(const char *model, const char *cls, char *label, 2160static 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.
2593static 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
2514static void projs_collect(int local_idx) { 2608static 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
461float server_grenade_radius(int idx) { 468float server_grenade_radius(int idx) {