@adatechnology/game-characters-three 0.5.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5221,7 +5221,7 @@ var GameCharactersThree = (() => {
5221
5221
  });
5222
5222
  }
5223
5223
  const neckLen = 0.14 + Math.max(0, P.neck);
5224
- if (!cut.head) add(U, CYL(0.058, 0.068, neckLen, SEG), skinM, 0, 1.63 + Math.max(0, P.neck) / 2, 0);
5224
+ const neck = cut.head ? void 0 : add(U, CYL(0.058, 0.068, neckLen, SEG), skinM, 0, 1.63 + Math.max(0, P.neck) / 2, 0);
5225
5225
  const headG = new THREE.Group();
5226
5226
  headG.position.set(0, 1.64 + P.neck, P.headFwd || 0);
5227
5227
  headG.rotation.x = P.headX;
@@ -5428,6 +5428,7 @@ var GameCharactersThree = (() => {
5428
5428
  if (o.userData.flap) flaps.push(o);
5429
5429
  });
5430
5430
  const rig = { flaps, dangles: arms.filter((a) => a.el.userData.dangle), type: spec.type || "Lento", fig, upper, headG, arms, legs, P, ph: spec.phase ?? options.random() * 6, dropBase: fig.position.y, leanBase: P.lean, hxBase: P.headX };
5431
+ if (neck) Object.defineProperty(rig, "neck", { value: neck });
5431
5432
  if (spec.lying) {
5432
5433
  root.rotation.x = -PI / 2;
5433
5434
  root.position.set(0, 0.16 * P.s, 0.9 * P.s);
@@ -8165,7 +8166,8 @@ var GameCharactersThree = (() => {
8165
8166
  const makeKit = () => options.createKit ? options.createKit({ penetration: settings.penetration, deathRule: settings.deathRule }) : createCharacters({ ...options.packs ? { packs: options.packs } : {}, penetration: settings.penetration, damage: { deathRule: settings.deathRule } });
8166
8167
  let kit = makeKit();
8167
8168
  let undead = options.createUndead ? options.createUndead(kit) : null;
8168
- const random = createRng(options.seed ?? 1);
8169
+ let stream = createRng(options.seed ?? 1);
8170
+ const random = () => stream();
8169
8171
  let seedCounter = 100;
8170
8172
  let host = null;
8171
8173
  let three = null;
@@ -8545,6 +8547,14 @@ var GameCharactersThree = (() => {
8545
8547
  wounds = createWounds(lent.THREE, { random, scene: scene2, groundAt: (x, z) => lent.groundAt(x, z), ...bridge ? { bridge } : {}, ...sound ? { sound } : {} });
8546
8548
  for (const entry of entries.values()) trackEntry(entry);
8547
8549
  const stopChange = lent.bench?.onChange(onChange);
8550
+ const stopReset = lent.onReset?.(() => {
8551
+ stream = createRng(options.seed ?? 1);
8552
+ seedCounter = 100;
8553
+ time = 0;
8554
+ selected = null;
8555
+ lastCrossed = 0;
8556
+ lastEnergy = 1;
8557
+ });
8548
8558
  const stopEvents = lent.on((event) => {
8549
8559
  if (event.type !== "explosion") return;
8550
8560
  for (const entry of entries.values()) {
@@ -8569,6 +8579,7 @@ var GameCharactersThree = (() => {
8569
8579
  });
8570
8580
  return () => {
8571
8581
  stopChange?.();
8582
+ stopReset?.();
8572
8583
  stopEvents();
8573
8584
  stopFrame();
8574
8585
  clearOverlays();
@@ -8637,12 +8648,272 @@ var GameCharactersThree = (() => {
8637
8648
  shirtColor: "3d5a73"
8638
8649
  };
8639
8650
 
8651
+ // src/first-person/ik.ts
8652
+ var solveTwoBone = (root, target, upper, lower, pole, out) => {
8653
+ const line = out.line.copy(target).sub(root);
8654
+ let distance3 = line.length();
8655
+ if (distance3 < 1e-6) line.set(0, 0, -1);
8656
+ else line.divideScalar(distance3);
8657
+ const min = Math.abs(upper - lower) + 1e-4;
8658
+ const max = upper + lower - 1e-4;
8659
+ out.clamped = distance3 > max || distance3 < min;
8660
+ distance3 = Math.min(max, Math.max(min, distance3));
8661
+ const along = (distance3 * distance3 + upper * upper - lower * lower) / (2 * distance3);
8662
+ const across = Math.sqrt(Math.max(0, upper * upper - along * along));
8663
+ const side = out.side.copy(pole);
8664
+ side.addScaledVector(line, -side.dot(line));
8665
+ if (side.lengthSq() < 1e-8) side.set(0, 1, 0).addScaledVector(line, -line.y);
8666
+ if (side.lengthSq() < 1e-8) side.set(1, 0, 0);
8667
+ side.normalize();
8668
+ out.joint.copy(root).addScaledVector(line, along).addScaledVector(side, across);
8669
+ out.end.copy(root).addScaledVector(line, distance3);
8670
+ return out;
8671
+ };
8672
+ var createTwoBone = (THREE) => ({ joint: new THREE.Vector3(), end: new THREE.Vector3(), clamped: false, line: new THREE.Vector3(), side: new THREE.Vector3() });
8673
+
8674
+ // src/player/arm-reach.ts
8675
+ var ARM_REACH = {
8676
+ /** The elbow never straightens past this bend (a locked elbow reads as a stick). */
8677
+ elbowMin: 0.12,
8678
+ /** Full flexion of the elbow. */
8679
+ elbowMax: 2.5,
8680
+ /** The wrist bends (flexion, extension, deviation) up to this angle from the line of the forearm (flexion reaches about 80 degrees). */
8681
+ wristBend: 1.3,
8682
+ /** The forearm turns (pronation and supination) up to this angle about its own axis. */
8683
+ wristTwist: 1.6,
8684
+ /** The most the chest turns to bring the support shoulder toward a far hand. */
8685
+ chestTurn: 0.6,
8686
+ /** The most a shoulder blade slides toward a far hand (protraction and elevation together). */
8687
+ protraction: 0.11,
8688
+ /** A hand is within reach below this fraction of the arm's length. */
8689
+ slack: 0.96,
8690
+ /** Where the elbow points: down and out from the body, a little back (body space, x outward). */
8691
+ elbowPole: [0.55, -1, -0.25],
8692
+ /** The support arm's elbow: under the gun, hardly out of the body (the first-person arms' `elbowPoleLeft`), so the forearm meets the handguard nearer the aim's line. */
8693
+ elbowPoleSupport: [0.1, -1, -0.2],
8694
+ /** From the wrist joint to the middle of the palm, metres of the body. */
8695
+ palm: 0.042,
8696
+ /** In a view the elbow keeps at least this interior bend where a hand is out of reach, right then left (the first-person arms' numbers). */
8697
+ viewBend: [1.35, 0.95],
8698
+ /** In a view a hand stays this far before a surface, metres. */
8699
+ contactMargin: 5e-3,
8700
+ /** In a view a hand is never nearer than this to the eye, metres. */
8701
+ contactNearest: 0.12,
8702
+ /** How far in front of the eye a surface is looked for, metres. */
8703
+ contactProbe: 1.4,
8704
+ /** How far a hand reaches forward of its middle at least (the thickness of the palm), and per metre of fingers pointing ahead. */
8705
+ contactPalm: 0.03,
8706
+ /** From the middle of the palm to the fingertips. */
8707
+ contactFingers: 0.085
8708
+ };
8709
+ var createArmReach = (THREE, rig, hands) => {
8710
+ const arms = {
8711
+ R: { g: rig.arms[1].g, el: rig.arms[1].el, hand: hands.R, outward: -1 },
8712
+ L: { g: rig.arms[0].g, el: rig.arms[0].el, hand: hands.L, outward: 1 }
8713
+ };
8714
+ const upper = rig.upper;
8715
+ const handBasis = {
8716
+ R: new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().makeBasis(new THREE.Vector3(0, -1, 0), new THREE.Vector3(-1, 0, 0), new THREE.Vector3(0, 0, -1))).invert(),
8717
+ L: new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().makeBasis(new THREE.Vector3(0, -1, 0), new THREE.Vector3(1, 0, 0), new THREE.Vector3(0, 0, 1))).invert()
8718
+ };
8719
+ const saved = {
8720
+ twist: 0,
8721
+ arms: { R: { position: new THREE.Vector3(), g: new THREE.Quaternion(), el: new THREE.Quaternion(), hand: new THREE.Quaternion() }, L: { position: new THREE.Vector3(), g: new THREE.Quaternion(), el: new THREE.Quaternion(), hand: new THREE.Quaternion() } }
8722
+ };
8723
+ let applied = false;
8724
+ const bone = createTwoBone(THREE);
8725
+ const v = { shoulder: new THREE.Vector3(), elbow: new THREE.Vector3(), wrist: new THREE.Vector3(), palm: new THREE.Vector3(), target: new THREE.Vector3(), pole: new THREE.Vector3(), a: new THREE.Vector3(), b: new THREE.Vector3(), c: new THREE.Vector3() };
8726
+ const q = { hand: new THREE.Quaternion(), parent: new THREE.Quaternion(), body: new THREE.Quaternion(), local: new THREE.Quaternion(), swing: new THREE.Quaternion(), twist: new THREE.Quaternion(), rest: new THREE.Quaternion() };
8727
+ const basis = new THREE.Matrix4();
8728
+ const camera = { position: new THREE.Vector3(), quaternion: new THREE.Quaternion(), inverse: new THREE.Quaternion(), probe: new THREE.Vector3(), forward: new THREE.Vector3(), fingers: new THREE.Vector3(), hand: new THREE.Quaternion() };
8729
+ let bendLimit = null;
8730
+ const yAxis = new THREE.Vector3(0, 1, 0);
8731
+ const lengthsOf = (arm) => {
8732
+ const scale = arm.g.getWorldScale(v.a).y;
8733
+ return { upper: arm.el.position.length() * scale, fore: arm.hand.position.length() * scale, palm: ARM_REACH.palm * scale };
8734
+ };
8735
+ const limitOf = (side, upperLength, fore) => {
8736
+ if (!bendLimit) return (upperLength + fore) * ARM_REACH.slack;
8737
+ return Math.sqrt(upperLength * upperLength + fore * fore + 2 * upperLength * fore * Math.cos(bendLimit[side === "R" ? 0 : 1]));
8738
+ };
8739
+ const wristOf = (side, pose, palm) => {
8740
+ q.hand.set(...pose.quaternion).multiply(handBasis[side]);
8741
+ v.palm.set(...pose.position);
8742
+ v.wrist.copy(v.palm).addScaledVector(v.a.set(0, -1, 0).applyQuaternion(q.hand), -palm);
8743
+ };
8744
+ const turnChest = (pose) => {
8745
+ const { upper: up, fore, palm } = lengthsOf(arms.L);
8746
+ wristOf("L", pose, palm);
8747
+ const excess = arms.L.g.getWorldPosition(v.shoulder).distanceTo(v.wrist) - limitOf("L", up, fore);
8748
+ if (excess <= 0) return;
8749
+ const chestRadius = Math.hypot(arms.L.g.position.x, arms.L.g.position.z) * arms.L.g.getWorldScale(v.a).y;
8750
+ upper.rotation.y -= Math.min(ARM_REACH.chestTurn, excess / Math.max(0.05, chestRadius));
8751
+ upper.updateMatrixWorld(true);
8752
+ };
8753
+ const protract = (arm, limit) => {
8754
+ const excess = arm.g.getWorldPosition(v.shoulder).distanceTo(v.wrist) - limit;
8755
+ if (excess <= 0) return;
8756
+ const parent = arm.g.parent;
8757
+ v.b.copy(v.wrist).sub(v.shoulder).normalize().multiplyScalar(Math.min(excess, ARM_REACH.protraction)).add(v.shoulder);
8758
+ arm.g.position.copy(parent.worldToLocal(v.b));
8759
+ arm.g.updateMatrixWorld(true);
8760
+ };
8761
+ const limitWrist = (local) => {
8762
+ const projection = v.c.set(local.x, local.y, local.z).projectOnVector(yAxis);
8763
+ q.twist.set(projection.x, projection.y, projection.z, local.w).normalize();
8764
+ const twistAngle = 2 * Math.atan2(q.twist.y, q.twist.w);
8765
+ const swing = q.swing.copy(local).multiply(q.twist.invert());
8766
+ const swingAngle = 2 * Math.acos(Math.min(1, Math.abs(swing.w)));
8767
+ if (swingAngle > ARM_REACH.wristBend) swing.slerp(q.rest.identity(), 1 - ARM_REACH.wristBend / swingAngle);
8768
+ const clampedTwist = Math.max(-ARM_REACH.wristTwist, Math.min(ARM_REACH.wristTwist, Math.atan2(Math.sin(twistAngle), Math.cos(twistAngle))));
8769
+ return local.copy(swing).multiply(q.twist.setFromAxisAngle(yAxis, clampedTwist));
8770
+ };
8771
+ const reach = (side, pose) => {
8772
+ const arm = arms[side];
8773
+ const { upper: up, fore, palm } = lengthsOf(arm);
8774
+ wristOf(side, pose, palm);
8775
+ protract(arm, limitOf(side, up, fore));
8776
+ arm.g.getWorldPosition(v.shoulder);
8777
+ upper.getWorldQuaternion(q.body);
8778
+ const pole = side === "L" ? ARM_REACH.elbowPoleSupport : ARM_REACH.elbowPole;
8779
+ v.pole.set(pole[0] * arm.outward, pole[1], pole[2]).applyQuaternion(q.body);
8780
+ const lockLimit = Math.sqrt(up * up + fore * fore + 2 * up * fore * Math.cos(ARM_REACH.elbowMin));
8781
+ const most = bendLimit ? Math.min(lockLimit, limitOf(side, up, fore)) : lockLimit;
8782
+ const least = Math.sqrt(up * up + fore * fore + 2 * up * fore * Math.cos(ARM_REACH.elbowMax));
8783
+ const line = v.c.copy(v.wrist).sub(v.shoulder);
8784
+ const distance3 = Math.min(most, Math.max(least, line.length()));
8785
+ v.target.copy(v.shoulder).addScaledVector(line.normalize(), distance3);
8786
+ solveTwoBone(v.shoulder, v.target, up, fore, v.pole, bone);
8787
+ const along = v.a.copy(bone.joint).sub(v.shoulder).normalize();
8788
+ const forearm = v.b.copy(bone.end).sub(bone.joint).normalize();
8789
+ const fold = v.c.copy(forearm).addScaledVector(along, -forearm.dot(along));
8790
+ if (fold.lengthSq() < 1e-8) fold.copy(v.pole).addScaledVector(along, -v.pole.dot(along)).negate();
8791
+ fold.normalize();
8792
+ const yWorld = v.elbow.copy(along).negate();
8793
+ basis.makeBasis(v.pole.crossVectors(yWorld, fold), yWorld, fold);
8794
+ arm.g.parent.getWorldQuaternion(q.parent);
8795
+ arm.g.quaternion.setFromRotationMatrix(basis).premultiply(q.parent.invert());
8796
+ arm.el.rotation.set(-Math.acos(Math.max(-1, Math.min(1, along.dot(forearm)))), 0, 0);
8797
+ arm.g.updateMatrixWorld(true);
8798
+ const reached = arm.hand.getWorldPosition(v.a).sub(v.shoulder).normalize();
8799
+ q.swing.setFromUnitVectors(reached, v.b.copy(v.target).sub(v.shoulder).normalize());
8800
+ arm.g.parent.getWorldQuaternion(q.parent);
8801
+ arm.g.quaternion.premultiply(q.parent).premultiply(q.swing).premultiply(q.parent.invert());
8802
+ arm.g.updateMatrixWorld(true);
8803
+ arm.el.getWorldQuaternion(q.parent);
8804
+ arm.hand.quaternion.copy(limitWrist(q.local.copy(q.parent.invert()).multiply(q.hand)));
8805
+ };
8806
+ const keepOutside = (pose, outside) => {
8807
+ const place = v.a.set(...pose.position).sub(camera.position).applyQuaternion(camera.inverse);
8808
+ camera.probe.set(place.x, place.y, 0).applyQuaternion(camera.quaternion).add(camera.position);
8809
+ camera.forward.set(0, 0, -1).applyQuaternion(camera.quaternion);
8810
+ const distance3 = outside.contact([camera.probe.x, camera.probe.y, camera.probe.z], [camera.forward.x, camera.forward.y, camera.forward.z], ARM_REACH.contactProbe);
8811
+ if (distance3 === null) return pose;
8812
+ camera.hand.set(...pose.quaternion).premultiply(camera.inverse);
8813
+ const ahead = Math.max(ARM_REACH.contactPalm, -camera.fingers.set(1, 0, 0).applyQuaternion(camera.hand).z * ARM_REACH.contactFingers);
8814
+ const limit = Math.max(outside.nearest ?? ARM_REACH.contactNearest, distance3 - ahead - (outside.margin ?? ARM_REACH.contactMargin));
8815
+ if (-place.z <= limit) return pose;
8816
+ place.z = -limit;
8817
+ place.applyQuaternion(camera.quaternion).add(camera.position);
8818
+ return { ...pose, position: [place.x, place.y, place.z] };
8819
+ };
8820
+ const save = () => {
8821
+ saved.twist = upper.rotation.y;
8822
+ for (const side of ["R", "L"]) {
8823
+ const arm = arms[side];
8824
+ const into = saved.arms[side];
8825
+ into.position.copy(arm.g.position);
8826
+ into.g.copy(arm.g.quaternion);
8827
+ into.el.copy(arm.el.quaternion);
8828
+ into.hand.copy(arm.hand.quaternion);
8829
+ }
8830
+ };
8831
+ return {
8832
+ apply(targets, view) {
8833
+ if (!targets || !targets.R && !targets.L) return;
8834
+ save();
8835
+ applied = true;
8836
+ upper.updateWorldMatrix(true, true);
8837
+ let { R, L } = targets;
8838
+ bendLimit = view ? view.reachBend ?? ARM_REACH.viewBend : null;
8839
+ if (view?.keepOutside) {
8840
+ view.camera.updateWorldMatrix(true, false);
8841
+ view.camera.matrixWorld.decompose(camera.position, camera.quaternion, v.a);
8842
+ camera.inverse.copy(camera.quaternion).invert();
8843
+ if (R) R = keepOutside(R, view.keepOutside);
8844
+ if (L) L = keepOutside(L, view.keepOutside);
8845
+ }
8846
+ if (L && R) turnChest(L);
8847
+ if (R) reach("R", R);
8848
+ if (L) reach("L", L);
8849
+ bendLimit = null;
8850
+ },
8851
+ restore() {
8852
+ if (!applied) return;
8853
+ applied = false;
8854
+ upper.rotation.y = saved.twist;
8855
+ for (const side of ["R", "L"]) {
8856
+ const arm = arms[side];
8857
+ const from = saved.arms[side];
8858
+ arm.g.position.copy(from.position);
8859
+ arm.g.quaternion.copy(from.g);
8860
+ arm.el.quaternion.copy(from.el);
8861
+ arm.hand.quaternion.copy(from.hand);
8862
+ }
8863
+ }
8864
+ };
8865
+ };
8866
+
8867
+ // src/player/head-hiding.ts
8868
+ var isDrawable = (object2) => {
8869
+ const flagged = object2;
8870
+ return Boolean(flagged.isMesh || flagged.isSprite || flagged.isPoints);
8871
+ };
8872
+ var createHeadHider = (roots) => {
8873
+ let isHiding = false;
8874
+ let viewCamera = null;
8875
+ const drawables = [];
8876
+ for (const root of roots) root.traverse((object2) => isDrawable(object2) && drawables.push(object2));
8877
+ for (const drawable of drawables) {
8878
+ const previousBefore = drawable.onBeforeRender;
8879
+ const previousAfter = drawable.onAfterRender;
8880
+ let saved = [];
8881
+ drawable.onBeforeRender = (renderer, scene2, camera, geometry, material, group) => {
8882
+ previousBefore.call(drawable, renderer, scene2, camera, geometry, material, group);
8883
+ if (!isHiding || camera !== viewCamera) return;
8884
+ const drawn = Array.isArray(drawable.material) ? drawable.material : [material];
8885
+ saved = drawn.map((entry) => ({ material: entry, colorWrite: entry.colorWrite, depthWrite: entry.depthWrite }));
8886
+ for (const entry of drawn) {
8887
+ entry.colorWrite = false;
8888
+ entry.depthWrite = false;
8889
+ }
8890
+ };
8891
+ drawable.onAfterRender = (renderer, scene2, camera, geometry, material, group) => {
8892
+ previousAfter.call(drawable, renderer, scene2, camera, geometry, material, group);
8893
+ for (const flags of saved) {
8894
+ flags.material.colorWrite = flags.colorWrite;
8895
+ flags.material.depthWrite = flags.depthWrite;
8896
+ }
8897
+ saved = [];
8898
+ };
8899
+ }
8900
+ return {
8901
+ setView(mode, camera) {
8902
+ isHiding = mode === "first" && camera !== null;
8903
+ viewCamera = camera;
8904
+ }
8905
+ };
8906
+ };
8907
+
8640
8908
  // src/player/avatar.ts
8641
8909
  var TWO_PI2 = Math.PI * 2;
8642
8910
  var MAX_GAIT = 1.4;
8643
8911
  var CLIP_BLEND = 0.12;
8644
8912
  var CLIP_CASCADE = 5;
8645
8913
  var FALL_SECONDS2 = 0.3;
8914
+ var BUILDER_EYE = [0, 1.815, 0.16];
8915
+ var EYE_OFFSET = [0, -0.068, 0];
8916
+ var EYE_BOB = 0.5;
8646
8917
  var HIP_HEIGHT = 0.95;
8647
8918
  var WALK_ARM = 0.28;
8648
8919
  var WALK_BOB = 0.035;
@@ -8652,7 +8923,11 @@ var GameCharactersThree = (() => {
8652
8923
  var REST_ARM_Z = 0.18;
8653
8924
  var CROUCH = { thigh: -0.95, thighWalk: 0.3, shin: 1.9, lean: 0.25, drop: -0.35 };
8654
8925
  var AIMED = { shoulder: -1.42, perAim: 0.85, swing: 0.02, zRight: -0.54, zLeft: 0.36, elbowRight: -0.04, elbowLeft: -0.12 };
8926
+ var SIT = { thigh: -1.5, lean: -0.08, arm: -0.45, armZ: 0.12, elbow: -0.75 };
8927
+ var LIE = { knee: 0.08, armZ: 0.12, elbow: -0.1 };
8655
8928
  var DOWN = { thighWalk: 0.5, shinSwing: 0.85, shinBend: 1.3, armHigh: -2.8, armLow: -0.35, armWave: 0.3, armZ: 0.5, armZWave: 0.3, tilt: 1.5, roll: 0.3, hipY: 0.16, hipZ: 0.6, hipZFlip: 1.2 };
8929
+ var DOOR_SHOULDER = 1.42;
8930
+ var DOOR_ARM = 0.62;
8656
8931
  var HAND_OFFSET = { x: 0, y: -0.33, z: 0.04 };
8657
8932
  var hexOf = (value) => typeof value === "string" ? Number.parseInt(value, 16) : value;
8658
8933
  var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
@@ -8682,6 +8957,8 @@ var GameCharactersThree = (() => {
8682
8957
  const fallSeconds = options.fallSeconds ?? FALL_SECONDS2;
8683
8958
  const smooth = options.cascade ?? true;
8684
8959
  const clipCascade = options.clipCascade ?? CLIP_CASCADE;
8960
+ const eyeBob = options.eyeBob ?? EYE_BOB;
8961
+ const eyeOffset = options.eyeOffset ?? EYE_OFFSET;
8685
8962
  const root = new THREE.Group();
8686
8963
  const tilt = new THREE.Group();
8687
8964
  tilt.add(body);
@@ -8733,6 +9010,7 @@ var GameCharactersThree = (() => {
8733
9010
  const built = buildHand({ THREE, add: addMesh, hg, handM: handMaterial, s: side, detailed: true });
8734
9011
  hands.push({ side, hg, fingers: built.fingers, th: built.th });
8735
9012
  });
9013
+ const armReach = createArmReach(THREE, rig, { R: hands.find((hand) => hand.side < 0).hg, L: hands.find((hand) => hand.side > 0).hg });
8736
9014
  const setHandGrip = (hand, grip, wristX = 0) => {
8737
9015
  hand.fingers.forEach((finger, j) => {
8738
9016
  finger.rotation.z = -hand.side * grip * (j % 2 ? 1.1 : 0.9);
@@ -8740,7 +9018,23 @@ var GameCharactersThree = (() => {
8740
9018
  if (hand.th) hand.th.rotation.z = -hand.side * (0.32 + grip * 0.25);
8741
9019
  hand.hg.rotation.x = wristX;
8742
9020
  };
9021
+ const eyeSocket = new THREE.Object3D();
9022
+ eyeSocket.name = "eye";
9023
+ eyeSocket.position.set(BUILDER_EYE[0] + eyeOffset[0], BUILDER_EYE[1] + eyeOffset[1], BUILDER_EYE[2] + eyeOffset[2]);
9024
+ rig.headG.children[0].add(eyeSocket);
9025
+ const headHider = createHeadHider([rig.headG, ...rig.neck ? [rig.neck] : []]);
9026
+ const eyeLocal = new THREE.Vector3();
9027
+ let bob = 0;
9028
+ let hipWithoutBob = null;
8743
9029
  const legRestX = rig.legs.map((leg) => leg.g.position.x);
9030
+ const legLengths = (() => {
9031
+ const thigh = rig.legs[0].g;
9032
+ const shin = rig.legs[0].kn;
9033
+ root.updateMatrixWorld(true);
9034
+ const hipY = thigh.getWorldPosition(new THREE.Vector3()).y;
9035
+ const kneeY = shin.getWorldPosition(new THREE.Vector3()).y;
9036
+ return { hipBelowTrunk: HIP_HEIGHT - hipY, thigh: hipY - kneeY, shin: kneeY - new THREE.Box3().setFromObject(shin).min.y };
9037
+ })();
8744
9038
  const legSide = (index) => index === 0 ? 1 : -1;
8745
9039
  let cycles = 0;
8746
9040
  let time = 0;
@@ -8799,10 +9093,29 @@ var GameCharactersThree = (() => {
8799
9093
  ...strain ? { legL: -0.5, legR: 0.4, kneeL: 0.55, kneeR: 0.08, drop: -0.05 } : {}
8800
9094
  };
8801
9095
  };
9096
+ const doorPose = (door) => {
9097
+ const { phase, progress } = door;
9098
+ const on = phase === "reach" ? progress : phase === "release" ? 1 - progress : 1;
9099
+ const swing = phase === "swing" ? progress : phase === "release" ? 1 : 0;
9100
+ const shake = phase === "rattle" ? Math.sin(time * 55) * 0.06 * (1 - progress) : 0;
9101
+ const drop = clamp((DOOR_SHOULDER - door.height) / DOOR_ARM, -0.9, 1);
9102
+ const low = clamp((0.75 - door.height) / 0.5, 0, 1);
9103
+ const raise = -Math.acos(clamp(drop, -1, 1)) * on - 0.35 * low * on;
9104
+ const elbow = door.pull ? -0.35 - 0.75 * swing : -0.95 + 0.75 * swing;
9105
+ return {
9106
+ armR: raise + shake,
9107
+ armRz: REST_ARM_Z * (1 - on) + 0.05 * on,
9108
+ elbR: -0.12 + (elbow + 0.12) * on,
9109
+ armLz: -REST_ARM_Z,
9110
+ elbL: -0.12,
9111
+ lean: ((door.pull ? -0.06 : 0.1) * swing + 0.35 * low) * on,
9112
+ headX: 0.06 * on + 0.2 * low * on
9113
+ };
9114
+ };
8802
9115
  const holdingPose = (gait, itemUse) => {
8803
- const bob = Math.sin(cycles * TWO_PI2) * 0.05 * gait;
9116
+ const bob2 = Math.sin(cycles * TWO_PI2) * 0.05 * gait;
8804
9117
  if (!itemUse) {
8805
- return { armL: -0.45 + bob, armLz: 0.22, elbL: -1.05 };
9118
+ return { armL: -0.45 + bob2, armLz: 0.22, elbL: -1.05 };
8806
9119
  }
8807
9120
  const { id, position, rotation } = itemUse;
8808
9121
  if (id === "drink" || id === "sip" || id === "chug") {
@@ -8815,7 +9128,7 @@ var GameCharactersThree = (() => {
8815
9128
  headX: -0.26 * tilt3
8816
9129
  };
8817
9130
  }
8818
- if (id === "open" || id === "close") {
9131
+ if (id === "open" || id === "close" || itemUse.gripL !== void 0) {
8819
9132
  const reach = clamp((0.22 - position[0]) / 0.12, 0, 1);
8820
9133
  return {
8821
9134
  armL: -0.45 - 0.35 * reach,
@@ -8914,6 +9227,31 @@ var GameCharactersThree = (() => {
8914
9227
  drop: Math.abs(Math.sin(ph)) * WALK_BOB * gait
8915
9228
  };
8916
9229
  };
9230
+ const sitPose = (hip) => {
9231
+ const knee = hip - legLengths.hipBelowTrunk - legLengths.thigh * Math.cos(SIT.thigh);
9232
+ const shinFromVertical = Math.acos(clamp(knee / legLengths.shin, 0, 1));
9233
+ const bend = -SIT.thigh - shinFromVertical;
9234
+ return {
9235
+ legL: SIT.thigh,
9236
+ legR: SIT.thigh,
9237
+ kneeL: bend,
9238
+ kneeR: bend,
9239
+ lean: SIT.lean,
9240
+ drop: hip - HIP_HEIGHT,
9241
+ armR: SIT.arm,
9242
+ armL: SIT.arm,
9243
+ armRz: SIT.armZ,
9244
+ armLz: -SIT.armZ,
9245
+ elbR: SIT.elbow,
9246
+ elbL: SIT.elbow
9247
+ };
9248
+ };
9249
+ const liePose = () => ({ legL: 0, legR: 0, kneeL: LIE.knee, kneeR: LIE.knee, lean: 0, drop: 0, armR: 0, armL: 0, armRz: LIE.armZ, armLz: -LIE.armZ, elbR: LIE.elbow, elbL: LIE.elbow });
9250
+ const layBody = (hip) => {
9251
+ tilt.rotation.set(-Math.PI / 2, 0, 0, "XYZ");
9252
+ const pivot = new THREE.Vector3(0, HIP_HEIGHT, 0).applyEuler(tilt.rotation);
9253
+ tilt.position.set(0, hip, 0).sub(pivot);
9254
+ };
8917
9255
  const placeBody = (progress) => {
8918
9256
  const fk = progress * progress * (3 - 2 * progress);
8919
9257
  const flip = fract(cycles * 1.7) >= 0.5 ? 1 : 0;
@@ -8963,6 +9301,7 @@ var GameCharactersThree = (() => {
8963
9301
  return run ? run.clip.id : null;
8964
9302
  },
8965
9303
  update(dt, state = {}) {
9304
+ armReach.restore();
8966
9305
  const speed = state.speed ?? 0;
8967
9306
  const moving = speed >= 0.05;
8968
9307
  const down = !!state.down;
@@ -8973,13 +9312,27 @@ var GameCharactersThree = (() => {
8973
9312
  profile.bob = (running ? RUN_BOB : WALK_BOB) * gait;
8974
9313
  time = cycles / (walkCadence(Math.max(speed, 0)) * (running ? 1.3 : 1));
8975
9314
  animator.animate(body, time, { speed });
9315
+ bob = rig.fig.position.y - rig.dropBase;
8976
9316
  fall = fallSeconds <= 0 ? down ? 1 : 0 : clamp(fall + (down ? dt : -dt) / fallSeconds, 0, 1);
8977
9317
  resetLegYaw();
8978
- if (down) animator.setPose(rig, downPose());
8979
- else {
8980
- if (running) animator.setPose(rig, runningPose(gait, Boolean(state.armed || state.holding || state.itemUse)));
8981
- if (state.crouch) animator.setPose(rig, crouchPose(gait));
9318
+ const seat = down ? null : state.seat ?? null;
9319
+ if (down) {
9320
+ animator.setPose(rig, downPose());
9321
+ bob = rig.fig.position.y - rig.dropBase;
9322
+ } else if (seat) {
9323
+ animator.setPose(rig, seat.pose === "lie" ? liePose() : sitPose(seat.hip));
9324
+ bob = 0;
9325
+ } else {
9326
+ if (running) {
9327
+ animator.setPose(rig, runningPose(gait, Boolean(state.armed || state.holding || state.itemUse)));
9328
+ bob = rig.fig.position.y - rig.dropBase;
9329
+ }
9330
+ if (state.crouch) {
9331
+ animator.setPose(rig, crouchPose(gait));
9332
+ bob = rig.fig.position.y - rig.dropBase - CROUCH.drop;
9333
+ }
8982
9334
  if (state.push) animator.setPose(rig, pushingPose(state.push));
9335
+ else if (state.door) animator.setPose(rig, doorPose(state.door));
8983
9336
  else if (state.armed) animator.setPose(rig, armed(state.pitch ?? 0, gait, state.hip ?? 0));
8984
9337
  else if (state.holding || state.itemUse) animator.setPose(rig, holdingPose(gait, state.itemUse));
8985
9338
  else if (!running) {
@@ -8991,20 +9344,33 @@ var GameCharactersThree = (() => {
8991
9344
  });
8992
9345
  }
8993
9346
  }
8994
- placeBody(fall);
8995
- stepClip(dt, down, Boolean(state.crouch));
8996
- if (smooth) animator.cascade(rig, run && !run.clip.loop ? dt * clipCascade : dt);
9347
+ if (seat?.pose === "lie") layBody(seat.hip);
9348
+ else placeBody(fall);
9349
+ stepClip(dt, down, Boolean(state.crouch || seat));
9350
+ const cascadeDt = run && !run.clip.loop ? dt * clipCascade : dt;
9351
+ const targetWithoutBob = rig.fig.position.y - bob;
9352
+ if (smooth) animator.cascade(rig, cascadeDt);
9353
+ hipWithoutBob = smooth && hipWithoutBob !== null ? stepCascade(hipWithoutBob, targetWithoutBob, cascadeDt, "hip") : targetWithoutBob;
9354
+ bob = rig.fig.position.y - hipWithoutBob;
8997
9355
  const blow = run?.clip.events?.find((event) => (event.type === "impact" || event.type === "grip") && event.data?.kind)?.data?.kind;
8998
9356
  const ph = cycles * TWO_PI2;
8999
9357
  hands.forEach((hand) => {
9000
9358
  const isRight = hand.side < 0;
9001
9359
  const holdRight = isRight && (!!state.holding || !!state.itemUse);
9002
- const holdLeft = !isRight && (state.itemUse?.id === "open" || state.itemUse?.id === "close");
9003
- const grip = blow === "punch" ? 1.4 : blow === "shove" ? 0.15 : state.armed || holdRight || holdLeft ? 1.22 : running ? 0.85 : 0.12;
9360
+ const holdLeft = !isRight && (state.itemUse?.id === "open" || state.itemUse?.id === "close" || state.itemUse?.gripL !== void 0);
9361
+ const grip = !isRight && state.itemUse?.gripL !== void 0 ? 1.22 * state.itemUse.gripL : blow === "punch" ? 1.4 : blow === "shove" ? 0.15 : state.armed || holdRight || holdLeft ? 1.22 : running ? 0.85 : 0.12;
9004
9362
  const wristSwing = holdRight || state.armed || running ? 0 : -Math.min(1, gait) * Math.sin(ph - 0.6) * 0.12 * hand.side;
9005
9363
  setHandGrip(hand, grip, wristSwing);
9006
9364
  });
9365
+ root.updateMatrixWorld(true);
9366
+ root.worldToLocal(eyeSocket.getWorldPosition(eyeLocal));
9367
+ eyeLocal.y -= (1 - eyeBob) * bob;
9368
+ },
9369
+ eye(out = new THREE.Vector3()) {
9370
+ root.updateWorldMatrix(true, false);
9371
+ return root.localToWorld(out.copy(eyeLocal));
9007
9372
  },
9373
+ setView: (mode, camera) => headHider.setView(mode, camera),
9008
9374
  play(clip, playOptions = {}) {
9009
9375
  const found = typeof clip === "string" ? clips.get(clip) : clip;
9010
9376
  if (!found) throw new Error(`Unknown pose clip: ${String(clip)}`);
@@ -9016,6 +9382,12 @@ var GameCharactersThree = (() => {
9016
9382
  if (run) run.target = 0;
9017
9383
  },
9018
9384
  hand: (side) => sockets[side],
9385
+ shoulder: (side) => armOf[side].g,
9386
+ reach(targets, view) {
9387
+ if (run || fall > 0) return;
9388
+ root.updateMatrixWorld(true);
9389
+ armReach.apply(targets, view);
9390
+ },
9019
9391
  on(_type, listener) {
9020
9392
  listeners.add(listener);
9021
9393
  return () => void listeners.delete(listener);
@@ -9446,7 +9818,9 @@ var GameCharactersThree = (() => {
9446
9818
  shoulder: [0.16, -0.36, 0.18],
9447
9819
  upper: 0.32,
9448
9820
  fore: 0.3,
9449
- reachShift: 0.35,
9821
+ reachShift: 0.42,
9822
+ reachBend: [1.35, 0.95],
9823
+ elbowView: [[0.96, -0.26, -0.06], null],
9450
9824
  wrist: 0.07,
9451
9825
  elbowPole: [0.45, -1, 0.2],
9452
9826
  shoulderLeft: [-0.07, -0.46, 0.18],
@@ -9473,22 +9847,14 @@ var GameCharactersThree = (() => {
9473
9847
  };
9474
9848
 
9475
9849
  // src/first-person/hand.ts
9476
- var FINGERS = [
9477
- { z: -0.03, lengths: [0.034, 0.024, 0.02], width: 0.016 },
9478
- { z: -0.0105, lengths: [0.038, 0.027, 0.022], width: 0.0165 },
9479
- { z: 9e-3, lengths: [0.035, 0.025, 0.02], width: 0.0155 },
9480
- { z: 0.027, lengths: [0.029, 0.02, 0.018], width: 0.0135 }
9481
- ];
9482
- var PALM = { length: 0.09, thick: 0.026, width: 0.09, wristWidth: 0.066 };
9483
- var FINGER_THICK = 0.0135;
9484
- var CURL = { near: 1.05, mid: 1.25, far: 0.85, spread: 0.06 };
9485
- var THUMB = { base: [5e-3, -6e-3, -0.044], near: 0.034, far: 0.028, width: 0.019 };
9850
+ var PALM = { radius: 0.048, scale: [0.6, 1.05, 1], down: 0.035 };
9851
+ var CURL = { closed: 1.25, near: 0.9, far: 1.1 };
9852
+ var THUMB = { open: 0.32, closed: 0.25 };
9486
9853
  var createHandMesh = (THREE, skin, wristSkin = skin) => {
9487
9854
  const root = new THREE.Group();
9488
9855
  root.name = "first-person-hand";
9489
9856
  const geometries = [];
9490
- const box2 = (parent, sx, sy, sz, x, y, z, material = skin) => {
9491
- const geometry = new THREE.BoxGeometry(sx, sy, sz);
9857
+ const add = (parent, geometry, material, x = 0, y = 0, z = 0) => {
9492
9858
  geometries.push(geometry);
9493
9859
  const mesh2 = new THREE.Mesh(geometry, material);
9494
9860
  mesh2.position.set(x, y, z);
@@ -9496,64 +9862,26 @@ var GameCharactersThree = (() => {
9496
9862
  parent.add(mesh2);
9497
9863
  return mesh2;
9498
9864
  };
9499
- const palmGeometry = new THREE.BoxGeometry(PALM.length, PALM.thick, PALM.width);
9500
- const palmPosition = palmGeometry.getAttribute("position");
9501
- for (let i = 0; i < palmPosition.count; i++) {
9502
- if (palmPosition.getX(i) < 0) palmPosition.setZ(i, palmPosition.getZ(i) * PALM.wristWidth / PALM.width);
9503
- }
9504
- palmGeometry.computeVertexNormals();
9505
- geometries.push(palmGeometry);
9506
- const palm = new THREE.Mesh(palmGeometry, skin);
9507
- palm.frustumCulled = false;
9508
- root.add(palm);
9509
- const wristGeometry = new THREE.CylinderGeometry(0.03, 0.033, 0.05, 8);
9865
+ const group = new THREE.Group();
9866
+ group.quaternion.setFromRotationMatrix(new THREE.Matrix4().makeBasis(new THREE.Vector3(0, -1, 0), new THREE.Vector3(-1, 0, 0), new THREE.Vector3(0, 0, -1)));
9867
+ group.position.set(-PALM.down, 0, 0);
9868
+ root.add(group);
9869
+ add(group, new THREE.SphereGeometry(PALM.radius, 14, 10), skin, 0, -PALM.down, 0).scale.set(...PALM.scale);
9870
+ const built = buildHand({ THREE, add, hg: group, handM: skin, s: -1, detailed: true });
9871
+ const wristGeometry = new THREE.CylinderGeometry(0.03, 0.033, 0.05, 10);
9510
9872
  wristGeometry.rotateZ(Math.PI / 2);
9511
- geometries.push(wristGeometry);
9512
- const wrist = new THREE.Mesh(wristGeometry, wristSkin);
9513
- wrist.position.set(-PALM.length / 2 - 0.018, 0, 0);
9514
- wrist.frustumCulled = false;
9515
- root.add(wrist);
9516
- const joints = [];
9517
- FINGERS.forEach((finger, index) => {
9518
- const segments = [];
9519
- let parent = root;
9520
- let at = PALM.length / 2;
9521
- finger.lengths.forEach((length, k) => {
9522
- const group = new THREE.Group();
9523
- group.position.set(at, k === 0 ? 2e-3 : 0, k === 0 ? finger.z : 0);
9524
- const taper = 1 - k * 0.07;
9525
- box2(group, length, FINGER_THICK * taper, finger.width * taper, length / 2, 0, 0);
9526
- parent.add(group);
9527
- segments.push(group);
9528
- parent = group;
9529
- at = length;
9530
- });
9531
- joints.push({ segments, weight: 1 + index * (CURL.spread / CURL.near) });
9532
- });
9533
- const thumbNear = new THREE.Group();
9534
- thumbNear.rotation.order = "YZX";
9535
- thumbNear.position.set(...THUMB.base);
9536
- box2(thumbNear, THUMB.near, FINGER_THICK * 1.1, THUMB.width, THUMB.near / 2, 0, 0);
9537
- const thumbFar = new THREE.Group();
9538
- thumbFar.position.set(THUMB.near, 0, 0);
9539
- box2(thumbFar, THUMB.far, FINGER_THICK, THUMB.width * 0.9, THUMB.far / 2, 0, 0);
9540
- thumbNear.add(thumbFar);
9541
- root.add(thumbNear);
9873
+ add(root, wristGeometry, wristSkin, -PALM.down - 0.03, 0, 0);
9542
9874
  let lastGrip = 0;
9543
9875
  let indexCurl;
9544
- const curl = [CURL.near, CURL.mid, CURL.far];
9545
- const applyIndex = () => {
9546
- const finger = joints[0];
9547
- if (!finger) return;
9548
- const k = indexCurl === void 0 ? lastGrip : Math.min(1, Math.max(0, indexCurl));
9549
- finger.segments.forEach((segment, i) => segment.rotation.z = -curl[i] * k * finger.weight);
9876
+ const curlFinger = (finger, k) => {
9877
+ built.fingers[finger * 2].rotation.z = k * CURL.closed * CURL.near;
9878
+ built.fingers[finger * 2 + 1].rotation.z = k * CURL.closed * CURL.far;
9550
9879
  };
9880
+ const applyIndex = () => curlFinger(0, indexCurl === void 0 ? lastGrip : Math.min(1, Math.max(0, indexCurl)));
9551
9881
  const setGrip = (grip) => {
9552
- const k = Math.min(1, Math.max(0, grip));
9553
- lastGrip = k;
9554
- for (const joint of joints) joint.segments.forEach((segment, i) => segment.rotation.z = -curl[i] * k * joint.weight);
9555
- thumbNear.rotation.set(0, 0.55 - 1.45 * k, -0.1 - 0.75 * k);
9556
- thumbFar.rotation.z = -0.1 - 0.7 * k;
9882
+ lastGrip = Math.min(1, Math.max(0, grip));
9883
+ for (let finger = 1; finger < 4; finger++) curlFinger(finger, lastGrip);
9884
+ if (built.th) built.th.rotation.z = THUMB.open + lastGrip * THUMB.closed;
9557
9885
  applyIndex();
9558
9886
  };
9559
9887
  const setIndex = (value) => {
@@ -9571,33 +9899,12 @@ var GameCharactersThree = (() => {
9571
9899
  };
9572
9900
  };
9573
9901
 
9574
- // src/first-person/ik.ts
9575
- var solveTwoBone = (root, target, upper, lower, pole, out) => {
9576
- const line = out.line.copy(target).sub(root);
9577
- let distance3 = line.length();
9578
- if (distance3 < 1e-6) line.set(0, 0, -1);
9579
- else line.divideScalar(distance3);
9580
- const min = Math.abs(upper - lower) + 1e-4;
9581
- const max = upper + lower - 1e-4;
9582
- out.clamped = distance3 > max || distance3 < min;
9583
- distance3 = Math.min(max, Math.max(min, distance3));
9584
- const along = (distance3 * distance3 + upper * upper - lower * lower) / (2 * distance3);
9585
- const across = Math.sqrt(Math.max(0, upper * upper - along * along));
9586
- const side = out.side.copy(pole);
9587
- side.addScaledVector(line, -side.dot(line));
9588
- if (side.lengthSq() < 1e-8) side.set(0, 1, 0).addScaledVector(line, -line.y);
9589
- if (side.lengthSq() < 1e-8) side.set(1, 0, 0);
9590
- side.normalize();
9591
- out.joint.copy(root).addScaledVector(line, along).addScaledVector(side, across);
9592
- out.end.copy(root).addScaledVector(line, distance3);
9593
- return out;
9594
- };
9595
- var createTwoBone = (THREE) => ({ joint: new THREE.Vector3(), end: new THREE.Vector3(), clamped: false, line: new THREE.Vector3(), side: new THREE.Vector3() });
9596
-
9597
9902
  // src/first-person/arms.ts
9598
9903
  var DEFAULT_BLEND = 0.18;
9599
9904
  var ELBOW_SEGMENTS = 8;
9600
9905
  var REACH_SLACK = 0.97;
9906
+ var ELBOW_VIEW_MARGIN = 0.86;
9907
+ var ELBOW_VIEW_PASSES = 4;
9601
9908
  var CONTACT_PROBE = 1.4;
9602
9909
  var CONTACT_MIN = 0.12;
9603
9910
  var CONTACT_MARGIN = 5e-3;
@@ -9746,6 +10053,11 @@ var GameCharactersThree = (() => {
9746
10053
  const shoulderOf = (sign2, out) => sign2 === -1 && rig.shoulderLeft ? out.set(...rig.shoulderLeft) : out.set(rig.shoulder[0] * sign2, rig.shoulder[1], rig.shoulder[2]);
9747
10054
  const poleOf = (sign2) => sign2 === -1 && rig.elbowPoleLeft ? new THREE.Vector3(...rig.elbowPoleLeft) : new THREE.Vector3(rig.elbowPole[0] * sign2, rig.elbowPole[1], rig.elbowPole[2]);
9748
10055
  const kneePole = new THREE.Vector3(...rig.kneePole);
10056
+ const reachOf = (sign2) => {
10057
+ const bend = rig.reachBend?.[sign2 === 1 ? 0 : 1];
10058
+ if (bend === void 0) return (rig.upper + rig.fore) * REACH_SLACK;
10059
+ return Math.sqrt(rig.upper * rig.upper + rig.fore * rig.fore + 2 * rig.upper * rig.fore * Math.cos(bend));
10060
+ };
9749
10061
  const hip = new THREE.Vector3(...rig.hip);
9750
10062
  const euler = new THREE.Euler(0, 0, 0, "YZX");
9751
10063
  const quaternionOf = (r) => new THREE.Quaternion().setFromEuler(euler.set(r[0], r[1], r[2], "YZX"));
@@ -9835,15 +10147,45 @@ var GameCharactersThree = (() => {
9835
10147
  out.scale = base.scale;
9836
10148
  return out;
9837
10149
  };
9838
- const drawArm = (arm, pose) => {
10150
+ const elbowAt = new THREE.Vector3();
10151
+ const viewOf = (camera) => {
10152
+ const lens = camera;
10153
+ if (!lens.isPerspectiveCamera) return null;
10154
+ const tanV = Math.tan(lens.fov * Math.PI / 360);
10155
+ return { tanV, tanH: tanV * lens.aspect };
10156
+ };
10157
+ const placeElbowInView = (sign2, view) => {
10158
+ const way = rig.elbowView?.[sign2 === 1 ? 0 : 1];
10159
+ if (!way) return false;
10160
+ elbowAt.set(way[0], way[1], way[2]).setLength(rig.fore).add(wristAt);
10161
+ for (let pass = 0; pass < ELBOW_VIEW_PASSES; pass++) {
10162
+ const depth = Math.max(0.05, -elbowAt.z);
10163
+ const high = view.tanV * ELBOW_VIEW_MARGIN * depth;
10164
+ const wide = view.tanH * ELBOW_VIEW_MARGIN * depth;
10165
+ elbowAt.y = Math.max(elbowAt.y, -high);
10166
+ elbowAt.x = Math.min(wide, Math.max(-wide, elbowAt.x));
10167
+ elbowAt.sub(wristAt).setLength(rig.fore).add(wristAt);
10168
+ }
10169
+ shoulder.sub(elbowAt).setLength(rig.upper).add(elbowAt);
10170
+ return true;
10171
+ };
10172
+ const drawArm = (arm, pose, view) => {
9839
10173
  copyPose(arm.drawn, pose);
9840
10174
  const scale = pose.scale;
9841
10175
  fingers.set(1, 0, 0).applyQuaternion(pose.q);
9842
10176
  wristAt.copy(pose.p).addScaledVector(fingers, -rig.wrist * scale);
9843
10177
  shoulderOf(arm.sign, shoulder).add(bobOffset);
9844
- const excess = shoulder.distanceTo(wristAt) - (rig.upper + rig.fore) * REACH_SLACK;
10178
+ if (view && placeElbowInView(arm.sign, view)) {
10179
+ solveTwoBone(shoulder, wristAt, rig.upper, rig.fore, towards.copy(elbowAt).sub(shoulder).add(elbowAt).sub(wristAt), bone);
10180
+ finishArm(arm, pose, scale);
10181
+ return;
10182
+ }
10183
+ const excess = shoulder.distanceTo(wristAt) - reachOf(arm.sign);
9845
10184
  if (excess > 0 && rig.reachShift > 0) shoulder.addScaledVector(towards.subVectors(wristAt, shoulder).normalize(), Math.min(excess, rig.reachShift));
9846
10185
  solveTwoBone(shoulder, wristAt, rig.upper, rig.fore, poleOf(arm.sign), bone);
10186
+ finishArm(arm, pose, scale);
10187
+ };
10188
+ const finishArm = (arm, pose, scale) => {
9847
10189
  palmAt.copy(bone.end).addScaledVector(fingers, rig.wrist * scale);
9848
10190
  arm.drawn.p.copy(palmAt);
9849
10191
  arm.anchor.position.copy(palmAt);
@@ -9955,7 +10297,7 @@ var GameCharactersThree = (() => {
9955
10297
  dropped.p.y -= AWAY_DROP * ease2(arm.away);
9956
10298
  pose = dropped;
9957
10299
  }
9958
- drawArm(arm, pose);
10300
+ drawArm(arm, pose, mode === "target" && targets?.elbows !== "body" ? viewOf(state.camera) : null);
9959
10301
  const shown = arm.away < AWAY_HIDDEN;
9960
10302
  for (const part of [arm.anchor, arm.upper, arm.fore, arm.cuff, arm.elbow]) part.visible = shown;
9961
10303
  }
@@ -10003,7 +10345,7 @@ var GameCharactersThree = (() => {
10003
10345
  var isPunch = (clip) => clip.events?.some((event) => event.type === "impact" && event.data?.kind === "punch") ?? false;
10004
10346
  var GUARD_SPEED = 0.3;
10005
10347
  var toRoomAvatar = (avatar, options = {}) => {
10006
- const arms = options.firstPerson === false ? null : createFirstPersonArms(avatar.three, {
10348
+ const arms = options.firstPerson === false || options.firstPerson === void 0 ? null : createFirstPersonArms(avatar.three, {
10007
10349
  colors: { skin: avatar.colors.skin, sleeve: avatar.colors.longSleeves ? avatar.colors.shirt : avatar.colors.skin, trouser: avatar.colors.trousers, shoe: avatar.colors.shoes, ...options.firstPerson?.colors },
10008
10350
  clips: options.firstPersonClips ?? FIRST_PERSON_CLIPS,
10009
10351
  hideWhenIdle: true,
@@ -10024,7 +10366,7 @@ var GameCharactersThree = (() => {
10024
10366
  let guarding = false;
10025
10367
  const stepGuard = (dt, state) => {
10026
10368
  if (!guard) return;
10027
- const free = state.speed < GUARD_SPEED && !state.down && !state.armed && !state.holding && !state.push && !state.itemUse && !state.running;
10369
+ const free = state.speed < GUARD_SPEED && !state.down && !state.armed && !state.holding && !state.push && !state.door && !state.itemUse && !state.running;
10028
10370
  if (strikeLeft > 0) {
10029
10371
  strikeLeft -= dt;
10030
10372
  if (strikeLeft <= 0 && guardLeft > 0 && free) {
@@ -10043,7 +10385,8 @@ var GameCharactersThree = (() => {
10043
10385
  root: avatar.root,
10044
10386
  avatar,
10045
10387
  arms,
10046
- ...arms ? { firstPerson: { root: arms.root, update: (dt, state) => arms.update(dt, state) } } : {},
10388
+ eye: (out) => avatar.eye(out),
10389
+ setView: (mode, camera) => avatar.setView(mode, camera),
10047
10390
  update: (dt, state) => {
10048
10391
  stepGuard(dt, state);
10049
10392
  avatar.update(dt, {
@@ -10055,10 +10398,14 @@ var GameCharactersThree = (() => {
10055
10398
  pitch: -state.pitch,
10056
10399
  ...state.holding !== void 0 ? { holding: state.holding } : {},
10057
10400
  ...state.itemUse !== void 0 ? { itemUse: state.itemUse } : {},
10058
- ...state.push !== void 0 ? { push: state.push } : {}
10401
+ ...state.push !== void 0 ? { push: state.push } : {},
10402
+ ...state.seat !== void 0 ? { seat: state.seat } : {},
10403
+ ...state.door !== void 0 ? { door: state.door } : {}
10059
10404
  });
10060
10405
  },
10061
10406
  hand: (side) => avatar.hand(side),
10407
+ shoulder: (side) => avatar.shoulder(side),
10408
+ reach: (targets, view) => avatar.reach(targets, view),
10062
10409
  strike: (clipId, strikeOptions) => {
10063
10410
  const clip = strikes.get(clipId);
10064
10411
  if (!clip) return null;