diff options
Diffstat (limited to '')
| -rw-r--r-- | src/esp.cpp | 69 | ||||
| -rw-r--r-- | src/gui.cpp | 5 | ||||
| -rw-r--r-- | src/hl2h.h | 4 | ||||
| -rw-r--r-- | src/menu.cpp | 11 | ||||
| -rw-r--r-- | src/server.cpp | 19 |
5 files changed, 100 insertions, 8 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 |
diff --git a/src/gui.cpp b/src/gui.cpp index ebc1f62..b5fbc1d 100644 --- a/src/gui.cpp +++ b/src/gui.cpp | |||
| @@ -359,12 +359,13 @@ static void build_cards() { | |||
| 359 | static const char *esp_names[ESP_OPT_COUNT] = { | 359 | static const char *esp_names[ESP_OPT_COUNT] = { |
| 360 | "Show neutral", "Show healthbar", "Show healthnum", | 360 | "Show neutral", "Show healthbar", "Show healthnum", |
| 361 | "Show name", "Show skeleton", "Show box", | 361 | "Show name", "Show skeleton", "Show box", |
| 362 | "Show speed graph"}; | 362 | "Show speed graph", "Damage tags"}; |
| 363 | static const char *esp_descs[ESP_OPT_COUNT] = { | 363 | static const char *esp_descs[ESP_OPT_COUNT] = { |
| 364 | "Include neutral wildlife.", "Draw a health bar.", | 364 | "Include neutral wildlife.", "Draw a health bar.", |
| 365 | "Draw the numeric health value.", "Draw the model name.", | 365 | "Draw the numeric health value.", "Draw the model name.", |
| 366 | "Draw the animated bone links.", "Draw a bounding box.", | 366 | "Draw the animated bone links.", "Draw a bounding box.", |
| 367 | "Draw the lower-center speed graph."}; | 367 | "Draw the lower-center speed graph.", |
| 368 | "Label ARMORED (needs blast/RPG) and VULN (gravity gun) units."}; | ||
| 368 | for (int i = 0; i < ESP_OPT_COUNT; i++) | 369 | for (int i = 0; i < ESP_OPT_COUNT; i++) |
| 369 | add_toggle(esp, esp_names[i], esp_descs[i], &g_esp_opt[i]); | 370 | add_toggle(esp, esp_names[i], esp_descs[i], &g_esp_opt[i]); |
| 370 | 371 | ||
| @@ -192,7 +192,8 @@ enum EspOption { | |||
| 192 | ESP_OPT_SHOW_SKELETON = 4, | 192 | ESP_OPT_SHOW_SKELETON = 4, |
| 193 | ESP_OPT_SHOW_BOX = 5, | 193 | ESP_OPT_SHOW_BOX = 5, |
| 194 | ESP_OPT_SHOW_SPEEDGRAPH = 6, | 194 | ESP_OPT_SHOW_SPEEDGRAPH = 6, |
| 195 | ESP_OPT_COUNT = 7 | 195 | ESP_OPT_DAMAGE_TAGS = 7, |
| 196 | ESP_OPT_COUNT = 8 | ||
| 196 | }; | 197 | }; |
| 197 | enum AimBone { | 198 | enum AimBone { |
| 198 | AIM_BONE_HEAD = 0, | 199 | AIM_BONE_HEAD = 0, |
| @@ -273,6 +274,7 @@ int server_active_weapon(int idx); | |||
| 273 | int server_npc_relation(int idx, int local_idx); | 274 | int server_npc_relation(int idx, int local_idx); |
| 274 | bool server_npc_has_follow_target(int idx); | 275 | bool server_npc_has_follow_target(int idx); |
| 275 | int server_npc_state(int idx); | 276 | int server_npc_state(int idx); |
| 277 | int server_npc_takedamage(int idx); | ||
| 276 | int server_npc_turret_flags(int idx); | 278 | int server_npc_turret_flags(int idx); |
| 277 | int server_model_index(void *base); | 279 | int server_model_index(void *base); |
| 278 | void find_modules(); | 280 | void find_modules(); |
diff --git a/src/menu.cpp b/src/menu.cpp index 0ce6c05..eb1075a 100644 --- a/src/menu.cpp +++ b/src/menu.cpp | |||
| @@ -78,7 +78,8 @@ bool g_aim_fov_circle = true; | |||
| 78 | int g_silent_fov = 60; | 78 | int g_silent_fov = 60; |
| 79 | 79 | ||
| 80 | // ESP sub-options. Defaults match the pre-option behaviour: everything on. | 80 | // ESP sub-options. Defaults match the pre-option behaviour: everything on. |
| 81 | bool g_esp_opt[ESP_OPT_COUNT] = {true, true, true, true, true, true, true}; | 81 | bool g_esp_opt[ESP_OPT_COUNT] = {true, true, true, true, true, true, true, |
| 82 | true}; | ||
| 82 | 83 | ||
| 83 | // --------------------------------------------------------------------------- | 84 | // --------------------------------------------------------------------------- |
| 84 | // config persistence (INI format) | 85 | // config persistence (INI format) |
| @@ -221,12 +222,14 @@ void config_save() { | |||
| 221 | (int)g_item_ammo, (int)g_item_armor, (int)g_item_weapons, | 222 | (int)g_item_ammo, (int)g_item_armor, (int)g_item_weapons, |
| 222 | (int)g_item_crates, (int)g_item_distance, g_item_max_dist); | 223 | (int)g_item_crates, (int)g_item_distance, g_item_max_dist); |
| 223 | fprintf(f, "[esp]\nactive=%d\nbind=%d\nneutral=%d\nhealthbar=%d\n" | 224 | fprintf(f, "[esp]\nactive=%d\nbind=%d\nneutral=%d\nhealthbar=%d\n" |
| 224 | "healthnum=%d\nname=%d\nskeleton=%d\nbox=%d\nspeedgraph=%d\n", | 225 | "healthnum=%d\nname=%d\nskeleton=%d\nbox=%d\nspeedgraph=%d\n" |
| 226 | "dmg=%d\n", | ||
| 225 | g_esp.active, g_esp.bind, g_esp_opt[ESP_OPT_SHOW_NEUTRAL], | 227 | g_esp.active, g_esp.bind, g_esp_opt[ESP_OPT_SHOW_NEUTRAL], |
| 226 | g_esp_opt[ESP_OPT_SHOW_HEALTHBAR], | 228 | g_esp_opt[ESP_OPT_SHOW_HEALTHBAR], |
| 227 | g_esp_opt[ESP_OPT_SHOW_HEALTHNUM], g_esp_opt[ESP_OPT_SHOW_NAME], | 229 | g_esp_opt[ESP_OPT_SHOW_HEALTHNUM], g_esp_opt[ESP_OPT_SHOW_NAME], |
| 228 | g_esp_opt[ESP_OPT_SHOW_SKELETON], g_esp_opt[ESP_OPT_SHOW_BOX], | 230 | g_esp_opt[ESP_OPT_SHOW_SKELETON], g_esp_opt[ESP_OPT_SHOW_BOX], |
| 229 | g_esp_opt[ESP_OPT_SHOW_SPEEDGRAPH]); | 231 | g_esp_opt[ESP_OPT_SHOW_SPEEDGRAPH], |
| 232 | g_esp_opt[ESP_OPT_DAMAGE_TAGS]); | ||
| 230 | gui_config_write(f); | 233 | gui_config_write(f); |
| 231 | bool ok = ferror(f) == 0; | 234 | bool ok = ferror(f) == 0; |
| 232 | if (fflush(f) != 0) | 235 | if (fflush(f) != 0) |
| @@ -427,6 +430,8 @@ static bool config_load() { | |||
| 427 | g_esp_opt[ESP_OPT_SHOW_BOX] = atoi(val) != 0; | 430 | g_esp_opt[ESP_OPT_SHOW_BOX] = atoi(val) != 0; |
| 428 | else if (strcmp(key, "speedgraph") == 0) | 431 | else if (strcmp(key, "speedgraph") == 0) |
| 429 | g_esp_opt[ESP_OPT_SHOW_SPEEDGRAPH] = atoi(val) != 0; | 432 | g_esp_opt[ESP_OPT_SHOW_SPEEDGRAPH] = atoi(val) != 0; |
| 433 | else if (strcmp(key, "dmg") == 0) | ||
| 434 | g_esp_opt[ESP_OPT_DAMAGE_TAGS] = atoi(val) != 0; | ||
| 430 | } | 435 | } |
| 431 | if (strcmp(key, "active") == 0) | 436 | if (strcmp(key, "active") == 0) |
| 432 | cur->active = atoi(val) != 0; | 437 | cur->active = atoi(val) != 0; |
diff --git a/src/server.cpp b/src/server.cpp index c2864c8..e9fd921 100644 --- a/src/server.cpp +++ b/src/server.cpp | |||
| @@ -51,6 +51,7 @@ static int g_srv_followtarget = -1; | |||
| 51 | static int g_srv_spawnflags = -1; | 51 | static int g_srv_spawnflags = -1; |
| 52 | static int g_srv_hacked = -1; | 52 | static int g_srv_hacked = -1; |
| 53 | static int g_srv_npcstate = -1; | 53 | static int g_srv_npcstate = -1; |
| 54 | static int g_srv_takedamage = -1; | ||
| 54 | static bool g_srv_ready = false; | 55 | static bool g_srv_ready = false; |
| 55 | static bool g_srv_checked = false; | 56 | static bool g_srv_checked = false; |
| 56 | 57 | ||
| @@ -141,15 +142,17 @@ bool resolve_server_netvars() { | |||
| 141 | g_srv_spawnflags = datamap_offset("m_spawnflags", FIELD_INTEGER, 0x4); | 142 | g_srv_spawnflags = datamap_offset("m_spawnflags", FIELD_INTEGER, 0x4); |
| 142 | g_srv_hacked = datamap_offset("m_bHackedByAlyx", FIELD_BOOLEAN); | 143 | g_srv_hacked = datamap_offset("m_bHackedByAlyx", FIELD_BOOLEAN); |
| 143 | g_srv_npcstate = datamap_offset("m_NPCState", FIELD_INTEGER); | 144 | g_srv_npcstate = datamap_offset("m_NPCState", FIELD_INTEGER); |
| 145 | g_srv_takedamage = datamap_offset("m_takedamage", FIELD_INTEGER); | ||
| 144 | g_srv_ready = g_srv_health > 0; | 146 | g_srv_ready = g_srv_health > 0; |
| 145 | if (g_srv_ready) { | 147 | if (g_srv_ready) { |
| 146 | logf("server datamap (%s): health=%d maxhealth=%d lifeste=%d " | 148 | logf("server datamap (%s): health=%d maxhealth=%d lifeste=%d " |
| 147 | "modelindex=%d absvel=%d vel=%d activeweapon=%d relationship=%d " | 149 | "modelindex=%d absvel=%d vel=%d activeweapon=%d relationship=%d " |
| 148 | "followtarget=%d spawnflags=%d hacked=%d npcstate=%d", | 150 | "followtarget=%d spawnflags=%d hacked=%d npcstate=%d " |
| 151 | "takedamage=%d", | ||
| 149 | g_server.path.c_str(), g_srv_health, g_srv_maxhealth, g_srv_lifestate, | 152 | g_server.path.c_str(), g_srv_health, g_srv_maxhealth, g_srv_lifestate, |
| 150 | g_srv_modelindex, g_srv_absvelocity, g_srv_velocity, | 153 | g_srv_modelindex, g_srv_absvelocity, g_srv_velocity, |
| 151 | g_srv_activeweapon, g_srv_relationship, g_srv_followtarget, | 154 | g_srv_activeweapon, g_srv_relationship, g_srv_followtarget, |
| 152 | g_srv_spawnflags, g_srv_hacked, g_srv_npcstate); | 155 | g_srv_spawnflags, g_srv_hacked, g_srv_npcstate, g_srv_takedamage); |
| 153 | fail_logged = false; | 156 | fail_logged = false; |
| 154 | } else if (!fail_logged) { | 157 | } else if (!fail_logged) { |
| 155 | fail_logged = true; | 158 | fail_logged = true; |
| @@ -268,6 +271,18 @@ int server_npc_state(int idx) { | |||
| 268 | return *(int *)((uintptr_t)base + g_srv_npcstate); | 271 | return *(int *)((uintptr_t)base + g_srv_npcstate); |
| 269 | } | 272 | } |
| 270 | 273 | ||
| 274 | // TAKEDAMAGE: 0 = DAMAGE_NO, 1 = DAMAGE_EVENTS_ONLY, 2 = DAMAGE_YES, | ||
| 275 | // 3 = DAMAGE_AIM. 0/1 cannot be damaged at all. | ||
| 276 | int server_npc_takedamage(int idx) { | ||
| 277 | if (!g_srv_ready || g_srv_takedamage < 0) | ||
| 278 | return -1; | ||
| 279 | void *base = nullptr; | ||
| 280 | server_entity_health(idx, nullptr, nullptr, &base); | ||
| 281 | if (!base) | ||
| 282 | return -1; | ||
| 283 | return *(int *)((uintptr_t)base + g_srv_takedamage); | ||
| 284 | } | ||
| 285 | |||
| 271 | bool server_npc_has_follow_target(int idx) { | 286 | bool server_npc_has_follow_target(int idx) { |
| 272 | if (!g_srv_ready || g_srv_followtarget < 0) | 287 | if (!g_srv_ready || g_srv_followtarget < 0) |
| 273 | return false; | 288 | return false; |
