@adatechnology/game-characters-three 0.2.1 → 0.3.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 CHANGED
@@ -27,3 +27,34 @@ time.value += dt
27
27
 
28
28
  - `createCharacterSound(host.audio, { style? })`: `voice(typeId, 'idle' | 'alert' | 'special', position)` for the 13 undead types (`UNDEAD_VOICES`: the game's groans and scream, the art scene's `voice` groan, scream and leap, with the class rule of the page for the types it does not have), `body(event, position)` (wound = flesh, cut = sever, burst or split = gore), `player('grab' | 'bite' | 'hurt')`. The 16 recipes (`CHARACTER_SOUND_BANK`) leave the same trace as the team code.
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
+
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 }`.
33
+
34
+ ```ts
35
+ const player = createPlayerAvatar(THREE)
36
+ scene.add(player.root)
37
+ player.update(dt, { speed, crouch, down, armed, pitch, hip }) // the gait is tied to the ground speed (cadence and swing from `walkCadence`/`walkGait`)
38
+ player.hand('R').add(weaponMesh) // a socket at the weapon hand
39
+ player.play('grabbed_bitten_human', { onEvent: (event) => event.type === 'impact' && applyBlow() }) // or player.play(myClip); player.on('event', fn); player.stop()
40
+ ```
41
+
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
+ 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).
45
+
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
+ `@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
+ 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
+ The module takes the body, the view (`camera: { mode, set }`) and the state (`state: { set }`) by option and imports only `@adatechnology/game-contracts`.
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).
52
+
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
+
55
+ ## Geometry
56
+
57
+ The builders (`createPersonBuilder`, `createBestBuilder`, `bakeBody`, `buildInstancedBody`, `createFlyingLimbs`, `createEye`) draw with
58
+ `shapeGeometries(host)` of [`game-shapes-three`](../game-shapes-three): boxes, cylinders, cones, spheres, circles, rings, lathes and tori come
59
+ from game-shapes with the same bytes; dodecahedron, shape and capsule geometries are three's. `tests/exact-golden.test.ts` holds every bit as
60
+ the old path made them.
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as THREE_NS from 'three';
2
- import { BodySpec, CascadeJoint, RestPose, StrideParams, GaitSample, lerpPose, GaitProfile, SegmentId, CharacterKit, CharacterPack, CharacterSpec, DamageCharacter, BodyHit } from '@adatechnology/game-characters';
3
- import { Vec3 as Vec3$1, DestructionEvent, SoundRequest, TestableModule } from '@adatechnology/game-contracts';
2
+ import { BodySpec, CascadeJoint, RestPose as RestPose$1, StrideParams, GaitSample, lerpPose, GaitProfile, SegmentId, CharacterKit, CharacterPack, CharacterSpec, DamageCharacter, BodyHit, PoseClipEvent, PoseClip, PoseClipRegistry, FirstPersonClip } from '@adatechnology/game-characters';
3
+ import { Vec3 as Vec3$1, DestructionEvent, SoundRequest, TestableModule, HandTargets } from '@adatechnology/game-contracts';
4
4
  import { SoundRecipe, SoundBank } from '@adatechnology/game-audio';
5
5
 
6
6
  /** The three.js namespace the host passes in: the package never imports three itself, so one script tag works. */
@@ -49,7 +49,7 @@ type BuildInstancedBodyParams = {
49
49
  * The crowd body as one merged geometry, torso, head, hair, jaw, eyes, arms and legs tagged with the bone, pivot and material the shader reads
50
50
  * (G 65367 `buildBody`). The joints come from the spec, so a different `body` moves the limbs; the parts' own sizes follow the segment lengths.
51
51
  */
52
- declare const buildInstancedBody: (three: Three, { segments, body }: BuildInstancedBodyParams) => Geometry;
52
+ declare const buildInstancedBody: (host: Three, { segments, body }: BuildInstancedBodyParams) => Geometry;
53
53
 
54
54
  type BodyMaterialParams = {
55
55
  /** The depth material for shadows (true) or the lit body (false). */
@@ -161,7 +161,7 @@ type BakedBody = {
161
161
  * Bakes a rich body into one indexable geometry, each vertex with its bone, pivot, parent pivot and colour, so the instanced shader can animate it
162
162
  * (G 66019 `bakeBody`). Blood stays a vivid wine red at any decay; eyes are enlarged far away so they read.
163
163
  */
164
- declare const bakeBody: (three: Three, builders: BakeBuilders, spec: BakeSpec, lod: number) => BakedBody;
164
+ declare const bakeBody: (host: Three, builders: BakeBuilders, spec: BakeSpec, lod: number) => BakedBody;
165
165
 
166
166
  type VariantType = {
167
167
  readonly id: string;
@@ -250,7 +250,7 @@ type FlyingLimbs = {
250
250
  update(dt: number): void;
251
251
  };
252
252
  /** Torn-off pieces that fly, bounce and stay on the ground, as one instanced mesh of capsules (G 67953 `fxLimb`, 68040 update). */
253
- declare const createFlyingLimbs: (three: Three, { max, random, effects }: FlyingLimbsOptions) => FlyingLimbs;
253
+ declare const createFlyingLimbs: (host: Three, { max, random, effects }: FlyingLimbsOptions) => FlyingLimbs;
254
254
 
255
255
  type Euler3 = {
256
256
  x: number;
@@ -314,7 +314,7 @@ type RigAnimatorOptions = {
314
314
  };
315
315
  type RigAnimator = {
316
316
  /** The pose the gait swings around (legs `lost` stay out of the swing). */
317
- restOf(rig: RichRig): RestPose;
317
+ restOf(rig: RichRig): RestPose$1;
318
318
  /** The Bestiário's `animate0`: sets the joint rotations of one gait at `time`. `stride` ties cadence and swing to the ground speed. */
319
319
  animate0(root: THREE_NS.Object3D, time: number, stride?: StrideParams): GaitSample | undefined;
320
320
  /** `animate0` plus the bits that hang loose: the dangling forearm and the flaps of torn flesh. */
@@ -349,7 +349,7 @@ type BestBuilder = {
349
349
  /** The shirt material of the last body built (the bake marks it as material 1). */
350
350
  topM(): any;
351
351
  };
352
- declare const createBestBuilder: (THREE: Three, { types, random }: BestBuilderOptions) => BestBuilder;
352
+ declare const createBestBuilder: (host: Three, { types, random }: BestBuilderOptions) => BestBuilder;
353
353
 
354
354
  type PersonBuilderOptions = {
355
355
  /** The stream for stains and phases. The page shared one `rng(77)` across the cast, built in order; default the same. */
@@ -371,7 +371,7 @@ type PersonBuilder = {
371
371
  chars: any[];
372
372
  };
373
373
  };
374
- declare const createPersonBuilder: (THREE: Three, options?: PersonBuilderOptions) => PersonBuilder;
374
+ declare const createPersonBuilder: (host: Three, options?: PersonBuilderOptions) => PersonBuilder;
375
375
 
376
376
  type ExtraContext = {
377
377
  THREE: any;
@@ -471,7 +471,7 @@ type EyeOptions = {
471
471
  readonly texture?: THREE_NS.Texture | null;
472
472
  };
473
473
  /** One glowing eye: an unlit ball (emissive by itself) and an additive sprite on top. Add it to the head group at the socket. */
474
- declare const createEye: (three: Three, { color, radius, glow, glowOpacity, texture }: EyeOptions) => THREE_NS.Group;
474
+ declare const createEye: (host: Three, { color, radius, glow, glowOpacity, texture }: EyeOptions) => THREE_NS.Group;
475
475
  type Eyes = {
476
476
  /** The eyeballs: the meshes the builder marked `userData.eye`. They are what a test layer can ask for with `isolate`. */
477
477
  readonly markable: readonly THREE_NS.Mesh[];
@@ -1023,4 +1023,322 @@ declare const UNDEAD_VOICE_GROUP = "undead_voice";
1023
1023
  declare const CHARACTER_SOUNDS: readonly SoundRecipe[];
1024
1024
  declare const CHARACTER_SOUND_BANK: SoundBank;
1025
1025
 
1026
- export { type AreaContext, 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, type FilmSink, 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 PoseTargets, type ProfessionCard, type RichRig, type RigAnimator, type RigAnimatorOptions, type Three, 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, createBestBuilder, createBodyBatch, createBodyChunk, createBodyMaterial, createCharacterSound, createCharactersTestModule, createEye, createFluidsBridge, createFlyingLimbs, createFlyingPieces, createGlowTexture, createPendulum, createPersonBuilder, createRigAnimator, createVariantBatches, createWoundMaterials, createWounds, exitWound, explodeChunks, findEyes, gouge, jointsOf, lodAt, makeCut, makeStump, registerBodyMaterial, ringWound, starGeometry, stepPendulum, woundBodyOf, woundDecal };
1026
+ type PlayerAvatarSpec = Record<string, unknown>;
1027
+ type PlayerAvatarOptions = {
1028
+ /** The body spec for the rich builder (`createBestBuilder.build`). Default: the team game's player (`player-avatar.json`). Merged over the default. */
1029
+ readonly spec?: PlayerAvatarSpec;
1030
+ /** Shirt colour, 0xRRGGBB. Default the team game's `PLAYER_COR`. */
1031
+ readonly shirtColor?: number;
1032
+ /** The clips `play(id)` finds. Default: a registry with `POSE_CLIPS`. */
1033
+ readonly clips?: PoseClipRegistry;
1034
+ /** Seconds to blend a clip in and out. Default 0.12. */
1035
+ readonly blend?: number;
1036
+ /** Seconds the body takes to go down (the shader's `fk` smoothstep). 0 is instant. Default 0.3. */
1037
+ readonly fallSeconds?: number;
1038
+ /** Smooth the joints with the Bestiário cascade (default true). */
1039
+ readonly cascade?: boolean;
1040
+ };
1041
+ type AvatarEvent = PoseClipEvent & {
1042
+ clip: string;
1043
+ };
1044
+ type AvatarUpdate = {
1045
+ /** Ground speed, m/s. Ties cadence and swing to the ground (the game's `walkCad` and `walkGait`). */
1046
+ speed?: number;
1047
+ /** Accepted for the game's state; the gait depends on the speed alone, as in the team game. */
1048
+ running?: boolean;
1049
+ crouch?: boolean;
1050
+ down?: boolean;
1051
+ /** Weapon in hand: both arms follow the aim (the game's arm code). Default false. */
1052
+ armed?: boolean;
1053
+ /** The camera pitch the game's `playerArmCode()` reads (`view.pitch`). */
1054
+ pitch?: number;
1055
+ /** Leg yaw toward the step while armed (the game's `P.hip`, radians). */
1056
+ hip?: number;
1057
+ };
1058
+ type PlayPoseOptions = {
1059
+ onEvent?: (event: AvatarEvent) => void;
1060
+ /** Overrides the avatar's blend for this clip. */
1061
+ blend?: number;
1062
+ };
1063
+ /** The colours the body was drawn with, for whatever has to match it (the first-person arms): skin, shirt, trousers and shoes as 0xRRGGBB, and whether the sleeves cover the forearms. */
1064
+ type PlayerAvatarColors = {
1065
+ skin: number;
1066
+ shirt: number;
1067
+ trousers: number;
1068
+ shoes: number;
1069
+ longSleeves: boolean;
1070
+ };
1071
+ type PlayerAvatar = {
1072
+ root: THREE_NS.Object3D;
1073
+ rig: RichRig;
1074
+ /** What the body wears, read from the body itself. */
1075
+ readonly colors: PlayerAvatarColors;
1076
+ /** The three.js namespace the body was made with, so what is made to fit it (the first-person arms) uses the same one. */
1077
+ readonly three: Three;
1078
+ update(dt: number, state?: AvatarUpdate): void;
1079
+ play(clip: string | PoseClip, options?: PlayPoseOptions): void;
1080
+ stop(): void;
1081
+ readonly playing: string | null;
1082
+ /** A socket at the hand: 'R' is the weapon hand (the puppet's right, at -x), 'L' the other one. */
1083
+ hand(side: 'R' | 'L'): THREE_NS.Object3D;
1084
+ on(type: 'event', listener: (event: AvatarEvent) => void): () => void;
1085
+ dispose(): void;
1086
+ };
1087
+ /**
1088
+ * The player's body: the rich human of the Bestiário/Elenco, built with the team game's player spec (data, overridable), walking with the engine's gait tied
1089
+ * to the ground speed, with the crouch and the knock-down of the game's GPU pose code (`BODY_CHUNK`) and a pose-clip overlay (`play`).
1090
+ */
1091
+ declare const createPlayerAvatar: (THREE: Three, options?: PlayerAvatarOptions) => PlayerAvatar;
1092
+
1093
+ type Triple = [number, number, number];
1094
+ /**
1095
+ * A hand (or the foot) at rest, in the camera's space: x to the right, y up, the view looks down -z. `p` is the middle of the palm, `r` the Euler angles `[roll, yaw, pitch]` in the order YZX:
1096
+ * with all three at 0 the fingers point to the right (+x) and the back of the hand up; a yaw of pi/2 turns the fingers to the front (-z), the pitch tilts them up (+) or down (-) and the roll turns
1097
+ * the hand about its fingers. The hand-local frame is the one of `HandPose` in `game-contracts`: x to the knuckles, y the back of the hand, z the little-finger side of a right hand.
1098
+ */
1099
+ type RestPose = {
1100
+ p: Triple;
1101
+ r: Triple;
1102
+ grip?: number;
1103
+ };
1104
+ /** Where the hands are while the stance is held. `follow` takes the hands from the targets (a hand without one goes to the `fallback` stance); a left hand of `'mirror'` is the mirror image of the right. */
1105
+ type StanceDef = {
1106
+ R?: RestPose;
1107
+ L?: RestPose | 'mirror';
1108
+ follow?: boolean;
1109
+ fallback?: string;
1110
+ };
1111
+ /** The body of the arms, in metres in the camera's space. */
1112
+ type FirstPersonRig = {
1113
+ /** Right shoulder; the left one is its mirror image. */
1114
+ shoulder: Triple;
1115
+ upper: number;
1116
+ fore: number;
1117
+ /** How far (m) a shoulder may move toward a hand it cannot reach, so long tools stay in the hands. 0 keeps the shoulders where they are. */
1118
+ reachShift: number;
1119
+ /** From the wrist (where the sleeve ends) to the middle of the palm. */
1120
+ wrist: number;
1121
+ /** The way the elbow bends, for the right arm; mirrored for the left. */
1122
+ elbowPole: Triple;
1123
+ /** The left shoulder and the way its elbow bends, when they are not the mirror image of the right's: the support hand cups a handguard from below, so its shoulder sits nearer the middle and the elbow hangs under the forearm. */
1124
+ shoulderLeft?: Triple;
1125
+ elbowPoleLeft?: Triple;
1126
+ radius: {
1127
+ shoulder: number;
1128
+ elbow: number;
1129
+ wrist: number;
1130
+ cuff: number;
1131
+ };
1132
+ /** The right hip, the bones of the leg, the way the knee bends, and where the foot waits below the view. */
1133
+ hip: Triple;
1134
+ thigh: number;
1135
+ shin: number;
1136
+ kneePole: Triple;
1137
+ foot: RestPose;
1138
+ };
1139
+ type FirstPersonBob = {
1140
+ /** Radians of the bob per metre walked. */
1141
+ frequency: number;
1142
+ /** Metres of sway to the side and of rise and fall at a full walk. */
1143
+ x: number;
1144
+ y: number;
1145
+ /** The bob of running against walking. */
1146
+ run: number;
1147
+ /** Speed (m/s) at which the bob is full. */
1148
+ fullSpeed: number;
1149
+ /** Metres the arms sink when crouched. */
1150
+ crouch: number;
1151
+ };
1152
+ type FirstPersonColors = {
1153
+ /** 0xRRGGBB. Defaults come from the player's preset (`player-avatar.json`) and, with `createPlayerAvatar`, from the body itself. */
1154
+ skin: number;
1155
+ sleeve: number;
1156
+ cuff: number;
1157
+ trouser: number;
1158
+ shoe: number;
1159
+ };
1160
+ type FirstPersonArmsOptions = {
1161
+ colors?: Partial<FirstPersonColors>;
1162
+ rig?: Partial<Omit<FirstPersonRig, 'radius' | 'foot'>> & {
1163
+ radius?: Partial<FirstPersonRig['radius']>;
1164
+ foot?: RestPose;
1165
+ };
1166
+ /** Stances by id, merged over `relaxed`, `fists` and `hold`. */
1167
+ stances?: Readonly<Record<string, StanceDef>>;
1168
+ /** The clips `play(id)` finds, such as `FIRST_PERSON_CLIPS` of game-characters-people. Default none. */
1169
+ clips?: readonly FirstPersonClip[];
1170
+ /** Seconds to move from one stance (or from a held tool to the rest) to the next. Default 0.18. */
1171
+ blend?: number;
1172
+ bob?: Partial<FirstPersonBob>;
1173
+ };
1174
+ type FirstPersonArmsState = {
1175
+ /** The player's eye. The arms are drawn in its space, so they cannot leave the view. */
1176
+ camera: THREE_NS.Object3D;
1177
+ /** Where the tool in the hands asks them to be (world space), as `ModuleHost.setHandTargets` lends it. */
1178
+ targets?: HandTargets | null;
1179
+ /** `hold` follows the targets; any other stance is a rest pose. Default `relaxed`. */
1180
+ stance?: string;
1181
+ /** Where the world is: the distance along a ray (world space) to the first surface, or null for none within `maxDistance`. The hands and the foot stop at the surface (the elbow bends) instead of going into it. */
1182
+ contact?(origin: [number, number, number], direction: [number, number, number], maxDistance: number): number | null;
1183
+ /** Ground speed, m/s. */
1184
+ speed?: number;
1185
+ running?: boolean;
1186
+ crouch?: boolean;
1187
+ };
1188
+ type FirstPersonEvent = PoseClipEvent & {
1189
+ clip: string;
1190
+ };
1191
+ type FirstPersonArms = {
1192
+ /** Add it to the scene: it follows the camera by itself. */
1193
+ root: THREE_NS.Group;
1194
+ /** Starts a clip; false when no registered clip has that id. The previous clip is dropped. */
1195
+ /** `leg: 'left'` plays the foot of the clip (the right foot) as the left one, in a mirror. */
1196
+ play(clip: string, options?: {
1197
+ leg?: 'left' | 'right';
1198
+ }): boolean;
1199
+ stop(): void;
1200
+ /** The clip being played, or null. */
1201
+ readonly playing: string | null;
1202
+ /** The stance of the last update. */
1203
+ readonly stance: string;
1204
+ update(dt: number, state: FirstPersonArmsState): void;
1205
+ /** The clip's events (`impact` included), at the moment the clock reaches them. Returns the unsubscribe function. */
1206
+ on(type: 'event', listener: (event: FirstPersonEvent) => void): () => void;
1207
+ /** The camera-space pose the hand is drawn at (for tests and for hosts that attach things to it). */
1208
+ handPose(side: 'R' | 'L'): {
1209
+ position: THREE_NS.Vector3;
1210
+ quaternion: THREE_NS.Quaternion;
1211
+ grip: number;
1212
+ };
1213
+ /** Where the arm is drawn, in the camera's space: the shoulder, the elbow and the wrist (for tests of the way an arm reaches a hand). */
1214
+ joints(side: 'R' | 'L'): {
1215
+ shoulder: THREE_NS.Vector3;
1216
+ elbow: THREE_NS.Vector3;
1217
+ wrist: THREE_NS.Vector3;
1218
+ };
1219
+ dispose(): void;
1220
+ };
1221
+
1222
+ /** What `@adatechnology/game-test-room` calls a `RoomAvatar`, restated here so this package does not depend on the room. */
1223
+ type RoomAvatarLike = {
1224
+ root: THREE_NS.Object3D;
1225
+ update(dt: number, state: {
1226
+ speed: number;
1227
+ running: boolean;
1228
+ crouch: boolean;
1229
+ down: boolean;
1230
+ armed: boolean;
1231
+ pitch: number;
1232
+ }): void;
1233
+ hand(side: 'R' | 'L'): THREE_NS.Object3D;
1234
+ /** The arms of the first-person view (`RoomAvatar.firstPerson`); absent when `toRoomAvatar` was told to make none. */
1235
+ firstPerson?: {
1236
+ root: THREE_NS.Object3D;
1237
+ update(dt: number, state: {
1238
+ camera: THREE_NS.Object3D;
1239
+ targets: HandTargets | null;
1240
+ stance: string;
1241
+ speed: number;
1242
+ running: boolean;
1243
+ crouch: boolean;
1244
+ contact?(origin: [number, number, number], direction: [number, number, number], maxDistance: number): number | null;
1245
+ }): void;
1246
+ };
1247
+ /** Plays a strike on the body and the arms and answers the seconds to its impact (`RoomAvatar.strike`). */
1248
+ strike(clipId: string, options?: {
1249
+ leg?: 'left' | 'right';
1250
+ }): number | null;
1251
+ /** The avatar it wraps. */
1252
+ readonly avatar: PlayerAvatar;
1253
+ /** The first-person arms, for whoever wants more of them than the room asks (`play`, `on('event')`). */
1254
+ readonly arms: FirstPersonArms | null;
1255
+ };
1256
+ type ToRoomAvatarOptions = {
1257
+ /** The first-person arms: `false` for none, else the options of `createFirstPersonArms` (merged over the colours of the body and `firstPersonClips`). */
1258
+ firstPerson?: false | FirstPersonArmsOptions;
1259
+ /** The third-person strike clips `strike` finds by id. Default `ATTACK_CLIPS` of game-characters-people. */
1260
+ strikeClips?: readonly PoseClip[];
1261
+ /** The first-person clips the arms play. Default `FIRST_PERSON_CLIPS` of game-characters-people. */
1262
+ firstPersonClips?: readonly FirstPersonClip[];
1263
+ };
1264
+ /**
1265
+ * 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
1266
+ * 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
1267
+ * 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
1268
+ * impact, which are the same in both views.
1269
+ */
1270
+ declare const toRoomAvatar: (avatar: PlayerAvatar, options?: ToRoomAvatarOptions) => RoomAvatarLike;
1271
+
1272
+ declare const AVATAR_AREA_ID = "avatar_area";
1273
+ type AvatarTestOptions = {
1274
+ /** The body under test (the example makes it with `createPlayerAvatar(THREE)` and gives the same one to the room). */
1275
+ avatar: PlayerAvatar;
1276
+ /** The text language when the host does not ask for one. Default `pt-BR`. */
1277
+ locale?: string;
1278
+ /** The clips offered as buttons. Default `POSE_CLIPS` (the two of the Bestiário); a game adds its strikes, such as `ATTACK_CLIPS` of game-characters-people. */
1279
+ clips?: readonly PoseClip[];
1280
+ /** Texts of the `phase` labels of `clips` by locale, merged over `POSE_CLIP_LOCALES` (for example `ATTACK_CLIP_LOCALES`). */
1281
+ clipLocales?: Readonly<Record<string, Readonly<Record<string, string>>>>;
1282
+ /** The room's view: `mode()` reads it, `set` changes it (`room.cameraMode` and `room.setCameraMode`). Without it the camera buttons only log. */
1283
+ camera?: {
1284
+ mode(): 'first' | 'third';
1285
+ set(mode: 'first' | 'third'): void;
1286
+ };
1287
+ /** The room's avatar state (`room.setAvatarState`): the module drives crouch, down and the armed arms through it. */
1288
+ state?: {
1289
+ set(state: {
1290
+ crouch?: boolean;
1291
+ down?: boolean;
1292
+ armed?: boolean | null;
1293
+ }): void;
1294
+ };
1295
+ };
1296
+ /**
1297
+ * The player's body as a `TestableModule` for the area `avatar_area`: bench buttons for the view (1st and 3rd person), crouch, fall and get up, the armed arms and every registered
1298
+ * pose clip (grab, bite), and metrics for the camera, the clip, its last event and the joints. Every action goes to the room's
1299
+ * log as JSON. It uses only the contracts: the body, the camera and the state come in by option, so the module knows no room.
1300
+ */
1301
+ declare function createAvatarTestModule(options: AvatarTestOptions): TestableModule & {
1302
+ readonly state: {
1303
+ crouch: boolean;
1304
+ down: boolean;
1305
+ armed: boolean | null;
1306
+ };
1307
+ };
1308
+
1309
+ /** The stances of the preset: `relaxed` and `fists` are rest poses, `hold` follows the targets of the tool in the hands. */
1310
+ declare const FIRST_PERSON_STANCES: Readonly<Record<string, StanceDef>>;
1311
+ /** The rig of the preset: shoulders, bones, elbow pole, hips and the leg of the kick. */
1312
+ declare const FIRST_PERSON_RIG: FirstPersonRig;
1313
+ declare const FIRST_PERSON_BOB: FirstPersonBob;
1314
+ /** The colours of the player's preset: the skin and the shirt of the team game's player, trousers and shoes of a plain outfit. */
1315
+ declare const FIRST_PERSON_COLORS: FirstPersonColors;
1316
+ /**
1317
+ * The player's arms in first person, drawn in the space of the camera so they cannot leave the view. Each arm is a sleeve (upper arm and forearm, an elbow and a cuff) and a hand of skin (a palm,
1318
+ * four fingers of three segments and a thumb that close on a grip); the right leg (trouser and shoe) appears only while a kick holds it. The hands go where the stance puts them (`relaxed`, `fists`) or,
1319
+ * in the `hold` stance, where the tool in the hands asks (`HandTargets` of game-contracts, world space); a two-bone IK from fixed shoulders brings the sleeves to them, the elbow bending down and out. A
1320
+ * clip (`play`) moves the hands and the foot over that, by a weight that is 0 at its start and end.
1321
+ *
1322
+ * Hand frame (both hands, as `HandPose`): x from the wrist to the knuckles, y the back of the hand, z the little-finger side of a right hand; a left hand is the right one mirrored.
1323
+ */
1324
+ declare function createFirstPersonArms(THREE: Three, options?: FirstPersonArmsOptions): FirstPersonArms;
1325
+
1326
+ type TwoBone = {
1327
+ /** The middle joint (elbow, knee). */
1328
+ joint: THREE_NS.Vector3;
1329
+ /** The end of the second bone: the target when it is within reach, else the point on the way to it at full stretch. */
1330
+ end: THREE_NS.Vector3;
1331
+ /** The target was out of reach and `end` is short of it. */
1332
+ clamped: boolean;
1333
+ /** Scratch vectors the solver works in. */
1334
+ line: THREE_NS.Vector3;
1335
+ side: THREE_NS.Vector3;
1336
+ };
1337
+ /**
1338
+ * Two-bone analytic IK. The first bone (`upper`) starts at `root`, the second (`lower`) ends at `end`; the middle joint bends toward `pole` (a direction, not a point: the part of it
1339
+ * across the root-to-target line decides which way the elbow goes). A target nearer than the bones can fold, or farther than they can stretch, is clamped to the nearest reachable point on
1340
+ * the same line. All vectors are in one space; `out` (made by `createTwoBone`) is filled and returned, so a frame allocates nothing.
1341
+ */
1342
+ declare const solveTwoBone: (root: THREE_NS.Vector3, target: THREE_NS.Vector3, upper: number, lower: number, pole: THREE_NS.Vector3, out: TwoBone) => TwoBone;
1343
+
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 };