@adatechnology/game-test-room 0.6.1 → 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 +10 -1
- package/dist/index.d.ts +20 -1
- package/dist/index.global.js +12793 -1778
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +178 -17
- package/dist/index.js.map +1 -1
- package/package.json +10 -7
package/dist/index.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
// src/room.ts
|
|
2
2
|
import { createDestruction, JOINT_DEFAULTS as JOINT_DEFAULTS2, resolveStrings as resolveDestructionStrings } from "@adatechnology/game-destruction";
|
|
3
|
-
import {
|
|
3
|
+
import { createAudioEngine, createAudioSink } from "@adatechnology/game-audio";
|
|
4
|
+
import { createDestructionRenderer, createDestructionSound, specFromObject3D } from "@adatechnology/game-destruction-three";
|
|
4
5
|
import { createArsenal, DEFAULT_LOADOUT } from "@adatechnology/game-weapons";
|
|
5
|
-
import { createFluids, createItemUse, ITEM_USES, resolveStrings as resolveFluidStrings } from "@adatechnology/game-fluids";
|
|
6
|
-
import { createFluidRenderer } from "@adatechnology/game-fluids-three";
|
|
7
|
-
import {
|
|
6
|
+
import { createFilmLcg, createFluids, createItemUse, ITEM_USES, resolveStrings as resolveFluidStrings } from "@adatechnology/game-fluids";
|
|
7
|
+
import { createFilmBridge, createFilmManager, createFluidRenderer } from "@adatechnology/game-fluids-three";
|
|
8
|
+
import { createGroundField, createVfx } from "@adatechnology/game-vfx";
|
|
9
|
+
import { createGroundPatch, createVfxRenderer } from "@adatechnology/game-vfx-three";
|
|
10
|
+
import { checkAnimation, checkBench, checkDestructible, checkSpec, createEnvironmentBus } from "@adatechnology/game-contracts";
|
|
8
11
|
|
|
9
12
|
// src/items.ts
|
|
10
13
|
import { buildFrame } from "@adatechnology/game-destruction";
|
|
@@ -1255,6 +1258,7 @@ function createBenchStore(module, locale, onListenerError) {
|
|
|
1255
1258
|
const changed = () => {
|
|
1256
1259
|
for (const watcher of watchers) watcher();
|
|
1257
1260
|
};
|
|
1261
|
+
let sheet = null;
|
|
1258
1262
|
const controlOf = (id) => controls.find((control) => control.id === id);
|
|
1259
1263
|
const store = {
|
|
1260
1264
|
moduleId: module.id,
|
|
@@ -1296,6 +1300,11 @@ function createBenchStore(module, locale, onListenerError) {
|
|
|
1296
1300
|
watchers.add(listener);
|
|
1297
1301
|
return () => watchers.delete(listener);
|
|
1298
1302
|
},
|
|
1303
|
+
sheet: () => sheet,
|
|
1304
|
+
setSheet(next) {
|
|
1305
|
+
sheet = next;
|
|
1306
|
+
changed();
|
|
1307
|
+
},
|
|
1299
1308
|
patch(id, patch) {
|
|
1300
1309
|
const index = controls.findIndex((control2) => control2.id === id);
|
|
1301
1310
|
if (index < 0) return;
|
|
@@ -1517,7 +1526,10 @@ function createExplosives(context) {
|
|
|
1517
1526
|
const weaponName = context.toolName(projectile.weapon);
|
|
1518
1527
|
const reach = Math.max(detonation.injuryRadius, detonation.lethalRadius);
|
|
1519
1528
|
const reached = detonation.energy <= 0 ? 0 : detonation.cone ? coneBlast(detonation) : world.explode(detonation.point, detonation.energy, reach, "player").length;
|
|
1520
|
-
if (detonation.energy > 0)
|
|
1529
|
+
if (detonation.energy > 0) {
|
|
1530
|
+
const isPlayed = context.effects.explosion(detonation.explosive, detonation.point, { direction: detonation.direction }) !== null;
|
|
1531
|
+
renderer.explode(detonation.point, detonation.energy, reach, { visual: !isPlayed });
|
|
1532
|
+
}
|
|
1521
1533
|
if (detonation.stunSeconds !== void 0) renderer.emit("flash", detonation.point, [0, 1, 0], 1, 1);
|
|
1522
1534
|
const onPlayer = blastEffect(detonation, toArray(camera.position));
|
|
1523
1535
|
action("explode", translate("act.explode", { weapon: weaponName, energy: format(detonation.energy, 0), radius: format(reach, 1), n: reached }), {
|
|
@@ -1526,7 +1538,7 @@ function createExplosives(context) {
|
|
|
1526
1538
|
if (onPlayer.zone !== "none") {
|
|
1527
1539
|
action("blast_player", translate("act.blast_player", { weapon: weaponName, zone: translate(`blast.zone.${onPlayer.zone}`), distance: format(onPlayer.distance, 1), stun: format(onPlayer.stunSeconds, 1) }), { data: { ...onPlayer } });
|
|
1528
1540
|
}
|
|
1529
|
-
context.emitHost({ type: "explosion", source: projectile.weapon, point: detonation.point, energy: detonation.energy, radius: reach, cause: detonation.cause });
|
|
1541
|
+
context.emitHost({ type: "explosion", source: projectile.weapon, point: detonation.point, energy: detonation.energy, radius: reach, cause: detonation.cause, explosive: detonation.explosive });
|
|
1530
1542
|
if (detonation.fire) context.startFire(projectile.weapon, detonation.point, detonation.fire, detonation.cause === "impact");
|
|
1531
1543
|
};
|
|
1532
1544
|
const detonateBare = (projectile, isImpact) => {
|
|
@@ -1853,9 +1865,10 @@ function createRoomMetrics(sources) {
|
|
|
1853
1865
|
if (!["objects", "fragments", "particles", "decals"].some((id) => wanted.has(id))) return out;
|
|
1854
1866
|
const destruction = sources.destruction.stats();
|
|
1855
1867
|
const fluids = sources.fluids.stats();
|
|
1868
|
+
const effects = sources.effects.stats();
|
|
1856
1869
|
put("objects", destruction.pieces);
|
|
1857
1870
|
put("fragments", destruction.fragments);
|
|
1858
|
-
put("particles", destruction.particles + fluids.particles);
|
|
1871
|
+
put("particles", destruction.particles + fluids.particles + effects.puffs + effects.sparks + effects.glows + effects.chunks);
|
|
1859
1872
|
put("decals", destruction.decals + fluids.stains);
|
|
1860
1873
|
return out;
|
|
1861
1874
|
}
|
|
@@ -2955,7 +2968,7 @@ var mk = (tag, cls, text) => {
|
|
|
2955
2968
|
if (text !== void 0) e.textContent = text;
|
|
2956
2969
|
return e;
|
|
2957
2970
|
};
|
|
2958
|
-
var shapeOf = (view) => JSON.stringify([view.title, view.controls.map((c) => ({ ...c, value: void 0 })), view.layers.map((l) => [l.id, l.name])]);
|
|
2971
|
+
var shapeOf = (view) => JSON.stringify([view.title, view.controls.map((c) => ({ ...c, value: void 0 })), view.layers.map((l) => [l.id, l.name]), view.sheet ?? null]);
|
|
2959
2972
|
function createBenchPanel(o) {
|
|
2960
2973
|
const { t } = o;
|
|
2961
2974
|
const element = mk("div", "tr-panel tr-bench");
|
|
@@ -3010,7 +3023,8 @@ function createBenchPanel(o) {
|
|
|
3010
3023
|
const choiceControl = (control) => {
|
|
3011
3024
|
const wrap = mk("div", "tr-ctl");
|
|
3012
3025
|
wrap.appendChild(mk("span", void 0, control.name));
|
|
3013
|
-
|
|
3026
|
+
const grouped = control.options.some((option) => option.group !== void 0);
|
|
3027
|
+
if (control.options.length <= SEGMENTED_MAX && !grouped) {
|
|
3014
3028
|
const group = mk("div", "tr-seg");
|
|
3015
3029
|
group.setAttribute("role", "group");
|
|
3016
3030
|
const buttons = control.options.map((option) => {
|
|
@@ -3033,10 +3047,22 @@ function createBenchPanel(o) {
|
|
|
3033
3047
|
}
|
|
3034
3048
|
const select = mk("select");
|
|
3035
3049
|
select.disabled = control.disabled;
|
|
3050
|
+
let heading;
|
|
3051
|
+
let section = select;
|
|
3036
3052
|
for (const option of control.options) {
|
|
3053
|
+
if (option.group !== heading) {
|
|
3054
|
+
heading = option.group;
|
|
3055
|
+
section = select;
|
|
3056
|
+
if (heading !== void 0) {
|
|
3057
|
+
const optgroup = mk("optgroup");
|
|
3058
|
+
optgroup.label = heading;
|
|
3059
|
+
select.appendChild(optgroup);
|
|
3060
|
+
section = optgroup;
|
|
3061
|
+
}
|
|
3062
|
+
}
|
|
3037
3063
|
const op = mk("option", void 0, option.name);
|
|
3038
3064
|
op.value = option.id;
|
|
3039
|
-
|
|
3065
|
+
section.appendChild(op);
|
|
3040
3066
|
}
|
|
3041
3067
|
select.onchange = () => o.onControl(control.id, select.value);
|
|
3042
3068
|
wrap.appendChild(select);
|
|
@@ -3074,7 +3100,63 @@ function createBenchPanel(o) {
|
|
|
3074
3100
|
}
|
|
3075
3101
|
return wrap;
|
|
3076
3102
|
};
|
|
3103
|
+
let playhead = null;
|
|
3104
|
+
const sheetCard = (sheet) => {
|
|
3105
|
+
const card = mk("div", "tr-sheet");
|
|
3106
|
+
if (sheet.kicker) card.appendChild(mk("div", "sub", sheet.kicker));
|
|
3107
|
+
card.appendChild(mk("h4", void 0, sheet.title));
|
|
3108
|
+
if (sheet.body) card.appendChild(mk("p", void 0, sheet.body));
|
|
3109
|
+
if (sheet.facts?.length) {
|
|
3110
|
+
const facts = mk("dl", "tr-facts");
|
|
3111
|
+
for (const fact of sheet.facts) facts.append(mk("dt", void 0, fact.label), mk("dd", void 0, fact.value));
|
|
3112
|
+
card.appendChild(facts);
|
|
3113
|
+
}
|
|
3114
|
+
if (sheet.steps?.length) {
|
|
3115
|
+
const steps = mk("ol", "tr-steps");
|
|
3116
|
+
for (const step of sheet.steps) {
|
|
3117
|
+
const li = mk("li");
|
|
3118
|
+
li.append(mk("b", void 0, step.at), document.createTextNode(` ${step.text}`));
|
|
3119
|
+
steps.appendChild(li);
|
|
3120
|
+
}
|
|
3121
|
+
card.appendChild(steps);
|
|
3122
|
+
}
|
|
3123
|
+
if (sheet.swatches?.length) {
|
|
3124
|
+
const row = mk("div", "tr-swatches");
|
|
3125
|
+
for (const swatch of sheet.swatches) {
|
|
3126
|
+
const chip = mk("span", "tr-swatch");
|
|
3127
|
+
const dot = mk("i");
|
|
3128
|
+
dot.style.background = swatch.color;
|
|
3129
|
+
chip.append(dot, document.createTextNode(swatch.name));
|
|
3130
|
+
row.appendChild(chip);
|
|
3131
|
+
}
|
|
3132
|
+
card.appendChild(row);
|
|
3133
|
+
}
|
|
3134
|
+
if (sheet.timeline) {
|
|
3135
|
+
const { segments, ticks, metric } = sheet.timeline;
|
|
3136
|
+
const end = Math.max(1e-3, ...segments.map((segment) => segment.end), ...ticks);
|
|
3137
|
+
const strip = mk("div", "tr-timeline");
|
|
3138
|
+
for (const segment of segments) {
|
|
3139
|
+
const bar = mk("div", "tr-segment", segment.name);
|
|
3140
|
+
bar.style.left = `${segment.start / end * 100}%`;
|
|
3141
|
+
bar.style.width = `${(segment.end - segment.start) / end * 100}%`;
|
|
3142
|
+
bar.style.background = segment.color;
|
|
3143
|
+
strip.appendChild(bar);
|
|
3144
|
+
}
|
|
3145
|
+
for (const tick of ticks) {
|
|
3146
|
+
const mark = mk("u", "tr-tick");
|
|
3147
|
+
mark.style.left = `${tick / end * 100}%`;
|
|
3148
|
+
strip.appendChild(mark);
|
|
3149
|
+
}
|
|
3150
|
+
const head = mk("s", "tr-playhead");
|
|
3151
|
+
strip.appendChild(head);
|
|
3152
|
+
playhead = { node: head, metric, end };
|
|
3153
|
+
card.appendChild(strip);
|
|
3154
|
+
}
|
|
3155
|
+
if (sheet.note) card.appendChild(mk("p", "sub", sheet.note));
|
|
3156
|
+
return card;
|
|
3157
|
+
};
|
|
3077
3158
|
const rebuild = (view) => {
|
|
3159
|
+
playhead = null;
|
|
3078
3160
|
body.textContent = "";
|
|
3079
3161
|
refreshers.length = 0;
|
|
3080
3162
|
body.appendChild(mk("div", "sub", view.title));
|
|
@@ -3082,6 +3164,7 @@ function createBenchPanel(o) {
|
|
|
3082
3164
|
for (const control of view.controls) if (!control.hidden) list.appendChild(controlElement(control));
|
|
3083
3165
|
body.appendChild(list);
|
|
3084
3166
|
if (view.layers.length) body.appendChild(layerList(view.layers));
|
|
3167
|
+
if (view.sheet) body.appendChild(sheetCard(view.sheet));
|
|
3085
3168
|
};
|
|
3086
3169
|
const renderMetrics = (rows) => {
|
|
3087
3170
|
metricList.textContent = "";
|
|
@@ -3112,6 +3195,7 @@ function createBenchPanel(o) {
|
|
|
3112
3195
|
hasBench = view !== null;
|
|
3113
3196
|
syncDisplay();
|
|
3114
3197
|
if (!view) {
|
|
3198
|
+
playhead = null;
|
|
3115
3199
|
shape = "";
|
|
3116
3200
|
body.textContent = "";
|
|
3117
3201
|
refreshers.length = 0;
|
|
@@ -3141,6 +3225,10 @@ function createBenchPanel(o) {
|
|
|
3141
3225
|
node.text.textContent = row.text;
|
|
3142
3226
|
if (node.bar && row.ratio !== void 0) fillBar(node.bar, row.ratio);
|
|
3143
3227
|
}
|
|
3228
|
+
if (playhead) {
|
|
3229
|
+
const at = rows.module.find((row) => row.id === playhead?.metric)?.value;
|
|
3230
|
+
playhead.node.style.left = `${Math.max(0, Math.min(1, (at ?? 0) / playhead.end)) * 100}%`;
|
|
3231
|
+
}
|
|
3144
3232
|
},
|
|
3145
3233
|
get visible() {
|
|
3146
3234
|
if (element.style.display === "none") return false;
|
|
@@ -3173,6 +3261,13 @@ var CSS = `
|
|
|
3173
3261
|
.tr-status{margin-top:4px;min-height:16px;color:#ffd23f;text-shadow:0 1px 2px #000}
|
|
3174
3262
|
.tr-prog{margin:4px auto 0;width:260px;background:rgba(10,12,16,.72);border-radius:6px;padding:4px 8px;display:none}
|
|
3175
3263
|
.tr-prog b{display:block;font-weight:500;margin-bottom:3px}
|
|
3264
|
+
.tr-sheet{margin-top:8px;padding:8px;border:1px solid #2a2e36;border-radius:6px;font-size:12px;line-height:1.4}
|
|
3265
|
+
.tr-sheet h4{margin:2px 0 4px;font-size:13px}.tr-sheet p{margin:4px 0}
|
|
3266
|
+
.tr-facts{display:grid;grid-template-columns:auto 1fr;gap:2px 8px;margin:4px 0}.tr-facts dt{opacity:.7}.tr-facts dd{margin:0}
|
|
3267
|
+
.tr-steps{margin:4px 0;padding-left:16px}.tr-swatches{display:flex;flex-wrap:wrap;gap:6px}.tr-swatch i{display:inline-block;width:10px;height:10px;border-radius:50%;margin-right:4px}
|
|
3268
|
+
.tr-timeline{position:relative;height:22px;margin:8px 0;background:#1b1e24;border-radius:4px;overflow:hidden}
|
|
3269
|
+
.tr-segment{position:absolute;top:0;height:100%;font-size:10px;overflow:hidden;white-space:nowrap;opacity:.85;padding:0 3px;box-sizing:border-box;color:#000}
|
|
3270
|
+
.tr-tick{position:absolute;top:0;bottom:0;width:1px;background:#fff6;text-decoration:none}.tr-playhead{position:absolute;top:0;bottom:0;width:2px;background:#fff;text-decoration:none}
|
|
3176
3271
|
.tr-bar{height:7px;background:#2a2e36;border-radius:4px;overflow:hidden}
|
|
3177
3272
|
.tr-bar i{display:block;height:100%;background:#4fc3f7;width:0}
|
|
3178
3273
|
.tr-bar.hp i{background:#66bb6a}.tr-bar.warn i{background:#ffb300}.tr-bar.bad i{background:#ef5350}
|
|
@@ -3777,13 +3872,40 @@ function createTestRoom(options) {
|
|
|
3777
3872
|
let simTime = 0;
|
|
3778
3873
|
const log = createLog({ exportCancelled: () => t("log.export_cancelled") });
|
|
3779
3874
|
log.setClock(() => simTime);
|
|
3875
|
+
const ownAudio = options.audio === void 0;
|
|
3876
|
+
const audio = options.audio === false ? null : options.audio ?? createAudioEngine();
|
|
3877
|
+
const destructionSound = audio ? createDestructionSound(audio) : null;
|
|
3878
|
+
const playSound = (key, point, volume) => {
|
|
3879
|
+
destructionSound?.onSound(key, point, volume);
|
|
3880
|
+
options.onSound?.(key, point, volume);
|
|
3881
|
+
};
|
|
3882
|
+
const craterField = options.craters ? createGroundField() : void 0;
|
|
3883
|
+
const craterPatch = craterField ? createGroundPatch(THREE, { field: craterField, material: new THREE.MeshStandardMaterial({ color: 9342602, roughness: 0.95 }) }) : void 0;
|
|
3884
|
+
if (craterPatch) {
|
|
3885
|
+
const floor = arena.group.getObjectByName("floor");
|
|
3886
|
+
if (floor) floor.visible = false;
|
|
3887
|
+
scene.add(craterPatch.mesh);
|
|
3888
|
+
}
|
|
3889
|
+
const vfx = createVfxRenderer(THREE, {
|
|
3890
|
+
scene,
|
|
3891
|
+
camera,
|
|
3892
|
+
overlay: container,
|
|
3893
|
+
quality,
|
|
3894
|
+
pointScale,
|
|
3895
|
+
vfx: createVfx({
|
|
3896
|
+
profile: quality,
|
|
3897
|
+
groundAt: (x, z) => groundAt(x, z) + (craterField?.heightAt(x, z) ?? 0),
|
|
3898
|
+
...craterField ? { ground: craterField } : {}
|
|
3899
|
+
})
|
|
3900
|
+
});
|
|
3780
3901
|
const renderer = createDestructionRenderer(THREE, {
|
|
3781
3902
|
scene,
|
|
3782
3903
|
world,
|
|
3904
|
+
vfx: vfx.world,
|
|
3783
3905
|
quality,
|
|
3784
3906
|
groundAt,
|
|
3785
3907
|
pointScale,
|
|
3786
|
-
onSound:
|
|
3908
|
+
onSound: playSound,
|
|
3787
3909
|
onPiece: (piece) => {
|
|
3788
3910
|
rendererPieces.set(piece.objectId, piece);
|
|
3789
3911
|
loose.push(piece);
|
|
@@ -3805,7 +3927,17 @@ function createTestRoom(options) {
|
|
|
3805
3927
|
if (best) splitOf.set(piece.objectId, best);
|
|
3806
3928
|
}
|
|
3807
3929
|
});
|
|
3808
|
-
const fluidView = createFluidRenderer(THREE, { scene, fluids, quality, pointScale });
|
|
3930
|
+
const fluidView = createFluidRenderer(THREE, { scene, fluids, quality, pointScale, vfx: vfx.world });
|
|
3931
|
+
const filmRandom = createFilmLcg(1);
|
|
3932
|
+
const film = createFilmManager(THREE, {
|
|
3933
|
+
scene,
|
|
3934
|
+
camera,
|
|
3935
|
+
random: filmRandom,
|
|
3936
|
+
fluids,
|
|
3937
|
+
groundAt,
|
|
3938
|
+
...typeof gl.copyTextureToTexture === "function" ? { gpu: gl } : {}
|
|
3939
|
+
});
|
|
3940
|
+
const filmBridge = createFilmBridge({ fluids, manager: film, random: filmRandom });
|
|
3809
3941
|
const specs = /* @__PURE__ */ new Map();
|
|
3810
3942
|
const loose = [];
|
|
3811
3943
|
const arsenal = options.arsenal ?? createArsenal({ locale, weapons: options.weapons, seed: 1 });
|
|
@@ -3921,7 +4053,7 @@ function createTestRoom(options) {
|
|
|
3921
4053
|
for (const ev of batch) {
|
|
3922
4054
|
if (ev.type === "leak") puncture(ev);
|
|
3923
4055
|
else if (ev.type === "ignite") igniteNear(ev.point, 0.9);
|
|
3924
|
-
else if (ev.type === "explode") emitHost({ type: "explosion", source: ev.object, point: ev.point, energy: ev.energy, radius: ev.radius, cause: "part" });
|
|
4056
|
+
else if (ev.type === "explode") emitHost({ type: "explosion", source: ev.object, point: ev.point, energy: ev.energy, radius: ev.radius, cause: "part", ...ev.effect !== void 0 ? { explosive: ev.effect } : {} });
|
|
3925
4057
|
fire("event", ev);
|
|
3926
4058
|
options.onEvent?.(ev);
|
|
3927
4059
|
}
|
|
@@ -4109,8 +4241,10 @@ function createTestRoom(options) {
|
|
|
4109
4241
|
renderer.restore(e.so.objectId);
|
|
4110
4242
|
}
|
|
4111
4243
|
renderer.clear();
|
|
4244
|
+
vfx.world.clear();
|
|
4112
4245
|
fluids.clearStains([0, 0, 0], 1e4);
|
|
4113
4246
|
fluidView.clear();
|
|
4247
|
+
film.clear();
|
|
4114
4248
|
fluids.setTap(TAP_ID, false);
|
|
4115
4249
|
};
|
|
4116
4250
|
const clear = () => {
|
|
@@ -4501,6 +4635,7 @@ function createTestRoom(options) {
|
|
|
4501
4635
|
camera,
|
|
4502
4636
|
world,
|
|
4503
4637
|
renderer,
|
|
4638
|
+
effects: vfx.world,
|
|
4504
4639
|
arsenal,
|
|
4505
4640
|
objects: () => [...entries.values()].filter((e) => !e.arena).map((e) => e.so.root),
|
|
4506
4641
|
bodies: crossingBodies,
|
|
@@ -4934,7 +5069,7 @@ function createTestRoom(options) {
|
|
|
4934
5069
|
let metricsAcc = 0;
|
|
4935
5070
|
let ownsLoop = false;
|
|
4936
5071
|
const roomMetricIds = options.metrics === false ? [] : options.metrics ?? ROOM_METRICS;
|
|
4937
|
-
const roomMetrics = createRoomMetrics({ gl, destruction: renderer, fluids: fluidView, ids: roomMetricIds });
|
|
5072
|
+
const roomMetrics = createRoomMetrics({ gl, destruction: renderer, fluids: fluidView, effects: vfx.world, ids: roomMetricIds });
|
|
4938
5073
|
let qualityName = quality;
|
|
4939
5074
|
const syncContainers = (sdt) => {
|
|
4940
5075
|
for (const e of entries.values()) {
|
|
@@ -4983,12 +5118,20 @@ function createTestRoom(options) {
|
|
|
4983
5118
|
}
|
|
4984
5119
|
syncLiters();
|
|
4985
5120
|
renderer.update(dt);
|
|
5121
|
+
vfx.update(dt);
|
|
5122
|
+
craterPatch?.sync();
|
|
4986
5123
|
updateImpacts();
|
|
4987
5124
|
fluidView.update(dt);
|
|
5125
|
+
filmBridge.update(dt);
|
|
5126
|
+
film.update(dt);
|
|
4988
5127
|
for (const [id, s] of tools) {
|
|
4989
5128
|
s.update(dt);
|
|
4990
5129
|
flushEvents(id, s);
|
|
4991
5130
|
}
|
|
5131
|
+
if (audio) {
|
|
5132
|
+
const look = camera.getWorldDirection(listenerDirection);
|
|
5133
|
+
audio.setListener({ position: [camera.position.x, camera.position.y, camera.position.z], yaw: Math.atan2(-look.x, -look.z) });
|
|
5134
|
+
}
|
|
4992
5135
|
for (const update2 of frameListeners) update2(dt);
|
|
4993
5136
|
for (const tr of tracers) {
|
|
4994
5137
|
if (tr.life > 0) {
|
|
@@ -5098,7 +5241,7 @@ function createTestRoom(options) {
|
|
|
5098
5241
|
const ev = evs[pl.fired++];
|
|
5099
5242
|
action("animation_event", t("act.anim_event", { name: pl.entry.name, clip: pl.clip.id, type: ev.type }), { objectId: pl.entry.so.objectId, itemId: pl.entry.so.itemId, data: { clip: pl.clip.id, type: ev.type, at: ev.t, ...ev.data ?? {} } });
|
|
5100
5243
|
const key = ev.type === "sound" ? ev.data?.key : void 0;
|
|
5101
|
-
if (typeof key === "string")
|
|
5244
|
+
if (typeof key === "string") playSound(key, v3(pl.entry.so.root.getWorldPosition(tmpV)), 1);
|
|
5102
5245
|
}
|
|
5103
5246
|
if (pl.t >= pl.clip.duration) {
|
|
5104
5247
|
playing.splice(i, 1);
|
|
@@ -5145,7 +5288,7 @@ function createTestRoom(options) {
|
|
|
5145
5288
|
const benchView = () => {
|
|
5146
5289
|
const store = activeBench ? benches.get(activeBench) : void 0;
|
|
5147
5290
|
if (!store) return null;
|
|
5148
|
-
return { title: modules.get(store.moduleId)?.name ?? store.moduleId, controls: store.controls(), layers: store.layers() };
|
|
5291
|
+
return { title: modules.get(store.moduleId)?.name ?? store.moduleId, controls: store.controls(), layers: store.layers(), sheet: store.sheet() };
|
|
5149
5292
|
};
|
|
5150
5293
|
const METRIC_UNAVAILABLE = "n/a";
|
|
5151
5294
|
const metricText = (value, digits, unit) => typeof value === "number" ? `${num(value, digits)}${unit ? ` ${unit}` : ""}` : value ?? METRIC_UNAVAILABLE;
|
|
@@ -5180,7 +5323,7 @@ function createTestRoom(options) {
|
|
|
5180
5323
|
const module = declared.map((metric) => {
|
|
5181
5324
|
const value = values[metric.id];
|
|
5182
5325
|
const ratio = metric.max && typeof value === "number" ? value / metric.max : void 0;
|
|
5183
|
-
return { id: metric.id, name: metric.name, text: metricText(value, metric.digits ?? 0, metric.unit), ...ratio === void 0 ? {} : { ratio } };
|
|
5326
|
+
return { id: metric.id, name: metric.name, text: metricText(value, metric.digits ?? 0, metric.unit), ...typeof value === "number" ? { value } : {}, ...ratio === void 0 ? {} : { ratio } };
|
|
5184
5327
|
});
|
|
5185
5328
|
return { room: room2, module };
|
|
5186
5329
|
};
|
|
@@ -5191,6 +5334,9 @@ function createTestRoom(options) {
|
|
|
5191
5334
|
};
|
|
5192
5335
|
const hostListeners = /* @__PURE__ */ new Set();
|
|
5193
5336
|
const frameListeners = /* @__PURE__ */ new Set();
|
|
5337
|
+
const listenerDirection = new THREE.Vector3();
|
|
5338
|
+
const environment = options.environment ?? createEnvironmentBus();
|
|
5339
|
+
const audioSink = audio ? createAudioSink(audio) : void 0;
|
|
5194
5340
|
function emitHost(event) {
|
|
5195
5341
|
for (const listener of hostListeners) listener(event);
|
|
5196
5342
|
}
|
|
@@ -5240,7 +5386,10 @@ function createTestRoom(options) {
|
|
|
5240
5386
|
scene,
|
|
5241
5387
|
camera,
|
|
5242
5388
|
locale,
|
|
5389
|
+
...audioSink ? { audio: audioSink } : {},
|
|
5390
|
+
environment,
|
|
5243
5391
|
groundAt,
|
|
5392
|
+
effects: vfx,
|
|
5244
5393
|
tool: () => ({
|
|
5245
5394
|
weapon: toolId,
|
|
5246
5395
|
category: tool.def.category,
|
|
@@ -5294,6 +5443,7 @@ function createTestRoom(options) {
|
|
|
5294
5443
|
layer: store.layer,
|
|
5295
5444
|
onChange: store.onChange,
|
|
5296
5445
|
patch: store.patch,
|
|
5446
|
+
sheet: store.setSheet,
|
|
5297
5447
|
look: (eye, target) => room.look(eye, target)
|
|
5298
5448
|
});
|
|
5299
5449
|
const checkModuleBench = (mod, fail) => {
|
|
@@ -5491,6 +5641,8 @@ function createTestRoom(options) {
|
|
|
5491
5641
|
arsenal,
|
|
5492
5642
|
fluids,
|
|
5493
5643
|
fluidRenderer: fluidView,
|
|
5644
|
+
audio,
|
|
5645
|
+
environment,
|
|
5494
5646
|
log,
|
|
5495
5647
|
get tool() {
|
|
5496
5648
|
return tool;
|
|
@@ -5579,10 +5731,13 @@ function createTestRoom(options) {
|
|
|
5579
5731
|
const id = moduleId ?? activeBench;
|
|
5580
5732
|
return (id ? benches.get(id) : void 0) ?? null;
|
|
5581
5733
|
},
|
|
5734
|
+
applyCamera: () => vfx.applyCamera(),
|
|
5582
5735
|
metrics() {
|
|
5583
5736
|
return { room: roomMetrics.sample(), module: moduleMetrics() };
|
|
5584
5737
|
},
|
|
5585
5738
|
dispose() {
|
|
5739
|
+
destructionSound?.dispose();
|
|
5740
|
+
if (ownAudio) audio?.dispose();
|
|
5586
5741
|
for (const detach of detachers.values()) detach();
|
|
5587
5742
|
detachers.clear();
|
|
5588
5743
|
for (const store of benches.values()) store.dispose();
|
|
@@ -5592,7 +5747,11 @@ function createTestRoom(options) {
|
|
|
5592
5747
|
player.dispose();
|
|
5593
5748
|
ui?.dispose();
|
|
5594
5749
|
renderer.dispose();
|
|
5750
|
+
vfx.dispose();
|
|
5751
|
+
craterPatch?.dispose();
|
|
5595
5752
|
fluidView.dispose();
|
|
5753
|
+
filmBridge.dispose();
|
|
5754
|
+
film.dispose();
|
|
5596
5755
|
for (const tr of tracers) tr.line.removeFromParent();
|
|
5597
5756
|
explosives.clear();
|
|
5598
5757
|
if (ownRenderer) {
|
|
@@ -5682,7 +5841,9 @@ function createTestRoom(options) {
|
|
|
5682
5841
|
const dt = Math.min(0.05, realDelta);
|
|
5683
5842
|
last = now;
|
|
5684
5843
|
if (!scenarioRunning) update(dt);
|
|
5844
|
+
const restoreCamera = vfx.applyCamera();
|
|
5685
5845
|
gl.render(scene, camera);
|
|
5846
|
+
restoreCamera();
|
|
5686
5847
|
roomMetrics.rendered();
|
|
5687
5848
|
};
|
|
5688
5849
|
raf = requestAnimationFrame(loop);
|