@adatechnology/game-test-room 0.1.3 → 0.2.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/dist/index.d.ts +7 -0
- package/dist/index.global.js +136 -19
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +136 -19
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -291,6 +291,9 @@ var en = {
|
|
|
291
291
|
"ui.quality.medium": "medium",
|
|
292
292
|
"ui.quality.high": "high",
|
|
293
293
|
"ui.equip": "Equip as a tool",
|
|
294
|
+
"ui.search_items": "Search items (all groups)",
|
|
295
|
+
"ui.search_tests": "Search tests",
|
|
296
|
+
"ui.no_results": "Nothing found",
|
|
294
297
|
"ui.objects": "{n} objects",
|
|
295
298
|
"ui.tests": "Tests",
|
|
296
299
|
"ui.run_all": "Run all",
|
|
@@ -580,6 +583,9 @@ var ptBR = {
|
|
|
580
583
|
"ui.quality.medium": "m\xE9dia",
|
|
581
584
|
"ui.quality.high": "alta",
|
|
582
585
|
"ui.equip": "Equipar como ferramenta",
|
|
586
|
+
"ui.search_items": "Buscar item (todas as abas)",
|
|
587
|
+
"ui.search_tests": "Buscar teste",
|
|
588
|
+
"ui.no_results": "Nada encontrado",
|
|
583
589
|
"ui.objects": "{n} objetos",
|
|
584
590
|
"ui.tests": "Testes",
|
|
585
591
|
"ui.run_all": "Rodar todos",
|
|
@@ -2318,7 +2324,11 @@ var SCENARIOS = scenariosFor("en");
|
|
|
2318
2324
|
var CSS = `
|
|
2319
2325
|
.tr-root{position:absolute;inset:0;pointer-events:none;font:12px/1.35 system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;color:#e8eaee;overflow:hidden}
|
|
2320
2326
|
.tr-root *{box-sizing:border-box}
|
|
2321
|
-
.tr-root button,.tr-root select,.tr-root label{pointer-events:auto}
|
|
2327
|
+
.tr-root button,.tr-root select,.tr-root label,.tr-root input{pointer-events:auto}
|
|
2328
|
+
.tr-search{width:100%;margin:0 0 6px;padding:5px 8px;border-radius:5px;border:1px solid #3a404c;background:#0f1116;color:#e8eaee;font:inherit}
|
|
2329
|
+
.tr-search::placeholder{color:#7d8594}
|
|
2330
|
+
.tr-item small{color:#7d8594;margin-left:4px}
|
|
2331
|
+
.tr-empty{color:#9aa3b2;padding:4px 2px;grid-column:1/-1}
|
|
2322
2332
|
.tr-cross{position:absolute;left:50%;top:50%;width:18px;height:18px;margin:-9px 0 0 -9px}
|
|
2323
2333
|
.tr-cross:before,.tr-cross:after{content:"";position:absolute;background:#fff;opacity:.85;box-shadow:0 0 2px #000}
|
|
2324
2334
|
.tr-cross:before{left:8px;top:0;width:2px;height:18px}.tr-cross:after{top:8px;left:0;height:2px;width:18px}
|
|
@@ -2369,6 +2379,7 @@ var CSS = `
|
|
|
2369
2379
|
.tr-summary{margin-top:6px;color:#9aa3b2}
|
|
2370
2380
|
@media (max-width:980px){.tr-left,.tr-right{display:none}.tr-root.open .tr-left,.tr-root.open .tr-right{display:flex}.tr-toggle{display:block;pointer-events:auto}.tr-root.open .tr-toggle{right:min(310px,34vw)}.tr-left{width:min(400px,46vw)}.tr-help{display:none}}
|
|
2371
2381
|
`;
|
|
2382
|
+
var fold = (text) => text.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
|
|
2372
2383
|
var mk = (tag, cls, text) => {
|
|
2373
2384
|
const e = document.createElement(tag);
|
|
2374
2385
|
if (cls) e.className = cls;
|
|
@@ -2508,15 +2519,27 @@ function createUi(o) {
|
|
|
2508
2519
|
const tests = mk("div", "tr-panel tr-tests");
|
|
2509
2520
|
right.append(cat, anim, tests);
|
|
2510
2521
|
cat.appendChild(mk("h3", void 0, t("ui.catalog")));
|
|
2522
|
+
const itemSearch = mk("input", "tr-search");
|
|
2523
|
+
itemSearch.type = "search";
|
|
2524
|
+
itemSearch.placeholder = t("ui.search_items");
|
|
2511
2525
|
const tabs = mk("div", "tr-tabs");
|
|
2512
2526
|
const items = mk("div", "tr-items");
|
|
2513
|
-
cat.append(tabs, items);
|
|
2527
|
+
cat.append(itemSearch, tabs, items);
|
|
2514
2528
|
let activeGroup = "";
|
|
2529
|
+
const visibleItems = () => {
|
|
2530
|
+
const term = fold(itemSearch.value.trim());
|
|
2531
|
+
if (!term) return (o.groups().get(activeGroup) ?? []).map((item) => ({ item, group: activeGroup }));
|
|
2532
|
+
return [...o.groups()].flatMap(([group, list2]) => list2.filter((item) => fold(`${item.name} ${item.id} ${group}`).includes(term)).map((item) => ({ item, group })));
|
|
2533
|
+
};
|
|
2515
2534
|
const renderItems = () => {
|
|
2516
2535
|
items.textContent = "";
|
|
2517
|
-
|
|
2536
|
+
const searching = itemSearch.value.trim() !== "";
|
|
2537
|
+
const found = visibleItems();
|
|
2538
|
+
if (!found.length) items.appendChild(mk("div", "tr-empty", t("ui.no_results")));
|
|
2539
|
+
for (const { item: it, group } of found) {
|
|
2518
2540
|
const wrap = mk("div", "tr-item");
|
|
2519
2541
|
const b = mk("button", "tr-btn", it.name);
|
|
2542
|
+
if (searching) b.appendChild(mk("small", void 0, group));
|
|
2520
2543
|
b.type = "button";
|
|
2521
2544
|
b.title = it.note ?? it.name;
|
|
2522
2545
|
b.onclick = () => o.onSpawn(it.id);
|
|
@@ -2547,6 +2570,8 @@ function createUi(o) {
|
|
|
2547
2570
|
renderItems();
|
|
2548
2571
|
};
|
|
2549
2572
|
renderTabs();
|
|
2573
|
+
itemSearch.oninput = renderItems;
|
|
2574
|
+
for (const box2 of [itemSearch]) box2.addEventListener("keydown", (e) => e.stopPropagation());
|
|
2550
2575
|
const controls = mk("div", "tr-row");
|
|
2551
2576
|
const ctl = (label, fn) => {
|
|
2552
2577
|
const b = mk("button", "tr-btn", label);
|
|
@@ -2588,8 +2613,16 @@ function createUi(o) {
|
|
|
2588
2613
|
const runAll = mk("button", "tr-btn", t("ui.run_all"));
|
|
2589
2614
|
runAll.type = "button";
|
|
2590
2615
|
const summary = mk("div", "tr-summary");
|
|
2616
|
+
const testSearch = mk("input", "tr-search");
|
|
2617
|
+
testSearch.type = "search";
|
|
2618
|
+
testSearch.placeholder = t("ui.search_tests");
|
|
2619
|
+
testSearch.addEventListener("keydown", (e) => e.stopPropagation());
|
|
2591
2620
|
const list = mk("div", "list");
|
|
2592
|
-
tests.append(runAll, summary, list);
|
|
2621
|
+
tests.append(runAll, summary, testSearch, list);
|
|
2622
|
+
testSearch.oninput = () => {
|
|
2623
|
+
const term = fold(testSearch.value.trim());
|
|
2624
|
+
for (const b of scBtns.values()) b.style.display = !term || fold(b.textContent ?? "").includes(term) ? "" : "none";
|
|
2625
|
+
};
|
|
2593
2626
|
const scBtns = /* @__PURE__ */ new Map();
|
|
2594
2627
|
let busy = false;
|
|
2595
2628
|
const mark = (rep) => {
|
|
@@ -2788,6 +2821,11 @@ function createUi(o) {
|
|
|
2788
2821
|
renderTabs();
|
|
2789
2822
|
renderScenarios();
|
|
2790
2823
|
},
|
|
2824
|
+
showGroup(group) {
|
|
2825
|
+
activeGroup = group;
|
|
2826
|
+
itemSearch.value = "";
|
|
2827
|
+
renderTabs();
|
|
2828
|
+
},
|
|
2791
2829
|
togglePanels,
|
|
2792
2830
|
dispose() {
|
|
2793
2831
|
offLog();
|
|
@@ -2858,7 +2896,13 @@ function createTestRoom(options) {
|
|
|
2858
2896
|
const p = world.part(arena.table.id, arena.table.topPart);
|
|
2859
2897
|
return !!p && p.status !== "broken" && p.status !== "melted" && p.status !== "ashes";
|
|
2860
2898
|
};
|
|
2861
|
-
const
|
|
2899
|
+
const restingTops = [];
|
|
2900
|
+
const restingTopAt = (x, z) => {
|
|
2901
|
+
let top = 0;
|
|
2902
|
+
for (const c of restingTops) if (x > c.minX && x < c.maxX && z > c.minZ && z < c.maxZ && world.has(c.objectId)) top = Math.max(top, c.top);
|
|
2903
|
+
return top;
|
|
2904
|
+
};
|
|
2905
|
+
const groundAt = (x, z) => Math.max(x > arena.table.minX && x < arena.table.maxX && z > arena.table.minZ && z < arena.table.maxZ && tableOk() ? arena.table.top : 0, restingTopAt(x, z));
|
|
2862
2906
|
const ownFluids = !options.fluids;
|
|
2863
2907
|
const fluids = options.fluids ?? createFluids({ seed: 1, locale, liquids: options.liquids, groundAt });
|
|
2864
2908
|
const pristineFluids = ownFluids ? fluids.serialize() : null;
|
|
@@ -2991,16 +3035,16 @@ function createTestRoom(options) {
|
|
|
2991
3035
|
const puncture = (ev) => {
|
|
2992
3036
|
const e = entryOf(ev.object);
|
|
2993
3037
|
if (!e?.containerId || e.fluidPart !== ev.part) return;
|
|
2994
|
-
const
|
|
2995
|
-
|
|
2996
|
-
tmpM.copy(
|
|
3038
|
+
const host2 = hostOf(e);
|
|
3039
|
+
host2.root.updateMatrixWorld(true);
|
|
3040
|
+
tmpM.copy(host2.root.matrixWorld).invert();
|
|
2997
3041
|
const local = tmpV.set(ev.point[0], ev.point[1], ev.point[2]).applyMatrix4(tmpM);
|
|
2998
3042
|
local.x -= e.fluidOffset[0];
|
|
2999
3043
|
local.y -= e.fluidOffset[1];
|
|
3000
3044
|
local.z -= e.fluidOffset[2];
|
|
3001
|
-
|
|
3045
|
+
host2.root.getWorldQuaternion(tmpQ).invert();
|
|
3002
3046
|
const n = tmpV2.set(ev.direction[0], ev.direction[1], ev.direction[2]).applyQuaternion(tmpQ);
|
|
3003
|
-
const part = world.part(
|
|
3047
|
+
const part = world.part(host2.objectId, ev.part);
|
|
3004
3048
|
const hole = part?.holes[part.holes.length - 1];
|
|
3005
3049
|
fluids.puncture(e.containerId, [local.x, local.y, local.z], [n.x, n.y, n.z], hole?.diameter ?? 9e-3);
|
|
3006
3050
|
};
|
|
@@ -3206,6 +3250,7 @@ function createTestRoom(options) {
|
|
|
3206
3250
|
};
|
|
3207
3251
|
const clear = () => {
|
|
3208
3252
|
clearEverything();
|
|
3253
|
+
restingTops.length = 0;
|
|
3209
3254
|
pending = [];
|
|
3210
3255
|
action("clear", t("act.clear"));
|
|
3211
3256
|
};
|
|
@@ -3425,6 +3470,7 @@ function createTestRoom(options) {
|
|
|
3425
3470
|
}
|
|
3426
3471
|
});
|
|
3427
3472
|
player.kick(recoil[0], recoil[1]);
|
|
3473
|
+
emitHost({ type: "shot", weapon: toolId, shots: shots.length, recoil });
|
|
3428
3474
|
const f = first;
|
|
3429
3475
|
const d = f ? num(f.distance, 1) : "";
|
|
3430
3476
|
let text;
|
|
@@ -3664,7 +3710,8 @@ function createTestRoom(options) {
|
|
|
3664
3710
|
if (cat === "hand") return;
|
|
3665
3711
|
const res = tool.trigger([aimO[0], aimO[1], aimO[2]], [aimD[0], aimD[1], aimD[2]], dt, held, "player");
|
|
3666
3712
|
if (res.status === "empty") {
|
|
3667
|
-
|
|
3713
|
+
emitHost({ type: "empty", weapon: toolId });
|
|
3714
|
+
if (!tool.reloading && tool.reserve > 0) startReload();
|
|
3668
3715
|
say(t(tool.reloading ? "hud.reloading" : "hud.empty"));
|
|
3669
3716
|
return;
|
|
3670
3717
|
}
|
|
@@ -3716,6 +3763,7 @@ function createTestRoom(options) {
|
|
|
3716
3763
|
progress = null;
|
|
3717
3764
|
action("tool", t("act.tool", { weapon: toolName(id) }), { data: { weapon: id } });
|
|
3718
3765
|
fire("tool", id);
|
|
3766
|
+
emitHost({ type: "tool", weapon: id });
|
|
3719
3767
|
};
|
|
3720
3768
|
const cycleTool = (dir) => {
|
|
3721
3769
|
if (!loadout.length) return;
|
|
@@ -3943,7 +3991,7 @@ function createTestRoom(options) {
|
|
|
3943
3991
|
case "KeyR":
|
|
3944
3992
|
if (heldPiece) heldYaw += Math.PI / 8;
|
|
3945
3993
|
else {
|
|
3946
|
-
|
|
3994
|
+
startReload();
|
|
3947
3995
|
say(t("hud.reloading"));
|
|
3948
3996
|
}
|
|
3949
3997
|
break;
|
|
@@ -4041,10 +4089,10 @@ function createTestRoom(options) {
|
|
|
4041
4089
|
const syncContainers = (sdt) => {
|
|
4042
4090
|
for (const e of entries.values()) {
|
|
4043
4091
|
if (!e.containerId) continue;
|
|
4044
|
-
const
|
|
4045
|
-
|
|
4046
|
-
const q =
|
|
4047
|
-
const p = tmpV.set(e.fluidOffset[0], e.fluidOffset[1], e.fluidOffset[2]).applyMatrix4(
|
|
4092
|
+
const host2 = hostOf(e);
|
|
4093
|
+
host2.root.updateMatrixWorld(true);
|
|
4094
|
+
const q = host2.root.getWorldQuaternion(tmpQ);
|
|
4095
|
+
const p = tmpV.set(e.fluidOffset[0], e.fluidOffset[1], e.fluidOffset[2]).applyMatrix4(host2.root.matrixWorld);
|
|
4048
4096
|
const vel = sdt > 0 ? [(p.x - e.last[0]) / sdt, (p.y - e.last[1]) / sdt, (p.z - e.last[2]) / sdt] : void 0;
|
|
4049
4097
|
e.last = [p.x, p.y, p.z];
|
|
4050
4098
|
fluids.setTransform(e.containerId, [p.x, p.y, p.z], [q.x, q.y, q.z, q.w], vel);
|
|
@@ -4055,9 +4103,9 @@ function createTestRoom(options) {
|
|
|
4055
4103
|
if (!e.containerId || !e.fluidPart) continue;
|
|
4056
4104
|
const c = fluids.container(e.containerId);
|
|
4057
4105
|
if (!c) continue;
|
|
4058
|
-
const
|
|
4059
|
-
const part = world.part(
|
|
4060
|
-
if (part && part.liters !== void 0 && Math.abs(part.liters - c.liters) > 1e-4) world.setLiters(
|
|
4106
|
+
const host2 = hostOf(e);
|
|
4107
|
+
const part = world.part(host2.objectId, e.fluidPart);
|
|
4108
|
+
if (part && part.liters !== void 0 && Math.abs(part.liters - c.liters) > 1e-4) world.setLiters(host2.objectId, e.fluidPart, c.liters);
|
|
4061
4109
|
}
|
|
4062
4110
|
};
|
|
4063
4111
|
let paused = false;
|
|
@@ -4088,6 +4136,9 @@ function createTestRoom(options) {
|
|
|
4088
4136
|
updateImpacts();
|
|
4089
4137
|
fluidView.update(dt);
|
|
4090
4138
|
for (const s of tools.values()) s.update(dt);
|
|
4139
|
+
if (reloadingSeen && !tool.reloading) emitHost({ type: "reload", weapon: toolId, phase: "end" });
|
|
4140
|
+
reloadingSeen = tool.reloading;
|
|
4141
|
+
for (const update2 of frameListeners) update2(dt);
|
|
4091
4142
|
for (const tr of tracers) {
|
|
4092
4143
|
if (tr.life > 0) {
|
|
4093
4144
|
tr.life -= dt;
|
|
@@ -4229,6 +4280,36 @@ function createTestRoom(options) {
|
|
|
4229
4280
|
const moduleCheck = (ok, text, data, itemId) => {
|
|
4230
4281
|
log.add({ channel: "check", type: ok ? "pass" : "fail", text: `${ok ? "\u2713" : "\u2717"} ${text}`, itemId, data: { ok, ...data } });
|
|
4231
4282
|
};
|
|
4283
|
+
const modules = /* @__PURE__ */ new Map();
|
|
4284
|
+
const detachers = [];
|
|
4285
|
+
const hostListeners = /* @__PURE__ */ new Set();
|
|
4286
|
+
const frameListeners = /* @__PURE__ */ new Set();
|
|
4287
|
+
let reloadingSeen = false;
|
|
4288
|
+
function emitHost(event) {
|
|
4289
|
+
for (const listener of hostListeners) listener(event);
|
|
4290
|
+
}
|
|
4291
|
+
function startReload() {
|
|
4292
|
+
if (tool.reload()) emitHost({ type: "reload", weapon: toolId, phase: "start" });
|
|
4293
|
+
}
|
|
4294
|
+
const host = {
|
|
4295
|
+
THREE,
|
|
4296
|
+
scene,
|
|
4297
|
+
camera,
|
|
4298
|
+
locale,
|
|
4299
|
+
groundAt,
|
|
4300
|
+
tool: () => ({ weapon: toolId, category: tool.def.category, ammo: tool.ammo, capacity: tool.capacity, reserve: tool.reserve, reloading: tool.reloading }),
|
|
4301
|
+
on(listener) {
|
|
4302
|
+
hostListeners.add(listener);
|
|
4303
|
+
return () => hostListeners.delete(listener);
|
|
4304
|
+
},
|
|
4305
|
+
onFrame(update2) {
|
|
4306
|
+
frameListeners.add(update2);
|
|
4307
|
+
return () => frameListeners.delete(update2);
|
|
4308
|
+
},
|
|
4309
|
+
log(text, data) {
|
|
4310
|
+
action("module_note", text, { data });
|
|
4311
|
+
}
|
|
4312
|
+
};
|
|
4232
4313
|
const loadModule = (mod) => {
|
|
4233
4314
|
const report = { module: mod.id, items: [], scenarios: [], ok: true, problems: [] };
|
|
4234
4315
|
const fail = (problem) => {
|
|
@@ -4293,6 +4374,8 @@ function createTestRoom(options) {
|
|
|
4293
4374
|
report.scenarios.push(sc.id);
|
|
4294
4375
|
}
|
|
4295
4376
|
action("module", t("act.module_loaded", { name: mod.name, items: report.items.length, scenarios: report.scenarios.length, result: t(report.ok ? "mod.all_ok" : "mod.some_failed") }), { data: { module: mod.id, items: report.items, scenarios: report.scenarios, ok: report.ok } });
|
|
4377
|
+
modules.set(mod.id, mod);
|
|
4378
|
+
if (mod.attach) detachers.push(mod.attach(host));
|
|
4296
4379
|
ui?.refresh();
|
|
4297
4380
|
return report;
|
|
4298
4381
|
};
|
|
@@ -4348,6 +4431,37 @@ function createTestRoom(options) {
|
|
|
4348
4431
|
}
|
|
4349
4432
|
return report;
|
|
4350
4433
|
};
|
|
4434
|
+
const layArea = (layout) => {
|
|
4435
|
+
const holders = layout.placements.filter((p) => p.surface);
|
|
4436
|
+
const rest = layout.placements.filter((p) => !p.surface);
|
|
4437
|
+
const placed = [...holders, ...rest].map((p) => {
|
|
4438
|
+
const so = spawn(p.item, p.position, p.rotationY ?? 0);
|
|
4439
|
+
if (p.surface) {
|
|
4440
|
+
const [hx, hz] = p.surface.halfSize;
|
|
4441
|
+
restingTops.push({ objectId: so.objectId, minX: p.position[0] - hx, maxX: p.position[0] + hx, minZ: p.position[2] - hz, maxZ: p.position[2] + hz, top: p.surface.height });
|
|
4442
|
+
}
|
|
4443
|
+
return { so, asleep: p.asleep === true };
|
|
4444
|
+
});
|
|
4445
|
+
for (const { so, asleep } of placed) {
|
|
4446
|
+
const piece = asleep ? rendererPieces.get(so.objectId) : void 0;
|
|
4447
|
+
if (piece) piece.sleeping = true;
|
|
4448
|
+
}
|
|
4449
|
+
if (layout.view) room.look(layout.view.eye, layout.view.target);
|
|
4450
|
+
return placed.map(({ so }) => so);
|
|
4451
|
+
};
|
|
4452
|
+
const openArea = (area) => {
|
|
4453
|
+
clear();
|
|
4454
|
+
const mod = [...modules.values()].find((m) => m.id === area || m.group === area);
|
|
4455
|
+
const label = mod?.group ?? S.groups[area] ?? area;
|
|
4456
|
+
ui?.showGroup(label);
|
|
4457
|
+
const layout = mod?.area?.(locale);
|
|
4458
|
+
if (layout) return layArea(layout);
|
|
4459
|
+
const items = catalog.filter((item) => item.group === label);
|
|
4460
|
+
const front = items.filter((item) => item.movable !== false);
|
|
4461
|
+
const back = items.filter((item) => item.movable === false);
|
|
4462
|
+
const row = (list, gap, z) => list.map((item, index) => spawn(item.id, [(index - (list.length - 1) / 2) * gap, 0, z], 0));
|
|
4463
|
+
return [...row(front, 1.6, 1.5), ...row(back, 4.5, -4)];
|
|
4464
|
+
};
|
|
4351
4465
|
const room = {
|
|
4352
4466
|
world,
|
|
4353
4467
|
renderer,
|
|
@@ -4427,10 +4541,12 @@ function createTestRoom(options) {
|
|
|
4427
4541
|
fastForward = on;
|
|
4428
4542
|
},
|
|
4429
4543
|
equip,
|
|
4544
|
+
openArea,
|
|
4430
4545
|
loadModule,
|
|
4431
4546
|
animations: animationsOf,
|
|
4432
4547
|
playAnimation,
|
|
4433
4548
|
dispose() {
|
|
4549
|
+
for (const detach of detachers.splice(0)) detach();
|
|
4434
4550
|
cancelAnimationFrame?.(raf);
|
|
4435
4551
|
window.removeEventListener("resize", onResize);
|
|
4436
4552
|
player.dispose();
|
|
@@ -4520,6 +4636,7 @@ function createTestRoom(options) {
|
|
|
4520
4636
|
if (options.initial) {
|
|
4521
4637
|
for (const [i, id] of options.initial.entries()) spawn(id, [(i - (options.initial.length - 1) / 2) * 1.3, 0, 0.5]);
|
|
4522
4638
|
}
|
|
4639
|
+
if (options.area) openArea(options.area);
|
|
4523
4640
|
return room;
|
|
4524
4641
|
}
|
|
4525
4642
|
function structuredCloneSpec(spec) {
|