@adatechnology/game-characters-three 0.3.0 → 0.4.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.
package/dist/index.js CHANGED
@@ -648,6 +648,7 @@ var createRigAnimator = (options = {}) => {
648
648
  };
649
649
 
650
650
  // src/rich/best-builder.ts
651
+ import { markKitOrigin } from "@adatechnology/game-contracts";
651
652
  import { shapeGeometries as shapeGeometries4 } from "@adatechnology/game-shapes-three";
652
653
  import { createBloodStream, createRng, drawAppearance } from "@adatechnology/game-characters";
653
654
  var createBestBuilder = (host, { types, random = Math.random }) => {
@@ -1209,13 +1210,15 @@ var createBestBuilder = (host, { types, random = Math.random }) => {
1209
1210
  root.userData.rig = rig;
1210
1211
  return root;
1211
1212
  }
1212
- return { build, TYPES, setLOD(k) {
1213
+ const marked = (...args) => markKitOrigin(build(...args), { owner: "game-characters-three", kind: "person" });
1214
+ return { build: marked, TYPES, setLOD(k) {
1213
1215
  LK = k;
1214
1216
  SEG = Math.max(5, Math.round(16 * k));
1215
1217
  }, topM: () => topM_ref };
1216
1218
  };
1217
1219
 
1218
1220
  // src/rich/person-builder.ts
1221
+ import { markKitOrigin as markKitOrigin2 } from "@adatechnology/game-contracts";
1219
1222
  import { shapeGeometries as shapeGeometries6 } from "@adatechnology/game-shapes-three";
1220
1223
  import { MOVES, createRng as createRng2 } from "@adatechnology/game-characters";
1221
1224
 
@@ -2440,7 +2443,11 @@ var createPersonBuilder = (host, options = {}) => {
2440
2443
  chars.forEach((c) => pose(c, c.z, 0, 0));
2441
2444
  return { root, chars };
2442
2445
  }
2443
- return { build, pose, zombify, buildPerson };
2446
+ const mark = (c) => {
2447
+ markKitOrigin2(c.root, { owner: "game-characters-three", kind: "person" });
2448
+ return c;
2449
+ };
2450
+ return { build: (sp, idx) => mark(build(sp, idx)), pose, zombify, buildPerson: (p2, zed) => mark(buildPerson(p2, zed)) };
2444
2451
  };
2445
2452
 
2446
2453
  // src/eyes.ts
@@ -4212,7 +4219,17 @@ var player_avatar_default = {
4212
4219
  fem: false,
4213
4220
  life: 1,
4214
4221
  hs: "short",
4215
- hair: "2a1e16"
4222
+ hair: "2a1e16",
4223
+ pose: {
4224
+ armL: 0,
4225
+ armR: 0,
4226
+ elbL: -0.12,
4227
+ elbR: -0.12,
4228
+ armOut: 0.11,
4229
+ lean: 0.04,
4230
+ headX: 0,
4231
+ headZ: 0
4232
+ }
4216
4233
  },
4217
4234
  shirtColor: "3d5a73"
4218
4235
  };
@@ -4223,9 +4240,12 @@ var MAX_GAIT = 1.4;
4223
4240
  var CLIP_BLEND = 0.12;
4224
4241
  var FALL_SECONDS2 = 0.3;
4225
4242
  var HIP_HEIGHT = 0.95;
4226
- var WALK_ARM = 0.45;
4243
+ var WALK_ARM = 0.28;
4227
4244
  var WALK_BOB = 0.035;
4228
4245
  var WALK_LEAN = 0.04;
4246
+ var RUN_LEAN = 0.16;
4247
+ var RUN_BOB = 0.05;
4248
+ var REST_ARM_Z = 0.18;
4229
4249
  var CROUCH = { thigh: -0.95, thighWalk: 0.3, shin: 1.9, lean: 0.25, drop: -0.35 };
4230
4250
  var AIMED = { shoulder: -1.42, perAim: 0.85, swing: 0.02, zRight: -0.54, zLeft: 0.36, elbowRight: -0.04, elbowLeft: -0.12 };
4231
4251
  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 };
@@ -4234,7 +4254,18 @@ var hexOf = (value) => typeof value === "string" ? Number.parseInt(value, 16) :
4234
4254
  var clamp = (value, min, max) => Math.min(max, Math.max(min, value));
4235
4255
  var fract = (value) => value - Math.floor(value);
4236
4256
  var createPlayerAvatar = (THREE, options = {}) => {
4237
- const spec = { ...player_avatar_default.spec, ...options.spec };
4257
+ const pose = {
4258
+ armL: 0,
4259
+ armR: 0,
4260
+ elbL: -0.08,
4261
+ elbR: -0.08,
4262
+ lean: 0.04,
4263
+ headX: 0,
4264
+ headZ: 0,
4265
+ ...player_avatar_default.spec.pose,
4266
+ ...options.spec?.pose
4267
+ };
4268
+ const spec = { ...player_avatar_default.spec, ...options.spec, pose };
4238
4269
  for (const key of ["skin", "hair"]) if (typeof spec[key] === "string") spec[key] = hexOf(spec[key]);
4239
4270
  const best = createBestBuilder(THREE, { types: [{ name: String(spec.type), eye: 0 }], random: createRng4(Number(spec.seed ?? 1)) });
4240
4271
  const body = best.build(spec);
@@ -4269,6 +4300,41 @@ var createPlayerAvatar = (THREE, options = {}) => {
4269
4300
  sockets[side].position.set(HAND_OFFSET.x, HAND_OFFSET.y, HAND_OFFSET.z);
4270
4301
  armOf[side].el.add(sockets[side]);
4271
4302
  }
4303
+ const addMesh = (parent, geometry, material, x = 0, y = 0, z = 0, rx = 0, ry = 0, rz = 0) => {
4304
+ const mesh = new THREE.Mesh(geometry, material);
4305
+ mesh.position.set(x, y, z);
4306
+ if (rx || ry || rz) mesh.rotation.set(rx, ry, rz);
4307
+ mesh.castShadow = true;
4308
+ mesh.receiveShadow = true;
4309
+ parent.add(mesh);
4310
+ return mesh;
4311
+ };
4312
+ const hands = [];
4313
+ rig.arms.forEach((arm, index) => {
4314
+ const el = arm.el;
4315
+ const side = index === 0 ? 1 : -1;
4316
+ const oldHand = meshesOf(el).find((mesh) => Math.abs(mesh.position.y + 0.33) < 0.025);
4317
+ const handMaterial = oldHand?.material ?? new THREE.MeshLambertMaterial({ color: colors.skin });
4318
+ if (oldHand) {
4319
+ oldHand.geometry?.dispose();
4320
+ el.remove(oldHand);
4321
+ }
4322
+ const hg = new THREE.Group();
4323
+ hg.name = side > 0 ? "hand_group_L" : "hand_group_R";
4324
+ hg.position.set(side * 0.02, -0.3, 0.01);
4325
+ el.add(hg);
4326
+ const palm = addMesh(hg, new THREE.SphereGeometry(0.048, 14, 10), handMaterial, 0, -0.035, 0);
4327
+ palm.scale.set(0.6, 1.05, 1);
4328
+ const built = buildHand({ THREE, add: addMesh, hg, handM: handMaterial, s: side, detailed: true });
4329
+ hands.push({ side, hg, fingers: built.fingers, th: built.th });
4330
+ });
4331
+ const setHandGrip = (hand, grip, wristX = 0) => {
4332
+ hand.fingers.forEach((finger, j) => {
4333
+ finger.rotation.z = -hand.side * grip * (j % 2 ? 1.1 : 0.9);
4334
+ });
4335
+ if (hand.th) hand.th.rotation.z = -hand.side * (0.32 + grip * 0.25);
4336
+ hand.hg.rotation.x = wristX;
4337
+ };
4272
4338
  const legRestX = rig.legs.map((leg) => leg.g.position.x);
4273
4339
  const legSide = (index) => index === 0 ? 1 : -1;
4274
4340
  let cycles = 0;
@@ -4310,6 +4376,42 @@ var createPlayerAvatar = (THREE, options = {}) => {
4310
4376
  });
4311
4377
  return { armR: shoulder, armL: shoulder, armRz: AIMED.zRight, armLz: AIMED.zLeft, elbR: AIMED.elbowRight, elbL: AIMED.elbowLeft };
4312
4378
  };
4379
+ const holdingPose = (gait, itemUse) => {
4380
+ const bob = Math.sin(cycles * TWO_PI) * 0.05 * gait;
4381
+ if (!itemUse) {
4382
+ return { armL: -0.45 + bob, armLz: 0.22, elbL: -1.05 };
4383
+ }
4384
+ const { id, position, rotation } = itemUse;
4385
+ if (id === "drink" || id === "sip" || id === "chug") {
4386
+ const raise2 = clamp((position[1] + 0.2) / 0.22, 0, 1);
4387
+ const tilt3 = clamp(Math.abs(rotation[0]) / 1.22, 0, 1.15);
4388
+ return {
4389
+ armL: -0.45 - 0.95 * raise2 - 0.22 * tilt3,
4390
+ armLz: 0.22 + 0.44 * raise2,
4391
+ elbL: -1.05 - 0.85 * raise2 + 0.12 * tilt3,
4392
+ headX: -0.26 * tilt3
4393
+ };
4394
+ }
4395
+ if (id === "open" || id === "close") {
4396
+ const reach = clamp((0.22 - position[0]) / 0.12, 0, 1);
4397
+ return {
4398
+ armL: -0.45 - 0.35 * reach,
4399
+ armLz: 0.22 + 0.28 * reach,
4400
+ elbL: -1.05 - 0.25 * reach,
4401
+ armR: -0.78 * reach,
4402
+ armRz: -0.48 * reach,
4403
+ elbR: -1.25 * reach
4404
+ };
4405
+ }
4406
+ const raise = clamp((position[1] + 0.2) / 0.25, 0, 1);
4407
+ const tilt2 = clamp(Math.abs(rotation[0]) / 1.75, 0, 1.2);
4408
+ const ext = Math.max(raise, tilt2 * 0.75);
4409
+ return {
4410
+ armL: -0.45 - 0.48 * ext,
4411
+ armLz: 0.22 + 0.08 * ext,
4412
+ elbL: -1.05 + 0.38 * ext
4413
+ };
4414
+ };
4313
4415
  const resetLegYaw = () => {
4314
4416
  rig.legs.forEach((leg, index) => {
4315
4417
  leg.g.rotation.y = 0;
@@ -4317,6 +4419,32 @@ var createPlayerAvatar = (THREE, options = {}) => {
4317
4419
  leg.g.position.z = 0;
4318
4420
  });
4319
4421
  };
4422
+ const runningPose = (gait, busyHands = false) => {
4423
+ const ph = cycles * TWO_PI;
4424
+ const sine = Math.sin(ph);
4425
+ const cosine = Math.cos(ph);
4426
+ const legL = sine * 0.65 * gait;
4427
+ const legR = -sine * 0.65 * gait;
4428
+ const kneeL = Math.max(0, -cosine) * 1.15 * gait;
4429
+ const kneeR = Math.max(0, cosine) * 1.15 * gait;
4430
+ const targets = {
4431
+ lean: RUN_LEAN * Math.min(1, gait),
4432
+ drop: Math.abs(cosine) * RUN_BOB * gait,
4433
+ legL,
4434
+ legR,
4435
+ kneeL,
4436
+ kneeR
4437
+ };
4438
+ if (!busyHands) {
4439
+ targets.armR = -0.32 - sine * 0.65 * gait;
4440
+ targets.armL = -0.32 + sine * 0.65 * gait;
4441
+ targets.elbR = -1.25 + Math.max(0, sine) * 0.3 * gait;
4442
+ targets.elbL = -1.25 + Math.max(0, -sine) * 0.3 * gait;
4443
+ targets.armRz = 0.22;
4444
+ targets.armLz = -0.22;
4445
+ }
4446
+ return targets;
4447
+ };
4320
4448
  const crouchPose = (gait) => {
4321
4449
  const swing = (leg) => {
4322
4450
  const legS = legSide(leg);
@@ -4412,23 +4540,44 @@ var createPlayerAvatar = (THREE, options = {}) => {
4412
4540
  update(dt, state = {}) {
4413
4541
  const speed = state.speed ?? 0;
4414
4542
  const moving = speed >= 0.05;
4415
- if (moving) cycles += dt * walkCadence(speed);
4543
+ const down = !!state.down;
4544
+ const running = Boolean(state.running && moving && !down && !state.crouch);
4545
+ if (moving) cycles += dt * (running ? walkCadence(speed) * 1.3 : walkCadence(speed));
4416
4546
  const gait = Math.min(MAX_GAIT, walkGait(speed));
4417
- profile.arm = WALK_ARM * gait;
4418
- profile.bob = WALK_BOB * gait;
4419
- time = cycles / walkCadence(Math.max(speed, 0));
4547
+ profile.arm = (running ? 0.55 : WALK_ARM) * gait;
4548
+ profile.bob = (running ? RUN_BOB : WALK_BOB) * gait;
4549
+ time = cycles / (walkCadence(Math.max(speed, 0)) * (running ? 1.3 : 1));
4420
4550
  animator.animate(body, time, { speed });
4421
- const down = !!state.down;
4422
4551
  fall = fallSeconds <= 0 ? down ? 1 : 0 : clamp(fall + (down ? dt : -dt) / fallSeconds, 0, 1);
4423
4552
  resetLegYaw();
4424
4553
  if (down) animator.setPose(rig, downPose());
4425
4554
  else {
4555
+ if (running) animator.setPose(rig, runningPose(gait, Boolean(state.armed || state.holding || state.itemUse)));
4426
4556
  if (state.crouch) animator.setPose(rig, crouchPose(gait));
4427
4557
  if (state.armed) animator.setPose(rig, armed(state.pitch ?? 0, gait, state.hip ?? 0));
4558
+ else if (state.holding || state.itemUse) animator.setPose(rig, holdingPose(gait, state.itemUse));
4559
+ else if (!running) {
4560
+ animator.setPose(rig, {
4561
+ armRz: REST_ARM_Z,
4562
+ armLz: -REST_ARM_Z,
4563
+ elbR: -0.12,
4564
+ elbL: -0.12
4565
+ });
4566
+ }
4428
4567
  }
4429
4568
  placeBody(fall);
4430
4569
  stepClip(dt, down);
4431
4570
  if (smooth) animator.cascade(rig, dt);
4571
+ const clipId = run ? run.clip.id : null;
4572
+ const ph = cycles * TWO_PI;
4573
+ hands.forEach((hand) => {
4574
+ const isRight = hand.side < 0;
4575
+ const holdRight = isRight && (!!state.holding || !!state.itemUse);
4576
+ const holdLeft = !isRight && (state.itemUse?.id === "open" || state.itemUse?.id === "close");
4577
+ const grip = clipId === "punch_right" ? 1.4 : clipId === "shove_two_hands" ? 0.15 : state.armed || holdRight || holdLeft ? 1.22 : running ? 0.85 : 0.12;
4578
+ const wristSwing = holdRight || state.armed || running ? 0 : -Math.min(1, gait) * Math.sin(ph - 0.6) * 0.12 * hand.side;
4579
+ setHandGrip(hand, grip, wristSwing);
4580
+ });
4432
4581
  },
4433
4582
  play(clip, playOptions = {}) {
4434
4583
  const found = typeof clip === "string" ? clips.get(clip) : clip;
@@ -4466,6 +4615,7 @@ import { mirrorPoseClip } from "@adatechnology/game-characters";
4466
4615
 
4467
4616
  // src/first-person/arms.ts
4468
4617
  import { clipEventsBetween as clipEventsBetween2, sampleFirstPersonClip } from "@adatechnology/game-characters";
4618
+ import { markKitOrigin as markKitOrigin3 } from "@adatechnology/game-contracts";
4469
4619
 
4470
4620
  // src/presets/first-person-arms.json
4471
4621
  var first_person_arms_default = {
@@ -4490,7 +4640,7 @@ var first_person_arms_default = {
4490
4640
  },
4491
4641
  stances: {
4492
4642
  relaxed: {
4493
- R: { p: [0.2, -0.2, -0.36], r: [0.2, 1.45, -0.4], grip: 0.35 },
4643
+ R: { p: [0.2, -0.22, -0.36], r: [0.05, 1.57, -0.05], grip: 0.2 },
4494
4644
  L: "mirror"
4495
4645
  },
4496
4646
  fists: {
@@ -4634,6 +4784,10 @@ var CONTACT_MARGIN = 5e-3;
4634
4784
  var CONTACT_PALM = 0.03;
4635
4785
  var FOOT_LENGTH = 0.24;
4636
4786
  var FOOT_SHOWN = 1e-3;
4787
+ var AWAY_DROP = 0.55;
4788
+ var AWAY_OUT = 7;
4789
+ var AWAY_IN = 20;
4790
+ var AWAY_HIDDEN = 0.97;
4637
4791
  var FILL = 0.3;
4638
4792
  var FIRST_PERSON_STANCES = first_person_arms_default.stances;
4639
4793
  var FIRST_PERSON_RIG = first_person_arms_default.rig;
@@ -4692,7 +4846,7 @@ function createFirstPersonArms(THREE, options = {}) {
4692
4846
  const bob = { ...FIRST_PERSON_BOB, ...options.bob };
4693
4847
  const blendSeconds = options.blend ?? DEFAULT_BLEND;
4694
4848
  const clips = new Map((options.clips ?? []).map((clip) => [clip.id, clip]));
4695
- const root = new THREE.Group();
4849
+ const root = markKitOrigin3(new THREE.Group(), { owner: "game-characters-three", kind: "person", id: "first-person-arms" });
4696
4850
  root.name = "first-person-arms";
4697
4851
  root.userData.viewmodel = true;
4698
4852
  const lit = (color) => new THREE.MeshLambertMaterial({ color, emissive: scaleHex(color, FILL) });
@@ -4747,7 +4901,7 @@ function createFirstPersonArms(THREE, options = {}) {
4747
4901
  const cuff = limb(root, rig.radius.cuff, rig.radius.cuff, 0.03, materials.cuff);
4748
4902
  const elbow = ball(root, rig.radius.elbow * 1.02, materials.sleeve);
4749
4903
  elbow.name = `first-person-elbow-${side}`;
4750
- arms[side] = { side, sign, 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() } };
4904
+ arms[side] = { side, sign, 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 };
4751
4905
  }
4752
4906
  const leg = new THREE.Group();
4753
4907
  leg.name = "first-person-leg";
@@ -4841,6 +4995,7 @@ function createFirstPersonArms(THREE, options = {}) {
4841
4995
  const desired = newPose(THREE);
4842
4996
  const channelPose = newPose(THREE);
4843
4997
  const mixed = newPose(THREE);
4998
+ const dropped = newPose(THREE);
4844
4999
  let indexCurl;
4845
5000
  const bone = createTwoBone(THREE);
4846
5001
  const shoulder = new THREE.Vector3();
@@ -4965,11 +5120,22 @@ function createFirstPersonArms(THREE, options = {}) {
4965
5120
  if (arm.blend >= 1) copyPose(arm.shown, desired);
4966
5121
  else mixPose(THREE, arm.shown, arm.from, desired, ease(arm.blend));
4967
5122
  const channel = sample?.[side];
5123
+ let pose;
4968
5124
  if (channel && channel.w > 0) {
4969
5125
  fromChannel(channel, side, arm.shown, channelPose);
4970
5126
  mixPose(THREE, mixed, arm.shown, channelPose, channel.w);
4971
- drawArm(arm, mode === "target" ? mixed : keepOutside(mixed, handReach(mixed)));
4972
- } else drawArm(arm, mode === "target" ? arm.shown : keepOutside(arm.shown, handReach(arm.shown)));
5127
+ pose = mode === "target" ? mixed : keepOutside(mixed, handReach(mixed));
5128
+ } else pose = mode === "target" ? arm.shown : keepOutside(arm.shown, handReach(arm.shown));
5129
+ const idle = options.hideWhenIdle === true && mode === "rest:relaxed" && !(channel && channel.w > 0);
5130
+ 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;
5131
+ if (arm.away > 0) {
5132
+ copyPose(dropped, pose);
5133
+ dropped.p.y -= AWAY_DROP * ease(arm.away);
5134
+ pose = dropped;
5135
+ }
5136
+ drawArm(arm, pose);
5137
+ const shown = arm.away < AWAY_HIDDEN;
5138
+ for (const part of [arm.anchor, arm.upper, arm.fore, arm.cuff, arm.elbow]) part.visible = shown;
4973
5139
  }
4974
5140
  const foot = sample?.F;
4975
5141
  leg.visible = !!foot && foot.w > FOOT_SHOWN;
@@ -5016,6 +5182,7 @@ var toRoomAvatar = (avatar, options = {}) => {
5016
5182
  const arms = options.firstPerson === false ? null : createFirstPersonArms(avatar.three, {
5017
5183
  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 },
5018
5184
  clips: options.firstPersonClips ?? FIRST_PERSON_CLIPS,
5185
+ hideWhenIdle: true,
5019
5186
  ...options.firstPerson
5020
5187
  });
5021
5188
  const strikes = new Map((options.strikeClips ?? ATTACK_CLIPS).map((clip) => [clip.id, clip]));
@@ -5030,7 +5197,16 @@ var toRoomAvatar = (avatar, options = {}) => {
5030
5197
  avatar,
5031
5198
  arms,
5032
5199
  ...arms ? { firstPerson: { root: arms.root, update: (dt, state) => arms.update(dt, state) } } : {},
5033
- update: (dt, state) => avatar.update(dt, { speed: state.speed, running: state.running, crouch: state.crouch, down: state.down, armed: state.armed, pitch: -state.pitch }),
5200
+ update: (dt, state) => avatar.update(dt, {
5201
+ speed: state.speed,
5202
+ running: state.running,
5203
+ crouch: state.crouch,
5204
+ down: state.down,
5205
+ armed: state.armed,
5206
+ pitch: -state.pitch,
5207
+ ...state.holding !== void 0 ? { holding: state.holding } : {},
5208
+ ...state.itemUse !== void 0 ? { itemUse: state.itemUse } : {}
5209
+ }),
5034
5210
  hand: (side) => avatar.hand(side),
5035
5211
  strike: (clipId, strikeOptions) => {
5036
5212
  const clip = strikes.get(clipId);
@@ -5262,6 +5438,7 @@ export {
5262
5438
  lodAt,
5263
5439
  makeCut,
5264
5440
  makeStump,
5441
+ personSpecOf,
5265
5442
  registerBodyMaterial,
5266
5443
  ringWound,
5267
5444
  solveTwoBone,