summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/bones.cpp27
1 files changed, 23 insertions, 4 deletions
diff --git a/src/bones.cpp b/src/bones.cpp
index 12c7406..1940449 100644
--- a/src/bones.cpp
+++ b/src/bones.cpp
@@ -168,6 +168,18 @@ static bool contains_ci(const char *hay, const char *needle) {
168 return false; 168 return false;
169} 169}
170 170
171// Models without a humanoid head: headcrabs use teeth/front bones, others may
172// expose only eye/jaw/mouth names. The fallback is the first hitbox center.
173static bool head_like_name(const char *n) {
174 static const char *patterns[] = {"head", "teeth", "mouth", "jaw",
175 "face", "eye", "front", "snout",
176 "beak", nullptr};
177 for (int i = 0; patterns[i]; i++)
178 if (contains_ci(n, patterns[i]))
179 return true;
180 return false;
181}
182
171static bool hitbox_world(const BoneEntry *e, const Hitbox &b, Vector &out) { 183static bool hitbox_world(const BoneEntry *e, const Hitbox &b, Vector &out) {
172 if (b.bone < 0 || b.bone >= e->count) 184 if (b.bone < 0 || b.bone >= e->count)
173 return false; 185 return false;
@@ -423,8 +435,8 @@ bool hitbox_center(int idx, const char *name, Vector &out) {
423} 435}
424 436
425// Head position with fallbacks, because several HL2 models have no hitbox 437// Head position with fallbacks, because several HL2 models have no hitbox
426// names: exact "head" hitbox -> hitbox whose bone name contains "head" -> 438// names: exact "head" hitbox -> HITGROUP_HEAD hitbox -> head-like bone name
427// head bone origin. Dumps the hitbox set once for diagnosis. 439// (teeth/front/jaw/eye/...) -> first hitbox center. Dumps the hitbox set once.
428bool head_position(int idx, Vector &out) { 440bool head_position(int idx, Vector &out) {
429 BoneEntry *e = find_entry(idx); 441 BoneEntry *e = find_entry(idx);
430 if (!e || !e->valid || !e->hdr) 442 if (!e || !e->valid || !e->hdr)
@@ -449,15 +461,22 @@ bool head_position(int idx, Vector &out) {
449 if (boxes[i].bone < 0 || boxes[i].bone >= e->count) 461 if (boxes[i].bone < 0 || boxes[i].bone >= e->count)
450 continue; 462 continue;
451 const char *bn = bone_name_raw(e->hdr, boxes[i].bone); 463 const char *bn = bone_name_raw(e->hdr, boxes[i].bone);
452 if (bn && contains_ci(bn, "head")) 464 if (bn && head_like_name(bn))
453 return hitbox_world(e, boxes[i], out); 465 return hitbox_world(e, boxes[i], out);
454 } 466 }
455 } 467 }
456 for (int i = 0; i < e->count; i++) { 468 for (int i = 0; i < e->count; i++) {
457 const char *bn = bone_name_raw(e->hdr, i); 469 const char *bn = bone_name_raw(e->hdr, i);
458 if (bn && contains_ci(bn, "head")) 470 if (bn && head_like_name(bn))
459 return bone_pos(idx, i, out); 471 return bone_pos(idx, i, out);
460 } 472 }
473 // Last resort for non-humanoid models with unnamed heads/hitboxes: the
474 // first hitbox is the body (still a valid aim point).
475 if (s && s->numhitboxes > 0) {
476 const Hitbox *boxes = hitboxes(s);
477 if (hitbox_world(e, boxes[0], out))
478 return true;
479 }
461 return false; 480 return false;
462} 481}
463 482