@adatechnology/game-test-room 0.14.0 → 0.15.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 +16 -0
- package/dist/index.d.ts +158 -17
- package/dist/index.global.js +548 -96
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +505 -91
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
package/dist/index.global.js
CHANGED
|
@@ -23,20 +23,26 @@ var GameTestRoom = (() => {
|
|
|
23
23
|
__export(index_exports, {
|
|
24
24
|
DEFAULT_HAND_STANCES: () => DEFAULT_HAND_STANCES,
|
|
25
25
|
DEFAULT_STRIKE_CLIPS: () => DEFAULT_STRIKE_CLIPS,
|
|
26
|
+
DEFAULT_TRIALS: () => DEFAULT_TRIALS,
|
|
26
27
|
LOADING_CLASS: () => LOADING_CLASS,
|
|
27
28
|
LOADING_CSS: () => LOADING_CSS,
|
|
28
29
|
MATERIAL_COLORS: () => MATERIAL_COLORS,
|
|
30
|
+
TRIAL_KINDS: () => TRIAL_KINDS,
|
|
29
31
|
brickWallSpec: () => brickWallSpec,
|
|
30
32
|
buildFromSpec: () => buildFromSpec,
|
|
31
33
|
createLoading: () => createLoading,
|
|
32
34
|
createLog: () => createLog,
|
|
33
35
|
createTestRoom: () => createTestRoom,
|
|
36
|
+
createTestTable: () => createTestTable,
|
|
34
37
|
decodeBenchState: () => decodeBenchState,
|
|
35
38
|
encodeBenchState: () => encodeBenchState,
|
|
39
|
+
fitRow: () => fitRow,
|
|
36
40
|
loadingMarkup: () => loadingMarkup,
|
|
37
41
|
locales: () => locales4,
|
|
38
42
|
nextPaint: () => nextPaint,
|
|
39
|
-
resolveStrings: () => resolveStrings5
|
|
43
|
+
resolveStrings: () => resolveStrings5,
|
|
44
|
+
rowCentres: () => rowCentres,
|
|
45
|
+
trialView: () => trialView
|
|
40
46
|
});
|
|
41
47
|
|
|
42
48
|
// ../game-contracts/dist/index.js
|
|
@@ -806,6 +812,23 @@ var GameTestRoom = (() => {
|
|
|
806
812
|
return angle === 0 ? q : quat.mul(q, quat.fromAxisAngle([0, 0, 1], angle));
|
|
807
813
|
};
|
|
808
814
|
var clamp = (v, a, b) => Math.max(a, Math.min(b, v));
|
|
815
|
+
var tearStack = (inp, root, t, u, v, n, sheet) => {
|
|
816
|
+
const { dims, rnd: rnd2, maxFragments } = inp;
|
|
817
|
+
const layers = clamp(Math.ceil(dims[t] / STACK_LAYER), 2, 4);
|
|
818
|
+
const per = clamp(n, 3, Math.max(3, Math.floor(maxFragments / layers)));
|
|
819
|
+
const out = [];
|
|
820
|
+
for (let k = 0; k < layers; k++) {
|
|
821
|
+
const off = -dims[t] / 2 + (k + 0.5) * dims[t] / layers;
|
|
822
|
+
for (const b of bsp(root, per, [u, v], rnd2)) {
|
|
823
|
+
if (out.length >= maxFragments) return out;
|
|
824
|
+
const c = [b.c[0], b.c[1], b.c[2]];
|
|
825
|
+
c[t] = off;
|
|
826
|
+
out.push({ center: c, size: [b.s[u], b.s[v], sheet], q: frame(u, v, t, (rnd2() - 0.5) * 0.8), shape: "scrap", weight: b.s[u] * b.s[v], inner: out.length % 4 !== 0 });
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
return out;
|
|
830
|
+
};
|
|
831
|
+
var STACK_LAYER = 0.012;
|
|
809
832
|
var planFragments = (inp) => {
|
|
810
833
|
const { style, dims, rnd: rnd2, maxFragments } = inp;
|
|
811
834
|
const [fmin, fmax] = inp.fragmentSize;
|
|
@@ -848,6 +871,8 @@ var GameTestRoom = (() => {
|
|
|
848
871
|
}
|
|
849
872
|
if (style === "tear") {
|
|
850
873
|
const n2 = clamp(Math.round(dims[u] * dims[v] / (mean * mean)), 3, maxFragments);
|
|
874
|
+
const sheet = inp.sheet ?? 0;
|
|
875
|
+
if (sheet > 0 && dims[t] > sheet * 4) return tearStack(inp, root, t, u, v, n2, sheet);
|
|
851
876
|
const th2 = Math.min(inp.thickness, dims[t]);
|
|
852
877
|
for (const b of bsp(root, n2, [u, v], rnd2)) {
|
|
853
878
|
out.push({ center: [b.c[0], b.c[1], b.c[2]], size: [b.s[u], b.s[v], th2], q: frame(u, v, t), shape: "scrap", weight: b.s[u] * b.s[v] });
|
|
@@ -2420,6 +2445,7 @@ var GameTestRoom = (() => {
|
|
|
2420
2445
|
0.03,
|
|
2421
2446
|
0.15
|
|
2422
2447
|
],
|
|
2448
|
+
sheet: 1e-3,
|
|
2423
2449
|
damageScale: {
|
|
2424
2450
|
slash: 3
|
|
2425
2451
|
},
|
|
@@ -2435,7 +2461,8 @@ var GameTestRoom = (() => {
|
|
|
2435
2461
|
smoke: "grey"
|
|
2436
2462
|
},
|
|
2437
2463
|
visual: {
|
|
2438
|
-
charColor: 1709072
|
|
2464
|
+
charColor: 1709072,
|
|
2465
|
+
innerColor: 15524048
|
|
2439
2466
|
},
|
|
2440
2467
|
tags: [
|
|
2441
2468
|
"fabric",
|
|
@@ -4153,7 +4180,8 @@ var GameTestRoom = (() => {
|
|
|
4153
4180
|
hit: hitLocal,
|
|
4154
4181
|
fragmentSize: p.mat.fragmentSize ?? [0.04, 0.2],
|
|
4155
4182
|
maxFragments,
|
|
4156
|
-
rnd: rnd2
|
|
4183
|
+
rnd: rnd2,
|
|
4184
|
+
sheet: p.mat.sheet
|
|
4157
4185
|
});
|
|
4158
4186
|
const holes = p.st.holes.length > 0 ? localHolesOf(p) : [];
|
|
4159
4187
|
const mass = p.st.mass;
|
|
@@ -4177,7 +4205,8 @@ var GameTestRoom = (() => {
|
|
|
4177
4205
|
velocity: [dv[0] * k, dv[1] * k + 0.5 * rnd2(), dv[2] * k],
|
|
4178
4206
|
angularVelocity: [(rnd2() - 0.5) * 12, (rnd2() - 0.5) * 12, (rnd2() - 0.5) * 12],
|
|
4179
4207
|
mass: mass * f.weight / totW,
|
|
4180
|
-
...marks.length > 0 ? { marks } : {}
|
|
4208
|
+
...marks.length > 0 ? { marks } : {},
|
|
4209
|
+
...f.inner ? { inner: true } : {}
|
|
4181
4210
|
};
|
|
4182
4211
|
});
|
|
4183
4212
|
const reinforcement = p.spec.reinforcement;
|
|
@@ -15742,6 +15771,9 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
15742
15771
|
const bl = Math.round(ab + ((b & 255) - ab) * t);
|
|
15743
15772
|
return r << 16 | g << 8 | bl;
|
|
15744
15773
|
}
|
|
15774
|
+
var SHEET_MAX = 4e-3;
|
|
15775
|
+
var SHEET_AIR = 2.5;
|
|
15776
|
+
var SHEET_SWAY = 3;
|
|
15745
15777
|
var FragmentSystem = class {
|
|
15746
15778
|
constructor(THREE, o) {
|
|
15747
15779
|
__publicField2(this, "THREE", THREE);
|
|
@@ -15847,7 +15879,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
15847
15879
|
asleep: false,
|
|
15848
15880
|
rest: 0,
|
|
15849
15881
|
fade: 1,
|
|
15850
|
-
dying: false
|
|
15882
|
+
dying: false,
|
|
15883
|
+
air: f.shape === "scrap" && sz[2] <= SHEET_MAX ? SHEET_AIR : 0
|
|
15851
15884
|
};
|
|
15852
15885
|
this.list.push(live);
|
|
15853
15886
|
let alive = this.list.reduce((n, l) => n + (l.dying ? 0 : 1), 0);
|
|
@@ -15931,6 +15964,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
15931
15964
|
}
|
|
15932
15965
|
} else {
|
|
15933
15966
|
f.ang.multiplyScalar(Math.exp(-0.15 * dt));
|
|
15967
|
+
if (f.air > 0) this.flutter(f, dt);
|
|
15934
15968
|
}
|
|
15935
15969
|
if (contact && f.vel.lengthSq() < 4) this.settle(f, dt);
|
|
15936
15970
|
const slow = f.vel.lengthSq() < 0.012 && f.ang.lengthSq() < 0.2;
|
|
@@ -15945,6 +15979,14 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
15945
15979
|
f.still = 0;
|
|
15946
15980
|
}
|
|
15947
15981
|
}
|
|
15982
|
+
/** A sheet in the air: drag slows it to a slow fall, and it sways and turns over as it goes. */
|
|
15983
|
+
flutter(f, dt) {
|
|
15984
|
+
f.vel.multiplyScalar(Math.exp(-f.air * dt));
|
|
15985
|
+
f.vel.x += (this.rng() - 0.5) * SHEET_SWAY * dt;
|
|
15986
|
+
f.vel.z += (this.rng() - 0.5) * SHEET_SWAY * dt;
|
|
15987
|
+
f.ang.x += (this.rng() - 0.5) * SHEET_SWAY * 4 * dt;
|
|
15988
|
+
f.ang.z += (this.rng() - 0.5) * SHEET_SWAY * 4 * dt;
|
|
15989
|
+
}
|
|
15948
15990
|
/** Tip the fragment towards lying on its flattest face. */
|
|
15949
15991
|
settle(f, dt) {
|
|
15950
15992
|
const mesh2 = f.mesh;
|
|
@@ -18225,6 +18267,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
18225
18267
|
const def = pv ? this.world.materials.get(pv.material) : void 0;
|
|
18226
18268
|
if (kind === "smoke") return { color: SMOKE_TONES[def?.thermal.smoke ?? "grey"] };
|
|
18227
18269
|
if (kind === "fibers" || kind === "leaves") {
|
|
18270
|
+
if (def?.visual?.innerColor !== void 0) return { color: def.visual.innerColor };
|
|
18228
18271
|
const c = pv ? this.visuals.baseColorHex(pv.meshes[0] ?? {}) : -1;
|
|
18229
18272
|
return c >= 0 ? { color: c } : void 0;
|
|
18230
18273
|
}
|
|
@@ -18373,7 +18416,8 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
18373
18416
|
for (let i = 0; i < ev.fragments.length; i += step) {
|
|
18374
18417
|
const fr = ev.fragments[i];
|
|
18375
18418
|
const fdef = this.world.materials.get(fr.material);
|
|
18376
|
-
const
|
|
18419
|
+
const tone2 = fr.inner && fdef?.visual?.innerColor !== void 0 ? fdef.visual.innerColor : fdef === def || !fdef ? color : fallbackColor(fr.material, fdef);
|
|
18420
|
+
const mat = this.fragments.materialFor(fr.material, tone2, transparent || fdef?.visual?.transparent === true, opacity);
|
|
18377
18421
|
this.fragments.spawn(fr, mat, ev.object, maxAlive);
|
|
18378
18422
|
}
|
|
18379
18423
|
const up = [0, 1, 0];
|
|
@@ -18391,7 +18435,7 @@ void main(){vec2 d=gl_PointCoord-.5;float r=length(d)*2.;if(r>1.)discard;float a
|
|
|
18391
18435
|
this.vfx.burst("dust", ev.point, up, 5, 0.8, { color: 11901556 });
|
|
18392
18436
|
break;
|
|
18393
18437
|
case "tear":
|
|
18394
|
-
this.vfx.burst("fibers", ev.point, up, 12 * sizeK, 1.6, { color });
|
|
18438
|
+
this.vfx.burst("fibers", ev.point, up, 12 * sizeK, 1.6, { color: def?.visual?.innerColor ?? color });
|
|
18395
18439
|
break;
|
|
18396
18440
|
case "snap":
|
|
18397
18441
|
this.vfx.burst("chips", ev.point, up, 8 * sizeK, 3);
|
|
@@ -38748,6 +38792,14 @@ void main(){
|
|
|
38748
38792
|
...projectile.fire ? { fire: projectile.fire } : {}
|
|
38749
38793
|
});
|
|
38750
38794
|
};
|
|
38795
|
+
const blastAt = (weapon, explosive, point) => {
|
|
38796
|
+
const def = arsenal.explosives.get(explosive);
|
|
38797
|
+
if (!def) return false;
|
|
38798
|
+
const event = new ExplosiveBody({ explosive: def, origin: point, triggers: ["remote"], rules: arsenal.rules.explosives }).trigger("remote");
|
|
38799
|
+
if (event?.type !== "detonate") return false;
|
|
38800
|
+
detonate({ weapon }, event.detonation);
|
|
38801
|
+
return true;
|
|
38802
|
+
};
|
|
38751
38803
|
const detonateCharges = () => {
|
|
38752
38804
|
let count = 0;
|
|
38753
38805
|
for (const projectile of [...projectiles]) {
|
|
@@ -38839,7 +38891,172 @@ void main(){
|
|
|
38839
38891
|
}
|
|
38840
38892
|
}
|
|
38841
38893
|
};
|
|
38842
|
-
return { throwUse, placeUse, backblastUse, detonateCharges, update, clear };
|
|
38894
|
+
return { throwUse, placeUse, backblastUse, detonateCharges, blastAt, update, clear };
|
|
38895
|
+
}
|
|
38896
|
+
|
|
38897
|
+
// src/test-table.ts
|
|
38898
|
+
var TRIAL_KINDS = ["shot", "punch", "stab", "fire", "blast"];
|
|
38899
|
+
var DEFAULT_TRIALS = {
|
|
38900
|
+
shot: { weapon: "pistol_9mm", distance: 2.5, lift: 0.5 },
|
|
38901
|
+
punch: { weapon: "fists", distance: 0.3, lift: 0.15 },
|
|
38902
|
+
stab: { weapon: "knife", distance: 0.22, lift: 0.1 },
|
|
38903
|
+
fire: { weapon: "flamethrower", distance: 1.6, lift: 0.4, hold: 0.8 },
|
|
38904
|
+
blast: { weapon: "frag_grenade", distance: 0.3, lift: 1, watch: 6 }
|
|
38905
|
+
};
|
|
38906
|
+
var resolveTrials = (overrides = {}) => Object.fromEntries(TRIAL_KINDS.map((kind) => [kind, { ...DEFAULT_TRIALS[kind], ...overrides[kind] }]));
|
|
38907
|
+
function fitRow(width, start, total, length3, gap) {
|
|
38908
|
+
let used = 0;
|
|
38909
|
+
let count = 0;
|
|
38910
|
+
for (let i = start; i < total; i++) {
|
|
38911
|
+
const w = width(i);
|
|
38912
|
+
const next = count === 0 ? w : used + gap + w;
|
|
38913
|
+
if (count > 0 && next > length3) break;
|
|
38914
|
+
used = next;
|
|
38915
|
+
count++;
|
|
38916
|
+
}
|
|
38917
|
+
return count;
|
|
38918
|
+
}
|
|
38919
|
+
function rowCentres(widths, centreX, gap) {
|
|
38920
|
+
const length3 = widths.reduce((sum2, w) => sum2 + w, 0) + gap * Math.max(0, widths.length - 1);
|
|
38921
|
+
let x = centreX - length3 / 2;
|
|
38922
|
+
return widths.map((w) => {
|
|
38923
|
+
const centre = x + w / 2;
|
|
38924
|
+
x += w + gap;
|
|
38925
|
+
return centre;
|
|
38926
|
+
});
|
|
38927
|
+
}
|
|
38928
|
+
function trialView(tool, kind, min2, max2) {
|
|
38929
|
+
const target = [(min2[0] + max2[0]) / 2, (min2[1] + max2[1]) / 2, (min2[2] + max2[2]) / 2];
|
|
38930
|
+
const away = kind === "blast" ? tool.watch ?? 6 : tool.distance;
|
|
38931
|
+
return { eye: [target[0], target[1] + tool.lift, max2[2] + away], target };
|
|
38932
|
+
}
|
|
38933
|
+
function createTestTable(context, options = {}) {
|
|
38934
|
+
const gap = options.gap ?? 0.12;
|
|
38935
|
+
const margin = options.margin ?? 0.1;
|
|
38936
|
+
const trials = resolveTrials(options.trials);
|
|
38937
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
38938
|
+
let current = null;
|
|
38939
|
+
const itemsOf = (group) => context.groups().get(group) ?? [];
|
|
38940
|
+
const emit2 = () => {
|
|
38941
|
+
const page = state();
|
|
38942
|
+
for (const listener of [...listeners]) listener(page);
|
|
38943
|
+
return page;
|
|
38944
|
+
};
|
|
38945
|
+
const state = () => {
|
|
38946
|
+
if (!current) return null;
|
|
38947
|
+
const total = itemsOf(current.group).length;
|
|
38948
|
+
const live = current.objects.filter((o) => context.box(o.objectId));
|
|
38949
|
+
return {
|
|
38950
|
+
group: current.group,
|
|
38951
|
+
page: current.page,
|
|
38952
|
+
first: current.first,
|
|
38953
|
+
total,
|
|
38954
|
+
objects: live,
|
|
38955
|
+
selected: current.selected,
|
|
38956
|
+
hasPrev: current.page > 0,
|
|
38957
|
+
hasNext: current.first + current.objects.length < total
|
|
38958
|
+
};
|
|
38959
|
+
};
|
|
38960
|
+
const bounds = /* @__PURE__ */ new Map();
|
|
38961
|
+
const widthOf = (itemId) => context.measure(itemId).w;
|
|
38962
|
+
const boundsUntil = (group, length3, until) => {
|
|
38963
|
+
const items = itemsOf(group);
|
|
38964
|
+
const known = bounds.get(group) ?? [0];
|
|
38965
|
+
bounds.set(group, known);
|
|
38966
|
+
while (known[known.length - 1] < items.length && !until(known)) {
|
|
38967
|
+
const first = known[known.length - 1];
|
|
38968
|
+
known.push(first + fitRow((i) => widthOf(items[i].id), first, items.length, length3, gap));
|
|
38969
|
+
}
|
|
38970
|
+
return known;
|
|
38971
|
+
};
|
|
38972
|
+
const lengthOf2 = (top) => top.maxX - top.minX - 2 * margin;
|
|
38973
|
+
const lay = (group, page, top) => {
|
|
38974
|
+
const items = itemsOf(group);
|
|
38975
|
+
const known = boundsUntil(group, lengthOf2(top), (b) => b.length > page + 1);
|
|
38976
|
+
const index = Math.max(0, Math.min(page, known.length - 2));
|
|
38977
|
+
const first = known[index];
|
|
38978
|
+
const laid = items.slice(first, known[index + 1]);
|
|
38979
|
+
context.clear();
|
|
38980
|
+
const sizes = laid.map((item) => context.measure(item.id));
|
|
38981
|
+
const centres = rowCentres(sizes.map((size) => size.w), (top.minX + top.maxX) / 2, gap);
|
|
38982
|
+
const objects = laid.map((item, i) => {
|
|
38983
|
+
const d = Math.min(sizes[i].d, top.maxZ - top.minZ);
|
|
38984
|
+
const { objectId } = context.spawn(item.id, [centres[i], top.top, top.maxZ - margin / 2 - d / 2]);
|
|
38985
|
+
return { objectId, itemId: item.id, name: item.name };
|
|
38986
|
+
});
|
|
38987
|
+
current = { group, page: index, first, objects, selected: objects[0]?.objectId ?? null };
|
|
38988
|
+
context.log("test_table", context.translate("act.test_table", { group, from: first + 1, to: first + laid.length, total: items.length }), { group, page: index, items: laid.map((i) => i.id) });
|
|
38989
|
+
return emit2();
|
|
38990
|
+
};
|
|
38991
|
+
const open = (group, page = 0) => {
|
|
38992
|
+
const top = context.top();
|
|
38993
|
+
if (!itemsOf(group).length || !top) return null;
|
|
38994
|
+
return lay(group, page, top);
|
|
38995
|
+
};
|
|
38996
|
+
const find = (itemId) => {
|
|
38997
|
+
const top = context.top();
|
|
38998
|
+
const group = [...context.groups()].find(([, items]) => items.some((i) => i.id === itemId))?.[0];
|
|
38999
|
+
if (group === void 0 || !top) return null;
|
|
39000
|
+
const index = itemsOf(group).findIndex((i) => i.id === itemId);
|
|
39001
|
+
const known = boundsUntil(group, lengthOf2(top), (b) => b[b.length - 1] > index);
|
|
39002
|
+
lay(group, known.findIndex((bound, i) => bound <= index && index < known[i + 1]), top);
|
|
39003
|
+
const target = current?.objects.find((o) => o.itemId === itemId);
|
|
39004
|
+
if (current && target) current.selected = target.objectId;
|
|
39005
|
+
return emit2();
|
|
39006
|
+
};
|
|
39007
|
+
const selected = (objectId) => {
|
|
39008
|
+
const id = objectId ?? current?.selected ?? null;
|
|
39009
|
+
return id && context.box(id) ? id : null;
|
|
39010
|
+
};
|
|
39011
|
+
const trial = (kind, objectId) => {
|
|
39012
|
+
const id = selected(objectId);
|
|
39013
|
+
const box2 = id ? context.box(id) : null;
|
|
39014
|
+
if (!id || !box2) return false;
|
|
39015
|
+
const tool = trials[kind];
|
|
39016
|
+
if (!context.equip(tool.weapon)) return false;
|
|
39017
|
+
const view = trialView(tool, kind, box2.min, box2.max);
|
|
39018
|
+
context.look(view.eye, view.target);
|
|
39019
|
+
const name = current?.objects.find((o) => o.objectId === id)?.name ?? id;
|
|
39020
|
+
context.log("trial", context.translate(`act.trial.${kind}`, { name }), { kind, objectId: id, weapon: tool.weapon });
|
|
39021
|
+
if (kind === "blast") {
|
|
39022
|
+
const point = [view.target[0], box2.min[1] + 0.05, box2.min[2] - tool.distance];
|
|
39023
|
+
return context.blastAt(tool.weapon, point);
|
|
39024
|
+
}
|
|
39025
|
+
context.use(tool.hold ?? 0);
|
|
39026
|
+
emit2();
|
|
39027
|
+
return true;
|
|
39028
|
+
};
|
|
39029
|
+
return {
|
|
39030
|
+
groups: () => [...context.groups()].filter(([, items]) => items.length > 0).map(([group]) => group),
|
|
39031
|
+
items: () => [...context.groups()].flatMap(([group, items]) => items.map((item) => ({ id: item.id, name: item.name, group }))),
|
|
39032
|
+
open,
|
|
39033
|
+
find,
|
|
39034
|
+
next: () => {
|
|
39035
|
+
const page = state();
|
|
39036
|
+
return page?.hasNext ? open(page.group, page.page + 1) : page;
|
|
39037
|
+
},
|
|
39038
|
+
prev: () => {
|
|
39039
|
+
const page = state();
|
|
39040
|
+
return page?.hasPrev ? open(page.group, page.page - 1) : page;
|
|
39041
|
+
},
|
|
39042
|
+
relay: () => {
|
|
39043
|
+
if (!current) return null;
|
|
39044
|
+
context.resetAll();
|
|
39045
|
+
return open(current.group, current.page);
|
|
39046
|
+
},
|
|
39047
|
+
state,
|
|
39048
|
+
select: (objectId) => {
|
|
39049
|
+
if (!current || !current.objects.some((o) => o.objectId === objectId) || !context.box(objectId)) return false;
|
|
39050
|
+
current.selected = objectId;
|
|
39051
|
+
emit2();
|
|
39052
|
+
return true;
|
|
39053
|
+
},
|
|
39054
|
+
trial,
|
|
39055
|
+
on: (listener) => {
|
|
39056
|
+
listeners.add(listener);
|
|
39057
|
+
return () => listeners.delete(listener);
|
|
39058
|
+
}
|
|
39059
|
+
};
|
|
38843
39060
|
}
|
|
38844
39061
|
|
|
38845
39062
|
// src/host-events.ts
|
|
@@ -40271,7 +40488,7 @@ void main(){
|
|
|
40271
40488
|
"hud.throw": "throw",
|
|
40272
40489
|
"hud.tilt": "tilt (hold)",
|
|
40273
40490
|
"hud.equip": "equip {weapon}",
|
|
40274
|
-
"ui.help": "Click: look \xB7 WASD: walk \xB7 Shift: run \xB7 C: crouch \xB7 Space: jump \xB7 1-0 / wheel: tool \xB7 LMB: use \xB7 E: grab/drop \xB7 G or RMB: throw \xB7 R: reload \xB7 Q: fire mode \xB7 X: clear a jam \xB7 B: safety \xB7 Y: detonate charges set down \xB7 Tab: panels \xB7 Esc: free the mouse",
|
|
40491
|
+
"ui.help": "Click: look \xB7 WASD: walk \xB7 Shift: run \xB7 C: crouch \xB7 Space: jump \xB7 1-0 / wheel: tool \xB7 LMB: use (bare hands: punch) \xB7 E: grab/drop \xB7 G or RMB: throw \xB7 R: reload \xB7 Q: fire mode \xB7 X: clear a jam \xB7 B: safety \xB7 Y: detonate charges set down \xB7 Tab: panels \xB7 Esc: free the mouse",
|
|
40275
40492
|
"ui.help.camera": "V: 1st/3rd person (Shift+V: kick)",
|
|
40276
40493
|
"camera.first": "1st person",
|
|
40277
40494
|
"camera.third": "3rd person",
|
|
@@ -40285,6 +40502,32 @@ void main(){
|
|
|
40285
40502
|
"ui.copy_json": "Copy JSON",
|
|
40286
40503
|
"ui.download_ndjson": "Download NDJSON",
|
|
40287
40504
|
"ui.download_txt": "Download TXT",
|
|
40505
|
+
"ui.test_table": "Test table",
|
|
40506
|
+
"ui.test_table.group": "Group to lay on the table",
|
|
40507
|
+
"ui.test_table.lay": "Lay",
|
|
40508
|
+
"ui.test_table.prev": "Previous items",
|
|
40509
|
+
"ui.test_table.next": "Next items",
|
|
40510
|
+
"ui.test_table.relay": "Put back",
|
|
40511
|
+
"ui.test_table.search": "Find an item to put on the table",
|
|
40512
|
+
"ui.test_table.hint": "Pick a group and lay it on the table, then pick an item and try it.",
|
|
40513
|
+
"ui.test_table.page": "Items {from}\u2013{to} of {total}. Pick one, then a trial:",
|
|
40514
|
+
"ui.trial.shot": "Shot",
|
|
40515
|
+
"ui.trial.punch": "Punch",
|
|
40516
|
+
"ui.trial.stab": "Knife",
|
|
40517
|
+
"ui.trial.fire": "Fire",
|
|
40518
|
+
"ui.trial.blast": "Blast",
|
|
40519
|
+
"ui.trial.shot.hint": "A 9 mm round from 2.5 m",
|
|
40520
|
+
"ui.trial.punch.hint": "A punch, up close",
|
|
40521
|
+
"ui.trial.stab.hint": "A knife blow, up close",
|
|
40522
|
+
"ui.trial.fire.hint": "A flamethrower burst from 1.6 m",
|
|
40523
|
+
"ui.trial.blast.hint": "A fragmentation grenade goes off just behind it (it reaches the neighbours too)",
|
|
40524
|
+
"act.test_table": "Test table: {group}, items {from}\u2013{to} of {total}",
|
|
40525
|
+
"act.trial.shot": "Trial: shooting {name}",
|
|
40526
|
+
"act.trial.punch": "Trial: punching {name}",
|
|
40527
|
+
"act.trial.stab": "Trial: stabbing {name}",
|
|
40528
|
+
"act.trial.fire": "Trial: setting fire to {name}",
|
|
40529
|
+
"act.trial.blast": "Trial: a blast beside {name}",
|
|
40530
|
+
"arena.test_table": "Test table",
|
|
40288
40531
|
"ui.clear_log": "Clear",
|
|
40289
40532
|
"ui.catalog": "Catalogue",
|
|
40290
40533
|
"ui.clear": "Clear",
|
|
@@ -40552,7 +40795,7 @@ void main(){
|
|
|
40552
40795
|
"hud.throw": "arremessar",
|
|
40553
40796
|
"hud.tilt": "inclinar (segurar)",
|
|
40554
40797
|
"hud.equip": "equipar {weapon}",
|
|
40555
|
-
"ui.help": "Clique: olhar \xB7 WASD: andar \xB7 Shift: correr \xB7 C: agachar \xB7 Espa\xE7o: pular \xB7 1-0 / roda: ferramenta \xB7 bot\xE3o esquerdo: usar \xB7 E: pegar/soltar \xB7 G ou bot\xE3o direito: arremessar \xB7 R: recarregar \xB7 Q: modo de tiro \xB7 X: destravar \xB7 B: trava \xB7 Y: detonar cargas postas \xB7 Tab: pain\xE9is \xB7 Esc: soltar o mouse",
|
|
40798
|
+
"ui.help": "Clique: olhar \xB7 WASD: andar \xB7 Shift: correr \xB7 C: agachar \xB7 Espa\xE7o: pular \xB7 1-0 / roda: ferramenta \xB7 bot\xE3o esquerdo: usar (m\xE3o livre: soco) \xB7 E: pegar/soltar \xB7 G ou bot\xE3o direito: arremessar \xB7 R: recarregar \xB7 Q: modo de tiro \xB7 X: destravar \xB7 B: trava \xB7 Y: detonar cargas postas \xB7 Tab: pain\xE9is \xB7 Esc: soltar o mouse",
|
|
40556
40799
|
"ui.help.camera": "V: 1\xAA/3\xAA pessoa (Shift+V: chute)",
|
|
40557
40800
|
"camera.first": "1\xAA pessoa",
|
|
40558
40801
|
"camera.third": "3\xAA pessoa",
|
|
@@ -40566,6 +40809,32 @@ void main(){
|
|
|
40566
40809
|
"ui.copy_json": "Copiar JSON",
|
|
40567
40810
|
"ui.download_ndjson": "Baixar NDJSON",
|
|
40568
40811
|
"ui.download_txt": "Baixar TXT",
|
|
40812
|
+
"ui.test_table": "Mesa de testes",
|
|
40813
|
+
"ui.test_table.group": "Grupo para p\xF4r na mesa",
|
|
40814
|
+
"ui.test_table.lay": "P\xF4r na mesa",
|
|
40815
|
+
"ui.test_table.prev": "Itens anteriores",
|
|
40816
|
+
"ui.test_table.next": "Pr\xF3ximos itens",
|
|
40817
|
+
"ui.test_table.relay": "Repor",
|
|
40818
|
+
"ui.test_table.search": "Buscar item para p\xF4r na mesa",
|
|
40819
|
+
"ui.test_table.hint": "Escolha um grupo e ponha na mesa; depois escolha um item e teste.",
|
|
40820
|
+
"ui.test_table.page": "Itens {from}\u2013{to} de {total}. Escolha um e um teste:",
|
|
40821
|
+
"ui.trial.shot": "Tiro",
|
|
40822
|
+
"ui.trial.punch": "Soco",
|
|
40823
|
+
"ui.trial.stab": "Faca",
|
|
40824
|
+
"ui.trial.fire": "Fogo",
|
|
40825
|
+
"ui.trial.blast": "Explos\xE3o",
|
|
40826
|
+
"ui.trial.shot.hint": "Um tiro de 9 mm a 2,5 m",
|
|
40827
|
+
"ui.trial.punch.hint": "Um soco, de perto",
|
|
40828
|
+
"ui.trial.stab.hint": "Um golpe de faca, de perto",
|
|
40829
|
+
"ui.trial.fire.hint": "Uma rajada de lan\xE7a-chamas a 1,6 m",
|
|
40830
|
+
"ui.trial.blast.hint": "Uma granada de fragmenta\xE7\xE3o explode logo atr\xE1s dele (pega os vizinhos tamb\xE9m)",
|
|
40831
|
+
"act.test_table": "Mesa de testes: {group}, itens {from}\u2013{to} de {total}",
|
|
40832
|
+
"act.trial.shot": "Teste: tiro em {name}",
|
|
40833
|
+
"act.trial.punch": "Teste: soco em {name}",
|
|
40834
|
+
"act.trial.stab": "Teste: faca em {name}",
|
|
40835
|
+
"act.trial.fire": "Teste: fogo em {name}",
|
|
40836
|
+
"act.trial.blast": "Teste: explos\xE3o ao lado de {name}",
|
|
40837
|
+
"arena.test_table": "Mesa de testes",
|
|
40569
40838
|
"ui.clear_log": "Limpar",
|
|
40570
40839
|
"ui.catalog": "Cat\xE1logo",
|
|
40571
40840
|
"ui.clear": "Limpar",
|
|
@@ -40907,6 +41176,122 @@ void main(){
|
|
|
40907
41176
|
};
|
|
40908
41177
|
}
|
|
40909
41178
|
|
|
41179
|
+
// src/test-table-panel.ts
|
|
41180
|
+
var TEST_TABLE_CSS = `
|
|
41181
|
+
.tr-table{flex:0 0 auto;max-height:42%}
|
|
41182
|
+
.tr-table .sub{color:#9aa3b2;margin:4px 0}
|
|
41183
|
+
.tr-table .list{display:flex;flex-wrap:wrap;gap:3px}
|
|
41184
|
+
.tr-table .list .tr-btn{max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
41185
|
+
.tr-table select{flex:1;min-width:0;max-width:55%}
|
|
41186
|
+
.tr-table .tr-search{margin:6px 0 0}
|
|
41187
|
+
.tr-root .tr-table button.tr-btn.on{background:#3b3a14;border-color:#ffd23f;color:#fff}
|
|
41188
|
+
`;
|
|
41189
|
+
var mk2 = (tag, cls, text) => {
|
|
41190
|
+
const e = document.createElement(tag);
|
|
41191
|
+
if (cls) e.className = cls;
|
|
41192
|
+
if (text !== void 0) e.textContent = text;
|
|
41193
|
+
return e;
|
|
41194
|
+
};
|
|
41195
|
+
function createTestTablePanel(table, t) {
|
|
41196
|
+
const element = mk2("div", "tr-panel tr-table");
|
|
41197
|
+
element.appendChild(mk2("h3", void 0, t("ui.test_table")));
|
|
41198
|
+
const head = mk2("div", "tr-row");
|
|
41199
|
+
const select = mk2("select");
|
|
41200
|
+
select.setAttribute("aria-label", t("ui.test_table.group"));
|
|
41201
|
+
const lay = mk2("button", "tr-btn", t("ui.test_table.lay"));
|
|
41202
|
+
const prev = mk2("button", "tr-btn", "\u25C0");
|
|
41203
|
+
const next = mk2("button", "tr-btn", "\u25B6");
|
|
41204
|
+
prev.title = t("ui.test_table.prev");
|
|
41205
|
+
next.title = t("ui.test_table.next");
|
|
41206
|
+
for (const b of [lay, prev, next]) b.type = "button";
|
|
41207
|
+
head.append(select, lay, prev, next);
|
|
41208
|
+
const search = mk2("input", "tr-search");
|
|
41209
|
+
search.type = "search";
|
|
41210
|
+
search.placeholder = t("ui.test_table.search");
|
|
41211
|
+
const found = mk2("div", "list");
|
|
41212
|
+
const sub4 = mk2("div", "sub", t("ui.test_table.hint"));
|
|
41213
|
+
const list = mk2("div", "list");
|
|
41214
|
+
const trials = mk2("div", "tr-row");
|
|
41215
|
+
const trialButtons = TRIAL_KINDS.map((kind) => {
|
|
41216
|
+
const b = mk2("button", "tr-btn", t(`ui.trial.${kind}`));
|
|
41217
|
+
b.type = "button";
|
|
41218
|
+
b.dataset.trial = kind;
|
|
41219
|
+
b.title = t(`ui.trial.${kind}.hint`);
|
|
41220
|
+
b.onclick = () => table.trial(kind);
|
|
41221
|
+
trials.appendChild(b);
|
|
41222
|
+
return b;
|
|
41223
|
+
});
|
|
41224
|
+
const relay = mk2("button", "tr-btn", t("ui.test_table.relay"));
|
|
41225
|
+
relay.type = "button";
|
|
41226
|
+
relay.onclick = () => table.relay();
|
|
41227
|
+
trials.appendChild(relay);
|
|
41228
|
+
element.append(head, search, found, sub4, list, trials);
|
|
41229
|
+
const groups = () => {
|
|
41230
|
+
const keep = select.value;
|
|
41231
|
+
select.textContent = "";
|
|
41232
|
+
for (const group of table.groups()) {
|
|
41233
|
+
const op = mk2("option", void 0, group);
|
|
41234
|
+
op.value = group;
|
|
41235
|
+
select.appendChild(op);
|
|
41236
|
+
}
|
|
41237
|
+
if ([...select.options].some((o) => o.value === keep)) select.value = keep;
|
|
41238
|
+
};
|
|
41239
|
+
const render = (page) => {
|
|
41240
|
+
list.textContent = "";
|
|
41241
|
+
prev.disabled = !page?.hasPrev;
|
|
41242
|
+
next.disabled = !page?.hasNext;
|
|
41243
|
+
for (const b of [...trialButtons, relay]) b.disabled = !page?.selected;
|
|
41244
|
+
if (!page) {
|
|
41245
|
+
sub4.textContent = t("ui.test_table.hint");
|
|
41246
|
+
return;
|
|
41247
|
+
}
|
|
41248
|
+
if (select.value !== page.group) select.value = page.group;
|
|
41249
|
+
sub4.textContent = t("ui.test_table.page", { from: page.first + 1, to: page.first + page.objects.length, total: page.total });
|
|
41250
|
+
for (const o of page.objects) {
|
|
41251
|
+
const b = mk2("button", o.objectId === page.selected ? "tr-btn on" : "tr-btn", o.name);
|
|
41252
|
+
b.type = "button";
|
|
41253
|
+
b.title = o.itemId;
|
|
41254
|
+
b.onclick = () => table.select(o.objectId);
|
|
41255
|
+
list.appendChild(b);
|
|
41256
|
+
}
|
|
41257
|
+
};
|
|
41258
|
+
const fold2 = (text) => text.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
|
|
41259
|
+
search.oninput = () => {
|
|
41260
|
+
found.textContent = "";
|
|
41261
|
+
const term = fold2(search.value.trim());
|
|
41262
|
+
if (!term) return;
|
|
41263
|
+
const hits = table.items().filter((item) => fold2(`${item.name} ${item.id}`).includes(term)).slice(0, 8);
|
|
41264
|
+
for (const item of hits) {
|
|
41265
|
+
const b = mk2("button", "tr-btn", item.name);
|
|
41266
|
+
b.type = "button";
|
|
41267
|
+
b.title = `${item.group} \xB7 ${item.id}`;
|
|
41268
|
+
b.onclick = () => {
|
|
41269
|
+
search.value = "";
|
|
41270
|
+
found.textContent = "";
|
|
41271
|
+
table.find(item.id);
|
|
41272
|
+
};
|
|
41273
|
+
found.appendChild(b);
|
|
41274
|
+
}
|
|
41275
|
+
if (!hits.length) found.appendChild(mk2("div", "sub", t("ui.no_results")));
|
|
41276
|
+
};
|
|
41277
|
+
lay.onclick = () => table.open(select.value);
|
|
41278
|
+
prev.onclick = () => table.prev();
|
|
41279
|
+
next.onclick = () => table.next();
|
|
41280
|
+
select.onchange = () => table.open(select.value);
|
|
41281
|
+
for (const box2 of [select, search]) box2.addEventListener("keydown", (e) => e.stopPropagation());
|
|
41282
|
+
const off = table.on(render);
|
|
41283
|
+
groups();
|
|
41284
|
+
render(table.state());
|
|
41285
|
+
return {
|
|
41286
|
+
element,
|
|
41287
|
+
refresh: () => {
|
|
41288
|
+
groups();
|
|
41289
|
+
render(table.state());
|
|
41290
|
+
},
|
|
41291
|
+
dispose: off
|
|
41292
|
+
};
|
|
41293
|
+
}
|
|
41294
|
+
|
|
40910
41295
|
// src/ui.ts
|
|
40911
41296
|
var CSS = `
|
|
40912
41297
|
.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}
|
|
@@ -40985,7 +41370,7 @@ void main(){
|
|
|
40985
41370
|
@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}}
|
|
40986
41371
|
`;
|
|
40987
41372
|
var fold = (text) => text.normalize("NFD").replace(/[\u0300-\u036f]/g, "").toLowerCase();
|
|
40988
|
-
var
|
|
41373
|
+
var mk3 = (tag, cls, text) => {
|
|
40989
41374
|
const e = document.createElement(tag);
|
|
40990
41375
|
if (cls) e.className = cls;
|
|
40991
41376
|
if (text !== void 0) e.textContent = text;
|
|
@@ -40996,7 +41381,7 @@ void main(){
|
|
|
40996
41381
|
return source.replace(/["\\\n\r]/g, (char) => encodeURIComponent(char));
|
|
40997
41382
|
};
|
|
40998
41383
|
var thumb = (thumbnail, credit) => {
|
|
40999
|
-
const e =
|
|
41384
|
+
const e = mk3("span", "tr-thumb");
|
|
41000
41385
|
if (credit) e.title = credit;
|
|
41001
41386
|
else e.setAttribute("aria-hidden", "true");
|
|
41002
41387
|
e.style.setProperty("--tr-thumb", `url("${thumbSource(thumbnail)}")`);
|
|
@@ -41017,42 +41402,43 @@ void main(){
|
|
|
41017
41402
|
}
|
|
41018
41403
|
function createUi(o) {
|
|
41019
41404
|
const { t } = o;
|
|
41020
|
-
const style =
|
|
41021
|
-
style.textContent = CSS;
|
|
41022
|
-
const root =
|
|
41405
|
+
const style = mk3("style");
|
|
41406
|
+
style.textContent = CSS + (o.testTable ? TEST_TABLE_CSS : "");
|
|
41407
|
+
const root = mk3("div", "tr-root");
|
|
41023
41408
|
const pos = getComputedStyle?.(o.container)?.position;
|
|
41024
41409
|
if (!pos || pos === "static") o.container.style.position = "relative";
|
|
41025
41410
|
root.appendChild(style);
|
|
41026
41411
|
o.container.appendChild(root);
|
|
41027
|
-
const cross5 =
|
|
41028
|
-
cross5.appendChild(
|
|
41029
|
-
const top =
|
|
41030
|
-
const toolBox =
|
|
41031
|
-
const status =
|
|
41032
|
-
const prog =
|
|
41033
|
-
const progLabel =
|
|
41034
|
-
const progBar =
|
|
41035
|
-
const progFill =
|
|
41412
|
+
const cross5 = mk3("div", "tr-cross");
|
|
41413
|
+
cross5.appendChild(mk3("i"));
|
|
41414
|
+
const top = mk3("div", "tr-top");
|
|
41415
|
+
const toolBox = mk3("div", "tr-tool");
|
|
41416
|
+
const status = mk3("div", "tr-status");
|
|
41417
|
+
const prog = mk3("div", "tr-prog");
|
|
41418
|
+
const progLabel = mk3("b");
|
|
41419
|
+
const progBar = mk3("div", "tr-bar");
|
|
41420
|
+
const progFill = mk3("i");
|
|
41036
41421
|
progBar.appendChild(progFill);
|
|
41037
41422
|
prog.append(progLabel, progBar);
|
|
41038
41423
|
top.append(toolBox, status, prog);
|
|
41039
|
-
const bottom =
|
|
41040
|
-
const held =
|
|
41041
|
-
const hotbar =
|
|
41424
|
+
const bottom = mk3("div", "tr-bottom");
|
|
41425
|
+
const held = mk3("div", "tr-held");
|
|
41426
|
+
const hotbar = mk3("div", "tr-hotbar");
|
|
41042
41427
|
bottom.append(held, hotbar);
|
|
41043
|
-
const help =
|
|
41428
|
+
const help = mk3("div", "tr-help");
|
|
41044
41429
|
help.textContent = t("ui.help");
|
|
41045
|
-
const left =
|
|
41046
|
-
const insp =
|
|
41047
|
-
const logPanel =
|
|
41048
|
-
|
|
41049
|
-
|
|
41050
|
-
const
|
|
41051
|
-
const
|
|
41430
|
+
const left = mk3("div", "tr-left");
|
|
41431
|
+
const insp = mk3("div", "tr-panel tr-insp");
|
|
41432
|
+
const logPanel = mk3("div", "tr-panel tr-log");
|
|
41433
|
+
const tablePanel = o.testTable ? createTestTablePanel(o.testTable, t) : null;
|
|
41434
|
+
left.append(insp, ...tablePanel ? [tablePanel.element] : [], logPanel);
|
|
41435
|
+
const logTitle = mk3("h3", void 0, t("ui.log"));
|
|
41436
|
+
const logBar = mk3("div", "tr-logbar");
|
|
41437
|
+
const logBody = mk3("div", "tr-logbody");
|
|
41052
41438
|
const filters = { action: true, event: true, check: true };
|
|
41053
41439
|
const filterBtns = [];
|
|
41054
41440
|
for (const ch of ["action", "event", "check"]) {
|
|
41055
|
-
const b =
|
|
41441
|
+
const b = mk3("button", "tr-btn on", t(`ui.filter.${ch}`));
|
|
41056
41442
|
b.type = "button";
|
|
41057
41443
|
b.onclick = () => {
|
|
41058
41444
|
filters[ch] = !filters[ch];
|
|
@@ -41063,8 +41449,8 @@ void main(){
|
|
|
41063
41449
|
logBar.appendChild(b);
|
|
41064
41450
|
}
|
|
41065
41451
|
let autoscroll = true;
|
|
41066
|
-
const asLabel =
|
|
41067
|
-
const asBox =
|
|
41452
|
+
const asLabel = mk3("label");
|
|
41453
|
+
const asBox = mk3("input");
|
|
41068
41454
|
asBox.type = "checkbox";
|
|
41069
41455
|
asBox.checked = true;
|
|
41070
41456
|
asBox.onchange = () => {
|
|
@@ -41073,7 +41459,7 @@ void main(){
|
|
|
41073
41459
|
asLabel.append(asBox, document.createTextNode(" " + t("ui.autoscroll")));
|
|
41074
41460
|
logBar.appendChild(asLabel);
|
|
41075
41461
|
const logBtn = (label, fn) => {
|
|
41076
|
-
const b =
|
|
41462
|
+
const b = mk3("button", "tr-btn", label);
|
|
41077
41463
|
b.type = "button";
|
|
41078
41464
|
b.onclick = fn;
|
|
41079
41465
|
logBar.appendChild(b);
|
|
@@ -41081,7 +41467,7 @@ void main(){
|
|
|
41081
41467
|
logBtn(t("ui.copy_json"), () => {
|
|
41082
41468
|
const text = o.log.toJSON();
|
|
41083
41469
|
const select = () => {
|
|
41084
|
-
const ta =
|
|
41470
|
+
const ta = mk3("textarea");
|
|
41085
41471
|
ta.value = text;
|
|
41086
41472
|
ta.setAttribute("readonly", "");
|
|
41087
41473
|
ta.style.cssText = "position:fixed;left:8px;bottom:8px;width:min(480px,80vw);height:120px;z-index:99999";
|
|
@@ -41115,12 +41501,12 @@ void main(){
|
|
|
41115
41501
|
const fill = (n, e) => {
|
|
41116
41502
|
n.className = lineClass(e);
|
|
41117
41503
|
n.textContent = "";
|
|
41118
|
-
const tm =
|
|
41504
|
+
const tm = mk3("time", void 0, fmtTime(e.t));
|
|
41119
41505
|
n.append(tm, document.createTextNode(e.text));
|
|
41120
41506
|
};
|
|
41121
41507
|
const addNode = (e) => {
|
|
41122
41508
|
if (!filters[e.channel]) return;
|
|
41123
|
-
const n =
|
|
41509
|
+
const n = mk3("div");
|
|
41124
41510
|
fill(n, e);
|
|
41125
41511
|
nodes.set(e.seq, n);
|
|
41126
41512
|
logBody.appendChild(n);
|
|
@@ -41142,18 +41528,18 @@ void main(){
|
|
|
41142
41528
|
if (n) fill(n, e);
|
|
41143
41529
|
} else addNode(e);
|
|
41144
41530
|
});
|
|
41145
|
-
const right =
|
|
41146
|
-
const cat =
|
|
41147
|
-
const anim =
|
|
41148
|
-
const tests =
|
|
41531
|
+
const right = mk3("div", "tr-right");
|
|
41532
|
+
const cat = mk3("div", "tr-panel tr-cat");
|
|
41533
|
+
const anim = mk3("div", "tr-panel tr-anim");
|
|
41534
|
+
const tests = mk3("div", "tr-panel tr-tests");
|
|
41149
41535
|
const benchPanel = createBenchPanel({ t, num: (n, d) => num(n, d), onControl: o.onControl, onLayer: o.onLayer, onAction: o.onAction });
|
|
41150
41536
|
right.append(cat, anim, benchPanel.element, tests);
|
|
41151
|
-
cat.appendChild(
|
|
41152
|
-
const itemSearch =
|
|
41537
|
+
cat.appendChild(mk3("h3", void 0, t("ui.catalog")));
|
|
41538
|
+
const itemSearch = mk3("input", "tr-search");
|
|
41153
41539
|
itemSearch.type = "search";
|
|
41154
41540
|
itemSearch.placeholder = t("ui.search_items");
|
|
41155
|
-
const tabs =
|
|
41156
|
-
const items =
|
|
41541
|
+
const tabs = mk3("div", "tr-tabs");
|
|
41542
|
+
const items = mk3("div", "tr-items");
|
|
41157
41543
|
cat.append(itemSearch, tabs, items);
|
|
41158
41544
|
let activeGroup = "";
|
|
41159
41545
|
const visibleItems = () => {
|
|
@@ -41165,18 +41551,18 @@ void main(){
|
|
|
41165
41551
|
items.textContent = "";
|
|
41166
41552
|
const searching = itemSearch.value.trim() !== "";
|
|
41167
41553
|
const found = visibleItems();
|
|
41168
|
-
if (!found.length) items.appendChild(
|
|
41554
|
+
if (!found.length) items.appendChild(mk3("div", "tr-empty", t("ui.no_results")));
|
|
41169
41555
|
for (const { item: it, group } of found) {
|
|
41170
|
-
const wrap =
|
|
41171
|
-
const b =
|
|
41172
|
-
if (it.thumbnail) b.append(thumb(it.thumbnail),
|
|
41173
|
-
if (searching) b.appendChild(
|
|
41556
|
+
const wrap = mk3("div", "tr-item");
|
|
41557
|
+
const b = mk3("button", it.thumbnail ? "tr-btn has-thumb" : "tr-btn", it.thumbnail ? void 0 : it.name);
|
|
41558
|
+
if (it.thumbnail) b.append(thumb(it.thumbnail), mk3("span", "tr-name", it.name));
|
|
41559
|
+
if (searching) b.appendChild(mk3("small", void 0, group));
|
|
41174
41560
|
b.type = "button";
|
|
41175
41561
|
b.title = it.note ?? it.name;
|
|
41176
41562
|
b.onclick = () => o.onSpawn(it.id);
|
|
41177
41563
|
wrap.appendChild(b);
|
|
41178
41564
|
if (it.weapon) {
|
|
41179
|
-
const w =
|
|
41565
|
+
const w = mk3("button", "tr-btn", "\u2694");
|
|
41180
41566
|
w.type = "button";
|
|
41181
41567
|
w.title = t("ui.equip");
|
|
41182
41568
|
w.onclick = () => o.onEquip(it.id);
|
|
@@ -41190,7 +41576,7 @@ void main(){
|
|
|
41190
41576
|
const groupNames = [...o.groups().keys()];
|
|
41191
41577
|
if (!groupNames.includes(activeGroup)) activeGroup = groupNames[0] ?? "";
|
|
41192
41578
|
for (const g of groupNames) {
|
|
41193
|
-
const b =
|
|
41579
|
+
const b = mk3("button", g === activeGroup ? "tr-btn on" : "tr-btn", g);
|
|
41194
41580
|
b.type = "button";
|
|
41195
41581
|
b.onclick = () => {
|
|
41196
41582
|
activeGroup = g;
|
|
@@ -41203,9 +41589,9 @@ void main(){
|
|
|
41203
41589
|
renderTabs();
|
|
41204
41590
|
itemSearch.oninput = renderItems;
|
|
41205
41591
|
for (const box2 of [itemSearch]) box2.addEventListener("keydown", (e) => e.stopPropagation());
|
|
41206
|
-
const controls =
|
|
41592
|
+
const controls = mk3("div", "tr-row");
|
|
41207
41593
|
const ctl = (label, fn) => {
|
|
41208
|
-
const b =
|
|
41594
|
+
const b = mk3("button", "tr-btn", label);
|
|
41209
41595
|
b.type = "button";
|
|
41210
41596
|
b.onclick = () => fn(b);
|
|
41211
41597
|
controls.appendChild(b);
|
|
@@ -41230,29 +41616,29 @@ void main(){
|
|
|
41230
41616
|
fullscreenBtn.title = t("ui.fullscreen.hint");
|
|
41231
41617
|
const blastBtns = ["shelter", "fragmentHits", "confinement"].map((key) => ({ key, b: ctl(t(`ui.blast.${key}`), () => o.onBlast(key)) }));
|
|
41232
41618
|
for (const { key, b } of blastBtns) b.title = t(`ui.blast.${key}.hint`);
|
|
41233
|
-
const qSel =
|
|
41619
|
+
const qSel = mk3("select");
|
|
41234
41620
|
for (const q of ["low", "medium", "high"]) {
|
|
41235
|
-
const op =
|
|
41621
|
+
const op = mk3("option", void 0, t(`ui.quality.${q}`));
|
|
41236
41622
|
op.value = q;
|
|
41237
41623
|
qSel.appendChild(op);
|
|
41238
41624
|
}
|
|
41239
41625
|
qSel.value = o.quality;
|
|
41240
41626
|
qSel.onchange = () => o.onQuality(qSel.value);
|
|
41241
|
-
const qLabel =
|
|
41627
|
+
const qLabel = mk3("label", void 0, t("ui.quality") + " ");
|
|
41242
41628
|
qLabel.appendChild(qSel);
|
|
41243
41629
|
controls.appendChild(qLabel);
|
|
41244
|
-
const count =
|
|
41630
|
+
const count = mk3("span");
|
|
41245
41631
|
controls.appendChild(count);
|
|
41246
41632
|
cat.appendChild(controls);
|
|
41247
|
-
tests.appendChild(
|
|
41248
|
-
const runAll =
|
|
41633
|
+
tests.appendChild(mk3("h3", void 0, t("ui.tests")));
|
|
41634
|
+
const runAll = mk3("button", "tr-btn", t("ui.run_all"));
|
|
41249
41635
|
runAll.type = "button";
|
|
41250
|
-
const summary =
|
|
41251
|
-
const testSearch =
|
|
41636
|
+
const summary = mk3("div", "tr-summary");
|
|
41637
|
+
const testSearch = mk3("input", "tr-search");
|
|
41252
41638
|
testSearch.type = "search";
|
|
41253
41639
|
testSearch.placeholder = t("ui.search_tests");
|
|
41254
41640
|
testSearch.addEventListener("keydown", (e) => e.stopPropagation());
|
|
41255
|
-
const list =
|
|
41641
|
+
const list = mk3("div", "list");
|
|
41256
41642
|
tests.append(runAll, summary, testSearch, list);
|
|
41257
41643
|
testSearch.oninput = () => {
|
|
41258
41644
|
const term = fold(testSearch.value.trim());
|
|
@@ -41275,7 +41661,7 @@ void main(){
|
|
|
41275
41661
|
list.textContent = "";
|
|
41276
41662
|
scBtns.clear();
|
|
41277
41663
|
for (const s of o.scenarios()) {
|
|
41278
|
-
const b =
|
|
41664
|
+
const b = mk3("button", "tr-btn", s.name);
|
|
41279
41665
|
b.type = "button";
|
|
41280
41666
|
b.onclick = async () => {
|
|
41281
41667
|
if (busy) return;
|
|
@@ -41301,7 +41687,7 @@ void main(){
|
|
|
41301
41687
|
busy = false;
|
|
41302
41688
|
}
|
|
41303
41689
|
};
|
|
41304
|
-
const toggle =
|
|
41690
|
+
const toggle = mk3("button", "tr-btn tr-toggle", t("ui.panel"));
|
|
41305
41691
|
toggle.type = "button";
|
|
41306
41692
|
const togglePanels = () => {
|
|
41307
41693
|
root.classList.toggle("open");
|
|
@@ -41310,15 +41696,15 @@ void main(){
|
|
|
41310
41696
|
toggle.onclick = togglePanels;
|
|
41311
41697
|
root.append(cross5, top, bottom, help, left, right, toggle);
|
|
41312
41698
|
const bar2 = (value, max2, cls) => {
|
|
41313
|
-
const b =
|
|
41314
|
-
const i =
|
|
41699
|
+
const b = mk3("div", `tr-bar ${cls}`);
|
|
41700
|
+
const i = mk3("i");
|
|
41315
41701
|
i.style.width = `${Math.max(0, Math.min(100, value / Math.max(1e-9, max2) * 100))}%`;
|
|
41316
41702
|
b.appendChild(i);
|
|
41317
41703
|
return b;
|
|
41318
41704
|
};
|
|
41319
41705
|
const row = (k, v) => {
|
|
41320
|
-
const r =
|
|
41321
|
-
r.append(
|
|
41706
|
+
const r = mk3("div", "r");
|
|
41707
|
+
r.append(mk3("span", void 0, k), mk3("span", void 0, v));
|
|
41322
41708
|
return r;
|
|
41323
41709
|
};
|
|
41324
41710
|
const num = (n, d = 1) => {
|
|
@@ -41327,13 +41713,13 @@ void main(){
|
|
|
41327
41713
|
};
|
|
41328
41714
|
const renderInspector = (i) => {
|
|
41329
41715
|
insp.textContent = "";
|
|
41330
|
-
insp.appendChild(
|
|
41716
|
+
insp.appendChild(mk3("h3", void 0, t("ui.inspector")));
|
|
41331
41717
|
if (!i) {
|
|
41332
|
-
insp.appendChild(
|
|
41718
|
+
insp.appendChild(mk3("div", void 0, t("ui.aim_at_something")));
|
|
41333
41719
|
return;
|
|
41334
41720
|
}
|
|
41335
41721
|
if (i.thumbnail) insp.appendChild(thumb(i.thumbnail, i.thumbnailCredit));
|
|
41336
|
-
insp.appendChild(
|
|
41722
|
+
insp.appendChild(mk3("div", "t", `${i.object} \u203A ${i.part}`));
|
|
41337
41723
|
insp.appendChild(row(t("ins.material"), i.material));
|
|
41338
41724
|
insp.appendChild(row(t("ins.status"), i.status + (i.burning ? ` \xB7 ${t("ins.burning")}` : "")));
|
|
41339
41725
|
insp.appendChild(row(t("ins.hp"), `${num(i.hp, 0)} / ${num(i.maxHp, 0)}`));
|
|
@@ -41352,30 +41738,30 @@ void main(){
|
|
|
41352
41738
|
insp.appendChild(bar2(Math.min(i.stress, 1.2), 1.2, i.stress > 1 ? "bad" : i.stress > 0.7 ? "warn" : "hp"));
|
|
41353
41739
|
}
|
|
41354
41740
|
if (i.joints.length) {
|
|
41355
|
-
insp.appendChild(
|
|
41741
|
+
insp.appendChild(mk3("div", void 0, t("ins.joints") + ":"));
|
|
41356
41742
|
for (const j of i.joints) {
|
|
41357
41743
|
const tools = j.tools.length ? j.tools.join(", ") : t("tool.none");
|
|
41358
|
-
insp.appendChild(
|
|
41744
|
+
insp.appendChild(mk3("div", void 0, `\u2022 ${j.kind} (${j.id})${j.open ? ` \xB7 ${t("ins.open")}` : ` \u2192 ${tools}`}`));
|
|
41359
41745
|
}
|
|
41360
41746
|
}
|
|
41361
|
-
if (i.note) insp.appendChild(
|
|
41747
|
+
if (i.note) insp.appendChild(mk3("div", void 0, i.note));
|
|
41362
41748
|
};
|
|
41363
41749
|
let lastAnim = "";
|
|
41364
41750
|
const renderAnimations = (a) => {
|
|
41365
41751
|
anim.textContent = "";
|
|
41366
|
-
anim.appendChild(
|
|
41752
|
+
anim.appendChild(mk3("h3", void 0, t("ui.animations")));
|
|
41367
41753
|
if (!a) {
|
|
41368
|
-
anim.appendChild(
|
|
41754
|
+
anim.appendChild(mk3("div", "sub", t("ui.animations_aim")));
|
|
41369
41755
|
return;
|
|
41370
41756
|
}
|
|
41371
|
-
anim.appendChild(
|
|
41757
|
+
anim.appendChild(mk3("div", "sub", a.name));
|
|
41372
41758
|
if (a.clips.length === 0) {
|
|
41373
|
-
anim.appendChild(
|
|
41759
|
+
anim.appendChild(mk3("div", "sub", t("ui.animations_none")));
|
|
41374
41760
|
return;
|
|
41375
41761
|
}
|
|
41376
|
-
const l =
|
|
41762
|
+
const l = mk3("div", "list");
|
|
41377
41763
|
for (const c of a.clips) {
|
|
41378
|
-
const b =
|
|
41764
|
+
const b = mk3("button", a.playing === c.id ? "tr-btn on" : "tr-btn", `\u25B6 ${t(`anim.${c.id}`) === `anim.${c.id}` ? c.id : t(`anim.${c.id}`)} (${num(c.duration, 1)} s)`);
|
|
41379
41765
|
b.type = "button";
|
|
41380
41766
|
b.onclick = () => o.onPlay(a.objectId, c.id);
|
|
41381
41767
|
l.appendChild(b);
|
|
@@ -41392,8 +41778,8 @@ void main(){
|
|
|
41392
41778
|
hud(s) {
|
|
41393
41779
|
toolBox.textContent = "";
|
|
41394
41780
|
toolBox.append(document.createTextNode(s.toolName));
|
|
41395
|
-
if (s.ammo) toolBox.append(
|
|
41396
|
-
if (s.camera) toolBox.append(
|
|
41781
|
+
if (s.ammo) toolBox.append(mk3("small", void 0, s.ammo));
|
|
41782
|
+
if (s.camera) toolBox.append(mk3("small", void 0, t(`camera.${s.camera}`)));
|
|
41397
41783
|
const helpText = s.camera ? `${t("ui.help")} \xB7 ${t("ui.help.camera")}` : t("ui.help");
|
|
41398
41784
|
if (help.textContent !== helpText) help.textContent = helpText;
|
|
41399
41785
|
if (s.status) status.textContent = s.status;
|
|
@@ -41416,7 +41802,7 @@ void main(){
|
|
|
41416
41802
|
];
|
|
41417
41803
|
if (s.held.weapon) keys.push(["H", t("hud.equip", { weapon: s.held.weapon })]);
|
|
41418
41804
|
for (const [k, label] of keys) {
|
|
41419
|
-
held.append(
|
|
41805
|
+
held.append(mk3("kbd", void 0, k), document.createTextNode(` ${label} `));
|
|
41420
41806
|
}
|
|
41421
41807
|
} else held.style.display = "none";
|
|
41422
41808
|
const key = s.slots.join("|") + s.page;
|
|
@@ -41426,9 +41812,9 @@ void main(){
|
|
|
41426
41812
|
slotsEl.length = 0;
|
|
41427
41813
|
hudPage = s.page - 1;
|
|
41428
41814
|
s.slots.forEach((name, i) => {
|
|
41429
|
-
const b =
|
|
41815
|
+
const b = mk3("button", "tr-slot");
|
|
41430
41816
|
b.type = "button";
|
|
41431
|
-
b.append(
|
|
41817
|
+
b.append(mk3("b", void 0, String((i + 1) % 10)), document.createTextNode(name));
|
|
41432
41818
|
b.title = name;
|
|
41433
41819
|
b.onclick = () => o.onTool(hudPage * 10 + i);
|
|
41434
41820
|
hotbar.appendChild(b);
|
|
@@ -41460,6 +41846,7 @@ void main(){
|
|
|
41460
41846
|
refresh() {
|
|
41461
41847
|
renderTabs();
|
|
41462
41848
|
renderScenarios();
|
|
41849
|
+
tablePanel?.refresh();
|
|
41463
41850
|
},
|
|
41464
41851
|
showGroup(group) {
|
|
41465
41852
|
activeGroup = group;
|
|
@@ -41474,6 +41861,7 @@ void main(){
|
|
|
41474
41861
|
},
|
|
41475
41862
|
dispose() {
|
|
41476
41863
|
offLog();
|
|
41864
|
+
tablePanel?.dispose();
|
|
41477
41865
|
if (toastTimer) clearTimeout(toastTimer);
|
|
41478
41866
|
root.remove();
|
|
41479
41867
|
}
|
|
@@ -42663,6 +43051,10 @@ void main(){
|
|
|
42663
43051
|
heldPiece = null;
|
|
42664
43052
|
heldTilt = 0;
|
|
42665
43053
|
};
|
|
43054
|
+
const handUse = () => {
|
|
43055
|
+
if (heldPiece) grab();
|
|
43056
|
+
else useTool(FIXED_DT, false);
|
|
43057
|
+
};
|
|
42666
43058
|
const grab = () => {
|
|
42667
43059
|
if (heldPiece) {
|
|
42668
43060
|
const name = heldName;
|
|
@@ -43131,10 +43523,10 @@ void main(){
|
|
|
43131
43523
|
if (tiltKey || heldTilt > 0) {
|
|
43132
43524
|
heldTilt = Math.max(0, Math.min(2.4, heldTilt + (tiltKey ? 2.2 : -3) * dt));
|
|
43133
43525
|
}
|
|
43134
|
-
if (useHeld && !paused) {
|
|
43526
|
+
if ((useHeld || useFirst) && !paused) {
|
|
43135
43527
|
if (tool.def.category === "hand") {
|
|
43136
|
-
if (useFirst)
|
|
43137
|
-
} else useTool(dt, !useFirst);
|
|
43528
|
+
if (useFirst) handUse();
|
|
43529
|
+
} else if (useHeld) useTool(dt, !useFirst);
|
|
43138
43530
|
useFirst = false;
|
|
43139
43531
|
}
|
|
43140
43532
|
if (!paused) followHeld(dt);
|
|
@@ -43689,6 +44081,63 @@ void main(){
|
|
|
43689
44081
|
const row = (list, gap, z) => list.map((item, index) => spawn2(item.id, [(index - (list.length - 1) / 2) * gap, 0, z], 0));
|
|
43690
44082
|
return [...row(front, 1.6, 1.5), ...row(back, 4.5, -4)];
|
|
43691
44083
|
};
|
|
44084
|
+
const catalogGroups = () => {
|
|
44085
|
+
const m = /* @__PURE__ */ new Map();
|
|
44086
|
+
for (const i of catalog) {
|
|
44087
|
+
const g = m.get(i.group) ?? [];
|
|
44088
|
+
g.push(i);
|
|
44089
|
+
m.set(i.group, g);
|
|
44090
|
+
}
|
|
44091
|
+
return m;
|
|
44092
|
+
};
|
|
44093
|
+
const tableOptions = options.testTable === false ? {} : options.testTable ?? {};
|
|
44094
|
+
const tableScenery = tableOptions.scenery ?? "test_table";
|
|
44095
|
+
const measured = /* @__PURE__ */ new Map();
|
|
44096
|
+
const testTable = createTestTable(
|
|
44097
|
+
{
|
|
44098
|
+
groups: catalogGroups,
|
|
44099
|
+
top: () => {
|
|
44100
|
+
const top = arena.tops.find((x) => x.id === tableScenery);
|
|
44101
|
+
return top && topOk(top) ? top : null;
|
|
44102
|
+
},
|
|
44103
|
+
measure: (itemId) => {
|
|
44104
|
+
let size = measured.get(itemId);
|
|
44105
|
+
if (!size) {
|
|
44106
|
+
const item = catalogMap.get(itemId);
|
|
44107
|
+
const box2 = item ? new THREE.Box3().setFromObject(buildRoot(item)) : null;
|
|
44108
|
+
size = box2 && !box2.isEmpty() ? { w: box2.max.x - box2.min.x, d: box2.max.z - box2.min.z } : { w: 0.3, d: 0.3 };
|
|
44109
|
+
measured.set(itemId, size);
|
|
44110
|
+
}
|
|
44111
|
+
return size;
|
|
44112
|
+
},
|
|
44113
|
+
spawn: (itemId, position) => spawn2(itemId, position, 0),
|
|
44114
|
+
clear,
|
|
44115
|
+
resetAll,
|
|
44116
|
+
box: (objectId) => {
|
|
44117
|
+
const e = entries.get(objectId);
|
|
44118
|
+
if (!e || !world.has(objectId)) return null;
|
|
44119
|
+
const box2 = new THREE.Box3().setFromObject(e.so.root);
|
|
44120
|
+
return box2.isEmpty() ? null : { min: [box2.min.x, box2.min.y, box2.min.z], max: [box2.max.x, box2.max.y, box2.max.z] };
|
|
44121
|
+
},
|
|
44122
|
+
equip: (weapon) => {
|
|
44123
|
+
if (!arsenal.weapons.has(weapon)) return false;
|
|
44124
|
+
if (heldEntry) releaseHeld();
|
|
44125
|
+
equip(weapon);
|
|
44126
|
+
room.waitUntilReady();
|
|
44127
|
+
return true;
|
|
44128
|
+
},
|
|
44129
|
+
look: (eye, target) => player.look(eye, target),
|
|
44130
|
+
use: (hold) => room.use(hold),
|
|
44131
|
+
blastAt: (weapon, point) => {
|
|
44132
|
+
const def = arsenal.weapons.get(weapon);
|
|
44133
|
+
const explosive = def?.action.kind === "throwable" ? def.action.explosive : def?.action.kind === "launcher" ? def.action.projectile : void 0;
|
|
44134
|
+
return explosive !== void 0 && explosives.blastAt(weapon, explosive, point);
|
|
44135
|
+
},
|
|
44136
|
+
log: (type, text, data) => action(type, text, { data }),
|
|
44137
|
+
translate: t
|
|
44138
|
+
},
|
|
44139
|
+
tableOptions
|
|
44140
|
+
);
|
|
43692
44141
|
const room = {
|
|
43693
44142
|
world,
|
|
43694
44143
|
renderer,
|
|
@@ -43708,6 +44157,7 @@ void main(){
|
|
|
43708
44157
|
get scenarios() {
|
|
43709
44158
|
return allScenarios();
|
|
43710
44159
|
},
|
|
44160
|
+
testTable,
|
|
43711
44161
|
spawn: spawn2,
|
|
43712
44162
|
clear,
|
|
43713
44163
|
resetAll,
|
|
@@ -43715,7 +44165,8 @@ void main(){
|
|
|
43715
44165
|
useItem,
|
|
43716
44166
|
use(hold = 0) {
|
|
43717
44167
|
if (tool.def.category === "hand") {
|
|
43718
|
-
|
|
44168
|
+
handUse();
|
|
44169
|
+
flush();
|
|
43719
44170
|
return;
|
|
43720
44171
|
}
|
|
43721
44172
|
useTool(FIXED_DT, false);
|
|
@@ -43948,7 +44399,8 @@ void main(){
|
|
|
43948
44399
|
},
|
|
43949
44400
|
onAction: (id) => {
|
|
43950
44401
|
if (activeBench) slowly(activeBench, id, () => benches.get(activeBench)?.act(id));
|
|
43951
|
-
}
|
|
44402
|
+
},
|
|
44403
|
+
...options.testTable !== false && arena.tops.some((x) => x.id === tableScenery) ? { testTable } : {}
|
|
43952
44404
|
});
|
|
43953
44405
|
ui.hud(hudState());
|
|
43954
44406
|
showBench();
|