diff options
| author | salmonik <salmonik@tuta.io> | 2026-11-04 12:39:45 +0100 |
|---|---|---|
| committer | salmonik <salmonik@tuta.io> | 2026-11-04 12:39:45 +0100 |
| commit | 1fa3b61163b233f6c8c6b86dfa196fb798a6f094 (patch) | |
| tree | e21fd421dcfc78ef6ec73883442a60cdb6fe6597 /src/esp.cpp | |
| parent | Props: remove studio diagnostics; document the prop tint path (diff) | |
| download | hl2h-1fa3b61163b233f6c8c6b86dfa196fb798a6f094.tar.gz hl2h-1fa3b61163b233f6c8c6b86dfa196fb798a6f094.zip | |
ESP: classify NPC damage immunity/armour/vulnerability
- Resolve server m_takedamage (DAMAGE_NO/DAMAGE_EVENTS_ONLY = immune).
- Classify gunship/strider/dropship as ARMORED (OnTakeDamage only accepts
DMG_BLAST; only the RPG passes), and headcrab/antlion/manhack as VULN
(gravity gun / physics impact).
- ESP draws ARMORED/VULN/IMMUNE tags above the name (new "Damage tags"
toggle, [esp] dmg).
- Aimbot, silent aim, triggerbot and melee skip immune targets and armoured
targets unless the active weapon is the RPG.
Diffstat (limited to 'src/esp.cpp')
| -rw-r--r-- | src/esp.cpp | 69 |
1 files changed, 69 insertions, 0 deletions
diff --git a/src/esp.cpp b/src/esp.cpp index 4a6b513..aa9f645 100644 --- a/src/esp.cpp +++ b/src/esp.cpp | |||
| @@ -23,6 +23,7 @@ struct EspTarget { | |||
| 23 | bool show_health; | 23 | bool show_health; |
| 24 | bool friendly; | 24 | bool friendly; |
| 25 | bool neutral; | 25 | bool neutral; |
| 26 | int damage; // NpcDamageClass | ||
| 26 | char name[48]; | 27 | char name[48]; |
| 27 | }; | 28 | }; |
| 28 | static EspTarget g_targets[128]; | 29 | static EspTarget g_targets[128]; |
| @@ -220,6 +221,48 @@ static bool npc_is_friendly(int idx, const char *model, int local_idx, | |||
| 220 | } | 221 | } |
| 221 | 222 | ||
| 222 | 223 | ||
| 224 | enum NpcDamageClass { | ||
| 225 | NPC_DMG_NORMAL = 0, | ||
| 226 | NPC_DMG_ARMORED, // only blast damage (RPG) hurts it | ||
| 227 | NPC_DMG_VULNERABLE, // gravity gun / physics damage works well | ||
| 228 | NPC_DMG_IMMUNE // m_takedamage says no damage at all | ||
| 229 | }; | ||
| 230 | |||
| 231 | static bool model_has(const char *model, const char *needle) { | ||
| 232 | return model && strcasestr(model, needle) != nullptr; | ||
| 233 | } | ||
| 234 | |||
| 235 | // Server m_takedamage first (authoritative immunity), then the HL2 damage | ||
| 236 | // rules for units whose OnTakeDamage filters specific damage types. | ||
| 237 | static int npc_damage_class(int idx, const char *model) { | ||
| 238 | int td = server_npc_takedamage(idx); | ||
| 239 | if (td == 0 || td == 1) // DAMAGE_NO / DAMAGE_EVENTS_ONLY | ||
| 240 | return NPC_DMG_IMMUNE; | ||
| 241 | if (model_has(model, "gunship") || model_has(model, "combine_strider") || | ||
| 242 | model_has(model, "dropship")) | ||
| 243 | return NPC_DMG_ARMORED; | ||
| 244 | if (model_has(model, "headcrab") || model_has(model, "antlion") || | ||
| 245 | model_has(model, "manhack")) | ||
| 246 | return NPC_DMG_VULNERABLE; | ||
| 247 | return NPC_DMG_NORMAL; | ||
| 248 | } | ||
| 249 | |||
| 250 | static bool active_weapon_contains(void *local_ent, const char *needle); | ||
| 251 | |||
| 252 | // Armoured units need blast damage; the RPG is the only stock weapon that | ||
| 253 | // passes their OnTakeDamage filters. Immune units can never be damaged. | ||
| 254 | static bool npc_can_be_damaged(int idx, const char *model) { | ||
| 255 | int dc = npc_damage_class(idx, model); | ||
| 256 | if (dc == NPC_DMG_IMMUNE) | ||
| 257 | return false; | ||
| 258 | if (dc != NPC_DMG_ARMORED) | ||
| 259 | return true; | ||
| 260 | int local = engine_get_local_player(); | ||
| 261 | void *le = local > 0 ? entitylist_get(local) : nullptr; | ||
| 262 | return active_weapon_contains(le, "rocket_launcher") || | ||
| 263 | active_weapon_contains(le, "rpg"); | ||
| 264 | } | ||
| 265 | |||
| 223 | static void short_name(const char *model, char *out, size_t outn) { | 266 | static void short_name(const char *model, char *out, size_t outn) { |
| 224 | if (!model) { snprintf(out, outn, "NPC"); return; } | 267 | if (!model) { snprintf(out, outn, "NPC"); return; } |
| 225 | const char *base = strrchr(model, '/'); | 268 | const char *base = strrchr(model, '/'); |
| @@ -334,6 +377,7 @@ static void esp_collect(int local_idx) { | |||
| 334 | bool neutral = false; | 377 | bool neutral = false; |
| 335 | t.friendly = npc_is_friendly(idx, model, local_idx, neutral); | 378 | t.friendly = npc_is_friendly(idx, model, local_idx, neutral); |
| 336 | t.neutral = neutral; | 379 | t.neutral = neutral; |
| 380 | t.damage = npc_damage_class(idx, model); | ||
| 337 | short_name(model, t.name, sizeof(t.name)); | 381 | short_name(model, t.name, sizeof(t.name)); |
| 338 | if (npc && !model) snprintf(t.name, sizeof(t.name), "NPC"); | 382 | if (npc && !model) snprintf(t.name, sizeof(t.name), "NPC"); |
| 339 | } | 383 | } |
| @@ -603,6 +647,8 @@ static bool select_aim_target(const QAngle &view, unsigned bone_mask, | |||
| 603 | bool neutral = false; | 647 | bool neutral = false; |
| 604 | if (npc_is_friendly(idx, model, local, neutral) || neutral) | 648 | if (npc_is_friendly(idx, model, local, neutral) || neutral) |
| 605 | return false; | 649 | return false; |
| 650 | if (!npc_can_be_damaged(idx, model)) | ||
| 651 | return false; | ||
| 606 | if (!bones_update(idx, ent)) | 652 | if (!bones_update(idx, ent)) |
| 607 | return false; | 653 | return false; |
| 608 | Vector pts[32]; | 654 | Vector pts[32]; |
| @@ -682,6 +728,8 @@ static bool select_aim_target(const QAngle &view, unsigned bone_mask, | |||
| 682 | bool neutral = false; | 728 | bool neutral = false; |
| 683 | if (npc_is_friendly(idx, model, local, neutral) || neutral) | 729 | if (npc_is_friendly(idx, model, local, neutral) || neutral) |
| 684 | continue; | 730 | continue; |
| 731 | if (!npc_can_be_damaged(idx, model)) | ||
| 732 | continue; | ||
| 685 | 733 | ||
| 686 | float maxz = g_net.maxs >= 0 ? *(float *)(e + g_net.maxs + 8) : 72.0f; | 734 | float maxz = g_net.maxs >= 0 ? *(float *)(e + g_net.maxs + 8) : 72.0f; |
| 687 | if (!(maxz > 4.0f && maxz < 512.0f)) | 735 | if (!(maxz > 4.0f && maxz < 512.0f)) |
| @@ -1063,6 +1111,8 @@ bool melee_aimbot_apply(CUserCmd *cmd) { | |||
| 1063 | bool neutral = false; | 1111 | bool neutral = false; |
| 1064 | if (npc_is_friendly(idx, model, local, neutral) || neutral) | 1112 | if (npc_is_friendly(idx, model, local, neutral) || neutral) |
| 1065 | continue; | 1113 | continue; |
| 1114 | if (!npc_can_be_damaged(idx, model)) | ||
| 1115 | continue; | ||
| 1066 | 1116 | ||
| 1067 | Vector origin{*(float *)(e + g_net.origin), | 1117 | Vector origin{*(float *)(e + g_net.origin), |
| 1068 | *(float *)(e + g_net.origin + 4), | 1118 | *(float *)(e + g_net.origin + 4), |
| @@ -1394,6 +1444,8 @@ bool triggerbot_apply(CUserCmd *cmd) { | |||
| 1394 | bool neutral = false; | 1444 | bool neutral = false; |
| 1395 | if (npc_is_friendly(idx, model, local, neutral) || neutral) | 1445 | if (npc_is_friendly(idx, model, local, neutral) || neutral) |
| 1396 | return false; | 1446 | return false; |
| 1447 | if (!npc_can_be_damaged(idx, model)) | ||
| 1448 | return false; | ||
| 1397 | cmd->buttons |= IN_ATTACK; | 1449 | cmd->buttons |= IN_ATTACK; |
| 1398 | static int logs = 0; | 1450 | static int logs = 0; |
| 1399 | if (logs < 5) { | 1451 | if (logs < 5) { |
| @@ -1517,6 +1569,23 @@ void esp_draw() { | |||
| 1517 | } | 1569 | } |
| 1518 | if (esp_option(ESP_OPT_SHOW_NAME)) | 1570 | if (esp_option(ESP_OPT_SHOW_NAME)) |
| 1519 | draw_text_col(left, top - 14, t.name, 255, 255, 255); | 1571 | draw_text_col(left, top - 14, t.name, 255, 255, 255); |
| 1572 | if (esp_option(ESP_OPT_DAMAGE_TAGS)) { | ||
| 1573 | const char *tag = nullptr; | ||
| 1574 | int tr = 255, tg = 255, tb = 255; | ||
| 1575 | if (t.damage == NPC_DMG_ARMORED) { | ||
| 1576 | tag = "ARMORED"; | ||
| 1577 | tr = 255; tg = 170; tb = 60; | ||
| 1578 | } else if (t.damage == NPC_DMG_VULNERABLE) { | ||
| 1579 | tag = "VULN"; | ||
| 1580 | tr = 90; tg = 220; tb = 255; | ||
| 1581 | } else if (t.damage == NPC_DMG_IMMUNE) { | ||
| 1582 | tag = "IMMUNE"; | ||
| 1583 | tr = 180; tg = 180; tb = 180; | ||
| 1584 | } | ||
| 1585 | if (tag) | ||
| 1586 | draw_text_col(left, top - (esp_option(ESP_OPT_SHOW_NAME) ? 28 : 14), | ||
| 1587 | tag, tr, tg, tb); | ||
| 1588 | } | ||
| 1520 | } | 1589 | } |
| 1521 | 1590 | ||
| 1522 | // Groundwork probe: HL2H_BONE_DEBUG=1 draws a white marker at each NPC's | 1591 | // Groundwork probe: HL2H_BONE_DEBUG=1 draws a white marker at each NPC's |
