@adatechnology/game-characters-three 0.7.2 → 0.8.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 +1 -1
- package/dist/chunk-3CHBEUHW.js +4661 -0
- package/dist/chunk-3CHBEUHW.js.map +1 -0
- package/dist/index.d.ts +328 -619
- package/dist/index.global.js +3387 -2083
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +592 -4236
- package/dist/index.js.map +1 -1
- package/dist/testing-DyQw8dCw.d.ts +606 -0
- package/dist/testing.d.ts +4 -0
- package/dist/testing.js +25 -0
- package/dist/testing.js.map +1 -0
- package/package.json +13 -7
|
@@ -0,0 +1,606 @@
|
|
|
1
|
+
import { Vec3, DestructionEvent, SoundRequest, TestableModule, HandTargets } from '@adatechnology/game-contracts';
|
|
2
|
+
import * as THREE_NS from 'three';
|
|
3
|
+
import { CascadeJoint, RestPose, StrideParams, GaitSample, lerpPose, GaitProfile, CharacterKit, CharacterPack, CharacterSpec, DamageCharacter, BodyHit, PoseClipEvent, PoseClip, PoseClipRegistry } from '@adatechnology/game-characters';
|
|
4
|
+
|
|
5
|
+
/** The three.js namespace the host passes in: the package never imports three itself, so one script tag works. */
|
|
6
|
+
type Three = typeof THREE_NS;
|
|
7
|
+
|
|
8
|
+
type Euler3 = {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
z: number;
|
|
12
|
+
};
|
|
13
|
+
type Node3 = {
|
|
14
|
+
rotation: Euler3;
|
|
15
|
+
position: {
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
z: number;
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
/** A limb of the rich body, as the Bestiário builds it: the group at the joint, the one below it (elbow, knee) and the rest angles. */
|
|
22
|
+
type RigArm = {
|
|
23
|
+
g: Node3;
|
|
24
|
+
el: Node3 & {
|
|
25
|
+
userData: Record<string, unknown>;
|
|
26
|
+
};
|
|
27
|
+
base: number;
|
|
28
|
+
eBase: number;
|
|
29
|
+
};
|
|
30
|
+
type RigLeg = {
|
|
31
|
+
g: Node3;
|
|
32
|
+
kn: Node3;
|
|
33
|
+
base: number;
|
|
34
|
+
kBase: number;
|
|
35
|
+
lost?: boolean;
|
|
36
|
+
};
|
|
37
|
+
/** What `RichBodyBuilder.build` leaves in `root.userData.rig` (the page's `rig`). */
|
|
38
|
+
type RichRig = {
|
|
39
|
+
readonly type: string;
|
|
40
|
+
readonly fig: Node3;
|
|
41
|
+
readonly upper: Node3;
|
|
42
|
+
readonly headG: Node3;
|
|
43
|
+
/** The neck cylinder, taken by identity; absent when the head is cut off. */
|
|
44
|
+
readonly neck?: Node3;
|
|
45
|
+
readonly arms: RigArm[];
|
|
46
|
+
readonly legs: RigLeg[];
|
|
47
|
+
readonly dangles: {
|
|
48
|
+
g: Node3;
|
|
49
|
+
el: Node3;
|
|
50
|
+
}[];
|
|
51
|
+
readonly flaps: (Node3 & {
|
|
52
|
+
userData: Record<string, unknown>;
|
|
53
|
+
})[];
|
|
54
|
+
readonly ph: number;
|
|
55
|
+
readonly dropBase: number;
|
|
56
|
+
readonly leanBase: number;
|
|
57
|
+
readonly hxBase: number;
|
|
58
|
+
/** Cascade state, made on the first call of `cascade`. */
|
|
59
|
+
cur?: Map<readonly [Node3 | Euler3, string, CascadeJoint], number>;
|
|
60
|
+
jl?: (readonly [Node3 | Euler3, string, CascadeJoint])[];
|
|
61
|
+
};
|
|
62
|
+
/** The names a pose may set (the Bestiário's `setPose`). All are optional: a joint a pose does not name keeps what it had. */
|
|
63
|
+
type PoseTargets = Partial<Record<'lean' | 'twist' | 'side' | 'drop' | 'headX' | 'headY' | 'headZ' | 'armR' | 'armL' | 'elbR' | 'elbL' | 'armRz' | 'armLz' | 'legL' | 'legR' | 'kneeL' | 'kneeR', number>>;
|
|
64
|
+
type RigAnimatorOptions = {
|
|
65
|
+
/** Gait profile by the type name the rig carries (`rig.type`); a type that is not here walks like the first type of the Bestiário (the slow one). */
|
|
66
|
+
readonly gaits?: Readonly<Record<string, GaitProfile>>;
|
|
67
|
+
/** Cascade rates per joint. Default: the engine's `CASCADE`. */
|
|
68
|
+
readonly cascade?: Readonly<Record<CascadeJoint, number>>;
|
|
69
|
+
};
|
|
70
|
+
type RigAnimator = {
|
|
71
|
+
/** The pose the gait swings around (legs `lost` stay out of the swing). */
|
|
72
|
+
restOf(rig: RichRig): RestPose;
|
|
73
|
+
/** The Bestiário's `animate0`: sets the joint rotations of one gait at `time`. `stride` ties cadence and swing to the ground speed. */
|
|
74
|
+
animate0(root: THREE_NS.Object3D, time: number, stride?: StrideParams): GaitSample | undefined;
|
|
75
|
+
/** `animate0` plus the bits that hang loose: the dangling forearm and the flaps of torn flesh. */
|
|
76
|
+
animate(root: THREE_NS.Object3D, time: number, stride?: StrideParams): void;
|
|
77
|
+
/** Moves every joint a fraction toward the value the animation left on it, from the hips to the head (the Bestiário's `cascade`). */
|
|
78
|
+
cascade(rig: RichRig, dt: number): void;
|
|
79
|
+
setPose(rig: RichRig, targets: PoseTargets): void;
|
|
80
|
+
lerpPose: typeof lerpPose;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* The rich body's animator: the Bestiário's `animate0`, `animate`, `cascade`, `setPose` and `lerpPose` over the rig the best builder leaves in
|
|
84
|
+
* `root.userData.rig`. The gait maths is the engine's (`animateProfile`, pure CPU); this only reads the rest pose off the rig and writes the result back.
|
|
85
|
+
*/
|
|
86
|
+
declare const createRigAnimator: (options?: RigAnimatorOptions) => RigAnimator;
|
|
87
|
+
|
|
88
|
+
/** The part of a fluid world the bridge needs; `FluidWorld` from `@adatechnology/game-fluids` has it. The package does not import the fluids packages. */
|
|
89
|
+
type BloodSink = {
|
|
90
|
+
splash(liquid: string, point: Vec3, liters: number, velocity?: Vec3): void;
|
|
91
|
+
};
|
|
92
|
+
/** Colours of one body's blood: the wet film and what it dries to (the infection age of a zombie: `ages.json`). */
|
|
93
|
+
type BloodLook = {
|
|
94
|
+
/** Liquid of the blood in the fluid world and in the film (default: the bridge's `liquid`). An old body can bleed `old_blood`. */
|
|
95
|
+
readonly liquid?: string;
|
|
96
|
+
readonly tint?: {
|
|
97
|
+
readonly fresh: number;
|
|
98
|
+
readonly dry: number;
|
|
99
|
+
};
|
|
100
|
+
/** How much of the blood is left, 0 to 1 (the age's `blood` factor): it scales the drops thrown into the film, so a dried-out body throws none. Default 1. */
|
|
101
|
+
readonly amount?: number;
|
|
102
|
+
};
|
|
103
|
+
/** The part of an infection age the look needs (`UndeadAge` of `@adatechnology/game-characters-undead` has all of it). */
|
|
104
|
+
type BloodAge = {
|
|
105
|
+
readonly id: string;
|
|
106
|
+
readonly blood: number;
|
|
107
|
+
readonly freshBloodColor: string;
|
|
108
|
+
readonly dryBloodColor: string;
|
|
109
|
+
};
|
|
110
|
+
type BloodLookOptions = {
|
|
111
|
+
/** Ages with a blood factor below this bleed `thinLiquid` (default 0.3: the old and the decrepit). */
|
|
112
|
+
readonly thinBelow?: number;
|
|
113
|
+
/** Liquid of the thin blood (default `old_blood`). */
|
|
114
|
+
readonly thinLiquid?: string;
|
|
115
|
+
};
|
|
116
|
+
/** The blood of an infection age: its fresh and dry colours (`ages.json`), its amount, and `old_blood` for the thin ones. */
|
|
117
|
+
declare const bloodLookOfAge: (age: BloodAge, options?: BloodLookOptions) => BloodLook;
|
|
118
|
+
/** A drop thrown into the running film of the liquids (`createFilmBridge(...)` of `@adatechnology/game-fluids-three` has it). The package does not import the fluids packages. */
|
|
119
|
+
type FilmSink = {
|
|
120
|
+
splat(splat: {
|
|
121
|
+
fluid: string;
|
|
122
|
+
point: Vec3;
|
|
123
|
+
direction?: Vec3;
|
|
124
|
+
speed?: number;
|
|
125
|
+
radius?: number;
|
|
126
|
+
count?: number;
|
|
127
|
+
tint?: {
|
|
128
|
+
fresh: number;
|
|
129
|
+
dry: number;
|
|
130
|
+
};
|
|
131
|
+
}): number;
|
|
132
|
+
};
|
|
133
|
+
/** What a `wound` throws into the film: the drops, how fast and how big. Defaults 3 drops at 2.5 m/s of 3.2 mm. */
|
|
134
|
+
type WoundSpray = {
|
|
135
|
+
readonly count?: number;
|
|
136
|
+
readonly speed?: number;
|
|
137
|
+
readonly radius?: number;
|
|
138
|
+
};
|
|
139
|
+
type FluidsBridgeOptions = {
|
|
140
|
+
readonly sink: BloodSink;
|
|
141
|
+
/** The running film: when given, a wound, a severed or destroyed segment and a split throw drops of blood into it, in the colours of the body's `BloodLook`. */
|
|
142
|
+
readonly film?: FilmSink;
|
|
143
|
+
/** Drops of a wound (`wound` change of a `body` event). */
|
|
144
|
+
readonly spray?: WoundSpray;
|
|
145
|
+
/** Drops thrown by a cut, a burst and a split (default 4, 7 and 10). */
|
|
146
|
+
readonly burstDrops?: Readonly<Record<'severed' | 'destroyed' | 'split', number>>;
|
|
147
|
+
/** Id of the blood in the fluid world. Default `blood`. */
|
|
148
|
+
readonly liquid?: string;
|
|
149
|
+
/** Metres per second a pressurised leak (a cut artery) throws its drops. Default 1.4. */
|
|
150
|
+
readonly spurtSpeed?: number;
|
|
151
|
+
};
|
|
152
|
+
type FluidsBridge = {
|
|
153
|
+
/** Reads a body's events: `leak` (bleeding) becomes blood on the surface below. Other events are ignored. */
|
|
154
|
+
handle(event: DestructionEvent): void;
|
|
155
|
+
/** The blood a cut, a burst or a split spills at once, at the world point the piece left from (and the drops it throws into the film, when there is one). */
|
|
156
|
+
burst(change: 'severed' | 'destroyed' | 'split', point: Vec3, look?: BloodLook, direction?: Vec3): void;
|
|
157
|
+
/** A wound that does not cut anything off (the `wound` change of the `body` event): drops of blood in the film, thrown along `direction` (default straight down). Returns how many found a grid. */
|
|
158
|
+
wound(point: Vec3, look?: BloodLook, direction?: Vec3): number;
|
|
159
|
+
/** Blood from the engine's own bleeders: `litersPerSecond` over `dt` seconds at `point`. */
|
|
160
|
+
bleed(point: Vec3, litersPerSecond: number, dt: number, direction?: Vec3, pressurized?: boolean): void;
|
|
161
|
+
};
|
|
162
|
+
/**
|
|
163
|
+
* Connects the wounds to the fluids packages, with the fluid world injected: the body keeps its own bleeders (rate, duration, spurt) and this turns them
|
|
164
|
+
* into blood, so the stains, pools and drying are the fluids' business and the character code draws none.
|
|
165
|
+
*/
|
|
166
|
+
declare const createFluidsBridge: ({ sink, film, spray, burstDrops, liquid, spurtSpeed }: FluidsBridgeOptions) => FluidsBridge;
|
|
167
|
+
|
|
168
|
+
/** One sound of a voice: the recipe and, for a sequence, the seconds after the first. */
|
|
169
|
+
type VoiceStep = {
|
|
170
|
+
readonly sound: string;
|
|
171
|
+
readonly delay?: number;
|
|
172
|
+
};
|
|
173
|
+
/**
|
|
174
|
+
* The voice of one undead type:
|
|
175
|
+
* - `origin` says where it comes from: `team` (the page has a sound for this very type), `rule` (the page's class rule applied to the type: the Brutamonte,
|
|
176
|
+
* a chase speed above 3.4, or a walker) or `extra` (the nearest sound of the pages to the voice line of `types.json`, the kit's choice);
|
|
177
|
+
* - `groan` / `gameGroan` is the groan of the art scene and of the game;
|
|
178
|
+
* - `idle` (a body that wanders or chases) and `alert` (it noticed the player or woke up) default to the groan; `special` is what the type does at its
|
|
179
|
+
* ability (the screamer's call, the leaper's jump), and `gameSpecial` its game version (the game has no leap sound, so the art scene's is kept).
|
|
180
|
+
*/
|
|
181
|
+
type UndeadVoice = {
|
|
182
|
+
readonly origin: 'team' | 'rule' | 'extra';
|
|
183
|
+
readonly groan: string;
|
|
184
|
+
readonly gameGroan: string;
|
|
185
|
+
readonly idle?: readonly VoiceStep[];
|
|
186
|
+
readonly alert?: readonly VoiceStep[];
|
|
187
|
+
readonly special?: readonly VoiceStep[];
|
|
188
|
+
readonly gameSpecial?: readonly VoiceStep[];
|
|
189
|
+
};
|
|
190
|
+
/** The voices of the 13 types of the Bestiario, by type id. */
|
|
191
|
+
declare const UNDEAD_VOICES: Readonly<Record<string, UndeadVoice>>;
|
|
192
|
+
declare const UNDEAD_VOICE_SOURCES: readonly string[];
|
|
193
|
+
type VoiceMoment = 'idle' | 'alert' | 'special';
|
|
194
|
+
type CharacterSoundStyle = 'art_scene' | 'game';
|
|
195
|
+
/** What a change of a body sounds like, by the `change` of the `body` event. A head or a jaw hit is brighter (`flesh_head`). */
|
|
196
|
+
type BodySoundRule = {
|
|
197
|
+
readonly sound: string;
|
|
198
|
+
readonly head?: string;
|
|
199
|
+
};
|
|
200
|
+
/** Art scene: bloodHit (cena L1084) is `flesh`, a cut is `sever` (L1127, L1143), a burst or an opened belly is `gore` (L1101, L1132). Game: the flesh class of `sfx`. */
|
|
201
|
+
declare const BODY_SOUNDS: Readonly<Record<CharacterSoundStyle, Readonly<Record<string, BodySoundRule>>>>;
|
|
202
|
+
/** What the player hears when a body grabs, bites or hurts him (cena L1350-L1352, at the listener). */
|
|
203
|
+
declare const PLAYER_SOUNDS: {
|
|
204
|
+
readonly grab: "grab";
|
|
205
|
+
readonly bite: "bite";
|
|
206
|
+
readonly hurt: "hurt";
|
|
207
|
+
};
|
|
208
|
+
/** The part of the host's sound a body needs: `play`, and `register` to put the banks in its engine (`createAudioSink(...)` has both). */
|
|
209
|
+
type CharacterSoundSink = {
|
|
210
|
+
play(request: SoundRequest): unknown;
|
|
211
|
+
register?(bank: unknown): void;
|
|
212
|
+
};
|
|
213
|
+
type CharacterSoundOptions = {
|
|
214
|
+
/** Which team answers (default `art_scene`, the kit mix). `game` plays the game's groans, scream and flesh, and the art scene for what the game lacks. */
|
|
215
|
+
readonly style?: CharacterSoundStyle;
|
|
216
|
+
/** Replaces a type's voice; `null` silences it. */
|
|
217
|
+
readonly voices?: Readonly<Record<string, UndeadVoice | null>>;
|
|
218
|
+
/** Replaces a `body` change's rule; `null` silences it. */
|
|
219
|
+
readonly body?: Readonly<Record<string, BodySoundRule | null>>;
|
|
220
|
+
/** The type used for an id that has no voice (default none: silent). */
|
|
221
|
+
readonly fallbackType?: string;
|
|
222
|
+
};
|
|
223
|
+
type CharacterSound = {
|
|
224
|
+
/** The steps a type plays at a moment. */
|
|
225
|
+
stepsOf(typeId: string, moment: VoiceMoment): readonly VoiceStep[];
|
|
226
|
+
/** Plays the voice of a type at a moment where the body is. Returns how many sounds were started or queued. */
|
|
227
|
+
voice(typeId: string, moment: VoiceMoment, position: Vec3, volume?: number): number;
|
|
228
|
+
/** Reads a `body` event of a body at `position`: a wound is flesh, a cut is `sever`, a burst or a split is `gore`. Other events are ignored. Returns true when a sound was asked for. */
|
|
229
|
+
body(event: DestructionEvent, position: Vec3, volume?: number): boolean;
|
|
230
|
+
/** The player is grabbed, bitten or hurt (heard at the listener). */
|
|
231
|
+
player(kind: keyof typeof PLAYER_SOUNDS, volume?: number): void;
|
|
232
|
+
/** Runs the sequences (the screamer's inhale, then the call): call it each frame. */
|
|
233
|
+
update(dt: number): void;
|
|
234
|
+
/** Sounds waiting for their time. */
|
|
235
|
+
readonly pending: () => number;
|
|
236
|
+
dispose(): void;
|
|
237
|
+
};
|
|
238
|
+
/**
|
|
239
|
+
* Plays the sounds of the bodies on the host's sound: the voices of the undead types (the Bestiario's 13), what a wound sounds like and what the player
|
|
240
|
+
* hears. The banks are registered in the host's engine, the sounds are positional, and the groans go through the engine's `undead_voice` limiter
|
|
241
|
+
* (five at once, one every 0.25 s, nearer than 40 m). The team sounds are the recipes of `./recipes`; the table of who says what is data.
|
|
242
|
+
*/
|
|
243
|
+
declare function createCharacterSound(sink: CharacterSoundSink, options?: CharacterSoundOptions): CharacterSound;
|
|
244
|
+
|
|
245
|
+
/** A crowd on the instanced path: variants baked on demand from the rich builder, one batch per variant and level of detail, members walking a field. */
|
|
246
|
+
type Crowd = {
|
|
247
|
+
readonly group: THREE_NS.Group;
|
|
248
|
+
spawn(count: number): void;
|
|
249
|
+
clear(): void;
|
|
250
|
+
update(dt: number, camera: THREE_NS.Camera): void;
|
|
251
|
+
readonly count: number;
|
|
252
|
+
readonly bakeMs: number;
|
|
253
|
+
readonly drawCalls: number;
|
|
254
|
+
readonly triangles: number;
|
|
255
|
+
/** Triangles of one body at each level of detail (the budget the benchmark checks). */
|
|
256
|
+
trianglesPerLevel(typeId: string): readonly number[];
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
/** One undead type as the area needs it: the content pack's own record has all of these fields. */
|
|
260
|
+
type UndeadCard = {
|
|
261
|
+
readonly id: string;
|
|
262
|
+
readonly name: Readonly<Record<string, string>>;
|
|
263
|
+
readonly eye: {
|
|
264
|
+
readonly color: string;
|
|
265
|
+
};
|
|
266
|
+
readonly gait: Readonly<Record<string, number | boolean>>;
|
|
267
|
+
};
|
|
268
|
+
/** A profession of the people pack (look, accessories, what it holds). */
|
|
269
|
+
type ProfessionCard = {
|
|
270
|
+
readonly id: string;
|
|
271
|
+
readonly name: Readonly<Record<string, string>>;
|
|
272
|
+
readonly female?: boolean;
|
|
273
|
+
readonly look: {
|
|
274
|
+
readonly skin: string;
|
|
275
|
+
readonly hair: {
|
|
276
|
+
readonly style: string;
|
|
277
|
+
readonly color: string;
|
|
278
|
+
};
|
|
279
|
+
readonly hat?: {
|
|
280
|
+
readonly kind: string;
|
|
281
|
+
readonly color: string;
|
|
282
|
+
};
|
|
283
|
+
readonly face?: readonly string[];
|
|
284
|
+
readonly top: {
|
|
285
|
+
readonly color: string;
|
|
286
|
+
readonly sleeve: string;
|
|
287
|
+
readonly plaid?: string;
|
|
288
|
+
};
|
|
289
|
+
readonly pants: {
|
|
290
|
+
readonly color: string;
|
|
291
|
+
};
|
|
292
|
+
readonly shoes: string;
|
|
293
|
+
};
|
|
294
|
+
readonly accessories: readonly {
|
|
295
|
+
readonly id: string;
|
|
296
|
+
readonly color?: string;
|
|
297
|
+
}[];
|
|
298
|
+
readonly hand?: string;
|
|
299
|
+
readonly leftHand?: string;
|
|
300
|
+
};
|
|
301
|
+
/** The part of the undead engine the area uses (`createUndead(kit)` has all of it). */
|
|
302
|
+
type UndeadLike = {
|
|
303
|
+
create(spec: Pick<CharacterSpec, 'seed'> & Partial<CharacterSpec>, options?: {
|
|
304
|
+
readonly type?: string;
|
|
305
|
+
}): DamageCharacter;
|
|
306
|
+
turn(person: DamageCharacter, options?: {
|
|
307
|
+
readonly type?: string;
|
|
308
|
+
}): DamageCharacter;
|
|
309
|
+
traitsOf(character: DamageCharacter): {
|
|
310
|
+
readonly type: string;
|
|
311
|
+
readonly age?: string;
|
|
312
|
+
readonly speeds: Readonly<Record<string, number>>;
|
|
313
|
+
} | undefined;
|
|
314
|
+
speedFor(traits: {
|
|
315
|
+
readonly type: string;
|
|
316
|
+
readonly speeds: Readonly<Record<string, number>>;
|
|
317
|
+
}, context: {
|
|
318
|
+
readonly moment: string;
|
|
319
|
+
readonly limp?: boolean;
|
|
320
|
+
readonly crawling?: boolean;
|
|
321
|
+
}): number;
|
|
322
|
+
onDeath?(character: DamageCharacter): readonly {
|
|
323
|
+
readonly kind: string;
|
|
324
|
+
}[];
|
|
325
|
+
infect?(wound: 'bite' | 'scratch' | 'laceration' | undefined, random: () => number): {
|
|
326
|
+
advance(seconds: number): unknown;
|
|
327
|
+
readonly reanimated: boolean;
|
|
328
|
+
} | undefined;
|
|
329
|
+
};
|
|
330
|
+
type CharactersTestOptions = {
|
|
331
|
+
/** `en` or `pt-BR` (default). */
|
|
332
|
+
readonly locale?: string;
|
|
333
|
+
/** Builds the kit the area uses; called again when the bench changes the penetration model or the death rule. Default `createCharacters` with `packs`. */
|
|
334
|
+
readonly createKit?: (options: {
|
|
335
|
+
readonly penetration: 'physical' | 'team-game';
|
|
336
|
+
readonly deathRule: 'vitality-or-head' | 'head-only';
|
|
337
|
+
}) => CharacterKit;
|
|
338
|
+
/** Packs for the default kit (people, uniformed, undead). */
|
|
339
|
+
readonly packs?: readonly CharacterPack[];
|
|
340
|
+
readonly undeadTypes?: readonly UndeadCard[];
|
|
341
|
+
readonly createUndead?: (kit: CharacterKit) => UndeadLike;
|
|
342
|
+
readonly professions?: readonly ProfessionCard[];
|
|
343
|
+
/** Where the blood of the wounds goes (the room's fluid world: `room.fluids`). Without it the wounds draw no blood on the ground. */
|
|
344
|
+
readonly fluids?: BloodSink;
|
|
345
|
+
/** The running film of the liquids (the room's `room.film`): wounds throw drops of blood into it, in the colours `bloodOf` gives. Without it only the fluid world gets blood. */
|
|
346
|
+
readonly film?: FilmSink;
|
|
347
|
+
/** The sounds of the bodies on the room's engine (`host.audio`): wounds, cuts and the voices of the bench. `false` leaves the area silent. */
|
|
348
|
+
readonly sound?: false | CharacterSoundOptions;
|
|
349
|
+
/** The blood of a body by its infection age (`bloodLookOfAge` over the content's ages). Bodies without an age, and games without this option, bleed the liquid's own colours. */
|
|
350
|
+
readonly bloodOf?: (age: string) => BloodLook | undefined;
|
|
351
|
+
/** Seed of the draws of the area (default 1). */
|
|
352
|
+
readonly seed?: number;
|
|
353
|
+
/** Crowd types to bake for the instanced field, by undead id (default walker and runner). */
|
|
354
|
+
readonly crowdTypes?: readonly string[];
|
|
355
|
+
/** Triangles a baked body may have at each level of detail (default 2500, 800, 250, the Bancada de Hordas budget). Given explicitly, every level is a hard check. */
|
|
356
|
+
readonly triangleBudget?: readonly number[];
|
|
357
|
+
};
|
|
358
|
+
|
|
359
|
+
/** What a scenario needs of the room: the pass/fail line and time. The test room's `ScenarioContext` has both. */
|
|
360
|
+
type AreaContext = {
|
|
361
|
+
expect(condition: boolean, label: string, data?: Record<string, unknown>): void;
|
|
362
|
+
step(seconds: number): void;
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
declare const CHARACTERS_AREA_ID = "characters_area";
|
|
366
|
+
declare const ZOMBIE_ITEM_PREFIX = "zombie_";
|
|
367
|
+
declare const PERSON_ITEM_PREFIX = "person_";
|
|
368
|
+
/**
|
|
369
|
+
* The characters test area as a `TestableModule`: the undead types and the professions as bodies the room can shoot (each one a `DestructibleImpl` of the damage model, drawn with the
|
|
370
|
+
* rich builders, wounds, eyes and gait), a shooting lane, a crowd on the instanced path, and a bench with the damage rules, the hits, the protection, the moment and the layers.
|
|
371
|
+
* Everything comes in by option: the packs of content (people, undead) are the host page's, so this package knows no game.
|
|
372
|
+
*/
|
|
373
|
+
declare function createCharactersTestModule(options?: CharactersTestOptions): TestableModule<AreaContext> & {
|
|
374
|
+
readonly entries: ReadonlyMap<string, unknown>;
|
|
375
|
+
readonly crowd: Crowd | null;
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* What the "Hit" control of the bench does to the body: typical loads, in joules and millimetres, as the arsenal's calibre table gives them.
|
|
380
|
+
* `caliberId` is used only when the kit's table knows it (the `team-game` model).
|
|
381
|
+
*/
|
|
382
|
+
type HitPreset = {
|
|
383
|
+
readonly id: string;
|
|
384
|
+
readonly hit: Omit<BodyHit, 'point' | 'direction'>;
|
|
385
|
+
readonly range?: number;
|
|
386
|
+
};
|
|
387
|
+
declare const HIT_PRESETS: readonly HitPreset[];
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* The material the parts of a living body carry (`BODY_MATERIAL` of the engine), as a destruction world needs it declared: pass it in the world's material list
|
|
391
|
+
* (`worldOptions.materials` of the test room) before loading the characters area. The body model owns the damage; the world only needs the density and thermal data
|
|
392
|
+
* to raycast, blast and burn a body like any other object. Numbers are those of soft tissue (about 1050 kg/m3).
|
|
393
|
+
*/
|
|
394
|
+
declare const BODY_MATERIAL_DEF: {
|
|
395
|
+
readonly id: "flesh";
|
|
396
|
+
readonly density: 1050;
|
|
397
|
+
readonly hpPerKg: 15;
|
|
398
|
+
readonly penetration: 1.2;
|
|
399
|
+
readonly ductility: 0.8;
|
|
400
|
+
readonly fracture: "tear";
|
|
401
|
+
readonly fragmentSize: readonly [0.03, 0.1];
|
|
402
|
+
readonly damageScale: {
|
|
403
|
+
readonly slash: 1.6;
|
|
404
|
+
};
|
|
405
|
+
readonly hitDebris: "drips";
|
|
406
|
+
readonly breakDebris: "drips";
|
|
407
|
+
readonly thermal: {
|
|
408
|
+
readonly ignitionC: 260;
|
|
409
|
+
readonly flameC: 700;
|
|
410
|
+
readonly fuelFraction: 0.25;
|
|
411
|
+
readonly burnRate: 0.004;
|
|
412
|
+
readonly conductivity: 0.5;
|
|
413
|
+
readonly heatCapacity: 3.5;
|
|
414
|
+
readonly smoke: "grey";
|
|
415
|
+
};
|
|
416
|
+
readonly tags: readonly ["organic", "body"];
|
|
417
|
+
readonly compressive: 1000;
|
|
418
|
+
};
|
|
419
|
+
/** Puts the body material in a destruction world that does not know it yet (`room.world`); a world that has it is left alone. Call it before `room.loadModule`. */
|
|
420
|
+
declare const registerBodyMaterial: (world: {
|
|
421
|
+
readonly materials: Map<string, unknown>;
|
|
422
|
+
}) => void;
|
|
423
|
+
|
|
424
|
+
/** The person builder's spec for a profession of the people pack (look and gear as numbers; the builder draws the rest). */
|
|
425
|
+
declare const personSpecOf: (profession: ProfessionCard) => Record<string, any>;
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* The eye the hands are seen from, given per call to `apply` (null or none is the body seen from outside: nothing changes). `reachBend` keeps the elbow bent where a hand is out of reach, the hand stopping
|
|
429
|
+
* short instead of the arm locking; `keepOutside` pulls a hand back so it stays in front of the surface `contact` finds along the camera's forward.
|
|
430
|
+
*/
|
|
431
|
+
type ReachView = {
|
|
432
|
+
readonly camera: THREE_NS.Object3D;
|
|
433
|
+
/** The least interior bend of the elbow, radians, right then left. Default `ARM_REACH.viewBend`. */
|
|
434
|
+
readonly reachBend?: readonly [number, number];
|
|
435
|
+
readonly keepOutside?: {
|
|
436
|
+
/** The distance to the surface from `origin` along `direction` within `maxDistance`, or null. */
|
|
437
|
+
contact(origin: [number, number, number], direction: [number, number, number], maxDistance: number): number | null;
|
|
438
|
+
/** Default `ARM_REACH.contactMargin`. */
|
|
439
|
+
readonly margin?: number;
|
|
440
|
+
/** Default `ARM_REACH.contactNearest`. */
|
|
441
|
+
readonly nearest?: number;
|
|
442
|
+
};
|
|
443
|
+
};
|
|
444
|
+
|
|
445
|
+
type AvatarViewMode = 'first' | 'third';
|
|
446
|
+
|
|
447
|
+
type PlayerAvatarSpec = Record<string, unknown>;
|
|
448
|
+
type PlayerAvatarOptions = {
|
|
449
|
+
/** The body spec for the rich builder (`createBestBuilder.build`). Default: the team game's player (`player-avatar.json`). Merged over the default. */
|
|
450
|
+
readonly spec?: PlayerAvatarSpec;
|
|
451
|
+
/** Shirt colour, 0xRRGGBB. Default the team game's `PLAYER_COR`. */
|
|
452
|
+
readonly shirtColor?: number;
|
|
453
|
+
/** The clips `play(id)` finds. Default: a registry with `POSE_CLIPS`. */
|
|
454
|
+
readonly clips?: PoseClipRegistry;
|
|
455
|
+
/** Seconds to blend a clip in and out. Default 0.12. */
|
|
456
|
+
readonly blend?: number;
|
|
457
|
+
/** Seconds the body takes to go down (the shader's `fk` smoothstep). 0 is instant. Default 0.3. */
|
|
458
|
+
readonly fallSeconds?: number;
|
|
459
|
+
/** Smooth the joints with the Bestiário cascade (default true). */
|
|
460
|
+
readonly cascade?: boolean;
|
|
461
|
+
/**
|
|
462
|
+
* 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
|
|
463
|
+
* still be on their way when it ends (a punch that never reaches the shoulder). 1 keeps the walk's pace. Default 5.
|
|
464
|
+
*/
|
|
465
|
+
readonly clipCascade?: number;
|
|
466
|
+
/** Where the eye sits against the builder's eye point, in the head's frame (so it follows the head's scale and turn). Default [0, -0.068, 0]: about 1.70 m standing. */
|
|
467
|
+
readonly eyeOffset?: readonly [number, number, number];
|
|
468
|
+
/** How much of the walking and running bob of the body reaches the eye: 0 none, 1 all of it. Default 0.5. */
|
|
469
|
+
readonly eyeBob?: number;
|
|
470
|
+
};
|
|
471
|
+
type AvatarEvent = PoseClipEvent & {
|
|
472
|
+
clip: string;
|
|
473
|
+
};
|
|
474
|
+
type AvatarItemUsePose = {
|
|
475
|
+
readonly id: string;
|
|
476
|
+
readonly position: readonly [number, number, number];
|
|
477
|
+
readonly rotation: readonly [number, number, number];
|
|
478
|
+
/** How closed the other hand is (0 open to 1 closed) when it works on the item, as a hand turning a cap off opens between strokes; the room brings it there with `reach`. */
|
|
479
|
+
readonly gripL?: number;
|
|
480
|
+
};
|
|
481
|
+
type AvatarUpdate = {
|
|
482
|
+
/** Ground speed, m/s. Ties cadence and swing to the ground (the game's `walkCad` and `walkGait`). */
|
|
483
|
+
speed?: number;
|
|
484
|
+
/** Accepted for the game's state; the gait depends on the speed alone, as in the team game. */
|
|
485
|
+
running?: boolean;
|
|
486
|
+
crouch?: boolean;
|
|
487
|
+
down?: boolean;
|
|
488
|
+
/** Weapon in hand: both arms follow the aim (the game's arm code). Default false. */
|
|
489
|
+
armed?: boolean;
|
|
490
|
+
/** The camera pitch the game's `playerArmCode()` reads (`view.pitch`). */
|
|
491
|
+
pitch?: number;
|
|
492
|
+
/** Leg yaw toward the step while armed (the game's `P.hip`, radians). */
|
|
493
|
+
hip?: number;
|
|
494
|
+
/** Whether the player is carrying an item in the right hand. */
|
|
495
|
+
holding?: boolean;
|
|
496
|
+
/** Active scripted item use (`drink`, `sip`, `chug`, `pour`, `open`, `close`, `tilt`). */
|
|
497
|
+
itemUse?: AvatarItemUsePose | null;
|
|
498
|
+
/** Hands on a piece of furniture: `grip` closes on it with `progress`, `push` walks with it, `strain` strains against what does not give. */
|
|
499
|
+
push?: {
|
|
500
|
+
readonly phase: 'grip' | 'push' | 'strain';
|
|
501
|
+
readonly progress: number;
|
|
502
|
+
} | null;
|
|
503
|
+
/** On a seat: `sit` folds the legs under hips `hip` metres above the feet, `lie` lays the body face up with the pelvis there, the head behind the facing. */
|
|
504
|
+
seat?: {
|
|
505
|
+
readonly pose: 'sit' | 'lie';
|
|
506
|
+
readonly hip: number;
|
|
507
|
+
} | null;
|
|
508
|
+
/**
|
|
509
|
+
* A hand on a door, lid or drawer: `reach` to the handle, `swing` with it (`pull` toward the body or pushed away), `release` back, `rattle` on a locked one.
|
|
510
|
+
* `height` is the hand over the feet, metres (a fridge handle, a car door, a low drawer).
|
|
511
|
+
*/
|
|
512
|
+
door?: {
|
|
513
|
+
readonly phase: 'reach' | 'swing' | 'release' | 'rattle';
|
|
514
|
+
readonly progress: number;
|
|
515
|
+
readonly height: number;
|
|
516
|
+
readonly pull: boolean;
|
|
517
|
+
} | null;
|
|
518
|
+
};
|
|
519
|
+
type PlayPoseOptions = {
|
|
520
|
+
onEvent?: (event: AvatarEvent) => void;
|
|
521
|
+
/** Overrides the avatar's blend for this clip. */
|
|
522
|
+
blend?: number;
|
|
523
|
+
};
|
|
524
|
+
/** 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. */
|
|
525
|
+
type PlayerAvatarColors = {
|
|
526
|
+
skin: number;
|
|
527
|
+
shirt: number;
|
|
528
|
+
trousers: number;
|
|
529
|
+
shoes: number;
|
|
530
|
+
longSleeves: boolean;
|
|
531
|
+
};
|
|
532
|
+
type PlayerAvatar = {
|
|
533
|
+
root: THREE_NS.Object3D;
|
|
534
|
+
rig: RichRig;
|
|
535
|
+
/** What the body wears, read from the body itself. */
|
|
536
|
+
readonly colors: PlayerAvatarColors;
|
|
537
|
+
/** The three.js namespace the body was made with, so what is made to fit it (the first-person arms) uses the same one. */
|
|
538
|
+
readonly three: Three;
|
|
539
|
+
update(dt: number, state?: AvatarUpdate): void;
|
|
540
|
+
/**
|
|
541
|
+
* The world position of the eyes, as `update` left them: sampled at its end, before any `reach()` (which turns the chest and would swing the head), and kept in the
|
|
542
|
+
* body's own frame so it stays right when the host moves `root` afterwards. With `out` it fills that vector instead of making one.
|
|
543
|
+
*/
|
|
544
|
+
eye(out?: THREE_NS.Vector3): THREE_NS.Vector3;
|
|
545
|
+
/** In 'first' with a camera, the head and neck are not drawn for that camera only (shadows, mirrors and every other camera still see them); 'third' draws them. */
|
|
546
|
+
setView(mode: AvatarViewMode, camera: THREE_NS.Object3D | null): void;
|
|
547
|
+
play(clip: string | PoseClip, options?: PlayPoseOptions): void;
|
|
548
|
+
stop(): void;
|
|
549
|
+
readonly playing: string | null;
|
|
550
|
+
/** A socket at the hand: 'R' is the weapon hand (the puppet's right, at -x), 'L' the other one. */
|
|
551
|
+
hand(side: 'R' | 'L'): THREE_NS.Object3D;
|
|
552
|
+
/** The joint of a shoulder ('R' the weapon side, at -x): where a long gun's stock rests, just inside it. */
|
|
553
|
+
shoulder(side: 'R' | 'L'): THREE_NS.Object3D;
|
|
554
|
+
/**
|
|
555
|
+
* Brings the hands to where the tool in them asks (`HandTargets`, world space) after `update`: shoulder, elbow and wrist each bend inside the human range, the chest turns and the shoulder
|
|
556
|
+
* blade slides toward a hand out of reach. Null, a strike playing or the body going down leave the arms as `update` posed them. Call it after the tool is placed and the root moved.
|
|
557
|
+
* `view` (given on each call, as the camera moves) is for the body seen from its own eyes: the elbows keep their bend and the hands stay out of walls; without it nothing changes.
|
|
558
|
+
*/
|
|
559
|
+
reach(targets: HandTargets | null, view?: ReachView | null): void;
|
|
560
|
+
on(type: 'event', listener: (event: AvatarEvent) => void): () => void;
|
|
561
|
+
dispose(): void;
|
|
562
|
+
};
|
|
563
|
+
/**
|
|
564
|
+
* 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
|
|
565
|
+
* 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`).
|
|
566
|
+
*/
|
|
567
|
+
declare const createPlayerAvatar: (THREE: Three, options?: PlayerAvatarOptions) => PlayerAvatar;
|
|
568
|
+
|
|
569
|
+
declare const AVATAR_AREA_ID = "avatar_area";
|
|
570
|
+
type AvatarTestOptions = {
|
|
571
|
+
/** The body under test (the example makes it with `createPlayerAvatar(THREE)` and gives the same one to the room). */
|
|
572
|
+
avatar: PlayerAvatar;
|
|
573
|
+
/** The text language when the host does not ask for one. Default `pt-BR`. */
|
|
574
|
+
locale?: string;
|
|
575
|
+
/** 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. */
|
|
576
|
+
clips?: readonly PoseClip[];
|
|
577
|
+
/** Texts of the `phase` labels of `clips` by locale, merged over `POSE_CLIP_LOCALES` (for example `ATTACK_CLIP_LOCALES`). */
|
|
578
|
+
clipLocales?: Readonly<Record<string, Readonly<Record<string, string>>>>;
|
|
579
|
+
/** The room's view: `mode()` reads it, `set` changes it (`room.cameraMode` and `room.setCameraMode`). Without it the camera buttons only log. */
|
|
580
|
+
camera?: {
|
|
581
|
+
mode(): 'first' | 'third';
|
|
582
|
+
set(mode: 'first' | 'third'): void;
|
|
583
|
+
};
|
|
584
|
+
/** The room's avatar state (`room.setAvatarState`): the module drives crouch, down and the armed arms through it. */
|
|
585
|
+
state?: {
|
|
586
|
+
set(state: {
|
|
587
|
+
crouch?: boolean;
|
|
588
|
+
down?: boolean;
|
|
589
|
+
armed?: boolean | null;
|
|
590
|
+
}): void;
|
|
591
|
+
};
|
|
592
|
+
};
|
|
593
|
+
/**
|
|
594
|
+
* 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
|
|
595
|
+
* pose clip (grab, bite), and metrics for the camera, the clip, its last event and the joints. Every action goes to the room's
|
|
596
|
+
* 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.
|
|
597
|
+
*/
|
|
598
|
+
declare function createAvatarTestModule(options: AvatarTestOptions): TestableModule & {
|
|
599
|
+
readonly state: {
|
|
600
|
+
crouch: boolean;
|
|
601
|
+
down: boolean;
|
|
602
|
+
armed: boolean | null;
|
|
603
|
+
};
|
|
604
|
+
};
|
|
605
|
+
|
|
606
|
+
export { createPlayerAvatar as $, type AvatarItemUsePose as A, type BloodLook as B, type CharacterSound as C, type PlayerAvatarSpec as D, type PoseTargets as E, type FluidsBridge as F, type ProfessionCard as G, HIT_PRESETS as H, type RigAnimator as I, type RigAnimatorOptions as J, UNDEAD_VOICE_SOURCES as K, type UndeadCard as L, type UndeadLike as M, type UndeadVoice as N, type VoiceStep as O, type PlayerAvatar as P, bloodLookOfAge as Q, type RichRig as R, createAvatarTestModule as S, type Three as T, UNDEAD_VOICES as U, type VoiceMoment as V, type WoundSpray as W, createCharacterSound as X, createCharactersTestModule as Y, ZOMBIE_ITEM_PREFIX as Z, createFluidsBridge as _, type AvatarViewMode as a, createRigAnimator as a0, personSpecOf as a1, registerBodyMaterial as a2, type ReachView as b, AVATAR_AREA_ID as c, type AreaContext as d, type AvatarEvent as e, type AvatarTestOptions as f, type AvatarUpdate as g, BODY_MATERIAL_DEF as h, BODY_SOUNDS as i, type BloodAge as j, type BloodLookOptions as k, type BloodSink as l, type BodySoundRule as m, CHARACTERS_AREA_ID as n, type CharacterSoundOptions as o, type CharacterSoundSink as p, type CharacterSoundStyle as q, type CharactersTestOptions as r, type FilmSink as s, type FluidsBridgeOptions as t, type HitPreset as u, PERSON_ITEM_PREFIX as v, PLAYER_SOUNDS as w, type PlayPoseOptions as x, type PlayerAvatarColors as y, type PlayerAvatarOptions as z };
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export { c as AVATAR_AREA_ID, d as AreaContext, f as AvatarTestOptions, h as BODY_MATERIAL_DEF, n as CHARACTERS_AREA_ID, r as CharactersTestOptions, H as HIT_PRESETS, u as HitPreset, v as PERSON_ITEM_PREFIX, G as ProfessionCard, L as UndeadCard, M as UndeadLike, Z as ZOMBIE_ITEM_PREFIX, S as createAvatarTestModule, Y as createCharactersTestModule, a1 as personSpecOf, a2 as registerBodyMaterial } from './testing-DyQw8dCw.js';
|
|
2
|
+
import '@adatechnology/game-contracts';
|
|
3
|
+
import 'three';
|
|
4
|
+
import '@adatechnology/game-characters';
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AVATAR_AREA_ID,
|
|
3
|
+
BODY_MATERIAL_DEF,
|
|
4
|
+
CHARACTERS_AREA_ID,
|
|
5
|
+
HIT_PRESETS,
|
|
6
|
+
PERSON_ITEM_PREFIX,
|
|
7
|
+
ZOMBIE_ITEM_PREFIX,
|
|
8
|
+
createAvatarTestModule,
|
|
9
|
+
createCharactersTestModule,
|
|
10
|
+
personSpecOf,
|
|
11
|
+
registerBodyMaterial
|
|
12
|
+
} from "./chunk-3CHBEUHW.js";
|
|
13
|
+
export {
|
|
14
|
+
AVATAR_AREA_ID,
|
|
15
|
+
BODY_MATERIAL_DEF,
|
|
16
|
+
CHARACTERS_AREA_ID,
|
|
17
|
+
HIT_PRESETS,
|
|
18
|
+
PERSON_ITEM_PREFIX,
|
|
19
|
+
ZOMBIE_ITEM_PREFIX,
|
|
20
|
+
createAvatarTestModule,
|
|
21
|
+
createCharactersTestModule,
|
|
22
|
+
personSpecOf,
|
|
23
|
+
registerBodyMaterial
|
|
24
|
+
};
|
|
25
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|