@adatechnology/game-test-room 0.2.0 → 0.4.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 +29 -0
- package/dist/index.d.ts +50 -2
- package/dist/index.global.js +7124 -635
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +1189 -193
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/README.md
CHANGED
|
@@ -46,6 +46,7 @@ const room = createTestRoom({
|
|
|
46
46
|
{ id: 'my_crate_breaks', name: 'My crate breaks', run: ({ room, step, expect }) => {
|
|
47
47
|
const o = room.spawn('my_crate', [0, 0, -4])
|
|
48
48
|
room.setTool('pistol_9mm')
|
|
49
|
+
room.waitUntilReady() // setTool plays the draw: the tool answers `drawing` until the rule's draw time is over
|
|
49
50
|
room.look([0, 1, -2], [0, 0.5, -4])
|
|
50
51
|
room.use()
|
|
51
52
|
step(2)
|
|
@@ -68,6 +69,31 @@ const report = room.loadModule(RENTAL_CARS) // { module, items, scenarios, ok, p
|
|
|
68
69
|
|
|
69
70
|
The items join the catalogue under the module's group and its scenarios become buttons. The contract checks run at once and write one line per result to the log: `checkDestructible` for every item with an `impl`, `checkAnimation` for every clip (`✓ Rental car: destructible contract ok (4 parts...)` or `✗ ...: <problem>`). The animations panel lists the clips of the aimed or held item; `room.playAnimation(clipId)` plays one (poses are offsets from the rest pose, events go to the log and `sound` events to `onSound`). The example (`examples/test-room`) loads a `DEMO_MODULE` rental car that owns its damage rule, its door `open` clip and a wheel that comes off with a wrench.
|
|
70
71
|
|
|
72
|
+
## Module bench
|
|
73
|
+
|
|
74
|
+
A module that declares `controls`, `layers` or `metrics` (see `@adatechnology/game-contracts`) gets a bench: `loadModule` checks it with
|
|
75
|
+
`checkBench` (a `✓ ... bench ok` / `✗ ... bench broken` line), lends `host.bench` to `attach`, then sends one change per control and layer. The
|
|
76
|
+
panel between the animations and the tests shows the active module (the one of the last `openArea`; else the first loaded with a bench, a later module never takes the panel from the one on screen): a
|
|
77
|
+
range per number, a checkbox per boolean, buttons (up to four options) or a select per choice, a button per action, the layers, and the
|
|
78
|
+
module counters at 4 Hz with a bar when they have a budget (`warn` from 80 %, `bad` past it). A compact line on top shows the room counters
|
|
79
|
+
(`fps`, `frame_ms`, `draw_calls`, `triangles`, `objects`, `fragments`, `particles`, `decals`); `fps` uses the real frame time, so slow motion
|
|
80
|
+
does not change it.
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
const room = createTestRoom({ ..., metrics: ['fps', 'draw_calls'], bench: { state: { lab: params.get('bench.lab') ?? '' }, onChange: (moduleId, encoded) => keepInUrl(moduleId, encoded) } })
|
|
84
|
+
room.bench()?.set('size', 2) // clamped and snapped; false for an unknown id, a wrong type or an option not offered
|
|
85
|
+
room.metrics() // { room: { fps, draw_calls, ... }, module: { particles: 120 } }
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
The state is `id=value;id=value` (`encodeBenchState` / `decodeBenchState`); decoding treats it as hostile input. `bench.state` is keyed by
|
|
89
|
+
module id, so two modules can share a control id; a module loaded again keeps its live values. The room never touches
|
|
90
|
+
`location`: the example page keeps it in `?bench.<moduleId>=`. `fps` and `frame_ms` use the real frame time, even past the 50 ms the simulation is clamped to. Loading a module again detaches the previous `attach` first. Q now reports the new fire
|
|
91
|
+
mode (`mode` event) and `shot` events carry `rounds` and `mode`.
|
|
92
|
+
|
|
93
|
+
Grenades are cooked: hold the button to pull the pin and let go to throw with the fuse that is left (`cook` host events: `start`, `throw`, `burst` when it goes off in the hand; `room.use(hold)` does the same
|
|
94
|
+
by script). A charge set down gets a marker (`charge_marker`, a stake with a flag) and, when it waits for a trip wire (the Claymore), the wire is drawn (`charge_wire`) and the charge goes off by itself
|
|
95
|
+
when a body (the player, an object of the room) crosses it; whatever was already on the wire when it was set is let through until it steps off. Y only fires the charges for the remote detonator (C4).
|
|
96
|
+
|
|
71
97
|
## Controls
|
|
72
98
|
|
|
73
99
|
| Input | Action |
|
|
@@ -110,6 +136,9 @@ Uma sala de testes em primeira pessoa para three.js. Você passa o seu catálogo
|
|
|
110
136
|
derrama líquidos, e lê o que aconteceu em linguagem simples no registro de comportamento (também exportável em JSON, NDJSON ou texto). Os cenários
|
|
111
137
|
automáticos conferem o resultado e gravam aprovado/reprovado no mesmo registro. Use `locale: 'pt-BR'` para textos em português.
|
|
112
138
|
|
|
139
|
+
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
|
|
140
|
+
`room.bench()` / `room.metrics()` dirigem e leem a bancada por script.
|
|
141
|
+
|
|
113
142
|
## Known limitations
|
|
114
143
|
|
|
115
144
|
The quality select does not recreate the destruction renderer; it only changes the room's own settings. Unit tests use an injected stub renderer (no WebGL).
|
package/dist/index.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { SpecFromObjectOptions, DestructionRenderer, Quality } from '@adatechnol
|
|
|
5
5
|
import { Arsenal, WeaponState, WeaponDef } from '@adatechnology/game-weapons';
|
|
6
6
|
import { FluidRenderer } from '@adatechnology/game-fluids-three';
|
|
7
7
|
import { ContainerSpec, FluidWorld, LiquidDef, ItemUseDef, SurfaceSpec } from '@adatechnology/game-fluids';
|
|
8
|
-
import { DestructibleImpl, AnimationClipSpec, TestableModule } from '@adatechnology/game-contracts';
|
|
8
|
+
import { ModuleControl, ControlValue, ModuleLayer, DestructibleImpl, AnimationClipSpec, TestableModule } from '@adatechnology/game-contracts';
|
|
9
9
|
export { AnimationClipSpec, DestructibleImpl, ItemAnimationContract, ModuleScenario, TestableItem, TestableModule } from '@adatechnology/game-contracts';
|
|
10
10
|
|
|
11
11
|
type NewEntry = NewLogEntry;
|
|
@@ -186,6 +186,40 @@ interface TestRoomOptions {
|
|
|
186
186
|
onSound?: (key: string, point: Vec3, volume: number) => void;
|
|
187
187
|
/** Show the built-in HUD and panels (default true). */
|
|
188
188
|
ui?: boolean;
|
|
189
|
+
/** Room counters the bench panel shows and `TestRoom.metrics()` returns (default all); `false` for none. */
|
|
190
|
+
metrics?: readonly RoomMetricId[] | false;
|
|
191
|
+
/**
|
|
192
|
+
* Module bench state. `state` maps a module id to its encoded state, restored when that module is first loaded (a module reloaded
|
|
193
|
+
* keeps its live state instead) before its first changes are sent; ids are per module, so two modules may share a control id.
|
|
194
|
+
* `onChange` gets the encoded state of a module whenever it changes (a page keeps it in its URL). The room never touches `location`.
|
|
195
|
+
*/
|
|
196
|
+
bench?: {
|
|
197
|
+
readonly state?: Readonly<Record<string, string>>;
|
|
198
|
+
readonly onChange?: (moduleId: string, encoded: string) => void;
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
/** Counters the room measures itself. `fps` and `frame_ms` use the real frame time, so slow motion does not change them. */
|
|
202
|
+
type RoomMetricId = 'fps' | 'frame_ms' | 'draw_calls' | 'triangles' | 'objects' | 'fragments' | 'particles' | 'decals';
|
|
203
|
+
/** A module control as the room holds it: the declaration with the module's patches applied, and its value (none for an action). */
|
|
204
|
+
type BenchControl = ModuleControl & {
|
|
205
|
+
readonly disabled: boolean;
|
|
206
|
+
readonly value?: ControlValue;
|
|
207
|
+
};
|
|
208
|
+
type BenchLayer = ModuleLayer & {
|
|
209
|
+
readonly on: boolean;
|
|
210
|
+
};
|
|
211
|
+
/** The bench of a loaded module, driven from outside the panel (tests, scripts, a page that keeps the state in its URL). */
|
|
212
|
+
interface BenchHandle {
|
|
213
|
+
controls(): BenchControl[];
|
|
214
|
+
layers(): BenchLayer[];
|
|
215
|
+
value(id: string): ControlValue | undefined;
|
|
216
|
+
layer(id: string): boolean;
|
|
217
|
+
/** Numbers are clamped and snapped to the step. False for an unknown id, a wrong type, an option not offered or a disabled control. */
|
|
218
|
+
set(id: string, value: ControlValue): boolean;
|
|
219
|
+
setLayer(id: string, on: boolean): boolean;
|
|
220
|
+
act(id: string): boolean;
|
|
221
|
+
/** `id=value;id=value`, the format `TestRoomOptions.bench.state[moduleId]` reads back. */
|
|
222
|
+
encode(): string;
|
|
189
223
|
}
|
|
190
224
|
interface SpawnedObject {
|
|
191
225
|
objectId: string;
|
|
@@ -230,6 +264,8 @@ interface TestRoom {
|
|
|
230
264
|
spawned(): SpawnedObject[];
|
|
231
265
|
/** Advance one frame (only needed when the host owns the loop). */
|
|
232
266
|
update(dt: number): void;
|
|
267
|
+
/** Advance frames until the tool in hand has no phase left (the draw `setTool` plays, a reload, a cycle) or `maxSeconds` pass; plus one frame so the next pull is not inside the phase; returns the seconds waited. */
|
|
268
|
+
waitUntilReady(maxSeconds?: number): number;
|
|
233
269
|
on(type: 'spawn' | 'tool' | 'grab' | 'throw' | 'event', fn: (data: unknown) => void): () => void;
|
|
234
270
|
/** Tilt the held item by `rad` radians around its own x axis (hold Z in the room). Pouring and spilling follow from the tilt. */
|
|
235
271
|
tilt(rad: number): void;
|
|
@@ -273,6 +309,13 @@ interface TestRoom {
|
|
|
273
309
|
* offsets from the rest pose, interpolated linearly; the item keeps the last pose. Events are logged (and `sound` events reach `onSound`).
|
|
274
310
|
*/
|
|
275
311
|
playAnimation(clipId: string, objectId?: string): boolean;
|
|
312
|
+
/** The bench of a loaded module (default the active one: the module of the last `openArea`, else the last loaded with a bench), or null. */
|
|
313
|
+
bench(moduleId?: string): BenchHandle | null;
|
|
314
|
+
/** Room counters and the active module's metrics, read now. */
|
|
315
|
+
metrics(): {
|
|
316
|
+
room: Partial<Record<RoomMetricId, number>>;
|
|
317
|
+
module: Record<string, number | string>;
|
|
318
|
+
};
|
|
276
319
|
dispose(): void;
|
|
277
320
|
}
|
|
278
321
|
/** What `TestRoom.loadModule` did. */
|
|
@@ -437,4 +480,9 @@ interface BuildFromSpecOptions {
|
|
|
437
480
|
*/
|
|
438
481
|
declare function buildFromSpec(THREE: typeof THREE_NS, spec: DestructibleSpec, options?: BuildFromSpecOptions): THREE_NS.Group;
|
|
439
482
|
|
|
440
|
-
|
|
483
|
+
/** `id=value;id=value`, values with `encodeURIComponent`, booleans and layers as `1` / `0`. */
|
|
484
|
+
declare function encodeBenchState(controls: readonly BenchControl[], layers: readonly BenchLayer[]): string;
|
|
485
|
+
/** Reads an encoded state as hostile input: unknown ids, NaN, options not offered and malformed pairs are dropped; numbers are clamped. */
|
|
486
|
+
declare function decodeBenchState(encoded: string, controls: readonly ModuleControl[], layers: readonly ModuleLayer[]): Map<string, ControlValue>;
|
|
487
|
+
|
|
488
|
+
export { type AnimationInfo, type BenchControl, type BenchHandle, type BenchLayer, type BuildFromSpecOptions, DEMO_ITEMS, DEMO_MATERIALS, type HudState, type InspectInfo, type LogEntry, MATERIAL_COLORS, type ModuleReport, type NewEntry, type NewLogEntry, type RoomMetricId, type RoomStrings, SCENARIOS, type ScenarioContext, type ScenarioReport, type SpawnedObject, type TestRoom, type TestRoomItem, type TestRoomLocale, type TestRoomLog, type TestRoomOptions, type TestRoomScenario, type ThreeNamespace, type WritableLog, brickWallSpec, buildFromSpec, createLog, createTestRoom, decodeBenchState, demoItems, encodeBenchState, locales, makeScenarios, resolveStrings, scenariosFor };
|