From 5ee8eb4a2b96b4a9916f85ddec5b43ae24ca93d1 Mon Sep 17 00:00:00 2001 From: salmonik Date: Tue, 3 Nov 2026 23:36:58 +0100 Subject: AI: treat scripted NPCs as neutral; Lamarr neutral Read the server m_NPCState and mark NPC_STATE_SCRIPT actors neutral so cutscene NPCs and scripted pets are not aimbot targets and draw in the neutral colour. Add Lamarr (Kleiner's pet headcrab) to the neutral model list as requested. Also log state changes in the NPC debug line and resolve/log m_NPCState in the server datamap summary. --- src/esp.cpp | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) (limited to 'src/esp.cpp') diff --git a/src/esp.cpp b/src/esp.cpp index 405dea7..50d7b5d 100644 --- a/src/esp.cpp +++ b/src/esp.cpp @@ -139,8 +139,10 @@ static bool model_is_friendly(const char *m) { // harmless wildlife that also happens to be CAI_BaseNPC static bool model_is_neutral(const char *m) { + // Lamarr is Kleiner's pet headcrab: scripted and harmless, and the user + // wants her shown neutral rather than hostile. static const char *patterns[] = {"crow", "gull", "pigeon", "seagull", - "bird", nullptr}; + "bird", "Lamarr", nullptr}; if (!m) return false; for (int i = 0; patterns[i]; i++) if (strstr(m, patterns[i])) @@ -155,42 +157,55 @@ static bool model_is_floor_turret(const char *m) { static bool npc_is_friendly(int idx, const char *model, int local_idx, bool &neutral) { int rel = server_npc_relation(idx, local_idx); + int state = server_npc_state(idx); int turret = -1; // Citizen-built and Alyx-hacked turrets never add an m_Relationship row; // their Classify() (CLASS_PLAYER_ALLY) comes from server runtime state. if (model_is_floor_turret(model)) turret = server_npc_turret_flags(idx); - // Debug-only: relation values are the authoritative answer and are the - // first thing to check when a friendly NPC still looks hostile. Logged only - // when an entity's relation changes, so this does not flood the log. + // Debug-only: relation/state changes are the authoritative signals and the + // first thing to check when a friendly NPC still looks hostile. static int rel_cache[2048]; - static bool rel_cache_init = false; - if (!rel_cache_init) { + static int state_cache[2048]; + static bool cache_init = false; + if (!cache_init) { for (int i = 0; i < 2048; i++) - rel_cache[i] = -999; - rel_cache_init = true; + rel_cache[i] = state_cache[i] = -999; + cache_init = true; } - if (idx > 0 && idx < 2048 && rel_cache[idx] != rel) { + if (idx > 0 && idx < 2048 && + (rel_cache[idx] != rel || state_cache[idx] != state)) { rel_cache[idx] = rel; - logd("npc %d (%s): relation=%d followtarget=%d turret=%d local=%d", idx, - model ? model : "?", rel, + state_cache[idx] = state; + logd("npc %d (%s): relation=%d state=%d followtarget=%d turret=%d " + "local=%d", idx, model ? model : "?", rel, state, (int)server_npc_has_follow_target(idx), turret, local_idx); } if (rel == 3) { // D_LI neutral = false; return true; } + if (turret > 0) { // citizen-built / Alyx-hacked + neutral = false; + return true; + } if (rel == 4) { // D_NU neutral = true; return false; } + // NPC_STATE_SCRIPT = 4: cutscene actors and scripted pets are passive even + // when their class relationship says hate (Lamarr, scripted Combine, ...). + if (state == 4) { + neutral = true; + return false; + } if (rel == 1 || rel == 2) { // D_HT / D_FR neutral = false; return false; } - if (turret >= 0) { + if (turret >= 0) { // ordinary floor turret neutral = false; - return turret != 0; + return false; } // Bugbait-controlled antlions follow a target even when their default // relationship was never rewritten. -- cgit v1.2.3