@adatechnology/game-characters-three 0.3.0 → 0.3.1

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.
package/dist/index.d.ts CHANGED
@@ -1041,6 +1041,11 @@ type PlayerAvatarOptions = {
1041
1041
  type AvatarEvent = PoseClipEvent & {
1042
1042
  clip: string;
1043
1043
  };
1044
+ type AvatarItemUsePose = {
1045
+ readonly id: string;
1046
+ readonly position: readonly [number, number, number];
1047
+ readonly rotation: readonly [number, number, number];
1048
+ };
1044
1049
  type AvatarUpdate = {
1045
1050
  /** Ground speed, m/s. Ties cadence and swing to the ground (the game's `walkCad` and `walkGait`). */
1046
1051
  speed?: number;
@@ -1054,6 +1059,10 @@ type AvatarUpdate = {
1054
1059
  pitch?: number;
1055
1060
  /** Leg yaw toward the step while armed (the game's `P.hip`, radians). */
1056
1061
  hip?: number;
1062
+ /** Whether the player is carrying an item in the right hand. */
1063
+ holding?: boolean;
1064
+ /** Active scripted item use (`drink`, `sip`, `chug`, `pour`, `open`, `close`, `tilt`). */
1065
+ itemUse?: AvatarItemUsePose | null;
1057
1066
  };
1058
1067
  type PlayPoseOptions = {
1059
1068
  onEvent?: (event: AvatarEvent) => void;
@@ -1169,6 +1178,8 @@ type FirstPersonArmsOptions = {
1169
1178
  clips?: readonly FirstPersonClip[];
1170
1179
  /** Seconds to move from one stance (or from a held tool to the rest) to the next. Default 0.18. */
1171
1180
  blend?: number;
1181
+ /** In the `relaxed` stance with no clip playing (walking, standing, nothing in the hands) the hands drop out of the view and are not drawn; a strike, a shove, a held tool or `fists` bring them back. Default false. */
1182
+ hideWhenIdle?: boolean;
1172
1183
  bob?: Partial<FirstPersonBob>;
1173
1184
  };
1174
1185
  type FirstPersonArmsState = {
@@ -1229,6 +1240,8 @@ type RoomAvatarLike = {
1229
1240
  down: boolean;
1230
1241
  armed: boolean;
1231
1242
  pitch: number;
1243
+ holding?: boolean;
1244
+ itemUse?: AvatarItemUsePose | null;
1232
1245
  }): void;
1233
1246
  hand(side: 'R' | 'L'): THREE_NS.Object3D;
1234
1247
  /** The arms of the first-person view (`RoomAvatar.firstPerson`); absent when `toRoomAvatar` was told to make none. */
@@ -1341,4 +1354,4 @@ type TwoBone = {
1341
1354
  */
1342
1355
  declare const solveTwoBone: (root: THREE_NS.Vector3, target: THREE_NS.Vector3, upper: number, lower: number, pole: THREE_NS.Vector3, out: TwoBone) => TwoBone;
1343
1356
 
1344
- export { AVATAR_AREA_ID, type AreaContext, type AvatarEvent, type AvatarTestOptions, type AvatarUpdate, BAKE_NEUTRAL_POSE, BODY_CHUNK, BODY_MATERIAL_DEF, BODY_SOUNDS, type BakeBuilders, type BakeSpec, type BakedBody, type BestBuilder, type BestBuilderOptions, type BestTypeCard, type BloodAge, type BloodLook, type BloodLookOptions, type BloodSink, type BodyBatch, type BodyBatchOptions, type BodyChunkOptions, type BodyInstance, type BodyJoints, type BodyMaterialParams, type BodySoundRule, type BodyVariant, type BuildInstancedBodyParams, CHARACTERS_AREA_ID, CHARACTER_SOUNDS, CHARACTER_SOUND_BANK, type CharacterSound, type CharacterSoundOptions, type CharacterSoundSink, type CharacterSoundStyle, type CharactersTestOptions, EXTRAS, type EyeOptions, type Eyes, FACE_FEATURES, FIRST_PERSON_BOB, FIRST_PERSON_COLORS, FIRST_PERSON_RIG, FIRST_PERSON_STANCES, type FilmSink, type FirstPersonArms, type FirstPersonArmsOptions, type FirstPersonArmsState, type FirstPersonBob, type FirstPersonColors, type FirstPersonEvent, type FirstPersonRig, type FluidsBridge, type FluidsBridgeOptions, type FlyingLimb, type FlyingLimbs, type FlyingLimbsOptions, type FlyingPiece, type FlyingPieces, type FlyingPiecesOptions, HAND_ITEMS, HATS, HIT_PRESETS, type HitPreset, LOD_BAKE, LOD_BATCH_CAPACITY, LOD_CASTS_SHADOW, LOD_DISTANCES, LOD_SEGMENTS, type LimbEffects, type LimbLaunch, PENDULUM, PERSON_ITEM_PREFIX, PLAYER_SOUNDS, type Pendulum, type PersonBuilder, type PersonBuilderOptions, type PlayPoseOptions, type PlayerAvatar, type PlayerAvatarColors, type PlayerAvatarOptions, type PlayerAvatarSpec, type PoseTargets, type ProfessionCard, type RestPose, type RichRig, type RigAnimator, type RigAnimatorOptions, type RoomAvatarLike, type StanceDef, type Three, type ToRoomAvatarOptions, type TwoBone, UNDEAD_VOICES, UNDEAD_VOICE_GROUP, UNDEAD_VOICE_SOURCES, type UndeadCard, type UndeadLike, type UndeadVoice, type VariantType, type VoiceMoment, type VoiceStep, type WoundBody, type WoundMaterials, type WoundNode, type WoundSpray, type Wounds, type WoundsOptions, ZOMBIE_ITEM_PREFIX, bakeBody, bakeVariants, blobGeometry, blobRadii, bloodLookOfAge, buildInstancedBody, createAvatarTestModule, createBestBuilder, createBodyBatch, createBodyChunk, createBodyMaterial, createCharacterSound, createCharactersTestModule, createEye, createFirstPersonArms, createFluidsBridge, createFlyingLimbs, createFlyingPieces, createGlowTexture, createPendulum, createPersonBuilder, createPlayerAvatar, createRigAnimator, createVariantBatches, createWoundMaterials, createWounds, exitWound, explodeChunks, findEyes, gouge, jointsOf, lodAt, makeCut, makeStump, registerBodyMaterial, ringWound, solveTwoBone, starGeometry, stepPendulum, toRoomAvatar, woundBodyOf, woundDecal };
1357
+ export { AVATAR_AREA_ID, type AreaContext, type AvatarEvent, type AvatarItemUsePose, type AvatarTestOptions, type AvatarUpdate, BAKE_NEUTRAL_POSE, BODY_CHUNK, BODY_MATERIAL_DEF, BODY_SOUNDS, type BakeBuilders, type BakeSpec, type BakedBody, type BestBuilder, type BestBuilderOptions, type BestTypeCard, type BloodAge, type BloodLook, type BloodLookOptions, type BloodSink, type BodyBatch, type BodyBatchOptions, type BodyChunkOptions, type BodyInstance, type BodyJoints, type BodyMaterialParams, type BodySoundRule, type BodyVariant, type BuildInstancedBodyParams, CHARACTERS_AREA_ID, CHARACTER_SOUNDS, CHARACTER_SOUND_BANK, type CharacterSound, type CharacterSoundOptions, type CharacterSoundSink, type CharacterSoundStyle, type CharactersTestOptions, EXTRAS, type EyeOptions, type Eyes, FACE_FEATURES, FIRST_PERSON_BOB, FIRST_PERSON_COLORS, FIRST_PERSON_RIG, FIRST_PERSON_STANCES, type FilmSink, type FirstPersonArms, type FirstPersonArmsOptions, type FirstPersonArmsState, type FirstPersonBob, type FirstPersonColors, type FirstPersonEvent, type FirstPersonRig, type FluidsBridge, type FluidsBridgeOptions, type FlyingLimb, type FlyingLimbs, type FlyingLimbsOptions, type FlyingPiece, type FlyingPieces, type FlyingPiecesOptions, HAND_ITEMS, HATS, HIT_PRESETS, type HitPreset, LOD_BAKE, LOD_BATCH_CAPACITY, LOD_CASTS_SHADOW, LOD_DISTANCES, LOD_SEGMENTS, type LimbEffects, type LimbLaunch, PENDULUM, PERSON_ITEM_PREFIX, PLAYER_SOUNDS, type Pendulum, type PersonBuilder, type PersonBuilderOptions, type PlayPoseOptions, type PlayerAvatar, type PlayerAvatarColors, type PlayerAvatarOptions, type PlayerAvatarSpec, type PoseTargets, type ProfessionCard, type RestPose, type RichRig, type RigAnimator, type RigAnimatorOptions, type RoomAvatarLike, type StanceDef, type Three, type ToRoomAvatarOptions, type TwoBone, UNDEAD_VOICES, UNDEAD_VOICE_GROUP, UNDEAD_VOICE_SOURCES, type UndeadCard, type UndeadLike, type UndeadVoice, type VariantType, type VoiceMoment, type VoiceStep, type WoundBody, type WoundMaterials, type WoundNode, type WoundSpray, type Wounds, type WoundsOptions, ZOMBIE_ITEM_PREFIX, bakeBody, bakeVariants, blobGeometry, blobRadii, bloodLookOfAge, buildInstancedBody, createAvatarTestModule, createBestBuilder, createBodyBatch, createBodyChunk, createBodyMaterial, createCharacterSound, createCharactersTestModule, createEye, createFirstPersonArms, createFluidsBridge, createFlyingLimbs, createFlyingPieces, createGlowTexture, createPendulum, createPersonBuilder, createPlayerAvatar, createRigAnimator, createVariantBatches, createWoundMaterials, createWounds, exitWound, explodeChunks, findEyes, gouge, jointsOf, lodAt, makeCut, makeStump, registerBodyMaterial, ringWound, solveTwoBone, starGeometry, stepPendulum, toRoomAvatar, woundBodyOf, woundDecal };
@@ -8608,7 +8608,17 @@ var GameCharactersThree = (() => {
8608
8608
  fem: false,
8609
8609
  life: 1,
8610
8610
  hs: "short",
8611
- hair: "2a1e16"
8611
+ hair: "2a1e16",
8612
+ pose: {
8613
+ armL: 0,
8614
+ armR: 0,
8615
+ elbL: -0.12,
8616
+ elbR: -0.12,
8617
+ armOut: 0.11,
8618
+ lean: 0.04,
8619
+ headX: 0,
8620
+ headZ: 0
8621
+ }
8612
8622
  },
8613
8623
  shirtColor: "3d5a73"
8614
8624
  };
@@ -8619,9 +8629,12 @@ var GameCharactersThree = (() => {
8619
8629
  var CLIP_BLEND = 0.12;
8620
8630
  var FALL_SECONDS2 = 0.3;
8621
8631
  var HIP_HEIGHT = 0.95;
8622
- var WALK_ARM = 0.45;
8632
+ var WALK_ARM = 0.28;
8623
8633
  var WALK_BOB = 0.035;
8624
8634
  var WALK_LEAN = 0.04;
8635
+ var RUN_LEAN = 0.16;
8636
+ var RUN_BOB = 0.05;
8637
+ var REST_ARM_Z = 0.18;
8625
8638
  var CROUCH = { thigh: -0.95, thighWalk: 0.3, shin: 1.9, lean: 0.25, drop: -0.35 };
8626
8639
  var AIMED = { shoulder: -1.42, perAim: 0.85, swing: 0.02, zRight: -0.54, zLeft: 0.36, elbowRight: -0.04, elbowLeft: -0.12 };
8627
8640
  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 };
@@ -8630,7 +8643,18 @@ var GameCharactersThree = (() => {
8630
8643
  var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
8631
8644
  var fract = (value) => value - Math.floor(value);
8632
8645
  var createPlayerAvatar = (THREE, options = {}) => {
8633
- const spec = { ...player_avatar_default.spec, ...options.spec };
8646
+ const pose = {
8647
+ armL: 0,
8648
+ armR: 0,
8649
+ elbL: -0.08,
8650
+ elbR: -0.08,
8651
+ lean: 0.04,
8652
+ headX: 0,
8653
+ headZ: 0,
8654
+ ...player_avatar_default.spec.pose,
8655
+ ...options.spec?.pose
8656
+ };
8657
+ const spec = { ...player_avatar_default.spec, ...options.spec, pose };
8634
8658
  for (const key of ["skin", "hair"]) if (typeof spec[key] === "string") spec[key] = hexOf(spec[key]);
8635
8659
  const best = createBestBuilder(THREE, { types: [{ name: String(spec.type), eye: 0 }], random: createRng(Number(spec.seed ?? 1)) });
8636
8660
  const body = best.build(spec);
@@ -8665,6 +8689,41 @@ var GameCharactersThree = (() => {
8665
8689
  sockets[side].position.set(HAND_OFFSET.x, HAND_OFFSET.y, HAND_OFFSET.z);
8666
8690
  armOf[side].el.add(sockets[side]);
8667
8691
  }
8692
+ const addMesh = (parent, geometry, material, x = 0, y = 0, z = 0, rx = 0, ry = 0, rz = 0) => {
8693
+ const mesh2 = new THREE.Mesh(geometry, material);
8694
+ mesh2.position.set(x, y, z);
8695
+ if (rx || ry || rz) mesh2.rotation.set(rx, ry, rz);
8696
+ mesh2.castShadow = true;
8697
+ mesh2.receiveShadow = true;
8698
+ parent.add(mesh2);
8699
+ return mesh2;
8700
+ };
8701
+ const hands = [];
8702
+ rig.arms.forEach((arm, index) => {
8703
+ const el = arm.el;
8704
+ const side = index === 0 ? 1 : -1;
8705
+ const oldHand = meshesOf(el).find((mesh2) => Math.abs(mesh2.position.y + 0.33) < 0.025);
8706
+ const handMaterial = oldHand?.material ?? new THREE.MeshLambertMaterial({ color: colors.skin });
8707
+ if (oldHand) {
8708
+ oldHand.geometry?.dispose();
8709
+ el.remove(oldHand);
8710
+ }
8711
+ const hg = new THREE.Group();
8712
+ hg.name = side > 0 ? "hand_group_L" : "hand_group_R";
8713
+ hg.position.set(side * 0.02, -0.3, 0.01);
8714
+ el.add(hg);
8715
+ const palm = addMesh(hg, new THREE.SphereGeometry(0.048, 14, 10), handMaterial, 0, -0.035, 0);
8716
+ palm.scale.set(0.6, 1.05, 1);
8717
+ const built = buildHand({ THREE, add: addMesh, hg, handM: handMaterial, s: side, detailed: true });
8718
+ hands.push({ side, hg, fingers: built.fingers, th: built.th });
8719
+ });
8720
+ const setHandGrip = (hand, grip, wristX = 0) => {
8721
+ hand.fingers.forEach((finger, j) => {
8722
+ finger.rotation.z = -hand.side * grip * (j % 2 ? 1.1 : 0.9);
8723
+ });
8724
+ if (hand.th) hand.th.rotation.z = -hand.side * (0.32 + grip * 0.25);
8725
+ hand.hg.rotation.x = wristX;
8726
+ };
8668
8727
  const legRestX = rig.legs.map((leg) => leg.g.position.x);
8669
8728
  const legSide = (index) => index === 0 ? 1 : -1;
8670
8729
  let cycles = 0;
@@ -8706,6 +8765,42 @@ var GameCharactersThree = (() => {
8706
8765
  });
8707
8766
  return { armR: shoulder, armL: shoulder, armRz: AIMED.zRight, armLz: AIMED.zLeft, elbR: AIMED.elbowRight, elbL: AIMED.elbowLeft };
8708
8767
  };
8768
+ const holdingPose = (gait, itemUse) => {
8769
+ const bob = Math.sin(cycles * TWO_PI2) * 0.05 * gait;
8770
+ if (!itemUse) {
8771
+ return { armL: -0.45 + bob, armLz: 0.22, elbL: -1.05 };
8772
+ }
8773
+ const { id, position, rotation } = itemUse;
8774
+ if (id === "drink" || id === "sip" || id === "chug") {
8775
+ const raise2 = clamp((position[1] + 0.2) / 0.22, 0, 1);
8776
+ const tilt3 = clamp(Math.abs(rotation[0]) / 1.22, 0, 1.15);
8777
+ return {
8778
+ armL: -0.45 - 0.95 * raise2 - 0.22 * tilt3,
8779
+ armLz: 0.22 + 0.44 * raise2,
8780
+ elbL: -1.05 - 0.85 * raise2 + 0.12 * tilt3,
8781
+ headX: -0.26 * tilt3
8782
+ };
8783
+ }
8784
+ if (id === "open" || id === "close") {
8785
+ const reach = clamp((0.22 - position[0]) / 0.12, 0, 1);
8786
+ return {
8787
+ armL: -0.45 - 0.35 * reach,
8788
+ armLz: 0.22 + 0.28 * reach,
8789
+ elbL: -1.05 - 0.25 * reach,
8790
+ armR: -0.78 * reach,
8791
+ armRz: -0.48 * reach,
8792
+ elbR: -1.25 * reach
8793
+ };
8794
+ }
8795
+ const raise = clamp((position[1] + 0.2) / 0.25, 0, 1);
8796
+ const tilt2 = clamp(Math.abs(rotation[0]) / 1.75, 0, 1.2);
8797
+ const ext = Math.max(raise, tilt2 * 0.75);
8798
+ return {
8799
+ armL: -0.45 - 0.48 * ext,
8800
+ armLz: 0.22 + 0.08 * ext,
8801
+ elbL: -1.05 + 0.38 * ext
8802
+ };
8803
+ };
8709
8804
  const resetLegYaw = () => {
8710
8805
  rig.legs.forEach((leg, index) => {
8711
8806
  leg.g.rotation.y = 0;
@@ -8713,6 +8808,32 @@ var GameCharactersThree = (() => {
8713
8808
  leg.g.position.z = 0;
8714
8809
  });
8715
8810
  };
8811
+ const runningPose = (gait, busyHands = false) => {
8812
+ const ph = cycles * TWO_PI2;
8813
+ const sine = Math.sin(ph);
8814
+ const cosine = Math.cos(ph);
8815
+ const legL = sine * 0.65 * gait;
8816
+ const legR = -sine * 0.65 * gait;
8817
+ const kneeL = Math.max(0, -cosine) * 1.15 * gait;
8818
+ const kneeR = Math.max(0, cosine) * 1.15 * gait;
8819
+ const targets = {
8820
+ lean: RUN_LEAN * Math.min(1, gait),
8821
+ drop: Math.abs(cosine) * RUN_BOB * gait,
8822
+ legL,
8823
+ legR,
8824
+ kneeL,
8825
+ kneeR
8826
+ };
8827
+ if (!busyHands) {
8828
+ targets.armR = -0.32 - sine * 0.65 * gait;
8829
+ targets.armL = -0.32 + sine * 0.65 * gait;
8830
+ targets.elbR = -1.25 + Math.max(0, sine) * 0.3 * gait;
8831
+ targets.elbL = -1.25 + Math.max(0, -sine) * 0.3 * gait;
8832
+ targets.armRz = 0.22;
8833
+ targets.armLz = -0.22;
8834
+ }
8835
+ return targets;
8836
+ };
8716
8837
  const crouchPose = (gait) => {
8717
8838
  const swing = (leg) => {
8718
8839
  const legS = legSide(leg);
@@ -8808,23 +8929,44 @@ var GameCharactersThree = (() => {
8808
8929
  update(dt, state = {}) {
8809
8930
  const speed = state.speed ?? 0;
8810
8931
  const moving = speed >= 0.05;
8811
- if (moving) cycles += dt * walkCadence(speed);
8932
+ const down = !!state.down;
8933
+ const running = Boolean(state.running && moving && !down && !state.crouch);
8934
+ if (moving) cycles += dt * (running ? walkCadence(speed) * 1.3 : walkCadence(speed));
8812
8935
  const gait = Math.min(MAX_GAIT, walkGait(speed));
8813
- profile.arm = WALK_ARM * gait;
8814
- profile.bob = WALK_BOB * gait;
8815
- time = cycles / walkCadence(Math.max(speed, 0));
8936
+ profile.arm = (running ? 0.55 : WALK_ARM) * gait;
8937
+ profile.bob = (running ? RUN_BOB : WALK_BOB) * gait;
8938
+ time = cycles / (walkCadence(Math.max(speed, 0)) * (running ? 1.3 : 1));
8816
8939
  animator.animate(body, time, { speed });
8817
- const down = !!state.down;
8818
8940
  fall = fallSeconds <= 0 ? down ? 1 : 0 : clamp(fall + (down ? dt : -dt) / fallSeconds, 0, 1);
8819
8941
  resetLegYaw();
8820
8942
  if (down) animator.setPose(rig, downPose());
8821
8943
  else {
8944
+ if (running) animator.setPose(rig, runningPose(gait, Boolean(state.armed || state.holding || state.itemUse)));
8822
8945
  if (state.crouch) animator.setPose(rig, crouchPose(gait));
8823
8946
  if (state.armed) animator.setPose(rig, armed(state.pitch ?? 0, gait, state.hip ?? 0));
8947
+ else if (state.holding || state.itemUse) animator.setPose(rig, holdingPose(gait, state.itemUse));
8948
+ else if (!running) {
8949
+ animator.setPose(rig, {
8950
+ armRz: REST_ARM_Z,
8951
+ armLz: -REST_ARM_Z,
8952
+ elbR: -0.12,
8953
+ elbL: -0.12
8954
+ });
8955
+ }
8824
8956
  }
8825
8957
  placeBody(fall);
8826
8958
  stepClip(dt, down);
8827
8959
  if (smooth) animator.cascade(rig, dt);
8960
+ const clipId = run ? run.clip.id : null;
8961
+ const ph = cycles * TWO_PI2;
8962
+ hands.forEach((hand) => {
8963
+ const isRight = hand.side < 0;
8964
+ const holdRight = isRight && (!!state.holding || !!state.itemUse);
8965
+ const holdLeft = !isRight && (state.itemUse?.id === "open" || state.itemUse?.id === "close");
8966
+ const grip = clipId === "punch_right" ? 1.4 : clipId === "shove_two_hands" ? 0.15 : state.armed || holdRight || holdLeft ? 1.22 : running ? 0.85 : 0.12;
8967
+ const wristSwing = holdRight || state.armed || running ? 0 : -Math.min(1, gait) * Math.sin(ph - 0.6) * 0.12 * hand.side;
8968
+ setHandGrip(hand, grip, wristSwing);
8969
+ });
8828
8970
  },
8829
8971
  play(clip, playOptions = {}) {
8830
8972
  const found = typeof clip === "string" ? clips.get(clip) : clip;
@@ -9152,7 +9294,7 @@ var GameCharactersThree = (() => {
9152
9294
  },
9153
9295
  stances: {
9154
9296
  relaxed: {
9155
- R: { p: [0.2, -0.2, -0.36], r: [0.2, 1.45, -0.4], grip: 0.35 },
9297
+ R: { p: [0.2, -0.22, -0.36], r: [0.05, 1.57, -0.05], grip: 0.2 },
9156
9298
  L: "mirror"
9157
9299
  },
9158
9300
  fists: {
@@ -9296,6 +9438,10 @@ var GameCharactersThree = (() => {
9296
9438
  var CONTACT_PALM = 0.03;
9297
9439
  var FOOT_LENGTH = 0.24;
9298
9440
  var FOOT_SHOWN = 1e-3;
9441
+ var AWAY_DROP = 0.55;
9442
+ var AWAY_OUT = 7;
9443
+ var AWAY_IN = 20;
9444
+ var AWAY_HIDDEN = 0.97;
9299
9445
  var FILL = 0.3;
9300
9446
  var FIRST_PERSON_STANCES = first_person_arms_default.stances;
9301
9447
  var FIRST_PERSON_RIG = first_person_arms_default.rig;
@@ -9409,7 +9555,7 @@ var GameCharactersThree = (() => {
9409
9555
  const cuff = limb2(root, rig.radius.cuff, rig.radius.cuff, 0.03, materials.cuff);
9410
9556
  const elbow = ball(root, rig.radius.elbow * 1.02, materials.sleeve);
9411
9557
  elbow.name = `first-person-elbow-${side}`;
9412
- arms[side] = { side, sign: sign2, hand, anchor, upper, fore, cuff, elbow, shown: newPose(THREE), from: newPose(THREE), mode: null, blend: 1, drawn: newPose(THREE), joints: { shoulder: new THREE.Vector3(), elbow: new THREE.Vector3(), wrist: new THREE.Vector3() } };
9558
+ arms[side] = { side, sign: sign2, hand, anchor, upper, fore, cuff, elbow, shown: newPose(THREE), from: newPose(THREE), mode: null, blend: 1, drawn: newPose(THREE), joints: { shoulder: new THREE.Vector3(), elbow: new THREE.Vector3(), wrist: new THREE.Vector3() }, away: options.hideWhenIdle ? 1 : 0 };
9413
9559
  }
9414
9560
  const leg = new THREE.Group();
9415
9561
  leg.name = "first-person-leg";
@@ -9503,6 +9649,7 @@ var GameCharactersThree = (() => {
9503
9649
  const desired = newPose(THREE);
9504
9650
  const channelPose = newPose(THREE);
9505
9651
  const mixed = newPose(THREE);
9652
+ const dropped = newPose(THREE);
9506
9653
  let indexCurl;
9507
9654
  const bone = createTwoBone(THREE);
9508
9655
  const shoulder = new THREE.Vector3();
@@ -9627,11 +9774,22 @@ var GameCharactersThree = (() => {
9627
9774
  if (arm.blend >= 1) copyPose(arm.shown, desired);
9628
9775
  else mixPose(THREE, arm.shown, arm.from, desired, ease2(arm.blend));
9629
9776
  const channel = sample?.[side];
9777
+ let pose;
9630
9778
  if (channel && channel.w > 0) {
9631
9779
  fromChannel(channel, side, arm.shown, channelPose);
9632
9780
  mixPose(THREE, mixed, arm.shown, channelPose, channel.w);
9633
- drawArm(arm, mode === "target" ? mixed : keepOutside(mixed, handReach(mixed)));
9634
- } else drawArm(arm, mode === "target" ? arm.shown : keepOutside(arm.shown, handReach(arm.shown)));
9781
+ pose = mode === "target" ? mixed : keepOutside(mixed, handReach(mixed));
9782
+ } else pose = mode === "target" ? arm.shown : keepOutside(arm.shown, handReach(arm.shown));
9783
+ const idle = options.hideWhenIdle === true && mode === "rest:relaxed" && !(channel && channel.w > 0);
9784
+ arm.away = options.hideWhenIdle ? Math.min(1, Math.max(0, arm.away + ((idle ? 1 : 0) - arm.away) * Math.min(1, dt * (idle ? AWAY_OUT : AWAY_IN)))) : 0;
9785
+ if (arm.away > 0) {
9786
+ copyPose(dropped, pose);
9787
+ dropped.p.y -= AWAY_DROP * ease2(arm.away);
9788
+ pose = dropped;
9789
+ }
9790
+ drawArm(arm, pose);
9791
+ const shown = arm.away < AWAY_HIDDEN;
9792
+ for (const part of [arm.anchor, arm.upper, arm.fore, arm.cuff, arm.elbow]) part.visible = shown;
9635
9793
  }
9636
9794
  const foot = sample?.F;
9637
9795
  leg.visible = !!foot && foot.w > FOOT_SHOWN;
@@ -9678,6 +9836,7 @@ var GameCharactersThree = (() => {
9678
9836
  const arms = options.firstPerson === false ? null : createFirstPersonArms(avatar.three, {
9679
9837
  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 },
9680
9838
  clips: options.firstPersonClips ?? FIRST_PERSON_CLIPS,
9839
+ hideWhenIdle: true,
9681
9840
  ...options.firstPerson
9682
9841
  });
9683
9842
  const strikes = new Map((options.strikeClips ?? ATTACK_CLIPS).map((clip) => [clip.id, clip]));
@@ -9692,7 +9851,16 @@ var GameCharactersThree = (() => {
9692
9851
  avatar,
9693
9852
  arms,
9694
9853
  ...arms ? { firstPerson: { root: arms.root, update: (dt, state) => arms.update(dt, state) } } : {},
9695
- update: (dt, state) => avatar.update(dt, { speed: state.speed, running: state.running, crouch: state.crouch, down: state.down, armed: state.armed, pitch: -state.pitch }),
9854
+ update: (dt, state) => avatar.update(dt, {
9855
+ speed: state.speed,
9856
+ running: state.running,
9857
+ crouch: state.crouch,
9858
+ down: state.down,
9859
+ armed: state.armed,
9860
+ pitch: -state.pitch,
9861
+ ...state.holding !== void 0 ? { holding: state.holding } : {},
9862
+ ...state.itemUse !== void 0 ? { itemUse: state.itemUse } : {}
9863
+ }),
9696
9864
  hand: (side) => avatar.hand(side),
9697
9865
  strike: (clipId, strikeOptions) => {
9698
9866
  const clip = strikes.get(clipId);