From 1e3b1cabaea62ef0e30ce8a478fb8418aa835229 Mon Sep 17 00:00:00 2001 From: salmonik Date: Mon, 26 Oct 2026 16:25:28 +0100 Subject: ESP: derive skeleton core from hitbox bone paths (fixes unnamed ValveBiped bones) --- src/bones.cpp | 63 +++++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 42 insertions(+), 21 deletions(-) (limited to 'src/bones.cpp') diff --git a/src/bones.cpp b/src/bones.cpp index a9c6887..35756c2 100644 --- a/src/bones.cpp +++ b/src/bones.cpp @@ -91,8 +91,10 @@ struct BoneEntry { void *ent; double stamp; bool valid; + bool humanoid; const StudioHdr *hdr; int count; + bool core[MAX_BONES]; Matrix3x4 mats[MAX_BONES]; }; @@ -182,23 +184,41 @@ static bool hitbox_world(const BoneEntry *e, const Hitbox &b, Vector &out) { return true; } -// HL2 humanoids (combine, metrocop, citizen, zombie, ...) use the ValveBiped -// skeleton. Draw only the core chain: pelvis/spine/neck/head, clavicle to -// hand, thigh to foot. Fingers, toes and extra model bones (e.g. the headcrab -// on a zombie) are skipped. -static bool core_bone_name(const char *n) { - if (!n || strncasecmp(n, "ValveBiped.Bip01_", 17) != 0) - return false; - if (contains_ci(n, "Finger") || contains_ci(n, "Toe") || - contains_ci(n, "Headcrab")) - return false; - static const char *tokens[] = {"Pelvis", "Spine", "Neck", "Head1", - "Clavicle", "UpperArm", "Forearm", "Hand", - "Thigh", "Calf", "Foot", nullptr}; - for (int i = 0; tokens[i]; i++) - if (contains_ci(n, tokens[i])) - return true; - return false; +// HL2 humanoids use the ValveBiped skeleton, but many of its bone names are +// stripped (the zombie's spine/legs/left arm are unnamed). Instead of matching +// names, mark the paths from every hitbox bone up to the root: that yields the +// chest/spine, arms and legs, while fingers/toes have no hitboxes and the +// headcrab's HC_* hitbox is skipped explicitly. +static void build_core_mask(BoneEntry *e) { + e->humanoid = false; + for (int i = 0; i < e->count; i++) { + const char *n = bone_name_raw(e->hdr, i); + if (n && strncasecmp(n, "ValveBiped.Bip01_", 17) == 0) { + e->humanoid = true; + break; + } + } + memset(e->core, 0, sizeof(e->core)); + if (!e->humanoid) + return; + const HitboxSet *s = hitbox_set0(e->hdr); + if (!s) + return; + const Hitbox *boxes = hitboxes(s); + for (int i = 0; i < s->numhitboxes; i++) { + int b = boxes[i].bone; + if (b < 0 || b >= e->count) + continue; + const char *bn = bone_name_raw(e->hdr, b); + if (bn && (contains_ci(bn, "Headcrab") || contains_ci(bn, "HC_") || + contains_ci(bn, "Finger") || contains_ci(bn, "Toe"))) + continue; + int cur = b; + for (int guard = 0; cur >= 0 && cur < e->count && guard < 64; guard++) { + e->core[cur] = true; + cur = bone_at(e->hdr, cur)->parent; + } + } } static void dump_hitboxes(const BoneEntry *e) { @@ -297,6 +317,7 @@ bool bones_update(int idx, void *ent) { e->hdr = hdr; e->count = hdr->numbones; + build_core_mask(e); e->valid = true; e->stamp = now; if (g_logged < 4) { @@ -333,18 +354,18 @@ int bone_parent(int idx, int bone) { bool bone_is_skeleton_core(int idx, int bone) { BoneEntry *e = find_entry(idx); - if (!e || !e->valid || bone < 0 || bone >= e->count) + if (!e || !e->valid || !e->humanoid || bone < 0 || bone >= e->count) return false; - return core_bone_name(bone_name_raw(e->hdr, bone)); + return e->core[bone]; } int bone_nearest_core_parent(int idx, int bone) { BoneEntry *e = find_entry(idx); - if (!e || !e->valid || bone < 0 || bone >= e->count) + if (!e || !e->valid || !e->humanoid || bone < 0 || bone >= e->count) return -1; int p = bone_at(e->hdr, bone)->parent; for (int guard = 0; p >= 0 && p < e->count && guard < 64; guard++) { - if (core_bone_name(bone_name_raw(e->hdr, p))) + if (e->core[p]) return p; p = bone_at(e->hdr, p)->parent; } -- cgit v1.2.3