@adatechnology/game-characters-three 0.6.0 → 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();
@@ -8678,8 +8689,22 @@ var GameCharactersThree = (() => {
8678
8689
  slack: 0.96,
8679
8690
  /** Where the elbow points: down and out from the body, a little back (body space, x outward). */
8680
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],
8681
8694
  /** From the wrist joint to the middle of the palm, metres of the body. */
8682
- palm: 0.042
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
8683
8708
  };
8684
8709
  var createArmReach = (THREE, rig, hands) => {
8685
8710
  const arms = {
@@ -8700,11 +8725,17 @@ var GameCharactersThree = (() => {
8700
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() };
8701
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() };
8702
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;
8703
8730
  const yAxis = new THREE.Vector3(0, 1, 0);
8704
8731
  const lengthsOf = (arm) => {
8705
8732
  const scale = arm.g.getWorldScale(v.a).y;
8706
8733
  return { upper: arm.el.position.length() * scale, fore: arm.hand.position.length() * scale, palm: ARM_REACH.palm * scale };
8707
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
+ };
8708
8739
  const wristOf = (side, pose, palm) => {
8709
8740
  q.hand.set(...pose.quaternion).multiply(handBasis[side]);
8710
8741
  v.palm.set(...pose.position);
@@ -8713,14 +8744,14 @@ var GameCharactersThree = (() => {
8713
8744
  const turnChest = (pose) => {
8714
8745
  const { upper: up, fore, palm } = lengthsOf(arms.L);
8715
8746
  wristOf("L", pose, palm);
8716
- const excess = arms.L.g.getWorldPosition(v.shoulder).distanceTo(v.wrist) - (up + fore) * ARM_REACH.slack;
8747
+ const excess = arms.L.g.getWorldPosition(v.shoulder).distanceTo(v.wrist) - limitOf("L", up, fore);
8717
8748
  if (excess <= 0) return;
8718
8749
  const chestRadius = Math.hypot(arms.L.g.position.x, arms.L.g.position.z) * arms.L.g.getWorldScale(v.a).y;
8719
8750
  upper.rotation.y -= Math.min(ARM_REACH.chestTurn, excess / Math.max(0.05, chestRadius));
8720
8751
  upper.updateMatrixWorld(true);
8721
8752
  };
8722
- const protract = (arm, length) => {
8723
- const excess = arm.g.getWorldPosition(v.shoulder).distanceTo(v.wrist) - length * ARM_REACH.slack;
8753
+ const protract = (arm, limit) => {
8754
+ const excess = arm.g.getWorldPosition(v.shoulder).distanceTo(v.wrist) - limit;
8724
8755
  if (excess <= 0) return;
8725
8756
  const parent = arm.g.parent;
8726
8757
  v.b.copy(v.wrist).sub(v.shoulder).normalize().multiplyScalar(Math.min(excess, ARM_REACH.protraction)).add(v.shoulder);
@@ -8741,11 +8772,13 @@ var GameCharactersThree = (() => {
8741
8772
  const arm = arms[side];
8742
8773
  const { upper: up, fore, palm } = lengthsOf(arm);
8743
8774
  wristOf(side, pose, palm);
8744
- protract(arm, up + fore);
8775
+ protract(arm, limitOf(side, up, fore));
8745
8776
  arm.g.getWorldPosition(v.shoulder);
8746
8777
  upper.getWorldQuaternion(q.body);
8747
- v.pole.set(ARM_REACH.elbowPole[0] * arm.outward, ARM_REACH.elbowPole[1], ARM_REACH.elbowPole[2]).applyQuaternion(q.body);
8748
- const most = Math.sqrt(up * up + fore * fore + 2 * up * fore * Math.cos(ARM_REACH.elbowMin));
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;
8749
8782
  const least = Math.sqrt(up * up + fore * fore + 2 * up * fore * Math.cos(ARM_REACH.elbowMax));
8750
8783
  const line = v.c.copy(v.wrist).sub(v.shoulder);
8751
8784
  const distance3 = Math.min(most, Math.max(least, line.length()));
@@ -8770,6 +8803,20 @@ var GameCharactersThree = (() => {
8770
8803
  arm.el.getWorldQuaternion(q.parent);
8771
8804
  arm.hand.quaternion.copy(limitWrist(q.local.copy(q.parent.invert()).multiply(q.hand)));
8772
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
+ };
8773
8820
  const save = () => {
8774
8821
  saved.twist = upper.rotation.y;
8775
8822
  for (const side of ["R", "L"]) {
@@ -8782,14 +8829,24 @@ var GameCharactersThree = (() => {
8782
8829
  }
8783
8830
  };
8784
8831
  return {
8785
- apply(targets) {
8832
+ apply(targets, view) {
8786
8833
  if (!targets || !targets.R && !targets.L) return;
8787
8834
  save();
8788
8835
  applied = true;
8789
8836
  upper.updateWorldMatrix(true, true);
8790
- if (targets.L && targets.R) turnChest(targets.L);
8791
- if (targets.R) reach("R", targets.R);
8792
- if (targets.L) reach("L", targets.L);
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;
8793
8850
  },
8794
8851
  restore() {
8795
8852
  if (!applied) return;
@@ -8807,12 +8864,56 @@ var GameCharactersThree = (() => {
8807
8864
  };
8808
8865
  };
8809
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
+
8810
8908
  // src/player/avatar.ts
8811
8909
  var TWO_PI2 = Math.PI * 2;
8812
8910
  var MAX_GAIT = 1.4;
8813
8911
  var CLIP_BLEND = 0.12;
8814
8912
  var CLIP_CASCADE = 5;
8815
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;
8816
8917
  var HIP_HEIGHT = 0.95;
8817
8918
  var WALK_ARM = 0.28;
8818
8919
  var WALK_BOB = 0.035;
@@ -8822,7 +8923,11 @@ var GameCharactersThree = (() => {
8822
8923
  var REST_ARM_Z = 0.18;
8823
8924
  var CROUCH = { thigh: -0.95, thighWalk: 0.3, shin: 1.9, lean: 0.25, drop: -0.35 };
8824
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 };
8825
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;
8826
8931
  var HAND_OFFSET = { x: 0, y: -0.33, z: 0.04 };
8827
8932
  var hexOf = (value) => typeof value === "string" ? Number.parseInt(value, 16) : value;
8828
8933
  var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
@@ -8852,6 +8957,8 @@ var GameCharactersThree = (() => {
8852
8957
  const fallSeconds = options.fallSeconds ?? FALL_SECONDS2;
8853
8958
  const smooth = options.cascade ?? true;
8854
8959
  const clipCascade = options.clipCascade ?? CLIP_CASCADE;
8960
+ const eyeBob = options.eyeBob ?? EYE_BOB;
8961
+ const eyeOffset = options.eyeOffset ?? EYE_OFFSET;
8855
8962
  const root = new THREE.Group();
8856
8963
  const tilt = new THREE.Group();
8857
8964
  tilt.add(body);
@@ -8911,7 +9018,23 @@ var GameCharactersThree = (() => {
8911
9018
  if (hand.th) hand.th.rotation.z = -hand.side * (0.32 + grip * 0.25);
8912
9019
  hand.hg.rotation.x = wristX;
8913
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;
8914
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
+ })();
8915
9038
  const legSide = (index) => index === 0 ? 1 : -1;
8916
9039
  let cycles = 0;
8917
9040
  let time = 0;
@@ -8970,10 +9093,29 @@ var GameCharactersThree = (() => {
8970
9093
  ...strain ? { legL: -0.5, legR: 0.4, kneeL: 0.55, kneeR: 0.08, drop: -0.05 } : {}
8971
9094
  };
8972
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
+ };
8973
9115
  const holdingPose = (gait, itemUse) => {
8974
- const bob = Math.sin(cycles * TWO_PI2) * 0.05 * gait;
9116
+ const bob2 = Math.sin(cycles * TWO_PI2) * 0.05 * gait;
8975
9117
  if (!itemUse) {
8976
- return { armL: -0.45 + bob, armLz: 0.22, elbL: -1.05 };
9118
+ return { armL: -0.45 + bob2, armLz: 0.22, elbL: -1.05 };
8977
9119
  }
8978
9120
  const { id, position, rotation } = itemUse;
8979
9121
  if (id === "drink" || id === "sip" || id === "chug") {
@@ -8986,7 +9128,7 @@ var GameCharactersThree = (() => {
8986
9128
  headX: -0.26 * tilt3
8987
9129
  };
8988
9130
  }
8989
- if (id === "open" || id === "close") {
9131
+ if (id === "open" || id === "close" || itemUse.gripL !== void 0) {
8990
9132
  const reach = clamp((0.22 - position[0]) / 0.12, 0, 1);
8991
9133
  return {
8992
9134
  armL: -0.45 - 0.35 * reach,
@@ -9085,6 +9227,31 @@ var GameCharactersThree = (() => {
9085
9227
  drop: Math.abs(Math.sin(ph)) * WALK_BOB * gait
9086
9228
  };
9087
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
+ };
9088
9255
  const placeBody = (progress) => {
9089
9256
  const fk = progress * progress * (3 - 2 * progress);
9090
9257
  const flip = fract(cycles * 1.7) >= 0.5 ? 1 : 0;
@@ -9145,13 +9312,27 @@ var GameCharactersThree = (() => {
9145
9312
  profile.bob = (running ? RUN_BOB : WALK_BOB) * gait;
9146
9313
  time = cycles / (walkCadence(Math.max(speed, 0)) * (running ? 1.3 : 1));
9147
9314
  animator.animate(body, time, { speed });
9315
+ bob = rig.fig.position.y - rig.dropBase;
9148
9316
  fall = fallSeconds <= 0 ? down ? 1 : 0 : clamp(fall + (down ? dt : -dt) / fallSeconds, 0, 1);
9149
9317
  resetLegYaw();
9150
- if (down) animator.setPose(rig, downPose());
9151
- else {
9152
- if (running) animator.setPose(rig, runningPose(gait, Boolean(state.armed || state.holding || state.itemUse)));
9153
- 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
+ }
9154
9334
  if (state.push) animator.setPose(rig, pushingPose(state.push));
9335
+ else if (state.door) animator.setPose(rig, doorPose(state.door));
9155
9336
  else if (state.armed) animator.setPose(rig, armed(state.pitch ?? 0, gait, state.hip ?? 0));
9156
9337
  else if (state.holding || state.itemUse) animator.setPose(rig, holdingPose(gait, state.itemUse));
9157
9338
  else if (!running) {
@@ -9163,20 +9344,33 @@ var GameCharactersThree = (() => {
9163
9344
  });
9164
9345
  }
9165
9346
  }
9166
- placeBody(fall);
9167
- stepClip(dt, down, Boolean(state.crouch));
9168
- 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;
9169
9355
  const blow = run?.clip.events?.find((event) => (event.type === "impact" || event.type === "grip") && event.data?.kind)?.data?.kind;
9170
9356
  const ph = cycles * TWO_PI2;
9171
9357
  hands.forEach((hand) => {
9172
9358
  const isRight = hand.side < 0;
9173
9359
  const holdRight = isRight && (!!state.holding || !!state.itemUse);
9174
- const holdLeft = !isRight && (state.itemUse?.id === "open" || state.itemUse?.id === "close");
9175
- 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;
9176
9362
  const wristSwing = holdRight || state.armed || running ? 0 : -Math.min(1, gait) * Math.sin(ph - 0.6) * 0.12 * hand.side;
9177
9363
  setHandGrip(hand, grip, wristSwing);
9178
9364
  });
9365
+ root.updateMatrixWorld(true);
9366
+ root.worldToLocal(eyeSocket.getWorldPosition(eyeLocal));
9367
+ eyeLocal.y -= (1 - eyeBob) * bob;
9179
9368
  },
9369
+ eye(out = new THREE.Vector3()) {
9370
+ root.updateWorldMatrix(true, false);
9371
+ return root.localToWorld(out.copy(eyeLocal));
9372
+ },
9373
+ setView: (mode, camera) => headHider.setView(mode, camera),
9180
9374
  play(clip, playOptions = {}) {
9181
9375
  const found = typeof clip === "string" ? clips.get(clip) : clip;
9182
9376
  if (!found) throw new Error(`Unknown pose clip: ${String(clip)}`);
@@ -9188,10 +9382,11 @@ var GameCharactersThree = (() => {
9188
9382
  if (run) run.target = 0;
9189
9383
  },
9190
9384
  hand: (side) => sockets[side],
9191
- reach(targets) {
9385
+ shoulder: (side) => armOf[side].g,
9386
+ reach(targets, view) {
9192
9387
  if (run || fall > 0) return;
9193
9388
  root.updateMatrixWorld(true);
9194
- armReach.apply(targets);
9389
+ armReach.apply(targets, view);
9195
9390
  },
9196
9391
  on(_type, listener) {
9197
9392
  listeners.add(listener);
@@ -9623,7 +9818,9 @@ var GameCharactersThree = (() => {
9623
9818
  shoulder: [0.16, -0.36, 0.18],
9624
9819
  upper: 0.32,
9625
9820
  fore: 0.3,
9626
- reachShift: 0.35,
9821
+ reachShift: 0.42,
9822
+ reachBend: [1.35, 0.95],
9823
+ elbowView: [[0.96, -0.26, -0.06], null],
9627
9824
  wrist: 0.07,
9628
9825
  elbowPole: [0.45, -1, 0.2],
9629
9826
  shoulderLeft: [-0.07, -0.46, 0.18],
@@ -9650,22 +9847,14 @@ var GameCharactersThree = (() => {
9650
9847
  };
9651
9848
 
9652
9849
  // src/first-person/hand.ts
9653
- var FINGERS = [
9654
- { z: -0.03, lengths: [0.034, 0.024, 0.02], width: 0.016 },
9655
- { z: -0.0105, lengths: [0.038, 0.027, 0.022], width: 0.0165 },
9656
- { z: 9e-3, lengths: [0.035, 0.025, 0.02], width: 0.0155 },
9657
- { z: 0.027, lengths: [0.029, 0.02, 0.018], width: 0.0135 }
9658
- ];
9659
- var PALM = { length: 0.09, thick: 0.026, width: 0.09, wristWidth: 0.066 };
9660
- var FINGER_THICK = 0.0135;
9661
- var CURL = { near: 1.05, mid: 1.25, far: 0.85, spread: 0.06 };
9662
- 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 };
9663
9853
  var createHandMesh = (THREE, skin, wristSkin = skin) => {
9664
9854
  const root = new THREE.Group();
9665
9855
  root.name = "first-person-hand";
9666
9856
  const geometries = [];
9667
- const box2 = (parent, sx, sy, sz, x, y, z, material = skin) => {
9668
- const geometry = new THREE.BoxGeometry(sx, sy, sz);
9857
+ const add = (parent, geometry, material, x = 0, y = 0, z = 0) => {
9669
9858
  geometries.push(geometry);
9670
9859
  const mesh2 = new THREE.Mesh(geometry, material);
9671
9860
  mesh2.position.set(x, y, z);
@@ -9673,64 +9862,26 @@ var GameCharactersThree = (() => {
9673
9862
  parent.add(mesh2);
9674
9863
  return mesh2;
9675
9864
  };
9676
- const palmGeometry = new THREE.BoxGeometry(PALM.length, PALM.thick, PALM.width);
9677
- const palmPosition = palmGeometry.getAttribute("position");
9678
- for (let i = 0; i < palmPosition.count; i++) {
9679
- if (palmPosition.getX(i) < 0) palmPosition.setZ(i, palmPosition.getZ(i) * PALM.wristWidth / PALM.width);
9680
- }
9681
- palmGeometry.computeVertexNormals();
9682
- geometries.push(palmGeometry);
9683
- const palm = new THREE.Mesh(palmGeometry, skin);
9684
- palm.frustumCulled = false;
9685
- root.add(palm);
9686
- 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);
9687
9872
  wristGeometry.rotateZ(Math.PI / 2);
9688
- geometries.push(wristGeometry);
9689
- const wrist = new THREE.Mesh(wristGeometry, wristSkin);
9690
- wrist.position.set(-PALM.length / 2 - 0.018, 0, 0);
9691
- wrist.frustumCulled = false;
9692
- root.add(wrist);
9693
- const joints = [];
9694
- FINGERS.forEach((finger, index) => {
9695
- const segments = [];
9696
- let parent = root;
9697
- let at = PALM.length / 2;
9698
- finger.lengths.forEach((length, k) => {
9699
- const group = new THREE.Group();
9700
- group.position.set(at, k === 0 ? 2e-3 : 0, k === 0 ? finger.z : 0);
9701
- const taper = 1 - k * 0.07;
9702
- box2(group, length, FINGER_THICK * taper, finger.width * taper, length / 2, 0, 0);
9703
- parent.add(group);
9704
- segments.push(group);
9705
- parent = group;
9706
- at = length;
9707
- });
9708
- joints.push({ segments, weight: 1 + index * (CURL.spread / CURL.near) });
9709
- });
9710
- const thumbNear = new THREE.Group();
9711
- thumbNear.rotation.order = "YZX";
9712
- thumbNear.position.set(...THUMB.base);
9713
- box2(thumbNear, THUMB.near, FINGER_THICK * 1.1, THUMB.width, THUMB.near / 2, 0, 0);
9714
- const thumbFar = new THREE.Group();
9715
- thumbFar.position.set(THUMB.near, 0, 0);
9716
- box2(thumbFar, THUMB.far, FINGER_THICK, THUMB.width * 0.9, THUMB.far / 2, 0, 0);
9717
- thumbNear.add(thumbFar);
9718
- root.add(thumbNear);
9873
+ add(root, wristGeometry, wristSkin, -PALM.down - 0.03, 0, 0);
9719
9874
  let lastGrip = 0;
9720
9875
  let indexCurl;
9721
- const curl = [CURL.near, CURL.mid, CURL.far];
9722
- const applyIndex = () => {
9723
- const finger = joints[0];
9724
- if (!finger) return;
9725
- const k = indexCurl === void 0 ? lastGrip : Math.min(1, Math.max(0, indexCurl));
9726
- 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;
9727
9879
  };
9880
+ const applyIndex = () => curlFinger(0, indexCurl === void 0 ? lastGrip : Math.min(1, Math.max(0, indexCurl)));
9728
9881
  const setGrip = (grip) => {
9729
- const k = Math.min(1, Math.max(0, grip));
9730
- lastGrip = k;
9731
- for (const joint of joints) joint.segments.forEach((segment, i) => segment.rotation.z = -curl[i] * k * joint.weight);
9732
- thumbNear.rotation.set(0, 0.55 - 1.45 * k, -0.1 - 0.75 * k);
9733
- 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;
9734
9885
  applyIndex();
9735
9886
  };
9736
9887
  const setIndex = (value) => {
@@ -9752,6 +9903,8 @@ var GameCharactersThree = (() => {
9752
9903
  var DEFAULT_BLEND = 0.18;
9753
9904
  var ELBOW_SEGMENTS = 8;
9754
9905
  var REACH_SLACK = 0.97;
9906
+ var ELBOW_VIEW_MARGIN = 0.86;
9907
+ var ELBOW_VIEW_PASSES = 4;
9755
9908
  var CONTACT_PROBE = 1.4;
9756
9909
  var CONTACT_MIN = 0.12;
9757
9910
  var CONTACT_MARGIN = 5e-3;
@@ -9900,6 +10053,11 @@ var GameCharactersThree = (() => {
9900
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]);
9901
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]);
9902
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
+ };
9903
10061
  const hip = new THREE.Vector3(...rig.hip);
9904
10062
  const euler = new THREE.Euler(0, 0, 0, "YZX");
9905
10063
  const quaternionOf = (r) => new THREE.Quaternion().setFromEuler(euler.set(r[0], r[1], r[2], "YZX"));
@@ -9989,15 +10147,45 @@ var GameCharactersThree = (() => {
9989
10147
  out.scale = base.scale;
9990
10148
  return out;
9991
10149
  };
9992
- 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) => {
9993
10173
  copyPose(arm.drawn, pose);
9994
10174
  const scale = pose.scale;
9995
10175
  fingers.set(1, 0, 0).applyQuaternion(pose.q);
9996
10176
  wristAt.copy(pose.p).addScaledVector(fingers, -rig.wrist * scale);
9997
10177
  shoulderOf(arm.sign, shoulder).add(bobOffset);
9998
- 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);
9999
10184
  if (excess > 0 && rig.reachShift > 0) shoulder.addScaledVector(towards.subVectors(wristAt, shoulder).normalize(), Math.min(excess, rig.reachShift));
10000
10185
  solveTwoBone(shoulder, wristAt, rig.upper, rig.fore, poleOf(arm.sign), bone);
10186
+ finishArm(arm, pose, scale);
10187
+ };
10188
+ const finishArm = (arm, pose, scale) => {
10001
10189
  palmAt.copy(bone.end).addScaledVector(fingers, rig.wrist * scale);
10002
10190
  arm.drawn.p.copy(palmAt);
10003
10191
  arm.anchor.position.copy(palmAt);
@@ -10109,7 +10297,7 @@ var GameCharactersThree = (() => {
10109
10297
  dropped.p.y -= AWAY_DROP * ease2(arm.away);
10110
10298
  pose = dropped;
10111
10299
  }
10112
- drawArm(arm, pose);
10300
+ drawArm(arm, pose, mode === "target" && targets?.elbows !== "body" ? viewOf(state.camera) : null);
10113
10301
  const shown = arm.away < AWAY_HIDDEN;
10114
10302
  for (const part of [arm.anchor, arm.upper, arm.fore, arm.cuff, arm.elbow]) part.visible = shown;
10115
10303
  }
@@ -10157,7 +10345,7 @@ var GameCharactersThree = (() => {
10157
10345
  var isPunch = (clip) => clip.events?.some((event) => event.type === "impact" && event.data?.kind === "punch") ?? false;
10158
10346
  var GUARD_SPEED = 0.3;
10159
10347
  var toRoomAvatar = (avatar, options = {}) => {
10160
- const arms = options.firstPerson === false ? null : createFirstPersonArms(avatar.three, {
10348
+ const arms = options.firstPerson === false || options.firstPerson === void 0 ? null : createFirstPersonArms(avatar.three, {
10161
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 },
10162
10350
  clips: options.firstPersonClips ?? FIRST_PERSON_CLIPS,
10163
10351
  hideWhenIdle: true,
@@ -10178,7 +10366,7 @@ var GameCharactersThree = (() => {
10178
10366
  let guarding = false;
10179
10367
  const stepGuard = (dt, state) => {
10180
10368
  if (!guard) return;
10181
- 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;
10182
10370
  if (strikeLeft > 0) {
10183
10371
  strikeLeft -= dt;
10184
10372
  if (strikeLeft <= 0 && guardLeft > 0 && free) {
@@ -10197,7 +10385,8 @@ var GameCharactersThree = (() => {
10197
10385
  root: avatar.root,
10198
10386
  avatar,
10199
10387
  arms,
10200
- ...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),
10201
10390
  update: (dt, state) => {
10202
10391
  stepGuard(dt, state);
10203
10392
  avatar.update(dt, {
@@ -10209,11 +10398,14 @@ var GameCharactersThree = (() => {
10209
10398
  pitch: -state.pitch,
10210
10399
  ...state.holding !== void 0 ? { holding: state.holding } : {},
10211
10400
  ...state.itemUse !== void 0 ? { itemUse: state.itemUse } : {},
10212
- ...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 } : {}
10213
10404
  });
10214
10405
  },
10215
10406
  hand: (side) => avatar.hand(side),
10216
- reach: (targets) => avatar.reach(targets),
10407
+ shoulder: (side) => avatar.shoulder(side),
10408
+ reach: (targets, view) => avatar.reach(targets, view),
10217
10409
  strike: (clipId, strikeOptions) => {
10218
10410
  const clip = strikes.get(clipId);
10219
10411
  if (!clip) return null;