@adatechnology/game-characters-three 0.4.1 → 0.5.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
@@ -4065,6 +4065,7 @@ function createCharactersTestModule(options = {}) {
4065
4065
  return {
4066
4066
  id: CHARACTERS_AREA_ID,
4067
4067
  name: text.name,
4068
+ ground: { surface: "concrete_slab" },
4068
4069
  group: text.group,
4069
4070
  get entries() {
4070
4071
  return entries;
@@ -4238,6 +4239,7 @@ var player_avatar_default = {
4238
4239
  var TWO_PI = Math.PI * 2;
4239
4240
  var MAX_GAIT = 1.4;
4240
4241
  var CLIP_BLEND = 0.12;
4242
+ var CLIP_CASCADE = 5;
4241
4243
  var FALL_SECONDS2 = 0.3;
4242
4244
  var HIP_HEIGHT = 0.95;
4243
4245
  var WALK_ARM = 0.28;
@@ -4277,6 +4279,7 @@ var createPlayerAvatar = (THREE, options = {}) => {
4277
4279
  const blendDefault = options.blend ?? CLIP_BLEND;
4278
4280
  const fallSeconds = options.fallSeconds ?? FALL_SECONDS2;
4279
4281
  const smooth = options.cascade ?? true;
4282
+ const clipCascade = options.clipCascade ?? CLIP_CASCADE;
4280
4283
  const root = new THREE.Group();
4281
4284
  const tilt = new THREE.Group();
4282
4285
  tilt.add(body);
@@ -4376,6 +4379,24 @@ var createPlayerAvatar = (THREE, options = {}) => {
4376
4379
  });
4377
4380
  return { armR: shoulder, armL: shoulder, armRz: AIMED.zRight, armLz: AIMED.zLeft, elbR: AIMED.elbowRight, elbL: AIMED.elbowLeft };
4378
4381
  };
4382
+ const pushingPose = (push) => {
4383
+ const on = push.phase === "grip" ? Math.min(1, push.progress * 2.2) : 1;
4384
+ const strain = push.phase === "strain" ? 1 : 0;
4385
+ const shake = strain * Math.sin(time * 40) * 0.025;
4386
+ const raise = -1.5 * on;
4387
+ const elbow = -0.9 + 0.72 * (push.phase === "grip" ? push.progress : 1) + strain * 0.12;
4388
+ return {
4389
+ lean: (0.2 + 0.2 * strain) * on,
4390
+ headX: (0.05 + 0.08 * strain) * on,
4391
+ armR: raise + shake,
4392
+ armL: raise - shake,
4393
+ armRz: -0.08 * on,
4394
+ armLz: 0.08 * on,
4395
+ elbR: elbow,
4396
+ elbL: elbow,
4397
+ ...strain ? { legL: -0.5, legR: 0.4, kneeL: 0.55, kneeR: 0.08, drop: -0.05 } : {}
4398
+ };
4399
+ };
4379
4400
  const holdingPose = (gait, itemUse) => {
4380
4401
  const bob = Math.sin(cycles * TWO_PI) * 0.05 * gait;
4381
4402
  if (!itemUse) {
@@ -4503,7 +4524,7 @@ var createPlayerAvatar = (THREE, options = {}) => {
4503
4524
  const fireClipEvents = (r, from, to) => {
4504
4525
  for (const event of clipEventsBetween(r.clip, from, to)) emit({ ...event, clip: r.clip.id }, r.onEvent);
4505
4526
  };
4506
- const stepClip = (dt, down) => {
4527
+ const stepClip = (dt, down, crouch) => {
4507
4528
  if (!run) return;
4508
4529
  const r = run;
4509
4530
  const from = r.time;
@@ -4523,7 +4544,9 @@ var createPlayerAvatar = (THREE, options = {}) => {
4523
4544
  return;
4524
4545
  }
4525
4546
  if (down) return;
4526
- const sampled = filterPoseChannels(samplePoseClip(r.clip, Math.min(r.time, duration)), r.clip.channels ?? "full");
4547
+ const keepsCrouch = crouch && !r.clip.events?.some((event) => event.data?.kind === "kick");
4548
+ const channels = r.clip.channels ?? "full";
4549
+ const sampled = filterPoseChannels(filterPoseChannels(samplePoseClip(r.clip, Math.min(r.time, duration)), channels), keepsCrouch ? "upper" : "full");
4527
4550
  const current = readPose();
4528
4551
  const blended = {};
4529
4552
  for (const [name, value] of Object.entries(sampled)) if (name in current) blended[name] = current[name] + (value - current[name]) * r.weight;
@@ -4554,7 +4577,8 @@ var createPlayerAvatar = (THREE, options = {}) => {
4554
4577
  else {
4555
4578
  if (running) animator.setPose(rig, runningPose(gait, Boolean(state.armed || state.holding || state.itemUse)));
4556
4579
  if (state.crouch) animator.setPose(rig, crouchPose(gait));
4557
- if (state.armed) animator.setPose(rig, armed(state.pitch ?? 0, gait, state.hip ?? 0));
4580
+ if (state.push) animator.setPose(rig, pushingPose(state.push));
4581
+ else if (state.armed) animator.setPose(rig, armed(state.pitch ?? 0, gait, state.hip ?? 0));
4558
4582
  else if (state.holding || state.itemUse) animator.setPose(rig, holdingPose(gait, state.itemUse));
4559
4583
  else if (!running) {
4560
4584
  animator.setPose(rig, {
@@ -4566,15 +4590,15 @@ var createPlayerAvatar = (THREE, options = {}) => {
4566
4590
  }
4567
4591
  }
4568
4592
  placeBody(fall);
4569
- stepClip(dt, down);
4570
- if (smooth) animator.cascade(rig, dt);
4571
- const clipId = run ? run.clip.id : null;
4593
+ stepClip(dt, down, Boolean(state.crouch));
4594
+ if (smooth) animator.cascade(rig, run && !run.clip.loop ? dt * clipCascade : dt);
4595
+ const blow = run?.clip.events?.find((event) => (event.type === "impact" || event.type === "grip") && event.data?.kind)?.data?.kind;
4572
4596
  const ph = cycles * TWO_PI;
4573
4597
  hands.forEach((hand) => {
4574
4598
  const isRight = hand.side < 0;
4575
4599
  const holdRight = isRight && (!!state.holding || !!state.itemUse);
4576
4600
  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;
4601
+ const grip = blow === "punch" ? 1.4 : blow === "shove" ? 0.15 : state.armed || holdRight || holdLeft ? 1.22 : running ? 0.85 : 0.12;
4578
4602
  const wristSwing = holdRight || state.armed || running ? 0 : -Math.min(1, gait) * Math.sin(ph - 0.6) * 0.12 * hand.side;
4579
4603
  setHandGrip(hand, grip, wristSwing);
4580
4604
  });
@@ -5003,10 +5027,11 @@ function createFirstPersonArms(THREE, options = {}) {
5003
5027
  const palmAt = new THREE.Vector3();
5004
5028
  const fingers = new THREE.Vector3();
5005
5029
  const towards = new THREE.Vector3();
5006
- const fromChannel = (channel, side, base, out) => {
5030
+ const fromChannel = (channel, side, base, out, from = side) => {
5007
5031
  out.p.set(...channel.p);
5008
5032
  out.q.copy(quaternionOf(channel.r));
5009
- if (channel.mirror && side === "L") {
5033
+ const rightNumbers = from === "R" || channel.mirror;
5034
+ if (rightNumbers !== (side === "R")) {
5010
5035
  out.p.x = -out.p.x;
5011
5036
  out.q.copy(mirrorQuaternion(THREE, out.q));
5012
5037
  }
@@ -5071,7 +5096,7 @@ function createFirstPersonArms(THREE, options = {}) {
5071
5096
  play(id, options2) {
5072
5097
  const clip = clips.get(id);
5073
5098
  if (!clip) return false;
5074
- run = { clip, time: 0, left: options2?.leg === "left" };
5099
+ run = { clip, time: 0, left: options2?.leg === "left", swap: options2?.hand === "left" };
5075
5100
  fire(clip, -1e-9, 0);
5076
5101
  return true;
5077
5102
  },
@@ -5119,10 +5144,11 @@ function createFirstPersonArms(THREE, options = {}) {
5119
5144
  arm.blend = blendSeconds <= 0 ? 1 : Math.min(1, arm.blend + dt / blendSeconds);
5120
5145
  if (arm.blend >= 1) copyPose(arm.shown, desired);
5121
5146
  else mixPose(THREE, arm.shown, arm.from, desired, ease(arm.blend));
5122
- const channel = sample?.[side];
5147
+ const from = run?.swap ? side === "R" ? "L" : "R" : side;
5148
+ const channel = sample?.[from];
5123
5149
  let pose;
5124
5150
  if (channel && channel.w > 0) {
5125
- fromChannel(channel, side, arm.shown, channelPose);
5151
+ fromChannel(channel, side, arm.shown, channelPose, from);
5126
5152
  mixPose(THREE, mixed, arm.shown, channelPose, channel.w);
5127
5153
  pose = mode === "target" ? mixed : keepOutside(mixed, handReach(mixed));
5128
5154
  } else pose = mode === "target" ? arm.shown : keepOutside(arm.shown, handReach(arm.shown));
@@ -5178,6 +5204,8 @@ function createFirstPersonArms(THREE, options = {}) {
5178
5204
 
5179
5205
  // src/player/room-avatar.ts
5180
5206
  var impactOf = (clip) => clip.events?.find((event) => event.type === "impact")?.t ?? null;
5207
+ var isPunch = (clip) => clip.events?.some((event) => event.type === "impact" && event.data?.kind === "punch") ?? false;
5208
+ var GUARD_SPEED = 0.3;
5181
5209
  var toRoomAvatar = (avatar, options = {}) => {
5182
5210
  const arms = options.firstPerson === false ? null : createFirstPersonArms(avatar.three, {
5183
5211
  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 },
@@ -5192,28 +5220,59 @@ var toRoomAvatar = (avatar, options = {}) => {
5192
5220
  if (!found) mirrored.set(clip.id, found = mirrorPoseClip(clip, `${clip.id}_left`));
5193
5221
  return found;
5194
5222
  };
5223
+ const guard = options.guardClip === false ? void 0 : strikes.get(options.guardClip ?? "fight_guard");
5224
+ const guardHold = options.guardHold ?? 1.2;
5225
+ const punchBlend = options.punchBlend ?? 0.05;
5226
+ let strikeLeft = 0;
5227
+ let guardLeft = 0;
5228
+ let guarding = false;
5229
+ const stepGuard = (dt, state) => {
5230
+ if (!guard) return;
5231
+ const free = state.speed < GUARD_SPEED && !state.down && !state.armed && !state.holding && !state.push && !state.itemUse && !state.running;
5232
+ if (strikeLeft > 0) {
5233
+ strikeLeft -= dt;
5234
+ if (strikeLeft <= 0 && guardLeft > 0 && free) {
5235
+ avatar.play(guard);
5236
+ guarding = true;
5237
+ }
5238
+ return;
5239
+ }
5240
+ guardLeft = Math.max(0, guardLeft - dt);
5241
+ if (guarding && (guardLeft <= 0 || !free || avatar.playing !== guard.id)) {
5242
+ if (avatar.playing === guard.id) avatar.stop();
5243
+ guarding = false;
5244
+ }
5245
+ };
5195
5246
  return {
5196
5247
  root: avatar.root,
5197
5248
  avatar,
5198
5249
  arms,
5199
5250
  ...arms ? { firstPerson: { root: arms.root, update: (dt, state) => arms.update(dt, state) } } : {},
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
- }),
5251
+ update: (dt, state) => {
5252
+ stepGuard(dt, state);
5253
+ avatar.update(dt, {
5254
+ speed: state.speed,
5255
+ running: state.running,
5256
+ crouch: state.crouch,
5257
+ down: state.down,
5258
+ armed: state.armed,
5259
+ pitch: -state.pitch,
5260
+ ...state.holding !== void 0 ? { holding: state.holding } : {},
5261
+ ...state.itemUse !== void 0 ? { itemUse: state.itemUse } : {},
5262
+ ...state.push !== void 0 ? { push: state.push } : {}
5263
+ });
5264
+ },
5210
5265
  hand: (side) => avatar.hand(side),
5211
5266
  strike: (clipId, strikeOptions) => {
5212
5267
  const clip = strikes.get(clipId);
5213
5268
  if (!clip) return null;
5214
- const left = strikeOptions?.leg === "left";
5215
- avatar.play(left ? leftOf(clip) : clip);
5216
- arms?.play(clipId, left ? { leg: "left" } : void 0);
5269
+ const left = strikeOptions?.leg === "left" || strikeOptions?.hand === "left";
5270
+ const punch = guard !== void 0 && isPunch(clip);
5271
+ avatar.play(left ? leftOf(clip) : clip, punch ? { blend: punchBlend } : void 0);
5272
+ strikeLeft = punch ? clip.duration : 0;
5273
+ guardLeft = punch ? guardHold : 0;
5274
+ guarding = false;
5275
+ arms?.play(clipId, strikeOptions?.leg === "left" ? { leg: "left" } : strikeOptions?.hand === "left" ? { hand: "left" } : void 0);
5217
5276
  return impactOf(clip);
5218
5277
  }
5219
5278
  };
@@ -5328,6 +5387,7 @@ function createAvatarTestModule(options) {
5328
5387
  return {
5329
5388
  id: AVATAR_AREA_ID,
5330
5389
  name: textOf().name,
5390
+ ground: { surface: "concrete_slab" },
5331
5391
  group: textOf().group,
5332
5392
  state,
5333
5393
  items: () => [],