@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/README.md +5 -4
- package/dist/index.d.ts +25 -2
- package/dist/index.global.js +236 -47
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +85 -25
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -29,7 +29,7 @@ time.value += dt
|
|
|
29
29
|
- `createFluidsBridge({ sink, film })` and `bloodLookOfAge(age)`: a `body` event throws drops of blood into the film, in the fresh and dry colours of the infection age (`ages.json`), `old_blood` for the thin ones.
|
|
30
30
|
|
|
31
31
|
**Player avatar.** `createPlayerAvatar(THREE, options?)` is the team game's player body: the rich human of the Bestiário/Elenco built from `presets/player-avatar.json`
|
|
32
|
-
(`{ type: 'Lento', human: true, seed: 4242, skin, hs: 'short', hair }`, shirt `0x3d5a73`), overridable with `{ spec, shirtColor, clips, blend, fallSeconds }
|
|
32
|
+
(`{ type: 'Lento', human: true, seed: 4242, skin, hs: 'short', hair }`, shirt `0x3d5a73`), overridable with `{ spec, shirtColor, clips, blend, fallSeconds, cascade, clipCascade }` (`clipCascade`, default 5: how many times faster the joints follow while a clip that does not loop plays, so a strike reaches its pose by its impact).
|
|
33
33
|
|
|
34
34
|
```ts
|
|
35
35
|
const player = createPlayerAvatar(THREE)
|
|
@@ -41,16 +41,17 @@ player.play('grabbed_bitten_human', { onEvent: (event) => event.type === 'impact
|
|
|
41
41
|
|
|
42
42
|
Crouch, down and the aimed arms are derived literally from the game's GPU pose code (`BODY_CHUNK`, lines quoted in `src/player/avatar.ts`): crouch is thigh `-0.95`, knee `1.9`, trunk `+0.25`, hip `-0.35`;
|
|
43
43
|
down turns the body about the hip (`±1.5` rad, face up or down by the phase) with the limbs of pose 2; `armed` follows `(pitch + 1) / 3`. `play` overlays a `PoseClip` of `@adatechnology/game-characters`
|
|
44
|
-
(`channels: 'upper'` leaves the legs to the gait) with a 0.12 s blend, and fires its events (`impact`, `phase`) once through `onEvent` and `on('event')`. `hand('R')` is the weapon hand (the puppet's right, at -x).
|
|
44
|
+
(`channels: 'upper'` leaves the legs to the gait; while crouched only a kick takes the legs, so a punch or the guard stays low) with a 0.12 s blend, and fires its events (`impact`, `phase`) once through `onEvent` and `on('event')`. `hand('R')` is the weapon hand (the puppet's right, at -x). A pose clip's `armL`/`legL` channels move that side (`arms[1]`/`legs[0]`), so `punch_right` and `front_kick` of `ATTACK_CLIPS` drive `armL` and `legL`, the same arm and leg the first-person view shows.
|
|
45
45
|
|
|
46
46
|
**In the test room.** `room.setAvatar(toRoomAvatar(player))` shows the body at the player's feet (`toRoomAvatar` only flips the pitch: the room counts a look up as positive, the team game a look down) and V switches between the first and the third person of
|
|
47
47
|
`@adatechnology/game-test-room`. `createAvatarTestModule({ avatar, camera, state })` is the module of the area `avatar_area` (`AVATAR_AREA_ID`): bench buttons for the view (1st/3rd person), crouch, stand, fall, get up, armed on/off,
|
|
48
48
|
one button per registered clip (`Tocar: grabbed_bitten_human`, `grab_bite_zombie`, and with `clips: [...POSE_CLIPS, ...ATTACK_CLIPS]` the punch, shove, kick and the recoils; `clipLocales` gives the phase texts) and stop; metrics for the camera, the clip playing, the last clip event and the joints. Every action is a JSON line in the room log (`{"type":"avatar","action":"play_clip","clip":...}`), clip events included, and an `impact` event also gets its own `{"action":"impact","clip":...,"t":...}` line.
|
|
49
49
|
The module takes the body, the view (`camera: { mode, set }`) and the state (`state: { set }`) by option and imports only `@adatechnology/game-contracts`.
|
|
50
50
|
|
|
51
|
-
`toRoomAvatar` also gives the room the first-person arms (`firstPerson: false` for none) and `strike(clipId)`, which plays the attack clip on the body and the first-person clip of the same id on the arms and answers the seconds to the impact (the same in both views).
|
|
51
|
+
`toRoomAvatar` also gives the room the first-person arms (`firstPerson: false` for none) and `strike(clipId)`, which plays the attack clip on the body and the first-person clip of the same id on the arms and answers the seconds to the impact (the same in both views). `strike(clip, { leg: 'left' })` kicks with the left leg and `strike(clip, { hand: 'left' })` punches with the left hand: the mirror of the clip on the body (`mirrorPoseClip`, id `<clip>_left`) and on the arms. After a punch (an `impact` of kind `punch`) the body holds the guard (`guardClip`, default `'fight_guard'`, `false` for none) for
|
|
52
|
+
`guardHold` seconds (default 1.2) while it stands still, so a combo goes guard, punch, guard; the punch blends in over `punchBlend` (default 0.05 s). Walking, running, a weapon or a held item drop the guard.
|
|
52
53
|
|
|
53
|
-
**First-person arms.** `createFirstPersonArms(THREE, { colors?, rig?, stances?, clips?, blend?, bob? })` builds sleeves (upper arm, forearm, elbow, cuff) and hands of skin (palm, four fingers of three segments, a thumb that close on a grip) drawn in the camera's space, plus the right leg that only shows during a kick. `arms.update(dt, { camera, targets, stance, speed, running, crouch, contact? })`: the `relaxed` and `fists` stances are rest poses (`FIRST_PERSON_STANCES`, from `presets/first-person-arms.json`), `hold` follows the `HandTargets` of `game-contracts` (world space) with a two-bone IK from fixed shoulders, the elbow bending down and out; a target out of reach is clamped (the shoulder moves `reachShift` toward it first). `arms.play('punch_right' | 'shove_two_hands' | 'front_kick')` overlays a `FirstPersonClip` (`FIRST_PERSON_CLIPS` of `game-characters-people`, same durations and impact seconds as the attack clips); `arms.on('event', fn)` gives its `impact`; `arms.handPose(side)` reads where a hand is drawn. `contact(origin, direction, maxDistance)` (optional; distance to the first surface or null) makes the hands and the kick foot stop a few millimetres before a surface along the camera forward (the elbow bends); `arms.play('front_kick', { leg: 'left' })` kicks with the left leg. Hand frame: x wrist to knuckles, y back of the hand, z little-finger side; a left hand is the right one mirrored.
|
|
54
|
+
**First-person arms.** `createFirstPersonArms(THREE, { colors?, rig?, stances?, clips?, blend?, bob? })` builds sleeves (upper arm, forearm, elbow, cuff) and hands of skin (palm, four fingers of three segments, a thumb that close on a grip) drawn in the camera's space, plus the right leg that only shows during a kick. `arms.update(dt, { camera, targets, stance, speed, running, crouch, contact? })`: the `relaxed` and `fists` stances are rest poses (`FIRST_PERSON_STANCES`, from `presets/first-person-arms.json`), `hold` follows the `HandTargets` of `game-contracts` (world space) with a two-bone IK from fixed shoulders, the elbow bending down and out; a target out of reach is clamped (the shoulder moves `reachShift` toward it first). `arms.play('punch_right' | 'shove_two_hands' | 'front_kick')` overlays a `FirstPersonClip` (`FIRST_PERSON_CLIPS` of `game-characters-people`, same durations and impact seconds as the attack clips); `arms.on('event', fn)` gives its `impact`; `arms.handPose(side)` reads where a hand is drawn. `contact(origin, direction, maxDistance)` (optional; distance to the first surface or null) makes the hands and the kick foot stop a few millimetres before a surface along the camera forward (the elbow bends); `arms.play('front_kick', { leg: 'left' })` kicks with the left leg and `arms.play('punch_right', { hand: 'left' })` punches with the left hand (the hands of the clip in a mirror). Hand frame: x wrist to knuckles, y back of the hand, z little-finger side; a left hand is the right one mirrored.
|
|
54
55
|
|
|
55
56
|
## Geometry
|
|
56
57
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1040,6 +1040,11 @@ type PlayerAvatarOptions = {
|
|
|
1040
1040
|
readonly fallSeconds?: number;
|
|
1041
1041
|
/** Smooth the joints with the Bestiário cascade (default true). */
|
|
1042
1042
|
readonly cascade?: boolean;
|
|
1043
|
+
/**
|
|
1044
|
+
* How many times faster the cascade follows while a clip that does not loop plays: a strike is over in a third of a second, and at the walk's pace the joints would
|
|
1045
|
+
* still be on their way when it ends (a punch that never reaches the shoulder). 1 keeps the walk's pace. Default 5.
|
|
1046
|
+
*/
|
|
1047
|
+
readonly clipCascade?: number;
|
|
1043
1048
|
};
|
|
1044
1049
|
type AvatarEvent = PoseClipEvent & {
|
|
1045
1050
|
clip: string;
|
|
@@ -1066,6 +1071,11 @@ type AvatarUpdate = {
|
|
|
1066
1071
|
holding?: boolean;
|
|
1067
1072
|
/** Active scripted item use (`drink`, `sip`, `chug`, `pour`, `open`, `close`, `tilt`). */
|
|
1068
1073
|
itemUse?: AvatarItemUsePose | null;
|
|
1074
|
+
/** Hands on a piece of furniture: `grip` closes on it with `progress`, `push` walks with it, `strain` strains against what does not give. */
|
|
1075
|
+
push?: {
|
|
1076
|
+
readonly phase: 'grip' | 'push' | 'strain';
|
|
1077
|
+
readonly progress: number;
|
|
1078
|
+
} | null;
|
|
1069
1079
|
};
|
|
1070
1080
|
type PlayPoseOptions = {
|
|
1071
1081
|
onEvent?: (event: AvatarEvent) => void;
|
|
@@ -1206,9 +1216,10 @@ type FirstPersonArms = {
|
|
|
1206
1216
|
/** Add it to the scene: it follows the camera by itself. */
|
|
1207
1217
|
root: THREE_NS.Group;
|
|
1208
1218
|
/** Starts a clip; false when no registered clip has that id. The previous clip is dropped. */
|
|
1209
|
-
/** `leg: 'left'` plays the foot of the clip (the right foot) as the left one, in a mirror. */
|
|
1219
|
+
/** `leg: 'left'` plays the foot of the clip (the right foot) as the left one, in a mirror; `hand: 'left'` plays the hands in a mirror (the right hand's punch thrown by the left). */
|
|
1210
1220
|
play(clip: string, options?: {
|
|
1211
1221
|
leg?: 'left' | 'right';
|
|
1222
|
+
hand?: 'left' | 'right';
|
|
1212
1223
|
}): boolean;
|
|
1213
1224
|
stop(): void;
|
|
1214
1225
|
/** The clip being played, or null. */
|
|
@@ -1243,6 +1254,10 @@ type RoomAvatarLike = {
|
|
|
1243
1254
|
down: boolean;
|
|
1244
1255
|
armed: boolean;
|
|
1245
1256
|
pitch: number;
|
|
1257
|
+
push?: {
|
|
1258
|
+
readonly phase: 'grip' | 'push' | 'strain';
|
|
1259
|
+
readonly progress: number;
|
|
1260
|
+
} | null;
|
|
1246
1261
|
holding?: boolean;
|
|
1247
1262
|
itemUse?: AvatarItemUsePose | null;
|
|
1248
1263
|
}): void;
|
|
@@ -1263,6 +1278,7 @@ type RoomAvatarLike = {
|
|
|
1263
1278
|
/** Plays a strike on the body and the arms and answers the seconds to its impact (`RoomAvatar.strike`). */
|
|
1264
1279
|
strike(clipId: string, options?: {
|
|
1265
1280
|
leg?: 'left' | 'right';
|
|
1281
|
+
hand?: 'left' | 'right';
|
|
1266
1282
|
}): number | null;
|
|
1267
1283
|
/** The avatar it wraps. */
|
|
1268
1284
|
readonly avatar: PlayerAvatar;
|
|
@@ -1276,12 +1292,19 @@ type ToRoomAvatarOptions = {
|
|
|
1276
1292
|
strikeClips?: readonly PoseClip[];
|
|
1277
1293
|
/** The first-person clips the arms play. Default `FIRST_PERSON_CLIPS` of game-characters-people. */
|
|
1278
1294
|
firstPersonClips?: readonly FirstPersonClip[];
|
|
1295
|
+
/** The looping clip the body holds between punches (a clip of `strikeClips`), so a combo goes guard, punch, guard instead of dropping the hands each time; `false` for none. Default `'fight_guard'`. */
|
|
1296
|
+
guardClip?: string | false;
|
|
1297
|
+
/** Seconds the guard stays up after a punch ends. Default 1.2. */
|
|
1298
|
+
guardHold?: number;
|
|
1299
|
+
/** Seconds a punch takes to blend in over the walk (or the guard it comes from). Default 0.05: the fist leaves at once. */
|
|
1300
|
+
punchBlend?: number;
|
|
1279
1301
|
};
|
|
1280
1302
|
/**
|
|
1281
1303
|
* Fits the player's body to the test room's `RoomAvatar` (`room.setAvatar(toRoomAvatar(createPlayerAvatar(THREE)))`). It changes the sign of the pitch: the room
|
|
1282
1304
|
* counts a look up as positive, the team game's `view.pitch` (what `AvatarUpdate.pitch` reads) counts a look down as positive. It also gives the room the first-person arms, dressed in the
|
|
1283
1305
|
* colours of the body (`firstPerson: false` for none), and `strike(clipId)`: the strike plays on the body (the attack clips) and on the arms (the first-person clips), and answers the seconds to its
|
|
1284
|
-
* impact, which are the same in both views.
|
|
1306
|
+
* impact, which are the same in both views. A kick with the left leg (`leg: 'left'`) or a punch with the left hand (`hand: 'left'`) plays the mirror of the clip written for the right.
|
|
1307
|
+
* After a punch the body holds the guard (`guardClip`) for `guardHold` seconds while it stands, so the next punch leaves from the guard.
|
|
1285
1308
|
*/
|
|
1286
1309
|
declare const toRoomAvatar: (avatar: PlayerAvatar, options?: ToRoomAvatarOptions) => RoomAvatarLike;
|
|
1287
1310
|
|
package/dist/index.global.js
CHANGED
|
@@ -8479,6 +8479,7 @@ var GameCharactersThree = (() => {
|
|
|
8479
8479
|
return {
|
|
8480
8480
|
id: CHARACTERS_AREA_ID,
|
|
8481
8481
|
name: text.name,
|
|
8482
|
+
ground: { surface: "concrete_slab" },
|
|
8482
8483
|
group: text.group,
|
|
8483
8484
|
get entries() {
|
|
8484
8485
|
return entries;
|
|
@@ -8640,6 +8641,7 @@ var GameCharactersThree = (() => {
|
|
|
8640
8641
|
var TWO_PI2 = Math.PI * 2;
|
|
8641
8642
|
var MAX_GAIT = 1.4;
|
|
8642
8643
|
var CLIP_BLEND = 0.12;
|
|
8644
|
+
var CLIP_CASCADE = 5;
|
|
8643
8645
|
var FALL_SECONDS2 = 0.3;
|
|
8644
8646
|
var HIP_HEIGHT = 0.95;
|
|
8645
8647
|
var WALK_ARM = 0.28;
|
|
@@ -8679,6 +8681,7 @@ var GameCharactersThree = (() => {
|
|
|
8679
8681
|
const blendDefault = options.blend ?? CLIP_BLEND;
|
|
8680
8682
|
const fallSeconds = options.fallSeconds ?? FALL_SECONDS2;
|
|
8681
8683
|
const smooth = options.cascade ?? true;
|
|
8684
|
+
const clipCascade = options.clipCascade ?? CLIP_CASCADE;
|
|
8682
8685
|
const root = new THREE.Group();
|
|
8683
8686
|
const tilt = new THREE.Group();
|
|
8684
8687
|
tilt.add(body);
|
|
@@ -8778,6 +8781,24 @@ var GameCharactersThree = (() => {
|
|
|
8778
8781
|
});
|
|
8779
8782
|
return { armR: shoulder, armL: shoulder, armRz: AIMED.zRight, armLz: AIMED.zLeft, elbR: AIMED.elbowRight, elbL: AIMED.elbowLeft };
|
|
8780
8783
|
};
|
|
8784
|
+
const pushingPose = (push) => {
|
|
8785
|
+
const on = push.phase === "grip" ? Math.min(1, push.progress * 2.2) : 1;
|
|
8786
|
+
const strain = push.phase === "strain" ? 1 : 0;
|
|
8787
|
+
const shake = strain * Math.sin(time * 40) * 0.025;
|
|
8788
|
+
const raise = -1.5 * on;
|
|
8789
|
+
const elbow = -0.9 + 0.72 * (push.phase === "grip" ? push.progress : 1) + strain * 0.12;
|
|
8790
|
+
return {
|
|
8791
|
+
lean: (0.2 + 0.2 * strain) * on,
|
|
8792
|
+
headX: (0.05 + 0.08 * strain) * on,
|
|
8793
|
+
armR: raise + shake,
|
|
8794
|
+
armL: raise - shake,
|
|
8795
|
+
armRz: -0.08 * on,
|
|
8796
|
+
armLz: 0.08 * on,
|
|
8797
|
+
elbR: elbow,
|
|
8798
|
+
elbL: elbow,
|
|
8799
|
+
...strain ? { legL: -0.5, legR: 0.4, kneeL: 0.55, kneeR: 0.08, drop: -0.05 } : {}
|
|
8800
|
+
};
|
|
8801
|
+
};
|
|
8781
8802
|
const holdingPose = (gait, itemUse) => {
|
|
8782
8803
|
const bob = Math.sin(cycles * TWO_PI2) * 0.05 * gait;
|
|
8783
8804
|
if (!itemUse) {
|
|
@@ -8905,7 +8926,7 @@ var GameCharactersThree = (() => {
|
|
|
8905
8926
|
const fireClipEvents = (r, from, to) => {
|
|
8906
8927
|
for (const event of clipEventsBetween(r.clip, from, to)) emit({ ...event, clip: r.clip.id }, r.onEvent);
|
|
8907
8928
|
};
|
|
8908
|
-
const stepClip = (dt, down) => {
|
|
8929
|
+
const stepClip = (dt, down, crouch) => {
|
|
8909
8930
|
if (!run) return;
|
|
8910
8931
|
const r = run;
|
|
8911
8932
|
const from = r.time;
|
|
@@ -8925,7 +8946,9 @@ var GameCharactersThree = (() => {
|
|
|
8925
8946
|
return;
|
|
8926
8947
|
}
|
|
8927
8948
|
if (down) return;
|
|
8928
|
-
const
|
|
8949
|
+
const keepsCrouch = crouch && !r.clip.events?.some((event) => event.data?.kind === "kick");
|
|
8950
|
+
const channels = r.clip.channels ?? "full";
|
|
8951
|
+
const sampled = filterPoseChannels(filterPoseChannels(samplePoseClip(r.clip, Math.min(r.time, duration)), channels), keepsCrouch ? "upper" : "full");
|
|
8929
8952
|
const current = readPose();
|
|
8930
8953
|
const blended = {};
|
|
8931
8954
|
for (const [name, value] of Object.entries(sampled)) if (name in current) blended[name] = current[name] + (value - current[name]) * r.weight;
|
|
@@ -8956,7 +8979,8 @@ var GameCharactersThree = (() => {
|
|
|
8956
8979
|
else {
|
|
8957
8980
|
if (running) animator.setPose(rig, runningPose(gait, Boolean(state.armed || state.holding || state.itemUse)));
|
|
8958
8981
|
if (state.crouch) animator.setPose(rig, crouchPose(gait));
|
|
8959
|
-
if (state.
|
|
8982
|
+
if (state.push) animator.setPose(rig, pushingPose(state.push));
|
|
8983
|
+
else if (state.armed) animator.setPose(rig, armed(state.pitch ?? 0, gait, state.hip ?? 0));
|
|
8960
8984
|
else if (state.holding || state.itemUse) animator.setPose(rig, holdingPose(gait, state.itemUse));
|
|
8961
8985
|
else if (!running) {
|
|
8962
8986
|
animator.setPose(rig, {
|
|
@@ -8968,15 +8992,15 @@ var GameCharactersThree = (() => {
|
|
|
8968
8992
|
}
|
|
8969
8993
|
}
|
|
8970
8994
|
placeBody(fall);
|
|
8971
|
-
stepClip(dt, down);
|
|
8972
|
-
if (smooth) animator.cascade(rig, dt);
|
|
8973
|
-
const
|
|
8995
|
+
stepClip(dt, down, Boolean(state.crouch));
|
|
8996
|
+
if (smooth) animator.cascade(rig, run && !run.clip.loop ? dt * clipCascade : dt);
|
|
8997
|
+
const blow = run?.clip.events?.find((event) => (event.type === "impact" || event.type === "grip") && event.data?.kind)?.data?.kind;
|
|
8974
8998
|
const ph = cycles * TWO_PI2;
|
|
8975
8999
|
hands.forEach((hand) => {
|
|
8976
9000
|
const isRight = hand.side < 0;
|
|
8977
9001
|
const holdRight = isRight && (!!state.holding || !!state.itemUse);
|
|
8978
9002
|
const holdLeft = !isRight && (state.itemUse?.id === "open" || state.itemUse?.id === "close");
|
|
8979
|
-
const grip =
|
|
9003
|
+
const grip = blow === "punch" ? 1.4 : blow === "shove" ? 0.15 : state.armed || holdRight || holdLeft ? 1.22 : running ? 0.85 : 0.12;
|
|
8980
9004
|
const wristSwing = holdRight || state.armed || running ? 0 : -Math.min(1, gait) * Math.sin(ph - 0.6) * 0.12 * hand.side;
|
|
8981
9005
|
setHandGrip(hand, grip, wristSwing);
|
|
8982
9006
|
});
|
|
@@ -9016,22 +9040,97 @@ var GameCharactersThree = (() => {
|
|
|
9016
9040
|
clips: [
|
|
9017
9041
|
{
|
|
9018
9042
|
id: "punch_right",
|
|
9019
|
-
source: { authored: "game-kit", note: "authored in the kit; replace with the Elenco team's clip when delivered" },
|
|
9020
|
-
duration: 0.
|
|
9043
|
+
source: { authored: "game-kit", note: "authored in the kit from boxing technique (guard, hip and shoulder turn, full extension, weight over the front knee, back to the guard); the right hand's straight punch (the cross; the left plays its mirror); replace with the Elenco team's clip when delivered" },
|
|
9044
|
+
duration: 0.38,
|
|
9021
9045
|
loop: false,
|
|
9022
|
-
channels: "
|
|
9046
|
+
channels: "full",
|
|
9023
9047
|
keys: [
|
|
9024
|
-
{ t: 0, pose: { lean: 0, twist: 0, side: 0, headX: 0, headY: 0, headZ: 0, armR: 0.
|
|
9025
|
-
{ t: 0.
|
|
9026
|
-
{ t: 0.
|
|
9027
|
-
{ t: 0.
|
|
9028
|
-
{ t: 0.
|
|
9048
|
+
{ t: 0, pose: { lean: 0.12, twist: -0.3, side: 0, drop: -0.06, headX: 0.18, headY: 0.28, headZ: 0, armR: -0.85, elbR: -1.95, armRz: -0.5, armL: -0.75, elbL: -2, armLz: 0.55, legL: 0.25, kneeL: 0.3, legR: -0.3, kneeR: 0.3 } },
|
|
9049
|
+
{ t: 0.06, pose: { lean: 0.12, twist: -0.42, side: 0, drop: -0.09, headX: 0.18, headY: 0.34, headZ: 0, armR: -0.85, elbR: -1.95, armRz: -0.5, armL: -0.7, elbL: -2.1, armLz: 0.55, legL: 0.25, kneeL: 0.42, legR: -0.3, kneeR: 0.3 } },
|
|
9050
|
+
{ t: 0.14, pose: { lean: 0.22, twist: 0.5, side: 0, drop: -0.08, headX: 0.18, headY: -0.3, headZ: 0, armR: -0.8, elbR: -2.05, armRz: -0.3, armL: -1.75, elbL: -0.05, armLz: 0.12, legL: 0.35, kneeL: 0.12, legR: -0.3, kneeR: 0.45 } },
|
|
9051
|
+
{ t: 0.2, pose: { lean: 0.2, twist: 0.42, side: 0, drop: -0.08, headX: 0.18, headY: -0.26, headZ: 0, armR: -0.8, elbR: -2.05, armRz: -0.3, armL: -1.6, elbL: -0.3, armLz: 0.15, legL: 0.33, kneeL: 0.15, legR: -0.3, kneeR: 0.42 } },
|
|
9052
|
+
{ t: 0.38, pose: { lean: 0.12, twist: -0.3, side: 0, drop: -0.06, headX: 0.18, headY: 0.28, headZ: 0, armR: -0.85, elbR: -1.95, armRz: -0.5, armL: -0.75, elbL: -2, armLz: 0.55, legL: 0.25, kneeL: 0.3, legR: -0.3, kneeR: 0.3 } }
|
|
9029
9053
|
],
|
|
9030
9054
|
events: [
|
|
9031
9055
|
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
9032
|
-
{ t: 0.
|
|
9033
|
-
{ t: 0.
|
|
9034
|
-
{ t: 0.
|
|
9056
|
+
{ t: 0.06, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
9057
|
+
{ t: 0.14, type: "impact", data: { kind: "punch" } },
|
|
9058
|
+
{ t: 0.2, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
9059
|
+
]
|
|
9060
|
+
},
|
|
9061
|
+
{
|
|
9062
|
+
id: "jab",
|
|
9063
|
+
source: { authored: "game-kit", note: "authored in the kit from boxing technique (guard, hip and shoulder turn, full extension, weight over the front knee, back to the guard); the lead hand's jab, written for the right hand (the left plays its mirror, from the orthodox guard); one step of the punch combo; replace with the Elenco team's clip when delivered" },
|
|
9064
|
+
duration: 0.3,
|
|
9065
|
+
loop: false,
|
|
9066
|
+
channels: "full",
|
|
9067
|
+
keys: [
|
|
9068
|
+
{ t: 0, pose: { lean: 0.12, twist: 0.3, side: 0, drop: -0.06, headX: 0.18, headY: -0.28, headZ: 0, armL: -0.85, elbL: -1.95, armLz: 0.5, armR: -0.75, elbR: -2, armRz: -0.55, legR: 0.25, kneeR: 0.3, legL: -0.3, kneeL: 0.3 } },
|
|
9069
|
+
{ t: 0.04, pose: { lean: 0.12, twist: 0.34, side: 0, drop: -0.08, headX: 0.18, headY: -0.28, headZ: 0, armL: -0.85, elbL: -1.75, armLz: 0.5, armR: -0.75, elbR: -2, armRz: -0.55, legR: 0.25, kneeR: 0.3, legL: -0.3, kneeL: 0.36 } },
|
|
9070
|
+
{ t: 0.11, pose: { lean: 0.16, twist: 0.55, side: 0, drop: -0.08, headX: 0.18, headY: -0.35, headZ: 0, armL: -1.75, elbL: -0.05, armLz: 0.08, armR: -0.75, elbR: -2, armRz: -0.55, legR: 0.25, kneeR: 0.22, legL: -0.38, kneeL: 0.38 } },
|
|
9071
|
+
{ t: 0.16, pose: { lean: 0.15, twist: 0.5, side: 0, drop: -0.08, headX: 0.18, headY: -0.33, headZ: 0, armL: -1.62, elbL: -0.35, armLz: 0.12, armR: -0.75, elbR: -2, armRz: -0.55, legR: 0.25, kneeR: 0.3, legL: -0.36, kneeL: 0.36 } },
|
|
9072
|
+
{ t: 0.3, pose: { lean: 0.12, twist: 0.3, side: 0, drop: -0.06, headX: 0.18, headY: -0.28, headZ: 0, armL: -0.85, elbL: -1.95, armLz: 0.5, armR: -0.75, elbR: -2, armRz: -0.55, legR: 0.25, kneeR: 0.3, legL: -0.3, kneeL: 0.3 } }
|
|
9073
|
+
],
|
|
9074
|
+
events: [
|
|
9075
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
9076
|
+
{ t: 0.04, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
9077
|
+
{ t: 0.11, type: "impact", data: { kind: "punch" } },
|
|
9078
|
+
{ t: 0.16, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
9079
|
+
]
|
|
9080
|
+
},
|
|
9081
|
+
{
|
|
9082
|
+
id: "hook",
|
|
9083
|
+
source: { authored: "game-kit", note: "authored in the kit from boxing technique (guard, hip and shoulder turn, full extension, weight over the front knee, back to the guard); the lead hand's hook, written for the right hand (the left plays its mirror, from the orthodox guard); one step of the punch combo; replace with the Elenco team's clip when delivered" },
|
|
9084
|
+
duration: 0.42,
|
|
9085
|
+
loop: false,
|
|
9086
|
+
channels: "full",
|
|
9087
|
+
keys: [
|
|
9088
|
+
{ t: 0, pose: { lean: 0.12, twist: 0.3, side: 0, drop: -0.06, headX: 0.18, headY: -0.28, headZ: 0, armL: -0.85, elbL: -1.95, armLz: 0.5, armR: -0.75, elbR: -2, armRz: -0.55, legR: 0.25, kneeR: 0.3, legL: -0.3, kneeL: 0.3 } },
|
|
9089
|
+
{ t: 0.08, pose: { lean: 0.12, twist: 0.12, side: 0, drop: -0.1, headX: 0.18, headY: -0.15, headZ: 0, armL: -0.45, elbL: -1.9, armLz: -0.85, armR: -0.75, elbR: -2, armRz: -0.55, legR: 0.25, kneeR: 0.3, legL: -0.3, kneeL: 0.5 } },
|
|
9090
|
+
{ t: 0.18, pose: { lean: 0.14, twist: 0.85, side: 0, drop: -0.08, headX: 0.18, headY: -0.48, headZ: 0, armL: 0, elbL: -1.6, armLz: -1.62, armR: -0.75, elbR: -2, armRz: -0.55, legR: 0.25, kneeR: 0.38, legL: -0.3, kneeL: 0.36 } },
|
|
9091
|
+
{ t: 0.25, pose: { lean: 0.13, twist: 0.95, side: 0, drop: -0.08, headX: 0.18, headY: -0.5, headZ: 0, armL: 0, elbL: -1.7, armLz: -1.5, armR: -0.75, elbR: -2, armRz: -0.55, legR: 0.25, kneeR: 0.38, legL: -0.3, kneeL: 0.36 } },
|
|
9092
|
+
{ t: 0.42, pose: { lean: 0.12, twist: 0.3, side: 0, drop: -0.06, headX: 0.18, headY: -0.28, headZ: 0, armL: -0.85, elbL: -1.95, armLz: 0.5, armR: -0.75, elbR: -2, armRz: -0.55, legR: 0.25, kneeR: 0.3, legL: -0.3, kneeL: 0.3 } }
|
|
9093
|
+
],
|
|
9094
|
+
events: [
|
|
9095
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
9096
|
+
{ t: 0.08, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
9097
|
+
{ t: 0.18, type: "impact", data: { kind: "punch" } },
|
|
9098
|
+
{ t: 0.25, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
9099
|
+
]
|
|
9100
|
+
},
|
|
9101
|
+
{
|
|
9102
|
+
id: "uppercut",
|
|
9103
|
+
source: { authored: "game-kit", note: "authored in the kit from boxing technique (guard, hip and shoulder turn, full extension, weight over the front knee, back to the guard); the rear hand's uppercut; one step of the punch combo; replace with the Elenco team's clip when delivered" },
|
|
9104
|
+
duration: 0.42,
|
|
9105
|
+
loop: false,
|
|
9106
|
+
channels: "full",
|
|
9107
|
+
keys: [
|
|
9108
|
+
{ t: 0, pose: { lean: 0.12, twist: -0.3, side: 0, drop: -0.06, headX: 0.18, headY: 0.28, headZ: 0, armR: -0.85, elbR: -1.95, armRz: -0.5, armL: -0.75, elbL: -2, armLz: 0.55, legL: 0.25, kneeL: 0.3, legR: -0.3, kneeR: 0.3 } },
|
|
9109
|
+
{ t: 0.09, pose: { lean: 0.25, twist: -0.12, side: 0, drop: -0.15, headX: 0.18, headY: 0.3, headZ: 0, armR: -0.85, elbR: -1.95, armRz: -0.5, armL: -0.15, elbL: -1.75, armLz: 0.1, legL: 0.25, kneeL: 0.65, legR: -0.3, kneeR: 0.55 } },
|
|
9110
|
+
{ t: 0.19, pose: { lean: -0.04, twist: 0.45, side: 0, drop: -0.02, headX: 0.08, headY: -0.25, headZ: 0, armR: -0.8, elbR: -2.05, armRz: -0.5, armL: -0.95, elbL: -2, armLz: 0.6, legL: 0.25, kneeL: 0.12, legR: -0.3, kneeR: 0.25 } },
|
|
9111
|
+
{ t: 0.26, pose: { lean: -0.02, twist: 0.4, side: 0, drop: -0.03, headX: 0.1, headY: -0.22, headZ: 0, armR: -0.8, elbR: -2.05, armRz: -0.5, armL: -1, elbL: -2.05, armLz: 0.55, legL: 0.25, kneeL: 0.15, legR: -0.3, kneeR: 0.28 } },
|
|
9112
|
+
{ t: 0.42, pose: { lean: 0.12, twist: -0.3, side: 0, drop: -0.06, headX: 0.18, headY: 0.28, headZ: 0, armR: -0.85, elbR: -1.95, armRz: -0.5, armL: -0.75, elbL: -2, armLz: 0.55, legL: 0.25, kneeL: 0.3, legR: -0.3, kneeR: 0.3 } }
|
|
9113
|
+
],
|
|
9114
|
+
events: [
|
|
9115
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
9116
|
+
{ t: 0.09, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
9117
|
+
{ t: 0.19, type: "impact", data: { kind: "punch" } },
|
|
9118
|
+
{ t: 0.26, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
9119
|
+
]
|
|
9120
|
+
},
|
|
9121
|
+
{
|
|
9122
|
+
id: "fight_guard",
|
|
9123
|
+
source: { authored: "game-kit", note: "authored in the kit from boxing technique (guard, hip and shoulder turn, full extension, weight over the front knee, back to the guard); the guard a punch comes from and goes back to (orthodox: left foot and left fist forward, chin down); replace with the Elenco team's clip when delivered" },
|
|
9124
|
+
duration: 1.2,
|
|
9125
|
+
loop: true,
|
|
9126
|
+
channels: "full",
|
|
9127
|
+
keys: [
|
|
9128
|
+
{ t: 0, pose: { lean: 0.12, twist: -0.3, side: 0, drop: -0.06, headX: 0.18, headY: 0.28, headZ: 0, armR: -0.85, elbR: -1.95, armRz: -0.5, armL: -0.75, elbL: -2, armLz: 0.55, legL: 0.25, kneeL: 0.3, legR: -0.3, kneeR: 0.3 } },
|
|
9129
|
+
{ t: 0.6, pose: { lean: 0.13, twist: -0.3, side: 0, drop: -0.075, headX: 0.2, headY: 0.28, headZ: 0, armR: -0.85, elbR: -1.95, armRz: -0.5, armL: -0.75, elbL: -2, armLz: 0.55, legL: 0.25, kneeL: 0.3, legR: -0.3, kneeR: 0.3 } },
|
|
9130
|
+
{ t: 1.2, pose: { lean: 0.12, twist: -0.3, side: 0, drop: -0.06, headX: 0.18, headY: 0.28, headZ: 0, armR: -0.85, elbR: -1.95, armRz: -0.5, armL: -0.75, elbL: -2, armLz: 0.55, legL: 0.25, kneeL: 0.3, legR: -0.3, kneeR: 0.3 } }
|
|
9131
|
+
],
|
|
9132
|
+
events: [
|
|
9133
|
+
{ t: 0, type: "grip", data: { kind: "punch" } }
|
|
9035
9134
|
]
|
|
9036
9135
|
},
|
|
9037
9136
|
{
|
|
@@ -9062,9 +9161,9 @@ var GameCharactersThree = (() => {
|
|
|
9062
9161
|
channels: "full",
|
|
9063
9162
|
keys: [
|
|
9064
9163
|
{ t: 0, pose: { lean: 0, twist: 0, side: 0, drop: 0, headX: 0, headY: 0, headZ: 0, armR: 0.05, armL: -0.1, elbR: -0.2, elbL: -0.2, armRz: 0, armLz: 0, legL: -0.05, legR: 0.05, kneeL: 0.05, kneeR: 0.05 } },
|
|
9065
|
-
{ t: 0.18, pose: { lean: -0.1, twist: 0, side: 0.04, drop: -0.05, headX: 0.05, headY: 0, headZ: 0, armR:
|
|
9066
|
-
{ t: 0.32, pose: { lean: -0.22, twist: 0, side: 0.04, drop: -0.06, headX: 0.1, headY: 0, headZ: 0, armR:
|
|
9067
|
-
{ t: 0.42, pose: { lean: -0.18, twist: 0, side: 0.03, drop: -0.05, headX: 0.08, headY: 0, headZ: 0, armR:
|
|
9164
|
+
{ t: 0.18, pose: { lean: -0.1, twist: 0, side: -0.04, drop: -0.05, headX: 0.05, headY: 0, headZ: 0, armR: 0.4, armL: -0.5, elbR: -0.4, elbL: -0.5, armRz: -0.1, armLz: 0.1, legL: -1.1, legR: 0.1, kneeL: 1.8, kneeR: 0.35 } },
|
|
9165
|
+
{ t: 0.32, pose: { lean: -0.22, twist: 0, side: -0.04, drop: -0.06, headX: 0.1, headY: 0, headZ: 0, armR: 0.7, armL: -0.7, elbR: -0.3, elbL: -0.6, armRz: -0.15, armLz: 0.15, legL: -1.45, legR: 0.12, kneeL: 0.1, kneeR: 0.2 } },
|
|
9166
|
+
{ t: 0.42, pose: { lean: -0.18, twist: 0, side: -0.03, drop: -0.05, headX: 0.08, headY: 0, headZ: 0, armR: 0.5, armL: -0.5, elbR: -0.3, elbL: -0.5, armRz: -0.1, armLz: 0.1, legL: -1.2, legR: 0.1, kneeL: 1.2, kneeR: 0.3 } },
|
|
9068
9167
|
{ t: 0.6, pose: { lean: 0, twist: 0, side: 0, drop: 0, headX: 0, headY: 0, headZ: 0, armR: 0.05, armL: -0.1, elbR: -0.2, elbL: -0.2, armRz: 0, armLz: 0, legL: -0.05, legR: 0.05, kneeL: 0.05, kneeR: 0.05 } }
|
|
9069
9168
|
],
|
|
9070
9169
|
events: [
|
|
@@ -9127,19 +9226,73 @@ var GameCharactersThree = (() => {
|
|
|
9127
9226
|
{
|
|
9128
9227
|
id: "punch_right",
|
|
9129
9228
|
source: { authored: "game-kit", note: "first-person counterpart of attack-clips.json (same duration, same events); authored in the kit, replace with the Elenco team's clips when delivered" },
|
|
9130
|
-
duration: 0.
|
|
9229
|
+
duration: 0.38,
|
|
9230
|
+
keys: [
|
|
9231
|
+
{ t: 0, R: { p: [0.15, -0.12, -0.4], r: [0, 1.5, 0.15], grip: 1, w: 0 } },
|
|
9232
|
+
{ t: 0.06, R: { p: [0.21, -0.15, -0.29], r: [0.1, 1.45, 0.2], grip: 1 } },
|
|
9233
|
+
{ t: 0.14, R: { p: [0.06, -0.09, -0.62], r: [0, 1.5708, -0.05], grip: 1 } },
|
|
9234
|
+
{ t: 0.2, R: { p: [0.07, -0.1, -0.56], r: [0, 1.5708, -0.05], grip: 1 } },
|
|
9235
|
+
{ t: 0.38, R: { p: [0.15, -0.12, -0.4], r: [0, 1.5, 0.15], grip: 1, w: 0 } }
|
|
9236
|
+
],
|
|
9237
|
+
events: [
|
|
9238
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
9239
|
+
{ t: 0.06, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
9240
|
+
{ t: 0.14, type: "impact", data: { kind: "punch" } },
|
|
9241
|
+
{ t: 0.2, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
9242
|
+
]
|
|
9243
|
+
},
|
|
9244
|
+
{
|
|
9245
|
+
id: "jab",
|
|
9246
|
+
source: { authored: "game-kit", note: "first-person counterpart of attack-clips.json (same duration, same events), for the right hand (the left plays its mirror); authored in the kit, replace with the Elenco team's clips when delivered" },
|
|
9247
|
+
duration: 0.3,
|
|
9248
|
+
keys: [
|
|
9249
|
+
{ t: 0, R: { p: [0.15, -0.12, -0.4], r: [0, 1.5, 0.15], grip: 1, w: 0 } },
|
|
9250
|
+
{ t: 0.04, R: { p: [0.17, -0.13, -0.34], r: [0.1, 1.45, 0.2], grip: 1 } },
|
|
9251
|
+
{ t: 0.11, R: { p: [0.08, -0.08, -0.62], r: [0, 1.5708, -0.05], grip: 1 } },
|
|
9252
|
+
{ t: 0.16, R: { p: [0.09, -0.09, -0.57], r: [0, 1.5708, -0.05], grip: 1 } },
|
|
9253
|
+
{ t: 0.3, R: { p: [0.15, -0.12, -0.4], r: [0, 1.5, 0.15], grip: 1, w: 0 } }
|
|
9254
|
+
],
|
|
9255
|
+
events: [
|
|
9256
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
9257
|
+
{ t: 0.04, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
9258
|
+
{ t: 0.11, type: "impact", data: { kind: "punch" } },
|
|
9259
|
+
{ t: 0.16, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
9260
|
+
]
|
|
9261
|
+
},
|
|
9262
|
+
{
|
|
9263
|
+
id: "hook",
|
|
9264
|
+
source: { authored: "game-kit", note: "first-person counterpart of attack-clips.json (same duration, same events), for the right hand (the left plays its mirror); authored in the kit, replace with the Elenco team's clips when delivered" },
|
|
9265
|
+
duration: 0.42,
|
|
9266
|
+
keys: [
|
|
9267
|
+
{ t: 0, R: { p: [0.15, -0.12, -0.4], r: [0, 1.5, 0.15], grip: 1, w: 0 } },
|
|
9268
|
+
{ t: 0.08, R: { p: [0.3, -0.12, -0.4], r: [0, 1.5, 0.6], grip: 1 } },
|
|
9269
|
+
{ t: 0.18, R: { p: [0.02, -0.06, -0.5], r: [0, 2.5, 1.3], grip: 1 } },
|
|
9270
|
+
{ t: 0.25, R: { p: [0, -0.07, -0.47], r: [0, 2.5, 1.3], grip: 1 } },
|
|
9271
|
+
{ t: 0.42, R: { p: [0.15, -0.12, -0.4], r: [0, 1.5, 0.15], grip: 1, w: 0 } }
|
|
9272
|
+
],
|
|
9273
|
+
events: [
|
|
9274
|
+
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
9275
|
+
{ t: 0.08, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
9276
|
+
{ t: 0.18, type: "impact", data: { kind: "punch" } },
|
|
9277
|
+
{ t: 0.25, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
9278
|
+
]
|
|
9279
|
+
},
|
|
9280
|
+
{
|
|
9281
|
+
id: "uppercut",
|
|
9282
|
+
source: { authored: "game-kit", note: "first-person counterpart of attack-clips.json (same duration, same events), for the right hand (the left plays its mirror); authored in the kit, replace with the Elenco team's clips when delivered" },
|
|
9283
|
+
duration: 0.42,
|
|
9131
9284
|
keys: [
|
|
9132
9285
|
{ t: 0, R: { p: [0.15, -0.12, -0.4], r: [0, 1.5, 0.15], grip: 1, w: 0 } },
|
|
9133
|
-
{ t: 0.
|
|
9134
|
-
{ t: 0.
|
|
9135
|
-
{ t: 0.
|
|
9136
|
-
{ t: 0.
|
|
9286
|
+
{ t: 0.09, R: { p: [0.12, -0.3, -0.34], r: [0, -1.5708, 1.3], grip: 1 } },
|
|
9287
|
+
{ t: 0.19, R: { p: [0.05, -0.02, -0.5], r: [0, -1.5708, 1.45], grip: 1 } },
|
|
9288
|
+
{ t: 0.26, R: { p: [0.06, -0.04, -0.48], r: [0, -1.5708, 1.45], grip: 1 } },
|
|
9289
|
+
{ t: 0.42, R: { p: [0.15, -0.12, -0.4], r: [0, 1.5, 0.15], grip: 1, w: 0 } }
|
|
9137
9290
|
],
|
|
9138
9291
|
events: [
|
|
9139
9292
|
{ t: 0, type: "phase", data: { label: "attack_clip.phase.windup" } },
|
|
9140
|
-
{ t: 0.
|
|
9141
|
-
{ t: 0.
|
|
9142
|
-
{ t: 0.
|
|
9293
|
+
{ t: 0.09, type: "phase", data: { label: "attack_clip.phase.strike" } },
|
|
9294
|
+
{ t: 0.19, type: "impact", data: { kind: "punch" } },
|
|
9295
|
+
{ t: 0.26, type: "phase", data: { label: "attack_clip.phase.recover" } }
|
|
9143
9296
|
]
|
|
9144
9297
|
},
|
|
9145
9298
|
{
|
|
@@ -9670,10 +9823,11 @@ var GameCharactersThree = (() => {
|
|
|
9670
9823
|
const palmAt = new THREE.Vector3();
|
|
9671
9824
|
const fingers = new THREE.Vector3();
|
|
9672
9825
|
const towards = new THREE.Vector3();
|
|
9673
|
-
const fromChannel = (channel, side, base, out) => {
|
|
9826
|
+
const fromChannel = (channel, side, base, out, from = side) => {
|
|
9674
9827
|
out.p.set(...channel.p);
|
|
9675
9828
|
out.q.copy(quaternionOf(channel.r));
|
|
9676
|
-
|
|
9829
|
+
const rightNumbers = from === "R" || channel.mirror;
|
|
9830
|
+
if (rightNumbers !== (side === "R")) {
|
|
9677
9831
|
out.p.x = -out.p.x;
|
|
9678
9832
|
out.q.copy(mirrorQuaternion(THREE, out.q));
|
|
9679
9833
|
}
|
|
@@ -9738,7 +9892,7 @@ var GameCharactersThree = (() => {
|
|
|
9738
9892
|
play(id, options2) {
|
|
9739
9893
|
const clip = clips.get(id);
|
|
9740
9894
|
if (!clip) return false;
|
|
9741
|
-
run = { clip, time: 0, left: options2?.leg === "left" };
|
|
9895
|
+
run = { clip, time: 0, left: options2?.leg === "left", swap: options2?.hand === "left" };
|
|
9742
9896
|
fire(clip, -1e-9, 0);
|
|
9743
9897
|
return true;
|
|
9744
9898
|
},
|
|
@@ -9786,10 +9940,11 @@ var GameCharactersThree = (() => {
|
|
|
9786
9940
|
arm.blend = blendSeconds <= 0 ? 1 : Math.min(1, arm.blend + dt / blendSeconds);
|
|
9787
9941
|
if (arm.blend >= 1) copyPose(arm.shown, desired);
|
|
9788
9942
|
else mixPose(THREE, arm.shown, arm.from, desired, ease2(arm.blend));
|
|
9789
|
-
const
|
|
9943
|
+
const from = run?.swap ? side === "R" ? "L" : "R" : side;
|
|
9944
|
+
const channel = sample?.[from];
|
|
9790
9945
|
let pose;
|
|
9791
9946
|
if (channel && channel.w > 0) {
|
|
9792
|
-
fromChannel(channel, side, arm.shown, channelPose);
|
|
9947
|
+
fromChannel(channel, side, arm.shown, channelPose, from);
|
|
9793
9948
|
mixPose(THREE, mixed, arm.shown, channelPose, channel.w);
|
|
9794
9949
|
pose = mode === "target" ? mixed : keepOutside(mixed, handReach(mixed));
|
|
9795
9950
|
} else pose = mode === "target" ? arm.shown : keepOutside(arm.shown, handReach(arm.shown));
|
|
@@ -9845,6 +10000,8 @@ var GameCharactersThree = (() => {
|
|
|
9845
10000
|
|
|
9846
10001
|
// src/player/room-avatar.ts
|
|
9847
10002
|
var impactOf = (clip) => clip.events?.find((event) => event.type === "impact")?.t ?? null;
|
|
10003
|
+
var isPunch = (clip) => clip.events?.some((event) => event.type === "impact" && event.data?.kind === "punch") ?? false;
|
|
10004
|
+
var GUARD_SPEED = 0.3;
|
|
9848
10005
|
var toRoomAvatar = (avatar, options = {}) => {
|
|
9849
10006
|
const arms = options.firstPerson === false ? null : createFirstPersonArms(avatar.three, {
|
|
9850
10007
|
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 },
|
|
@@ -9859,28 +10016,59 @@ var GameCharactersThree = (() => {
|
|
|
9859
10016
|
if (!found) mirrored.set(clip.id, found = mirrorPoseClip(clip, `${clip.id}_left`));
|
|
9860
10017
|
return found;
|
|
9861
10018
|
};
|
|
10019
|
+
const guard = options.guardClip === false ? void 0 : strikes.get(options.guardClip ?? "fight_guard");
|
|
10020
|
+
const guardHold = options.guardHold ?? 1.2;
|
|
10021
|
+
const punchBlend = options.punchBlend ?? 0.05;
|
|
10022
|
+
let strikeLeft = 0;
|
|
10023
|
+
let guardLeft = 0;
|
|
10024
|
+
let guarding = false;
|
|
10025
|
+
const stepGuard = (dt, state) => {
|
|
10026
|
+
if (!guard) return;
|
|
10027
|
+
const free = state.speed < GUARD_SPEED && !state.down && !state.armed && !state.holding && !state.push && !state.itemUse && !state.running;
|
|
10028
|
+
if (strikeLeft > 0) {
|
|
10029
|
+
strikeLeft -= dt;
|
|
10030
|
+
if (strikeLeft <= 0 && guardLeft > 0 && free) {
|
|
10031
|
+
avatar.play(guard);
|
|
10032
|
+
guarding = true;
|
|
10033
|
+
}
|
|
10034
|
+
return;
|
|
10035
|
+
}
|
|
10036
|
+
guardLeft = Math.max(0, guardLeft - dt);
|
|
10037
|
+
if (guarding && (guardLeft <= 0 || !free || avatar.playing !== guard.id)) {
|
|
10038
|
+
if (avatar.playing === guard.id) avatar.stop();
|
|
10039
|
+
guarding = false;
|
|
10040
|
+
}
|
|
10041
|
+
};
|
|
9862
10042
|
return {
|
|
9863
10043
|
root: avatar.root,
|
|
9864
10044
|
avatar,
|
|
9865
10045
|
arms,
|
|
9866
10046
|
...arms ? { firstPerson: { root: arms.root, update: (dt, state) => arms.update(dt, state) } } : {},
|
|
9867
|
-
update: (dt, state) =>
|
|
9868
|
-
|
|
9869
|
-
|
|
9870
|
-
|
|
9871
|
-
|
|
9872
|
-
|
|
9873
|
-
|
|
9874
|
-
|
|
9875
|
-
|
|
9876
|
-
|
|
10047
|
+
update: (dt, state) => {
|
|
10048
|
+
stepGuard(dt, state);
|
|
10049
|
+
avatar.update(dt, {
|
|
10050
|
+
speed: state.speed,
|
|
10051
|
+
running: state.running,
|
|
10052
|
+
crouch: state.crouch,
|
|
10053
|
+
down: state.down,
|
|
10054
|
+
armed: state.armed,
|
|
10055
|
+
pitch: -state.pitch,
|
|
10056
|
+
...state.holding !== void 0 ? { holding: state.holding } : {},
|
|
10057
|
+
...state.itemUse !== void 0 ? { itemUse: state.itemUse } : {},
|
|
10058
|
+
...state.push !== void 0 ? { push: state.push } : {}
|
|
10059
|
+
});
|
|
10060
|
+
},
|
|
9877
10061
|
hand: (side) => avatar.hand(side),
|
|
9878
10062
|
strike: (clipId, strikeOptions) => {
|
|
9879
10063
|
const clip = strikes.get(clipId);
|
|
9880
10064
|
if (!clip) return null;
|
|
9881
|
-
const left = strikeOptions?.leg === "left";
|
|
9882
|
-
|
|
9883
|
-
|
|
10065
|
+
const left = strikeOptions?.leg === "left" || strikeOptions?.hand === "left";
|
|
10066
|
+
const punch = guard !== void 0 && isPunch(clip);
|
|
10067
|
+
avatar.play(left ? leftOf(clip) : clip, punch ? { blend: punchBlend } : void 0);
|
|
10068
|
+
strikeLeft = punch ? clip.duration : 0;
|
|
10069
|
+
guardLeft = punch ? guardHold : 0;
|
|
10070
|
+
guarding = false;
|
|
10071
|
+
arms?.play(clipId, strikeOptions?.leg === "left" ? { leg: "left" } : strikeOptions?.hand === "left" ? { hand: "left" } : void 0);
|
|
9884
10072
|
return impactOf(clip);
|
|
9885
10073
|
}
|
|
9886
10074
|
};
|
|
@@ -9992,6 +10180,7 @@ var GameCharactersThree = (() => {
|
|
|
9992
10180
|
return {
|
|
9993
10181
|
id: AVATAR_AREA_ID,
|
|
9994
10182
|
name: textOf().name,
|
|
10183
|
+
ground: { surface: "concrete_slab" },
|
|
9995
10184
|
group: textOf().group,
|
|
9996
10185
|
state,
|
|
9997
10186
|
items: () => [],
|