summaryrefslogtreecommitdiffstats
path: root/src/esp.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/esp.cpp')
-rw-r--r--src/esp.cpp318
1 files changed, 253 insertions, 65 deletions
diff --git a/src/esp.cpp b/src/esp.cpp
index ca7bae0..d10a2cf 100644
--- a/src/esp.cpp
+++ b/src/esp.cpp
@@ -1132,71 +1132,6 @@ static void spread_compute() {
1132 g_spread_ready = true; 1132 g_spread_ready = true;
1133} 1133}
1134 1134
1135static bool icontains(const char *hay, const char *needle) {
1136 if (!hay || !needle || !*needle)
1137 return false;
1138 size_t n = strlen(needle);
1139 for (const char *p = hay; *p; p++)
1140 if (strncasecmp(p, needle, n) == 0)
1141 return true;
1142 return false;
1143}
1144
1145bool entity_is_physics_prop(int idx) {
1146 if (idx <= 0 || idx > 4096)
1147 return false;
1148 void *ent = entitylist_get(idx);
1149 if (!ent)
1150 return false;
1151 int mt = -1;
1152 // The reliable signal: physics objects use MOVETYPE_VPHYSICS. The client
1153 // class is CPhysicsProp / CPhysBox, not the server's prop_physics name.
1154 if (g_net.movetype >= 0) {
1155 mt = *(int *)((uintptr_t)ent + g_net.movetype);
1156 if (mt == MOVETYPE_VPHYSICS)
1157 return true;
1158 }
1159 void *net = entity_networkable(idx);
1160 const char *cls = net ? entity_class_name(net, nullptr) : nullptr;
1161 const char *model = entity_model_name(ent);
1162 bool is_prop = icontains(cls, "physicsprop") || icontains(cls, "physbox") ||
1163 icontains(cls, "prop_physics") ||
1164 icontains(model, "props_junk") ||
1165 icontains(model, "props_physics") ||
1166 icontains(model, "props_c17") ||
1167 icontains(model, "props_interiors") ||
1168 icontains(model, "props_lab");
1169 if (!is_prop) {
1170 static int miss_logs = 0;
1171 if (miss_logs < 6) {
1172 miss_logs++;
1173 logf("stucklaunch prop? idx=%d movetype=%d cls=%s model=%s", idx, mt,
1174 cls ? cls : "?", model ? model : "?");
1175 }
1176 }
1177 return is_prop;
1178}
1179
1180bool active_weapon_is_gravity_gun(void *local_ent) {
1181 if (!local_ent || g_net.activeweapon < 0)
1182 return false;
1183 uint32_t h = *(uint32_t *)((uintptr_t)local_ent + g_net.activeweapon);
1184 if (h == 0xFFFFFFFFu)
1185 return false;
1186 int idx = (int)(h & 0xFFFF);
1187 if (idx <= 0 || idx > 4096)
1188 return false;
1189 void *wep = entitylist_get(idx);
1190 void *net = entity_networkable(idx);
1191 const char *cls = net ? entity_class_name(net, nullptr) : nullptr;
1192 const char *model = wep ? entity_model_name(wep) : nullptr;
1193 if (cls && strstr(cls, "physcannon"))
1194 return true;
1195 if (model && (strstr(model, "physcannon") || strstr(model, "w_physics")))
1196 return true;
1197 return false;
1198}
1199
1200static bool active_weapon_is_shotgun(void *local_ent) { 1135static bool active_weapon_is_shotgun(void *local_ent) {
1201 if (!local_ent || g_net.activeweapon < 0) 1136 if (!local_ent || g_net.activeweapon < 0)
1202 return false; 1137 return false;
@@ -1402,3 +1337,256 @@ void esp_draw() {
1402 1337
1403 draw_speed_graph(); 1338 draw_speed_graph();
1404} 1339}
1340
1341
1342// ---------------------------------------------------------------------------
1343// Valuable item tags
1344// ---------------------------------------------------------------------------
1345
1346enum ItemKind { IK_HEALTH, IK_AMMO, IK_ARMOR, IK_CRATE, IK_OTHER };
1347
1348struct ItemTarget {
1349 int idx;
1350 int kind;
1351 Vector origin;
1352 char label[48];
1353};
1354
1355static ItemTarget g_item_targets[192];
1356static int g_nitems = 0;
1357static bool g_items_logged = false;
1358
1359static bool item_icontains(const char *hay, const char *needle) {
1360 if (!hay || !needle || !*needle)
1361 return false;
1362 size_t n = strlen(needle);
1363 for (const char *p = hay; *p; p++)
1364 if (strncasecmp(p, needle, n) == 0)
1365 return true;
1366 return false;
1367}
1368
1369static int classify_item(const char *model, const char *cls, char *label,
1370 size_t outn) {
1371 if (!model && !cls)
1372 return -1;
1373
1374 if (item_icontains(model, "healthkit")) {
1375 snprintf(label, outn, "Medkit");
1376 return IK_HEALTH;
1377 }
1378 if (item_icontains(model, "healthvial")) {
1379 snprintf(label, outn, "Health vial");
1380 return IK_HEALTH;
1381 }
1382 if (item_icontains(model, "health_charger")) {
1383 snprintf(label, outn, "Health charger");
1384 return IK_HEALTH;
1385 }
1386 if (item_icontains(model, "battery")) {
1387 snprintf(label, outn, "Battery");
1388 return IK_ARMOR;
1389 }
1390 if (item_icontains(model, "hevsuit")) {
1391 snprintf(label, outn, "HEV suit");
1392 return IK_ARMOR;
1393 }
1394 if (item_icontains(model, "w_longjump")) {
1395 snprintf(label, outn, "Long jump");
1396 return IK_ARMOR;
1397 }
1398 if (item_icontains(model, "suit_charger")) {
1399 snprintf(label, outn, "Suit charger");
1400 return IK_ARMOR;
1401 }
1402 if (item_icontains(model, "item_item_crate") ||
1403 item_icontains(model, "item_beacon_crate")) {
1404 snprintf(label, outn, "Supply crate");
1405 return IK_CRATE;
1406 }
1407 if (item_icontains(model, "ammocrate")) {
1408 snprintf(label, outn, "Ammo crate");
1409 return IK_AMMO;
1410 }
1411 if (item_icontains(model, "w_missile_closed")) {
1412 snprintf(label, outn, "RPG ammo");
1413 return IK_AMMO;
1414 }
1415 if (item_icontains(model, "models/items/")) {
1416 if (item_icontains(model, "crossbowrounds"))
1417 snprintf(label, outn, "Crossbow bolts");
1418 else if (item_icontains(model, "ar2_grenade"))
1419 snprintf(label, outn, "AR2 grenade");
1420 else if (item_icontains(model, "357ammo"))
1421 snprintf(label, outn, "357 ammo");
1422 else if (item_icontains(model, "boxbuckshot"))
1423 snprintf(label, outn, "Buckshot");
1424 else if (item_icontains(model, "boxsniperrounds"))
1425 snprintf(label, outn, "Sniper ammo");
1426 else
1427 snprintf(label, outn, "Ammo");
1428 return IK_AMMO;
1429 }
1430
1431 if (item_icontains(cls, "healthkit") || item_icontains(cls, "healthvial") ||
1432 item_icontains(cls, "wallhealth")) {
1433 snprintf(label, outn, "Health");
1434 return IK_HEALTH;
1435 }
1436 if (item_icontains(cls, "battery") || item_icontains(cls, "itemsuit") ||
1437 item_icontains(cls, "recharge")) {
1438 snprintf(label, outn, "Armor");
1439 return IK_ARMOR;
1440 }
1441 if (item_icontains(cls, "itemcrate") ||
1442 item_icontains(cls, "dynamicresupply")) {
1443 snprintf(label, outn, "Supply crate");
1444 return IK_CRATE;
1445 }
1446 if (item_icontains(cls, "item_ammo") || item_icontains(cls, "itemammo")) {
1447 snprintf(label, outn, "Ammo");
1448 return IK_AMMO;
1449 }
1450 if (item_icontains(cls, "item") || item_icontains(cls, "pickup")) {
1451 snprintf(label, outn, "Item");
1452 return IK_OTHER;
1453 }
1454 return -1;
1455}
1456
1457static bool item_kind_enabled(int kind) {
1458 switch (kind) {
1459 case IK_HEALTH: return g_item_health;
1460 case IK_AMMO: return g_item_ammo;
1461 case IK_ARMOR: return g_item_armor;
1462 case IK_CRATE: return g_item_crates;
1463 default: return true;
1464 }
1465}
1466
1467static void items_collect(int local_idx) {
1468 static double last_scan = 0.0;
1469 double now = esp_now();
1470 if (now - last_scan < 0.10)
1471 return;
1472 last_scan = now;
1473 g_nitems = 0;
1474 if (!g_iface.entitylist || g_net.origin < 0 || g_net.modelindex < 0)
1475 return;
1476
1477 using Fn = int (*)(void *);
1478 int hi = vfunc<Fn>(g_iface.entitylist, 6)(g_iface.entitylist);
1479 if (hi < 0 || hi > 4096)
1480 hi = 4096;
1481
1482 static int dbg_lines = 0;
1483 for (int idx = 1; idx <= hi && g_nitems < 192; idx++) {
1484 if (idx == local_idx)
1485 continue;
1486 void *ent = entitylist_get(idx);
1487 if (!ent)
1488 continue;
1489 void *net = entity_networkable(idx);
1490 if (!net)
1491 continue;
1492 const char *cls = entity_class_name(net, nullptr);
1493 const char *model = entity_model_name(ent);
1494 char label[48];
1495 int kind = classify_item(model, cls, label, sizeof(label));
1496 if (kind < 0)
1497 continue;
1498
1499 Vector origin{*(float *)((uintptr_t)ent + g_net.origin),
1500 *(float *)((uintptr_t)ent + g_net.origin + 4),
1501 *(float *)((uintptr_t)ent + g_net.origin + 8)};
1502 float dx = origin.x - g_cam_eye.x;
1503 float dy = origin.y - g_cam_eye.y;
1504 float dz = origin.z - g_cam_eye.z;
1505 float dist = sqrtf(dx * dx + dy * dy + dz * dz);
1506 if (g_item_max_dist > 0 && dist > (float)g_item_max_dist)
1507 continue;
1508
1509 ItemTarget &t = g_item_targets[g_nitems++];
1510 t.idx = idx;
1511 t.kind = kind;
1512 t.origin = origin;
1513 snprintf(t.label, sizeof(t.label), "%s", label);
1514 if (dbg_lines < 8) {
1515 dbg_lines++;
1516 logf("item: idx=%d kind=%d cls=%s model=%s", idx, kind,
1517 cls ? cls : "?", model ? model : "?");
1518 }
1519 }
1520
1521 if (!g_items_logged) {
1522 g_items_logged = true;
1523 logf("items: tagging %d valuable entities", g_nitems);
1524 }
1525}
1526
1527void esp_draw_items() {
1528 if (!g_surface || !g_iface.entitylist || g_net.origin < 0)
1529 return;
1530 if (!g_item_health && !g_item_ammo && !g_item_armor && !g_item_crates)
1531 return;
1532 int local = engine_get_local_player();
1533 if (local <= 0)
1534 return;
1535 if (!g_scr_w) {
1536 using Fn = void (*)(void *, int &, int &);
1537 vfunc<Fn>(g_surface, SURF_GetScreenSize)(g_surface, g_scr_w, g_scr_h);
1538 if (!g_scr_w)
1539 return;
1540 }
1541 items_collect(local);
1542
1543 for (int i = 0; i < g_nitems; i++) {
1544 const ItemTarget &t = g_item_targets[i];
1545 if (!item_kind_enabled(t.kind))
1546 continue;
1547 float sx, sy;
1548 if (!world_to_screen(t.origin, sx, sy))
1549 continue;
1550 if (sx < -40 || sx > (float)g_scr_w + 40 || sy < -20 ||
1551 sy > (float)g_scr_h + 20)
1552 continue;
1553
1554 int r = 230, g = 230, b = 230;
1555 switch (t.kind) {
1556 case IK_HEALTH: r = 90; g = 255; b = 130; break;
1557 case IK_AMMO: r = 255; g = 210; b = 80; break;
1558 case IK_ARMOR: r = 90; g = 180; b = 255; break;
1559 case IK_CRATE: r = 220; g = 150; b = 255; break;
1560 default: break;
1561 }
1562 int x = (int)sx, y = (int)sy;
1563 surf_color(r, g, b, 255);
1564 surf_rect(0, x - 4, y - 4, x + 4, y + 4);
1565 surf_line(x - 8, y, x - 5, y);
1566 surf_line(x + 5, y, x + 8, y);
1567 surf_line(x, y - 8, x, y - 5);
1568 surf_line(x, y + 5, x, y + 8);
1569
1570 float dx = t.origin.x - g_cam_eye.x;
1571 float dy = t.origin.y - g_cam_eye.y;
1572 float dz = t.origin.z - g_cam_eye.z;
1573 int dist = (int)sqrtf(dx * dx + dy * dy + dz * dz);
1574 char text[96];
1575 if (g_item_distance)
1576 snprintf(text, sizeof(text), "%s %du", t.label, dist);
1577 else
1578 snprintf(text, sizeof(text), "%s", t.label);
1579 int tw = (int)strlen(text) * 9;
1580 int tx = x + 10;
1581 if (tx + tw > g_scr_w - 4)
1582 tx = x - 10 - tw;
1583 if (tx < 4)
1584 tx = 4;
1585 int ty = y - 7;
1586 if (ty < 0)
1587 ty = 0;
1588 if (ty + 16 > g_scr_h)
1589 ty = g_scr_h - 16;
1590 draw_text_col(tx, ty, text, r, g, b);
1591 }
1592}