@adatechnology/game-test-room 0.12.0 → 0.14.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 +20 -0
- package/dist/index.global.js +260 -47
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +166 -35
- package/dist/index.js.map +1 -1
- package/package.json +10 -10
package/dist/index.js
CHANGED
|
@@ -9,6 +9,9 @@ import { createGroundField, createVfx } from "@adatechnology/game-vfx";
|
|
|
9
9
|
import { createGroundPatch, createVfxRenderer } from "@adatechnology/game-vfx-three";
|
|
10
10
|
import { checkAnimation, checkBench, checkDestructible, checkSpec, createEnvironmentBus, shotExtras, useToHit } from "@adatechnology/game-contracts";
|
|
11
11
|
|
|
12
|
+
// src/arena.ts
|
|
13
|
+
import { markKitOrigin } from "@adatechnology/game-contracts";
|
|
14
|
+
|
|
12
15
|
// src/items.ts
|
|
13
16
|
var box = (id, material, size, position, extra = {}) => ({ id, material, shape: { kind: "box", size }, position, ...extra });
|
|
14
17
|
var joint = (a, b, kind, extra = {}) => ({ a, b, kind, ...extra });
|
|
@@ -110,7 +113,7 @@ function resolveScenery(scenery) {
|
|
|
110
113
|
return [...merged.entries()].map(([id, o]) => ({ id, spec: o.spec, position: o.position, rotationY: o.rotationY ?? 0, colors: o.colors, build: o.build, label: o.label, top: o.top, spout: o.spout }));
|
|
111
114
|
}
|
|
112
115
|
function createArena(THREE, size, scene, scenery, tapOption) {
|
|
113
|
-
const group = new THREE.Group();
|
|
116
|
+
const group = markKitOrigin(new THREE.Group(), { owner: "game-test-room", kind: "room" });
|
|
114
117
|
group.name = "test-room-arena";
|
|
115
118
|
scene.background = new THREE.Color(10466504);
|
|
116
119
|
group.add(new THREE.HemisphereLight(13624319, 7037527, 0.75));
|
|
@@ -1706,6 +1709,9 @@ function createNarrator(c) {
|
|
|
1706
1709
|
var EYE = 1.7;
|
|
1707
1710
|
var PLAYER_RADIUS = 0.3;
|
|
1708
1711
|
var WALK = 3.4;
|
|
1712
|
+
var CROUCH_DROP = 0.55;
|
|
1713
|
+
var CROUCH_WALK = 1.7;
|
|
1714
|
+
var CROUCH_RATE = 9;
|
|
1709
1715
|
var RUN = 6.2;
|
|
1710
1716
|
var JUMP = 4.6;
|
|
1711
1717
|
var GRAVITY = 13;
|
|
@@ -1716,17 +1722,20 @@ function createPlayer(THREE, o) {
|
|
|
1716
1722
|
let pitch = 0;
|
|
1717
1723
|
let y = 0;
|
|
1718
1724
|
let eye = EYE;
|
|
1725
|
+
let crouch = 0;
|
|
1719
1726
|
let vy = 0;
|
|
1720
1727
|
let walked = 0;
|
|
1721
1728
|
let running = false;
|
|
1722
1729
|
const px = { x: o.start[0], z: o.start[2] };
|
|
1723
1730
|
let locked = false;
|
|
1724
1731
|
const keys = /* @__PURE__ */ new Set();
|
|
1732
|
+
const crouchKeys = o.crouchKeys ?? ["KeyC"];
|
|
1733
|
+
const isCrouchHeld = () => crouchKeys.some((code) => keys.has(code));
|
|
1725
1734
|
const euler = new THREE.Euler(0, 0, 0, "YXZ");
|
|
1726
1735
|
const apply = () => {
|
|
1727
1736
|
euler.set(pitch, yaw, 0, "YXZ");
|
|
1728
1737
|
cam.quaternion.setFromEuler(euler);
|
|
1729
|
-
cam.position.set(px.x, eye + y, px.z);
|
|
1738
|
+
cam.position.set(px.x, eye + y - CROUCH_DROP * crouch, px.z);
|
|
1730
1739
|
cam.updateMatrixWorld(true);
|
|
1731
1740
|
};
|
|
1732
1741
|
apply();
|
|
@@ -1753,6 +1762,20 @@ function createPlayer(THREE, o) {
|
|
|
1753
1762
|
};
|
|
1754
1763
|
const onLockChange = () => {
|
|
1755
1764
|
locked = document.pointerLockElement === o.dom;
|
|
1765
|
+
if (locked) {
|
|
1766
|
+
try {
|
|
1767
|
+
const nav = typeof navigator !== "undefined" ? navigator : null;
|
|
1768
|
+
nav?.keyboard?.lock?.().catch(() => {
|
|
1769
|
+
});
|
|
1770
|
+
} catch {
|
|
1771
|
+
}
|
|
1772
|
+
} else {
|
|
1773
|
+
try {
|
|
1774
|
+
const nav = typeof navigator !== "undefined" ? navigator : null;
|
|
1775
|
+
nav?.keyboard?.unlock?.();
|
|
1776
|
+
} catch {
|
|
1777
|
+
}
|
|
1778
|
+
}
|
|
1756
1779
|
};
|
|
1757
1780
|
const onMouseDown = (e) => {
|
|
1758
1781
|
o.dom.focus?.();
|
|
@@ -1822,6 +1845,10 @@ function createPlayer(THREE, o) {
|
|
|
1822
1845
|
const onKeyDown = (e) => {
|
|
1823
1846
|
if (typing(e)) return;
|
|
1824
1847
|
if (e.code === "Space" || e.code === "Tab") e.preventDefault();
|
|
1848
|
+
const isControlHeld = e.ctrlKey || e.metaKey || keys.has("ControlLeft") || keys.has("ControlRight");
|
|
1849
|
+
if (isControlHeld || e.altKey && locked) {
|
|
1850
|
+
if (!/^F\d+$/.test(e.code)) e.preventDefault();
|
|
1851
|
+
}
|
|
1825
1852
|
if (!e.repeat) keys.add(e.code);
|
|
1826
1853
|
o.hooks.key(e.code, true, e);
|
|
1827
1854
|
};
|
|
@@ -1832,6 +1859,11 @@ function createPlayer(THREE, o) {
|
|
|
1832
1859
|
};
|
|
1833
1860
|
const onContext = (e) => e.preventDefault();
|
|
1834
1861
|
const onBlur = () => keys.clear();
|
|
1862
|
+
const onBeforeUnload = (e) => {
|
|
1863
|
+
if (!locked && !keys.has("ControlLeft") && !keys.has("ControlRight")) return;
|
|
1864
|
+
e.preventDefault();
|
|
1865
|
+
e.returnValue = "";
|
|
1866
|
+
};
|
|
1835
1867
|
o.dom.addEventListener("mousedown", onMouseDown);
|
|
1836
1868
|
window.addEventListener("mouseup", onMouseUp);
|
|
1837
1869
|
window.addEventListener("mousemove", onMouseMove);
|
|
@@ -1841,6 +1873,7 @@ function createPlayer(THREE, o) {
|
|
|
1841
1873
|
window.addEventListener("keydown", onKeyDown);
|
|
1842
1874
|
window.addEventListener("keyup", onKeyUp);
|
|
1843
1875
|
window.addEventListener("blur", onBlur);
|
|
1876
|
+
window.addEventListener("beforeunload", onBeforeUnload);
|
|
1844
1877
|
return {
|
|
1845
1878
|
get yaw() {
|
|
1846
1879
|
return yaw;
|
|
@@ -1869,12 +1902,18 @@ function createPlayer(THREE, o) {
|
|
|
1869
1902
|
get running() {
|
|
1870
1903
|
return running;
|
|
1871
1904
|
},
|
|
1905
|
+
get crouching() {
|
|
1906
|
+
return isCrouchHeld();
|
|
1907
|
+
},
|
|
1872
1908
|
keys,
|
|
1873
1909
|
update(dt) {
|
|
1874
|
-
const
|
|
1910
|
+
const crouched = isCrouchHeld();
|
|
1911
|
+
crouch += ((crouched ? 1 : 0) - crouch) * Math.min(1, dt * CROUCH_RATE);
|
|
1912
|
+
if (!crouched && crouch < 1e-3) crouch = 0;
|
|
1913
|
+
const run = !crouched && (keys.has("ShiftLeft") || keys.has("ShiftRight"));
|
|
1875
1914
|
const fromX = px.x;
|
|
1876
1915
|
const fromZ = px.z;
|
|
1877
|
-
const speed = run ? RUN : WALK;
|
|
1916
|
+
const speed = crouched ? CROUCH_WALK : run ? RUN : WALK;
|
|
1878
1917
|
let f = 0;
|
|
1879
1918
|
let r = 0;
|
|
1880
1919
|
if (keys.has("KeyW") || keys.has("ArrowUp")) f += 1;
|
|
@@ -1950,13 +1989,14 @@ function createPlayer(THREE, o) {
|
|
|
1950
1989
|
window.removeEventListener("keydown", onKeyDown);
|
|
1951
1990
|
window.removeEventListener("keyup", onKeyUp);
|
|
1952
1991
|
window.removeEventListener("blur", onBlur);
|
|
1992
|
+
window.removeEventListener("beforeunload", onBeforeUnload);
|
|
1953
1993
|
if (document.pointerLockElement === o.dom) document.exitPointerLock?.();
|
|
1954
1994
|
}
|
|
1955
1995
|
};
|
|
1956
1996
|
}
|
|
1957
1997
|
|
|
1958
1998
|
// src/avatar.ts
|
|
1959
|
-
var DEFAULT_STRIKE_CLIPS = { fists: "punch_right", shove: "shove_two_hands", kick: "front_kick" };
|
|
1999
|
+
var DEFAULT_STRIKE_CLIPS = { hand: "punch_right", fists: "punch_right", shove: "shove_two_hands", kick: "front_kick" };
|
|
1960
2000
|
var DEFAULT_HAND_STANCES = { fists: "fists" };
|
|
1961
2001
|
var THIRD_PERSON_DEFAULTS = {
|
|
1962
2002
|
back: 3.8,
|
|
@@ -2197,7 +2237,7 @@ var en = {
|
|
|
2197
2237
|
"hud.throw": "throw",
|
|
2198
2238
|
"hud.tilt": "tilt (hold)",
|
|
2199
2239
|
"hud.equip": "equip {weapon}",
|
|
2200
|
-
"ui.help": "Click: look \xB7 WASD: walk \xB7 Shift: run \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",
|
|
2240
|
+
"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",
|
|
2201
2241
|
"ui.help.camera": "V: 1st/3rd person (Shift+V: kick)",
|
|
2202
2242
|
"camera.first": "1st person",
|
|
2203
2243
|
"camera.third": "3rd person",
|
|
@@ -2218,6 +2258,8 @@ var en = {
|
|
|
2218
2258
|
"ui.slow": "Slow motion 0.25\xD7",
|
|
2219
2259
|
"ui.fast": "Time \xD710",
|
|
2220
2260
|
"ui.tap": "Tap",
|
|
2261
|
+
"ui.fullscreen": "Full screen",
|
|
2262
|
+
"ui.fullscreen.hint": "Full screen opened from here keeps the whole keyboard in the game, Ctrl+W included (F11 does not)",
|
|
2221
2263
|
"ui.blast.shelter": "Shelter",
|
|
2222
2264
|
"ui.blast.fragmentHits": "Fragments",
|
|
2223
2265
|
"ui.blast.confinement": "Confinement",
|
|
@@ -2476,7 +2518,7 @@ var ptBR = {
|
|
|
2476
2518
|
"hud.throw": "arremessar",
|
|
2477
2519
|
"hud.tilt": "inclinar (segurar)",
|
|
2478
2520
|
"hud.equip": "equipar {weapon}",
|
|
2479
|
-
"ui.help": "Clique: olhar \xB7 WASD: andar \xB7 Shift: correr \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",
|
|
2521
|
+
"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",
|
|
2480
2522
|
"ui.help.camera": "V: 1\xAA/3\xAA pessoa (Shift+V: chute)",
|
|
2481
2523
|
"camera.first": "1\xAA pessoa",
|
|
2482
2524
|
"camera.third": "3\xAA pessoa",
|
|
@@ -2497,6 +2539,8 @@ var ptBR = {
|
|
|
2497
2539
|
"ui.slow": "C\xE2mera lenta 0,25\xD7",
|
|
2498
2540
|
"ui.fast": "Tempo \xD710",
|
|
2499
2541
|
"ui.tap": "Torneira",
|
|
2542
|
+
"ui.fullscreen": "Tela cheia",
|
|
2543
|
+
"ui.fullscreen.hint": "A tela cheia aberta por aqui segura o teclado todo no jogo, inclusive Ctrl+W (o F11 n\xE3o)",
|
|
2500
2544
|
"ui.blast.shelter": "Abrigo",
|
|
2501
2545
|
"ui.blast.fragmentHits": "Estilha\xE7os",
|
|
2502
2546
|
"ui.blast.confinement": "Confinamento",
|
|
@@ -2924,6 +2968,19 @@ var thumb = (thumbnail, credit) => {
|
|
|
2924
2968
|
e.style.setProperty("--tr-thumb", `url("${thumbSource(thumbnail)}")`);
|
|
2925
2969
|
return e;
|
|
2926
2970
|
};
|
|
2971
|
+
async function toggleFullscreen() {
|
|
2972
|
+
const keyboard = navigator.keyboard;
|
|
2973
|
+
try {
|
|
2974
|
+
if (document.fullscreenElement) {
|
|
2975
|
+
keyboard?.unlock?.();
|
|
2976
|
+
await document.exitFullscreen();
|
|
2977
|
+
return;
|
|
2978
|
+
}
|
|
2979
|
+
await document.documentElement.requestFullscreen();
|
|
2980
|
+
await keyboard?.lock?.();
|
|
2981
|
+
} catch {
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
2927
2984
|
function createUi(o) {
|
|
2928
2985
|
const { t } = o;
|
|
2929
2986
|
const style = mk2("style");
|
|
@@ -3135,6 +3192,8 @@ function createUi(o) {
|
|
|
3135
3192
|
o.onFast(fast);
|
|
3136
3193
|
});
|
|
3137
3194
|
const tapBtn = ctl(t("ui.tap"), () => o.onTap());
|
|
3195
|
+
const fullscreenBtn = ctl(t("ui.fullscreen"), () => void toggleFullscreen());
|
|
3196
|
+
fullscreenBtn.title = t("ui.fullscreen.hint");
|
|
3138
3197
|
const blastBtns = ["shelter", "fragmentHits", "confinement"].map((key) => ({ key, b: ctl(t(`ui.blast.${key}`), () => o.onBlast(key)) }));
|
|
3139
3198
|
for (const { key, b } of blastBtns) b.title = t(`ui.blast.${key}.hint`);
|
|
3140
3199
|
const qSel = mk2("select");
|
|
@@ -3688,6 +3747,7 @@ function createTestRoom(options) {
|
|
|
3688
3747
|
blockers,
|
|
3689
3748
|
limit: arenaSize,
|
|
3690
3749
|
start: arena.playerStart,
|
|
3750
|
+
crouchKeys: options.crouch?.keys,
|
|
3691
3751
|
hooks: {
|
|
3692
3752
|
primaryDown: () => {
|
|
3693
3753
|
useHeld = true;
|
|
@@ -3940,6 +4000,7 @@ function createTestRoom(options) {
|
|
|
3940
4000
|
const third = { ...THIRD_PERSON_DEFAULTS, ...options.thirdPerson };
|
|
3941
4001
|
let avatar = null;
|
|
3942
4002
|
let cameraMode = options.cameraMode ?? "first";
|
|
4003
|
+
let crouchForced = false;
|
|
3943
4004
|
const avatarState = { speed: 0, running: false, crouch: false, down: false, armed: false, pitch: 0 };
|
|
3944
4005
|
let feetPrev = null;
|
|
3945
4006
|
let armedForced = null;
|
|
@@ -3993,9 +4054,12 @@ function createTestRoom(options) {
|
|
|
3993
4054
|
if (avatarState.speed < 0.02) avatarState.speed = 0;
|
|
3994
4055
|
}
|
|
3995
4056
|
feetPrev = [x, z];
|
|
3996
|
-
avatarState.running = player.keys.has("ShiftLeft") || player.keys.has("ShiftRight");
|
|
4057
|
+
avatarState.running = !player.crouching && (player.keys.has("ShiftLeft") || player.keys.has("ShiftRight"));
|
|
4058
|
+
avatarState.crouch = crouchForced || player.crouching;
|
|
3997
4059
|
avatarState.armed = armedForced ?? tool.def.action.kind === "firearm";
|
|
3998
4060
|
avatarState.pitch = player.pitch;
|
|
4061
|
+
avatarState.holding = heldPiece !== null;
|
|
4062
|
+
avatarState.itemUse = useItemPlayer ? { id: useItemName, ...useItemPlayer.pose() } : heldPiece && heldTilt > 0.01 ? { id: "tilt", position: [0.2, -0.1, -0.38], rotation: [-heldTilt, 0, 0] } : null;
|
|
3999
4063
|
avatar.root.position.set(x, player.feet, z);
|
|
4000
4064
|
avatar.root.rotation.set(0, player.yaw + Math.PI, 0);
|
|
4001
4065
|
avatar.update(dt, avatarState);
|
|
@@ -4451,7 +4515,11 @@ function createTestRoom(options) {
|
|
|
4451
4515
|
const useTool = (dt, held) => {
|
|
4452
4516
|
aim();
|
|
4453
4517
|
const cat = tool.def.category;
|
|
4454
|
-
if (cat === "hand")
|
|
4518
|
+
if (cat === "hand") {
|
|
4519
|
+
if (strikes.some((strike) => strike.key === "hand" || strike.key === "fists")) return;
|
|
4520
|
+
strikeUse({ kind: "melee", damage: "blunt", energy: 25, radius: 0.1, reach: 0.8, origin: [aimO[0], aimO[1], aimO[2]], direction: [aimD[0], aimD[1], aimD[2]] }, "hand");
|
|
4521
|
+
return;
|
|
4522
|
+
}
|
|
4455
4523
|
if (strikes.some((strike) => strike.key === toolId)) return;
|
|
4456
4524
|
const res = tool.trigger([aimO[0], aimO[1], aimO[2]], [aimD[0], aimD[1], aimD[2]], dt, held, "player");
|
|
4457
4525
|
flushEvents(toolId, tool, res.recoil);
|
|
@@ -4544,6 +4612,7 @@ function createTestRoom(options) {
|
|
|
4544
4612
|
let heldTilt = 0;
|
|
4545
4613
|
let tiltKey = 0;
|
|
4546
4614
|
const heldLocal = new THREE.Vector3();
|
|
4615
|
+
const heldSize = new THREE.Vector3(0.2, 0.2, 0.2);
|
|
4547
4616
|
let heldRadius = 0.2;
|
|
4548
4617
|
const heldVel = [0, 0, 0];
|
|
4549
4618
|
let useItemPlayer = null;
|
|
@@ -4589,12 +4658,15 @@ function createTestRoom(options) {
|
|
|
4589
4658
|
heldName = nameOf(r.object);
|
|
4590
4659
|
piece.held = true;
|
|
4591
4660
|
piece.sleeping = false;
|
|
4661
|
+
const savedQ = piece.root.quaternion.clone();
|
|
4662
|
+
piece.root.quaternion.identity();
|
|
4663
|
+
piece.root.updateMatrixWorld(true);
|
|
4592
4664
|
const box2 = new THREE.Box3().setFromObject(piece.root);
|
|
4593
|
-
|
|
4665
|
+
box2.getCenter(heldLocal).sub(piece.root.position);
|
|
4666
|
+
box2.getSize(heldSize);
|
|
4667
|
+
piece.root.quaternion.copy(savedQ);
|
|
4594
4668
|
piece.root.updateMatrixWorld(true);
|
|
4595
|
-
|
|
4596
|
-
heldLocal.applyQuaternion(piece.root.quaternion.clone().invert());
|
|
4597
|
-
heldRadius = box2.getSize(new THREE.Vector3()).length() / 3;
|
|
4669
|
+
heldRadius = heldSize.length() / 3;
|
|
4598
4670
|
heldYaw = 0;
|
|
4599
4671
|
heldTilt = 0;
|
|
4600
4672
|
heldVel[0] = heldVel[1] = heldVel[2] = 0;
|
|
@@ -4615,18 +4687,43 @@ function createTestRoom(options) {
|
|
|
4615
4687
|
fire("throw", { objectId: id, speed });
|
|
4616
4688
|
return true;
|
|
4617
4689
|
};
|
|
4618
|
-
const carriedTargets = (root) => {
|
|
4690
|
+
const carriedTargets = (root, gripQ) => {
|
|
4691
|
+
const width2 = Math.max(heldSize.x, heldSize.z);
|
|
4692
|
+
const sideGrip = heldSize.y > width2 * 1.04 && width2 <= 0.22;
|
|
4693
|
+
if (sideGrip) {
|
|
4694
|
+
const center2 = heldLocal.clone().applyQuaternion(root.quaternion).add(root.position);
|
|
4695
|
+
const radius = Math.min(heldSize.x, heldSize.z) * 0.5;
|
|
4696
|
+
const handX = new THREE.Vector3(-0.35, 0, -0.94).normalize().applyQuaternion(gripQ);
|
|
4697
|
+
const handY = new THREE.Vector3(0.94, 0, -0.35).normalize().applyQuaternion(gripQ);
|
|
4698
|
+
const handZ = new THREE.Vector3().crossVectors(handX, handY).normalize();
|
|
4699
|
+
const q2 = new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().makeBasis(handX, handY, handZ));
|
|
4700
|
+
const up = new THREE.Vector3(0, 1, 0).applyQuaternion(gripQ);
|
|
4701
|
+
const palm = center2.clone().addScaledVector(handY, radius + CARRY_PALM).addScaledVector(handX, -0.022).addScaledVector(up, heldSize.y > 0.16 ? -heldSize.y * 0.08 : -5e-3);
|
|
4702
|
+
if (useItemName === "open" || useItemName === "close") {
|
|
4703
|
+
const leftX = new THREE.Vector3(0.35, 0, -0.94).normalize().applyQuaternion(gripQ);
|
|
4704
|
+
const leftY = new THREE.Vector3(-0.94, 0, -0.35).normalize().applyQuaternion(gripQ);
|
|
4705
|
+
const leftZ = new THREE.Vector3().crossVectors(leftX, leftY).normalize();
|
|
4706
|
+
const lq = new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().makeBasis(leftX, leftY, leftZ));
|
|
4707
|
+
const leftPalm = center2.clone().addScaledVector(up, heldSize.y * 0.45).addScaledVector(leftY, radius * 0.65 + CARRY_PALM).addScaledVector(leftX, -0.018);
|
|
4708
|
+
return {
|
|
4709
|
+
R: { position: [palm.x, palm.y, palm.z], quaternion: [q2.x, q2.y, q2.z, q2.w] },
|
|
4710
|
+
L: { position: [leftPalm.x, leftPalm.y, leftPalm.z], quaternion: [lq.x, lq.y, lq.z, lq.w] },
|
|
4711
|
+
grip: 0.85,
|
|
4712
|
+
gripL: 0.8
|
|
4713
|
+
};
|
|
4714
|
+
}
|
|
4715
|
+
return { R: { position: [palm.x, palm.y, palm.z], quaternion: [q2.x, q2.y, q2.z, q2.w] }, grip: 0.85 };
|
|
4716
|
+
}
|
|
4619
4717
|
const box2 = new THREE.Box3().setFromObject(root);
|
|
4620
4718
|
const center = box2.getCenter(new THREE.Vector3());
|
|
4621
|
-
const forward =
|
|
4622
|
-
|
|
4623
|
-
|
|
4624
|
-
forward.normalize();
|
|
4625
|
-
const
|
|
4626
|
-
const z = forward.clone().cross(back).normalize();
|
|
4627
|
-
const q = new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().makeBasis(forward, back, z));
|
|
4719
|
+
const forward = new THREE.Vector3(0, 0, -1).applyQuaternion(gripQ).normalize();
|
|
4720
|
+
const back = new THREE.Vector3(0, -1, 0).applyQuaternion(gripQ).normalize();
|
|
4721
|
+
const z = new THREE.Vector3().crossVectors(forward, back).normalize();
|
|
4722
|
+
const y = new THREE.Vector3().crossVectors(z, forward).normalize();
|
|
4723
|
+
const q = new THREE.Quaternion().setFromRotationMatrix(new THREE.Matrix4().makeBasis(forward, y, z));
|
|
4628
4724
|
return { R: { position: [center.x, box2.min.y - CARRY_PALM, center.z], quaternion: [q.x, q.y, q.z, q.w] }, grip: CARRY_GRIP };
|
|
4629
4725
|
};
|
|
4726
|
+
const gripQuat = new THREE.Quaternion();
|
|
4630
4727
|
const followHeld = (dt) => {
|
|
4631
4728
|
const piece = heldPiece;
|
|
4632
4729
|
carriedHand = null;
|
|
@@ -4636,21 +4733,47 @@ function createTestRoom(options) {
|
|
|
4636
4733
|
let pos;
|
|
4637
4734
|
const targetQ = tmpQ;
|
|
4638
4735
|
const hand = thirdActive() ? avatar?.hand?.("R") : void 0;
|
|
4736
|
+
const isDrink = useItemName === "drink" || useItemName === "sip" || useItemName === "chug";
|
|
4639
4737
|
if (hand) {
|
|
4640
|
-
|
|
4641
|
-
|
|
4642
|
-
|
|
4738
|
+
const handWorld = hand.getWorldPosition(tmpHand);
|
|
4739
|
+
const yawQ = new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), player.yaw);
|
|
4740
|
+
const pose = useItemPlayer?.pose();
|
|
4741
|
+
const rx = pose ? isDrink ? Math.abs(pose.rotation[0]) * 0.9 : pose.rotation[0] : -heldTilt;
|
|
4742
|
+
const ry = pose ? pose.rotation[1] : 0;
|
|
4743
|
+
const rz = pose ? pose.rotation[2] : 0;
|
|
4744
|
+
gripQuat.copy(yawQ).multiply(new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.max(-1.35, Math.min(1.25, rx)), ry, rz, "XYZ")));
|
|
4745
|
+
targetQ.copy(yawQ).multiply(new THREE.Quaternion().setFromEuler(new THREE.Euler(rx, ry, rz, "XYZ")));
|
|
4746
|
+
targetQ.multiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), heldYaw));
|
|
4747
|
+
const gripOffset = new THREE.Vector3(-0.02, 0.015, -0.04).applyQuaternion(gripQuat);
|
|
4748
|
+
pos = tmpV.copy(handWorld).add(gripOffset);
|
|
4643
4749
|
} else if (useItemPlayer) {
|
|
4644
4750
|
const pose = useItemPlayer.pose();
|
|
4645
|
-
const
|
|
4646
|
-
|
|
4647
|
-
const
|
|
4648
|
-
|
|
4751
|
+
const frameQ = avatar?.firstPerson ? camera.quaternion : new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), player.yaw);
|
|
4752
|
+
const raise = isDrink ? Math.max(0, Math.min(1, (pose.position[1] + 0.2) / 0.22)) : 0;
|
|
4753
|
+
const px2 = pose.position[0] + (isDrink ? 0.02 * raise : 0);
|
|
4754
|
+
const py2 = pose.position[1] - (isDrink ? 0.08 * raise : 0);
|
|
4755
|
+
const pz2 = pose.position[2] - (isDrink ? 0.05 * raise : 0);
|
|
4756
|
+
pos = tmpV.set(px2, py2, pz2).applyQuaternion(frameQ).add(camera.position);
|
|
4757
|
+
const rx = isDrink ? Math.abs(pose.rotation[0]) * 0.85 : pose.rotation[0];
|
|
4758
|
+
const eq = new THREE.Quaternion().setFromEuler(new THREE.Euler(rx, pose.rotation[1], pose.rotation[2], "XYZ"));
|
|
4759
|
+
const wristEq = new THREE.Quaternion().setFromEuler(new THREE.Euler(Math.max(-1.35, Math.min(1.25, rx)), pose.rotation[1], pose.rotation[2], "XYZ"));
|
|
4760
|
+
gripQuat.copy(frameQ).multiply(wristEq);
|
|
4761
|
+
targetQ.copy(frameQ).multiply(eq);
|
|
4649
4762
|
targetQ.multiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), heldYaw));
|
|
4650
4763
|
} else {
|
|
4651
|
-
|
|
4764
|
+
const small = heldRadius < 0.22;
|
|
4765
|
+
const holdX = avatar?.firstPerson ? small ? 0.22 : 0.08 : 0.05;
|
|
4766
|
+
const holdY = avatar?.firstPerson ? small ? -0.2 : -0.26 : -0.28;
|
|
4767
|
+
const holdZ = avatar?.firstPerson ? small ? -0.42 : -Math.min(1.15, CARRY_NEAR + heldRadius) : -1.15;
|
|
4768
|
+
pos = tmpV.set(holdX, holdY, holdZ).applyMatrix4(camera.matrixWorld);
|
|
4769
|
+
const baseQ = avatar?.firstPerson && small ? camera.quaternion : new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), player.yaw);
|
|
4770
|
+
gripQuat.copy(baseQ).multiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), Math.max(-1.35, -heldTilt)));
|
|
4652
4771
|
targetQ.setFromAxisAngle(new THREE.Vector3(0, 1, 0), player.yaw + heldYaw);
|
|
4653
|
-
|
|
4772
|
+
if (avatar?.firstPerson && small && heldTilt <= 1e-4) {
|
|
4773
|
+
targetQ.copy(camera.quaternion).multiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(0, 1, 0), heldYaw));
|
|
4774
|
+
} else {
|
|
4775
|
+
targetQ.multiply(new THREE.Quaternion().setFromAxisAngle(new THREE.Vector3(1, 0, 0), -heldTilt));
|
|
4776
|
+
}
|
|
4654
4777
|
}
|
|
4655
4778
|
const k = Math.min(1, (useItemPlayer ? 30 : 16) * dt);
|
|
4656
4779
|
const px = root.position.x;
|
|
@@ -4665,7 +4788,7 @@ function createTestRoom(options) {
|
|
|
4665
4788
|
heldVel[2] = (root.position.z - pz) / dt;
|
|
4666
4789
|
}
|
|
4667
4790
|
root.updateMatrixWorld(true);
|
|
4668
|
-
if (avatar?.firstPerson && cameraMode === "first") carriedHand = carriedTargets(root);
|
|
4791
|
+
if (avatar?.firstPerson && cameraMode === "first") carriedHand = carriedTargets(root, gripQuat);
|
|
4669
4792
|
};
|
|
4670
4793
|
const useItem = (useId) => {
|
|
4671
4794
|
const e = heldEntry;
|
|
@@ -4675,11 +4798,13 @@ function createTestRoom(options) {
|
|
|
4675
4798
|
const c = fluids.container(e.containerId);
|
|
4676
4799
|
if (!c) return false;
|
|
4677
4800
|
if (def.liquids && def.liquids.length > 0 && (!c.liquid || !def.liquids.includes(c.liquid))) return false;
|
|
4678
|
-
|
|
4801
|
+
const consuming = useId === "drink" || useId === "sip" || useId === "chug" || useId === "pour" || useId === "pour_out";
|
|
4802
|
+
if (consuming && c.liters <= 1e-6) {
|
|
4679
4803
|
say(t("hud.empty_container", { name: e.name }));
|
|
4680
4804
|
return false;
|
|
4681
4805
|
}
|
|
4682
4806
|
if (useItemPlayer && !useItemPlayer.done) return false;
|
|
4807
|
+
if (consuming && c.closed) fluids.open(e.containerId);
|
|
4683
4808
|
useItemPlayer = createItemUse(fluids, e.containerId, def);
|
|
4684
4809
|
useItemName = def.id;
|
|
4685
4810
|
action("use_item", t("act.use_item", { name: e.name, use: S.text[`use.${def.id}`] ?? def.id }), { objectId: e.so.objectId, itemId: e.so.itemId, data: { use: def.id } });
|
|
@@ -4801,7 +4926,7 @@ function createTestRoom(options) {
|
|
|
4801
4926
|
if (heldEntry?.uses[0]) useItem(heldEntry.uses[0]);
|
|
4802
4927
|
break;
|
|
4803
4928
|
case "KeyC":
|
|
4804
|
-
if (heldEntry?.uses[1]) useItem(heldEntry.uses[1]);
|
|
4929
|
+
if (heldEntry?.uses[1] && !(options.crouch?.keys ?? ["KeyC"]).includes("KeyC")) useItem(heldEntry.uses[1]);
|
|
4805
4930
|
break;
|
|
4806
4931
|
case "KeyV":
|
|
4807
4932
|
if (heldEntry?.uses[2]) useItem(heldEntry.uses[2]);
|
|
@@ -4893,6 +5018,7 @@ function createTestRoom(options) {
|
|
|
4893
5018
|
const roomMetrics = createRoomMetrics({ gl, destruction: renderer, fluids: fluidView, effects: vfx.world, ids: roomMetricIds, blast: () => lastBlast });
|
|
4894
5019
|
let qualityName = quality;
|
|
4895
5020
|
const syncContainers = (sdt) => {
|
|
5021
|
+
const drinkingHeld = useItemPlayer !== null && (useItemName === "drink" || useItemName === "sip" || useItemName === "chug");
|
|
4896
5022
|
for (const e of entries.values()) {
|
|
4897
5023
|
if (!e.containerId) continue;
|
|
4898
5024
|
const host2 = hostOf(e);
|
|
@@ -4901,7 +5027,8 @@ function createTestRoom(options) {
|
|
|
4901
5027
|
const p = tmpV.set(e.fluidOffset[0], e.fluidOffset[1], e.fluidOffset[2]).applyMatrix4(host2.root.matrixWorld);
|
|
4902
5028
|
const vel = sdt > 0 ? [(p.x - e.last[0]) / sdt, (p.y - e.last[1]) / sdt, (p.z - e.last[2]) / sdt] : void 0;
|
|
4903
5029
|
e.last = [p.x, p.y, p.z];
|
|
4904
|
-
|
|
5030
|
+
const rot = drinkingHeld && e === heldEntry ? [0, 0, 0, 1] : [q.x, q.y, q.z, q.w];
|
|
5031
|
+
fluids.setTransform(e.containerId, [p.x, p.y, p.z], rot, vel);
|
|
4905
5032
|
}
|
|
4906
5033
|
};
|
|
4907
5034
|
const syncLiters = () => {
|
|
@@ -5625,6 +5752,7 @@ function createTestRoom(options) {
|
|
|
5625
5752
|
},
|
|
5626
5753
|
equip,
|
|
5627
5754
|
openArea,
|
|
5755
|
+
modules: () => [...modules.values()].map((m) => ({ id: m.id, name: m.name, group: m.group, hasArea: typeof m.area === "function" })),
|
|
5628
5756
|
openAreaAsync: (area) => loading.busy(t("loading.open_area", { area: areaLabel(area) }), () => openArea(area)),
|
|
5629
5757
|
loading,
|
|
5630
5758
|
busy: (label, work, busyOptions) => loading.busy(label, work, busyOptions),
|
|
@@ -5652,7 +5780,10 @@ function createTestRoom(options) {
|
|
|
5652
5780
|
return avatar;
|
|
5653
5781
|
},
|
|
5654
5782
|
setAvatarState(state) {
|
|
5655
|
-
if (state.crouch !== void 0)
|
|
5783
|
+
if (state.crouch !== void 0) {
|
|
5784
|
+
crouchForced = state.crouch;
|
|
5785
|
+
avatarState.crouch = state.crouch || player.crouching;
|
|
5786
|
+
}
|
|
5656
5787
|
if (state.down !== void 0) avatarState.down = state.down;
|
|
5657
5788
|
if (state.armed !== void 0) armedForced = state.armed;
|
|
5658
5789
|
},
|