@adatechnology/game-test-room 0.16.0 → 0.17.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
@@ -181,6 +181,7 @@ when a body (the player, an object of the room) crosses it; whatever was already
181
181
  | E | grab / drop |
182
182
  | R | rotate the held item |
183
183
  | Right button or G | throw |
184
+ | K | kick with the `kick` tool whatever is in hand (`kick.keys`, default `['KeyK']`; also the Kick button and `room.kick()`); a 250 J blunt blow, ten times the punch, that lands from the leg (`kickHeights`, every 10 cm from 0.25 to 0.85 m above the feet, level) on what stands low in front, else where the eye aims |
184
185
  | F | use a fluid item (drink); Z tilt; C fill from the tap; V pour (holding a fluid item); otherwise V toggles 1st/3rd person when a body is set (`setAvatar`), and kicks when there is none (Shift+V kicks with a body too; the `kick` tool whatever is selected) |
185
186
  | Tab | show / hide the panels |
186
187
 
@@ -202,7 +203,9 @@ interface RoomAvatar {
202
203
 
203
204
  Every frame the room puts `root` at the player's feet turned to where the player looks (the body's front is +z, as the catalogue items'; `rotation.y` is the yaw plus pi) and calls `update` with the ground speed of the player's movement, Shift, `armed` (a firearm is the tool in hand) and the camera `pitch` (radians, positive looking up). `crouch` and `down` are not known to the room: a player module sets them with `room.setAvatarState({ crouch, down })` (`avatarState()` reads them back). `@adatechnology/game-characters-three` has such a body: `toRoomAvatar(createPlayerAvatar(THREE))`.
204
205
 
205
- **First-person arms and strikes.** An avatar may carry `firstPerson` (arms drawn in the camera's space; the room shows them only in the first person, updates them every frame after the modules' frame hooks and hands them where the tool in the hands asks the hands to be) and `strike(clipId)`. The hand targets come from the module that places the tool (`host.setHandTargets(source)`, `HandTargets` of `@adatechnology/game-contracts`: world-space poses of the right and left hand plus the grip closure and the size of the hands), else from the carried item, else null; the stance is `hold` when there are targets, else `handStances[toolId]` (default `{ fists: 'fists' }`) or `relaxed`. A melee strike (`fists`, `shove`, Shift+V kick) no longer lands at the click: the room calls `avatar.strike(strikeClips[toolId])` (default `{ fists: 'punch_right', shove: 'shove_two_hands', kick: 'front_kick' }`), which plays the clip on the arms and on the body and answers the seconds to its impact, writes a `strike` line to the log, and calls the existing `meleeUse` at that second with the aim read again. The left click with bare hands (`hand`, the default tool) punches too (E or the right button grab). Punches alternate hands, right first (`punchHand`: `alternate`, `left`, `right`; `punchTools`, default `['hand', 'fists']`): the room calls `strike(clip, { hand })` and logs the hand, as the kick does with `kickLeg` and `{ leg }`. A tool without a clip, or an avatar that answers null, keeps the instant blow; a strike pending blocks the same tool. `toRoomAvatar(createPlayerAvatar(THREE))` already gives all of it (`firstPerson: false` for no arms).
206
+ **Pushing furniture.** Against a piece that stands on the floor, with bare hands, a tap on E grabs as ever and holding E fills a ring around the crosshair (longer the heavier). Then W pushes, S pulls, slower with the mass; letting go ends it. Before each step the volume of the piece is swept (`world.sweepBox`) against everything and the arena: at an obstacle the piece and the player stop and the player strains, and a piece too heavy for the player (`RoomOptions.push`, `FurniturePushRules` of game-player) never moves. A piece lower than a table is pushed from above, bent over it. Scripted: `room.push`. Try it: `?scene=push` in the example.
207
+
208
+ **First-person arms and strikes.** An avatar may carry `firstPerson` (arms drawn in the camera's space; the room shows them only in the first person, updates them every frame after the modules' frame hooks and hands them where the tool in the hands asks the hands to be) and `strike(clipId)`. The hand targets come from the module that places the tool (`host.setHandTargets(source)`, `HandTargets` of `@adatechnology/game-contracts`: world-space poses of the right and left hand plus the grip closure and the size of the hands), else from the carried item, else null; the stance is `hold` when there are targets, else `handStances[toolId]` (default `{ fists: 'fists' }`) or `relaxed`. A melee strike (`fists`, `shove`, Shift+V kick) no longer lands at the click: the room calls `avatar.strike(strikeClips[toolId])` (default `{ fists: 'punch_right', shove: 'shove_two_hands', kick: 'front_kick' }`), which plays the clip on the arms and on the body and answers the seconds to its impact, writes a `strike` line to the log, and calls the existing `meleeUse` at that second with the aim read again. The left click with bare hands (`hand`, the default tool) punches too (E or the right button grab). Punches alternate hands, right first (`punchHand`: `alternate`, `left`, `right`; `punchTools`, default `['hand', 'fists']`): the room calls `strike(clip, { hand })` and logs the hand, as the kick does with `kickLeg` and `{ leg }`. A **strike combo** is an ability the page gives (`strikeCombo`, such as `PUNCH_COMBO` of `game-characters-people`: jab, cross, hook, uppercut): while it is on (N, its button, `setStrikeCombo(true)`, `strikeComboOn`) each blow of its tools plays its next step (`{ clip, hand }`) and a pause longer than its `reset` starts it over; off, the tools strike as usual. The strike line carries `combo` and `step`. A tool without a clip, or an avatar that answers null, keeps the instant blow; a strike pending blocks the same tool. `toRoomAvatar(createPlayerAvatar(THREE))` already gives all of it (`firstPerson: false` for no arms).
206
209
 
207
210
  `room.setCameraMode('first' | 'third')` (option `cameraMode`, property `cameraMode`, event `room.on('camera', fn)`; every change is a `camera` action in the log). V toggles it when a body is set. In `first` the body is hidden; in `third` it is shown, the camera stands over the shoulder, and a carried item rides `avatar.hand('R')` instead of the camera frame. Option `thirdPerson` changes the view; the defaults are the team game's shoulder camera (`zombiada-modo-livre.html`, `cameraUpdate`, line 76546): `back` 3.8 m, `side` 0.9 m to the right, `up` 1.95 m (`crouchUp` 1.4 m), `lift` 0.1 m, three probe rays (`probes` 0 and +-0.2 m) that pull the camera in `margin` 0.3 m short of a wall (never closer than `minDistance` 0.2 m) and let it out at `recover` 4 per second.
208
211
 
@@ -237,7 +240,7 @@ automáticos conferem o resultado e gravam aprovado/reprovado no mesmo registro.
237
240
  Um módulo que declara controles, camadas ou contadores ganha um painel de bancada genérico; o estado vai para a URL do exemplo (`?bench.<idDoMódulo>=`) e
238
241
  `room.bench()` / `room.metrics()` dirigem e leem a bancada por script.
239
242
 
240
- Corpo do jogador: `room.setAvatar(corpo)` põe um corpo (qualquer objeto no formato `RoomAvatar`) nos pés do jogador; `room.setCameraMode('first' | 'third')` e a tecla V alternam entre 1ª e 3ª pessoa (câmera sobre o ombro com os números do jogo da equipe, puxada para perto de paredes). `room.setAvatarState({ crouch, down })` deixa um módulo de jogador agachar e derrubar o corpo. Com corpo, V alterna a câmera e Shift+V chuta. Em primeira pessoa o corpo pode trazer braços e mãos (`avatar.firstPerson`) que seguram a arma onde o módulo manda (`host.setHandTargets`): mão direita no punho, esquerda no guarda-mão, as duas na pistola, esquerda ao carregador na recarga; sem nada na mão ficam relaxadas, e `fists` levanta os punhos. Soco, empurrão e chute tocam o clipe (`avatar.strike`, opção `strikeClips`) e o efeito só acontece no instante do impacto, não no clique; um golpe pendente bloqueia a mesma ferramenta.
243
+ Corpo do jogador: `room.setAvatar(corpo)` põe um corpo (qualquer objeto no formato `RoomAvatar`) nos pés do jogador; `room.setCameraMode('first' | 'third')` e a tecla V alternam entre 1ª e 3ª pessoa (câmera sobre o ombro com os números do jogo da equipe, puxada para perto de paredes). `room.setAvatarState({ crouch, down })` deixa um módulo de jogador agachar e derrubar o corpo. Com corpo, V alterna a câmera; K (ou Shift+V, o botão Chute e `room.kick()`) chuta com qualquer ferramenta na mão. Em primeira pessoa o corpo pode trazer braços e mãos (`avatar.firstPerson`) que seguram a arma onde o módulo manda (`host.setHandTargets`): mão direita no punho, esquerda no guarda-mão, as duas na pistola, esquerda ao carregador na recarga; sem nada na mão ficam relaxadas, e `fists` levanta os punhos. Soco, empurrão e chute tocam o clipe (`avatar.strike`, opção `strikeClips`) e o efeito só acontece no instante do impacto, não no clique; um golpe pendente bloqueia a mesma ferramenta.
241
244
 
242
245
  ## Known limitations
243
246
 
package/dist/index.d.ts CHANGED
@@ -2,14 +2,40 @@ import * as THREE_NS from 'three';
2
2
  import { AudioEngine } from '@adatechnology/game-audio';
3
3
  import { Vec3 as Vec3$1, DestructionWorld, BlastOptions, DestructibleSpec, DestructionOptions, DestructionEvent } from '@adatechnology/game-destruction';
4
4
  export { DestructionEvent, Vec3 } from '@adatechnology/game-destruction';
5
- import { BusySink, Vec3, HandTargets, ModuleControl, ControlValue, ModuleLayer, EnvironmentBus, BusyTask, BusyOptions, TestableModule, DestructibleImpl, AnimationClipSpec, BlastShoveOptions } from '@adatechnology/game-contracts';
5
+ import { FurniturePushPhase, FurniturePushBlock, FurniturePushRules } from '@adatechnology/game-player';
6
+ import { BusySink, Vec3, HandTargets, ModuleControl, ControlValue, ModuleLayer, EnvironmentBus, BusyTask, BusyOptions, TestableModule, DestructibleImpl, AnimationClipSpec, GroundLayout, BlastShoveOptions } from '@adatechnology/game-contracts';
6
7
  export { AnimationClipSpec, DestructibleImpl, ItemAnimationContract, ModuleScenario, TestableItem, TestableModule } from '@adatechnology/game-contracts';
7
8
  import { DestructionRenderer, SpecFromObjectOptions, Quality } from '@adatechnology/game-destruction-three';
8
9
  import { Arsenal, WeaponState, WeaponDef } from '@adatechnology/game-weapons';
9
10
  import { VfxWorld } from '@adatechnology/game-vfx';
11
+ import { Terrain, TerrainBudget } from '@adatechnology/game-terrain';
10
12
  import { FilmBridge, FluidRenderer } from '@adatechnology/game-fluids-three';
11
13
  import { FluidWorld, ContainerSpec, LiquidDef, ItemUseDef, SurfaceSpec } from '@adatechnology/game-fluids';
12
14
 
15
+ /**
16
+ * The room's side of pushing furniture: finds what the player leans on, asks `createFurniturePush` (game-player) how it goes, tests the volume of the piece before each
17
+ * step (the world, the walls of the arena, the blockers of the modules) and moves it with the player. The animations read `avatarState` and `handTargets`.
18
+ */
19
+
20
+ type RoomPushEvent = {
21
+ readonly type: 'grip' | 'release';
22
+ readonly objectId: string;
23
+ readonly name: string;
24
+ readonly massKg: number;
25
+ } | {
26
+ readonly type: 'engaged' | 'too_heavy' | 'obstacle';
27
+ readonly objectId: string;
28
+ readonly name: string;
29
+ readonly massKg: number;
30
+ };
31
+ type RoomPushState = {
32
+ readonly objectId: string;
33
+ readonly phase: FurniturePushPhase;
34
+ readonly progress: number;
35
+ readonly block: FurniturePushBlock;
36
+ readonly massKg: number;
37
+ };
38
+
13
39
  /** What the indicator shows now: the label of the task on top, its line of text, and how far it is (the bar) when it knows. */
14
40
  interface LoadingState {
15
41
  readonly label: string;
@@ -273,6 +299,8 @@ interface HudState {
273
299
  timeScale: number;
274
300
  fast: boolean;
275
301
  tap: boolean;
302
+ /** The strike combo (`strikeCombo`) is on; null when the room has none. */
303
+ combo?: boolean | null;
276
304
  /** The room's blast options that are on. */
277
305
  blast: {
278
306
  shelter: boolean;
@@ -311,6 +339,11 @@ interface RoomAvatarState {
311
339
  holding?: boolean;
312
340
  /** Active scripted item use (`drink`, `sip`, `chug`, `pour`, `open`, `close`, `tilt`). */
313
341
  itemUse?: RoomAvatarItemUse | null;
342
+ /** Hands on a piece of furniture: `grip` while the hold timer fills (`progress`), `push` walking with it, `strain` straining against what does not give. */
343
+ push?: {
344
+ readonly phase: 'grip' | 'push' | 'strain';
345
+ readonly progress: number;
346
+ } | null;
314
347
  }
315
348
  /** What the room tells the first-person arms every frame, after the modules have placed the tool in the hands. */
316
349
  interface RoomFirstPersonState {
@@ -398,6 +431,18 @@ interface ThirdPersonOptions {
398
431
  * from buttons (or headless in tests) and write pass/fail checks to the same log, so people and agents can verify the behaviour.
399
432
  */
400
433
 
434
+ /** What `strikeCombo` takes: the shape of `StrikeCombo` of game-characters-people, restated so the room does not depend on it. */
435
+ type StrikeComboOption = {
436
+ readonly id: string;
437
+ readonly name?: Readonly<Record<string, string>>;
438
+ readonly tools: readonly string[];
439
+ readonly steps: ReadonlyArray<{
440
+ readonly clip: string;
441
+ readonly hand?: 'left' | 'right';
442
+ }>;
443
+ readonly reset: number;
444
+ };
445
+
401
446
  type ThreeNamespace = typeof THREE_NS;
402
447
  /** One entry of the catalogue: something the host can build. */
403
448
  interface TestRoomItem {
@@ -478,6 +523,7 @@ interface SceneryObject {
478
523
  top?: {
479
524
  part: string;
480
525
  y: number;
526
+ parts?: readonly string[];
481
527
  };
482
528
  /**
483
529
  * Where a water tap of the piece pours (a sink, a tub), in the spec's space: the room's tap (its stream and its HUD button) pours there, and the piece's own model is the tap. The first piece with a `spout` wins over the table tap.
@@ -538,8 +584,10 @@ interface TestRoomOptions {
538
584
  world?: DestructionWorld;
539
585
  worldOptions?: DestructionOptions;
540
586
  quality?: Quality;
541
- /** Replaces the arena floor with a deformable patch (60 m, 33 cm cells) that the craters of big explosions dig into. Default false. */
542
- craters?: boolean;
587
+ /** What the floor is made of until an area lays its own (`TestableModule.ground`). Default a concrete slab. */
588
+ ground?: GroundLayout;
589
+ /** Limits of the floor's marks, thrown pieces and redraws per frame. */
590
+ terrainBudget?: Partial<TerrainBudget>;
543
591
  /** Heaviest object the player can carry (default 60 kg). */
544
592
  maxCarryKg?: number;
545
593
  /** Arena half size in metres (default 14). */
@@ -557,7 +605,7 @@ interface TestRoomOptions {
557
605
  * supplies the real model; where it answers nothing the room draws a plain primitive (a sphere, a cylinder, a box).
558
606
  */
559
607
  projectileModel?: (query: ProjectileQuery) => ProjectileModel | null | undefined;
560
- /** Keys (`KeyboardEvent.code`) that crouch while held; default `['KeyC']`. Ctrl is left out on purpose: Ctrl+W closes the tab. */
608
+ /** Keys (`KeyboardEvent.code`) that crouch (a tap stays crouched until the next tap, a longer press crouches while held); default `['KeyC']`. Ctrl is left out on purpose: Ctrl+W closes the tab. */
561
609
  crouch?: {
562
610
  keys?: string[];
563
611
  };
@@ -586,12 +634,27 @@ interface TestRoomOptions {
586
634
  * the avatar's `strike` answers; a tool that is not here, or an avatar that cannot play the clip, strikes at the click. Default `{ fists: 'punch_right', shove: 'shove_two_hands', kick: 'front_kick' }`.
587
635
  */
588
636
  strikeClips?: Readonly<Record<string, string>>;
637
+ /** The numbers of pushing furniture (`FurniturePushRules` of game-player): the player's force, the friction by material, the hold time, the speed. */
638
+ push?: Partial<FurniturePushRules>;
589
639
  /** Which leg the kick uses: `alternate` (default) swaps at each kick starting with the right, or always `left` or `right`. */
590
640
  kickLeg?: KickLeg;
641
+ /** Metres above the feet the kick looks for a target from (level, the way the player faces; the nearest hit wins), so it finds what stands low in front; default every 10 cm from 0.25 to 0.85 m (shin to hip). */
642
+ kickHeights?: readonly number[];
643
+ /** Keys (`KeyboardEvent.code`) that kick with the arsenal's `kick` tool, whatever tool is in hand; default `['KeyK']` (Shift+V kicks too). */
644
+ kick?: {
645
+ keys?: string[];
646
+ };
591
647
  /** Which hand throws the one-handed strikes (`punchTools`): `alternate` (default) swaps at each punch starting with the right, or always `left` or `right`. */
592
648
  punchHand?: PunchHand;
593
649
  /** The tools whose strike is thrown with one hand, so `punchHand` picks it (default `['hand', 'fists']`); the shove and the kick are not. */
594
650
  punchTools?: readonly string[];
651
+ /**
652
+ * A strike combo the player has (an ability, such as `PUNCH_COMBO` of game-characters-people): while it is on (N, the combo button or `setStrikeCombo`), each blow of its
653
+ * `tools` plays its next step (`{ clip, hand }`) and a pause longer than `reset` seconds starts it over; while it is off the tools strike as usual. Default none.
654
+ */
655
+ strikeCombo?: StrikeComboOption;
656
+ /** Start with the combo on. Default false. */
657
+ strikeComboOn?: boolean;
595
658
  /** The first-person stance of the arms by tool id, for the tools that hold nothing the module places (default `{ fists: 'fists' }`; any other tool holds, or is `relaxed` with nothing to hold). */
596
659
  handStances?: Readonly<Record<string, string>>;
597
660
  /** The over-the-shoulder view of the `third` mode: distance, shoulder, height, wall probes. Defaults are the team game's. */
@@ -681,8 +744,20 @@ interface TestRoom {
681
744
  useItem(useId: string): boolean;
682
745
  /** Use the current tool once from the camera (tests and scripted demos), as the click does: bare hands punch (or let go of what they hold). `hold` seconds for continuous tools. */
683
746
  use(hold?: number): void;
747
+ /** Where the player's feet are. */
748
+ readonly playerPosition: Vec3$1;
684
749
  /** Grab what the camera looks at (or drop what is held). */
685
750
  grab(): boolean;
751
+ /**
752
+ * Pushing furniture, as holding E against it does: `begin` leans on what the camera looks at (false: nothing within reach to lean on), `setWalk` walks by script (world `[x, z]`, unit; null: the keys), `end`
753
+ * lets go, and `state` says the phase (`grip` while the hold timer fills, `push`, `strain`), the timer and what stops it (`too_heavy`, `obstacle`).
754
+ */
755
+ readonly push: {
756
+ begin(): boolean;
757
+ end(): boolean;
758
+ setWalk(walk: readonly [number, number] | null): void;
759
+ readonly state: RoomPushState | null;
760
+ };
686
761
  /** Throw what is held at `speed` m/s (default 9). */
687
762
  throwHeld(speed?: number): boolean;
688
763
  /** Point the camera (scripted demos and tests). */
@@ -718,12 +793,20 @@ interface TestRoom {
718
793
  setSlowMotion(on: boolean): void;
719
794
  /** Run the simulation 10x per frame (fire, melting and drying take a tenth of the time). */
720
795
  setFastForward(on: boolean): void;
796
+ /** Turn the strike combo (`strikeCombo`) on or off (N and the combo button do the same); nothing without a combo. */
797
+ setStrikeCombo(on: boolean): void;
798
+ /** Whether the strike combo is on; null when the room has none. */
799
+ readonly strikeCombo: boolean | null;
800
+ /** Kick with the arsenal's `kick` tool, whatever tool is in hand (K, Shift+V and the kick button do the same); false when there is none or it is not ready yet. */
801
+ kick(): boolean;
721
802
  /** Equip a weapon by id, adding it to the hotbar when it is not there. Same as selecting a catalogue item with `TestRoomItem.weapon`. */
722
803
  equip(weaponId: string): void;
723
804
  /**
724
805
  * Set the room up to test one catalogue group or loaded module (by its group or id): clear it and open the catalogue on that tab. A module
725
806
  * that brings an `area` is laid out by it; any other group is laid in rows (small items in front, fixed big ones behind). Unknown: nothing.
726
807
  */
808
+ /** The floor: what it is made of, how deep it was dug, its marks and thrown pieces. */
809
+ readonly terrain: Terrain;
727
810
  openArea(group: string): SpawnedObject[];
728
811
  /** The loaded modules (`loadModule`): id, name, catalogue group and whether it lays an area of its own. */
729
812
  modules(): Array<{
@@ -974,4 +1057,4 @@ declare function encodeBenchState(controls: readonly BenchControl[], layers: rea
974
1057
  /** Reads an encoded state as hostile input: unknown ids, NaN, options not offered and malformed pairs are dropped; numbers are clamped. */
975
1058
  declare function decodeBenchState(encoded: string, controls: readonly ModuleControl[], layers: readonly ModuleLayer[]): Map<string, ControlValue>;
976
1059
 
977
- export { type AnimationInfo, type BenchControl, type BenchHandle, type BenchLayer, type BuildFromSpecOptions, type CameraMode, DEFAULT_HAND_STANCES, DEFAULT_STRIKE_CLIPS, DEFAULT_TRIALS, type HudState, type InspectInfo, type KickLeg, LOADING_CLASS, LOADING_CSS, type LoadingOptions, type LoadingState, type LogEntry, MATERIAL_COLORS, type ModuleReport, type NewEntry, type NewLogEntry, type ProjectileModel, type ProjectileQuery, type PunchHand, type RoomAvatar, type RoomAvatarState, type RoomFirstPersonState, type RoomLoading, type RoomMetricId, type RoomStrings, type ScenarioContext, type ScenarioReport, type SceneryObject, type SceneryTap, type SpawnedObject, TRIAL_KINDS, type TestRoom, type TestRoomItem, type TestRoomLocale, type TestRoomLog, type TestRoomOptions, type TestRoomScenario, type TestTable, type TestTableContext, type TestTableObject, type TestTableOptions, type TestTablePage, type ThirdPersonOptions, type ThreeNamespace, type TrialKind, type TrialTool, type WritableLog, brickWallSpec, buildFromSpec, createLoading, createLog, createTestRoom, createTestTable, decodeBenchState, encodeBenchState, fitRow, loadingMarkup, locales, nextPaint, resolveStrings, rowCentres, trialView };
1060
+ export { type AnimationInfo, type BenchControl, type BenchHandle, type BenchLayer, type BuildFromSpecOptions, type CameraMode, DEFAULT_HAND_STANCES, DEFAULT_STRIKE_CLIPS, DEFAULT_TRIALS, type HudState, type InspectInfo, type KickLeg, LOADING_CLASS, LOADING_CSS, type LoadingOptions, type LoadingState, type LogEntry, MATERIAL_COLORS, type ModuleReport, type NewEntry, type NewLogEntry, type ProjectileModel, type ProjectileQuery, type PunchHand, type RoomAvatar, type RoomAvatarState, type RoomFirstPersonState, type RoomLoading, type RoomMetricId, type RoomPushEvent, type RoomPushState, type RoomStrings, type ScenarioContext, type ScenarioReport, type SceneryObject, type SceneryTap, type SpawnedObject, type StrikeComboOption, TRIAL_KINDS, type TestRoom, type TestRoomItem, type TestRoomLocale, type TestRoomLog, type TestRoomOptions, type TestRoomScenario, type TestTable, type TestTableContext, type TestTableObject, type TestTableOptions, type TestTablePage, type ThirdPersonOptions, type ThreeNamespace, type TrialKind, type TrialTool, type WritableLog, brickWallSpec, buildFromSpec, createLoading, createLog, createTestRoom, createTestTable, decodeBenchState, encodeBenchState, fitRow, loadingMarkup, locales, nextPaint, resolveStrings, rowCentres, trialView };