diff options
| author | salmonik <salmonik@tuta.io> | 2026-10-27 21:37:41 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-10-27 21:37:41 +0100 |
| commit | 77bd6ba7054b19332dc357e866f158321ec889fb (patch) | |
| tree | 741c046d925bd1dbfc8af935954f3eab6c027660 | |
| parent | Item ESP: tag breakable wood/plastic/metal crates (diff) | |
| download | hl2h-77bd6ba7054b19332dc357e866f158321ec889fb.tar.gz hl2h-77bd6ba7054b19332dc357e866f158321ec889fb.zip | |
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.
Diffstat (limited to '')
| -rw-r--r-- | CONTEXT.md | 18 | ||||
| -rw-r--r-- | src/esp.cpp | 241 | ||||
| -rw-r--r-- | src/gui.cpp | 3 | ||||
| -rw-r--r-- | src/hl2h.h | 3 | ||||
| -rw-r--r-- | src/menu.cpp | 10 | ||||
| -rw-r--r-- | src/netvars.cpp | 7 |
6 files changed, 243 insertions, 39 deletions
| @@ -82,6 +82,7 @@ Relevant vtable slots (this build): | |||
| 82 | | `m_nModelIndex` | `0x7C` | | 82 | | `m_nModelIndex` | `0x7C` | |
| 83 | | `m_vecAbsVelocity` (client local, not netvar) | `0x11C` | | 83 | | `m_vecAbsVelocity` (client local, not netvar) | `0x11C` | |
| 84 | | `m_hActiveWeapon` (client netvar; server fallback `0x824`) | resolved by name | | 84 | | `m_hActiveWeapon` (client netvar; server fallback `0x824`) | resolved by name | |
| 85 | | `m_hOwnerEntity` / `m_hOwner` (held weapons) | resolved by name | | ||
| 85 | 86 | ||
| 86 | - Eye position = `m_vecOrigin + m_vecViewOffset` on **all three axes**. | 87 | - Eye position = `m_vecOrigin + m_vecViewOffset` on **all three axes**. |
| 87 | (The old code added the X component to Z, which left the eye ~64 units low.) | 88 | (The old code added the X component to Z, which left the eye ~64 units low.) |
| @@ -271,16 +272,19 @@ vocabulary / decisions. | |||
| 271 | ## Valuable items | 272 | ## Valuable items |
| 272 | 273 | ||
| 273 | - Visuals feature; Active is on by default and unbound (always-on). | 274 | - Visuals feature; Active is on by default and unbound (always-on). |
| 274 | - Scans the client entity list every 100 ms for item models/classes and tags | 275 | - Scans the client entity list every 100 ms and tags health, ammo, armor/suit, |
| 275 | health, ammo, armor/suit, supply crates and generic items. | 276 | world weapon pickups, the two supply-crate models and generic items. |
| 276 | - Each tag is a colored marker with a name and optional distance, filtered by | 277 | - Each tag is a colored marker with a name and optional distance, filtered by |
| 277 | category toggles and a max-distance slider. Drawn through walls and | 278 | category toggles and a max-distance slider. Drawn through walls and |
| 278 | independently of the hostile NPC ESP. | 279 | independently of the hostile NPC ESP. |
| 279 | - Detection is model-first (`models/items/...`, `models/healthvial.mdl`, | 280 | - Ammo labels are specific where the model is known: pistol, SMG, AR2, .357, |
| 280 | `models/props_combine/*_charger*.mdl`, `models/w_longjump.mdl`) with class | 281 | crossbow, shotgun, sniper, flares, AR2 grenade, RPG rocket, plus ammo-crate |
| 281 | fallbacks. Generic breakable wood/plastic/metal crates are tagged as | 282 | variants. Unknown ammo falls back to "Ammo". |
| 282 | "Crate"; crate chunk debris is ignored. Weapon pickups are intentionally | 283 | - Weapons are world pickups only: the active weapon and anything with a valid |
| 283 | not tagged. | 284 | `m_hOwner`/`m_hOwnerEntity` handle is skipped. |
| 285 | - Only `item_item_crate` and `item_beacon_crate` are tagged as supply crates. | ||
| 286 | Other breakable crates are intentionally ignored; crate chunk debris is | ||
| 287 | skipped. | ||
| 284 | 288 | ||
| 285 | ## Roadmap / TODO | 289 | ## Roadmap / TODO |
| 286 | 290 | ||
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() { | |||
| 1343 | // Valuable item tags | 1343 | // Valuable item tags |
| 1344 | // --------------------------------------------------------------------------- | 1344 | // --------------------------------------------------------------------------- |
| 1345 | 1345 | ||
| 1346 | enum ItemKind { IK_HEALTH, IK_AMMO, IK_ARMOR, IK_CRATE, IK_OTHER }; | 1346 | enum ItemKind { |
| 1347 | IK_HEALTH, | ||
| 1348 | IK_AMMO, | ||
| 1349 | IK_ARMOR, | ||
| 1350 | IK_WEAPON, | ||
| 1351 | IK_CRATE, | ||
| 1352 | IK_OTHER | ||
| 1353 | }; | ||
| 1347 | 1354 | ||
| 1348 | struct ItemTarget { | 1355 | struct ItemTarget { |
| 1349 | int idx; | 1356 | int idx; |
| @@ -1366,6 +1373,78 @@ static bool item_icontains(const char *hay, const char *needle) { | |||
| 1366 | return false; | 1373 | return false; |
| 1367 | } | 1374 | } |
| 1368 | 1375 | ||
| 1376 | static bool item_handle_valid(uint32_t h) { | ||
| 1377 | return h != 0 && h != 0xFFFFFFFFu; | ||
| 1378 | } | ||
| 1379 | |||
| 1380 | // Weapons held by the player or an NPC are not world pickups. | ||
| 1381 | static bool item_weapon_is_owned(void *ent) { | ||
| 1382 | if (!ent) | ||
| 1383 | return false; | ||
| 1384 | if (g_net.owner >= 0) { | ||
| 1385 | uint32_t h = *(uint32_t *)((uintptr_t)ent + g_net.owner); | ||
| 1386 | if (item_handle_valid(h)) | ||
| 1387 | return true; | ||
| 1388 | } | ||
| 1389 | if (g_net.weapon_owner >= 0) { | ||
| 1390 | uint32_t h = *(uint32_t *)((uintptr_t)ent + g_net.weapon_owner); | ||
| 1391 | if (item_handle_valid(h)) | ||
| 1392 | return true; | ||
| 1393 | } | ||
| 1394 | return false; | ||
| 1395 | } | ||
| 1396 | |||
| 1397 | static int local_active_weapon_idx(int local_idx) { | ||
| 1398 | if (local_idx <= 0 || g_net.activeweapon < 0) | ||
| 1399 | return -1; | ||
| 1400 | void *local = entitylist_get(local_idx); | ||
| 1401 | if (!local) | ||
| 1402 | return -1; | ||
| 1403 | uint32_t h = *(uint32_t *)((uintptr_t)local + g_net.activeweapon); | ||
| 1404 | if (!item_handle_valid(h)) | ||
| 1405 | return -1; | ||
| 1406 | return (int)(h & 0xFFFF); | ||
| 1407 | } | ||
| 1408 | |||
| 1409 | static int classify_weapon(const char *model, const char *cls, char *label, | ||
| 1410 | size_t outn) { | ||
| 1411 | if (!item_icontains(model, "models/weapons/w_") && | ||
| 1412 | !item_icontains(cls, "weapon")) | ||
| 1413 | return 0; | ||
| 1414 | if (item_icontains(model, "w_crowbar")) | ||
| 1415 | snprintf(label, outn, "Crowbar"); | ||
| 1416 | else if (item_icontains(model, "w_pistol")) | ||
| 1417 | snprintf(label, outn, "Pistol"); | ||
| 1418 | else if (item_icontains(model, "w_357")) | ||
| 1419 | snprintf(label, outn, "Revolver"); | ||
| 1420 | else if (item_icontains(model, "w_smg1") || | ||
| 1421 | item_icontains(model, "w_smg2")) | ||
| 1422 | snprintf(label, outn, "SMG"); | ||
| 1423 | else if (item_icontains(model, "w_ar2")) | ||
| 1424 | snprintf(label, outn, "AR2"); | ||
| 1425 | else if (item_icontains(model, "w_shotgun")) | ||
| 1426 | snprintf(label, outn, "Shotgun"); | ||
| 1427 | else if (item_icontains(model, "w_crossbow")) | ||
| 1428 | snprintf(label, outn, "Crossbow"); | ||
| 1429 | else if (item_icontains(model, "w_rpg")) | ||
| 1430 | snprintf(label, outn, "RPG"); | ||
| 1431 | else if (item_icontains(model, "w_frag") || | ||
| 1432 | item_icontains(model, "w_grenade")) | ||
| 1433 | snprintf(label, outn, "Grenade"); | ||
| 1434 | else if (item_icontains(model, "w_physcannon") || | ||
| 1435 | item_icontains(model, "w_physics")) | ||
| 1436 | snprintf(label, outn, "Gravity gun"); | ||
| 1437 | else if (item_icontains(model, "w_stunstick")) | ||
| 1438 | snprintf(label, outn, "Stunstick"); | ||
| 1439 | else if (item_icontains(model, "w_bugbait")) | ||
| 1440 | snprintf(label, outn, "Bugbait"); | ||
| 1441 | else if (item_icontains(model, "w_sniper")) | ||
| 1442 | snprintf(label, outn, "Sniper"); | ||
| 1443 | else | ||
| 1444 | snprintf(label, outn, "Weapon"); | ||
| 1445 | return 1; | ||
| 1446 | } | ||
| 1447 | |||
| 1369 | static int classify_item(const char *model, const char *cls, char *label, | 1448 | static int classify_item(const char *model, const char *cls, char *label, |
| 1370 | size_t outn) { | 1449 | size_t outn) { |
| 1371 | if (!model && !cls) | 1450 | if (!model && !cls) |
| @@ -1403,40 +1482,100 @@ static int classify_item(const char *model, const char *cls, char *label, | |||
| 1403 | snprintf(label, outn, "Suit charger"); | 1482 | snprintf(label, outn, "Suit charger"); |
| 1404 | return IK_ARMOR; | 1483 | return IK_ARMOR; |
| 1405 | } | 1484 | } |
| 1485 | |||
| 1486 | // Only the two item-crate models are supply crates. Other crates are just | ||
| 1487 | // breakable map props and deliberately not tagged. | ||
| 1406 | if (item_icontains(model, "item_item_crate") || | 1488 | if (item_icontains(model, "item_item_crate") || |
| 1407 | item_icontains(model, "item_beacon_crate")) { | 1489 | item_icontains(model, "item_beacon_crate")) { |
| 1408 | snprintf(label, outn, "Supply crate"); | 1490 | snprintf(label, outn, "Supply crate"); |
| 1409 | return IK_CRATE; | 1491 | return IK_CRATE; |
| 1410 | } | 1492 | } |
| 1493 | |||
| 1494 | if (item_icontains(model, "ammocrate_pistol")) { | ||
| 1495 | snprintf(label, outn, "Pistol ammo crate"); | ||
| 1496 | return IK_AMMO; | ||
| 1497 | } | ||
| 1498 | if (item_icontains(model, "ammocrate_smg1")) { | ||
| 1499 | snprintf(label, outn, "SMG ammo crate"); | ||
| 1500 | return IK_AMMO; | ||
| 1501 | } | ||
| 1502 | if (item_icontains(model, "ammocrate_ar2")) { | ||
| 1503 | snprintf(label, outn, "AR2 ammo crate"); | ||
| 1504 | return IK_AMMO; | ||
| 1505 | } | ||
| 1506 | if (item_icontains(model, "ammocrate_rockets")) { | ||
| 1507 | snprintf(label, outn, "RPG ammo crate"); | ||
| 1508 | return IK_AMMO; | ||
| 1509 | } | ||
| 1510 | if (item_icontains(model, "ammocrate_buckshot")) { | ||
| 1511 | snprintf(label, outn, "Shotgun ammo crate"); | ||
| 1512 | return IK_AMMO; | ||
| 1513 | } | ||
| 1514 | if (item_icontains(model, "ammocrate_grenade")) { | ||
| 1515 | snprintf(label, outn, "Grenade crate"); | ||
| 1516 | return IK_AMMO; | ||
| 1517 | } | ||
| 1518 | if (item_icontains(model, "ammocrate_smg2")) { | ||
| 1519 | snprintf(label, outn, "SMG grenade crate"); | ||
| 1520 | return IK_AMMO; | ||
| 1521 | } | ||
| 1411 | if (item_icontains(model, "ammocrate")) { | 1522 | if (item_icontains(model, "ammocrate")) { |
| 1412 | snprintf(label, outn, "Ammo crate"); | 1523 | snprintf(label, outn, "Ammo crate"); |
| 1413 | return IK_AMMO; | 1524 | return IK_AMMO; |
| 1414 | } | 1525 | } |
| 1415 | // Generic breakable crates (wood/plastic/metal) are supply caches too. | 1526 | |
| 1416 | if (item_icontains(model, "crate")) { | ||
| 1417 | snprintf(label, outn, "Crate"); | ||
| 1418 | return IK_CRATE; | ||
| 1419 | } | ||
| 1420 | if (item_icontains(model, "w_missile_closed")) { | 1527 | if (item_icontains(model, "w_missile_closed")) { |
| 1421 | snprintf(label, outn, "RPG ammo"); | 1528 | snprintf(label, outn, "RPG rocket"); |
| 1529 | return IK_AMMO; | ||
| 1530 | } | ||
| 1531 | if (item_icontains(model, "boxsrounds")) { | ||
| 1532 | snprintf(label, outn, "Pistol ammo"); | ||
| 1533 | return IK_AMMO; | ||
| 1534 | } | ||
| 1535 | if (item_icontains(model, "boxmrounds")) { | ||
| 1536 | snprintf(label, outn, "SMG ammo"); | ||
| 1537 | return IK_AMMO; | ||
| 1538 | } | ||
| 1539 | if (item_icontains(model, "boxlrounds") || | ||
| 1540 | item_icontains(model, "combine_rifle_cartridge") || | ||
| 1541 | item_icontains(model, "combine_rifle_ammo")) { | ||
| 1542 | snprintf(label, outn, "AR2 ammo"); | ||
| 1543 | return IK_AMMO; | ||
| 1544 | } | ||
| 1545 | if (item_icontains(model, "357ammo") || | ||
| 1546 | item_icontains(model, "357ammobox")) { | ||
| 1547 | snprintf(label, outn, ".357 ammo"); | ||
| 1548 | return IK_AMMO; | ||
| 1549 | } | ||
| 1550 | if (item_icontains(model, "crossbowrounds")) { | ||
| 1551 | snprintf(label, outn, "Crossbow bolts"); | ||
| 1552 | return IK_AMMO; | ||
| 1553 | } | ||
| 1554 | if (item_icontains(model, "boxbuckshot")) { | ||
| 1555 | snprintf(label, outn, "Shotgun shells"); | ||
| 1556 | return IK_AMMO; | ||
| 1557 | } | ||
| 1558 | if (item_icontains(model, "boxsniperrounds")) { | ||
| 1559 | snprintf(label, outn, "Sniper ammo"); | ||
| 1560 | return IK_AMMO; | ||
| 1561 | } | ||
| 1562 | if (item_icontains(model, "boxflares") || | ||
| 1563 | item_icontains(model, "models/items/flare")) { | ||
| 1564 | snprintf(label, outn, "Flares"); | ||
| 1565 | return IK_AMMO; | ||
| 1566 | } | ||
| 1567 | if (item_icontains(model, "ar2_grenade")) { | ||
| 1568 | snprintf(label, outn, "AR2 grenade"); | ||
| 1422 | return IK_AMMO; | 1569 | return IK_AMMO; |
| 1423 | } | 1570 | } |
| 1424 | if (item_icontains(model, "models/items/")) { | 1571 | if (item_icontains(model, "models/items/")) { |
| 1425 | if (item_icontains(model, "crossbowrounds")) | 1572 | snprintf(label, outn, "Ammo"); |
| 1426 | snprintf(label, outn, "Crossbow bolts"); | ||
| 1427 | else if (item_icontains(model, "ar2_grenade")) | ||
| 1428 | snprintf(label, outn, "AR2 grenade"); | ||
| 1429 | else if (item_icontains(model, "357ammo")) | ||
| 1430 | snprintf(label, outn, "357 ammo"); | ||
| 1431 | else if (item_icontains(model, "boxbuckshot")) | ||
| 1432 | snprintf(label, outn, "Buckshot"); | ||
| 1433 | else if (item_icontains(model, "boxsniperrounds")) | ||
| 1434 | snprintf(label, outn, "Sniper ammo"); | ||
| 1435 | else | ||
| 1436 | snprintf(label, outn, "Ammo"); | ||
| 1437 | return IK_AMMO; | 1573 | return IK_AMMO; |
| 1438 | } | 1574 | } |
| 1439 | 1575 | ||
| 1576 | if (classify_weapon(model, cls, label, outn) == 1) | ||
| 1577 | return IK_WEAPON; | ||
| 1578 | |||
| 1440 | if (item_icontains(cls, "healthkit") || item_icontains(cls, "healthvial") || | 1579 | if (item_icontains(cls, "healthkit") || item_icontains(cls, "healthvial") || |
| 1441 | item_icontains(cls, "wallhealth")) { | 1580 | item_icontains(cls, "wallhealth")) { |
| 1442 | snprintf(label, outn, "Health"); | 1581 | snprintf(label, outn, "Health"); |
| @@ -1447,8 +1586,7 @@ static int classify_item(const char *model, const char *cls, char *label, | |||
| 1447 | snprintf(label, outn, "Armor"); | 1586 | snprintf(label, outn, "Armor"); |
| 1448 | return IK_ARMOR; | 1587 | return IK_ARMOR; |
| 1449 | } | 1588 | } |
| 1450 | if (item_icontains(cls, "itemcrate") || | 1589 | if (item_icontains(cls, "itemcrate")) { |
| 1451 | item_icontains(cls, "dynamicresupply")) { | ||
| 1452 | snprintf(label, outn, "Supply crate"); | 1590 | snprintf(label, outn, "Supply crate"); |
| 1453 | return IK_CRATE; | 1591 | return IK_CRATE; |
| 1454 | } | 1592 | } |
| @@ -1456,9 +1594,9 @@ static int classify_item(const char *model, const char *cls, char *label, | |||
| 1456 | snprintf(label, outn, "Ammo"); | 1594 | snprintf(label, outn, "Ammo"); |
| 1457 | return IK_AMMO; | 1595 | return IK_AMMO; |
| 1458 | } | 1596 | } |
| 1459 | if (item_icontains(cls, "crate")) { | 1597 | if (item_icontains(cls, "weapon")) { |
| 1460 | snprintf(label, outn, "Crate"); | 1598 | snprintf(label, outn, "Weapon"); |
| 1461 | return IK_CRATE; | 1599 | return IK_WEAPON; |
| 1462 | } | 1600 | } |
| 1463 | if (item_icontains(cls, "item") || item_icontains(cls, "pickup")) { | 1601 | if (item_icontains(cls, "item") || item_icontains(cls, "pickup")) { |
| 1464 | snprintf(label, outn, "Item"); | 1602 | snprintf(label, outn, "Item"); |
| @@ -1472,6 +1610,7 @@ static bool item_kind_enabled(int kind) { | |||
| 1472 | case IK_HEALTH: return g_item_health; | 1610 | case IK_HEALTH: return g_item_health; |
| 1473 | case IK_AMMO: return g_item_ammo; | 1611 | case IK_AMMO: return g_item_ammo; |
| 1474 | case IK_ARMOR: return g_item_armor; | 1612 | case IK_ARMOR: return g_item_armor; |
| 1613 | case IK_WEAPON: return g_item_weapons; | ||
| 1475 | case IK_CRATE: return g_item_crates; | 1614 | case IK_CRATE: return g_item_crates; |
| 1476 | default: return true; | 1615 | default: return true; |
| 1477 | } | 1616 | } |
| @@ -1491,8 +1630,25 @@ static void items_collect(int local_idx) { | |||
| 1491 | int hi = vfunc<Fn>(g_iface.entitylist, 6)(g_iface.entitylist); | 1630 | int hi = vfunc<Fn>(g_iface.entitylist, 6)(g_iface.entitylist); |
| 1492 | if (hi < 0 || hi > 4096) | 1631 | if (hi < 0 || hi > 4096) |
| 1493 | hi = 4096; | 1632 | hi = 4096; |
| 1494 | 1633 | int active_idx = local_active_weapon_idx(local_idx); | |
| 1634 | static bool cats_logged = false; | ||
| 1635 | if (!cats_logged) { | ||
| 1636 | cats_logged = true; | ||
| 1637 | logf("items: categories health=%d ammo=%d armor=%d weapons=%d " | ||
| 1638 | "crates=%d distance=%d max=%d", | ||
| 1639 | (int)g_item_health, (int)g_item_ammo, (int)g_item_armor, | ||
| 1640 | (int)g_item_weapons, (int)g_item_crates, (int)g_item_distance, | ||
| 1641 | g_item_max_dist); | ||
| 1642 | } | ||
| 1495 | static int dbg_lines = 0; | 1643 | static int dbg_lines = 0; |
| 1644 | static int prop_logs = 0; | ||
| 1645 | static int crate_logs = 0; | ||
| 1646 | static bool owner_warned = false; | ||
| 1647 | if (!owner_warned && g_net.owner < 0 && g_net.weapon_owner < 0) { | ||
| 1648 | owner_warned = true; | ||
| 1649 | logf("items: weapon owner netvar unavailable; held weapons may tag"); | ||
| 1650 | } | ||
| 1651 | |||
| 1496 | for (int idx = 1; idx <= hi && g_nitems < 192; idx++) { | 1652 | for (int idx = 1; idx <= hi && g_nitems < 192; idx++) { |
| 1497 | if (idx == local_idx) | 1653 | if (idx == local_idx) |
| 1498 | continue; | 1654 | continue; |
| @@ -1506,7 +1662,14 @@ static void items_collect(int local_idx) { | |||
| 1506 | const char *model = entity_model_name(ent); | 1662 | const char *model = entity_model_name(ent); |
| 1507 | char label[48]; | 1663 | char label[48]; |
| 1508 | int kind = classify_item(model, cls, label, sizeof(label)); | 1664 | int kind = classify_item(model, cls, label, sizeof(label)); |
| 1509 | if (kind < 0) | 1665 | |
| 1666 | bool propish = item_icontains(cls, "crate") || | ||
| 1667 | item_icontains(model, "crate") || | ||
| 1668 | item_icontains(cls, "physicsprop") || | ||
| 1669 | item_icontains(cls, "breakableprop") || | ||
| 1670 | item_icontains(cls, "func_breakable") || | ||
| 1671 | item_icontains(cls, "physbox"); | ||
| 1672 | if (kind < 0 && !propish) | ||
| 1510 | continue; | 1673 | continue; |
| 1511 | 1674 | ||
| 1512 | Vector origin{*(float *)((uintptr_t)ent + g_net.origin), | 1675 | Vector origin{*(float *)((uintptr_t)ent + g_net.origin), |
| @@ -1516,9 +1679,31 @@ static void items_collect(int local_idx) { | |||
| 1516 | float dy = origin.y - g_cam_eye.y; | 1679 | float dy = origin.y - g_cam_eye.y; |
| 1517 | float dz = origin.z - g_cam_eye.z; | 1680 | float dz = origin.z - g_cam_eye.z; |
| 1518 | float dist = sqrtf(dx * dx + dy * dy + dz * dz); | 1681 | float dist = sqrtf(dx * dx + dy * dy + dz * dz); |
| 1682 | |||
| 1683 | if (propish && prop_logs < 24) { | ||
| 1684 | prop_logs++; | ||
| 1685 | logf("item prop: idx=%d cls=%s model=%s kind=%d dist=%.0f " | ||
| 1686 | "origin=(%.0f %.0f %.0f)", | ||
| 1687 | idx, cls ? cls : "?", model ? model : "?", kind, dist, origin.x, | ||
| 1688 | origin.y, origin.z); | ||
| 1689 | } | ||
| 1690 | if (kind < 0) | ||
| 1691 | continue; | ||
| 1692 | |||
| 1693 | if (kind == IK_WEAPON) { | ||
| 1694 | if (idx == active_idx || item_weapon_is_owned(ent)) | ||
| 1695 | continue; | ||
| 1696 | } | ||
| 1519 | if (g_item_max_dist > 0 && dist > (float)g_item_max_dist) | 1697 | if (g_item_max_dist > 0 && dist > (float)g_item_max_dist) |
| 1520 | continue; | 1698 | continue; |
| 1521 | 1699 | ||
| 1700 | if (kind == IK_CRATE && crate_logs < 8) { | ||
| 1701 | crate_logs++; | ||
| 1702 | logf("item crate: idx=%d cls=%s model=%s origin=(%.0f %.0f %.0f)", idx, | ||
| 1703 | cls ? cls : "?", model ? model : "?", origin.x, origin.y, | ||
| 1704 | origin.z); | ||
| 1705 | } | ||
| 1706 | |||
| 1522 | ItemTarget &t = g_item_targets[g_nitems++]; | 1707 | ItemTarget &t = g_item_targets[g_nitems++]; |
| 1523 | t.idx = idx; | 1708 | t.idx = idx; |
| 1524 | t.kind = kind; | 1709 | t.kind = kind; |
| @@ -1540,7 +1725,8 @@ static void items_collect(int local_idx) { | |||
| 1540 | void esp_draw_items() { | 1725 | void esp_draw_items() { |
| 1541 | if (!g_surface || !g_iface.entitylist || g_net.origin < 0) | 1726 | if (!g_surface || !g_iface.entitylist || g_net.origin < 0) |
| 1542 | return; | 1727 | return; |
| 1543 | if (!g_item_health && !g_item_ammo && !g_item_armor && !g_item_crates) | 1728 | if (!g_item_health && !g_item_ammo && !g_item_armor && !g_item_weapons && |
| 1729 | !g_item_crates) | ||
| 1544 | return; | 1730 | return; |
| 1545 | int local = engine_get_local_player(); | 1731 | int local = engine_get_local_player(); |
| 1546 | if (local <= 0) | 1732 | if (local <= 0) |
| @@ -1569,6 +1755,7 @@ void esp_draw_items() { | |||
| 1569 | case IK_HEALTH: r = 90; g = 255; b = 130; break; | 1755 | case IK_HEALTH: r = 90; g = 255; b = 130; break; |
| 1570 | case IK_AMMO: r = 255; g = 210; b = 80; break; | 1756 | case IK_AMMO: r = 255; g = 210; b = 80; break; |
| 1571 | case IK_ARMOR: r = 90; g = 180; b = 255; break; | 1757 | case IK_ARMOR: r = 90; g = 180; b = 255; break; |
| 1758 | case IK_WEAPON: r = 120; g = 230; b = 255; break; | ||
| 1572 | case IK_CRATE: r = 220; g = 150; b = 255; break; | 1759 | case IK_CRATE: r = 220; g = 150; b = 255; break; |
| 1573 | default: break; | 1760 | default: break; |
| 1574 | } | 1761 | } |
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() { | |||
| 350 | add_toggle(esp, esp_names[i], esp_descs[i], &g_esp_opt[i]); | 350 | add_toggle(esp, esp_names[i], esp_descs[i], &g_esp_opt[i]); |
| 351 | 351 | ||
| 352 | int items = add_card(2, &g_item_esp, "Valuable items", | 352 | int items = add_card(2, &g_item_esp, "Valuable items", |
| 353 | "Tag medkits, ammo, armor and supply crates.", true); | 353 | "Tag medkits, ammo, armor, weapons and supply crates.", true); |
| 354 | add_toggle(items, "Medkits / health", | 354 | add_toggle(items, "Medkits / health", |
| 355 | "Tag health kits, health vials and health chargers.", | 355 | "Tag health kits, health vials and health chargers.", |
| 356 | &g_item_health); | 356 | &g_item_health); |
| @@ -358,6 +358,7 @@ static void build_cards() { | |||
| 358 | &g_item_ammo); | 358 | &g_item_ammo); |
| 359 | add_toggle(items, "Armor / suit", "Tag batteries, HEV suits and chargers.", | 359 | add_toggle(items, "Armor / suit", "Tag batteries, HEV suits and chargers.", |
| 360 | &g_item_armor); | 360 | &g_item_armor); |
| 361 | add_toggle(items, "Weapons", "Tag world weapon pickups.", &g_item_weapons); | ||
| 361 | add_toggle(items, "Supply crates", "Tag item and beacon crates.", | 362 | add_toggle(items, "Supply crates", "Tag item and beacon crates.", |
| 362 | &g_item_crates); | 363 | &g_item_crates); |
| 363 | add_toggle(items, "Show distance", "Append the distance in units.", | 364 | add_toggle(items, "Show distance", "Append the distance in units.", |
| @@ -92,6 +92,8 @@ struct NetVars { | |||
| 92 | int modelindex = -1; | 92 | int modelindex = -1; |
| 93 | int absvelocity = -1; // client C_BaseEntity::m_vecAbsVelocity (prediction) | 93 | int absvelocity = -1; // client C_BaseEntity::m_vecAbsVelocity (prediction) |
| 94 | int activeweapon = -1; // client m_hActiveWeapon handle | 94 | int activeweapon = -1; // client m_hActiveWeapon handle |
| 95 | int owner = -1; // client m_hOwnerEntity handle | ||
| 96 | int weapon_owner = -1; // client m_hOwner handle (C_BaseCombatWeapon) | ||
| 95 | // ponytail: mins/maxs may be absent from some HL2 netvar tables; esp.cpp falls back to h=72 | 97 | // ponytail: mins/maxs may be absent from some HL2 netvar tables; esp.cpp falls back to h=72 |
| 96 | bool ok() const { return flags >= 0 && velocity >= 0 && movetype >= 0; } | 98 | bool ok() const { return flags >= 0 && velocity >= 0 && movetype >= 0; } |
| 97 | // ponytail: m_iMaxHealth isn't a netvar in HL2 SP; esp.cpp falls back to 100. | 99 | // 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; | |||
| 212 | extern bool g_item_health; | 214 | extern bool g_item_health; |
| 213 | extern bool g_item_ammo; | 215 | extern bool g_item_ammo; |
| 214 | extern bool g_item_armor; | 216 | extern bool g_item_armor; |
| 217 | extern bool g_item_weapons; | ||
| 215 | extern bool g_item_crates; | 218 | extern bool g_item_crates; |
| 216 | extern bool g_item_distance; | 219 | extern bool g_item_distance; |
| 217 | extern int g_item_max_dist; | 220 | 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 | |||
| 48 | bool g_item_health = true; | 48 | bool g_item_health = true; |
| 49 | bool g_item_ammo = true; | 49 | bool g_item_ammo = true; |
| 50 | bool g_item_armor = true; | 50 | bool g_item_armor = true; |
| 51 | bool g_item_weapons = true; | ||
| 51 | bool g_item_crates = true; | 52 | bool g_item_crates = true; |
| 52 | bool g_item_distance = true; | 53 | bool g_item_distance = true; |
| 53 | int g_item_max_dist = 2500; | 54 | int g_item_max_dist = 2500; |
| @@ -184,10 +185,10 @@ void config_save() { | |||
| 184 | fprintf(f, "[silentaim]\nactive=%d\nbind=%d\nfov=%d\n", g_silentaim.active, | 185 | fprintf(f, "[silentaim]\nactive=%d\nbind=%d\nfov=%d\n", g_silentaim.active, |
| 185 | g_silentaim.bind, g_silent_fov); | 186 | g_silentaim.bind, g_silent_fov); |
| 186 | fprintf(f, "[items]\nactive=%d\nbind=%d\nhealth=%d\nammo=%d\n" | 187 | fprintf(f, "[items]\nactive=%d\nbind=%d\nhealth=%d\nammo=%d\n" |
| 187 | "armor=%d\ncrates=%d\ndistance=%d\nmax_dist=%d\n", | 188 | "armor=%d\nweapons=%d\ncrates=%d\ndistance=%d\nmax_dist=%d\n", |
| 188 | g_item_esp.active, g_item_esp.bind, (int)g_item_health, | 189 | g_item_esp.active, g_item_esp.bind, (int)g_item_health, |
| 189 | (int)g_item_ammo, (int)g_item_armor, (int)g_item_crates, | 190 | (int)g_item_ammo, (int)g_item_armor, (int)g_item_weapons, |
| 190 | (int)g_item_distance, g_item_max_dist); | 191 | (int)g_item_crates, (int)g_item_distance, g_item_max_dist); |
| 191 | fprintf(f, "[esp]\nactive=%d\nbind=%d\nneutral=%d\nhealthbar=%d\n" | 192 | fprintf(f, "[esp]\nactive=%d\nbind=%d\nneutral=%d\nhealthbar=%d\n" |
| 192 | "healthnum=%d\nname=%d\nskeleton=%d\nbox=%d\nspeedgraph=%d\n", | 193 | "healthnum=%d\nname=%d\nskeleton=%d\nbox=%d\nspeedgraph=%d\n", |
| 193 | g_esp.active, g_esp.bind, g_esp_opt[ESP_OPT_SHOW_NEUTRAL], | 194 | g_esp.active, g_esp.bind, g_esp_opt[ESP_OPT_SHOW_NEUTRAL], |
| @@ -271,6 +272,8 @@ static bool config_load() { | |||
| 271 | g_item_ammo = atoi(val) != 0; | 272 | g_item_ammo = atoi(val) != 0; |
| 272 | else if (strcmp(key, "armor") == 0) | 273 | else if (strcmp(key, "armor") == 0) |
| 273 | g_item_armor = atoi(val) != 0; | 274 | g_item_armor = atoi(val) != 0; |
| 275 | else if (strcmp(key, "weapons") == 0) | ||
| 276 | g_item_weapons = atoi(val) != 0; | ||
| 274 | else if (strcmp(key, "crates") == 0) | 277 | else if (strcmp(key, "crates") == 0) |
| 275 | g_item_crates = atoi(val) != 0; | 278 | g_item_crates = atoi(val) != 0; |
| 276 | else if (strcmp(key, "distance") == 0) | 279 | else if (strcmp(key, "distance") == 0) |
| @@ -372,6 +375,7 @@ void config_reset_defaults() { | |||
| 372 | g_item_health = true; | 375 | g_item_health = true; |
| 373 | g_item_ammo = true; | 376 | g_item_ammo = true; |
| 374 | g_item_armor = true; | 377 | g_item_armor = true; |
| 378 | g_item_weapons = true; | ||
| 375 | g_item_crates = true; | 379 | g_item_crates = true; |
| 376 | g_item_distance = true; | 380 | g_item_distance = true; |
| 377 | g_item_max_dist = 2500; | 381 | 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) { | |||
| 92 | } else if (strcmp(pname2, "m_hActiveWeapon") == 0 && | 92 | } else if (strcmp(pname2, "m_hActiveWeapon") == 0 && |
| 93 | g_net.activeweapon < 0) { | 93 | g_net.activeweapon < 0) { |
| 94 | g_net.activeweapon = (int)(base_off + (uint32_t)poff); | 94 | g_net.activeweapon = (int)(base_off + (uint32_t)poff); |
| 95 | } else if (strcmp(pname2, "m_hOwnerEntity") == 0 && g_net.owner < 0) { | ||
| 96 | g_net.owner = (int)(base_off + (uint32_t)poff); | ||
| 97 | } else if (strcmp(pname2, "m_hOwner") == 0 && g_net.weapon_owner < 0) { | ||
| 98 | g_net.weapon_owner = (int)(base_off + (uint32_t)poff); | ||
| 95 | } else if (strcmp(pname2, "m_iTeamNum") == 0 && g_net.teamnum < 0) { | 99 | } else if (strcmp(pname2, "m_iTeamNum") == 0 && g_net.teamnum < 0) { |
| 96 | g_net.teamnum = (int)(base_off + (uint32_t)poff); | 100 | g_net.teamnum = (int)(base_off + (uint32_t)poff); |
| 97 | } else if (strcmp(pname2, "m_vecOrigin") == 0 && g_net.origin < 0) { | 101 | } else if (strcmp(pname2, "m_vecOrigin") == 0 && g_net.origin < 0) { |
| @@ -272,7 +276,8 @@ void resolve_netvars() { | |||
| 272 | logf("m_vecAbsVelocity unresolved; jumpbug assist disabled"); | 276 | logf("m_vecAbsVelocity unresolved; jumpbug assist disabled"); |
| 273 | logf("netvars: flags=%d velocity=%d movetype=%d health=%d maxhealth=%d " | 277 | logf("netvars: flags=%d velocity=%d movetype=%d health=%d maxhealth=%d " |
| 274 | "teamnum=%d origin=%d viewoffset=%d mins=%d maxs=%d lifeste=%d " | 278 | "teamnum=%d origin=%d viewoffset=%d mins=%d maxs=%d lifeste=%d " |
| 275 | "modelindex=%d absvelocity=%d activeweapon=%d", | 279 | "modelindex=%d absvelocity=%d activeweapon=%d owner=%d " |
| 280 | "weapon_owner=%d", | ||
| 276 | g_net.flags, g_net.velocity, g_net.movetype, g_net.health, | 281 | g_net.flags, g_net.velocity, g_net.movetype, g_net.health, |
| 277 | g_net.maxhealth, g_net.teamnum, g_net.origin, g_net.viewoffset, | 282 | g_net.maxhealth, g_net.teamnum, g_net.origin, g_net.viewoffset, |
| 278 | g_net.mins, g_net.maxs, g_net.lifestate, g_net.modelindex, | 283 | g_net.mins, g_net.maxs, g_net.lifestate, g_net.modelindex, |
